From 107696c3c93679b10facf956e6e99fe2669fbc2e Mon Sep 17 00:00:00 2001 From: Sebastian Martinez Date: Mon, 5 Jun 2023 15:28:54 +0200 Subject: [PATCH] Add a cobs fixture and issue and patch tests Signed-off-by: Sebastian Martinez --- src/views/projects/Cob/Revision.svelte | 9 +- tests/e2e/project.spec.ts | 13 +- tests/e2e/project/commits.spec.ts | 4 +- tests/e2e/project/issues.spec.ts | 19 +++ tests/e2e/project/patches.spec.ts | 98 ++++++++++++++ tests/support/cobs/issue.ts | 26 ++++ tests/support/cobs/patch.ts | 53 ++++++++ tests/support/fixtures.ts | 169 +++++++++++++++++++++++++ tests/support/globalSetup.ts | 3 + tests/support/heartwood-version | 2 +- tests/support/peerManager.ts | 4 +- tests/support/project.ts | 4 +- tests/support/support.ts | 9 ++ tests/visual/cob.spec.ts | 94 ++++++++++++++ 14 files changed, 489 insertions(+), 18 deletions(-) create mode 100644 tests/e2e/project/issues.spec.ts create mode 100644 tests/e2e/project/patches.spec.ts create mode 100644 tests/support/cobs/issue.ts create mode 100644 tests/support/cobs/patch.ts create mode 100644 tests/visual/cob.spec.ts diff --git a/src/views/projects/Cob/Revision.svelte b/src/views/projects/Cob/Revision.svelte index 7b3db7ee4..6a8b5ed8f 100644 --- a/src/views/projects/Cob/Revision.svelte +++ b/src/views/projects/Cob/Revision.svelte @@ -18,9 +18,9 @@ import Floating from "@app/components/Floating.svelte"; import Icon from "@app/components/Icon.svelte"; import InlineMarkdown from "@app/components/InlineMarkdown.svelte"; - import ProjectLink from "@app/components/ProjectLink.svelte"; - import ThreadComponent from "@app/components/Thread.svelte"; import Markdown from "@app/components/Markdown.svelte"; + import ProjectLink from "@app/components/ProjectLink.svelte"; + import Thread from "@app/components/Thread.svelte"; export let authorId: string; export let authorAlias: string | undefined = undefined; @@ -208,6 +208,9 @@ {/if} {#if previousRevOid} @@ -254,7 +257,7 @@ {#each timelines as element} {#if element.type === "thread"}
- diff --git a/tests/e2e/project.spec.ts b/tests/e2e/project.spec.ts index 76fb35f3e..f4ca55c79 100644 --- a/tests/e2e/project.spec.ts +++ b/tests/e2e/project.spec.ts @@ -183,8 +183,7 @@ test("files with special characters in the filename", async ({ page }) => { await sourceTree.getByText("_underscore_").click(); await expect(page.locator(".file-name")).toContainText("_underscore_"); - // TODO: fix these errors in `racdicle-client-services/http-api` for the - // following edge cases. + // TODO: fix these errors in `radicle-httpd` for the following edge cases. // // await sourceTree.getByText("back\\slash").click(); // await expect(page.locator(".file-name")).toContainText("back\\slash"); @@ -268,9 +267,9 @@ test("peer and branch switching", async ({ page }) => { await page.getByTitle("Change peer").click(); await page.locator(`text=${aliceRemote}`).click(); await expect(page.getByTitle("Change peer")).toHaveText( - ` did:key:${aliceRemote - .substring(8) - .substring(0, 6)}…${aliceRemote.slice(-6)} delegate`, + `did:key:${aliceRemote.substring(8).substring(0, 6)}…${aliceRemote.slice( + -6, + )} delegate`, ); await expect( page.locator( @@ -333,9 +332,9 @@ test("peer and branch switching", async ({ page }) => { await page.getByTitle("Change peer").click(); await page.locator(`text=${bobRemote}`).click(); await expect(page.getByTitle("Change peer")).toHaveText( - ` did:key:${bobRemote.substring(8).substring(0, 6)}…${bobRemote.slice( + `did:key:${bobRemote.substring(8).substring(0, 6)}…${bobRemote.slice( -6, - )} `, + )}`, ); await expect(page.getByTitle("Change peer")).not.toHaveText("delegate"); diff --git a/tests/e2e/project/commits.spec.ts b/tests/e2e/project/commits.spec.ts index 3dbe40a91..6904dc281 100644 --- a/tests/e2e/project/commits.spec.ts +++ b/tests/e2e/project/commits.spec.ts @@ -104,9 +104,7 @@ test("relative timestamps", async ({ page }) => { await page.getByTitle("Change peer").click(); await page.locator(`text=${bobRemote}`).click(); await expect(page.getByTitle("Change peer")).toHaveText( - ` did:key:${bobRemote.substring(8).substring(0, 6)}…${bobRemote.slice( - -6, - )} `, + `did:key:${bobRemote.substring(8).substring(0, 6)}…${bobRemote.slice(-6)}`, ); const latestCommit = page.locator(".teaser").first(); await expect(latestCommit).toContainText("Bob Belcher committed now"); diff --git a/tests/e2e/project/issues.spec.ts b/tests/e2e/project/issues.spec.ts new file mode 100644 index 000000000..001cee618 --- /dev/null +++ b/tests/e2e/project/issues.spec.ts @@ -0,0 +1,19 @@ +import { test, cobUrl, expect } from "@tests/support/fixtures.js"; + +test("navigate issue listing", async ({ page }) => { + await page.goto(cobUrl); + await page.locator('role=link[name="1 issue"]').click(); + await expect(page).toHaveURL(`${cobUrl}/issues`); + + await page.locator('role=link[name="2 closed"]').click(); + await expect(page).toHaveURL(`${cobUrl}/issues?state=closed`); +}); + +test("navigate single issue", async ({ page }) => { + await page.goto(`${cobUrl}/issues`); + await page.locator("text=This title has markdown").click(); + + await expect(page).toHaveURL( + `${cobUrl}/issues/4fc727e722d3979fd2073d9b56b2751658a4ae79`, + ); +}); diff --git a/tests/e2e/project/patches.spec.ts b/tests/e2e/project/patches.spec.ts new file mode 100644 index 000000000..80109d6b1 --- /dev/null +++ b/tests/e2e/project/patches.spec.ts @@ -0,0 +1,98 @@ +import { test, cobUrl, expect } from "@tests/support/fixtures.js"; + +test("navigate listing", async ({ page }) => { + await page.goto(cobUrl); + await page.locator('role=link[name="2 patches"]').click(); + await expect(page).toHaveURL(`${cobUrl}/patches`); + + await page.locator('role=link[name="1 merged"]').click(); + await expect(page).toHaveURL(`${cobUrl}/patches?state=merged`); +}); + +test("navigate patch details", async ({ page }) => { + await page.goto(`${cobUrl}/patches`); + await page.locator("text=Add subtitle to README").click(); + await expect(page).toHaveURL( + `${cobUrl}/patches/013f8b2734df1840b2e33d52ff5632c8d66b199a`, + ); + await page.locator("role=link[name='Add subtitle to README']").click(); + await expect(page).toHaveURL( + `${cobUrl}/commits/8c900d6cb38811e099efb3cbbdbfaba817bcf970`, + ); + await page.goBack(); + { + await page.locator("role=link[name='Commits']").click(); + await expect(page).toHaveURL( + `${cobUrl}/patches/013f8b2734df1840b2e33d52ff5632c8d66b199a?tab=commits`, + ); + } + { + await page.locator("role=link[name='Files']").click(); + await expect(page).toHaveURL( + `${cobUrl}/patches/013f8b2734df1840b2e33d52ff5632c8d66b199a?tab=files`, + ); + } +}); + +test("use revision selector", async ({ page }) => { + await page.goto(`${cobUrl}/patches/0f3697fed2743549e3bf531e9fa81284a6de1466`); + await page.locator("role=link[name='Files']").click(); + await page.locator("text='Revision febcbbd'").click(); + await page.locator("role=link[name='Revision febcbbd']").click(); + + await expect(page).toHaveURL( + `${cobUrl}/patches/0f3697fed2743549e3bf531e9fa81284a6de1466/febcbbd60c4977ac6a985f7589ec65bbae7f2d60?tab=files`, + ); +}); + +test("navigate through revision diffs", async ({ page }) => { + await page.goto(`${cobUrl}/patches/0f3697fed2743549e3bf531e9fa81284a6de1466`); + + const firstRevision = page.locator(".revision").first(); + const secondRevision = page.locator(".revision").nth(1); + + // Second revision + { + await secondRevision.locator(".toggle").click(); + await secondRevision + .locator("role=link[name='Compare to main (38c225e)']") + .click(); + await expect( + page.locator("role=link[name='Diff 38c225..5b35de']"), + ).toBeVisible(); + await expect(page).toHaveURL( + `${cobUrl}/patches/0f3697fed2743549e3bf531e9fa81284a6de1466?diff=38c225e2a0b47ba59def211f4e4825c31d9463ec..5b35def19c2c7f0c0f1b3fd12d0a7c5930ef6b09`, + ); + await page.goBack(); + await secondRevision.locator(".toggle").click(); + await secondRevision + .locator("role=link[name='Compare to previous revision (0f3697f)']") + .click(); + await expect( + page.locator("role=link[name='Diff 0dc373..5b35de']"), + ).toBeVisible(); + + await expect(page).toHaveURL( + `${cobUrl}/patches/0f3697fed2743549e3bf531e9fa81284a6de1466?diff=0dc373db601ccbcffa80dec932e4006516709ca6..5b35def19c2c7f0c0f1b3fd12d0a7c5930ef6b09`, + ); + await page.goBack(); + + await secondRevision + .locator("role=link[name='Compare 0dc373d..5b35def']") + .click(); + await page.goBack(); + } + // First revision + { + await firstRevision.locator(".toggle").click(); + await firstRevision + .locator("role=link[name='Compare to main (38c225e)']") + .click(); + await expect( + page.locator("role=link[name='Diff 38c225..0dc373']"), + ).toBeVisible(); + await expect(page).toHaveURL( + `${cobUrl}/patches/0f3697fed2743549e3bf531e9fa81284a6de1466?diff=38c225e2a0b47ba59def211f4e4825c31d9463ec..0dc373db601ccbcffa80dec932e4006516709ca6`, + ); + } +}); diff --git a/tests/support/cobs/issue.ts b/tests/support/cobs/issue.ts new file mode 100644 index 000000000..824e642ea --- /dev/null +++ b/tests/support/cobs/issue.ts @@ -0,0 +1,26 @@ +import type { RadiclePeer } from "@tests/support/peerManager.js"; +import type { Options } from "execa"; + +export async function create( + peer: RadiclePeer, + title: string, + description: string, + tags: string[], + options: Options, +): Promise { + const issueOptions: string[] = [ + "issue", + "open", + "--title", + title, + "--description", + description, + ...tags.map(tag => ["--tag", tag]).flat(), + ]; + const { stdout } = await peer.rad(issueOptions, options); + const match = stdout.match(/Issue {3}([a-zA-Z0-9]*)/); + if (!match) { + throw new Error("Not able to parse issue id"); + } + return match[1]; +} diff --git a/tests/support/cobs/patch.ts b/tests/support/cobs/patch.ts new file mode 100644 index 000000000..006c8fc78 --- /dev/null +++ b/tests/support/cobs/patch.ts @@ -0,0 +1,53 @@ +import type { RadiclePeer } from "@tests/support/peerManager.js"; +import type { Options } from "execa"; + +export async function create( + peer: RadiclePeer, + commit: string, + branch: string, + changeFn: () => Promise, + messages: string[], + options: Options, +): Promise { + if (branch) { + await peer.git(["reset", "--hard"], options); + await peer.git(["switch", "main"], options); + await peer.git(["switch", "-c", branch], options); + } + await changeFn(); + await peer.git(["add", "."], options); + await peer.git(["commit", "-m", commit], options); + const cmd = [ + "push", + ...messages.map(msg => ["-o", `patch.message=${msg}`]).flat(), + "rad", + "HEAD:refs/patches", + ]; + const { stderr } = await peer.git(cmd, options); + const match = stderr.match(/✓ Patch ([a-zA-Z0-9]*) opened/); + if (!match) { + throw new Error("Not able to parse patch id"); + } + return match[1]; +} + +export async function merge( + peer: RadiclePeer, + targetBranch: string, + featureBranch: string, + options: Options, +): Promise { + await peer.git(["switch", targetBranch], options); + await peer.git(["merge", featureBranch], options); + await peer.git(["push", "rad", targetBranch], options); +} + +export async function update( + peer: RadiclePeer, + featureBranch: string, + message: string, + options: Options, +): Promise { + await peer.git(["commit", "--allow-empty", "-m", message], options); + await peer.git(["push", "rad", featureBranch], options); +} diff --git a/tests/support/fixtures.ts b/tests/support/fixtures.ts index 5af01735a..e30a0dc85 100644 --- a/tests/support/fixtures.ts +++ b/tests/support/fixtures.ts @@ -10,7 +10,10 @@ import { fileURLToPath } from "node:url"; import { test as base, expect } from "@playwright/test"; import * as Process from "./process.js"; +import * as issue from "@tests/support/cobs/issue.js"; import * as logLabel from "@tests/support/logLabel.js"; +import * as patch from "@tests/support/cobs/patch.js"; +import { createOptions } from "@tests/support/support.js"; import { createPeerManager } from "@tests/support/peerManager.js"; import { createProject } from "@tests/support/project.js"; @@ -297,6 +300,170 @@ export async function createSourceBrowsingFixture( ); } +export async function createCobsFixture(peer: RadiclePeer) { + await peer.rad(["track", peer.nodeId, "--alias", "palm"]); + await Fs.mkdir(Path.join(tmpDir, "repos", "cobs")); + const { projectFolder, defaultBranch } = await createProject(peer, "cobs"); + const issueOne = await issue.create( + peer, + "This `title` has **markdown**", + "This is a description\nWith some multiline text.", + ["bug", "feature-request"], + { cwd: projectFolder }, + ); + await peer.rad( + ["assign", issueOne, "--to", `did:key:${peer.nodeId}`], + createOptions(projectFolder, 1), + ); + await peer.rad( + [ + "comment", + issueOne, + "--message", + "This is a multiline comment\n\nWith some more text.", + ], + createOptions(projectFolder, 2), + ); + await peer.rad( + [ + "comment", + issueOne, + "--message", + "This is a reply, to a first comment.", + "--reply-to", + "a299a997301eae6528cb9f6fbdb8fac2cb4c3df0", + ], + createOptions(projectFolder, 3), + ); + + const issueTwo = await issue.create( + peer, + "A closed issue", + "This issue has been closed\n\nsource: [link](https://radicle.xyz)", + [], + { cwd: projectFolder }, + ); + await peer.rad( + ["issue", "state", issueTwo, "--closed"], + createOptions(projectFolder, 1), + ); + + const issueThree = await issue.create( + peer, + "A solved issue", + "This issue has been solved\n\n```js\nconsole.log('hello world')\nconsole.log(\"\")\n```", + [], + { cwd: projectFolder }, + ); + await peer.rad( + ["issue", "state", issueThree, "--solved"], + createOptions(projectFolder, 1), + ); + + const patchOne = await patch.create( + peer, + "Add README\n\nThis commit adds more information to the README", + "feature/add-readme", + () => Fs.writeFile(Path.join(projectFolder, "README.md"), "# Cobs Repo"), + ["Let's add a README", "This repo needed a README"], + { cwd: projectFolder }, + ); + await peer.rad( + ["comment", patchOne, "--message", "I'll review the patch"], + createOptions(projectFolder, 1), + ); + await peer.rad( + [ + "comment", + patchOne, + "--message", + "Thanks for that!", + "--reply-to", + "fa9e1d3d0d064449a2415072fe2d4eef28a2f603", + ], + createOptions(projectFolder, 2), + ); + await peer.rad( + ["review", patchOne, "-m", "LGTM", "--accept"], + createOptions(projectFolder, 3), + ); + await patch.merge( + peer, + defaultBranch, + "feature/add-readme", + createOptions(projectFolder, 4), + ); + + const patchTwo = await patch.create( + peer, + "Add subtitle to README", + "feature/add-more-text", + () => + Fs.appendFile(Path.join(projectFolder, "README.md"), "\n\n## Subtitle"), + [], + { cwd: projectFolder }, + ); + await peer.rad( + ["review", patchTwo, "-m", "Not the README we are looking for", "--reject"], + createOptions(projectFolder, 1), + ); + + const patchThree = await patch.create( + peer, + "Rewrite subtitle to README", + "feature/better-subtitle", + () => + Fs.appendFile(Path.join(projectFolder, "README.md"), "\n\n## Better?"), + [ + "Taking another stab at the README", + "This is a big improvement over the last one", + "Hopefully **this** is the last time", + ], + { cwd: projectFolder }, + ); + await peer.rad( + ["tag", patchThree, "documentation"], + createOptions(projectFolder, 1), + ); + await peer.rad( + ["review", patchThree, "-m", "This looks better"], + createOptions(projectFolder, 2), + ); + await patch.update( + peer, + "feature/better-subtitle", + "Some minor rebase", + createOptions(projectFolder, 3), + ); + + const patchFour = await patch.create( + peer, + "This patch is going to be archived", + "feature/archived", + () => + Fs.writeFile(Path.join(projectFolder, "CONTRIBUTING.md"), "# Archived"), + [], + { cwd: projectFolder }, + ); + await peer.rad( + ["patch", "archive", patchFour], + createOptions(projectFolder, 1), + ); + + const patchFive = await patch.create( + peer, + "This patch is going to be reverted to draft", + "feature/draft", + () => Fs.writeFile(Path.join(projectFolder, "LICENSE"), "Draft"), + [], + { cwd: projectFolder }, + ); + await peer.rad( + ["patch", "ready", patchFive, "--undo"], + createOptions(projectFolder, 1), + ); +} + export async function createMarkdownFixture(peer: RadiclePeer) { await Fs.mkdir(Path.join(tmpDir, "repos", "markdown")); await Process.spawn("tar", [ @@ -330,8 +497,10 @@ export const bobRemote = "did:key:z6Mkg49NtQR2LyYRDCQFK4w1VVHqhypZSSRo7HsyuN7SV7v5"; export const bobHead = "28f37105bb78db48111e36281291ff253dd050e8"; export const sourceBrowsingRid = "rad:z4BwwjPCFNVP27FwVbDFgwVwkjcir"; +export const cobRid = "rad:z3fpY7nttPPa6MBnAv2DccHzQJnqe"; export const markdownRid = "rad:z2tchH2Ti4LxRKdssPQYs6VHE5rsg"; export const sourceBrowsingUrl = `/seeds/127.0.0.1/${sourceBrowsingRid}`; +export const cobUrl = `/seeds/127.0.0.1/${cobRid}`; export const markdownUrl = `/seeds/127.0.0.1/${markdownRid}`; export const seedPort = 8080; export const seedRemote = "z6MktULudTtAsAhRegYPiZ6631RV3viv12qd4GQF8z1xB22S"; diff --git a/tests/support/globalSetup.ts b/tests/support/globalSetup.ts index 599d5a780..6bd51eb24 100644 --- a/tests/support/globalSetup.ts +++ b/tests/support/globalSetup.ts @@ -7,6 +7,7 @@ import * as readline from "node:readline/promises"; import { execa } from "execa"; import { + createCobsFixture, createMarkdownFixture, createSourceBrowsingFixture, gitOptions, @@ -58,6 +59,8 @@ export default async function globalSetup(_config: FullConfig): Promise { await createSourceBrowsingFixture(peerManager, palm); console.log("Creating markdown fixture"); await createMarkdownFixture(palm); + console.log("Creating cobs fixture"); + await createCobsFixture(palm); console.log("Running tests"); } else { await startPalmHttpd(); diff --git a/tests/support/heartwood-version b/tests/support/heartwood-version index ce545a929..16a33dcc7 100644 --- a/tests/support/heartwood-version +++ b/tests/support/heartwood-version @@ -1 +1 @@ -fd91cf4d28bd370a6c5beb9ca158e4d91432df25 +6bbdc574f0d298a178e9f06f07652af290a5eaeb diff --git a/tests/support/peerManager.ts b/tests/support/peerManager.ts index be1b73d16..bf8a6810f 100644 --- a/tests/support/peerManager.ts +++ b/tests/support/peerManager.ts @@ -311,8 +311,6 @@ export class RadiclePeer { opts = { ...opts, env: { - ...opts?.env, - ...this.#gitOptions, GIT_CONFIG_GLOBAL: "/dev/null", GIT_CONFIG_NOSYSTEM: "1", RAD_HOME: this.#radHome, @@ -320,6 +318,8 @@ export class RadiclePeer { RAD_COMMIT_TIME: "1671125284", RAD_SEED: this.#seed, RAD_SOCKET: this.#socket, + ...opts?.env, + ...this.#gitOptions, }, }; const childProcess = Process.spawn(cmd, args, opts); diff --git a/tests/support/project.ts b/tests/support/project.ts index 19fed3752..446d77d18 100644 --- a/tests/support/project.ts +++ b/tests/support/project.ts @@ -8,7 +8,7 @@ export async function createProject( name: string, description = "", defaultBranch = "main", -): Promise<{ rid: string; projectFolder: string }> { +): Promise<{ rid: string; projectFolder: string; defaultBranch: string }> { const projectFolder = Path.join(peer.checkoutPath, name); await peer.git(["init", name, "--initial-branch", defaultBranch], { @@ -37,5 +37,5 @@ export async function createProject( cwd: projectFolder, }); - return { rid, projectFolder }; + return { rid, projectFolder, defaultBranch }; } diff --git a/tests/support/support.ts b/tests/support/support.ts index 2128754a1..67d017795 100644 --- a/tests/support/support.ts +++ b/tests/support/support.ts @@ -1,6 +1,15 @@ +import type { Options } from "execa"; import * as Crypto from "node:crypto"; // Generate string of 12 random characters with 8 bits of entropy. export function randomTag(): string { return Crypto.randomBytes(8).toString("hex"); } + +export function createOptions(projectFolder: string, days: number): Options { + return { + cwd: projectFolder, + // eslint-disable-next-line @typescript-eslint/naming-convention + env: { RAD_COMMIT_TIME: (1671211684 + days * 86400).toString() }, + }; +} diff --git a/tests/visual/cob.spec.ts b/tests/visual/cob.spec.ts new file mode 100644 index 000000000..e93db3cfd --- /dev/null +++ b/tests/visual/cob.spec.ts @@ -0,0 +1,94 @@ +import { test, expect, cobUrl } from "@tests/support/fixtures.js"; + +test.beforeEach(async ({ page }) => { + await page.addInitScript(() => { + window.initializeTestStubs = () => { + window.e2eTestStubs.FakeTimers.install({ + now: new Date("November 24 2022 12:00:00").valueOf(), + shouldClearNativeTimers: true, + shouldAdvanceTime: false, + }); + }; + }); +}); + +test("issues page", async ({ page }) => { + await page.goto(`${cobUrl}/issues`, { waitUntil: "networkidle" }); + await expect(page).toHaveScreenshot({ fullPage: true }); + + await page.goto(`${cobUrl}/issues?state=closed`, { + waitUntil: "networkidle", + }); + await expect(page).toHaveScreenshot({ fullPage: true }); +}); + +test("issue page", async ({ page }) => { + await page.goto(`${cobUrl}/issues/4fc727e722d3979fd2073d9b56b2751658a4ae79`, { + waitUntil: "networkidle", + }); + await expect(page).toHaveScreenshot({ fullPage: true }); + await page.goto(`${cobUrl}/issues/4038cc5bf6d38f0a5606982236e2abb113affaea`, { + waitUntil: "networkidle", + }); + await expect(page).toHaveScreenshot({ fullPage: true }); + await page.goto(`${cobUrl}/issues/673c51821aee4b780d9661c20d267d66ec43d7ae`, { + waitUntil: "networkidle", + }); + await expect(page).toHaveScreenshot({ fullPage: true }); +}); + +test("patches page", async ({ page }) => { + await page.goto(`${cobUrl}/patches`, { + waitUntil: "networkidle", + }); + await expect(page).toHaveScreenshot({ fullPage: true }); + await page.goto(`${cobUrl}/patches?state=draft`, { + waitUntil: "networkidle", + }); + await expect(page).toHaveScreenshot({ fullPage: true }); + await page.goto(`${cobUrl}/patches?state=archived`, { + waitUntil: "networkidle", + }); + await expect(page).toHaveScreenshot({ fullPage: true }); + await page.goto(`${cobUrl}/patches?state=merged`, { + waitUntil: "networkidle", + }); + await expect(page).toHaveScreenshot({ fullPage: true }); +}); + +test("patch page", async ({ page }) => { + // Draft patch + await page.goto( + `${cobUrl}/patches/f85dce5dced961ee0f47735401cee72a0ee77900`, + { waitUntil: "networkidle" }, + ); + await expect(page).toHaveScreenshot({ fullPage: true }); + // Archived patch + await page.goto( + `${cobUrl}/patches/8d83959d9889da0a94129d9ba06b87c8823972a8`, + { waitUntil: "networkidle" }, + ); + await expect(page).toHaveScreenshot({ fullPage: true }); + // Merged patch + await page.goto( + `${cobUrl}/patches/a5b1d30035da686ba1c4742f6fd25c43238df671`, + { waitUntil: "networkidle" }, + ); + await expect(page).toHaveScreenshot({ fullPage: true }); + // Open patch + await page.goto( + `${cobUrl}/patches/0f3697fed2743549e3bf531e9fa81284a6de1466`, + { waitUntil: "networkidle" }, + ); + await expect(page).toHaveScreenshot({ fullPage: true }); + await page.goto( + `${cobUrl}/patches/0f3697fed2743549e3bf531e9fa81284a6de1466?tab=commits`, + { waitUntil: "networkidle" }, + ); + await expect(page).toHaveScreenshot({ fullPage: true }); + await page.goto( + `${cobUrl}/patches/0f3697fed2743549e3bf531e9fa81284a6de1466?tab=files`, + { waitUntil: "networkidle" }, + ); + await expect(page).toHaveScreenshot({ fullPage: true }); +});