Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
"start": "next start",
"lint": "next lint",
"test:e2e": "playwright test",
"test:visual": "playwright test tests/e2e/vrt.spec.ts --project=chromium",
"test:visual:update": "playwright test tests/e2e/vrt.spec.ts --project=chromium --update-snapshots"
"test:e2e:update": "playwright test --update-snapshots",
"test:visual": "playwright test tests/e2e/checkout.visual.spec.ts tests/e2e/create-payment.visual.spec.ts",
"test:visual:update": "playwright test tests/e2e/checkout.visual.spec.ts tests/e2e/create-payment.visual.spec.ts --update-snapshots"
},
"dependencies": {
"@ducanh2912/next-pwa": "^10.2.9",
Expand Down
32 changes: 24 additions & 8 deletions frontend/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,48 @@
import { defineConfig, devices } from "@playwright/test";

const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";

export default defineConfig({
testDir: "./tests/e2e",
fullyParallel: true,
retries: process.env.CI ? 2 : 0,
reporter: process.env.CI ? "github" : "list",
forbidOnly: !!process.env.CI,
snapshotPathTemplate:
"{testDir}/__screenshots__/{testFilePath}/{arg}-{projectName}{ext}",
expect: {
toHaveScreenshot: {
animations: "disabled",
caret: "hide",
scale: "css",
maxDiffPixelRatio: 0.01,
},
},
use: {
baseURL: "http://127.0.0.1:3000",
locale: "en-US",
timezoneId: "UTC",
viewport: {
width: 1440,
height: 1200,
},
trace: "on-first-retry",
},
webServer: {
command: "npm run dev",
command: `${npmCommand} run dev -- --hostname 127.0.0.1 --port 3000`,
url: "http://127.0.0.1:3000",
reuseExistingServer: true,
timeout: 120 * 1000,
},
projects: [
{ name: "chromium", use: { ...devices["Desktop Chrome"] } },
{ name: "firefox", use: { ...devices["Desktop Firefox"] } },
{ name: "webkit", use: { ...devices["Desktop Safari"] } },
{
name: "desktop-chrome",
use: {
...devices["Desktop Chrome"],
viewport: { width: 1440, height: 1100 },
},
},
{
name: "mobile-chrome",
use: {
...devices["Pixel 7"],
},
},
],
});
4 changes: 2 additions & 2 deletions frontend/src/app/(authenticated)/dashboard/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default async function CreatePaymentPage() {
const t = await getTranslations("createPaymentPage");

return (
<main className="mx-auto flex min-h-screen max-w-lg flex-col justify-center gap-10 px-6 py-16">
<main className="mx-auto flex min-h-screen w-full min-w-0 max-w-lg flex-col justify-center gap-10 px-6 py-16">
<header className="flex flex-col gap-3 text-center">
<p className="font-mono text-xs uppercase tracking-[0.3em] text-mint">
{t("eyebrow")}
Expand All @@ -25,7 +25,7 @@ export default async function CreatePaymentPage() {
</p>
</header>

<div className="rounded-3xl border border-white/10 bg-white/5 p-8 shadow-2xl backdrop-blur">
<div className="min-w-0 rounded-3xl border border-white/10 bg-white/5 p-8 shadow-2xl backdrop-blur">
<CreatePaymentForm />
</div>

Expand Down
8 changes: 4 additions & 4 deletions frontend/src/app/(authenticated)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ export default function AuthenticatedLayout({

return (
<AuthGuard>
<div className="flex min-h-screen bg-black">
<div className="flex min-h-screen overflow-x-hidden bg-black">
{/* Sidebar - fixed width for desktop layout offset */}
<Sidebar />
<MobileNav />
<PaymentToastListener />

{/* Main Content Area */}
<main className="flex-1 transition-all lg:pl-[260px]">
<div className="mx-auto flex max-w-7xl flex-col p-6 lg:p-10">
<main className="min-w-0 flex-1 overflow-x-hidden transition-all lg:pl-[260px]">
<div className="mx-auto flex w-full min-w-0 max-w-7xl flex-col p-6 lg:p-10">
{/* Header with Breadcrumbs */}
<header className="mb-10 flex flex-col gap-6">
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
Expand All @@ -41,7 +41,7 @@ export default function AuthenticatedLayout({
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, ease: "easeOut" }}
className="pb-20 lg:pb-0"
className="min-w-0 pb-20 lg:pb-0"
>
{children}
</motion.section>
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

:root {
color-scheme: light;
--font-sans: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
--font-mono: "Cascadia Code", Consolas, "SFMono-Regular", monospace;
}

:root.dark {
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ import ToastProvider from "@/components/ToastProvider";
import CommandPalette from "@/components/CommandPalette";
import KeyboardShortcuts from "@/components/KeyboardShortcuts";
import { WalletContextProvider } from "@/lib/wallet-context";
import { Metadata, Viewport } from "next";

const spaceGrotesk = Space_Grotesk({ subsets: ["latin"], variable: "--font-sans", display: "swap" });
const spaceMono = Space_Mono({ subsets: ["latin"], weight: ["400", "700"], variable: "--font-mono", display: "swap" });

import { Metadata, Viewport } from "next";

export const metadata: Metadata = {
title: "Stellar Payment Dashboard",
description: "Accept Stellar payments with simple links and status tracking.",
Expand Down
18 changes: 10 additions & 8 deletions frontend/src/components/CreatePaymentForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -278,15 +278,19 @@ function SuccessCard({ created, onReset, t }: SuccessCardProps) {

export default function CreatePaymentForm() {
const t = useTranslations("createPaymentForm");
const [amount, setAmount] = useState("");
const [asset, setAsset] = useState<"XLM" | "USDC">("XLM");
const [recipient, setRecipient] = useState("");
const [description, setDescription] = useState("");
const [amount, setAmount] = useLocalStorage("payment_amount", "");
const [asset, setAsset] = useLocalStorage<"XLM" | "USDC">(
"payment_asset",
"XLM",
);
const [recipient, setRecipient] = useLocalStorage("payment_recipient", "");
const [description, setDescription] = useLocalStorage(
"payment_description",
"",
);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [created, setCreated] = useState<CreatedPayment | null>(null);


const apiKey = useMerchantApiKey();
const hydrated = useMerchantHydrated();
const trustedAddresses = useMerchantTrustedAddresses();
Expand All @@ -302,8 +306,6 @@ export default function CreatePaymentForm() {
"payment_trusted_address",
"",
);


useHydrateMerchantStore();

// ── Rate-limit countdown ──────────────────────────────────
Expand Down
68 changes: 0 additions & 68 deletions frontend/tests/e2e/checkout-branding.spec.ts

This file was deleted.

27 changes: 27 additions & 0 deletions frontend/tests/e2e/checkout.visual.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { expect, test } from "@playwright/test";
import {
checkoutPaymentId,
expectNoHorizontalOverflow,
mockCheckoutPayment,
prepareVisualSnapshot,
} from "./helpers/fixtures";

test.describe("Checkout Visual Regression", () => {
test.beforeEach(async ({ page }) => {
await mockCheckoutPayment(page);
await page.goto(`/pay/${checkoutPaymentId}`);
await prepareVisualSnapshot(page);
});

test("checkout layout remains stable across viewports", async ({ page }) => {
const checkoutMain = page.locator("main");
await expect(checkoutMain).toBeVisible();
await expect(page.getByText("Complete Payment")).toBeVisible();
await expect(page.getByText("Styled payment")).toBeVisible();

const noOverflow = await expectNoHorizontalOverflow(page);
expect(noOverflow).toBeTruthy();

await expect(checkoutMain).toHaveScreenshot("checkout-page.png");
});
});
28 changes: 28 additions & 0 deletions frontend/tests/e2e/create-payment.visual.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { expect, test } from "@playwright/test";
import {
expectNoHorizontalOverflow,
prepareVisualSnapshot,
seedMerchantSession,
} from "./helpers/fixtures";

test.describe("Create Payment Visual Regression", () => {
test.beforeEach(async ({ page }) => {
await seedMerchantSession(page);
await page.goto("/dashboard/create");
await prepareVisualSnapshot(page);
});

test("create payment form remains visually stable", async ({ page }) => {
const heading = page.getByRole("heading", { name: "Create Payment Link" });
const formShell = heading.locator("xpath=ancestor::main[1]");

await expect(heading).toBeVisible();
await expect(formShell).toBeVisible();
await expect(page.locator("select#trusted-address")).toBeVisible();

const noOverflow = await expectNoHorizontalOverflow(page);
expect(noOverflow).toBeTruthy();

await expect(formShell).toHaveScreenshot("create-payment-form.png");
});
});
Loading
Loading