Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
"extends": "<%= relativePathToWorkspaceRoot %>/tsconfig.json",
"compilerOptions": {
"outDir": "<%= relativePathToWorkspaceRoot %>/out-tsc/spec",
"types": [
"jasmine"
]
},
"types": [
"<%= testRunner === 'vitest' ? 'vitest/globals' : 'jasmine' %>"
] },
"include": [
"src/**/*.ts"
]
Expand Down
90 changes: 78 additions & 12 deletions packages/schematics/angular/application/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ export default function (options: ApplicationOptions): Rule {
appName: options.name,
folderName,
suffix,
testRunner: options.testRunner,
}),
move(appDir),
]),
Expand Down Expand Up @@ -183,6 +184,65 @@ function addDependenciesToPackageJson(options: ApplicationOptions): Rule {
);
}

if (!options.skipTests) {
if (options.testRunner === 'vitest') {
rules.push(
addDependency('vitest', latestVersions['vitest'], {
type: DependencyType.Dev,
existing: ExistingBehavior.Skip,
install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto,
}),
addDependency('jsdom', latestVersions['jsdom'], {
type: DependencyType.Dev,
existing: ExistingBehavior.Skip,
install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto,
}),
);
} else {
rules.push(
addDependency('karma', latestVersions['karma'], {
type: DependencyType.Dev,
existing: ExistingBehavior.Skip,
install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto,
}),
addDependency('karma-chrome-launcher', latestVersions['karma-chrome-launcher'], {
type: DependencyType.Dev,
existing: ExistingBehavior.Skip,
install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto,
}),
addDependency('karma-coverage', latestVersions['karma-coverage'], {
type: DependencyType.Dev,
existing: ExistingBehavior.Skip,
install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto,
}),
addDependency('karma-jasmine', latestVersions['karma-jasmine'], {
type: DependencyType.Dev,
existing: ExistingBehavior.Skip,
install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto,
}),
addDependency(
'karma-jasmine-html-reporter',
latestVersions['karma-jasmine-html-reporter'],
{
type: DependencyType.Dev,
existing: ExistingBehavior.Skip,
install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto,
},
),
addDependency('jasmine-core', latestVersions['jasmine-core'], {
type: DependencyType.Dev,
existing: ExistingBehavior.Skip,
install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto,
}),
addDependency('@types/jasmine', latestVersions['@types/jasmine'], {
type: DependencyType.Dev,
existing: ExistingBehavior.Skip,
install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto,
}),
);
}
}

return chain(rules);
}

Expand Down Expand Up @@ -327,18 +387,24 @@ function addAppToWorkspaceFile(options: ApplicationOptions, appDir: string): Rul
},
},
},
test: options.minimal
? undefined
: {
builder: Builders.BuildKarma,
options: {
polyfills: options.zoneless ? undefined : ['zone.js', 'zone.js/testing'],
tsConfig: `${projectRoot}tsconfig.spec.json`,
inlineStyleLanguage,
assets: [{ 'glob': '**/*', 'input': `${projectRoot}public` }],
styles: [`${sourceRoot}/styles.${options.style}`],
},
},
test:
options.skipTests || options.minimal
? undefined
: options.testRunner === 'vitest'
? {
builder: Builders.BuildUnitTest,
options: {},
}
: {
builder: Builders.BuildKarma,
options: {
polyfills: options.zoneless ? undefined : ['zone.js', 'zone.js/testing'],
tsConfig: `${projectRoot}tsconfig.spec.json`,
inlineStyleLanguage,
assets: [{ 'glob': '**/*', 'input': `${projectRoot}public` }],
styles: [`${sourceRoot}/styles.${options.style}`],
},
},
},
};

Expand Down
94 changes: 44 additions & 50 deletions packages/schematics/angular/application/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,19 @@ describe('Application Schematic', () => {
expect(_extends).toBe('../../tsconfig.json');
});

it('should set the right types in the tsconfig.spec.json when testRunner is karma', async () => {
const tree = await schematicRunner.runSchematic(
'application',
{ ...defaultOptions, testRunner: 'karma' },
workspaceTree,
);

const {
compilerOptions: { types },
} = readJsonFile(tree, '/projects/foo/tsconfig.spec.json');
expect(types).toEqual(['jasmine']);
});

it('should add project references in the root tsconfig.json', async () => {
const tree = await schematicRunner.runSchematic('application', defaultOptions, workspaceTree);

Expand Down Expand Up @@ -324,6 +337,30 @@ describe('Application Schematic', () => {
expect(pkg.dependencies['zone.js']).toBeUndefined();
});

it('should add karma dependencies when testRunner is karma', async () => {
const tree = await schematicRunner.runSchematic(
'application',
{
...defaultOptions,
testRunner: 'karma',
},
workspaceTree,
);

const pkg = JSON.parse(tree.readContent('/package.json'));
expect(pkg.devDependencies['karma']).toEqual(latestVersions['karma']);
expect(pkg.devDependencies['karma-chrome-launcher']).toEqual(
latestVersions['karma-chrome-launcher'],
);
expect(pkg.devDependencies['karma-coverage']).toEqual(latestVersions['karma-coverage']);
expect(pkg.devDependencies['karma-jasmine']).toEqual(latestVersions['karma-jasmine']);
expect(pkg.devDependencies['karma-jasmine-html-reporter']).toEqual(
latestVersions['karma-jasmine-html-reporter'],
);
expect(pkg.devDependencies['jasmine-core']).toEqual(latestVersions['jasmine-core']);
expect(pkg.devDependencies['@types/jasmine']).toEqual(latestVersions['@types/jasmine']);
});

it(`should not override existing users dependencies`, async () => {
const oldPackageJson = workspaceTree.readContent('package.json');
workspaceTree.overwrite(
Expand Down Expand Up @@ -391,12 +428,6 @@ describe('Application Schematic', () => {
expect(buildOpt.assets).toEqual([{ 'glob': '**/*', 'input': 'public' }]);
expect(buildOpt.polyfills).toBeUndefined();
expect(buildOpt.tsConfig).toEqual('tsconfig.app.json');

const testOpt = prj.architect.test.options;
expect(testOpt.tsConfig).toEqual('tsconfig.spec.json');
expect(testOpt.karmaConfig).toBeUndefined();
expect(testOpt.assets).toEqual([{ 'glob': '**/*', 'input': 'public' }]);
expect(testOpt.styles).toEqual(['src/styles.css']);
});

it('should set values in angular.json correctly when using a style preprocessor', async () => {
Expand All @@ -407,51 +438,20 @@ describe('Application Schematic', () => {
const prj = config.projects.foo;
const buildOpt = prj.architect.build.options;
expect(buildOpt.styles).toEqual(['src/styles.sass']);
const testOpt = prj.architect.test.options;
expect(testOpt.styles).toEqual(['src/styles.sass']);
expect(tree.exists('src/styles.sass')).toBe(true);
});

it('sets "inlineStyleLanguage" in angular.json when using a style preprocessor', async () => {
const options = { ...defaultOptions, projectRoot: '', style: Style.Sass };
const tree = await schematicRunner.runSchematic('application', options, workspaceTree);

const config = JSON.parse(tree.readContent('/angular.json'));
const prj = config.projects.foo;

const buildOpt = prj.architect.build.options;
expect(buildOpt.inlineStyleLanguage).toBe('sass');

const testOpt = prj.architect.test.options;
expect(testOpt.inlineStyleLanguage).toBe('sass');
});

it('does not set "inlineStyleLanguage" in angular.json when not using a style preprocessor', async () => {
const options = { ...defaultOptions, projectRoot: '' };
it('should set values in angular.json correctly when testRunner is karma', async () => {
const options = { ...defaultOptions, projectRoot: '', testRunner: 'karma' as const };
const tree = await schematicRunner.runSchematic('application', options, workspaceTree);

const config = JSON.parse(tree.readContent('/angular.json'));
const prj = config.projects.foo;

const buildOpt = prj.architect.build.options;
expect(buildOpt.inlineStyleLanguage).toBeUndefined();

const testOpt = prj.architect.test.options;
expect(testOpt.inlineStyleLanguage).toBeUndefined();
});

it('does not set "inlineStyleLanguage" in angular.json when using CSS styles', async () => {
const options = { ...defaultOptions, projectRoot: '', style: Style.Css };
const tree = await schematicRunner.runSchematic('application', options, workspaceTree);

const config = JSON.parse(tree.readContent('/angular.json'));
const prj = config.projects.foo;

const buildOpt = prj.architect.build.options;
expect(buildOpt.inlineStyleLanguage).toBeUndefined();

const testOpt = prj.architect.test.options;
expect(testOpt.inlineStyleLanguage).toBeUndefined();
const testOpt = prj.architect.test;
expect(testOpt.builder).toEqual('@angular/build:karma');
expect(testOpt.options.tsConfig).toEqual('tsconfig.spec.json');
expect(testOpt.options.assets).toEqual([{ glob: '**/*', input: 'public' }]);
expect(testOpt.options.styles).toEqual(['src/styles.css']);
});

it('should set the relative tsconfig paths', async () => {
Expand Down Expand Up @@ -482,12 +482,6 @@ describe('Application Schematic', () => {
expect(buildOpt.tsConfig).toEqual('foo/tsconfig.app.json');
expect(buildOpt.assets).toEqual([{ 'glob': '**/*', 'input': 'foo/public' }]);

const testOpt = project.architect.test.options;
expect(testOpt.tsConfig).toEqual('foo/tsconfig.spec.json');
expect(testOpt.karmaConfig).toBeUndefined();
expect(testOpt.assets).toEqual([{ 'glob': '**/*', 'input': 'foo/public' }]);
expect(testOpt.styles).toEqual(['foo/src/styles.css']);

const appTsConfig = readJsonFile(tree, '/foo/tsconfig.app.json');
expect(appTsConfig.extends).toEqual('../tsconfig.json');
const specTsConfig = readJsonFile(tree, '/foo/tsconfig.spec.json');
Expand Down
6 changes: 6 additions & 0 deletions packages/schematics/angular/application/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@
"default": false,
"alias": "S"
},
"testRunner": {
"description": "The unit testing runner to use.",
"type": "string",
"enum": ["vitest", "karma"],
"default": "vitest"
},
"skipPackageJson": {
"type": "boolean",
"default": false,
Expand Down
6 changes: 6 additions & 0 deletions packages/schematics/angular/config/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ describe('Config Schematic', () => {
defaultAppOptions,
workspaceTree,
);

// Set builder to a karma builder for testing purposes
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const angularJson = applicationTree.readJson('angular.json') as any;
angularJson['projects']['foo']['architect']['test']['builder'] = '@angular/build:karma';
applicationTree.overwrite('angular.json', JSON.stringify(angularJson));
});

describe(`when 'type' is 'karma'`, () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"compilerOptions": {
"outDir": "<%= relativePathToWorkspaceRoot %>/out-tsc/spec",
"types": [
"jasmine"
"<%= testTypesPackage %>"
]
},
"include": [
Expand Down
26 changes: 18 additions & 8 deletions packages/schematics/angular/library/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ function addLibToWorkspaceFile(
projectRoot: string,
projectName: string,
hasZoneDependency: boolean,
hasVitest: boolean,
): Rule {
return updateWorkspace((workspace) => {
workspace.projects.add({
Expand All @@ -112,13 +113,20 @@ function addLibToWorkspaceFile(
},
},
},
test: {
builder: Builders.BuildKarma,
options: {
tsConfig: `${projectRoot}/tsconfig.spec.json`,
polyfills: hasZoneDependency ? ['zone.js', 'zone.js/testing'] : undefined,
},
},
test: hasVitest
? {
builder: Builders.BuildUnitTest,
options: {
tsConfig: `${projectRoot}/tsconfig.spec.json`,
},
}
: {
builder: Builders.BuildKarma,
options: {
tsConfig: `${projectRoot}/tsconfig.spec.json`,
polyfills: hasZoneDependency ? ['zone.js', 'zone.js/testing'] : undefined,
},
},
},
});
});
Expand Down Expand Up @@ -150,6 +158,7 @@ export default function (options: LibraryOptions): Rule {

const distRoot = `dist/${folderName}`;
const sourceDir = `${libDir}/src/lib`;
const hasVitest = getDependency(host, 'vitest') !== null;

const templateSource = apply(url('./files'), [
applyTemplates({
Expand All @@ -163,6 +172,7 @@ export default function (options: LibraryOptions): Rule {
angularLatestVersion: latestVersions.Angular.replace(/~|\^/, ''),
tsLibLatestVersion: latestVersions['tslib'].replace(/~|\^/, ''),
folderName,
testTypesPackage: hasVitest ? 'vitest/globals' : 'jasmine',
}),
move(libDir),
]);
Expand All @@ -171,7 +181,7 @@ export default function (options: LibraryOptions): Rule {

return chain([
mergeWith(templateSource),
addLibToWorkspaceFile(options, libDir, packageName, hasZoneDependency),
addLibToWorkspaceFile(options, libDir, packageName, hasZoneDependency, hasVitest),
options.skipPackageJson ? noop() : addDependenciesToPackageJson(!!options.skipInstall),
options.skipTsConfig ? noop() : updateTsConfig(packageName, './' + distRoot),
options.skipTsConfig
Expand Down
11 changes: 11 additions & 0 deletions packages/schematics/angular/library/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,17 @@ describe('Library Schematic', () => {
expect(workspace.projects.foo.architect.test.builder).toBe('@angular/build:karma');
});

it(`should add 'unit-test' test builder`, async () => {
const packageJson = getJsonFileContent(workspaceTree, 'package.json');
packageJson['devDependencies']['vitest'] = '^4.0.0';
workspaceTree.overwrite('package.json', JSON.stringify(packageJson));

const tree = await schematicRunner.runSchematic('library', defaultOptions, workspaceTree);

const workspace = JSON.parse(tree.readContent('/angular.json'));
expect(workspace.projects.foo.architect.test.builder).toBe('@angular/build:unit-test');
});

describe('standalone=false', () => {
const defaultNonStandaloneOptions = { ...defaultOptions, standalone: false };

Expand Down
Loading