diff --git a/.all-contributorsrc b/.all-contributorsrc
index 8f50d57..7780dd0 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -341,6 +341,15 @@
"bug",
"ideas"
]
+ },
+ {
+ "login": "TrustNoOneElse",
+ "name": "Florian Pabst",
+ "avatar_url": "https://avatars.githubusercontent.com/u/25935352?v=4",
+ "profile": "https://github.com/TrustNoOneElse",
+ "contributions": [
+ "code"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/README.md b/README.md
index e0b7702..46f5c6a 100644
--- a/README.md
+++ b/README.md
@@ -185,49 +185,52 @@ Thanks goes to these people ([emoji key][emojis]):
diff --git a/apps/example-app/src/app/examples/03-forms.ts b/apps/example-app/src/app/examples/03-forms.ts
index df861a3..cbf0a31 100644
--- a/apps/example-app/src/app/examples/03-forms.ts
+++ b/apps/example-app/src/app/examples/03-forms.ts
@@ -1,5 +1,5 @@
import { Component } from '@angular/core';
-import { UntypedFormBuilder, Validators } from '@angular/forms';
+import { FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'app-fixture',
@@ -35,13 +35,14 @@ export class FormsComponent {
{ id: 'B', value: 'Blue' },
{ id: 'G', value: 'Green' },
];
+
form = this.formBuilder.group({
- name: ['', Validators.required],
+ name: ['', [Validators.required]],
score: [0, { validators: [Validators.min(1), Validators.max(10)], updateOn: 'blur' }],
- color: ['', Validators.required],
+ color: [null as string | null, Validators.required],
});
- constructor(private formBuilder: UntypedFormBuilder) {}
+ constructor(private formBuilder: FormBuilder) {}
get formErrors() {
return Object.keys(this.form.controls)
diff --git a/apps/example-app/src/app/examples/04-forms-with-material.spec.ts b/apps/example-app/src/app/examples/04-forms-with-material.spec.ts
index 64ace4a..e1e16e3 100644
--- a/apps/example-app/src/app/examples/04-forms-with-material.spec.ts
+++ b/apps/example-app/src/app/examples/04-forms-with-material.spec.ts
@@ -13,12 +13,14 @@ test('is possible to fill in a form and verify error messages (with the help of
const scoreControl = screen.getByRole('spinbutton', { name: /score/i });
const colorControl = screen.getByPlaceholderText(/color/i);
const dateControl = screen.getByRole('textbox', { name: /Choose a date/i });
+ const checkboxControl = screen.getByRole('checkbox', { name: /agree/i });
const errors = screen.getByRole('alert');
expect(errors).toContainElement(screen.queryByText('name is required'));
expect(errors).toContainElement(screen.queryByText('score must be greater than 1'));
expect(errors).toContainElement(screen.queryByText('color is required'));
+ expect(errors).toContainElement(screen.queryByText('agree is required'));
userEvent.type(nameControl, 'Tim');
userEvent.clear(scoreControl);
@@ -26,9 +28,15 @@ test('is possible to fill in a form and verify error messages (with the help of
userEvent.click(colorControl);
userEvent.click(screen.getByText(/green/i));
+ expect(checkboxControl).not.toBeChecked();
+ userEvent.click(checkboxControl);
+ expect(checkboxControl).toBeChecked();
+ expect(checkboxControl).toBeValid();
+
expect(screen.queryByText('name is required')).not.toBeInTheDocument();
expect(screen.getByText('score must be lesser than 10')).toBeInTheDocument();
expect(screen.queryByText('color is required')).not.toBeInTheDocument();
+ expect(screen.queryByText('agree is required')).not.toBeInTheDocument();
expect(scoreControl).toBeInvalid();
userEvent.clear(scoreControl);
@@ -42,6 +50,7 @@ test('is possible to fill in a form and verify error messages (with the help of
expect(nameControl).toHaveValue('Tim');
expect(scoreControl).toHaveValue(7);
expect(colorControl).toHaveTextContent('Green');
+ expect(checkboxControl).toBeChecked();
const form = screen.getByRole('form');
expect(form).toHaveFormValues({
@@ -50,6 +59,7 @@ test('is possible to fill in a form and verify error messages (with the help of
});
// material doesn't add these to the form
+ expect((fixture.componentInstance as MaterialFormsComponent).form?.get('agree')?.value).toBe(true);
expect((fixture.componentInstance as MaterialFormsComponent).form?.get('color')?.value).toBe('G');
expect((fixture.componentInstance as MaterialFormsComponent).form?.get('date')?.value).toEqual(new Date(2022, 7, 11));
});
@@ -64,22 +74,29 @@ test('set and show pre-set form values', async () => {
score: 4,
color: 'B',
date: new Date(2022, 7, 11),
+ agree: true,
});
detectChanges();
const nameControl = screen.getByLabelText(/name/i);
const scoreControl = screen.getByRole('spinbutton', { name: /score/i });
const colorControl = screen.getByPlaceholderText(/color/i);
+ const checkboxControl = screen.getByRole('checkbox', { name: /agree/i });
expect(nameControl).toHaveValue('Max');
expect(scoreControl).toHaveValue(4);
expect(colorControl).toHaveTextContent('Blue');
+ expect(checkboxControl).toBeChecked();
+ userEvent.click(checkboxControl);
const form = screen.getByRole('form');
expect(form).toHaveFormValues({
name: 'Max',
score: 4,
});
+
+ // material doesn't add these to the form
+ expect((fixture.componentInstance as MaterialFormsComponent).form?.get('agree')?.value).toBe(false);
expect((fixture.componentInstance as MaterialFormsComponent).form?.get('color')?.value).toBe('B');
expect((fixture.componentInstance as MaterialFormsComponent).form?.get('date')?.value).toEqual(new Date(2022, 7, 11));
});
diff --git a/apps/example-app/src/app/examples/04-forms-with-material.ts b/apps/example-app/src/app/examples/04-forms-with-material.ts
index ed510d1..852a345 100644
--- a/apps/example-app/src/app/examples/04-forms-with-material.ts
+++ b/apps/example-app/src/app/examples/04-forms-with-material.ts
@@ -1,5 +1,5 @@
import { Component } from '@angular/core';
-import { UntypedFormBuilder, Validators } from '@angular/forms';
+import { FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'app-fixture',
@@ -10,6 +10,8 @@ import { UntypedFormBuilder, Validators } from '@angular/forms';
+ I Agree
+
Score
-
+ Current Count: {{ counter }}
+ `,
+})
+class AppComponent {
+ @Input() counter = 0;
+
+ increment() {
+ this.counter += 1;
+ }
+
+ decrement() {
+ this.counter -= 1;
+ }
+}
+
+describe('Counter', () => {
+ it('should render counter', async () => {
+ await render(AppComponent, {
+ componentProperties: { counter: 5 },
+ });
+
+ expect(screen.getByText('Current Count: 5')).toBeInTheDocument();
+ });
+
+ it('should increment the counter on click', async () => {
+ await render(AppComponent, {
+ componentProperties: { counter: 5 },
+ });
+
+ fireEvent.click(screen.getByText('+'));
+
+ expect(screen.getByText('Current Count: 6')).toBeInTheDocument();
+ });
+});
diff --git a/apps/example-app/src/app/material.module.ts b/apps/example-app/src/app/material.module.ts
index 297c9d7..51a9189 100644
--- a/apps/example-app/src/app/material.module.ts
+++ b/apps/example-app/src/app/material.module.ts
@@ -1,11 +1,12 @@
import { NgModule } from '@angular/core';
+import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatNativeDateModule } from '@angular/material/core';
@NgModule({
- exports: [MatInputModule, MatSelectModule, MatDatepickerModule, MatNativeDateModule],
+ exports: [MatInputModule, MatSelectModule, MatDatepickerModule, MatNativeDateModule, MatCheckboxModule],
})
export class MaterialModule {}
diff --git a/projects/testing-library/src/lib/models.ts b/projects/testing-library/src/lib/models.ts
index 51fe5f7..0e4b404 100644
--- a/projects/testing-library/src/lib/models.ts
+++ b/projects/testing-library/src/lib/models.ts
@@ -328,6 +328,21 @@ export interface RenderComponentOptions(
routes = [],
removeAngularAttributes = false,
defaultImports = [],
+ initialRoute = '',
} = { ...globalConfig, ...renderOptions };
dtlConfigure({
@@ -107,6 +108,8 @@ export async function render(
const zone = safeInject(NgZone);
const router = safeInject(Router);
+ if (initialRoute) await router.navigate([initialRoute]);
+
if (typeof router?.initialNavigation === 'function') {
if (zone) {
zone.run(() => router.initialNavigation());
diff --git a/projects/testing-library/tests/render.spec.ts b/projects/testing-library/tests/render.spec.ts
index 12a1f62..28e5996 100644
--- a/projects/testing-library/tests/render.spec.ts
+++ b/projects/testing-library/tests/render.spec.ts
@@ -12,6 +12,7 @@ import {
import { NoopAnimationsModule, BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TestBed } from '@angular/core/testing';
import { render, fireEvent, screen } from '../src/public_api';
+import { Resolve, RouterModule } from '@angular/router';
@Component({
selector: 'atl-fixture',
@@ -296,3 +297,48 @@ describe('DebugElement', () => {
expect(view.debugElement.componentInstance).toBeInstanceOf(FixtureComponent);
});
});
+
+describe('initialRoute', () => {
+ @Component({
+ standalone: true,
+ selector: 'atl-fixture2',
+ template: ``,
+ })
+ class SecondaryFixtureComponent {}
+
+ @Component({
+ standalone: true,
+ selector: 'atl-router-fixture',
+ template: ``,
+ imports: [RouterModule],
+ })
+ class RouterFixtureComponent {}
+
+ @Injectable()
+ class FixtureResolver implements Resolve {
+ public isResolved = false;
+
+ public resolve() {
+ this.isResolved = true;
+ }
+ }
+
+ it('allows initially rendering a specific route to avoid triggering a resolver for the default route', async () => {
+ const initialRoute = 'initial-route';
+ const routes = [
+ { path: initialRoute, component: FixtureComponent },
+ { path: '**', resolve: { data: FixtureResolver }, component: SecondaryFixtureComponent },
+ ];
+
+ await render(RouterFixtureComponent, {
+ initialRoute,
+ routes,
+ providers: [FixtureResolver],
+ });
+ const resolver = TestBed.inject(FixtureResolver);
+
+ expect(resolver.isResolved).toBe(false);
+ expect(screen.queryByText('Secondary Component')).not.toBeInTheDocument();
+ expect(screen.getByText('button')).toBeInTheDocument();
+ });
+});