Multi-agent defaults: 5 built-in agents with per-agent tools, prompts, and suggestions #1173
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: E2E Tests | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| concurrency: | |
| group: e2e-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| e2e: | |
| name: Playwright E2E (WP ${{ matrix.wp }} shard ${{ matrix.shard }}) | |
| runs-on: ubuntu-latest | |
| # Each shard runs ~1/3 of the 13 spec files. 90 min accounts for setup | |
| # overhead (~20 min: npm ci, composer, wp-env start) + worst-case test | |
| # runtime (2 retries at 90 s/test × 3 attempts / 2 workers). The 60-min | |
| # limit was exceeded after the per-test timeout increased from 60 s to | |
| # 90 s in playwright.config.js — setup + test time could exceed 60 min. | |
| timeout-minutes: 90 | |
| # WP trunk may fail due to PSR namespace conflicts between our compat | |
| # layer and core's native AI Client SDK (see tests.yml for details). | |
| continue-on-error: ${{ matrix.wp == 'trunk' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| wp: ['6.9', 'trunk'] | |
| # 3 shards split 13 spec files into ~4-5 files each. With the 90-min | |
| # job timeout, each shard has ample headroom even with 2 retries. | |
| shard: [1, 2, 3] | |
| include: | |
| - wp: '6.9' | |
| wp_env_port: '8890' | |
| wp_env_override: '' | |
| # 2 workers on WP 6.9: 3 workers cause resource contention that | |
| # manifests as login and SPA-render timeouts on CI runners. | |
| playwright_workers: '2' | |
| - wp: 'trunk' | |
| wp_env_port: '8890' | |
| wp_env_override: '{"core":"WordPress/WordPress#master"}' | |
| # 2 workers on WP trunk: same resource contention as WP 6.9 — | |
| # 3 workers cause login and SPA-render timeouts on CI runners. | |
| playwright_workers: '2' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| cache: 'npm' | |
| - name: Setup PHP | |
| uses: shivammathur/setup-php@v2 | |
| with: | |
| php-version: '8.2' | |
| extensions: mbstring, intl, zip | |
| tools: composer:v2 | |
| coverage: none | |
| - name: Get Composer cache directory | |
| id: composer-cache | |
| run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT | |
| - name: Cache Composer dependencies | |
| uses: actions/cache@v5 | |
| with: | |
| path: ${{ steps.composer-cache.outputs.dir }} | |
| key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} | |
| restore-keys: ${{ runner.os }}-composer- | |
| - name: Install Composer dependencies | |
| run: composer install --prefer-dist --no-progress | |
| - name: Install Node dependencies | |
| run: npm ci | |
| - name: Patch @wordpress/env phpunit constraint | |
| # @wordpress/env's CLI.Dockerfile installs phpunit globally. Composer | |
| # 2.6+ defaults audit.block-insecure to true, which blocks ALL phpunit | |
| # versions (v5–v11) due to security advisories PKSA-5jz8-6tcw-pbk4 and | |
| # PKSA-z3gr-8qht-p93v. COMPOSER_NO_AUDIT=1 only skips post-install | |
| # audit reporting — it does NOT disable block-insecure resolution blocking. | |
| # Fix: prepend 'composer config --global audit.block-insecure false &&' | |
| # to disable the resolver-level block before running the install. | |
| # v10.x uses init-config.js; v11.x renamed it to docker-config.js. | |
| # Remove once @wordpress/env ships a fix upstream. | |
| run: | | |
| node -e " | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const base = 'node_modules/@wordpress/env/lib/runtime/docker/'; | |
| const candidates = ['init-config.js', 'docker-config.js']; | |
| const before = 'RUN composer global require --dev phpunit/phpunit:\"^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0\"'; | |
| const after = 'RUN composer config --global audit.block-insecure false && composer global require --dev phpunit/phpunit:\"^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0\"'; | |
| let patched = false; | |
| for (const name of candidates) { | |
| const f = path.resolve(base + name); | |
| if (!fs.existsSync(f)) continue; | |
| let c = fs.readFileSync(f, 'utf8'); | |
| if (!c.includes(before)) { console.log(name + ': already patched or pattern changed, skipping'); continue; } | |
| fs.writeFileSync(f, c.replace(before, after)); | |
| console.log('Patched ' + f); | |
| patched = true; | |
| } | |
| if (!patched) console.log('No files needed patching'); | |
| " | |
| - name: Install Playwright browsers | |
| run: npx playwright install chromium --with-deps | |
| - name: Build plugin assets | |
| run: npm run build | |
| - name: Override wp-env core version | |
| if: matrix.wp_env_override != '' | |
| env: | |
| WP_ENV_OVERRIDE: ${{ matrix.wp_env_override }} | |
| run: | | |
| node -e " | |
| const fs = require('fs'); | |
| const base = JSON.parse(fs.readFileSync('.wp-env.json', 'utf8')); | |
| const override = JSON.parse(process.env.WP_ENV_OVERRIDE); | |
| Object.assign(base, override); | |
| fs.writeFileSync('.wp-env.override.json', JSON.stringify(base, null, 2)); | |
| " | |
| - name: Start wp-env | |
| run: npm run wp-env:start | |
| timeout-minutes: 10 | |
| env: | |
| WP_ENV_HOME: /tmp/wp-env | |
| - name: Wait for wp-env to be ready | |
| run: | | |
| ready=0 | |
| for i in $(seq 1 30); do | |
| if curl -s -o /dev/null -w "%{http_code}" http://localhost:${{ matrix.wp_env_port }}/wp-login.php | grep -q "200"; then | |
| echo "wp-env is ready on port ${{ matrix.wp_env_port }}" | |
| ready=1 | |
| break | |
| fi | |
| echo "Waiting for wp-env... attempt $i" | |
| sleep 5 | |
| done | |
| if [ "$ready" -ne 1 ]; then | |
| echo "wp-env did not become ready in time on port ${{ matrix.wp_env_port }}" >&2 | |
| exit 1 | |
| fi | |
| - name: Verify plugin health | |
| run: | | |
| echo "=== Plugin status ===" | |
| npx wp-env run cli wp plugin list --format=table | |
| echo "" | |
| echo "=== Verify plugin is active ===" | |
| npx wp-env run cli wp plugin is-active gratis-ai-agent && echo "Plugin is active" || echo "ERROR: Plugin is NOT active" | |
| echo "" | |
| echo "=== Check admin page loads (HTTP status) ===" | |
| # Log in and fetch the admin page to verify the SPA renders. | |
| # First get a login cookie, then check the plugin page. | |
| LOGIN_COOKIE=$(curl -s -c - -d "log=admin&pwd=password&wp-submit=Log+In" \ | |
| "http://localhost:${{ matrix.wp_env_port }}/wp-login.php" | grep wordpress_logged_in | awk '{print $NF}') | |
| STATUS=$(curl -s -o /dev/null -w "%{http_code}" -b "wordpress_logged_in_=$(echo $LOGIN_COOKIE)" \ | |
| "http://localhost:${{ matrix.wp_env_port }}/wp-admin/admin.php?page=gratis-ai-agent") | |
| echo "Admin page HTTP status: $STATUS" | |
| echo "" | |
| echo "=== PHP error log (last 30 lines) ===" | |
| npx wp-env run cli cat /var/www/html/wp-content/debug.log 2>/dev/null | tail -30 || echo "(no error log)" | |
| env: | |
| WP_ENV_HOME: /tmp/wp-env | |
| - name: Configure plugin for E2E tests | |
| run: | | |
| # Mark onboarding complete so the main chat UI renders (not the wizard). | |
| # Disable site_builder_mode so the FAB renders instead of the full-screen | |
| # SiteBuilderOverlay (wp-env is a fresh install, which auto-enables it). | |
| npx wp-env run cli wp option update gratis_ai_agent_settings \ | |
| '{"onboarding_complete":true,"site_builder_mode":false}' --format=json | |
| # Configure a fake OpenAI key so the /providers endpoint returns at | |
| # least one provider. Without a provider the AdminPageApp renders the | |
| # ConnectorGate instead of the chat layout, breaking all tests that | |
| # look for .gratis-ai-agent-layout or .gratis-ai-agent-chat-panel. | |
| # The key is never used for real API calls — E2E specs mock the | |
| # /stream endpoint — but its non-empty value is enough for the | |
| # SettingsController::handle_providers() to include OpenAI in the | |
| # list returned to the React app. | |
| npx wp-env run cli wp option update gratis_ai_agent_provider_keys \ | |
| '{"openai":"sk-test-fake-key-for-e2e"}' --format=json | |
| # Prevent FreshInstallDetector from re-enabling site_builder_mode on | |
| # every page load. The wp-env environment is always a "fresh install" | |
| # (default theme, only sample posts), so isFreshInstall() returns true | |
| # and FloatingWidget::enqueue_widget_assets() re-enables site_builder_mode | |
| # even after we set it to false above. Setting this transient to '0' | |
| # caches the detection result as "not fresh" for 5 minutes. | |
| npx wp-env run cli wp transient set gratis_ai_agent_fresh_install 0 300 | |
| # Create a real post so the site is no longer detected as a fresh install | |
| # even after the transient expires (belt-and-suspenders). | |
| npx wp-env run cli wp post create --post_title='E2E Test Post' \ | |
| --post_status=publish --post_type=post | |
| # Create the second admin user required by shared-conversations.spec.js. | |
| # Done here (not in test.beforeAll) so WP_ENV_HOME is available and | |
| # wp-env can locate its docker-compose.yml. | |
| npx wp-env run cli wp user create admin2 admin2@example.com \ | |
| --role=administrator --user_pass=password2 --send-email=false || true | |
| env: | |
| WP_ENV_HOME: /tmp/wp-env | |
| - name: Run Playwright E2E tests (shard ${{ matrix.shard }}/3) | |
| run: npx playwright test --shard=${{ matrix.shard }}/3 | |
| env: | |
| WP_BASE_URL: http://localhost:${{ matrix.wp_env_port }} | |
| WP_ADMIN_USER: admin | |
| WP_ADMIN_PASSWORD: password | |
| CI: true | |
| PLAYWRIGHT_WORKERS: ${{ matrix.playwright_workers }} | |
| - name: Upload Playwright report | |
| uses: actions/upload-artifact@v7 | |
| if: always() | |
| with: | |
| # Artifact name includes shard index to avoid name collisions between | |
| # shards of the same WP version. | |
| name: playwright-report-wp${{ matrix.wp }}-shard${{ matrix.shard }} | |
| path: playwright-report/ | |
| retention-days: 14 | |
| - name: Upload test results | |
| uses: actions/upload-artifact@v7 | |
| if: failure() | |
| with: | |
| name: test-results-wp${{ matrix.wp }}-shard${{ matrix.shard }} | |
| path: test-results/ | |
| retention-days: 7 | |
| - name: Dump PHP error log on failure | |
| if: failure() | |
| run: | | |
| echo "=== PHP debug.log ===" | |
| npx wp-env run cli cat /var/www/html/wp-content/debug.log 2>/dev/null | tail -100 || echo "(no error log)" | |
| env: | |
| WP_ENV_HOME: /tmp/wp-env | |
| - name: Stop wp-env | |
| if: always() | |
| run: npm run wp-env:stop | |
| env: | |
| WP_ENV_HOME: /tmp/wp-env |