Skip to content

Commit

Permalink
Improve Playwright navigation utilities (#1869)
Browse files Browse the repository at this point in the history
* Improve Playwright navigation utilities

* Use setBreakpointCookie

* Remove extra lines

* Lint
  • Loading branch information
obulat committed May 1, 2023
1 parent ec96c6f commit 1f2eef5
Show file tree
Hide file tree
Showing 17 changed files with 281 additions and 331 deletions.
7 changes: 4 additions & 3 deletions frontend/test/playwright/e2e/filters-sidebar-keyboard.spec.ts
Expand Up @@ -2,8 +2,9 @@ import { test, expect, Page } from "@playwright/test"

import {
LanguageDirection,
languageDirections,
pathWithDir,
setCookies,
setBreakpointCookie,
t,
} from "~~/test/playwright/utils/navigation"

Expand All @@ -28,10 +29,10 @@ const firstFilterCheckbox = (page: Page, dir: LanguageDirection) => {

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

for (const dir of ["ltr", "rtl"]) {
for (const dir of languageDirections) {
test.describe(`search header keyboard accessibility test in ${dir}`, () => {
test.beforeEach(async ({ page }) => {
await setCookies(page.context(), { uiBreakpoint: "lg" })
await setBreakpointCookie(page, "lg")
/**
* To simplify finding the last focusable element in the filters sidebar,
* we use the image search page. After the removal of the "searchBy" filter,
Expand Down
45 changes: 22 additions & 23 deletions frontend/test/playwright/e2e/filters.spec.ts
Expand Up @@ -2,11 +2,10 @@ import { test, expect, Page } from "@playwright/test"

import {
assertCheckboxStatus,
openFilters,
changeContentType,
changeSearchType,
goToSearchTerm,
closeFilters,
isPageDesktop,
filters,
} from "~~/test/playwright/utils/navigation"

import { mockProviderApis } from "~~/test/playwright/utils/route"
Expand Down Expand Up @@ -52,7 +51,7 @@ breakpoints.describeMobileAndDesktop(() => {
}) => {
await goToSearchTerm(page, "cat", { searchType })

await openFilters(page)
await filters.open(page)

await assertCheckboxCount(page, "total", FILTER_COUNTS[searchType])
})
Expand All @@ -62,7 +61,7 @@ breakpoints.describeMobileAndDesktop(() => {
await page.goto(
"/search/?q=cat&license_type=commercial&license=cc0&searchBy=creator"
)
await openFilters(page)
await filters.open(page)
// Creator filter was removed from the UI
const expectedFilters = ["cc0", "commercial"]

Expand All @@ -77,19 +76,19 @@ breakpoints.describeMobileAndDesktop(() => {
await page.goto(
"/search/?q=cat&license_type=commercial&license=cc0&searchBy=creator"
)
await openFilters(page)
await filters.open(page)
// Creator filter was removed from the UI
const expectedFilters = ["cc0", "commercial"]

for (const checkbox of expectedFilters) {
await assertCheckboxStatus(page, checkbox)
}
await changeContentType(page, "Images")
await changeSearchType(page, IMAGE)

await expect(page).toHaveURL(
"/search/image?q=cat&license_type=commercial&license=cc0&searchBy=creator"
)
await openFilters(page)
await filters.open(page)
for (const checkbox of expectedFilters) {
await assertCheckboxStatus(page, checkbox)
}
Expand All @@ -101,16 +100,16 @@ breakpoints.describeMobileAndDesktop(() => {
await page.goto(
"/search/image?q=cat&license_type=commercial&license=cc0&searchBy=creator"
)
await openFilters(page)
await filters.open(page)

// Creator filter was removed from the UI
for (const checkbox of ["cc0", "commercial"]) {
await assertCheckboxStatus(page, checkbox)
}

await changeContentType(page, "All content")
await changeSearchType(page, ALL_MEDIA)

await openFilters(page)
await filters.open(page)
await expect(page.locator('input[type="checkbox"]:checked')).toHaveCount(2)

await expect(page).toHaveURL(
Expand All @@ -122,22 +121,22 @@ breakpoints.describeMobileAndDesktop(() => {
page,
}) => {
await page.goto("/search/audio?q=cat&license_type=commercial")
await openFilters(page)
await filters.open(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, checkbox, "disabled")
}
await assertCheckboxStatus(page, "commercial")

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

await assertCheckboxStatus(page, "commercial", "", "unchecked")
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")
await assertCheckboxStatus(page, checkbox, "unchecked")
}
})

Expand All @@ -152,18 +151,18 @@ breakpoints.describeMobileAndDesktop(() => {
*/
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 filters.open(page)

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

await changeContentType(page, "Audio")
await openFilters(page)
await changeSearchType(page, AUDIO)
await filters.open(page)

// Only CC0 checkbox is checked, and the filter button label is
// '1 Filter' on `xl` or '1' on `lg` screens
await assertCheckboxStatus(page, "cc0")
await closeFilters(page)
await filters.close(page)
if (isPageDesktop(page)) {
const filterButtonText = await page
.locator('[aria-controls="filters"] span:visible')
Expand All @@ -184,9 +183,9 @@ breakpoints.describeMobileAndDesktop(() => {
page,
}) => {
await page.goto("/search/image?q=cat")
await openFilters(page)
await filters.open(page)

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

const [response] = await Promise.all([
page.waitForResponse((response) => response.url().includes("cc0")),
Expand All @@ -210,9 +209,9 @@ breakpoints.describeMobileAndDesktop(() => {
await page.goto(
`/search/${searchType}?q=birds&source=${source.toLowerCase()}`
)
await openFilters(page)
await filters.open(page)

await assertCheckboxStatus(page, source, "", "checked")
await assertCheckboxStatus(page, source, "checked")
})
}
})
41 changes: 24 additions & 17 deletions frontend/test/playwright/e2e/header-internal.spec.ts
@@ -1,22 +1,30 @@
import { test, expect, Page } from "@playwright/test"

import {
isMobileMenuOpen,
isDialogOpen,
LanguageDirection,
scrollToBottom,
setCookies,
setBreakpointCookie,
t,
} from "~~/test/playwright/utils/navigation"
import breakpoints from "~~/test/playwright/utils/breakpoints"

const modalCloseButton = `div[role="dialog"] >> [aria-label="${t(
"modal.close-pages-menu"
)}"]`
const currentPageLink = 'div[role="dialog"] >> [aria-current="page"]'
const currentPageLinkInPopover = '.popover-content >> [aria-current="page"]'
const menuButton = `[aria-label="${t("header.aria.menu")}"]`

const clickMenuButton = async (page: Page) => await page.click(menuButton)
const closeMenu = async (page: Page) => await page.click(modalCloseButton)
const getMenuButton = async (page: Page) => {
return page.getByRole("button", { name: t("header.aria.menu") })
}

const clickMenuButton = async (page: Page) => {
return (await getMenuButton(page)).click()
}

const closeMenu = async (page: Page, dir: LanguageDirection = "ltr") => {
await page
.getByRole("button", { name: t("modal.close-pages-menu", dir) })
.click()
}

const isPagesPopoverOpen = async (page: Page) =>
page.locator(".popover-content").isVisible({ timeout: 100 })
Expand All @@ -25,20 +33,20 @@ test.describe.configure({ mode: "parallel" })

test.describe("Header internal", () => {
breakpoints.describeXs(() => {
test.beforeEach(async ({ context }) => {
await setCookies(context, { uiBreakpoint: "xs" })
test.beforeEach(async ({ page }) => {
await setBreakpointCookie(page, "xs")
})

test("can open and close the modal on xs breakpoint", async ({ page }) => {
await page.goto("/about")
await clickMenuButton(page)
expect(await isMobileMenuOpen(page)).toBe(true)
expect(await isDialogOpen(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()
expect(await isDialogOpen(page)).toBe(false)
await expect(await getMenuButton(page)).toBeVisible()
})

test("the modal locks the scroll on xs breakpoint", async ({ page }) => {
Expand Down Expand Up @@ -67,7 +75,7 @@ test.describe("Header internal", () => {
await popup.close()
// If we want the modal to stay open, we'll need to change this to `true`,
// and implement the change
expect(await isMobileMenuOpen(page)).toBe(false)
expect(await isDialogOpen(page)).toBe(false)
})

test("content page opened from home should be scrollable", async ({
Expand Down Expand Up @@ -95,10 +103,9 @@ test.describe("Header internal", () => {

breakpoints.describeMd(() => {
test("can open and close the popover on sm breakpoint", async ({
context,
page,
}) => {
await setCookies(context, { breakpoint: "sm" })
await setBreakpointCookie(page, "sm")
await page.goto("/about")
await clickMenuButton(page)
expect(await isPagesPopoverOpen(page)).toBe(true)
Expand All @@ -107,7 +114,7 @@ test.describe("Header internal", () => {

await clickMenuButton(page)
expect(await isPagesPopoverOpen(page)).toBe(false)
await expect(page.locator(menuButton)).toBeVisible()
await expect(await getMenuButton(page)).toBeVisible()
})
})
})
10 changes: 3 additions & 7 deletions frontend/test/playwright/e2e/homepage.spec.ts
@@ -1,13 +1,9 @@
import { expect, Page, test } from "@playwright/test"

import { mockProviderApis } from "~~/test/playwright/utils/route"
import {
goToSearchTerm,
searchTypePath,
t,
} from "~~/test/playwright/utils/navigation"
import { goToSearchTerm, t } from "~~/test/playwright/utils/navigation"

import { supportedSearchTypes } from "~/constants/media"
import { searchPath, supportedSearchTypes } from "~/constants/media"

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

Expand All @@ -24,7 +20,7 @@ for (const searchType of supportedSearchTypes) {
mode: "CSR",
})

const expectedUrl = `/search/${searchTypePath(searchType)}?q=cat`
const expectedUrl = `${searchPath(searchType)}?q=cat`
await expect(page).toHaveURL(expectedUrl)
})
}
Expand Down
6 changes: 2 additions & 4 deletions frontend/test/playwright/e2e/load-more.spec.ts
Expand Up @@ -19,10 +19,8 @@ const openSingleMediaView = async (
) => {
const contentLinkSelector =
mediaType === IMAGE ? "See all images" : "See all audio"
return await Promise.all([
page.waitForNavigation(),
page.click(`text=${contentLinkSelector}`),
])
await page.click(`text=${contentLinkSelector}`)
await page.waitForURL(/search\/(audio|image)/)
}
/**
* Cases, check both SSR and CSR:
Expand Down
36 changes: 17 additions & 19 deletions frontend/test/playwright/e2e/mobile-menu.spec.ts
@@ -1,12 +1,10 @@
import { test, expect } from "@playwright/test"

import {
closeFilters,
closeMobileMenu,
goToSearchTerm,
isMobileMenuOpen,
openContentTypes,
openFilters,
isDialogOpen,
searchTypes,
filters,
} from "~~/test/playwright/utils/navigation"
import breakpoints from "~~/test/playwright/utils/breakpoints"

Expand All @@ -17,26 +15,26 @@ test.describe("mobile menu", () => {
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 filters.open(page)
expect(await isDialogOpen(page)).toBe(true)
await filters.close(page)

await openFilters(page)
expect(await isMobileMenuOpen(page)).toBe(true)
await closeFilters(page)
expect(await isMobileMenuOpen(page)).toBe(false)
await filters.open(page)
expect(await isDialogOpen(page)).toBe(true)
await filters.close(page)
expect(await isDialogOpen(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 searchTypes.open(page)
expect(await isDialogOpen(page)).toBe(true)
await searchTypes.close(page)

await openContentTypes(page)
expect(await isMobileMenuOpen(page)).toBe(true)
await closeMobileMenu(page)
expect(await isMobileMenuOpen(page)).toBe(false)
await searchTypes.open(page)
expect(await isDialogOpen(page)).toBe(true)
await searchTypes.close(page)
expect(await isDialogOpen(page)).toBe(false)
})
})
})
9 changes: 4 additions & 5 deletions frontend/test/playwright/e2e/search-navigation.spec.ts
Expand Up @@ -2,10 +2,10 @@ import { expect, test } from "@playwright/test"

import {
goToSearchTerm,
openFilters,
filters,
searchFromHeader,
t,
openFirstResult,
t,
} from "~~/test/playwright/utils/navigation"
import { mockProviderApis } from "~~/test/playwright/utils/route"
import breakpoints from "~~/test/playwright/utils/breakpoints"
Expand All @@ -23,19 +23,18 @@ test.describe("search history navigation", () => {
}) => {
await goToSearchTerm(page, "galah")
// Open filter sidebar
await openFilters(page)
await filters.open(page)

// Apply a filter
await page.click("#modification")
// There is a debounce when choosing a filter.
// we need to wait for the page to reload before running the test
await page.waitForNavigation()
await page.waitForURL(/license_type=modification/)

// Verify the filter is applied to the URL and the checkbox is checked
// Note: Need to add that a search was actually executed with the new
// filters and that the page results have been updated for the new filters
// @todo(sarayourfriend): ^?
expect(page.url()).toContain("license_type=modification")
expect(await page.isChecked("#modification")).toBe(true)

// Navigate backwards and verify URL is updated and the filter is unapplied
Expand Down

0 comments on commit 1f2eef5

Please sign in to comment.