diff --git a/desktop/src/features/projects/hooks.ts b/desktop/src/features/projects/hooks.ts index d121115a69..6fc0c60891 100644 --- a/desktop/src/features/projects/hooks.ts +++ b/desktop/src/features/projects/hooks.ts @@ -2,6 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import * as React from "react"; import { relayClient } from "@/shared/api/relayClient"; +import { getRelaySelf } from "@/features/moderation/lib/relaySelf"; import { getCachedRelayOrigin } from "@/shared/lib/mediaUrl"; import { signRelayEvent } from "@/shared/api/tauri"; import { getIdentity } from "@/shared/api/tauriIdentity"; @@ -345,9 +346,17 @@ function eventToRepoState(event: RelayEvent): RepoState { } async function fetchRepoState(project: Project): Promise { + const relaySelf = await getRelaySelf(); + const trustedAuthors = [ + ...new Set( + [project.owner, relaySelf].filter((value): value is string => + Boolean(value), + ), + ), + ]; const events = await relayClient.fetchEvents({ kinds: [KIND_REPO_STATE], - authors: [project.owner], + authors: trustedAuthors, "#d": [project.dtag], limit: 1, }); diff --git a/desktop/src/testing/e2eBridge.ts b/desktop/src/testing/e2eBridge.ts index d79d2f6cf7..61bb1117b1 100644 --- a/desktop/src/testing/e2eBridge.ts +++ b/desktop/src/testing/e2eBridge.ts @@ -4748,6 +4748,36 @@ function mulberry32(seed: number) { } let mockProjectEventStore: RelayEvent[] | null = null; +const MOCK_PROJECT_BRANCHES_KEY = "buzz-e2e-project-branches"; + +function readMockProjectBranches(): Record> { + try { + return JSON.parse( + window.sessionStorage.getItem(MOCK_PROJECT_BRANCHES_KEY) ?? "{}", + ); + } catch { + return {}; + } +} + +function writeMockProjectBranch( + dtag: string, + branch: string, + commit: string | null, +) { + const projects = readMockProjectBranches(); + const branches = { ...(projects[dtag] ?? {}) }; + if (commit) { + branches[branch] = commit; + } else { + delete branches[branch]; + } + projects[dtag] = branches; + window.sessionStorage.setItem( + MOCK_PROJECT_BRANCHES_KEY, + JSON.stringify(projects), + ); +} function buildMockProjectEvents(): RelayEvent[] { const events: RelayEvent[] = []; @@ -4795,8 +4825,11 @@ function buildMockProjectEvents(): RelayEvent[] { }`, ], ["refs/heads/main", "0123456789abcdef0123456789abcdef01234567"], + ...Object.entries(readMockProjectBranches()[seed.dtag] ?? {}).map( + ([branch, commit]) => [`refs/heads/${branch}`, commit], + ), ], - owner, + getConfig()?.mock?.relaySelf ?? owner, now - projectIndex, `mock-repo-state-${seed.dtag}`.replace(/[^a-zA-Z0-9]/g, ""), ), @@ -9427,6 +9460,9 @@ export function maybeInstallE2eTauriMocks() { ]); repoState.created_at = Math.floor(Date.now() / 1000); } + if (dtag) { + writeMockProjectBranch(dtag, input.newBranch, input.expectedCommit); + } return { branch: input.newBranch, commit: input.expectedCommit, @@ -9455,6 +9491,9 @@ export function maybeInstallE2eTauriMocks() { ); repoState.created_at = Math.floor(Date.now() / 1000); } + if (dtag) { + writeMockProjectBranch(dtag, input.branch, null); + } return { branch: input.branch, commit: input.expectedCommit, diff --git a/desktop/tests/e2e/project-pr-review.spec.ts b/desktop/tests/e2e/project-pr-review.spec.ts index d028f3e831..48289ceeee 100644 --- a/desktop/tests/e2e/project-pr-review.spec.ts +++ b/desktop/tests/e2e/project-pr-review.spec.ts @@ -712,7 +712,10 @@ test("project branches can be created from the selected remote branch", async ({ page, }) => { await enableProjectsFeature(page); - await installMockBridge(page, { projectHeadBranch: "master" }); + await installMockBridge(page, { + projectHeadBranch: "master", + relaySelf: TEST_IDENTITIES.bob.pubkey, + }); await openBuzzProject(page); await page.getByRole("button", { name: /main/ }).click(); @@ -745,6 +748,12 @@ test("project branches can be created from the selected remote branch", async ({ () => window.__BUZZ_E2E_COMMANDS__ ?? [], ); expect(commands).toContain("create_project_remote_branch"); + + await openBuzzProject(page); + await page.getByRole("button", { name: /main/ }).click(); + await expect( + page.getByRole("menuitemradio", { name: "feature/branch-management" }), + ).toBeVisible(); }); test("project branches can be deleted but the default branch cannot", async ({ diff --git a/desktop/tests/helpers/bridge.ts b/desktop/tests/helpers/bridge.ts index b5a3edadfa..71bb7ba6e3 100644 --- a/desktop/tests/helpers/bridge.ts +++ b/desktop/tests/helpers/bridge.ts @@ -129,6 +129,8 @@ export type MockAgentMemoryListing = { type MockBridgeOptions = { /** Advertised HEAD for the first mock project without adding that branch. */ projectHeadBranch?: string; + /** Relay NIP-11 identity used to sign authoritative repository state. */ + relaySelf?: string | null; /** Builderlab account returned by hosted-community onboarding. Null/omitted = signed out. */ builderlabAuth?: { email?: string; name?: string; expiresAt: string } | null; /** Bound Builderlab Nostr identity. Null/omitted = not linked yet. */