rem last build change

This commit is contained in:
southseact-3d
2026-02-08 17:38:43 +00:00
parent c30d2ba715
commit 1e0168b301
3 changed files with 1037 additions and 50 deletions

View File

@@ -55,10 +55,6 @@
flex: 1;
overflow-y: auto;
min-height: 0;
-webkit-overflow-scrolling: touch;
scroll-behavior: auto;
transform: translateZ(0);
will-change: scroll-position;
}
.app-shell.builder-single>* {

View File

@@ -1520,7 +1520,7 @@ function closeHistoryModal() {
if (el.historyModal) el.historyModal.style.display = 'none';
}
function scrollChatToBottom() {
function scrollChatToBottom(force = false) {
if (!el.chatArea) return;
const target = el.chatArea;
@@ -1529,48 +1529,91 @@ function scrollChatToBottom() {
target.scrollTop = target.scrollHeight;
};
// Use multiple scroll techniques in sequence for reliability
// 1. Immediate scroll
// Use multiple techniques to ensure scroll happens after content is rendered
// 1. Immediate scroll attempt
doScroll();
// 2. After DOM updates
setTimeout(doScroll, 50);
setTimeout(doScroll, 100);
setTimeout(doScroll, 200);
setTimeout(doScroll, 500);
// 2. After short delays to allow DOM updates
setTimeout(() => { doScroll(); }, 10);
setTimeout(() => { doScroll(); }, 50);
setTimeout(() => { doScroll(); }, 100);
setTimeout(() => { doScroll(); }, 250);
setTimeout(() => { doScroll(); }, 500);
// 3. After content fully renders
setTimeout(doScroll, 1000);
// 3. Longer delay for content to fully settle (especially on page load)
setTimeout(() => { doScroll(); }, 1000);
setTimeout(() => { doScroll(); }, 2000);
// 4. requestAnimationFrame for smooth scrolling
// 4. Use requestAnimationFrame for smooth scrolling
requestAnimationFrame(() => {
doScroll();
requestAnimationFrame(doScroll);
requestAnimationFrame(() => {
doScroll();
requestAnimationFrame(() => {
doScroll();
});
});
});
// 5. Scroll to last message element if it exists (most reliable method)
setTimeout(() => {
const lastMessage = target.querySelector('.message:last-child');
if (lastMessage) {
lastMessage.scrollIntoView({ behavior: 'smooth', block: 'end' });
} else {
// Fallback to regular scroll
doScroll();
// 5. Wait for ALL images to load before scrolling
const images = target.querySelectorAll('img');
let imagesToLoad = 0;
images.forEach((img) => {
if (!img.complete) {
imagesToLoad++;
img.addEventListener('load', () => {
imagesToLoad--;
doScroll();
// Scroll again after a short delay to ensure layout is updated
setTimeout(() => doScroll(), 100);
}, { once: true });
img.addEventListener('error', () => {
imagesToLoad--;
doScroll();
}, { once: true });
}
}, 100);
// 6. ResizeObserver for dynamic content
if (typeof ResizeObserver !== 'undefined') {
const resizeObserver = new ResizeObserver(() => doScroll());
resizeObserver.observe(target);
setTimeout(() => resizeObserver.disconnect(), 2000);
});
// If there are images loading, scroll again after they all complete
if (imagesToLoad > 0) {
const checkImagesLoaded = setInterval(() => {
if (imagesToLoad === 0) {
clearInterval(checkImagesLoaded);
doScroll();
setTimeout(() => doScroll(), 100);
setTimeout(() => doScroll(), 500);
}
}, 100);
// Stop checking after 5 seconds to avoid infinite interval
setTimeout(() => clearInterval(checkImagesLoaded), 5000);
}
// 7. MutationObserver for DOM changes
// 6. Use ResizeObserver to detect when content height changes
if (typeof ResizeObserver !== 'undefined') {
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
if (entry.target === target) {
doScroll();
}
}
});
resizeObserver.observe(target);
// Stop observing after a while
setTimeout(() => resizeObserver.disconnect(), 3000);
}
// 7. Also scroll when any content finishes rendering (using MutationObserver)
if (typeof MutationObserver !== 'undefined') {
const observer = new MutationObserver(() => doScroll());
const observer = new MutationObserver(() => {
doScroll();
});
observer.observe(target, { childList: true, subtree: true });
setTimeout(() => observer.disconnect(), 2000);
// Stop observing after a while to avoid memory leaks
setTimeout(() => observer.disconnect(), 3000);
}
}
@@ -1984,22 +2027,7 @@ function renderMessages(session) {
}
}
// Scroll to bottom after DOM updates
scrollChatToBottom();
// Additional scroll with longer delay to ensure all content is rendered
setTimeout(() => {
scrollChatToBottom();
// Force scroll by accessing scrollHeight directly
if (el.chatArea) {
el.chatArea.scrollTop = el.chatArea.scrollHeight;
// Scroll last element into view as fallback
const lastMessage = el.chatArea.querySelector('.message:last-child');
if (lastMessage) {
lastMessage.scrollIntoView({ block: 'end' });
}
}
}, 200);
updateExportButtonVisibility(session);
}

963
external-wp-testing-plan.md Normal file
View File

@@ -0,0 +1,963 @@
# PluginCompass External WordPress Testing System
## Executive Summary
A production-ready testing system that uses an externally hosted WordPress site with WP-CLI to verify plugin functionality. Supports high concurrency (20+ sessions) through multisite subsite isolation, with two testing modes (automated WP-CLI commands and visual browser testing).
---
## Architecture Overview
```
┌─────────────────────────────────────────────────────────────────┐
│ PluginCompass CLI Users │
│ (20+ Concurrent Sessions) │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Test Orchestrator Layer │
│ - Session Queue Manager (prevents conflicts) │
│ - Subsite Provisioning (auto-creates test sites) │
│ - Resource Cleanup (automatic teardown) │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ External WordPress Multisite │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Session 1 │ │ Session 2 │ │ Session N │ │
│ │ Subsite │ │ Subsite │ │ Subsite │ │
│ │ /test-abc │ │ /test-def │ │ /test-xyz │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ Shared: Core WP, Plugins, Themes (isolated per subsite) │
└─────────────────────────────────────────────────────────────────┘
```
---
## Core Components
### 1. Test Orchestrator (`test_orchestrator.js`)
Central controller managing all test sessions.
**Responsibilities:**
- Queue incoming test requests
- Provision subsites automatically
- Manage plugin installations
- Coordinate cleanup
- Prevent cross-session contamination
**Configuration:**
```javascript
const TEST_CONFIG = {
// External WP Site Connection
wpHost: process.env.TEST_WP_HOST || 'testsite.example.com',
wpSshUser: process.env.TEST_WP_SSH_USER || 'wordpress',
wpSshKey: process.env.TEST_WP_SSH_KEY || '~/.ssh/wp-test-key',
wpPath: process.env.TEST_WP_PATH || '/var/www/html',
// Multisite Settings
enableMultisite: true,
subsitePrefix: 'test',
subsiteDomain: 'testsite.example.com',
// Concurrency
maxConcurrentTests: 20,
queueTimeout: 300000, // 5 minutes
testTimeout: 600000, // 10 minutes
// Cleanup
autoCleanup: true,
cleanupDelay: 3600000, // 1 hour after test completion
// Testing Modes
enableVisualTesting: process.env.ENABLE_VISUAL_TESTING === 'true',
visualTestBrowser: 'chromium', // chromium, firefox, webkit
};
```
---
### 2. New Tool: `test_plugin_external_wp`
**Tool Name:** `test_plugin_external_wp`
**Description:** Deploys and tests plugin on external WordPress site with full isolation. Automatically provisions subsite, installs dependencies, runs verification tests, and provides detailed results.
**Parameters:**
```json
{
"name": "test_plugin_external_wp",
"parameters": {
"type": "object",
"properties": {
"plugin_path": {
"type": "string",
"description": "Local path to generated plugin directory"
},
"test_mode": {
"type": "string",
"enum": ["cli", "visual", "both"],
"default": "cli",
"description": "Testing mode: cli (WP-CLI commands), visual (browser automation), or both"
},
"required_plugins": {
"type": "array",
"description": "Other plugins that must be installed for this plugin to work",
"items": {
"type": "object",
"properties": {
"plugin_slug": { "type": "string" },
"version": { "type": "string" },
"source": {
"type": "string",
"enum": ["wordpress.org", "url", "local"],
"default": "wordpress.org"
},
"source_url": { "type": "string" },
"activate": { "type": "boolean", "default": true }
},
"required": ["plugin_slug"]
}
},
"test_scenarios": {
"type": "array",
"description": "Specific test scenarios to verify",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"type": {
"type": "string",
"enum": ["endpoint", "admin_page", "ajax", "shortcode", "hook", "visual", "custom"]
},
"url": { "type": "string" },
"selector": { "type": "string" },
"expected_text": { "type": "string" },
"wp_cli_command": { "type": "string" },
"assertions": {
"type": "object",
"properties": {
"status_code": { "type": "number" },
"contains": { "type": "array", "items": { "type": "string" } },
"not_contains": { "type": "array", "items": { "type": "string" } },
"wp_cli_success": { "type": "boolean" }
}
}
},
"required": ["name", "type"]
}
},
"visual_tests": {
"type": "array",
"description": "Visual testing scenarios (only used when test_mode is 'visual' or 'both')",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"url": { "type": "string" },
"actions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": ["goto", "click", "fill", "wait", "screenshot", "evaluate"]
},
"selector": { "type": "string" },
"value": { "type": "string" },
"timeout": { "type": "number" }
}
}
},
"assertions": {
"type": "object",
"properties": {
"visible": { "type": "array", "items": { "type": "string" } },
"hidden": { "type": "array", "items": { "type": "string" } },
"text_contains": { "type": "array", "items": { "type": "string" } },
"no_console_errors": { "type": "boolean" }
}
}
}
}
},
"timeout": {
"type": "number",
"default": 300,
"description": "Test timeout in seconds"
},
"auto_fix": {
"type": "boolean",
"default": true,
"description": "Automatically retry and fix on test failure"
},
"max_fix_attempts": {
"type": "number",
"default": 3,
"description": "Maximum auto-fix attempts before giving up"
}
},
"required": ["plugin_path"]
}
}
```
**Return Value:**
```javascript
{
"ok": true,
"session_id": "sess_abc123",
"subsite_url": "https://testsite.example.com/test-abc123",
"test_results": {
"mode": "both",
"cli_tests": {
"passed": 5,
"failed": 0,
"results": [
{
"name": "Plugin activates without errors",
"status": "passed",
"command": "wp plugin activate test-plugin",
"output": "Success: Activated 1 of 1 plugins.",
"duration": 2340
},
{
"name": "Custom login endpoint responds",
"status": "passed",
"command": "wp eval 'echo wp_remote_retrieve_body(wp_remote_get(home_url(\"/custom-login\")));'",
"assertions": {
"contains": ["<form", "login"],
"status_code": 200
}
}
]
},
"visual_tests": {
"passed": 3,
"failed": 0,
"screenshots": [
{
"name": "login-page",
"path": "/results/sess_abc123/login-page.png",
"url": "https://testsite.example.com/test-abc123/custom-login"
}
]
}
},
"installed_plugins": [
{ "slug": "woocommerce", "version": "8.0.0", "status": "active" },
{ "slug": "test-plugin", "version": "1.0.0", "status": "active" }
],
"errors": [],
"warnings": [],
"duration": 45230,
"cleanup_scheduled": "2024-01-15T10:30:00Z"
}
```
---
## Testing Modes
### Mode 1: WP-CLI Automated Testing
**Use Case:** Fast, deterministic testing without browser overhead
**How it works:**
1. Deploy plugin to subsite
2. Run WP-CLI commands to verify functionality
3. Check HTTP endpoints via `wp eval` with `wp_remote_get()`
4. Verify database state changes
5. Check error logs
**Example Test Scenarios:**
```javascript
[
{
"name": "Plugin activates cleanly",
"type": "custom",
"wp_cli_command": "wp plugin activate {plugin_slug} --skip-plugins",
"assertions": {
"wp_cli_success": true,
"not_contains": ["Fatal error", "Parse error", "Warning"]
}
},
{
"name": "Custom endpoint accessible",
"type": "endpoint",
"url": "/custom-login",
"wp_cli_command": "wp eval 'echo wp_remote_retrieve_response_code(wp_remote_get(home_url(\"/custom-login\")));'",
"assertions": {
"status_code": 200,
"contains": ["<form", "name=\"log\""]
}
},
{
"name": "Rewrite rules flushed",
"type": "custom",
"wp_cli_command": "wp rewrite list --match=/custom-login --format=json",
"assertions": {
"wp_cli_success": true,
"contains": ["custom-login"]
}
},
{
"name": "No PHP errors in logs",
"type": "custom",
"wp_cli_command": "tail -n 50 /var/log/wp-errors.log | grep -i 'test-plugin' || echo 'No errors found'",
"assertions": {
"not_contains": ["Fatal error", "Parse error", "test-plugin"]
}
}
]
```
**Benefits:**
- Fast (5-30 seconds per test)
- No browser dependencies
- Perfect for API/endpoint testing
- Can check database state directly
- Scales to 100+ concurrent tests
---
### Mode 2: Visual Browser Testing
**Use Case:** UI verification, JavaScript functionality, styling checks
**How it works:**
1. Deploy plugin to subsite
2. Launch headless browser (Playwright)
3. Navigate to specified URLs
4. Perform user interactions (click, type, submit)
5. Capture screenshots
6. Verify visual elements and JavaScript execution
**Example Visual Tests:**
```javascript
[
{
"name": "Login form renders correctly",
"url": "/custom-login",
"actions": [
{ "action": "goto", "url": "/custom-login" },
{ "action": "screenshot", "name": "initial-load" },
{ "action": "wait", "selector": "form" }
],
"assertions": {
"visible": ["input[name='log']", "input[name='pwd']", ".submit-button"],
"no_console_errors": true
}
},
{
"name": "Add to cart button works",
"url": "/shop",
"actions": [
{ "action": "goto", "url": "/shop" },
{ "action": "click", "selector": "[data-product_id='123']" },
{ "action": "wait", "timeout": 2000 },
{ "action": "screenshot", "name": "after-click" }
],
"assertions": {
"visible": [".cart-count"],
"text_contains": ["1 item"],
"no_console_errors": true
}
},
{
"name": "Admin settings page loads",
"url": "/wp-admin/admin.php?page=my-plugin",
"actions": [
{
"action": "evaluate",
"value": "document.querySelector('#user_login').value = 'admin'; document.querySelector('#user_pass').value = 'password'; document.querySelector('#wp-submit').click();"
},
{ "action": "wait", "url": "/wp-admin/admin.php?page=my-plugin" },
{ "action": "screenshot", "name": "admin-page" }
],
"assertions": {
"visible": [".wrap h1", "form"],
"no_console_errors": true
}
}
]
```
**Benefits:**
- Catches visual/styling issues
- Verifies JavaScript execution
- Tests actual user workflows
- Screenshots provide proof
- Finds timing/race condition issues
---
## Session Isolation Strategy
### High Concurrency Solution: Multisite Subsites
**Why Multisite:**
- Each session gets completely isolated WordPress instance
- Shared core (efficient) but isolated content/options
- Can run 20+ tests simultaneously without conflicts
- Automatic cleanup by deleting subsite
**Setup Requirements:**
1. Convert hosted WP to multisite (one-time)
2. Enable subdomain or subdirectory mode
3. Configure wildcard DNS or subdirectory rewrite rules
**Subsite Provisioning:**
```javascript
class SubsiteProvisioner {
async createTestSubsite(sessionId) {
const slug = `test-${sessionId.substring(0, 8)}`;
const title = `Test Environment ${sessionId}`;
const adminEmail = 'test@example.com';
// Create subsite via WP-CLI
await this.runWpCli(`
wp site create
--slug=${slug}
--title="${title}"
--email=${adminEmail}
`);
// Configure subsite
await this.runWpCli(`
wp site switch --url=https://${this.config.subsiteDomain}/${slug}
wp option update blogname "Test Site"
wp option update permalink_structure "/%postname%/"
wp rewrite flush
`);
return {
slug,
url: `https://${this.config.subsiteDomain}/${slug}`,
adminUrl: `https://${this.config.subsiteDomain}/${slug}/wp-admin`
};
}
async deleteTestSubsite(slug) {
await this.runWpCli(`wp site delete --slug=${slug} --yes`);
}
}
```
---
## Dependency Management
### Installing Required Plugins
The AI can specify dependencies that must be present:
```javascript
{
"required_plugins": [
{
"plugin_slug": "woocommerce",
"version": ">=8.0",
"source": "wordpress.org",
"activate": true
},
{
"plugin_slug": "advanced-custom-fields",
"version": "6.2.0",
"source": "wordpress.org",
"activate": true
},
{
"plugin_slug": "custom-premium-plugin",
"source": "url",
"source_url": "https://example.com/plugins/custom.zip",
"activate": true
}
]
}
```
**Installation Process:**
```javascript
class PluginInstaller {
async installPlugin(subsiteUrl, pluginSpec) {
const { plugin_slug, source, source_url, activate } = pluginSpec;
switch (source) {
case 'wordpress.org':
await this.runWpCli(`
wp --url=${subsiteUrl} plugin install ${plugin_slug} --activate
`);
break;
case 'url':
// Download and install from URL
await this.runWpCli(`
wp --url=${subsiteUrl} plugin install ${source_url} --activate
`);
break;
case 'local':
// Copy from local path to subsite
const targetPath = `${this.config.wpPath}/wp-content/plugins/${plugin_slug}`;
await this.ssh.copyFile(source_url, targetPath);
if (activate) {
await this.runWpCli(`
wp --url=${subsiteUrl} plugin activate ${plugin_slug}
`);
}
break;
}
}
}
```
---
## Queue Management for High Concurrency
### Session Queue System
**Challenge:** Even with multisite, some operations (like subsite creation) can have race conditions.
**Solution:** Queue system with parallel execution lanes
```javascript
class TestQueue {
constructor(maxConcurrent = 20) {
this.maxConcurrent = maxConcurrent;
this.activeSessions = new Map();
this.pendingQueue = [];
this.lanes = new Array(maxConcurrent).fill(null); // Execution lanes
}
async enqueue(testRequest) {
const sessionId = this.generateSessionId();
// Find available lane
const laneIndex = this.findAvailableLane();
if (laneIndex === -1) {
// All lanes busy, add to pending
return new Promise((resolve) => {
this.pendingQueue.push({
sessionId,
request: testRequest,
resolve,
enqueuedAt: Date.now()
});
});
}
// Execute immediately in available lane
return this.executeInLane(laneIndex, sessionId, testRequest);
}
async executeInLane(laneIndex, sessionId, request) {
this.lanes[laneIndex] = sessionId;
try {
const result = await this.runTest(sessionId, request);
return result;
} finally {
this.lanes[laneIndex] = null;
this.processQueue();
}
}
processQueue() {
if (this.pendingQueue.length === 0) return;
const laneIndex = this.findAvailableLane();
if (laneIndex === -1) return;
const next = this.pendingQueue.shift();
// Check for timeout
if (Date.now() - next.enqueuedAt > TEST_CONFIG.queueTimeout) {
next.resolve({
ok: false,
error: 'Test queue timeout - too many concurrent tests'
});
this.processQueue();
return;
}
this.executeInLane(laneIndex, next.sessionId, next.request)
.then(next.resolve)
.catch(error => next.resolve({ ok: false, error: error.message }));
}
}
```
---
## Complete Workflow
### Step-by-Step Process
```
User Request
AI Generates Plugin Code
AI Calls test_plugin_external_wp
┌─────────────────────────────────────┐
│ 1. QUEUE & PREPARE │
│ - Add to test queue │
│ - Wait for available lane │
│ - Generate session ID │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 2. PROVISION SUBSITE │
│ - Create multisite subsite │
│ - Configure permalinks │
│ - Set up test database tables │
│ Duration: 3-5 seconds │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 3. INSTALL DEPENDENCIES │
│ - Install WordPress (if needed) │
│ - Install WooCommerce │
│ - Install other required plugins │
│ - Activate all │
│ Duration: 10-30 seconds │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 4. DEPLOY TEST PLUGIN │
│ - Copy plugin files via SFTP │
│ - Activate plugin │
│ - Check for activation errors │
│ Duration: 2-5 seconds │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 5. RUN TESTS │
│ CLI Mode: │
│ - WP-CLI verification commands │
│ - HTTP endpoint checks │
│ - Database state validation │
│ - Error log scanning │
│ │
│ Visual Mode (if enabled): │
│ - Launch headless browser │
│ - Navigate to test URLs │
│ - Execute user interactions │
│ - Capture screenshots │
│ - Check JavaScript console │
│ Duration: 10-60 seconds │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 6. EVALUATE & AUTO-FIX (if needed) │
│ IF tests failed AND auto_fix=true:│
│ - Analyze failure patterns │
│ - Generate fix suggestions │
│ - Modify plugin code │
│ - Re-deploy and re-test │
│ - Max 3 attempts │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 7. RETURN RESULTS │
│ - Compile test results │
│ - Include screenshots (visual) │
│ - Provide fix recommendations │
│ - Schedule cleanup │
└─────────────────────────────────────┘
AI Receives Results
IF passed → Complete task
IF failed → Fix issues and re-call tool
┌─────────────────────────────────────┐
│ 8. CLEANUP (async, after delay) │
│ - Delete subsite │
│ - Remove plugin files │
│ - Clear test data │
│ - Free up lane │
└─────────────────────────────────────┘
```
---
## Error Handling & Auto-Fix
### Common Failures & Fixes
```javascript
const AUTO_FIX_PATTERNS = [
{
pattern: /Rewrite rules not working/i,
check: async (subsite) => {
const rules = await runWpCli(`wp --url=${subsite} rewrite list`);
return !rules.includes('custom-login');
},
fix: async (pluginPath) => {
// Add flush_rewrite_rules() to activation hook
await editFile(pluginPath + '/includes/activation.php',
'function activate_plugin() {',
'function activate_plugin() {\n flush_rewrite_rules();'
);
}
},
{
pattern: /Undefined function.*wc_/i,
check: async (subsite) => {
const logs = await runWpCli(`tail -n 20 /var/log/wp-errors.log`);
return logs.includes('Call to undefined function wc_');
},
fix: async (pluginPath) => {
// Add WooCommerce dependency check
await editFile(pluginPath + '/main.php',
'<?php',
'<?php\n// Check WooCommerce is active\nif (!in_array(\'woocommerce/woocommerce.php\', apply_filters(\'active_plugins\', get_option(\'active_plugins\')))) {\n return;\n}'
);
}
},
{
pattern: /CSS not loading/i,
check: async (subsite) => {
const html = await runWpCli(`wp --url=${subsite} eval 'echo wp_remote_retrieve_body(wp_remote_get(home_url("/custom-login")));'`);
return !html.includes('custom-login.css');
},
fix: async (pluginPath) => {
// Ensure CSS is enqueued
await editFile(pluginPath + '/includes/enqueue.php',
'',
'wp_enqueue_style(\'custom-login-css\', plugins_url(\'css/custom-login.css\', __FILE__));'
);
}
}
];
```
---
## Configuration & Environment Variables
### Required Environment Variables
```bash
# WordPress Test Site Connection
TEST_WP_HOST=testsite.example.com
TEST_WP_SSH_USER=wordpress
TEST_WP_SSH_KEY_PATH=/path/to/ssh/key
TEST_WP_PATH=/var/www/html
# Multisite Configuration
TEST_WP_MULTISITE=true
TEST_WP_SUBSITE_DOMAIN=testsite.example.com
# Testing Configuration
TEST_ENABLE_VISUAL=true
TEST_VISUAL_BROWSER=chromium
TEST_MAX_CONCURRENT=20
TEST_TIMEOUT=300
TEST_AUTO_CLEANUP=true
TEST_CLEANUP_DELAY=3600
# Optional: Browser Testing (for visual mode)
TEST_BROWSER_HEADLESS=true
TEST_BROWSER_TIMEOUT=30000
TEST_SCREENSHOT_PATH=/var/results/screenshots
```
---
## Implementation Phases
### Phase 1: Core Infrastructure (Week 1)
- [ ] Set up multisite WordPress
- [ ] Implement SSH/SFTP connection layer
- [ ] Create `test_plugin_external_wp` tool
- [ ] Build subsite provisioning system
- [ ] Basic CLI test mode
### Phase 2: Testing Modes (Week 2)
- [ ] Implement CLI test scenarios
- [ ] Add visual testing with Playwright
- [ ] Create queue management system
- [ ] Build dependency installer
### Phase 3: Auto-Fix & Polish (Week 3)
- [ ] Implement auto-fix patterns
- [ ] Add retry logic
- [ ] Error classification
- [ ] Result formatting for AI
### Phase 4: Production Ready (Week 4)
- [ ] Concurrent session testing
- [ ] Performance optimization
- [ ] Documentation
- [ ] Monitoring & alerting
---
## Example Usage in AI Workflow
### Example 1: Custom Login Plugin
**User:** "Create a plugin that adds a custom login page at /vendor-login"
**AI Actions:**
1. Generates plugin code
2. Calls `test_plugin_external_wp`:
```javascript
{
"plugin_path": "./vendor-login-plugin",
"test_mode": "both",
"required_plugins": [],
"test_scenarios": [
{
"name": "Endpoint accessible",
"type": "endpoint",
"url": "/vendor-login",
"assertions": {
"status_code": 200,
"contains": ["<form", "login"]
}
},
{
"name": "Rewrite rule exists",
"type": "custom",
"wp_cli_command": "wp rewrite list --match=/vendor-login",
"assertions": {
"contains": ["vendor-login"]
}
}
],
"visual_tests": [
{
"name": "Login form renders",
"url": "/vendor-login",
"actions": [
{ "action": "goto", "url": "/vendor-login" },
{ "action": "screenshot", "name": "login-page" }
],
"assertions": {
"visible": ["input[name='log']", "input[type='submit']"]
}
}
]
}
```
**Result:**
- Subsite created: `https://testsite.example.com/test-a3f7d2`
- Plugin installed and activated
- Tests run (5 CLI, 3 visual)
- Screenshot: `login-page.png` showing working form
- AI confirms: "Plugin tested and verified - custom login page working"
---
### Example 2: WooCommerce Payment Gateway
**User:** "Create a WooCommerce payment gateway"
**AI Actions:**
```javascript
{
"plugin_path": "./custom-gateway",
"test_mode": "both",
"required_plugins": [
{ "plugin_slug": "woocommerce", "activate": true }
],
"test_scenarios": [
{
"name": "Gateway registered",
"type": "custom",
"wp_cli_command": "wp eval 'var_dump(array_keys(WC()->payment_gateways->payment_gateways()));'",
"assertions": {
"contains": ["custom_gateway"]
}
},
{
"name": "Checkout loads without errors",
"type": "endpoint",
"url": "/checkout",
"assertions": {
"status_code": 200,
"not_contains": ["Fatal error", "Warning"]
}
}
],
"visual_tests": [
{
"name": "Gateway appears in checkout",
"url": "/checkout",
"actions": [
{ "action": "goto", "url": "/product/test-product" },
{ "action": "click", "selector": ".add_to_cart_button" },
{ "action": "wait", "timeout": 2000 },
{ "action": "goto", "url": "/checkout" },
{ "action": "screenshot", "name": "checkout-gateway" }
],
"assertions": {
"visible": ["#payment_method_custom_gateway"],
"no_console_errors": true
}
}
]
}
```
---
## Monitoring & Maintenance
### Health Checks
```javascript
// Periodic health check
async function healthCheck() {
const checks = {
ssh: await checkSshConnection(),
wp_cli: await checkWpCli(),
multisite: await checkMultisiteEnabled(),
disk_space: await checkDiskSpace(),
queue_length: testQueue.pendingQueue.length
};
if (Object.values(checks).some(c => !c.ok)) {
await alertAdmin(checks);
}
}
```
### Metrics to Track
- Test queue length and wait times
- Average test duration per mode
- Failure rate by plugin type
- Auto-fix success rate
- Resource usage (disk, memory)
---
## Summary
This system provides:
1. **Full Isolation:** Each test runs in its own multisite subsite
2. **High Concurrency:** Queue system handles 20+ simultaneous tests
3. **Two Testing Modes:**
- CLI mode: Fast, deterministic WP-CLI testing
- Visual mode: Real browser automation with screenshots
4. **Automatic Setup:** Provisions subsite, installs dependencies, deploys plugin
5. **Auto-Fix:** Attempts to fix common failures automatically
6. **Proof of Work:** Returns screenshots, logs, and detailed test results
7. **Cleanup:** Automatic teardown after test completion
**Result:** Users receive plugins that are proven to work on a real WordPress environment, with objective evidence of functionality.