diff --git a/.github/workflows/check-visual.yml b/.github/workflows/check-visual.yml index 54007fa7b..512823b46 100644 --- a/.github/workflows/check-visual.yml +++ b/.github/workflows/check-visual.yml @@ -46,7 +46,7 @@ jobs: - name: Install Playwright browsers if: steps.playwright-dep-cache.outputs.cache-hit != 'true' - run: npx playwright install chromium + run: npx playwright install chromium webkit --with-deps - name: Install Radicle binaries run: | @@ -57,9 +57,9 @@ jobs: - name: Run Playwright tests run: | if [ ${{ github.ref }} = "refs/heads/master" ]; then - npm run test:e2e -- --project visual --update-snapshots || true; + npm run test:e2e -- --project visual-desktop visual-mobile --update-snapshots || true; else - npm run test:e2e -- --project visual; + npm run test:e2e -- --project visual-desktop visual-mobile; fi # We don't care about logs in visual snapshot tests, only the images. diff --git a/playwright.config.ts b/playwright.config.ts index 94b741f05..35e0bb9be 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -41,7 +41,7 @@ const config: PlaywrightTestConfig = { }, }, { - name: "visual", + name: "visual-desktop", timeout: 60_000, expect: { timeout: 30_000, @@ -51,13 +51,32 @@ const config: PlaywrightTestConfig = { animations: "disabled", }, }, - testDir: "./tests/visual", - snapshotDir: "./tests/visual/snapshots", + testDir: "./tests/visual/desktop", + snapshotDir: "./tests/visual/snapshots/desktop", retries: 0, use: { - ...devices["Desktop Chrome"], + ...devices["Desktop Chrome HiDPI"], + actionTimeout: 0, + trace: "off", + }, + }, + { + name: "visual-mobile", + timeout: 60_000, + expect: { + timeout: 30_000, + toHaveScreenshot: { + threshold: 0.01, + scale: "device", + animations: "disabled", + }, + }, + testDir: "./tests/visual/mobile", + snapshotDir: "./tests/visual/snapshots/mobile", + retries: 0, + use: { + ...devices["iPhone 13 Mini"], actionTimeout: 0, - deviceScaleFactor: 2, trace: "off", }, }, diff --git a/tests/support/fixtures.ts b/tests/support/fixtures.ts index 0e9468717..d5e383772 100644 --- a/tests/support/fixtures.ts +++ b/tests/support/fixtures.ts @@ -3,7 +3,7 @@ import type * as Stream from "node:stream"; import * as Fs from "node:fs/promises"; import * as Path from "node:path"; import assert from "node:assert"; -import { test as base, expect, type ViewportSize } from "@playwright/test"; +import { test as base, expect } from "@playwright/test"; import * as Process from "./process.js"; import * as issue from "@tests/support/cobs/issue.js"; @@ -18,13 +18,6 @@ export { expect }; const fixturesDir = Path.resolve(supportDir, "..", "./fixtures"); -type ViewportTypes = "iPhoneXR" | "Desktop"; - -export const viewportSizes: Record = { - iPhoneXR: { width: 414, height: 896 }, - Desktop: { width: 1280, height: 720 }, -}; - export const test = base.extend<{ // eslint-disable-next-line @typescript-eslint/no-invalid-void-type forAllTests: void; diff --git a/tests/visual/cob.spec.ts b/tests/visual/desktop/cob.spec.ts similarity index 100% rename from tests/visual/cob.spec.ts rename to tests/visual/desktop/cob.spec.ts diff --git a/tests/visual/landingPage.spec.ts b/tests/visual/desktop/landingPage.spec.ts similarity index 100% rename from tests/visual/landingPage.spec.ts rename to tests/visual/desktop/landingPage.spec.ts diff --git a/tests/visual/desktop/markdown.spec.ts b/tests/visual/desktop/markdown.spec.ts new file mode 100644 index 000000000..949f2d282 --- /dev/null +++ b/tests/visual/desktop/markdown.spec.ts @@ -0,0 +1,148 @@ +import type { Page } from "@playwright/test"; +import { test, expect, markdownUrl } from "@tests/support/fixtures.js"; + +async function goToSection(section: string, page: Page) { + await page.goto(`${markdownUrl}/tree/main/cheatsheet.md`, { + waitUntil: "networkidle", + }); + await page.getByText("Collapse").click(); + await page.locator(`[href="${section}"]`).click(); +} + +test.describe("markdown rendering basics", async () => { + test.use({ viewport: { width: 1600, height: 1200 } }); + + test("table of contents", async ({ page }) => { + await page.goto( + `${markdownUrl}/tree/main/cheatsheet.md#table-of-contents`, + { + waitUntil: "networkidle", + }, + ); + await page.getByText("Collapse").click(); + await expect(page.getByText("Table of Contents")).toBeVisible(); + await expect(page).toHaveScreenshot(); + }); + + test("headers", async ({ page }) => { + await goToSection("#headers", page); + await expect(page.getByText("###### H6")).toBeVisible(); + await expect(page).toHaveScreenshot(); + }); + + test("emphasis", async ({ page }) => { + await goToSection("#emphasis", page); + await expect(page.getByText("Emphasis, aka").first()).toBeVisible(); + await expect(page).toHaveScreenshot(); + }); + + test("lists", async ({ page }) => { + await goToSection("#lists", page); + await expect(page.getByText("First ordered list").first()).toBeVisible(); + await expect(page).toHaveScreenshot(); + }); + + test("links", async ({ page }) => { + await goToSection("#links", page); + await expect(page.getByText("There are two ways")).toBeVisible(); + await expect(page).toHaveScreenshot(); + }); + + test("images", async ({ page }) => { + await goToSection("#images", page); + await expect(page.getByText("Here's our logo").first()).toBeVisible(); + await expect(page).toHaveScreenshot(); + }); + + test("code and syntax highlighting", async ({ page }) => { + await goToSection("#code", page); + await expect(page.getByText("Code blocks are part")).toBeVisible(); + await expect(page).toHaveScreenshot(); + }); + + test("tables", async ({ page }) => { + await goToSection("#tables", page); + await expect(page.getByText("Tables aren't part of the")).toBeVisible(); + await expect(page).toHaveScreenshot(); + }); + + test("blockquotes", async ({ page }) => { + await goToSection("#blockquotes", page); + await expect(page.getByText("Blockquotes are").first()).toBeVisible(); + await expect(page).toHaveScreenshot(); + }); + + test("inline HTML", async ({ page }) => { + await goToSection("#html", page); + await expect(page.getByText("You can also use raw HTML")).toBeVisible(); + await expect(page).toHaveScreenshot(); + }); + + test("horizontal rule", async ({ page }) => { + await goToSection("#hr", page); + await expect(page.getByText("Three or more...").first()).toBeVisible(); + await expect(page).toHaveScreenshot(); + }); + + test("line breaks", async ({ page }) => { + await goToSection("#lines", page); + await expect(page.getByText("My basic recommendation")).toBeVisible(); + await expect(page).toHaveScreenshot(); + }); + + test("videos", async ({ page }) => { + await goToSection("#videos", page); + await expect(page.getByText("They can't be added")).toBeVisible(); + await expect(page).toHaveScreenshot(); + }); +}); + +test("footnotes", async ({ page }) => { + await page.goto(`${markdownUrl}/tree/main/footnotes.md#footnotes`, { + waitUntil: "networkidle", + }); + await expect( + page.locator( + "text=This is an example footnote[0]. And some radicle[1] examples.", + ), + ).toBeVisible(); + await page.getByText("Collapse").click(); + await expect(page.getByText("0. https://example.com ↩")).toBeVisible(); + await expect(page.getByText("1. https://radicle.xyz ↩")).toBeVisible(); + await expect(page).toHaveScreenshot({ fullPage: true }); + + await page.getByText("Plain").click(); + await expect( + page.locator( + "text=This is an example footnote[^0]. And some radicle[^1] examples.", + ), + ).toBeVisible(); + await expect(page.getByText("[^0]: https://example.com")).toBeVisible(); + await expect(page.getByText("[^1]: https://radicle.xyz")).toBeVisible(); +}); + +test("math", async ({ page }) => { + await page.goto(`${markdownUrl}/tree/main/math.md`, { + waitUntil: "networkidle", + }); + await expect(page.getByText("The Cauchy-Schwarz Inequality")).toBeVisible(); + await page.getByText("Collapse").click(); + await expect(page).toHaveScreenshot({ fullPage: true }); +}); + +test("relative image not able to being loaded", async ({ page }) => { + await page.goto(`${markdownUrl}/tree/main/loading-image.md`, { + waitUntil: "networkidle", + }); + await page.getByText("Collapse").click(); + await expect(page).toHaveScreenshot({ fullPage: true }); +}); + +test("markdown in issues is not overflowing", async ({ page }) => { + await page.goto(`${markdownUrl}/issues`, { + waitUntil: "networkidle", + }); + await page.getByText("Collapse").click(); + await page.getByRole("link", { name: "This title has markdown" }).click(); + await expect(page).toHaveScreenshot({ fullPage: true }); +}); diff --git a/tests/visual/node.spec.ts b/tests/visual/desktop/node.spec.ts similarity index 100% rename from tests/visual/node.spec.ts rename to tests/visual/desktop/node.spec.ts diff --git a/tests/visual/notFound.spec.ts b/tests/visual/desktop/notFound.spec.ts similarity index 100% rename from tests/visual/notFound.spec.ts rename to tests/visual/desktop/notFound.spec.ts diff --git a/tests/visual/project.spec.ts b/tests/visual/desktop/project.spec.ts similarity index 100% rename from tests/visual/project.spec.ts rename to tests/visual/desktop/project.spec.ts diff --git a/tests/visual/markdown.spec.ts b/tests/visual/markdown.spec.ts deleted file mode 100644 index c2fe52a56..000000000 --- a/tests/visual/markdown.spec.ts +++ /dev/null @@ -1,194 +0,0 @@ -import type { Page } from "@playwright/test"; -import { - test, - expect, - markdownUrl, - viewportSizes, -} from "@tests/support/fixtures.js"; - -async function goToSection(section: string, page: Page) { - await page.goto(`${markdownUrl}/tree/main/cheatsheet.md`, { - waitUntil: "networkidle", - }); - await page.locator(`[href="${section}"]`).click(); -} - -const viewportWidth = viewportSizes["Desktop"].width; - -test.describe("markdown rendering", async () => { - test.describe(async () => { - test.use({ viewport: { width: viewportWidth, height: 450 } }); - test("table of contents", async ({ page }) => { - await page.goto( - `${markdownUrl}/tree/main/cheatsheet.md#table-of-contents`, - { - waitUntil: "networkidle", - }, - ); - await expect(page.getByText("Table of Contents")).toBeVisible(); - await expect(page).toHaveScreenshot(); - }); - }); - - test.describe(async () => { - test.use({ - viewport: { width: viewportWidth, height: 1030 }, - }); - test("headers", async ({ page }) => { - await goToSection("#headers", page); - await expect(page.getByText("###### H6")).toBeVisible(); - await expect(page).toHaveScreenshot(); - }); - }); - - test.describe(async () => { - test.use({ viewport: { width: viewportWidth, height: 470 } }); - test("emphasis", async ({ page }) => { - await goToSection("#emphasis", page); - await expect(page.getByText("Emphasis, aka").first()).toBeVisible(); - await expect(page).toHaveScreenshot(); - }); - }); - - test.describe(async () => { - test.use({ viewport: { width: viewportWidth, height: 1100 } }); - test("lists", async ({ page }) => { - await goToSection("#lists", page); - await expect(page.getByText("First ordered list").first()).toBeVisible(); - await expect(page).toHaveScreenshot(); - }); - }); - - test.describe(async () => { - test.use({ viewport: { width: viewportWidth, height: 1024 } }); - test("links", async ({ page }) => { - await goToSection("#links", page); - await expect(page.getByText("There are two ways")).toBeVisible(); - await expect(page).toHaveScreenshot(); - }); - }); - - test.describe(async () => { - test.use({ viewport: { width: viewportWidth, height: 520 } }); - test("images", async ({ page }) => { - await goToSection("#images", page); - await expect(page.getByText("Here's our logo").first()).toBeVisible(); - await expect(page).toHaveScreenshot(); - }); - }); - - test.describe(async () => { - test.use({ viewport: { width: viewportWidth, height: 1130 } }); - test("code and syntax highlighting", async ({ page }) => { - await goToSection("#code", page); - await expect(page.getByText("Code blocks are part")).toBeVisible(); - await expect(page).toHaveScreenshot(); - }); - }); - - test.describe(async () => { - test("footnotes", async ({ page }) => { - await page.goto(`${markdownUrl}/tree/main/footnotes.md#footnotes`, { - waitUntil: "networkidle", - }); - await expect( - page.locator( - "text=This is an example footnote[0]. And some radicle[1] examples.", - ), - ).toBeVisible(); - await expect(page.getByText("0. https://example.com ↩")).toBeVisible(); - await expect(page.getByText("1. https://radicle.xyz ↩")).toBeVisible(); - await expect(page).toHaveScreenshot({ fullPage: true }); - - await page.getByText("Plain").click(); - await expect( - page.locator( - "text=This is an example footnote[^0]. And some radicle[^1] examples.", - ), - ).toBeVisible(); - await expect(page.getByText("[^0]: https://example.com")).toBeVisible(); - await expect(page.getByText("[^1]: https://radicle.xyz")).toBeVisible(); - }); - }); - - test.describe(async () => { - test("math", async ({ page }) => { - await page.goto(`${markdownUrl}/tree/main/math.md`, { - waitUntil: "networkidle", - }); - await expect( - page.getByText("The Cauchy-Schwarz Inequality"), - ).toBeVisible(); - await expect(page).toHaveScreenshot({ fullPage: true }); - }); - }); - - test.describe(async () => { - test.use({ viewport: { width: viewportWidth, height: 1100 } }); - test("tables", async ({ page }) => { - await goToSection("#tables", page); - await expect(page.getByText("Tables aren't part of the")).toBeVisible(); - await expect(page).toHaveScreenshot(); - }); - }); - - test.describe(async () => { - test.use({ viewport: { width: viewportWidth, height: 450 } }); - test("blockquotes", async ({ page }) => { - await goToSection("#blockquotes", page); - await expect(page.getByText("Blockquotes are").first()).toBeVisible(); - await expect(page).toHaveScreenshot(); - }); - }); - - test.describe(async () => { - test.use({ viewport: { width: viewportWidth, height: 510 } }); - test("inline HTML", async ({ page }) => { - await goToSection("#html", page); - await expect(page.getByText("You can also use raw HTML")).toBeVisible(); - await expect(page).toHaveScreenshot(); - }); - }); - - test.describe(async () => { - test.use({ viewport: { width: viewportWidth, height: 710 } }); - test("horizontal rule", async ({ page }) => { - await goToSection("#hr", page); - await expect(page.getByText("Three or more...").first()).toBeVisible(); - await expect(page).toHaveScreenshot(); - }); - }); - - test.describe(async () => { - test.use({ viewport: { width: viewportWidth, height: 625 } }); - test("line breaks", async ({ page }) => { - await goToSection("#lines", page); - await expect(page.getByText("My basic recommendation")).toBeVisible(); - await expect(page).toHaveScreenshot(); - }); - }); - - test.describe(async () => { - test.use({ viewport: { width: viewportWidth, height: 500 } }); - test("videos", async ({ page }) => { - await goToSection("#videos", page); - await expect(page.getByText("They can't be added")).toBeVisible(); - await expect(page).toHaveScreenshot(); - }); - }); -}); - -test("relative image not able to being loaded", async ({ page }) => { - await page.goto(`${markdownUrl}/tree/main/loading-image.md`, { - waitUntil: "networkidle", - }); - await expect(page).toHaveScreenshot({ fullPage: true }); -}); - -test("markdown in issues is not overflowing", async ({ page }) => { - await page.goto(`${markdownUrl}/issues`, { - waitUntil: "networkidle", - }); - await page.getByRole("link", { name: "This title has markdown" }).click(); - await expect(page).toHaveScreenshot({ fullPage: true }); -}); diff --git a/tests/visual/mobile/cob.spec.ts b/tests/visual/mobile/cob.spec.ts index a9e1b7bfe..ee0e643f1 100644 --- a/tests/visual/mobile/cob.spec.ts +++ b/tests/visual/mobile/cob.spec.ts @@ -1,11 +1,4 @@ -import { - test, - expect, - cobUrl, - viewportSizes, -} from "@tests/support/fixtures.js"; - -test.use({ viewport: viewportSizes["iPhoneXR"] }); +import { test, expect, cobUrl } from "@tests/support/fixtures.js"; test.beforeEach(async ({ page }) => { await page.addInitScript(() => { @@ -68,11 +61,12 @@ test("patch page", async ({ page }) => { ["This patch is going to be archived", "archived"], ["Let's add a README", "merged"], ["Add subtitle to README", "open"], - ["Taking another stab at the README", "open"], ]; for (const [name, state] of patches) { - await page.goto(`${cobUrl}/patches?state=${state}`); + await page.goto(`${cobUrl}/patches?state=${state}`, { + waitUntil: "networkidle", + }); await page.getByRole("link", { name }).click(); await page.getByRole("heading", { name }).waitFor(); await expect(page).toHaveScreenshot({ fullPage: true }); diff --git a/tests/visual/mobile/project.spec.ts b/tests/visual/mobile/project.spec.ts index 217b41828..4c83ca92a 100644 --- a/tests/visual/mobile/project.spec.ts +++ b/tests/visual/mobile/project.spec.ts @@ -3,11 +3,8 @@ import { expect, sourceBrowsingUrl, test, - viewportSizes, } from "@tests/support/fixtures.js"; -test.use({ viewport: viewportSizes["iPhoneXR"] }); - test("source tree page", async ({ page }) => { await page.goto(sourceBrowsingUrl, { waitUntil: "networkidle" }); await expect(page).toHaveScreenshot(); diff --git a/tests/visual/snapshots/.gitkeep b/tests/visual/snapshots/.gitkeep deleted file mode 100644 index e69de29bb..000000000