Skip to content

Commit 4fea701

Browse files
feat: release v18 (#533)
* feat: update to Angular 20 (#530) BREAKING CHANGE: The angular minimum version has changed. BEFORE: Angular 17,18,19 were supported. AFTER: Angular 20 (and up) is supported. Reason: The method `TestBed.get` has been removed. * feat: remove animations dependency (#531) BREAKING CHANGE: Angular recommends using CSS animations, https://angular.dev/guide/animations/migration Because of the removal of the animations dependency, the `NoopAnimationsModule` is no longer automatically imported in the render function. BEFORE: The `NoopAnimationsModule` was always imported to the render the component. AFTER: Import the `NoopAnimationsModule` in your render configuration (where needed): ```ts await render(SutComponent, { imports: [NoopAnimationsModule], }); ``` * feat: add stronger types (#413)
1 parent 420f0d3 commit 4fea701

File tree

12 files changed

+70
-88
lines changed

12 files changed

+70
-88
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222

2323
strategy:
2424
matrix:
25-
node-version: ${{ fromJSON((github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta') && '[22]' || '[18, 20, 22]') }}
25+
node-version: ${{ fromJSON((github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta') && '[22]' || '[20, 22, 24]') }}
2626
os: ${{ fromJSON((github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta') && '["ubuntu-latest"]' || '["ubuntu-latest", "windows-latest"]') }}
2727
runs-on: ${{ matrix.os }}
2828

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,4 @@ yarn.lock
4646
Thumbs.db
4747
.cursor/rules/nx-rules.mdc
4848
.github/instructions/nx.instructions.md
49+
.history

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -177,15 +177,16 @@ You may also be interested in installing `jest-dom` so you can use
177177
178178
## Version compatibility
179179

180-
| Angular | Angular Testing Library |
181-
| ------- | ---------------------------- |
182-
| 19.x | 17.x, 16.x, 15.x, 14.x, 13.x |
183-
| 18.x | 17.x, 16.x, 15.x, 14.x, 13.x |
184-
| 17.x | 17.x, 16.x, 15.x, 14.x, 13.x |
185-
| 16.x | 14.x, 13.x |
186-
| >= 15.1 | 14.x, 13.x |
187-
| < 15.1 | 12.x, 11.x |
188-
| 14.x | 12.x, 11.x |
180+
| Angular | Angular Testing Library |
181+
| ------- | ---------------------------------- |
182+
| 20.x | 18.x, 17.x, 16.x, 15.x, 14.x, 13.x |
183+
| 19.x | 17.x, 16.x, 15.x, 14.x, 13.x |
184+
| 18.x | 17.x, 16.x, 15.x, 14.x, 13.x |
185+
| 17.x | 17.x, 16.x, 15.x, 14.x, 13.x |
186+
| 16.x | 14.x, 13.x |
187+
| >= 15.1 | 14.x, 13.x |
188+
| < 15.1 | 12.x, 11.x |
189+
| 14.x | 12.x, 11.x |
189190

190191
## Guiding Principles
191192

apps/example-app-karma/src/app/issues/issue-491.spec.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Component } from '@angular/core';
22
import { Router } from '@angular/router';
3-
import { render, screen, waitForElementToBeRemoved } from '@testing-library/angular';
3+
import { render, screen } from '@testing-library/angular';
44
import userEvent from '@testing-library/user-event';
55

66
it('test click event with router.navigate', async () => {
@@ -31,8 +31,6 @@ it('test click event with router.navigate', async () => {
3131

3232
await user.click(screen.getByRole('button', { name: 'submit' }));
3333

34-
await waitForElementToBeRemoved(() => screen.queryByRole('heading', { name: 'Login' }));
35-
3634
expect(await screen.findByRole('heading', { name: 'Logged In' })).toBeVisible();
3735
});
3836

apps/example-app/src/app/examples/15-dialog.component.spec.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { MatDialogRef } from '@angular/material/dialog';
2+
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
23
import { render, screen } from '@testing-library/angular';
34
import userEvent from '@testing-library/user-event';
45

@@ -9,6 +10,7 @@ test('dialog closes', async () => {
910

1011
const closeFn = jest.fn();
1112
await render(DialogContentComponent, {
13+
imports: [NoopAnimationsModule],
1214
providers: [
1315
{
1416
provide: MatDialogRef,
@@ -28,7 +30,9 @@ test('dialog closes', async () => {
2830
test('closes the dialog via the backdrop', async () => {
2931
const user = userEvent.setup();
3032

31-
await render(DialogComponent);
33+
await render(DialogComponent, {
34+
imports: [NoopAnimationsModule],
35+
});
3236

3337
const openDialogButton = await screen.findByRole('button', { name: /open dialog/i });
3438
await user.click(openDialogButton);
@@ -50,7 +54,9 @@ test('closes the dialog via the backdrop', async () => {
5054
test('opens and closes the dialog with buttons', async () => {
5155
const user = userEvent.setup();
5256

53-
await render(DialogComponent);
57+
await render(DialogComponent, {
58+
imports: [NoopAnimationsModule],
59+
});
5460

5561
const openDialogButton = await screen.findByRole('button', { name: /open dialog/i });
5662
await user.click(openDialogButton);

package.json

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@
2727
"prepare": "git config core.hookspath .githooks"
2828
},
2929
"dependencies": {
30-
"@angular/animations": "19.2.14",
31-
"@angular/cdk": "19.2.18",
32-
"@angular/common": "19.2.14",
33-
"@angular/compiler": "19.2.14",
34-
"@angular/core": "19.2.14",
35-
"@angular/material": "19.2.18",
36-
"@angular/platform-browser": "19.2.14",
37-
"@angular/platform-browser-dynamic": "19.2.14",
38-
"@angular/router": "19.2.14",
30+
"@angular/animations": "20.0.0",
31+
"@angular/cdk": "20.0.0",
32+
"@angular/common": "20.0.0",
33+
"@angular/compiler": "20.0.0",
34+
"@angular/core": "20.0.0",
35+
"@angular/material": "20.0.0",
36+
"@angular/platform-browser": "20.0.0",
37+
"@angular/platform-browser-dynamic": "20.0.0",
38+
"@angular/router": "20.0.0",
3939
"@ngrx/store": "19.0.0",
4040
"@nx/angular": "21.1.2",
4141
"@testing-library/dom": "^10.4.0",
@@ -44,26 +44,26 @@
4444
"zone.js": "^0.15.0"
4545
},
4646
"devDependencies": {
47-
"@angular-devkit/build-angular": "19.2.9",
48-
"@angular-devkit/core": "19.2.9",
49-
"@angular-devkit/schematics": "19.2.9",
47+
"@angular-devkit/build-angular": "20.0.0",
48+
"@angular-devkit/core": "20.0.0",
49+
"@angular-devkit/schematics": "20.0.0",
5050
"@angular-eslint/builder": "19.2.0",
5151
"@angular-eslint/eslint-plugin": "19.2.0",
5252
"@angular-eslint/eslint-plugin-template": "19.2.0",
5353
"@angular-eslint/schematics": "19.2.0",
5454
"@angular-eslint/template-parser": "19.2.0",
55-
"@angular/cli": "~19.2.0",
56-
"@angular/compiler-cli": "19.2.14",
57-
"@angular/forms": "19.2.14",
58-
"@angular/language-service": "19.2.14",
55+
"@angular/cli": "~20.0.0",
56+
"@angular/compiler-cli": "20.0.0",
57+
"@angular/forms": "20.0.0",
58+
"@angular/language-service": "20.0.0",
5959
"@eslint/eslintrc": "^2.1.1",
6060
"@nx/eslint": "21.1.2",
6161
"@nx/eslint-plugin": "21.1.2",
6262
"@nx/jest": "21.1.2",
6363
"@nx/node": "21.1.2",
6464
"@nx/plugin": "21.1.2",
6565
"@nx/workspace": "21.1.2",
66-
"@schematics/angular": "19.2.9",
66+
"@schematics/angular": "20.0.0",
6767
"@testing-library/jasmine-dom": "^1.3.3",
6868
"@testing-library/jest-dom": "^6.6.3",
6969
"@testing-library/user-event": "^14.5.2",
@@ -91,7 +91,7 @@
9191
"karma-jasmine-html-reporter": "2.0.0",
9292
"lint-staged": "^15.3.0",
9393
"ng-mocks": "^14.13.1",
94-
"ng-packagr": "19.2.2",
94+
"ng-packagr": "20.0.0",
9595
"nx": "21.1.2",
9696
"postcss": "^8.4.49",
9797
"postcss-import": "14.1.0",
@@ -102,7 +102,7 @@
102102
"semantic-release": "^24.2.1",
103103
"ts-jest": "29.1.0",
104104
"ts-node": "10.9.1",
105-
"typescript": "5.7.3",
105+
"typescript": "5.8.2",
106106
"typescript-eslint": "^8.19.0"
107107
}
108108
}

projects/testing-library/package.json

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,10 @@
2929
"migrations": "./schematics/migrations/migrations.json"
3030
},
3131
"peerDependencies": {
32-
"@angular/animations": ">= 17.0.0",
33-
"@angular/common": ">= 17.0.0",
34-
"@angular/platform-browser": ">= 17.0.0",
35-
"@angular/router": ">= 17.0.0",
36-
"@angular/core": ">= 17.0.0",
32+
"@angular/common": ">= 20.0.0",
33+
"@angular/platform-browser": ">= 20.0.0",
34+
"@angular/router": ">= 20.0.0",
35+
"@angular/core": ">= 20.0.0",
3736
"@testing-library/dom": "^10.0.0"
3837
},
3938
"dependencies": {

projects/testing-library/src/lib/config.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,9 @@ let config: Config = {
77

88
export function configure(newConfig: Partial<Config> | ((config: Partial<Config>) => Partial<Config>)) {
99
if (typeof newConfig === 'function') {
10-
// Pass the existing config out to the provided function
11-
// and accept a delta in return
1210
newConfig = newConfig(config);
1311
}
1412

15-
// Merge the incoming config delta
1613
config = {
1714
...config,
1815
...newConfig,

projects/testing-library/src/lib/models.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
import { Type, DebugElement, EventEmitter, Signal, InputSignalWithTransform } from '@angular/core';
1+
import {
2+
Type,
3+
DebugElement,
4+
ModuleWithProviders,
5+
EventEmitter,
6+
EnvironmentProviders,
7+
Provider,
8+
Signal,
9+
InputSignalWithTransform,
10+
} from '@angular/core';
211
import { ComponentFixture, DeferBlockBehavior, DeferBlockState, TestBed } from '@angular/core/testing';
312
import { Routes } from '@angular/router';
413
import { BoundFunction, Queries, queries, Config as dtlConfig, PrettyDOMOptions } from '@testing-library/dom';
@@ -153,7 +162,7 @@ export interface RenderComponentOptions<ComponentType, Q extends Queries = typeo
153162
* declarations: [ CustomerDetailComponent, ButtonComponent ]
154163
* })
155164
*/
156-
declarations?: any[];
165+
declarations?: (Type<unknown> | unknown[])[];
157166
/**
158167
* @description
159168
* 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<ComponentType, Q extends Queries = typeo
174183
* ]
175184
* })
176185
*/
177-
providers?: any[];
186+
providers?: (Provider | EnvironmentProviders)[];
178187
/**
179188
* @description
180189
* A collection of imports needed to render the component, for example, shared modules.
181-
* Adds `NoopAnimationsModule` by default if `BrowserAnimationsModule` isn't added to the collection.
182190
*
183191
* For more info see https://angular.io/api/core/NgModule#imports
184192
*
185193
* @default
186-
* `[NoopAnimationsModule]`
194+
* `[]`
187195
*
188196
* @example
189197
* await render(AppComponent, {
@@ -193,7 +201,7 @@ export interface RenderComponentOptions<ComponentType, Q extends Queries = typeo
193201
* ]
194202
* })
195203
*/
196-
imports?: any[];
204+
imports?: (Type<unknown> | ModuleWithProviders<unknown>)[];
197205
/**
198206
* @description
199207
* A collection of schemas needed to render the component.
@@ -315,7 +323,7 @@ export interface RenderComponentOptions<ComponentType, Q extends Queries = typeo
315323
* ]
316324
* })
317325
*/
318-
componentProviders?: any[];
326+
componentProviders?: Provider[];
319327
/**
320328
* @description
321329
* Collection of child component specified providers to override with
@@ -349,7 +357,7 @@ export interface RenderComponentOptions<ComponentType, Q extends Queries = typeo
349357
* ]
350358
* })
351359
*/
352-
componentImports?: (Type<any> | any[])[];
360+
componentImports?: (Type<unknown> | unknown[])[];
353361
/**
354362
* @description
355363
* Queries to bind. Overrides the default set from DOM Testing Library unless merged.
@@ -463,7 +471,7 @@ export interface RenderComponentOptions<ComponentType, Q extends Queries = typeo
463471

464472
export interface ComponentOverride<T> {
465473
component: Type<T>;
466-
providers: any[];
474+
providers: Provider[];
467475
}
468476

469477
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
@@ -497,5 +505,5 @@ export interface Config extends Pick<RenderComponentOptions<any>, 'excludeCompon
497505
/**
498506
* Imports that are added to the imports
499507
*/
500-
defaultImports: any[];
508+
defaultImports?: (Type<unknown> | ModuleWithProviders<unknown>)[];
501509
}

projects/testing-library/src/lib/testing-library.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import {
66
OnChanges,
77
OutputRef,
88
OutputRefSubscription,
9+
Provider,
910
SimpleChange,
1011
SimpleChanges,
1112
Type,
1213
isStandalone,
1314
} from '@angular/core';
1415
import { ComponentFixture, DeferBlockBehavior, DeferBlockState, TestBed, tick } from '@angular/core/testing';
15-
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
1616
import { NavigationExtras, Router } from '@angular/router';
1717
import { RouterTestingModule } from '@angular/router/testing';
1818
import type { BoundFunctions, Queries } from '@testing-library/dom';
@@ -40,7 +40,6 @@ import {
4040
type SubscribedOutput<T> = readonly [key: keyof T, callback: (v: any) => void, subscription: OutputRefSubscription];
4141

4242
const mountedFixtures = new Set<ComponentFixture<any>>();
43-
const safeInject = TestBed.inject || TestBed.get;
4443

4544
export async function render<ComponentType>(
4645
component: Type<ComponentType>,
@@ -108,7 +107,7 @@ export async function render<SutType, WrapperType = SutType>(
108107
imports: imports.concat(defaultImports),
109108
routes,
110109
}),
111-
providers: [...providers],
110+
providers,
112111
schemas: [...schemas],
113112
deferBlockBehavior: deferBlockBehavior ?? DeferBlockBehavior.Manual,
114113
});
@@ -126,8 +125,8 @@ export async function render<SutType, WrapperType = SutType>(
126125

127126
const componentContainer = createComponentFixture(sut, wrapper);
128127

129-
const zone = safeInject(NgZone);
130-
const router = safeInject(Router);
128+
const zone = TestBed.inject(NgZone);
129+
const router = TestBed.inject(Router);
131130
const _navigate = async (elementOrPath: Element | string, basePath = ''): Promise<boolean> => {
132131
const href = typeof elementOrPath === 'string' ? elementOrPath : elementOrPath.getAttribute('href');
133132
const [path, params] = (basePath + href).split('?');
@@ -338,7 +337,7 @@ export async function render<SutType, WrapperType = SutType>(
338337

339338
async function createComponent<SutType>(component: Type<SutType>): Promise<ComponentFixture<SutType>> {
340339
/* Make sure angular application is initialized before creating component */
341-
await safeInject(ApplicationInitStatus).donePromise;
340+
await TestBed.inject(ApplicationInitStatus).donePromise;
342341
return TestBed.createComponent(component);
343342
}
344343

@@ -435,7 +434,7 @@ function overrideComponentImports<SutType>(sut: Type<SutType> | string, imports:
435434
function overrideChildComponentProviders(componentOverrides: ComponentOverride<any>[]) {
436435
if (componentOverrides) {
437436
for (const { component, providers } of componentOverrides) {
438-
TestBed.overrideComponent(component, { set: { providers } });
437+
TestBed.overrideComponent(component, { set: { providers: providers as Provider[] } });
439438
}
440439
}
441440
}
@@ -498,7 +497,7 @@ function addAutoDeclarations<SutType>(
498497
wrapper,
499498
}: Pick<RenderTemplateOptions<any>, 'declarations' | 'excludeComponentDeclaration' | 'wrapper'>,
500499
) {
501-
const nonStandaloneDeclarations = declarations?.filter((d) => !isStandalone(d));
500+
const nonStandaloneDeclarations = declarations.filter((d) => !isStandalone(d as Type<any>));
502501
if (typeof sut === 'string') {
503502
if (wrapper && isStandalone(wrapper)) {
504503
return nonStandaloneDeclarations;
@@ -514,15 +513,9 @@ function addAutoImports<SutType>(
514513
sut: Type<SutType> | string,
515514
{ imports = [], routes }: Pick<RenderComponentOptions<any>, 'imports' | 'routes'>,
516515
) {
517-
const animations = () => {
518-
const animationIsDefined =
519-
imports.indexOf(NoopAnimationsModule) > -1 || imports.indexOf(BrowserAnimationsModule) > -1;
520-
return animationIsDefined ? [] : [NoopAnimationsModule];
521-
};
522-
523516
const routing = () => (routes ? [RouterTestingModule.withRoutes(routes)] : []);
524517
const components = () => (typeof sut !== 'string' && isStandalone(sut) ? [sut] : []);
525-
return [...imports, ...components(), ...animations(), ...routing()];
518+
return [...imports, ...components(), ...routing()];
526519
}
527520

528521
async function renderDeferBlock<SutType>(

0 commit comments

Comments
 (0)