mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-18 19:30:27 +08:00
@@ -438,6 +438,59 @@ test("peer and branch switching", async ({ page }) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Links to a canonical branch encode its name so a slash cannot be mistaken
|
||||
// for a path separator, which leaves the encoded form in the route after an
|
||||
// in-app navigation. The label has to show the branch name, not the encoding.
|
||||
// Only the canonical list encodes: the peer list covered above passes branch
|
||||
// names through untouched, so the branch has to be canonical to reproduce it.
|
||||
test("canonical branch with a slash in the name", async ({ page, peer }) => {
|
||||
const { rid, repoFolder } = await createRepo(peer, {
|
||||
name: "slash-branch",
|
||||
});
|
||||
|
||||
// A branch is only resolved by delegate quorum when a canonical-ref rule
|
||||
// matches it. Without this the branch is listed under the peer instead,
|
||||
// where names are never encoded.
|
||||
await peer.rad(
|
||||
[
|
||||
"id",
|
||||
"update",
|
||||
"--title",
|
||||
"Add canonical ref rule",
|
||||
"--description",
|
||||
"",
|
||||
"--payload",
|
||||
"xyz.radicle.crefs",
|
||||
"rules",
|
||||
JSON.stringify({
|
||||
"refs/heads/feature/*": {
|
||||
allow: [`did:key:${peer.nodeId}`],
|
||||
threshold: 1,
|
||||
},
|
||||
}),
|
||||
"--no-confirm",
|
||||
],
|
||||
{ cwd: repoFolder },
|
||||
);
|
||||
|
||||
await peer.git(["checkout", "-b", "feature/branch"], { cwd: repoFolder });
|
||||
await peer.git(
|
||||
["commit", "--allow-empty", "--message", "branch off the default"],
|
||||
{ cwd: repoFolder },
|
||||
);
|
||||
await peer.git(["push", "rad", "feature/branch"], { cwd: repoFolder });
|
||||
|
||||
await page.goto(peer.ridUrl(rid));
|
||||
const trigger = page.locator('[title="Change branch"]:visible').first();
|
||||
await trigger.click();
|
||||
await page.getByRole("button", { name: /feature\/branch Canonical/ }).click();
|
||||
|
||||
await expect(trigger).toHaveText(/feature\/branch/);
|
||||
await expect(trigger).not.toHaveText(/%2F/);
|
||||
// The URL keeps the encoded form; only what the user reads is decoded.
|
||||
await expect(page).toHaveURL(/tree\/feature%2Fbranch/);
|
||||
});
|
||||
|
||||
test("only one modal can be open at a time", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingUrl);
|
||||
|
||||
|
||||
@@ -352,3 +352,26 @@ describe("repoSegment", () => {
|
||||
expect(utils.repoSegment(repo)).toEqual(rid);
|
||||
});
|
||||
});
|
||||
|
||||
describe("safeDecodeURIComponent", () => {
|
||||
test("decodes an encoded branch name", () => {
|
||||
expect(utils.safeDecodeURIComponent("releaseprep%2Fv27.1")).toEqual(
|
||||
"releaseprep/v27.1",
|
||||
);
|
||||
});
|
||||
|
||||
test("leaves an already decoded name untouched", () => {
|
||||
expect(utils.safeDecodeURIComponent("releaseprep/v27.1")).toEqual(
|
||||
"releaseprep/v27.1",
|
||||
);
|
||||
});
|
||||
|
||||
// Branch names, tags and repo aliases may all contain a bare `%`, which is
|
||||
// not a well-formed encoding and would otherwise throw.
|
||||
test("returns an undecodable segment unchanged", () => {
|
||||
expect(utils.safeDecodeURIComponent("100%")).toEqual("100%");
|
||||
expect(utils.safeDecodeURIComponent("wip/50%-done")).toEqual(
|
||||
"wip/50%-done",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user