Add a cobs fixture and issue and patch tests

Signed-off-by: Sebastian Martinez <me@sebastinez.dev>
This commit is contained in:
Sebastian Martinez
2023-06-08 11:13:28 +02:00
parent 5b3c293293
commit 107696c3c9
14 changed files with 489 additions and 18 deletions
+6 -3
View File
@@ -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 />
+6 -7
View File
@@ -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");
+1 -3
View File
@@ -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");
+19
View File
@@ -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`,
);
});
+98
View File
@@ -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`,
);
}
});
+26
View File
@@ -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];
}
+53
View File
@@ -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);
}
+169
View File
@@ -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";
+3
View File
@@ -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();
+1 -1
View File
@@ -1 +1 @@
fd91cf4d28bd370a6c5beb9ca158e4d91432df25
6bbdc574f0d298a178e9f06f07652af290a5eaeb
+2 -2
View File
@@ -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);
+2 -2
View File
@@ -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 };
}
+9
View File
@@ -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() },
};
}
+94
View File
@@ -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 });
});