chore(repo): split angular e2e tests into core and extensions (#6470)

This commit is contained in:
Miroslav Jonaš 2021-07-22 15:46:23 +02:00 committed by GitHub
parent f3b5ffc165
commit 1ca34f9669
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1212 additions and 1076 deletions

View File

@ -36,7 +36,7 @@ jobs:
- yarn
- pnpm
packages:
- e2e-angular
- e2e-angular-core,e2e-angular-extensions
- e2e-cli,e2e-nx-plugin,e2e-jest,e2e-linter
- e2e-cypress
- e2e-gatsby,e2e-react
@ -104,7 +104,6 @@ jobs:
NODE_OPTIONS: --max_old_space_size=8192
SELECTED_PM: ${{ matrix.package_manager }}
YARN_REGISTRY: http://localhost:4872
SELECTED_CLI: ${{ matrix.packages == 'e2e-angular' && 'angular' || 'nx' }}
- name: Setup tmate session
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled && failure() }}

View File

@ -6,5 +6,5 @@ module.exports = {
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
maxWorkers: 1,
globals: { 'ts-jest': { tsconfig: '<rootDir>/tsconfig.spec.json' } },
displayName: 'e2e-angular',
displayName: 'e2e-angular-core',
};

View File

@ -1,6 +1,6 @@
{
"root": "e2e/angular",
"sourceRoot": "e2e/angular",
"root": "e2e/angular-core",
"sourceRoot": "e2e/angular-core",
"projectType": "application",
"targets": {
"e2e": {
@ -14,7 +14,7 @@
"command": "yarn e2e-build-package-publish"
},
{
"command": "nx run-e2e-tests e2e-angular"
"command": "nx run-e2e-tests e2e-angular-core"
}
],
"parallel": false
@ -23,11 +23,11 @@
"run-e2e-tests": {
"executor": "@nrwl/jest:jest",
"options": {
"jestConfig": "e2e/angular/jest.config.js",
"jestConfig": "e2e/angular-core/jest.config.js",
"passWithNoTests": true,
"runInBand": true
},
"outputs": ["coverage/e2e/angular"]
"outputs": ["coverage/e2e/angular-core"]
}
},
"implicitDependencies": ["angular"]

View File

@ -0,0 +1,118 @@
import {
checkFilesExist,
expectTestsPass,
getSelectedPackageManager,
getSize,
killPorts,
newProject,
removeProject,
runCLI,
runCLIAsync,
tmpProjPath,
uniq,
updateFile,
runCypressTests,
} from '@nrwl/e2e/utils';
import { names } from '@nrwl/devkit';
describe('Angular Package', () => {
describe('core', () => {
let proj: string;
beforeEach(() => (proj = newProject()));
afterEach(() => removeProject({ onlyOnCI: true }));
it('should work', async () => {
// TODO: npm build is failing for Angular because of webpack 4
// remove this condition once `node` is migrated to webpack 5
if (getSelectedPackageManager() !== 'npm') {
const myapp = uniq('myapp');
const mylib = uniq('mylib');
runCLI(
`generate @nrwl/angular:app ${myapp} --directory=myDir --no-interactive`
);
runCLI(
`generate @nrwl/angular:lib ${mylib} --directory=myDir --add-module-spec --no-interactive`
);
updateFile(
`apps/my-dir/${myapp}/src/app/app.module.ts`,
`
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MyDir${
names(mylib).className
}Module } from '@${proj}/my-dir/${mylib}';
import { AppComponent } from './app.component';
@NgModule({
imports: [BrowserModule, MyDir${names(mylib).className}Module],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
`
);
runCLI(`build my-dir-${myapp} --prod --output-hashing none`);
checkFilesExist(`dist/apps/my-dir/${myapp}/main.js`);
// This is a loose requirement because there are a lot of
// influences external from this project that affect this.
const es2015BundleSize = getSize(
tmpProjPath(`dist/apps/my-dir/${myapp}/main.js`)
);
console.log(
`The current es2015 bundle size is ${es2015BundleSize / 1000} KB`
);
expect(es2015BundleSize).toBeLessThanOrEqual(160000);
// running tests for the app
expectTestsPass(await runCLIAsync(`test my-dir-${myapp} --no-watch`));
// running tests for the lib
expectTestsPass(await runCLIAsync(`test my-dir-${mylib} --no-watch`));
if (runCypressTests()) {
const e2eResults = runCLI(
`e2e my-dir-${myapp}-e2e --headless --no-watch`
);
expect(e2eResults).toContain('All specs passed!');
expect(await killPorts()).toBeTruthy();
}
}
}, 1000000);
it('should support building in parallel', () => {
// TODO: npm build is failing for Angular because of webpack 4
// remove this condition once `node` is migrated to webpack 5
if (getSelectedPackageManager() !== 'npm') {
if (getSelectedPackageManager() === 'pnpm') {
// TODO: This tests fails with pnpm but we should still enable this for other package managers
return;
}
const myapp = uniq('myapp');
const myapp2 = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp}`);
runCLI(`generate @nrwl/angular:app ${myapp2}`);
runCLI('run-many --target build --all --parallel');
}
});
it('should support Ivy', async () => {
// TODO: npm build is failing for Angular because of webpack 4
// remove this condition once `node` is migrated to webpack 5
if (getSelectedPackageManager() !== 'npm') {
const myapp = uniq('myapp');
runCLI(
`generate @nrwl/angular:app ${myapp} --directory=myDir --routing --enable-ivy`
);
runCLI(`build my-dir-${myapp} --aot`);
expectTestsPass(await runCLIAsync(`test my-dir-${myapp} --no-watch`));
}
}, 1000000);
});
});

View File

@ -0,0 +1,70 @@
import {
newProject,
removeProject,
runCLI,
uniq,
updateFile,
} from '@nrwl/e2e/utils';
import * as path from 'path';
describe('Angular Package', () => {
describe('linting', () => {
beforeEach(() => newProject());
afterEach(() => removeProject({ onlyOnCI: true }));
it('should support eslint and pass linting on the standard generated code', async () => {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --linter=eslint`);
expect(runCLI(`lint ${myapp}`)).toContain('All files pass linting.');
const mylib = uniq('mylib');
runCLI(`generate @nrwl/angular:lib ${mylib} --linter=eslint`);
expect(runCLI(`lint ${mylib}`)).toContain('All files pass linting.');
});
it('should support eslint and successfully lint external HTML files and inline templates', async () => {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --linter=eslint`);
const templateWhichFailsBananaInBoxLintCheck = `<div ([foo])="bar"></div>`;
const wrappedAsInlineTemplate = `
import { Component } from '@angular/core';
@Component({
selector: 'inline-template-component',
template: \`
${templateWhichFailsBananaInBoxLintCheck}
\`,
})
export class InlineTemplateComponent {}
`;
// External HTML template file
updateFile(
`apps/${myapp}/src/app/app.component.html`,
templateWhichFailsBananaInBoxLintCheck
);
// Inline template within component.ts file
updateFile(
`apps/${myapp}/src/app/inline-template.component.ts`,
wrappedAsInlineTemplate
);
const appLintStdOut = runCLI(`lint ${myapp}`, { silenceError: true });
expect(appLintStdOut).toContain(
path.normalize(`apps/${myapp}/src/app/app.component.html`)
);
expect(appLintStdOut).toContain(`1:6`);
expect(appLintStdOut).toContain(`Invalid binding syntax`);
expect(appLintStdOut).toContain(
path.normalize(`apps/${myapp}/src/app/inline-template.component.ts`)
);
expect(appLintStdOut).toContain(
`The selector should start with one of these prefixes`
);
expect(appLintStdOut).toContain(`7:18`);
});
});
});

View File

@ -0,0 +1,48 @@
process.env.SELECTED_CLI = 'angular';
import {
expectTestsPass,
newProject,
readJson,
removeProject,
runCLI,
runCLIAsync,
uniq,
updateFile,
} from '@nrwl/e2e/utils';
describe('Angular Package', () => {
describe('config compat', () => {
beforeEach(() => newProject());
afterEach(() => removeProject({ onlyOnCI: true }));
it('should work', async () => {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --no-interactive`);
// update the angular.json
const workspaceJson = readJson(`angular.json`);
workspaceJson.version = 2;
workspaceJson.projects[myapp].targets = updateConfig(
workspaceJson.projects[myapp].architect
);
workspaceJson.generators = workspaceJson.schematics;
delete workspaceJson.schematics;
updateFile('angular.json', JSON.stringify(workspaceJson, null, 2));
const myapp2 = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp2} --no-interactive`);
expectTestsPass(await runCLIAsync(`test ${myapp2} --no-watch`));
}, 1000000);
});
});
function updateConfig(targets: any) {
const res = {};
Object.entries(targets).forEach(([name, t]: any) => {
t.executor = t.builder;
delete t.builder;
res[name] = t;
});
return res;
}

View File

@ -0,0 +1,366 @@
process.env.SELECTED_CLI = 'angular';
import {
checkFilesExist,
readJson,
removeProject,
runCLI,
runCommand,
runNgAdd,
runNgNew,
uniq,
updateFile,
} from '@nrwl/e2e/utils';
// TODO: Check why generated angular app is different
xdescribe('Angular Package', () => {
describe('convert to Nx workspace', () => {
let proj;
afterEach(() => {
removeProject({ onlyOnCI: true });
});
it('should generate a workspace', () => {
proj = uniq('proj');
runNgNew(proj);
// update package.json
const packageJson = readJson('package.json');
packageJson.description = 'some description';
updateFile('package.json', JSON.stringify(packageJson, null, 2));
// confirm that @nrwl and @ngrx dependencies do not exist yet
expect(packageJson.devDependencies['@nrwl/workspace']).not.toBeDefined();
expect(packageJson.dependencies['@ngrx/store']).not.toBeDefined();
expect(packageJson.dependencies['@ngrx/effects']).not.toBeDefined();
expect(packageJson.dependencies['@ngrx/router-store']).not.toBeDefined();
expect(
packageJson.devDependencies['@ngrx/store-devtools']
).not.toBeDefined();
// update tsconfig.json
const tsconfigJson = readJson('tsconfig.json');
tsconfigJson.compilerOptions.paths = { a: ['b'] };
updateFile('tsconfig.json', JSON.stringify(tsconfigJson, null, 2));
updateFile('src/scripts.ts', '');
// update angular-cli.json
const angularCLIJson = readJson('angular.json');
angularCLIJson.projects[proj].architect.build.options.scripts =
angularCLIJson.projects[proj].architect.test.options.scripts = [
'src/scripts.ts',
];
angularCLIJson.projects[proj].architect.test.options.styles = [
'src/styles.css',
];
updateFile('angular.json', JSON.stringify(angularCLIJson, null, 2));
// run the command
runNgAdd('--npm-scope projscope');
// check that prettier config exits and that files have been moved!
checkFilesExist(
'.vscode/extensions.json',
'.prettierrc',
`apps/${proj}/src/main.ts`,
`apps/${proj}/src/app/app.module.ts`
);
expect(readJson('.vscode/extensions.json').recommendations).toEqual([
'nrwl.angular-console',
'angular.ng-template',
'dbaeumer.vscode-eslint',
'esbenp.prettier-vscode',
]);
// check that package.json got merged
const updatedPackageJson = readJson('package.json');
expect(updatedPackageJson.description).toEqual('some description');
expect(updatedPackageJson.scripts).toEqual({
ng: 'nx',
nx: 'nx',
start: 'ng serve',
build: 'ng build',
test: 'ng test',
lint: 'nx workspace-lint && ng lint',
e2e: 'ng e2e',
'affected:apps': 'nx affected:apps',
'affected:libs': 'nx affected:libs',
'affected:build': 'nx affected:build',
'affected:e2e': 'nx affected:e2e',
'affected:test': 'nx affected:test',
'affected:lint': 'nx affected:lint',
'affected:dep-graph': 'nx affected:dep-graph',
affected: 'nx affected',
format: 'nx format:write',
'format:write': 'nx format:write',
'format:check': 'nx format:check',
update: 'ng update @nrwl/workspace',
'update:check': 'ng update',
postinstall: 'node ./decorate-angular-cli.js',
'dep-graph': 'nx dep-graph',
'workspace-generator': 'nx workspace-generator',
help: 'nx help',
});
expect(
updatedPackageJson.devDependencies['@nrwl/workspace']
).toBeDefined();
expect(updatedPackageJson.devDependencies['@angular/cli']).toBeDefined();
const nxJson = readJson('nx.json');
expect(nxJson).toEqual({
npmScope: 'projscope',
implicitDependencies: {
'angular.json': '*',
'package.json': '*',
'tslint.json': '*',
'.eslintrc.json': '*',
'tsconfig.base.json': '*',
'nx.json': '*',
},
projects: {
[`${proj}`]: {
tags: [],
},
[`${proj}-e2e`]: {
tags: [],
},
},
});
// check if angular-cli.json get merged
const updatedAngularCLIJson = readJson('angular.json');
expect(updatedAngularCLIJson.projects[proj].root).toEqual(`apps/${proj}`);
expect(updatedAngularCLIJson.projects[proj].sourceRoot).toEqual(
`apps/${proj}/src`
);
expect(updatedAngularCLIJson.projects[proj].architect.build).toEqual({
builder: '@angular-devkit/build-angular:browser',
options: {
aot: true,
outputPath: `dist/apps/${proj}`,
index: `apps/${proj}/src/index.html`,
main: `apps/${proj}/src/main.ts`,
polyfills: `apps/${proj}/src/polyfills.ts`,
tsConfig: `apps/${proj}/tsconfig.app.json`,
assets: [`apps/${proj}/src/favicon.ico`, `apps/${proj}/src/assets`],
styles: [`apps/${proj}/src/styles.css`],
scripts: [`apps/${proj}/src/scripts.ts`],
},
configurations: {
production: {
fileReplacements: [
{
replace: `apps/${proj}/src/environments/environment.ts`,
with: `apps/${proj}/src/environments/environment.prod.ts`,
},
],
budgets: [
{
maximumError: '5mb',
maximumWarning: '2mb',
type: 'initial',
},
{
maximumError: '10kb',
maximumWarning: '6kb',
type: 'anyComponentStyle',
},
],
optimization: true,
outputHashing: 'all',
sourceMap: false,
extractCss: true,
namedChunks: false,
extractLicenses: true,
vendorChunk: false,
buildOptimizer: true,
},
},
});
expect(updatedAngularCLIJson.projects[proj].architect.serve).toEqual({
builder: '@angular-devkit/build-angular:dev-server',
options: {
browserTarget: 'proj:build',
},
configurations: {
production: {
browserTarget: 'proj:build:production',
},
},
});
expect(updatedAngularCLIJson.projects[proj].architect.test).toEqual({
builder: '@angular-devkit/build-angular:karma',
options: {
main: `apps/${proj}/src/test.ts`,
polyfills: `apps/${proj}/src/polyfills.ts`,
tsConfig: `apps/${proj}/tsconfig.spec.json`,
karmaConfig: `apps/${proj}/karma.conf.js`,
styles: [`apps/${proj}/src/styles.css`],
scripts: [`apps/${proj}/src/scripts.ts`],
assets: [`apps/${proj}/src/favicon.ico`, `apps/${proj}/src/assets`],
},
});
expect(updatedAngularCLIJson.projects[proj].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
tsConfig: [
`apps/${proj}/tsconfig.app.json`,
`apps/${proj}/tsconfig.spec.json`,
],
exclude: ['**/node_modules/**'],
},
});
expect(updatedAngularCLIJson.projects[`${proj}-e2e`].root).toEqual(
`apps/${proj}-e2e`
);
expect(
updatedAngularCLIJson.projects[`${proj}-e2e`].architect.e2e
).toEqual({
builder: '@angular-devkit/build-angular:protractor',
configurations: {
production: {
devServerTarget: `${proj}:serve:production`,
},
},
options: {
protractorConfig: `apps/${proj}-e2e/protractor.conf.js`,
devServerTarget: `${proj}:serve`,
},
});
expect(
updatedAngularCLIJson.projects[`${proj}-e2e`].architect.lint
).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
tsConfig: `apps/${proj}-e2e/tsconfig.json`,
exclude: ['**/node_modules/**'],
},
});
const updatedTslint = readJson('tslint.json');
expect(updatedTslint.rules['nx-enforce-module-boundaries']).toEqual([
true,
{
allow: [],
depConstraints: [{ sourceTag: '*', onlyDependOnLibsWithTags: ['*'] }],
},
]);
runCLI('build --prod --outputHashing none');
checkFilesExist(`dist/apps/${proj}/main-es2015.js`);
});
it('should generate a workspace and not change dependencies, devDependencies, or vscode extensions if they already exist', () => {
// create a new AngularCLI app
proj = uniq('proj');
runNgNew(proj);
const schematicsVersion = '12.0.0';
const ngrxVersion = '12.0.0';
// update package.json
const existingPackageJson = readJson('package.json');
existingPackageJson.devDependencies['@nrwl/workspace'] =
schematicsVersion;
existingPackageJson.dependencies['@ngrx/store'] = ngrxVersion;
existingPackageJson.dependencies['@ngrx/effects'] = ngrxVersion;
existingPackageJson.dependencies['@ngrx/router-store'] = ngrxVersion;
existingPackageJson.devDependencies['@ngrx/store-devtools'] = ngrxVersion;
updateFile('package.json', JSON.stringify(existingPackageJson, null, 2));
updateFile(
'.vscode/extensions.json',
JSON.stringify({
recommendations: ['eamodio.gitlens', 'angular.ng-template'],
})
);
// run the command
runNgAdd('--npm-scope projscope --skip-install');
// check that dependencies and devDependencies remained the same
const packageJson = readJson('package.json');
expect(packageJson.devDependencies['@nrwl/workspace']).toEqual(
schematicsVersion
);
expect(packageJson.dependencies['@ngrx/store']).toEqual(ngrxVersion);
expect(packageJson.dependencies['@ngrx/effects']).toEqual(ngrxVersion);
expect(packageJson.dependencies['@ngrx/router-store']).toEqual(
ngrxVersion
);
expect(packageJson.devDependencies['@ngrx/store-devtools']).toEqual(
ngrxVersion
);
expect(readJson('.vscode/extensions.json').recommendations).toEqual([
'eamodio.gitlens',
'angular.ng-template',
'nrwl.angular-console',
'dbaeumer.vscode-eslint',
'esbenp.prettier-vscode',
]);
});
it('should handle different types of errors', () => {
// create a new AngularCLI app
proj = uniq('proj');
runNgNew(proj);
// Only remove e2e directory
runCommand('mv e2e e2e-bak');
try {
runNgAdd('--npm-scope projscope --skip-install');
fail('Did not handle not having a e2e directory');
} catch (e) {
expect(e.stderr.toString()).toContain(
'Your workspace could not be converted into an Nx Workspace because of the above error.'
);
}
// Put e2e back
runCommand('mv e2e-bak e2e');
// Remove package.json
runCommand('mv package.json package.json.bak');
try {
runNgAdd('--npm-scope projscope --skip-install');
fail('Did not handle not having a package.json');
} catch (e) {
expect(e.stderr.toString()).toContain(
'Your workspace could not be converted into an Nx Workspace because of the above error.'
);
}
// Put package.json back
runCommand('mv package.json.bak package.json');
// Remove src
runCommand('mv src src-bak');
try {
runNgAdd('--npm-scope projscope --skip-install');
fail('Did not handle not having a src directory');
} catch (e) {
expect(e.stderr.toString()).toContain('Path: src does not exist');
}
// Put src back
runCommand('mv src-bak src');
});
it('should support preserveAngularCLILayout', () => {
proj = uniq('proj');
runNgNew(proj);
runNgAdd('--preserveAngularCLILayout');
const updatedAngularCLIJson = readJson('angular.json');
expect(updatedAngularCLIJson.projects[proj].root).toEqual('');
expect(updatedAngularCLIJson.projects[proj].sourceRoot).toEqual('src');
const output = runCLI('build');
expect(output).toContain(`> ng run ${proj}:build`);
});
});
});

View File

@ -0,0 +1,10 @@
module.exports = {
preset: '../../jest.preset.js',
transform: {
'^.+\\.[tj]sx?$': 'ts-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
maxWorkers: 1,
globals: { 'ts-jest': { tsconfig: '<rootDir>/tsconfig.spec.json' } },
displayName: 'e2e-angular-extensions',
};

View File

@ -0,0 +1,34 @@
{
"root": "e2e/angular-extensions",
"sourceRoot": "e2e/angular-extensions",
"projectType": "application",
"targets": {
"e2e": {
"executor": "@nrwl/workspace:run-commands",
"options": {
"commands": [
{
"command": "yarn e2e-start-local-registry"
},
{
"command": "yarn e2e-build-package-publish"
},
{
"command": "nx run-e2e-tests e2e-angular-extensions"
}
],
"parallel": false
}
},
"run-e2e-tests": {
"executor": "@nrwl/jest:jest",
"options": {
"jestConfig": "e2e/angular-extensions/jest.config.js",
"passWithNoTests": true,
"runInBand": true
},
"outputs": ["coverage/e2e/angular-extensions"]
}
},
"implicitDependencies": ["angular"]
}

View File

@ -0,0 +1,88 @@
process.env.SELECTED_CLI = 'angular';
import {
getSelectedPackageManager,
newProject,
readFile,
readJson,
removeProject,
runCLI,
uniq,
updateFile,
} from '@nrwl/e2e/utils';
import { names } from '@nrwl/devkit';
// TODO: Check why this fails on yarn and npm
describe('Angular Package', () => {
describe('app builder', () => {
let app;
let buildableLib;
let proj: string;
// This fails with pnpm due to incompatibilities with ngcc.
// Since this suite has a single test, we wrap everything to avoid the hooks to run and
// waste time.
if (getSelectedPackageManager() !== 'pnpm') {
beforeEach(() => {
app = uniq('app');
buildableLib = uniq('buildlib1');
proj = newProject();
runCLI(
`generate @nrwl/angular:app ${app} --style=css --no-interactive`
);
runCLI(
`generate @nrwl/angular:library ${buildableLib} --buildable=true --no-interactive`
);
// update the app module to include a ref to the buildable lib
updateFile(
`apps/${app}/src/app/app.module.ts`,
`
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import {${
names(buildableLib).className
}Module} from '@${proj}/${buildableLib}';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, ${names(buildableLib).className}Module],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
`
);
// update the angular.json
const workspaceJson = readJson(`angular.json`);
workspaceJson.projects[app].architect.build.builder =
'@nrwl/angular:webpack-browser';
updateFile('angular.json', JSON.stringify(workspaceJson, null, 2));
});
afterEach(() => removeProject({ onlyOnCI: true }));
it('should build the dependent buildable lib as well as the app', () => {
const libOutput = runCLI(
`build ${app} --with-deps --configuration=development`
);
expect(libOutput).toContain(
`Building entry point '@${proj}/${buildableLib}'`
);
expect(libOutput).toContain(`nx run ${app}:build:development`);
// to proof it has been built from source the "main.js" should actually contain
// the path to dist
const mainBundle = readFile(`dist/apps/${app}/main.js`);
expect(mainBundle).toContain(`dist/libs/${buildableLib}`);
});
} else {
it('Skip tests with pnpm', () => {});
}
});
});

View File

@ -0,0 +1,178 @@
process.env.SELECTED_CLI = 'angular';
import {
checkFilesExist,
getSelectedPackageManager,
newProject,
readJson,
removeProject,
runCLI,
uniq,
updateFile,
} from '@nrwl/e2e/utils';
import { names } from '@nrwl/devkit';
describe('Angular Package', () => {
['publishable', 'buildable'].forEach((testConfig) => {
describe(`library builder - ${testConfig}`, () => {
/**
* Graph:
*
* childLib
* /
* parentLib =>
* \
* \
* childLib2
*
*/
let parentLib: string;
let childLib: string;
let childLib2: string;
let proj: string;
beforeEach(() => {
parentLib = uniq('parentlib');
childLib = uniq('childlib');
childLib2 = uniq('childlib2');
proj = newProject();
if (testConfig === 'buildable') {
runCLI(
`generate @nrwl/angular:library ${parentLib} --buildable=true --no-interactive`
);
runCLI(
`generate @nrwl/angular:library ${childLib} --buildable=true --no-interactive`
);
runCLI(
`generate @nrwl/angular:library ${childLib2} --buildable=true --no-interactive`
);
} else {
runCLI(
`generate @nrwl/angular:library ${parentLib} --publishable=true --importPath=@${proj}/${parentLib} --no-interactive`
);
runCLI(
`generate @nrwl/angular:library ${childLib} --publishable=true --importPath=@${proj}/${childLib} --no-interactive`
);
runCLI(
`generate @nrwl/angular:library ${childLib2} --publishable=true --importPath=@${proj}/${childLib2} --no-interactive`
);
// create secondary entrypoint
updateFile(
`libs/${childLib}/sub/package.json`,
`
{
"ngPackage": {}
}
`
);
updateFile(
`libs/${childLib}/sub/src/lib/sub.module.ts`,
`
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({ imports: [CommonModule] })
export class SubModule {}
`
);
updateFile(
`libs/${childLib}/sub/src/public_api.ts`,
`export * from './lib/sub.module';`
);
updateFile(
`libs/${childLib}/sub/src/index.ts`,
`export * from './public_api';`
);
updateFile(`tsconfig.base.json`, (s) => {
return s.replace(
`"@${proj}/${childLib}": ["libs/${childLib}/src/index.ts"],`,
`"@${proj}/${childLib}": ["libs/${childLib}/src/index.ts"],
"@${proj}/${childLib}/sub": ["libs/${childLib}/sub/src/index.ts"],
`
);
});
}
// create dependencies by importing
const createDep = (parent, children: string[]) => {
let moduleContent = `
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
${children
.map(
(entry) =>
`import { ${
names(entry).className
}Module } from '@${proj}/${entry}';`
)
.join('\n')}
`;
if (testConfig === 'publishable') {
moduleContent += `
import { SubModule } from '@${proj}/${childLib}/sub';
@NgModule({
imports: [CommonModule, ${children
.map((entry) => `${names(entry).className}Module`)
.join(',')}, SubModule]
})
export class ${names(parent).className}Module {}`;
}
updateFile(
`libs/${parent}/src/lib/${parent}.module.ts`,
moduleContent
);
};
createDep(parentLib, [childLib, childLib2]);
});
afterEach(() => removeProject({ onlyOnCI: true }));
it('empty test to make jest happy', () => {});
// These fail with pnpm due to incompatibilities with ngcc for buildable libraries.
if (
getSelectedPackageManager() !== 'pnpm' ||
testConfig === 'publishable'
) {
it('should build the library when it does not have any deps', () => {
runCLI(`build ${childLib}`);
checkFilesExist(`dist/libs/${childLib}/package.json`);
});
it('should properly add references to any dependency into the parent package.json', () => {
runCLI(`build ${childLib}`);
runCLI(`build ${childLib2}`);
runCLI(`build ${parentLib}`);
checkFilesExist(
`dist/libs/${childLib}/package.json`,
`dist/libs/${childLib2}/package.json`,
`dist/libs/${parentLib}/package.json`
);
const jsonFile = readJson(`dist/libs/${parentLib}/package.json`);
expect(jsonFile.dependencies['tslib']).toMatch(/\^2\.\d+\.\d+/); // match any ^2.x.x
expect(
jsonFile.peerDependencies[`@${proj}/${childLib}`]
).toBeDefined();
expect(
jsonFile.peerDependencies[`@${proj}/${childLib2}`]
).toBeDefined();
expect(jsonFile.peerDependencies['@angular/common']).toBeDefined();
expect(jsonFile.peerDependencies['@angular/core']).toBeDefined();
});
}
});
});
});

View File

@ -0,0 +1,50 @@
import {
expectTestsPass,
getSelectedPackageManager,
newProject,
removeProject,
runCLI,
runCLIAsync,
uniq,
} from '@nrwl/e2e/utils';
describe('Angular Package', () => {
describe('router config', () => {
beforeEach(() => newProject());
afterEach(() => removeProject({ onlyOnCI: true }));
it('should support router config generation (lazy)', async () => {
if (getSelectedPackageManager() !== 'npm') {
const myapp = uniq('myapp');
const mylib = uniq('mylib');
runCLI(
`generate @nrwl/angular:app ${myapp} --directory=myDir --routing`
);
runCLI(
`generate @nrwl/angular:lib ${mylib} --directory=myDir --routing --lazy --parentModule=apps/my-dir/${myapp}/src/app/app.module.ts`
);
runCLI(`build my-dir-${myapp} --aot`);
expectTestsPass(await runCLIAsync(`test my-dir-${myapp} --no-watch`));
}
}, 1000000);
it('should support router config generation (eager)', async () => {
// TODO: npm build is failing for Angular because of webpack 4
// remove this condition once `node` is migrated to webpack 5
if (getSelectedPackageManager() !== 'npm') {
const myapp = uniq('myapp');
runCLI(
`generate @nrwl/angular:app ${myapp} --directory=myDir --routing`
);
const mylib = uniq('mylib');
runCLI(
`generate @nrwl/angular:lib ${mylib} --directory=myDir --routing --parentModule=apps/my-dir/${myapp}/src/app/app.module.ts`
);
runCLI(`build my-dir-${myapp} --aot`);
expectTestsPass(await runCLIAsync(`test my-dir-${myapp} --no-watch`));
}
}, 1000000);
});
});

View File

@ -0,0 +1,72 @@
import {
expectTestsPass,
newProject,
readJson,
removeProject,
runCLI,
runCLIAsync,
uniq,
} from '@nrwl/e2e/utils';
describe('Angular Package', () => {
describe('ngrx', () => {
beforeEach(() => newProject());
afterAll(() => removeProject({ onlyOnCI: true }));
it('should work', async () => {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --no-interactive`);
// Generate root ngrx state management
runCLI(
`generate @nrwl/angular:ngrx users --module=apps/${myapp}/src/app/app.module.ts --root --minimal=false --syntax=classes --useDataPersistence=true`
);
const packageJson = readJson('package.json');
expect(packageJson.dependencies['@ngrx/store']).toBeDefined();
expect(packageJson.dependencies['@ngrx/effects']).toBeDefined();
expect(packageJson.dependencies['@ngrx/router-store']).toBeDefined();
expect(packageJson.devDependencies['@ngrx/store-devtools']).toBeDefined();
const mylib = uniq('mylib');
// Generate feature library and ngrx state within that library
runCLI(`g @nrwl/angular:lib ${mylib} --prefix=fl`);
runCLI(
`generate @nrwl/angular:ngrx flights --module=libs/${mylib}/src/lib/${mylib}.module.ts --facade --syntax=classes`
);
expect(runCLI(`build ${myapp}`)).toMatch(/main\.[a-z0-9]+\.js/);
expectTestsPass(await runCLIAsync(`test ${myapp} --no-watch`));
expectTestsPass(await runCLIAsync(`test ${mylib} --no-watch`));
}, 1000000);
it('should work with creators', async () => {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --routing --no-interactive`);
// Generate root ngrx state management
runCLI(
`generate @nrwl/angular:ngrx users --module=apps/${myapp}/src/app/app.module.ts --root`
);
const packageJson = readJson('package.json');
expect(packageJson.dependencies['@ngrx/entity']).toBeDefined();
expect(packageJson.dependencies['@ngrx/store']).toBeDefined();
expect(packageJson.dependencies['@ngrx/effects']).toBeDefined();
expect(packageJson.dependencies['@ngrx/router-store']).toBeDefined();
expect(packageJson.devDependencies['@ngrx/schematics']).toBeDefined();
expect(packageJson.devDependencies['@ngrx/store-devtools']).toBeDefined();
const mylib = uniq('mylib');
// Generate feature library and ngrx state within that library
runCLI(`g @nrwl/angular:lib ${mylib} --prefix=fl`);
const flags = `--facade --barrels`;
runCLI(
`generate @nrwl/angular:ngrx flights --module=libs/${mylib}/src/lib/${mylib}.module.ts ${flags}`
);
expect(runCLI(`build ${myapp}`)).toMatch(/main\.[a-z0-9]+\.js/);
expectTestsPass(await runCLIAsync(`test ${myapp} --no-watch`));
expectTestsPass(await runCLIAsync(`test ${mylib} --no-watch`));
}, 1000000);
});
});

View File

@ -14,7 +14,8 @@ import {
} from '@nrwl/e2e/utils';
import { writeFileSync } from 'fs';
describe('Storybook schematics', () => {
describe('Angular Package', () => {
describe('storybook schematics', () => {
let proj: string;
beforeEach(() => (proj = newProject()));
@ -160,7 +161,9 @@ describe('Storybook schematics', () => {
// mkdirSync(tmpProjPath(`apps/${myReactLib}-e2e/src/integration`));
writeFileSync(
tmpProjPath(`apps/${myReactLib}-e2e/src/integration/button.spec.ts`),
tmpProjPath(
`apps/${myReactLib}-e2e/src/integration/button.spec.ts`
),
`
describe('react-ui', () => {
it('should render the component', () => {
@ -191,9 +194,9 @@ describe('Storybook schematics', () => {
runCLI(`run ${myAngularLib}:build-storybook`);
checkFilesExist(`dist/storybook/${myAngularLib}/index.html`);
expect(readFile(`dist/storybook/${myAngularLib}/index.html`)).toContain(
`<title>Storybook</title>`
);
expect(
readFile(`dist/storybook/${myAngularLib}/index.html`)
).toContain(`<title>Storybook</title>`);
}
}, 1000000);
@ -274,6 +277,7 @@ describe('Storybook schematics', () => {
}, 1000000);
});
});
});
export function createTestUILib(libName: string): void {
runCLI(

View File

@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"types": ["node", "jest"]
},
"include": [],
"files": [],
"references": [
{
"path": "./tsconfig.spec.json"
}
]
}

View File

@ -0,0 +1,16 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": [
"**/*.test.ts",
"**/*.spec.ts",
"**/*.spec.tsx",
"**/*.spec.js",
"**/*.spec.jsx",
"**/*.d.ts"
]
}

View File

@ -1,82 +0,0 @@
process.env.SELECTED_CLI = 'angular';
import {
getSelectedPackageManager,
newProject,
readFile,
readJson,
removeProject,
runCLI,
uniq,
updateFile,
} from '@nrwl/e2e/utils';
import { names } from '@nrwl/devkit';
// TODO: Check why this fails on yarn and npm
xdescribe('Angular Nrwl app builder', () => {
let app;
let buildableLib;
let proj: string;
// This fails with pnpm due to incompatibilities with ngcc.
// Since this suite has a single test, we wrap everything to avoid the hooks to run and
// waste time.
if (getSelectedPackageManager() !== 'pnpm') {
beforeEach(() => {
app = uniq('app');
buildableLib = uniq('buildlib1');
proj = newProject();
runCLI(`generate @nrwl/angular:app ${app} --style=css --no-interactive`);
runCLI(
`generate @nrwl/angular:library ${buildableLib} --buildable=true --no-interactive`
);
// update the app module to include a ref to the buildable lib
updateFile(
`apps/${app}/src/app/app.module.ts`,
`
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import {${
names(buildableLib).className
}Module} from '@${proj}/${buildableLib}';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, ${names(buildableLib).className}Module],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
`
);
// update the angular.json
const workspaceJson = readJson(`angular.json`);
workspaceJson.projects[app].architect.build.builder =
'@nrwl/angular:webpack-browser';
updateFile('angular.json', JSON.stringify(workspaceJson, null, 2));
});
afterEach(() => removeProject({ onlyOnCI: true }));
it('should build the dependent buildable lib as well as the app', () => {
const libOutput = runCLI(`build ${app} --with-deps`);
expect(libOutput).toContain(
`Building entry point '@${proj}/${buildableLib}'`
);
expect(libOutput).toContain(`nx run ${app}:build`);
// to proof it has been built from source the "main.js" should actually contain
// the path to dist
const mainBundle = readFile(`dist/apps/${app}/main.js`);
expect(mainBundle).toContain(`dist/libs/${buildableLib}`);
});
} else {
it('Skip tests with pnpm', () => {});
}
});

View File

@ -1,202 +0,0 @@
import * as path from 'path';
import {
checkFilesExist,
expectTestsPass,
getSelectedPackageManager,
getSize,
killPorts,
newProject,
removeProject,
runCLI,
runCLIAsync,
tmpProjPath,
uniq,
updateFile,
runCypressTests,
} from '@nrwl/e2e/utils';
import { names } from '@nrwl/devkit';
describe('Angular Package', () => {
let proj: string;
beforeEach(() => (proj = newProject()));
afterEach(() => removeProject({ onlyOnCI: true }));
it('should work', async () => {
// TODO: npm build is failing for Angular because of webpack 4
// remove this condition once `node` is migrated to webpack 5
if (getSelectedPackageManager() !== 'npm') {
const myapp = uniq('myapp');
const mylib = uniq('mylib');
runCLI(
`generate @nrwl/angular:app ${myapp} --directory=myDir --no-interactive`
);
runCLI(
`generate @nrwl/angular:lib ${mylib} --directory=myDir --add-module-spec --no-interactive`
);
updateFile(
`apps/my-dir/${myapp}/src/app/app.module.ts`,
`
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MyDir${
names(mylib).className
}Module } from '@${proj}/my-dir/${mylib}';
import { AppComponent } from './app.component';
@NgModule({
imports: [BrowserModule, MyDir${names(mylib).className}Module],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
`
);
runCLI(`build my-dir-${myapp} --prod --output-hashing none`);
checkFilesExist(`dist/apps/my-dir/${myapp}/main.js`);
// This is a loose requirement because there are a lot of
// influences external from this project that affect this.
const es2015BundleSize = getSize(
tmpProjPath(`dist/apps/my-dir/${myapp}/main.js`)
);
console.log(
`The current es2015 bundle size is ${es2015BundleSize / 1000} KB`
);
expect(es2015BundleSize).toBeLessThanOrEqual(160000);
// running tests for the app
expectTestsPass(await runCLIAsync(`test my-dir-${myapp} --no-watch`));
// running tests for the lib
expectTestsPass(await runCLIAsync(`test my-dir-${mylib} --no-watch`));
if (runCypressTests()) {
const e2eResults = runCLI(
`e2e my-dir-${myapp}-e2e --headless --no-watch`
);
expect(e2eResults).toContain('All specs passed!');
expect(await killPorts()).toBeTruthy();
}
}
}, 1000000);
it('should support router config generation (lazy)', async () => {
if (getSelectedPackageManager() !== 'npm') {
const myapp = uniq('myapp');
const mylib = uniq('mylib');
runCLI(`generate @nrwl/angular:app ${myapp} --directory=myDir --routing`);
runCLI(
`generate @nrwl/angular:lib ${mylib} --directory=myDir --routing --lazy --parentModule=apps/my-dir/${myapp}/src/app/app.module.ts`
);
runCLI(`build my-dir-${myapp} --aot`);
expectTestsPass(await runCLIAsync(`test my-dir-${myapp} --no-watch`));
}
}, 1000000);
it('should support router config generation (eager)', async () => {
// TODO: npm build is failing for Angular because of webpack 4
// remove this condition once `node` is migrated to webpack 5
if (getSelectedPackageManager() !== 'npm') {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --directory=myDir --routing`);
const mylib = uniq('mylib');
runCLI(
`generate @nrwl/angular:lib ${mylib} --directory=myDir --routing --parentModule=apps/my-dir/${myapp}/src/app/app.module.ts`
);
runCLI(`build my-dir-${myapp} --aot`);
expectTestsPass(await runCLIAsync(`test my-dir-${myapp} --no-watch`));
}
}, 1000000);
it('should support Ivy', async () => {
// TODO: npm build is failing for Angular because of webpack 4
// remove this condition once `node` is migrated to webpack 5
if (getSelectedPackageManager() !== 'npm') {
const myapp = uniq('myapp');
runCLI(
`generate @nrwl/angular:app ${myapp} --directory=myDir --routing --enable-ivy`
);
runCLI(`build my-dir-${myapp} --aot`);
expectTestsPass(await runCLIAsync(`test my-dir-${myapp} --no-watch`));
}
}, 1000000);
it('should support building in parallel', () => {
// TODO: npm build is failing for Angular because of webpack 4
// remove this condition once `node` is migrated to webpack 5
if (getSelectedPackageManager() !== 'npm') {
if (getSelectedPackageManager() === 'pnpm') {
// TODO: This tests fails with pnpm but we should still enable this for other package managers
return;
}
const myapp = uniq('myapp');
const myapp2 = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp}`);
runCLI(`generate @nrwl/angular:app ${myapp2}`);
runCLI('run-many --target build --all --parallel');
}
});
it('should support eslint and pass linting on the standard generated code', async () => {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --linter=eslint`);
expect(runCLI(`lint ${myapp}`)).toContain('All files pass linting.');
const mylib = uniq('mylib');
runCLI(`generate @nrwl/angular:lib ${mylib} --linter=eslint`);
expect(runCLI(`lint ${mylib}`)).toContain('All files pass linting.');
});
it('should support eslint and successfully lint external HTML files and inline templates', async () => {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --linter=eslint`);
const templateWhichFailsBananaInBoxLintCheck = `<div ([foo])="bar"></div>`;
const wrappedAsInlineTemplate = `
import { Component } from '@angular/core';
@Component({
selector: 'inline-template-component',
template: \`
${templateWhichFailsBananaInBoxLintCheck}
\`,
})
export class InlineTemplateComponent {}
`;
// External HTML template file
updateFile(
`apps/${myapp}/src/app/app.component.html`,
templateWhichFailsBananaInBoxLintCheck
);
// Inline template within component.ts file
updateFile(
`apps/${myapp}/src/app/inline-template.component.ts`,
wrappedAsInlineTemplate
);
const appLintStdOut = runCLI(`lint ${myapp}`, { silenceError: true });
expect(appLintStdOut).toContain(
path.normalize(`apps/${myapp}/src/app/app.component.html`)
);
expect(appLintStdOut).toContain(`1:6`);
expect(appLintStdOut).toContain(`Invalid binding syntax`);
expect(appLintStdOut).toContain(
path.normalize(`apps/${myapp}/src/app/inline-template.component.ts`)
);
expect(appLintStdOut).toContain(
`The selector should start with one of these prefixes`
);
expect(appLintStdOut).toContain(`7:18`);
});
});

View File

@ -1,171 +0,0 @@
process.env.SELECTED_CLI = 'angular';
import {
checkFilesExist,
getSelectedPackageManager,
newProject,
readJson,
removeProject,
runCLI,
uniq,
updateFile,
} from '@nrwl/e2e/utils';
import { names } from '@nrwl/devkit';
['publishable', 'buildable'].forEach((testConfig) => {
describe('Build Angular library', () => {
/**
* Graph:
*
* childLib
* /
* parentLib =>
* \
* \
* childLib2
*
*/
let parentLib: string;
let childLib: string;
let childLib2: string;
let proj: string;
beforeEach(() => {
parentLib = uniq('parentlib');
childLib = uniq('childlib');
childLib2 = uniq('childlib2');
proj = newProject();
if (testConfig === 'buildable') {
runCLI(
`generate @nrwl/angular:library ${parentLib} --buildable=true --no-interactive`
);
runCLI(
`generate @nrwl/angular:library ${childLib} --buildable=true --no-interactive`
);
runCLI(
`generate @nrwl/angular:library ${childLib2} --buildable=true --no-interactive`
);
} else {
runCLI(
`generate @nrwl/angular:library ${parentLib} --publishable=true --importPath=@${proj}/${parentLib} --no-interactive`
);
runCLI(
`generate @nrwl/angular:library ${childLib} --publishable=true --importPath=@${proj}/${childLib} --no-interactive`
);
runCLI(
`generate @nrwl/angular:library ${childLib2} --publishable=true --importPath=@${proj}/${childLib2} --no-interactive`
);
// create secondary entrypoint
updateFile(
`libs/${childLib}/sub/package.json`,
`
{
"ngPackage": {}
}
`
);
updateFile(
`libs/${childLib}/sub/src/lib/sub.module.ts`,
`
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({ imports: [CommonModule] })
export class SubModule {}
`
);
updateFile(
`libs/${childLib}/sub/src/public_api.ts`,
`export * from './lib/sub.module';`
);
updateFile(
`libs/${childLib}/sub/src/index.ts`,
`export * from './public_api';`
);
updateFile(`tsconfig.base.json`, (s) => {
return s.replace(
`"@${proj}/${childLib}": ["libs/${childLib}/src/index.ts"],`,
`"@${proj}/${childLib}": ["libs/${childLib}/src/index.ts"],
"@${proj}/${childLib}/sub": ["libs/${childLib}/sub/src/index.ts"],
`
);
});
}
// create dependencies by importing
const createDep = (parent, children: string[]) => {
let moduleContent = `
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
${children
.map(
(entry) =>
`import { ${
names(entry).className
}Module } from '@${proj}/${entry}';`
)
.join('\n')}
`;
if (testConfig === 'publishable') {
moduleContent += `
import { SubModule } from '@${proj}/${childLib}/sub';
@NgModule({
imports: [CommonModule, ${children
.map((entry) => `${names(entry).className}Module`)
.join(',')}, SubModule]
})
export class ${names(parent).className}Module {}`;
}
updateFile(`libs/${parent}/src/lib/${parent}.module.ts`, moduleContent);
};
createDep(parentLib, [childLib, childLib2]);
});
afterEach(() => removeProject({ onlyOnCI: true }));
it('empty test to make jest happy', () => {});
// These fail with pnpm due to incompatibilities with ngcc for buildable libraries.
if (
getSelectedPackageManager() !== 'pnpm' ||
testConfig === 'publishable'
) {
it('should build the library when it does not have any deps', () => {
runCLI(`build ${childLib}`);
checkFilesExist(`dist/libs/${childLib}/package.json`);
});
it('should properly add references to any dependency into the parent package.json', () => {
runCLI(`build ${childLib}`);
runCLI(`build ${childLib2}`);
runCLI(`build ${parentLib}`);
checkFilesExist(
`dist/libs/${childLib}/package.json`,
`dist/libs/${childLib2}/package.json`,
`dist/libs/${parentLib}/package.json`
);
const jsonFile = readJson(`dist/libs/${parentLib}/package.json`);
expect(jsonFile.dependencies['tslib']).toMatch(/\^2\.\d+\.\d+/); // match any ^2.x.x
expect(jsonFile.peerDependencies[`@${proj}/${childLib}`]).toBeDefined();
expect(
jsonFile.peerDependencies[`@${proj}/${childLib2}`]
).toBeDefined();
expect(jsonFile.peerDependencies['@angular/common']).toBeDefined();
expect(jsonFile.peerDependencies['@angular/core']).toBeDefined();
});
}
});
});

View File

@ -1,46 +0,0 @@
process.env.SELECTED_CLI = 'angular';
import {
expectTestsPass,
newProject,
readJson,
removeProject,
runCLI,
runCLIAsync,
uniq,
updateFile,
} from '@nrwl/e2e/utils';
describe('new config format', () => {
beforeEach(() => newProject());
afterEach(() => removeProject({ onlyOnCI: true }));
it('should work', async () => {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --no-interactive`);
// update the angular.json
const workspaceJson = readJson(`angular.json`);
workspaceJson.version = 2;
workspaceJson.projects[myapp].targets = updateConfig(
workspaceJson.projects[myapp].architect
);
workspaceJson.generators = workspaceJson.schematics;
delete workspaceJson.schematics;
updateFile('angular.json', JSON.stringify(workspaceJson, null, 2));
const myapp2 = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp2} --no-interactive`);
expectTestsPass(await runCLIAsync(`test ${myapp2} --no-watch`));
}, 1000000);
});
function updateConfig(targets: any) {
const res = {};
Object.entries(targets).forEach(([name, t]: any) => {
t.executor = t.builder;
delete t.builder;
res[name] = t;
});
return res;
}

View File

@ -1,359 +0,0 @@
process.env.SELECTED_CLI = 'angular';
import {
checkFilesExist,
readJson,
removeProject,
runCLI,
runCommand,
runNgAdd,
runNgNew,
uniq,
updateFile,
} from '@nrwl/e2e/utils';
// TODO: Check why generated angular app is different
xdescribe('Nrwl Convert to Nx Workspace', () => {
let proj;
afterEach(() => {
removeProject({ onlyOnCI: true });
});
it('should generate a workspace', () => {
proj = uniq('proj');
runNgNew(proj);
// update package.json
const packageJson = readJson('package.json');
packageJson.description = 'some description';
updateFile('package.json', JSON.stringify(packageJson, null, 2));
// confirm that @nrwl and @ngrx dependencies do not exist yet
expect(packageJson.devDependencies['@nrwl/workspace']).not.toBeDefined();
expect(packageJson.dependencies['@ngrx/store']).not.toBeDefined();
expect(packageJson.dependencies['@ngrx/effects']).not.toBeDefined();
expect(packageJson.dependencies['@ngrx/router-store']).not.toBeDefined();
expect(
packageJson.devDependencies['@ngrx/store-devtools']
).not.toBeDefined();
// update tsconfig.json
const tsconfigJson = readJson('tsconfig.json');
tsconfigJson.compilerOptions.paths = { a: ['b'] };
updateFile('tsconfig.json', JSON.stringify(tsconfigJson, null, 2));
updateFile('src/scripts.ts', '');
// update angular-cli.json
const angularCLIJson = readJson('angular.json');
angularCLIJson.projects[proj].architect.build.options.scripts =
angularCLIJson.projects[proj].architect.test.options.scripts = [
'src/scripts.ts',
];
angularCLIJson.projects[proj].architect.test.options.styles = [
'src/styles.css',
];
updateFile('angular.json', JSON.stringify(angularCLIJson, null, 2));
// run the command
runNgAdd('--npm-scope projscope');
// check that prettier config exits and that files have been moved!
checkFilesExist(
'.vscode/extensions.json',
'.prettierrc',
`apps/${proj}/src/main.ts`,
`apps/${proj}/src/app/app.module.ts`
);
expect(readJson('.vscode/extensions.json').recommendations).toEqual([
'nrwl.angular-console',
'angular.ng-template',
'dbaeumer.vscode-eslint',
'esbenp.prettier-vscode',
]);
// check that package.json got merged
const updatedPackageJson = readJson('package.json');
expect(updatedPackageJson.description).toEqual('some description');
expect(updatedPackageJson.scripts).toEqual({
ng: 'nx',
nx: 'nx',
start: 'ng serve',
build: 'ng build',
test: 'ng test',
lint: 'nx workspace-lint && ng lint',
e2e: 'ng e2e',
'affected:apps': 'nx affected:apps',
'affected:libs': 'nx affected:libs',
'affected:build': 'nx affected:build',
'affected:e2e': 'nx affected:e2e',
'affected:test': 'nx affected:test',
'affected:lint': 'nx affected:lint',
'affected:dep-graph': 'nx affected:dep-graph',
affected: 'nx affected',
format: 'nx format:write',
'format:write': 'nx format:write',
'format:check': 'nx format:check',
update: 'ng update @nrwl/workspace',
'update:check': 'ng update',
postinstall: 'node ./decorate-angular-cli.js',
'dep-graph': 'nx dep-graph',
'workspace-generator': 'nx workspace-generator',
help: 'nx help',
});
expect(updatedPackageJson.devDependencies['@nrwl/workspace']).toBeDefined();
expect(updatedPackageJson.devDependencies['@angular/cli']).toBeDefined();
const nxJson = readJson('nx.json');
expect(nxJson).toEqual({
npmScope: 'projscope',
implicitDependencies: {
'angular.json': '*',
'package.json': '*',
'tslint.json': '*',
'.eslintrc.json': '*',
'tsconfig.base.json': '*',
'nx.json': '*',
},
projects: {
[`${proj}`]: {
tags: [],
},
[`${proj}-e2e`]: {
tags: [],
},
},
});
// check if angular-cli.json get merged
const updatedAngularCLIJson = readJson('angular.json');
expect(updatedAngularCLIJson.projects[proj].root).toEqual(`apps/${proj}`);
expect(updatedAngularCLIJson.projects[proj].sourceRoot).toEqual(
`apps/${proj}/src`
);
expect(updatedAngularCLIJson.projects[proj].architect.build).toEqual({
builder: '@angular-devkit/build-angular:browser',
options: {
aot: true,
outputPath: `dist/apps/${proj}`,
index: `apps/${proj}/src/index.html`,
main: `apps/${proj}/src/main.ts`,
polyfills: `apps/${proj}/src/polyfills.ts`,
tsConfig: `apps/${proj}/tsconfig.app.json`,
assets: [`apps/${proj}/src/favicon.ico`, `apps/${proj}/src/assets`],
styles: [`apps/${proj}/src/styles.css`],
scripts: [`apps/${proj}/src/scripts.ts`],
},
configurations: {
production: {
fileReplacements: [
{
replace: `apps/${proj}/src/environments/environment.ts`,
with: `apps/${proj}/src/environments/environment.prod.ts`,
},
],
budgets: [
{
maximumError: '5mb',
maximumWarning: '2mb',
type: 'initial',
},
{
maximumError: '10kb',
maximumWarning: '6kb',
type: 'anyComponentStyle',
},
],
optimization: true,
outputHashing: 'all',
sourceMap: false,
extractCss: true,
namedChunks: false,
extractLicenses: true,
vendorChunk: false,
buildOptimizer: true,
},
},
});
expect(updatedAngularCLIJson.projects[proj].architect.serve).toEqual({
builder: '@angular-devkit/build-angular:dev-server',
options: {
browserTarget: 'proj:build',
},
configurations: {
production: {
browserTarget: 'proj:build:production',
},
},
});
expect(updatedAngularCLIJson.projects[proj].architect.test).toEqual({
builder: '@angular-devkit/build-angular:karma',
options: {
main: `apps/${proj}/src/test.ts`,
polyfills: `apps/${proj}/src/polyfills.ts`,
tsConfig: `apps/${proj}/tsconfig.spec.json`,
karmaConfig: `apps/${proj}/karma.conf.js`,
styles: [`apps/${proj}/src/styles.css`],
scripts: [`apps/${proj}/src/scripts.ts`],
assets: [`apps/${proj}/src/favicon.ico`, `apps/${proj}/src/assets`],
},
});
expect(updatedAngularCLIJson.projects[proj].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
tsConfig: [
`apps/${proj}/tsconfig.app.json`,
`apps/${proj}/tsconfig.spec.json`,
],
exclude: ['**/node_modules/**'],
},
});
expect(updatedAngularCLIJson.projects[`${proj}-e2e`].root).toEqual(
`apps/${proj}-e2e`
);
expect(updatedAngularCLIJson.projects[`${proj}-e2e`].architect.e2e).toEqual(
{
builder: '@angular-devkit/build-angular:protractor',
configurations: {
production: {
devServerTarget: `${proj}:serve:production`,
},
},
options: {
protractorConfig: `apps/${proj}-e2e/protractor.conf.js`,
devServerTarget: `${proj}:serve`,
},
}
);
expect(
updatedAngularCLIJson.projects[`${proj}-e2e`].architect.lint
).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
tsConfig: `apps/${proj}-e2e/tsconfig.json`,
exclude: ['**/node_modules/**'],
},
});
const updatedTslint = readJson('tslint.json');
expect(updatedTslint.rules['nx-enforce-module-boundaries']).toEqual([
true,
{
allow: [],
depConstraints: [{ sourceTag: '*', onlyDependOnLibsWithTags: ['*'] }],
},
]);
runCLI('build --prod --outputHashing none');
checkFilesExist(`dist/apps/${proj}/main-es2015.js`);
});
it('should generate a workspace and not change dependencies, devDependencies, or vscode extensions if they already exist', () => {
// create a new AngularCLI app
proj = uniq('proj');
runNgNew(proj);
const schematicsVersion = '12.0.0';
const ngrxVersion = '12.0.0';
// update package.json
const existingPackageJson = readJson('package.json');
existingPackageJson.devDependencies['@nrwl/workspace'] = schematicsVersion;
existingPackageJson.dependencies['@ngrx/store'] = ngrxVersion;
existingPackageJson.dependencies['@ngrx/effects'] = ngrxVersion;
existingPackageJson.dependencies['@ngrx/router-store'] = ngrxVersion;
existingPackageJson.devDependencies['@ngrx/store-devtools'] = ngrxVersion;
updateFile('package.json', JSON.stringify(existingPackageJson, null, 2));
updateFile(
'.vscode/extensions.json',
JSON.stringify({
recommendations: ['eamodio.gitlens', 'angular.ng-template'],
})
);
// run the command
runNgAdd('--npm-scope projscope --skip-install');
// check that dependencies and devDependencies remained the same
const packageJson = readJson('package.json');
expect(packageJson.devDependencies['@nrwl/workspace']).toEqual(
schematicsVersion
);
expect(packageJson.dependencies['@ngrx/store']).toEqual(ngrxVersion);
expect(packageJson.dependencies['@ngrx/effects']).toEqual(ngrxVersion);
expect(packageJson.dependencies['@ngrx/router-store']).toEqual(ngrxVersion);
expect(packageJson.devDependencies['@ngrx/store-devtools']).toEqual(
ngrxVersion
);
expect(readJson('.vscode/extensions.json').recommendations).toEqual([
'eamodio.gitlens',
'angular.ng-template',
'nrwl.angular-console',
'dbaeumer.vscode-eslint',
'esbenp.prettier-vscode',
]);
});
it('should handle different types of errors', () => {
// create a new AngularCLI app
proj = uniq('proj');
runNgNew(proj);
// Only remove e2e directory
runCommand('mv e2e e2e-bak');
try {
runNgAdd('--npm-scope projscope --skip-install');
fail('Did not handle not having a e2e directory');
} catch (e) {
expect(e.stderr.toString()).toContain(
'Your workspace could not be converted into an Nx Workspace because of the above error.'
);
}
// Put e2e back
runCommand('mv e2e-bak e2e');
// Remove package.json
runCommand('mv package.json package.json.bak');
try {
runNgAdd('--npm-scope projscope --skip-install');
fail('Did not handle not having a package.json');
} catch (e) {
expect(e.stderr.toString()).toContain(
'Your workspace could not be converted into an Nx Workspace because of the above error.'
);
}
// Put package.json back
runCommand('mv package.json.bak package.json');
// Remove src
runCommand('mv src src-bak');
try {
runNgAdd('--npm-scope projscope --skip-install');
fail('Did not handle not having a src directory');
} catch (e) {
expect(e.stderr.toString()).toContain('Path: src does not exist');
}
// Put src back
runCommand('mv src-bak src');
});
it('should support preserveAngularCLILayout', () => {
proj = uniq('proj');
runNgNew(proj);
runNgAdd('--preserveAngularCLILayout');
const updatedAngularCLIJson = readJson('angular.json');
expect(updatedAngularCLIJson.projects[proj].root).toEqual('');
expect(updatedAngularCLIJson.projects[proj].sourceRoot).toEqual('src');
const output = runCLI('build');
expect(output).toContain(`> ng run ${proj}:build`);
});
});

View File

@ -1,71 +0,0 @@
import {
expectTestsPass,
newProject,
readJson,
removeProject,
runCLI,
runCLIAsync,
uniq,
} from '@nrwl/e2e/utils';
// TODO: Uncomment when the node is migrated to webpack 5
xdescribe('ngrx', () => {
beforeEach(() => newProject());
afterAll(() => removeProject({ onlyOnCI: true }));
it('should work', async () => {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --no-interactive`);
// Generate root ngrx state management
runCLI(
`generate @nrwl/angular:ngrx users --module=apps/${myapp}/src/app/app.module.ts --root --minimal=false --syntax=classes --useDataPersistence=true`
);
const packageJson = readJson('package.json');
expect(packageJson.dependencies['@ngrx/store']).toBeDefined();
expect(packageJson.dependencies['@ngrx/effects']).toBeDefined();
expect(packageJson.dependencies['@ngrx/router-store']).toBeDefined();
expect(packageJson.devDependencies['@ngrx/store-devtools']).toBeDefined();
const mylib = uniq('mylib');
// Generate feature library and ngrx state within that library
runCLI(`g @nrwl/angular:lib ${mylib} --prefix=fl`);
runCLI(
`generate @nrwl/angular:ngrx flights --module=libs/${mylib}/src/lib/${mylib}.module.ts --facade --syntax=classes`
);
expect(runCLI(`build ${myapp}`)).toMatch(/main\.[a-z0-9]+\.js/);
expectTestsPass(await runCLIAsync(`test ${myapp} --no-watch`));
expectTestsPass(await runCLIAsync(`test ${mylib} --no-watch`));
}, 1000000);
it('should work with creators', async () => {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --routing --no-interactive`);
// Generate root ngrx state management
runCLI(
`generate @nrwl/angular:ngrx users --module=apps/${myapp}/src/app/app.module.ts --root`
);
const packageJson = readJson('package.json');
expect(packageJson.dependencies['@ngrx/entity']).toBeDefined();
expect(packageJson.dependencies['@ngrx/store']).toBeDefined();
expect(packageJson.dependencies['@ngrx/effects']).toBeDefined();
expect(packageJson.dependencies['@ngrx/router-store']).toBeDefined();
expect(packageJson.devDependencies['@ngrx/schematics']).toBeDefined();
expect(packageJson.devDependencies['@ngrx/store-devtools']).toBeDefined();
const mylib = uniq('mylib');
// Generate feature library and ngrx state within that library
runCLI(`g @nrwl/angular:lib ${mylib} --prefix=fl`);
const flags = `--facade --barrels`;
runCLI(
`generate @nrwl/angular:ngrx flights --module=libs/${mylib}/src/lib/${mylib}.module.ts ${flags}`
);
expect(runCLI(`build ${myapp}`)).toMatch(/main\.[a-z0-9]+\.js/);
expectTestsPass(await runCLIAsync(`test ${myapp} --no-watch`));
expectTestsPass(await runCLIAsync(`test ${mylib} --no-watch`));
}, 1000000);
});

View File

@ -20,7 +20,8 @@
"dep-graph-dep-graph-e2e": "dep-graph/dep-graph-e2e",
"devkit": "packages/devkit",
"docs": "docs",
"e2e-angular": "e2e/angular",
"e2e-angular-core": "e2e/angular-core",
"e2e-angular-extensions": "e2e/angular-extensions",
"e2e-cli": "e2e/cli",
"e2e-cypress": "e2e/cypress",
"e2e-gatsby": "e2e/gatsby",