diff --git a/package.json b/package.json index 596e0075..8dad202a 100644 --- a/package.json +++ b/package.json @@ -43,8 +43,8 @@ "@babel/preset-env": "^7.9.6", "@faker-js/faker": "^6.2.0", "@floating-ui/dom": "^1.0.3", - "@testing-library/cypress": "^8.0.2", - "@testing-library/vue": "^6.5.1", + "@testing-library/user-event": "^14.4.3", + "@testing-library/vue": "^6.6.1", "@types/axios": "^0.14.0", "@types/blueimp-md5": "^2.7.0", "@types/local-storage": "^1.4.0", diff --git a/resources/assets/js/__tests__/UnitTestCase.ts b/resources/assets/js/__tests__/UnitTestCase.ts index bffa4b97..958072eb 100644 --- a/resources/assets/js/__tests__/UnitTestCase.ts +++ b/resources/assets/js/__tests__/UnitTestCase.ts @@ -10,6 +10,8 @@ import { DialogBoxKey, MessageToasterKey, OverlayKey, RouterKey } from '@/symbol import { DialogBoxStub, MessageToasterStub, OverlayStub } from '@/__tests__/stubs' import { routes } from '@/config' import Router from '@/router' +import userEvent from '@testing-library/user-event' +import { UserEvent } from '@testing-library/user-event/dist/types/setup/setup' // A deep-merge function that // - supports symbols as keys (_.merge doesn't) @@ -27,10 +29,13 @@ const deepMerge = (first: object, second: object) => { export default abstract class UnitTestCase { private backupMethods = new Map() protected router: Router + protected user: UserEvent public constructor () { this.router = new Router(routes) this.mock(http, 'request') // prevent actual HTTP requests from being made + this.user = userEvent.setup({ delay: null }) // @see https://github.com/testing-library/user-event/issues/833 + this.beforeEach() this.afterEach() this.test() @@ -150,5 +155,10 @@ export default abstract class UnitTestCase { }) } + protected async type (element: HTMLElement, value: string) { + await this.user.clear(element) + await this.user.type(element, value) + } + protected abstract test () } diff --git a/resources/assets/js/components/album/AlbumCard.spec.ts b/resources/assets/js/components/album/AlbumCard.spec.ts index cdecdfe4..5d413fd7 100644 --- a/resources/assets/js/components/album/AlbumCard.spec.ts +++ b/resources/assets/js/components/album/AlbumCard.spec.ts @@ -1,10 +1,10 @@ -import { fireEvent } from '@testing-library/vue' +import { screen } from '@testing-library/vue' import { expect, it } from 'vitest' import { downloadService, playbackService } from '@/services' import factory from '@/__tests__/factory' import UnitTestCase from '@/__tests__/UnitTestCase' import AlbumCard from './AlbumCard.vue' -import { songStore } from '@/stores' +import { commonStore, songStore } from '@/stores' let album: Album @@ -34,20 +34,27 @@ new class extends UnitTestCase { it('downloads', async () => { const mock = this.mock(downloadService, 'fromAlbum') - const { getByTestId } = this.renderComponent() + this.renderComponent() - await fireEvent.click(getByTestId('download-album')) + await this.user.click(screen.getByTitle('Download all songs in the album IV')) expect(mock).toHaveBeenCalledTimes(1) }) + it('does not have an option to download if downloading is disabled', async () => { + commonStore.state.allow_download = false + this.renderComponent() + + expect(screen.queryByText('Download')).toBeNull() + }) + it('shuffles', async () => { const songs = factory('song', 10) const fetchMock = this.mock(songStore, 'fetchForAlbum').mockResolvedValue(songs) const shuffleMock = this.mock(playbackService, 'queueAndPlay').mockResolvedValue(void 0) - const { getByTestId } = this.renderComponent() + this.renderComponent() - await fireEvent.click(getByTestId('shuffle-album')) + await this.user.click(screen.getByTitle('Shuffle all songs in the album IV')) await this.tick() expect(fetchMock).toHaveBeenCalledWith(album) diff --git a/resources/assets/js/components/album/AlbumCard.vue b/resources/assets/js/components/album/AlbumCard.vue index ad836ce8..eaa9781e 100644 --- a/resources/assets/js/components/album/AlbumCard.vue +++ b/resources/assets/js/components/album/AlbumCard.vue @@ -18,7 +18,6 @@ { - const { html } = await this.renderComponent() - expect(html()).toMatchSnapshot() - }) + it('renders', async () => expect((await this.renderComponent()).html()).toMatchSnapshot()) it('plays all', async () => { const songs = factory('song', 10) const fetchMock = this.mock(songStore, 'fetchForAlbum').mockResolvedValue(songs) const playMock = this.mock(playbackService, 'queueAndPlay') - const { getByText } = await this.renderComponent() - await getByText('Play All').click() + await this.renderComponent() + await this.user.click(screen.getByText('Play All')) await this.tick() expect(fetchMock).toHaveBeenCalledWith(album) @@ -45,8 +43,8 @@ new class extends UnitTestCase { const fetchMock = this.mock(songStore, 'fetchForAlbum').mockResolvedValue(songs) const playMock = this.mock(playbackService, 'queueAndPlay') - const { getByText } = await this.renderComponent() - await getByText('Shuffle All').click() + await this.renderComponent() + await this.user.click(screen.getByText('Shuffle All')) await this.tick() expect(fetchMock).toHaveBeenCalledWith(album) @@ -55,42 +53,42 @@ new class extends UnitTestCase { it('downloads', async () => { const downloadMock = this.mock(downloadService, 'fromAlbum') + await this.renderComponent() - const { getByText } = await this.renderComponent() - await getByText('Download').click() + await this.user.click(screen.getByText('Download')) expect(downloadMock).toHaveBeenCalledWith(album) }) it('does not have an option to download if downloading is disabled', async () => { commonStore.state.allow_download = false - const { queryByText } = await this.renderComponent() + await this.renderComponent() - expect(queryByText('Download')).toBeNull() + expect(screen.queryByText('Download')).toBeNull() }) it('goes to album', async () => { const mock = this.mock(this.router, 'go') - const { getByText } = await this.renderComponent() + await this.renderComponent() - await getByText('Go to Album').click() + await this.user.click(screen.getByText('Go to Album')) expect(mock).toHaveBeenCalledWith(`album/${album.id}`) }) it('does not have an option to download or go to Unknown Album and Artist', async () => { - const { queryByTestId } = await this.renderComponent(factory.states('unknown')('album')) + await this.renderComponent(factory.states('unknown')('album')) - expect(queryByTestId('view-album')).toBeNull() - expect(queryByTestId('view-artist')).toBeNull() - expect(queryByTestId('download')).toBeNull() + expect(screen.queryByText('Go to Album')).toBeNull() + expect(screen.queryByText('Go to Artist')).toBeNull() + expect(screen.queryByText('Download')).toBeNull() }) it('goes to artist', async () => { const mock = this.mock(this.router, 'go') - const { getByText } = await this.renderComponent() + await this.renderComponent() - await getByText('Go to Artist').click() + await this.user.click(screen.getByText('Go to Artist')) expect(mock).toHaveBeenCalledWith(`artist/${album.artist_id}`) }) diff --git a/resources/assets/js/components/album/AlbumContextMenu.vue b/resources/assets/js/components/album/AlbumContextMenu.vue index cd9f0615..be61e590 100644 --- a/resources/assets/js/components/album/AlbumContextMenu.vue +++ b/resources/assets/js/components/album/AlbumContextMenu.vue @@ -1,14 +1,14 @@