diff --git a/src/App/MobileFooter.svelte b/src/App/MobileFooter.svelte index a0f30f122..a367d5912 100644 --- a/src/App/MobileFooter.svelte +++ b/src/App/MobileFooter.svelte @@ -29,7 +29,7 @@ - + diff --git a/src/components/File.svelte b/src/components/File.svelte index 42f229521..e6eb59040 100644 --- a/src/components/File.svelte +++ b/src/components/File.svelte @@ -16,24 +16,20 @@ display: flex; height: 3rem; align-items: center; - padding: 0 0.5rem 0 1rem; - border: 1px solid var(--color-border-subtle); - border-top-left-radius: var(--border-radius-md); - border-top-right-radius: var(--border-radius-md); + padding: 0 1rem; background-color: var(--color-surface-canvas); z-index: 2; } + .header-border { + border-bottom: 1px solid var(--color-border-subtle); + } + .sticky { position: sticky; top: 0; } - .collapsed { - border-radius: var(--border-radius-md); - border: 1px solid var(--color-border-subtle); - } - .left { display: flex; gap: 0.5rem; @@ -51,27 +47,19 @@ .container { position: relative; overflow-x: auto; - border: 1px solid var(--color-border-subtle); - border-top: 0; - border-bottom-left-radius: var(--border-radius-md); - border-bottom-right-radius: var(--border-radius-md); } @media (max-width: 719.98px) { .header { - border-radius: 0; - border-left: 0; - border-right: 0; padding: 0 1rem 0 1rem; } - .container { - border-radius: 0; - border-left: 0; - border-right: 0; - } } -
+
{#if collapsable} .list { - border-top: 1px solid var(--color-border-subtle); border-bottom: 1px solid var(--color-border-subtle); } .list-item:not(:last-child) { diff --git a/src/components/RepoCard.svelte b/src/components/RepoCard.svelte index dd0b31def..894319fe0 100644 --- a/src/components/RepoCard.svelte +++ b/src/components/RepoCard.svelte @@ -110,7 +110,7 @@ color: var(--color-text-on-brand); } - @container (max-width: 20rem) { + @container (max-width: 30rem) { .activity { display: none; } diff --git a/src/views/repos/Changeset.svelte b/src/views/repos/Changeset.svelte index 4b40ba2ec..d927eaa7e 100644 --- a/src/views/repos/Changeset.svelte +++ b/src/views/repos/Changeset.svelte @@ -58,6 +58,11 @@ background-color: var(--color-surface-base); padding: 1rem; } + .file { + border: 1px solid var(--color-border-subtle); + border-radius: var(--border-radius-md); + overflow: clip; + } .summary { font: var(--txt-body-m-regular); } @@ -99,7 +104,7 @@ {#each diff.files as file} {@const path = "path" in file ? file.path : file.newPath} -
+
{#if "diff" in file} - import type { BaseUrl, Commit, Repo, SeedingPolicy } from "@http-client"; + import type { BaseUrl, Commit, Repo } from "@http-client"; import dompurify from "dompurify"; import escape from "lodash/escape"; @@ -14,10 +14,8 @@ import Layout from "./Layout.svelte"; import Link from "@app/components/Link.svelte"; import Separator from "./Separator.svelte"; - import Share from "./Share.svelte"; export let baseUrl: BaseUrl; - export let seedingPolicy: SeedingPolicy; export let commit: Commit; export let repo: Repo; export let nodeAvatarUrl: string | undefined; @@ -70,7 +68,7 @@ } - + {/if} -
diff --git a/src/views/repos/Header.svelte b/src/views/repos/Header.svelte index b12b4ef3e..21189d4f1 100644 --- a/src/views/repos/Header.svelte +++ b/src/views/repos/Header.svelte @@ -5,20 +5,65 @@ @@ -54,11 +107,7 @@ node: baseUrl, path: "/", }}> - @@ -69,12 +118,7 @@ repo: repo.rid, node: baseUrl, }}> - + +
+ +
+ {#if activeTab !== "issues" && activeTab !== "patches"} + + {/if} + + +
diff --git a/src/views/repos/Header/CloneButton.svelte b/src/views/repos/Header/CloneButton.svelte index 20025c195..bf5da275e 100644 --- a/src/views/repos/Header/CloneButton.svelte +++ b/src/views/repos/Header/CloneButton.svelte @@ -11,11 +11,23 @@ import Popover from "@app/components/Popover.svelte"; import Radio from "@app/components/Radio.svelte"; + import { baseUrlToString } from "@app/lib/utils"; + export let baseUrl: BaseUrl; export let id: string; export let name: string; export let currentRefname: string; - export let enabledArchiveDownload: boolean; + + let enabledArchiveDownload = false; + + void fetch( + `${baseUrlToString(baseUrl)}/raw/${id}/archive/${currentRefname}`, + { + method: "HEAD", + }, + ).then(response => { + enabledArchiveDownload = response.ok; + }); let activeTab: "radicle" | "git" | "archive" = "radicle"; diff --git a/src/views/repos/Header/SeedButton.svelte b/src/views/repos/Header/SeedButton.svelte index fa639c923..0d723820d 100644 --- a/src/views/repos/Header/SeedButton.svelte +++ b/src/views/repos/Header/SeedButton.svelte @@ -27,8 +27,8 @@ padding: 0 0.25rem; } .not-seeding { - background-color: var(--color-surface-brand-secondary); - color: var(--color-text-on-brand); + background-color: var(--color-surface-mid); + color: var(--color-text-secondary); } .disabled { background-color: var(--color-surface-mid); @@ -44,7 +44,7 @@ on:click={() => { toggle(); }} - variant="secondary-toggle-off"> + variant="gray"> Seed diff --git a/src/views/repos/History.svelte b/src/views/repos/History.svelte index 80c898d8a..04ddf16df 100644 --- a/src/views/repos/History.svelte +++ b/src/views/repos/History.svelte @@ -15,6 +15,7 @@ import { groupCommits } from "@app/lib/commit"; import Button from "@app/components/Button.svelte"; + import CloneButton from "@app/views/repos/Header/CloneButton.svelte"; import CommitTeaser from "./Commit/CommitTeaser.svelte"; import ErrorMessage from "@app/components/ErrorMessage.svelte"; import Header from "./Source/Header.svelte"; @@ -36,7 +37,21 @@ export let tree: Tree; export let nodeAvatarUrl: string | undefined; + $: currentRefname = formatQualifiedRefname( + revision || repo.payloads["xyz.radicle.project"].data.defaultBranch, + peer, + ); + const api = new HttpdClient(baseUrl); + let totalCommits: number | undefined = undefined; + + function fetchTotalCommits(rid: string, sha: string) { + void api.repo.getTreeStatsBySha(rid, sha).then(stats => { + totalCommits = stats.commits; + }); + } + + $: fetchTotalCommits(repo.rid, commit); // eslint-disable-next-line @typescript-eslint/no-explicit-any let error: any; @@ -80,18 +95,13 @@ justify-content: center; } .group-header { - margin-left: 1rem; - margin-top: 3rem; - margin-bottom: 1rem; + margin: 1rem 0 0.5rem 1rem; font: var(--txt-body-m-regular); color: var(--color-text-tertiary); } - .group-header:first-child { - margin-top: 0; - } - + - + + + + -
+
- {#await api.repo.getTreeStatsBySha(repo.rid, commit)} + {#if totalCommits === undefined || loading || allCommitHeaders.length < totalCommits}
- + {#if totalCommits === undefined || loading} + + {:else if allCommitHeaders.length < totalCommits} + + {/if}
- {:then stats} - {#if loading || allCommitHeaders.length < stats.commits} -
- {#if loading} - - {:else if allCommitHeaders.length < stats.commits} - - {/if} -
- {/if} + {/if} - {#if error} -
- -
- {/if} - {:catch error} + {#if error}
- {/await} + {/if} diff --git a/src/views/repos/Issue.svelte b/src/views/repos/Issue.svelte index 24b234e68..8cb697a7e 100644 --- a/src/views/repos/Issue.svelte +++ b/src/views/repos/Issue.svelte @@ -1,5 +1,5 @@ - - - - diff --git a/src/views/repos/Sidebar/ContextRepo.svelte b/src/views/repos/Sidebar/ContextRepo.svelte index b299df410..e51ac0585 100644 --- a/src/views/repos/Sidebar/ContextRepo.svelte +++ b/src/views/repos/Sidebar/ContextRepo.svelte @@ -2,53 +2,78 @@ import type { BaseUrl, Repo, SeedingPolicy } from "@http-client"; import capitalize from "lodash/capitalize"; - - import IconButton from "@app/components/IconButton.svelte"; - import Icon from "@app/components/Icon.svelte"; + import HoverPopover from "@app/components/HoverPopover.svelte"; + import Link from "@app/components/Link.svelte"; import NodeId from "@app/components/NodeId.svelte"; + import UserAvatar from "@app/components/UserAvatar.svelte"; export let baseUrl: BaseUrl; + export let repoThreshold: number; export let repoDelegates: Repo["delegates"]; export let seedingPolicy: SeedingPolicy; - - let delegateExpanded = false; - let policyExpanded = false; -
- Delegates -
- +
+
+ Delegates + {repoThreshold}/{repoDelegates.length} - (delegateExpanded = !delegateExpanded)}> - - +
+ {#each repoDelegates as delegate} + + + + +
+ +
+
+ {/each} +
-
-{#if delegateExpanded} -
+
{#if repoDelegates.length === 1} Any changes accepted by the sole delegate will be included in the canonical branch. @@ -57,29 +82,15 @@ to be included in the canonical branch. {/if}
-
- {#each repoDelegates as delegate} -
- -
- {/each} -
-{/if} -
- Seeding Scope -
- +
+ Seeding Scope + {capitalize( "scope" in seedingPolicy ? seedingPolicy.scope : "not defined", )} - (policyExpanded = !policyExpanded)}> - -
-
-{#if policyExpanded} -
+
{#if seedingPolicy.policy === "block"} Seeding scope only has an effect when a repository is seeded. This repo isn't seeded by the seed node. @@ -89,4 +100,4 @@ This repository tracks only peers followed by the seed node. {/if}
-{/if} +
diff --git a/src/views/repos/Source.svelte b/src/views/repos/Source.svelte index 9a985c118..4e8102031 100644 --- a/src/views/repos/Source.svelte +++ b/src/views/repos/Source.svelte @@ -10,17 +10,18 @@ import { HttpdClient } from "@http-client"; + import { formatQualifiedRefname } from "@app/lib/utils"; + + import BlobComponent from "./Source/Blob.svelte"; import Button from "@app/components/Button.svelte"; + import CloneButton from "@app/views/repos/Header/CloneButton.svelte"; + import FilePath from "@app/components/FilePath.svelte"; import Header from "./Source/Header.svelte"; import Layout from "./Layout.svelte"; import Placeholder from "@app/components/Placeholder.svelte"; - - import BlobComponent from "./Source/Blob.svelte"; - import FilePath from "@app/components/FilePath.svelte"; import RepoNameHeader from "./Source/RepoNameHeader.svelte"; import Separator from "./Separator.svelte"; import TreeComponent from "./Source/Tree.svelte"; - import { formatQualifiedRefname } from "@app/lib/utils"; export let baseUrl: BaseUrl; export let blobResult: BlobResult; @@ -52,6 +53,11 @@ }); }; + $: currentRefname = formatQualifiedRefname( + revision || repo.payloads["xyz.radicle.project"].data.defaultBranch, + peer, + ); + $: baseRoute = { resource: "repo.source", node: baseUrl, @@ -68,13 +74,14 @@ .container { display: flex; width: inherit; - padding: 0 1rem 1rem 1rem; + padding: 0; } .column-left { display: flex; flex-direction: column; padding-right: 0.5rem; + border-right: 1px solid var(--color-border-subtle); } .column-right { @@ -82,8 +89,6 @@ flex-direction: column; width: 100%; padding-bottom: 2.5rem; - max-width: 75rem; - margin: 0 auto; /* To allow pre elements to shrink when overflowing */ min-width: 0; } @@ -122,7 +127,6 @@ {baseUrl} {nodeAvatarUrl} {repo} - {seedingPolicy} activeTab="source" stylePaddingBottom="0"> @@ -131,16 +135,19 @@ {/if} - + + + + -
+
+ // Cache commit counts across component remounts (tab navigation). + const commitCountCache: Record = {}; + + -
+
{#if selectedBranch} {/if} -
+ + {#if !onCanonical} + + + + {/if} +
+ +
+
+ + + + + + + +
+ +
+ {#if selectedBranch} + + {/if} @@ -134,43 +211,3 @@ {/if}
- -
-
- - - - - - - -
-
diff --git a/src/views/repos/Source/PeerBranchSelector.svelte b/src/views/repos/Source/PeerBranchSelector.svelte index 12287f8f2..f275b3833 100644 --- a/src/views/repos/Source/PeerBranchSelector.svelte +++ b/src/views/repos/Source/PeerBranchSelector.svelte @@ -180,7 +180,7 @@ + style={subgridStyle}>
@@ -231,7 +231,7 @@ {:else}
No entries found @@ -245,9 +245,7 @@ searchInput = ""; toggle(); }}> - +
{repo.payloads["xyz.radicle.project"].data.defaultBranch} diff --git a/src/views/repos/Source/PeerBranchSelector/Peer.svelte b/src/views/repos/Source/PeerBranchSelector/Peer.svelte index 64e3b505e..c97086eb9 100644 --- a/src/views/repos/Source/PeerBranchSelector/Peer.svelte +++ b/src/views/repos/Source/PeerBranchSelector/Peer.svelte @@ -68,7 +68,7 @@ peer: peer.remote.id, revision: name, })} - style={`${subgridStyle} padding-left: 2.3rem; gap: inherit;`}> + style={`${subgridStyle} padding-left: 2.3rem;`}>
diff --git a/src/views/repos/Source/RepoNameHeader.svelte b/src/views/repos/Source/RepoNameHeader.svelte index 22d80d856..48141f246 100644 --- a/src/views/repos/Source/RepoNameHeader.svelte +++ b/src/views/repos/Source/RepoNameHeader.svelte @@ -1,32 +1,20 @@ -
-
- - - - {project.data.name} - - - - {#if repo.visibility.type === "private"} - - - Private - - {/if} -
- -
- - -
-
- -
+
+
+ +
+
+
+ + + + {project.data.name} + + + + {#if repo.visibility.type === "private"} + + + Private + + {/if} +
+
+ + {formatRepositoryId(repo.rid)} + +
+
+ {@html render(project.data.description)}
-
- +
+ {@html render(project.data.description)} +
+
+
-
- {@html render(project.data.description)} -
diff --git a/src/views/repos/router.ts b/src/views/repos/router.ts index e1174fcea..06f1f61f2 100644 --- a/src/views/repos/router.ts +++ b/src/views/repos/router.ts @@ -145,7 +145,6 @@ export type RepoLoadedRoute = resource: "repo.commit"; params: { baseUrl: BaseUrl; - seedingPolicy: SeedingPolicy; repo: Repo; commit: Commit; nodeAvatarUrl: string | undefined; @@ -155,7 +154,6 @@ export type RepoLoadedRoute = resource: "repo.issue"; params: { baseUrl: BaseUrl; - seedingPolicy: SeedingPolicy; repo: Repo; rawPath: (commit?: string) => string; issue: Issue; @@ -166,7 +164,6 @@ export type RepoLoadedRoute = resource: "repo.issues"; params: { baseUrl: BaseUrl; - seedingPolicy: SeedingPolicy; repo: Repo; issues: Issue[]; status: IssueState["status"]; @@ -177,7 +174,6 @@ export type RepoLoadedRoute = resource: "repo.patches"; params: { baseUrl: BaseUrl; - seedingPolicy: SeedingPolicy; repo: Repo; patches: Patch[]; status: PatchState["status"]; @@ -188,7 +184,6 @@ export type RepoLoadedRoute = resource: "repo.patch"; params: { baseUrl: BaseUrl; - seedingPolicy: SeedingPolicy; repo: Repo; rawPath: (commit?: string) => string; patch: Patch; @@ -270,10 +265,9 @@ export async function loadRepoRoute( } else if (route.resource === "repo.history") { return await loadHistoryView(route, previousLoaded); } else if (route.resource === "repo.commit") { - const [repo, commit, seedingPolicy, node] = await Promise.all([ + const [repo, commit, node] = await Promise.all([ api.repo.getByRid(route.repo), api.repo.getCommitBySha(route.repo, route.commit), - api.getPolicyByRid(route.repo), api.getNode(), ]); @@ -281,7 +275,6 @@ export async function loadRepoRoute( resource: "repo.commit", params: { baseUrl: route.node, - seedingPolicy, repo, commit, nodeAvatarUrl: node.avatarUrl, @@ -318,14 +311,13 @@ async function loadPatchesView( const searchParams = new URLSearchParams(route.search || ""); const status = (searchParams.get("status") as PatchState["status"]) || "open"; - const [repo, patches, seedingPolicy, node] = await Promise.all([ + const [repo, patches, node] = await Promise.all([ api.repo.getByRid(route.repo), api.repo.getAllPatches(route.repo, { status, page: 0, perPage: PATCHES_PER_PAGE, }), - api.getPolicyByRid(route.repo), api.getNode(), ]); @@ -333,7 +325,6 @@ async function loadPatchesView( resource: "repo.patches", params: { baseUrl: route.node, - seedingPolicy, patches, status, repo, @@ -348,14 +339,13 @@ async function loadIssuesView( const api = new HttpdClient(route.node); const status = route.status || "open"; - const [repo, issues, seedingPolicy, node] = await Promise.all([ + const [repo, issues, node] = await Promise.all([ api.repo.getByRid(route.repo), api.repo.getAllIssues(route.repo, { status, page: 0, perPage: ISSUES_PER_PAGE, }), - api.getPolicyByRid(route.repo), api.getNode(), ]); @@ -363,7 +353,6 @@ async function loadIssuesView( resource: "repo.issues", params: { baseUrl: route.node, - seedingPolicy, issues, status, repo, @@ -623,17 +612,15 @@ async function loadIssueView(route: RepoIssueRoute): Promise { route.repo }${commit ? `/${commit}` : ""}`; - const [repo, issue, seedingPolicy, node] = await Promise.all([ + const [repo, issue, node] = await Promise.all([ api.repo.getByRid(route.repo), api.repo.getIssueById(route.repo, route.issue), - api.getPolicyByRid(route.repo), api.getNode(), ]); return { resource: "repo.issue", params: { baseUrl: route.node, - seedingPolicy, repo, rawPath, issue, @@ -655,7 +642,6 @@ async function loadPatchView( let repoPromise: Promise; let patchPromise: Promise; let nodePromise: Promise>; - let seedingPolicyPromise: Promise; if ( previousLoaded.resource === "repo.patch" && @@ -664,20 +650,17 @@ async function loadPatchView( ) { repoPromise = Promise.resolve(previousLoaded.params.repo); patchPromise = Promise.resolve(previousLoaded.params.patch); - seedingPolicyPromise = Promise.resolve(previousLoaded.params.seedingPolicy); nodePromise = Promise.resolve({ avatarUrl: previousLoaded.params.nodeAvatarUrl, }); } else { repoPromise = api.repo.getByRid(route.repo); patchPromise = api.repo.getPatchById(route.repo, route.patch); - seedingPolicyPromise = api.getPolicyByRid(route.repo); nodePromise = api.getNode(); } - const [repo, patch, seedingPolicy, { avatarUrl }] = await Promise.all([ + const [repo, patch, { avatarUrl }] = await Promise.all([ repoPromise, patchPromise, - seedingPolicyPromise, nodePromise, ]); @@ -740,7 +723,6 @@ async function loadPatchView( resource: "repo.patch", params: { baseUrl: route.node, - seedingPolicy, repo, rawPath, patch, diff --git a/tests/e2e/clipboard.spec.ts b/tests/e2e/clipboard.spec.ts index fa2def234..13940d1de 100644 --- a/tests/e2e/clipboard.spec.ts +++ b/tests/e2e/clipboard.spec.ts @@ -69,8 +69,7 @@ test("copy to clipboard", async ({ page, browserName, context }) => { await page.getByRole("button", { name: "59a0821", exact: true }).click(); await expectClipboard("59a0821edc73630bce540596cffc7854da557365"); - await page.getByLabel("filter-dropdown").click(); - await page.getByRole("button", { name: "Draft" }).click(); + await page.getByRole("link", { name: "Draft" }).click(); await page.getByRole("button", { name: "783d33c", exact: true }).click(); await expectClipboard("783d33c5b14e13234d4d7affa98bd0b52d1b1ea3"); } diff --git a/tests/e2e/repo.spec.ts b/tests/e2e/repo.spec.ts index 81d678961..d42c8c997 100644 --- a/tests/e2e/repo.spec.ts +++ b/tests/e2e/repo.spec.ts @@ -21,10 +21,10 @@ test("navigate to repo", async ({ page }) => { // Header. { const name = page.getByRole("link", { name: "source-browsing" }).nth(1); - const id = page.getByText(sourceBrowsingRid); - const description = page.getByText( - "Git repository for source browsing tests", - ); + const id = page.getByLabel("repo-id"); + const description = page + .getByText("Git repository for source browsing tests") + .first(); await expect(name).toBeVisible(); await expect(id).toBeVisible(); @@ -33,7 +33,9 @@ test("navigate to repo", async ({ page }) => { // Repo menu shows default selected branch and commit and contributor counts. { - await expect(page.getByTitle("Change branch")).toBeVisible(); + await expect( + page.locator('[title="Change branch"]:visible').first(), + ).toBeVisible(); await expect( page .getByRole("button", { @@ -66,7 +68,7 @@ test("repo description", async ({ page, peer }) => { await page.goto(peer.ridUrl(rid)); await page.waitForLoadState("networkidle"); await expect( - page.getByText("Radicle Heartwood Protocol & Stack"), + page.getByText("Radicle Heartwood Protocol & Stack").first(), ).toBeVisible(); }); @@ -83,7 +85,9 @@ test("show source tree at specific revision", async ({ page }) => { }) .click(); - await expect(page.getByTitle("Current HEAD")).toContainText("335dd6d"); + await expect(page.getByTitle("Current HEAD").first()).toContainText( + "335dd6d", + ); await expect(page.locator(".source-tree")).toHaveText("bin src"); await expect( page.getByRole("link", { @@ -317,11 +321,15 @@ test("peer and branch switching", async ({ page }) => { // Alice's peer. { await changeBranch("alice", `main ${shortAliceHead}`, page); - await expect(page.getByTitle("Change branch")).toHaveText(/alice/); + await expect( + page.locator('[title="Change branch"]:visible').first(), + ).toHaveText(/alice/); // Default `main` branch. { - await expect(page.getByTitle("Change branch")).toHaveText(/main/); + await expect( + page.locator('[title="Change branch"]:visible').first(), + ).toHaveText(/main/); await expect( page .getByRole("button", { @@ -339,8 +347,11 @@ test("peer and branch switching", async ({ page }) => { // Feature branch with a slash in the name. { await changeBranch("alice", "feature/branch", page); - await page.getByTitle("Change branch").click(); - await page.getByText("feature/branch").click(); + await page.locator('[title="Change branch"]:visible').first().click(); + await page + .getByRole("button", { name: "feature/branch" }) + .first() + .click(); await expect( page.getByRole("button", { name: "feature/branch" }), @@ -379,10 +390,16 @@ test("peer and branch switching", async ({ page }) => { { await page.getByRole("link", { name: "source-browsing" }).nth(1).click(); - await expect(page.getByTitle("Change branch")).not.toContainText("alice"); - await expect(page.getByTitle("Change branch")).not.toContainText("bob"); + await expect( + page.locator('[title="Change branch"]:visible').first(), + ).not.toContainText("alice"); + await expect( + page.locator('[title="Change branch"]:visible').first(), + ).not.toContainText("bob"); - await expect(page.getByTitle("Change branch")).toBeVisible(); + await expect( + page.locator('[title="Change branch"]:visible').first(), + ).toBeVisible(); await expect( page .getByRole("button", { @@ -437,7 +454,7 @@ test("only one modal can be open at a time", async ({ page }) => { await expect(page.getByText("Use the Radicle CLI")).not.toBeVisible(); await expect(page.getByText("bob")).not.toBeVisible(); - await page.getByTitle("Change branch").click(); + await page.locator('[title="Change branch"]:visible').first().click(); await expect(page.getByText("Code font")).not.toBeVisible(); await expect(page.getByText("Use the Radicle CLI")).not.toBeVisible(); await expect(page.getByText("bob")).toBeVisible(); diff --git a/tests/e2e/repo/commit.spec.ts b/tests/e2e/repo/commit.spec.ts index 6f9a857fa..e75f9f7c4 100644 --- a/tests/e2e/repo/commit.spec.ts +++ b/tests/e2e/repo/commit.spec.ts @@ -14,7 +14,7 @@ test("navigation from commit list", async ({ page }) => { await page.goto(sourceBrowsingUrl); await changeBranch("bob", `main ${shortBobHead}`, page); - await page.getByText("Update readme").first().click(); + await page.getByRole("link", { name: "Update readme" }).first().click(); await expect(page).toHaveURL(commitUrl); }); @@ -103,13 +103,10 @@ test("navigation to source tree at specific revision", async ({ page }) => { // Go to source tree at this revision. await page.getByTitle("View file at this commit").click(); const branchSelectorCommitButton = page.getByTitle("Current HEAD").first(); - await expect( - branchSelectorCommitButton.getByText("Add a deeply nested directory tree"), - ).toBeVisible(); + await expect(branchSelectorCommitButton).toContainText("0801ace"); await expect(page).toHaveURL( `${sourceBrowsingUrl}/tree/0801aceeab500033f8d608778218657bd626ef73/deep/directory/hierarchy/is/entirely/possible/in/git/repositories/.gitkeep`, ); - await expect(branchSelectorCommitButton).toContainText("0801ace"); await expect(page.locator(".source-tree >> text=.gitkeep")).toBeVisible(); await expect( page diff --git a/tests/e2e/repo/commits.spec.ts b/tests/e2e/repo/commits.spec.ts index 50148d336..48740a1ca 100644 --- a/tests/e2e/repo/commits.spec.ts +++ b/tests/e2e/repo/commits.spec.ts @@ -23,9 +23,9 @@ test("peer and branch switching", async ({ page }) => { { await changeBranch("alice", `main ${shortAliceHead}`, page); - await expect(page.getByTitle("Change branch")).toHaveText( - "alice Delegate / main", - ); + await expect( + page.locator('[title="Change branch"]:visible').first(), + ).toHaveText("alice Delegate / main"); await expect(page.getByText("Thursday, November 17, 2022")).toBeVisible(); await expect(page.locator(".list .teaser")).toHaveCount( @@ -63,7 +63,9 @@ test("peer and branch switching", async ({ page }) => { { await changeBranch("bob", `main ${shortBobHead}`, page); - await expect(page.getByTitle("Change branch")).toContainText("bob"); + await expect( + page.locator('[title="Change branch"]:visible').first(), + ).toContainText("bob"); await expect(page.getByText("Wednesday, December 21, 2022")).toBeVisible(); await expect(page.locator(".list").first().locator(".teaser")).toHaveCount( @@ -158,7 +160,9 @@ test("relative timestamps", async ({ page }) => { await expect(page.getByText("Thursday, December 15,")).toBeVisible(); await changeBranch("bob", `main ${shortBobHead}`, page); - await expect(page.getByTitle("Change branch")).toHaveText(/bob/); + await expect( + page.locator('[title="Change branch"]:visible').first(), + ).toHaveText(/bob/); const latestCommit = page.locator(".teaser").first(); await expect(latestCommit).toContainText( `Bob Belcher committed ${shortBobHead} now`, @@ -194,7 +198,9 @@ test("pushing changes while viewing history", async ({ page, peerManager }) => { await expect(page).toHaveURL(`${alice.uiUrl()}/${rid}/history`); await expect(page.getByRole("link", { name: "Commits 2" })).toBeVisible(); - await expect(page.getByTitle("Change branch")).toHaveText("main Canonical"); + await expect( + page.locator('[title="Change branch"]:visible').first(), + ).toHaveText("main Canonical"); const branchSelectorCommitButton = page.getByTitle("Current HEAD").first(); await expect(branchSelectorCommitButton).toHaveText("516fa74 first change"); diff --git a/tests/e2e/repo/issues.spec.ts b/tests/e2e/repo/issues.spec.ts index 7df147fd2..9230ef078 100644 --- a/tests/e2e/repo/issues.spec.ts +++ b/tests/e2e/repo/issues.spec.ts @@ -7,8 +7,7 @@ test("navigate issue listing", async ({ page }) => { await page.getByRole("link", { name: "Issues 1" }).click(); await expect(page).toHaveURL(`${cobUrl}/issues`); - await page.getByRole("button", { name: "filter-dropdown" }).first().click(); - await page.getByRole("link", { name: "Closed 2" }).click(); + await page.getByRole("link", { name: "Closed" }).click(); await expect(page).toHaveURL(`${cobUrl}/issues?status=closed`); }); @@ -39,12 +38,8 @@ test("issue counters", async ({ page, peer }) => { ], { cwd: repoFolder }, ); - await page.getByRole("button", { name: "filter-dropdown" }).first().click(); - await page.locator(".dropdown-item").getByText("Open 1").click(); + await page.getByRole("link", { name: "Open" }).first().click(); await expect(page.getByRole("button", { name: "Issues 2" })).toBeVisible(); - await expect( - page.getByRole("button", { name: "filter-dropdown" }).first(), - ).toHaveText("Open 2"); await expect(page.locator(".issue-teaser")).toHaveCount(2); await page diff --git a/tests/e2e/repo/patches.spec.ts b/tests/e2e/repo/patches.spec.ts index 49239e312..0161c90a0 100644 --- a/tests/e2e/repo/patches.spec.ts +++ b/tests/e2e/repo/patches.spec.ts @@ -6,8 +6,7 @@ test("navigate patch listing", async ({ page }) => { await page.getByRole("link", { name: "Patches 2" }).click(); await expect(page).toHaveURL(`${cobUrl}/patches`); - await page.getByRole("button", { name: "filter-dropdown" }).first().click(); - await page.getByRole("link", { name: "Merged 1" }).click(); + await page.getByRole("link", { name: "Merged" }).click(); await expect(page).toHaveURL(`${cobUrl}/patches?status=merged`); await expect( page.locator(".comments").filter({ hasText: "5" }), @@ -40,11 +39,7 @@ test("patches counters", async ({ page, peer }) => { await peer.git(["push", "rad", "HEAD:refs/patches"], { cwd: repoFolder, }); - await page.getByRole("button", { name: "filter-dropdown" }).first().click(); - await page.locator(".dropdown-item").getByText("Open 1").click(); + await page.getByRole("link", { name: "Open" }).first().click(); await expect(page.getByRole("button", { name: "Patches 2" })).toBeVisible(); - await expect( - page.getByRole("button", { name: "filter-dropdown" }).first(), - ).toHaveText("Open 2"); await expect(page.locator(".patch-teaser")).toHaveCount(2); }); diff --git a/tests/support/repo.ts b/tests/support/repo.ts index d5b2227ab..f1faa3e4e 100644 --- a/tests/support/repo.ts +++ b/tests/support/repo.ts @@ -4,7 +4,7 @@ import type { RadiclePeer } from "@tests/support/peerManager"; import * as Path from "node:path"; export async function changeBranch(peer: string, branch: string, page: Page) { - await page.getByTitle("Change branch").click(); + await page.locator('[title="Change branch"]:visible').first().click(); const peerLocator = page.getByLabel("peer-item").filter({ hasText: peer }); await peerLocator.getByTitle("Expand peer").click(); await page.getByRole("button", { name: branch }).click();