Radish alpha
r
Radicle web interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
Add a cobs fixture and issue and patch tests
Sebastian Martinez committed 2 years ago
commit 107696c3c93679b10facf956e6e99fe2669fbc2e
parent 5b3c293293bbd1a290e74b174be7ea3135a19d56
14 files changed +489 -18
modified 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}
        <ProjectLink
+
          title="Compare {utils.formatObjectId(
+
            previousRevOid,
+
          )}..{utils.formatObjectId(revisionOid)}"
          projectParams={{
            search: `diff=${previousRevOid}..${revisionOid}`,
          }}>
@@ -254,7 +257,7 @@
      {#each timelines as element}
        {#if element.type === "thread"}
          <div class="review">
-
            <ThreadComponent
+
            <Thread
              rawPath={utils.getRawBasePath(projectId, baseUrl, projectHead)}
              thread={element.inner}
              on:reply />
modified 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");

modified 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");
added 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`,
+
  );
+
});
added 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`,
+
    );
+
  }
+
});
added 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<string> {
+
  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];
+
}
added 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<void>,
+
  messages: string[],
+
  options: Options,
+
): Promise<string> {
+
  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<void> {
+
  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<void> {
+
  await peer.git(["commit", "--allow-empty", "-m", message], options);
+
  await peer.git(["push", "rad", featureBranch], options);
+
}
modified 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";
modified 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<void> {
    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();
modified tests/support/heartwood-version
@@ -1 +1 @@
-
fd91cf4d28bd370a6c5beb9ca158e4d91432df25
+
6bbdc574f0d298a178e9f06f07652af290a5eaeb
modified 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);
modified 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 };
}
modified 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() },
+
  };
+
}
added 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 });
+
});