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/.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/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 💻 |
+
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;