Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
Add the new e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
obulat committed Oct 18, 2022
1 parent bd8da8b commit 6773c69
Show file tree
Hide file tree
Showing 7 changed files with 830 additions and 51 deletions.
225 changes: 225 additions & 0 deletions test/playwright/e2e/filters.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import { test, expect, Page } from '@playwright/test'

import {
assertCheckboxStatus,
openFilters,
changeContentType,
goToSearchTerm,
enableNewHeader,
closeFilters,
isPageDesktop,
} from '~~/test/playwright/utils/navigation'

import { mockProviderApis } from '~~/test/playwright/utils/route'

import { TestScreen, testScreens } from '~~/test/playwright/utils/breakpoints'

import { SCREEN_SIZES } from '~~/src/constants/screens'

import {
supportedSearchTypes,
ALL_MEDIA,
IMAGE,
AUDIO,
} from '~/constants/media'

test.describe.configure({ mode: 'parallel' })

const assertCheckboxCount = async (
page: Page,
checked: 'checked' | 'notChecked' | 'total',
count: number
) => {
const checkedString = {
checked: ':checked',
notChecked: ':not(:checked)',
total: '',
}[checked]
const locatorString = `input[type="checkbox"]${checkedString}`
await expect(page.locator(locatorString)).toHaveCount(count, { timeout: 200 })
}

const FILTER_COUNTS = {
[ALL_MEDIA]: 11,
[AUDIO]: 32,
[IMAGE]: 70,
}

for (const breakpoint of testScreens) {
test.describe(`filters on ${breakpoint}`, () => {
const width = SCREEN_SIZES.get(breakpoint as TestScreen) as number

test.use({ viewport: { width, height: 700 } })

test.beforeEach(async ({ context, page }) => {
await mockProviderApis(context)
await enableNewHeader(page)
})

for (const searchType of supportedSearchTypes) {
test(`correct total number of filters is displayed for ${searchType}`, async ({
page,
}) => {
await goToSearchTerm(page, 'cat', { searchType })

await openFilters(page)

await assertCheckboxCount(page, 'total', FILTER_COUNTS[searchType])
})
}

test('initial filters are applied based on the url', async ({ page }) => {
await page.goto(
'/search/?q=cat&license_type=commercial&license=cc0&searchBy=creator'
)
await openFilters(page)
const expectedFilters = ['cc0', 'commercial', 'creator']

for (const checkbox of expectedFilters) {
await assertCheckboxStatus(page, checkbox)
}
})

test('common filters are retained when media type changes from all media to single type', async ({
page,
}) => {
await page.goto(
'/search/?q=cat&license_type=commercial&license=cc0&searchBy=creator'
)
await openFilters(page)
const expectedFilters = ['cc0', 'commercial', 'creator']

for (const checkbox of expectedFilters) {
await assertCheckboxStatus(page, checkbox)
}
await changeContentType(page, 'Images')

await expect(page).toHaveURL(
'/search/image?q=cat&license_type=commercial&license=cc0&searchBy=creator'
)
await openFilters(page)
for (const checkbox of expectedFilters) {
await assertCheckboxStatus(page, checkbox)
}
})

test('common filters are retained when media type changes from single type to all media', async ({
page,
}) => {
await page.goto(
'/search/image?q=cat&license_type=commercial&license=cc0&searchBy=creator'
)
await openFilters(page)

for (const checkbox of ['cc0', 'commercial', 'creator']) {
await assertCheckboxStatus(page, checkbox)
}

await changeContentType(page, 'All content')

await openFilters(page)
for (const checkbox of ['cc0', 'commercial', 'creator']) {
await assertCheckboxStatus(page, checkbox)
}
await expect(page).toHaveURL(
'/search/?q=cat&license_type=commercial&license=cc0&searchBy=creator'
)
})

test('selecting some filters can disable dependent filters', async ({
page,
}) => {
await page.goto('/search/audio?q=cat&license_type=commercial')
await openFilters(page)

// by-nc is special because we normally test for fuzzy match, and by-nc matches 3 labels.
const byNc = page.locator('input[value="by-nc"]')
await expect(byNc).toBeDisabled()
for (const checkbox of ['by-nc-sa', 'by-nc-nd']) {
await assertCheckboxStatus(page, checkbox, '', 'disabled')
}
await assertCheckboxStatus(page, 'commercial')

await page.click('label:has-text("commercial")')

await assertCheckboxStatus(page, 'commercial', '', 'unchecked')
await expect(byNc).not.toBeDisabled()
for (const checkbox of ['commercial', 'by-nc-sa', 'by-nc-nd']) {
await assertCheckboxStatus(page, checkbox, '', 'unchecked')
}
})

/**
* When the search type changes:
* - image-specific filter (aspect_ration=tall) is discarded
* - common filter (license_type=CC0) is kept
* - filter button text updates
* - URL updates
* Tests for the missing checkbox with `toHaveCount` are flaky, so we use filter button
* text and the URL instead.
*/
test('filters are updated when media type changes', async ({ page }) => {
await page.goto('/search/image?q=cat&aspect_ratio=tall&license=cc0')
await openFilters(page)

await assertCheckboxStatus(page, 'tall')
await assertCheckboxStatus(page, 'cc0')

await changeContentType(page, 'Audio')
await openFilters(page)

// Only CC0 checkbox is checked, and the filter button label is '1 Filter'
await assertCheckboxStatus(page, 'cc0')
await closeFilters(page)
if (isPageDesktop(page)) {
await expect(
page.locator('[aria-controls="filters"] span:visible')
).toHaveText('1 Filter')
} else {
const filtersAriaLabel =
(await page
.locator('[aria-controls="content-settings-modal"]')
.getAttribute('aria-label')) ?? ''
expect(filtersAriaLabel.trim()).toEqual('Menu. 1 filter applied')
}

await expect(page).toHaveURL('/search/audio?q=cat&license=cc0')
})

test('new media request is sent when a filter is selected', async ({
page,
}) => {
await page.goto('/search/image?q=cat')
await openFilters(page)

await assertCheckboxStatus(page, 'cc0', '', 'unchecked')

const [response] = await Promise.all([
page.waitForResponse((response) => response.url().includes('cc0')),
page.click('label:has-text("CC0")'),
])

await assertCheckboxStatus(page, 'cc0')
// Remove the host url and path because when proxied, the 'http://localhost:49153' is used instead of the
// real API url
const queryString = response.url().split('/images/')[1]
expect(queryString).toEqual('?q=cat&license=cc0')
})

for (const [searchType, source] of [
['audio', 'Freesound'],
['image', 'Flickr'],
]) {
test(`Provider filters are correctly initialized from the URL: ${source} - ${searchType}`, async ({
page,
}) => {
await page.goto(
`/search/${searchType}?q=birds&source=${source.toLowerCase()}`
)
await openFilters(page)

await assertCheckboxStatus(page, source, '', 'checked')
})
}
})
}
104 changes: 53 additions & 51 deletions test/playwright/e2e/header-internal.spec.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,61 @@
import { test, expect } from '@playwright/test'
import { test, expect, Page } from '@playwright/test'

import {
enableNewHeader,
isMobileMenuOpen,
scrollToBottom,
t,
} from '~~/test/playwright/utils/navigation'

test.use({
viewport: { width: 640, height: 600 },
})

test.skip('can open and close the modal on md breakpoint', async ({ page }) => {
await enableNewHeader(page)

await page.goto('/about')
const menuAriaLabel = t('header.aria.menu')

await page.locator(`[aria-label="${menuAriaLabel}"]`).click()
await expect(page.locator('[role="dialog"]')).toBeVisible()
await expect(
page.locator('div[role="dialog"] >> [aria-current="page"]')
).toBeVisible()
await expect(
page.locator('div[role="dialog"] >> [aria-current="page"]')
).toHaveText('About')

await page.locator('div[role="dialog"] >> [aria-label="Close"]').click()
await expect(page.locator(`[aria-label="${menuAriaLabel}"]`)).toBeVisible()
})

test.skip('the modal locks the scroll on md breakpoint', async ({ page }) => {
await enableNewHeader(page)

await page.goto('/about')
const menuAriaLabel = t('header.aria.menu')

await scrollToBottom(page)
await page.locator(`[aria-label="${menuAriaLabel}"]`).click()
await page.locator('div[role="dialog"] >> [aria-label="Close"]').click()

const scrollPosition = await page.evaluate(() => window.scrollY)
expect(scrollPosition).toBeGreaterThan(100)
})

test("the modal opens an external link in a new window and it doesn't close the modal", async ({
page,
}) => {
await enableNewHeader(page)

await page.goto('/about')
const menuAriaLabel = t('header.aria.menu')

await scrollToBottom(page)
await page.locator(`[aria-label="${menuAriaLabel}"]`).click()
await page.locator('div[role="dialog"] >> text=API').click()

await expect(page.locator('[role="dialog"]')).toBeVisible()
const modalCloseButton = 'div[role="dialog"] >> [aria-label="Close"]'
const currentPageLink = 'div[role="dialog"] >> [aria-current="page"]'
const menuButton = `[aria-label="${t('header.aria.menu')}"]`

const openMenu = async (page: Page) => await page.click(menuButton)
const closeMenu = async (page: Page) => await page.click(modalCloseButton)

test.describe('Header internal', () => {
test.use({
viewport: { width: 640, height: 600 },
})
test.beforeEach(async ({ page }) => {
await enableNewHeader(page)
await page.goto('/about')
})
test('can open and close the modal on md breakpoint', async ({ page }) => {
await openMenu(page)
expect(await isMobileMenuOpen(page)).toBe(true)
await expect(page.locator(currentPageLink)).toBeVisible()
await expect(page.locator(currentPageLink)).toHaveText('About')

await closeMenu(page)
expect(await isMobileMenuOpen(page)).toBe(false)
await expect(page.locator(menuButton)).toBeVisible()
})

test('the modal locks the scroll on md breakpoint', async ({ page }) => {
await scrollToBottom(page)

await openMenu(page)
await closeMenu(page)

const scrollPosition = await page.evaluate(() => window.scrollY)
expect(scrollPosition).toBeGreaterThan(100)
})

test("the modal opens an external link in a new window and it doesn't close the modal", async ({
page,
}) => {
await scrollToBottom(page)
await openMenu(page)

// Open the external link in a new tab, close the tab
const [popup] = await Promise.all([
page.waitForEvent('popup'),
page.locator('div[role="dialog"] >> text=API').click(),
])
await popup.close()

expect(await isMobileMenuOpen(page)).toBe(true)
})
})
52 changes: 52 additions & 0 deletions test/playwright/e2e/mobile-menu.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { test, expect } from '@playwright/test'

import {
closeFilters,
closeMobileMenu,
enableNewHeader,
goToSearchTerm,
isMobileMenuOpen,
openContentTypes,
openFilters,
} from '~~/test/playwright/utils/navigation'

const mockUaString =
'Mozilla/5.0 (Android 7.0; Mobile; rv:54.0) Gecko/54.0 Firefox/54.0'
const mobileFixture = {
viewport: { width: 640, height: 700 },
userAgent: mockUaString,
}

test.describe.configure({ mode: 'parallel' })

test.describe('Mobile menu', () => {
test.use(mobileFixture)
test.beforeEach(async ({ page }) => {
await enableNewHeader(page)
})

test('Can open filters menu on mobile at least twice', async ({ page }) => {
await page.goto('/search/?q=cat')

await openFilters(page)
expect(await isMobileMenuOpen(page)).toBe(true)
await closeFilters(page)

await openFilters(page)
expect(await isMobileMenuOpen(page)).toBe(true)
await closeFilters(page)
expect(await isMobileMenuOpen(page)).toBe(false)
})

test('Can open mobile menu at least twice', async ({ page }) => {
await goToSearchTerm(page, 'cat')
await openContentTypes(page)
expect(await isMobileMenuOpen(page)).toBe(true)
await closeMobileMenu(page)

await openContentTypes(page)
expect(await isMobileMenuOpen(page)).toBe(true)
await closeMobileMenu(page)
expect(await isMobileMenuOpen(page)).toBe(false)
})
})
Loading

0 comments on commit 6773c69

Please sign in to comment.