From 9dd96c815a086b21863b1adcd24057d10edef0af Mon Sep 17 00:00:00 2001 From: peptiderepo <273788462+peptiderepo@users.noreply.github.com> Date: Wed, 17 Jun 2026 01:30:21 +0700 Subject: [PATCH] feat(e2e): add PR Vision admin smoke spec (08) Covers: - Dashboard: HTTP 200, no fatal, h1 "PR Vision" heading visible - Settings: page loads + API-key-manager card (#prv-key-card) present + status indicator (#prv-key-source-badge) visible - Write-only invariant: #prv_api_key input has value="" (stored key never output); full page HTML asserted to not contain "sk-or-" prefix - Costs (pr-vision-costs): loads without fatal - Call Log (pr-vision-calls): loads without fatal Skip-if-absent on every test: plugin absent or Settings page (v0.2.0 admin) not yet deployed => loud annotation + test.skip(). Selectors sourced from: includes/core/class-prv-admin-page.php (h1 heading) includes/core/class-prv-key-manager-renderer.php (#prv-key-card, #prv-key-source-badge, #prv_api_key) includes/core/class-prv-settings-page.php (MENU_SLUG) includes/core/class-prv-costs-page.php (MENU_SLUG) includes/core/class-prv-call-log-page.php (MENU_SLUG) Agent-Session: claude/e2e-news-vision-admin-20260617 --- tests/08-pr-vision-admin.spec.ts | 206 +++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 tests/08-pr-vision-admin.spec.ts diff --git a/tests/08-pr-vision-admin.spec.ts b/tests/08-pr-vision-admin.spec.ts new file mode 100644 index 0000000..1719ac9 --- /dev/null +++ b/tests/08-pr-vision-admin.spec.ts @@ -0,0 +1,206 @@ +/** + * 08-pr-vision-admin.spec.ts + * + * What: Smoke tests for the PR Vision plugin admin surfaces. + * Covers the dashboard, Settings (API-key-manager card + write-only + * invariant), Costs, and Call Log sub-pages. Guards against PHP fatals, + * missing menu registration, and key leakage regressions. + * + * Who triggers it: Playwright chromium project (post-auth setup). + * Dependencies: wp-admin authenticated session (from global.setup.ts). + * + * SKIP-IF-ABSENT: When PR Vision is not installed/activated on this + * environment, each test detects the absent page and skips with a loud + * annotation - consistent with the 05/06 merge-early binding pattern. + * + * Admin menu slugs (sourced from includes/core/class-prv-admin-page.php + * and includes/core/class-prv-settings-page.php, + * includes/core/class-prv-costs-page.php, + * includes/core/class-prv-call-log-page.php): + * Dashboard -> admin.php?page=pr-vision (MENU_SLUG = 'pr-vision') + * Settings -> admin.php?page=pr-vision-settings (MENU_SLUG = 'pr-vision-settings') + * Costs -> admin.php?page=pr-vision-costs (MENU_SLUG = 'pr-vision-costs') + * Call Log -> admin.php?page=pr-vision-calls (MENU_SLUG = 'pr-vision-calls') + * + * Selectors sourced from includes/core/class-prv-admin-page.php: + * h1 "PR Vision - AI Visibility" - dashboard page heading + * + * Selectors sourced from includes/core/class-prv-key-manager-renderer.php: + * #prv-key-card - the API-key-manager card container + * #prv-key-source-badge - key source status indicator span + * #prv_api_key - password input (must render empty - write-only invariant) + * + * Write-only invariant (security): + * The stored OpenRouter key must NEVER appear in page source. + * #prv_api_key renders with value="" on every page load regardless of + * whether a key is stored. This spec asserts the empty value and asserts + * that known API key prefixes ("sk-or-") are absent from the full body. + * + * @see peptiderepo/pr-vision includes/core/class-prv-admin-page.php + * @see peptiderepo/pr-vision includes/core/class-prv-settings-page.php + * @see peptiderepo/pr-vision includes/core/class-prv-key-manager-renderer.php + */ + +import { test, expect } from '@playwright/test'; + +const ADMIN = '/wp-admin'; + +/** + * Detect the "page not registered" condition WordPress returns when a plugin + * menu slug is unknown. Returns true when the plugin is absent. + */ +function isPluginAbsent(body: string, url: string): boolean { + return ( + body.includes('Invalid plugin page') || + body.includes('do not have sufficient permissions') || + body.includes('The page you are looking for') || + ( url.includes('/wp-admin/') && ! url.includes('page=pr-vision') ) + ); +} + +/** Push a loud skip annotation and call test.skip(). */ +function skipMissing(): void { + test.info().annotations.push({ + type: 'skip', + description: + '[pr-vision smoke] PR Vision plugin is not installed or activated ' + + 'on this environment. Skipping per the merge-early binding.', + }); + test.skip(); +} + +test.describe('WP Admin - PR Vision dashboard', () => { + test('dashboard page loads (HTTP 200, no PHP fatal)', async ({ page }) => { + const res = await page.goto(`${ADMIN}/admin.php?page=pr-vision`); + expect(res?.status()).toBe(200); + + await expect(page.locator('#wpbody')).toBeVisible(); + + const body = (await page.textContent('body')) ?? ''; + expect(body).not.toContain('Fatal error'); + + if (isPluginAbsent(body, page.url())) { skipMissing(); return; } + + // Heading from class-prv-admin-page.php render_page(): + // echo '