588 lines
19 KiB
TypeScript
588 lines
19 KiB
TypeScript
process.env.SELECTED_CLI = 'angular';
|
|
|
|
import {
|
|
checkFilesDoNotExist,
|
|
checkFilesExist,
|
|
cleanupProject,
|
|
getSelectedPackageManager,
|
|
readJson,
|
|
runCLI,
|
|
runCommand,
|
|
runNgAdd,
|
|
runNgNew,
|
|
uniq,
|
|
updateFile,
|
|
} from '@nrwl/e2e/utils';
|
|
import { PackageManager } from 'nx/src/utils/package-manager';
|
|
|
|
describe('convert Angular CLI workspace to an Nx workspace', () => {
|
|
let project: string;
|
|
let packageManager: PackageManager;
|
|
|
|
// utility to manually add protractor since it's not generated
|
|
// in the latest Angular CLI versions, but older projects updated
|
|
// to latest versions might still have it
|
|
function addProtractor() {
|
|
updateFile('e2e/protractor.conf.js', 'exports.config = {};');
|
|
updateFile(
|
|
'e2e/tsconfig.json',
|
|
JSON.stringify({ extends: '../tsconfig.json' }, null, 2)
|
|
);
|
|
updateFile(
|
|
'e2e/src/app.e2e-spec.ts',
|
|
`describe('app', () => {
|
|
it('should pass', {
|
|
expect(true).toBe(true);
|
|
});
|
|
});`
|
|
);
|
|
|
|
const angularJson = readJson('angular.json');
|
|
angularJson.projects[project].architect.e2e = {
|
|
builder: '@angular-devkit/build-angular:protractor',
|
|
options: {
|
|
protractorConfig: 'e2e/protractor.conf.js',
|
|
devServerTarget: `${project}:serve`,
|
|
},
|
|
configurations: {
|
|
production: { devServerTarget: `${project}:serve:production` },
|
|
},
|
|
};
|
|
updateFile('angular.json', JSON.stringify(angularJson, null, 2));
|
|
}
|
|
|
|
function addCypress() {
|
|
// TODO(leo): @cypress/schematic latest comes with Cypress 10 support
|
|
// which we don't support yet in our Cypress plugin.
|
|
runNgAdd('@cypress/schematic', '--e2e-update', '1.7.0');
|
|
}
|
|
|
|
function addEsLint() {
|
|
runNgAdd('@angular-eslint/schematics', undefined, 'latest');
|
|
}
|
|
|
|
beforeEach(() => {
|
|
project = uniq('proj');
|
|
packageManager = getSelectedPackageManager();
|
|
// TODO: solve issues with pnpm and remove this fallback
|
|
packageManager = packageManager === 'pnpm' ? 'yarn' : packageManager;
|
|
runNgNew(project, packageManager);
|
|
});
|
|
|
|
afterEach(() => {
|
|
cleanupProject();
|
|
});
|
|
|
|
it('should generate a workspace', () => {
|
|
addProtractor();
|
|
|
|
// update package.json
|
|
const packageJson = readJson('package.json');
|
|
packageJson.description = 'some description';
|
|
updateFile('package.json', JSON.stringify(packageJson, null, 2));
|
|
|
|
// update tsconfig.json
|
|
const tsConfig = readJson('tsconfig.json');
|
|
tsConfig.compilerOptions.paths = { a: ['b'] };
|
|
updateFile('tsconfig.json', JSON.stringify(tsConfig, null, 2));
|
|
|
|
// add an extra script file
|
|
updateFile('src/scripts.js', 'const x = 1;');
|
|
|
|
// update angular.json
|
|
const angularJson = readJson('angular.json');
|
|
angularJson.projects[project].architect.build.options.scripts =
|
|
angularJson.projects[project].architect.test.options.scripts = [
|
|
'src/scripts.js',
|
|
];
|
|
angularJson.projects[project].architect.test.options.styles = [
|
|
'src/styles.css',
|
|
];
|
|
updateFile('angular.json', JSON.stringify(angularJson, null, 2));
|
|
|
|
// confirm that @nrwl dependencies do not exist yet
|
|
expect(packageJson.devDependencies['@nrwl/workspace']).not.toBeDefined();
|
|
|
|
// run ng add
|
|
runNgAdd('@nrwl/angular', '--npm-scope projscope --default-base main');
|
|
|
|
// check that prettier config exits and that files have been moved
|
|
checkFilesExist(
|
|
'.vscode/extensions.json',
|
|
'.prettierrc',
|
|
`apps/${project}/src/main.ts`,
|
|
`apps/${project}/src/app/app.module.ts`
|
|
);
|
|
|
|
// check the right VSCode extensions are recommended
|
|
expect(readJson('.vscode/extensions.json').recommendations).toEqual([
|
|
'angular.ng-template',
|
|
'nrwl.angular-console',
|
|
'dbaeumer.vscode-eslint',
|
|
'esbenp.prettier-vscode',
|
|
]);
|
|
|
|
// check package.json
|
|
const updatedPackageJson = readJson('package.json');
|
|
expect(updatedPackageJson.description).toEqual('some description');
|
|
expect(updatedPackageJson.scripts).toEqual({
|
|
ng: 'nx',
|
|
start: 'nx serve',
|
|
build: 'nx build',
|
|
watch: 'nx build --watch --configuration development',
|
|
test: 'nx test',
|
|
postinstall: 'node ./decorate-angular-cli.js',
|
|
});
|
|
expect(updatedPackageJson.devDependencies['@nrwl/workspace']).toBeDefined();
|
|
expect(updatedPackageJson.devDependencies['@angular/cli']).toBeDefined();
|
|
|
|
// check nx.json
|
|
const nxJson = readJson('nx.json');
|
|
expect(nxJson).toEqual({
|
|
affected: {
|
|
defaultBase: 'main',
|
|
},
|
|
cli: {
|
|
defaultCollection: '@nrwl/angular',
|
|
packageManager: packageManager,
|
|
},
|
|
implicitDependencies: {
|
|
'.eslintrc.json': '*',
|
|
'package.json': {
|
|
dependencies: '*',
|
|
devDependencies: '*',
|
|
},
|
|
},
|
|
npmScope: 'projscope',
|
|
targetDependencies: {
|
|
build: [
|
|
{
|
|
projects: 'dependencies',
|
|
target: 'build',
|
|
},
|
|
],
|
|
},
|
|
tasksRunnerOptions: {
|
|
default: {
|
|
options: {
|
|
cacheableOperations: ['build', 'lint', 'test', 'e2e'],
|
|
},
|
|
runner: 'nx/tasks-runners/default',
|
|
},
|
|
},
|
|
});
|
|
|
|
// check angular.json
|
|
expect(readJson('angular.json')).toStrictEqual({
|
|
version: 2,
|
|
projects: {
|
|
[project]: `apps/${project}`,
|
|
[`${project}-e2e`]: `apps/${project}-e2e`,
|
|
},
|
|
});
|
|
|
|
// check project configuration
|
|
const projectConfig = readJson(`apps/${project}/project.json`);
|
|
expect(projectConfig.sourceRoot).toEqual(`apps/${project}/src`);
|
|
expect(projectConfig.targets.build).toEqual({
|
|
executor: '@angular-devkit/build-angular:browser',
|
|
options: {
|
|
outputPath: `dist/apps/${project}`,
|
|
index: `apps/${project}/src/index.html`,
|
|
main: `apps/${project}/src/main.ts`,
|
|
polyfills: `apps/${project}/src/polyfills.ts`,
|
|
tsConfig: `apps/${project}/tsconfig.app.json`,
|
|
assets: [
|
|
`apps/${project}/src/favicon.ico`,
|
|
`apps/${project}/src/assets`,
|
|
],
|
|
styles: [`apps/${project}/src/styles.css`],
|
|
scripts: [`apps/${project}/src/scripts.js`],
|
|
},
|
|
configurations: {
|
|
production: {
|
|
fileReplacements: [
|
|
{
|
|
replace: `apps/${project}/src/environments/environment.ts`,
|
|
with: `apps/${project}/src/environments/environment.prod.ts`,
|
|
},
|
|
],
|
|
budgets: [
|
|
{
|
|
type: 'initial',
|
|
maximumWarning: '500kb',
|
|
maximumError: '1mb',
|
|
},
|
|
{
|
|
type: 'anyComponentStyle',
|
|
maximumWarning: '2kb',
|
|
maximumError: '4kb',
|
|
},
|
|
],
|
|
outputHashing: 'all',
|
|
},
|
|
development: {
|
|
buildOptimizer: false,
|
|
optimization: false,
|
|
vendorChunk: true,
|
|
extractLicenses: false,
|
|
sourceMap: true,
|
|
namedChunks: true,
|
|
},
|
|
},
|
|
defaultConfiguration: 'production',
|
|
});
|
|
expect(projectConfig.targets.serve).toEqual({
|
|
executor: '@angular-devkit/build-angular:dev-server',
|
|
configurations: {
|
|
production: { browserTarget: `${project}:build:production` },
|
|
development: { browserTarget: `${project}:build:development` },
|
|
},
|
|
defaultConfiguration: 'development',
|
|
});
|
|
expect(projectConfig.targets.test).toEqual({
|
|
executor: '@angular-devkit/build-angular:karma',
|
|
options: {
|
|
main: `apps/${project}/src/test.ts`,
|
|
polyfills: `apps/${project}/src/polyfills.ts`,
|
|
tsConfig: `apps/${project}/tsconfig.spec.json`,
|
|
karmaConfig: `apps/${project}/karma.conf.js`,
|
|
assets: [
|
|
`apps/${project}/src/favicon.ico`,
|
|
`apps/${project}/src/assets`,
|
|
],
|
|
styles: [`apps/${project}/src/styles.css`],
|
|
scripts: [`apps/${project}/src/scripts.js`],
|
|
},
|
|
});
|
|
expect(projectConfig.targets.e2e).toBeUndefined();
|
|
|
|
// check e2e project config
|
|
const e2eProjectConfig = readJson(`apps/${project}-e2e/project.json`);
|
|
expect(e2eProjectConfig.targets.e2e).toEqual({
|
|
executor: '@angular-devkit/build-angular:protractor',
|
|
options: {
|
|
protractorConfig: `apps/${project}-e2e/protractor.conf.js`,
|
|
devServerTarget: `${project}:serve`,
|
|
},
|
|
configurations: {
|
|
production: {
|
|
devServerTarget: `${project}:serve:production`,
|
|
},
|
|
},
|
|
});
|
|
|
|
runCLI(`build ${project} --configuration production --outputHashing none`);
|
|
checkFilesExist(`dist/apps/${project}/main.js`);
|
|
});
|
|
|
|
it('should handle different types of errors', () => {
|
|
addProtractor();
|
|
|
|
// Remove e2e directory
|
|
runCommand('mv e2e e2e-bak');
|
|
expect(() =>
|
|
runNgAdd('@nrwl/angular', '--npm-scope projscope --skip-install')
|
|
).toThrow(
|
|
'The specified Protractor config file "e2e/protractor.conf.js" in the "e2e" target could not be found.'
|
|
);
|
|
// Restore e2e directory
|
|
runCommand('mv e2e-bak e2e');
|
|
|
|
// TODO: this functionality is currently broken, this validation doesn't exist
|
|
// // Remove src
|
|
// runCommand('mv src src-bak');
|
|
// expect(() => runNgAdd('@nrwl/angular', '--npm-scope projscope --skip-install')).toThrow(
|
|
// 'Path: src does not exist'
|
|
// );
|
|
|
|
// // Put src back
|
|
// runCommand('mv src-bak src');
|
|
});
|
|
|
|
it('should handle wrong cypress setup', () => {
|
|
addCypress();
|
|
|
|
// Remove cypress.json
|
|
runCommand('mv cypress.json cypress.json.bak');
|
|
expect(() =>
|
|
runNgAdd('@nrwl/angular', '--npm-scope projscope --skip-install')
|
|
).toThrow(
|
|
'The "e2e" target is using the "@cypress/schematic:cypress" builder but the "configFile" option is not specified and a "cypress.json" file could not be found at the project root.'
|
|
);
|
|
// Restore cypress.json
|
|
runCommand('mv cypress.json.bak cypress.json');
|
|
|
|
// Remove cypress directory
|
|
runCommand('mv cypress cypress-bak');
|
|
expect(() =>
|
|
runNgAdd('@nrwl/angular', '--npm-scope projscope --skip-install')
|
|
).toThrow(
|
|
'The "e2e" target is using the "@cypress/schematic:cypress" builder but the "cypress" directory could not be found at the project root.'
|
|
);
|
|
// Restore cypress.json
|
|
runCommand('mv cypress-bak cypress');
|
|
});
|
|
|
|
it('should handle a workspace with cypress', () => {
|
|
addCypress();
|
|
|
|
runNgAdd('@nrwl/angular', '--npm-scope projscope --skip-install');
|
|
|
|
const e2eProject = `${project}-e2e`;
|
|
//check e2e project files
|
|
checkFilesDoNotExist(
|
|
'cypress.json',
|
|
'cypress/tsconfig.json',
|
|
'cypress/integration/spec.ts',
|
|
'cypress/plugins/index.ts',
|
|
'cypress/support/commands.ts',
|
|
'cypress/support/index.ts'
|
|
);
|
|
checkFilesExist(
|
|
`apps/${e2eProject}/cypress.json`,
|
|
`apps/${e2eProject}/tsconfig.json`,
|
|
`apps/${e2eProject}/src/integration/spec.ts`,
|
|
`apps/${e2eProject}/src/plugins/index.ts`,
|
|
`apps/${e2eProject}/src/support/commands.ts`,
|
|
`apps/${e2eProject}/src/support/index.ts`
|
|
);
|
|
|
|
const projectConfig = readJson(`apps/${project}/project.json`);
|
|
expect(projectConfig.targets['cypress-run']).toBeUndefined();
|
|
expect(projectConfig.targets['cypress-open']).toBeUndefined();
|
|
expect(projectConfig.targets.e2e).toBeUndefined();
|
|
|
|
// check e2e project config
|
|
const e2eProjectConfig = readJson(`apps/${project}-e2e/project.json`);
|
|
expect(e2eProjectConfig.targets['cypress-run']).toEqual({
|
|
executor: '@nrwl/cypress:cypress',
|
|
options: {
|
|
devServerTarget: `${project}:serve`,
|
|
cypressConfig: `apps/${e2eProject}/cypress.json`,
|
|
},
|
|
configurations: {
|
|
production: {
|
|
devServerTarget: `${project}:serve:production`,
|
|
},
|
|
},
|
|
});
|
|
expect(e2eProjectConfig.targets['cypress-open']).toEqual({
|
|
executor: '@nrwl/cypress:cypress',
|
|
options: {
|
|
watch: true,
|
|
headless: false,
|
|
cypressConfig: `apps/${e2eProject}/cypress.json`,
|
|
},
|
|
});
|
|
expect(e2eProjectConfig.targets.e2e).toEqual({
|
|
executor: '@nrwl/cypress:cypress',
|
|
options: {
|
|
devServerTarget: `${project}:serve`,
|
|
watch: true,
|
|
headless: false,
|
|
cypressConfig: `apps/${e2eProject}/cypress.json`,
|
|
},
|
|
configurations: {
|
|
production: {
|
|
devServerTarget: `${project}:serve:production`,
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
// TODO(leo): The current Verdaccio setup fails to resolve older versions
|
|
// of @nrwl/* packages, the @angular-eslint/builder package depends on an
|
|
// older version of @nrwl/devkit so we skip this test for now.
|
|
it.skip('should handle a workspace with ESLint', () => {
|
|
addEsLint();
|
|
|
|
runNgAdd('@nrwl/angular', '--npm-scope projscope');
|
|
|
|
checkFilesExist(`apps/${project}/.eslintrc.json`, `.eslintrc.json`);
|
|
|
|
const projectConfig = readJson(`apps/${project}/project.json`);
|
|
expect(projectConfig.targets.lint).toStrictEqual({
|
|
executor: '@nrwl/linter:eslint',
|
|
options: {
|
|
lintFilePatterns: [
|
|
`apps/${project}/src/**/*.ts`,
|
|
`apps/${project}/src/**/*.html`,
|
|
],
|
|
},
|
|
});
|
|
|
|
let output = runCLI(`lint ${project}`);
|
|
expect(output).toContain(`> nx run ${project}:lint`);
|
|
expect(output).toContain('All files pass linting.');
|
|
expect(output).toContain(
|
|
`Successfully ran target lint for project ${project}`
|
|
);
|
|
|
|
output = runCLI(`lint ${project}`);
|
|
expect(output).toContain(
|
|
`> nx run ${project}:lint [existing outputs match the cache, left as is]`
|
|
);
|
|
expect(output).toContain('All files pass linting.');
|
|
expect(output).toContain(
|
|
`Successfully ran target lint for project ${project}`
|
|
);
|
|
});
|
|
|
|
it('should support a workspace with multiple libraries', () => {
|
|
// add some libraries
|
|
const lib1 = uniq('lib1');
|
|
const lib2 = uniq('lib2');
|
|
runCommand(`ng g @schematics/angular:library ${lib1}`);
|
|
runCommand(`ng g @schematics/angular:library ${lib2}`);
|
|
|
|
runNgAdd('@nrwl/angular', '--npm-scope projscope');
|
|
|
|
// check angular.json
|
|
expect(readJson('angular.json')).toStrictEqual({
|
|
version: 2,
|
|
projects: {
|
|
[project]: `apps/${project}`,
|
|
[lib1]: `libs/${lib1}`,
|
|
[lib2]: `libs/${lib2}`,
|
|
},
|
|
});
|
|
|
|
// check building lib1
|
|
let output = runCLI(`build ${lib1}`);
|
|
expect(output).toContain(`> nx run ${lib1}:build:production`);
|
|
expect(output).toContain(
|
|
`Successfully ran target build for project ${lib1}`
|
|
);
|
|
checkFilesExist(`dist/${lib1}/package.json`);
|
|
|
|
output = runCLI(`build ${lib1}`);
|
|
expect(output).toContain(
|
|
`> nx run ${lib1}:build:production [existing outputs match the cache, left as is]`
|
|
);
|
|
expect(output).toContain(
|
|
`Successfully ran target build for project ${lib1}`
|
|
);
|
|
|
|
// check building lib2
|
|
output = runCLI(`build ${lib2}`);
|
|
expect(output).toContain(`> nx run ${lib2}:build:production`);
|
|
expect(output).toContain(
|
|
`Successfully ran target build for project ${lib2}`
|
|
);
|
|
checkFilesExist(`dist/${lib2}/package.json`);
|
|
|
|
output = runCLI(`build ${lib2}`);
|
|
expect(output).toContain(
|
|
`> nx run ${lib2}:build:production [existing outputs match the cache, left as is]`
|
|
);
|
|
expect(output).toContain(
|
|
`Successfully ran target build for project ${lib2}`
|
|
);
|
|
});
|
|
|
|
it('should support a workspace with multiple applications', () => {
|
|
// add another app
|
|
const app1 = uniq('app1');
|
|
runCommand(`ng g @schematics/angular:application ${app1}`);
|
|
|
|
runNgAdd('@nrwl/angular', '--npm-scope projscope');
|
|
|
|
// check angular.json
|
|
expect(readJson('angular.json')).toStrictEqual({
|
|
version: 2,
|
|
projects: {
|
|
[project]: `apps/${project}`,
|
|
[app1]: `apps/${app1}`,
|
|
},
|
|
});
|
|
|
|
// check building project
|
|
let output = runCLI(`build ${project} --outputHashing none`);
|
|
expect(output).toContain(
|
|
`> nx run ${project}:build:production --outputHashing=none`
|
|
);
|
|
expect(output).toContain(
|
|
`Successfully ran target build for project ${project}`
|
|
);
|
|
checkFilesExist(`dist/apps/${project}/main.js`);
|
|
|
|
output = runCLI(`build ${project} --outputHashing none`);
|
|
expect(output).toContain(
|
|
`> nx run ${project}:build:production --outputHashing=none [existing outputs match the cache, left as is]`
|
|
);
|
|
expect(output).toContain(
|
|
`Successfully ran target build for project ${project}`
|
|
);
|
|
|
|
// check building app1
|
|
output = runCLI(`build ${app1} --outputHashing none`);
|
|
expect(output).toContain(
|
|
`> nx run ${app1}:build:production --outputHashing=none`
|
|
);
|
|
expect(output).toContain(
|
|
`Successfully ran target build for project ${app1}`
|
|
);
|
|
checkFilesExist(`dist/apps/${app1}/main.js`);
|
|
|
|
output = runCLI(`build ${app1} --outputHashing none`);
|
|
expect(output).toContain(
|
|
`> nx run ${app1}:build:production --outputHashing=none [existing outputs match the cache, left as is]`
|
|
);
|
|
expect(output).toContain(
|
|
`Successfully ran target build for project ${app1}`
|
|
);
|
|
});
|
|
|
|
it('should support --preserve-angular-cli-layout', () => {
|
|
// add another app and a library
|
|
runCommand(`ng g @schematics/angular:application app2`);
|
|
runCommand(`ng g @schematics/angular:library lib1`);
|
|
|
|
runNgAdd('@nrwl/angular', '--preserve-angular-cli-layout');
|
|
|
|
// check config still uses Angular CLI layout
|
|
const updatedAngularJson = readJson('angular.json');
|
|
expect(updatedAngularJson.projects[project].root).toEqual('');
|
|
expect(updatedAngularJson.projects[project].sourceRoot).toEqual('src');
|
|
expect(updatedAngularJson.projects.app2.root).toEqual('projects/app2');
|
|
expect(updatedAngularJson.projects.app2.sourceRoot).toEqual(
|
|
'projects/app2/src'
|
|
);
|
|
expect(updatedAngularJson.projects.lib1.root).toEqual('projects/lib1');
|
|
expect(updatedAngularJson.projects.lib1.sourceRoot).toEqual(
|
|
'projects/lib1/src'
|
|
);
|
|
|
|
// check building an app
|
|
let output = runCLI(`build ${project} --outputHashing none`);
|
|
expect(output).toContain(
|
|
`> nx run ${project}:build:production --outputHashing=none`
|
|
);
|
|
expect(output).toContain(
|
|
`Successfully ran target build for project ${project}`
|
|
);
|
|
checkFilesExist(`dist/${project}/main.js`);
|
|
|
|
output = runCLI(`build ${project} --outputHashing none`);
|
|
expect(output).toContain(
|
|
`> nx run ${project}:build:production --outputHashing=none [existing outputs match the cache, left as is]`
|
|
);
|
|
expect(output).toContain(
|
|
`Successfully ran target build for project ${project}`
|
|
);
|
|
|
|
// check building lib1
|
|
output = runCLI('build lib1');
|
|
expect(output).toContain('> nx run lib1:build:production');
|
|
expect(output).toContain('Successfully ran target build for project lib1');
|
|
checkFilesExist('dist/lib1/package.json');
|
|
|
|
output = runCLI('build lib1');
|
|
expect(output).toContain(
|
|
'> nx run lib1:build:production [existing outputs match the cache, left as is]'
|
|
);
|
|
expect(output).toContain('Successfully ran target build for project lib1');
|
|
});
|
|
});
|