cac7bd5727
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m52s
159 tests across three Playwright projects (api, chromium, mobile): - tests/api/security.test.ts: security headers, CORS on /api/supabase-config (origin allowlist enforced), auth gate on /api/demo, Stripe webhook signature enforcement, source file leakage, path traversal, input validation (8000-char message cap) - tests/api/endpoints.test.ts: /api/health, /api/founding-count shape invariants, /api/supabase-config JWT shape, sitemap.xml, robots.txt, /llms.txt, /api/soul-health internal gate, 404 for unknown routes - tests/e2e/landing.spec.ts: title, h1 count, meta description, OG tags, canonical (no stage leak), JSON-LD schema, demo widget DOM presence, JS error filtering (known GTM/CSP noise excluded) - tests/e2e/seo.spec.ts: per-page title patterns, noindex on checkout, canonical URLs, sitemap production-URL enforcement - tests/e2e/checkout.spec.ts: all three plan variants, auth section, payment element, canonical - tests/e2e/chat.spec.ts: widget DOM structure, auth gate (send button disabled without session), API-level auth rejection - tests/e2e/navigation.spec.ts: all public routes return 200, 404s for removed/old paths (/terms, /enterprise-terms, /gallery), static files All 159 pass against stage. CI step added to stage.yaml after smoke test.
59 lines
2.5 KiB
TypeScript
59 lines
2.5 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
// All three plan variants must render without error
|
|
for (const plan of ['free', 'professional', 'founding']) {
|
|
test(`Checkout loads for plan=${plan}`, async ({ page }) => {
|
|
const r = await page.goto(`/checkout?plan=${plan}`);
|
|
expect(r?.status()).toBe(200);
|
|
await expect(page.locator('body')).not.toBeEmpty();
|
|
// Title must be set (not empty)
|
|
const title = await page.title();
|
|
expect(title.length).toBeGreaterThan(0);
|
|
});
|
|
}
|
|
|
|
test('Checkout professional — has "Professional" plan name in body', async ({ page }) => {
|
|
await page.goto('/checkout?plan=professional');
|
|
await expect(page.locator('body')).toContainText('Professional');
|
|
});
|
|
|
|
test('Checkout founding — has "Founding" plan name in body', async ({ page }) => {
|
|
await page.goto('/checkout?plan=founding');
|
|
await expect(page.locator('body')).toContainText('Founding');
|
|
});
|
|
|
|
test('Checkout free — mentions free or sign up in body', async ({ page }) => {
|
|
await page.goto('/checkout?plan=free');
|
|
const body = await page.locator('body').textContent();
|
|
expect(body?.toLowerCase()).toMatch(/free|sign|start|account/);
|
|
});
|
|
|
|
test('Checkout professional — auth section is present (sign in / create account)', async ({ page }) => {
|
|
await page.goto('/checkout?plan=professional');
|
|
// auth-section div is present in the DOM (may be hidden via CSS but rendered)
|
|
await expect(page.locator('#auth-section')).toBeAttached();
|
|
// Payment form is present
|
|
await expect(page.locator('#payment-form')).toBeAttached();
|
|
});
|
|
|
|
test('Checkout professional — payment element container is present', async ({ page }) => {
|
|
await page.goto('/checkout?plan=professional');
|
|
await expect(page.locator('#payment-element')).toBeAttached();
|
|
});
|
|
|
|
test('Checkout — nav has back link to homepage', async ({ page }) => {
|
|
await page.goto('/checkout?plan=professional');
|
|
// The checkout nav has both a logo link and an explicit "← Back" nav-link,
|
|
// both pointing to /. Use first() to avoid strict-mode violation.
|
|
const navLink = page.locator('nav a[href="/"]').first();
|
|
await expect(navLink).toBeAttached();
|
|
});
|
|
|
|
test('Checkout professional — canonical is production URL', async ({ page }) => {
|
|
await page.goto('/checkout?plan=professional');
|
|
const canonical = await page.locator('link[rel="canonical"]').getAttribute('href');
|
|
expect(canonical).toContain('neurontechnologies.ai');
|
|
expect(canonical).not.toContain('run.app');
|
|
expect(canonical).not.toContain('stage');
|
|
});
|