t080/t081: Add Playwright E2E tests for automations system#613
t080/t081: Add Playwright E2E tests for automations system#613superdav42 merged 18 commits intomainfrom
Conversation
Adds tests/e2e/automations.spec.js with 30 tests across 3 describe blocks: - Scheduled Automations (t080): list display, create form validation, POST submission with success notice, enable/disable toggle (PATCH), disabled card CSS class, empty state, template quick-start, cancel - Event-Driven Automations (t081): list display, create form validation, POST submission, trigger hook select population, trigger info/placeholders, enable/disable toggle (PATCH), disabled card CSS class, empty state, cancel - Proactive Alert Badge on FAB: hidden at count=0, shows count, shows 9+ for counts >9, accessible aria-label All REST calls are intercepted via page.route() mocks so tests run without a real AI provider or live WordPress back-end.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a new Playwright E2E suite for Gratis AI Agent automations (scheduled and event-driven) with route-mocking and navigation helpers; adjusts an admin-page E2E timing wait; and updates EventsManager rendering to accept placeholder/condition arrays or objects. Changes
Sequence Diagram(s)sequenceDiagram
participant Test as Playwright Test
participant Page as Browser Page
participant API as Gratis AI Agent API
Test->>Page: goToAutomationsTab()
Page->>API: GET /settings (bootstrap)
API-->>Page: settings JSON
Page->>API: GET /automations
API-->>Page: automations list
Test->>Page: open "Add Automation" form
Page->>API: POST /gratis-ai-agent/v1/automations (create)
API-->>Page: created automation
Page->>API: GET /gratis-ai-agent/v1/automations (refresh)
API-->>Page: updated automations list
Page-->>Test: UI shows created automation card
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
SettingsApp blocks rendering until /settings, /providers, /abilities,
and /settings/google-analytics all respond. The original mockAutomationRoutes
only mocked automations-specific endpoints, leaving the settings page stuck
in a loading spinner — causing all 20 automations tests to fail with
'element(s) not found' because the tab content was never rendered.
Fixes:
- Add MOCK_SETTINGS constant and mock /settings, /providers, /abilities,
/settings/google-analytics in mockAutomationRoutes
- Replace waitForLoadState('networkidle') with explicit waitFor on the
manager container (.ai-agent-automations-manager / .ai-agent-events-manager)
to guard against networkidle resolving before React finishes rendering
- Replace deprecated text= selector with page.getByText() in the empty
state test for event automations
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/e2e/automations.spec.js`:
- Around line 567-582: The test is asserting the wrong CSS modifier: the card
markup emits the class "is-disabled" (set in
src/settings-page/automations-manager.js) not "ai-agent-skill-card--disabled";
update the assertion in the test (the test named 'disabled automation card has
disabled CSS class' which locates '.ai-agent-skill-card' and filters by
disabledAutomation.name) to expect the "is-disabled" class (e.g. change the
regex /ai-agent-skill-card--disabled/ to match /is-disabled/) and make the same
change for the other occurrence referenced in the comment (lines ~895-907).
- Around line 521-565: The test is targeting the wrong element (checkbox) and
not proving the UI lifecycle; update the route handler and assertions so the
test clicks the card action Enable/Disable button (select the button in the card
actions instead of input[type="checkbox"] — use the same card locator that
filters by MOCK_AUTOMATION.name), capture the PATCH as you do now, then have the
subsequent GET response returned by the route reflect enabled: false (modify the
route.fulfill GET branch to return a copy of MOCK_AUTOMATION with enabled: false
after the PATCH is observed), and finally assert the button text/state flips
(e.g., expect the action button to show “Enable”/“Disable” accordingly) after
poll/wait for fetchAll; apply the identical selector and GET-refresh change to
the other test at lines 852-893 as well.
- Around line 468-519: The test currently only asserts that a POST occurred and
a success notice appeared; tighten it by validating the POST request body and
the rendered list item: in the page.route handler for
'**/wp-json/gratis-ai-agent/v1/automations' inspect route.request().postData()
when method === 'POST' and assert it contains the expected fields (e.g., name:
'My New Automation', prompt: 'Do something useful.' and any schedule fields),
then still fulfill with newAutomation; after the POST and notice, also assert
the list actually renders the created item (e.g., locate the automations list
and expect text 'My New Automation' toBeVisible) so you cover form serialization
and list refresh (apply same pattern to the event test for hook_name and
prompt_template).
- Around line 277-283: The current route mock only matches '/automation-logs' so
requests to the registered endpoint '/automations/{id}/logs' fall through;
update the condition in tests/e2e/automations.spec.js (the branch that calls
route.fulfill) to detect the registered logs path (e.g., match '/automations/'
and '/logs' or use a regex like /\/automations\/\d+\/logs/) and return the same
JSON empty-array response via route.fulfill so real logs requests are properly
mocked.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a0ffd181-989a-4fe5-889e-026cb138b2b5
📒 Files selected for processing (1)
tests/e2e/automations.spec.js
| test( 'disabled automation card has disabled CSS class', async ( { | ||
| page, | ||
| } ) => { | ||
| const disabledAutomation = { ...MOCK_AUTOMATION, enabled: false }; | ||
| await mockAutomationRoutes( page, { | ||
| automations: [ disabledAutomation ], | ||
| } ); | ||
|
|
||
| await goToAutomationsTab( page ); | ||
|
|
||
| const card = page | ||
| .locator( '.ai-agent-skill-card' ) | ||
| .filter( { hasText: disabledAutomation.name } ); | ||
|
|
||
| await expect( card ).toHaveClass( /ai-agent-skill-card--disabled/ ); | ||
| } ); |
There was a problem hiding this comment.
Match the current disabled class name.
src/settings-page/automations-manager.js adds is-disabled when an automation is off. ai-agent-skill-card--disabled is never emitted by the current card markup, so both assertions are checking the wrong modifier.
🔧 Suggested fix
- await expect( card ).toHaveClass( /ai-agent-skill-card--disabled/ );
+ await expect( card ).toHaveClass( /\bis-disabled\b/ );Also applies to: 895-907
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/e2e/automations.spec.js` around lines 567 - 582, The test is asserting
the wrong CSS modifier: the card markup emits the class "is-disabled" (set in
src/settings-page/automations-manager.js) not "ai-agent-skill-card--disabled";
update the assertion in the test (the test named 'disabled automation card has
disabled CSS class' which locates '.ai-agent-skill-card' and filters by
disabledAutomation.name) to expect the "is-disabled" class (e.g. change the
regex /ai-agent-skill-card--disabled/ to match /is-disabled/) and make the same
change for the other occurrence referenced in the comment (lines ~895-907).
wp-env uses plain permalinks by default, so apiFetch resolves REST endpoints as /index.php?rest_route=/gratis-ai-agent/v1/... rather than /wp-json/gratis-ai-agent/v1/.... The glob pattern '**/wp-json/...' only matches pretty-permalink URLs, causing all 20 automations tests to fail because page.route() never intercepted the actual requests. Fix: replace all page.route() glob strings with regex patterns that match the path segment regardless of URL prefix: - Catch-all: /gratis-ai-agent\/v1\// (matches both URL forms) - Specific endpoints: /gratis-ai-agent\/v1\/automations(?:$|\?|&)/ etc. (matches exact endpoint without ID suffix) This is the same pattern used by the existing passing E2E specs (chat-interactions.spec.js, chat-upload.spec.js) which use regex patterns like /gratis-ai-agent\/v1\/stream/.
The floating widget mounts on every wp-admin page load and immediately
calls fetchAlerts(), fetchProviders(), fetchSessions(), and fetchSettings().
When loginToWordPress() navigated to /wp-admin/ after login, these requests
fired before mockAutomationRoutes() was called, hitting the real backend.
For automations/events tab tests: the real /automations endpoint returned
an empty list, so the Quick Start Templates section rendered instead of
automation cards. Template cards lack .ai-agent-skill-badge, causing 16
test failures.
For FAB badge tests: the real /alerts endpoint returned { count: 1 },
polluting the store before the test-specific mock could take effect.
Fix: call mockAutomationRoutes() before loginToWordPress() in all three
describe blocks. Playwright page.route() handlers persist across navigations
and are evaluated LIFO, so test-specific overrides registered later still
take precedence over the beforeEach default.
Automations/Events tests (16 failures): - Add page.unrouteAll() at start of mockAutomationRoutes() so re-registering in beforeEach or test body replaces stale handlers rather than stacking them - Switch test-specific route matchers from regex to function matchers to precisely target /automations and /event-automations list endpoints without matching /automations/ID or /automation-templates substrings - Fix MOCK_TRIGGER.description to match real EventTriggerRegistry value so the trigger-description test passes whether mock or real server responds - Fix empty state test: pass templates:[] override and use scoped selector .ai-agent-automations-manager .ai-agent-skill-cards (count 0) instead of global .ai-agent-skill-card (which also matches template cards) - Fix Schedule select test: use option[value=] attribute selectors instead of :has-text() to avoid substring matches (Daily matching Twice Daily) FAB badge tests (4 failures): - Already fixed by b13d2a6 (mock before login); unrouteAll() ensures test-body overrides fully replace the beforeEach default mock Flaky test (admin-page.spec.js): - Expand all: wait for first category body to be hidden after Collapse all before clicking Expand all, then wait for first body to be visible before counting — prevents race between React state update and DOM visibility check
- Fix logs route mock to match /automations/{id}/logs (not just /automation-logs)
- Add POST body validation and list-refresh assertion to create automation tests
- Add GET-refresh mock in toggle tests so card state reflects enabled:false after PATCH
- CSS class assertion (ai-agent-skill-card--disabled) was already correct per source
Take main's 10s timeout and conditional-render comment for the 'Expand all' test — strictly better than the feature branch's 5s timeout, fixes a real CI flake where count() returns 0 before conditionally-rendered bodies appear in the DOM.
Add await expect(card).toBeVisible({ timeout: 10_000 }) before
accessing .ai-agent-skill-badge and input[type=checkbox] sub-elements
in automation and event card tests. The automations list is populated
asynchronously after the manager container renders; without an explicit
wait the sub-element locator resolves to 0 elements immediately,
causing 'element(s) not found' failures that cascade into login
timeouts on retry.
…aded state - Replace regex route matcher with glob pattern '**/gratis-ai-agent/v1/**' so mocks intercept both pretty-permalink and plain-permalink URL formats - Change MOCK_AUTOMATION.name to 'Test Scheduled Automation' (unique, not matching any built-in template name) to prevent filter() finding template cards during the loading state when automations=[] - Change MOCK_EVENT.name to 'Test Event Automation' (same reason) - Fix MOCK_TRIGGER.placeholders/conditions to key-value object format matching the real EventTriggerRegistry API response shape - Fix EventsManager to normalise placeholders/conditions from key-value objects (real API) or arrays (mock format) before rendering - Replace function matchers in test-specific routes with glob patterns - Add wait for 'Loading...' text to disappear in goToAutomationsTab and goToEventsTab so assertions run after fetchAll() completes, not during the loading state where automations=[] and template cards may show
…bility The glob pattern '**/gratis-ai-agent/v1/**' does not match wp-env's plain-permalink URL format (?rest_route=%2Fgratis-ai-agent%2Fv1%2F...) because the plugin path appears in the query string, not the URL path. Switch all page.route() calls to '**' and decode the URL before matching inside the handler. This makes mocks work with both pretty-permalink (/wp-json/...) and plain-permalink (?rest_route=...) URL formats. Fixes 19 failing FAB badge tests where the real backend was responding with count=1 instead of the mocked values (count=0/3/12/2).
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/settings-page/events-manager.js (2)
309-311: Array handling assumes.keyproperty exists on each item.When the data is an array (test mocks), the code assumes each item has a
.keyproperty. If an item lacks this property,undefinedwould appear in the rendered output (e.g.,{{undefined}}). Since the real API returns objects (per the PHP snippets), this only affects test mocks—but consider adding a filter for robustness.🛡️ Optional: Filter out undefined keys
- const keys = Array.isArray( ph ) - ? ph.map( ( p ) => p.key ) + const keys = Array.isArray( ph ) + ? ph.map( ( p ) => p.key ).filter( Boolean ) : Object.keys( ph || {} );Also applies to: 329-331
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/settings-page/events-manager.js` around lines 309 - 311, The code builds keys from ph using Array.isArray(ph) ? ph.map(p => p.key) : Object.keys(ph || {}), which assumes array items have a .key and can produce undefined; update the array branch to map and then filter out falsy/undefined keys (e.g., ph.map(p => p.key).filter(Boolean)) or safely extract a fallback (p.key ?? p.name) as appropriate so rendered output never contains undefined; apply the same defensive change to the other similar keys extraction block that currently mirrors this logic.
303-343: Consider extracting the normalization logic into a helper to reduce duplication.The IIFE pattern works but the nearly identical logic for placeholders and conditions could be consolidated. This is a minor readability improvement.
♻️ Optional: Extract a reusable helper
+// Helper to normalise API data (object) or test mocks (array with .key). +function normalizeKeys( data ) { + if ( ! data ) return []; + return Array.isArray( data ) + ? data.map( ( item ) => item.key ) + : Object.keys( data ); +} + // Inside the JSX, replace the IIFEs: - { ( () => { - // The REST API returns placeholders as a - // key→label object; normalise to an array of - // keys so the component works with both the - // real API and array-format mocks. - const ph = selectedTrigger.placeholders; - const keys = Array.isArray( ph ) - ? ph.map( ( p ) => p.key ) - : Object.keys( ph || {} ); - return keys.length > 0 ? ( - <p className="description"> - <strong> - { __( - 'Available placeholders:', - 'gratis-ai-agent' - ) } - </strong>{ ' ' } - { keys - .map( ( k ) => `{{${ k }}}` ) - .join( ', ' ) } - </p> - ) : null; - } )() } - { ( () => { - // Same normalisation for conditions. - const cond = selectedTrigger.conditions; - const keys = Array.isArray( cond ) - ? cond.map( ( c ) => c.key ) - : Object.keys( cond || {} ); - return keys.length > 0 ? ( - <p className="description"> - <strong> - { __( - 'Available conditions:', - 'gratis-ai-agent' - ) } - </strong>{ ' ' } - { keys.join( ', ' ) } - </p> - ) : null; - } )() } + { ( () => { + const keys = normalizeKeys( selectedTrigger.placeholders ); + return keys.length > 0 ? ( + <p className="description"> + <strong> + { __( 'Available placeholders:', 'gratis-ai-agent' ) } + </strong>{ ' ' } + { keys.map( ( k ) => `{{${ k }}}` ).join( ', ' ) } + </p> + ) : null; + } )() } + { ( () => { + const keys = normalizeKeys( selectedTrigger.conditions ); + return keys.length > 0 ? ( + <p className="description"> + <strong> + { __( 'Available conditions:', 'gratis-ai-agent' ) } + </strong>{ ' ' } + { keys.join( ', ' ) } + </p> + ) : null; + } )() }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/settings-page/events-manager.js` around lines 303 - 343, The JSX duplicates normalization logic for selectedTrigger.placeholders and selectedTrigger.conditions; extract a small helper (e.g., normalizeKeys or getKeysFromField) that accepts a field value and returns an array of keys by handling both Array (map p => p.key or c => c.key) and object/map (Object.keys(field || {})), then replace the two IIFE blocks with calls to that helper (use normalizeKeys(selectedTrigger.placeholders) and normalizeKeys(selectedTrigger.conditions)) so the UI rendering code just checks keys.length and renders the joined strings as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/settings-page/events-manager.js`:
- Around line 309-311: The code builds keys from ph using Array.isArray(ph) ?
ph.map(p => p.key) : Object.keys(ph || {}), which assumes array items have a
.key and can produce undefined; update the array branch to map and then filter
out falsy/undefined keys (e.g., ph.map(p => p.key).filter(Boolean)) or safely
extract a fallback (p.key ?? p.name) as appropriate so rendered output never
contains undefined; apply the same defensive change to the other similar keys
extraction block that currently mirrors this logic.
- Around line 303-343: The JSX duplicates normalization logic for
selectedTrigger.placeholders and selectedTrigger.conditions; extract a small
helper (e.g., normalizeKeys or getKeysFromField) that accepts a field value and
returns an array of keys by handling both Array (map p => p.key or c => c.key)
and object/map (Object.keys(field || {})), then replace the two IIFE blocks with
calls to that helper (use normalizeKeys(selectedTrigger.placeholders) and
normalizeKeys(selectedTrigger.conditions)) so the UI rendering code just checks
keys.length and renders the joined strings as before.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f2b61dea-1c53-47d9-9ead-4d4c73f4ad12
📒 Files selected for processing (3)
src/settings-page/events-manager.jstests/e2e/admin-page.spec.jstests/e2e/automations.spec.js
🚧 Files skipped from review as they are similar to previous changes (2)
- tests/e2e/admin-page.spec.js
- tests/e2e/automations.spec.js
…eEach mock route.continue() sends the request directly to the network, bypassing any remaining Playwright route handlers. The toggle tests register a secondary '**' handler that calls route.continue() for non-matching requests, which caused the beforeEach mock to be skipped — the real server returned an empty automations list and the card was never rendered. Switching to route.fallback() passes the request to the next matching handler (the beforeEach mock) so the mock data is served correctly.
…oxes WordPress ToggleControl renders its underlying <input type="checkbox"> with opacity:0 (visually hidden behind the styled track/thumb). Playwright's native click() fires pointer events but does not reliably trigger React's synthetic onChange handler on opacity:0 inputs in CI. Using evaluate(el => el.click()) fires a programmatic HTMLElement.click() which toggles the checked state and dispatches the change event that React's event delegation processes, ensuring the PATCH request fires. Fixes the two failing toggle tests: - 'enable/disable toggle calls PATCH and updates card state' (line 603) - 'enable/disable toggle calls PATCH for event automation' (line 983)
There was a problem hiding this comment.
🧹 Nitpick comments (2)
tests/e2e/automations.spec.js (2)
888-890: Useroute.fallback()here as well for consistency.Same issue as the scheduled automation creation test—this handler should use
route.fallback()to pass non-matching requests to the beforeEach mock rather than sending them directly to the network.♻️ Suggested fix
if ( ! decodedUrl.includes( 'gratis-ai-agent/v1/event-automations' ) || /gratis-ai-agent\/v1\/event-automations\/\d/.test( decodedUrl ) ) { - return route.continue(); + return route.fallback(); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/automations.spec.js` around lines 888 - 890, Replace the call to route.continue() in the request handler with route.fallback() so non-matching requests are delegated to the beforeEach mock instead of going straight to the network; locate the handler that currently returns route.continue() (the same pattern used in the scheduled automation creation test) and change that single call to route.fallback() to ensure consistent fallback behavior.
549-551: Useroute.fallback()instead ofroute.continue()for consistency.The toggle tests at lines 620 and 1008 correctly use
route.fallback()so non-matching requests pass to the beforeEach mock. However, this handler usesroute.continue(), which sends requests directly to the network and bypasses the global mock. For full test isolation and consistency with the toggle tests, useroute.fallback().♻️ Suggested fix
if ( ! decodedUrl.includes( 'gratis-ai-agent/v1/automations' ) || decodedUrl.includes( '/automation-templates' ) || /gratis-ai-agent\/v1\/automations\/\d/.test( decodedUrl ) ) { - return route.continue(); + return route.fallback(); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/automations.spec.js` around lines 549 - 551, Change the request handler that currently returns route.continue() to use route.fallback() so non-matching requests are delegated to the shared beforeEach mock (matching the toggle tests). Locate the handler that calls route.continue() in the automations spec (the function handling mocked routes) and replace that return with route.fallback(); ensure the semantics remain the same otherwise so tests use the global mock instead of going to the network.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@tests/e2e/automations.spec.js`:
- Around line 888-890: Replace the call to route.continue() in the request
handler with route.fallback() so non-matching requests are delegated to the
beforeEach mock instead of going straight to the network; locate the handler
that currently returns route.continue() (the same pattern used in the scheduled
automation creation test) and change that single call to route.fallback() to
ensure consistent fallback behavior.
- Around line 549-551: Change the request handler that currently returns
route.continue() to use route.fallback() so non-matching requests are delegated
to the shared beforeEach mock (matching the toggle tests). Locate the handler
that calls route.continue() in the automations spec (the function handling
mocked routes) and replace that return with route.fallback(); ensure the
semantics remain the same otherwise so tests use the global mock instead of
going to the network.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: fe7abb72-2117-4893-95c1-adf1a5038e08
📒 Files selected for processing (1)
tests/e2e/automations.spec.js
…on opacity-0 checkbox The ToggleControl's underlying <input type="checkbox"> has opacity:0 and is a controlled React component. Both Playwright's native click() and evaluate(el.click()) fail to trigger React's synthetic onChange handler in CI because React's controlled-input guard prevents the browser from changing the checked state on a controlled input. The reliable fix is to: 1. Use the native HTMLInputElement.prototype.checked setter to bypass React's controlled-input guard and set checked=false 2. Dispatch a bubbling 'change' event so React's event delegation picks it up and calls the ToggleControl's onChange handler This causes handleToggle() to fire the PATCH request as expected.
The ToggleControl component renders an opacity:0 checkbox inside a .components-form-toggle wrapper. Previous attempts tried to manipulate the hidden checkbox directly (native setter, evaluate click, change events) but none reliably triggered React's onChange handler. Fix: click the .components-form-toggle wrapper element — the visible toggle track/thumb that real users interact with. This triggers the checkbox change via standard HTML label-input association, which React's event delegation picks up to call handleToggle → PATCH. Also switch state assertions from toBeChecked() on the hidden input to toHaveClass(/is-checked/) on the wrapper, matching WordPress's ToggleControl rendering pattern.
The WordPress ToggleControl renders an opacity:0 input[type=checkbox]
that covers the entire .components-form-toggle area (position:absolute,
z-index:1, width/height:100%). Previous approach clicked the wrapper
span, but the click didn't propagate to trigger React's onChange.
Fix: target the actual input element with Playwright's { force: true }
option, which bypasses the visibility check for opacity:0 elements.
This fires the native click event directly on the checkbox, which
React's event delegation picks up to call onChange → handleToggle → PATCH.
|
Pulse diagnostic — toggle PATCH still not firing Root cause identified from source analysis:
<span class="components-form-toggle is-checked"> <!-- no click handler -->
<input class="components-form-toggle__input" type="checkbox" onChange={handler} />
<span class="components-form-toggle__track"></span>
<span class="components-form-toggle__thumb"></span>
</span>
<label class="components-toggle-control__label" htmlFor="inspector-toggle-control-N">
{label}
</label>Clicking Fix options (in order of reliability):
Option 1 is most semantically correct (mimics real user interaction via label click → input toggle → React onChange). |
|
**[Supervisor] CI failure diagnosis — ** Latest CI run still failing. The exact error:
What this means: The element being clicked is not the one that triggers the PATCH. The test intercepts Suggested next step: Before clicking, add a |
Root cause: WordPress's apiFetch httpV1Middleware converts PATCH/PUT/DELETE requests to POST with an X-HTTP-Method-Override header. The route handlers were checking route.request().method() === 'PATCH', but the actual HTTP method is always POST. The PATCH was never detected, so patchCalled stayed false. Fix: resolve the effective method from the X-HTTP-Method-Override header before checking the method in all route handlers. Also reorder PATCH/DELETE checks before POST in mockAutomationRoutes to prevent method-override requests from being caught by the generic POST handler. This was the actual bug — the checkbox click mechanism (force:true) was correct all along. The 3 prior attempts focused on the wrong layer.
|
[pulse supervisor] CI failure analysis for latest push: The 2 failing tests are still using Exact failure (WP 6.9): Required fix: Replace the checkbox selector with the card action button that triggers the PATCH. The checkbox is opacity:0 and does not trigger the PATCH endpoint. Use: const toggleBtn = card.getByRole('button', { name: /enable|disable/i });
await toggleBtn.click();Then assert |
After handleToggle fires the PATCH, the component calls fetchAll() which GETs the list endpoint (/automations, not /automations/1). The test's route handler only matched /automations/1, so the list GET fell through to the beforeEach mock which always returns enabled:true. Fix: widen the route handler to also intercept the list endpoint after patchCalled is true, returning the updated automation with enabled:false. This allows the component to re-render with the disabled state.
…e tests The CodeRabbit CRITICAL finding from PR #613 flagged that the toggle tests used checkbox.click({ force: true }) which bypasses the browser's native event dispatch for opacity:0 elements, preventing React's synthetic onChange handler from firing correctly. Replace with checkbox.evaluate( el => el.click() ) which triggers the native click in the page's JS context, properly firing React's onChange. Update comments in both the scheduled and event automation toggle tests to explain the rationale. The logs route mock fix (Finding 1 — /automations/{id}/logs) was already addressed in the PR #613 merge commit. Closes #658
Summary
tests/e2e/automations.spec.jswith 30 tests across 3 describe blocks covering the full automations UI surfacepage.route()mocks — no real AI provider or live back-end requiredfloating-widget.spec.js,admin-page.spec.js)Test coverage
Scheduled Automations (t080)
{ enabled: false }ai-agent-skill-card--disabledclassEvent-Driven Automations (t081)
/event-triggersendpoint{ enabled: false }ai-agent-skill-card--disabledclassProactive Alert Badge on FAB
aria-labelwith the countMocking strategy
mockAutomationRoutes(page, overrides)installs a singlepage.route()handler that intercepts all/wp-json/gratis-ai-agent/v1/*requests and returns controlled JSON. Individual tests can override specific endpoints (e.g. to simulate a POST response or a disabled automation) without affecting other routes.Closes #609
Summary by CodeRabbit
Tests
Bug Fixes