diff --git a/.all-contributorsrc b/.all-contributorsrc
index 07a9a93..cc7f5ba 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -437,6 +437,26 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "Christian24",
+ "name": "Christian24",
+ "avatar_url": "https://avatars.githubusercontent.com/u/2406635?v=4",
+ "profile": "https://github.com/Christian24",
+ "contributions": [
+ "code",
+ "review"
+ ]
+ },
+ {
+ "login": "mikeshtro",
+ "name": "Michal Štrajt",
+ "avatar_url": "https://avatars.githubusercontent.com/u/93714867?v=4",
+ "profile": "https://github.com/mikeshtro",
+ "contributions": [
+ "code",
+ "bug"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 215ef7d..b35c5ab 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') && '[22]' || '[18, 20, 22]') }}
+ node-version: ${{ fromJSON((github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta') && '[22]' || '[20, 22, 24]') }}
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/.gitignore b/.gitignore
index 1204690..22faaca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,3 +46,4 @@ yarn.lock
Thumbs.db
.cursor/rules/nx-rules.mdc
.github/instructions/nx.instructions.md
+.history
diff --git a/README.md b/README.md
index 028f721..d9d16a3 100644
--- a/README.md
+++ b/README.md
@@ -177,15 +177,16 @@ You may also be interested in installing `jest-dom` so you can use
## Version compatibility
-| 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 |
-| >= 15.1 | 14.x, 13.x |
-| < 15.1 | 12.x, 11.x |
-| 14.x | 12.x, 11.x |
+| Angular | Angular Testing Library |
+| ------- | ---------------------------------- |
+| 20.x | 18.x, 17.x, 16.x, 15.x, 14.x, 13.x |
+| 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 |
+| >= 15.1 | 14.x, 13.x |
+| < 15.1 | 12.x, 11.x |
+| 14.x | 12.x, 11.x |
## Guiding Principles
@@ -275,6 +276,8 @@ Thanks goes to these people ([emoji key][emojis]):
 Arthur Petrie 💻 |
 Fabien Dehopré 💻 |
 Jamie Vereecken 💻 |
+  Christian24 💻 👀 |
+  Michal Štrajt 💻 🐛 |
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
index 7da4d6d..9320251 100644
--- a/apps/example-app-karma/src/app/issues/issue-491.spec.ts
+++ b/apps/example-app-karma/src/app/issues/issue-491.spec.ts
@@ -1,6 +1,6 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
-import { render, screen, waitForElementToBeRemoved } from '@testing-library/angular';
+import { render, screen } from '@testing-library/angular';
import userEvent from '@testing-library/user-event';
it('test click event with router.navigate', async () => {
@@ -31,8 +31,6 @@ it('test click event with router.navigate', async () => {
await user.click(screen.getByRole('button', { name: 'submit' }));
- await waitForElementToBeRemoved(() => screen.queryByRole('heading', { name: 'Login' }));
-
expect(await screen.findByRole('heading', { name: 'Logged In' })).toBeVisible();
});
diff --git a/apps/example-app/src/app/examples/15-dialog.component.spec.ts b/apps/example-app/src/app/examples/15-dialog.component.spec.ts
index 017afdc..df172be 100644
--- a/apps/example-app/src/app/examples/15-dialog.component.spec.ts
+++ b/apps/example-app/src/app/examples/15-dialog.component.spec.ts
@@ -1,4 +1,5 @@
import { MatDialogRef } from '@angular/material/dialog';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { render, screen } from '@testing-library/angular';
import userEvent from '@testing-library/user-event';
@@ -9,6 +10,7 @@ test('dialog closes', async () => {
const closeFn = jest.fn();
await render(DialogContentComponent, {
+ imports: [NoopAnimationsModule],
providers: [
{
provide: MatDialogRef,
@@ -28,7 +30,9 @@ test('dialog closes', async () => {
test('closes the dialog via the backdrop', async () => {
const user = userEvent.setup();
- await render(DialogComponent);
+ await render(DialogComponent, {
+ imports: [NoopAnimationsModule],
+ });
const openDialogButton = await screen.findByRole('button', { name: /open dialog/i });
await user.click(openDialogButton);
@@ -50,7 +54,9 @@ test('closes the dialog via the backdrop', async () => {
test('opens and closes the dialog with buttons', async () => {
const user = userEvent.setup();
- await render(DialogComponent);
+ await render(DialogComponent, {
+ imports: [NoopAnimationsModule],
+ });
const openDialogButton = await screen.findByRole('button', { name: /open dialog/i });
await user.click(openDialogButton);
diff --git a/package.json b/package.json
index b3f540b..3888395 100644
--- a/package.json
+++ b/package.json
@@ -27,15 +27,15 @@
"prepare": "git config core.hookspath .githooks"
},
"dependencies": {
- "@angular/animations": "19.2.14",
- "@angular/cdk": "19.2.18",
- "@angular/common": "19.2.14",
- "@angular/compiler": "19.2.14",
- "@angular/core": "19.2.14",
- "@angular/material": "19.2.18",
- "@angular/platform-browser": "19.2.14",
- "@angular/platform-browser-dynamic": "19.2.14",
- "@angular/router": "19.2.14",
+ "@angular/animations": "20.0.0",
+ "@angular/cdk": "20.0.0",
+ "@angular/common": "20.0.0",
+ "@angular/compiler": "20.0.0",
+ "@angular/core": "20.0.0",
+ "@angular/material": "20.0.0",
+ "@angular/platform-browser": "20.0.0",
+ "@angular/platform-browser-dynamic": "20.0.0",
+ "@angular/router": "20.0.0",
"@ngrx/store": "19.0.0",
"@nx/angular": "21.1.2",
"@testing-library/dom": "^10.4.0",
@@ -44,18 +44,18 @@
"zone.js": "^0.15.0"
},
"devDependencies": {
- "@angular-devkit/build-angular": "19.2.9",
- "@angular-devkit/core": "19.2.9",
- "@angular-devkit/schematics": "19.2.9",
+ "@angular-devkit/build-angular": "20.0.0",
+ "@angular-devkit/core": "20.0.0",
+ "@angular-devkit/schematics": "20.0.0",
"@angular-eslint/builder": "19.2.0",
"@angular-eslint/eslint-plugin": "19.2.0",
"@angular-eslint/eslint-plugin-template": "19.2.0",
"@angular-eslint/schematics": "19.2.0",
"@angular-eslint/template-parser": "19.2.0",
- "@angular/cli": "~19.2.0",
- "@angular/compiler-cli": "19.2.14",
- "@angular/forms": "19.2.14",
- "@angular/language-service": "19.2.14",
+ "@angular/cli": "~20.0.0",
+ "@angular/compiler-cli": "20.0.0",
+ "@angular/forms": "20.0.0",
+ "@angular/language-service": "20.0.0",
"@eslint/eslintrc": "^2.1.1",
"@nx/eslint": "21.1.2",
"@nx/eslint-plugin": "21.1.2",
@@ -63,7 +63,7 @@
"@nx/node": "21.1.2",
"@nx/plugin": "21.1.2",
"@nx/workspace": "21.1.2",
- "@schematics/angular": "19.2.9",
+ "@schematics/angular": "20.0.0",
"@testing-library/jasmine-dom": "^1.3.3",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/user-event": "^14.5.2",
@@ -91,7 +91,7 @@
"karma-jasmine-html-reporter": "2.0.0",
"lint-staged": "^15.3.0",
"ng-mocks": "^14.13.1",
- "ng-packagr": "19.2.2",
+ "ng-packagr": "20.0.0",
"nx": "21.1.2",
"postcss": "^8.4.49",
"postcss-import": "14.1.0",
@@ -102,7 +102,7 @@
"semantic-release": "^24.2.1",
"ts-jest": "29.1.0",
"ts-node": "10.9.1",
- "typescript": "5.7.3",
+ "typescript": "5.8.2",
"typescript-eslint": "^8.19.0"
}
}
diff --git a/projects/testing-library/package.json b/projects/testing-library/package.json
index 0c3abd6..6ea1a38 100644
--- a/projects/testing-library/package.json
+++ b/projects/testing-library/package.json
@@ -29,11 +29,10 @@
"migrations": "./schematics/migrations/migrations.json"
},
"peerDependencies": {
- "@angular/animations": ">= 17.0.0",
- "@angular/common": ">= 17.0.0",
- "@angular/platform-browser": ">= 17.0.0",
- "@angular/router": ">= 17.0.0",
- "@angular/core": ">= 17.0.0",
+ "@angular/common": ">= 20.0.0",
+ "@angular/platform-browser": ">= 20.0.0",
+ "@angular/router": ">= 20.0.0",
+ "@angular/core": ">= 20.0.0",
"@testing-library/dom": "^10.0.0"
},
"dependencies": {
diff --git a/projects/testing-library/src/lib/config.ts b/projects/testing-library/src/lib/config.ts
index bd8ee9b..075c91c 100644
--- a/projects/testing-library/src/lib/config.ts
+++ b/projects/testing-library/src/lib/config.ts
@@ -7,12 +7,9 @@ let config: Config = {
export function configure(newConfig: Partial | ((config: Partial) => Partial)) {
if (typeof newConfig === 'function') {
- // Pass the existing config out to the provided function
- // and accept a delta in return
newConfig = newConfig(config);
}
- // Merge the incoming config delta
config = {
...config,
...newConfig,
diff --git a/projects/testing-library/src/lib/models.ts b/projects/testing-library/src/lib/models.ts
index 47ea5bb..318bd2b 100644
--- a/projects/testing-library/src/lib/models.ts
+++ b/projects/testing-library/src/lib/models.ts
@@ -1,4 +1,13 @@
-import { Type, DebugElement, EventEmitter, Signal, InputSignalWithTransform } from '@angular/core';
+import {
+ Type,
+ DebugElement,
+ ModuleWithProviders,
+ EventEmitter,
+ EnvironmentProviders,
+ Provider,
+ 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';
@@ -153,7 +162,7 @@ export interface RenderComponentOptions | unknown[])[];
/**
* @description
* A collection of providers needed to render the component via Dependency Injection, for example, injectable services or tokens.
@@ -174,16 +183,15 @@ export interface RenderComponentOptions | ModuleWithProviders)[];
/**
* @description
* A collection of schemas needed to render the component.
@@ -315,7 +323,7 @@ export interface RenderComponentOptions | any[])[];
+ componentImports?: (Type | unknown[])[];
/**
* @description
* Queries to bind. Overrides the default set from DOM Testing Library unless merged.
@@ -463,7 +471,7 @@ export interface RenderComponentOptions {
component: Type;
- providers: any[];
+ providers: Provider[];
}
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
@@ -497,5 +505,5 @@ export interface Config extends Pick, 'excludeCompon
/**
* Imports that are added to the imports
*/
- defaultImports: any[];
+ defaultImports?: (Type | ModuleWithProviders)[];
}
diff --git a/projects/testing-library/src/lib/testing-library.ts b/projects/testing-library/src/lib/testing-library.ts
index f498a89..4667727 100644
--- a/projects/testing-library/src/lib/testing-library.ts
+++ b/projects/testing-library/src/lib/testing-library.ts
@@ -6,13 +6,13 @@ import {
OnChanges,
OutputRef,
OutputRefSubscription,
+ Provider,
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';
import { NavigationExtras, Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import type { BoundFunctions, Queries } from '@testing-library/dom';
@@ -40,7 +40,6 @@ import {
type SubscribedOutput = readonly [key: keyof T, callback: (v: any) => void, subscription: OutputRefSubscription];
const mountedFixtures = new Set>();
-const safeInject = TestBed.inject || TestBed.get;
export async function render(
component: Type,
@@ -108,7 +107,7 @@ export async function render(
imports: imports.concat(defaultImports),
routes,
}),
- providers: [...providers],
+ providers,
schemas: [...schemas],
deferBlockBehavior: deferBlockBehavior ?? DeferBlockBehavior.Manual,
});
@@ -126,8 +125,8 @@ export async function render(
const componentContainer = createComponentFixture(sut, wrapper);
- const zone = safeInject(NgZone);
- const router = safeInject(Router);
+ const zone = TestBed.inject(NgZone);
+ const router = TestBed.inject(Router);
const _navigate = async (elementOrPath: Element | string, basePath = ''): Promise => {
const href = typeof elementOrPath === 'string' ? elementOrPath : elementOrPath.getAttribute('href');
const [path, params] = (basePath + href).split('?');
@@ -338,7 +337,7 @@ export async function render(
async function createComponent(component: Type): Promise> {
/* Make sure angular application is initialized before creating component */
- await safeInject(ApplicationInitStatus).donePromise;
+ await TestBed.inject(ApplicationInitStatus).donePromise;
return TestBed.createComponent(component);
}
@@ -435,7 +434,7 @@ function overrideComponentImports(sut: Type | string, imports:
function overrideChildComponentProviders(componentOverrides: ComponentOverride[]) {
if (componentOverrides) {
for (const { component, providers } of componentOverrides) {
- TestBed.overrideComponent(component, { set: { providers } });
+ TestBed.overrideComponent(component, { set: { providers: providers as Provider[] } });
}
}
}
@@ -498,7 +497,7 @@ function addAutoDeclarations(
wrapper,
}: Pick, 'declarations' | 'excludeComponentDeclaration' | 'wrapper'>,
) {
- const nonStandaloneDeclarations = declarations?.filter((d) => !isStandalone(d));
+ const nonStandaloneDeclarations = declarations.filter((d) => !isStandalone(d as Type));
if (typeof sut === 'string') {
if (wrapper && isStandalone(wrapper)) {
return nonStandaloneDeclarations;
@@ -514,15 +513,9 @@ function addAutoImports(
sut: Type | string,
{ imports = [], routes }: Pick, 'imports' | 'routes'>,
) {
- const animations = () => {
- const animationIsDefined =
- imports.indexOf(NoopAnimationsModule) > -1 || imports.indexOf(BrowserAnimationsModule) > -1;
- return animationIsDefined ? [] : [NoopAnimationsModule];
- };
-
const routing = () => (routes ? [RouterTestingModule.withRoutes(routes)] : []);
const components = () => (typeof sut !== 'string' && isStandalone(sut) ? [sut] : []);
- return [...imports, ...components(), ...animations(), ...routing()];
+ return [...imports, ...components(), ...routing()];
}
async function renderDeferBlock(
diff --git a/projects/testing-library/tests/defer-blocks.spec.ts b/projects/testing-library/tests/defer-blocks.spec.ts
index 7405a4d..ffd5e95 100644
--- a/projects/testing-library/tests/defer-blocks.spec.ts
+++ b/projects/testing-library/tests/defer-blocks.spec.ts
@@ -33,7 +33,6 @@ test('renders a defer block in different states using DeferBlockBehavior.Playthr
deferBlockBehavior: DeferBlockBehavior.Playthrough,
});
- expect(await screen.findByText(/loading/i)).toBeInTheDocument();
expect(await screen.findByText(/Defer block content/i)).toBeInTheDocument();
});
diff --git a/projects/testing-library/tests/render.spec.ts b/projects/testing-library/tests/render.spec.ts
index dc54ac5..a93da90 100644
--- a/projects/testing-library/tests/render.spec.ts
+++ b/projects/testing-library/tests/render.spec.ts
@@ -17,7 +17,6 @@ import {
model,
} from '@angular/core';
import { outputFromObservable } from '@angular/core/rxjs-interop';
-import { NoopAnimationsModule, BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TestBed } from '@angular/core/testing';
import { render, fireEvent, screen, OutputRefKeysWithCallback, aliasedInput } from '../src/public_api';
import { ActivatedRoute, Resolve, RouterModule } from '@angular/router';
@@ -331,25 +330,6 @@ describe('excludeComponentDeclaration', () => {
});
});
-describe('animationModule', () => {
- it('adds NoopAnimationsModule by default', async () => {
- await render(FixtureComponent);
- const noopAnimationsModule = TestBed.inject(NoopAnimationsModule);
- expect(noopAnimationsModule).toBeDefined();
- });
-
- it('does not add NoopAnimationsModule if BrowserAnimationsModule is an import', async () => {
- await render(FixtureComponent, {
- imports: [BrowserAnimationsModule],
- });
-
- const browserAnimationsModule = TestBed.inject(BrowserAnimationsModule);
- expect(browserAnimationsModule).toBeDefined();
-
- expect(() => TestBed.inject(NoopAnimationsModule)).toThrow();
- });
-});
-
describe('Angular component life-cycle hooks', () => {
@Component({
selector: 'atl-fixture',