From 77cae6942271d229c0e41a1eb86e98c368560698 Mon Sep 17 00:00:00 2001
From: "allcontributors[bot]"
<46447321+allcontributors[bot]@users.noreply.github.com>
Date: Fri, 13 Jan 2023 20:31:30 +0100
Subject: [PATCH 1/5] docs: add TrustNoOneElse as a contributor for code (#355)
* 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 | 73 +++++++++++++++++++++++----------------------
2 files changed, 47 insertions(+), 35 deletions(-)
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]):
From 8bdc12ede8f68c975e403618e9129b0c9f23f798 Mon Sep 17 00:00:00 2001
From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com>
Date: Sat, 21 Jan 2023 17:22:44 +0100
Subject: [PATCH 2/5] test: reproduce #358 (#359)
---
.../src/app/issues/issue-358.spec.ts | 40 +++++++++++++++++++
1 file changed, 40 insertions(+)
create mode 100644 apps/example-app/src/app/issues/issue-358.spec.ts
diff --git a/apps/example-app/src/app/issues/issue-358.spec.ts b/apps/example-app/src/app/issues/issue-358.spec.ts
new file mode 100644
index 0000000..6d936c0
--- /dev/null
+++ b/apps/example-app/src/app/issues/issue-358.spec.ts
@@ -0,0 +1,40 @@
+import { Component, Input } from '@angular/core';
+import { render, screen, fireEvent } from '@testing-library/angular';
+
+@Component({
+ selector: 'app-root',
+ template: `
+ 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();
+ });
+});
From 52a25864de96c24d908abd1462adc1169c76fb6b Mon Sep 17 00:00:00 2001
From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com>
Date: Sat, 28 Jan 2023 16:03:49 +0100
Subject: [PATCH 3/5] add checkbox example (#363)
---
.../app/examples/04-forms-with-material.spec.ts | 17 +++++++++++++++++
.../src/app/examples/04-forms-with-material.ts | 3 +++
apps/example-app/src/app/material.module.ts | 3 ++-
3 files changed, 22 insertions(+), 1 deletion(-)
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..48b1410 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
@@ -10,6 +10,8 @@ import { UntypedFormBuilder, Validators } from '@angular/forms';
+ I Agree
+
Score
Date: Sat, 28 Jan 2023 16:12:54 +0100
Subject: [PATCH 4/5] docs: refactor form examples to typed forms
---
apps/example-app/src/app/examples/03-forms.ts | 9 +++++----
.../src/app/examples/04-forms-with-material.ts | 12 ++++++------
2 files changed, 11 insertions(+), 10 deletions(-)
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.ts b/apps/example-app/src/app/examples/04-forms-with-material.ts
index 48b1410..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',
@@ -74,14 +74,14 @@ export class MaterialFormsComponent {
{ id: 'G', value: 'Green' },
];
form = this.formBuilder.group({
- name: ['', Validators.required],
+ name: ['', [Validators.required]],
score: [0, [Validators.min(1), Validators.max(10)]],
- color: [null, Validators.required],
- date: [null, Validators.required],
- agree: [null, Validators.requiredTrue],
+ color: [null as string | null, Validators.required],
+ date: [null as Date | null, Validators.required],
+ agree: [false, Validators.requiredTrue],
});
- constructor(private formBuilder: UntypedFormBuilder) {}
+ constructor(private formBuilder: FormBuilder) {}
get colorControlDisplayValue(): string | undefined {
const selectedId = this.form.get('color')?.value;
From d3407ca2f2541ec2f954a5ad975bfb32f35be04b Mon Sep 17 00:00:00 2001
From: Josh Joseph
Date: Sat, 18 Feb 2023 12:31:23 -0500
Subject: [PATCH 5/5] feat: add initialRoute parameter to avoid resolver issues
with using a default route (#367)
---
projects/testing-library/src/lib/models.ts | 15 ++++++
.../src/lib/testing-library.ts | 3 ++
projects/testing-library/tests/render.spec.ts | 46 +++++++++++++++++++
3 files changed, 64 insertions(+)
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();
+ });
+});