diff --git a/.eslintrc.json b/.eslintrc.json
index 765bf882..0a96094f 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -24,12 +24,18 @@
{
"files": ["*.ts", "*.tsx"],
"extends": ["plugin:@nx/typescript"],
- "rules": {}
+ "rules": {
+ "@typescript-eslint/no-extra-semi": "error",
+ "no-extra-semi": "off"
+ }
},
{
"files": ["*.js", "*.jsx"],
"extends": ["plugin:@nx/javascript"],
- "rules": {}
+ "rules": {
+ "@typescript-eslint/no-extra-semi": "error",
+ "no-extra-semi": "off"
+ }
},
{
"files": ["*.ts"],
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 28aac0d0..5820814f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -33,7 +33,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- name: install
- run: npm install
+ run: npm install --force
- name: build
run: npm run build -- --skip-nx-cache
- name: test
diff --git a/README.md b/README.md
index eed18ab4..ce2e906b 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ practices.
[![version][version-badge]][package] [![downloads][downloads-badge]][npmtrends]
[![MIT License][license-badge]][license]
-[](#contributors)
+[](#contributors)
[![PRs Welcome][prs-badge]][prs] [![Code of Conduct][coc-badge]][coc]
[![Discord][discord-badge]][discord]
@@ -147,10 +147,18 @@ describe('Counter', () => {
## Installation
This module is distributed via [npm][npm] which is bundled with [node][node] and
-should be installed as one of your project's `devDependencies`:
+should be installed as one of your project's `devDependencies`.
+Starting from ATL version 17, you also need to install `@testing-library/dom`:
```bash
-npm install @testing-library/angular --save-dev
+npm install --save-dev @testing-library/angular @testing-library/dom
+```
+
+Or, you can use the `ng add` command.
+This sets up your project to use Angular Testing Library, which also includes the installation of `@testing-library/dom`.
+
+```bash
+ng add @testing-library/angular
```
You may also be interested in installing `jest-dom` so you can use
@@ -160,13 +168,14 @@ You may also be interested in installing `jest-dom` so you can use
## Version compatibility
-| Angular | Angular Testing Library |
-| ------- | ----------------------- |
-| 17.x | 15.x, 14.x, 13.x |
-| 16.x | 14.x, 13.x |
-| >= 15.1 | 14.x, 13.x |
-| < 15.1 | 12.x, 11.x |
-| 14.x | 12.x, 11.x |
+| Angular | Angular Testing Library |
+| ------- | ---------------------------- |
+| 18.x | 17.x, 16.x, 15.x, 14.x, 13.x |
+| 17.x | 17.x, 16.x, 15.x, 14.x, 13.x |
+| 16.x | 14.x, 13.x |
+| >= 15.1 | 14.x, 13.x |
+| < 15.1 | 12.x, 11.x |
+| 14.x | 12.x, 11.x |
## Guiding Principles
diff --git a/apps/example-app-karma/project.json b/apps/example-app-karma/project.json
index 733fe68d..00820e35 100644
--- a/apps/example-app-karma/project.json
+++ b/apps/example-app-karma/project.json
@@ -4,6 +4,7 @@
"projectType": "application",
"sourceRoot": "apps/example-app-karma/src",
"prefix": "app",
+ "tags": [],
"generators": {},
"targets": {
"build": {
@@ -52,8 +53,7 @@
"defaultConfiguration": "development"
},
"lint": {
- "executor": "@nx/eslint:lint",
- "outputs": ["{options.outputFile}"]
+ "executor": "@nx/eslint:lint"
},
"test": {
"executor": "@angular-devkit/build-angular:karma",
@@ -63,6 +63,5 @@
"karmaConfig": "apps/example-app-karma/karma.conf.js"
}
}
- },
- "tags": []
+ }
}
diff --git a/apps/example-app/project.json b/apps/example-app/project.json
index 8af56975..ecbadfcb 100644
--- a/apps/example-app/project.json
+++ b/apps/example-app/project.json
@@ -4,6 +4,7 @@
"projectType": "application",
"sourceRoot": "apps/example-app/src",
"prefix": "app",
+ "tags": [],
"generators": {},
"targets": {
"build": {
@@ -59,8 +60,7 @@
}
},
"lint": {
- "executor": "@nx/eslint:lint",
- "outputs": ["{options.outputFile}"]
+ "executor": "@nx/eslint:lint"
},
"test": {
"executor": "@nx/jest:jest",
@@ -70,6 +70,5 @@
},
"outputs": ["{workspaceRoot}/coverage/"]
}
- },
- "tags": []
+ }
}
diff --git a/apps/example-app/src/app/examples/08-directive.spec.ts b/apps/example-app/src/app/examples/08-directive.spec.ts
index 5ba450bd..8b70b38c 100644
--- a/apps/example-app/src/app/examples/08-directive.spec.ts
+++ b/apps/example-app/src/app/examples/08-directive.spec.ts
@@ -1,8 +1,34 @@
+import { Component } from '@angular/core';
import { render, screen } from '@testing-library/angular';
import userEvent from '@testing-library/user-event';
import { SpoilerDirective } from './08-directive';
+test('it is possible to test directives with container component', async () => {
+ @Component({
+ template: ``,
+ imports: [SpoilerDirective],
+ standalone: true,
+ })
+ class FixtureComponent {}
+
+ const user = userEvent.setup();
+ await render(FixtureComponent);
+
+ const directive = screen.getByTestId('dir');
+
+ expect(screen.queryByText('I am visible now...')).not.toBeInTheDocument();
+ expect(screen.getByText('SPOILER')).toBeInTheDocument();
+
+ await user.hover(directive);
+ expect(screen.queryByText('SPOILER')).not.toBeInTheDocument();
+ expect(screen.getByText('I am visible now...')).toBeInTheDocument();
+
+ await user.unhover(directive);
+ expect(screen.getByText('SPOILER')).toBeInTheDocument();
+ expect(screen.queryByText('I am visible now...')).not.toBeInTheDocument();
+});
+
test('it is possible to test directives', async () => {
const user = userEvent.setup();
diff --git a/apps/example-app/src/app/examples/22-signal-inputs.component.spec.ts b/apps/example-app/src/app/examples/22-signal-inputs.component.spec.ts
index 6a55e61d..113d3302 100644
--- a/apps/example-app/src/app/examples/22-signal-inputs.component.spec.ts
+++ b/apps/example-app/src/app/examples/22-signal-inputs.component.spec.ts
@@ -1,4 +1,4 @@
-import { render, screen } from '@testing-library/angular';
+import { render, screen, within } from '@testing-library/angular';
import { SignalInputComponent } from './22-signal-inputs.component';
import userEvent from '@testing-library/user-event';
@@ -10,7 +10,20 @@ test('works with signal inputs', async () => {
},
});
- expect(screen.getByText(/hello world/i)).toBeInTheDocument();
+ const inputValue = within(screen.getByTestId('input-value'));
+ expect(inputValue.getByText(/hello world/i)).toBeInTheDocument();
+});
+
+test('works with computed', async () => {
+ await render(SignalInputComponent, {
+ componentInputs: {
+ greeting: 'Hello',
+ name: 'world',
+ },
+ });
+
+ const computedValue = within(screen.getByTestId('computed-value'));
+ expect(computedValue.getByText(/hello world/i)).toBeInTheDocument();
});
test('can update signal inputs', async () => {
@@ -21,11 +34,16 @@ test('can update signal inputs', async () => {
},
});
- expect(screen.getByText(/hello world/i)).toBeInTheDocument();
+ const inputValue = within(screen.getByTestId('input-value'));
+ const computedValue = within(screen.getByTestId('computed-value'));
+
+ expect(inputValue.getByText(/hello world/i)).toBeInTheDocument();
fixture.componentInstance.name.set('updated');
// set doesn't trigger change detection within the test, findBy is needed to update the template
- expect(await screen.findByText(/hello updated/i)).toBeInTheDocument();
+ expect(await inputValue.findByText(/hello updated/i)).toBeInTheDocument();
+ expect(await computedValue.findByText(/hello updated/i)).toBeInTheDocument();
+
// it's not recommended to access the model directly, but it's possible
expect(fixture.componentInstance.name()).toBe('updated');
});
@@ -55,22 +73,29 @@ test('model update also updates the template', async () => {
},
});
- expect(screen.getByText(/hello initial/i)).toBeInTheDocument();
+ const inputValue = within(screen.getByTestId('input-value'));
+ const computedValue = within(screen.getByTestId('computed-value'));
+
+ expect(inputValue.getByText(/hello initial/i)).toBeInTheDocument();
+ expect(computedValue.getByText(/hello initial/i)).toBeInTheDocument();
await userEvent.clear(screen.getByRole('textbox'));
await userEvent.type(screen.getByRole('textbox'), 'updated');
- expect(screen.getByText(/hello updated/i)).toBeInTheDocument();
+ expect(inputValue.getByText(/hello updated/i)).toBeInTheDocument();
+ expect(computedValue.getByText(/hello updated/i)).toBeInTheDocument();
expect(fixture.componentInstance.name()).toBe('updated');
fixture.componentInstance.name.set('new value');
// set doesn't trigger change detection within the test, findBy is needed to update the template
- expect(await screen.findByText(/hello new value/i)).toBeInTheDocument();
+ expect(await inputValue.findByText(/hello new value/i)).toBeInTheDocument();
+ expect(await computedValue.findByText(/hello new value/i)).toBeInTheDocument();
+
// it's not recommended to access the model directly, but it's possible
expect(fixture.componentInstance.name()).toBe('new value');
});
-test('works with signal inputs and rerenders', async () => {
+test('works with signal inputs, computed values, and rerenders', async () => {
const view = await render(SignalInputComponent, {
componentInputs: {
greeting: 'Hello',
@@ -78,7 +103,11 @@ test('works with signal inputs and rerenders', async () => {
},
});
- expect(screen.getByText(/hello world/i)).toBeInTheDocument();
+ const inputValue = within(screen.getByTestId('input-value'));
+ const computedValue = within(screen.getByTestId('computed-value'));
+
+ expect(inputValue.getByText(/hello world/i)).toBeInTheDocument();
+ expect(computedValue.getByText(/hello world/i)).toBeInTheDocument();
await view.rerender({
componentInputs: {
@@ -87,5 +116,6 @@ test('works with signal inputs and rerenders', async () => {
},
});
- expect(screen.getByText(/bye test/i)).toBeInTheDocument();
+ expect(inputValue.getByText(/bye test/i)).toBeInTheDocument();
+ expect(computedValue.getByText(/bye test/i)).toBeInTheDocument();
});
diff --git a/apps/example-app/src/app/examples/22-signal-inputs.component.ts b/apps/example-app/src/app/examples/22-signal-inputs.component.ts
index ae1e7791..ddc0c905 100644
--- a/apps/example-app/src/app/examples/22-signal-inputs.component.ts
+++ b/apps/example-app/src/app/examples/22-signal-inputs.component.ts
@@ -1,10 +1,11 @@
-import { Component, input, model, output } from '@angular/core';
+import { Component, computed, input, model, output } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-signal-input',
template: `
- {{ greetings() }} {{ name() }}
+ {{ greetings() }} {{ name() }}
+ {{ greetingMessage() }}
`,
@@ -18,6 +19,8 @@ export class SignalInputComponent {
name = model.required();
submit = output();
+ greetingMessage = computed(() => `${this.greetings()} ${this.name()}`);
+
submitName() {
this.submit.emit(this.name());
}
diff --git a/decorate-angular-cli.js b/decorate-angular-cli.js
deleted file mode 100644
index cff5ab7d..00000000
--- a/decorate-angular-cli.js
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * This file decorates the Angular CLI with the Nx CLI to enable features such as computation caching
- * and faster execution of tasks.
- *
- * It does this by:
- *
- * - Patching the Angular CLI to warn you in case you accidentally use the undecorated ng command.
- * - Symlinking the ng to nx command, so all commands run through the Nx CLI
- * - Updating the package.json postinstall script to give you control over this script
- *
- * The Nx CLI decorates the Angular CLI, so the Nx CLI is fully compatible with it.
- * Every command you run should work the same when using the Nx CLI, except faster.
- *
- * Because of symlinking you can still type `ng build/test/lint` in the terminal. The ng command, in this case,
- * will point to nx, which will perform optimizations before invoking ng. So the Angular CLI is always invoked.
- * The Nx CLI simply does some optimizations before invoking the Angular CLI.
- *
- * To opt out of this patch:
- * - Replace occurrences of nx with ng in your package.json
- * - Remove the script from your postinstall script in your package.json
- * - Delete and reinstall your node_modules
- */
-
-const fs = require('fs');
-const os = require('os');
-const cp = require('child_process');
-const isWindows = os.platform() === 'win32';
-let output;
-try {
- output = require('@nx/workspace').output;
-} catch (e) {
- console.warn(
- 'Angular CLI could not be decorated to enable computation caching. Please ensure @nx/workspace is installed.',
- );
- process.exit(0);
-}
-
-/**
- * Symlink of ng to nx, so you can keep using `ng build/test/lint` and still
- * invoke the Nx CLI and get the benefits of computation caching.
- */
-function symlinkNgCLItoNxCLI() {
- try {
- const ngPath = './node_modules/.bin/ng';
- const nxPath = './node_modules/.bin/nx';
- if (isWindows) {
- /**
- * This is the most reliable way to create symlink-like behavior on Windows.
- * Such that it works in all shells and works with npx.
- */
- ['', '.cmd', '.ps1'].forEach((ext) => {
- if (fs.existsSync(nxPath + ext)) fs.writeFileSync(ngPath + ext, fs.readFileSync(nxPath + ext));
- });
- } else {
- // If unix-based, symlink
- cp.execSync(`ln -sf ./nx ${ngPath}`);
- }
- } catch (e) {
- output.error({ title: 'Unable to create a symlink from the Angular CLI to the Nx CLI:' + e.message });
- throw e;
- }
-}
-
-try {
- symlinkNgCLItoNxCLI();
- require('@nrwl/cli/lib/decorate-cli').decorateCli();
- output.log({ title: 'Angular CLI has been decorated to enable computation caching.' });
-} catch (e) {
- output.error({ title: 'Decoration of the Angular CLI did not complete successfully' });
-}
diff --git a/nx.json b/nx.json
index 7040d68c..df534f76 100644
--- a/nx.json
+++ b/nx.json
@@ -1,7 +1,4 @@
{
- "affected": {
- "defaultBase": "main"
- },
"workspaceLayout": {
"appsDir": "apps",
"libsDir": "projects"
@@ -71,10 +68,6 @@
"inputs": ["default", "^production"],
"cache": true
},
- "lint": {
- "inputs": ["default", "{workspaceRoot}/.eslintrc.json"],
- "cache": true
- },
"@nx/jest:jest": {
"inputs": ["default", "^production"],
"cache": true,
@@ -87,6 +80,10 @@
"codeCoverage": true
}
}
+ },
+ "@nx/eslint:lint": {
+ "inputs": ["default", "{workspaceRoot}/.eslintrc.json"],
+ "cache": true
}
},
"namedInputs": {
@@ -104,5 +101,7 @@
]
},
"nxCloudAccessToken": "M2Q4YjlkNjMtMzY1NC00ZjkwLTk1ZjgtZjg5Y2VkMzFjM2FifHJlYWQtd3JpdGU=",
- "parallel": 3
+ "parallel": 3,
+ "useInferencePlugins": false,
+ "defaultBase": "main"
}
diff --git a/package.json b/package.json
index 6c96abe4..235d0eca 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,6 @@
"scripts": {
"ng": "nx",
"nx": "nx",
- "postinstall": "node ./decorate-angular-cli.js && ngcc --properties es2020 browser module main",
"start": "nx serve",
"prebuild": "rimraf dist",
"build": "nx run-many --target=build --projects=testing-library",
@@ -28,42 +27,42 @@
"prepare": "git config core.hookspath .githooks"
},
"dependencies": {
- "@angular/animations": "17.3.2",
- "@angular/cdk": "17.3.2",
- "@angular/common": "17.3.2",
- "@angular/compiler": "17.3.2",
- "@angular/core": "17.3.2",
- "@angular/material": "17.3.2",
- "@angular/platform-browser": "17.3.2",
- "@angular/platform-browser-dynamic": "17.3.2",
- "@angular/router": "17.3.2",
- "@ngrx/store": "17.1.0",
- "@nx/angular": "17.2.8",
+ "@angular/animations": "18.0.0",
+ "@angular/cdk": "18.0.0",
+ "@angular/common": "18.0.0",
+ "@angular/compiler": "18.0.0",
+ "@angular/core": "18.0.0",
+ "@angular/material": "18.0.0",
+ "@angular/platform-browser": "18.0.0",
+ "@angular/platform-browser-dynamic": "18.0.0",
+ "@angular/router": "18.0.0",
+ "@ngrx/store": "18.0.0-beta.1",
+ "@nx/angular": "19.1.0",
"@testing-library/dom": "^10.0.0",
"rxjs": "7.8.0",
"tslib": "~2.3.1",
"zone.js": "0.14.2"
},
"devDependencies": {
- "@angular-devkit/build-angular": "17.3.2",
- "@angular-devkit/core": "17.3.2",
- "@angular-devkit/schematics": "17.3.2",
- "@angular-eslint/builder": "17.0.1",
- "@angular-eslint/eslint-plugin": "17.0.1",
- "@angular-eslint/eslint-plugin-template": "17.0.1",
- "@angular-eslint/schematics": "17.0.1",
- "@angular-eslint/template-parser": "17.0.1",
- "@angular/cli": "~17.3.2",
- "@angular/compiler-cli": "17.3.2",
- "@angular/forms": "17.3.2",
- "@angular/language-service": "17.3.2",
- "@nx/eslint": "17.2.8",
- "@nx/eslint-plugin": "17.2.8",
- "@nx/jest": "17.2.8",
- "@nx/node": "17.2.8",
- "@nx/plugin": "17.2.8",
- "@nx/workspace": "17.2.8",
- "@schematics/angular": "17.3.2",
+ "@angular-devkit/build-angular": "18.0.1",
+ "@angular-devkit/core": "18.0.1",
+ "@angular-devkit/schematics": "18.0.1",
+ "@angular-eslint/builder": "17.3.0",
+ "@angular-eslint/eslint-plugin": "17.3.0",
+ "@angular-eslint/eslint-plugin-template": "17.3.0",
+ "@angular-eslint/schematics": "17.5.1",
+ "@angular-eslint/template-parser": "17.3.0",
+ "@angular/cli": "~18.0.0",
+ "@angular/compiler-cli": "18.0.0",
+ "@angular/forms": "18.0.0",
+ "@angular/language-service": "18.0.0",
+ "@nx/eslint": "19.1.0",
+ "@nx/eslint-plugin": "19.1.0",
+ "@nx/jest": "19.1.0",
+ "@nx/node": "19.1.0",
+ "@nx/plugin": "19.1.0",
+ "@nx/workspace": "19.1.0",
+ "@schematics/angular": "18.0.1",
"@testing-library/jasmine-dom": "^1.2.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/user-event": "^14.4.3",
@@ -71,10 +70,11 @@
"@types/jest": "29.5.1",
"@types/node": "18.16.9",
"@types/testing-library__jasmine-dom": "^1.3.0",
- "@typescript-eslint/eslint-plugin": "6.9.1",
- "@typescript-eslint/parser": "6.9.1",
+ "@typescript-eslint/eslint-plugin": "7.3.0",
+ "@typescript-eslint/parser": "7.3.0",
+ "autoprefixer": "^10.4.0",
"cpy-cli": "^3.1.1",
- "eslint": "8.48.0",
+ "eslint": "8.57.0",
"eslint-config-prettier": "9.0.0",
"eslint-plugin-import": "~2.25.4",
"eslint-plugin-jasmine": "~4.1.3",
@@ -85,7 +85,7 @@
"jasmine-spec-reporter": "7.0.0",
"jest": "29.7.0",
"jest-environment-jsdom": "29.5.0",
- "jest-preset-angular": "14.0.3",
+ "jest-preset-angular": "14.1.0",
"karma": "6.4.0",
"karma-chrome-launcher": "^3.1.0",
"karma-coverage": "^2.2.1",
@@ -93,8 +93,8 @@
"karma-jasmine-html-reporter": "2.0.0",
"lint-staged": "^12.1.6",
"ng-mocks": "^14.11.0",
- "ng-packagr": "17.3.0",
- "nx": "17.2.8",
+ "ng-packagr": "18.0.0",
+ "nx": "19.1.0",
"postcss": "^8.4.5",
"postcss-import": "14.1.0",
"postcss-preset-env": "7.5.0",
@@ -104,6 +104,6 @@
"semantic-release": "^18.0.0",
"ts-jest": "29.1.0",
"ts-node": "10.9.1",
- "typescript": "5.2.2"
+ "typescript": "5.4.5"
}
}
diff --git a/projects/testing-library/package.json b/projects/testing-library/package.json
index fd3cfac9..2852d027 100644
--- a/projects/testing-library/package.json
+++ b/projects/testing-library/package.json
@@ -26,16 +26,16 @@
"save": "devDependencies"
},
"ng-update": {
- "migrations": "./schematics/migrations/migration.json"
+ "migrations": "./schematics/migrations/migrations.json"
},
"peerDependencies": {
"@angular/common": ">= 17.0.0",
"@angular/platform-browser": ">= 17.0.0",
"@angular/router": ">= 17.0.0",
- "@angular/core": ">= 17.0.0"
+ "@angular/core": ">= 17.0.0",
+ "@testing-library/dom": "^10.0.0"
},
"dependencies": {
- "@testing-library/dom": "^10.0.0",
"tslib": "^2.3.1"
},
"publishConfig": {
diff --git a/projects/testing-library/project.json b/projects/testing-library/project.json
index b91e85c5..1deb065a 100644
--- a/projects/testing-library/project.json
+++ b/projects/testing-library/project.json
@@ -4,6 +4,7 @@
"projectType": "library",
"sourceRoot": "projects/testing-library/src",
"prefix": "lib",
+ "tags": [],
"targets": {
"build-package": {
"executor": "@nx/angular:package",
@@ -22,8 +23,7 @@
"defaultConfiguration": "production"
},
"lint": {
- "executor": "@nx/eslint:lint",
- "outputs": ["{options.outputFile}"]
+ "executor": "@nx/eslint:lint"
},
"build": {
"executor": "nx:run-commands",
@@ -50,6 +50,5 @@
},
"outputs": ["{workspaceRoot}/coverage/projects/testing-library"]
}
- },
- "tags": []
+ }
}
diff --git a/projects/testing-library/schematics/migrations/dtl-as-dev-dependency/index.spec.ts b/projects/testing-library/schematics/migrations/dtl-as-dev-dependency/index.spec.ts
new file mode 100644
index 00000000..a3c0fd1e
--- /dev/null
+++ b/projects/testing-library/schematics/migrations/dtl-as-dev-dependency/index.spec.ts
@@ -0,0 +1,44 @@
+import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
+import * as path from 'path';
+import { EmptyTree } from '@angular-devkit/schematics';
+
+test('adds DTL to devDependencies', async () => {
+ const tree = await setup({});
+ const pkg = tree.readContent('package.json');
+
+ expect(pkg).toMatchInlineSnapshot(`
+ "{
+ \\"devDependencies\\": {
+ \\"@testing-library/dom\\": \\"^10.0.0\\"
+ }
+ }"
+ `);
+});
+
+test('ignores if DTL is already listed as a dev dependency', async () => {
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const tree = await setup({ devDependencies: { '@testing-library/dom': '^9.0.0' } });
+ const pkg = tree.readContent('package.json');
+
+ expect(pkg).toMatchInlineSnapshot(`"{\\"devDependencies\\":{\\"@testing-library/dom\\":\\"^9.0.0\\"}}"`);
+});
+
+test('ignores if DTL is already listed as a dependency', async () => {
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const tree = await setup({ dependencies: { '@testing-library/dom': '^11.0.0' } });
+ const pkg = tree.readContent('package.json');
+
+ expect(pkg).toMatchInlineSnapshot(`"{\\"dependencies\\":{\\"@testing-library/dom\\":\\"^11.0.0\\"}}"`);
+});
+
+async function setup(packageJson: object) {
+ const collectionPath = path.join(__dirname, '../migrations.json');
+ const schematicRunner = new SchematicTestRunner('schematics', collectionPath);
+
+ const tree = new UnitTestTree(new EmptyTree());
+ tree.create('package.json', JSON.stringify(packageJson));
+
+ await schematicRunner.runSchematic(`atl-add-dtl-as-dev-dependency`, {}, tree);
+
+ return tree;
+}
diff --git a/projects/testing-library/schematics/migrations/dtl-as-dev-dependency/index.ts b/projects/testing-library/schematics/migrations/dtl-as-dev-dependency/index.ts
new file mode 100644
index 00000000..1c06e2f6
--- /dev/null
+++ b/projects/testing-library/schematics/migrations/dtl-as-dev-dependency/index.ts
@@ -0,0 +1,20 @@
+import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
+import {
+ addPackageJsonDependency,
+ getPackageJsonDependency,
+ NodeDependencyType,
+} from '@schematics/angular/utility/dependencies';
+
+const dtl = '@testing-library/dom';
+
+export default function (): Rule {
+ return async (tree: Tree, context: SchematicContext) => {
+ const dtlDep = getPackageJsonDependency(tree, dtl);
+ if (dtlDep) {
+ context.logger.info(`Skipping installation of '@testing-library/dom' because it's already installed.`);
+ } else {
+ context.logger.info(`Adding '@testing-library/dom' as a peer dependency.`);
+ addPackageJsonDependency(tree, { name: dtl, type: NodeDependencyType.Dev, overwrite: false, version: '^10.0.0' });
+ }
+ };
+}
diff --git a/projects/testing-library/schematics/migrations/migration.json b/projects/testing-library/schematics/migrations/migration.json
deleted file mode 100644
index 63001b44..00000000
--- a/projects/testing-library/schematics/migrations/migration.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "schematics": {}
-}
diff --git a/projects/testing-library/schematics/migrations/migrations.json b/projects/testing-library/schematics/migrations/migrations.json
new file mode 100644
index 00000000..711b7ae0
--- /dev/null
+++ b/projects/testing-library/schematics/migrations/migrations.json
@@ -0,0 +1,10 @@
+{
+ "$schema": "../../../../node_modules/@angular-devkit/schematics/collection-schema.json",
+ "schematics": {
+ "atl-add-dtl-as-dev-dependency": {
+ "description": "Add @testing-library/dom as a dev dependency",
+ "version": "17.0.0-beta.3",
+ "factory": "./dtl-as-dev-dependency/index"
+ }
+ }
+}
diff --git a/projects/testing-library/schematics/ng-add/index.ts b/projects/testing-library/schematics/ng-add/index.ts
index 68b9dfa2..24a0a3dc 100644
--- a/projects/testing-library/schematics/ng-add/index.ts
+++ b/projects/testing-library/schematics/ng-add/index.ts
@@ -1,11 +1,27 @@
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
+import {
+ addPackageJsonDependency,
+ getPackageJsonDependency,
+ NodeDependencyType,
+} from '@schematics/angular/utility/dependencies';
+
+const dtl = '@testing-library/dom';
export default function (): Rule {
- return (host: Tree, context: SchematicContext) => {
+ return (tree: Tree, context: SchematicContext) => {
+ const dtlDep = getPackageJsonDependency(tree, dtl);
+ if (dtlDep) {
+ context.logger.info(`Skipping installation of '@testing-library/dom' because it's already installed.`);
+ } else {
+ context.logger.info(`Adding '@testing-library/dom' as a dev dependency.`);
+ addPackageJsonDependency(tree, { name: dtl, type: NodeDependencyType.Dev, overwrite: false, version: '^10.0.0' });
+ }
+
context.logger.info(
`Correctly installed @testing-library/angular.
See our docs at https://testing-library.com/docs/angular-testing-library/intro/ to get started.`,
);
- return host;
+
+ return tree;
};
}
diff --git a/projects/testing-library/test-setup.ts b/projects/testing-library/test-setup.ts
index 0da94a0a..600d0857 100644
--- a/projects/testing-library/test-setup.ts
+++ b/projects/testing-library/test-setup.ts
@@ -1,2 +1,6 @@
import 'jest-preset-angular/setup-jest';
import '@testing-library/jest-dom';
+import { TextEncoder, TextDecoder } from 'util';
+
+// eslint-disable-next-line @typescript-eslint/naming-convention
+Object.assign(global, { TextDecoder, TextEncoder });
diff --git a/projects/testing-library/tsconfig.lib.prod.json b/projects/testing-library/tsconfig.lib.prod.json
index 1f041c94..752ed5ea 100644
--- a/projects/testing-library/tsconfig.lib.prod.json
+++ b/projects/testing-library/tsconfig.lib.prod.json
@@ -8,5 +8,5 @@
"angularCompilerOptions": {
"compilationMode": "partial"
},
- "exclude": ["jest.config.ts"]
+ "exclude": ["src/test-setup.ts", "**/*.spec.ts", "**/*.test.ts", "jest.config.ts"]
}
diff --git a/projects/testing-library/tsconfig.schematics.json b/projects/testing-library/tsconfig.schematics.json
index 481a34bc..c0118513 100644
--- a/projects/testing-library/tsconfig.schematics.json
+++ b/projects/testing-library/tsconfig.schematics.json
@@ -8,10 +8,11 @@
"esModuleInterop": true,
"resolveJsonModule": true,
"forceConsistentCasingInFileNames": true,
- "outDir": "../../dist/@testing-library/angular/schematics/ng-add",
+ "outDir": "../../dist/@testing-library/angular/schematics",
"removeComments": true,
"skipLibCheck": true,
"sourceMap": false
},
- "include": ["schematics/**/*.ts"]
+ "include": ["schematics/**/*.ts"],
+ "exclude": ["src/test-setup.ts", "**/*.spec.ts", "**/*.test.ts", "jest.config.ts"]
}