Files
will.anderson cac7bd5727
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m52s
test: full Playwright + API test suite for stage
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.
2026-05-11 00:28:33 -05:00

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');
});