From cb56ff59f95c3fee102cf02d71b6bafff98b6c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20P=C3=B6hls?= Date: Wed, 26 Oct 2022 10:53:39 +0200 Subject: [PATCH 01/10] run tests for node 19 --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 6b46c3a..4b793f7 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - node-version: [16.x, 18.x] + node-version: [16.x, 18.x, 19.x] name: Node ${{ matrix.node-version }} From f174e1201ba3048e55dba64c3e621d151c7d99c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20P=C3=B6hls?= Date: Sat, 18 Nov 2023 05:18:31 +0100 Subject: [PATCH 02/10] require node 20 and migrate to ESM --- CHANGELOG.md | 12 +- bin/test.js | 49 -- package.json | 52 +- src/contracts/index.ts | 4 +- src/contracts/page-contract.ts | 2 +- src/index.ts | 12 +- src/inertia-request.ts | 2 +- src/inertia-response.ts | 8 +- src/inertia-service-provider.ts | 26 +- src/utils.ts | 7 +- test/fixtures/ssr.default-export-commonjs.cjs | 5 + test/fixtures/ssr.default-export.js | 11 +- ...xport.js => ssr.named-export-commonjs.cjs} | 2 +- test/fixtures/ssr.named-export.js | 7 +- test/fixtures/ssr.string-export.js | 5 +- test/helpers/index.js | 29 +- test/inertia-service-provider.js | 83 ++- test/redirects.js | 286 +++++---- test/render.js | 543 +++++++++--------- test/ssr.js | 157 +++-- test/versioning.js | 176 +++--- tsconfig.json | 7 +- 22 files changed, 755 insertions(+), 730 deletions(-) delete mode 100644 bin/test.js create mode 100644 test/fixtures/ssr.default-export-commonjs.cjs rename test/fixtures/{ssr.module-export.js => ssr.named-export-commonjs.cjs} (78%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fdb902..95be299 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ # Changelog -## [1.2.2](https://github.com/supercharge/inertia/compare/v1.2.0...v1.2.1) - 2022-09-26 +## [2.0.0](https://github.com/supercharge/inertia/compare/v1.2.2...v2.0.0) - 2023-xx-xx + +### Updated +- bump dependencies + +### Breaking Changes +- require Node.js v20 +- this package migrated to ESM + + +## [1.2.2](https://github.com/supercharge/inertia/compare/v1.2.1...v1.2.2) - 2022-09-26 ### Updated - use `^` for Supercharge framework dependency versions to avoid release issues because of mismatching versions diff --git a/bin/test.js b/bin/test.js deleted file mode 100644 index edf3403..0000000 --- a/bin/test.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict' - -const { specReporter } = require('@japa/spec-reporter') -const { runFailedTests } = require('@japa/run-failed-tests') -const { processCliArgs, configure, run } = require('@japa/runner') - -/* -|-------------------------------------------------------------------------- -| Configure tests -|-------------------------------------------------------------------------- -| -| The configure method accepts the configuration to configure the Japa -| tests runner. -| -| The first method call "processCliArgs" process the command line arguments -| and turns them into a config object. Using this method is not mandatory. -| -| Please consult japa.dev/runner-config for the config docs. -*/ - -const plugins = [] - -if (!process.env.CI) { - plugins.push(runFailedTests()) -} - -configure({ - ...processCliArgs(process.argv.slice(2)), - ...{ - plugins, - timeout: 2000, - files: ['test/**/*.js'], - reporters: [specReporter()], - importer: (filePath) => require(filePath), - filters: { - // files: ['inertia-service-provider.js'] - } - } -}) - -/* -|-------------------------------------------------------------------------- -| Run tests -|-------------------------------------------------------------------------- -| -| The following "run" method is required to execute all the tests. -| -*/ -run() diff --git a/package.json b/package.json index 694edff..b46c422 100644 --- a/package.json +++ b/package.json @@ -7,34 +7,38 @@ "url": "https://github.com/supercharge/inertia/issues" }, "dependencies": { - "@supercharge/contracts": "^3.9.0", + "@supercharge/contracts": "^3.20.4", "@supercharge/fs": "~3.4.0", - "@supercharge/goodies": "~1.11.1", - "@supercharge/support": "^3.9.0", - "dedent": "~0.7.0" + "@supercharge/goodies": "~2.0.0", + "@supercharge/support": "^3.20.4", + "dedent": "~1.5.1" }, "devDependencies": { - "@japa/run-failed-tests": "~1.1.0", - "@japa/runner": "~2.2.1", - "@japa/spec-reporter": "~1.3.1", - "@supercharge/core": "~3.9.0", - "@supercharge/eslint-config-typescript": "~2.3.1", - "@supercharge/http": "~3.9.0", - "@supercharge/tsconfig": "~4.0.0", - "@supercharge/view": "~3.9.0", - "@types/dedent": "~0.7.0", - "c8": "~7.12.0", - "eslint": "~8.24.0", - "expect": "~29.0.3", - "supertest": "~6.2.4", - "typescript": "~4.8.3" + "@supercharge/core": "~3.20.4", + "@supercharge/eslint-config-typescript": "~4.0.0", + "@supercharge/http": "~3.20.4", + "@supercharge/tsconfig": "~7.0.0", + "@supercharge/view": "~3.20.4", + "@types/dedent": "~0.7.2", + "c8": "~8.0.1", + "eslint": "~8.54.0", + "expect": "~29.7.0", + "supertest": "~6.3.3", + "typescript": "~5.2.2", + "uvu": "~0.5.6" }, "engines": { - "node": ">=16" + "node": ">=20" }, "files": [ "dist" ], + "type": "module", + "main": "dist/index.js", + "types": "dist", + "exports": { + ".": "./dist/index.js" + }, "homepage": "https://github.com/supercharge/inertia", "keywords": [ "inertia", @@ -45,7 +49,6 @@ "superchargejs" ], "license": "MIT", - "main": "dist", "publishConfig": { "access": "public" }, @@ -59,9 +62,8 @@ "lint": "eslint src", "lint:fix": "npm run lint -- --fix", "posttest": "c8 report --reporter=html", - "test": "npm run build && npm run test:run", - "test:full": "npm run build && npm run lint && npm run test:run", - "test:run": "c8 node bin/test.js" - }, - "types": "dist" + "test": "npm run build && npm run lint && npm run test:coverage", + "test:coverage": "c8 --include=dist npm run test:run", + "test:run": "uvu --ignore helpers --ignore fixtures" + } } diff --git a/src/contracts/index.ts b/src/contracts/index.ts index 15aaad6..35fbbd2 100644 --- a/src/contracts/index.ts +++ b/src/contracts/index.ts @@ -1,4 +1,4 @@ 'use strict' -export * from './page-contract' -export * from './config-contract' +export * from './page-contract.js' +export * from './config-contract.js' diff --git a/src/contracts/page-contract.ts b/src/contracts/page-contract.ts index 0894b88..e9e9ed8 100644 --- a/src/contracts/page-contract.ts +++ b/src/contracts/page-contract.ts @@ -1,6 +1,6 @@ 'use strict' -import { InertiaOptions } from './config-contract' +import { InertiaOptions } from './config-contract.js' /** * Defines the Inertia page contract. diff --git a/src/index.ts b/src/index.ts index cac02a4..a12002a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ 'use strict' -export * from './contracts' -export * from './inertia' -export * from './inertia-middleware' -export * from './inertia-request' -export * from './inertia-response' -export * from './inertia-service-provider' +export * from './contracts/index.js' +export * from './inertia.js' +export * from './inertia-middleware.js' +export * from './inertia-request.js' +export * from './inertia-response.js' +export * from './inertia-service-provider.js' diff --git a/src/inertia-request.ts b/src/inertia-request.ts index 5d8c243..085e85c 100644 --- a/src/inertia-request.ts +++ b/src/inertia-request.ts @@ -1,6 +1,6 @@ 'use strict' -import { SharesData } from './shares-data' +import { SharesData } from './shares-data.js' export class InertiaRequest extends SharesData { /** diff --git a/src/inertia-response.ts b/src/inertia-response.ts index 54a4673..8e791c3 100644 --- a/src/inertia-response.ts +++ b/src/inertia-response.ts @@ -1,11 +1,11 @@ 'use strict' import Os from 'node:os' -import { SharesData } from './shares-data' -import { resolveRenderFunctionFrom } from './utils' +import { SharesData } from './shares-data.js' import { isAsyncFunction } from '@supercharge/goodies' +import { resolveRenderFunctionFrom } from './utils.js' import { Application, HttpContext, HttpResponse } from '@supercharge/contracts' -import { InertiaOptions, InertiaVersionValue, PageContract } from './contracts' +import { InertiaOptions, InertiaVersionValue, PageContract } from './contracts/index.js' export class InertiaResponse extends SharesData { /** @@ -220,7 +220,7 @@ export class InertiaResponse extends SharesData { * a "render" function either via a "default" export a named "render" export. */ protected async renderSsrPage (page: PageContract): Promise<{ head: string[], body: string }> { - const render = resolveRenderFunctionFrom(this.config.ssr!.resolveRenderFunctionFrom!) + const render = await resolveRenderFunctionFrom(this.config.ssr!.resolveRenderFunctionFrom!) return render(page) } diff --git a/src/inertia-service-provider.ts b/src/inertia-service-provider.ts index a55e73e..030d18e 100644 --- a/src/inertia-service-provider.ts +++ b/src/inertia-service-provider.ts @@ -2,11 +2,11 @@ import Fs from 'node:fs' import dedent from 'dedent' -import { InertiaOptions } from './contracts' -import { InertiaRequest } from './inertia-request' -import { resolveRenderFunctionFrom } from './utils' -import { InertiaResponse } from './inertia-response' +import { InertiaOptions } from './contracts/index.js' +import { InertiaRequest } from './inertia-request.js' import { ServiceProvider } from '@supercharge/support' +import { resolveRenderFunctionFrom } from './utils.js' +import { InertiaResponse } from './inertia-response.js' import { Application, HttpRequest, HttpRequestCtor, HttpResponse, HttpResponseCtor, ViewEngine } from '@supercharge/contracts' /** @@ -45,7 +45,7 @@ export class InertiaServiceProvider extends ServiceProvider { override async boot (): Promise { this.registerInertiaPartialViews() this.registerInertiaRequestMacros() - this.registerInertiaResponseMacros() + await this.registerInertiaResponseMacros() } /** @@ -105,15 +105,19 @@ export class InertiaServiceProvider extends ServiceProvider { /** * Register the Inertia resposne macros. */ - protected registerInertiaResponseMacros (): void { + protected async registerInertiaResponseMacros (): Promise { const app = this.app().make('app') - const Response = this.app().make('response') - const inertiaConfig = app.config().get('inertia', { view: 'app', ssr: { enabled: false } }) + const inertiaConfig = app.config().get('inertia', { + view: 'app', + ssr: { enabled: false } + }) if (inertiaConfig.ssr?.enabled) { - this.ensureSsrRenderFunction(inertiaConfig) + await this.ensureSsrRenderFunction(inertiaConfig) } + const Response = this.app().make('response') + Response.macro('inertia', function (this: HttpResponse) { return new InertiaResponse(app, this.ctx(), inertiaConfig) }) @@ -122,7 +126,7 @@ export class InertiaServiceProvider extends ServiceProvider { /** * Ensure the configured SSR render function is available. */ - protected ensureSsrRenderFunction (inertiaConfig: InertiaOptions): void { + protected async ensureSsrRenderFunction (inertiaConfig: InertiaOptions): Promise { const renderFunctionPath = inertiaConfig.ssr?.resolveRenderFunctionFrom if (!renderFunctionPath) { @@ -133,6 +137,6 @@ export class InertiaServiceProvider extends ServiceProvider { throw new Error(`Inertia SSR is enabled but we cannot resolve the file at "${renderFunctionPath}".`) } - resolveRenderFunctionFrom(renderFunctionPath) + await resolveRenderFunctionFrom(renderFunctionPath) } } diff --git a/src/utils.ts b/src/utils.ts index 00a6e4c..5d59a47 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,15 +1,14 @@ 'use strict' -import { esmRequire } from '@supercharge/goodies' +import { resolveDefaultImport } from '@supercharge/goodies' type RenderFunction = Function | { render: Function } /** * Returns the SSR "render" function from the given `path`. */ -export function resolveRenderFunctionFrom (renderFunctionPath: string): Function { - // TODO: cache require() calls - const renderFn = esmRequire(renderFunctionPath) +export async function resolveRenderFunctionFrom (renderFunctionPath: string): Promise { + const renderFn = await resolveDefaultImport<{ default: RenderFunction | undefined }>(renderFunctionPath) if (typeof renderFn === 'function') { return renderFn diff --git a/test/fixtures/ssr.default-export-commonjs.cjs b/test/fixtures/ssr.default-export-commonjs.cjs new file mode 100644 index 0000000..0079700 --- /dev/null +++ b/test/fixtures/ssr.default-export-commonjs.cjs @@ -0,0 +1,5 @@ +'use strict' + +const render = require('./ssr.named-export-commonjs.cjs') + +module.exports = render diff --git a/test/fixtures/ssr.default-export.js b/test/fixtures/ssr.default-export.js index 0986a54..c70ea31 100644 --- a/test/fixtures/ssr.default-export.js +++ b/test/fixtures/ssr.default-export.js @@ -1,5 +1,10 @@ 'use strict' -const render = require('./ssr.module-export') - -exports.default = render +export default function render (page) { + return { + head: [ + 'Supercharge Inertia SSR' + ], + body: `

Hello Test SSR: ${page.props.name}

` + } +} diff --git a/test/fixtures/ssr.module-export.js b/test/fixtures/ssr.named-export-commonjs.cjs similarity index 78% rename from test/fixtures/ssr.module-export.js rename to test/fixtures/ssr.named-export-commonjs.cjs index 6225fbd..d9d99ce 100644 --- a/test/fixtures/ssr.module-export.js +++ b/test/fixtures/ssr.named-export-commonjs.cjs @@ -1,6 +1,6 @@ 'use strict' -module.exports = function render (page) { +exports.render = function render (page) { return { head: [ 'Supercharge Inertia SSR' diff --git a/test/fixtures/ssr.named-export.js b/test/fixtures/ssr.named-export.js index 5945d88..54469b9 100644 --- a/test/fixtures/ssr.named-export.js +++ b/test/fixtures/ssr.named-export.js @@ -1,5 +1,8 @@ 'use strict' -const render = require('./ssr.module-export') +import RenderFunction from './ssr.named-export-commonjs.cjs' -exports.render = render +export const render = RenderFunction + +// this export default is required by ESM +export default render diff --git a/test/fixtures/ssr.string-export.js b/test/fixtures/ssr.string-export.js index fd0bcfe..01ed58e 100644 --- a/test/fixtures/ssr.string-export.js +++ b/test/fixtures/ssr.string-export.js @@ -1,3 +1,6 @@ 'use strict' -exports.render = 'Should Fail' +export const render = 'Should Fail' + +// this export default is required by ESM +export default render diff --git a/test/helpers/index.js b/test/helpers/index.js index e2479cf..a383bbc 100644 --- a/test/helpers/index.js +++ b/test/helpers/index.js @@ -1,10 +1,13 @@ 'use strict' -const Path = require('path') -const { InertiaServiceProvider } = require('../../dist') -const { HttpServiceProvider } = require('@supercharge/http') -const { ViewServiceProvider } = require('@supercharge/view') -const { Application, ErrorHandler } = require('@supercharge/core') +import Path from 'node:path' +import { fileURLToPath } from 'node:url' +import { ViewServiceProvider } from '@supercharge/view' +import { InertiaServiceProvider } from '../../dist/index.js' +import { Application, ErrorHandler } from '@supercharge/core' +import { HttpServiceProvider, Server } from '@supercharge/http' + +const __dirname = Path.dirname(fileURLToPath(import.meta.url)) /** * Returns a test application. @@ -13,7 +16,7 @@ const { Application, ErrorHandler } = require('@supercharge/core') * * @returns {Promise} */ -async function createApp (inertiaConfig = {}) { +export async function createApp (inertiaConfig = {}) { const app = Application .createWithAppRoot(Path.resolve(__dirname)) .withErrorHandler(ErrorHandler) @@ -52,7 +55,7 @@ async function createApp (inertiaConfig = {}) { * * @returns {Promise} */ -async function createSsrApp (inertiaSsrConfig = {}) { +export async function createSsrApp (inertiaSsrConfig = {}) { return await createApp({ ssr: { enabled: true, @@ -61,7 +64,13 @@ async function createSsrApp (inertiaSsrConfig = {}) { }) } -module.exports = { - createApp, - createSsrApp +/** + * @param {Application} app + */ +export function createServer (app) { + return new Server( + app, + app.config().get('app', {}), + app.config().get('http', {}) + ) } diff --git a/test/inertia-service-provider.js b/test/inertia-service-provider.js index e69fa7b..fd47dbb 100644 --- a/test/inertia-service-provider.js +++ b/test/inertia-service-provider.js @@ -1,52 +1,51 @@ 'use strict' -const { expect } = require('expect') -const Supertest = require('supertest') -const { test } = require('@japa/runner') -const { createApp } = require('./helpers') -const { Server } = require('@supercharge/http') -const { InertiaRequest, InertiaResponse } = require('../dist') - -test.group('InertiaServiceProvider', () => { - test('registers request macros', async () => { - const app = await createApp() - - const server = new Server(app).use(({ request, response }) => { - expect(typeof request.inertia === 'function').toBe(true) - expect(request.inertia()).toBeInstanceOf(InertiaRequest) - - expect(typeof request.isInertia === 'function').toBe(true) - expect(typeof request.isNotInertia === 'function').toBe(true) - - return response.payload({ - isInertia: request.isInertia(), - isNotInertia: request.isNotInertia(), - inertiaVersion: request.inertia().version() - }) +import { test } from 'uvu' +import { expect } from 'expect' +import Supertest from 'supertest' +import { createApp, createServer } from './helpers/index.js' +import { InertiaRequest, InertiaResponse } from '../dist/index.js' + +test('registers request macros', async () => { + const app = await createApp() + + const server = createServer(app).use(({ request, response }) => { + expect(typeof request.inertia === 'function').toBe(true) + expect(request.inertia()).toBeInstanceOf(InertiaRequest) + + expect(typeof request.isInertia === 'function').toBe(true) + expect(typeof request.isNotInertia === 'function').toBe(true) + + return response.payload({ + isInertia: request.isInertia(), + isNotInertia: request.isNotInertia(), + inertiaVersion: request.inertia().version() }) - - await Supertest(server.callback()) - .get('/') - .set('X-Inertia-Version', 'test-version') - .expect(200, { - isInertia: false, - isNotInertia: true, - inertiaVersion: 'test-version' - }) }) - test('registers response macros', async () => { - const app = await createApp() + await Supertest(server.callback()) + .get('/') + .set('X-Inertia-Version', 'test-version') + .expect(200, { + isInertia: false, + isNotInertia: true, + inertiaVersion: 'test-version' + }) +}) - const server = new Server(app).use(({ response }) => { - expect(typeof response.inertia === 'function').toBe(true) - expect(response.inertia()).toBeInstanceOf(InertiaResponse) +test('registers response macros', async () => { + const app = await createApp() - return response.payload('ok') - }) + const server = createServer(app).use(({ response }) => { + expect(typeof response.inertia === 'function').toBe(true) + expect(response.inertia()).toBeInstanceOf(InertiaResponse) - await Supertest(server.callback()) - .get('/') - .expect(200, 'ok') + return response.payload('ok') }) + + await Supertest(server.callback()) + .get('/') + .expect(200, 'ok') }) + +test.run() diff --git a/test/redirects.js b/test/redirects.js index 3b343c8..1e80c19 100644 --- a/test/redirects.js +++ b/test/redirects.js @@ -1,147 +1,145 @@ 'use strict' -// const { expect } = require('expect') -const Supertest = require('supertest') -const { test } = require('@japa/runner') -const { createApp } = require('./helpers') -const { Server } = require('@supercharge/http') -const { HandleInertiaRequestsMiddleware } = require('../dist') - -test.group('Inertia Redirects', () => { - test('keeps response status code 302 for GET requests', async () => { - const app = await createApp() - const server = new Server(app) - .use(HandleInertiaRequestsMiddleware) - .use(({ response }) => { - return response.redirect('/some/path') - }) - - await Supertest(server.callback()) - .get('/') - .set('X-Inertia', 'true') - .expect(302) - }) - - test('uses response status code 303 for POST requests', async () => { - const app = await createApp() - const server = new Server(app) - .use(HandleInertiaRequestsMiddleware) - .use(({ response }) => { - return response.redirect('/some/path') - }) - - await Supertest(server.callback()) - .post('/') - .set('X-Inertia', 'true') - .expect(303) - }) - - test('keeps response status code 302 for non-Inertia POST requests', async () => { - const app = await createApp() - const server = new Server(app) - .use(HandleInertiaRequestsMiddleware) - .use(({ response }) => { - return response.redirect('/some/path') - }) - - await Supertest(server.callback()) - .post('/') - .expect(302) - }) - - test('uses response status code 303 for PUT requests', async () => { - const app = await createApp() - const server = new Server(app) - .use(HandleInertiaRequestsMiddleware) - .use(({ response }) => { - return response.redirect('/some/path') - }) - - await Supertest(server.callback()) - .put('/') - .set('X-Inertia', 'true') - .expect(303) - }) - - test('keeps response status code 302 for non-Inertia PUT requests', async () => { - const app = await createApp() - const server = new Server(app) - .use(HandleInertiaRequestsMiddleware) - .use(({ response }) => { - return response.redirect('/some/path') - }) - - await Supertest(server.callback()) - .put('/') - .expect(302) - }) - - test('uses response status code 303 for PATCH requests', async () => { - const app = await createApp() - const server = new Server(app) - .use(HandleInertiaRequestsMiddleware) - .use(({ response }) => { - return response.redirect('/some/path') - }) - - await Supertest(server.callback()) - .patch('/') - .set('X-Inertia', 'true') - .expect(303) - }) - - test('keeps response status code 302 for non-Inertia PATCH requests', async () => { - const app = await createApp() - const server = new Server(app) - .use(HandleInertiaRequestsMiddleware) - .use(({ response }) => { - return response.redirect('/some/path') - }) - - await Supertest(server.callback()) - .patch('/') - .expect(302) - }) - - test('keeps response status code 302 for DELETE requests', async () => { - const app = await createApp() - const server = new Server(app) - .use(HandleInertiaRequestsMiddleware) - .use(({ response }) => { - return response.redirect('/some/path') - }) - - await Supertest(server.callback()) - .delete('/') - .set('X-Inertia', 'true') - .expect(302) - }) - - test('uses response status code 409 for internal redirect', async () => { - const app = await createApp() - const server = new Server(app) - .use(HandleInertiaRequestsMiddleware) - .use(({ response }) => { - return response.inertia().location('/profile') - }) - - await Supertest(server.callback()) - .get('/') - .set('X-Inertia', 'true') - .expect(409) - }) - - test('uses response status code 409 for external redirect', async () => { - const app = await createApp() - const server = new Server(app) - .use(HandleInertiaRequestsMiddleware) - .use(({ response }) => { - return response.inertia().location('https://superchargejs.com') - }) - - await Supertest(server.callback()) - .get('/') - .set('X-Inertia', 'true') - .expect(409) - }) +import { test } from 'uvu' +import Supertest from 'supertest' +import { createApp, createServer } from './helpers/index.js' +import { HandleInertiaRequestsMiddleware } from '../dist/index.js' + +test('keeps response status code 302 for GET requests', async () => { + const app = await createApp() + const server = createServer(app) + .use(HandleInertiaRequestsMiddleware) + .use(({ response }) => { + return response.redirect('/some/path') + }) + + await Supertest(server.callback()) + .get('/') + .set('X-Inertia', 'true') + .expect(302) }) + +test('uses response status code 303 for POST requests', async () => { + const app = await createApp() + const server = createServer(app) + .use(HandleInertiaRequestsMiddleware) + .use(({ response }) => { + return response.redirect('/some/path') + }) + + await Supertest(server.callback()) + .post('/') + .set('X-Inertia', 'true') + .expect(303) +}) + +test('keeps response status code 302 for non-Inertia POST requests', async () => { + const app = await createApp() + const server = createServer(app) + .use(HandleInertiaRequestsMiddleware) + .use(({ response }) => { + return response.redirect('/some/path') + }) + + await Supertest(server.callback()) + .post('/') + .expect(302) +}) + +test('uses response status code 303 for PUT requests', async () => { + const app = await createApp() + const server = createServer(app) + .use(HandleInertiaRequestsMiddleware) + .use(({ response }) => { + return response.redirect('/some/path') + }) + + await Supertest(server.callback()) + .put('/') + .set('X-Inertia', 'true') + .expect(303) +}) + +test('keeps response status code 302 for non-Inertia PUT requests', async () => { + const app = await createApp() + const server = createServer(app) + .use(HandleInertiaRequestsMiddleware) + .use(({ response }) => { + return response.redirect('/some/path') + }) + + await Supertest(server.callback()) + .put('/') + .expect(302) +}) + +test('uses response status code 303 for PATCH requests', async () => { + const app = await createApp() + const server = createServer(app) + .use(HandleInertiaRequestsMiddleware) + .use(({ response }) => { + return response.redirect('/some/path') + }) + + await Supertest(server.callback()) + .patch('/') + .set('X-Inertia', 'true') + .expect(303) +}) + +test('keeps response status code 302 for non-Inertia PATCH requests', async () => { + const app = await createApp() + const server = createServer(app) + .use(HandleInertiaRequestsMiddleware) + .use(({ response }) => { + return response.redirect('/some/path') + }) + + await Supertest(server.callback()) + .patch('/') + .expect(302) +}) + +test('keeps response status code 302 for DELETE requests', async () => { + const app = await createApp() + const server = createServer(app) + .use(HandleInertiaRequestsMiddleware) + .use(({ response }) => { + return response.redirect('/some/path') + }) + + await Supertest(server.callback()) + .delete('/') + .set('X-Inertia', 'true') + .expect(302) +}) + +test('uses response status code 409 for internal redirect', async () => { + const app = await createApp() + const server = createServer(app) + .use(HandleInertiaRequestsMiddleware) + .use(({ response }) => { + return response.inertia().location('/profile') + }) + + await Supertest(server.callback()) + .get('/') + .set('X-Inertia', 'true') + .expect(409) +}) + +test('uses response status code 409 for external redirect', async () => { + const app = await createApp() + const server = createServer(app) + .use(HandleInertiaRequestsMiddleware) + .use(({ response }) => { + return response.inertia().location('https://superchargejs.com') + }) + + await Supertest(server.callback()) + .get('/') + .set('X-Inertia', 'true') + .expect(409) +}) + +test.run() diff --git a/test/render.js b/test/render.js index 445face..bfbd150 100644 --- a/test/render.js +++ b/test/render.js @@ -1,46 +1,44 @@ 'use strict' -const { expect } = require('expect') -const Supertest = require('supertest') -const { test } = require('@japa/runner') -const { createApp } = require('./helpers') -const { Server } = require('@supercharge/http') - -test.group('Inertia render response', () => { - test('fails when not providing a component name', async () => { - const app = await createApp() - const server = new Server(app) - - server.use(({ response }) => { - return response.inertia().render() - }) +import { test } from 'uvu' +import { expect } from 'expect' +import Supertest from 'supertest' +import { createApp, createServer } from './helpers/index.js' - const response = await Supertest(server.callback()) - .get('/') - .set('accept', 'application/json') - .expect(500) +test('fails when not providing a component name', async () => { + const app = await createApp() + const server = createServer(app) - expect(response.body).toMatchObject({ - statusCode: 500, - message: 'Missing component name when calling "response.inertia().render()"' - }) + server.use(({ response }) => { + return response.inertia().render() }) - test('returns HTML on initial request', async () => { - const app = await createApp() - const server = new Server(app) + const response = await Supertest(server.callback()) + .get('/') + .set('accept', 'application/json') + .expect(500) - server.use(({ response }) => { - return response.inertia().render('Users/List', { - users: [{ name: 'Supercharge' }] - }) + expect(response.body).toMatchObject({ + statusCode: 500, + message: 'Missing component name when calling "response.inertia().render()"' + }) +}) + +test('returns HTML on initial request', async () => { + const app = await createApp() + const server = createServer(app) + + server.use(({ response }) => { + return response.inertia().render('Users/List', { + users: [{ name: 'Supercharge' }] }) + }) - const response = await Supertest(server.callback()) - .get('/') - .expect(200) + const response = await Supertest(server.callback()) + .get('/') + .expect(200) - expect(response.text).toEqual(` + expect(response.text).toEqual(` @@ -53,299 +51,300 @@ test.group('Inertia render response', () => { `) - }) +}) - test('returns JSON on subsequent request', async () => { - const app = await createApp() - const server = new Server(app) +test('returns JSON on subsequent request', async () => { + const app = await createApp() + const server = createServer(app) - server.use(({ response }) => { - return response.inertia().render('Users/List', { - users: [{ name: 'Supercharge' }] - }) + server.use(({ response }) => { + return response.inertia().render('Users/List', { + users: [{ name: 'Supercharge' }] }) + }) - const response = await Supertest(server.callback()) - .get('/') - .set('X-Inertia', 'true') - .set('X-Inertia-Version', '1.0.0') - .expect(200) - - expect(response.body).toEqual({ - component: 'Users/List', - props: { users: [{ name: 'Supercharge' }] }, - version: '1.0.0', - url: '/' - }) + const response = await Supertest(server.callback()) + .get('/') + .set('X-Inertia', 'true') + .set('X-Inertia-Version', '1.0.0') + .expect(200) + + expect(response.body).toEqual({ + component: 'Users/List', + props: { users: [{ name: 'Supercharge' }] }, + version: '1.0.0', + url: '/' }) +}) - test('returns the URL with query parameters', async () => { - const app = await createApp() - const server = new Server(app) +test('returns the URL with query parameters', async () => { + const app = await createApp() + const server = createServer(app) - server.use(({ response }) => { - return response.inertia().render('Users/List', { users: [] }) - }) + server.use(({ response }) => { + return response.inertia().render('Users/List', { users: [] }) + }) - const response = await Supertest(server.callback()) - .get('/some/path?with=query¶meters=foo') - .set('X-Inertia', 'true') - .set('X-Inertia-Version', '1.0.0') - .expect(200) - - expect(response.body).toEqual({ - component: 'Users/List', - props: { users: [] }, - version: '1.0.0', - url: '/some/path?with=query¶meters=foo' - }) + const response = await Supertest(server.callback()) + .get('/some/path?with=query¶meters=foo') + .set('X-Inertia', 'true') + .set('X-Inertia-Version', '1.0.0') + .expect(200) + + expect(response.body).toEqual({ + component: 'Users/List', + props: { users: [] }, + version: '1.0.0', + url: '/some/path?with=query¶meters=foo' }) +}) - test('defaults to an empty props object', async () => { - const app = await createApp() - const server = new Server(app) +test('defaults to an empty props object', async () => { + const app = await createApp() + const server = createServer(app) - server.use(({ response }) => { - return response.inertia().render('Users/List') - }) + server.use(({ response }) => { + return response.inertia().render('Users/List') + }) - const response = await Supertest(server.callback()) - .get('/some/path?foo=bar') - .set('X-Inertia', 'true') - .set('X-Inertia-Version', '1.0.0') - .expect(200) - - expect(response.body).toEqual({ - component: 'Users/List', - props: {}, - version: '1.0.0', - url: '/some/path?foo=bar' - }) + const response = await Supertest(server.callback()) + .get('/some/path?foo=bar') + .set('X-Inertia', 'true') + .set('X-Inertia-Version', '1.0.0') + .expect(200) + + expect(response.body).toEqual({ + component: 'Users/List', + props: {}, + version: '1.0.0', + url: '/some/path?foo=bar' }) +}) - test('resolves lazy props', async () => { - const app = await createApp() - const server = new Server(app) +test('resolves lazy props', async () => { + const app = await createApp() + const server = createServer(app) - server.use(({ response }) => { - return response.inertia().render('Users/List', { + server.use(({ response }) => { + return response.inertia().render('Users/List', { + users: () => [{ name: 'Supercharge' }], + name: async () => { + return await new Promise(resolve => { + setTimeout(resolve('Supercharge'), 10) + }) + }, + nested: { users: () => [{ name: 'Supercharge' }], name: async () => { return await new Promise(resolve => { setTimeout(resolve('Supercharge'), 10) }) - }, - nested: { - users: () => [{ name: 'Supercharge' }], - name: async () => { - return await new Promise(resolve => { - setTimeout(resolve('Supercharge'), 10) - }) - } } - }) + } }) + }) - const response = await Supertest(server.callback()) - .get('/some/path?foo=bar') - .set('X-Inertia', 'true') - .set('X-Inertia-Version', '1.0.0') - .expect(200) - - expect(response.body).toEqual({ - component: 'Users/List', - props: { + const response = await Supertest(server.callback()) + .get('/some/path?foo=bar') + .set('X-Inertia', 'true') + .set('X-Inertia-Version', '1.0.0') + .expect(200) + + expect(response.body).toEqual({ + component: 'Users/List', + props: { + name: 'Supercharge', + users: [{ name: 'Supercharge' }], + nested: { name: 'Supercharge', - users: [{ name: 'Supercharge' }], - nested: { - name: 'Supercharge', - users: [{ name: 'Supercharge' }] - } - }, - version: '1.0.0', - url: '/some/path?foo=bar' - }) + users: [{ name: 'Supercharge' }] + } + }, + version: '1.0.0', + url: '/some/path?foo=bar' }) +}) - test('returns partial data', async () => { - const app = await createApp() - const server = new Server(app) +test('returns partial data', async () => { + const app = await createApp() + const server = createServer(app) - server.use(({ response }) => { - return response.inertia().render('Users/List', { - users: () => [{ name: 'Supercharge' }], - name: 'Supercharge', - 'keep-me': 'Supercharge' - }) + server.use(({ response }) => { + return response.inertia().render('Users/List', { + users: () => [{ name: 'Supercharge' }], + name: 'Supercharge', + 'keep-me': 'Supercharge' }) + }) - const response = await Supertest(server.callback()) - .get('/some/path?foo=bar') - .set('X-Inertia', 'true') - .set('X-Inertia-Version', '1.0.0') - .set('X-Inertia-Partial-Data', 'users,keep-me') - .set('X-Inertia-Partial-Component', 'Users/List') - .expect(200) - - expect(response.body.props.name).toBeUndefined() - expect(response.body).toEqual({ - component: 'Users/List', - props: { - users: [{ name: 'Supercharge' }], - 'keep-me': 'Supercharge' - }, - version: '1.0.0', - url: '/some/path?foo=bar' - }) + const response = await Supertest(server.callback()) + .get('/some/path?foo=bar') + .set('X-Inertia', 'true') + .set('X-Inertia-Version', '1.0.0') + .set('X-Inertia-Partial-Data', 'users,keep-me') + .set('X-Inertia-Partial-Component', 'Users/List') + .expect(200) + + expect(response.body.props.name).toBeUndefined() + expect(response.body).toEqual({ + component: 'Users/List', + props: { + users: [{ name: 'Supercharge' }], + 'keep-me': 'Supercharge' + }, + version: '1.0.0', + url: '/some/path?foo=bar' }) +}) - test('returns partial data and supports spaces between partial data keys', async () => { - const app = await createApp() - const server = new Server(app) +test('returns partial data and supports spaces between partial data keys', async () => { + const app = await createApp() + const server = createServer(app) - server.use(({ response }) => { - return response.inertia().render('Users/List', { - users: () => [{ name: 'Supercharge' }], - name: 'Supercharge', - 'keep-me': 'Supercharge' - }) + server.use(({ response }) => { + return response.inertia().render('Users/List', { + users: () => [{ name: 'Supercharge' }], + name: 'Supercharge', + 'keep-me': 'Supercharge' }) + }) - const response = await Supertest(server.callback()) - .get('/some/path?foo=bar') - .set('X-Inertia', 'true') - .set('X-Inertia-Version', '1.0.0') - .set('X-Inertia-Partial-Data', 'users, keep-me') - .set('X-Inertia-Partial-Component', 'Users/List') - .expect(200) - - expect(response.body.props.name).toBeUndefined() - expect(response.body).toEqual({ - component: 'Users/List', - props: { - users: [{ name: 'Supercharge' }], - 'keep-me': 'Supercharge' - }, - version: '1.0.0', - url: '/some/path?foo=bar' - }) + const response = await Supertest(server.callback()) + .get('/some/path?foo=bar') + .set('X-Inertia', 'true') + .set('X-Inertia-Version', '1.0.0') + .set('X-Inertia-Partial-Data', 'users, keep-me') + .set('X-Inertia-Partial-Component', 'Users/List') + .expect(200) + + expect(response.body.props.name).toBeUndefined() + expect(response.body).toEqual({ + component: 'Users/List', + props: { + users: [{ name: 'Supercharge' }], + 'keep-me': 'Supercharge' + }, + version: '1.0.0', + url: '/some/path?foo=bar' }) +}) - test('returns full data when rendering different component', async () => { - const app = await createApp() - const server = new Server(app) +test('returns full data when rendering different component', async () => { + const app = await createApp() + const server = createServer(app) - server.use(({ response }) => { - return response.inertia().render('Users/List', { - users: () => [{ name: 'Supercharge' }], - name: 'Supercharge' - }) - }) - - const response = await Supertest(server.callback()) - .get('/some/path?foo=bar') - .set('X-Inertia', 'true') - .set('X-Inertia-Version', '1.0.0') - .set('X-Inertia-Partial-Data', 'users') - .set('X-Inertia-Partial-Component', 'Other/Component') - .expect(200) - - expect(response.body).toEqual({ - component: 'Users/List', - props: { - name: 'Supercharge', - users: [{ name: 'Supercharge' }] - }, - version: '1.0.0', - url: '/some/path?foo=bar' + server.use(({ response }) => { + return response.inertia().render('Users/List', { + users: () => [{ name: 'Supercharge' }], + name: 'Supercharge' }) }) - test('shares data', async () => { - const app = await createApp() - const server = new Server(app) + const response = await Supertest(server.callback()) + .get('/some/path?foo=bar') + .set('X-Inertia', 'true') + .set('X-Inertia-Version', '1.0.0') + .set('X-Inertia-Partial-Data', 'users') + .set('X-Inertia-Partial-Component', 'Other/Component') + .expect(200) + + expect(response.body).toEqual({ + component: 'Users/List', + props: { + name: 'Supercharge', + users: [{ name: 'Supercharge' }] + }, + version: '1.0.0', + url: '/some/path?foo=bar' + }) +}) - server - .use(async ({ request, response }, next) => { - response.inertia().share({ - appName: 'Supercharge', - shared: async () => 'async-shared-data' - }) +test('shares data', async () => { + const app = await createApp() + const server = createServer(app) - request.inertia().share({ - user: () => { - return { name: 'Marcus' } - } - }) - - await next() - }) - .use(({ response }) => { - return response.inertia().render('Users/List', { - users: [{ name: 'Supercharge' }] - }) + server + .use(async ({ request, response }, next) => { + response.inertia().share({ + appName: 'Supercharge', + shared: async () => 'async-shared-data' }) - const response = await Supertest(server.callback()) - .get('/') - .set('X-Inertia', 'true') - .set('X-Inertia-Version', '1.0.0') - .expect(200) + request.inertia().share({ + user: () => { + return { name: 'Marcus' } + } + }) - expect(response.body).toEqual({ - component: 'Users/List', - props: { - appName: 'Supercharge', - user: { name: 'Marcus' }, - shared: 'async-shared-data', + await next() + }) + .use(({ response }) => { + return response.inertia().render('Users/List', { users: [{ name: 'Supercharge' }] - }, - version: '1.0.0', - url: '/' + }) }) - }) - - test('shared data: is filerable when requesting partial data', async () => { - const app = await createApp() - const server = new Server(app) - server - .use(async ({ request, response }, next) => { - response.inertia().share({ - appName: 'Supercharge', - shared: async () => 'async-shared-data' - }) + const response = await Supertest(server.callback()) + .get('/') + .set('X-Inertia', 'true') + .set('X-Inertia-Version', '1.0.0') + .expect(200) + + expect(response.body).toEqual({ + component: 'Users/List', + props: { + appName: 'Supercharge', + user: { name: 'Marcus' }, + shared: 'async-shared-data', + users: [{ name: 'Supercharge' }] + }, + version: '1.0.0', + url: '/' + }) +}) - request.inertia().share({ - user: () => { - return { name: 'Marcus' } - } - }) +test('shared data: is filerable when requesting partial data', async () => { + const app = await createApp() + const server = createServer(app) - await next() + server + .use(async ({ request, response }, next) => { + response.inertia().share({ + appName: 'Supercharge', + shared: async () => 'async-shared-data' }) - .use(({ response }) => { - return response.inertia().render('Users/List', { - users: [{ name: 'Supercharge' }] - }) + + request.inertia().share({ + user: () => { + return { name: 'Marcus' } + } }) - const response = await Supertest(server.callback()) - .get('/') - .set('X-Inertia', 'true') - .set('X-Inertia-Version', '1.0.0') - .set('X-Inertia-Partial-Data', 'users, appName') - .set('X-Inertia-Partial-Component', 'Users/List') - .expect(200) - - expect(response.body).toEqual({ - component: 'Users/List', - props: { - appName: 'Supercharge', + await next() + }) + .use(({ response }) => { + return response.inertia().render('Users/List', { users: [{ name: 'Supercharge' }] - }, - version: '1.0.0', - url: '/' + }) }) + + const response = await Supertest(server.callback()) + .get('/') + .set('X-Inertia', 'true') + .set('X-Inertia-Version', '1.0.0') + .set('X-Inertia-Partial-Data', 'users, appName') + .set('X-Inertia-Partial-Component', 'Users/List') + .expect(200) + + expect(response.body).toEqual({ + component: 'Users/List', + props: { + appName: 'Supercharge', + users: [{ name: 'Supercharge' }] + }, + version: '1.0.0', + url: '/' }) }) + +test.run() diff --git a/test/ssr.js b/test/ssr.js index 4b04bc0..5cebb7e 100644 --- a/test/ssr.js +++ b/test/ssr.js @@ -1,48 +1,49 @@ 'use strict' -const Path = require('node:path') -const { expect } = require('expect') -const Supertest = require('supertest') -const { test } = require('@japa/runner') -const { createSsrApp } = require('./helpers') -const { Server } = require('@supercharge/http') - -test.group('Inertia SSR', () => { - test('fails when not providing a path from where to resolve the render function', async () => { - expect(createSsrApp()) - .rejects.toThrow('Inertia SSR is enabled but the path to the file exporting the render function is missing.') - }) +import { test } from 'uvu' +import Path from 'node:path' +import { expect } from 'expect' +import Supertest from 'supertest' +import { fileURLToPath } from 'node:url' +import { createSsrApp, createServer } from './helpers/index.js' + +const __dirname = Path.dirname(fileURLToPath(import.meta.url)) + +test('fails when not providing a path from where to resolve the render function', async () => { + expect(createSsrApp()) + .rejects.toThrow('Inertia SSR is enabled but the path to the file exporting the render function is missing.') +}) - test('fails when provided path to render function does not exist', async () => { - expect(createSsrApp({ resolveRenderFunctionFrom: './does-not-exist.js' })) - .rejects.toThrow('Inertia SSR is enabled but we cannot resolve the file at "./does-not-exist.js".') - }) +test('fails when provided path to render function does not exist', async () => { + await expect(createSsrApp({ resolveRenderFunctionFrom: './does-not-exist.js' })) + .rejects.toThrow('Inertia SSR is enabled but we cannot resolve the file at "./does-not-exist.js".') +}) - test('fails when provided path to render function does not export a function', async () => { - const resolveRenderFunctionFrom = Path.resolve(__dirname, 'fixtures', 'ssr.string-export.js') +test('fails when provided path to render function does not export a function', async () => { + const resolveRenderFunctionFrom = Path.resolve(__dirname, 'fixtures', 'ssr.string-export.js') - expect(createSsrApp({ resolveRenderFunctionFrom })) - .rejects.toThrow(`Inertia SSR is enabled but no "render" function is exported in "${resolveRenderFunctionFrom}".`) - }) + await expect(createSsrApp({ resolveRenderFunctionFrom })) + .rejects.toThrow(`Inertia SSR is enabled but no "render" function is exported in "${resolveRenderFunctionFrom}".`) +}) - test('resolves render function from named "render" export', async () => { - const app = await createSsrApp({ - resolveRenderFunctionFrom: Path.resolve(__dirname, 'fixtures', 'ssr.named-export.js') - }) +test('resolves render function from named "render" export', async () => { + const app = await createSsrApp({ + resolveRenderFunctionFrom: Path.resolve(__dirname, 'fixtures', 'ssr.named-export.js') + }) - const server = new Server(app) + const server = createServer(app) - server.use(({ response }) => { - return response.inertia().render('Some/Page', { - name: 'Supercharge' - }) + server.use(({ response }) => { + return response.inertia().render('Some/Page', { + name: 'Supercharge' }) + }) - const response = await Supertest(server.callback()) - .get('/') - .expect(200) + const response = await Supertest(server.callback()) + .get('/') + .expect(200) - expect(response.text).toEqual(` + expect(response.text).toEqual(` @@ -56,26 +57,26 @@ test.group('Inertia SSR', () => { `) - }) +}) - test('resolves render function from default export', async () => { - const app = await createSsrApp({ - resolveRenderFunctionFrom: Path.resolve(__dirname, 'fixtures', 'ssr.default-export.js') - }) +test('resolves render function from default export', async () => { + const app = await createSsrApp({ + resolveRenderFunctionFrom: Path.resolve(__dirname, 'fixtures', 'ssr.default-export.js') + }) - const server = new Server(app) + const server = createServer(app) - server.use(({ response }) => { - return response.inertia().render('Some/Page', { - name: 'Supercharge' - }) + server.use(({ response }) => { + return response.inertia().render('Some/Page', { + name: 'Supercharge' }) + }) - const response = await Supertest(server.callback()) - .get('/') - .expect(200) + const response = await Supertest(server.callback()) + .get('/') + .expect(200) - expect(response.text).toEqual(` + expect(response.text).toEqual(` @@ -89,26 +90,59 @@ test.group('Inertia SSR', () => { `) +}) + +test('resolves render function from ESM default export', async () => { + const app = await createSsrApp({ + resolveRenderFunctionFrom: Path.resolve(__dirname, 'fixtures', 'ssr.default-export.js') }) - test('resolves render function from module exports (CommonJS)', async () => { - const app = await createSsrApp({ - resolveRenderFunctionFrom: Path.resolve(__dirname, 'fixtures', 'ssr.module-export.js') + const server = createServer(app) + + server.use(({ response }) => { + return response.inertia().render('Some/Page', { + name: 'Supercharge' }) + }) - const server = new Server(app) + const response = await Supertest(server.callback()) + .get('/') + .expect(200) - server.use(({ response }) => { - return response.inertia().render('Some/Page', { - name: 'Supercharge' - }) + expect(response.text).toEqual(` + + + + + Supercharge Inertia + + Supercharge Inertia SSR + + +

Hello Test SSR: Supercharge

+ + +`) +}) + +test('resolves render function from CommonJS module.exports', async () => { + const app = await createSsrApp({ + resolveRenderFunctionFrom: Path.resolve(__dirname, 'fixtures', 'ssr.default-export-commonjs.cjs') + }) + + const server = createServer(app) + + server.use(({ response }) => { + return response.inertia().render('Some/Page', { + name: 'Supercharge' }) + }) - const response = await Supertest(server.callback()) - .get('/') - .expect(200) + const response = await Supertest(server.callback()) + .get('/') + .expect(200) - expect(response.text).toEqual(` + expect(response.text).toEqual(` @@ -122,5 +156,6 @@ test.group('Inertia SSR', () => { `) - }) }) + +test.run() diff --git a/test/versioning.js b/test/versioning.js index 619c4af..dc62ae5 100644 --- a/test/versioning.js +++ b/test/versioning.js @@ -1,114 +1,116 @@ 'use strict' -const Path = require('path') -const { expect } = require('expect') -const Supertest = require('supertest') -const { Inertia } = require('../dist') -const { test } = require('@japa/runner') -const { createApp } = require('./helpers') -const { Server } = require('@supercharge/http') - -test.group('Inertia Versioning', () => { - test('returns 409 for different request and server asset versions', async () => { - const app = await createApp() - const server = new Server(app) - - server.use(({ response }) => { - return response.inertia().render('Users/List') - }) +import { test } from 'uvu' +import Path from 'node:path' +import { expect } from 'expect' +import Supertest from 'supertest' +import { fileURLToPath } from 'node:url' +import { Inertia } from '../dist/index.js' +import { createApp, createServer } from './helpers/index.js' + +const __dirname = Path.dirname(fileURLToPath(import.meta.url)) + +test('returns 409 for different request and server asset versions', async () => { + const app = await createApp() + const server = createServer(app) + + server.use(({ response }) => { + return response.inertia().render('Users/List') + }) - const response = await Supertest(server.callback()) - .get('/some/path?foo=bar') - .set('X-Inertia', 'true') - .set('X-Inertia-Version', 'old') - .expect(409) + const response = await Supertest(server.callback()) + .get('/some/path?foo=bar') + .set('X-Inertia', 'true') + .set('X-Inertia-Version', 'old') + .expect(409) - expect(response.headers).toMatchObject({ - 'x-inertia-location': '/some/path?foo=bar' - }) + expect(response.headers).toMatchObject({ + 'x-inertia-location': '/some/path?foo=bar' }) +}) - test('supports versioning using a function', async () => { - const version = '1.0.0' - - const app = await createApp() - app.config().set('inertia.version', () => version) +test('supports versioning using a function', async () => { + const version = '1.0.0' - const server = new Server(app).use(({ response }) => { - return response.inertia().render('Users/List') - }) + const app = await createApp() + app.config().set('inertia.version', () => version) - await Supertest(server.callback()) - .get('/') - .set('X-Inertia', 'true') - .set('X-Inertia-Version', version) - .expect(200) + const server = createServer(app).use(({ response }) => { + return response.inertia().render('Users/List') }) - test('supports versioning using an async function', async () => { - const version = '1.0.0' + await Supertest(server.callback()) + .get('/') + .set('X-Inertia', 'true') + .set('X-Inertia-Version', version) + .expect(200) +}) - const app = await createApp() - app.config().set('inertia.version', async () => { - return await new Promise(resolve => { - setTimeout(() => resolve(version), 10) - }) - }) +test('supports versioning using an async function', async () => { + const version = '1.0.0' - const server = new Server(app).use(({ response }) => { - return response.inertia().render('Users/List') + const app = await createApp() + app.config().set('inertia.version', async () => { + return await new Promise(resolve => { + setTimeout(() => resolve(version), 10) }) + }) - await Supertest(server.callback()) - .get('/') - .set('X-Inertia', 'true') - .set('X-Inertia-Version', version) - .expect(200) + const server = createServer(app).use(({ response }) => { + return response.inertia().render('Users/List') }) - test('conflicts when using a different version from manifest file', async () => { - const version = '1.0.0' + await Supertest(server.callback()) + .get('/') + .set('X-Inertia', 'true') + .set('X-Inertia-Version', version) + .expect(200) +}) - const app = await createApp() - app.config().set('inertia.version', async () => { - return Inertia.manifestFile( - Path.resolve(__dirname, 'fixtures/manifest.json') - ) - }) +test('conflicts when using a different version from manifest file', async () => { + const version = '1.0.0' - const server = new Server(app).use(({ response }) => { - return response.inertia().render('Users/List') - }) + const app = await createApp() + app.config().set('inertia.version', async () => { + return Inertia.manifestFile( + Path.resolve(__dirname, 'fixtures/manifest.json') + ) + }) - await Supertest(server.callback()) - .get('/') - .set('X-Inertia', 'true') - .set('X-Inertia-Version', version) - .expect(409) + const server = createServer(app).use(({ response }) => { + return response.inertia().render('Users/List') }) - test('fails when using a non-existing manifest file', async () => { - const version = '1.0.0' - const manifestFilePath = Path.resolve(__dirname, 'fixtures/non-existent-manifest.json') + await Supertest(server.callback()) + .get('/') + .set('X-Inertia', 'true') + .set('X-Inertia-Version', version) + .expect(409) +}) - const app = await createApp() - app.config().set('inertia.version', async () => { - return Inertia.manifestFile(manifestFilePath) - }) +test('fails when using a non-existing manifest file', async () => { + const version = '1.0.0' + const manifestFilePath = Path.resolve(__dirname, 'fixtures/non-existent-manifest.json') - const server = new Server(app).use(({ response }) => { - return response.inertia().render('Users/List') - }) + const app = await createApp() + app.config().set('inertia.version', async () => { + return Inertia.manifestFile(manifestFilePath) + }) - const response = await Supertest(server.callback()) - .get('/') - .set('X-Inertia', 'true') - .set('X-Inertia-Version', version) - .set('accept', 'application/json') - .expect(500) + const server = createServer(app).use(({ response }) => { + return response.inertia().render('Users/List') + }) - expect(response.body).toMatchObject({ - message: `Manifest file "${manifestFilePath}" does not exist.` - }) + const response = await Supertest(server.callback()) + .get('/') + .set('X-Inertia', 'true') + .set('X-Inertia-Version', version) + .set('accept', 'application/json') + .expect(500) + + expect(response.body).toMatchObject({ + message: `Manifest file "${manifestFilePath}" does not exist.` }) }) + +test.run() diff --git a/tsconfig.json b/tsconfig.json index 8d5665a..7117a95 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,11 @@ { "extends": "@supercharge/tsconfig", "compilerOptions": { - "experimentalDecorators": true, - "emitDecoratorMetadata": true, "rootDir": "./src", - "outDir": "./dist" + "outDir": "./dist", + "types": [ + "@supercharge/view" + ] }, "include": [ "./src" From 73f9cf897625b8196d84a9c3833e79a51fe9f557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20P=C3=B6hls?= Date: Sat, 18 Nov 2023 05:20:02 +0100 Subject: [PATCH 03/10] run tests with node 20 --- .github/workflows/run-tests.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 4b793f7..9e42beb 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -3,7 +3,10 @@ name: Run tests on: [push, pull_request] concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + # the group name is composed of two elements: + # 1. this workflow name "run-tests" + # 2. the branch name retrieved via the "github.ref" variable + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: @@ -12,16 +15,16 @@ jobs: strategy: matrix: - node-version: [16.x, 18.x, 19.x] + node-version: [20.x, latest] name: Node ${{ matrix.node-version }} steps: - name: Git checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} @@ -29,6 +32,6 @@ jobs: run: npm install - name: Run tests - run: npm run test:full + run: npm test env: CI: true From 71d0ea844605f367a566341ea74d65ddd71bd80d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20P=C3=B6hls?= Date: Sat, 18 Nov 2023 05:21:10 +0100 Subject: [PATCH 04/10] run tests on push --- .github/workflows/run-tests.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 9e42beb..248a5a1 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,6 +1,9 @@ name: Run tests -on: [push, pull_request] +on: + push: + paths-ignore: + - 'README.md' concurrency: # the group name is composed of two elements: From 5cca77893d46d45b5e0b925fca42367e9df6544c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20P=C3=B6hls?= Date: Sun, 3 Dec 2023 05:48:42 +0100 Subject: [PATCH 05/10] bump deps --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b46c422..177133f 100644 --- a/package.json +++ b/package.json @@ -21,10 +21,10 @@ "@supercharge/view": "~3.20.4", "@types/dedent": "~0.7.2", "c8": "~8.0.1", - "eslint": "~8.54.0", + "eslint": "~8.55.0", "expect": "~29.7.0", "supertest": "~6.3.3", - "typescript": "~5.2.2", + "typescript": "~5.3.2", "uvu": "~0.5.6" }, "engines": { From 8c7a1a6d214d0c2f63c8a7dce7c846f55d0fed6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20P=C3=B6hls?= Date: Sun, 3 Dec 2023 06:14:53 +0100 Subject: [PATCH 06/10] rename InertiaOptions to InertiaConfig --- src/contracts/config-contract.ts | 16 ++++++++-------- src/contracts/page-contract.ts | 4 ++-- src/inertia-response.ts | 6 +++--- src/inertia-service-provider.ts | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/contracts/config-contract.ts b/src/contracts/config-contract.ts index 1cfdbd7..92e2291 100644 --- a/src/contracts/config-contract.ts +++ b/src/contracts/config-contract.ts @@ -2,10 +2,17 @@ import { Application } from '@supercharge/contracts' +/** + * Defines the types for the Inertia version configuration. + */ +export type InertiaVersion = InertiaVersionValue | InertiaVersionFunction +export type InertiaVersionValue = string | number | undefined +export type InertiaVersionFunction = (app: Application) => Promise | InertiaVersionValue + /** * Defines the Inertia configuration. */ -export interface InertiaOptions { +export interface InertiaConfig { /** * Defines the root template view that will be loaded on the first page visit. * This root view template should be provided in the resources directory of @@ -47,10 +54,3 @@ export interface InertiaOptions { resolveRenderFunctionFrom?: string } } - -/** - * Defines the types for the Inertia version configuration. - */ -export type InertiaVersion = InertiaVersionValue | InertiaVersionFunction -export type InertiaVersionValue = string | number | undefined -export type InertiaVersionFunction = (app: Application) => Promise | InertiaVersionValue diff --git a/src/contracts/page-contract.ts b/src/contracts/page-contract.ts index e9e9ed8..ffb98b1 100644 --- a/src/contracts/page-contract.ts +++ b/src/contracts/page-contract.ts @@ -1,6 +1,6 @@ 'use strict' -import { InertiaOptions } from './config-contract.js' +import { InertiaConfig } from './config-contract.js' /** * Defines the Inertia page contract. @@ -9,5 +9,5 @@ export interface PageContract { component: string props: Record url?: string - version?: InertiaOptions['version'] + version?: InertiaConfig['version'] } diff --git a/src/inertia-response.ts b/src/inertia-response.ts index 8e791c3..ec3548d 100644 --- a/src/inertia-response.ts +++ b/src/inertia-response.ts @@ -5,7 +5,7 @@ import { SharesData } from './shares-data.js' import { isAsyncFunction } from '@supercharge/goodies' import { resolveRenderFunctionFrom } from './utils.js' import { Application, HttpContext, HttpResponse } from '@supercharge/contracts' -import { InertiaOptions, InertiaVersionValue, PageContract } from './contracts/index.js' +import { InertiaConfig, InertiaVersionValue, PageContract } from './contracts/index.js' export class InertiaResponse extends SharesData { /** @@ -21,12 +21,12 @@ export class InertiaResponse extends SharesData { /** * Stores the reference to the Inertia configuration. */ - private readonly config: InertiaOptions + private readonly config: InertiaConfig /** * Create a new instance. */ - constructor (app: Application, { request, response }: HttpContext, config: InertiaOptions) { + constructor (app: Application, { request, response }: HttpContext, config: InertiaConfig) { super(request) this.app = app diff --git a/src/inertia-service-provider.ts b/src/inertia-service-provider.ts index 030d18e..bc77f40 100644 --- a/src/inertia-service-provider.ts +++ b/src/inertia-service-provider.ts @@ -2,7 +2,7 @@ import Fs from 'node:fs' import dedent from 'dedent' -import { InertiaOptions } from './contracts/index.js' +import { InertiaConfig } from './contracts/index.js' import { InertiaRequest } from './inertia-request.js' import { ServiceProvider } from '@supercharge/support' import { resolveRenderFunctionFrom } from './utils.js' @@ -107,7 +107,7 @@ export class InertiaServiceProvider extends ServiceProvider { */ protected async registerInertiaResponseMacros (): Promise { const app = this.app().make('app') - const inertiaConfig = app.config().get('inertia', { + const inertiaConfig = app.config().get('inertia', { view: 'app', ssr: { enabled: false } }) @@ -126,7 +126,7 @@ export class InertiaServiceProvider extends ServiceProvider { /** * Ensure the configured SSR render function is available. */ - protected async ensureSsrRenderFunction (inertiaConfig: InertiaOptions): Promise { + protected async ensureSsrRenderFunction (inertiaConfig: InertiaConfig): Promise { const renderFunctionPath = inertiaConfig.ssr?.resolveRenderFunctionFrom if (!renderFunctionPath) { From 1b1acb1ac563c1f4e3e7f7c0962df85f494d5adf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20P=C3=B6hls?= Date: Sun, 3 Dec 2023 06:15:12 +0100 Subject: [PATCH 07/10] bump deps --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 177133f..f570c06 100644 --- a/package.json +++ b/package.json @@ -7,18 +7,18 @@ "url": "https://github.com/supercharge/inertia/issues" }, "dependencies": { - "@supercharge/contracts": "^3.20.4", + "@supercharge/contracts": "~4.0.0-alpha.1", "@supercharge/fs": "~3.4.0", "@supercharge/goodies": "~2.0.0", - "@supercharge/support": "^3.20.4", + "@supercharge/support": "~4.0.0-alpha.1", "dedent": "~1.5.1" }, "devDependencies": { - "@supercharge/core": "~3.20.4", - "@supercharge/eslint-config-typescript": "~4.0.0", - "@supercharge/http": "~3.20.4", + "@supercharge/core": "~4.0.0-alpha.1", + "@supercharge/eslint-config-typescript": "~4.0.1", + "@supercharge/http": "~4.0.0-alpha.1", "@supercharge/tsconfig": "~7.0.0", - "@supercharge/view": "~3.20.4", + "@supercharge/view": "~4.0.0-alpha.1", "@types/dedent": "~0.7.2", "c8": "~8.0.1", "eslint": "~8.55.0", From 76c4bb935e41840e6c5cf3b9479c21a4b4899d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20P=C3=B6hls?= Date: Sun, 3 Dec 2023 06:15:28 +0100 Subject: [PATCH 08/10] add contract for shared inertia data --- src/shares-data.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/shares-data.ts b/src/shares-data.ts index 3ed9408..09f4688 100644 --- a/src/shares-data.ts +++ b/src/shares-data.ts @@ -3,6 +3,14 @@ import { tap } from '@supercharge/goodies' import { HttpRequest } from '@supercharge/contracts' +type InertiaSharedDataType = Record + +declare module '@supercharge/contracts' { + export interface HttpStateData { + 'inertia': InertiaSharedDataType + } +} + export class SharesData { /** * Stores the reference to the HTTP request. @@ -19,7 +27,7 @@ export class SharesData { /** * Assign shared data that will be merged into the response props. */ - share (data: Record): this { + share (data: InertiaSharedDataType): this { return tap(this, () => { this.request.state().merge({ inertia: data }) }) @@ -28,7 +36,7 @@ export class SharesData { /** * Returns the shared Inertia data. */ - sharedData (): Record { - return this.request.state().get('inertia', {})! + sharedData (): InertiaSharedDataType { + return this.request.state().get('inertia', {}) } } From 324d37ce0dd1118b1cdb5a60869a2a5ea289be88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20P=C3=B6hls?= Date: Sun, 3 Dec 2023 06:15:37 +0100 Subject: [PATCH 09/10] update sample config to use InertiaConfig --- config/inertia.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/inertia.ts b/config/inertia.ts index c7a56b9..d420cc3 100644 --- a/config/inertia.ts +++ b/config/inertia.ts @@ -1,9 +1,9 @@ -'use strict' +import { App } from '@supercharge/facades' import { Application } from '@supercharge/contracts' -import { Inertia, InertiaOptions } from '@supercharge/inertia' +import { Inertia, InertiaConfig } from '@supercharge/inertia' -const inertiaConfig: InertiaOptions = { +const inertiaConfig: InertiaConfig = { /** * Defines the root template view that will be loaded on the first page visit. * This root view template should be provided in the resources directory of @@ -46,7 +46,7 @@ const inertiaConfig: InertiaOptions = { * render function is required for Inertia to create the rendered HTML. * Your file may use a default export or a named "render" export. */ - resolveRenderFunctionFrom: 'bootstrap/ssr.js' + resolveRenderFunctionFrom: App.resolveFromBasePath('bootstrap/ssr/ssr.js') } } From a52497e057ef8b66392261ab2aca7626c547743e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20P=C3=B6hls?= Date: Sun, 3 Dec 2023 06:15:52 +0100 Subject: [PATCH 10/10] add breaking InertiaConfig change --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95be299..860dccd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ ### Breaking Changes - require Node.js v20 - this package migrated to ESM +- renamed `InertiaOptions` to `InertiaConfig` + - renaming the Inertia config file to `InertiaConfig` aligns with the Supercharge config naming where all exported config contracts have the `Config` suffix. And the `config/inertia.ts` file location aligns with the `InertiaConfig` naming, too. ## [1.2.2](https://github.com/supercharge/inertia/compare/v1.2.1...v1.2.2) - 2022-09-26