diff --git a/craft/package.json b/craft/package.json index 873885c..d3acd5f 100644 --- a/craft/package.json +++ b/craft/package.json @@ -9,6 +9,7 @@ "preview": "vite preview", "test": "playwright test tests/site-builder.spec.ts --reporter=list", "test:headed": "playwright test tests/site-builder.spec.ts --reporter=list --headed", + "test:e2e:sitesmith": "playwright test tests/sitesmith.spec.ts --reporter=list", "test:unit": "vitest run", "test:unit:watch": "vitest" }, diff --git a/craft/tests/sitesmith.spec.ts b/craft/tests/sitesmith.spec.ts new file mode 100644 index 0000000..c7bbd4d --- /dev/null +++ b/craft/tests/sitesmith.spec.ts @@ -0,0 +1,72 @@ +import { test, expect } from '@playwright/test'; + +/** + * Sitesmith E2E. Requires staging users pre-created on whp-staging: + * - sitesmith_disabled (no entitlement) + * - sitesmith_enabled (sitesmith_enabled=1, cap=50, 0 used) + * - sitesmith_capped (sitesmith_enabled=1, cap=2, 2 used) + * - sitesmith_bonus (sitesmith_enabled=0, bonus=2) + * + * Env: + * PLAYWRIGHT_BASE_URL=https://192.168.1.105:8080 + * SITESMITH_TEST_PASSWORD=... + */ + +const BASE = process.env.PLAYWRIGHT_BASE_URL || 'http://192.168.1.105:8080'; +const PWD = process.env.SITESMITH_TEST_PASSWORD || 'changeme'; + +async function login(page: any, username: string) { + await page.goto(BASE); + // WHP login uses input[name="user"], not input[name="username"] + await page.fill('input[name="user"]', username); + await page.fill('input[name="password"]', PWD); + await page.click('button[type="submit"], input[type="submit"]'); + await page.waitForURL('**/index.php**'); +} + +async function openSiteBuilder(page: any) { + await page.goto(`${BASE}/?page=site-builder`); + await page.click('a:has-text("Open Editor")'); +} + +test('locked: disabled user sees upgrade banner', async ({ page }) => { + await login(page, 'sitesmith_disabled'); + await openSiteBuilder(page); + await page.click('button:has-text("Sitesmith")'); + await expect(page.getByText('Sitesmith is a paid addon')).toBeVisible(); + await expect(page.getByRole('link', { name: /Upgrade your plan/ })).toBeVisible(); + await expect(page.locator('textarea[placeholder*="Upgrade"]')).toBeDisabled(); +}); + +test('cap reached: enabled but at cap', async ({ page }) => { + await login(page, 'sitesmith_capped'); + await openSiteBuilder(page); + await page.click('button:has-text("Sitesmith")'); + await expect(page.getByText(/Monthly cap reached/)).toBeVisible(); +}); + +test('bonus: bonus credits allow chat when disabled', async ({ page }) => { + await login(page, 'sitesmith_bonus'); + await openSiteBuilder(page); + await page.click('button:has-text("Sitesmith")'); + await expect(page.locator('textarea[placeholder*="Describe"]')).toBeEnabled(); +}); + +test('full build → patch preserves manual edit', async ({ page }) => { + test.setTimeout(180_000); + await login(page, 'sitesmith_enabled'); + await openSiteBuilder(page); + await page.click('button:has-text("Sitesmith")'); + await page.fill('textarea[placeholder*="Describe"]', 'Two-page site for a small bakery. Friendly tone. Hero with cupcakes.'); + await page.click('button:has-text("→")'); + await expect(page.getByText('Replace your entire site?')).toBeVisible({ timeout: 90_000 }); + await page.click('button:has-text("Replace site")'); + await expect(page.locator('h1').first()).toBeVisible({ timeout: 30_000 }); + await page.locator('h1').first().click(); + await page.keyboard.press('Control+A'); + await page.keyboard.type('Custom Manual Edit'); + await page.fill('textarea[placeholder*="Describe"]', 'add a 3-tier pricing section'); + await page.click('button:has-text("→")'); + await expect(page.locator('h1:has-text("Custom Manual Edit")')).toBeVisible({ timeout: 90_000 }); + await expect(page.getByText(/pricing/i)).toBeVisible(); +});