9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
Signed-off-by: cls_宁波本机 <908705107@qq.com>
229 lines
9.6 KiB
TypeScript
229 lines
9.6 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
|
|
import { waitForAnimations } from "../helpers/animations";
|
|
import { installMockBridge, TEST_IDENTITIES } from "../helpers/bridge";
|
|
import { seedActiveIdentity } from "../helpers/onboarding";
|
|
|
|
const BLANK_TYLER_IDENTITY = {
|
|
...TEST_IDENTITIES.tyler,
|
|
username: "",
|
|
};
|
|
|
|
const SHOT_DIR = "test-results/onboarding-docked-cta";
|
|
const NCRYPTSEC =
|
|
"ncryptsec1qgg9947rlpvqu76pj5ecreduf9jxhselq2nae2kghhvd5g7dgjtcxfqtd67p9m0w57lspw8gsq6yphnm8623nsl8xn9j4jdzz84zm3frztj3z7s35vpzmqf6ksu8r89qk5z2zxfmu5gv8th8wclt0h4p";
|
|
|
|
test.use({ viewport: { width: 1280, height: 800 } });
|
|
|
|
test("machine onboarding: landing, backup, setup docked CTAs", async ({
|
|
page,
|
|
}) => {
|
|
await installMockBridge(page, undefined, {
|
|
skipCommunitySeed: true,
|
|
skipOnboardingSeed: true,
|
|
});
|
|
await page.goto("/");
|
|
|
|
const gate = page.getByTestId("machine-onboarding-gate");
|
|
await expect(gate).toBeVisible();
|
|
await waitForAnimations(page);
|
|
await page.screenshot({ path: `${SHOT_DIR}/01-landing.png` });
|
|
|
|
await page.getByRole("button", { name: "Use an existing key" }).click();
|
|
await expect(
|
|
page.getByRole("heading", { name: "Enter your private key" }),
|
|
).toBeVisible();
|
|
const importCard = page.getByTestId("nostr-import-card");
|
|
await expect(importCard).toBeVisible();
|
|
await expect(page.getByLabel("Private key", { exact: true })).toBeVisible();
|
|
// The production card uses a baked nine-slice texture: no runtime SVG
|
|
// filter, measurement, or texture regeneration during resize.
|
|
await expect(importCard).toHaveCSS("background-color", "rgba(0, 0, 0, 0)");
|
|
await expect(importCard).toHaveCSS("border-top-width", "0px");
|
|
await expect(importCard).toHaveCSS("border-image-repeat", "repeat");
|
|
await expect(importCard).toHaveCSS("border-image-outset", "96px");
|
|
// Icon SVGs (e.g. the reveal toggle) are fine; a filter would mean the
|
|
// texture regressed to the runtime SVG pipeline.
|
|
await expect(importCard.locator("svg filter")).toHaveCount(0);
|
|
await waitForAnimations(page);
|
|
await page.screenshot({ path: `${SHOT_DIR}/01b-enter-key.png` });
|
|
|
|
await page.getByTestId("nostr-import-nsec-input").fill(NCRYPTSEC);
|
|
await expect(
|
|
page.getByRole("heading", { name: "Unlock your account" }),
|
|
).toBeVisible();
|
|
await expect(page.getByTestId("backup-password-timeline")).toBeVisible();
|
|
await expect(page.getByTestId("restore-ncryptsec-affordance")).toBeVisible();
|
|
await expect(page.getByTestId("restore-unlock-icon")).toBeVisible();
|
|
await expect(page.getByTestId("nostr-import-passphrase")).toBeFocused();
|
|
await waitForAnimations(page);
|
|
await page.screenshot({ path: `${SHOT_DIR}/01c-restore-backup.png` });
|
|
|
|
// The first Back returns to key selection; the second leaves import.
|
|
await page.getByRole("button", { name: "Back", exact: true }).click();
|
|
await expect(importCard).toBeVisible();
|
|
await page.getByRole("button", { name: "Back", exact: true }).click();
|
|
await expect(
|
|
page.getByRole("button", { name: "Create a new identity key" }),
|
|
).toBeVisible();
|
|
await page.getByRole("button", { name: "Create a new identity key" }).click();
|
|
await expect(
|
|
page.getByRole("heading", {
|
|
name: "Your unique identity key has been created",
|
|
}),
|
|
).toBeVisible();
|
|
await waitForAnimations(page);
|
|
await page.screenshot({ path: `${SHOT_DIR}/02-backup.png` });
|
|
|
|
// The key stays masked behind an explicit reveal toggle.
|
|
await expect(page.getByTestId("backup-key-value")).toBeVisible();
|
|
|
|
// Reveal the key: box must not reflow (same-length monospace mask).
|
|
await page.getByTestId("backup-key-reveal-toggle").click();
|
|
await expect(page.getByTestId("backup-key-value")).toHaveClass(/select-text/);
|
|
await waitForAnimations(page);
|
|
await page.screenshot({ path: `${SHOT_DIR}/02b-backup-revealed.png` });
|
|
|
|
// Backup options leave the yellow flow for the dark security view without
|
|
// adding a progress step or a generic Next action.
|
|
await page.getByTestId("backup-options-link").click();
|
|
await expect(
|
|
page.getByTestId("onboarding-page-backup-options"),
|
|
).toBeVisible();
|
|
await expect(page.getByTestId("onboarding-next")).toHaveCount(0);
|
|
const optionPanels = page.getByTestId("backup-option-panel");
|
|
await expect(optionPanels).toHaveCount(3);
|
|
await expect(
|
|
page.getByTestId("backup-options").locator(".buzz-card-textured"),
|
|
).toHaveCount(0);
|
|
await expect(optionPanels.first()).toHaveCSS("padding-left", "24px");
|
|
const titleTops = await optionPanels
|
|
.locator("span.text-lg")
|
|
.evaluateAll((titles) =>
|
|
titles.map((title) => title.getBoundingClientRect().top),
|
|
);
|
|
expect(Math.max(...titleTops) - Math.min(...titleTops)).toBeLessThan(1);
|
|
await waitForAnimations(page);
|
|
await page.screenshot({ path: `${SHOT_DIR}/02c-backup-options.png` });
|
|
|
|
await page.getByTestId("backup-option-password").click();
|
|
await expect(page.getByTestId("onboarding-page-download")).toBeVisible();
|
|
const passwordPanel = page.getByTestId("backup-password-panel");
|
|
await expect(passwordPanel).toBeVisible();
|
|
await expect(passwordPanel).not.toHaveClass(/buzz-card-textured/);
|
|
await expect(passwordPanel).toHaveCSS("padding-left", "24px");
|
|
await waitForAnimations(page);
|
|
await page.screenshot({ path: `${SHOT_DIR}/02d-backup-password.png` });
|
|
|
|
await page.getByTestId("backup-passphrase-generate").click();
|
|
const generatorPopover = page.getByRole("dialog");
|
|
await expect(generatorPopover).toBeVisible();
|
|
await expect(generatorPopover).not.toHaveClass(/buzz-card-textured/);
|
|
await waitForAnimations(page);
|
|
await page.screenshot({ path: `${SHOT_DIR}/02e-backup-generator.png` });
|
|
await page.keyboard.press("Escape");
|
|
|
|
await page.getByTestId("backup-return-to-onboarding").click();
|
|
await expect(page.getByTestId("onboarding-page-backup")).toBeVisible();
|
|
await page.getByTestId("onboarding-next").click();
|
|
await expect(
|
|
page.getByRole("heading", { name: "Set up your agent harnesses" }),
|
|
).toBeVisible();
|
|
await waitForAnimations(page);
|
|
await page.screenshot({ path: `${SHOT_DIR}/03-setup.png` });
|
|
});
|
|
|
|
test("machine key import remains usable in a short viewport", async ({
|
|
page,
|
|
}) => {
|
|
await page.setViewportSize({ width: 900, height: 620 });
|
|
await installMockBridge(page, undefined, {
|
|
skipCommunitySeed: true,
|
|
skipOnboardingSeed: true,
|
|
});
|
|
await page.goto("/");
|
|
await page.getByRole("button", { name: "Use an existing key" }).click();
|
|
|
|
const heading = page.getByRole("heading", { name: "Enter your private key" });
|
|
const input = page.getByLabel("Private key", { exact: true });
|
|
const footer = page.getByTestId("onboarding-footer-slot");
|
|
await expect(heading).toBeVisible();
|
|
await expect(input).toBeVisible();
|
|
await expect(footer).toBeVisible();
|
|
|
|
const layout = await page.evaluate(() => {
|
|
const heading = document.querySelector("h1")?.getBoundingClientRect();
|
|
const input = document
|
|
.querySelector<HTMLInputElement>("#nostr-private-key")
|
|
?.getBoundingClientRect();
|
|
const footer = document
|
|
.querySelector('[data-testid="onboarding-footer-slot"]')
|
|
?.getBoundingClientRect();
|
|
return {
|
|
footerTop: footer?.top ?? 0,
|
|
headingBottom: heading?.bottom ?? 0,
|
|
inputBottom: input?.bottom ?? 0,
|
|
inputTop: input?.top ?? 0,
|
|
clientWidth: document.documentElement.clientWidth,
|
|
scrollHeight: document.documentElement.scrollHeight,
|
|
scrollWidth: document.documentElement.scrollWidth,
|
|
};
|
|
});
|
|
expect(layout.inputTop).toBeGreaterThan(layout.headingBottom);
|
|
expect(layout.footerTop).toBeGreaterThan(layout.inputBottom);
|
|
expect(layout.scrollHeight).toBeGreaterThanOrEqual(620);
|
|
expect(layout.scrollWidth).toBe(layout.clientWidth);
|
|
});
|
|
|
|
test("backup options keep one-column geometry on narrow windows", async ({
|
|
page,
|
|
}) => {
|
|
await page.setViewportSize({ width: 600, height: 700 });
|
|
await installMockBridge(page, undefined, {
|
|
skipCommunitySeed: true,
|
|
skipOnboardingSeed: true,
|
|
});
|
|
await page.goto("/");
|
|
await page.getByRole("button", { name: "Create a new identity key" }).click();
|
|
await expect(
|
|
page.getByRole("heading", {
|
|
name: "Your unique identity key has been created",
|
|
}),
|
|
).toBeVisible();
|
|
await page.getByTestId("backup-options-link").click();
|
|
|
|
const panels = page.getByTestId("backup-option-panel");
|
|
await expect(panels).toHaveCount(3);
|
|
const geometry = await panels.evaluateAll((elements) => ({
|
|
clientWidth: document.documentElement.clientWidth,
|
|
lefts: elements.map((element) => element.getBoundingClientRect().left),
|
|
rights: elements.map((element) => element.getBoundingClientRect().right),
|
|
scrollWidth: document.documentElement.scrollWidth,
|
|
}));
|
|
expect(new Set(geometry.lefts.map(Math.round)).size).toBe(1);
|
|
expect(geometry.lefts.every((left) => left >= 0)).toBe(true);
|
|
expect(geometry.rights.every((right) => right <= geometry.clientWidth)).toBe(
|
|
true,
|
|
);
|
|
expect(geometry.scrollWidth).toBe(geometry.clientWidth);
|
|
});
|
|
|
|
test("relay onboarding: profile and avatar docked CTAs", async ({ page }) => {
|
|
await seedActiveIdentity(page, BLANK_TYLER_IDENTITY);
|
|
await installMockBridge(page, undefined, { skipOnboardingSeed: true });
|
|
await page.goto("/");
|
|
|
|
await expect(page.getByTestId("onboarding-page-1")).toBeVisible();
|
|
await page.getByTestId("onboarding-display-name").fill("Ada Lovelace");
|
|
await waitForAnimations(page);
|
|
await page.screenshot({ path: `${SHOT_DIR}/04-profile.png` });
|
|
|
|
await page.getByTestId("onboarding-next").click();
|
|
await expect(page.getByTestId("onboarding-page-avatar")).toBeVisible();
|
|
await page
|
|
.getByTestId("onboarding-avatar-url")
|
|
.fill("https://example.com/onboarding-avatar.png");
|
|
await waitForAnimations(page);
|
|
await page.screenshot({ path: `${SHOT_DIR}/05-avatar.png` });
|
|
});
|