mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-18 19:30:27 +08:00
Add e2e helpers for writes and a second peer
The UI updates before a write lands, so a reload right after could cancel it; tests now wait for the command. WebKit also reports such a cancelled fetch as "Load failed". A delegate and a connected contributor let tests cover what non-delegates may do.
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import type { PeerManager, RadiclePeer } from "@tests/support/peerManager.js";
|
||||
|
||||
import * as Fs from "node:fs/promises";
|
||||
import * as Path from "node:path";
|
||||
|
||||
import * as issue from "@tests/support/cobs/issue.js";
|
||||
import * as patch from "@tests/support/cobs/patch.js";
|
||||
import { defaultConfig, gitOptions } from "@tests/support/fixtures.js";
|
||||
import { createRepo } from "@tests/support/repo.js";
|
||||
|
||||
// Bob maintains the repo; eve is connected to him and can clone it, but isn't
|
||||
// a delegate.
|
||||
export async function createCollaborators(peerManager: PeerManager) {
|
||||
const bob = await peerManager.createPeer({
|
||||
name: "bob",
|
||||
gitOptions: gitOptions["bob"],
|
||||
});
|
||||
await bob.startNode();
|
||||
const eve = await peerManager.createPeer({
|
||||
name: "eve",
|
||||
gitOptions: gitOptions["eve"],
|
||||
});
|
||||
await eve.startNode({
|
||||
node: { ...defaultConfig.node, connect: [bob.address], alias: "eve" },
|
||||
});
|
||||
return { bob, eve };
|
||||
}
|
||||
|
||||
// A repo with one file, one issue and one patch, all by `peer`.
|
||||
export async function createProject(peer: RadiclePeer, name = "project") {
|
||||
const { rid, repoFolder } = await createRepo(peer, { name });
|
||||
const file = Path.join(repoFolder, "notes.txt");
|
||||
await Fs.writeFile(file, "one\ntwo\nthree\nfour\nfive\n");
|
||||
await peer.git(["add", "."], { cwd: repoFolder });
|
||||
await peer.git(["commit", "-m", "Add notes"], { cwd: repoFolder });
|
||||
await peer.git(["push", "rad", "main"], { cwd: repoFolder });
|
||||
const issueId = await issue.create(
|
||||
peer,
|
||||
"The notes are too quiet",
|
||||
"Nobody reads them.",
|
||||
[],
|
||||
{ cwd: repoFolder },
|
||||
);
|
||||
const patchId = await patch.create(
|
||||
peer,
|
||||
["Shout the third line"],
|
||||
"feature/shout",
|
||||
() => Fs.writeFile(file, "one\ntwo\nTHREE\nfour\nfive\n"),
|
||||
[],
|
||||
{ cwd: repoFolder },
|
||||
);
|
||||
return { rid, repoFolder, issueId, patchId };
|
||||
}
|
||||
|
||||
export async function clone(peer: RadiclePeer, rid: string) {
|
||||
await peer.rad(["clone", rid], { cwd: peer.checkoutPath });
|
||||
}
|
||||
|
||||
// Resolves once `peer` has fetched `remote`'s changes to the COB `cobId`.
|
||||
export async function waitForCobFrom(
|
||||
peer: RadiclePeer,
|
||||
remote: RadiclePeer,
|
||||
rid: string,
|
||||
cobId: string,
|
||||
) {
|
||||
await peer.waitForEvent(
|
||||
event =>
|
||||
event.type === "refsFetched" &&
|
||||
event.rid === rid &&
|
||||
event.remote === remote.nodeId &&
|
||||
event.updated.some(update =>
|
||||
Object.values(update).some(ref => ref.name.endsWith(`/${cobId}`)),
|
||||
),
|
||||
15_000,
|
||||
);
|
||||
}
|
||||
@@ -173,9 +173,9 @@ const reloading = new WeakSet<Page>();
|
||||
const tearingDown = new WeakSet<Page>();
|
||||
|
||||
// WebKit reports a fetch that was cancelled because the page went away as an
|
||||
// access control failure.
|
||||
// access control failure, or as a failed load.
|
||||
function isCancelledFetch(error: Error) {
|
||||
return /Fetch API cannot load .* due to access control checks/.test(
|
||||
return /Fetch API cannot load .* due to access control checks|Load failed/.test(
|
||||
error.message + error.stack,
|
||||
);
|
||||
}
|
||||
@@ -192,6 +192,20 @@ export async function reload(page: Page) {
|
||||
}
|
||||
}
|
||||
|
||||
// Runs `action` and waits for the backend command it sends to finish. The UI
|
||||
// often updates before the write lands, and a reload would cancel it.
|
||||
export async function waitForCommand(
|
||||
page: Page,
|
||||
command: string,
|
||||
action: () => Promise<void>,
|
||||
) {
|
||||
const response = page.waitForResponse(
|
||||
response => new URL(response.url()).pathname === `/${command}`,
|
||||
);
|
||||
await action();
|
||||
await response;
|
||||
}
|
||||
|
||||
// Call before the first `page.goto`.
|
||||
export async function useBackend(page: Page, peer: RadiclePeer) {
|
||||
await page.addInitScript(port => {
|
||||
|
||||
Reference in New Issue
Block a user