Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3f5b53d
chore(ai): add AI assistant infrastructure
Miraeld May 18, 2026
109f8e5
chore(e2e): scaffold Playwright E2E test infrastructure
Miraeld May 18, 2026
c18b09d
chore(e2e): use existing IMAGIFY_TESTS_API_KEY secret name
Miraeld May 18, 2026
8c73926
fix(e2e): correct selectors after first run against real UI
Miraeld May 18, 2026
542f994
fix(e2e): use bulk.statsTable locator instead of stale inline selector
Miraeld May 18, 2026
6e183d3
fix(e2e): target primary save button by ID to avoid strict mode viola…
Miraeld May 19, 2026
76b4ffe
fix(e2e): dismiss SweetAlert2 info modal before expecting progress bar
Miraeld May 19, 2026
317431b
chore: gitignore .env.local for local secrets
Miraeld May 19, 2026
4320737
chore(e2e): add bin/test-e2e.sh local test runner
Miraeld May 19, 2026
9c3789f
fix(e2e): use #imagify-check-api-container selector for invalid key f…
Miraeld May 19, 2026
f90cf7c
fix(e2e): skip bulk progress test when no media attachments are seeded
Miraeld May 19, 2026
c15485d
fix(e2e): dismiss bulk info modal by button text, not SweetAlert2 class
Miraeld May 19, 2026
c077d38
fix(e2e): use delete_post_meta_by_key to reset Imagify state before b…
Miraeld May 19, 2026
3e09555
chore: resolve AGENTS.md merge conflict — combine ai-assistant rules …
Miraeld May 19, 2026
e1c5ab3
Merge pull request #1053 from wp-media/chore/e2e-setup
Miraeld May 19, 2026
0e28390
refactor: move agent definitions to .aiassistant/agents, symlink .cla…
Miraeld May 19, 2026
7ef6cbe
docs(agents): list sub-agents in AGENTS.md with their canonical paths
Miraeld May 19, 2026
6f5d712
feat(issue-workflow): hook qa-engineer and e2e-qa-tester into deliver…
Miraeld May 19, 2026
a6ab519
fix(issue-workflow): enforce pr-template structure when creating PRs
Miraeld May 19, 2026
6611f0e
feat(issue-workflow): run full E2E suite locally before opening PR
Miraeld May 19, 2026
e3caafa
chore(ai): update qa-engineer step to append QA findings to PR body
Miraeld May 19, 2026
8e4771f
feat(qa): post QA report as PR comment with screenshot list
Miraeld May 20, 2026
d841272
fix(settings): improve clarity of picture-tag method label and warning
Miraeld May 20, 2026
e71305a
test(e2e): add settings-webp-method spec for PR #1056 / issue #1015
Miraeld May 20, 2026
045bf12
chore(qa): add QA screenshots
Miraeld May 20, 2026
365c26c
chore(qa): remove QA screenshots
Miraeld May 20, 2026
d7c1471
chore(merge): merge develop into fix/883-htaccess-file-is-not
Miraeld May 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 3 additions & 28 deletions .aiassistant/agents/e2e-qa-tester.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,7 @@ You are an Imagify QA engineer specialized in end-to-end testing. You inherit th
- **Admin login:** `admin` / `password`
- **Boot the env:** `bash bin/dev-up.sh` (idempotent — safe to run if already up)
- **Seed demo content:** `bash bin/dev-seed.sh` — run at the start of every spec where state matters
- **Screenshots root:** `.e2e-screenshots/` (gitignored locally; create if missing)
- **Screenshot publishing:** After all screenshots for a PR are taken, commit them temporarily to the PR branch to get permanent GitHub-hosted URLs:
```bash
git add -f .e2e-screenshots/
git commit -m "chore(qa): add QA screenshots"
git push
SHA=$(git rev-parse HEAD)
# Permanent URL pattern (works forever, even after the file is removed):
# https://raw.githubusercontent.com/wp-media/imagify-plugin/$SHA/.e2e-screenshots/<filename>

# Remove screenshots from tracking in a follow-up commit to keep the branch clean
git rm --cached .e2e-screenshots/*.png
git commit -m "chore(qa): remove QA screenshots"
git push
```
- **Screenshots root:** `.e2e-screenshots/` (gitignored; create if missing)
- **Test files root:** `tests/e2e/`, fixtures under `tests/e2e/fixtures/`, page objects under `tests/e2e/pages/`

If `bin/dev-up.sh` is missing, fall back to `npx @wordpress/env start` and activate the plugin manually.
Expand Down Expand Up @@ -55,8 +41,6 @@ Walk through the PR's "How to test" steps one by one in the browser. At each mea
- Capture console errors and failed network requests.
- Record actual vs. expected.

After completing all manual steps, publish the screenshots using the **Screenshot publishing** steps in the Environment section above. Use the resulting SHA-based URLs in the report.

If the flow exposes a bug, write a clear repro: exact URL, exact clicks, exact observed output. Do not attempt a fix — that belongs to a different agent.

### Step 4 — Convert the validated flow into Playwright tests
Expand All @@ -74,7 +58,7 @@ Once a flow is green manually, write a deterministic spec under `tests/e2e/specs
- **Determinism rules:** never `setTimeout` / arbitrary `waitForTimeout`. Always assert with `expect(locator).toBeVisible({ timeout: ... })` or other web-first assertions.
- **API key guard:** wrap tests that require a live Imagify API key with:
```typescript
test.skip( ! process.env.IMAGIFY_TESTS_API_KEY, 'IMAGIFY_TESTS_API_KEY not set' );
test.skip( ! process.env.IMAGIFY_API_KEY, 'IMAGIFY_API_KEY not set' );
```
- Fixture data goes in `tests/e2e/fixtures/`.

Expand Down Expand Up @@ -109,18 +93,9 @@ Follow the `qa-engineer` output format. For every acceptance criterion or "How t
- Strategy used (Browser via Playwright, API via curl/WP-CLI, Analysis fallback)
- Exact action (URL, click, command)
- Observed result
- Evidence (raw.githubusercontent.com screenshot URL, console error excerpt, JSON response)
- Evidence (screenshot path, console error excerpt, JSON response)
- PASS / FAIL / PARTIAL

Include a `### Screenshots` section at the end with inline images using the SHA-based URLs:
```
### Screenshots
| Step | Screenshot |
|------|-----------|
| Settings page loaded | ![settings](https://raw.githubusercontent.com/wp-media/imagify-plugin/SHA/.e2e-screenshots/filename.png) |
| Warning text visible | ![warning](https://raw.githubusercontent.com/wp-media/imagify-plugin/SHA/.e2e-screenshots/filename2.png) |
```

End with **READY TO MERGE** or a blocker list.

## Constraints
Expand Down
11 changes: 5 additions & 6 deletions .aiassistant/agents/qa-engineer.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ After generating the report, post it as a PR comment so it is immediately visibl

**Post the comment regardless of the overall result** (PASS, FAIL, or PARTIAL) — reviewers need to see the QA status at all times.

If screenshots were captured by the `e2e-qa-tester` agent and published to GitHub (via the commit-SHA method), append a `### Screenshots` section with inline images using the SHA-based raw URLs. If publishing failed, list local paths instead.
If screenshots were captured by the `e2e-qa-tester` agent, check `.e2e-screenshots/` and append a `### Screenshots` section at the bottom of the comment listing each file path. Screenshots are local (not uploaded to GitHub), so list their paths so reviewers know where to find them.

Use the GitHub MCP tool (preferred) or fall back to `gh` CLI:

Expand Down Expand Up @@ -184,11 +184,10 @@ REPORT
- "[scenario]": [reason why it cannot be automated]

### Screenshots
<!-- Include this section only if e2e-qa-tester captured and published screenshots -->
| Step | Screenshot |
|------|-----------|
| [description] | ![step1](https://raw.githubusercontent.com/wp-media/imagify-plugin/SHA/.e2e-screenshots/filename.png) |
| [description] | ![step2](https://raw.githubusercontent.com/wp-media/imagify-plugin/SHA/.e2e-screenshots/filename2.png) |
<!-- Include this section only if e2e-qa-tester captured screenshots -->
The following screenshots were captured during browser testing and are available locally in `.e2e-screenshots/`:
- `.e2e-screenshots/<name>-step1.png`
- `.e2e-screenshots/<name>-step2.png`
```

If all criteria pass: print **READY TO MERGE** clearly.
Expand Down
11 changes: 4 additions & 7 deletions .aiassistant/skills/issue-workflow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ follow this workflow:
6. If relationships are unclear or missing (including Issue Type being `unknown` because Issue Types are disabled, or Project `Type` being `unknown` because the issue is not in a Project or access is missing), proceed as a standalone issue unless an Epic signal is present. Only ask for an epic/sub-issue number when at least one explicit Epic signal or parent/sub-issue is detected.
7. Summarize the issue, feasibility, constraints, and blockers.
8. If a truly blocking ambiguity exists, ask before coding. Otherwise proceed conservatively.
9. Run `.aiassistant/skills/issue-workflow/scripts/make-issue-branch.sh <issue-number> "<issue-title>" "origin/develop"`.
Always pass `origin/develop` as the third argument so the branch is always based on the latest remote develop, regardless of the current working branch or worktree state. Use a different base ref only when the user explicitly requests it.10. Follow `AGENTS.md`.
9. Run `.aiassistant/skills/issue-workflow/scripts/make-issue-branch.sh <issue-number> "<issue-title>"`.
10. Follow `AGENTS.md`.
11. Activate the relevant skills:
- `imagify-architecture`
- `wordpress-compliance`
Expand All @@ -38,10 +38,7 @@ follow this workflow:
15. Run `.aiassistant/skills/issue-workflow/scripts/init-pr-draft.sh <issue-number>`.
16. Fill every section of the PR draft at `.TemporaryItems/Issues/imagify-plugin/pull/<issue-number>.md`. The file was already initialized from `refs/pr-template.md` by the script in step 15. Complete every section with relevant content — do not skip sections or invent a different structure. Replace all placeholder text (`*Explain…*`, `*Describe…*`, etc.) with real content. Tick the appropriate `Type of change` checkbox.
17. Run `git push` to publish the branch.
18. Create the GitHub PR using the **exact content of the filled draft** as the PR body. Do not summarise or rewrite it — copy it verbatim. Set as draft if implementation is still in progress. Assign the PR to yourself immediately after creation:
```bash
gh pr edit <PR_number> --add-assignee @me
```
18. Create the GitHub PR using the **exact content of the filled draft** as the PR body. Do not summarise or rewrite it — copy it verbatim. Set as draft if implementation is still in progress.
19. **Invoke the `qa-engineer` sub-agent** — pass it the issue number and PR number. It will:
- Read the issue spec and PR diff.
- Select validation strategies (API, Browser, Analysis) based on what changed.
Expand Down Expand Up @@ -138,7 +135,7 @@ If an MCP tool is not available in the current session, fall back to the shell e
### PR creation
| Preferred (MCP) | Fallback |
|---|---|
| `mcp__GitKraken__pull_request_create` with `assign_to_me: true` | `gh pr create ... && gh pr edit <number> --add-assignee @me` |
| `mcp_github_github_create_pull_request` | Provide the filled draft manually |

### CI monitoring
| Preferred (MCP) | Fallback |
Expand Down
12 changes: 3 additions & 9 deletions .aiassistant/skills/issue-workflow/scripts/make-issue-branch.sh
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
#!/usr/bin/env bash
# Create a branch name from an issue number and title.
# Usage: make-issue-branch.sh <issue-number> "<issue-title>" [base-ref]
#
# base-ref: optional git ref to branch from (default: current HEAD).
# Pass an explicit ref such as "origin/chore/add-ai-assistant" when
# running inside a worktree or after a git fetch to avoid inheriting
# unexpected commits from a different branch.
# Usage: make-issue-branch.sh <issue-number> "<issue-title>"
set -euo pipefail

# Required arguments.
ISSUE_NUMBER="${1:?issue number required}"
TITLE="${2:?issue title required}"
BASE_REF="${3:-HEAD}"

# Build a short, URL-safe slug from the title (first 4 words max).
SLUG="$(printf '%s' "$TITLE" \
Expand All @@ -22,6 +16,6 @@ SLUG="$(printf '%s' "$TITLE" \
# Branch naming convention: fix/<issue>-<slug>
BRANCH="fix/${ISSUE_NUMBER}-${SLUG}"

# Create and switch to the branch from the explicit base.
git checkout -b "$BRANCH" "$BASE_REF"
# Create and switch to the branch.
git checkout -b "$BRANCH"
echo "$BRANCH"
82 changes: 82 additions & 0 deletions Tests/e2e/specs/settings-webp-method.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { test, expect } from '@playwright/test';
import { loginAsAdmin } from '../fixtures/auth';
import { wpCli } from '../fixtures/wp-cli';
import { SettingsPage } from '../pages/settings';

/**
* Settings — WebP/AVIF next-gen delivery method copy tests.
*
* Validates that the <picture> tags radio label reads "(CDN-compatible)"
* (not the old "(preferred)") and that the warning info box explicitly names
* the categories of content that can be affected.
*
* Related PR: #1056 (fix/1015-improve-clarity-picture-tag)
* Related issue: #1015
*/
test.describe( 'Settings — WebP delivery method copy', () => {
test.beforeEach( async ( { page } ) => {
// Ensure the display_nextgen option is enabled so the method selector
// and its info box are rendered in the DOM.
wpCli(
`eval "update_option('imagify_settings', array_merge(get_option('imagify_settings', []), ['display_nextgen' => true, 'display_nextgen_method' => 'picture']));"`,
);
await loginAsAdmin( page );
} );

test( 'picture-tag radio label reads CDN-compatible, not preferred', async ( { page } ) => {
const settings = new SettingsPage( page );
await settings.goto();
await settings.expectNoFatalError();

// The label associated with the "picture" radio must contain "CDN-compatible".
const pictureLabel = page.locator( 'label' ).filter( { hasText: /CDN-compatible/i } );
await expect( pictureLabel ).toBeVisible( { timeout: 10_000 } );

// The old "(preferred)" wording must not appear anywhere on the page.
const oldWording = page.locator( 'body' ).filter( { hasText: /\(preferred\)/i } );
await expect( oldWording ).toHaveCount( 0 );
} );

test( 'warning info box names sliders, WooCommerce, galleries, background images', async ( { page } ) => {
const settings = new SettingsPage( page );
await settings.goto();
await settings.expectNoFatalError();

// The info box below the delivery-method radios must contain explicit
// references to the content categories that can be broken by the
// <picture> tag method.
const infoBox = page.locator( '#describe-display_nextgen_method' );
await expect( infoBox ).toBeVisible( { timeout: 10_000 } );

const infoText = await infoBox.innerText();

expect( infoText ).toMatch( /sliders/i );
expect( infoText ).toMatch( /WooCommerce/i );
expect( infoText ).toMatch( /galleries/i );
expect( infoText ).toMatch( /[Bb]ackground images/i );
} );

test( 'warning info box explicitly warns about layout breakage', async ( { page } ) => {
const settings = new SettingsPage( page );
await settings.goto();
await settings.expectNoFatalError();

const infoBox = page.locator( '#describe-display_nextgen_method' );
await expect( infoBox ).toBeVisible( { timeout: 10_000 } );

// The warning should use the word "Warning" and mention reviewing the site.
const infoText = await infoBox.innerText();
expect( infoText ).toMatch( /[Ww]arning/i );
expect( infoText ).toMatch( /review/i );
} );

test( 'rewrite-rules radio option is still present', async ( { page } ) => {
const settings = new SettingsPage( page );
await settings.goto();
await settings.expectNoFatalError();

// The alternative "rewrite rules" option must not have been removed.
const rewriteLabel = page.locator( 'label' ).filter( { hasText: /rewrite rules/i } );
await expect( rewriteLabel ).toBeVisible( { timeout: 10_000 } );
} );
} );
4 changes: 2 additions & 2 deletions views/part-settings-webp.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
'values' => [
'rewrite' => __( 'Use rewrite rules', 'imagify' ),
/* translators: 1 and 2 are <em> tag opening and closing. */
'picture' => sprintf( __( 'Use &lt;picture&gt; tags %1$s(preferred)%2$s', 'imagify' ), '<em>', '</em>' ),
'picture' => sprintf( __( 'Use &lt;picture&gt; tags %1$s(CDN-compatible)%2$s', 'imagify' ), '<em>', '</em>' ),
],
'attributes' => [
'aria-describedby' => 'describe-convert_to_webp',
Expand Down Expand Up @@ -152,7 +152,7 @@

printf(
/* translators: 1 and 2 are HTML tag names, 3 is a <strong> tag opening, 4 is the <strong> tag closing. */
esc_html__( 'The second option replaces the %1$s tags with %2$s tags. %3$sThis is the preferred solution but some themes may break%4$s, so make sure to verify that everything seems fine.', 'imagify' ),
esc_html__( 'The second option replaces the %1$s tags with %2$s tags in your page source. Required when using a CDN. %3$sWarning: this may break your site layout — some themes, sliders, WooCommerce pages, or galleries can be affected. Background images are not covered by this method.%4$s Always review your site after enabling, and consider using rewrite rules if you are not on a CDN.', 'imagify' ),
'<code>&lt;img&gt;</code>',
'<code>&lt;picture&gt;</code>',
'<strong>',
Expand Down
Loading