diff --git a/.circleci/config.yml b/.circleci/config.yml index ac0069d931..d9bafce898 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -390,7 +390,7 @@ workflows: run-unit-tests: 'false' run-linting: 'false' e2e-test-filter: 'MACOS-Tests' - exclude-projects: 'e2e-next,e2e-gatsby,e2e-workspace,e2e-react,e2e-web,e2e-angular-extensions,e2e-angular-core,e2e-cli,e2e-nx-plugin,e2e-storybook,e2e-cypress,e2e-node,e2e-linter,e2e-jest' + exclude-projects: 'e2e-next,e2e-gatsby,e2e-workspace-create,e2e-workspace-integrations,e2e-workspace-core,e2e-react,e2e-web,e2e-angular-extensions,e2e-angular-core,e2e-cli,e2e-nx-plugin,e2e-storybook,e2e-cypress,e2e-node,e2e-linter,e2e-jest' filters: branches: ignore: master @@ -426,7 +426,7 @@ workflows: run-linting: 'false' e2e-test-filter: 'MACOS-Tests' run-cypress-tests: 'true' - exclude-projects: 'e2e-next,e2e-gatsby,e2e-workspace,e2e-react,e2e-web,e2e-angular-extensions,e2e-angular-core,e2e-cli,e2e-nx-plugin,e2e-storybook,e2e-cypress,e2e-node,e2e-linter,e2e-jest' + exclude-projects: 'e2e-next,e2e-gatsby,e2e-workspace-create,e2e-workspace-integrations,e2e-workspace-core,e2e-react,e2e-web,e2e-angular-extensions,e2e-angular-core,e2e-cli,e2e-nx-plugin,e2e-storybook,e2e-cypress,e2e-node,e2e-linter,e2e-jest' filters: branches: only: master diff --git a/.github/workflows/e2e-matrix.yml b/.github/workflows/e2e-matrix.yml index b9ca46aa97..1db5ddccab 100644 --- a/.github/workflows/e2e-matrix.yml +++ b/.github/workflows/e2e-matrix.yml @@ -45,7 +45,7 @@ jobs: - e2e-node - e2e-web - e2e-storybook - - e2e-workspace + - e2e-workspace-integrations,e2e-workspace-core,e2e-workspace-create - e2e-react-native,e2e-detox exclude: - os: ubuntu-latest @@ -64,7 +64,11 @@ jobs: - os: macos-latest package_manager: pnpm - os: macos-latest - packages: e2e-workspace + packages: e2e-workspace-core + - os: macos-latest + packages: e2e-workspace-integrations + - os: macos-latest + packages: e2e-workspace-create - os: windows-latest node_version: '14' - os: windows-latest diff --git a/dep-graph/dep-graph/src/assets/graphs/nx.json b/dep-graph/dep-graph/src/assets/graphs/nx.json index 1edd7a6a0c..fd24d373a1 100644 --- a/dep-graph/dep-graph/src/assets/graphs/nx.json +++ b/dep-graph/dep-graph/src/assets/graphs/nx.json @@ -188,9 +188,19 @@ "data": { "tags": [], "root": "e2e/storybook", "files": [] } }, { - "name": "e2e-workspace", + "name": "e2e-workspace-integrations", "type": "app", - "data": { "tags": [], "root": "e2e/workspace", "files": [] } + "data": { "tags": [], "root": "e2e/workspace-integrations", "files": [] } + }, + { + "name": "e2e-workspace-core", + "type": "app", + "data": { "tags": [], "root": "e2e/workspace-core", "files": [] } + }, + { + "name": "e2e-workspace-create", + "type": "app", + "data": { "tags": [], "root": "e2e/workspace-create", "files": [] } }, { "name": "jest", @@ -510,17 +520,61 @@ { "source": "e2e-storybook", "target": "storybook", "type": "implicit" }, { "source": "e2e-storybook", "target": "e2e-utils", "type": "static" } ], - "e2e-workspace": [ + "e2e-workspace-integrations": [ { - "source": "e2e-workspace", + "source": "e2e-workspace-integrations", + "target": "angular", + "type": "implicit" + }, + { + "source": "e2e-workspace-integrations", + "target": "react", + "type": "implicit" + }, + { + "source": "e2e-workspace-integrations", + "target": "e2e-utils", + "type": "static" + }, + { + "source": "e2e-workspace-integrations", + "target": "devkit", + "type": "static" + }, + { + "source": "e2e-workspace-integrations", + "target": "workspace", + "type": "static" + } + ], + "e2e-workspace-create": [ + { + "source": "e2e-workspace-create", "target": "create-nx-workspace", "type": "implicit" }, - { "source": "e2e-workspace", "target": "angular", "type": "implicit" }, - { "source": "e2e-workspace", "target": "react", "type": "implicit" }, - { "source": "e2e-workspace", "target": "e2e-utils", "type": "static" }, - { "source": "e2e-workspace", "target": "devkit", "type": "static" }, - { "source": "e2e-workspace", "target": "workspace", "type": "static" } + { + "source": "e2e-workspace-create", + "target": "e2e-utils", + "type": "static" + }, + { + "source": "e2e-workspace-create", + "target": "workspace", + "type": "static" + } + ], + "e2e-workspace-core": [ + { + "source": "e2e-workspace-core", + "target": "e2e-utils", + "type": "static" + }, + { + "source": "e2e-workspace-core", + "target": "workspace", + "type": "static" + } ], "jest": [ { "source": "jest", "target": "devkit", "type": "static" }, diff --git a/e2e/linter/src/linter.test.ts b/e2e/linter/src/linter.test.ts index 9ac2d6c28e..29f60997d4 100644 --- a/e2e/linter/src/linter.test.ts +++ b/e2e/linter/src/linter.test.ts @@ -230,6 +230,72 @@ describe('Linter', () => { expect(lintOutput).toContain(knownLintErrorMessage); }, 1000000); }); + + it('lint plugin should ensure module boundaries', () => { + const proj = newProject(); + const myapp = uniq('myapp'); + const myapp2 = uniq('myapp2'); + const mylib = uniq('mylib'); + const lazylib = uniq('lazylib'); + const invalidtaglib = uniq('invalidtaglib'); + const validtaglib = uniq('validtaglib'); + + runCLI(`generate @nrwl/angular:app ${myapp} --tags=validtag`); + runCLI(`generate @nrwl/angular:app ${myapp2}`); + runCLI(`generate @nrwl/angular:lib ${mylib}`); + runCLI(`generate @nrwl/angular:lib ${lazylib}`); + runCLI(`generate @nrwl/angular:lib ${invalidtaglib} --tags=invalidtag`); + runCLI(`generate @nrwl/angular:lib ${validtaglib} --tags=validtag`); + + const eslint = readJson('.eslintrc.json'); + eslint.overrides[0].rules[ + '@nrwl/nx/enforce-module-boundaries' + ][1].depConstraints = [ + { sourceTag: 'validtag', onlyDependOnLibsWithTags: ['validtag'] }, + ...eslint.overrides[0].rules['@nrwl/nx/enforce-module-boundaries'][1] + .depConstraints, + ]; + updateFile('.eslintrc.json', JSON.stringify(eslint, null, 2)); + + const tsConfig = readJson('tsconfig.base.json'); + + /** + * apps do not add themselves to the tsconfig file. + * + * Let's add it so that we can trigger the lint failure + */ + tsConfig.compilerOptions.paths[`@${proj}/${myapp2}`] = [ + `apps/${myapp2}/src/main.ts`, + ]; + + tsConfig.compilerOptions.paths[`@secondScope/${lazylib}`] = + tsConfig.compilerOptions.paths[`@${proj}/${lazylib}`]; + delete tsConfig.compilerOptions.paths[`@${proj}/${lazylib}`]; + updateFile('tsconfig.base.json', JSON.stringify(tsConfig, null, 2)); + + updateFile( + `apps/${myapp}/src/main.ts`, + ` + import '../../../libs/${mylib}'; + import '@secondScope/${lazylib}'; + import '@${proj}/${myapp2}'; + import '@${proj}/${invalidtaglib}'; + import '@${proj}/${validtaglib}'; + + const s = {loadChildren: '@${proj}/${lazylib}'}; + ` + ); + + const out = runCLI(`lint ${myapp}`, { silenceError: true }); + expect(out).toContain( + 'Libraries cannot be imported by a relative or absolute path, and must begin with a npm scope' + ); + // expect(out).toContain('Imports of lazy-loaded libraries are forbidden'); + expect(out).toContain('Imports of apps are forbidden'); + expect(out).toContain( + 'A project tagged with "validtag" can only depend on libs tagged with "validtag"' + ); + }, 1000000); }); /** diff --git a/e2e/workspace/jest.config.js b/e2e/workspace-core/jest.config.js similarity index 87% rename from e2e/workspace/jest.config.js rename to e2e/workspace-core/jest.config.js index e39e1275ce..5af18e8d7e 100644 --- a/e2e/workspace/jest.config.js +++ b/e2e/workspace-core/jest.config.js @@ -6,5 +6,5 @@ module.exports = { moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], maxWorkers: 1, globals: { 'ts-jest': { tsconfig: '/tsconfig.spec.json' } }, - displayName: 'e2e-workspace', + displayName: 'e2e-workspace-core', }; diff --git a/e2e/workspace/project.json b/e2e/workspace-core/project.json similarity index 65% rename from e2e/workspace/project.json rename to e2e/workspace-core/project.json index e28a13c435..47ad8cc98e 100644 --- a/e2e/workspace/project.json +++ b/e2e/workspace-core/project.json @@ -1,6 +1,6 @@ { - "root": "e2e/workspace", - "sourceRoot": "e2e/workspace", + "root": "e2e/workspace-core", + "sourceRoot": "e2e/workspace-core", "projectType": "application", "targets": { "e2e": { @@ -14,7 +14,7 @@ "command": "yarn e2e-build-package-publish" }, { - "command": "nx run-e2e-tests e2e-workspace" + "command": "nx run-e2e-tests e2e-workspace-core" } ], "parallel": false @@ -23,12 +23,12 @@ "run-e2e-tests": { "executor": "@nrwl/jest:jest", "options": { - "jestConfig": "e2e/workspace/jest.config.js", + "jestConfig": "e2e/workspace-core/jest.config.js", "passWithNoTests": true, "runInBand": true }, - "outputs": ["coverage/e2e/workspace"] + "outputs": ["coverage/e2e/workspace-core"] } }, - "implicitDependencies": ["create-nx-workspace", "angular", "react"] + "implicitDependencies": [] } diff --git a/e2e/workspace/src/workspace-aux-commands.test.ts b/e2e/workspace-core/src/aux-commands.test.ts similarity index 54% rename from e2e/workspace/src/workspace-aux-commands.test.ts rename to e2e/workspace-core/src/aux-commands.test.ts index f09da0279a..6a6507d3f7 100644 --- a/e2e/workspace/src/workspace-aux-commands.test.ts +++ b/e2e/workspace-core/src/aux-commands.test.ts @@ -1,251 +1,23 @@ -import * as path from 'path'; +import { NxJsonConfiguration } from '@nrwl/devkit'; import { checkFilesExist, exists, - isNotWindows, newProject, readFile, readJson, - removeProject, renameFile, runCLI, - runCLIAsync, tmpProjPath, uniq, updateFile, workspaceConfigName, } from '@nrwl/e2e/utils'; -import type { NxJsonConfiguration } from '@nrwl/devkit'; -import { classify } from '@nrwl/workspace/src/utils/strings'; -let proj: string; +let proj; beforeAll(() => { proj = newProject(); }); - -describe('lint', () => { - it('lint should ensure module boundaries', () => { - const myapp = uniq('myapp'); - const myapp2 = uniq('myapp2'); - const mylib = uniq('mylib'); - const lazylib = uniq('lazylib'); - const invalidtaglib = uniq('invalidtaglib'); - const validtaglib = uniq('validtaglib'); - - runCLI(`generate @nrwl/angular:app ${myapp} --tags=validtag`); - runCLI(`generate @nrwl/angular:app ${myapp2}`); - runCLI(`generate @nrwl/angular:lib ${mylib}`); - runCLI(`generate @nrwl/angular:lib ${lazylib}`); - runCLI(`generate @nrwl/angular:lib ${invalidtaglib} --tags=invalidtag`); - runCLI(`generate @nrwl/angular:lib ${validtaglib} --tags=validtag`); - - const eslint = readJson('.eslintrc.json'); - eslint.overrides[0].rules[ - '@nrwl/nx/enforce-module-boundaries' - ][1].depConstraints = [ - { sourceTag: 'validtag', onlyDependOnLibsWithTags: ['validtag'] }, - ...eslint.overrides[0].rules['@nrwl/nx/enforce-module-boundaries'][1] - .depConstraints, - ]; - updateFile('.eslintrc.json', JSON.stringify(eslint, null, 2)); - - const tsConfig = readJson('tsconfig.base.json'); - - /** - * apps do not add themselves to the tsconfig file. - * - * Let's add it so that we can trigger the lint failure - */ - tsConfig.compilerOptions.paths[`@${proj}/${myapp2}`] = [ - `apps/${myapp2}/src/main.ts`, - ]; - - tsConfig.compilerOptions.paths[`@secondScope/${lazylib}`] = - tsConfig.compilerOptions.paths[`@${proj}/${lazylib}`]; - delete tsConfig.compilerOptions.paths[`@${proj}/${lazylib}`]; - updateFile('tsconfig.base.json', JSON.stringify(tsConfig, null, 2)); - - updateFile( - `apps/${myapp}/src/main.ts`, - ` - import '../../../libs/${mylib}'; - import '@secondScope/${lazylib}'; - import '@${proj}/${myapp2}'; - import '@${proj}/${invalidtaglib}'; - import '@${proj}/${validtaglib}'; - - const s = {loadChildren: '@${proj}/${lazylib}'}; - ` - ); - - const out = runCLI(`lint ${myapp}`, { silenceError: true }); - expect(out).toContain( - 'Libraries cannot be imported by a relative or absolute path, and must begin with a npm scope' - ); - // expect(out).toContain('Imports of lazy-loaded libraries are forbidden'); - expect(out).toContain('Imports of apps are forbidden'); - expect(out).toContain( - 'A project tagged with "validtag" can only depend on libs tagged with "validtag"' - ); - }, 1000000); - - describe('nx workspace-lint', () => { - it('should identify issues with the workspace', () => { - const appBefore = uniq('before'); - const appAfter = uniq('after'); - - runCLI(`generate @nrwl/angular:app ${appBefore}`); - renameFile(`apps/${appBefore}`, `apps/${appAfter}`); - - const stdout = runCLI('workspace-lint', { silenceError: true }); - expect(stdout).toContain( - `- Cannot find project '${appBefore}' in 'apps/${appBefore}'` - ); - expect(stdout).toContain( - 'The following file(s) do not belong to any projects:' - ); - expect(stdout).toContain(`- apps/${appAfter}/jest.config.js`); - expect(stdout).toContain(`- apps/${appAfter}/src/app/app.component.css`); - expect(stdout).toContain(`- apps/${appAfter}/src/app/app.component.html`); - expect(stdout).toContain( - `- apps/${appAfter}/src/app/app.component.spec.ts` - ); - }); - }); -}); - -describe('format', () => { - it('should check and reformat the code', async () => { - if (isNotWindows()) { - const myapp = uniq('myapp'); - const mylib = uniq('mylib'); - - runCLI(`generate @nrwl/angular:app ${myapp}`); - runCLI(`generate @nrwl/angular:lib ${mylib}`); - updateFile( - `apps/${myapp}/src/main.ts`, - ` - const x = 1111; - ` - ); - - updateFile( - `apps/${myapp}/src/app/app.module.ts`, - ` - const y = 1111; - ` - ); - - updateFile( - `apps/${myapp}/src/app/app.component.ts`, - ` - const z = 1111; - ` - ); - - updateFile( - `libs/${mylib}/index.ts`, - ` - const x = 1111; - ` - ); - updateFile( - `libs/${mylib}/src/${mylib}.module.ts`, - ` - const y = 1111; - ` - ); - - updateFile( - `README.md`, - ` - my new readme; - ` - ); - - let stdout = runCLI( - `format:check --files="libs/${mylib}/index.ts,package.json" --libs-and-apps`, - { silenceError: true } - ); - expect(stdout).toContain(path.normalize(`libs/${mylib}/index.ts`)); - expect(stdout).toContain( - path.normalize(`libs/${mylib}/src/${mylib}.module.ts`) - ); - expect(stdout).not.toContain(path.normalize(`README.md`)); // It will be contained only in case of exception, that we fallback to all - - stdout = runCLI(`format:check --all`, { silenceError: true }); - expect(stdout).toContain(path.normalize(`apps/${myapp}/src/main.ts`)); - expect(stdout).toContain( - path.normalize(`apps/${myapp}/src/app/app.module.ts`) - ); - expect(stdout).toContain( - path.normalize(`apps/${myapp}/src/app/app.component.ts`) - ); - - stdout = runCLI(`format:check --projects=${myapp}`, { - silenceError: true, - }); - expect(stdout).toContain(path.normalize(`apps/${myapp}/src/main.ts`)); - expect(stdout).toContain( - path.normalize(`apps/${myapp}/src/app/app.module.ts`) - ); - expect(stdout).toContain( - path.normalize(`apps/${myapp}/src/app/app.component.ts`) - ); - expect(stdout).not.toContain(path.normalize(`libs/${mylib}/index.ts`)); - expect(stdout).not.toContain( - path.normalize(`libs/${mylib}/src/${mylib}.module.ts`) - ); - expect(stdout).not.toContain(path.normalize(`README.md`)); - - stdout = runCLI(`format:check --projects=${myapp},${mylib}`, { - silenceError: true, - }); - expect(stdout).toContain(path.normalize(`apps/${myapp}/src/main.ts`)); - expect(stdout).toContain( - path.normalize(`apps/${myapp}/src/app/app.module.ts`) - ); - expect(stdout).toContain( - path.normalize(`apps/${myapp}/src/app/app.component.ts`) - ); - expect(stdout).toContain(path.normalize(`libs/${mylib}/index.ts`)); - expect(stdout).toContain( - path.normalize(`libs/${mylib}/src/${mylib}.module.ts`) - ); - expect(stdout).not.toContain(path.normalize(`README.md`)); - - const { stderr } = await runCLIAsync( - `format:check --projects=${myapp},${mylib} --all`, - { - silenceError: true, - } - ); - expect(stderr).toContain( - 'Arguments all and projects are mutually exclusive' - ); - - runCLI( - `format:write --files="apps/${myapp}/src/app/app.module.ts,apps/${myapp}/src/app/app.component.ts"` - ); - - stdout = runCLI('format:check --all', { silenceError: true }); - expect(stdout).toContain(path.normalize(`apps/${myapp}/src/main.ts`)); - expect(stdout).not.toContain( - path.normalize(`apps/${myapp}/src/app/app.module.ts`) - ); - expect(stdout).not.toContain( - path.normalize(`apps/${myapp}/src/app/app.component.ts`) - ); - - runCLI('format:write --all'); - expect(runCLI('format:check --all')).not.toContain( - path.normalize(`apps/${myapp}/src/main.ts`) - ); - } - }, 90000); -}); - describe('workspace-generator', () => { let custom: string; let failing: string; @@ -399,329 +171,31 @@ describe('workspace-generator', () => { }); }); -describe('dep-graph', () => { - let proj: string; - let myapp: string; - let myapp2: string; - let myapp3: string; - let myappE2e: string; - let myapp2E2e: string; - let myapp3E2e: string; - let mylib: string; - let mylib2: string; - beforeAll(() => { - proj = newProject(); - myapp = uniq('myapp'); - myapp2 = uniq('myapp2'); - myapp3 = uniq('myapp3'); - myappE2e = `${myapp}-e2e`; - myapp2E2e = `${myapp2}-e2e`; - myapp3E2e = `${myapp3}-e2e`; - mylib = uniq('mylib'); - mylib2 = uniq('mylib2'); +describe('workspace-lint', () => { + it('should identify issues with the workspace', () => { + const appBefore = uniq('before'); + const appAfter = uniq('after'); - runCLI(`generate @nrwl/angular:app ${myapp}`); - runCLI(`generate @nrwl/angular:app ${myapp2}`); - runCLI(`generate @nrwl/angular:app ${myapp3}`); - runCLI(`generate @nrwl/angular:lib ${mylib}`); - runCLI(`generate @nrwl/angular:lib ${mylib2}`); + runCLI(`generate @nrwl/angular:app ${appBefore}`); + renameFile(`apps/${appBefore}`, `apps/${appAfter}`); - updateFile( - `apps/${myapp}/src/main.ts`, - ` - import '@${proj}/${mylib}'; - - const s = {loadChildren: '@${proj}/${mylib2}'}; - ` + const stdout = runCLI('workspace-lint', { silenceError: true }); + expect(stdout).toContain( + `- Cannot find project '${appBefore}' in 'apps/${appBefore}'` ); - - updateFile( - `apps/${myapp2}/src/app/app.component.spec.ts`, - `import '@${proj}/${mylib}';` + expect(stdout).toContain( + 'The following file(s) do not belong to any projects:' ); - - updateFile( - `libs/${mylib}/src/mylib.module.spec.ts`, - `import '@${proj}/${mylib2}';` + expect(stdout).toContain(`- apps/${appAfter}/jest.config.js`); + expect(stdout).toContain(`- apps/${appAfter}/src/app/app.component.css`); + expect(stdout).toContain(`- apps/${appAfter}/src/app/app.component.html`); + expect(stdout).toContain( + `- apps/${appAfter}/src/app/app.component.spec.ts` ); }); - - it('dep-graph should output json to file', () => { - runCLI(`dep-graph --file=project-graph.json`); - - expect(() => checkFilesExist('project-graph.json')).not.toThrow(); - - const jsonFileContents = readJson('project-graph.json'); - - expect(jsonFileContents.graph.dependencies).toEqual( - expect.objectContaining({ - [myapp3E2e]: [ - { - source: myapp3E2e, - target: myapp3, - type: 'implicit', - }, - ], - [myapp2]: [ - { - source: myapp2, - target: mylib, - type: 'static', - }, - ], - [myapp2E2e]: [ - { - source: myapp2E2e, - target: myapp2, - type: 'implicit', - }, - ], - [mylib]: [ - { - source: mylib, - target: mylib2, - type: 'static', - }, - ], - [mylib2]: [], - [myapp]: [ - { - source: myapp, - target: mylib, - type: 'static', - }, - { source: myapp, target: mylib2, type: 'static' }, - ], - [myappE2e]: [ - { - source: myappE2e, - target: myapp, - type: 'implicit', - }, - ], - [myapp3]: [], - }) - ); - - runCLI( - `affected:dep-graph --files="libs/${mylib}/src/index.ts" --file="project-graph.json"` - ); - - expect(() => checkFilesExist('project-graph.json')).not.toThrow(); - - const jsonFileContents2 = readJson('project-graph.json'); - - expect(jsonFileContents2.criticalPath).toContain(myapp); - expect(jsonFileContents2.criticalPath).toContain(myapp2); - expect(jsonFileContents2.criticalPath).toContain(mylib); - expect(jsonFileContents2.criticalPath).not.toContain(mylib2); - }, 1000000); - - it('dep-graph should focus requested project', () => { - runCLI(`dep-graph --focus=${myapp} --file=project-graph.json`); - - expect(() => checkFilesExist('project-graph.json')).not.toThrow(); - - const jsonFileContents = readJson('project-graph.json'); - const projectNames = Object.keys(jsonFileContents.graph.nodes); - - expect(projectNames).toContain(myapp); - expect(projectNames).toContain(mylib); - expect(projectNames).toContain(mylib2); - expect(projectNames).toContain(myappE2e); - - expect(projectNames).not.toContain(myapp2); - expect(projectNames).not.toContain(myapp3); - expect(projectNames).not.toContain(myapp2E2e); - expect(projectNames).not.toContain(myapp3E2e); - }, 1000000); - - it('dep-graph should exclude requested projects', () => { - runCLI( - `dep-graph --exclude=${myappE2e},${myapp2E2e},${myapp3E2e} --file=project-graph.json` - ); - - expect(() => checkFilesExist('project-graph.json')).not.toThrow(); - - const jsonFileContents = readJson('project-graph.json'); - const projectNames = Object.keys(jsonFileContents.graph.nodes); - - expect(projectNames).toContain(myapp); - expect(projectNames).toContain(mylib); - expect(projectNames).toContain(mylib2); - expect(projectNames).toContain(myapp2); - expect(projectNames).toContain(myapp3); - - expect(projectNames).not.toContain(myappE2e); - expect(projectNames).not.toContain(myapp2E2e); - expect(projectNames).not.toContain(myapp3E2e); - }, 1000000); - - it('dep-graph should exclude requested projects that were included by a focus', () => { - runCLI( - `dep-graph --focus=${myapp} --exclude=${myappE2e} --file=project-graph.json` - ); - - expect(() => checkFilesExist('project-graph.json')).not.toThrow(); - - const jsonFileContents = readJson('project-graph.json'); - const projectNames = Object.keys(jsonFileContents.graph.nodes); - - expect(projectNames).toContain(myapp); - expect(projectNames).toContain(mylib); - expect(projectNames).toContain(mylib2); - - expect(projectNames).not.toContain(myappE2e); - expect(projectNames).not.toContain(myapp2); - expect(projectNames).not.toContain(myapp3); - expect(projectNames).not.toContain(myapp2E2e); - expect(projectNames).not.toContain(myapp3E2e); - }, 1000000); - - it('dep-graph should output a deployable static website in an html file accompanied by a folder with static assets', () => { - runCLI(`dep-graph --file=project-graph.html`); - - expect(() => checkFilesExist('project-graph.html')).not.toThrow(); - expect(() => checkFilesExist('static/styles.css')).not.toThrow(); - expect(() => checkFilesExist('static/runtime.esm.js')).not.toThrow(); - expect(() => checkFilesExist('static/polyfills.esm.js')).not.toThrow(); - expect(() => checkFilesExist('static/main.esm.js')).not.toThrow(); - }); }); -describe('Move Angular Project', () => { - let proj: string; - let app1: string; - let app2: string; - let newPath: string; - - beforeEach(() => { - proj = newProject(); - app1 = uniq('app1'); - app2 = uniq('app2'); - newPath = `subfolder/${app2}`; - runCLI(`generate @nrwl/angular:app ${app1}`); - }); - - afterEach(() => removeProject({ onlyOnCI: true })); - - /** - * Tries moving an app from ${app1} -> subfolder/${app2} - */ - it('should work for apps', () => { - const moveOutput = runCLI( - `generate @nrwl/angular:move --project ${app1} ${newPath}` - ); - - // just check the output - expect(moveOutput).toContain(`DELETE apps/${app1}`); - expect(moveOutput).toContain(`CREATE apps/${newPath}/.browserslistrc`); - expect(moveOutput).toContain(`CREATE apps/${newPath}/jest.config.js`); - expect(moveOutput).toContain(`CREATE apps/${newPath}/tsconfig.app.json`); - expect(moveOutput).toContain(`CREATE apps/${newPath}/tsconfig.json`); - expect(moveOutput).toContain(`CREATE apps/${newPath}/tsconfig.spec.json`); - expect(moveOutput).toContain(`CREATE apps/${newPath}/.eslintrc.json`); - expect(moveOutput).toContain(`CREATE apps/${newPath}/src/favicon.ico`); - expect(moveOutput).toContain(`CREATE apps/${newPath}/src/index.html`); - expect(moveOutput).toContain(`CREATE apps/${newPath}/src/main.ts`); - expect(moveOutput).toContain(`CREATE apps/${newPath}/src/polyfills.ts`); - expect(moveOutput).toContain(`CREATE apps/${newPath}/src/styles.css`); - expect(moveOutput).toContain(`CREATE apps/${newPath}/src/test-setup.ts`); - expect(moveOutput).toContain( - `CREATE apps/${newPath}/src/app/app.component.html` - ); - expect(moveOutput).toContain( - `CREATE apps/${newPath}/src/app/app.module.ts` - ); - expect(moveOutput).toContain(`CREATE apps/${newPath}/src/assets/.gitkeep`); - expect(moveOutput).toContain( - `CREATE apps/${newPath}/src/environments/environment.prod.ts` - ); - expect(moveOutput).toContain( - `CREATE apps/${newPath}/src/environments/environment.ts` - ); - expect(moveOutput).toContain(`UPDATE nx.json`); - expect(moveOutput).toContain(`UPDATE workspace.json`); - }); - - /** - * Tries moving an e2e project from ${app1} -> ${newPath} - */ - it('should work for e2e projects', () => { - const moveOutput = runCLI( - `generate @nrwl/angular:move --projectName=${app1}-e2e --destination=${newPath}-e2e` - ); - - // just check that the cypress.json is updated correctly - const cypressJsonPath = `apps/${newPath}-e2e/cypress.json`; - expect(moveOutput).toContain(`CREATE ${cypressJsonPath}`); - checkFilesExist(cypressJsonPath); - const cypressJson = readJson(cypressJsonPath); - expect(cypressJson.videosFolder).toEqual( - `../../../dist/cypress/apps/${newPath}-e2e/videos` - ); - expect(cypressJson.screenshotsFolder).toEqual( - `../../../dist/cypress/apps/${newPath}-e2e/screenshots` - ); - }); - - /** - * Tries moving a library from ${lib} -> shared/${lib} - */ - it('should work for libraries', () => { - const lib1 = uniq('mylib'); - const lib2 = uniq('mylib'); - runCLI(`generate @nrwl/angular:lib ${lib1}`); - - /** - * Create a library which imports the module from the other lib - */ - - runCLI(`generate @nrwl/angular:lib ${lib2}`); - - updateFile( - `libs/${lib2}/src/lib/${lib2}.module.ts`, - `import { ${classify(lib1)}Module } from '@${proj}/${lib1}'; - - export class ExtendedModule extends ${classify(lib1)}Module { }` - ); - - const moveOutput = runCLI( - `generate @nrwl/angular:move --projectName=${lib1} --destination=shared/${lib1}` - ); - - const newPath = `libs/shared/${lib1}`; - const newModule = `Shared${classify(lib1)}Module`; - - const testSetupPath = `${newPath}/src/test-setup.ts`; - expect(moveOutput).toContain(`CREATE ${testSetupPath}`); - checkFilesExist(testSetupPath); - - const modulePath = `${newPath}/src/lib/shared-${lib1}.module.ts`; - expect(moveOutput).toContain(`CREATE ${modulePath}`); - checkFilesExist(modulePath); - const moduleFile = readFile(modulePath); - expect(moduleFile).toContain(`export class ${newModule}`); - - const indexPath = `${newPath}/src/index.ts`; - expect(moveOutput).toContain(`CREATE ${indexPath}`); - checkFilesExist(indexPath); - const index = readFile(indexPath); - expect(index).toContain(`export * from './lib/shared-${lib1}.module'`); - - /** - * Check that the import in lib2 has been updated - */ - const lib2FilePath = `libs/${lib2}/src/lib/${lib2}.module.ts`; - const lib2File = readFile(lib2FilePath); - expect(lib2File).toContain( - `import { ${newModule} } from '@${proj}/shared-${lib1}';` - ); - expect(lib2File).toContain(`extends ${newModule}`); - }); -}); - -describe('Move Project', () => { +describe('move project', () => { /** * Tries moving a library from ${lib}/data-access -> shared/${lib}/data-access */ @@ -1138,7 +612,7 @@ describe('Move Project', () => { }); }); -describe('Remove Project', () => { +describe('remove project', () => { /** * Tries creating then deleting a lib */ diff --git a/e2e/workspace/src/plugins.test.ts b/e2e/workspace-core/src/plugins.test.ts similarity index 100% rename from e2e/workspace/src/plugins.test.ts rename to e2e/workspace-core/src/plugins.test.ts diff --git a/e2e/workspace/src/run-commands.test.ts b/e2e/workspace-core/src/run-commands.test.ts similarity index 76% rename from e2e/workspace/src/run-commands.test.ts rename to e2e/workspace-core/src/run-commands.test.ts index 0a52916f58..ae28b34b1b 100644 --- a/e2e/workspace/src/run-commands.test.ts +++ b/e2e/workspace-core/src/run-commands.test.ts @@ -13,20 +13,20 @@ describe('Run Commands', () => { afterAll(() => removeProject({ onlyOnCI: true })); it('should not override environment variables already set when setting a custom env file path', async () => { - const nodeapp = uniq('nodeapp'); + const mylib = uniq('lib'); updateFile( `.env`, 'SHARED_VAR=shared-root-value\nROOT_ONLY=root-only-value' ); - runCLI(`generate @nrwl/express:app ${nodeapp}`); + runCLI(`generate @nrwl/workspace:lib ${mylib}`); updateFile( - `apps/${nodeapp}/.custom.env`, + `apps/${mylib}/.custom.env`, 'SHARED_VAR=shared-nested-value\nNESTED_ONLY=nested-only-value' ); - const envFile = `apps/${nodeapp}/.custom.env`; + const envFile = `apps/${mylib}/.custom.env`; runCLI( - `generate @nrwl/workspace:run-commands echoEnvVariables --command=echo --envFile=${envFile} --project=${nodeapp}` + `generate @nrwl/workspace:run-commands echoEnvVariables --command=echo --envFile=${envFile} --project=${mylib}` ); const command = @@ -35,11 +35,11 @@ describe('Run Commands', () => { : `$SHARED_VAR $ROOT_ONLY $NESTED_ONLY`; const config = readJson(workspaceConfigName()); config.projects[ - nodeapp + mylib ].targets.echoEnvVariables.options.command += ` ${command}`; updateFile(workspaceConfigName(), JSON.stringify(config, null, 2)); - const result = runCLI('echoEnvVariables'); + const result = runCLI(`run ${mylib}:echoEnvVariables`); expect(result).toContain('shared-root-value'); expect(result).not.toContain('shared-nested-value'); expect(result).toContain('root-only-value'); @@ -47,12 +47,12 @@ describe('Run Commands', () => { }, 120000); it('should pass options', async () => { - const myapp = uniq('myapp1'); + const mylib = uniq('lib'); - runCLI(`generate @nrwl/web:app ${myapp}`); + runCLI(`generate @nrwl/workspace:lib ${mylib}`); const config = readJson(workspaceConfigName()); - config.projects[myapp].targets.echo = { + config.projects[mylib].targets.echo = { executor: '@nrwl/workspace:run-commands', options: { command: 'echo', @@ -64,19 +64,19 @@ describe('Run Commands', () => { }; updateFile(workspaceConfigName(), JSON.stringify(config)); - const result = runCLI(`run ${myapp}:echo`, { silent: true }); + const result = runCLI(`run ${mylib}:echo`, { silent: true }); expect(result).toContain( '--var1=a --var2=b --var-hyphen=c --varCamelCase=d' ); }, 120000); it('should interpolate provided arguments', async () => { - const myapp = uniq('myapp1'); + const mylib = uniq('lib'); - runCLI(`generate @nrwl/web:app ${myapp}`); + runCLI(`generate @nrwl/workspace:lib ${mylib}`); const config = readJson(workspaceConfigName()); - config.projects[myapp].targets.echo = { + config.projects[mylib].targets.echo = { executor: '@nrwl/workspace:run-commands', options: { commands: [ @@ -92,7 +92,7 @@ describe('Run Commands', () => { updateFile(workspaceConfigName(), JSON.stringify(config)); const result = runCLI( - `run ${myapp}:echo --var1=a --var2=b --var-hyphen=c --varCamelCase=d` + `run ${mylib}:echo --var1=a --var2=b --var-hyphen=c --varCamelCase=d` ); expect(result).toContain('var1: a'); expect(result).toContain('var2: b'); @@ -100,7 +100,7 @@ describe('Run Commands', () => { expect(result).toContain('camel: d'); const resultArgs = runCLI( - `run ${myapp}:echo --args="--var1=a --var2=b --var-hyphen=c --varCamelCase=d"` + `run ${mylib}:echo --args="--var1=a --var2=b --var-hyphen=c --varCamelCase=d"` ); expect(resultArgs).toContain('var1: a'); expect(resultArgs).toContain('var2: b'); @@ -109,12 +109,12 @@ describe('Run Commands', () => { }, 120000); it('should fail when a process exits non-zero', () => { - const myapp = uniq('myapp1'); + const mylib = uniq('lib'); - runCLI(`generate @nrwl/web:app ${myapp}`); + runCLI(`generate @nrwl/workspace:lib ${mylib}`); const config = readJson(workspaceConfigName()); - config.projects[myapp].targets.error = { + config.projects[mylib].targets.error = { executor: '@nrwl/workspace:run-commands', options: { command: `exit 1`, @@ -123,7 +123,7 @@ describe('Run Commands', () => { updateFile(workspaceConfigName(), JSON.stringify(config)); try { - runCLI(`run ${myapp}:error`); + runCLI(`run ${mylib}:error`); fail('Should error if process errors'); } catch (e) { expect(e.stderr.toString()).toContain( @@ -133,17 +133,17 @@ describe('Run Commands', () => { }); it('run command should not break if output property is missing in options and arguments', () => { - const myapp = uniq('myapp'); + const mylib = uniq('mylib'); - runCLI(`generate @nrwl/web:app ${myapp}`); + runCLI(`generate @nrwl/workspace:lib ${mylib}`); const workspaceJson = readJson(`workspace.json`); - workspaceJson.projects[myapp].targets.lint.outputs = [ + workspaceJson.projects[mylib].targets.lint.outputs = [ '{options.outputFile}', ]; updateFile('workspace.json', JSON.stringify(workspaceJson, null, 2)); expect(() => - runCLI(`run ${myapp}:lint --format=json`, { + runCLI(`run ${mylib}:lint --format=json`, { silenceError: true, }) ).not.toThrow(); diff --git a/e2e/workspace/src/workspace-lib.test.ts b/e2e/workspace-core/src/workspace-lib.test.ts similarity index 100% rename from e2e/workspace/src/workspace-lib.test.ts rename to e2e/workspace-core/src/workspace-lib.test.ts diff --git a/e2e/workspace/tsconfig.json b/e2e/workspace-core/tsconfig.json similarity index 100% rename from e2e/workspace/tsconfig.json rename to e2e/workspace-core/tsconfig.json diff --git a/e2e/workspace/tsconfig.spec.json b/e2e/workspace-core/tsconfig.spec.json similarity index 100% rename from e2e/workspace/tsconfig.spec.json rename to e2e/workspace-core/tsconfig.spec.json diff --git a/e2e/workspace-create/jest.config.js b/e2e/workspace-create/jest.config.js new file mode 100644 index 0000000000..63eecbf07c --- /dev/null +++ b/e2e/workspace-create/jest.config.js @@ -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: '/tsconfig.spec.json' } }, + displayName: 'e2e-workspace-create', +}; diff --git a/e2e/workspace-create/project.json b/e2e/workspace-create/project.json new file mode 100644 index 0000000000..cb4a8e9115 --- /dev/null +++ b/e2e/workspace-create/project.json @@ -0,0 +1,34 @@ +{ + "root": "e2e/workspace-create", + "sourceRoot": "e2e/workspace-create", + "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-workspace-create" + } + ], + "parallel": false + } + }, + "run-e2e-tests": { + "executor": "@nrwl/jest:jest", + "options": { + "jestConfig": "e2e/workspace-create/jest.config.js", + "passWithNoTests": true, + "runInBand": true + }, + "outputs": ["coverage/e2e/workspace-create"] + } + }, + "implicitDependencies": ["create-nx-workspace"] +} diff --git a/e2e/workspace/src/create-nx-workspace.test.ts b/e2e/workspace-create/src/create-nx-workspace.test.ts similarity index 100% rename from e2e/workspace/src/create-nx-workspace.test.ts rename to e2e/workspace-create/src/create-nx-workspace.test.ts diff --git a/e2e/workspace-create/tsconfig.json b/e2e/workspace-create/tsconfig.json new file mode 100644 index 0000000000..6d5abf8483 --- /dev/null +++ b/e2e/workspace-create/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "types": ["node", "jest"] + }, + "include": [], + "files": [], + "references": [ + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/e2e/workspace-create/tsconfig.spec.json b/e2e/workspace-create/tsconfig.spec.json new file mode 100644 index 0000000000..af4ac638d6 --- /dev/null +++ b/e2e/workspace-create/tsconfig.spec.json @@ -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" + ] +} diff --git a/e2e/workspace-integrations/jest.config.js b/e2e/workspace-integrations/jest.config.js new file mode 100644 index 0000000000..78f4bd2db8 --- /dev/null +++ b/e2e/workspace-integrations/jest.config.js @@ -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: '/tsconfig.spec.json' } }, + displayName: 'e2e-workspace-integrations', +}; diff --git a/e2e/workspace-integrations/project.json b/e2e/workspace-integrations/project.json new file mode 100644 index 0000000000..2b83427acd --- /dev/null +++ b/e2e/workspace-integrations/project.json @@ -0,0 +1,34 @@ +{ + "root": "e2e/workspace-integrations", + "sourceRoot": "e2e/workspace-integrations", + "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-workspace-integrations" + } + ], + "parallel": false + } + }, + "run-e2e-tests": { + "executor": "@nrwl/jest:jest", + "options": { + "jestConfig": "e2e/workspace-integrations/jest.config.js", + "passWithNoTests": true, + "runInBand": true + }, + "outputs": ["coverage/e2e/workspace-integrations"] + } + }, + "implicitDependencies": ["angular", "react"] +} diff --git a/e2e/workspace-integrations/src/custom-layout.test.ts b/e2e/workspace-integrations/src/custom-layout.test.ts new file mode 100644 index 0000000000..5a3bb6002f --- /dev/null +++ b/e2e/workspace-integrations/src/custom-layout.test.ts @@ -0,0 +1,56 @@ +import { + checkFilesExist, + readFile, + readJson, + runCLI, + runCLIAsync, + runCreateWorkspace, + uniq, + packageInstall, + removeProject, + getSelectedPackageManager, +} from '@nrwl/e2e/utils'; + +describe('custom workspace layout', () => { + afterAll(() => removeProject({ onlyOnCI: true })); + + it('should work', async () => { + // TODO (meeroslav): what is the purpose of this test? + // const proj = uniq('custom-layout-proj'); + // const packageManager = getSelectedPackageManager(); + // runCreateWorkspace(proj, { preset: 'npm', packageManager }); + // packageInstall('@nrwl/react @nrwl/angular @nrwl/express'); + // const nxJson = readJson('nx.json'); + // expect(nxJson.extends).toEqual('@nrwl/workspace/presets/npm.json'); + // const reactApp = uniq('reactapp'); + // const reactLib = uniq('reactlib'); + // const ngApp = uniq('ngapp'); + // const ngLib = uniq('nglib'); + // const expressApp = uniq('expessapp'); + // const expressLib = uniq('expresslib'); + // runCLI(`generate @nrwl/react:app ${reactApp} --no-interactive`); + // runCLI(`generate @nrwl/react:lib ${reactLib} --no-interactive`); + // runCLI(`generate @nrwl/angular:app ${ngApp} --no-interactive`); + // runCLI(`generate @nrwl/angular:lib ${ngLib} --no-interactive`); + // runCLI(`generate @nrwl/express:app ${expressApp} --no-interactive`); + // runCLI(`generate @nrwl/express:lib ${expressLib} --no-interactive`); + // checkFilesExist( + // `packages/${reactLib}/src/index.ts`, + // `packages/${reactApp}/src/main.tsx`, + // `packages/${reactApp}-e2e/cypress.json`, + // `packages/${ngLib}/src/index.ts`, + // `packages/${ngApp}/src/main.ts`, + // `packages/${ngApp}-e2e/cypress.json`, + // `packages/${expressLib}/src/index.ts`, + // `packages/${expressApp}/src/main.ts` + // ); + // const workspaceJson = readFile('workspace.json'); + // expect(workspaceJson).not.toContain('apps/'); + // expect(workspaceJson).not.toContain('libs/'); + // const libTestResults = await runCLIAsync(`test ${expressLib}`); + // expect(libTestResults.stdout).toContain(`nx run ${expressLib}:test`); + // const appBuildResults = await runCLIAsync(`build ${expressApp}`); + // expect(appBuildResults.stdout).toContain(`nx run ${expressApp}:build`); + // checkFilesExist(`dist/packages/${expressApp}/main.js`); + }, 1000000); +}); diff --git a/e2e/workspace-integrations/src/workspace-aux-commands.test.ts b/e2e/workspace-integrations/src/workspace-aux-commands.test.ts new file mode 100644 index 0000000000..76734a6691 --- /dev/null +++ b/e2e/workspace-integrations/src/workspace-aux-commands.test.ts @@ -0,0 +1,476 @@ +import * as path from 'path'; +import { + checkFilesExist, + exists, + isNotWindows, + newProject, + readFile, + readJson, + removeProject, + runCLI, + runCLIAsync, + tmpProjPath, + uniq, + updateFile, +} from '@nrwl/e2e/utils'; +import type { NxJsonConfiguration } from '@nrwl/devkit'; +import { classify } from '@nrwl/workspace/src/utils/strings'; + +let proj: string; + +beforeAll(() => { + proj = newProject(); +}); + +describe('format', () => { + it('should check and reformat the code', async () => { + if (isNotWindows()) { + const myapp = uniq('myapp'); + const mylib = uniq('mylib'); + + runCLI(`generate @nrwl/angular:app ${myapp}`); + runCLI(`generate @nrwl/angular:lib ${mylib}`); + updateFile( + `apps/${myapp}/src/main.ts`, + ` + const x = 1111; + ` + ); + + updateFile( + `apps/${myapp}/src/app/app.module.ts`, + ` + const y = 1111; + ` + ); + + updateFile( + `apps/${myapp}/src/app/app.component.ts`, + ` + const z = 1111; + ` + ); + + updateFile( + `libs/${mylib}/index.ts`, + ` + const x = 1111; + ` + ); + updateFile( + `libs/${mylib}/src/${mylib}.module.ts`, + ` + const y = 1111; + ` + ); + + updateFile( + `README.md`, + ` + my new readme; + ` + ); + + let stdout = runCLI( + `format:check --files="libs/${mylib}/index.ts,package.json" --libs-and-apps`, + { silenceError: true } + ); + expect(stdout).toContain(path.normalize(`libs/${mylib}/index.ts`)); + expect(stdout).toContain( + path.normalize(`libs/${mylib}/src/${mylib}.module.ts`) + ); + expect(stdout).not.toContain(path.normalize(`README.md`)); // It will be contained only in case of exception, that we fallback to all + + stdout = runCLI(`format:check --all`, { silenceError: true }); + expect(stdout).toContain(path.normalize(`apps/${myapp}/src/main.ts`)); + expect(stdout).toContain( + path.normalize(`apps/${myapp}/src/app/app.module.ts`) + ); + expect(stdout).toContain( + path.normalize(`apps/${myapp}/src/app/app.component.ts`) + ); + + stdout = runCLI(`format:check --projects=${myapp}`, { + silenceError: true, + }); + expect(stdout).toContain(path.normalize(`apps/${myapp}/src/main.ts`)); + expect(stdout).toContain( + path.normalize(`apps/${myapp}/src/app/app.module.ts`) + ); + expect(stdout).toContain( + path.normalize(`apps/${myapp}/src/app/app.component.ts`) + ); + expect(stdout).not.toContain(path.normalize(`libs/${mylib}/index.ts`)); + expect(stdout).not.toContain( + path.normalize(`libs/${mylib}/src/${mylib}.module.ts`) + ); + expect(stdout).not.toContain(path.normalize(`README.md`)); + + stdout = runCLI(`format:check --projects=${myapp},${mylib}`, { + silenceError: true, + }); + expect(stdout).toContain(path.normalize(`apps/${myapp}/src/main.ts`)); + expect(stdout).toContain( + path.normalize(`apps/${myapp}/src/app/app.module.ts`) + ); + expect(stdout).toContain( + path.normalize(`apps/${myapp}/src/app/app.component.ts`) + ); + expect(stdout).toContain(path.normalize(`libs/${mylib}/index.ts`)); + expect(stdout).toContain( + path.normalize(`libs/${mylib}/src/${mylib}.module.ts`) + ); + expect(stdout).not.toContain(path.normalize(`README.md`)); + + const { stderr } = await runCLIAsync( + `format:check --projects=${myapp},${mylib} --all`, + { + silenceError: true, + } + ); + expect(stderr).toContain( + 'Arguments all and projects are mutually exclusive' + ); + + runCLI( + `format:write --files="apps/${myapp}/src/app/app.module.ts,apps/${myapp}/src/app/app.component.ts"` + ); + + stdout = runCLI('format:check --all', { silenceError: true }); + expect(stdout).toContain(path.normalize(`apps/${myapp}/src/main.ts`)); + expect(stdout).not.toContain( + path.normalize(`apps/${myapp}/src/app/app.module.ts`) + ); + expect(stdout).not.toContain( + path.normalize(`apps/${myapp}/src/app/app.component.ts`) + ); + + runCLI('format:write --all'); + expect(runCLI('format:check --all')).not.toContain( + path.normalize(`apps/${myapp}/src/main.ts`) + ); + } + }, 90000); +}); + +describe('dep-graph', () => { + let proj: string; + let myapp: string; + let myapp2: string; + let myapp3: string; + let myappE2e: string; + let myapp2E2e: string; + let myapp3E2e: string; + let mylib: string; + let mylib2: string; + beforeAll(() => { + proj = newProject(); + myapp = uniq('myapp'); + myapp2 = uniq('myapp2'); + myapp3 = uniq('myapp3'); + myappE2e = `${myapp}-e2e`; + myapp2E2e = `${myapp2}-e2e`; + myapp3E2e = `${myapp3}-e2e`; + mylib = uniq('mylib'); + mylib2 = uniq('mylib2'); + + runCLI(`generate @nrwl/angular:app ${myapp}`); + runCLI(`generate @nrwl/angular:app ${myapp2}`); + runCLI(`generate @nrwl/angular:app ${myapp3}`); + runCLI(`generate @nrwl/angular:lib ${mylib}`); + runCLI(`generate @nrwl/angular:lib ${mylib2}`); + + updateFile( + `apps/${myapp}/src/main.ts`, + ` + import '@${proj}/${mylib}'; + + const s = {loadChildren: '@${proj}/${mylib2}'}; + ` + ); + + updateFile( + `apps/${myapp2}/src/app/app.component.spec.ts`, + `import '@${proj}/${mylib}';` + ); + + updateFile( + `libs/${mylib}/src/mylib.module.spec.ts`, + `import '@${proj}/${mylib2}';` + ); + }); + + it('dep-graph should output json to file', () => { + runCLI(`dep-graph --file=project-graph.json`); + + expect(() => checkFilesExist('project-graph.json')).not.toThrow(); + + const jsonFileContents = readJson('project-graph.json'); + + expect(jsonFileContents.graph.dependencies).toEqual( + expect.objectContaining({ + [myapp3E2e]: [ + { + source: myapp3E2e, + target: myapp3, + type: 'implicit', + }, + ], + [myapp2]: [ + { + source: myapp2, + target: mylib, + type: 'static', + }, + ], + [myapp2E2e]: [ + { + source: myapp2E2e, + target: myapp2, + type: 'implicit', + }, + ], + [mylib]: [ + { + source: mylib, + target: mylib2, + type: 'static', + }, + ], + [mylib2]: [], + [myapp]: [ + { + source: myapp, + target: mylib, + type: 'static', + }, + { source: myapp, target: mylib2, type: 'static' }, + ], + [myappE2e]: [ + { + source: myappE2e, + target: myapp, + type: 'implicit', + }, + ], + [myapp3]: [], + }) + ); + + runCLI( + `affected:dep-graph --files="libs/${mylib}/src/index.ts" --file="project-graph.json"` + ); + + expect(() => checkFilesExist('project-graph.json')).not.toThrow(); + + const jsonFileContents2 = readJson('project-graph.json'); + + expect(jsonFileContents2.criticalPath).toContain(myapp); + expect(jsonFileContents2.criticalPath).toContain(myapp2); + expect(jsonFileContents2.criticalPath).toContain(mylib); + expect(jsonFileContents2.criticalPath).not.toContain(mylib2); + }, 1000000); + + it('dep-graph should focus requested project', () => { + runCLI(`dep-graph --focus=${myapp} --file=project-graph.json`); + + expect(() => checkFilesExist('project-graph.json')).not.toThrow(); + + const jsonFileContents = readJson('project-graph.json'); + const projectNames = Object.keys(jsonFileContents.graph.nodes); + + expect(projectNames).toContain(myapp); + expect(projectNames).toContain(mylib); + expect(projectNames).toContain(mylib2); + expect(projectNames).toContain(myappE2e); + + expect(projectNames).not.toContain(myapp2); + expect(projectNames).not.toContain(myapp3); + expect(projectNames).not.toContain(myapp2E2e); + expect(projectNames).not.toContain(myapp3E2e); + }, 1000000); + + it('dep-graph should exclude requested projects', () => { + runCLI( + `dep-graph --exclude=${myappE2e},${myapp2E2e},${myapp3E2e} --file=project-graph.json` + ); + + expect(() => checkFilesExist('project-graph.json')).not.toThrow(); + + const jsonFileContents = readJson('project-graph.json'); + const projectNames = Object.keys(jsonFileContents.graph.nodes); + + expect(projectNames).toContain(myapp); + expect(projectNames).toContain(mylib); + expect(projectNames).toContain(mylib2); + expect(projectNames).toContain(myapp2); + expect(projectNames).toContain(myapp3); + + expect(projectNames).not.toContain(myappE2e); + expect(projectNames).not.toContain(myapp2E2e); + expect(projectNames).not.toContain(myapp3E2e); + }, 1000000); + + it('dep-graph should exclude requested projects that were included by a focus', () => { + runCLI( + `dep-graph --focus=${myapp} --exclude=${myappE2e} --file=project-graph.json` + ); + + expect(() => checkFilesExist('project-graph.json')).not.toThrow(); + + const jsonFileContents = readJson('project-graph.json'); + const projectNames = Object.keys(jsonFileContents.graph.nodes); + + expect(projectNames).toContain(myapp); + expect(projectNames).toContain(mylib); + expect(projectNames).toContain(mylib2); + + expect(projectNames).not.toContain(myappE2e); + expect(projectNames).not.toContain(myapp2); + expect(projectNames).not.toContain(myapp3); + expect(projectNames).not.toContain(myapp2E2e); + expect(projectNames).not.toContain(myapp3E2e); + }, 1000000); + + it('dep-graph should output a deployable static website in an html file accompanied by a folder with static assets', () => { + runCLI(`dep-graph --file=project-graph.html`); + + expect(() => checkFilesExist('project-graph.html')).not.toThrow(); + expect(() => checkFilesExist('static/styles.css')).not.toThrow(); + expect(() => checkFilesExist('static/runtime.esm.js')).not.toThrow(); + expect(() => checkFilesExist('static/polyfills.esm.js')).not.toThrow(); + expect(() => checkFilesExist('static/main.esm.js')).not.toThrow(); + }); +}); + +describe('Move Angular Project', () => { + let proj: string; + let app1: string; + let app2: string; + let newPath: string; + + beforeEach(() => { + proj = newProject(); + app1 = uniq('app1'); + app2 = uniq('app2'); + newPath = `subfolder/${app2}`; + runCLI(`generate @nrwl/angular:app ${app1}`); + }); + + afterEach(() => removeProject({ onlyOnCI: true })); + + /** + * Tries moving an app from ${app1} -> subfolder/${app2} + */ + it('should work for apps', () => { + const moveOutput = runCLI( + `generate @nrwl/angular:move --project ${app1} ${newPath}` + ); + + // just check the output + expect(moveOutput).toContain(`DELETE apps/${app1}`); + expect(moveOutput).toContain(`CREATE apps/${newPath}/.browserslistrc`); + expect(moveOutput).toContain(`CREATE apps/${newPath}/jest.config.js`); + expect(moveOutput).toContain(`CREATE apps/${newPath}/tsconfig.app.json`); + expect(moveOutput).toContain(`CREATE apps/${newPath}/tsconfig.json`); + expect(moveOutput).toContain(`CREATE apps/${newPath}/tsconfig.spec.json`); + expect(moveOutput).toContain(`CREATE apps/${newPath}/.eslintrc.json`); + expect(moveOutput).toContain(`CREATE apps/${newPath}/src/favicon.ico`); + expect(moveOutput).toContain(`CREATE apps/${newPath}/src/index.html`); + expect(moveOutput).toContain(`CREATE apps/${newPath}/src/main.ts`); + expect(moveOutput).toContain(`CREATE apps/${newPath}/src/polyfills.ts`); + expect(moveOutput).toContain(`CREATE apps/${newPath}/src/styles.css`); + expect(moveOutput).toContain(`CREATE apps/${newPath}/src/test-setup.ts`); + expect(moveOutput).toContain( + `CREATE apps/${newPath}/src/app/app.component.html` + ); + expect(moveOutput).toContain( + `CREATE apps/${newPath}/src/app/app.module.ts` + ); + expect(moveOutput).toContain(`CREATE apps/${newPath}/src/assets/.gitkeep`); + expect(moveOutput).toContain( + `CREATE apps/${newPath}/src/environments/environment.prod.ts` + ); + expect(moveOutput).toContain( + `CREATE apps/${newPath}/src/environments/environment.ts` + ); + expect(moveOutput).toContain(`UPDATE nx.json`); + expect(moveOutput).toContain(`UPDATE workspace.json`); + }); + + /** + * Tries moving an e2e project from ${app1} -> ${newPath} + */ + it('should work for e2e projects', () => { + const moveOutput = runCLI( + `generate @nrwl/angular:move --projectName=${app1}-e2e --destination=${newPath}-e2e` + ); + + // just check that the cypress.json is updated correctly + const cypressJsonPath = `apps/${newPath}-e2e/cypress.json`; + expect(moveOutput).toContain(`CREATE ${cypressJsonPath}`); + checkFilesExist(cypressJsonPath); + const cypressJson = readJson(cypressJsonPath); + expect(cypressJson.videosFolder).toEqual( + `../../../dist/cypress/apps/${newPath}-e2e/videos` + ); + expect(cypressJson.screenshotsFolder).toEqual( + `../../../dist/cypress/apps/${newPath}-e2e/screenshots` + ); + }); + + /** + * Tries moving a library from ${lib} -> shared/${lib} + */ + it('should work for libraries', () => { + const lib1 = uniq('mylib'); + const lib2 = uniq('mylib'); + runCLI(`generate @nrwl/angular:lib ${lib1}`); + + /** + * Create a library which imports the module from the other lib + */ + + runCLI(`generate @nrwl/angular:lib ${lib2}`); + + updateFile( + `libs/${lib2}/src/lib/${lib2}.module.ts`, + `import { ${classify(lib1)}Module } from '@${proj}/${lib1}'; + + export class ExtendedModule extends ${classify(lib1)}Module { }` + ); + + const moveOutput = runCLI( + `generate @nrwl/angular:move --projectName=${lib1} --destination=shared/${lib1}` + ); + + const newPath = `libs/shared/${lib1}`; + const newModule = `Shared${classify(lib1)}Module`; + + const testSetupPath = `${newPath}/src/test-setup.ts`; + expect(moveOutput).toContain(`CREATE ${testSetupPath}`); + checkFilesExist(testSetupPath); + + const modulePath = `${newPath}/src/lib/shared-${lib1}.module.ts`; + expect(moveOutput).toContain(`CREATE ${modulePath}`); + checkFilesExist(modulePath); + const moduleFile = readFile(modulePath); + expect(moduleFile).toContain(`export class ${newModule}`); + + const indexPath = `${newPath}/src/index.ts`; + expect(moveOutput).toContain(`CREATE ${indexPath}`); + checkFilesExist(indexPath); + const index = readFile(indexPath); + expect(index).toContain(`export * from './lib/shared-${lib1}.module'`); + + /** + * Check that the import in lib2 has been updated + */ + const lib2FilePath = `libs/${lib2}/src/lib/${lib2}.module.ts`; + const lib2File = readFile(lib2FilePath); + expect(lib2File).toContain( + `import { ${newModule} } from '@${proj}/shared-${lib1}';` + ); + expect(lib2File).toContain(`extends ${newModule}`); + }); +}); diff --git a/e2e/workspace/src/workspace.test.ts b/e2e/workspace-integrations/src/workspace.test.ts similarity index 100% rename from e2e/workspace/src/workspace.test.ts rename to e2e/workspace-integrations/src/workspace.test.ts diff --git a/e2e/workspace-integrations/tsconfig.json b/e2e/workspace-integrations/tsconfig.json new file mode 100644 index 0000000000..6d5abf8483 --- /dev/null +++ b/e2e/workspace-integrations/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "types": ["node", "jest"] + }, + "include": [], + "files": [], + "references": [ + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/e2e/workspace-integrations/tsconfig.spec.json b/e2e/workspace-integrations/tsconfig.spec.json new file mode 100644 index 0000000000..af4ac638d6 --- /dev/null +++ b/e2e/workspace-integrations/tsconfig.spec.json @@ -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" + ] +} diff --git a/e2e/workspace/src/custom-layout.test.ts b/e2e/workspace/src/custom-layout.test.ts deleted file mode 100644 index 0a4b16f668..0000000000 --- a/e2e/workspace/src/custom-layout.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { - checkFilesExist, - readFile, - readJson, - runCLI, - runCLIAsync, - runCreateWorkspace, - uniq, - packageInstall, - removeProject, - getSelectedPackageManager, -} from '@nrwl/e2e/utils'; - -describe('custom workspace layout', () => { - afterAll(() => removeProject({ onlyOnCI: true })); - - it('should work', async () => { - const proj = uniq('custom-layout-proj'); - const packageManager = getSelectedPackageManager(); - runCreateWorkspace(proj, { preset: 'npm', packageManager }); - packageInstall('@nrwl/react @nrwl/angular @nrwl/express'); - - const nxJson = readJson('nx.json'); - expect(nxJson.extends).toEqual('@nrwl/workspace/presets/npm.json'); - - const reactApp = uniq('reactapp'); - const reactLib = uniq('reactlib'); - - const ngApp = uniq('ngapp'); - const ngLib = uniq('nglib'); - - const expressApp = uniq('expessapp'); - const expressLib = uniq('expresslib'); - - runCLI(`generate @nrwl/react:app ${reactApp} --no-interactive`); - runCLI(`generate @nrwl/react:lib ${reactLib} --no-interactive`); - - runCLI(`generate @nrwl/angular:app ${ngApp} --no-interactive`); - runCLI(`generate @nrwl/angular:lib ${ngLib} --no-interactive`); - - runCLI(`generate @nrwl/express:app ${expressApp} --no-interactive`); - runCLI(`generate @nrwl/express:lib ${expressLib} --no-interactive`); - - checkFilesExist( - `packages/${reactLib}/src/index.ts`, - `packages/${reactApp}/src/main.tsx`, - `packages/${reactApp}-e2e/cypress.json`, - - `packages/${ngLib}/src/index.ts`, - `packages/${ngApp}/src/main.ts`, - `packages/${ngApp}-e2e/cypress.json`, - - `packages/${expressLib}/src/index.ts`, - `packages/${expressApp}/src/main.ts` - ); - - const workspaceJson = readFile('workspace.json'); - expect(workspaceJson).not.toContain('apps/'); - expect(workspaceJson).not.toContain('libs/'); - - const libTestResults = await runCLIAsync(`test ${expressLib}`); - expect(libTestResults.stdout).toContain(`nx run ${expressLib}:test`); - - const appBuildResults = await runCLIAsync(`build ${expressApp}`); - expect(appBuildResults.stdout).toContain(`nx run ${expressApp}:build`); - - checkFilesExist(`dist/packages/${expressApp}/main.js`); - }, 1000000); -}); diff --git a/workspace.json b/workspace.json index 0d2b1d7204..8ea53d562d 100644 --- a/workspace.json +++ b/workspace.json @@ -37,7 +37,9 @@ "e2e-storybook": "e2e/storybook", "e2e-utils": "e2e/utils", "e2e-web": "e2e/web", - "e2e-workspace": "e2e/workspace", + "e2e-workspace-core": "e2e/workspace-core", + "e2e-workspace-create": "e2e/workspace-create", + "e2e-workspace-integrations": "e2e/workspace-integrations", "eslint-plugin-nx": "packages/eslint-plugin-nx", "express": "packages/express", "gatsby": "packages/gatsby",