mirror of
https://seed.radicle.dev/rad:z4D5UCArafTzTQpDZNQRuqswh3ury.git
synced 2026-10-18 19:10:45 +08:00
- Add an UntrustedWarning banner - Warn on the Others releases list - Warn on a release by a non-delegate - Warn on artifacts by non-delegates on a delegate release - Warn in the download popover for a non-delegate artifact - Add E2E tests for both release cases Co-authored-by: Rūdolfs Ošiņš <rudolfs@osins.org>
571 lines
21 KiB
TypeScript
571 lines
21 KiB
TypeScript
import type { Page } from "@playwright/test";
|
|
import type { PeerManager, RadiclePeer } from "@tests/support/peerManager.js";
|
|
|
|
import * as Fs from "node:fs/promises";
|
|
import * as Path from "node:path";
|
|
|
|
import {
|
|
clone,
|
|
createCollaborators,
|
|
createProject,
|
|
} from "@tests/support/collaboration.js";
|
|
import {
|
|
expect,
|
|
goto,
|
|
reload,
|
|
test,
|
|
useBackend,
|
|
} from "@tests/support/fixtures.js";
|
|
|
|
const OTHER_DID = "did:key:z6MkkfM3tPXNPrPevKr3uSiQtHPuwnNhu2yUVjgd2jXVsVz5";
|
|
|
|
test("the commit picker lists commits and rejects unknown SHAs", async ({
|
|
page,
|
|
peer,
|
|
}) => {
|
|
const { rid, repoFolder } = await createProject(peer);
|
|
const { stdout: head } = await peer.git(["rev-parse", "main"], {
|
|
cwd: repoFolder,
|
|
});
|
|
|
|
await page.goto(`/repos/${rid}/releases`);
|
|
await page.getByRole("button", { name: "New release" }).click();
|
|
|
|
const input = page.getByPlaceholder("Pick a commit or paste a SHA");
|
|
await input.click();
|
|
await page.getByText("Add notes").click();
|
|
await expect(input).toHaveValue(head.trim());
|
|
|
|
await input.fill("cafecafecafecafecafecafecafecafecafecafe");
|
|
await expect(
|
|
page.getByText("Commit cafecaf is not in this repo."),
|
|
).toBeVisible();
|
|
await expect(
|
|
page.getByRole("button", { name: "Create release" }),
|
|
).toBeDisabled();
|
|
});
|
|
|
|
// Fills the New release modal with one artifact, without submitting it.
|
|
async function fillRelease(page: Page, peer: RadiclePeer) {
|
|
const { rid, repoFolder } = await createProject(peer);
|
|
const file = Path.join(repoFolder, "build.tar");
|
|
await Fs.writeFile(file, "artifact bytes");
|
|
// The native file dialog only exists under Tauri.
|
|
await page.route(/\/pick_artifact_files$/, route =>
|
|
route.fulfill({ json: [file] }),
|
|
);
|
|
|
|
await page.goto(`/repos/${rid}/releases`);
|
|
await page.getByRole("button", { name: "New release" }).click();
|
|
await page.getByPlaceholder("Pick a commit or paste a SHA").click();
|
|
await page.getByText("Add notes").click();
|
|
await page.getByRole("button", { name: "Choose files" }).click();
|
|
return rid;
|
|
}
|
|
|
|
test("creating a release opens it and lists it", async ({ page, peer }) => {
|
|
const rid = await fillRelease(page, peer);
|
|
await page.getByRole("button", { name: "Create release" }).click();
|
|
|
|
await expect(page).toHaveURL(new RegExp(`/repos/${rid}/releases/\\w+`));
|
|
await expect(page.getByRole("button", { name: /build\.tar/ })).toBeVisible();
|
|
// The test peer runs no artifact node, so there is nothing to seed with.
|
|
await expect(
|
|
page.getByTitle("Your artifact node is not running"),
|
|
).toBeDisabled();
|
|
|
|
await goto(page, `/repos/${rid}/releases`);
|
|
await expect(page.getByText("Add notes")).toBeVisible();
|
|
});
|
|
|
|
test("a release whose seeding failed still opens", async ({ page, peer }) => {
|
|
await page.route(/\/artifact_node_running$/, route =>
|
|
route.fulfill({ json: true }),
|
|
);
|
|
await page.route(/\/seed_artifact$/, route =>
|
|
route.fulfill({
|
|
status: 500,
|
|
json: { code: "UnknownError", message: "seeding failed" },
|
|
}),
|
|
);
|
|
const rid = await fillRelease(page, peer);
|
|
await page.getByRole("button", { name: "Create release" }).click();
|
|
|
|
await expect(
|
|
page.getByText("Registered, but 1 artifact could not be seeded."),
|
|
).toBeVisible();
|
|
await expect(page.getByRole("button", { name: "Retry" })).toBeEnabled();
|
|
await page.getByRole("button", { name: "Open release" }).click();
|
|
|
|
await expect(page).toHaveURL(new RegExp(`/repos/${rid}/releases/\\w+`));
|
|
await expect(page.getByRole("button", { name: /build\.tar/ })).toBeVisible();
|
|
});
|
|
|
|
test("a release can be deleted", async ({ page, peer }) => {
|
|
const rid = await fillRelease(page, peer);
|
|
await page.getByRole("button", { name: "Create release" }).click();
|
|
await expect(page).toHaveURL(new RegExp(`/repos/${rid}/releases/\\w+`));
|
|
|
|
await page.getByRole("button", { name: "Delete" }).click();
|
|
await page
|
|
.locator("[data-modal-content]")
|
|
.getByRole("button", { name: "Delete" })
|
|
.click();
|
|
|
|
await expect(page).toHaveURL(new RegExp(`/repos/${rid}/releases$`));
|
|
await expect(page.getByText("No releases")).toBeVisible();
|
|
});
|
|
|
|
test("re-registering an artifact only signs what changes", async ({
|
|
page,
|
|
peer,
|
|
}) => {
|
|
const rid = await fillRelease(page, peer);
|
|
await page.getByRole("button", { name: "Create release" }).click();
|
|
await expect(page).toHaveURL(new RegExp(`/repos/${rid}/releases/\\w+`));
|
|
const modal = page.locator("[data-modal-content]");
|
|
|
|
// The same bytes under the same name change nothing.
|
|
await page.getByRole("button", { name: "Register artifacts" }).click();
|
|
await page.getByRole("button", { name: "Files…" }).click();
|
|
await expect(modal.getByText("Nothing will be registered.")).toBeVisible();
|
|
await expect(
|
|
modal.getByRole("button", { name: "icon-plus Register" }),
|
|
).toBeHidden();
|
|
await modal.getByRole("button", { name: "Close", exact: true }).click();
|
|
|
|
// The same bytes under a new name rename your own artifact.
|
|
const renamed = Path.join(peer.checkoutPath, "renamed.tar");
|
|
await Fs.writeFile(renamed, "artifact bytes");
|
|
await page.route(/\/pick_artifact_files$/, route =>
|
|
route.fulfill({ json: [renamed] }),
|
|
);
|
|
await page.getByRole("button", { name: "Register artifacts" }).click();
|
|
await page.getByRole("button", { name: "Files…" }).click();
|
|
await expect(modal.getByText("renames “build.tar”")).toBeVisible();
|
|
await modal.getByRole("button", { name: "icon-plus Update" }).click();
|
|
await expect(
|
|
page.getByRole("button", { name: /renamed\.tar/ }),
|
|
).toBeVisible();
|
|
});
|
|
|
|
test("picks with the same contents are registered once", async ({
|
|
page,
|
|
peer,
|
|
}) => {
|
|
const rid = await fillRelease(page, peer);
|
|
const copy = Path.join(peer.checkoutPath, "copy.tar");
|
|
await Fs.writeFile(copy, "artifact bytes");
|
|
await page.route(/\/pick_artifact_files$/, route =>
|
|
route.fulfill({ json: [copy] }),
|
|
);
|
|
await page.getByRole("button", { name: "Choose files" }).click();
|
|
await expect(page.getByText("same as “build.tar”")).toBeVisible();
|
|
await page.getByRole("button", { name: "Create release" }).click();
|
|
await expect(page).toHaveURL(new RegExp(`/repos/${rid}/releases/\\w+`));
|
|
await expect(page.getByRole("button", { name: /build\.tar/ })).toBeVisible();
|
|
|
|
// Two new picks with the same contents in one go.
|
|
const first = Path.join(peer.checkoutPath, "a.tar");
|
|
const second = Path.join(peer.checkoutPath, "b.tar");
|
|
await Fs.writeFile(first, "other bytes");
|
|
await Fs.writeFile(second, "other bytes");
|
|
await page.route(/\/pick_artifact_files$/, route =>
|
|
route.fulfill({ json: [first, second] }),
|
|
);
|
|
const modal = page.locator("[data-modal-content]");
|
|
await page.getByRole("button", { name: "Register artifacts" }).click();
|
|
await page.getByRole("button", { name: "Files…" }).click();
|
|
await expect(modal.getByText("same as “a.tar” · skipped")).toBeVisible();
|
|
await modal.getByRole("button", { name: "icon-plus Register" }).click();
|
|
await expect(modal).toBeHidden();
|
|
await expect(page.getByRole("button", { name: /a\.tar/ })).toBeVisible();
|
|
await expect(page.getByRole("button", { name: /b\.tar/ })).toBeHidden();
|
|
});
|
|
|
|
test("a location can be added and removed", async ({ page, peer }) => {
|
|
const rid = await fillRelease(page, peer);
|
|
await page.getByRole("button", { name: "Create release" }).click();
|
|
await expect(page).toHaveURL(new RegExp(`/repos/${rid}/releases/\\w+`));
|
|
await page.getByRole("button", { name: /build\.tar/ }).click();
|
|
|
|
const add = page.getByTitle("Add a URL others can download");
|
|
const input = page.getByPlaceholder("https://…");
|
|
await add.click();
|
|
await input.fill("iroh://abc");
|
|
await input.press("Enter");
|
|
await expect(
|
|
page.getByText("This is not a URL peers can fetch from."),
|
|
).toBeVisible();
|
|
|
|
const url = "https://example.com/build.tar";
|
|
await input.fill(url);
|
|
await input.press("Enter");
|
|
await expect(page.getByText(url)).toBeVisible();
|
|
|
|
await page.getByTitle("Remove location").click();
|
|
await page.getByRole("button", { name: "icon-trash Remove" }).click();
|
|
await expect(page.getByText(url)).toBeHidden();
|
|
await expect(page.getByText("No locations")).toBeVisible();
|
|
});
|
|
|
|
test("a location added by someone else cannot be removed", async ({
|
|
page,
|
|
peer,
|
|
}) => {
|
|
const rid = await fillRelease(page, peer);
|
|
await page.getByRole("button", { name: "Create release" }).click();
|
|
await expect(page).toHaveURL(new RegExp(`/repos/${rid}/releases/\\w+`));
|
|
|
|
// The test peers run no artifact node, so another user's location is
|
|
// injected into the release the view reads.
|
|
const url = "https://example.com/theirs.tar";
|
|
await page.route(/\/release_by_id$/, async route => {
|
|
const release = await (await route.fetch()).json();
|
|
for (const artifact of release.artifacts) {
|
|
artifact.locations.push({
|
|
user: { did: OTHER_DID },
|
|
url,
|
|
});
|
|
}
|
|
await route.fulfill({ json: release });
|
|
});
|
|
await reload(page);
|
|
await page.getByRole("button", { name: /build\.tar/ }).click();
|
|
|
|
await expect(page.getByText(url)).toBeVisible();
|
|
await expect(page.getByTitle("Remove location")).toBeHidden();
|
|
});
|
|
|
|
test("removing your node's location while seeding stops seeding", async ({
|
|
page,
|
|
peer,
|
|
}) => {
|
|
const rid = await fillRelease(page, peer);
|
|
await page.getByRole("button", { name: "Create release" }).click();
|
|
await expect(page).toHaveURL(new RegExp(`/repos/${rid}/releases/\\w+`));
|
|
await page.getByRole("button", { name: /build\.tar/ }).click();
|
|
await page.getByTitle("Add a URL others can download").click();
|
|
await page.getByPlaceholder("https://…").fill("radiroh://");
|
|
const added = page.waitForRequest(/\/add_artifact_location$/);
|
|
await page.getByPlaceholder("https://…").press("Enter");
|
|
const { cid, releaseId } = (await added).postDataJSON();
|
|
await expect(page.getByText("radiroh://")).toBeVisible();
|
|
|
|
// The test peers run no artifact node, so it is made to report the
|
|
// artifact as seeded and to accept the unseed.
|
|
await page.route(/\/seeded_artifacts$/, route =>
|
|
route.fulfill({ json: [cid] }),
|
|
);
|
|
const unseed = page.waitForRequest(/\/unseed_artifact$/);
|
|
await page.route(/\/unseed_artifact$/, route =>
|
|
route.fulfill({ json: null }),
|
|
);
|
|
await reload(page);
|
|
await page.getByRole("button", { name: /build\.tar/ }).click();
|
|
|
|
await page.getByTitle("Remove location").click();
|
|
await expect(page.getByText("Unseed this artifact?")).toBeVisible();
|
|
await page.getByRole("button", { name: "icon-trash Unseed" }).click();
|
|
expect((await unseed).postDataJSON()).toMatchObject({ releaseId, cid });
|
|
});
|
|
|
|
test("a release by a non-delegate shows a warning", async ({ page, peer }) => {
|
|
const rid = await fillRelease(page, peer);
|
|
await page.getByRole("button", { name: "Create release" }).click();
|
|
await expect(page).toHaveURL(new RegExp(`/repos/${rid}/releases/\\w+`));
|
|
await expect(page.getByRole("note")).toBeHidden();
|
|
|
|
await page.route(/\/release_by_id$/, async route => {
|
|
const release = await (await route.fetch()).json();
|
|
release.creator.did = OTHER_DID;
|
|
await route.fulfill({ json: release });
|
|
});
|
|
await reload(page);
|
|
|
|
await expect(
|
|
page.getByRole("note").getByText("Not from a delegate."),
|
|
).toBeVisible();
|
|
});
|
|
|
|
test("an artifact by a non-delegate shows a warning", async ({
|
|
page,
|
|
peer,
|
|
}) => {
|
|
const rid = await fillRelease(page, peer);
|
|
await page.getByRole("button", { name: "Create release" }).click();
|
|
await expect(page).toHaveURL(new RegExp(`/repos/${rid}/releases/\\w+`));
|
|
|
|
// A location makes the download button usable.
|
|
await page.route(/\/release_by_id$/, async route => {
|
|
const release = await (await route.fetch()).json();
|
|
for (const artifact of release.artifacts) {
|
|
artifact.author.did = OTHER_DID;
|
|
artifact.locations.push({
|
|
user: { did: OTHER_DID },
|
|
url: "https://example.com/theirs.tar",
|
|
});
|
|
}
|
|
await route.fulfill({ json: release });
|
|
});
|
|
await reload(page);
|
|
|
|
await expect(
|
|
page.getByRole("note").getByText("Not from delegates."),
|
|
).toBeVisible();
|
|
await page.getByRole("button", { name: "icon-download Download" }).click();
|
|
await expect(page.getByText("Not from a delegate.")).toBeVisible();
|
|
});
|
|
|
|
test("a link by a non-delegate on a delegate's artifact shows a warning", async ({
|
|
page,
|
|
peer,
|
|
}) => {
|
|
const rid = await fillRelease(page, peer);
|
|
await page.getByRole("button", { name: "Create release" }).click();
|
|
await expect(page).toHaveURL(new RegExp(`/repos/${rid}/releases/\\w+`));
|
|
|
|
await page.route(/\/release_by_id$/, async route => {
|
|
const release = await (await route.fetch()).json();
|
|
for (const artifact of release.artifacts) {
|
|
artifact.locations.push({
|
|
user: { did: OTHER_DID },
|
|
url: "https://example.com/theirs.tar",
|
|
});
|
|
}
|
|
await route.fulfill({ json: release });
|
|
});
|
|
await reload(page);
|
|
|
|
await page.getByRole("button", { name: "icon-download Download" }).click();
|
|
await expect(page.getByText("Not from a delegate.")).toBeHidden();
|
|
await page
|
|
.getByRole("button", { name: "icon-open-external Browser" })
|
|
.click();
|
|
await expect(
|
|
page.getByRole("note").getByText("Links without a delegate badge"),
|
|
).toBeVisible();
|
|
});
|
|
|
|
test("metadata can be added, edited and removed", async ({ page, peer }) => {
|
|
const rid = await fillRelease(page, peer);
|
|
await page.getByRole("button", { name: "Create release" }).click();
|
|
await expect(page).toHaveURL(new RegExp(`/repos/${rid}/releases/\\w+`));
|
|
await page.getByRole("button", { name: /build\.tar/ }).click();
|
|
const metadata = page.locator(".section").filter({ hasText: /^Metadata/ });
|
|
|
|
await metadata.getByRole("button", { name: "Add" }).click();
|
|
await metadata.getByPlaceholder("Key").fill("arch");
|
|
await metadata.getByPlaceholder("Value").fill("x86_64");
|
|
await metadata.getByPlaceholder("Value").press("Enter");
|
|
await expect(metadata.getByText("x86_64")).toBeVisible();
|
|
await reload(page);
|
|
await page.getByRole("button", { name: /build\.tar/ }).click();
|
|
await expect(metadata.getByText("x86_64")).toBeVisible();
|
|
|
|
await metadata.getByTitle("Edit").click();
|
|
await metadata.getByPlaceholder("Value").fill("aarch64");
|
|
await metadata.getByRole("button", { name: "Save" }).click();
|
|
await expect(metadata.getByText("aarch64")).toBeVisible();
|
|
|
|
await metadata.getByTitle("Remove").click();
|
|
await page.getByRole("button", { name: "icon-trash Remove" }).click();
|
|
await expect(metadata.getByText("No metadata")).toBeVisible();
|
|
});
|
|
|
|
test("an artifact can be redacted", async ({ page, peer }) => {
|
|
const rid = await fillRelease(page, peer);
|
|
await page.getByRole("button", { name: "Create release" }).click();
|
|
await expect(page).toHaveURL(new RegExp(`/repos/${rid}/releases/\\w+`));
|
|
await page.getByRole("button", { name: /build\.tar/ }).click();
|
|
|
|
await page.getByRole("button", { name: "Redact" }).click();
|
|
const modal = page.locator("[data-modal-content]");
|
|
await modal
|
|
.getByPlaceholder("Why should this no longer be used?")
|
|
.fill("bad");
|
|
await modal.getByRole("button", { name: "Redact" }).click();
|
|
await expect(modal).toBeHidden();
|
|
|
|
// A redaction by the author hides the artifact by default.
|
|
await expect(page.getByText("No artifacts")).toBeVisible();
|
|
await page.getByRole("button", { name: /Show redacted/ }).click();
|
|
await page.getByRole("button", { name: /build\.tar/ }).click();
|
|
await expect(page.getByText("bad", { exact: true })).toBeVisible();
|
|
});
|
|
|
|
// Calls a command on the peer's test backend directly, bypassing the app.
|
|
function backend(peer: RadiclePeer) {
|
|
return async (cmd: string, args: object) => {
|
|
const { hostname, port } = peer.httpdBaseUrl;
|
|
const response = await fetch(`http://${hostname}:${port}/${cmd}`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(args),
|
|
});
|
|
expect(response.ok).toBe(true);
|
|
return response.json();
|
|
};
|
|
}
|
|
|
|
test("opening another release starts from a fresh page", async ({
|
|
page,
|
|
peer,
|
|
}) => {
|
|
const { rid, repoFolder } = await createProject(peer);
|
|
const file = Path.join(repoFolder, "build.tar");
|
|
await Fs.writeFile(file, "artifact bytes");
|
|
const call = backend(peer);
|
|
const releaseIds: string[] = [];
|
|
// Both releases carry the same bytes, so their artifacts share a CID.
|
|
for (const revision of ["main~1", "main"]) {
|
|
const { stdout: oid } = await peer.git(["rev-parse", revision], {
|
|
cwd: repoFolder,
|
|
});
|
|
const releaseId: string = await call("create_or_open_release", {
|
|
rid,
|
|
oid: oid.trim(),
|
|
tag: null,
|
|
});
|
|
const { cid, sizeBytes } = await call("compute_artifact_cid", {
|
|
path: file,
|
|
});
|
|
await call("register_artifact", {
|
|
rid,
|
|
releaseId,
|
|
cid,
|
|
name: "build.tar",
|
|
sizeBytes,
|
|
});
|
|
releaseIds.push(releaseId);
|
|
}
|
|
const [first, second] = releaseIds;
|
|
|
|
await page.goto(`/repos/${rid}/releases/${first}`);
|
|
await page.getByRole("button", { name: /build\.tar/ }).click();
|
|
const attestations = page
|
|
.locator(".section")
|
|
.filter({ hasText: /^Attestations/ });
|
|
await expect(attestations).toBeVisible();
|
|
|
|
// A link to another release, as a mention renders one, navigates in place.
|
|
await page.evaluate(href => {
|
|
const link = document.createElement("a");
|
|
link.href = href;
|
|
link.textContent = "Next release";
|
|
document.body.append(link);
|
|
}, `/repos/${rid}/releases/${second}`);
|
|
await page.getByRole("link", { name: "Next release" }).click();
|
|
|
|
await expect(page.locator(".title").getByText("Add notes")).toBeVisible();
|
|
await expect(attestations).toBeHidden();
|
|
});
|
|
|
|
test("a failed refresh after registering is not reported as a failure", async ({
|
|
page,
|
|
peer,
|
|
}) => {
|
|
const rid = await fillRelease(page, peer);
|
|
await page.getByRole("button", { name: "Create release" }).click();
|
|
await expect(page).toHaveURL(new RegExp(`/repos/${rid}/releases/\\w+`));
|
|
|
|
const other = Path.join(peer.checkoutPath, "other.tar");
|
|
await Fs.writeFile(other, "other bytes");
|
|
await page.route(/\/pick_artifact_files$/, route =>
|
|
route.fulfill({ json: [other] }),
|
|
);
|
|
await page.route(/\/release_by_id$/, route =>
|
|
route.fulfill({
|
|
status: 500,
|
|
json: { code: "UnknownError", message: "refresh failed" },
|
|
}),
|
|
);
|
|
const modal = page.locator("[data-modal-content]");
|
|
await page.getByRole("button", { name: "Register artifacts" }).click();
|
|
await page.getByRole("button", { name: "Files…" }).click();
|
|
await modal.getByRole("button", { name: "icon-plus Register" }).click();
|
|
|
|
await expect(modal).toBeHidden();
|
|
await expect(page.getByText("Registering failed.")).toBeHidden();
|
|
});
|
|
|
|
// Bob publishes an artifact; the app runs as eve, who isn't a delegate.
|
|
async function openBobsArtifact(page: Page, peerManager: PeerManager) {
|
|
const { bob, eve } = await createCollaborators(peerManager);
|
|
const { rid, repoFolder } = await createProject(bob);
|
|
const { stdout: head } = await bob.git(["rev-parse", "main"], {
|
|
cwd: repoFolder,
|
|
});
|
|
const file = Path.join(bob.checkoutPath, "build.tar");
|
|
await Fs.writeFile(file, "artifact bytes");
|
|
|
|
await bob.startHttpd();
|
|
const call = backend(bob);
|
|
const releaseId: string = await call("create_or_open_release", {
|
|
rid,
|
|
oid: head.trim(),
|
|
tag: null,
|
|
});
|
|
const { cid, sizeBytes } = await call("compute_artifact_cid", { path: file });
|
|
await call("register_artifact", {
|
|
rid,
|
|
releaseId,
|
|
cid,
|
|
name: "build.tar",
|
|
sizeBytes,
|
|
});
|
|
|
|
await clone(eve, rid);
|
|
await eve.startHttpd();
|
|
await useBackend(page, eve);
|
|
await page.goto(`/repos/${rid}/releases/${releaseId}`);
|
|
await page.getByRole("button", { name: /build\.tar/ }).click();
|
|
return eve;
|
|
}
|
|
|
|
test("a non-delegate can attest an artifact by reproducing it", async ({
|
|
page,
|
|
peerManager,
|
|
}) => {
|
|
const eve = await openBobsArtifact(page, peerManager);
|
|
const other = Path.join(eve.checkoutPath, "other.tar");
|
|
const same = Path.join(eve.checkoutPath, "same.tar");
|
|
await Fs.writeFile(other, "other bytes");
|
|
await Fs.writeFile(same, "artifact bytes");
|
|
const attestations = page
|
|
.locator(".section")
|
|
.filter({ hasText: /^Attestations/ });
|
|
|
|
await page.route(/\/pick_artifact_files$/, route =>
|
|
route.fulfill({ json: [other] }),
|
|
);
|
|
await attestations.getByRole("button", { name: /Attest/ }).click();
|
|
await expect(
|
|
page.getByText("Your build has a different CID, so it was not attested."),
|
|
).toBeVisible();
|
|
|
|
await page.route(/\/pick_artifact_files$/, route =>
|
|
route.fulfill({ json: [same] }),
|
|
);
|
|
await attestations.getByRole("button", { name: /Attest/ }).click();
|
|
await expect(attestations.getByText("eve")).toBeVisible();
|
|
await expect(
|
|
attestations.getByRole("button", { name: /Attest/ }),
|
|
).toBeHidden();
|
|
});
|
|
|
|
test("a non-delegate cannot redact, edit or delete someone else's release", async ({
|
|
page,
|
|
peerManager,
|
|
}) => {
|
|
await openBobsArtifact(page, peerManager);
|
|
|
|
await expect(
|
|
page.locator(".section").filter({ hasText: /^Attestations/ }),
|
|
).toBeVisible();
|
|
await expect(page.getByRole("button", { name: "Redact" })).toBeHidden();
|
|
await expect(page.getByRole("button", { name: "Delete" })).toBeHidden();
|
|
const metadata = page.locator(".section").filter({ hasText: /^Metadata/ });
|
|
await expect(metadata.getByRole("button", { name: "Add" })).toBeHidden();
|
|
});
|