Skip to content

Commit 301238c

Browse files
committed
chore: add CLAUDE.md and AGENTS.md with symlink
1 parent 1782145 commit 301238c

2 files changed

Lines changed: 303 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CLAUDE.md

CLAUDE.md

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
# VRTs WordPress Plugin
2+
3+
## Overview
4+
5+
WordPress plugin for visual regression testing. Connects to the VRTs Service API to manage test schedules, trigger screenshot comparisons, and display visual change alerts in the WordPress admin.
6+
7+
**Tech Stack:** PHP 7.0+, WordPress APIs, @wordpress/scripts (React/webpack), SCSS
8+
9+
## Bootstrap Sequence
10+
11+
Entry point: `visual-regression-tests.php`
12+
13+
1. Defines `VRTS_PLUGIN_FILE` and `VRTS_SERVICE_ENDPOINT` (env-overridable, default: `https://bleech-vrts-app.blee.ch/api/v1/`)
14+
2. Loads Composer autoloader (`vendor/autoload.php`) if present
15+
3. Loads custom autoloader (`includes/autoload.php`)
16+
4. Creates global `vrts()` function returning the `Plugin` singleton
17+
5. Calls `vrts()->setup('vrts', [...])` with three namespace-to-directory mappings that **auto-instantiate all classes** in:
18+
- `Vrts\Features\` -> `includes/features/` (20 feature classes)
19+
- `Vrts\Tables\` -> `includes/tables/` (3 table schema classes)
20+
- `Vrts\Rest_Api\` -> `includes/rest-api/` (4 REST controllers)
21+
22+
## Custom Autoloader
23+
24+
**File:** `includes/autoload.php` - WordPress-flavored PSR-4 via `spl_autoload_register`.
25+
26+
- **Namespace prefix:** `Vrts\`
27+
- **Base directory:** `includes/`
28+
- **Mapping:** Namespace segments are lowercased, underscores become hyphens, class files get a `class-` prefix
29+
30+
Examples:
31+
- `Vrts\Core\Plugin` -> `includes/core/class-plugin.php`
32+
- `Vrts\Models\Test` -> `includes/models/class-test.php`
33+
- `Vrts\Core\Utilities\Date_Time_Helpers` -> `includes/core/utilities/class-date-time-helpers.php`
34+
35+
This is NOT standard PSR-4 - it follows WordPress naming conventions.
36+
37+
## Namespace / Class Structure
38+
39+
```
40+
Vrts\
41+
├── Core\
42+
│ ├── Plugin # Singleton, orchestrates setup, component rendering
43+
│ ├── Traits\Singleton # Singleton pattern trait
44+
│ ├── Traits\Macroable # Dynamic method binding trait
45+
│ ├── Settings\Manager # WordPress Settings API abstraction
46+
│ └── Utilities\ # Url_Helpers, Image_Helpers, Date_Time_Helpers,
47+
│ # String_Helpers, Color_Helpers, Array_Helpers,
48+
│ # Sanitization, Async_Request, Background_Process
49+
├── Features\ # Auto-instantiated on setup() -- each hooks into WP
50+
│ ├── Admin # Menu registration, plugin action links
51+
│ ├── Admin_Columns # Custom columns in post list tables
52+
│ ├── Admin_Header # Shared admin page header component
53+
│ ├── Admin_Notices # Dismissible admin notifications
54+
│ ├── Bulk_Actions # Bulk "add test" from post list
55+
│ ├── Cron_Jobs # WP cron scheduling (hourly fetch, retry polling)
56+
│ ├── Deactivate # Plugin deactivation cleanup
57+
│ ├── Enqueue_Scripts # CSS/JS registration for admin + block editor
58+
│ ├── Install # Activation: table creation, service connection
59+
│ ├── Metaboxes # Classic editor sidebar (VRTs toggle per post)
60+
│ ├── Onboarding # Guided tour (driver.js)
61+
│ ├── Post_Update_Actions # Hooks into save_post, trash, slug changes
62+
│ ├── Service # HTTP client for Laravel API communication
63+
│ ├── Settings_Page # Click selectors, license, triggers, notifications
64+
│ ├── Subscription # Credit/tier management via WP options
65+
│ ├── Test_Runs_Page # Test runs admin page
66+
│ ├── Tests_Page # Tests admin page (add/run/bulk actions)
67+
│ ├── Tests # Additional test logic
68+
│ ├── Translations # i18n loading
69+
│ └── Upgrade_Page # Pricing/upgrade page
70+
├── Models\ # Data access layer (static methods, direct $wpdb)
71+
│ ├── Test # CRUD for vrts_tests, calculated status logic
72+
│ ├── Alert # CRUD for vrts_alerts, state management
73+
│ └── Test_Run # CRUD for vrts_test_runs, trigger metadata
74+
├── Services\ # Business logic layer
75+
│ ├── Test_Service # Create/delete/resume tests (local + remote)
76+
│ ├── Test_Run_Service # Process run webhooks, create alerts from comparisons
77+
│ ├── Alert_Service # Create alert records from comparison data
78+
│ ├── Email_Service # Send HTML test run notification emails
79+
│ └── Manual_Test_Service # Trigger manual test runs (subscription required)
80+
├── Tables\ # DB schema definitions (dbDelta)
81+
│ ├── Tests_Table
82+
│ ├── Alerts_Table
83+
│ └── Test_Runs_Table
84+
├── Rest_Api\ # REST endpoint controllers
85+
│ ├── Rest_Service_Controller # Webhook receiver (signature-verified)
86+
│ ├── Rest_Tests_Controller # Test CRUD via REST
87+
│ ├── Rest_Alerts_Controller # False positive + read status
88+
│ └── Rest_Test_Runs_Controller # Read status for runs
89+
└── List_Tables\ # WP_List_Table implementations
90+
├── Tests_List_Table
91+
├── Test_Runs_List_Table
92+
└── Test_Runs_Queue_List_Table
93+
```
94+
95+
## Custom Database Tables
96+
97+
### `{prefix}vrts_tests` (DB_VERSION 1.5)
98+
99+
| Column | Type | Purpose |
100+
|------------------------|---------------|-----------------------------------------|
101+
| `id` | bigint(20) PK | Auto-increment ID |
102+
| `status` | boolean | 0=paused, 1=active |
103+
| `post_id` | bigint(20) | WP post ID being tested |
104+
| `service_test_id` | varchar(40) | Remote test ID on Laravel service |
105+
| `base_screenshot_url` | varchar(2048) | S3 URL of baseline screenshot |
106+
| `base_screenshot_date` | datetime | When baseline was captured |
107+
| `last_comparison_date` | datetime | Last comparison timestamp |
108+
| `next_run_date` | datetime | Next scheduled run |
109+
| `is_running` | boolean | Whether comparison is in progress |
110+
| `hide_css_selectors` | longtext | CSS selectors to hide during screenshot |
111+
112+
**Calculated statuses** (in `Test` model): `disconnected`, `no-credit-left`, `post-not-published`, `waiting`, `running`, `scheduled`, `has-alert`, `passed`
113+
114+
### `{prefix}vrts_alerts` (DB_VERSION 1.2)
115+
116+
| Column | Type | Purpose |
117+
|---------------------------------|---------------|-------------------------------|
118+
| `id` | bigint(20) PK | Auto-increment ID |
119+
| `title` | text | "Alert #N" |
120+
| `post_id` | bigint(20) | WP post that had changes |
121+
| `test_run_id` | bigint(20) | FK to test_runs table |
122+
| `screenshot_test_id` | varchar(40) | Remote test ID |
123+
| `target_screenshot_url` | varchar(2048) | New screenshot URL |
124+
| `target_screenshot_finish_date` | datetime | When new screenshot was taken |
125+
| `base_screenshot_url` | varchar(2048) | Baseline screenshot URL |
126+
| `base_screenshot_finish_date` | datetime | When baseline was taken |
127+
| `comparison_screenshot_url` | varchar(2048) | Diff image URL |
128+
| `comparison_id` | varchar(40) | Remote comparison ID |
129+
| `differences` | int(4) | Pixel diff count |
130+
| `alert_state` | tinyint | 0=Open, 1=Archived |
131+
| `is_false_positive` | tinyint | 1=marked as false positive |
132+
| `meta` | text | Serialized metadata |
133+
134+
### `{prefix}vrts_test_runs` (DB_VERSION 1.1)
135+
136+
| Column | Type | Purpose |
137+
|-----------------------|---------------|--------------------------------------------------|
138+
| `id` | bigint(20) PK | Auto-increment ID |
139+
| `service_test_run_id` | varchar(40) | Remote run ID |
140+
| `tests` | text | Serialized array of test info |
141+
| `trigger` | varchar(20) | `manual`, `scheduled`, `api`, `update`, `legacy` |
142+
| `trigger_notes` | text | Human-readable trigger description |
143+
| `trigger_meta` | text | Serialized metadata (user_id, update info) |
144+
| `started_at` | datetime | Run start time |
145+
| `scheduled_at` | datetime | Scheduled time |
146+
| `finished_at` | datetime | Completion time |
147+
148+
## Service Communication
149+
150+
All API calls go through `Vrts\Features\Service` using `wp_remote_post()` / `wp_remote_get()`.
151+
152+
**Authentication:** Bearer token (`vrts_project_token` WP option), custom User-Agent: `VRTs/{version};{wp-user-agent}`
153+
154+
### Outbound (Plugin -> Laravel Service)
155+
156+
| Route | Method | Purpose |
157+
|------------------------------------|--------|------------------------------------|
158+
| `sites` | POST | Register site (initial connection) |
159+
| `sites/{id}` | PUT | Update site settings |
160+
| `sites/{id}` | DELETE | Disconnect site |
161+
| `sites/{id}/resume` | POST | Resume all tests |
162+
| `sites/{id}/trigger` | POST | Trigger manual test run |
163+
| `sites/{id}/updates` | GET | Poll for test/run updates |
164+
| `sites/{id}/runs` | GET | Fetch specific test runs |
165+
| `sites/{id}/secret` | POST | Create webhook signing secret |
166+
| `sites/{id}/register` | POST | Register license key |
167+
| `sites/{id}/unregister` | POST | Remove license key |
168+
| `tests` | POST | Create test(s) |
169+
| `tests/{id}` | PUT | Update test (URL, hide selectors) |
170+
| `tests/{id}` | DELETE | Delete test |
171+
| `tests/{id}/resume` | POST | Resume individual test |
172+
| `tests/{id}/false-positives` | POST | Mark comparison as false positive |
173+
| `tests/{id}/false-positives/{cid}` | DELETE | Unmark false positive |
174+
175+
### Inbound Webhooks (Laravel Service -> Plugin)
176+
177+
Endpoint: `wp-json/vrts/v1/service` (+ `admin-ajax.php` fallback for WPML compat)
178+
179+
Signature verification: HMAC-SHA256 of JSON payload using `vrts_project_secret`.
180+
181+
| Action | Purpose |
182+
|------------------------|----------------------------------------------------|
183+
| `test_updated` | Test screenshot/comparison ready |
184+
| `run_updated` | Test run completed, creates alerts for diffs > 1px |
185+
| `run_deleted` | Run deleted remotely |
186+
| `subscription_changed` | Subscription tier changed |
187+
188+
### Connection Flow
189+
190+
1. On activation, `Service::connect_service()` POSTs to `sites` with `create_token`, `rest_url`, `admin_ajax_url`
191+
2. Service responds with `id`, `token`, `secret`, credit info
192+
3. Plugin stores as WP options: `vrts_project_id`, `vrts_project_token`, `vrts_project_secret`
193+
4. Homepage is auto-added as the first test
194+
195+
## REST API Endpoints
196+
197+
All registered under `wp-json/vrts/v1/`:
198+
199+
| Endpoint | Method | Auth | Purpose |
200+
|-------------------------------|-------------|------------------|-------------------------------|
201+
| `/service` | POST | Signature | Webhook receiver from service |
202+
| `/tests` | GET | Public | Get remaining/total credits |
203+
| `/tests/post/{post_id}` | GET | Public | Get test data for a post |
204+
| `/tests/post/{post_id}` | POST | `manage_options` | Create test for a post |
205+
| `/tests/post/{post_id}` | DELETE | `manage_options` | Delete test for a post |
206+
| `/tests/post/{post_id}` | PUT/PATCH | `manage_options` | Update test (CSS selectors) |
207+
| `/alerts/{id}/false-positive` | POST/DELETE | `manage_options` | Toggle false positive |
208+
| `/alerts/{id}/read-status` | POST/DELETE | `manage_options` | Toggle read status |
209+
| `/test-runs/{id}/read-status` | POST/DELETE | `manage_options` | Toggle run read status |
210+
211+
## WordPress Hooks
212+
213+
### Activation / Deactivation
214+
- `register_activation_hook` -> Creates DB tables, connects to service, adds homepage test
215+
- `register_deactivation_hook` -> Deletes unfinished test runs, disconnects from service
216+
- `upgrader_process_complete` -> Reinstalls tables, reconnects on plugin update
217+
218+
### Post Integration
219+
- `save_post` -> Saves test toggle state from classic editor
220+
- `rest_after_insert_{post_type}` -> Updates hide CSS selectors from block editor
221+
- `wp_after_insert_post` -> Resumes test (retakes screenshot) after post update
222+
- `trashed_post` -> Deletes test, archives alerts when post is trashed
223+
- `transition_post_status` -> Creates/deletes remote test on publish/unpublish
224+
- `post_updated` -> Updates test URL on service when slug changes
225+
226+
### Cron Jobs
227+
- `vrts_fetch_updates_cron` (hourly) -- Polls service for all test/run updates
228+
- `vrts_fetch_test_updates` (single-fire, exponential backoff, 10 retries at `20s * 2 * try`) -- Polls after creating a new test until `base_screenshot_date` is set
229+
- `vrts_fetch_test_run_updates` (same backoff pattern) -- Polls after triggering a run until `finished_at` is set
230+
231+
## WordPress Options
232+
233+
| Option | Purpose |
234+
|-------------------------|-----------------------------|
235+
| `vrts_project_id` | Service project UUID |
236+
| `vrts_project_token` | API authentication token |
237+
| `vrts_project_secret` | Webhook HMAC signing secret |
238+
| `vrts_remaining_tests` | Credits remaining |
239+
| `vrts_total_tests` | Total test quota |
240+
| `vrts_has_subscription` | Premium subscription flag |
241+
| `vrts_tier_id` | Subscription tier |
242+
243+
## Frontend / JavaScript
244+
245+
### Build System
246+
247+
Webpack via `@wordpress/scripts` with two entry points:
248+
- `assets/admin.js` -> `build/admin.js` (admin pages)
249+
- `assets/editor.js` -> `build/editor.js` (Gutenberg block editor)
250+
251+
### Admin JS
252+
- Auto-imports all `script.js` from `components/` directories
253+
- Auto-imports all `_style.scss` from `components/`
254+
- `components/` contains PHP template components rendered via `vrts()->component()` with co-located JS/SCSS
255+
256+
### Editor JS
257+
- Uses `@wordpress/plugins` to register `PluginDocumentSettingPanel` + `PluginSidebar`
258+
- Renders React `<Metabox />` component (`editor/components/metabox/`)
259+
260+
### Key JS Libraries
261+
- `driver.js` -- Guided onboarding tours
262+
- `a11y-dialog` -- Accessible modals (comparison dialog)
263+
- `lottie-web` -- Animations
264+
- `dompurify` -- HTML sanitization
265+
- `iframe-resizer` -- Resizable iframes (upgrade page)
266+
267+
### Localized Data
268+
- `vrts_admin_vars`: `rest_url`, `rest_nonce`, `pluginUrl`, `currentUserId`, `onboarding`
269+
- `vrts_editor_vars`: `plugin_name`, `rest_url`, `has_post_alert`, `base_screenshot_url`, `remaining_tests`, `total_tests`, `test_status`, `test_settings`, etc.
270+
271+
## Admin Pages
272+
273+
Registered under the main "VRTs" menu (position 80):
274+
275+
| Slug | Class | Purpose |
276+
|-----------------|------------------|---------------------------------------------------|
277+
| `vrts` | `Tests_Page` | Test list with add/run/bulk actions |
278+
| `vrts-runs` | `Test_Runs_Page` | Test run history, alert list, comparison modal |
279+
| `vrts-settings` | `Settings_Page` | Click selectors, license, triggers, notifications |
280+
| `vrts-upgrade` | `Upgrade_Page` | Pricing/upgrade (iframe to external) |
281+
282+
## Linting
283+
284+
```bash
285+
npm run lint # Run all linters
286+
npm run lint:js # ESLint (@wordpress/eslint-plugin)
287+
npm run lint:css # Stylelint (@wordpress/stylelint-config)
288+
npm run lint:php # PHPCS (WordPress Coding Standards)
289+
290+
npm run lint-fix # Auto-fix all
291+
npm run lint-fix:js # Auto-fix JS
292+
npm run lint-fix:css # Auto-fix CSS
293+
npm run lint-fix:php # Auto-fix PHP (phpcbf)
294+
```
295+
296+
Always try `lint-fix` before fixing manually.
297+
298+
## Code Style
299+
300+
- **PHP:** WordPress Coding Standards (WPCS), `Vrts\` namespace, DocBlocks required
301+
- **JS:** ESLint with `@wordpress/eslint-plugin`, ES6+, React components
302+
- **CSS/SCSS:** Stylelint with `@wordpress/stylelint-config`, BEM naming, `.vrts-` prefix

0 commit comments

Comments
 (0)