Skip to content

Commit 95f7de0

Browse files
Merge pull request #101 from testing-library/pr/update-examples
2 parents 89319df + 8acaebb commit 95f7de0

19 files changed

+108
-156
lines changed

README.md

+8-6
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ counter.component.ts
9797
selector: 'counter',
9898
template: `
9999
<button (click)="decrement()">-</button>
100-
<span data-testid="count">Current Count: {{ counter }}</span>
100+
<span>Current Count: {{ counter }}</span>
101101
<button (click)="increment()">+</button>
102102
`,
103103
})
@@ -117,8 +117,8 @@ export class CounterComponent {
117117
counter.component.spec.ts
118118

119119
```typescript
120-
import { render, screen } from '@testing-library/angular';
121-
import CounterComponent from './counter.component.ts';
120+
import { render, screen, fireEvent } from '@testing-library/angular';
121+
import { CounterComponent } from './counter.component.ts';
122122

123123
describe('Counter', () => {
124124
test('should render counter', async () => {
@@ -128,11 +128,12 @@ describe('Counter', () => {
128128
});
129129

130130
test('should increment the counter on click', async () => {
131-
const { click } = await render(CounterComponent, { componentProperties: { counter: 5 } });
131+
await render(CounterComponent, { componentProperties: { counter: 5 } });
132132

133-
click(screen.getByText('+'));
133+
const incrementButton = screen.getByRole('button', { name: /increment/i });
134+
fireEvent.click(incrementControl);
134135

135-
expect(getByText('Current Count: 6'));
136+
expect(screen.getByText('Current Count: 6'));
136137
});
137138
});
138139
```
@@ -194,6 +195,7 @@ Thanks goes to these people ([emoji key][emojis]):
194195

195196
<!-- markdownlint-enable -->
196197
<!-- prettier-ignore-end -->
198+
197199
<!-- ALL-CONTRIBUTORS-LIST:END -->
198200

199201
This project follows the [all-contributors][all-contributors] specification.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"@angular/router": "^9.0.3",
3636
"@ngrx/store": "^9.0.0-rc.0",
3737
"@phenomnomnominal/tsquery": "^3.0.0",
38-
"@testing-library/dom": "^7.1.1",
38+
"@testing-library/dom": "^7.7.3",
3939
"@testing-library/user-event": "^8.1.0",
4040
"core-js": "^3.1.3",
4141
"rxjs": "^6.5.4",

projects/testing-library/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"@angular/core": "^9.0.0"
3030
},
3131
"dependencies": {
32-
"@testing-library/dom": "^7.1.0",
32+
"@testing-library/dom": "^7.7.3",
3333
"@testing-library/user-event": "^8.1.0",
3434
"@phenomnomnominal/tsquery": "^3.0.0",
3535
"tslint": "^5.16.0"

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

+10-48
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
fireEvent as dtlFireEvent,
1515
screen as dtlScreen,
1616
queries as dtlQueries,
17+
waitForOptions,
1718
} from '@testing-library/dom';
1819
import { RenderComponentOptions, RenderDirectiveOptions, RenderResult } from './models';
1920
import { createSelectOptions, createType, tab } from './user-events';
@@ -147,26 +148,13 @@ export async function render<SutType, WrapperType = SutType>(
147148
return result;
148149
};
149150

150-
function componentWaitFor<T>(
151-
callback,
152-
options: {
153-
container?: HTMLElement;
154-
timeout?: number;
155-
interval?: number;
156-
mutationObserverOptions?: MutationObserverInit;
157-
} = { container: fixture.nativeElement },
158-
): Promise<T> {
151+
function componentWaitFor<T>(callback, options: waitForOptions = { container: fixture.nativeElement }): Promise<T> {
159152
return waitForWrapper(detectChanges, callback, options);
160153
}
161154

162155
function componentWaitForElementToBeRemoved<T>(
163156
callback: (() => T) | T,
164-
options: {
165-
container?: HTMLElement;
166-
timeout?: number;
167-
interval?: number;
168-
mutationObserverOptions?: MutationObserverInit;
169-
} = { container: fixture.nativeElement },
157+
options: waitForOptions = { container: fixture.nativeElement },
170158
): Promise<T> {
171159
return waitForElementToBeRemovedWrapper(detectChanges, callback, options);
172160
}
@@ -255,13 +243,8 @@ function addAutoImports({ imports, routes }: Pick<RenderComponentOptions<any>, '
255243
*/
256244
async function waitForWrapper<T>(
257245
detectChanges: () => void,
258-
callback: () => T,
259-
options?: {
260-
container?: HTMLElement;
261-
timeout?: number;
262-
interval?: number;
263-
mutationObserverOptions?: MutationObserverInit;
264-
},
246+
callback: () => T extends Promise<any> ? never : T,
247+
options?: waitForOptions,
265248
): Promise<T> {
266249
return await dtlWaitFor(() => {
267250
detectChanges();
@@ -275,12 +258,7 @@ async function waitForWrapper<T>(
275258
async function waitForElementToBeRemovedWrapper<T>(
276259
detectChanges: () => void,
277260
callback: (() => T) | T,
278-
options?: {
279-
container?: HTMLElement;
280-
timeout?: number;
281-
interval?: number;
282-
mutationObserverOptions?: MutationObserverInit;
283-
},
261+
options?: waitForOptions,
284262
): Promise<T> {
285263
let cb;
286264
if (typeof callback !== 'function') {
@@ -328,12 +306,12 @@ function replaceFindWithFindAndDetectChanges<T>(container: HTMLElement, original
328306
(newQueries, key) => {
329307
if (key.startsWith('find')) {
330308
const getByQuery = dtlQueries[key.replace('find', 'get')];
331-
newQueries[key] = async (text, options, waitForOptions) => {
309+
newQueries[key] = async (text, options, waitOptions) => {
332310
// original implementation at https://github.com/testing-library/dom-testing-library/blob/master/src/query-helpers.js
333311
const result = await waitForWrapper(
334312
detectChangesForMountedFixtures,
335313
() => getByQuery(container, text, options),
336-
waitForOptions,
314+
waitOptions,
337315
);
338316
return result;
339317
};
@@ -377,30 +355,14 @@ const screen = replaceFindWithFindAndDetectChanges(document.body, dtlScreen);
377355
/**
378356
* Re-export waitFor with patched waitFor
379357
*/
380-
async function waitFor<T>(
381-
callback: () => T,
382-
options?: {
383-
container?: HTMLElement;
384-
timeout?: number;
385-
interval?: number;
386-
mutationObserverOptions?: MutationObserverInit;
387-
},
388-
): Promise<T> {
358+
async function waitFor<T>(callback: () => T extends Promise<any> ? never : T, options?: waitForOptions): Promise<T> {
389359
return waitForWrapper(detectChangesForMountedFixtures, callback, options);
390360
}
391361

392362
/**
393363
* Re-export waitForElementToBeRemoved with patched waitForElementToBeRemoved
394364
*/
395-
async function waitForElementToBeRemoved<T>(
396-
callback: (() => T) | T,
397-
options?: {
398-
container?: HTMLElement;
399-
timeout?: number;
400-
interval?: number;
401-
mutationObserverOptions?: MutationObserverInit;
402-
},
403-
): Promise<T> {
365+
async function waitForElementToBeRemoved<T>(callback: (() => T) | T, options?: waitForOptions): Promise<T> {
404366
return waitForElementToBeRemovedWrapper(detectChangesForMountedFixtures, callback, options);
405367
}
406368

projects/testing-library/src/lib/user-events/selectOptions.ts

+14-16
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
import {
2-
FireFunction,
3-
FireObject,
4-
Matcher,
5-
getByText,
6-
SelectorMatcherOptions,
7-
queryByText,
8-
} from '@testing-library/dom';
1+
import { FireFunction, FireObject, Matcher, screen, ByRoleOptions } from '@testing-library/dom';
92

103
// implementation from https://github.com/testing-library/user-event
114
export function createSelectOptions(fireEvent: FireFunction & FireObject) {
@@ -18,10 +11,16 @@ export function createSelectOptions(fireEvent: FireFunction & FireObject) {
1811
fireEvent.click(element);
1912
}
2013

21-
function selectOption(select: HTMLSelectElement, index: number, matcher: Matcher, options?: SelectorMatcherOptions) {
22-
// fallback to document.body, because libraries as Angular Material will have their custom select component
23-
const option = (queryByText(select, matcher, options) ||
24-
getByText(document.body, matcher, options)) as HTMLOptionElement;
14+
function selectOption(select: HTMLSelectElement, index: number, options: Matcher | ByRoleOptions) {
15+
const query =
16+
typeof options === 'string'
17+
? (({ name: new RegExp(options, 'i') } as unknown) as ByRoleOptions)
18+
: options instanceof RegExp
19+
? (({ name: options } as unknown) as ByRoleOptions)
20+
: typeof options === 'function'
21+
? (({ name: options } as unknown) as ByRoleOptions)
22+
: options;
23+
const option = screen.getByRole('option', query) as HTMLOptionElement;
2524

2625
fireEvent.mouseOver(option);
2726
fireEvent.mouseMove(option);
@@ -36,8 +35,7 @@ export function createSelectOptions(fireEvent: FireFunction & FireObject) {
3635

3736
return async function selectOptions(
3837
element: HTMLElement,
39-
matcher: Matcher | Matcher[],
40-
matcherOptions?: SelectorMatcherOptions,
38+
options: Matcher | ByRoleOptions | ((Matcher | ByRoleOptions)[]),
4139
) {
4240
const selectElement = element as HTMLSelectElement;
4341

@@ -55,10 +53,10 @@ export function createSelectOptions(fireEvent: FireFunction & FireObject) {
5553

5654
clickElement(selectElement);
5755

58-
const values = Array.isArray(matcher) ? matcher : [matcher];
56+
const values = Array.isArray(options) ? options : [options];
5957
values
6058
.filter((_, index) => index === 0 || selectElement.multiple)
61-
.forEach((val, index) => selectOption(selectElement, index, val, matcherOptions));
59+
.forEach((val, index) => selectOption(selectElement, index, val));
6260

6361
if (wasAnotherElementFocused) {
6462
fireEvent.blur(focusedElement);

src/app/examples/00-single-component.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { SingleComponent } from './00-single-component';
55
test('renders the current value and can increment and decrement', async () => {
66
await render(SingleComponent);
77

8-
const incrementControl = screen.getByText('Increment');
9-
const decrementControl = screen.getByText('Decrement');
8+
const incrementControl = screen.getByRole('button', { name: /increment/i });
9+
const decrementControl = screen.getByRole('button', { name: /decrement/i });
1010
const valueControl = screen.getByTestId('value');
1111

1212
expect(valueControl.textContent).toBe('0');

src/app/examples/01-nested-component.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ test('renders the current value and can increment and decrement', async () => {
77
declarations: [NestedButtonComponent, NestedValueComponent],
88
});
99

10-
const incrementControl = screen.getByText('Increment');
11-
const decrementControl = screen.getByText('Decrement');
10+
const incrementControl = screen.getByRole('button', { name: /increment/i });
11+
const decrementControl = screen.getByRole('button', { name: /decrement/i });
1212
const valueControl = screen.getByTestId('value');
1313

1414
expect(valueControl.textContent).toBe('0');

src/app/examples/02-input-output.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ test('is possible to set input and listen for output', async () => {
1414
},
1515
});
1616

17-
const incrementControl = screen.getByText('Increment');
17+
const incrementControl = screen.getByRole('button', { name: /increment/i });
18+
const sendControl = screen.getByRole('button', { name: /send/i });
1819
const valueControl = screen.getByTestId('value');
19-
const sendControl = screen.getByText('Send');
2020

2121
expect(valueControl.textContent).toBe('47');
2222

src/app/examples/03-forms.spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ test('is possible to fill in a form and verify error messages (with the help of
88
imports: [ReactiveFormsModule],
99
});
1010

11-
const nameControl = screen.getByLabelText('Name');
12-
const scoreControl = screen.getByLabelText(/score/i);
13-
const colorControl = screen.getByLabelText('color', { exact: false });
11+
const nameControl = screen.getByRole('textbox', { name: /name/i });
12+
const scoreControl = screen.getByRole('spinbutton', { name: /score/i });
13+
const colorControl = screen.getByRole('combobox', { name: /color/i });
1414
const errors = screen.getByRole('alert');
1515

1616
expect(errors).toContainElement(screen.queryByText('name is required'));
@@ -38,7 +38,7 @@ test('is possible to fill in a form and verify error messages (with the help of
3838
expect(scoreControl).toHaveValue(7);
3939
expect(colorControl).toHaveValue('G');
4040

41-
const form = screen.getByTestId('my-form');
41+
const form = screen.getByRole('form');
4242
expect(form).toHaveFormValues({
4343
name: 'Tim',
4444
score: 7,

src/app/examples/03-forms.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { FormBuilder, Validators, ReactiveFormsModule, ValidationErrors } from '
44
@Component({
55
selector: 'app-fixture',
66
template: `
7-
<form [formGroup]="form" data-testid="my-form">
7+
<form [formGroup]="form" name="form">
88
<div>
99
<label for="name">Name</label>
1010
<input type="text" id="name" name="name" formControlName="name" required />

src/app/examples/04-forms-with-material.spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ test('is possible to fill in a form and verify error messages (with the help of
99
imports: [ReactiveFormsModule, MaterialModule],
1010
});
1111

12-
const nameControl = screen.getByPlaceholderText('Name');
13-
const scoreControl = screen.getByPlaceholderText(/score/i);
14-
const colorControl = screen.getByPlaceholderText('color', { exact: false });
12+
const nameControl = screen.getByLabelText(/name/i);
13+
const scoreControl = screen.getByRole('spinbutton', { name: /score/i });
14+
const colorControl = screen.getByRole('listbox', { name: /color/i });
1515
const errors = screen.getByRole('alert');
1616

1717
expect(errors).toContainElement(screen.queryByText('name is required'));
@@ -35,7 +35,7 @@ test('is possible to fill in a form and verify error messages (with the help of
3535
expect(nameControl).toHaveValue('Tim');
3636
expect(scoreControl).toHaveValue(7);
3737

38-
const form = screen.getByTestId('my-form');
38+
const form = screen.getByRole('form');
3939
expect(form).toHaveFormValues({
4040
name: 'Tim',
4141
score: 7,

src/app/examples/04-forms-with-material.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { FormBuilder, Validators, ReactiveFormsModule, ValidationErrors } from '
44
@Component({
55
selector: 'app-fixture',
66
template: `
7-
<form [formGroup]="form" data-testid="my-form">
7+
<form [formGroup]="form" name="form">
88
<mat-form-field>
99
<input matInput placeholder="Name" name="name" formControlName="name" required />
1010
</mat-form-field>

src/app/examples/05-component-provider.spec.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ test('renders the current value and can increment and decrement', async () => {
1414
],
1515
});
1616

17-
const incrementControl = screen.getByText('Increment');
18-
const decrementControl = screen.getByText('Decrement');
17+
const incrementControl = screen.getByRole('button', { name: /increment/i });
18+
const decrementControl = screen.getByRole('button', { name: /decrement/i });
1919
const valueControl = screen.getByTestId('value');
2020

2121
expect(valueControl.textContent).toBe('0');
@@ -44,8 +44,8 @@ test('renders the current value and can increment and decrement with a mocked je
4444
],
4545
});
4646

47-
const incrementControl = screen.getByText('Increment');
48-
const decrementControl = screen.getByText('Decrement');
47+
const incrementControl = screen.getByRole('button', { name: /increment/i });
48+
const decrementControl = screen.getByRole('button', { name: /decrement/i });
4949
const valueControl = screen.getByTestId('value');
5050

5151
expect(valueControl.textContent).toBe('50');
@@ -63,8 +63,8 @@ test('renders the current value and can increment and decrement with provideMock
6363
componentProviders: [provideMock(CounterService)],
6464
});
6565

66-
const incrementControl = screen.getByText('Increment');
67-
const decrementControl = screen.getByText('Decrement');
66+
const incrementControl = screen.getByRole('button', { name: /increment/i });
67+
const decrementControl = screen.getByRole('button', { name: /decrement/i });
6868

6969
fireEvent.click(incrementControl);
7070
fireEvent.click(incrementControl);

src/app/examples/06-with-ngrx-store.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ test('works with ngrx store', async () => {
1717
],
1818
});
1919

20-
const incrementControl = screen.getByText('Increment');
21-
const decrementControl = screen.getByText('Decrement');
20+
const incrementControl = screen.getByRole('button', { name: /increment/i });
21+
const decrementControl = screen.getByRole('button', { name: /decrement/i });
2222
const valueControl = screen.getByTestId('value');
2323

2424
expect(valueControl.textContent).toBe('0');

src/app/examples/07-with-ngrx-mock-store.spec.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ test('works with provideMockStore', async () => {
2121
const store = TestBed.inject(MockStore);
2222
store.dispatch = jest.fn();
2323

24-
screen.getByText('Four');
25-
fireEvent.click(screen.getByText('Seven'));
24+
fireEvent.click(screen.getByRole('listitem', { name: /seven/i }));
2625

2726
expect(store.dispatch).toBeCalledWith({ type: '[Item List] send', item: 'Seven' });
2827
});

src/app/examples/07-with-ngrx-mock-store.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const selectItems = createSelector(
1010
selector: 'app-fixture',
1111
template: `
1212
<ul>
13-
<li *ngFor="let item of items | async" (click)="send(item)">{{ item }}</li>
13+
<li *ngFor="let item of items | async" (click)="send(item)" [attr.aria-label]="item">{{ item }}</li>
1414
</ul>
1515
`,
1616
})

0 commit comments

Comments
 (0)