import { render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, expect, it } from 'vitest';
import { ApproveWithProof } from '../../src/marketing/home/ApproveWithProof.tsx';
import { ClosingCta } from '../../src/marketing/home/ClosingCta.tsx';
import { Examples } from '../../src/marketing/home/Examples.tsx';
import { Faq } from '../../src/marketing/home/Faq.tsx';
import { Hero } from '../../src/marketing/home/Hero.tsx';
import { PricingTeaser } from '../../src/marketing/home/PricingTeaser.tsx';
import { ProofStrip } from '../../src/marketing/home/ProofStrip.tsx';
import { SafeByDesign } from '../../src/marketing/home/SafeByDesign.tsx';
import { SectionHeader } from '../../src/marketing/home/SectionHeader.tsx';
import { SurfacesShowcase } from '../../src/marketing/home/SurfacesShowcase.tsx';
import { WatchTheWork } from '../../src/marketing/home/WatchTheWork.tsx';
import HomePage from '../../app/(marketing)/page.tsx';
import { PRICING_CONFIRMED } from '../../src/marketing/content.ts';
describe('SectionHeader', () => {
it('has an eyebrow, a heading and a supporting sentence', () => {
render();
expect(screen.getByRole('heading', { level: 2, name: 'Talk. Watch. Decide.' })).toBeInTheDocument();
expect(screen.getByText('One screen')).toBeInTheDocument();
expect(screen.getByText('All in one place.')).toBeInTheDocument();
});
});
describe('Hero', () => {
it('leads with the promise as the page heading', () => {
render();
expect(screen.getByRole('heading', { level: 1, name: 'Your website, handled.' })).toBeInTheDocument();
expect(screen.getByText('Tell Wursor what to change. Watch it work. Approve what you like.')).toBeInTheDocument();
});
it('sends Get started to sign-up and See how it works to the how-it-works page (PX-13)', () => {
render();
expect(screen.getByRole('link', { name: 'Get started' })).toHaveAttribute('href', '/signup');
expect(screen.getByRole('link', { name: 'See how it works' })).toHaveAttribute('href', '/how-it-works');
});
it('announces the approval feature, linking to how approval works', () => {
render();
expect(screen.getByRole('link', { name: /Approve with proof: nine checks on every change/ })).toHaveAttribute('href', '/trust');
});
it('says who it is for', () => {
render();
expect(screen.getByText('For WordPress sites · We host it, or you do')).toBeInTheDocument();
});
it('shows the product as a picture, hidden from assistive technology and not focusable', () => {
const { container } = render();
const visual = container.querySelector('.m-hero-visual [aria-hidden="true"]');
expect(visual).not.toBeNull();
expect(visual).toHaveAttribute('inert');
});
});
describe('ProofStrip', () => {
it('shows the three proof points', () => {
render();
expect(screen.getByText('9')).toBeInTheDocument();
expect(screen.getByText('checks before anything goes live')).toBeInTheDocument();
expect(screen.getByText('1 click')).toBeInTheDocument();
expect(screen.getByText('to undo any change, any time')).toBeInTheDocument();
expect(screen.getByText('Every step')).toBeInTheDocument();
expect(screen.getByText('on the record, in plain words')).toBeInTheDocument();
});
});
describe('SurfacesShowcase', () => {
it('starts on the Conversation tab with its caption', () => {
render();
expect(screen.getByRole('tab', { name: 'Conversation' })).toHaveAttribute('aria-selected', 'true');
expect(screen.getByText('Conversation: ask in plain words. Wursor explains each step as it goes.')).toBeInTheDocument();
});
it('switches to the Preview and Board views and their captions', async () => {
render();
await userEvent.click(screen.getByRole('tab', { name: 'Preview' }));
expect(screen.getByRole('tab', { name: 'Preview' })).toHaveAttribute('aria-selected', 'true');
expect(screen.getByText('Preview: see the real page on a phone, tablet and laptop before anything goes live.')).toBeInTheDocument();
await userEvent.click(screen.getByRole('tab', { name: 'Board' }));
expect(screen.getByText('Board: every change in one place, from idea to published.')).toBeInTheDocument();
});
it('moves between tabs with the arrow keys', async () => {
render();
screen.getByRole('tab', { name: 'Conversation' }).focus();
await userEvent.keyboard('{ArrowRight}');
expect(screen.getByRole('tab', { name: 'Preview' })).toHaveAttribute('aria-selected', 'true');
});
});
describe('WatchTheWork', () => {
it('shows the same change at three levels of detail', () => {
render();
expect(screen.getByRole('heading', { level: 2, name: /Watch the work, your way\./ })).toBeInTheDocument();
// The design words the heading more briefly on a phone; both are in the page and CSS shows one.
expect(screen.getByText('Watch every step.')).toHaveClass('m-m');
for (const [name, text] of [
['Headline', 'Only what changed and what needs you.'],
['Steps', 'Each step, in a sentence. The default.'],
['Everything', 'Every look, edit, check and decision.'],
] as const) {
expect(screen.getByRole('heading', { level: 3, name })).toBeInTheDocument();
expect(screen.getByText(text)).toBeInTheDocument();
}
});
});
describe('ApproveWithProof', () => {
it('lists what the owner gets and links to how approval works', () => {
render();
expect(screen.getByRole('heading', { level: 2, name: 'See it before it’s real.' })).toBeInTheDocument();
expect(screen.getByText('Nine checks, written in plain words')).toBeInTheDocument();
expect(screen.getByText('Looks right on a phone, a tablet and a laptop')).toBeInTheDocument();
expect(screen.getByText('One click to approve and publish, one click to undo')).toBeInTheDocument();
expect(screen.getByRole('link', { name: 'How approval works' })).toHaveAttribute('href', '/trust');
});
});
describe('SafeByDesign', () => {
it('states the four rules', () => {
render();
expect(screen.getByRole('heading', { level: 2, name: 'Nothing goes live until you say so.' })).toBeInTheDocument();
for (const rule of ['Works on a safe copy', 'Nothing goes live without you', 'Everything is recorded', 'Undo in one click']) {
expect(screen.getByRole('heading', { level: 3, name: rule })).toBeInTheDocument();
}
});
});
describe('Examples', () => {
it('shows five small jobs, each with a before and after', () => {
render();
const list = screen.getByRole('list', { name: 'Examples' });
const items = within(list).getAllByRole('listitem');
expect(items).toHaveLength(5);
expect(within(items[0]!).getByText('Update the holiday hours')).toBeInTheDocument();
expect(within(items[0]!).getByText('Page · 2 min')).toBeInTheDocument();
expect(within(items[0]!).getByText('Before')).toBeInTheDocument();
expect(within(items[0]!).getByText('After')).toBeInTheDocument();
});
});
describe('PricingTeaser', () => {
it('does not show prices or allowances until they are decided (PRD PX-13, D-HL-8)', () => {
expect(PRICING_CONFIRMED).toBe(false);
render();
expect(screen.getByRole('heading', { level: 2, name: 'Simple plans. No surprises.' })).toBeInTheDocument();
expect(screen.queryByText(/\$\d/)).not.toBeInTheDocument();
expect(screen.queryByText(/free for 14 days/i)).not.toBeInTheDocument();
expect(screen.queryByText(/sites ·/)).not.toBeInTheDocument();
expect(screen.getByText('Plans and prices will be published before launch.')).toBeInTheDocument();
});
it('still leads to sign-up and the pricing page', () => {
render();
expect(screen.getByRole('link', { name: 'Get started' })).toHaveAttribute('href', '/signup');
expect(screen.getByRole('link', { name: 'See pricing' })).toHaveAttribute('href', '/pricing');
});
it('shows the plan cards when pricing is confirmed', () => {
render();
for (const plan of ['Starter', 'Studio', 'Agency']) {
expect(screen.getByRole('heading', { level: 3, name: plan })).toBeInTheDocument();
}
expect(screen.getAllByRole('link', { name: 'Choose plan' })).toHaveLength(3);
});
});
describe('Faq', () => {
it('lists the questions, with the first answered and open', () => {
render();
expect(screen.getByRole('heading', { level: 2, name: 'Questions, answered.' })).toBeInTheDocument();
const first = screen.getByText('Will Wursor change my live site without asking?').closest('details');
expect(first).toHaveAttribute('open');
expect(screen.getByText(/Nothing goes live until you review the preview and approve it\./)).toBeInTheDocument();
for (const q of ['Do I need my own hosting?', 'Can I undo a change?', 'Does it work with my theme and plugins?', 'How is my site kept safe?']) {
expect(screen.getByText(q).closest('details')).not.toHaveAttribute('open');
}
});
it('opens an item to show its answer', async () => {
render();
await userEvent.click(screen.getByText('Do I need my own hosting?'));
expect(screen.getByText('Do I need my own hosting?').closest('details')).toHaveAttribute('open');
});
});
describe('ClosingCta', () => {
it('ends with one clear action', () => {
render();
expect(screen.getByRole('heading', { level: 2, name: 'Hand off the busywork. Keep the final say.' })).toBeInTheDocument();
expect(screen.getByRole('link', { name: 'Get started' })).toHaveAttribute('href', '/signup');
});
});
describe('HomePage', () => {
it('has one page heading, a nav and a footer', () => {
render();
expect(screen.getAllByRole('heading', { level: 1 })).toHaveLength(1);
expect(screen.getByRole('navigation', { name: 'Primary' })).toBeInTheDocument();
expect(screen.getByRole('contentinfo')).toBeInTheDocument();
expect(screen.getByRole('main')).toBeInTheDocument();
});
it('routes every call to action to sign-up or sign-in (PX-13)', () => {
render();
const ctas = [...screen.getAllByRole('link', { name: /^(Get started|Sign in)$/ })];
expect(ctas.length).toBeGreaterThanOrEqual(3);
for (const a of ctas) expect(['/signup', '/signin']).toContain(a.getAttribute('href'));
});
});