Skip to content

Commit

Permalink
Add the ability to check two snapshots for each test
Browse files Browse the repository at this point in the history
  • Loading branch information
obulat committed Sep 13, 2024
1 parent c635aa1 commit 7d44ee8
Show file tree
Hide file tree
Showing 36 changed files with 358 additions and 119 deletions.
38 changes: 38 additions & 0 deletions frontend/.storybook/decorators/with-theme-switcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useDarkMode } from "#imports"

import { ref, watch, onMounted, h } from "vue"

import VThemeSelect from "~/components/VThemeSelect/VThemeSelect.vue"

export const WithThemeSwitcher = (story) => {
return {
components: { story },
setup() {
const element = ref()
const { cssClass } = useDarkMode()

onMounted(() => {
watch(cssClass, async (newClass, oldClass) => {
if (element.value) {
element.value.ownerDocument
.querySelector("#storybook-root")
.classList.add("bg-default")
element.value.ownerDocument.body.classList.add(newClass)
if (oldClass) {
element.value.ownerDocument.body.classList.remove(oldClass)
}
}
})
})
return () =>
h("div", { ref: element }, [
h(story()),
h(
"div",
{ class: "absolute bottom-0", id: "storybook-theme-switcher" },
[h(VThemeSelect)]
),
])
},
}
}
4 changes: 3 additions & 1 deletion frontend/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { VIEWPORTS } from "~/constants/screens"
import { WithUiStore } from "~~/.storybook/decorators/with-ui-store"
import { WithRTL } from "~~/.storybook/decorators/with-rtl"

import { WithThemeSwitcher } from "~~/.storybook/decorators/with-theme-switcher"

import type { Preview } from "@storybook/vue3"

const preview: Preview = {
decorators: [WithRTL, WithUiStore],
decorators: [WithRTL, WithUiStore, WithThemeSwitcher],
globalTypes: {
languageDirection: {
name: "RTL",
Expand Down
60 changes: 43 additions & 17 deletions frontend/test/playwright/utils/breakpoints.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { test, expect, Expect } from "@playwright/test"
import { test, expect, Expect, Page } from "@playwright/test"

import type { LanguageDirection } from "~~/test/playwright/utils/i18n"

import {
setThemeSwitcherVisibility,
turnOnDarkMode,
} from "~~/test/playwright/utils/theme-switcher"

import { VIEWPORTS } from "~/constants/screens"
import type { Breakpoint } from "~/constants/screens"
Expand All @@ -9,20 +16,25 @@ type ScreenshotAble = {
}

type ExpectSnapshot = <T extends ScreenshotAble>(
page: Page,
name: string,
s: T,
options?: Parameters<T["screenshot"]>[0],
snapshotOptions?: Parameters<ReturnType<Expect>["toMatchSnapshot"]>[0]
config?: {
dir?: LanguageDirection
options?: Parameters<T["screenshot"]>[0]
snapshotOptions?: Parameters<ReturnType<Expect>["toMatchSnapshot"]>[0]
}
) => Promise<Buffer | void>

type EffectiveColorMode = "dark" | "light"

type BreakpointBlock = (options: {
getConfigValues: (name: string) => {
name: `${typeof name}-${Breakpoint}-light.png`
}
breakpoint: Breakpoint
expectSnapshot: ExpectSnapshot
}) => void

const USE_DARK_MODE = false

export const desktopBreakpoints = ["2xl", "xl", "lg"] as const
export const mobileBreakpoints = ["md", "sm", "xs"] as const

Expand Down Expand Up @@ -87,24 +99,38 @@ const makeBreakpointDescribe =
userAgent: options.uaMocking ? mockUaStrings[breakpoint] : undefined,
})

const getConfigValues = (name: string) => ({
name: `${name}-${breakpoint}-light.png` as const,
})
const getSnapshotName = (
name: string,
colorMode: EffectiveColorMode = "light",
dir?: LanguageDirection
) => {
const dirString = dir ? `-${dir}` : ""
return `${name}${dirString}-${breakpoint}-${colorMode}.png` as const
}

const expectSnapshot = async <T extends ScreenshotAble>(
page: Page,
name: string,
screenshotAble: T,
options?: Parameters<T["screenshot"]>[0],
snapshotOptions?: Parameters<ReturnType<Expect>["toMatchSnapshot"]>[0]
{ dir, options, snapshotOptions }: Parameters<ExpectSnapshot>[3] = {}
) => {
const { name: snapshotName } = getConfigValues(name)
return expect(await screenshotAble.screenshot(options)).toMatchSnapshot(
snapshotName,
snapshotOptions
)
await setThemeSwitcherVisibility(page, "hidden")
expect
.soft(await screenshotAble.screenshot(options))
.toMatchSnapshot(getSnapshotName(name, "light", dir), snapshotOptions)

if (!USE_DARK_MODE) {
return
}

await turnOnDarkMode(page, dir ?? "ltr")

expect
.soft(await screenshotAble.screenshot(options))
.toMatchSnapshot(getSnapshotName(name, "dark", dir), snapshotOptions)
}

_block({ breakpoint, getConfigValues, expectSnapshot })
_block({ breakpoint, expectSnapshot })
})
}

Expand Down
29 changes: 29 additions & 0 deletions frontend/test/playwright/utils/theme-switcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Page } from "@playwright/test"

import { LanguageDirection, t } from "~~/test/playwright/utils/i18n"

type EffectiveColorMode = "dark" | "light"
const themeSelectLabel = (dir: LanguageDirection) => t("theme.theme", dir)
const themeOption = (colorMode: EffectiveColorMode, dir: LanguageDirection) =>
t(`theme.choices.${colorMode}`, dir)

export const setThemeSwitcherVisibility = async (
page: Page,
visibility: "visible" | "hidden"
) => {
await page.evaluate(
([visibility]) =>
(
document.querySelector("#storybook-theme-switcher") as HTMLElement
)?.style.setProperty("visibility", visibility),
[visibility]
)
}

export const turnOnDarkMode = async (page: Page, dir: LanguageDirection) => {
await setThemeSwitcherVisibility(page, "visible")
await page
.getByLabel(themeSelectLabel(dir))
.selectOption(themeOption("dark", dir))
await setThemeSwitcherVisibility(page, "hidden")
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ test.describe("content report form", () => {

await button.click()

await expectSnapshot("content-report", page, undefined, {
maxDiffPixelRatio: 0.1,
await expectSnapshot(page, "content-report", page, {
snapshotOptions: { maxDiffPixelRatio: 0.1 },
})
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,16 @@ for (const dir of languageDirections) {
await page.mouse.move(0, 0)

await expectSnapshot(
`external-${mediaType}-sources-popover-${dir}`,
page,
`external-${mediaType}-sources-popover`,
page.getByRole("dialog"),
{},
{ maxDiffPixelRatio: 0.01, maxDiffPixels: undefined }
{
dir,
snapshotOptions: {
maxDiffPixelRatio: 0.01,
maxDiffPixels: undefined,
},
}
)
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,32 @@ for (const dir of languageDirections) {
await filters.open(page, dir)
})
test(`filters modal none selected - ${dir}`, async ({ page }) => {
const snapshotName = `${getFiltersName(breakpoint)}-${dir}`

await expectSnapshot(
snapshotName,
page,
getFiltersName(breakpoint),
isDesktop ? page.locator(".sidebar") : page,
{},
{ maxDiffPixels: 2, maxDiffPixelRatio: undefined }
{
dir,
snapshotOptions: { maxDiffPixels: 2, maxDiffPixelRatio: undefined },
}
)
})

test(`filters modal 1 filter selected - ${dir}`, async ({ page }) => {
const firstFilter = page.getByRole("checkbox").first()
await firstFilter.check()

const snapshotName = `${getFiltersName(breakpoint)}-checked-${dir}`
const snapshotName = `${getFiltersName(breakpoint)}-checked`

await firstFilter.hover()
await expectSnapshot(
page,
snapshotName,
isDesktop ? page.locator(".sidebar") : page,
{},
{ maxDiffPixels: 2, maxDiffPixelRatio: undefined }
{
dir,
snapshotOptions: { maxDiffPixels: 2, maxDiffPixelRatio: undefined },
}
)
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ for (const dir of languageDirections) {
// To make the tests consistent, set the played area to the same position
await page.mouse.click(170, 650)

await expectSnapshot(`global-audio-player-on-search-${dir}`, page)
await expectSnapshot(page, "global-audio-player-on-search", page, { dir })
})
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ for (const dir of languageDirections) {
test("filters open", async ({ page }) => {
await page.mouse.move(0, 150)
await expectSnapshot(
`filters-open-${dir}`,
page.locator(headerSelector)
page,
"filters-open",
page.locator(headerSelector),
{ dir }
)
})

Expand All @@ -45,22 +47,28 @@ for (const dir of languageDirections) {
test("resting", async ({ page }) => {
// Make sure the header is not hovered on
await page.mouse.move(0, 150)
await expectSnapshot(`resting-${dir}`, page.locator(headerSelector))
await expectSnapshot(page, "resting", page.locator(headerSelector), {
dir,
})
})

test("scrolled", async ({ page }) => {
await scrollToBottom(page)
await page.mouse.move(0, 150)
await sleep(200)
await expectSnapshot(`scrolled-${dir}`, page.locator(headerSelector))
await expectSnapshot(page, "scrolled", page.locator(headerSelector), {
dir,
})
})

test("searchbar hovered", async ({ page }) => {
await page.hover("input")
await hideInputCursors(page)
await expectSnapshot(
`searchbar-hovered-${dir}`,
page.locator(headerSelector)
page,
"searchbar-hovered",
page.locator(headerSelector),
{ dir }
)
})

Expand All @@ -74,7 +82,7 @@ for (const dir of languageDirections) {
const locator = isMobileBreakpoint(breakpoint)
? page
: page.locator(headerSelector)
await expectSnapshot(`searchbar-active-${dir}`, locator)
await expectSnapshot(page, "searchbar-active", locator, { dir })
})
})
})
Expand Down
31 changes: 21 additions & 10 deletions frontend/test/playwright/visual-regression/pages/errors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ breakpoints.describeXl(({ breakpoint, expectSnapshot }) => {
await page.goto(`/image/${imageId}`)
// eslint-disable-next-line playwright/no-networkidle
await page.waitForLoadState("networkidle")
await expectSnapshot("generic-error", page, { fullPage: true })
await expectSnapshot(page, "generic-error", page, {
options: { fullPage: true },
})
})
}

Expand All @@ -57,7 +59,9 @@ breakpoints.describeXl(({ breakpoint, expectSnapshot }) => {
// eslint-disable-next-line playwright/no-networkidle
await page.waitForLoadState("networkidle")

await expectSnapshot("generic-error", page, { fullPage: true })
await expectSnapshot(page, "generic-error", page, {
options: { fullPage: true },
})
})
}
})
Expand All @@ -76,8 +80,8 @@ for (const searchType of supportedSearchTypes) {
await preparePageForTests(page, breakpoint)
await goToSearchTerm(page, `SearchPage500error`, { searchType })

await expectSnapshot("generic-error", page, {
fullPage: true,
await expectSnapshot(page, "generic-error", page, {
options: { fullPage: true },
})
})
})
Expand All @@ -100,7 +104,12 @@ for (const searchType of supportedSearchTypes) {
searchType,
})

await expectSnapshot("generic-error", page, { fullPage: true })
await expectSnapshot(page, "generic-error", page, {
dir,
options: {
fullPage: true,
},
})
})
}

Expand All @@ -115,10 +124,12 @@ for (const searchType of supportedSearchTypes) {
await page.mouse.move(0, 82)

await expectSnapshot(
page,
`search-result-${
searchType === ALL_MEDIA ? "image" : searchType
}-no-results-${dir}`,
page.locator("#main-page")
}-no-results`,
page.locator("#main-page"),
{ dir }
)
})

Expand All @@ -133,11 +144,11 @@ for (const searchType of supportedSearchTypes) {
await page.mouse.move(0, 82)

await expectSnapshot(
`search-result-timeout-${dir}`,
page,
"search-result-timeout",
page.locator("#main-page"),
{},
// Timeout pages attribution has icons that don't always load from CC site
{ maxDiffPixelRatio: 0.01 }
{ dir, snapshotOptions: { maxDiffPixelRatio: 0.01 } }
)
})
}
Expand Down
Loading

0 comments on commit 7d44ee8

Please sign in to comment.