From 653466bde5459ed0c62b4ebd9db0763f04d36ac8 Mon Sep 17 00:00:00 2001 From: Bharat Kashyap Date: Wed, 3 May 2023 22:27:25 +0530 Subject: [PATCH 01/23] test create-toolpad-app using health-check --- jest.config.ts | 1 + packages/create-toolpad-app/jest.config.ts | 6 ++ packages/create-toolpad-app/src/index.spec.ts | 74 +++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 packages/create-toolpad-app/jest.config.ts create mode 100644 packages/create-toolpad-app/src/index.spec.ts diff --git a/jest.config.ts b/jest.config.ts index c5d2817d204..8978261c01f 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -4,6 +4,7 @@ const config: Config.InitialOptions = { projects: [ '/packages/toolpad-app/jest.config.ts', '/packages/toolpad-utils/jest.config.ts', + '/packages/create-toolpad-app/jest.config.ts', ], setupFilesAfterEnv: ['/test/utils/jest-setup.ts'], }; diff --git a/packages/create-toolpad-app/jest.config.ts b/packages/create-toolpad-app/jest.config.ts new file mode 100644 index 00000000000..6e3cc2ee8ea --- /dev/null +++ b/packages/create-toolpad-app/jest.config.ts @@ -0,0 +1,6 @@ +import { Config } from 'jest'; + +export default { + preset: 'ts-jest', + transform: {}, +} satisfies Config; diff --git a/packages/create-toolpad-app/src/index.spec.ts b/packages/create-toolpad-app/src/index.spec.ts new file mode 100644 index 00000000000..0aa5ae77623 --- /dev/null +++ b/packages/create-toolpad-app/src/index.spec.ts @@ -0,0 +1,74 @@ +const { spawn, exec } = require('child_process'); +const fs = require('fs'); +const { rimraf } = require('rimraf'); +const http = require('http'); + +interface ServerResponse { + statusCode: number; +} + +describe('create-toolpad-app', () => { + let toolpadProcess: ReturnType; + let cp: ReturnType; + let toolpadAppAddress = ''; + const toolpadAppAddressRegex = /http:\/\/localhost:(\d+)/; + const directoryPath = './my-test-app'; + + test('create-toolpad-app launches the app', (done) => { + const scriptPath = './packages/create-toolpad-app/dist/index.js'; + cp = spawn('node', [scriptPath, directoryPath]); + const { stderr } = cp; + + stderr.on('data', (data: Buffer) => { + console.error(data.toString()); + }); + + cp.on('error', (err: Error) => { + console.error(`Failed to start create-toolpad-app: ${err}`); + }); + + cp.on('exit', (code: number | null, signal: string | null) => { + if (code !== 0) { + console.error(`create-toolpad-app exited with code ${code}`); + } else if (signal !== null) { + console.error(`create-toolpad-app exited due to signal ${signal}`); + } else { + toolpadProcess = exec('yarn dev', { + cwd: directoryPath, + }); + + toolpadProcess.on('error', (err: Error) => { + console.error(`Failed to start toolpad dev: ${err}`); + }); + + toolpadProcess.stdout.on('data', (data: Buffer) => { + // Match the URL in a line which says "ready on ": + const matches = data.toString().match(toolpadAppAddressRegex); + if (matches) { + toolpadAppAddress = matches[0]; + done(); + } + }); + } + }); + }, 60000); + + it('should return a 200 status code when hitting the health-check route', (done) => { + http.get(`${toolpadAppAddress}/health-check`, (res: ServerResponse) => { + expect(res.statusCode).toBe(200); + done(); + }); + }); + + afterAll(() => { + if (toolpadProcess) { + toolpadProcess.kill(); + } + + toolpadProcess.on('exit', () => { + if (fs.existsSync(directoryPath)) { + rimraf(directoryPath); + } + }); + }); +}); From cecde2ee941c6c201fff4ac730ff651f172a81ee Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Thu, 4 May 2023 10:15:10 +0200 Subject: [PATCH 02/23] Propose improved tests --- packages/create-toolpad-app/jest.config.ts | 3 +- packages/create-toolpad-app/src/index.spec.ts | 2 +- .../create-toolpad-app/tests/index.spec.ts | 49 +++++++++++++++++++ .../create-toolpad-app/tests/package.json | 3 ++ .../create-toolpad-app/tests/tsconfig.json | 6 +++ 5 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 packages/create-toolpad-app/tests/index.spec.ts create mode 100644 packages/create-toolpad-app/tests/package.json create mode 100644 packages/create-toolpad-app/tests/tsconfig.json diff --git a/packages/create-toolpad-app/jest.config.ts b/packages/create-toolpad-app/jest.config.ts index 6e3cc2ee8ea..2669edf4363 100644 --- a/packages/create-toolpad-app/jest.config.ts +++ b/packages/create-toolpad-app/jest.config.ts @@ -1,6 +1,5 @@ import { Config } from 'jest'; export default { - preset: 'ts-jest', - transform: {}, + preset: 'ts-jest/presets/default-esm', } satisfies Config; diff --git a/packages/create-toolpad-app/src/index.spec.ts b/packages/create-toolpad-app/src/index.spec.ts index 0aa5ae77623..915228db142 100644 --- a/packages/create-toolpad-app/src/index.spec.ts +++ b/packages/create-toolpad-app/src/index.spec.ts @@ -7,7 +7,7 @@ interface ServerResponse { statusCode: number; } -describe('create-toolpad-app', () => { +describe.skip('create-toolpad-app', () => { let toolpadProcess: ReturnType; let cp: ReturnType; let toolpadAppAddress = ''; diff --git a/packages/create-toolpad-app/tests/index.spec.ts b/packages/create-toolpad-app/tests/index.spec.ts new file mode 100644 index 00000000000..d8291c2c2ce --- /dev/null +++ b/packages/create-toolpad-app/tests/index.spec.ts @@ -0,0 +1,49 @@ +import * as fs from 'fs/promises'; +import { jest } from '@jest/globals'; +import * as path from 'path'; +import * as url from 'url'; +import { execa, ExecaChildProcess } from 'execa'; + +jest.setTimeout(60000); + +const currentDirectory = url.fileURLToPath(new URL('.', import.meta.url)); + +let testDir: string | undefined; +let cp: ExecaChildProcess | undefined; + +it('start', async () => { + testDir = await fs.mkdtemp(path.resolve(currentDirectory, './test-app-')); + cp = execa('create-toolpad-app', [path.basename(testDir)], { + cwd: currentDirectory, + }); + const result = await cp; + expect(result.stdout).toMatch('Run the following to get started'); + const packageJsonContent = await fs.readFile(path.resolve(testDir, './package.json'), { + encoding: 'utf-8', + }); + const packageJson = JSON.parse(packageJsonContent); + expect(packageJson).toEqual( + expect.objectContaining({ + dependencies: expect.objectContaining({ + '@mui/toolpad': expect.any(String), + }), + scripts: expect.objectContaining({ + build: 'toolpad build', + dev: 'toolpad dev', + start: 'toolpad start', + }), + }), + ); +}); + +afterEach(async () => { + if (testDir) { + await fs.rm(testDir, { recursive: true, force: true }); + } +}); + +afterEach(async () => { + if (cp) { + cp.kill(); + } +}); diff --git a/packages/create-toolpad-app/tests/package.json b/packages/create-toolpad-app/tests/package.json new file mode 100644 index 00000000000..3dbc1ca591c --- /dev/null +++ b/packages/create-toolpad-app/tests/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/packages/create-toolpad-app/tests/tsconfig.json b/packages/create-toolpad-app/tests/tsconfig.json new file mode 100644 index 00000000000..0421216283c --- /dev/null +++ b/packages/create-toolpad-app/tests/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "ESNext" + } +} From 0a3dae32e816db03c4e4d9f563894130a3d0fc63 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Thu, 4 May 2023 10:39:02 +0200 Subject: [PATCH 03/23] forgiot this --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 31ab9e7d607..bc8e05a8594 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "release:changelog": "dotenv -- node ./scripts/releaseChangelog.mjs --repo mui-toolpad", "test:build": "lerna run build --scope @mui/toolpad-core --scope @mui/toolpad-components --stream", "test:integration": "rm -rf ./node_modules/.vite && playwright test --config ./test/playwright.config.ts", - "test": "jest", + "test": "NODE_OPTIONS=--experimental-vm-modules jest", "check-types": "lerna run check-types", "toolpad": "toolpad" }, From 7bc84154d20877d22bb58613e8587e6ebd4e8bb4 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Thu, 4 May 2023 12:12:12 +0200 Subject: [PATCH 04/23] Try running tests separately --- jest.config.ts | 12 ------------ package.json | 2 +- packages/create-toolpad-app/package.json | 3 ++- packages/toolpad-app/package.json | 3 ++- packages/toolpad-components/package.json | 1 - packages/toolpad-core/package.json | 1 - packages/toolpad-utils/jest.config.ts | 2 +- packages/toolpad-utils/package.json | 4 ++-- packages/toolpad-utils/tsconfig.json | 4 ++-- test/utils/jest-setup.ts | 1 - 10 files changed, 10 insertions(+), 23 deletions(-) delete mode 100644 jest.config.ts delete mode 100644 test/utils/jest-setup.ts diff --git a/jest.config.ts b/jest.config.ts deleted file mode 100644 index 8978261c01f..00000000000 --- a/jest.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { Config } from '@jest/types'; - -const config: Config.InitialOptions = { - projects: [ - '/packages/toolpad-app/jest.config.ts', - '/packages/toolpad-utils/jest.config.ts', - '/packages/create-toolpad-app/jest.config.ts', - ], - setupFilesAfterEnv: ['/test/utils/jest-setup.ts'], -}; - -export default config; diff --git a/package.json b/package.json index bc8e05a8594..da8f5832207 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "release:changelog": "dotenv -- node ./scripts/releaseChangelog.mjs --repo mui-toolpad", "test:build": "lerna run build --scope @mui/toolpad-core --scope @mui/toolpad-components --stream", "test:integration": "rm -rf ./node_modules/.vite && playwright test --config ./test/playwright.config.ts", - "test": "NODE_OPTIONS=--experimental-vm-modules jest", + "test": "lerna run test", "check-types": "lerna run check-types", "toolpad": "toolpad" }, diff --git a/packages/create-toolpad-app/package.json b/packages/create-toolpad-app/package.json index 95db096a18f..0c469010015 100644 --- a/packages/create-toolpad-app/package.json +++ b/packages/create-toolpad-app/package.json @@ -24,7 +24,8 @@ "prebuild": "rimraf ./dist", "build": "tsup", "dev": "tsup --watch", - "check-types": "tsc --noEmit" + "check-types": "tsc --noEmit", + "test": "NODE_OPTIONS=--experimental-vm-modules jest" }, "engines": { "node": ">=16.17" diff --git a/packages/toolpad-app/package.json b/packages/toolpad-app/package.json index e43b1eaf636..f90085c9724 100644 --- a/packages/toolpad-app/package.json +++ b/packages/toolpad-app/package.json @@ -15,7 +15,8 @@ "dev:cli": "tsup --watch", "dev:typings": "yarn build:typings", "waitForApp": "ts-node --esm ./scripts/waitForApp.mts", - "check-types": "tsc --noEmit" + "check-types": "tsc --noEmit", + "test": "jest" }, "files": [ "public", diff --git a/packages/toolpad-components/package.json b/packages/toolpad-components/package.json index ecdc03949d5..62d79d4dc3c 100644 --- a/packages/toolpad-components/package.json +++ b/packages/toolpad-components/package.json @@ -33,7 +33,6 @@ "prebuild": "rimraf dist", "build": "tsup", "dev": "tsup --watch", - "test": "echo \"Error: run tests from root\" && exit 1", "check-types": "tsc" }, "bugs": { diff --git a/packages/toolpad-core/package.json b/packages/toolpad-core/package.json index 1863ce9e3e2..6800d5adfc9 100644 --- a/packages/toolpad-core/package.json +++ b/packages/toolpad-core/package.json @@ -35,7 +35,6 @@ "prebuild": "rimraf dist", "build": "tsup", "dev": "tsup --watch", - "test": "echo \"Error: run tests from root\" && exit 1", "check-types": "yarn build" }, "bugs": { diff --git a/packages/toolpad-utils/jest.config.ts b/packages/toolpad-utils/jest.config.ts index ff60f10bd48..2669edf4363 100644 --- a/packages/toolpad-utils/jest.config.ts +++ b/packages/toolpad-utils/jest.config.ts @@ -1,5 +1,5 @@ import { Config } from 'jest'; export default { - preset: 'ts-jest', + preset: 'ts-jest/presets/default-esm', } satisfies Config; diff --git a/packages/toolpad-utils/package.json b/packages/toolpad-utils/package.json index ab02bb58a14..b70b4ea9387 100644 --- a/packages/toolpad-utils/package.json +++ b/packages/toolpad-utils/package.json @@ -35,8 +35,8 @@ "prebuild": "rimraf dist", "build": "tsup", "dev": "tsup --watch", - "test": "echo \"Error: run tests from root\" && exit 1", - "check-types": "yarn build" + "check-types": "yarn build", + "test": "jest" }, "bugs": { "url": "https://github.com/mui/mui-toolpad/issues" diff --git a/packages/toolpad-utils/tsconfig.json b/packages/toolpad-utils/tsconfig.json index c27c70b76e2..d0850b14e85 100644 --- a/packages/toolpad-utils/tsconfig.json +++ b/packages/toolpad-utils/tsconfig.json @@ -1,8 +1,8 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "module": "esnext", - "moduleResolution": "bundler", + "module": "ESNext", + "moduleResolution": "nodenext", "alwaysStrict": true, "rootDir": "src", "outDir": "dist", diff --git a/test/utils/jest-setup.ts b/test/utils/jest-setup.ts deleted file mode 100644 index 7b0828bfa80..00000000000 --- a/test/utils/jest-setup.ts +++ /dev/null @@ -1 +0,0 @@ -import '@testing-library/jest-dom'; From 116b6dc9002847e75898b6952391deef5888e585 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Thu, 4 May 2023 13:42:22 +0200 Subject: [PATCH 05/23] path fix --- packages/create-toolpad-app/tests/index.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/create-toolpad-app/tests/index.spec.ts b/packages/create-toolpad-app/tests/index.spec.ts index d8291c2c2ce..cd38bf14494 100644 --- a/packages/create-toolpad-app/tests/index.spec.ts +++ b/packages/create-toolpad-app/tests/index.spec.ts @@ -8,12 +8,14 @@ jest.setTimeout(60000); const currentDirectory = url.fileURLToPath(new URL('.', import.meta.url)); +const cliPath = path.resolve(currentDirectory, '../dist/index.js'); + let testDir: string | undefined; let cp: ExecaChildProcess | undefined; it('start', async () => { testDir = await fs.mkdtemp(path.resolve(currentDirectory, './test-app-')); - cp = execa('create-toolpad-app', [path.basename(testDir)], { + cp = execa(cliPath, [path.basename(testDir)], { cwd: currentDirectory, }); const result = await cp; From 304885f072cd344fc45efea1cd59108f915fa99b Mon Sep 17 00:00:00 2001 From: Bharat Kashyap Date: Fri, 5 May 2023 12:31:39 +0530 Subject: [PATCH 06/23] Rename test, remove the old one --- packages/create-toolpad-app/src/index.spec.ts | 74 ------------------- .../create-toolpad-app/tests/index.spec.ts | 2 +- 2 files changed, 1 insertion(+), 75 deletions(-) delete mode 100644 packages/create-toolpad-app/src/index.spec.ts diff --git a/packages/create-toolpad-app/src/index.spec.ts b/packages/create-toolpad-app/src/index.spec.ts deleted file mode 100644 index 915228db142..00000000000 --- a/packages/create-toolpad-app/src/index.spec.ts +++ /dev/null @@ -1,74 +0,0 @@ -const { spawn, exec } = require('child_process'); -const fs = require('fs'); -const { rimraf } = require('rimraf'); -const http = require('http'); - -interface ServerResponse { - statusCode: number; -} - -describe.skip('create-toolpad-app', () => { - let toolpadProcess: ReturnType; - let cp: ReturnType; - let toolpadAppAddress = ''; - const toolpadAppAddressRegex = /http:\/\/localhost:(\d+)/; - const directoryPath = './my-test-app'; - - test('create-toolpad-app launches the app', (done) => { - const scriptPath = './packages/create-toolpad-app/dist/index.js'; - cp = spawn('node', [scriptPath, directoryPath]); - const { stderr } = cp; - - stderr.on('data', (data: Buffer) => { - console.error(data.toString()); - }); - - cp.on('error', (err: Error) => { - console.error(`Failed to start create-toolpad-app: ${err}`); - }); - - cp.on('exit', (code: number | null, signal: string | null) => { - if (code !== 0) { - console.error(`create-toolpad-app exited with code ${code}`); - } else if (signal !== null) { - console.error(`create-toolpad-app exited due to signal ${signal}`); - } else { - toolpadProcess = exec('yarn dev', { - cwd: directoryPath, - }); - - toolpadProcess.on('error', (err: Error) => { - console.error(`Failed to start toolpad dev: ${err}`); - }); - - toolpadProcess.stdout.on('data', (data: Buffer) => { - // Match the URL in a line which says "ready on ": - const matches = data.toString().match(toolpadAppAddressRegex); - if (matches) { - toolpadAppAddress = matches[0]; - done(); - } - }); - } - }); - }, 60000); - - it('should return a 200 status code when hitting the health-check route', (done) => { - http.get(`${toolpadAppAddress}/health-check`, (res: ServerResponse) => { - expect(res.statusCode).toBe(200); - done(); - }); - }); - - afterAll(() => { - if (toolpadProcess) { - toolpadProcess.kill(); - } - - toolpadProcess.on('exit', () => { - if (fs.existsSync(directoryPath)) { - rimraf(directoryPath); - } - }); - }); -}); diff --git a/packages/create-toolpad-app/tests/index.spec.ts b/packages/create-toolpad-app/tests/index.spec.ts index cd38bf14494..17d444e68c9 100644 --- a/packages/create-toolpad-app/tests/index.spec.ts +++ b/packages/create-toolpad-app/tests/index.spec.ts @@ -13,7 +13,7 @@ const cliPath = path.resolve(currentDirectory, '../dist/index.js'); let testDir: string | undefined; let cp: ExecaChildProcess | undefined; -it('start', async () => { +test('create-toolpad-app can bootstrap a Toolpad app', async () => { testDir = await fs.mkdtemp(path.resolve(currentDirectory, './test-app-')); cp = execa(cliPath, [path.basename(testDir)], { cwd: currentDirectory, From 8f10ea3aa9d167f63d889fd006bfa3875f683f8e Mon Sep 17 00:00:00 2001 From: Bharat Kashyap Date: Wed, 3 May 2023 22:27:25 +0530 Subject: [PATCH 07/23] test create-toolpad-app using health-check --- jest.config.ts | 1 + packages/create-toolpad-app/jest.config.ts | 6 ++ packages/create-toolpad-app/src/index.spec.ts | 74 +++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 packages/create-toolpad-app/jest.config.ts create mode 100644 packages/create-toolpad-app/src/index.spec.ts diff --git a/jest.config.ts b/jest.config.ts index c5d2817d204..8978261c01f 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -4,6 +4,7 @@ const config: Config.InitialOptions = { projects: [ '/packages/toolpad-app/jest.config.ts', '/packages/toolpad-utils/jest.config.ts', + '/packages/create-toolpad-app/jest.config.ts', ], setupFilesAfterEnv: ['/test/utils/jest-setup.ts'], }; diff --git a/packages/create-toolpad-app/jest.config.ts b/packages/create-toolpad-app/jest.config.ts new file mode 100644 index 00000000000..6e3cc2ee8ea --- /dev/null +++ b/packages/create-toolpad-app/jest.config.ts @@ -0,0 +1,6 @@ +import { Config } from 'jest'; + +export default { + preset: 'ts-jest', + transform: {}, +} satisfies Config; diff --git a/packages/create-toolpad-app/src/index.spec.ts b/packages/create-toolpad-app/src/index.spec.ts new file mode 100644 index 00000000000..0aa5ae77623 --- /dev/null +++ b/packages/create-toolpad-app/src/index.spec.ts @@ -0,0 +1,74 @@ +const { spawn, exec } = require('child_process'); +const fs = require('fs'); +const { rimraf } = require('rimraf'); +const http = require('http'); + +interface ServerResponse { + statusCode: number; +} + +describe('create-toolpad-app', () => { + let toolpadProcess: ReturnType; + let cp: ReturnType; + let toolpadAppAddress = ''; + const toolpadAppAddressRegex = /http:\/\/localhost:(\d+)/; + const directoryPath = './my-test-app'; + + test('create-toolpad-app launches the app', (done) => { + const scriptPath = './packages/create-toolpad-app/dist/index.js'; + cp = spawn('node', [scriptPath, directoryPath]); + const { stderr } = cp; + + stderr.on('data', (data: Buffer) => { + console.error(data.toString()); + }); + + cp.on('error', (err: Error) => { + console.error(`Failed to start create-toolpad-app: ${err}`); + }); + + cp.on('exit', (code: number | null, signal: string | null) => { + if (code !== 0) { + console.error(`create-toolpad-app exited with code ${code}`); + } else if (signal !== null) { + console.error(`create-toolpad-app exited due to signal ${signal}`); + } else { + toolpadProcess = exec('yarn dev', { + cwd: directoryPath, + }); + + toolpadProcess.on('error', (err: Error) => { + console.error(`Failed to start toolpad dev: ${err}`); + }); + + toolpadProcess.stdout.on('data', (data: Buffer) => { + // Match the URL in a line which says "ready on ": + const matches = data.toString().match(toolpadAppAddressRegex); + if (matches) { + toolpadAppAddress = matches[0]; + done(); + } + }); + } + }); + }, 60000); + + it('should return a 200 status code when hitting the health-check route', (done) => { + http.get(`${toolpadAppAddress}/health-check`, (res: ServerResponse) => { + expect(res.statusCode).toBe(200); + done(); + }); + }); + + afterAll(() => { + if (toolpadProcess) { + toolpadProcess.kill(); + } + + toolpadProcess.on('exit', () => { + if (fs.existsSync(directoryPath)) { + rimraf(directoryPath); + } + }); + }); +}); From 1dec69a5a8f63a3b39b7ad271ff2a83771259c86 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Thu, 4 May 2023 10:15:10 +0200 Subject: [PATCH 08/23] Propose improved tests --- packages/create-toolpad-app/jest.config.ts | 3 +- packages/create-toolpad-app/src/index.spec.ts | 2 +- .../create-toolpad-app/tests/index.spec.ts | 49 +++++++++++++++++++ .../create-toolpad-app/tests/package.json | 3 ++ .../create-toolpad-app/tests/tsconfig.json | 6 +++ 5 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 packages/create-toolpad-app/tests/index.spec.ts create mode 100644 packages/create-toolpad-app/tests/package.json create mode 100644 packages/create-toolpad-app/tests/tsconfig.json diff --git a/packages/create-toolpad-app/jest.config.ts b/packages/create-toolpad-app/jest.config.ts index 6e3cc2ee8ea..2669edf4363 100644 --- a/packages/create-toolpad-app/jest.config.ts +++ b/packages/create-toolpad-app/jest.config.ts @@ -1,6 +1,5 @@ import { Config } from 'jest'; export default { - preset: 'ts-jest', - transform: {}, + preset: 'ts-jest/presets/default-esm', } satisfies Config; diff --git a/packages/create-toolpad-app/src/index.spec.ts b/packages/create-toolpad-app/src/index.spec.ts index 0aa5ae77623..915228db142 100644 --- a/packages/create-toolpad-app/src/index.spec.ts +++ b/packages/create-toolpad-app/src/index.spec.ts @@ -7,7 +7,7 @@ interface ServerResponse { statusCode: number; } -describe('create-toolpad-app', () => { +describe.skip('create-toolpad-app', () => { let toolpadProcess: ReturnType; let cp: ReturnType; let toolpadAppAddress = ''; diff --git a/packages/create-toolpad-app/tests/index.spec.ts b/packages/create-toolpad-app/tests/index.spec.ts new file mode 100644 index 00000000000..d8291c2c2ce --- /dev/null +++ b/packages/create-toolpad-app/tests/index.spec.ts @@ -0,0 +1,49 @@ +import * as fs from 'fs/promises'; +import { jest } from '@jest/globals'; +import * as path from 'path'; +import * as url from 'url'; +import { execa, ExecaChildProcess } from 'execa'; + +jest.setTimeout(60000); + +const currentDirectory = url.fileURLToPath(new URL('.', import.meta.url)); + +let testDir: string | undefined; +let cp: ExecaChildProcess | undefined; + +it('start', async () => { + testDir = await fs.mkdtemp(path.resolve(currentDirectory, './test-app-')); + cp = execa('create-toolpad-app', [path.basename(testDir)], { + cwd: currentDirectory, + }); + const result = await cp; + expect(result.stdout).toMatch('Run the following to get started'); + const packageJsonContent = await fs.readFile(path.resolve(testDir, './package.json'), { + encoding: 'utf-8', + }); + const packageJson = JSON.parse(packageJsonContent); + expect(packageJson).toEqual( + expect.objectContaining({ + dependencies: expect.objectContaining({ + '@mui/toolpad': expect.any(String), + }), + scripts: expect.objectContaining({ + build: 'toolpad build', + dev: 'toolpad dev', + start: 'toolpad start', + }), + }), + ); +}); + +afterEach(async () => { + if (testDir) { + await fs.rm(testDir, { recursive: true, force: true }); + } +}); + +afterEach(async () => { + if (cp) { + cp.kill(); + } +}); diff --git a/packages/create-toolpad-app/tests/package.json b/packages/create-toolpad-app/tests/package.json new file mode 100644 index 00000000000..3dbc1ca591c --- /dev/null +++ b/packages/create-toolpad-app/tests/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/packages/create-toolpad-app/tests/tsconfig.json b/packages/create-toolpad-app/tests/tsconfig.json new file mode 100644 index 00000000000..0421216283c --- /dev/null +++ b/packages/create-toolpad-app/tests/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "ESNext" + } +} From 7dad2621bb1ec90238aca0c946a2d11848f4be99 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Thu, 4 May 2023 10:39:02 +0200 Subject: [PATCH 09/23] forgiot this --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 31ab9e7d607..bc8e05a8594 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "release:changelog": "dotenv -- node ./scripts/releaseChangelog.mjs --repo mui-toolpad", "test:build": "lerna run build --scope @mui/toolpad-core --scope @mui/toolpad-components --stream", "test:integration": "rm -rf ./node_modules/.vite && playwright test --config ./test/playwright.config.ts", - "test": "jest", + "test": "NODE_OPTIONS=--experimental-vm-modules jest", "check-types": "lerna run check-types", "toolpad": "toolpad" }, From fc0762727e6f55509514780764fbcc2caa282b18 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Thu, 4 May 2023 12:12:12 +0200 Subject: [PATCH 10/23] Try running tests separately --- jest.config.ts | 12 ------------ package.json | 2 +- packages/create-toolpad-app/package.json | 3 ++- packages/toolpad-app/package.json | 3 ++- packages/toolpad-components/package.json | 1 - packages/toolpad-core/package.json | 1 - packages/toolpad-utils/jest.config.ts | 2 +- packages/toolpad-utils/package.json | 4 ++-- packages/toolpad-utils/tsconfig.json | 4 ++-- test/utils/jest-setup.ts | 1 - 10 files changed, 10 insertions(+), 23 deletions(-) delete mode 100644 jest.config.ts delete mode 100644 test/utils/jest-setup.ts diff --git a/jest.config.ts b/jest.config.ts deleted file mode 100644 index 8978261c01f..00000000000 --- a/jest.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { Config } from '@jest/types'; - -const config: Config.InitialOptions = { - projects: [ - '/packages/toolpad-app/jest.config.ts', - '/packages/toolpad-utils/jest.config.ts', - '/packages/create-toolpad-app/jest.config.ts', - ], - setupFilesAfterEnv: ['/test/utils/jest-setup.ts'], -}; - -export default config; diff --git a/package.json b/package.json index bc8e05a8594..da8f5832207 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "release:changelog": "dotenv -- node ./scripts/releaseChangelog.mjs --repo mui-toolpad", "test:build": "lerna run build --scope @mui/toolpad-core --scope @mui/toolpad-components --stream", "test:integration": "rm -rf ./node_modules/.vite && playwright test --config ./test/playwright.config.ts", - "test": "NODE_OPTIONS=--experimental-vm-modules jest", + "test": "lerna run test", "check-types": "lerna run check-types", "toolpad": "toolpad" }, diff --git a/packages/create-toolpad-app/package.json b/packages/create-toolpad-app/package.json index 95db096a18f..0c469010015 100644 --- a/packages/create-toolpad-app/package.json +++ b/packages/create-toolpad-app/package.json @@ -24,7 +24,8 @@ "prebuild": "rimraf ./dist", "build": "tsup", "dev": "tsup --watch", - "check-types": "tsc --noEmit" + "check-types": "tsc --noEmit", + "test": "NODE_OPTIONS=--experimental-vm-modules jest" }, "engines": { "node": ">=16.17" diff --git a/packages/toolpad-app/package.json b/packages/toolpad-app/package.json index fdf098e50da..bfabfd76ed4 100644 --- a/packages/toolpad-app/package.json +++ b/packages/toolpad-app/package.json @@ -15,7 +15,8 @@ "dev:cli": "tsup --watch", "dev:typings": "yarn build:typings", "waitForApp": "ts-node --esm ./scripts/waitForApp.mts", - "check-types": "tsc --noEmit" + "check-types": "tsc --noEmit", + "test": "jest" }, "files": [ "public", diff --git a/packages/toolpad-components/package.json b/packages/toolpad-components/package.json index ecdc03949d5..62d79d4dc3c 100644 --- a/packages/toolpad-components/package.json +++ b/packages/toolpad-components/package.json @@ -33,7 +33,6 @@ "prebuild": "rimraf dist", "build": "tsup", "dev": "tsup --watch", - "test": "echo \"Error: run tests from root\" && exit 1", "check-types": "tsc" }, "bugs": { diff --git a/packages/toolpad-core/package.json b/packages/toolpad-core/package.json index 1863ce9e3e2..6800d5adfc9 100644 --- a/packages/toolpad-core/package.json +++ b/packages/toolpad-core/package.json @@ -35,7 +35,6 @@ "prebuild": "rimraf dist", "build": "tsup", "dev": "tsup --watch", - "test": "echo \"Error: run tests from root\" && exit 1", "check-types": "yarn build" }, "bugs": { diff --git a/packages/toolpad-utils/jest.config.ts b/packages/toolpad-utils/jest.config.ts index ff60f10bd48..2669edf4363 100644 --- a/packages/toolpad-utils/jest.config.ts +++ b/packages/toolpad-utils/jest.config.ts @@ -1,5 +1,5 @@ import { Config } from 'jest'; export default { - preset: 'ts-jest', + preset: 'ts-jest/presets/default-esm', } satisfies Config; diff --git a/packages/toolpad-utils/package.json b/packages/toolpad-utils/package.json index ab02bb58a14..b70b4ea9387 100644 --- a/packages/toolpad-utils/package.json +++ b/packages/toolpad-utils/package.json @@ -35,8 +35,8 @@ "prebuild": "rimraf dist", "build": "tsup", "dev": "tsup --watch", - "test": "echo \"Error: run tests from root\" && exit 1", - "check-types": "yarn build" + "check-types": "yarn build", + "test": "jest" }, "bugs": { "url": "https://github.com/mui/mui-toolpad/issues" diff --git a/packages/toolpad-utils/tsconfig.json b/packages/toolpad-utils/tsconfig.json index c27c70b76e2..d0850b14e85 100644 --- a/packages/toolpad-utils/tsconfig.json +++ b/packages/toolpad-utils/tsconfig.json @@ -1,8 +1,8 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "module": "esnext", - "moduleResolution": "bundler", + "module": "ESNext", + "moduleResolution": "nodenext", "alwaysStrict": true, "rootDir": "src", "outDir": "dist", diff --git a/test/utils/jest-setup.ts b/test/utils/jest-setup.ts deleted file mode 100644 index 7b0828bfa80..00000000000 --- a/test/utils/jest-setup.ts +++ /dev/null @@ -1 +0,0 @@ -import '@testing-library/jest-dom'; From b469274c4d90484c69d6ac47a6747a3ffddea660 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Thu, 4 May 2023 13:42:22 +0200 Subject: [PATCH 11/23] path fix --- packages/create-toolpad-app/tests/index.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/create-toolpad-app/tests/index.spec.ts b/packages/create-toolpad-app/tests/index.spec.ts index d8291c2c2ce..cd38bf14494 100644 --- a/packages/create-toolpad-app/tests/index.spec.ts +++ b/packages/create-toolpad-app/tests/index.spec.ts @@ -8,12 +8,14 @@ jest.setTimeout(60000); const currentDirectory = url.fileURLToPath(new URL('.', import.meta.url)); +const cliPath = path.resolve(currentDirectory, '../dist/index.js'); + let testDir: string | undefined; let cp: ExecaChildProcess | undefined; it('start', async () => { testDir = await fs.mkdtemp(path.resolve(currentDirectory, './test-app-')); - cp = execa('create-toolpad-app', [path.basename(testDir)], { + cp = execa(cliPath, [path.basename(testDir)], { cwd: currentDirectory, }); const result = await cp; From 79ce916db99b34b622d1372a0c286736c2bfe5f2 Mon Sep 17 00:00:00 2001 From: Bharat Kashyap Date: Fri, 5 May 2023 12:31:39 +0530 Subject: [PATCH 12/23] Rename test, remove the old one --- packages/create-toolpad-app/src/index.spec.ts | 74 ------------------- .../create-toolpad-app/tests/index.spec.ts | 2 +- 2 files changed, 1 insertion(+), 75 deletions(-) delete mode 100644 packages/create-toolpad-app/src/index.spec.ts diff --git a/packages/create-toolpad-app/src/index.spec.ts b/packages/create-toolpad-app/src/index.spec.ts deleted file mode 100644 index 915228db142..00000000000 --- a/packages/create-toolpad-app/src/index.spec.ts +++ /dev/null @@ -1,74 +0,0 @@ -const { spawn, exec } = require('child_process'); -const fs = require('fs'); -const { rimraf } = require('rimraf'); -const http = require('http'); - -interface ServerResponse { - statusCode: number; -} - -describe.skip('create-toolpad-app', () => { - let toolpadProcess: ReturnType; - let cp: ReturnType; - let toolpadAppAddress = ''; - const toolpadAppAddressRegex = /http:\/\/localhost:(\d+)/; - const directoryPath = './my-test-app'; - - test('create-toolpad-app launches the app', (done) => { - const scriptPath = './packages/create-toolpad-app/dist/index.js'; - cp = spawn('node', [scriptPath, directoryPath]); - const { stderr } = cp; - - stderr.on('data', (data: Buffer) => { - console.error(data.toString()); - }); - - cp.on('error', (err: Error) => { - console.error(`Failed to start create-toolpad-app: ${err}`); - }); - - cp.on('exit', (code: number | null, signal: string | null) => { - if (code !== 0) { - console.error(`create-toolpad-app exited with code ${code}`); - } else if (signal !== null) { - console.error(`create-toolpad-app exited due to signal ${signal}`); - } else { - toolpadProcess = exec('yarn dev', { - cwd: directoryPath, - }); - - toolpadProcess.on('error', (err: Error) => { - console.error(`Failed to start toolpad dev: ${err}`); - }); - - toolpadProcess.stdout.on('data', (data: Buffer) => { - // Match the URL in a line which says "ready on ": - const matches = data.toString().match(toolpadAppAddressRegex); - if (matches) { - toolpadAppAddress = matches[0]; - done(); - } - }); - } - }); - }, 60000); - - it('should return a 200 status code when hitting the health-check route', (done) => { - http.get(`${toolpadAppAddress}/health-check`, (res: ServerResponse) => { - expect(res.statusCode).toBe(200); - done(); - }); - }); - - afterAll(() => { - if (toolpadProcess) { - toolpadProcess.kill(); - } - - toolpadProcess.on('exit', () => { - if (fs.existsSync(directoryPath)) { - rimraf(directoryPath); - } - }); - }); -}); diff --git a/packages/create-toolpad-app/tests/index.spec.ts b/packages/create-toolpad-app/tests/index.spec.ts index cd38bf14494..17d444e68c9 100644 --- a/packages/create-toolpad-app/tests/index.spec.ts +++ b/packages/create-toolpad-app/tests/index.spec.ts @@ -13,7 +13,7 @@ const cliPath = path.resolve(currentDirectory, '../dist/index.js'); let testDir: string | undefined; let cp: ExecaChildProcess | undefined; -it('start', async () => { +test('create-toolpad-app can bootstrap a Toolpad app', async () => { testDir = await fs.mkdtemp(path.resolve(currentDirectory, './test-app-')); cp = execa(cliPath, [path.basename(testDir)], { cwd: currentDirectory, From bccad7a063024e91b3a4ebb629403d16d974bc94 Mon Sep 17 00:00:00 2001 From: Bharat Kashyap Date: Tue, 9 May 2023 18:34:46 +0530 Subject: [PATCH 13/23] Feat: Start app and run health-check --- .../create-toolpad-app/tests/index.spec.ts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/create-toolpad-app/tests/index.spec.ts b/packages/create-toolpad-app/tests/index.spec.ts index 17d444e68c9..e81a13f0aa2 100644 --- a/packages/create-toolpad-app/tests/index.spec.ts +++ b/packages/create-toolpad-app/tests/index.spec.ts @@ -12,6 +12,7 @@ const cliPath = path.resolve(currentDirectory, '../dist/index.js'); let testDir: string | undefined; let cp: ExecaChildProcess | undefined; +let toolpadProcess: ExecaChildProcess | undefined; test('create-toolpad-app can bootstrap a Toolpad app', async () => { testDir = await fs.mkdtemp(path.resolve(currentDirectory, './test-app-')); @@ -36,6 +37,43 @@ test('create-toolpad-app can bootstrap a Toolpad app', async () => { }), }), ); + toolpadProcess = execa('yarn', ['dev'], { + cwd: testDir, + }); + const { stdout: toolpadStream } = toolpadProcess; + if (toolpadStream) { + for await (const data of toolpadStream) { + const output = data.toString(); + let appUrl = ''; + // Check if the output contains the desired URL + if (output.includes('ready on')) { + const readyRegex = /ready on (.*)/; + const urlRegex = /http:\/\/localhost:(\d+)/; + const match = output.match(readyRegex); + + if (match && match[1]) { + appUrl = match[1].trim().match(urlRegex)?.[0] ?? ''; + } + } + // Check if the output contains the desired compilation success message + if (appUrl) { + try { + // Perform the health check on the running app + const healthCheckResponse = await (await fetch(`${appUrl}/health-check`)).json(); + expect(healthCheckResponse).toEqual( + expect.objectContaining({ + memoryUsage: expect.any(Object), + memoryUsagePretty: expect.any(Object), + }), + ); + } catch (error) { + throw new Error(`Health check failed: ${error}`); + } finally { + toolpadProcess?.kill(); + } + } + } + } }); afterEach(async () => { From e8b8ecab991b87756cd229d24ae268e2716c10e8 Mon Sep 17 00:00:00 2001 From: Bharat Kashyap Date: Tue, 9 May 2023 18:37:49 +0530 Subject: [PATCH 14/23] Fix: Use `@mui/toolpad-utils` and add `install` op --- packages/create-toolpad-app/package.json | 1 + packages/create-toolpad-app/src/index.ts | 125 ++++++++--------------- 2 files changed, 41 insertions(+), 85 deletions(-) diff --git a/packages/create-toolpad-app/package.json b/packages/create-toolpad-app/package.json index 1b1547f1302..69472e99d41 100644 --- a/packages/create-toolpad-app/package.json +++ b/packages/create-toolpad-app/package.json @@ -31,6 +31,7 @@ "node": ">=16.17" }, "dependencies": { + "@mui/toolpad-utils": "^0.1.10", "chalk": "5.2.0", "execa": "7.1.1", "inquirer": "9.2.2" diff --git a/packages/create-toolpad-app/src/index.ts b/packages/create-toolpad-app/src/index.ts index e15efe1d2fa..caf12eddb36 100644 --- a/packages/create-toolpad-app/src/index.ts +++ b/packages/create-toolpad-app/src/index.ts @@ -3,74 +3,14 @@ import * as fs from 'fs/promises'; import path from 'path'; import yargs from 'yargs'; +import { errorFrom } from '@mui/toolpad-utils/errors'; type PackageManager = 'npm' | 'pnpm' | 'yarn'; - -type Require = T & { [P in K]-?: T[P] }; - -type Ensure = K extends keyof U ? Require : U & Record; - declare global { interface Error { code?: unknown; } } -/** - * Type aware version of Object.protoype.hasOwnProperty. - * See https://fettblog.eu/typescript-hasownproperty/ - */ - -function hasOwnProperty(obj: X, prop: Y): obj is Ensure { - return obj.hasOwnProperty(prop); -} - -/** - * Limits the length of a string and adds ellipsis if necessary. - */ - -function truncate(str: string, maxLength: number, dots: string = '...') { - if (str.length <= maxLength) { - return str; - } - return str.slice(0, maxLength) + dots; -} - -/** - * Creates a javascript `Error` from an unkown value if it's not already an error. - * Does a best effort at inferring a message. Intended to be used typically in `catch` - * blocks, as there is no way to enforce only `Error` objects being thrown. - * - * ``` - * try { - * // ... - * } catch (rawError) { - * const error = errorFrom(rawError); - * console.assert(error instancof Error); - * } - * ``` - */ - -function errorFrom(maybeError: unknown): Error { - if (maybeError instanceof Error) { - return maybeError; - } - - if ( - typeof maybeError === 'object' && - maybeError && - hasOwnProperty(maybeError, 'message') && - typeof maybeError.message! === 'string' - ) { - return new Error(maybeError.message, { cause: maybeError }); - } - - if (typeof maybeError === 'string') { - return new Error(maybeError, { cause: maybeError }); - } - - const message = truncate(JSON.stringify(maybeError), 500); - return new Error(message, { cause: maybeError }); -} function getPackageManager(): PackageManager { const userAgent = process.env.npm_config_user_agent; @@ -159,7 +99,7 @@ const validatePath = async (relativePath: string): Promise => }; // Create a new `package.json` file and install dependencies -const scaffoldProject = async (absolutePath: string): Promise => { +const scaffoldProject = async (absolutePath: string, installFlag: boolean): Promise => { const { default: chalk } = await import('chalk'); const { execaCommand } = await import('execa'); @@ -179,29 +119,34 @@ const scaffoldProject = async (absolutePath: string): Promise => { build: 'toolpad build', start: 'toolpad start', }, + dependencies: { + '@mui/toolpad': 'latest', + }, }; await fs.writeFile(path.join(absolutePath, 'package.json'), JSON.stringify(packageJson, null, 2)); - // eslint-disable-next-line no-console - console.log( - `${chalk.blue('info')} - Installing the following dependencies: ${chalk.magenta( - '@mui/toolpad', - )}`, - ); - // eslint-disable-next-line no-console - console.log(); - - const installVerb = packageManager === 'yarn' ? 'add' : 'install'; - const command = `${packageManager} ${installVerb} @mui/toolpad`; - await execaCommand(command, { stdio: 'inherit', cwd: absolutePath }); - - // eslint-disable-next-line no-console - console.log(); - // eslint-disable-next-line no-console - console.log(`${chalk.green('success')} - Dependencies installed successfully!`); - // eslint-disable-next-line no-console - console.log(); + if (installFlag) { + // eslint-disable-next-line no-console + console.log( + `${chalk.blue('info')} - Installing the following dependencies: ${chalk.magenta( + '@mui/toolpad', + )}`, + ); + // eslint-disable-next-line no-console + console.log(); + + const installVerb = packageManager === 'yarn' ? 'add' : 'install'; + const command = `${packageManager} ${installVerb} @mui/toolpad`; + await execaCommand(command, { stdio: 'inherit', cwd: absolutePath }); + + // eslint-disable-next-line no-console + console.log(); + // eslint-disable-next-line no-console + console.log(`${chalk.green('success')} - Dependencies installed successfully!`); + // eslint-disable-next-line no-console + console.log(); + } }; // Run the CLI interaction with Inquirer.js @@ -211,14 +156,20 @@ const run = async () => { const args = await yargs(process.argv.slice(2)) .scriptName('create-toolpad-app') - .usage('$0 [path]') + .usage('$0 [path] [options]') .positional('path', { type: 'string', describe: 'The path where the Toolpad project directory will be created', }) + .option('install', { + type: 'boolean', + describe: 'Where to intall dependencies', + default: true, + }) .help().argv; const pathArg = args._?.[0] as string; + const installFlag = args.install as boolean; if (pathArg) { const pathValidOrError = await validatePath(pathArg); @@ -248,7 +199,7 @@ const run = async () => { const absolutePath = path.join(process.cwd(), answers.path || pathArg); - await scaffoldProject(absolutePath); + await scaffoldProject(absolutePath, installFlag); const changeDirectoryInstruction = /* `path.relative` is truth-y if the relative path @@ -256,11 +207,15 @@ const run = async () => { * is not empty */ path.relative(process.cwd(), absolutePath) - ? `cd ${path.relative(process.cwd(), absolutePath)} && ` + ? ` cd ${path.relative(process.cwd(), absolutePath)}\n` : ''; + const installInstruction = installFlag ? '' : ` ${packageManager} install\n`; + const message = `Run the following to get started: \n\n${chalk.magentaBright( - `${changeDirectoryInstruction}${packageManager}${packageManager === 'yarn' ? '' : ' run'} dev`, + `${changeDirectoryInstruction}${installInstruction} ${packageManager}${ + packageManager === 'yarn' ? '' : ' run' + } dev`, )}`; // eslint-disable-next-line no-console console.log(message); From f7756a910cdeb34ddcc1f04860a13feaad8f03f8 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Tue, 9 May 2023 17:36:12 +0200 Subject: [PATCH 15/23] add check --- .../create-toolpad-app/tests/index.spec.ts | 71 ++++++++++--------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/packages/create-toolpad-app/tests/index.spec.ts b/packages/create-toolpad-app/tests/index.spec.ts index e81a13f0aa2..ab1aac2c7db 100644 --- a/packages/create-toolpad-app/tests/index.spec.ts +++ b/packages/create-toolpad-app/tests/index.spec.ts @@ -3,6 +3,9 @@ import { jest } from '@jest/globals'; import * as path from 'path'; import * as url from 'url'; import { execa, ExecaChildProcess } from 'execa'; +import readline from 'readline'; +import { Readable } from 'stream'; +import { once } from 'events'; jest.setTimeout(60000); @@ -14,6 +17,23 @@ let testDir: string | undefined; let cp: ExecaChildProcess | undefined; let toolpadProcess: ExecaChildProcess | undefined; +async function waitForMatch(input: Readable, regex: RegExp): Promise { + return new Promise((resolve, reject) => { + const rl = readline.createInterface({ input }); + + rl.on('line', (line) => { + const match = regex.exec(line); + if (match) { + rl.close(); + input.resume(); + resolve(match); + } + }); + rl.on('error', (err) => reject(err)); + rl.on('end', () => resolve(null)); + }); +} + test('create-toolpad-app can bootstrap a Toolpad app', async () => { testDir = await fs.mkdtemp(path.resolve(currentDirectory, './test-app-')); cp = execa(cliPath, [path.basename(testDir)], { @@ -39,40 +59,27 @@ test('create-toolpad-app can bootstrap a Toolpad app', async () => { ); toolpadProcess = execa('yarn', ['dev'], { cwd: testDir, + env: { + FORCE_COLOR: '0', + BROWSER: 'none', + }, }); - const { stdout: toolpadStream } = toolpadProcess; - if (toolpadStream) { - for await (const data of toolpadStream) { - const output = data.toString(); - let appUrl = ''; - // Check if the output contains the desired URL - if (output.includes('ready on')) { - const readyRegex = /ready on (.*)/; - const urlRegex = /http:\/\/localhost:(\d+)/; - const match = output.match(readyRegex); + const { stdout: toolpadDevOutput } = toolpadProcess; - if (match && match[1]) { - appUrl = match[1].trim().match(urlRegex)?.[0] ?? ''; - } - } - // Check if the output contains the desired compilation success message - if (appUrl) { - try { - // Perform the health check on the running app - const healthCheckResponse = await (await fetch(`${appUrl}/health-check`)).json(); - expect(healthCheckResponse).toEqual( - expect.objectContaining({ - memoryUsage: expect.any(Object), - memoryUsagePretty: expect.any(Object), - }), - ); - } catch (error) { - throw new Error(`Health check failed: ${error}`); - } finally { - toolpadProcess?.kill(); - } - } - } + expect(toolpadDevOutput).toBeTruthy(); + const match = await waitForMatch(toolpadDevOutput!, /http:\/\/localhost:(\d+)/); + + expect(match).toBeTruthy(); + + const appUrl = match![0]; + const res = await fetch(`${appUrl}/health-check`); + expect(res).toHaveProperty('status', 200); +}); + +afterEach(async () => { + if (toolpadProcess && typeof toolpadProcess.exitCode !== 'number') { + toolpadProcess.kill(); + await once(toolpadProcess, 'exit'); } }); From ac372c7c5bd615abd95eb94592604485e319672b Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Tue, 9 May 2023 17:42:55 +0200 Subject: [PATCH 16/23] dynamic import ESM --- packages/create-toolpad-app/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-toolpad-app/src/index.ts b/packages/create-toolpad-app/src/index.ts index caf12eddb36..0a9593350ad 100644 --- a/packages/create-toolpad-app/src/index.ts +++ b/packages/create-toolpad-app/src/index.ts @@ -3,7 +3,6 @@ import * as fs from 'fs/promises'; import path from 'path'; import yargs from 'yargs'; -import { errorFrom } from '@mui/toolpad-utils/errors'; type PackageManager = 'npm' | 'pnpm' | 'yarn'; declare global { @@ -72,6 +71,7 @@ async function isFolderEmpty(pathDir: string): Promise { const packageManager = getPackageManager(); const validatePath = async (relativePath: string): Promise => { + const { errorFrom } = await import('@mui/toolpad-utils/errors'); const { default: chalk } = await import('chalk'); const absolutePath = path.join(process.cwd(), relativePath); From 63320e4cddcb3320dd0e8149234796cf49c0cd39 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Tue, 9 May 2023 17:51:32 +0200 Subject: [PATCH 17/23] Remove dynamic imports --- packages/create-toolpad-app/src/index.ts | 13 ++++--------- packages/create-toolpad-app/tsconfig.json | 4 ++-- packages/create-toolpad-app/tsup.config.ts | 3 ++- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/create-toolpad-app/src/index.ts b/packages/create-toolpad-app/src/index.ts index 0a9593350ad..c51651dbfec 100644 --- a/packages/create-toolpad-app/src/index.ts +++ b/packages/create-toolpad-app/src/index.ts @@ -3,6 +3,10 @@ import * as fs from 'fs/promises'; import path from 'path'; import yargs from 'yargs'; +import inquirer from 'inquirer'; +import chalk from 'chalk'; +import { errorFrom } from '@mui/toolpad-utils/errors'; +import { execaCommand } from 'execa'; type PackageManager = 'npm' | 'pnpm' | 'yarn'; declare global { @@ -71,9 +75,6 @@ async function isFolderEmpty(pathDir: string): Promise { const packageManager = getPackageManager(); const validatePath = async (relativePath: string): Promise => { - const { errorFrom } = await import('@mui/toolpad-utils/errors'); - const { default: chalk } = await import('chalk'); - const absolutePath = path.join(process.cwd(), relativePath); try { @@ -100,9 +101,6 @@ const validatePath = async (relativePath: string): Promise => // Create a new `package.json` file and install dependencies const scaffoldProject = async (absolutePath: string, installFlag: boolean): Promise => { - const { default: chalk } = await import('chalk'); - const { execaCommand } = await import('execa'); - // eslint-disable-next-line no-console console.log(); // eslint-disable-next-line no-console @@ -151,9 +149,6 @@ const scaffoldProject = async (absolutePath: string, installFlag: boolean): Prom // Run the CLI interaction with Inquirer.js const run = async () => { - const { default: chalk } = await import('chalk'); - const { default: inquirer } = await import('inquirer'); - const args = await yargs(process.argv.slice(2)) .scriptName('create-toolpad-app') .usage('$0 [path] [options]') diff --git a/packages/create-toolpad-app/tsconfig.json b/packages/create-toolpad-app/tsconfig.json index 0bc1b60ca4a..8698918fed6 100644 --- a/packages/create-toolpad-app/tsconfig.json +++ b/packages/create-toolpad-app/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { "target": "es2020", - "module": "commonjs", - "moduleResolution": "node16", + "module": "ESNext", + "moduleResolution": "bundler", "lib": ["esnext"], "isolatedModules": true, "strict": true, diff --git a/packages/create-toolpad-app/tsup.config.ts b/packages/create-toolpad-app/tsup.config.ts index a7281b10469..bc0d4ef49b6 100644 --- a/packages/create-toolpad-app/tsup.config.ts +++ b/packages/create-toolpad-app/tsup.config.ts @@ -3,8 +3,9 @@ import { defineConfig } from 'tsup'; export default defineConfig({ entry: ['./src/index.ts'], silent: true, - noExternal: ['chalk', 'execa'], + noExternal: ['chalk', 'execa', 'inquirer'], clean: true, + format: 'cjs', async onSuccess() { // eslint-disable-next-line no-console console.log('cli: build successful'); From 72751655b3b2f8415981e48e17f349e2cfee36d9 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Tue, 9 May 2023 18:25:17 +0200 Subject: [PATCH 18/23] try adding a small delay --- packages/create-toolpad-app/tests/index.spec.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/create-toolpad-app/tests/index.spec.ts b/packages/create-toolpad-app/tests/index.spec.ts index ab1aac2c7db..0f81b875e37 100644 --- a/packages/create-toolpad-app/tests/index.spec.ts +++ b/packages/create-toolpad-app/tests/index.spec.ts @@ -6,6 +6,7 @@ import { execa, ExecaChildProcess } from 'execa'; import readline from 'readline'; import { Readable } from 'stream'; import { once } from 'events'; +import { setTimeout } from 'timers/promises'; jest.setTimeout(60000); @@ -80,6 +81,7 @@ afterEach(async () => { if (toolpadProcess && typeof toolpadProcess.exitCode !== 'number') { toolpadProcess.kill(); await once(toolpadProcess, 'exit'); + await setTimeout(500); } }); @@ -90,7 +92,8 @@ afterEach(async () => { }); afterEach(async () => { - if (cp) { + if (cp && typeof cp.exitCode !== 'number') { cp.kill(); + await once(cp, 'exit'); } }); From c4f005802bcf63ac2cd25f02a4f7473e8bc9bac2 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Wed, 10 May 2023 18:38:43 +0200 Subject: [PATCH 19/23] trty longer timeout --- packages/create-toolpad-app/tests/index.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-toolpad-app/tests/index.spec.ts b/packages/create-toolpad-app/tests/index.spec.ts index 0f81b875e37..99c26c4c22c 100644 --- a/packages/create-toolpad-app/tests/index.spec.ts +++ b/packages/create-toolpad-app/tests/index.spec.ts @@ -81,7 +81,7 @@ afterEach(async () => { if (toolpadProcess && typeof toolpadProcess.exitCode !== 'number') { toolpadProcess.kill(); await once(toolpadProcess, 'exit'); - await setTimeout(500); + await setTimeout(5000); } }); From 2207b70ce1d3d1de45d01cd0d3c49ea999e64449 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Wed, 10 May 2023 18:54:30 +0200 Subject: [PATCH 20/23] hello --- packages/create-toolpad-app/package.json | 2 +- packages/create-toolpad-app/tests/index.spec.ts | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/create-toolpad-app/package.json b/packages/create-toolpad-app/package.json index 69472e99d41..3267794728b 100644 --- a/packages/create-toolpad-app/package.json +++ b/packages/create-toolpad-app/package.json @@ -25,7 +25,7 @@ "build": "tsup", "dev": "tsup --watch", "check-types": "tsc --noEmit", - "test": "NODE_OPTIONS=--experimental-vm-modules jest" + "test": "NODE_OPTIONS=--experimental-vm-modules jest --detectOpenHandles --forceExit" }, "engines": { "node": ">=16.17" diff --git a/packages/create-toolpad-app/tests/index.spec.ts b/packages/create-toolpad-app/tests/index.spec.ts index 99c26c4c22c..86bf918de1c 100644 --- a/packages/create-toolpad-app/tests/index.spec.ts +++ b/packages/create-toolpad-app/tests/index.spec.ts @@ -6,7 +6,6 @@ import { execa, ExecaChildProcess } from 'execa'; import readline from 'readline'; import { Readable } from 'stream'; import { once } from 'events'; -import { setTimeout } from 'timers/promises'; jest.setTimeout(60000); @@ -58,6 +57,7 @@ test('create-toolpad-app can bootstrap a Toolpad app', async () => { }), }), ); + toolpadProcess = execa('yarn', ['dev'], { cwd: testDir, env: { @@ -79,21 +79,26 @@ test('create-toolpad-app can bootstrap a Toolpad app', async () => { afterEach(async () => { if (toolpadProcess && typeof toolpadProcess.exitCode !== 'number') { - toolpadProcess.kill(); + const success = toolpadProcess.kill('SIGKILL'); + // eslint-disable-next-line no-console + console.log('killed toolpad', success); await once(toolpadProcess, 'exit'); - await setTimeout(5000); } }); afterEach(async () => { if (testDir) { + // eslint-disable-next-line no-console + console.log('process exit code', toolpadProcess?.exitCode); await fs.rm(testDir, { recursive: true, force: true }); } }); afterEach(async () => { if (cp && typeof cp.exitCode !== 'number') { - cp.kill(); + const success = cp.kill(); + // eslint-disable-next-line no-console + console.log('killed toolpad', success); await once(cp, 'exit'); } }); From fb914e7c0520d793f8f5214ad810627b8f51053b Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Wed, 10 May 2023 18:59:50 +0200 Subject: [PATCH 21/23] max retries --- packages/create-toolpad-app/tests/index.spec.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/create-toolpad-app/tests/index.spec.ts b/packages/create-toolpad-app/tests/index.spec.ts index 86bf918de1c..fd6e9dfda9e 100644 --- a/packages/create-toolpad-app/tests/index.spec.ts +++ b/packages/create-toolpad-app/tests/index.spec.ts @@ -79,26 +79,20 @@ test('create-toolpad-app can bootstrap a Toolpad app', async () => { afterEach(async () => { if (toolpadProcess && typeof toolpadProcess.exitCode !== 'number') { - const success = toolpadProcess.kill('SIGKILL'); - // eslint-disable-next-line no-console - console.log('killed toolpad', success); + toolpadProcess.kill('SIGKILL'); await once(toolpadProcess, 'exit'); } }); afterEach(async () => { if (testDir) { - // eslint-disable-next-line no-console - console.log('process exit code', toolpadProcess?.exitCode); - await fs.rm(testDir, { recursive: true, force: true }); + await fs.rm(testDir, { recursive: true, force: true, maxRetries: 3 }); } }); afterEach(async () => { if (cp && typeof cp.exitCode !== 'number') { - const success = cp.kill(); - // eslint-disable-next-line no-console - console.log('killed toolpad', success); + cp.kill('SIGKILL'); await once(cp, 'exit'); } }); From 98e813e75e998a48c37ca83f7d2a4b00628a8da8 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Wed, 10 May 2023 19:17:04 +0200 Subject: [PATCH 22/23] Dispose --- packages/toolpad-app/src/toolpadDataSources/local/server.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/toolpad-app/src/toolpadDataSources/local/server.ts b/packages/toolpad-app/src/toolpadDataSources/local/server.ts index 0c61ae1706b..e2df66c2393 100644 --- a/packages/toolpad-app/src/toolpadDataSources/local/server.ts +++ b/packages/toolpad-app/src/toolpadDataSources/local/server.ts @@ -493,6 +493,9 @@ export default dataSource; async function startDev() { const builder = await createBuilder(); + process.on('SIGTERM', async () => { + await builder.dispose(); + }); await builder.watch(); return builder; } From 902fbf8dd79674481ed185f7988842b6100713d4 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Thu, 11 May 2023 09:58:41 +0200 Subject: [PATCH 23/23] Don't dispose? --- packages/toolpad-app/src/toolpadDataSources/local/server.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/toolpad-app/src/toolpadDataSources/local/server.ts b/packages/toolpad-app/src/toolpadDataSources/local/server.ts index e2df66c2393..0c61ae1706b 100644 --- a/packages/toolpad-app/src/toolpadDataSources/local/server.ts +++ b/packages/toolpad-app/src/toolpadDataSources/local/server.ts @@ -493,9 +493,6 @@ export default dataSource; async function startDev() { const builder = await createBuilder(); - process.on('SIGTERM', async () => { - await builder.dispose(); - }); await builder.watch(); return builder; }