feat(vite): add angular option to vitest generator (#29055)
## Current Behavior `@nx/vite:vitest` generator does not provide Angular support in the `uiFramework` options. ## Expected Behavior `angular` option should generate the vitest configuration just like `@nx/angular:application` and `@nx/angular/library` do.
This commit is contained in:
parent
5068a26ed6
commit
77dc090a75
@ -16,8 +16,7 @@
|
||||
},
|
||||
"uiFramework": {
|
||||
"type": "string",
|
||||
"enum": ["react", "none"],
|
||||
"default": "none",
|
||||
"enum": ["angular", "react", "none"],
|
||||
"description": "UI framework to use with vitest."
|
||||
},
|
||||
"inSourceTests": {
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
ensurePackage,
|
||||
joinPathFragments,
|
||||
type Tree,
|
||||
} from '@nx/devkit';
|
||||
import { analogVitestAngular, nxVersion } from '../../utils/versions';
|
||||
import { ensurePackage, type Tree } from '@nx/devkit';
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
|
||||
export type AddVitestOptions = {
|
||||
name: string;
|
||||
@ -17,69 +12,16 @@ export async function addVitest(
|
||||
tree: Tree,
|
||||
options: AddVitestOptions
|
||||
): Promise<void> {
|
||||
if (!options.skipPackageJson) {
|
||||
addDependenciesToPackageJson(
|
||||
tree,
|
||||
{},
|
||||
{
|
||||
'@analogjs/vitest-angular': analogVitestAngular,
|
||||
'@analogjs/vite-plugin-angular': analogVitestAngular,
|
||||
},
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
const { createOrEditViteConfig, vitestGenerator } = ensurePackage<
|
||||
typeof import('@nx/vite')
|
||||
>('@nx/vite', nxVersion);
|
||||
|
||||
const relativeTestSetupPath = joinPathFragments('src', 'test-setup.ts');
|
||||
|
||||
const setupFile = joinPathFragments(
|
||||
options.projectRoot,
|
||||
relativeTestSetupPath
|
||||
const { vitestGenerator } = ensurePackage<typeof import('@nx/vite')>(
|
||||
'@nx/vite',
|
||||
nxVersion
|
||||
);
|
||||
if (!tree.exists(setupFile)) {
|
||||
tree.write(
|
||||
setupFile,
|
||||
`import '@analogjs/vitest-angular/setup-zone';
|
||||
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting,
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting()
|
||||
);
|
||||
`
|
||||
);
|
||||
|
||||
await vitestGenerator(tree, {
|
||||
project: options.name,
|
||||
uiFramework: 'none',
|
||||
skipViteConfig: true,
|
||||
testEnvironment: 'jsdom',
|
||||
coverageProvider: 'v8',
|
||||
addPlugin: false,
|
||||
});
|
||||
|
||||
createOrEditViteConfig(
|
||||
tree,
|
||||
{
|
||||
project: options.name,
|
||||
includeLib: false,
|
||||
includeVitest: true,
|
||||
inSourceTests: false,
|
||||
imports: [`import angular from '@analogjs/vite-plugin-angular'`],
|
||||
plugins: ['angular()'],
|
||||
setupFile: relativeTestSetupPath,
|
||||
useEsmExtension: true,
|
||||
},
|
||||
true
|
||||
);
|
||||
}
|
||||
await vitestGenerator(tree, {
|
||||
project: options.name,
|
||||
uiFramework: 'angular',
|
||||
testEnvironment: 'jsdom',
|
||||
coverageProvider: 'v8',
|
||||
addPlugin: false,
|
||||
});
|
||||
}
|
||||
|
||||
@ -14,10 +14,7 @@ export type PackageVersionNames =
|
||||
|
||||
export type VersionMap = {
|
||||
angularV17: Record<
|
||||
Exclude<
|
||||
CompatPackageVersionNames,
|
||||
'analogVitestAngular' | 'typescriptEslintVersion'
|
||||
>,
|
||||
Exclude<CompatPackageVersionNames, 'typescriptEslintVersion'>,
|
||||
string
|
||||
>;
|
||||
angularV18: Record<CompatPackageVersionNames, string>;
|
||||
@ -80,7 +77,6 @@ export const backwardCompatibleVersions: VersionMap = {
|
||||
jestPresetAngularVersion: '~14.1.0',
|
||||
typesNodeVersion: '18.16.9',
|
||||
jasmineMarblesVersion: '^0.9.2',
|
||||
analogVitestAngular: '~1.9.1',
|
||||
jsoncEslintParserVersion: '^2.1.0',
|
||||
},
|
||||
};
|
||||
|
||||
@ -28,6 +28,5 @@ export const tsNodeVersion = '10.9.1';
|
||||
export const jestPresetAngularVersion = '~14.4.0';
|
||||
export const typesNodeVersion = '18.16.9';
|
||||
export const jasmineMarblesVersion = '^0.9.2';
|
||||
export const analogVitestAngular = '~1.10.0';
|
||||
|
||||
export const jsoncEslintParserVersion = '^2.1.0';
|
||||
|
||||
@ -1,5 +1,52 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`vitest generator angular should generate src/test-setup.ts 1`] = `
|
||||
"import '@analogjs/vitest-angular/setup-zone';
|
||||
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting,
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting()
|
||||
);
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`vitest generator angular should generate vite.config.mts 1`] = `
|
||||
"/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import angular from '@analogjs/vite-plugin-angular';
|
||||
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||
import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/apps/my-test-angular-app',
|
||||
plugins: [angular(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
test: {
|
||||
watch: false,
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
setupFiles: ['src/test-setup.ts'],
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../../coverage/apps/my-test-angular-app',
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`vitest generator insourceTests should add the insourceSource option in the vite config 1`] = `
|
||||
"/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
@ -39,6 +86,36 @@ exports[`vitest generator tsconfig should add vitest.workspace.ts at the root 1`
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`vitest generator vite.config for libs should create correct vite.config.ts file for non buildable libs 1`] = `
|
||||
"/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||
import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/libs/react-lib-nonb-jest',
|
||||
plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
test: {
|
||||
watch: false,
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../../coverage/libs/react-lib-nonb-jest',
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`vitest generator vite.config should create correct vite.config.ts file for apps 1`] = `
|
||||
"/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
@ -68,33 +145,3 @@ export default defineConfig({
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`vitest generator vite.config should create correct vite.config.ts file for non buildable libs 1`] = `
|
||||
"/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||
import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/libs/react-lib-nonb-jest',
|
||||
plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
test: {
|
||||
watch: false,
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../../coverage/libs/react-lib-nonb-jest',
|
||||
provider: 'v8',
|
||||
},
|
||||
},
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
export interface VitestGeneratorSchema {
|
||||
project: string;
|
||||
uiFramework: 'react' | 'none';
|
||||
uiFramework?: 'angular' | 'react' | 'none';
|
||||
coverageProvider: 'v8' | 'istanbul' | 'custom';
|
||||
inSourceTests?: boolean;
|
||||
skipViteConfig?: boolean;
|
||||
|
||||
@ -15,8 +15,7 @@
|
||||
},
|
||||
"uiFramework": {
|
||||
"type": "string",
|
||||
"enum": ["react", "none"],
|
||||
"default": "none",
|
||||
"enum": ["angular", "react", "none"],
|
||||
"description": "UI framework to use with vitest."
|
||||
},
|
||||
"inSourceTests": {
|
||||
|
||||
@ -27,7 +27,11 @@ import {
|
||||
} from '../../utils/versions';
|
||||
import initGenerator from '../init/init';
|
||||
import { VitestGeneratorSchema } from './schema';
|
||||
import { detectUiFramework } from '../../utils/detect-ui-framework';
|
||||
|
||||
/**
|
||||
* @param hasPlugin some frameworks (e.g. Nuxt) provide their own plugin. Their generators handle the plugin detection.
|
||||
*/
|
||||
export function vitestGenerator(
|
||||
tree: Tree,
|
||||
schema: VitestGeneratorSchema,
|
||||
@ -56,6 +60,8 @@ export async function vitestGeneratorInternal(
|
||||
schema.project
|
||||
);
|
||||
const projectType = schema.projectType ?? _projectType;
|
||||
const uiFramework =
|
||||
schema.uiFramework ?? (await detectUiFramework(schema.project));
|
||||
const isRootProject = root === '.';
|
||||
|
||||
tasks.push(await jsInitGenerator(tree, { ...schema, skipFormat: true }));
|
||||
@ -64,22 +70,49 @@ export async function vitestGeneratorInternal(
|
||||
addPlugin: schema.addPlugin,
|
||||
});
|
||||
tasks.push(initTask);
|
||||
tasks.push(ensureDependencies(tree, schema));
|
||||
tasks.push(ensureDependencies(tree, { ...schema, uiFramework }));
|
||||
|
||||
const nxJson = readNxJson(tree);
|
||||
const hasPluginCheck = nxJson.plugins?.some(
|
||||
(p) =>
|
||||
(typeof p === 'string'
|
||||
? p === '@nx/vite/plugin'
|
||||
: p.plugin === '@nx/vite/plugin') || hasPlugin
|
||||
);
|
||||
if (!hasPluginCheck) {
|
||||
const testTarget = schema.testTarget ?? 'test';
|
||||
addOrChangeTestTarget(tree, schema, testTarget);
|
||||
}
|
||||
addOrChangeTestTarget(tree, schema, hasPlugin);
|
||||
|
||||
if (!schema.skipViteConfig) {
|
||||
if (schema.uiFramework === 'react') {
|
||||
if (uiFramework === 'angular') {
|
||||
const relativeTestSetupPath = joinPathFragments('src', 'test-setup.ts');
|
||||
|
||||
const setupFile = joinPathFragments(root, relativeTestSetupPath);
|
||||
if (!tree.exists(setupFile)) {
|
||||
tree.write(
|
||||
setupFile,
|
||||
`import '@analogjs/vitest-angular/setup-zone';
|
||||
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting,
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting()
|
||||
);
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
createOrEditViteConfig(
|
||||
tree,
|
||||
{
|
||||
project: schema.project,
|
||||
includeLib: false,
|
||||
includeVitest: true,
|
||||
inSourceTests: false,
|
||||
imports: [`import angular from '@analogjs/vite-plugin-angular'`],
|
||||
plugins: ['angular()'],
|
||||
setupFile: relativeTestSetupPath,
|
||||
useEsmExtension: true,
|
||||
},
|
||||
true
|
||||
);
|
||||
} else if (uiFramework === 'react') {
|
||||
createOrEditViteConfig(
|
||||
tree,
|
||||
{
|
||||
|
||||
@ -1,29 +1,79 @@
|
||||
import 'nx/src/internal-testing-utils/mock-project-graph';
|
||||
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { Tree } from '@nx/devkit';
|
||||
|
||||
import generator from './vitest-generator';
|
||||
import { VitestGeneratorSchema } from './schema';
|
||||
import {
|
||||
createProjectGraphAsync,
|
||||
readJson,
|
||||
Tree,
|
||||
updateJson,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
|
||||
import {
|
||||
mockAngularAppGenerator,
|
||||
mockReactAppGenerator,
|
||||
mockReactLibNonBuildableJestTestRunnerGenerator,
|
||||
} from '../../utils/test-utils';
|
||||
import { VitestGeneratorSchema } from './schema';
|
||||
import generator from './vitest-generator';
|
||||
|
||||
describe('vitest generator', () => {
|
||||
let appTree: Tree;
|
||||
const options: VitestGeneratorSchema = {
|
||||
project: 'my-test-react-app',
|
||||
uiFramework: 'react',
|
||||
coverageProvider: 'v8',
|
||||
addPlugin: true,
|
||||
};
|
||||
|
||||
describe('test target', () => {
|
||||
it('should fail if test target is already defined', async () => {
|
||||
const { runGenerator } = setUpAngularWorkspace();
|
||||
|
||||
await expect(
|
||||
runGenerator({
|
||||
addPlugin: false,
|
||||
})
|
||||
).rejects.toThrow('Target "test" already exists in the project.');
|
||||
});
|
||||
|
||||
it('should not add test target to the project', async () => {
|
||||
const { runGenerator, tree } = setUpAngularWorkspace();
|
||||
|
||||
updateJson(tree, 'apps/my-test-angular-app/project.json', (json) => {
|
||||
delete json.targets.test;
|
||||
return json;
|
||||
});
|
||||
|
||||
await runGenerator();
|
||||
|
||||
expect(
|
||||
readJson(tree, 'apps/my-test-angular-app/project.json').targets.test
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should add test target to the project if plugin is not used', async () => {
|
||||
const { runGenerator, tree } = setUpAngularWorkspace();
|
||||
|
||||
updateJson(tree, 'apps/my-test-angular-app/project.json', (json) => {
|
||||
delete json.targets.test;
|
||||
return json;
|
||||
});
|
||||
|
||||
await runGenerator({
|
||||
addPlugin: false,
|
||||
});
|
||||
|
||||
expect(
|
||||
readJson(tree, 'apps/my-test-angular-app/project.json').targets.test
|
||||
.executor
|
||||
).toBe('@nx/vite:test');
|
||||
});
|
||||
});
|
||||
|
||||
describe('tsconfig', () => {
|
||||
beforeAll(async () => {
|
||||
appTree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
mockReactAppGenerator(appTree);
|
||||
await generator(appTree, options);
|
||||
const { runGenerator, tree } = setUpReactWorkspace();
|
||||
appTree = tree;
|
||||
await runGenerator();
|
||||
});
|
||||
|
||||
it('should add vitest.workspace.ts at the root', async () => {
|
||||
@ -80,18 +130,61 @@ describe('vitest generator', () => {
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('vite.config', () => {
|
||||
beforeAll(async () => {
|
||||
const { runGenerator, tree } = setUpReactWorkspace();
|
||||
appTree = tree;
|
||||
await runGenerator();
|
||||
});
|
||||
|
||||
it('should add @nx/vite dependency', async () => {
|
||||
const { devDependencies } = readJson(appTree, 'package.json');
|
||||
expect(devDependencies['@nx/vite']).toBeDefined();
|
||||
});
|
||||
|
||||
it('should create correct vite.config.ts file for apps', async () => {
|
||||
expect(
|
||||
appTree.read('apps/my-test-react-app/vite.config.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('vite.config for libs', () => {
|
||||
it('should create correct vite.config.ts file for non buildable libs', async () => {
|
||||
const { runGenerator, tree } = setUpReactWorkspace();
|
||||
|
||||
mockReactLibNonBuildableJestTestRunnerGenerator(tree);
|
||||
setProjectGraphDependencies('react-lib-nonb-jest', ['npm:react']);
|
||||
|
||||
await runGenerator({ project: 'react-lib-nonb-jest' });
|
||||
|
||||
expect(
|
||||
tree.read('libs/react-lib-nonb-jest/vite.config.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('insourceTests', () => {
|
||||
it('should add the insourceSource option in the vite config', async () => {
|
||||
const { runGenerator, tree } = setUpReactWorkspace();
|
||||
|
||||
await runGenerator({ inSourceTests: true });
|
||||
|
||||
expect(
|
||||
tree.read('apps/my-test-react-app/vite.config.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should add vitest/importMeta when inSourceTests is true', async () => {
|
||||
mockReactAppGenerator(appTree, 'my-test-react-app-2');
|
||||
await generator(appTree, {
|
||||
...options,
|
||||
inSourceTests: true,
|
||||
project: 'my-test-react-app-2',
|
||||
});
|
||||
const { tree, runGenerator } = setUpReactWorkspace();
|
||||
|
||||
await runGenerator({ inSourceTests: true });
|
||||
|
||||
const tsconfig = JSON.parse(
|
||||
appTree
|
||||
.read('apps/my-test-react-app-2/tsconfig.app.json')
|
||||
?.toString() ?? '{}'
|
||||
tree.read('apps/my-test-react-app/tsconfig.app.json')?.toString() ??
|
||||
'{}'
|
||||
);
|
||||
expect(tsconfig.compilerOptions.types).toMatchInlineSnapshot(`
|
||||
[
|
||||
@ -101,37 +194,131 @@ describe('vitest generator', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('vite.config', () => {
|
||||
describe('angular', () => {
|
||||
beforeAll(async () => {
|
||||
appTree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
mockReactAppGenerator(appTree);
|
||||
await generator(appTree, options);
|
||||
const { tree, runGenerator } = setUpAngularWorkspace();
|
||||
appTree = tree;
|
||||
await runGenerator();
|
||||
});
|
||||
it('should create correct vite.config.ts file for apps', async () => {
|
||||
|
||||
it('should generate vite.config.mts', async () => {
|
||||
expect(
|
||||
appTree.read('apps/my-test-react-app/vite.config.ts', 'utf-8')
|
||||
appTree.read('apps/my-test-angular-app/vite.config.mts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should create correct vite.config.ts file for non buildable libs', async () => {
|
||||
mockReactLibNonBuildableJestTestRunnerGenerator(appTree);
|
||||
await generator(appTree, { ...options, project: 'react-lib-nonb-jest' });
|
||||
expect(
|
||||
appTree.read('libs/react-lib-nonb-jest/vite.config.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
it('should not generate vite.config.ts', async () => {
|
||||
expect(appTree.exists('apps/my-test-angular-app/vite.config.ts')).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('insourceTests', () => {
|
||||
beforeAll(async () => {
|
||||
appTree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
mockReactAppGenerator(appTree);
|
||||
await generator(appTree, { ...options, inSourceTests: true });
|
||||
});
|
||||
it('should add the insourceSource option in the vite config', async () => {
|
||||
it('should generate src/test-setup.ts', async () => {
|
||||
expect(
|
||||
appTree.read('apps/my-test-react-app/vite.config.ts', 'utf-8')
|
||||
appTree.read('apps/my-test-angular-app/src/test-setup.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should exclude src/test-setup.ts in tsconfig.app.json', async () => {
|
||||
const tsConfig = readJson(
|
||||
appTree,
|
||||
'apps/my-test-angular-app/tsconfig.app.json'
|
||||
);
|
||||
expect(tsConfig.exclude).toContain('src/test-setup.ts');
|
||||
});
|
||||
|
||||
it('should include src/test-setup.ts in tsconfig.spec.json', async () => {
|
||||
const tsConfig = readJson(
|
||||
appTree,
|
||||
'apps/my-test-angular-app/tsconfig.spec.json'
|
||||
);
|
||||
expect(tsConfig.files).toContain('src/test-setup.ts');
|
||||
});
|
||||
|
||||
it('should add vitest-angular', async () => {
|
||||
const { devDependencies } = readJson(appTree, 'package.json');
|
||||
expect(devDependencies['@analogjs/vite-plugin-angular']).toBeDefined();
|
||||
expect(devDependencies['@analogjs/vitest-angular']).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function setUpAngularWorkspace() {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
const project = 'my-test-angular-app';
|
||||
|
||||
(
|
||||
createProjectGraphAsync as jest.MockedFn<typeof createProjectGraphAsync>
|
||||
).mockResolvedValue({
|
||||
dependencies: {
|
||||
[project]: [
|
||||
{
|
||||
type: 'static',
|
||||
source: project,
|
||||
target: 'npm:@angular/core',
|
||||
},
|
||||
],
|
||||
},
|
||||
nodes: {},
|
||||
});
|
||||
|
||||
mockAngularAppGenerator(tree);
|
||||
|
||||
return {
|
||||
async runGenerator({ addPlugin = true }: { addPlugin?: boolean } = {}) {
|
||||
await generator(tree, {
|
||||
project,
|
||||
coverageProvider: 'v8',
|
||||
addPlugin,
|
||||
});
|
||||
return tree;
|
||||
},
|
||||
tree,
|
||||
};
|
||||
}
|
||||
|
||||
function setUpReactWorkspace() {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
const appName = 'my-test-react-app';
|
||||
|
||||
mockReactAppGenerator(tree);
|
||||
|
||||
setProjectGraphDependencies(appName, ['npm:react']);
|
||||
|
||||
return {
|
||||
project: appName,
|
||||
async runGenerator({
|
||||
addPlugin = true,
|
||||
inSourceTests,
|
||||
project,
|
||||
}: {
|
||||
addPlugin?: boolean;
|
||||
inSourceTests?: boolean;
|
||||
project?: string;
|
||||
} = {}) {
|
||||
await generator(tree, {
|
||||
project: project ?? appName,
|
||||
coverageProvider: 'v8',
|
||||
addPlugin,
|
||||
inSourceTests,
|
||||
});
|
||||
return tree;
|
||||
},
|
||||
tree,
|
||||
};
|
||||
}
|
||||
|
||||
function setProjectGraphDependencies(project: string, dependencies: string[]) {
|
||||
(
|
||||
createProjectGraphAsync as jest.MockedFn<typeof createProjectGraphAsync>
|
||||
).mockResolvedValue({
|
||||
dependencies: {
|
||||
[project]: dependencies.map((target) => ({
|
||||
type: 'static',
|
||||
source: project,
|
||||
target,
|
||||
})),
|
||||
},
|
||||
nodes: {},
|
||||
});
|
||||
}
|
||||
|
||||
66
packages/vite/src/utils/detect-ui-framework.spec.ts
Normal file
66
packages/vite/src/utils/detect-ui-framework.spec.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { createProjectGraphAsync, ProjectGraph } from '@nx/devkit';
|
||||
import { detectUiFramework } from './detect-ui-framework';
|
||||
|
||||
jest.mock('@nx/devkit', () => ({
|
||||
...jest.requireActual('@nx/devkit'),
|
||||
createProjectGraphAsync: jest.fn().mockImplementation(() => {
|
||||
throw new Error('createProjectGraphAsync stub is not configured');
|
||||
}),
|
||||
}));
|
||||
|
||||
describe(detectUiFramework.name, () => {
|
||||
it.each([
|
||||
{ framework: 'angular', dependency: '@angular/core' },
|
||||
{ framework: 'angular', dependency: '@nx/angular' },
|
||||
{ framework: 'react', dependency: 'react' },
|
||||
{ framework: 'react', dependency: '@nx/react' },
|
||||
{ framework: 'none', dependency: null },
|
||||
])(
|
||||
`should detect $framework when dependency "$dependency" is present`,
|
||||
async ({ framework, dependency }) => {
|
||||
const { projectGraphFake } = setUp();
|
||||
|
||||
if (dependency) {
|
||||
await projectGraphFake.addNpmDependency('my-project', dependency);
|
||||
}
|
||||
|
||||
expect(await detectUiFramework('my-project')).toBe(framework);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
function setUp() {
|
||||
const projectGraphFake = new ProjectGraphFake();
|
||||
(
|
||||
createProjectGraphAsync as jest.MockedFn<typeof createProjectGraphAsync>
|
||||
).mockImplementation(async (options) =>
|
||||
projectGraphFake.createProjectGraphAsync(options)
|
||||
);
|
||||
|
||||
return { projectGraphFake };
|
||||
}
|
||||
|
||||
class ProjectGraphFake {
|
||||
private _graph: ProjectGraph = {
|
||||
dependencies: {},
|
||||
nodes: {},
|
||||
};
|
||||
|
||||
async createProjectGraphAsync(
|
||||
opts: { exitOnError: boolean; resetDaemonClient?: boolean } = {
|
||||
exitOnError: false,
|
||||
resetDaemonClient: false,
|
||||
}
|
||||
): Promise<ProjectGraph> {
|
||||
return this._graph;
|
||||
}
|
||||
|
||||
async addNpmDependency(project: string, dependency: string) {
|
||||
this._graph.dependencies[project] ??= [];
|
||||
this._graph.dependencies[project].push({
|
||||
source: project,
|
||||
type: 'static',
|
||||
target: `npm:${dependency}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
32
packages/vite/src/utils/detect-ui-framework.ts
Normal file
32
packages/vite/src/utils/detect-ui-framework.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { createProjectGraphAsync } from '@nx/devkit';
|
||||
|
||||
const ANGULAR_NPM_SCOPE = 'angular';
|
||||
const ANGULAR_DEPS = ['@nx/angular'];
|
||||
const REACT_DEPS = ['react', '@nx/react'];
|
||||
|
||||
export async function detectUiFramework(
|
||||
project: string
|
||||
): Promise<'angular' | 'react' | 'none'> {
|
||||
const graph = await createProjectGraphAsync();
|
||||
|
||||
for (const dep of graph.dependencies[project] ?? []) {
|
||||
if (dep.source !== project || !dep.target.startsWith('npm:')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const npmDependency = dep.target.replace('npm:', '');
|
||||
|
||||
if (
|
||||
dep.target.startsWith(`npm:@${ANGULAR_NPM_SCOPE}/`) ||
|
||||
ANGULAR_DEPS.includes(npmDependency)
|
||||
) {
|
||||
return 'angular';
|
||||
}
|
||||
|
||||
if (REACT_DEPS.includes(npmDependency)) {
|
||||
return 'react';
|
||||
}
|
||||
}
|
||||
|
||||
return 'none';
|
||||
}
|
||||
@ -5,6 +5,7 @@ import {
|
||||
type Tree,
|
||||
} from '@nx/devkit';
|
||||
import {
|
||||
analogVitestAngular,
|
||||
edgeRuntimeVmVersion,
|
||||
happyDomVersion,
|
||||
jsdomVersion,
|
||||
@ -14,7 +15,7 @@ import {
|
||||
} from './versions';
|
||||
|
||||
export type EnsureDependenciesOptions = {
|
||||
uiFramework: 'react' | 'none';
|
||||
uiFramework: 'angular' | 'react' | 'none';
|
||||
compiler?: 'babel' | 'swc';
|
||||
includeLib?: boolean;
|
||||
testEnvironment?: 'node' | 'jsdom' | 'happy-dom' | 'edge-runtime' | string;
|
||||
@ -38,6 +39,11 @@ export function ensureDependencies(
|
||||
);
|
||||
}
|
||||
|
||||
if (schema.uiFramework === 'angular') {
|
||||
devDependencies['@analogjs/vitest-angular'] = analogVitestAngular;
|
||||
devDependencies['@analogjs/vite-plugin-angular'] = analogVitestAngular;
|
||||
}
|
||||
|
||||
if (schema.uiFramework === 'react') {
|
||||
if (schema.compiler === 'swc') {
|
||||
devDependencies['@vitejs/plugin-react-swc'] = vitePluginReactSwcVersion;
|
||||
|
||||
@ -3,6 +3,7 @@ import {
|
||||
logger,
|
||||
offsetFromRoot,
|
||||
readJson,
|
||||
readNxJson,
|
||||
readProjectConfiguration,
|
||||
TargetConfiguration,
|
||||
Tree,
|
||||
@ -16,6 +17,7 @@ import { VitePreviewServerExecutorOptions } from '../executors/preview-server/sc
|
||||
import { VitestExecutorOptions } from '../executors/test/schema';
|
||||
import { ViteConfigurationGeneratorSchema } from '../generators/configuration/schema';
|
||||
import { ensureViteConfigIsCorrect } from './vite-config-edit-utils';
|
||||
import { VitestGeneratorSchema } from '../generators/vitest/schema';
|
||||
|
||||
export type Target = 'build' | 'serve' | 'test' | 'preview';
|
||||
export type TargetFlags = Partial<Record<Target, boolean>>;
|
||||
@ -81,10 +83,23 @@ export function findExistingJsBuildTargetInProject(targets: {
|
||||
|
||||
export function addOrChangeTestTarget(
|
||||
tree: Tree,
|
||||
options: ViteConfigurationGeneratorSchema,
|
||||
target: string
|
||||
options: VitestGeneratorSchema,
|
||||
hasPlugin: boolean
|
||||
) {
|
||||
const nxJson = readNxJson(tree);
|
||||
|
||||
hasPlugin = nxJson.plugins?.some((p) =>
|
||||
typeof p === 'string'
|
||||
? p === '@nx/vite/plugin'
|
||||
: p.plugin === '@nx/vite/plugin' || hasPlugin
|
||||
);
|
||||
|
||||
if (hasPlugin) {
|
||||
return;
|
||||
}
|
||||
|
||||
const project = readProjectConfiguration(tree, options.project);
|
||||
const target = options.testTarget ?? 'test';
|
||||
|
||||
const reportsDirectory = joinPathFragments(
|
||||
offsetFromRoot(project.root),
|
||||
@ -98,8 +113,7 @@ export function addOrChangeTestTarget(
|
||||
project.targets ??= {};
|
||||
|
||||
if (project.targets[target]) {
|
||||
project.targets[target].executor = '@nx/vite:test';
|
||||
delete project.targets[target].options?.jestConfig;
|
||||
throw new Error(`Target "${target}" already exists in the project.`);
|
||||
} else {
|
||||
project.targets[target] = {
|
||||
executor: '@nx/vite:test',
|
||||
@ -373,10 +387,7 @@ export function createOrEditViteConfig(
|
||||
projectAlreadyHasViteTargets?: TargetFlags,
|
||||
vitestFileName?: boolean
|
||||
) {
|
||||
const { root: projectRoot, projectType } = readProjectConfiguration(
|
||||
tree,
|
||||
options.project
|
||||
);
|
||||
const { root: projectRoot } = readProjectConfiguration(tree, options.project);
|
||||
|
||||
const extension = options.useEsmExtension ? 'mts' : 'ts';
|
||||
const viteConfigPath = vitestFileName
|
||||
|
||||
@ -407,6 +407,67 @@ export function mockAngularAppGenerator(tree: Tree): Tree {
|
||||
projectType: 'application',
|
||||
});
|
||||
|
||||
writeJson(tree, `apps/${appName}/tsconfig.json`, {
|
||||
compilerOptions: {
|
||||
target: 'es2022',
|
||||
esModuleInterop: true,
|
||||
forceConsistentCasingInFileNames: true,
|
||||
strict: true,
|
||||
noImplicitOverride: true,
|
||||
noPropertyAccessFromIndexSignature: true,
|
||||
noImplicitReturns: true,
|
||||
noFallthroughCasesInSwitch: true,
|
||||
},
|
||||
files: [],
|
||||
include: [],
|
||||
references: [
|
||||
{
|
||||
path: './tsconfig.editor.json',
|
||||
},
|
||||
{
|
||||
path: './tsconfig.app.json',
|
||||
},
|
||||
{
|
||||
path: './tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
extends: '../../tsconfig.base.json',
|
||||
angularCompilerOptions: {
|
||||
enableI18nLegacyMessageIdFormat: false,
|
||||
strictInjectionParameters: true,
|
||||
strictInputAccessModifiers: true,
|
||||
strictTemplates: true,
|
||||
},
|
||||
});
|
||||
|
||||
writeJson(tree, `apps/${appName}/tsconfig.app.json`, {
|
||||
extends: './tsconfig.json',
|
||||
compilerOptions: {
|
||||
outDir: '../../dist/out-tsc',
|
||||
types: [],
|
||||
},
|
||||
files: ['src/main.ts'],
|
||||
include: ['src/**/*.d.ts'],
|
||||
exclude: ['jest.config.ts', 'src/**/*.test.ts', 'src/**/*.spec.ts'],
|
||||
});
|
||||
|
||||
writeJson(tree, `apps/${appName}/tsconfig.spec.json`, {
|
||||
extends: './tsconfig.json',
|
||||
compilerOptions: {
|
||||
outDir: '../../dist/out-tsc',
|
||||
module: 'commonjs',
|
||||
target: 'es2016',
|
||||
types: ['jest', 'node'],
|
||||
},
|
||||
files: ['src/test-setup.ts'],
|
||||
include: [
|
||||
'jest.config.ts',
|
||||
'src/**/*.test.ts',
|
||||
'src/**/*.spec.ts',
|
||||
'src/**/*.d.ts',
|
||||
],
|
||||
});
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
||||
@ -9,6 +9,8 @@ export const vitePluginDtsVersion = '~3.8.1';
|
||||
export const happyDomVersion = '~9.20.3';
|
||||
export const edgeRuntimeVmVersion = '~3.0.2';
|
||||
|
||||
export const analogVitestAngular = '~1.10.0';
|
||||
|
||||
// Coverage providers
|
||||
export const vitestCoverageV8Version = '^1.0.4';
|
||||
export const vitestCoverageIstanbulVersion = '^1.0.4';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user