diff --git a/.githooks/pre-commit b/.githooks/pre-commit old mode 100644 new mode 100755 diff --git a/apps/example-app/src/app/examples/00-single-component.spec.ts b/apps/example-app/src/app/examples/00-single-component.spec.ts index 73e429bb..44ad2500 100644 --- a/apps/example-app/src/app/examples/00-single-component.spec.ts +++ b/apps/example-app/src/app/examples/00-single-component.spec.ts @@ -1,8 +1,10 @@ -import { render, screen, fireEvent } from '@testing-library/angular'; +import { render, screen } from '@testing-library/angular'; +import userEvent from '@testing-library/user-event'; import { SingleComponent } from './00-single-component'; test('renders the current value and can increment and decrement', async () => { + const user = userEvent.setup(); await render(SingleComponent); const incrementControl = screen.getByRole('button', { name: /increment/i }); @@ -11,10 +13,10 @@ test('renders the current value and can increment and decrement', async () => { expect(valueControl).toHaveTextContent('0'); - fireEvent.click(incrementControl); - fireEvent.click(incrementControl); + await user.click(incrementControl); + await user.click(incrementControl); expect(valueControl).toHaveTextContent('2'); - fireEvent.click(decrementControl); + await user.click(decrementControl); expect(valueControl).toHaveTextContent('1'); }); diff --git a/apps/example-app/src/app/examples/01-nested-component.spec.ts b/apps/example-app/src/app/examples/01-nested-component.spec.ts index 8f3a242d..751d1c47 100644 --- a/apps/example-app/src/app/examples/01-nested-component.spec.ts +++ b/apps/example-app/src/app/examples/01-nested-component.spec.ts @@ -1,8 +1,10 @@ -import { render, screen, fireEvent } from '@testing-library/angular'; +import { render, screen } from '@testing-library/angular'; +import userEvent from '@testing-library/user-event'; import { NestedButtonComponent, NestedValueComponent, NestedContainerComponent } from './01-nested-component'; test('renders the current value and can increment and decrement', async () => { + const user = userEvent.setup(); await render(NestedContainerComponent, { declarations: [NestedButtonComponent, NestedValueComponent], }); @@ -13,10 +15,10 @@ test('renders the current value and can increment and decrement', async () => { expect(valueControl).toHaveTextContent('0'); - fireEvent.click(incrementControl); - fireEvent.click(incrementControl); + await user.click(incrementControl); + await user.click(incrementControl); expect(valueControl).toHaveTextContent('2'); - fireEvent.click(decrementControl); + await user.click(decrementControl); expect(valueControl).toHaveTextContent('1'); }); diff --git a/apps/example-app/src/app/examples/02-input-output.spec.ts b/apps/example-app/src/app/examples/02-input-output.spec.ts index abb71dcb..93edee4d 100644 --- a/apps/example-app/src/app/examples/02-input-output.spec.ts +++ b/apps/example-app/src/app/examples/02-input-output.spec.ts @@ -1,8 +1,10 @@ -import { render, screen, fireEvent } from '@testing-library/angular'; +import { render, screen } from '@testing-library/angular'; +import userEvent from '@testing-library/user-event'; import { InputOutputComponent } from './02-input-output'; test('is possible to set input and listen for output', async () => { + const user = userEvent.setup(); const sendValue = jest.fn(); await render(InputOutputComponent, { @@ -22,17 +24,18 @@ test('is possible to set input and listen for output', async () => { expect(valueControl).toHaveTextContent('47'); - fireEvent.click(incrementControl); - fireEvent.click(incrementControl); - fireEvent.click(incrementControl); + await user.click(incrementControl); + await user.click(incrementControl); + await user.click(incrementControl); expect(valueControl).toHaveTextContent('50'); - fireEvent.click(sendControl); + await user.click(sendControl); expect(sendValue).toHaveBeenCalledTimes(1); expect(sendValue).toHaveBeenCalledWith(50); }); test('is possible to set input and listen for output with the template syntax', async () => { + const user = userEvent.setup(); const sendSpy = jest.fn(); await render('', { @@ -48,12 +51,12 @@ test('is possible to set input and listen for output with the template syntax', expect(valueControl).toHaveTextContent('47'); - fireEvent.click(incrementControl); - fireEvent.click(incrementControl); - fireEvent.click(incrementControl); + await user.click(incrementControl); + await user.click(incrementControl); + await user.click(incrementControl); expect(valueControl).toHaveTextContent('50'); - fireEvent.click(sendControl); + await user.click(sendControl); expect(sendSpy).toHaveBeenCalledTimes(1); expect(sendSpy).toHaveBeenCalledWith(50); }); diff --git a/apps/example-app/src/app/examples/03-forms.spec.ts b/apps/example-app/src/app/examples/03-forms.spec.ts index 2a53a5f0..0e475834 100644 --- a/apps/example-app/src/app/examples/03-forms.spec.ts +++ b/apps/example-app/src/app/examples/03-forms.spec.ts @@ -4,6 +4,7 @@ import userEvent from '@testing-library/user-event'; import { FormsComponent } from './03-forms'; test('is possible to fill in a form and verify error messages (with the help of jest-dom https://testing-library.com/docs/ecosystem-jest-dom)', async () => { + const user = userEvent.setup(); await render(FormsComponent); const nameControl = screen.getByRole('textbox', { name: /name/i }); @@ -16,19 +17,19 @@ test('is possible to fill in a form and verify error messages (with the help of expect(errors).toContainElement(screen.queryByText('color is required')); expect(nameControl).toBeInvalid(); - userEvent.type(nameControl, 'Tim'); - userEvent.clear(scoreControl); - userEvent.type(scoreControl, '12'); + await user.type(nameControl, 'Tim'); + await user.clear(scoreControl); + await user.type(scoreControl, '12'); fireEvent.blur(scoreControl); - userEvent.selectOptions(colorControl, 'G'); + await user.selectOptions(colorControl, 'G'); 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(scoreControl).toBeInvalid(); - userEvent.clear(scoreControl); - userEvent.type(scoreControl, '7'); + await user.clear(scoreControl); + await user.type(scoreControl, '7'); fireEvent.blur(scoreControl); expect(scoreControl).toBeValid(); 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 e1e16e37..24b9cdc9 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 @@ -5,6 +5,8 @@ import { MaterialModule } from '../material.module'; import { MaterialFormsComponent } from './04-forms-with-material'; test('is possible to fill in a form and verify error messages (with the help of jest-dom https://testing-library.com/docs/ecosystem-jest-dom)', async () => { + const user = userEvent.setup(); + const { fixture } = await render(MaterialFormsComponent, { imports: [MaterialModule], }); @@ -22,14 +24,14 @@ test('is possible to fill in a form and verify error messages (with the help of expect(errors).toContainElement(screen.queryByText('color is required')); expect(errors).toContainElement(screen.queryByText('agree is required')); - userEvent.type(nameControl, 'Tim'); - userEvent.clear(scoreControl); - userEvent.type(scoreControl, '12'); - userEvent.click(colorControl); - userEvent.click(screen.getByText(/green/i)); + await user.type(nameControl, 'Tim'); + await user.clear(scoreControl); + await user.type(scoreControl, '12'); + await user.click(colorControl); + await user.click(screen.getByText(/green/i)); expect(checkboxControl).not.toBeChecked(); - userEvent.click(checkboxControl); + await user.click(checkboxControl); expect(checkboxControl).toBeChecked(); expect(checkboxControl).toBeValid(); @@ -39,11 +41,11 @@ test('is possible to fill in a form and verify error messages (with the help of expect(screen.queryByText('agree is required')).not.toBeInTheDocument(); expect(scoreControl).toBeInvalid(); - userEvent.clear(scoreControl); - userEvent.type(scoreControl, '7'); + await user.clear(scoreControl); + await user.type(scoreControl, '7'); expect(scoreControl).toBeValid(); - userEvent.type(dateControl, '08/11/2022'); + await user.type(dateControl, '08/11/2022'); expect(errors).not.toBeInTheDocument(); @@ -65,6 +67,8 @@ test('is possible to fill in a form and verify error messages (with the help of }); test('set and show pre-set form values', async () => { + const user = userEvent.setup(); + const { fixture, detectChanges } = await render(MaterialFormsComponent, { imports: [MaterialModule], }); @@ -87,7 +91,7 @@ test('set and show pre-set form values', async () => { expect(scoreControl).toHaveValue(4); expect(colorControl).toHaveTextContent('Blue'); expect(checkboxControl).toBeChecked(); - userEvent.click(checkboxControl); + await user.click(checkboxControl); const form = screen.getByRole('form'); expect(form).toHaveFormValues({ diff --git a/apps/example-app/src/app/examples/05-component-provider.spec.ts b/apps/example-app/src/app/examples/05-component-provider.spec.ts index 79811245..d23e849d 100644 --- a/apps/example-app/src/app/examples/05-component-provider.spec.ts +++ b/apps/example-app/src/app/examples/05-component-provider.spec.ts @@ -1,10 +1,13 @@ import { TestBed } from '@angular/core/testing'; -import { render, screen, fireEvent } from '@testing-library/angular'; +import { render, screen } from '@testing-library/angular'; import { provideMock, Mock, createMock } from '@testing-library/angular/jest-utils'; +import userEvent from '@testing-library/user-event'; import { ComponentWithProviderComponent, CounterService } from './05-component-provider'; test('renders the current value and can increment and decrement', async () => { + const user = userEvent.setup(); + await render(ComponentWithProviderComponent, { componentProviders: [ { @@ -20,15 +23,17 @@ test('renders the current value and can increment and decrement', async () => { expect(valueControl).toHaveTextContent('0'); - fireEvent.click(incrementControl); - fireEvent.click(incrementControl); + await user.click(incrementControl); + await user.click(incrementControl); expect(valueControl).toHaveTextContent('2'); - fireEvent.click(decrementControl); + await user.click(decrementControl); expect(valueControl).toHaveTextContent('1'); }); test('renders the current value and can increment and decrement with a mocked jest-utils service', async () => { + const user = userEvent.setup(); + const counter = createMock(CounterService); let fakeCounterValue = 50; counter.increment.mockImplementation(() => (fakeCounterValue += 10)); @@ -50,15 +55,17 @@ test('renders the current value and can increment and decrement with a mocked je expect(valueControl).toHaveTextContent('50'); - fireEvent.click(incrementControl); - fireEvent.click(incrementControl); + await user.click(incrementControl); + await user.click(incrementControl); expect(valueControl).toHaveTextContent('70'); - fireEvent.click(decrementControl); + await user.click(decrementControl); expect(valueControl).toHaveTextContent('60'); }); test('renders the current value and can increment and decrement with provideMocked from jest-utils', async () => { + const user = userEvent.setup(); + await render(ComponentWithProviderComponent, { componentProviders: [provideMock(CounterService)], }); @@ -66,9 +73,9 @@ test('renders the current value and can increment and decrement with provideMock const incrementControl = screen.getByRole('button', { name: /increment/i }); const decrementControl = screen.getByRole('button', { name: /decrement/i }); - fireEvent.click(incrementControl); - fireEvent.click(incrementControl); - fireEvent.click(decrementControl); + await user.click(incrementControl); + await user.click(incrementControl); + await user.click(decrementControl); const counterService = TestBed.inject(CounterService) as Mock; expect(counterService.increment).toHaveBeenCalledTimes(2); diff --git a/apps/example-app/src/app/examples/06-with-ngrx-store.spec.ts b/apps/example-app/src/app/examples/06-with-ngrx-store.spec.ts index b8a289bb..0f080658 100644 --- a/apps/example-app/src/app/examples/06-with-ngrx-store.spec.ts +++ b/apps/example-app/src/app/examples/06-with-ngrx-store.spec.ts @@ -1,9 +1,12 @@ -import { render, screen, fireEvent } from '@testing-library/angular'; +import { render, screen } from '@testing-library/angular'; import { StoreModule } from '@ngrx/store'; +import userEvent from '@testing-library/user-event'; import { WithNgRxStoreComponent, reducer } from './06-with-ngrx-store'; test('works with ngrx store', async () => { + const user = userEvent.setup(); + await render(WithNgRxStoreComponent, { imports: [ StoreModule.forRoot( @@ -23,10 +26,10 @@ test('works with ngrx store', async () => { expect(valueControl).toHaveTextContent('0'); - fireEvent.click(incrementControl); - fireEvent.click(incrementControl); + await user.click(incrementControl); + await user.click(incrementControl); expect(valueControl).toHaveTextContent('20'); - fireEvent.click(decrementControl); + await user.click(decrementControl); expect(valueControl).toHaveTextContent('10'); }); diff --git a/apps/example-app/src/app/examples/07-with-ngrx-mock-store.spec.ts b/apps/example-app/src/app/examples/07-with-ngrx-mock-store.spec.ts index 936168e5..eb51dbbc 100644 --- a/apps/example-app/src/app/examples/07-with-ngrx-mock-store.spec.ts +++ b/apps/example-app/src/app/examples/07-with-ngrx-mock-store.spec.ts @@ -1,10 +1,13 @@ import { TestBed } from '@angular/core/testing'; import { provideMockStore, MockStore } from '@ngrx/store/testing'; -import { render, screen, fireEvent } from '@testing-library/angular'; +import { render, screen } from '@testing-library/angular'; +import userEvent from '@testing-library/user-event'; import { WithNgRxMockStoreComponent, selectItems } from './07-with-ngrx-mock-store'; test('works with provideMockStore', async () => { + const user = userEvent.setup(); + await render(WithNgRxMockStoreComponent, { providers: [ provideMockStore({ @@ -21,7 +24,7 @@ test('works with provideMockStore', async () => { const store = TestBed.inject(MockStore); store.dispatch = jest.fn(); - fireEvent.click(screen.getByText(/seven/i)); + await user.click(screen.getByText(/seven/i)); expect(store.dispatch).toHaveBeenCalledWith({ type: '[Item List] send', item: 'Seven' }); }); diff --git a/apps/example-app/src/app/examples/08-directive.spec.ts b/apps/example-app/src/app/examples/08-directive.spec.ts index efd2a5b9..b81dcff0 100644 --- a/apps/example-app/src/app/examples/08-directive.spec.ts +++ b/apps/example-app/src/app/examples/08-directive.spec.ts @@ -1,8 +1,11 @@ -import { render, screen, fireEvent } from '@testing-library/angular'; +import { render, screen } from '@testing-library/angular'; +import userEvent from '@testing-library/user-event'; import { SpoilerDirective } from './08-directive'; test('it is possible to test directives', async () => { + const user = userEvent.setup(); + await render('
', { declarations: [SpoilerDirective], }); @@ -12,16 +15,17 @@ test('it is possible to test directives', async () => { expect(screen.queryByText('I am visible now...')).not.toBeInTheDocument(); expect(screen.getByText('SPOILER')).toBeInTheDocument(); - fireEvent.mouseOver(directive); + await user.hover(directive); expect(screen.queryByText('SPOILER')).not.toBeInTheDocument(); expect(screen.getByText('I am visible now...')).toBeInTheDocument(); - fireEvent.mouseLeave(directive); + await user.unhover(directive); expect(screen.getByText('SPOILER')).toBeInTheDocument(); expect(screen.queryByText('I am visible now...')).not.toBeInTheDocument(); }); test('it is possible to test directives with props', async () => { + const user = userEvent.setup(); const hidden = 'SPOILER ALERT'; const visible = 'There is nothing to see here ...'; @@ -36,16 +40,17 @@ test('it is possible to test directives with props', async () => { expect(screen.queryByText(visible)).not.toBeInTheDocument(); expect(screen.getByText(hidden)).toBeInTheDocument(); - fireEvent.mouseOver(screen.getByText(hidden)); + await user.hover(screen.getByText(hidden)); expect(screen.queryByText(hidden)).not.toBeInTheDocument(); expect(screen.getByText(visible)).toBeInTheDocument(); - fireEvent.mouseLeave(screen.getByText(visible)); + await user.unhover(screen.getByText(visible)); expect(screen.getByText(hidden)).toBeInTheDocument(); expect(screen.queryByText(visible)).not.toBeInTheDocument(); }); test('it is possible to test directives with props in template', async () => { + const user = userEvent.setup(); const hidden = 'SPOILER ALERT'; const visible = 'There is nothing to see here ...'; @@ -56,11 +61,11 @@ test('it is possible to test directives with props in template', async () => { expect(screen.queryByText(visible)).not.toBeInTheDocument(); expect(screen.getByText(hidden)).toBeInTheDocument(); - fireEvent.mouseOver(screen.getByText(hidden)); + await user.hover(screen.getByText(hidden)); expect(screen.queryByText(hidden)).not.toBeInTheDocument(); expect(screen.getByText(visible)).toBeInTheDocument(); - fireEvent.mouseLeave(screen.getByText(visible)); + await user.unhover(screen.getByText(visible)); expect(screen.getByText(hidden)).toBeInTheDocument(); expect(screen.queryByText(visible)).not.toBeInTheDocument(); }); diff --git a/apps/example-app/src/app/examples/09-router.spec.ts b/apps/example-app/src/app/examples/09-router.spec.ts index 2fd519fc..9483695d 100644 --- a/apps/example-app/src/app/examples/09-router.spec.ts +++ b/apps/example-app/src/app/examples/09-router.spec.ts @@ -4,6 +4,7 @@ import userEvent from '@testing-library/user-event'; import { DetailComponent, RootComponent, HiddenDetailComponent } from './09-router'; test('it can navigate to routes', async () => { + const user = userEvent.setup(); await render(RootComponent, { declarations: [DetailComponent, HiddenDetailComponent], routes: [ @@ -25,20 +26,20 @@ test('it can navigate to routes', async () => { expect(screen.queryByText(/Detail one/i)).not.toBeInTheDocument(); - userEvent.click(screen.getByRole('link', { name: /load one/i })); + await user.click(screen.getByRole('link', { name: /load one/i })); expect(await screen.findByRole('heading', { name: /Detail one/i })).toBeInTheDocument(); - userEvent.click(screen.getByRole('link', { name: /load three/i })); + await user.click(screen.getByRole('link', { name: /load three/i })); expect(screen.queryByRole('heading', { name: /Detail one/i })).not.toBeInTheDocument(); expect(await screen.findByRole('heading', { name: /Detail three/i })).toBeInTheDocument(); - userEvent.click(screen.getByRole('link', { name: /back to parent/i })); + await user.click(screen.getByRole('link', { name: /back to parent/i })); expect(screen.queryByRole('heading', { name: /Detail three/i })).not.toBeInTheDocument(); - userEvent.click(screen.getByRole('link', { name: /load two/i })); + await user.click(screen.getByRole('link', { name: /load two/i })); expect(await screen.findByRole('heading', { name: /Detail two/i })).toBeInTheDocument(); - userEvent.click(screen.getByRole('link', { name: /hidden x/i })); + await user.click(screen.getByRole('link', { name: /hidden x/i })); expect(await screen.findByText(/You found the treasure!/i)).toBeInTheDocument(); }); diff --git a/apps/example-app/src/app/examples/14-async-component.spec.ts b/apps/example-app/src/app/examples/14-async-component.spec.ts index cdbc0d08..b54740a2 100644 --- a/apps/example-app/src/app/examples/14-async-component.spec.ts +++ b/apps/example-app/src/app/examples/14-async-component.spec.ts @@ -3,6 +3,7 @@ import { render, screen, fireEvent } from '@testing-library/angular'; import { AsyncComponent } from './14-async-component'; +// eslint-disable-next-line jest/no-disabled-tests test.skip('can use fakeAsync utilities', fakeAsync(async () => { await render(AsyncComponent); @@ -20,6 +21,8 @@ test('can use fakeTimer utilities', async () => { await render(AsyncComponent); const load = await screen.findByRole('button', { name: /load/i }); + + // userEvent not working with fake timers fireEvent.click(load); jest.advanceTimersByTime(10_000); diff --git a/apps/example-app/src/app/examples/15-dialog.component.spec.ts b/apps/example-app/src/app/examples/15-dialog.component.spec.ts index 355b3904..1177f293 100644 --- a/apps/example-app/src/app/examples/15-dialog.component.spec.ts +++ b/apps/example-app/src/app/examples/15-dialog.component.spec.ts @@ -1,10 +1,12 @@ import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; -import { render, screen, fireEvent } from '@testing-library/angular'; +import { render, screen } from '@testing-library/angular'; import userEvent from '@testing-library/user-event'; import { DialogComponent, DialogContentComponent, DialogContentComponentModule } from './15-dialog.component'; test('dialog closes', async () => { + const user = userEvent.setup(); + const closeFn = jest.fn(); await render(DialogContentComponent, { imports: [MatDialogModule], @@ -19,28 +21,28 @@ test('dialog closes', async () => { }); const cancelButton = await screen.findByRole('button', { name: /cancel/i }); - userEvent.click(cancelButton); + await user.click(cancelButton); expect(closeFn).toHaveBeenCalledTimes(1); }); test('closes the dialog via the backdrop', async () => { + const user = userEvent.setup(); + await render(DialogComponent, { imports: [MatDialogModule, DialogContentComponentModule], }); const openDialogButton = await screen.findByRole('button', { name: /open dialog/i }); - userEvent.click(openDialogButton); + await user.click(openDialogButton); const dialogControl = await screen.findByRole('dialog'); expect(dialogControl).toBeInTheDocument(); const dialogTitleControl = await screen.findByRole('heading', { name: /dialog title/i }); expect(dialogTitleControl).toBeInTheDocument(); - // using fireEvent because of: - // unable to click element as it has or inherits pointer-events set to "none" - // eslint-disable-next-line testing-library/no-node-access, @typescript-eslint/no-non-null-assertion - fireEvent.click(document.querySelector('.cdk-overlay-backdrop')!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, testing-library/no-node-access + await user.click(document.querySelector('.cdk-overlay-backdrop')!); expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); @@ -49,12 +51,14 @@ test('closes the dialog via the backdrop', async () => { }); test('opens and closes the dialog with buttons', async () => { + const user = userEvent.setup(); + await render(DialogComponent, { imports: [MatDialogModule, DialogContentComponentModule], }); const openDialogButton = await screen.findByRole('button', { name: /open dialog/i }); - userEvent.click(openDialogButton); + await user.click(openDialogButton); const dialogControl = await screen.findByRole('dialog'); expect(dialogControl).toBeInTheDocument(); @@ -62,7 +66,7 @@ test('opens and closes the dialog with buttons', async () => { expect(dialogTitleControl).toBeInTheDocument(); const cancelButton = await screen.findByRole('button', { name: /cancel/i }); - userEvent.click(cancelButton); + await user.click(cancelButton); expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); diff --git a/apps/example-app/src/app/examples/20-test-harness.spec.ts b/apps/example-app/src/app/examples/20-test-harness.spec.ts index b43f7f7f..5023f662 100644 --- a/apps/example-app/src/app/examples/20-test-harness.spec.ts +++ b/apps/example-app/src/app/examples/20-test-harness.spec.ts @@ -6,6 +6,7 @@ import user from '@testing-library/user-event'; import { SnackBarComponent } from './20-test-harness'; +// eslint-disable-next-line jest/no-disabled-tests test.skip('can be used with TestHarness', async () => { const view = await render(``, { imports: [SnackBarComponent], @@ -20,6 +21,7 @@ test.skip('can be used with TestHarness', async () => { expect(await snackbarHarness.getMessage()).toMatch(/Pizza Party!!!/i); }); +// eslint-disable-next-line jest/no-disabled-tests test.skip('can be used in combination with TestHarness', async () => { const view = await render(SnackBarComponent); const loader = TestbedHarnessEnvironment.documentRootLoader(view.fixture); diff --git a/apps/example-app/src/app/issues/issue-304.spec.ts b/apps/example-app/src/app/issues/issue-304.spec.ts index d9bc355f..25d3b6f3 100644 --- a/apps/example-app/src/app/issues/issue-304.spec.ts +++ b/apps/example-app/src/app/issues/issue-304.spec.ts @@ -6,6 +6,8 @@ import { TestBed } from '@angular/core/testing'; // with goBackSpy, the implementation of goBack won't be invoked (because it's using the spy) test('should call a goBack when user click in the button', async () => { + const user = userEvent.setup(); + const goBackSpy = jest.fn(); await render(HeaderBackButtonComponent, { declarations: [IconButtonComponent], @@ -15,12 +17,14 @@ test('should call a goBack when user click in the button', async () => { }); const button = screen.getByLabelText(/icon button/i); - userEvent.click(button); + await user.click(button); expect(goBackSpy).toHaveBeenCalled(); }); // don't spy on goBack, this way the implementation of goBack is invoked, and you can test if location.back() is called test('should call a Location.back when user click in the button', async () => { + const user = userEvent.setup(); + await render(HeaderBackButtonComponent, { declarations: [IconButtonComponent], }); @@ -29,7 +33,7 @@ test('should call a Location.back when user click in the button', async () => { jest.spyOn(location, 'back'); const button = screen.getByLabelText(/icon button/i); - userEvent.click(button); + await user.click(button); expect(location.back).toHaveBeenCalled(); }); diff --git a/package.json b/package.json index e8af8a6d..f9264f29 100644 --- a/package.json +++ b/package.json @@ -38,12 +38,12 @@ "@angular/platform-browser-dynamic": "16.0.0", "@angular/router": "16.0.0", "@ngrx/store": "16.0.0", + "@nx/angular": "16.1.4", "@testing-library/dom": "^9.0.0", "nx-cloud": "16.0.5", "rxjs": "7.8.0", "tslib": "~2.3.1", - "zone.js": "0.13.0", - "@nx/angular": "16.1.4" + "zone.js": "0.13.0" }, "devDependencies": { "@angular-devkit/build-angular": "16.0.0", @@ -67,7 +67,7 @@ "@schematics/angular": "16.0.0", "@testing-library/jasmine-dom": "^1.2.0", "@testing-library/jest-dom": "^5.16.5", - "@testing-library/user-event": "^13.5.0", + "@testing-library/user-event": "^14.4.3", "@types/jasmine": "4.3.1", "@types/jest": "29.5.1", "@types/node": "20.1.4", diff --git a/projects/testing-library/tests/integration.spec.ts b/projects/testing-library/tests/integration.spec.ts index 112177e1..eedec0e9 100644 --- a/projects/testing-library/tests/integration.spec.ts +++ b/projects/testing-library/tests/integration.spec.ts @@ -1,9 +1,9 @@ import { Component, EventEmitter, Injectable, Input, Output } from '@angular/core'; import { TestBed } from '@angular/core/testing'; -import userEvent from '@testing-library/user-event'; 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'; const DEBOUNCE_TIME = 1_000; @@ -86,8 +86,9 @@ const entities = [ }, ]; -test('renders the table', async () => { +async function setup() { jest.useFakeTimers(); + const user = userEvent.setup(); await render(EntitiesComponent, { declarations: [TableComponent], @@ -106,29 +107,50 @@ test('renders the table', async () => { }, ], }); + const modalMock = TestBed.inject(ModalService); + return { + modalMock, + user, + }; +} + +test('renders the heading', async () => { + await setup(); + expect(await screen.findByRole('heading', { name: /Entities Title/i })).toBeInTheDocument(); +}); + +test('renders the entities', async () => { + await setup(); expect(await screen.findByRole('cell', { name: /Entity 1/i })).toBeInTheDocument(); expect(await screen.findByRole('cell', { name: /Entity 2/i })).toBeInTheDocument(); expect(await screen.findByRole('cell', { name: /Entity 3/i })).toBeInTheDocument(); +}); - userEvent.type(await screen.findByRole('textbox', { name: /Search entities/i }), 'Entity 2', {}); +test.skip('finds the cell', async () => { + const { user } = await setup(); + + await user.type(await screen.findByRole('textbox', { name: /Search entities/i }), 'Entity 2', {}); jest.advanceTimersByTime(DEBOUNCE_TIME); await waitForElementToBeRemoved(() => screen.queryByRole('cell', { name: /Entity 1/i })); expect(await screen.findByRole('cell', { name: /Entity 2/i })).toBeInTheDocument(); +}); - userEvent.click(await screen.findByRole('button', { name: /New Entity/i })); +test.skip('opens the modal', async () => { + const { modalMock, user } = await setup(); + await user.click(await screen.findByRole('button', { name: /New Entity/i })); expect(modalMock.open).toHaveBeenCalledWith('new entity'); const row = await screen.findByRole('row', { name: /Entity 2/i, }); - userEvent.click( + await user.click( await within(row).findByRole('button', { name: /edit/i, }), diff --git a/projects/testing-library/tests/issues/issue-386.spec.ts b/projects/testing-library/tests/issues/issue-386.spec.ts index a2a47865..b0c5613d 100644 --- a/projects/testing-library/tests/issues/issue-386.spec.ts +++ b/projects/testing-library/tests/issues/issue-386.spec.ts @@ -1,7 +1,6 @@ import { Component } from '@angular/core'; import { throwError } from 'rxjs'; -import userEvent from '@testing-library/user-event'; -import { render, screen } from '../../src/public_api'; +import { render, screen, fireEvent } from '../../src/public_api'; @Component({ selector: 'atl-fixture', @@ -26,11 +25,11 @@ describe('TestComponent', () => { it('does not fail', async () => { await render(TestComponent); - await userEvent.click(screen.getByText('Test')); + fireEvent.click(screen.getByText('Test')); }); it('fails because of the previous one', async () => { await render(TestComponent); - await userEvent.click(screen.getByText('Test')); + fireEvent.click(screen.getByText('Test')); }); }); 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 1f2f8eae..5c16a539 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 @@ -16,7 +16,7 @@ class FixtureComponent implements OnInit { test('waits for element to be removed (callback)', async () => { await render(FixtureComponent); - await waitForElementToBeRemoved(() => screen.getByTestId('im-here')); + await waitForElementToBeRemoved(() => screen.queryByTestId('im-here')); expect(screen.queryByTestId('im-here')).not.toBeInTheDocument(); }); @@ -24,7 +24,7 @@ test('waits for element to be removed (callback)', async () => { test('waits for element to be removed (element)', async () => { await render(FixtureComponent); - await waitForElementToBeRemoved(screen.getByTestId('im-here')); + await waitForElementToBeRemoved(screen.queryByTestId('im-here')); expect(screen.queryByTestId('im-here')).not.toBeInTheDocument(); }); @@ -32,7 +32,7 @@ test('waits for element to be removed (element)', async () => { test('allows to override options', async () => { await render(FixtureComponent); - await expect(waitForElementToBeRemoved(() => screen.getByTestId('im-here'), { timeout: 200 })).rejects.toThrow( + await expect(waitForElementToBeRemoved(() => screen.queryByTestId('im-here'), { timeout: 200 })).rejects.toThrow( /Timed out in waitForElementToBeRemoved/i, ); }); diff --git a/projects/testing-library/tests/wait-for.spec.ts b/projects/testing-library/tests/wait-for.spec.ts index e963b0c4..8c6562f0 100644 --- a/projects/testing-library/tests/wait-for.spec.ts +++ b/projects/testing-library/tests/wait-for.spec.ts @@ -1,6 +1,6 @@ import { Component } from '@angular/core'; import { timer } from 'rxjs'; -import { render, screen, fireEvent, waitFor } from '../src/public_api'; +import { render, screen, waitFor, fireEvent } from '../src/public_api'; @Component({ selector: 'atl-fixture', @@ -24,8 +24,7 @@ test('waits for assertion to become true', async () => { fireEvent.click(screen.getByTestId('button')); - await screen.findByText('Success'); - expect(screen.getByText('Success')).toBeInTheDocument(); + expect(await screen.findByText('Success')).toBeInTheDocument(); }); test('allows to override options', async () => {