diff --git a/jest.preset.js b/jest.preset.js index f078ddcec..92a790c53 100644 --- a/jest.preset.js +++ b/jest.preset.js @@ -1,3 +1,8 @@ const nxPreset = require('@nx/jest/preset').default; -module.exports = { ...nxPreset }; +module.exports = { + ...nxPreset, + maxWorkers: 1, + testEnvironment: 'node', + setupFiles: ['../../tools/scripts/unit-test-setup.js'], +}; diff --git a/nx.json b/nx.json index a7abe38df..0db7c05aa 100644 --- a/nx.json +++ b/nx.json @@ -62,6 +62,12 @@ "devTargetName": "dev", "startTargetName": "start" } + }, + { + "plugin": "@nx/jest/plugin", + "options": { + "targetName": "test" + } } ], "generators": { diff --git a/package.json b/package.json index 2c949c13f..c5ad80ae1 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "devDependencies": { "@angular-devkit/core": "~17.3.7", "@cloudflare/next-on-pages": "^1.12.0", + "@cloudflare/vitest-pool-workers": "^0.4.7", "@cloudflare/workers-types": "^4.20240512.0", "@commitlint/cli": "^17.8.1", "@commitlint/config-conventional": "^17.8.1", diff --git a/packages/e2e-utils/jest.config.ts b/packages/e2e-utils/jest.config.ts deleted file mode 100644 index 438ab5306..000000000 --- a/packages/e2e-utils/jest.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'e2e-utils', - preset: '../../jest.preset.js', - testEnvironment: 'node', - transform: { - '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], - }, - moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '../../coverage/packages/e2e-utils', -}; diff --git a/packages/e2e-utils/project.json b/packages/e2e-utils/project.json index b84d7252d..a56553407 100644 --- a/packages/e2e-utils/project.json +++ b/packages/e2e-utils/project.json @@ -4,13 +4,5 @@ "sourceRoot": "packages/e2e-utils/src", "projectType": "library", "tags": [], - "targets": { - "test": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], - "options": { - "jestConfig": "packages/e2e-utils/jest.config.ts" - } - } - } + "targets": {} } diff --git a/packages/nx-cloudflare-e2e/project.json b/packages/nx-cloudflare-e2e/project.json index ebf4522d9..2c5f5f50d 100644 --- a/packages/nx-cloudflare-e2e/project.json +++ b/packages/nx-cloudflare-e2e/project.json @@ -5,6 +5,9 @@ "sourceRoot": "packages/nx-cloudflare-e2e/src", "implicitDependencies": ["nx-cloudflare"], "targets": { + "test": { + "command": "echo 'Use e2e instead'" + }, "e2e": { "executor": "@nx/jest:jest", "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], diff --git a/packages/nx-cloudflare/README.md b/packages/nx-cloudflare/README.md index bdfb15e80..cf291db93 100644 --- a/packages/nx-cloudflare/README.md +++ b/packages/nx-cloudflare/README.md @@ -155,22 +155,22 @@ nx g @naxodev/nx-cloudflare:library my-worker-lib Available options: -| Option | Type | Default | Description | -| ------------------------ | ------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| name | string | \*required | What name would you like to use? | -| directory | string | null | The directory of the new application. | -| projectNameAndRootFormat | as-provided, derived | as-provided | Whether to generate the project name and root directory as provided (`as-provided`) or generate them composing their values and taking the configured layout into account (`derived`). | -| linter | eslint, none | eslint | The tool to use for running lint checks. | -| unitTestRunner | vitest, none | vitest | Test runner to use for unit tests. | -| tags | string | null | Add tags to the application (used for linting). | -| skipFormat | boolean | false | Skip formatting files. | -| js | boolean | false | Use JavaScript instead of TypeScript | -| strict | boolean | true | Whether to enable tsconfig strict mode or not. | -| publishable | boolean | false | Generate a publishable library. | -| importPath | string | null | The library name used to import it, like @myorg/my-awesome-lib. Required for publishable library. | -| bundler | swc, tsc, rollup, vite, esbuild, none | tsc | Which bundler would you like to use to build the library? Choose 'none' to skip build setup. | -| minimal | boolean | false | Generate a library with a minimal setup. No README.md generated. | -| simpleName | boolean | false | Don't include the directory in the generated file name. | +| Option | Type | Default | Description | +| ------------------------ | ----------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| name | string | \*required | What name would you like to use? | +| directory | string | null | The directory of the new application. | +| projectNameAndRootFormat | as-provided, derived | as-provided | Whether to generate the project name and root directory as provided (`as-provided`) or generate them composing their values and taking the configured layout into account (`derived`). | +| linter | eslint, none | eslint | The tool to use for running lint checks. | +| unitTestRunner | vitest, none | vitest | Test runner to use for unit tests. | +| tags | string | null | Add tags to the application (used for linting). | +| skipFormat | boolean | false | Skip formatting files. | +| js | boolean | false | Use JavaScript instead of TypeScript | +| strict | boolean | true | Whether to enable tsconfig strict mode or not. | +| publishable | boolean | false | Generate a publishable library. | +| importPath | string | null | The library name used to import it, like @myorg/my-awesome-lib. Required for publishable library. | +| bundler | swc, tsc, vite, esbuild, none | tsc | Which bundler would you like to use to build the library? Choose 'none' to skip build setup. | +| minimal | boolean | false | Generate a library with a minimal setup. No README.md generated. | +| simpleName | boolean | false | Don't include the directory in the generated file name. | ### NextJS on Cloudflare diff --git a/packages/nx-cloudflare/src/generators/application/files/common/wrangler.toml__tmpl__ b/packages/nx-cloudflare/src/generators/application/files/common/wrangler.toml__tmpl__ index 1cd78a1b3..7442711eb 100644 --- a/packages/nx-cloudflare/src/generators/application/files/common/wrangler.toml__tmpl__ +++ b/packages/nx-cloudflare/src/generators/application/files/common/wrangler.toml__tmpl__ @@ -1,5 +1,6 @@ name = "<%=name %>" -compatibility_date = "2024-04-19" +compatibility_date = "2024-01-01" +compatibility_flags = ["nodejs_compat"] main = "src/index.<%=extension %>" <%-accountId %> diff --git a/packages/nx-cloudflare/src/generators/application/files/fetch-handler/index.integration.test.ts__tmpl__ b/packages/nx-cloudflare/src/generators/application/files/fetch-handler/index.integration.test.ts__tmpl__ new file mode 100644 index 000000000..1c07a54ff --- /dev/null +++ b/packages/nx-cloudflare/src/generators/application/files/fetch-handler/index.integration.test.ts__tmpl__ @@ -0,0 +1,9 @@ +import { SELF } from "cloudflare:test"; +<%-vitestImports %> +import "../src"; + +// an integration test using SELF +it("sends request (integration style)", async () => { + const response = await SELF.fetch("http://example.com"); + expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`); +}); diff --git a/packages/nx-cloudflare/src/generators/application/files/fetch-handler/index.test.ts__tmpl__ b/packages/nx-cloudflare/src/generators/application/files/fetch-handler/index.test.ts__tmpl__ index da5a81e50..7a3de2d32 100644 --- a/packages/nx-cloudflare/src/generators/application/files/fetch-handler/index.test.ts__tmpl__ +++ b/packages/nx-cloudflare/src/generators/application/files/fetch-handler/index.test.ts__tmpl__ @@ -1,25 +1,13 @@ -import { unstable_dev } from 'wrangler'; -import type { UnstableDevWorker } from 'wrangler'; +import { env, createExecutionContext, waitOnExecutionContext, } from "cloudflare:test"; +import { worker } from './src' <%-vitestImports %> describe('Worker', () => { - let worker: UnstableDevWorker; - - beforeAll(async () => { - worker = await unstable_dev('src/index.ts', { - experimental: { disableExperimentalWarning: true }, - }); - }); - - afterAll(async () => { - await worker.stop(); - }); - it('should return Hello World', async () => { - const resp = await worker.fetch(); - if (resp) { - const text = await resp.text(); - expect(text).toMatchInlineSnapshot(`"Hello World!"`); - } + const request = new Request("http://example.com/"); + const ctx = createExecutionContext(); + const response = await worker.fetch(request, env, ctx); + await waitOnExecutionContext(ctx); + expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`); }); }); diff --git a/packages/nx-cloudflare/src/generators/application/files/scheduled-handler/index.integration.test.ts__tmpl__ b/packages/nx-cloudflare/src/generators/application/files/scheduled-handler/index.integration.test.ts__tmpl__ new file mode 100644 index 000000000..592f1607d --- /dev/null +++ b/packages/nx-cloudflare/src/generators/application/files/scheduled-handler/index.integration.test.ts__tmpl__ @@ -0,0 +1,10 @@ + +import { SELF } from "cloudflare:test"; +<%-vitestImports %> +import "../src"; + +// an integration test using SELF +it("sends request (integration style)", async () => { + const response = await SELF.fetch("http://example.com"); + expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`); +}); diff --git a/packages/nx-cloudflare/src/generators/application/files/scheduled-handler/index.test.ts__tmpl__ b/packages/nx-cloudflare/src/generators/application/files/scheduled-handler/index.test.ts__tmpl__ index da5a81e50..7a3de2d32 100644 --- a/packages/nx-cloudflare/src/generators/application/files/scheduled-handler/index.test.ts__tmpl__ +++ b/packages/nx-cloudflare/src/generators/application/files/scheduled-handler/index.test.ts__tmpl__ @@ -1,25 +1,13 @@ -import { unstable_dev } from 'wrangler'; -import type { UnstableDevWorker } from 'wrangler'; +import { env, createExecutionContext, waitOnExecutionContext, } from "cloudflare:test"; +import { worker } from './src' <%-vitestImports %> describe('Worker', () => { - let worker: UnstableDevWorker; - - beforeAll(async () => { - worker = await unstable_dev('src/index.ts', { - experimental: { disableExperimentalWarning: true }, - }); - }); - - afterAll(async () => { - await worker.stop(); - }); - it('should return Hello World', async () => { - const resp = await worker.fetch(); - if (resp) { - const text = await resp.text(); - expect(text).toMatchInlineSnapshot(`"Hello World!"`); - } + const request = new Request("http://example.com/"); + const ctx = createExecutionContext(); + const response = await worker.fetch(request, env, ctx); + await waitOnExecutionContext(ctx); + expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`); }); }); diff --git a/packages/nx-cloudflare/src/generators/application/generator.spec.ts b/packages/nx-cloudflare/src/generators/application/generator.spec.ts index a82a59187..4b0ef848c 100644 --- a/packages/nx-cloudflare/src/generators/application/generator.spec.ts +++ b/packages/nx-cloudflare/src/generators/application/generator.spec.ts @@ -23,10 +23,12 @@ describe('app', () => { it('should update project config', async () => { await applicationGenerator(tree, { name: 'myWorkerApp', + directory: 'myWorkerApp', + projectNameAndRootFormat: 'as-provided', port: 3001, }); - const project = readProjectConfiguration(tree, 'my-worker-app'); - expect(project.root).toEqual('my-worker-app'); + const project = readProjectConfiguration(tree, 'myWorkerApp'); + expect(project.root).toEqual('myWorkerApp'); expect(project.targets).toEqual( expect.objectContaining({ serve: { @@ -47,11 +49,13 @@ describe('app', () => { it('should update tags', async () => { await applicationGenerator(tree, { name: 'myWorkerApp', + directory: 'myWorkerApp', + projectNameAndRootFormat: 'as-provided', tags: 'one,two', }); const projects = Object.fromEntries(getProjects(tree)); expect(projects).toMatchObject({ - 'my-worker-app': { + myWorkerApp: { tags: ['one', 'two'], }, }); @@ -60,11 +64,16 @@ describe('app', () => { it('should generate files', async () => { await applicationGenerator(tree, { name: 'myWorkerApp', + directory: 'myWorkerApp', + projectNameAndRootFormat: 'as-provided', }); - expect(tree.exists('my-worker-app/src/index.ts')).toBeTruthy(); - expect(tree.exists('my-worker-app/src/index.test.ts')).toBeTruthy(); + expect(tree.exists('myWorkerApp/src/index.ts')).toBeTruthy(); + expect(tree.exists('myWorkerApp/src/index.test.ts')).toBeTruthy(); + expect( + tree.exists('myWorkerApp/src/index.integration.test.ts') + ).toBeTruthy(); - const tsconfig = readJson(tree, 'my-worker-app/tsconfig.json'); + const tsconfig = readJson(tree, 'myWorkerApp/tsconfig.json'); expect(tsconfig).toMatchInlineSnapshot(` { "compilerOptions": { @@ -77,11 +86,14 @@ describe('app', () => { { "path": "./tsconfig.app.json", }, + { + "path": "./tsconfig.spec.json", + }, ], } `); - const tsconfigApp = readJson(tree, 'my-worker-app/tsconfig.app.json'); + const tsconfigApp = readJson(tree, 'myWorkerApp/tsconfig.app.json'); expect(tsconfigApp.compilerOptions.outDir).toEqual('../dist/out-tsc'); expect(tsconfigApp.compilerOptions.target).toEqual('es2021'); expect(tsconfigApp.extends).toEqual('./tsconfig.json'); @@ -90,7 +102,7 @@ describe('app', () => { 'src/**/*.spec.ts', 'src/**/*.test.ts', ]); - const eslintrc = readJson(tree, 'my-worker-app/.eslintrc.json'); + const eslintrc = readJson(tree, 'myWorkerApp/.eslintrc.json'); expect(eslintrc).toMatchInlineSnapshot(` { "extends": [ @@ -131,29 +143,28 @@ describe('app', () => { it('should not generate files when template is none', async () => { await applicationGenerator(tree, { name: 'myWorkerApp', + directory: 'myWorkerApp', + projectNameAndRootFormat: 'as-provided', template: 'none', }); - expect(tree.exists('my-worker-app/src/index.ts')).toBeFalsy(); - expect(tree.exists('my-worker-app/src/index.test.ts')).toBeFalsy(); + expect(tree.exists('myWorkerApp/src/index.ts')).toBeFalsy(); + expect(tree.exists('myWorkerApp/src/index.test.ts')).toBeFalsy(); + expect( + tree.exists('myWorkerApp/src/index.integration.test.ts') + ).toBeFalsy(); }); - // TODO: Uncomment when Jest support is added back - // it('should not generate import vitest when testRunner is jest', async () => { - // await applicationGenerator(tree, { - // name: 'myWorkerApp', - // unitTestRunner: 'jest', - // }); - // expect( - // tree.read(`my-worker-app/src/index.test.ts`, 'utf-8') - // ).not.toContain('vitest'); - // }); - it('should not have test files if the unitTestRunner is none', async () => { await applicationGenerator(tree, { name: 'myWorkerApp', + directory: 'myWorkerApp', + projectNameAndRootFormat: 'as-provided', unitTestRunner: 'none', }); - expect(tree.exists(`my-worker-app/src/index.test.ts`)).toBeFalsy(); + expect(tree.exists(`myWorkerApp/src/index.test.ts`)).toBeFalsy(); + expect( + tree.exists(`myWorkerApp/src/index.integration.test.ts`) + ).toBeFalsy(); }); it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { @@ -161,54 +172,98 @@ describe('app', () => { await applicationGenerator(tree, { name: 'myWorkerApp', + directory: 'myWorkerApp', + projectNameAndRootFormat: 'as-provided', }); - const tsconfig = readJson(tree, 'my-worker-app/tsconfig.json'); + const tsconfig = readJson(tree, 'myWorkerApp/tsconfig.json'); expect(tsconfig.extends).toBe('../tsconfig.json'); }); it('should create the common configuration files', async () => { await applicationGenerator(tree, { name: 'myWorkerApp', + directory: 'myWorkerApp', + projectNameAndRootFormat: 'as-provided', }); - expect(tree.exists('my-worker-app/.gitignore')).toBeTruthy(); - expect(tree.exists('my-worker-app/package.json')).toBeTruthy(); - expect(tree.read('my-worker-app/wrangler.toml', 'utf-8')) + expect(tree.exists('myWorkerApp/.gitignore')).toBeTruthy(); + expect(tree.exists('myWorkerApp/package.json')).toBeTruthy(); + expect(tree.read('myWorkerApp/wrangler.toml', 'utf-8')) .toMatchInlineSnapshot(` - "name = "my-worker-app" - compatibility_date = "2023-07-31" + "name = "myWorkerApp" + compatibility_date = "2024-01-01" + compatibility_flags = ["nodejs_compat"] main = "src/index.ts" " `); }); + + it('should generate a modified vite config file to allow the poolOptions when vitest is the test runner', async () => { + await applicationGenerator(tree, { + name: 'myWorkerApp', + directory: 'myWorkerApp', + projectNameAndRootFormat: 'as-provided', + }); + expect(tree.read('myWorkerApp/vite.config.ts', 'utf-8')) + .toMatchInlineSnapshot(` + "import { defineConfig } from 'vite'; + + import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; + + export default defineConfig({ + root: __dirname, + cacheDir: '../node_modules/.vite/myWorkerApp', + + plugins: [nxViteTsPaths()], + + // Uncomment this if you are using workers. + // worker: { + // plugins: [ nxViteTsPaths() ], + // }, + + test: { + watch: false, + globals: true, + cache: { dir: '../node_modules/.vitest/myWorkerApp' }, + environment: 'node', + include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + reporters: ['default'], + coverage: { reportsDirectory: '../coverage/myWorkerApp', provider: 'v8' }, + }, + }); + " + `); + }); }); describe('nested', () => { it('should update project config', async () => { await applicationGenerator(tree, { name: 'myWorkerApp', - directory: 'myDir', + projectNameAndRootFormat: 'as-provided', + directory: 'myDir/myWorkerApp', }); - const project = readProjectConfiguration(tree, 'my-dir-my-worker-app'); + const project = readProjectConfiguration(tree, 'myWorkerApp'); - expect(project.root).toEqual('my-dir/my-worker-app'); + expect(project.root).toEqual('myDir/myWorkerApp'); - expect(() => - readProjectConfiguration(tree, 'my-dir-my-worker-app-e2e') - ).toThrow(/Cannot find/); + expect(() => readProjectConfiguration(tree, 'myWorkerApp-e2e')).toThrow( + /Cannot find/ + ); }); it('should update tags', async () => { await applicationGenerator(tree, { name: 'myWorkerApp', - directory: 'myDir', + projectNameAndRootFormat: 'as-provided', + directory: 'myDir/myWorkerApp', tags: 'one,two', }); const projects = Object.fromEntries(getProjects(tree)); expect(projects).toMatchObject({ - 'my-dir-my-worker-app': { + myWorkerApp: { tags: ['one', 'two'], }, }); @@ -223,13 +278,15 @@ describe('app', () => { await applicationGenerator(tree, { name: 'myWorkerApp', - directory: 'myDir', + projectNameAndRootFormat: 'as-provided', + directory: 'myDir/myWorkerApp', }); // Make sure these exist [ - 'my-dir/my-worker-app/src/index.ts', - 'my-dir/my-worker-app/src/index.test.ts', + 'myDir/myWorkerApp/src/index.ts', + 'myDir/myWorkerApp/src/index.test.ts', + 'myDir/myWorkerApp/src/index.integration.test.ts', ].forEach((path) => { expect(tree.exists(path)).toBeTruthy(); }); @@ -237,22 +294,26 @@ describe('app', () => { // Make sure these have properties [ { - path: 'my-dir/my-worker-app/tsconfig.app.json', + path: 'myDir/myWorkerApp/tsconfig.app.json', lookupFn: (json) => json.compilerOptions.outDir, expectedValue: '../../dist/out-tsc', }, { - path: 'my-dir/my-worker-app/tsconfig.app.json', + path: 'myDir/myWorkerApp/tsconfig.app.json', lookupFn: (json) => json.compilerOptions.target, expectedValue: 'es2021', }, { - path: 'my-dir/my-worker-app/tsconfig.app.json', + path: 'myDir/myWorkerApp/tsconfig.app.json', lookupFn: (json) => json.compilerOptions.types, - expectedValue: ['node', '@cloudflare/workers-types'], + expectedValue: [ + 'node', + '@cloudflare/workers-types/experimental', + '@cloudflare/vitest-pool-workers', + ], }, { - path: 'my-dir/my-worker-app/tsconfig.app.json', + path: 'myDir/myWorkerApp/tsconfig.app.json', lookupFn: (json) => json.exclude, expectedValue: [ 'jest.config.ts', @@ -261,7 +322,7 @@ describe('app', () => { ], }, { - path: 'my-dir/my-worker-app/.eslintrc.json', + path: 'myDir/myWorkerApp/.eslintrc.json', lookupFn: (json) => json.extends, expectedValue: ['../../.eslintrc.json'], }, @@ -273,117 +334,44 @@ describe('app', () => { it('should not generate test configuration', async () => { await applicationGenerator(tree, { name: 'myWorkerApp', + projectNameAndRootFormat: 'as-provided', + directory: 'myWorkerApp', unitTestRunner: 'none', }); expect(tree.exists('jest.config.ts')).toBeFalsy(); - expect(tree.exists('my-worker-app/src/test-setup.ts')).toBeFalsy(); - expect(tree.exists('my-worker-app/src/test.ts')).toBeFalsy(); - expect(tree.exists('my-worker-app/tsconfig.spec.json')).toBeFalsy(); - expect(tree.exists('my-worker-app/jest.config.ts')).toBeFalsy(); - const project = readProjectConfiguration(tree, 'my-worker-app'); + expect(tree.exists('myWorkerApp/src/test-setup.ts')).toBeFalsy(); + expect(tree.exists('myWorkerApp/src/test.ts')).toBeFalsy(); + expect(tree.exists('myWorkerApp/tsconfig.spec.json')).toBeFalsy(); + expect(tree.exists('myWorkerApp/jest.config.ts')).toBeFalsy(); + expect(tree.exists('myWorkerApp/vite.config.ts')).toBeFalsy(); + const project = readProjectConfiguration(tree, 'myWorkerApp'); expect(project.targets.test).toBeUndefined(); - const packageJSON = devkit.readJson(tree, 'my-worker-app/package.json'); - expect(packageJSON.scripts.test).toBeUndefined(); expect(project.targets.lint).toMatchInlineSnapshot(`undefined`); }); }); - // describe('--frontendProject', () => { - // it('should configure proxy', async () => { - // await angularApplicationGenerator(tree, { name: 'my-frontend' }); - // - // await applicationGenerator(tree, { - // name: 'myWorkerApp', - // frontendProject: 'my-frontend', - // }); - // - // expect(tree.exists('my-frontend/proxy.conf.json')).toBeTruthy(); - // const project = readProjectConfiguration(tree, 'my-frontend'); - // const serve = project.targets.serve; - // expect(serve.options.proxyConfig).toEqual('my-frontend/proxy.conf.json'); - // }); - // - // it('should configure proxies for multiple node projects with the same frontend app', async () => { - // await angularApplicationGenerator(tree, { name: 'my-frontend' }); - // - // await applicationGenerator(tree, { - // name: 'cart', - // frontendProject: 'my-frontend', - // }); - // - // await applicationGenerator(tree, { - // name: 'billing', - // frontendProject: 'my-frontend', - // }); - // - // expect(tree.exists('my-frontend/proxy.conf.json')).toBeTruthy(); - // - // expect(readJson(tree, 'my-frontend/proxy.conf.json')).toEqual({ - // '/api': { target: 'http://localhost:3000', secure: false }, - // '/billing-api': { target: 'http://localhost:3000', secure: false }, - // }); - // }); - // - // it('should work with unnormalized project names', async () => { - // await angularApplicationGenerator(tree, { name: 'myFrontend' }); - // - // await applicationGenerator(tree, { - // name: 'myWorkerApp', - // frontendProject: 'myFrontend', - // }); - // - // expect(tree.exists('my-frontend/proxy.conf.json')).toBeTruthy(); - // const project = readProjectConfiguration(tree, 'my-frontend'); - // const serve = project.targets.serve; - // expect(serve.options.proxyConfig).toEqual('my-frontend/proxy.conf.json'); - // }); - // }); - - // TODO: Uncomment when jest support is added back - // describe('--swcJest', () => { - // it('should use @swc/jest for jest', async () => { - // await applicationGenerator(tree, { - // name: 'myWorkerApp', - // tags: 'one,two', - // swcJest: true, - // unitTestRunner: 'jest', - // } as Schema); - // - // expect(tree.read(`my-worker-app/jest.config.ts`, 'utf-8')) - // .toMatchInlineSnapshot(` - // "/* eslint-disable */ - // export default { - // displayName: 'my-worker-app', - // preset: '../jest.preset.js', - // testEnvironment: 'node', - // transform: { - // '^.+\\\\.[tj]s$': '@swc/jest', - // }, - // moduleFileExtensions: ['ts', 'js', 'html'], - // coverageDirectory: '../coverage/my-worker-app', - // }; - // " - // `); - // }); - // }); - describe('--js flag', () => { it('should generate js files instead of ts files', async () => { await applicationGenerator(tree, { name: 'myWorkerApp', + projectNameAndRootFormat: 'as-provided', + directory: 'myWorkerApp', js: true, } as Schema); - expect(tree.exists('my-worker-app/src/index.js')).toBeTruthy(); - expect(tree.exists('my-worker-app/src/index.test.js')).toBeTruthy(); + expect(tree.exists('myWorkerApp/src/index.js')).toBeTruthy(); + expect(tree.exists('myWorkerApp/src/index.test.js')).toBeTruthy(); + expect( + tree.exists('myWorkerApp/src/index.integration.test.js') + ).toBeTruthy(); - const tsConfig = readJson(tree, 'my-worker-app/tsconfig.json'); + const tsConfig = readJson(tree, 'myWorkerApp/tsconfig.json'); expect(tsConfig.compilerOptions).toEqual({ allowJs: true, esModuleInterop: true, }); - const tsConfigApp = readJson(tree, 'my-worker-app/tsconfig.app.json'); + const tsConfigApp = readJson(tree, 'myWorkerApp/tsconfig.app.json'); expect(tsConfigApp.include).toEqual(['src/**/*.ts', 'src/**/*.js']); expect(tsConfigApp.exclude).toEqual([ 'jest.config.ts', @@ -397,9 +385,11 @@ describe('app', () => { it('should add project config', async () => { await applicationGenerator(tree, { name: 'myWorkerApp', + projectNameAndRootFormat: 'as-provided', + directory: 'myWorkerApp', js: true, } as Schema); - const project = readProjectConfiguration(tree, 'my-worker-app'); + const project = readProjectConfiguration(tree, 'myWorkerApp'); const buildTarget = project.targets.build; const serveTarget = project.targets.serve; @@ -410,26 +400,31 @@ describe('app', () => { it('should generate js files for nested libs as well', async () => { await applicationGenerator(tree, { name: 'myWorkerApp', - directory: 'myDir', + projectNameAndRootFormat: 'as-provided', + directory: 'myDir/myWorkerApp', js: true, } as Schema); - expect(tree.exists('my-dir/my-worker-app/src/index.js')).toBeTruthy(); + expect(tree.exists('myDir/myWorkerApp/src/index.js')).toBeTruthy(); + expect(tree.exists('myDir/myWorkerApp/src/index.test.js')).toBeTruthy(); expect( - tree.exists('my-dir/my-worker-app/src/index.test.js') + tree.exists('myDir/myWorkerApp/src/index.integration.test.js') ).toBeTruthy(); }); it('should create the common configuration files', async () => { await applicationGenerator(tree, { name: 'myWorkerApp', + projectNameAndRootFormat: 'as-provided', + directory: 'myWorkerApp', js: true, }); - expect(tree.exists('my-worker-app/.gitignore')).toBeTruthy(); - expect(tree.exists('my-worker-app/package.json')).toBeTruthy(); - expect(tree.read('my-worker-app/wrangler.toml', 'utf-8')) + expect(tree.exists('myWorkerApp/.gitignore')).toBeTruthy(); + expect(tree.exists('myWorkerApp/package.json')).toBeTruthy(); + expect(tree.read('myWorkerApp/wrangler.toml', 'utf-8')) .toMatchInlineSnapshot(` - "name = "my-worker-app" - compatibility_date = "2023-07-31" + "name = "myWorkerApp" + compatibility_date = "2024-01-01" + compatibility_flags = ["nodejs_compat"] main = "src/index.js" @@ -441,13 +436,16 @@ describe('app', () => { const accountId = 'fake40q5pchj988766d696c1ajek9mcd'; await applicationGenerator(tree, { name: 'myWorkerApp', + projectNameAndRootFormat: 'as-provided', + directory: 'myWorkerApp', js: true, accountId, }); - expect(tree.read('my-worker-app/wrangler.toml', 'utf-8')) + expect(tree.read('myWorkerApp/wrangler.toml', 'utf-8')) .toMatchInlineSnapshot(` - "name = "my-worker-app" - compatibility_date = "2023-07-31" + "name = "myWorkerApp" + compatibility_date = "2024-01-01" + compatibility_flags = ["nodejs_compat"] main = "src/index.js" account_id = "fake40q5pchj988766d696c1ajek9mcd" @@ -456,20 +454,15 @@ describe('app', () => { }); }); - describe('--pascalCaseFiles', () => { - it(`should notify that this flag doesn't do anything`, async () => { - await applicationGenerator(tree, { - name: 'myWorkerApp', - pascalCaseFiles: true, - } as Schema); - }); - }); - describe('--skipFormat', () => { it('should format files by default', async () => { jest.spyOn(devkit, 'formatFiles'); - await applicationGenerator(tree, { name: 'myWorkerApp' }); + await applicationGenerator(tree, { + name: 'myWorkerApp', + projectNameAndRootFormat: 'as-provided', + directory: 'myWorkerApp', + }); expect(devkit.formatFiles).toHaveBeenCalled(); }); @@ -479,6 +472,8 @@ describe('app', () => { await applicationGenerator(tree, { name: 'myWorkerApp', + projectNameAndRootFormat: 'as-provided', + directory: 'myWorkerApp', skipFormat: true, }); @@ -495,13 +490,16 @@ describe('app', () => { it('should generate the correct snippet of code', async () => { await applicationGenerator(tree, { name: 'api', + projectNameAndRootFormat: 'as-provided', + directory: 'api', template, + unitTestRunner: 'none', }); const project = readProjectConfiguration(tree, 'api'); expect(project.targets.test).toBeUndefined(); const packageJSON = devkit.readJson(tree, 'api/package.json'); - expect(packageJSON.scripts.test).toBeDefined(); + expect(packageJSON.name).toEqual('api'); if (checkFile) { expect(tree.exists(`api/src/index.ts`)).toBeTruthy(); diff --git a/packages/nx-cloudflare/src/generators/application/generator.ts b/packages/nx-cloudflare/src/generators/application/generator.ts index 610b39d0c..80719492a 100644 --- a/packages/nx-cloudflare/src/generators/application/generator.ts +++ b/packages/nx-cloudflare/src/generators/application/generator.ts @@ -4,7 +4,6 @@ import { formatFiles, generateFiles, GeneratorCallback, - names, readProjectConfiguration, runTasksInSerial, toJS, @@ -18,7 +17,6 @@ import { join } from 'path'; import initGenerator from '../init/generator'; import { vitestImports } from './utils/vitest-imports'; import { getAccountId } from './utils/get-account-id'; -import { vitestScript } from './utils/vitest-script'; import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils'; import { nxVersion } from '@nx/node/src/utils/versions'; @@ -58,6 +56,11 @@ export async function applicationGenerator(tree: Tree, schema: Schema) { coverageProvider: 'v8', skipFormat: true, testEnvironment: 'node', + poolOptions: { + workers: { + wrangler: { configPath: './wrangler.toml' }, + }, + }, }); tasks.push(vitestTask); createOrEditViteConfig( @@ -115,7 +118,8 @@ function updateTsAppConfig(tree: Tree, options: NormalizedSchema) { }; json.compilerOptions.types = [ ...json.compilerOptions.types, - '@cloudflare/workers-types', + '@cloudflare/workers-types/experimental', + '@cloudflare/vitest-pool-workers', ]; return json; } @@ -140,7 +144,6 @@ function addCloudflareFiles(tree: Tree, options: NormalizedSchema) { name: options.name, extension: options.js ? 'js' : 'ts', accountId: options.accountId ? getAccountId(options.accountId) : '', - vitestScript: options.unitTestRunner === 'vitest' ? vitestScript : '', } ); @@ -196,6 +199,7 @@ function addTargets(tree: Tree, options: NormalizedSchema) { function removeTestFiles(tree: Tree, options: NormalizedSchema) { tree.delete(join(options.appProjectRoot, 'src', 'index.test.ts')); + tree.delete(join(options.appProjectRoot, 'src', 'index.integration.test.ts')); } // Transform the options to the normalized schema. Loads defaults options. @@ -221,11 +225,10 @@ async function normalizeOptions( return { addPlugin: process.env.NX_ADD_PLUGINS !== 'false', ...options, - name: names(appProjectName).fileName, - frontendProject: options.frontendProject - ? names(options.frontendProject).fileName - : undefined, + name: appProjectName, + frontendProject: options.frontendProject, appProjectRoot, + projectNameAndRootFormat: options.projectNameAndRootFormat ?? 'as-provided', unitTestRunner: options.unitTestRunner ?? 'vitest', rootProject: options.rootProject ?? false, template: options.template ?? 'fetch-handler', diff --git a/packages/nx-cloudflare/src/generators/application/utils/vitest-script.ts b/packages/nx-cloudflare/src/generators/application/utils/vitest-script.ts deleted file mode 100644 index 854749129..000000000 --- a/packages/nx-cloudflare/src/generators/application/utils/vitest-script.ts +++ /dev/null @@ -1 +0,0 @@ -export const vitestScript = `"test": "vitest run"`; diff --git a/packages/nx-cloudflare/src/generators/init/generator.spec.ts b/packages/nx-cloudflare/src/generators/init/generator.spec.ts index 77a5a13f1..f40fb88f0 100644 --- a/packages/nx-cloudflare/src/generators/init/generator.spec.ts +++ b/packages/nx-cloudflare/src/generators/init/generator.spec.ts @@ -40,9 +40,15 @@ describe('init', () => { expect( packageJson.dependencies['@cloudflare/workers-types'] ).toBeUndefined(); + expect( + packageJson.dependencies['@cloudflare/vitest-pool-workers'] + ).toBeUndefined(); expect( packageJson.devDependencies['@cloudflare/workers-types'] ).toBeDefined(); + expect( + packageJson.devDependencies['@cloudflare/vitest-pool-workers'] + ).toBeDefined(); // keep existing packages expect(packageJson.devDependencies[existing]).toBeDefined(); expect(packageJson.dependencies[existing]).toBeDefined(); diff --git a/packages/nx-cloudflare/src/generators/init/generator.ts b/packages/nx-cloudflare/src/generators/init/generator.ts index e88e14500..64f4723ad 100644 --- a/packages/nx-cloudflare/src/generators/init/generator.ts +++ b/packages/nx-cloudflare/src/generators/init/generator.ts @@ -12,6 +12,7 @@ import { cloudflareWorkersTypeVersions, honoVersion, nxCloudflareVersion, + vitestPoolWorkersVersion, wranglerVersion, } from '../../utils/versions'; @@ -46,6 +47,7 @@ function updateDependencies(tree: Tree, schema: InitGeneratorSchema) { { wrangler: wranglerVersion, '@cloudflare/workers-types': cloudflareWorkersTypeVersions, + '@cloudflare/vitest-pool-workers': vitestPoolWorkersVersion, '@naxodev/nx-cloudflare': nxCloudflareVersion, } ); diff --git a/packages/nx-cloudflare/src/generators/library/__snapshots__/generator.spec.ts.snap b/packages/nx-cloudflare/src/generators/library/__snapshots__/generator.spec.ts.snap index d6c10fa65..b4a1d35b2 100644 --- a/packages/nx-cloudflare/src/generators/library/__snapshots__/generator.spec.ts.snap +++ b/packages/nx-cloudflare/src/generators/library/__snapshots__/generator.spec.ts.snap @@ -15,8 +15,7 @@ export default defineConfig({ nxViteTsPaths(), dts({ entryRoot: 'src', - tsConfigFilePath: path.join(__dirname, 'tsconfig.lib.json'), - skipDiagnostics: true, + tsconfigPath: path.join(__dirname, 'tsconfig.lib.json'), }), ], @@ -29,6 +28,7 @@ export default defineConfig({ // See: https://vitejs.dev/guide/build.html#library-mode build: { outDir: '../dist/my-lib', + emptyOutDir: true, reportCompressedSize: true, commonjsOptions: { transformMixedEsModules: true, @@ -49,9 +49,10 @@ export default defineConfig({ }, test: { + watch: false, globals: true, cache: { - dir: '../node_modules/.vitest', + dir: '../node_modules/.vitest/my-lib', }, environment: 'jsdom', include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], @@ -87,7 +88,12 @@ exports[`lib --bundler=vite should add build and test targets with vite and vite "compilerOptions": { "outDir": "../dist/out-tsc", "declaration": true, - "types": ["node", "vite/client", "@cloudflare/workers-types"] + "types": [ + "node", + "vite/client", + "@cloudflare/workers-types/experimental", + "@cloudflare/vitest-pool-workers" + ] }, "include": ["src/**/*.ts"], "exclude": ["vite.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] diff --git a/packages/nx-cloudflare/src/generators/library/generator.spec.ts b/packages/nx-cloudflare/src/generators/library/generator.spec.ts index 1fae0c8d0..f66d0ad89 100644 --- a/packages/nx-cloudflare/src/generators/library/generator.spec.ts +++ b/packages/nx-cloudflare/src/generators/library/generator.spec.ts @@ -17,7 +17,6 @@ describe('lib', () => { skipFormat: false, linter: 'eslint', js: false, - pascalCaseFiles: false, strict: true, config: 'project', }; @@ -39,7 +38,7 @@ describe('lib', () => { expect(readJson(tree, '/my-lib/package.json')).toEqual({ name: '@proj/my-lib', version: '0.0.1', - type: 'commonjs', + private: true, scripts: { build: "echo 'implement build'", test: "echo 'implement test'", @@ -433,29 +432,6 @@ describe('lib', () => { expect(tsconfigJson.compilerOptions.paths['@acme/core']).toBeDefined(); }); }); - - describe('--pascalCaseFiles', () => { - it('should generate files with upper case names', async () => { - await libraryGenerator(tree, { - ...defaultOptions, - name: 'my-lib', - pascalCaseFiles: true, - projectNameAndRootFormat: 'as-provided', - }); - expect(tree.exists('my-lib/src/lib/MyLib.ts')).toBeTruthy(); - }); - - it('should generate files with upper case names for nested libs as well', async () => { - await libraryGenerator(tree, { - ...defaultOptions, - name: 'my-lib', - directory: 'my-dir/my-lib', - pascalCaseFiles: true, - projectNameAndRootFormat: 'as-provided', - }); - expect(tree.exists('my-dir/my-lib/src/lib/MyLib.ts')).toBeTruthy(); - }); - }); }); describe('--linter', () => { @@ -765,35 +741,6 @@ describe('lib', () => { }); }); - describe('--bundler=rollup', () => { - it('should add build with rollup', async () => { - await libraryGenerator(tree, { - ...defaultOptions, - name: 'my-lib', - bundler: 'rollup', - unitTestRunner: 'none', - projectNameAndRootFormat: 'as-provided', - }); - - const project = readProjectConfiguration(tree, 'my-lib'); - expect(project.targets.build).toMatchObject({ - executor: '@nx/rollup:rollup', - }); - expect(readJson(tree, 'my-lib/.eslintrc.json').overrides).toContainEqual({ - files: ['*.json'], - parser: 'jsonc-eslint-parser', - rules: { - '@nx/dependency-checks': [ - 'error', - { - ignoredFiles: ['{projectRoot}/rollup.config.{js,ts,mjs,mts}'], - }, - ], - }, - }); - }); - }); - describe('--minimal', () => { it('should generate a README.md when minimal is set to false', async () => { await libraryGenerator(tree, { diff --git a/packages/nx-cloudflare/src/generators/library/generator.ts b/packages/nx-cloudflare/src/generators/library/generator.ts index c98a1da7d..0d8133b35 100644 --- a/packages/nx-cloudflare/src/generators/library/generator.ts +++ b/packages/nx-cloudflare/src/generators/library/generator.ts @@ -96,7 +96,8 @@ function updateTsLibConfig(tree: Tree, options: NormalizedSchema) { (json) => { json.compilerOptions.types = [ ...json.compilerOptions.types, - '@cloudflare/workers-types', + '@cloudflare/workers-types/experimental', + '@cloudflare/vitest-pool-workers', ]; return json; } diff --git a/packages/nx-cloudflare/src/generators/library/schema.d.ts b/packages/nx-cloudflare/src/generators/library/schema.d.ts index f80f41ded..34a9a786e 100644 --- a/packages/nx-cloudflare/src/generators/library/schema.d.ts +++ b/packages/nx-cloudflare/src/generators/library/schema.d.ts @@ -8,7 +8,6 @@ export interface NxCloudflareLibraryGeneratorSchema { skipFormat?: boolean; skipPackageJson?: boolean; skipTsConfig?: boolean; - pascalCaseFiles?: boolean; js?: boolean; strict?: boolean; publishable?: boolean; diff --git a/packages/nx-cloudflare/src/generators/library/schema.json b/packages/nx-cloudflare/src/generators/library/schema.json index a8c993147..c62bfb6da 100644 --- a/packages/nx-cloudflare/src/generators/library/schema.json +++ b/packages/nx-cloudflare/src/generators/library/schema.json @@ -59,12 +59,6 @@ "default": false, "x-priority": "internal" }, - "pascalCaseFiles": { - "type": "boolean", - "description": "Use pascal case file names.", - "alias": "P", - "default": false - }, "js": { "type": "boolean", "description": "Generate JavaScript files rather than TypeScript files.", @@ -101,7 +95,7 @@ "bundler": { "description": "The bundler to use. Choosing 'none' means this library is not buildable.", "type": "string", - "enum": ["swc", "tsc", "rollup", "vite", "esbuild", "none"], + "enum": ["swc", "tsc", "vite", "esbuild", "none"], "default": "tsc", "x-prompt": "Which bundler would you like to use to build the library? Choose 'none' to skip build setup.", "x-priority": "important" diff --git a/packages/nx-cloudflare/src/utils/versions.ts b/packages/nx-cloudflare/src/utils/versions.ts index 26f98f177..cdd9a00e5 100644 --- a/packages/nx-cloudflare/src/utils/versions.ts +++ b/packages/nx-cloudflare/src/utils/versions.ts @@ -4,3 +4,4 @@ export const nxCloudflareVersion = require('../../package.json').version; export const wranglerVersion = '^3.62.0'; export const cloudflareWorkersTypeVersions = '^4.20240512.0'; export const honoVersion = '^4.4.9'; +export const vitestPoolWorkersVersion = '^0.4.7'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4e39bada2..2e0c159c4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,6 +33,9 @@ importers: '@cloudflare/next-on-pages': specifier: ^1.12.0 version: 1.12.0(@cloudflare/workers-types@4.20240512.0)(vercel@34.2.0(@swc/core@1.5.7(@swc/helpers@0.5.11)))(wrangler@3.62.0(@cloudflare/workers-types@4.20240512.0)) + '@cloudflare/vitest-pool-workers': + specifier: ^0.4.7 + version: 0.4.7(@cloudflare/workers-types@4.20240512.0)(@vitest/runner@1.6.0)(@vitest/snapshot@1.6.0)(vitest@1.6.0(@edge-runtime/vm@3.2.0)(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@20.0.3)(less@4.1.3)(sass@1.77.2)(stylus@0.59.0)(terser@5.29.1)) '@cloudflare/workers-types': specifier: ^4.20240512.0 version: 4.20240512.0 @@ -1064,6 +1067,13 @@ packages: '@cloudflare/workers-types': optional: true + '@cloudflare/vitest-pool-workers@0.4.7': + resolution: {integrity: sha512-fX/qCPsQgi4w2xXpuCtz8LjvcANVC6KYpPDFO6UyhJ4q5F9VhN14CNesS8S8VWARzt8Q5rfVg9JlBysR3/8YQg==} + peerDependencies: + '@vitest/runner': 1.3.x - 1.5.x + '@vitest/snapshot': 1.3.x - 1.5.x + vitest: 1.3.x - 1.5.x + '@cloudflare/workerd-darwin-64@1.20240512.0': resolution: {integrity: sha512-VMp+CsSHFALQiBzPdQ5dDI4T1qwLu0mQ0aeKVNDosXjueN0f3zj/lf+mFil5/9jBbG3t4mG0y+6MMnalP9Lobw==} engines: {node: '>=16'} @@ -3377,6 +3387,9 @@ packages: bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + birpc@0.2.14: + resolution: {integrity: sha512-37FHE8rqsYM5JEKCnXFyHpBCzvgHEExwVVTq+nUmloInU7l8ezD1TpOhKpS8oe1DTYFqEK27rFZVKG43oTqXRA==} + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -4184,6 +4197,9 @@ packages: engines: {node: '>= 4.0.0'} hasBin: true + devalue@4.3.3: + resolution: {integrity: sha512-UH8EL6H2ifcY8TbD2QsxwCC/pr5xSwPvv85LrLXVihmHVC3T3YqTCIwnR5ak0yO1KYqlxrPVOA/JVZJYPy2ATg==} + diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -10562,6 +10578,25 @@ snapshots: - supports-color - utf-8-validate + '@cloudflare/vitest-pool-workers@0.4.7(@cloudflare/workers-types@4.20240512.0)(@vitest/runner@1.6.0)(@vitest/snapshot@1.6.0)(vitest@1.6.0(@edge-runtime/vm@3.2.0)(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@20.0.3)(less@4.1.3)(sass@1.77.2)(stylus@0.59.0)(terser@5.29.1))': + dependencies: + '@vitest/runner': 1.6.0 + '@vitest/snapshot': 1.6.0 + birpc: 0.2.14 + cjs-module-lexer: 1.3.1 + devalue: 4.3.3 + esbuild: 0.17.19 + miniflare: 3.20240620.0 + semver: 7.6.2 + vitest: 1.6.0(@edge-runtime/vm@3.2.0)(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@20.0.3)(less@4.1.3)(sass@1.77.2)(stylus@0.59.0)(terser@5.29.1) + wrangler: 3.62.0(@cloudflare/workers-types@4.20240512.0) + zod: 3.23.8 + transitivePeerDependencies: + - '@cloudflare/workers-types' + - bufferutil + - supports-color + - utf-8-validate + '@cloudflare/workerd-darwin-64@1.20240512.0': optional: true @@ -13788,6 +13823,8 @@ snapshots: dependencies: file-uri-to-path: 1.0.0 + birpc@0.2.14: {} + bl@4.1.0: dependencies: buffer: 5.7.1 @@ -14611,6 +14648,8 @@ snapshots: transitivePeerDependencies: - supports-color + devalue@4.3.3: {} + diff-sequences@29.6.3: {} diff@4.0.2: {} diff --git a/project.json b/project.json index c56720295..cf3938e56 100644 --- a/project.json +++ b/project.json @@ -2,6 +2,9 @@ "name": "@naxodev/oss", "$schema": "node_modules/nx/schemas/project-schema.json", "targets": { + "test": { + "command": "echo 'no test for root project'" + }, "local-registry": { "executor": "@nx/js:verdaccio", "options": { diff --git a/tools/scripts/unit-test-setup.js b/tools/scripts/unit-test-setup.js new file mode 100644 index 000000000..8436818b0 --- /dev/null +++ b/tools/scripts/unit-test-setup.js @@ -0,0 +1,34 @@ +module.exports = () => { + /** + * When the daemon is enabled during unit tests, + * and the daemon is already running, the daemon-client.ts + * code will be used, but it will hit the already running + * daemon which is from the installed version of Nx. + * + * In the vast majority of cases, this is fine. However, + * if a new message type has been added to the daemon in + * the source code, and isn't yet in the installed version, + * any test that hits that codepath will fail. This is because + * the installed version of the daemon doesn't know how to + * handle the new message type. + * + * To prevent this, we disable the daemon during unit tests. + */ + process.env.NX_DAEMON = 'false'; + + /** + * When `createProjectGraphAsync` is called during tests, + * if its not mocked, it will return the Nx repo's project + * graph. We don't want any unit tests to depend on the structure + * of the Nx repo, so we mock it to return an empty project graph. + */ + jest.doMock('@nx/devkit', () => ({ + ...jest.requireActual('@nx/devkit'), + createProjectGraphAsync: jest.fn().mockImplementation(async () => { + return { + nodes: {}, + dependencies: {}, + }; + }), + })); +};