From 9160da11d4870ea7d4a59e2912bdf75c4022ebe3 Mon Sep 17 00:00:00 2001
From: Mahdi Lazraq <94069699+mlz11@users.noreply.github.com>
Date: Tue, 13 Aug 2024 19:48:52 +0200
Subject: [PATCH 01/26] fix: add support for InputSignalWithTransform in inputs
property (#484)
Closes #483
---
.../22-signal-inputs.component.spec.ts | 25 ++++++++++++-------
.../examples/22-signal-inputs.component.ts | 7 +++---
projects/testing-library/src/lib/models.ts | 8 ++++--
3 files changed, 26 insertions(+), 14 deletions(-)
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 cb22ba6..470e639 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
@@ -7,11 +7,12 @@ test('works with signal inputs', async () => {
inputs: {
...aliasedInput('greeting', 'Hello'),
name: 'world',
+ age: '45',
},
});
const inputValue = within(screen.getByTestId('input-value'));
- expect(inputValue.getByText(/hello world/i)).toBeInTheDocument();
+ expect(inputValue.getByText(/hello world of 45 years old/i)).toBeInTheDocument();
});
test('works with computed', async () => {
@@ -19,11 +20,12 @@ test('works with computed', async () => {
inputs: {
...aliasedInput('greeting', 'Hello'),
name: 'world',
+ age: '45',
},
});
const computedValue = within(screen.getByTestId('computed-value'));
- expect(computedValue.getByText(/hello world/i)).toBeInTheDocument();
+ expect(computedValue.getByText(/hello world of 45 years old/i)).toBeInTheDocument();
});
test('can update signal inputs', async () => {
@@ -31,18 +33,19 @@ test('can update signal inputs', async () => {
inputs: {
...aliasedInput('greeting', 'Hello'),
name: 'world',
+ age: '45',
},
});
const inputValue = within(screen.getByTestId('input-value'));
const computedValue = within(screen.getByTestId('computed-value'));
- expect(inputValue.getByText(/hello world/i)).toBeInTheDocument();
+ expect(inputValue.getByText(/hello world of 45 years old/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 inputValue.findByText(/hello updated/i)).toBeInTheDocument();
- expect(await computedValue.findByText(/hello updated/i)).toBeInTheDocument();
+ expect(await inputValue.findByText(/hello updated of 45 years old/i)).toBeInTheDocument();
+ expect(await computedValue.findByText(/hello updated of 45 years old/i)).toBeInTheDocument();
// it's not recommended to access the model directly, but it's possible
expect(fixture.componentInstance.name()).toBe('updated');
@@ -54,6 +57,7 @@ test('output emits a value', async () => {
inputs: {
...aliasedInput('greeting', 'Hello'),
name: 'world',
+ age: '45',
},
on: {
submit: submitFn,
@@ -70,6 +74,7 @@ test('model update also updates the template', async () => {
inputs: {
...aliasedInput('greeting', 'Hello'),
name: 'initial',
+ age: '45',
},
});
@@ -100,22 +105,24 @@ test('works with signal inputs, computed values, and rerenders', async () => {
inputs: {
...aliasedInput('greeting', 'Hello'),
name: 'world',
+ age: '45',
},
});
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();
+ expect(inputValue.getByText(/hello world of 45 years old/i)).toBeInTheDocument();
+ expect(computedValue.getByText(/hello world of 45 years old/i)).toBeInTheDocument();
await view.rerender({
inputs: {
...aliasedInput('greeting', 'bye'),
name: 'test',
+ age: '0',
},
});
- expect(inputValue.getByText(/bye test/i)).toBeInTheDocument();
- expect(computedValue.getByText(/bye test/i)).toBeInTheDocument();
+ expect(inputValue.getByText(/bye test of 0 years old/i)).toBeInTheDocument();
+ expect(computedValue.getByText(/bye test of 0 years old/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 ddc0c90..dfe6bd0 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,10 @@
-import { Component, computed, input, model, output } from '@angular/core';
+import { Component, computed, input, model, numberAttribute, output } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-signal-input',
template: `
-
{{ greetings() }} {{ name() }}
+ {{ greetings() }} {{ name() }} of {{ age() }} years old
{{ greetingMessage() }}
@@ -16,10 +16,11 @@ export class SignalInputComponent {
greetings = input('', {
alias: 'greeting',
});
+ age = input.required({ transform: numberAttribute });
name = model.required();
submit = output();
- greetingMessage = computed(() => `${this.greetings()} ${this.name()}`);
+ greetingMessage = computed(() => `${this.greetings()} ${this.name()} of ${this.age()} years old`);
submitName() {
this.submit.emit(this.name());
diff --git a/projects/testing-library/src/lib/models.ts b/projects/testing-library/src/lib/models.ts
index f7697c5..1956728 100644
--- a/projects/testing-library/src/lib/models.ts
+++ b/projects/testing-library/src/lib/models.ts
@@ -1,4 +1,4 @@
-import { Type, DebugElement, EventEmitter, Signal } from '@angular/core';
+import { Type, DebugElement, EventEmitter, Signal, InputSignalWithTransform } from '@angular/core';
import { ComponentFixture, DeferBlockBehavior, DeferBlockState, TestBed } from '@angular/core/testing';
import { Routes } from '@angular/router';
import { BoundFunction, Queries, queries, Config as dtlConfig, PrettyDOMOptions } from '@testing-library/dom';
@@ -94,7 +94,11 @@ export type AliasedInputs = Record>;
export type ComponentInput =
| {
- [P in keyof T]?: T[P] extends Signal ? U : T[P];
+ [P in keyof T]?: T[P] extends InputSignalWithTransform
+ ? U
+ : T[P] extends Signal
+ ? U
+ : T[P];
}
| AliasedInputs;
From b11b6c3c60a02172ad5bf02e7d3b1d8bb5196f8f Mon Sep 17 00:00:00 2001
From: "allcontributors[bot]"
<46447321+allcontributors[bot]@users.noreply.github.com>
Date: Tue, 13 Aug 2024 19:50:36 +0200
Subject: [PATCH 02/26] docs: add mlz11 as a contributor for code, and test
(#485)
* docs: update README.md [skip ci]
* docs: update .all-contributorsrc [skip ci]
---------
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
---
.all-contributorsrc | 10 ++++++++++
README.md | 1 +
2 files changed, 11 insertions(+)
diff --git a/.all-contributorsrc b/.all-contributorsrc
index a919fb6..198cd09 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -400,6 +400,16 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "mlz11",
+ "name": "Mahdi Lazraq",
+ "avatar_url": "https://avatars.githubusercontent.com/u/94069699?v=4",
+ "profile": "https://github.com/mlz11",
+ "contributions": [
+ "code",
+ "test"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/README.md b/README.md
index f542b69..17bffce 100644
--- a/README.md
+++ b/README.md
@@ -268,6 +268,7 @@ Thanks goes to these people ([emoji key][emojis]):
 S. Mumenthaler 💻 ⚠️ |
 Andrei Alecu 💻 🤔 📖 |
 Daniel Ramírez Barrientos 💻 |
+  Mahdi Lazraq 💻 ⚠️ |
From 460fb32da1158dc8adc26ca6acd052078cb747d4 Mon Sep 17 00:00:00 2001
From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com>
Date: Tue, 13 Aug 2024 20:04:07 +0200
Subject: [PATCH 03/26]
From 81b457b8e5ec8e15d8af92cfdfdb666e83f90046 Mon Sep 17 00:00:00 2001
From: Mahdi Lazraq <94069699+mlz11@users.noreply.github.com>
Date: Wed, 14 Aug 2024 08:52:03 +0200
Subject: [PATCH 04/26] feat: deprecate componentProperties (#486)
Closes #471
---
projects/testing-library/src/lib/models.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/projects/testing-library/src/lib/models.ts b/projects/testing-library/src/lib/models.ts
index 1956728..62413fb 100644
--- a/projects/testing-library/src/lib/models.ts
+++ b/projects/testing-library/src/lib/models.ts
@@ -215,7 +215,7 @@ export interface RenderComponentOptions
Date: Sat, 17 Aug 2024 10:51:10 +0200
Subject: [PATCH 05/26] chore: bump @angular-eslint/eslint-plugin v17.3.0 ->
v18.3.0 (#488)
---
.github/workflows/ci.yml | 2 +-
package.json | 10 +++++-----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5820814..28aac0d 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 --force
+ run: npm install
- name: build
run: npm run build -- --skip-nx-cache
- name: test
diff --git a/package.json b/package.json
index 235d0ec..a934fb2 100644
--- a/package.json
+++ b/package.json
@@ -47,11 +47,11 @@
"@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-eslint/builder": "18.3.0",
+ "@angular-eslint/eslint-plugin": "18.3.0",
+ "@angular-eslint/eslint-plugin-template": "18.3.0",
+ "@angular-eslint/schematics": "18.3.0",
+ "@angular-eslint/template-parser": "18.3.0",
"@angular/cli": "~18.0.0",
"@angular/compiler-cli": "18.0.0",
"@angular/forms": "18.0.0",
From eb4fc7445b58af9b4fb6e0f0f5b886d4c0504065 Mon Sep 17 00:00:00 2001
From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com>
Date: Fri, 23 Aug 2024 18:47:53 +0200
Subject: [PATCH 06/26] fix: update description in JSDocs (#489)
---
projects/testing-library/src/lib/models.ts | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/projects/testing-library/src/lib/models.ts b/projects/testing-library/src/lib/models.ts
index 62413fb..a52371d 100644
--- a/projects/testing-library/src/lib/models.ts
+++ b/projects/testing-library/src/lib/models.ts
@@ -254,10 +254,11 @@ export interface RenderComponentOptions;
@@ -292,7 +293,7 @@ export interface RenderComponentOptions { ... }
* await render(AppComponent, {
* on: {
- * send: (_v:any) => void
+ * send: (value) => sendValue(value)
* }
* })
*/
From fbbed20eaabd71db5e480af54da738b151482c80 Mon Sep 17 00:00:00 2001
From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com>
Date: Mon, 14 Oct 2024 09:26:57 +0200
Subject: [PATCH 07/26] docs: add test case for #492 (#495)
---
.../tests/issues/issue-492.spec.ts | 48 +++++++++++++++++++
1 file changed, 48 insertions(+)
create mode 100644 projects/testing-library/tests/issues/issue-492.spec.ts
diff --git a/projects/testing-library/tests/issues/issue-492.spec.ts b/projects/testing-library/tests/issues/issue-492.spec.ts
new file mode 100644
index 0000000..981f5de
--- /dev/null
+++ b/projects/testing-library/tests/issues/issue-492.spec.ts
@@ -0,0 +1,48 @@
+import { AsyncPipe } from '@angular/common';
+import { Component, inject, Injectable } from '@angular/core';
+import { render, screen, waitFor } from '../../src/public_api';
+import { Observable, BehaviorSubject, map } from 'rxjs';
+
+test('displays username', async () => {
+ // stubbed user service using a Subject
+ const user = new BehaviorSubject({ name: 'username 1' });
+ const userServiceStub: Partial = {
+ getName: () => user.asObservable().pipe(map((u) => u.name)),
+ };
+
+ // render the component with injection of the stubbed service
+ await render(UserComponent, {
+ componentProviders: [
+ {
+ provide: UserService,
+ useValue: userServiceStub,
+ },
+ ],
+ });
+
+ // assert first username emitted is rendered
+ expect(await screen.findByRole('heading')).toHaveTextContent('username 1');
+
+ // emitting a second username
+ user.next({ name: 'username 2' });
+
+ // assert the second username is rendered
+ await waitFor(() => expect(screen.getByRole('heading')).toHaveTextContent('username 2'));
+});
+
+@Component({
+ selector: 'atl-user',
+ standalone: true,
+ template: `{{ username$ | async }}
`,
+ imports: [AsyncPipe],
+})
+class UserComponent {
+ readonly username$: Observable = inject(UserService).getName();
+}
+
+@Injectable()
+class UserService {
+ getName(): Observable {
+ throw new Error('Not implemented');
+ }
+}
From be9c3d5cb990b98768f95bbc12e5ed33cbed72a6 Mon Sep 17 00:00:00 2001
From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com>
Date: Mon, 14 Oct 2024 09:30:37 +0200
Subject: [PATCH 08/26] docs: add test case for #492
---
projects/testing-library/tests/issues/issue-492.spec.ts | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/projects/testing-library/tests/issues/issue-492.spec.ts b/projects/testing-library/tests/issues/issue-492.spec.ts
index 981f5de..a1e44b0 100644
--- a/projects/testing-library/tests/issues/issue-492.spec.ts
+++ b/projects/testing-library/tests/issues/issue-492.spec.ts
@@ -1,6 +1,6 @@
import { AsyncPipe } from '@angular/common';
import { Component, inject, Injectable } from '@angular/core';
-import { render, screen, waitFor } from '../../src/public_api';
+import { render, screen } from '../../src/public_api';
import { Observable, BehaviorSubject, map } from 'rxjs';
test('displays username', async () => {
@@ -21,13 +21,13 @@ test('displays username', async () => {
});
// assert first username emitted is rendered
- expect(await screen.findByRole('heading')).toHaveTextContent('username 1');
+ expect(await screen.findByRole('heading', { name: 'username 1' })).toBeInTheDocument();
// emitting a second username
user.next({ name: 'username 2' });
// assert the second username is rendered
- await waitFor(() => expect(screen.getByRole('heading')).toHaveTextContent('username 2'));
+ expect(await screen.findByRole('heading', { name: 'username 2' })).toBeInTheDocument();
});
@Component({
From 2cfca82442cc2d51ba08e649f37b65fa285eadb6 Mon Sep 17 00:00:00 2001
From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com>
Date: Mon, 14 Oct 2024 09:35:35 +0200
Subject: [PATCH 09/26] docs: add test case for #491 (#494)
---
.../src/app/issues/issue-491.spec.ts | 57 +++++++++++++++++++
1 file changed, 57 insertions(+)
create mode 100644 apps/example-app-karma/src/app/issues/issue-491.spec.ts
diff --git a/apps/example-app-karma/src/app/issues/issue-491.spec.ts b/apps/example-app-karma/src/app/issues/issue-491.spec.ts
new file mode 100644
index 0000000..7da4d6d
--- /dev/null
+++ b/apps/example-app-karma/src/app/issues/issue-491.spec.ts
@@ -0,0 +1,57 @@
+import { Component } from '@angular/core';
+import { Router } from '@angular/router';
+import { render, screen, waitForElementToBeRemoved } from '@testing-library/angular';
+import userEvent from '@testing-library/user-event';
+
+it('test click event with router.navigate', async () => {
+ const user = userEvent.setup();
+ await render(``, {
+ routes: [
+ {
+ path: '',
+ component: LoginComponent,
+ },
+ {
+ path: 'logged-in',
+ component: LoggedInComponent,
+ },
+ ],
+ });
+
+ expect(await screen.findByRole('heading', { name: 'Login' })).toBeVisible();
+ expect(screen.getByRole('button', { name: 'submit' })).toBeInTheDocument();
+
+ const email = screen.getByRole('textbox', { name: 'email' });
+ const password = screen.getByLabelText('password');
+
+ await user.type(email, 'user@example.com');
+ await user.type(password, 'with_valid_password');
+
+ expect(screen.getByRole('button', { name: 'submit' })).toBeEnabled();
+
+ await user.click(screen.getByRole('button', { name: 'submit' }));
+
+ await waitForElementToBeRemoved(() => screen.queryByRole('heading', { name: 'Login' }));
+
+ expect(await screen.findByRole('heading', { name: 'Logged In' })).toBeVisible();
+});
+
+@Component({
+ template: `
+ Login
+
+
+
+ `,
+})
+class LoginComponent {
+ constructor(private router: Router) {}
+ onSubmit(): void {
+ this.router.navigate(['logged-in']);
+ }
+}
+
+@Component({
+ template: ` Logged In
`,
+})
+class LoggedInComponent {}
From e8ddcf469a5c6dbadd6a1497cfa113ae56920461 Mon Sep 17 00:00:00 2001
From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com>
Date: Mon, 14 Oct 2024 19:06:58 +0200
Subject: [PATCH 10/26] docs: add test case for #493 (#496)
---
.../tests/issues/issue-493.spec.ts | 27 +++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 projects/testing-library/tests/issues/issue-493.spec.ts
diff --git a/projects/testing-library/tests/issues/issue-493.spec.ts b/projects/testing-library/tests/issues/issue-493.spec.ts
new file mode 100644
index 0000000..a49bc80
--- /dev/null
+++ b/projects/testing-library/tests/issues/issue-493.spec.ts
@@ -0,0 +1,27 @@
+import { HttpClient, provideHttpClient } from '@angular/common/http';
+import { provideHttpClientTesting } from '@angular/common/http/testing';
+import { Component, input } from '@angular/core';
+import { render, screen } from '../../src/public_api';
+
+test('succeeds', async () => {
+ await render(DummyComponent, {
+ inputs: {
+ value: 'test',
+ },
+ providers: [provideHttpClientTesting(), provideHttpClient()],
+ });
+
+ expect(screen.getByText('test')).toBeVisible();
+});
+
+@Component({
+ selector: 'atl-dummy',
+ standalone: true,
+ imports: [],
+ template: '{{ value() }}
',
+})
+class DummyComponent {
+ value = input.required();
+ // @ts-ignore
+ constructor(private http: HttpClient) {}
+}
From 5a0665fad22a79af5837d0c110148a09ff963d11 Mon Sep 17 00:00:00 2001
From: Suguru Inatomi
Date: Sun, 10 Nov 2024 01:58:38 +0900
Subject: [PATCH 11/26] fix: make wrapper component `standalone: false`
explicitly (#498)
---
projects/testing-library/src/lib/models.ts | 1 +
projects/testing-library/src/lib/testing-library.ts | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/projects/testing-library/src/lib/models.ts b/projects/testing-library/src/lib/models.ts
index a52371d..0c34aa8 100644
--- a/projects/testing-library/src/lib/models.ts
+++ b/projects/testing-library/src/lib/models.ts
@@ -473,6 +473,7 @@ export interface RenderTemplateOptions
Date: Fri, 29 Nov 2024 19:52:25 +0100
Subject: [PATCH 12/26] chore: update to NX 20 (#502)
---
.gitignore | 1 +
.prettierignore | 3 +-
jest.config.ts | 8 +++---
package.json | 73 +++++++++++++++++++++++++------------------------
4 files changed, 44 insertions(+), 41 deletions(-)
diff --git a/.gitignore b/.gitignore
index 215c8cb..d16a75b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,7 @@
/.angular/cache
.angular
.nx
+migrations.json
.cache
/.sass-cache
/connect.lock
diff --git a/.prettierignore b/.prettierignore
index 2bdc4f9..03ff48d 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -54,4 +54,5 @@ deployment.yaml
.DS_Store
Thumbs.db
-/.nx/cache
\ No newline at end of file
+/.nx/cache
+/.nx/workspace-data
\ No newline at end of file
diff --git a/jest.config.ts b/jest.config.ts
index 0830aab..f5c10f4 100644
--- a/jest.config.ts
+++ b/jest.config.ts
@@ -1,5 +1,5 @@
-const { getJestProjects } = require('@nx/jest');
+const { getJestProjectsAsync } = require('@nx/jest');
-export default {
- projects: getJestProjects(),
-};
+export default async () => ({
+ projects: await getJestProjectsAsync(),
+});
diff --git a/package.json b/package.json
index a934fb2..e5030d0 100644
--- a/package.json
+++ b/package.json
@@ -27,51 +27,52 @@
"prepare": "git config core.hookspath .githooks"
},
"dependencies": {
- "@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",
+ "@angular/animations": "18.2.13",
+ "@angular/cdk": "18.2.14",
+ "@angular/common": "18.2.13",
+ "@angular/compiler": "18.2.13",
+ "@angular/core": "18.2.13",
+ "@angular/material": "18.2.14",
+ "@angular/platform-browser": "18.2.13",
+ "@angular/platform-browser-dynamic": "18.2.13",
+ "@angular/router": "18.2.13",
+ "@ngrx/store": "18.0.2",
+ "@nx/angular": "20.1.3",
"@testing-library/dom": "^10.0.0",
"rxjs": "7.8.0",
"tslib": "~2.3.1",
- "zone.js": "0.14.2"
+ "zone.js": "0.14.10"
},
"devDependencies": {
- "@angular-devkit/build-angular": "18.0.1",
- "@angular-devkit/core": "18.0.1",
- "@angular-devkit/schematics": "18.0.1",
+ "@angular-devkit/build-angular": "18.2.9",
+ "@angular-devkit/core": "18.2.9",
+ "@angular-devkit/schematics": "18.2.9",
"@angular-eslint/builder": "18.3.0",
- "@angular-eslint/eslint-plugin": "18.3.0",
- "@angular-eslint/eslint-plugin-template": "18.3.0",
+ "@angular-eslint/eslint-plugin": "18.0.1",
+ "@angular-eslint/eslint-plugin-template": "18.0.1",
"@angular-eslint/schematics": "18.3.0",
- "@angular-eslint/template-parser": "18.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",
+ "@angular-eslint/template-parser": "18.0.1",
+ "@angular/cli": "~18.2.0",
+ "@angular/compiler-cli": "18.2.13",
+ "@angular/forms": "18.2.13",
+ "@angular/language-service": "18.2.13",
+ "@nx/eslint": "20.1.3",
+ "@nx/eslint-plugin": "20.1.3",
+ "@nx/jest": "20.1.3",
+ "@nx/node": "20.1.3",
+ "@nx/plugin": "20.1.3",
+ "@nx/workspace": "20.1.3",
+ "@schematics/angular": "18.2.9",
"@testing-library/jasmine-dom": "^1.2.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/user-event": "^14.4.3",
"@types/jasmine": "4.3.1",
- "@types/jest": "29.5.1",
+ "@types/jest": "29.5.14",
"@types/node": "18.16.9",
"@types/testing-library__jasmine-dom": "^1.3.0",
- "@typescript-eslint/eslint-plugin": "7.3.0",
- "@typescript-eslint/parser": "7.3.0",
+ "@typescript-eslint/eslint-plugin": "7.16.0",
+ "@typescript-eslint/parser": "7.16.0",
+ "@typescript-eslint/utils": "^7.16.0",
"autoprefixer": "^10.4.0",
"cpy-cli": "^3.1.1",
"eslint": "8.57.0",
@@ -84,7 +85,7 @@
"jasmine-core": "4.2.0",
"jasmine-spec-reporter": "7.0.0",
"jest": "29.7.0",
- "jest-environment-jsdom": "29.5.0",
+ "jest-environment-jsdom": "29.7.0",
"jest-preset-angular": "14.1.0",
"karma": "6.4.0",
"karma-chrome-launcher": "^3.1.0",
@@ -93,8 +94,8 @@
"karma-jasmine-html-reporter": "2.0.0",
"lint-staged": "^12.1.6",
"ng-mocks": "^14.11.0",
- "ng-packagr": "18.0.0",
- "nx": "19.1.0",
+ "ng-packagr": "18.2.1",
+ "nx": "20.1.3",
"postcss": "^8.4.5",
"postcss-import": "14.1.0",
"postcss-preset-env": "7.5.0",
@@ -104,6 +105,6 @@
"semantic-release": "^18.0.0",
"ts-jest": "29.1.0",
"ts-node": "10.9.1",
- "typescript": "5.4.5"
+ "typescript": "5.5.4"
}
}
From 061d5cc70aae3b2946707e1711ac833906f0aef5 Mon Sep 17 00:00:00 2001
From: Arthur Petrie
Date: Sat, 30 Nov 2024 13:30:06 +0100
Subject: [PATCH 13/26] perf: optimize reduce and foreach loops (#501)
---
.../src/lib/testing-library.ts | 73 ++++++++++---------
1 file changed, 40 insertions(+), 33 deletions(-)
diff --git a/projects/testing-library/src/lib/testing-library.ts b/projects/testing-library/src/lib/testing-library.ts
index ffd3898..8dfa946 100644
--- a/projects/testing-library/src/lib/testing-library.ts
+++ b/projects/testing-library/src/lib/testing-library.ts
@@ -2,7 +2,6 @@ import {
ApplicationInitStatus,
ChangeDetectorRef,
Component,
- isStandalone,
NgZone,
OnChanges,
OutputRef,
@@ -10,6 +9,7 @@ import {
SimpleChange,
SimpleChanges,
Type,
+ isStandalone,
} from '@angular/core';
import { ComponentFixture, DeferBlockBehavior, DeferBlockState, TestBed, tick } from '@angular/core/testing';
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
@@ -27,14 +27,14 @@ import {
waitForOptions as dtlWaitForOptions,
within as dtlWithin,
} from '@testing-library/dom';
+import { getConfig } from './config';
import {
ComponentOverride,
+ OutputRefKeysWithCallback,
RenderComponentOptions,
RenderResult,
RenderTemplateOptions,
- OutputRefKeysWithCallback,
} from './models';
-import { getConfig } from './config';
type SubscribedOutput = readonly [key: keyof T, callback: (v: any) => void, subscription: OutputRefSubscription];
@@ -71,7 +71,7 @@ export async function render(
on = {},
componentProviders = [],
childComponentOverrides = [],
- componentImports: componentImports,
+ componentImports,
excludeComponentDeclaration = false,
routes = [],
removeAngularAttributes = false,
@@ -116,12 +116,9 @@ export async function render(
await TestBed.compileComponents();
- componentProviders
- .reduce((acc, provider) => acc.concat(provider), [] as any[])
- .forEach((p: any) => {
- const { provide, ...provider } = p;
- TestBed.overrideProvider(provide, provider);
- });
+ for (const { provide, ...provider } of componentProviders) {
+ TestBed.overrideProvider(provide, provider);
+ }
const componentContainer = createComponentFixture(sut, wrapper);
@@ -158,7 +155,9 @@ export async function render(
let result;
if (zone) {
- await zone.run(() => (result = doNavigate()));
+ await zone.run(() => {
+ result = doNavigate();
+ });
} else {
result = doNavigate();
}
@@ -199,7 +198,7 @@ export async function render(
if (removeAngularAttributes) {
createdFixture.nativeElement.removeAttribute('ng-version');
const idAttribute = createdFixture.nativeElement.getAttribute('id');
- if (idAttribute && idAttribute.startsWith('root')) {
+ if (idAttribute?.startsWith('root')) {
createdFixture.nativeElement.removeAttribute('id');
}
}
@@ -207,7 +206,9 @@ export async function render(
mountedFixtures.add(createdFixture);
let isAlive = true;
- createdFixture.componentRef.onDestroy(() => (isAlive = false));
+ createdFixture.componentRef.onDestroy(() => {
+ isAlive = false;
+ });
if (hasOnChangesHook(createdFixture.componentInstance) && Object.keys(properties).length > 0) {
const changes = getChangesObj(null, componentProperties);
@@ -318,10 +319,15 @@ export async function render(
},
debugElement: fixture.debugElement,
container: fixture.nativeElement,
- debug: (element = fixture.nativeElement, maxLength, options) =>
- Array.isArray(element)
- ? element.forEach((e) => console.log(dtlPrettyDOM(e, maxLength, options)))
- : console.log(dtlPrettyDOM(element, maxLength, options)),
+ debug: (element = fixture.nativeElement, maxLength, options) => {
+ if (Array.isArray(element)) {
+ for (const e of element) {
+ console.log(dtlPrettyDOM(e, maxLength, options));
+ }
+ } else {
+ console.log(dtlPrettyDOM(element, maxLength, options));
+ }
+ },
...replaceFindWithFindAndDetectChanges(dtlGetQueriesForElement(fixture.nativeElement, queries)),
};
}
@@ -423,9 +429,11 @@ function overrideComponentImports(sut: Type | string, imports:
}
function overrideChildComponentProviders(componentOverrides: ComponentOverride[]) {
- componentOverrides?.forEach(({ component, providers }) => {
- TestBed.overrideComponent(component, { set: { providers } });
- });
+ if (componentOverrides) {
+ for (const { component, providers } of componentOverrides) {
+ TestBed.overrideComponent(component, { set: { providers } });
+ }
+ }
}
function hasOnChangesHook(componentInstance: SutType): componentInstance is SutType & OnChanges {
@@ -439,13 +447,10 @@ function hasOnChangesHook(componentInstance: SutType): componentInstanc
function getChangesObj(oldProps: Record | null, newProps: Record) {
const isFirstChange = oldProps === null;
- return Object.keys(newProps).reduce(
- (changes, key) => ({
- ...changes,
- [key]: new SimpleChange(isFirstChange ? null : oldProps[key], newProps[key], isFirstChange),
- }),
- {} as Record,
- );
+ return Object.keys(newProps).reduce((changes, key) => {
+ changes[key] = new SimpleChange(isFirstChange ? null : oldProps[key], newProps[key], isFirstChange);
+ return changes;
+ }, {} as Record);
}
function update(
@@ -461,10 +466,12 @@ function update(
const componentInstance = fixture.componentInstance as Record;
const simpleChanges: SimpleChanges = {};
- for (const key of prevRenderedKeys) {
- if (!partialUpdate && !Object.prototype.hasOwnProperty.call(newValues, key)) {
- simpleChanges[key] = new SimpleChange(componentInstance[key], undefined, false);
- delete componentInstance[key];
+ if (!partialUpdate) {
+ for (const key of prevRenderedKeys) {
+ if (!Object.prototype.hasOwnProperty.call(newValues, key)) {
+ simpleChanges[key] = new SimpleChange(componentInstance[key], undefined, false);
+ delete componentInstance[key];
+ }
}
}
@@ -643,7 +650,7 @@ function replaceFindWithFindAndDetectChanges>(orig
* Call detectChanges for all fixtures
*/
function detectChangesForMountedFixtures() {
- mountedFixtures.forEach((fixture) => {
+ for (const fixture of mountedFixtures) {
try {
fixture.detectChanges();
} catch (err: any) {
@@ -651,7 +658,7 @@ function detectChangesForMountedFixtures() {
throw err;
}
}
- });
+ }
}
/**
From 4bd4ab2541a77ebb8b9c265a046f4b17b9bfffe0 Mon Sep 17 00:00:00 2001
From: "allcontributors[bot]"
<46447321+allcontributors[bot]@users.noreply.github.com>
Date: Sat, 30 Nov 2024 13:45:33 +0100
Subject: [PATCH 14/26] docs: add Arthie as a contributor for code (#504)
* docs: update README.md [skip ci]
* docs: update .all-contributorsrc [skip ci]
---------
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
---
.all-contributorsrc | 9 +++++++++
README.md | 3 +++
2 files changed, 12 insertions(+)
diff --git a/.all-contributorsrc b/.all-contributorsrc
index 198cd09..3b3a470 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -410,6 +410,15 @@
"code",
"test"
]
+ },
+ {
+ "login": "Arthie",
+ "name": "Arthur Petrie",
+ "avatar_url": "https://avatars.githubusercontent.com/u/16376476?v=4",
+ "profile": "https://arthurpetrie.com",
+ "contributions": [
+ "code"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/README.md b/README.md
index 17bffce..c98936d 100644
--- a/README.md
+++ b/README.md
@@ -270,6 +270,9 @@ Thanks goes to these people ([emoji key][emojis]):
 Daniel Ramírez Barrientos 💻 |
 Mahdi Lazraq 💻 ⚠️ |
+
+  Arthur Petrie 💻 |
+
From fdcf5fa5c2e30b535439be0766d0b023fce7c35d Mon Sep 17 00:00:00 2001
From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com>
Date: Tue, 3 Dec 2024 10:12:09 +0100
Subject: [PATCH 15/26] fix: support Angular 19 (#503)
---
.github/workflows/ci.yml | 2 +-
package.json | 46 +++++++--------
.../src/lib/testing-library.ts | 15 +++--
projects/testing-library/test-setup.ts | 4 +-
projects/testing-library/tests/config.spec.ts | 1 +
.../testing-library/tests/find-by.spec.ts | 2 +
.../testing-library/tests/integration.spec.ts | 38 +++++++------
.../tests/issues/issue-230.spec.ts | 4 +-
.../tests/issues/issue-280.spec.ts | 9 +--
.../tests/render-template.spec.ts | 18 +++---
projects/testing-library/tests/render.spec.ts | 57 ++++++++++---------
.../wait-for-element-to-be-removed.spec.ts | 2 +
12 files changed, 111 insertions(+), 87 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 28aac0d..5820814 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/package.json b/package.json
index e5030d0..0f8aad8 100644
--- a/package.json
+++ b/package.json
@@ -27,35 +27,35 @@
"prepare": "git config core.hookspath .githooks"
},
"dependencies": {
- "@angular/animations": "18.2.13",
- "@angular/cdk": "18.2.14",
- "@angular/common": "18.2.13",
- "@angular/compiler": "18.2.13",
- "@angular/core": "18.2.13",
- "@angular/material": "18.2.14",
- "@angular/platform-browser": "18.2.13",
- "@angular/platform-browser-dynamic": "18.2.13",
- "@angular/router": "18.2.13",
- "@ngrx/store": "18.0.2",
+ "@angular/animations": "19.0.1",
+ "@angular/cdk": "19.0.1",
+ "@angular/common": "19.0.1",
+ "@angular/compiler": "19.0.1",
+ "@angular/core": "19.0.1",
+ "@angular/material": "19.0.1",
+ "@angular/platform-browser": "19.0.1",
+ "@angular/platform-browser-dynamic": "19.0.1",
+ "@angular/router": "19.0.1",
+ "@ngrx/store": "19.0.0-beta.0",
"@nx/angular": "20.1.3",
- "@testing-library/dom": "^10.0.0",
+ "@testing-library/dom": "^10.4.0",
"rxjs": "7.8.0",
"tslib": "~2.3.1",
- "zone.js": "0.14.10"
+ "zone.js": "^0.15.0"
},
"devDependencies": {
- "@angular-devkit/build-angular": "18.2.9",
- "@angular-devkit/core": "18.2.9",
- "@angular-devkit/schematics": "18.2.9",
+ "@angular-devkit/build-angular": "19.0.1",
+ "@angular-devkit/core": "19.0.1",
+ "@angular-devkit/schematics": "19.0.1",
"@angular-eslint/builder": "18.3.0",
"@angular-eslint/eslint-plugin": "18.0.1",
"@angular-eslint/eslint-plugin-template": "18.0.1",
"@angular-eslint/schematics": "18.3.0",
"@angular-eslint/template-parser": "18.0.1",
- "@angular/cli": "~18.2.0",
- "@angular/compiler-cli": "18.2.13",
- "@angular/forms": "18.2.13",
- "@angular/language-service": "18.2.13",
+ "@angular/cli": "19.0.1",
+ "@angular/compiler-cli": "19.0.1",
+ "@angular/forms": "19.0.1",
+ "@angular/language-service": "19.0.1",
"@nx/eslint": "20.1.3",
"@nx/eslint-plugin": "20.1.3",
"@nx/jest": "20.1.3",
@@ -68,7 +68,7 @@
"@testing-library/user-event": "^14.4.3",
"@types/jasmine": "4.3.1",
"@types/jest": "29.5.14",
- "@types/node": "18.16.9",
+ "@types/node": "22.10.1",
"@types/testing-library__jasmine-dom": "^1.3.0",
"@typescript-eslint/eslint-plugin": "7.16.0",
"@typescript-eslint/parser": "7.16.0",
@@ -86,7 +86,7 @@
"jasmine-spec-reporter": "7.0.0",
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
- "jest-preset-angular": "14.1.0",
+ "jest-preset-angular": "14.4.1",
"karma": "6.4.0",
"karma-chrome-launcher": "^3.1.0",
"karma-coverage": "^2.2.1",
@@ -94,7 +94,7 @@
"karma-jasmine-html-reporter": "2.0.0",
"lint-staged": "^12.1.6",
"ng-mocks": "^14.11.0",
- "ng-packagr": "18.2.1",
+ "ng-packagr": "19.0.1",
"nx": "20.1.3",
"postcss": "^8.4.5",
"postcss-import": "14.1.0",
@@ -105,6 +105,6 @@
"semantic-release": "^18.0.0",
"ts-jest": "29.1.0",
"ts-node": "10.9.1",
- "typescript": "5.5.4"
+ "typescript": "5.6.2"
}
}
diff --git a/projects/testing-library/src/lib/testing-library.ts b/projects/testing-library/src/lib/testing-library.ts
index 8dfa946..7c7de89 100644
--- a/projects/testing-library/src/lib/testing-library.ts
+++ b/projects/testing-library/src/lib/testing-library.ts
@@ -34,6 +34,7 @@ import {
RenderComponentOptions,
RenderResult,
RenderTemplateOptions,
+ Config,
} from './models';
type SubscribedOutput = readonly [key: keyof T, callback: (v: any) => void, subscription: OutputRefSubscription];
@@ -82,7 +83,9 @@ export async function render(
configureTestBed = () => {
/* noop*/
},
- } = { ...globalConfig, ...renderOptions };
+ } = { ...globalConfig, ...renderOptions } as RenderComponentOptions &
+ RenderTemplateOptions &
+ Config;
dtlConfigure({
eventWrapper: (cb) => {
@@ -228,7 +231,7 @@ export async function render(
return createdFixture;
};
- const fixture = await renderFixture(componentProperties, allInputs, componentOutputs, on);
+ const fixture = await renderFixture(componentProperties, allInputs as any, componentOutputs, on);
if (deferBlockStates) {
if (Array.isArray(deferBlockStates)) {
@@ -494,12 +497,16 @@ function addAutoDeclarations(
wrapper,
}: Pick, 'declarations' | 'excludeComponentDeclaration' | 'wrapper'>,
) {
+ const nonStandaloneDeclarations = declarations?.filter((d) => !isStandalone(d));
if (typeof sut === 'string') {
- return [...declarations, wrapper];
+ if (wrapper && isStandalone(wrapper)) {
+ return nonStandaloneDeclarations;
+ }
+ return [...nonStandaloneDeclarations, wrapper];
}
const components = () => (excludeComponentDeclaration || isStandalone(sut) ? [] : [sut]);
- return [...declarations, ...components()];
+ return [...nonStandaloneDeclarations, ...components()];
}
function addAutoImports(
diff --git a/projects/testing-library/test-setup.ts b/projects/testing-library/test-setup.ts
index 600d085..8d79c74 100644
--- a/projects/testing-library/test-setup.ts
+++ b/projects/testing-library/test-setup.ts
@@ -1,6 +1,8 @@
-import 'jest-preset-angular/setup-jest';
+import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
import '@testing-library/jest-dom';
import { TextEncoder, TextDecoder } from 'util';
+setupZoneTestEnv();
+
// eslint-disable-next-line @typescript-eslint/naming-convention
Object.assign(global, { TextDecoder, TextEncoder });
diff --git a/projects/testing-library/tests/config.spec.ts b/projects/testing-library/tests/config.spec.ts
index bb8c61f..041d991 100644
--- a/projects/testing-library/tests/config.spec.ts
+++ b/projects/testing-library/tests/config.spec.ts
@@ -13,6 +13,7 @@ import { ReactiveFormsModule, FormBuilder } from '@angular/forms';
`,
+ standalone: false,
})
class FormsComponent {
form = this.formBuilder.group({
diff --git a/projects/testing-library/tests/find-by.spec.ts b/projects/testing-library/tests/find-by.spec.ts
index 9d499fd..30f11ee 100644
--- a/projects/testing-library/tests/find-by.spec.ts
+++ b/projects/testing-library/tests/find-by.spec.ts
@@ -2,10 +2,12 @@ import { Component } from '@angular/core';
import { timer } from 'rxjs';
import { render, screen } from '../src/public_api';
import { mapTo } from 'rxjs/operators';
+import { AsyncPipe } from '@angular/common';
@Component({
selector: 'atl-fixture',
template: ` {{ result | async }}
`,
+ imports: [AsyncPipe],
})
class FixtureComponent {
result = timer(30).pipe(mapTo('I am visible'));
diff --git a/projects/testing-library/tests/integration.spec.ts b/projects/testing-library/tests/integration.spec.ts
index eedec0e..02ca290 100644
--- a/projects/testing-library/tests/integration.spec.ts
+++ b/projects/testing-library/tests/integration.spec.ts
@@ -4,6 +4,7 @@ import { of, BehaviorSubject } from 'rxjs';
import { debounceTime, switchMap, map, startWith } from 'rxjs/operators';
import { render, screen, waitFor, waitForElementToBeRemoved, within } from '../src/lib/testing-library';
import userEvent from '@testing-library/user-event';
+import { AsyncPipe, NgForOf } from '@angular/common';
const DEBOUNCE_TIME = 1_000;
@@ -21,6 +22,25 @@ class ModalService {
}
}
+@Component({
+ selector: 'atl-table',
+ template: `
+
+
+ {{ entity.name }} |
+
+
+ |
+
+
+ `,
+ imports: [NgForOf],
+})
+class TableComponent {
+ @Input() entities: any[] = [];
+ @Output() edit = new EventEmitter();
+}
+
@Component({
template: `
Entities Title
@@ -31,6 +51,7 @@ class ModalService {
`,
+ imports: [TableComponent, AsyncPipe],
})
class EntitiesComponent {
query = new BehaviorSubject('');
@@ -55,22 +76,6 @@ class EntitiesComponent {
}
}
-@Component({
- selector: 'atl-table',
- template: `
-
-
- {{ entity.name }} |
- |
-
-
- `,
-})
-class TableComponent {
- @Input() entities: any[] = [];
- @Output() edit = new EventEmitter();
-}
-
const entities = [
{
id: 1,
@@ -91,7 +96,6 @@ async function setup() {
const user = userEvent.setup();
await render(EntitiesComponent, {
- declarations: [TableComponent],
providers: [
{
provide: EntitiesService,
diff --git a/projects/testing-library/tests/issues/issue-230.spec.ts b/projects/testing-library/tests/issues/issue-230.spec.ts
index fe004b6..8df58f6 100644
--- a/projects/testing-library/tests/issues/issue-230.spec.ts
+++ b/projects/testing-library/tests/issues/issue-230.spec.ts
@@ -1,8 +1,10 @@
import { Component } from '@angular/core';
import { render, waitFor, screen } from '../../src/public_api';
+import { NgClass } from '@angular/common';
@Component({
template: ` `,
+ imports: [NgClass],
})
class LoopComponent {
get classes() {
@@ -17,7 +19,7 @@ test('wait does not end up in a loop', async () => {
await expect(
waitFor(() => {
- expect(true).toEqual(false);
+ expect(true).toBe(false);
}),
).rejects.toThrow();
});
diff --git a/projects/testing-library/tests/issues/issue-280.spec.ts b/projects/testing-library/tests/issues/issue-280.spec.ts
index 19f644e..5e59534 100644
--- a/projects/testing-library/tests/issues/issue-280.spec.ts
+++ b/projects/testing-library/tests/issues/issue-280.spec.ts
@@ -1,19 +1,21 @@
import { Location } from '@angular/common';
import { Component, NgModule } from '@angular/core';
-import { RouterModule, Routes } from '@angular/router';
+import { RouterLink, RouterModule, RouterOutlet, Routes } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import userEvent from '@testing-library/user-event';
import { render, screen } from '../../src/public_api';
@Component({
- template: `Navigate
+ template: ` Navigate
`,
+ imports: [RouterOutlet],
})
class MainComponent {}
@Component({
- template: `first page
+ template: ` first page
go to second`,
+ imports: [RouterLink],
})
class FirstComponent {}
@@ -35,7 +37,6 @@ const routes: Routes = [
];
@NgModule({
- declarations: [FirstComponent, SecondComponent],
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
diff --git a/projects/testing-library/tests/render-template.spec.ts b/projects/testing-library/tests/render-template.spec.ts
index a6892db..e185f70 100644
--- a/projects/testing-library/tests/render-template.spec.ts
+++ b/projects/testing-library/tests/render-template.spec.ts
@@ -45,7 +45,7 @@ class GreetingComponent {
test('the directive renders', async () => {
const view = await render('', {
- declarations: [OnOffDirective],
+ imports: [OnOffDirective],
});
// eslint-disable-next-line testing-library/no-container
@@ -54,7 +54,7 @@ test('the directive renders', async () => {
test('the component renders', async () => {
const view = await render('', {
- declarations: [GreetingComponent],
+ imports: [GreetingComponent],
});
// eslint-disable-next-line testing-library/no-container
@@ -64,7 +64,7 @@ test('the component renders', async () => {
test('uses the default props', async () => {
await render('', {
- declarations: [OnOffDirective],
+ imports: [OnOffDirective],
});
fireEvent.click(screen.getByText('init'));
@@ -74,7 +74,7 @@ test('uses the default props', async () => {
test('overrides input properties', async () => {
await render('', {
- declarations: [OnOffDirective],
+ imports: [OnOffDirective],
});
fireEvent.click(screen.getByText('init'));
@@ -85,7 +85,7 @@ test('overrides input properties', async () => {
test('overrides input properties via a wrapper', async () => {
// `bar` will be set as a property on the wrapper component, the property will be used to pass to the directive
await render('', {
- declarations: [OnOffDirective],
+ imports: [OnOffDirective],
componentProperties: {
bar: 'hello',
},
@@ -100,7 +100,7 @@ test('overrides output properties', async () => {
const clicked = jest.fn();
await render('', {
- declarations: [OnOffDirective],
+ imports: [OnOffDirective],
componentProperties: {
clicked,
},
@@ -116,7 +116,7 @@ test('overrides output properties', async () => {
describe('removeAngularAttributes', () => {
it('should remove angular attributes', async () => {
await render('', {
- declarations: [OnOffDirective],
+ imports: [OnOffDirective],
removeAngularAttributes: true,
});
@@ -126,7 +126,7 @@ describe('removeAngularAttributes', () => {
it('is disabled by default', async () => {
await render('', {
- declarations: [OnOffDirective],
+ imports: [OnOffDirective],
});
expect(document.querySelector('[ng-version]')).not.toBeNull();
@@ -136,7 +136,7 @@ describe('removeAngularAttributes', () => {
test('updates properties and invokes change detection', async () => {
const view = await render<{ value: string }>('', {
- declarations: [UpdateInputDirective],
+ imports: [UpdateInputDirective],
componentProperties: {
value: 'value1',
},
diff --git a/projects/testing-library/tests/render.spec.ts b/projects/testing-library/tests/render.spec.ts
index 59e0f75..52d318c 100644
--- a/projects/testing-library/tests/render.spec.ts
+++ b/projects/testing-library/tests/render.spec.ts
@@ -47,33 +47,31 @@ describe('DTL functionality', () => {
});
});
-describe('standalone', () => {
+describe('components', () => {
@Component({
selector: 'atl-fixture',
template: ` {{ name }} `,
})
- class StandaloneFixtureComponent {
+ class FixtureWithInputComponent {
@Input() name = '';
}
- it('renders standalone component', async () => {
- await render(StandaloneFixtureComponent, { componentProperties: { name: 'Bob' } });
+ it('renders component', async () => {
+ await render(FixtureWithInputComponent, { componentProperties: { name: 'Bob' } });
expect(screen.getByText('Bob')).toBeInTheDocument();
});
});
-describe('standalone with child', () => {
+describe('component with child', () => {
@Component({
selector: 'atl-child-fixture',
template: `A child fixture`,
- standalone: true,
})
class ChildFixtureComponent {}
@Component({
selector: 'atl-child-fixture',
template: `A mock child fixture`,
- standalone: true,
// eslint-disable-next-line @angular-eslint/no-host-metadata-property, @typescript-eslint/naming-convention
host: { 'collision-id': MockChildFixtureComponent.name },
})
@@ -83,18 +81,17 @@ describe('standalone with child', () => {
selector: 'atl-parent-fixture',
template: `Parent fixture
`,
- standalone: true,
imports: [ChildFixtureComponent],
})
class ParentFixtureComponent {}
- it('renders the standalone component with a mocked child', async () => {
+ it('renders the component with a mocked child', async () => {
await render(ParentFixtureComponent, { componentImports: [MockChildFixtureComponent] });
expect(screen.getByText('Parent fixture')).toBeInTheDocument();
expect(screen.getByText('A mock child fixture')).toBeInTheDocument();
});
- it('renders the standalone component with child', async () => {
+ it('renders the component with child', async () => {
await render(ParentFixtureComponent);
expect(screen.getByText('Parent fixture')).toBeInTheDocument();
expect(screen.getByText('A child fixture')).toBeInTheDocument();
@@ -118,7 +115,6 @@ describe('childComponentOverrides', () => {
@Component({
selector: 'atl-child-fixture',
template: `{{ simpleService.value }}`,
- standalone: true,
providers: [MySimpleService],
})
class NestedChildFixtureComponent {
@@ -128,7 +124,6 @@ describe('childComponentOverrides', () => {
@Component({
selector: 'atl-parent-fixture',
template: ``,
- standalone: true,
imports: [NestedChildFixtureComponent],
})
class ParentFixtureComponent {}
@@ -190,22 +185,22 @@ describe('componentOutputs', () => {
});
describe('on', () => {
- @Component({ template: ``, standalone: true })
+ @Component({ template: `` })
class TestFixtureWithEventEmitterComponent {
@Output() readonly event = new EventEmitter();
}
- @Component({ template: ``, standalone: true })
+ @Component({ template: `` })
class TestFixtureWithDerivedEventComponent {
@Output() readonly event = fromEvent(inject(ElementRef).nativeElement, 'click');
}
- @Component({ template: ``, standalone: true })
+ @Component({ template: `` })
class TestFixtureWithFunctionalOutputComponent {
readonly event = output();
}
- @Component({ template: ``, standalone: true })
+ @Component({ template: `` })
class TestFixtureWithFunctionalDerivedEventComponent {
readonly event = outputFromObservable(fromEvent(inject(ElementRef).nativeElement, 'click'));
}
@@ -313,20 +308,31 @@ describe('on', () => {
});
});
-describe('animationModule', () => {
+describe('excludeComponentDeclaration', () => {
+ @Component({
+ selector: 'atl-fixture',
+ template: `
+
+
+ `,
+ standalone: false,
+ })
+ class NotStandaloneFixtureComponent {}
+
@NgModule({
- declarations: [FixtureComponent],
+ declarations: [NotStandaloneFixtureComponent],
})
class FixtureModule {}
- describe('excludeComponentDeclaration', () => {
- it('does not throw if component is declared in an imported module', async () => {
- await render(FixtureComponent, {
- imports: [FixtureModule],
- excludeComponentDeclaration: true,
- });
+
+ it('does not throw if component is declared in an imported module', async () => {
+ await render(NotStandaloneFixtureComponent, {
+ imports: [FixtureModule],
+ excludeComponentDeclaration: true,
});
});
+});
+describe('animationModule', () => {
it('adds NoopAnimationsModule by default', async () => {
await render(FixtureComponent);
const noopAnimationsModule = TestBed.inject(NoopAnimationsModule);
@@ -458,14 +464,12 @@ describe('DebugElement', () => {
describe('initialRoute', () => {
@Component({
- standalone: true,
selector: 'atl-fixture2',
template: ``,
})
class SecondaryFixtureComponent {}
@Component({
- standalone: true,
selector: 'atl-router-fixture',
template: ``,
imports: [RouterModule],
@@ -502,7 +506,6 @@ describe('initialRoute', () => {
it('allows initially rendering a specific route with query parameters', async () => {
@Component({
- standalone: true,
selector: 'atl-query-param-fixture',
template: `paramPresent$: {{ paramPresent$ | async }}
`,
imports: [NgIf, AsyncPipe],
diff --git a/projects/testing-library/tests/wait-for-element-to-be-removed.spec.ts b/projects/testing-library/tests/wait-for-element-to-be-removed.spec.ts
index 5c16a53..64d6c35 100644
--- a/projects/testing-library/tests/wait-for-element-to-be-removed.spec.ts
+++ b/projects/testing-library/tests/wait-for-element-to-be-removed.spec.ts
@@ -1,10 +1,12 @@
import { Component, OnInit } from '@angular/core';
import { render, screen, waitForElementToBeRemoved } from '../src/public_api';
import { timer } from 'rxjs';
+import { NgIf } from '@angular/common';
@Component({
selector: 'atl-fixture',
template: ` 👋
`,
+ imports: [NgIf],
})
class FixtureComponent implements OnInit {
visible = true;
From d59e4f213a4441e63b3157f83e54e9e9d6070a6d Mon Sep 17 00:00:00 2001
From: Suguru Inatomi
Date: Sun, 15 Dec 2024 01:35:56 +0900
Subject: [PATCH 16/26] fix: flatten component providers for consistency to the
framework (#507)
Closes #506
---
.../src/lib/testing-library.ts | 3 ++-
.../providers/component-provider.spec.ts | 20 ++++++++++++++++++-
2 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/projects/testing-library/src/lib/testing-library.ts b/projects/testing-library/src/lib/testing-library.ts
index 7c7de89..2ddf389 100644
--- a/projects/testing-library/src/lib/testing-library.ts
+++ b/projects/testing-library/src/lib/testing-library.ts
@@ -119,7 +119,8 @@ export async function render(
await TestBed.compileComponents();
- for (const { provide, ...provider } of componentProviders) {
+ // Angular supports nested arrays of providers, so we need to flatten them to emulate the same behavior.
+ for (const { provide, ...provider } of componentProviders.flat(Infinity)) {
TestBed.overrideProvider(provide, provider);
}
diff --git a/projects/testing-library/tests/providers/component-provider.spec.ts b/projects/testing-library/tests/providers/component-provider.spec.ts
index 3c3ec0c..9290d5b 100644
--- a/projects/testing-library/tests/providers/component-provider.spec.ts
+++ b/projects/testing-library/tests/providers/component-provider.spec.ts
@@ -1,4 +1,4 @@
-import { Injectable } from '@angular/core';
+import { Injectable, Provider } from '@angular/core';
import { Component } from '@angular/core';
import { render, screen } from '../../src/public_api';
@@ -42,6 +42,24 @@ test('shows the provided service value with template syntax', async () => {
expect(screen.getByText('bar')).toBeInTheDocument();
});
+test('flatten the nested array of component providers', async () => {
+ const provideService = (): Provider => [
+ {
+ provide: Service,
+ useValue: {
+ foo() {
+ return 'bar';
+ },
+ },
+ },
+ ];
+ await render(FixtureComponent, {
+ componentProviders: [provideService()],
+ });
+
+ expect(screen.getByText('bar')).toBeInTheDocument();
+});
+
@Injectable()
class Service {
foo() {
From 5ef0863a2e984d47e077ada300e87d8d3adbfabf Mon Sep 17 00:00:00 2001
From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com>
Date: Sat, 21 Dec 2024 18:58:26 +0100
Subject: [PATCH 17/26] docs: add Angular 19 to Version compatibility table
(#509)
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index c98936d..5ecd39e 100644
--- a/README.md
+++ b/README.md
@@ -179,6 +179,7 @@ You may also be interested in installing `jest-dom` so you can use
| Angular | Angular Testing Library |
| ------- | ---------------------------- |
+| 19.x | 17.x, 16.x, 15.x, 14.x, 13.x |
| 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 |
From 33af2876d3dca98e52b6633b70cf088f7354071a Mon Sep 17 00:00:00 2001
From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com>
Date: Fri, 3 Jan 2025 20:20:39 +0100
Subject: [PATCH 18/26] chore: update deps (#510)
---
.devcontainer/devcontainer.json | 2 +-
.github/workflows/ci.yml | 2 +-
.node-version | 2 +-
package.json | 34 ++++++++++++++++-----------------
4 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index db976dc..72780fc 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,7 +1,7 @@
// For format details, see https://aka.ms/devcontainer.json.
{
"name": "angular-testing-library",
- "image": "mcr.microsoft.com/devcontainers/typescript-node:0-20-bullseye",
+ "image": "mcr.microsoft.com/devcontainers/typescript-node:0-22-bullseye",
// Features to add to the dev container. More info: https://containers.dev/features.
"features": {
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5820814..9955c0d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -22,7 +22,7 @@ jobs:
strategy:
matrix:
- node-version: ${{ fromJSON((github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta') && '[20]' || '[18, 20]') }}
+ node-version: ${{ fromJSON((github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta') && '[22]' || '[18, 20, 22]') }}
os: ${{ fromJSON((github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta') && '["ubuntu-latest"]' || '["ubuntu-latest", "windows-latest"]') }}
runs-on: ${{ matrix.os }}
diff --git a/.node-version b/.node-version
index 914d1a7..8fdd954 100644
--- a/.node-version
+++ b/.node-version
@@ -1 +1 @@
-20.9
\ No newline at end of file
+22
\ No newline at end of file
diff --git a/package.json b/package.json
index 0f8aad8..16ac142 100644
--- a/package.json
+++ b/package.json
@@ -36,8 +36,8 @@
"@angular/platform-browser": "19.0.1",
"@angular/platform-browser-dynamic": "19.0.1",
"@angular/router": "19.0.1",
- "@ngrx/store": "19.0.0-beta.0",
- "@nx/angular": "20.1.3",
+ "@ngrx/store": "19.0.0",
+ "@nx/angular": "20.3.0",
"@testing-library/dom": "^10.4.0",
"rxjs": "7.8.0",
"tslib": "~2.3.1",
@@ -47,21 +47,21 @@
"@angular-devkit/build-angular": "19.0.1",
"@angular-devkit/core": "19.0.1",
"@angular-devkit/schematics": "19.0.1",
- "@angular-eslint/builder": "18.3.0",
- "@angular-eslint/eslint-plugin": "18.0.1",
- "@angular-eslint/eslint-plugin-template": "18.0.1",
- "@angular-eslint/schematics": "18.3.0",
- "@angular-eslint/template-parser": "18.0.1",
- "@angular/cli": "19.0.1",
+ "@angular-eslint/builder": "19.0.2",
+ "@angular-eslint/eslint-plugin": "19.0.2",
+ "@angular-eslint/eslint-plugin-template": "19.0.2",
+ "@angular-eslint/schematics": "19.0.2",
+ "@angular-eslint/template-parser": "19.0.2",
+ "@angular/cli": "~19.0.0",
"@angular/compiler-cli": "19.0.1",
"@angular/forms": "19.0.1",
"@angular/language-service": "19.0.1",
- "@nx/eslint": "20.1.3",
- "@nx/eslint-plugin": "20.1.3",
- "@nx/jest": "20.1.3",
- "@nx/node": "20.1.3",
- "@nx/plugin": "20.1.3",
- "@nx/workspace": "20.1.3",
+ "@nx/eslint": "20.3.0",
+ "@nx/eslint-plugin": "20.3.0",
+ "@nx/jest": "20.3.0",
+ "@nx/node": "20.3.0",
+ "@nx/plugin": "20.3.0",
+ "@nx/workspace": "20.3.0",
"@schematics/angular": "18.2.9",
"@testing-library/jasmine-dom": "^1.2.0",
"@testing-library/jest-dom": "^5.16.5",
@@ -86,16 +86,16 @@
"jasmine-spec-reporter": "7.0.0",
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
- "jest-preset-angular": "14.4.1",
+ "jest-preset-angular": "14.4.2",
"karma": "6.4.0",
"karma-chrome-launcher": "^3.1.0",
"karma-coverage": "^2.2.1",
"karma-jasmine": "5.1.0",
"karma-jasmine-html-reporter": "2.0.0",
"lint-staged": "^12.1.6",
- "ng-mocks": "^14.11.0",
+ "ng-mocks": "^14.13.1",
"ng-packagr": "19.0.1",
- "nx": "20.1.3",
+ "nx": "20.3.0",
"postcss": "^8.4.5",
"postcss-import": "14.1.0",
"postcss-preset-env": "7.5.0",
From e0cd81e6a881dafe92cad10d19ecef26be977f88 Mon Sep 17 00:00:00 2001
From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com>
Date: Sat, 4 Jan 2025 13:51:48 +0100
Subject: [PATCH 19/26] chore: migrate to eslint flat config
---
.eslintignore | 1 -
.eslintrc.json | 127 ------------------
.github/workflows/ci.yml | 2 +
README.md | 2 +-
apps/example-app-karma/.eslintrc.json | 44 ------
apps/example-app-karma/eslint.config.cjs | 7 +
apps/example-app-karma/eslint.config.mjs | 8 ++
apps/example-app-karma/jasmine-dom.d.ts | 1 -
.../src/app/examples/login-form.spec.ts | 4 +-
.../src/app/issues/rerender.spec.ts | 4 +-
apps/example-app/.eslintrc.json | 47 -------
apps/example-app/eslint.config.cjs | 7 +
apps/example-app/eslint.config.mjs | 8 ++
apps/example-app/jest.config.ts | 1 -
.../src/app/examples/00-single-component.ts | 2 +-
.../src/app/examples/01-nested-component.ts | 12 +-
.../src/app/examples/02-input-output.spec.ts | 4 +-
.../src/app/examples/02-input-output.ts | 2 +-
apps/example-app/src/app/examples/03-forms.ts | 2 +-
.../app/examples/04-forms-with-material.ts | 2 +-
.../src/app/examples/05-component-provider.ts | 2 +-
.../src/app/examples/06-with-ngrx-store.ts | 2 +-
.../app/examples/07-with-ngrx-mock-store.ts | 6 +-
.../src/app/examples/08-directive.spec.ts | 8 +-
.../src/app/examples/08-directive.ts | 2 +-
.../example-app/src/app/examples/09-router.ts | 6 +-
.../examples/10-inject-token-dependency.ts | 2 +-
.../src/app/examples/11-ng-content.spec.ts | 2 +-
.../src/app/examples/11-ng-content.ts | 2 +-
.../src/app/examples/12-service-component.ts | 2 +-
.../app/examples/13-scrolling.component.ts | 2 +-
.../app/examples/14-async-component.spec.ts | 1 -
.../src/app/examples/14-async-component.ts | 2 +-
.../app/examples/15-dialog.component.spec.ts | 2 +-
.../src/app/examples/15-dialog.component.ts | 4 +-
.../app/examples/16-input-getter-setter.ts | 2 +-
...-component-with-attribute-selector.spec.ts | 2 +-
.../17-component-with-attribute-selector.ts | 2 +-
.../app/examples/19-standalone-component.ts | 6 +-
.../src/app/examples/20-test-harness.spec.ts | 4 +-
.../src/app/examples/20-test-harness.ts | 2 +-
.../examples/21-deferable-view.component.ts | 4 +-
.../22-signal-inputs.component.spec.ts | 2 +-
.../examples/22-signal-inputs.component.ts | 6 +-
apps/example-app/src/test-setup.ts | 4 +-
eslint.config.cjs | 7 +
eslint.config.mjs | 66 +++++++++
nx.json | 4 +-
package.json | 48 ++++---
projects/testing-library/.eslintrc.json | 62 ---------
projects/testing-library/eslint.config.cjs | 7 +
projects/testing-library/eslint.config.mjs | 8 ++
projects/testing-library/jest.config.ts | 1 -
.../dtl-as-dev-dependency/index.spec.ts | 2 -
.../schematics/ng-add/schema.ts | 1 -
projects/testing-library/src/lib/models.ts | 4 +-
.../src/lib/testing-library.ts | 2 +-
projects/testing-library/test-setup.ts | 1 -
projects/testing-library/tests/debug.spec.ts | 8 +-
.../tests/issues/issue-280.spec.ts | 4 +-
.../tests/issues/issue-389.spec.ts | 1 -
.../issue-396-standalone-stub-child.spec.ts | 1 -
...ssue-398-component-without-host-id.spec.ts | 2 -
.../issue-422-view-already-destroyed.spec.ts | 1 +
.../tests/issues/issue-437.spec.ts | 2 -
.../tests/issues/issue-493.spec.ts | 2 +-
projects/testing-library/tests/render.spec.ts | 19 ++-
67 files changed, 222 insertions(+), 397 deletions(-)
delete mode 100644 .eslintignore
delete mode 100644 .eslintrc.json
delete mode 100644 apps/example-app-karma/.eslintrc.json
create mode 100644 apps/example-app-karma/eslint.config.cjs
create mode 100644 apps/example-app-karma/eslint.config.mjs
delete mode 100644 apps/example-app/.eslintrc.json
create mode 100644 apps/example-app/eslint.config.cjs
create mode 100644 apps/example-app/eslint.config.mjs
create mode 100644 eslint.config.cjs
create mode 100644 eslint.config.mjs
delete mode 100644 projects/testing-library/.eslintrc.json
create mode 100644 projects/testing-library/eslint.config.cjs
create mode 100644 projects/testing-library/eslint.config.mjs
diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index 3c3629e..0000000
--- a/.eslintignore
+++ /dev/null
@@ -1 +0,0 @@
-node_modules
diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index 0a96094..0000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,127 +0,0 @@
-{
- "root": true,
- "ignorePatterns": ["**/*"],
- "plugins": ["@nx", "testing-library"],
- "overrides": [
- {
- "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
- "rules": {
- "@nx/enforce-module-boundaries": [
- "error",
- {
- "enforceBuildableLibDependency": true,
- "allow": [],
- "depConstraints": [
- {
- "sourceTag": "*",
- "onlyDependOnLibsWithTags": ["*"]
- }
- ]
- }
- ]
- }
- },
- {
- "files": ["*.ts", "*.tsx"],
- "extends": ["plugin:@nx/typescript"],
- "rules": {
- "@typescript-eslint/no-extra-semi": "error",
- "no-extra-semi": "off"
- }
- },
- {
- "files": ["*.js", "*.jsx"],
- "extends": ["plugin:@nx/javascript"],
- "rules": {
- "@typescript-eslint/no-extra-semi": "error",
- "no-extra-semi": "off"
- }
- },
- {
- "files": ["*.ts"],
- "plugins": ["eslint-plugin-import", "@angular-eslint/eslint-plugin", "@typescript-eslint"],
- "rules": {
- "@typescript-eslint/consistent-type-definitions": "error",
- "@typescript-eslint/dot-notation": "off",
- "@typescript-eslint/naming-convention": "error",
- "@typescript-eslint/no-shadow": [
- "error",
- {
- "hoist": "all"
- }
- ],
- "@typescript-eslint/no-unused-expressions": "error",
- "@typescript-eslint/prefer-function-type": "error",
- "@typescript-eslint/quotes": "off",
- "@typescript-eslint/type-annotation-spacing": "error",
- "@typescript-eslint/no-explicit-any": "off",
- "arrow-body-style": "off",
- "brace-style": ["error", "1tbs"],
- "curly": "error",
- "eol-last": "error",
- "eqeqeq": ["error", "smart"],
- "guard-for-in": "error",
- "id-blacklist": "off",
- "id-match": "off",
- "import/no-deprecated": "warn",
- "no-bitwise": "error",
- "no-caller": "error",
- "no-console": [
- "error",
- {
- "allow": [
- "log",
- "warn",
- "dir",
- "timeLog",
- "assert",
- "clear",
- "count",
- "countReset",
- "group",
- "groupEnd",
- "table",
- "dirxml",
- "error",
- "groupCollapsed",
- "Console",
- "profile",
- "profileEnd",
- "timeStamp",
- "context"
- ]
- }
- ],
- "no-empty": "off",
- "no-eval": "error",
- "no-new-wrappers": "error",
- "no-throw-literal": "error",
- "no-undef-init": "error",
- "no-underscore-dangle": "off",
- "radix": "error",
- "spaced-comment": [
- "error",
- "always",
- {
- "markers": ["/"]
- }
- ]
- }
- },
- {
- "files": ["*.html"],
- "rules": {}
- },
- {
- "files": ["*.ts", "*.js"],
- "extends": ["prettier"]
- },
- {
- "files": ["*.spec.ts"],
- "extends": ["plugin:testing-library/angular"],
- "rules": {
- "testing-library/prefer-explicit-assert": "error"
- }
- }
- ]
-}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9955c0d..215ef7d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -38,6 +38,8 @@ jobs:
run: npm run build -- --skip-nx-cache
- name: test
run: npm run test
+ - name: lint
+ run: npm run lint
- name: Release
if: github.repository == 'testing-library/angular-testing-library' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta')
run: npx semantic-release
diff --git a/README.md b/README.md
index 5ecd39e..e03ce68 100644
--- a/README.md
+++ b/README.md
@@ -98,7 +98,7 @@ counter.component.ts
```ts
@Component({
- selector: 'app-counter',
+ selector: 'atl-counter',
template: `
{{ hello() }}
diff --git a/apps/example-app-karma/.eslintrc.json b/apps/example-app-karma/.eslintrc.json
deleted file mode 100644
index 404aa66..0000000
--- a/apps/example-app-karma/.eslintrc.json
+++ /dev/null
@@ -1,44 +0,0 @@
-{
- "extends": "../../.eslintrc.json",
- "ignorePatterns": ["!**/*"],
- "overrides": [
- {
- "files": ["*.ts"],
- "extends": ["plugin:@nx/angular", "plugin:@angular-eslint/template/process-inline-templates"],
- "parserOptions": {
- "project": ["apps/example-app-karma/tsconfig.*?.json"]
- },
- "rules": {
- "@angular-eslint/directive-selector": [
- "error",
- {
- "type": "attribute",
- "prefix": "app",
- "style": "camelCase"
- }
- ],
- "@angular-eslint/component-selector": [
- "error",
- {
- "type": "element",
- "prefix": "app",
- "style": "kebab-case"
- }
- ]
- }
- },
- {
- "files": ["*.spec.ts"],
- "env": {
- "jasmine": true
- },
- "plugins": ["jasmine"],
- "extends": ["plugin:jasmine/recommended"]
- },
- {
- "files": ["*.html"],
- "extends": ["plugin:@nx/angular-template"],
- "rules": {}
- }
- ]
-}
diff --git a/apps/example-app-karma/eslint.config.cjs b/apps/example-app-karma/eslint.config.cjs
new file mode 100644
index 0000000..9e951e7
--- /dev/null
+++ b/apps/example-app-karma/eslint.config.cjs
@@ -0,0 +1,7 @@
+// @ts-check
+
+// TODO - https://github.com/nrwl/nx/issues/22576
+
+/** @type {import('@typescript-eslint/utils/ts-eslint').FlatConfig.ConfigPromise} */
+const config = (async () => (await import('./eslint.config.mjs')).default)();
+module.exports = config;
diff --git a/apps/example-app-karma/eslint.config.mjs b/apps/example-app-karma/eslint.config.mjs
new file mode 100644
index 0000000..8f627db
--- /dev/null
+++ b/apps/example-app-karma/eslint.config.mjs
@@ -0,0 +1,8 @@
+// @ts-check
+
+import tseslint from "typescript-eslint";
+import rootConfig from "../../eslint.config.mjs";
+
+export default tseslint.config(
+ ...rootConfig,
+);
diff --git a/apps/example-app-karma/jasmine-dom.d.ts b/apps/example-app-karma/jasmine-dom.d.ts
index f8fa4a7..54d7903 100644
--- a/apps/example-app-karma/jasmine-dom.d.ts
+++ b/apps/example-app-karma/jasmine-dom.d.ts
@@ -1,5 +1,4 @@
declare module '@testing-library/jasmine-dom' {
- // eslint-disable-next-line @typescript-eslint/naming-convention
const JasmineDOM: any;
export default JasmineDOM;
}
diff --git a/apps/example-app-karma/src/app/examples/login-form.spec.ts b/apps/example-app-karma/src/app/examples/login-form.spec.ts
index 9c51065..a028234 100644
--- a/apps/example-app-karma/src/app/examples/login-form.spec.ts
+++ b/apps/example-app-karma/src/app/examples/login-form.spec.ts
@@ -29,7 +29,7 @@ it('should display invalid message and submit button must be disabled', async ()
});
@Component({
- selector: 'app-login',
+ selector: 'atl-login',
standalone: true,
imports: [ReactiveFormsModule, NgIf],
template: `
@@ -51,7 +51,7 @@ class LoginComponent {
});
constructor(private fb: FormBuilder) {}
-
+
get email(): FormControl {
return this.form.get('email') as FormControl;
}
diff --git a/apps/example-app-karma/src/app/issues/rerender.spec.ts b/apps/example-app-karma/src/app/issues/rerender.spec.ts
index 9b044d1..324e8a1 100644
--- a/apps/example-app-karma/src/app/issues/rerender.spec.ts
+++ b/apps/example-app-karma/src/app/issues/rerender.spec.ts
@@ -7,9 +7,9 @@ it('can rerender component', async () => {
},
});
- expect(screen.getByText('Hello Sarah')).toBeTruthy();
+ expect(screen.getByText('Hello Sarah')).toBeInTheDocument();
await rerender({ componentProperties: { name: 'Mark' } });
- expect(screen.getByText('Hello Mark')).toBeTruthy();
+ expect(screen.getByText('Hello Mark')).toBeInTheDocument();
});
diff --git a/apps/example-app/.eslintrc.json b/apps/example-app/.eslintrc.json
deleted file mode 100644
index ed5e4d1..0000000
--- a/apps/example-app/.eslintrc.json
+++ /dev/null
@@ -1,47 +0,0 @@
-{
- "extends": "../../.eslintrc.json",
- "ignorePatterns": ["!**/*"],
- "overrides": [
- {
- "files": ["*.ts"],
- "extends": ["plugin:@nx/angular", "plugin:@angular-eslint/template/process-inline-templates"],
- "parserOptions": {
- "project": ["apps/example-app/tsconfig.*?.json"]
- },
- "rules": {
- "@angular-eslint/directive-selector": [
- "error",
- {
- "type": "attribute",
- "prefix": "app",
- "style": "camelCase"
- }
- ],
- "@angular-eslint/component-selector": [
- "error",
- {
- "type": "element",
- "prefix": "app",
- "style": "kebab-case"
- }
- ]
- }
- },
- {
- "files": ["*.spec.ts"],
- "env": {
- "jest": true
- },
- "extends": ["plugin:jest/recommended", "plugin:jest/style", "plugin:jest-dom/recommended"],
- "rules": {
- "jest/consistent-test-it": ["error"],
- "jest/expect-expect": "off"
- }
- },
- {
- "files": ["*.html"],
- "extends": ["plugin:@nx/angular-template"],
- "rules": {}
- }
- ]
-}
diff --git a/apps/example-app/eslint.config.cjs b/apps/example-app/eslint.config.cjs
new file mode 100644
index 0000000..9e951e7
--- /dev/null
+++ b/apps/example-app/eslint.config.cjs
@@ -0,0 +1,7 @@
+// @ts-check
+
+// TODO - https://github.com/nrwl/nx/issues/22576
+
+/** @type {import('@typescript-eslint/utils/ts-eslint').FlatConfig.ConfigPromise} */
+const config = (async () => (await import('./eslint.config.mjs')).default)();
+module.exports = config;
diff --git a/apps/example-app/eslint.config.mjs b/apps/example-app/eslint.config.mjs
new file mode 100644
index 0000000..0162584
--- /dev/null
+++ b/apps/example-app/eslint.config.mjs
@@ -0,0 +1,8 @@
+// @ts-check
+
+import tseslint from "typescript-eslint";
+import rootConfig from "../../eslint.config.mjs";
+
+export default tseslint.config(
+ ...rootConfig,
+);
\ No newline at end of file
diff --git a/apps/example-app/jest.config.ts b/apps/example-app/jest.config.ts
index 4b0c248..e0ea9c2 100644
--- a/apps/example-app/jest.config.ts
+++ b/apps/example-app/jest.config.ts
@@ -1,4 +1,3 @@
-/* eslint-disable */
export default {
displayName: {
name: 'Example App',
diff --git a/apps/example-app/src/app/examples/00-single-component.ts b/apps/example-app/src/app/examples/00-single-component.ts
index 7c132c2..4a09239 100644
--- a/apps/example-app/src/app/examples/00-single-component.ts
+++ b/apps/example-app/src/app/examples/00-single-component.ts
@@ -1,7 +1,7 @@
import { Component } from '@angular/core';
@Component({
- selector: 'app-fixture',
+ selector: 'atl-fixture',
standalone: true,
template: `
diff --git a/apps/example-app/src/app/examples/01-nested-component.ts b/apps/example-app/src/app/examples/01-nested-component.ts
index 645ce96..fd0d0c0 100644
--- a/apps/example-app/src/app/examples/01-nested-component.ts
+++ b/apps/example-app/src/app/examples/01-nested-component.ts
@@ -2,7 +2,7 @@ import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
standalone: true,
- selector: 'app-button',
+ selector: 'atl-button',
template: ' ',
})
export class NestedButtonComponent {
@@ -12,7 +12,7 @@ export class NestedButtonComponent {
@Component({
standalone: true,
- selector: 'app-value',
+ selector: 'atl-value',
template: ' {{ value }} ',
})
export class NestedValueComponent {
@@ -21,11 +21,11 @@ export class NestedValueComponent {
@Component({
standalone: true,
- selector: 'app-fixture',
+ selector: 'atl-fixture',
template: `
-
-
-
+
+
+
`,
imports: [NestedButtonComponent, NestedValueComponent],
})
diff --git a/apps/example-app/src/app/examples/02-input-output.spec.ts b/apps/example-app/src/app/examples/02-input-output.spec.ts
index 847f6e1..5a55bd5 100644
--- a/apps/example-app/src/app/examples/02-input-output.spec.ts
+++ b/apps/example-app/src/app/examples/02-input-output.spec.ts
@@ -36,7 +36,7 @@ test.skip('is possible to set input and listen for output with the template synt
const user = userEvent.setup();
const sendSpy = jest.fn();
- await render('', {
+ await render('', {
imports: [InputOutputComponent],
on: {
sendValue: sendSpy,
@@ -94,7 +94,7 @@ test('is possible to set input and listen for output with the template syntax (d
const user = userEvent.setup();
const sendSpy = jest.fn();
- await render('', {
+ await render('', {
imports: [InputOutputComponent],
componentProperties: {
sendValue: sendSpy,
diff --git a/apps/example-app/src/app/examples/02-input-output.ts b/apps/example-app/src/app/examples/02-input-output.ts
index 5bf70ab..3d7f979 100644
--- a/apps/example-app/src/app/examples/02-input-output.ts
+++ b/apps/example-app/src/app/examples/02-input-output.ts
@@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
standalone: true,
- selector: 'app-fixture',
+ selector: 'atl-fixture',
template: `
{{ value }}
diff --git a/apps/example-app/src/app/examples/03-forms.ts b/apps/example-app/src/app/examples/03-forms.ts
index 49756dc..a62d865 100644
--- a/apps/example-app/src/app/examples/03-forms.ts
+++ b/apps/example-app/src/app/examples/03-forms.ts
@@ -4,7 +4,7 @@ import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
@Component({
standalone: true,
- selector: 'app-fixture',
+ selector: 'atl-fixture',
imports: [ReactiveFormsModule, NgForOf, NgIf],
template: `