This repository has been archived by the owner on Feb 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
830 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') | ||
}) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
}) | ||
}) |
Oops, something went wrong.