Skip to content

feat: add Playwright e2e tests for the block editor#40

Merged
bfintal merged 8 commits into
developfrom
feat/playwright-e2e-tests
Jul 15, 2026
Merged

feat: add Playwright e2e tests for the block editor#40
bfintal merged 8 commits into
developfrom
feat/playwright-e2e-tests

Conversation

@bfintal

@bfintal bfintal commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add Playwright e2e infrastructure with wp-env support, custom fixtures, and block editor tests for the Interactions Library toolbar button, pinned sidebar button, and library insert flow
  • Add a shared CI build job and matrix test workflow (modeled after Stackable #3729) that reuses a pre-built plugin artifact across WP/PHP combinations
  • Add build:e2e scripts and a matrix sync tool driven by readme.txt tested-up-to versions

Test plan

  • npm run build:e2e completes successfully
  • npm run test passes locally (3/3 block editor tests)
  • Playwright CI workflow passes on all matrix jobs after merge
  • Premium repo PR with matching workflow is merged alongside this

Made with Cursor

Summary by CodeRabbit

  • New Features
    • Added Playwright end-to-end tests covering Interactions and Elementor editor workflows (insert and navigation flows).
    • Introduced a CI “Playwright Tests” pipeline running against a WordPress/PHP compatibility matrix.
    • Added environment configuration for WordPress test setup (URL/auth storage/credentials and plugin slugs).
  • Documentation
    • Added instructions for running the end-to-end test suite locally and in CI.
  • Bug Fixes
    • Improved test failure reporting with a generated error summary and linked HTML reports.

Introduce Playwright-based end-to-end testing with a shared CI build job,
block editor coverage for the Interactions toolbar and library insert flow,
and wp-env tooling for local and matrix testing across supported WP/PHP versions.

Co-authored-by: Cursor <cursoragent@cursor.com>
@cursor

cursor Bot commented Jul 15, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@bfintal, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 45 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2d7871af-eb00-481a-87ec-03e36ae49d26

📥 Commits

Reviewing files that changed from the base of the PR and between dab1697 and dd747ab.

📒 Files selected for processing (5)
  • .github/workflows/playwright.yml
  • .github/workflows/plugin-build.yml
  • .nvmrc
  • e2e/readme.md
  • package.json
📝 Walkthrough

Walkthrough

Adds a complete Playwright E2E testing setup for the Interactions plugin, including WordPress fixtures, Block Editor and Elementor coverage, custom reporting, local configuration, and a PHP/WordPress GitHub Actions matrix.

Changes

Playwright E2E testing

Layer / File(s) Summary
Environment and test tooling
.env.example, .wp-env.json, .gitignore, tsconfig.json, package.json, e2e/readme.md
Adds environment variables, WordPress and TypeScript configuration, test scripts, ignored outputs, and E2E execution instructions.
WordPress REST setup and fixtures
e2e/test-utils/*, e2e/config/global-setup.ts
Adds authenticated REST helpers, plugin activation management, content cleanup, editor helpers, UI locators, Elementor helpers, and reusable Playwright fixtures.
Playwright runner and reporting
e2e/playwright.config.ts, e2e/config/reporter.ts, e2e/test-utils/format.ts
Configures the Interactions project and adds formatted per-test output plus Markdown failure reporting.
Block Editor and Elementor coverage
e2e/tests/block-editor.spec.ts, e2e/tests/elementor.spec.ts
Tests toolbar visibility, preset insertion, editor block changes, Elementor sidebar behavior, modal closure, and interactions-list navigation.
CI matrix and workflow execution
tools/playwright-test-matrix.js, .github/workflows/playwright.yml
Generates WordPress/PHP combinations and runs builds, wp-env, browser installation, Playwright tests, and failure artifact publication in GitHub Actions.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions
  participant WordPressEnv
  participant Playwright
  participant InteractionsPlugin
  GitHubActions->>WordPressEnv: start PHP/WordPress matrix environment
  GitHubActions->>Playwright: run npm run test
  Playwright->>WordPressEnv: create authenticated request context
  Playwright->>InteractionsPlugin: open Block Editor or Elementor interaction UI
  InteractionsPlugin-->>Playwright: render sidebar, preset, or interaction list
  Playwright-->>GitHubActions: publish test results and failure artifacts
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title reflects a real part of the PR, though the changes also add broader e2e infrastructure and CI updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/playwright-e2e-tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Verify the Interactions launcher appears in the lower right when editing
a page with Elementor, and install/activate Elementor in wp-env and CI.

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (6)
e2e/readme.md (2)

10-16: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Specify the language for the code block.

Add the dotenv or env language identifier to the fenced code block to improve syntax highlighting and resolve a markdownlint warning.

♻️ Proposed refactor
-```
+```env
 WP_BASE_URL=http://localhost:8889
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/readme.md` around lines 10 - 16, Update the fenced environment-variable
block in the README to specify the env or dotenv language identifier, preserving
all existing variable entries.

Source: Linters/SAST tools


44-45: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fix capitalization of "GitHub".

The official name of the platform is spelled with a capital "H".

♻️ Proposed refactor
 - Our main basis: https://github.com/meszarosrob/wordpress-e2e-playwright-intro-2023
-- Gutenberg e2e Github workflow: https://github.com/WordPress/gutenberg/blob/trunk/.github/workflows/end2end-test.yml
+- Gutenberg e2e GitHub workflow: https://github.com/WordPress/gutenberg/blob/trunk/.github/workflows/end2end-test.yml
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/readme.md` around lines 44 - 45, Update the platform name in the README
reference to use the official capitalization “GitHub,” including the workflow
description and URL text where applicable.

Source: Linters/SAST tools

e2e/test-utils/editor.ts (1)

3-28: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use primitive string instead of the String object wrapper.

In TypeScript, String refers to the JavaScript String object, which behaves differently from the primitive string type. Best practice is to use the primitive string type for variables and arrays (string[] or Array<string>).

♻️ Proposed refactor
 class ExtendedEditor extends BaseEditor {
-	blockErrors: Array<String> = []
+	blockErrors: string[] = []

-	getBlockAttributes = async function( clientId : String ) {
+	getBlockAttributes = async function( clientId: string ) {
 		await this.page.waitForFunction(
 			() => window?.wp?.blocks && window?.wp?.data
 		)

 		const attributes = await this.page.evaluate( async ( [ _clientId ] ) => {
 			return await window.wp.data.select( 'core/block-editor' ).getBlockAttributes( _clientId )
 		}, [ clientId ] )

 		return attributes
 	}

-	getBlockErrors() :Array<String> {
+	getBlockErrors(): string[] {
 		this.blockErrors = []
 		this.page.on( 'console', msg => {
 			if ( msg.type() === 'error' && msg.text().startsWith( 'Block validation' ) ) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/test-utils/editor.ts` around lines 3 - 28, Replace the boxed String types
with the primitive string type in ExtendedEditor, including blockErrors, the
clientId parameter, and the getBlockErrors return type; preserve the existing
behavior.
e2e/config/global-setup.ts (1)

26-26: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Guard against a missing INTERACTIONS_SLUG.

If the env var isn't set, this calls activatePlugin(undefined), producing a confusing REST error instead of a clear setup failure. Since this runs once before the whole suite, a clear message saves debugging time for anyone missing the env var locally.

Suggested fix
+	if ( ! process.env.INTERACTIONS_SLUG ) {
+		throw new Error( 'INTERACTIONS_SLUG environment variable is required for global setup.' )
+	}
+
 	await requestUtils.activatePlugin( process.env.INTERACTIONS_SLUG )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/config/global-setup.ts` at line 26, Validate that INTERACTIONS_SLUG is
set before the activatePlugin call in global setup, and fail immediately with a
clear configuration error when it is missing. Only invoke
requestUtils.activatePlugin with a defined slug.
tools/playwright-test-matrix.js (2)

78-138: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add unit tests for the version-arithmetic helpers.

subtractMinorVersions, getMinorKey, resolveLatestWordPressPatch, and buildPlaywrightMatrix encode non-obvious invariants (e.g., minor-version wraparound assumes exactly 10 minors per major) and are already exported (lines 223-239) specifically for testability, but no spec accompanies this file in the reviewed diff. Given they directly determine the CI test matrix, cheap unit tests would catch version-math regressions before they silently produce a wrong matrix.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/playwright-test-matrix.js` around lines 78 - 138, Add unit tests for
the exported helpers subtractMinorVersions, getMinorKey,
resolveLatestWordPressPatch, and buildPlaywrightMatrix. Cover minor-version
subtraction and wraparound, minor-key normalization, latest patch selection with
no-match fallback, and the generated matrix entries for supported WordPress and
PHP versions.

203-214: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Redundant duplicate network call to the WordPress version-check API.

fetchLatestWordPressRelease() (lines 73-76) already calls fetchWordPressReleases() internally, but main() doesn't pass the resulting list into syncPlaywrightTestMatrix, so syncPlaywrightTestMatrix (line 173) fetches it a second time. This doubles network round-trips and doubles the chance of a transient failure for no benefit.

Suggested fix
 const fetchLatestWordPressRelease = async () => {
 	const releases = await fetchWordPressReleases()
-	return releases[ 0 ]
+	return { latest: releases[ 0 ], releases }
 }
 const main = async () => {
 	const testedUpTo = getTestedUpToFromReadme()
 	if ( ! testedUpTo ) {
 		throw new Error( 'Could not read "Tested up to" from readme.txt' )
 	}

-	const latestRelease = await fetchLatestWordPressRelease()
+	const { latest: latestRelease, releases } = await fetchLatestWordPressRelease()
 	await syncPlaywrightTestMatrix( {
 		testedUpTo,
 		minPhp: latestRelease.phpVersion,
+		releases,
 	} )
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/playwright-test-matrix.js` around lines 203 - 214, Reuse the WordPress
releases fetched by fetchLatestWordPressRelease() when calling
syncPlaywrightTestMatrix in main, so the matrix sync does not invoke
fetchWordPressReleases() again. Update the relevant function signatures and call
sites to pass the existing release list while preserving the current minPhp and
testedUpTo behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/playwright.yml:
- Line 11: Update both actions/checkout@v4 steps in the build and test jobs to
disable credential persistence by setting persist-credentials to false,
preserving the existing checkout behavior otherwise.
- Around line 1-9: Add an explicit least-privilege permissions block at the
workflow or build job level in the Playwright Tests workflow, granting only the
read access required for checkout and the write access required for artifact or
summary uploads; set all unspecified permissions to none.
- Around line 129-133: Update the “Add test results to summary” step to pass
steps.mto.outputs.body and steps.artifact-upload-step.outputs.artifact-id
through the step’s env context, then reference only those environment variables
inside run. Preserve the existing summary text and artifact URL while preventing
untrusted output from being interpolated into shell script syntax.

In `@e2e/config/reporter.ts`:
- Line 7: Import and use the exported ansiRegex in the reporter’s error
formatting flow to remove ANSI escape sequences from Playwright error.message
and error.snippet before generating Markdown or GitHub step-summary output.
Preserve the existing report content apart from this sanitization, and reuse the
ansiRegex export from e2e/test-utils/format.ts rather than adding a new pattern.
- Around line 58-67: Update the error-reporting loop in reporter handling to
apply the imported ansiRegex when appending each error.message and
error.snippet, ensuring ANSI escape codes are removed before writing the
Markdown testResult while preserving the existing formatting and conditional
checks.

In `@e2e/playwright.config.ts`:
- Around line 10-12: Update the Playwright configuration’s globalSetup path to
use path.resolve with the existing __dirname instead of fileURLToPath and new
URL, and remove the now-unused url module import. Preserve the resolved
global-setup.ts target while ensuring Windows-compatible path handling.

In `@e2e/tests/block-editor.spec.ts`:
- Around line 41-71: Align the enclosing timeout in this interaction-library
test with the cumulative waits used by the assertions, or reduce the individual
assertion timeouts so their worst-case duration fits within test.setTimeout.
Update the timeout configuration around the interaction insertion flow while
preserving the existing diagnostic assertions and their ordering.

---

Nitpick comments:
In `@e2e/config/global-setup.ts`:
- Line 26: Validate that INTERACTIONS_SLUG is set before the activatePlugin call
in global setup, and fail immediately with a clear configuration error when it
is missing. Only invoke requestUtils.activatePlugin with a defined slug.

In `@e2e/readme.md`:
- Around line 10-16: Update the fenced environment-variable block in the README
to specify the env or dotenv language identifier, preserving all existing
variable entries.
- Around line 44-45: Update the platform name in the README reference to use the
official capitalization “GitHub,” including the workflow description and URL
text where applicable.

In `@e2e/test-utils/editor.ts`:
- Around line 3-28: Replace the boxed String types with the primitive string
type in ExtendedEditor, including blockErrors, the clientId parameter, and the
getBlockErrors return type; preserve the existing behavior.

In `@tools/playwright-test-matrix.js`:
- Around line 78-138: Add unit tests for the exported helpers
subtractMinorVersions, getMinorKey, resolveLatestWordPressPatch, and
buildPlaywrightMatrix. Cover minor-version subtraction and wraparound, minor-key
normalization, latest patch selection with no-match fallback, and the generated
matrix entries for supported WordPress and PHP versions.
- Around line 203-214: Reuse the WordPress releases fetched by
fetchLatestWordPressRelease() when calling syncPlaywrightTestMatrix in main, so
the matrix sync does not invoke fetchWordPressReleases() again. Update the
relevant function signatures and call sites to pass the existing release list
while preserving the current minPhp and testedUpTo behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dfa7fec0-3e0d-4c98-8edb-fc95d6bd9129

📥 Commits

Reviewing files that changed from the base of the PR and between da7b2c7 and 34982f4.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (18)
  • .env.example
  • .github/workflows/playwright.yml
  • .gitignore
  • .wp-env.json
  • e2e/config/global-setup.ts
  • e2e/config/reporter.ts
  • e2e/playwright.config.ts
  • e2e/readme.md
  • e2e/test-utils/editor.ts
  • e2e/test-utils/format.ts
  • e2e/test-utils/index.ts
  • e2e/test-utils/interactions.ts
  • e2e/test-utils/requestUtils.ts
  • e2e/test-utils/test.ts
  • e2e/tests/block-editor.spec.ts
  • package.json
  • tools/playwright-test-matrix.js
  • tsconfig.json

Comment thread .github/workflows/playwright.yml
Comment thread .github/workflows/playwright.yml
Comment thread .github/workflows/playwright.yml Outdated
Comment thread e2e/config/reporter.ts
Reporter, Suite, TestCase,
} from '@playwright/test/reporter'

import { ms } from 'e2e/test-utils'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Import ansiRegex to strip color codes.

Playwright's error.message and error.snippet include ANSI escape codes for terminal coloring. If these aren't stripped, they will pollute the resulting Markdown report (e.g., showing up as [31mExpected[39m) and render poorly in GitHub step summaries.

You can utilize the ansiRegex that was exported in e2e/test-utils/format.ts but left unused.

🐛 Proposed fix to sanitize error outputs
-import { ms } from 'e2e/test-utils'
+import { ms, ansiRegex } from 'e2e/test-utils'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { ms } from 'e2e/test-utils'
import { ms, ansiRegex } from 'e2e/test-utils'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/config/reporter.ts` at line 7, Import and use the exported ansiRegex in
the reporter’s error formatting flow to remove ANSI escape sequences from
Playwright error.message and error.snippet before generating Markdown or GitHub
step-summary output. Preserve the existing report content apart from this
sanitization, and reuse the ansiRegex export from e2e/test-utils/format.ts
rather than adding a new pattern.

Comment thread e2e/config/reporter.ts
Comment on lines +58 to +67
if ( result.errors.length >= 1 ) {
result.errors.forEach( error => {
if ( error.message ) {
testResult += `${ error.message }` + '\n\n'
}

if ( error.snippet ) {
testResult += `${ error.snippet }` + `\n\n`
}
} )

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Strip ANSI codes from error messages and snippets.

Applying the imported ansiRegex will sanitize the error details before they are written to the Markdown report.

♻️ Proposed refactor
 			if ( result.errors.length >= 1 ) {
 				result.errors.forEach( error => {
 					if ( error.message ) {
-						testResult += `${ error.message }` + '\n\n'
+						testResult += `${ error.message.replace( ansiRegex, '' ) }` + '\n\n'
 					}
 
 					if ( error.snippet ) {
-						testResult += `${ error.snippet }` + `\n\n`
+						testResult += `${ error.snippet.replace( ansiRegex, '' ) }` + `\n\n`
 					}
 				} )
 			}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ( result.errors.length >= 1 ) {
result.errors.forEach( error => {
if ( error.message ) {
testResult += `${ error.message }` + '\n\n'
}
if ( error.snippet ) {
testResult += `${ error.snippet }` + `\n\n`
}
} )
if ( result.errors.length >= 1 ) {
result.errors.forEach( error => {
if ( error.message ) {
testResult += `${ error.message.replace( ansiRegex, '' ) }` + '\n\n'
}
if ( error.snippet ) {
testResult += `${ error.snippet.replace( ansiRegex, '' ) }` + `\n\n`
}
} )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/config/reporter.ts` around lines 58 - 67, Update the error-reporting loop
in reporter handling to apply the imported ansiRegex when appending each
error.message and error.snippet, ensuring ANSI escape codes are removed before
writing the Markdown testResult while preserving the existing formatting and
conditional checks.

Comment thread e2e/playwright.config.ts
Comment on lines +10 to +12
globalSetup: fileURLToPath(
new URL( './config/global-setup.ts', 'file:' + __filename ).href
),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Simplify file path resolution to prevent cross-platform crashes.

The use of fileURLToPath and new URL with 'file:' + __filename will throw a TypeError: Invalid URL on Windows, as drive letters (e.g., C:\) do not form a valid file:// base URL structure without additional parsing.

Since __dirname is successfully used on line 7, you can safely simplify this to use path.resolve, which also allows you to remove the url module import at the top of the file.

♻️ Proposed fix
-	globalSetup: fileURLToPath(
-		new URL( './config/global-setup.ts', 'file:' + __filename ).href
-	),
+	globalSetup: path.resolve( __dirname, './config/global-setup.ts' ),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
globalSetup: fileURLToPath(
new URL( './config/global-setup.ts', 'file:' + __filename ).href
),
globalSetup: path.resolve( __dirname, './config/global-setup.ts' ),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/playwright.config.ts` around lines 10 - 12, Update the Playwright
configuration’s globalSetup path to use path.resolve with the existing __dirname
instead of fileURLToPath and new URL, and remove the now-unused url module
import. Preserve the resolved global-setup.ts target while ensuring
Windows-compatible path handling.

Comment on lines +41 to +71
test.setTimeout( 60000 )

const initialBlockCount = await editor.canvas.locator( '[data-type]' ).count()

// Open the Interactions sidebar first so the app is mounted before the insert event fires.
await interactions.getSidebarToolbarButton().click()

// Open the Interaction Library from the top toolbar.
await interactions.getLibraryToolbarButton().click()
await expect( interactions.getInteractionLibraryModal() ).toBeVisible()

// Wait for presets to finish loading.
const firstFreePreset = interactions.getFirstFreePresetCard()
await expect( firstFreePreset ).toBeVisible( { timeout: 30000 } )

// Hover to reveal the Insert button, then click it.
await firstFreePreset.hover()
const insertButton = firstFreePreset.getByRole( 'button', { name: 'Insert' } )
await expect( insertButton ).toBeVisible()
await insertButton.click()

// Modal should close after inserting.
await expect( interactions.getInteractionLibraryModal() ).toBeHidden( { timeout: 15000 } )

// New blocks should be added to the editor.
await expect( editor.canvas.locator( '[data-type]' ) ).not.toHaveCount( initialBlockCount, { timeout: 15000 } )
await expect( editor.canvas.locator( '[data-type]' ).count() ).resolves.toBeGreaterThan( initialBlockCount )

// Interactions sidebar should show an interaction being edited.
await expect( page.locator( '.interface-complementary-area' ).filter( { has: interactions.getInteractionsSidebar() } ) ).toBeVisible( { timeout: 15000 } )
await expect( page.locator( '.interact-interaction-card' ).first() ).toBeVisible( { timeout: 30000 } )

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Nested wait timeouts can exceed the enclosing test timeout.

test.setTimeout(60000) caps the whole test at 60s, but the individual expect(...).toBeVisible/toBeHidden/not.toHaveCount(..., { timeout }) calls at lines 54, 63, 66, 70, and 71 alone sum to 105s of worst-case waiting. Under a slow/loaded CI runner, the test can hit the outer 60s timeout well before the later, more diagnostic assertions run, producing an opaque generic timeout instead of a specific assertion failure.

Suggested fix
-		test.setTimeout( 60000 )
+		test.setTimeout( 120000 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test.setTimeout( 60000 )
const initialBlockCount = await editor.canvas.locator( '[data-type]' ).count()
// Open the Interactions sidebar first so the app is mounted before the insert event fires.
await interactions.getSidebarToolbarButton().click()
// Open the Interaction Library from the top toolbar.
await interactions.getLibraryToolbarButton().click()
await expect( interactions.getInteractionLibraryModal() ).toBeVisible()
// Wait for presets to finish loading.
const firstFreePreset = interactions.getFirstFreePresetCard()
await expect( firstFreePreset ).toBeVisible( { timeout: 30000 } )
// Hover to reveal the Insert button, then click it.
await firstFreePreset.hover()
const insertButton = firstFreePreset.getByRole( 'button', { name: 'Insert' } )
await expect( insertButton ).toBeVisible()
await insertButton.click()
// Modal should close after inserting.
await expect( interactions.getInteractionLibraryModal() ).toBeHidden( { timeout: 15000 } )
// New blocks should be added to the editor.
await expect( editor.canvas.locator( '[data-type]' ) ).not.toHaveCount( initialBlockCount, { timeout: 15000 } )
await expect( editor.canvas.locator( '[data-type]' ).count() ).resolves.toBeGreaterThan( initialBlockCount )
// Interactions sidebar should show an interaction being edited.
await expect( page.locator( '.interface-complementary-area' ).filter( { has: interactions.getInteractionsSidebar() } ) ).toBeVisible( { timeout: 15000 } )
await expect( page.locator( '.interact-interaction-card' ).first() ).toBeVisible( { timeout: 30000 } )
test.setTimeout( 120000 )
const initialBlockCount = await editor.canvas.locator( '[data-type]' ).count()
// Open the Interactions sidebar first so the app is mounted before the insert event fires.
await interactions.getSidebarToolbarButton().click()
// Open the Interaction Library from the top toolbar.
await interactions.getLibraryToolbarButton().click()
await expect( interactions.getInteractionLibraryModal() ).toBeVisible()
// Wait for presets to finish loading.
const firstFreePreset = interactions.getFirstFreePresetCard()
await expect( firstFreePreset ).toBeVisible( { timeout: 30000 } )
// Hover to reveal the Insert button, then click it.
await firstFreePreset.hover()
const insertButton = firstFreePreset.getByRole( 'button', { name: 'Insert' } )
await expect( insertButton ).toBeVisible()
await insertButton.click()
// Modal should close after inserting.
await expect( interactions.getInteractionLibraryModal() ).toBeHidden( { timeout: 15000 } )
// New blocks should be added to the editor.
await expect( editor.canvas.locator( '[data-type]' ) ).not.toHaveCount( initialBlockCount, { timeout: 15000 } )
await expect( editor.canvas.locator( '[data-type]' ).count() ).resolves.toBeGreaterThan( initialBlockCount )
// Interactions sidebar should show an interaction being edited.
await expect( page.locator( '.interface-complementary-area' ).filter( { has: interactions.getInteractionsSidebar() } ) ).toBeVisible( { timeout: 15000 } )
await expect( page.locator( '.interact-interaction-card' ).first() ).toBeVisible( { timeout: 30000 } )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/tests/block-editor.spec.ts` around lines 41 - 71, Align the enclosing
timeout in this interaction-library test with the cumulative waits used by the
assertions, or reduce the individual assertion timeouts so their worst-case
duration fits within test.setTimeout. Update the timeout configuration around
the interaction insertion flow while preserving the existing diagnostic
assertions and their ordering.

bfintal and others added 3 commits July 15, 2026 13:06
Extend the Elementor Playwright test to click the launcher and assert the
Interactions panel opens with the sidebar visible.

Co-authored-by: Cursor <cursoragent@cursor.com>
Use domcontentloaded instead of the load event, wait for Elementor UI
markers rather than window.elementor, and use Playwright visibility checks
for the Interactions launcher.

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
e2e/tests/block-editor.spec.ts (1)

41-41: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Nested wait timeouts can exceed the enclosing test timeout.

test.setTimeout( 60000 ) caps the whole test at 60s, but the individual expect(...).toBeVisible/toBeHidden/not.toHaveCount(..., { timeout }) calls below sum to 105s of worst-case waiting. Under a slow/loaded CI runner, the test can hit the outer 60s timeout well before the later, more diagnostic assertions run, producing an opaque generic timeout instead of a specific assertion failure.

💚 Proposed fix
-		test.setTimeout( 60000 )
+		test.setTimeout( 120000 )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/tests/block-editor.spec.ts` at line 41, Update the timeout configuration
in the block editor test around test.setTimeout so the enclosing test timeout
accommodates the cumulative assertion wait time used by the toBeVisible,
toBeHidden, and not.toHaveCount checks, ensuring later diagnostic assertions can
execute before the test is terminated.
🧹 Nitpick comments (2)
e2e/tests/elementor.spec.ts (1)

33-36: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use non-null assertions to prevent strict TypeScript errors.

expect(...).not.toBeNull() is a runtime assertion and does not act as a type guard. If strictNullChecks is enabled in your tsconfig.json, accessing properties on box or viewport will trigger compiler errors. Consider using the non-null assertion operator ! to proactively satisfy the type checker.

♻️ Proposed refactor
 		expect( box ).not.toBeNull()
 		expect( viewport ).not.toBeNull()
-		expect( box.x + box.width ).toBeGreaterThan( viewport.width * 0.5 )
-		expect( box.y + box.height ).toBeGreaterThan( viewport.height * 0.5 )
+		expect( box!.x + box!.width ).toBeGreaterThan( viewport!.width * 0.5 )
+		expect( box!.y + box!.height ).toBeGreaterThan( viewport!.height * 0.5 )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/tests/elementor.spec.ts` around lines 33 - 36, Update the assertions in
the Elementor test so property access on box and viewport uses non-null
assertions after their runtime null checks. Preserve the existing geometry
expectations and reference the box and viewport variables directly.
e2e/test-utils/requestUtils.ts (1)

4-45: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

setup() largely duplicates the upstream RequestUtils.setup() implementation.

The mkdir/JSON.parse/ENOENT handling here is nearly identical to the base class's own static setup() in @wordpress/e2e-test-utils-playwright. Since ES class static methods preserve the calling class as this, simply inheriting the base setup() (without overriding it) would already return new ExtendedRequestUtils(...) correctly — no override may be needed unless the goal is specifically to pass a trimmed {cookies, origins: []} storageState into newContext (the base class's own newContext call isn't shown here, so it's unclear if it already handles this).

Worth confirming whether this override still earns its complexity, or whether it can be reduced to just relying on inherited setup() (or a thin wrapper), reducing future drift risk if upstream's setup() signature changes (there's an open upstream request to extend it with extraHTTPHeaders/httpCredentials).

Reference: upstream `RequestUtils.setup()` (gutenberg/packages/e2e-test-utils-playwright)

Confirmed via search that the base implementation performs the same fs.mkdir + JSON.parse + ENOENT-check sequence and returns new this(...).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/test-utils/requestUtils.ts` around lines 4 - 45, Remove the duplicated
static setup implementation from ExtendedRequestUtils and rely on the inherited
RequestUtils.setup() so the base class remains the single source of truth.
Preserve the ExtendedRequestUtils return type through the inherited this
binding, and only retain a wrapper if the trimmed storageState passed to
newContext is required by this subclass’s behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@e2e/test-utils/interactions.ts`:
- Around line 87-121: Remove the late waitForResponse for /dist/editor.js from
openElementorEditor, since it is registered after the editor load and can miss
the request. Keep the existing `#elementor-panel` and `#elementor-preview-iframe`
readiness waits and subsequent onboarding flow unchanged.

In `@e2e/test-utils/requestUtils.ts`:
- Around line 108-148: Update createInteraction so the generated interaction key
combines the current timestamp with an additional unique entropy source, such as
randomness or a unique identifier, ensuring parallel calls cannot produce the
same key while preserving the existing interaction_e2e_ prefix.

---

Duplicate comments:
In `@e2e/tests/block-editor.spec.ts`:
- Line 41: Update the timeout configuration in the block editor test around
test.setTimeout so the enclosing test timeout accommodates the cumulative
assertion wait time used by the toBeVisible, toBeHidden, and not.toHaveCount
checks, ensuring later diagnostic assertions can execute before the test is
terminated.

---

Nitpick comments:
In `@e2e/test-utils/requestUtils.ts`:
- Around line 4-45: Remove the duplicated static setup implementation from
ExtendedRequestUtils and rely on the inherited RequestUtils.setup() so the base
class remains the single source of truth. Preserve the ExtendedRequestUtils
return type through the inherited this binding, and only retain a wrapper if the
trimmed storageState passed to newContext is required by this subclass’s
behavior.

In `@e2e/tests/elementor.spec.ts`:
- Around line 33-36: Update the assertions in the Elementor test so property
access on box and viewport uses non-null assertions after their runtime null
checks. Preserve the existing geometry expectations and reference the box and
viewport variables directly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ff8e1a68-d200-48bd-b6a8-ab5d36414064

📥 Commits

Reviewing files that changed from the base of the PR and between 34982f4 and 7129096.

📒 Files selected for processing (9)
  • .env.example
  • .github/workflows/playwright.yml
  • .wp-env.json
  • e2e/config/global-setup.ts
  • e2e/readme.md
  • e2e/test-utils/interactions.ts
  • e2e/test-utils/requestUtils.ts
  • e2e/tests/block-editor.spec.ts
  • e2e/tests/elementor.spec.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • e2e/readme.md
  • e2e/config/global-setup.ts
  • .wp-env.json
  • .github/workflows/playwright.yml

Comment thread e2e/test-utils/interactions.ts
Comment on lines +108 to +148
createInteraction = async function( title = 'E2E Test Interaction' ) {
const key = `interaction_e2e_${ Date.now() }`
const data = JSON.stringify( {
key,
title,
active: true,
type: 'click',
target: {
type: 'block',
value: '',
},
timelines: [
{
loop: false,
onceOnly: false,
alternate: false,
reset: false,
reverse: false,
actions: [],
},
],
options: {},
locations: [
[
{
param: 'all',
operator: '==',
value: '',
},
],
],
} )

await this.rest( {
method: 'POST',
path: '/interact/v1/update_interaction',
data: { data },
} )

return key
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Timestamp-only interaction key risks collisions under parallel test workers.

key = interaction_e2e_${Date.now()} has no randomness. Playwright runs test files across multiple parallel workers by default; two createInteraction() calls in the same millisecond would produce identical keys, and if the backend upserts interactions by key, one test's fixture could silently overwrite another's, causing intermittent, hard-to-diagnose flaky failures.

💡 Suggested fix: add entropy to the key
-		const key = `interaction_e2e_${ Date.now() }`
+		const key = `interaction_e2e_${ Date.now() }_${ Math.random().toString( 36 ).slice( 2, 8 ) }`
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
createInteraction = async function( title = 'E2E Test Interaction' ) {
const key = `interaction_e2e_${ Date.now() }`
const data = JSON.stringify( {
key,
title,
active: true,
type: 'click',
target: {
type: 'block',
value: '',
},
timelines: [
{
loop: false,
onceOnly: false,
alternate: false,
reset: false,
reverse: false,
actions: [],
},
],
options: {},
locations: [
[
{
param: 'all',
operator: '==',
value: '',
},
],
],
} )
await this.rest( {
method: 'POST',
path: '/interact/v1/update_interaction',
data: { data },
} )
return key
}
createInteraction = async function( title = 'E2E Test Interaction' ) {
const key = `interaction_e2e_${ Date.now() }_${ Math.random().toString( 36 ).slice( 2, 8 ) }`
const data = JSON.stringify( {
key,
title,
active: true,
type: 'click',
target: {
type: 'block',
value: '',
},
timelines: [
{
loop: false,
onceOnly: false,
alternate: false,
reset: false,
reverse: false,
actions: [],
},
],
options: {},
locations: [
[
{
param: 'all',
operator: '==',
value: '',
},
],
],
} )
await this.rest( {
method: 'POST',
path: '/interact/v1/update_interaction',
data: { data },
} )
return key
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/test-utils/requestUtils.ts` around lines 108 - 148, Update
createInteraction so the generated interaction key combines the current
timestamp with an additional unique entropy source, such as randomness or a
unique identifier, ensuring parallel calls cannot produce the same key while
preserving the existing interaction_e2e_ prefix.

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown

🤖 Pull request artifacts

file commit
pr40-interactions-40-merge.zip dd747ab

github-actions Bot added a commit that referenced this pull request Jul 15, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/playwright.yml:
- Around line 139-142: Update the workflow step that normalizes WP_VERSION and
VERSION_SUFFIX to also emit both values via GITHUB_OUTPUT, then change
REPORT_NAME in the artifact upload and summary-related expressions to read those
values through the step’s outputs instead of env.WP_VERSION and
env.VERSION_SUFFIX. Preserve the existing normalized values and PHP matrix
naming.
- Around line 7-9: Remove the workflow-wide actions: write entry from the
permissions block in the Playwright workflow, retaining contents: read. Do not
add replacement Actions permissions unless an existing step explicitly requires
the Actions API.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2b859826-d880-4a68-9eac-c879bc3bce74

📥 Commits

Reviewing files that changed from the base of the PR and between 7129096 and dab1697.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (6)
  • .eslintignore
  • .eslintrc.js
  • .github/workflows/playwright.yml
  • .github/workflows/plugin-build.yml
  • e2e/test-utils/interactions.ts
  • e2e/tests/block-editor.spec.ts
💤 Files with no reviewable changes (1)
  • .eslintrc.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • e2e/tests/block-editor.spec.ts
  • e2e/test-utils/interactions.ts

Comment on lines +7 to +9
permissions:
contents: read
actions: write

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the workflow file around the relevant lines
cat -n .github/workflows/playwright.yml | sed -n '1,220p'

# Find artifact-related usage and permissions in the repository
rg -n "upload-artifact|download-artifact|artifact-metadata|permissions:" .github/workflows . -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**'

Repository: gambitph/Interactions

Length of output: 6443


🏁 Script executed:

#!/bin/bash
set -euo pipefail

cat -n .github/workflows/playwright.yml | sed -n '1,220p'

Repository: gambitph/Interactions

Length of output: 6167


🌐 Web query:

GitHub Actions upload-artifact permissions actions: write artifact-metadata write workflow permissions docs

💡 Result:

As of July 15, 2026, the artifact-metadata: write permission is a fine-grained GitHub Actions permission specifically required for interacting with artifact metadata APIs, such as creating artifact metadata storage records [1]. Key points regarding this permission: 1. Function: The artifact-metadata: write permission allows a workflow to create and manage artifact-related metadata [1]. It replaced the broader contents: write permission for these specific API operations as of early 2026 [1]. 2. Usage: It is primarily used by actions that generate or persist metadata for build artifacts, such as the actions/attest-build-provenance action when creating storage records [2][3][4]. 3. Requirements: If you are using an action that requires this capability (e.g., to create storage records for linked artifacts), you must explicitly define the permission in your workflow job configuration [2][3]: permissions: artifact-metadata: write # Other necessary permissions (e.g., id-token: write, attestations: write) 4. Context: Note that the standard actions/upload-artifact action does not require the artifact-metadata: write permission to upload artifacts themselves [5][6][7]. The artifact-metadata permission is specifically for advanced metadata operations accessed via the REST API [1][8]. If you encounter errors like Failed to create storage record, ensure the permission is correctly scoped within your workflow job permissions block [9].

Citations:


Remove the workflow-wide actions: write permission .github/workflows/playwright.yml:7-9
actions/upload-artifact and actions/download-artifact don’t need it, so this can stay at contents: read unless another step actually needs the Actions API.

🧰 Tools
🪛 zizmor (1.26.1)

[error] 9-9: overly broad permissions (excessive-permissions): actions: write is overly broad at the workflow level

(excessive-permissions)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/playwright.yml around lines 7 - 9, Remove the
workflow-wide actions: write entry from the permissions block in the Playwright
workflow, retaining contents: read. Do not add replacement Actions permissions
unless an existing step explicitly requires the Actions API.

Source: Linters/SAST tools

Comment on lines +139 to +142
env:
SUMMARY_BODY: ${{ steps.mto.outputs.body }}
REPORT_NAME: playwright-report-php_${{ matrix.php_version }}-wp_${{ env.WP_VERSION }}-${{ env.VERSION_SUFFIX }}
ARTIFACT_ID: ${{ steps.artifact-upload-step.outputs.artifact-id }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".github/workflows/playwright.yml"
rg -n 'GITHUB_ENV|env\.(WP_VERSION|VERSION_SUFFIX)|name: playwright-report' "$file"

# Confirm artifact names include both matrix dimensions.
rg -n 'matrix\.(php_version|wp_version)' "$file"

Repository: gambitph/Interactions

Length of output: 1030


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".github/workflows/playwright.yml"
nl -ba "$file" | sed -n '60,150p'

Repository: gambitph/Interactions

Length of output: 199


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".github/workflows/playwright.yml"
awk 'NR>=60 && NR<=150 { printf "%4d  %s\n", NR, $0 }' "$file"

Repository: gambitph/Interactions

Length of output: 4055


🌐 Web query:

GitHub Actions env context GITHUB_ENV values available in expressions upload-artifact with name step outputs

💡 Result:

In GitHub Actions, there are distinct mechanisms for managing data within a workflow, depending on whether you are using the env context, the GITHUB_ENV environment file, or step outputs [1][2][3]. The env context is used for variables defined in the workflow file (at the workflow, job, or step level) [4][5]. It is processed before the job is sent to the runner, meaning it is suitable for expressions in conditional statements [2][6]. GITHUB_ENV is a file on the runner used to persist environment variables for subsequent steps within the same job [5][3]. When you append a variable to this file (e.g., echo "VAR_NAME=value" >> $GITHUB_ENV), that variable becomes available to all following steps in that job as an environment variable [5][3]. Crucially, the step that writes to GITHUB_ENV does not have access to that new variable; only subsequent steps do [5][3]. Regarding the upload-artifact action (commonly used as actions/upload-artifact), it does not use environment variables to communicate its results back to the workflow [7][8]. Instead, it provides step outputs [7][8]. You can access these outputs by assigning an id to the step and referencing it via the steps context [7][9]. For example, if you want to use the artifact-id produced by an upload-artifact step: 1. Assign an id to the upload step: - uses: actions/upload-artifact@v7 id: my-artifact-upload with: name: my-artifact path: path/to/files 2. Reference the output in a subsequent step: - name: Use artifact ID run: echo "The artifact ID is ${{ steps.my-artifact-upload.outputs.artifact-id }}" The outputs provided by actions/upload-artifact include artifact-id, artifact-url, and artifact-digest [7][9][8]. These outputs are available to all subsequent steps in the same job [7][9]. To use them in a different job, you must explicitly define them in the job outputs section and use the needs context [7][9].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file=".github/workflows/playwright.yml"
awk 'NR>=1 && NR<=70 { printf "%4d  %s\n", NR, $0 }' "$file"

Repository: gambitph/Interactions

Length of output: 2600


Use step outputs for the artifact name. WP_VERSION and VERSION_SUFFIX are only written via $GITHUB_ENV, so ${{ env.WP_VERSION }} / ${{ env.VERSION_SUFFIX }} stay empty in the later actions/upload-artifact@v4 and summary expressions. That makes the matrix artifact names collide; emit the normalized values via $GITHUB_OUTPUT and read them as steps.<id>.outputs.* instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/playwright.yml around lines 139 - 142, Update the workflow
step that normalizes WP_VERSION and VERSION_SUFFIX to also emit both values via
GITHUB_OUTPUT, then change REPORT_NAME in the artifact upload and
summary-related expressions to read those values through the step’s outputs
instead of env.WP_VERSION and env.VERSION_SUFFIX. Preserve the existing
normalized values and PHP matrix naming.

Use .nvmrc as the single Node version source across CI workflows, reorder
the Playwright test job to extract the build artifact before npm cache setup,
and document the Node 22 requirement for e2e runs.

Co-authored-by: Cursor <cursoragent@cursor.com>
github-actions Bot added a commit that referenced this pull request Jul 15, 2026
@bfintal
bfintal merged commit 3e07105 into develop Jul 15, 2026
7 checks passed
@bfintal
bfintal deleted the feat/playwright-e2e-tests branch July 15, 2026 08:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant