diff --git a/.github/workflows/check-e2e.yml b/.github/workflows/check-e2e.yml index 3425ed187..f2f86b6df 100644 --- a/.github/workflows/check-e2e.yml +++ b/.github/workflows/check-e2e.yml @@ -28,7 +28,7 @@ jobs: - name: Install Playwright browsers if: steps.playwright-dep-cache.outputs.cache-hit != 'true' - run: npx playwright install webkit + run: npx playwright install webkit chromium - name: Install Radicle binaries run: | diff --git a/src/components/Header.svelte b/src/components/Header.svelte index 1c5e9b4f6..a02f88f9e 100644 --- a/src/components/Header.svelte +++ b/src/components/Header.svelte @@ -94,7 +94,7 @@ {#snippet toggle(onclick)} - + {/snippet} diff --git a/src/components/Id.svelte b/src/components/Id.svelte index c3ca99a63..d90e94b9f 100644 --- a/src/components/Id.svelte +++ b/src/components/Id.svelte @@ -2,7 +2,7 @@ import type { ComponentProps, Snippet } from "svelte"; import { debounce } from "lodash"; - import { writeText } from "@tauri-apps/plugin-clipboard-manager"; + import { writeToClipboard } from "@app/lib/invoke"; import { formatOid } from "@app/lib/utils"; @@ -18,7 +18,7 @@ }, 1000); async function copy() { - await writeText(clipboard); + await writeToClipboard(clipboard); icon = "checkmark"; tooltip = "Copied to clipboard"; restoreIcon(); diff --git a/src/components/NakedButton.svelte b/src/components/NakedButton.svelte index a72f6eb2b..3fd4b431d 100644 --- a/src/components/NakedButton.svelte +++ b/src/components/NakedButton.svelte @@ -3,11 +3,12 @@ interface Props { children: Snippet; + title?: string; variant: "primary" | "secondary" | "ghost"; onclick?: () => void; } - const { children, variant, onclick }: Props = $props(); + const { children, title, variant, onclick }: Props = $props(); const style = $derived( `--button-color-1: var(--color-fill-${variant});` + @@ -203,7 +204,7 @@ -
+
diff --git a/src/components/RepoCard.svelte b/src/components/RepoCard.svelte index ca80eff0e..d6076da01 100644 --- a/src/components/RepoCard.svelte +++ b/src/components/RepoCard.svelte @@ -52,6 +52,7 @@ {/if}
( }); } } + +export async function writeToClipboard( + text: string, + opts?: { + label?: string; + }, +) { + if (window.__TAURI_INTERNALS__) { + await tauri.invoke("plugin:clipboard-manager|write_text", { + label: opts?.label, + text, + }); + } else { + await navigator.clipboard.writeText(text); + } +} diff --git a/tests/e2e/clipboard.spec.ts b/tests/e2e/clipboard.spec.ts new file mode 100644 index 000000000..3828beca6 --- /dev/null +++ b/tests/e2e/clipboard.spec.ts @@ -0,0 +1,31 @@ +import { chromium } from "playwright"; + +import { expect, markdownRid, test } from "@tests/support/fixtures.js"; + +// We explicitly run all clipboard tests withing the context of a single test +// so that we don't run into race conditions, because there is no way to isolate +// the clipboard in Playwright yet. +test("copy to clipboard", async () => { + const browser = await chromium.launch(); + const context = await browser.newContext(); + await context.grantPermissions(["clipboard-read", "clipboard-write"]); + const page = await context.newPage(); + + await page.goto("/repos"); + + // Reset system clipboard to a known state. + await page.evaluate("navigator.clipboard.writeText('')"); + + // Repo ID. + { + await page.getByLabel("repo-id").first().click(); + const clipboardContent = await page.evaluate( + "navigator.clipboard.readText()", + ); + expect(clipboardContent).toBe(markdownRid); + } + + // Clear the system clipboard contents so developers don't wonder why there's + // random stuff in their clipboard after running tests. + await page.evaluate("navigator.clipboard.writeText('')"); +}); diff --git a/tests/e2e/repo/issue.spec.ts b/tests/e2e/repo/issue.spec.ts new file mode 100644 index 000000000..23861a3aa --- /dev/null +++ b/tests/e2e/repo/issue.spec.ts @@ -0,0 +1,8 @@ +import { test, expect, cobRid } from "@tests/support/fixtures.js"; + +test("navigate single issue", async ({ page }) => { + await page.goto(`/repos/${cobRid}/issues?status=all`); + await page.getByText("This title has **markdown**").click(); + + await expect(page).toHaveURL(/\/issues\/[0-9a-f]{40}/); +}); diff --git a/tests/e2e/repo/issues.spec.ts b/tests/e2e/repo/issues.spec.ts new file mode 100644 index 000000000..a6084fcfd --- /dev/null +++ b/tests/e2e/repo/issues.spec.ts @@ -0,0 +1,10 @@ +import { test, cobRid, expect } from "@tests/support/fixtures.js"; + +test("navigate issues listing", async ({ page }) => { + await page.goto(`/repos/${cobRid}/issues?show=all`); + await expect(page.locator(".issue-teaser")).toHaveCount(3); + + await page.getByRole("link", { name: "Closed" }).click(); + await expect(page.locator(".issue-teaser")).toHaveCount(2); + await expect(page).toHaveURL(`/repos/${cobRid}/issues?status=closed`); +}); diff --git a/tests/e2e/theme.spec.ts b/tests/e2e/theme.spec.ts new file mode 100644 index 000000000..8d0cde7d5 --- /dev/null +++ b/tests/e2e/theme.spec.ts @@ -0,0 +1,32 @@ +import { test, expect } from "@tests/support/fixtures.js"; + +test("default theme", async ({ page }) => { + await page.goto("/repos"); + + await expect(page.locator("html")).toHaveAttribute("data-theme", "dark"); +}); + +test("theme persistence", async ({ page }) => { + await page.goto("/repos"); + await expect(page.getByRole("button", { name: "markdown" })).toBeVisible(); + await page.getByRole("button", { name: "Settings" }).click(); + + await page.getByRole("button", { name: "Light", exact: true }).click(); + await expect(page.locator("html")).toHaveAttribute("data-theme", "light"); + + await page.reload(); + + await expect(page.locator("html")).toHaveAttribute("data-theme", "light"); +}); + +test("change theme", async ({ page }) => { + await page.goto("/repos"); + await expect(page.getByRole("button", { name: "markdown" })).toBeVisible(); + await page.getByRole("button", { name: "Settings" }).click(); + + await page.getByRole("button", { name: "Light", exact: true }).click(); + await expect(page.locator("html")).toHaveAttribute("data-theme", "light"); + + await page.getByRole("button", { name: "Dark", exact: true }).click(); + await expect(page.locator("html")).toHaveAttribute("data-theme", "dark"); +});