Skip to content
Merged
56 changes: 56 additions & 0 deletions bin/get-playwright-version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use strict';

/**
* Resolves the installed @playwright/test version for CI caching.
*
* Used by polylang/actions/e2e to build the Playwright browser cache key. Must be
* run from the consumer repository root (where npm ci has installed dependencies).
*
* Resolution order:
* 1. node_modules/@playwright/test/package.json (preferred — matches installed binaries)
* 2. package-lock.json (npm lockfile v2/v3 packages path, or legacy dependencies)
*
* @example
* PLAYWRIGHT_VERSION=$(node "${{ github.action_path }}/../bin/get-playwright-version.js")
*
* Output:
* - Prints the semver string to stdout on success.
* - Exits with code 1 and writes an error message to stderr when the version cannot be determined.
*/

const fs = require( 'fs' );
const path = require( 'path' );

/**
* @param {string} filePath
* @return {Object|null}
*/
const tryReadJson = ( filePath ) => {
try {
return JSON.parse( fs.readFileSync( filePath, 'utf8' ) );
} catch {
return null;
}
};

const root = process.cwd();
const fromPackage = tryReadJson(
path.join( root, 'node_modules', '@playwright', 'test', 'package.json' )
);

if ( fromPackage?.version ) {
process.stdout.write( fromPackage.version );
process.exit( 0 );
}

const lock = tryReadJson( path.join( root, 'package-lock.json' ) );
const fromLock = lock?.packages?.['node_modules/@playwright/test']?.version
|| lock?.dependencies?.['@playwright/test']?.version
|| '';

if ( ! fromLock ) {
process.stderr.write( 'Could not determine @playwright/test version.' );
process.exit( 1 );
}

process.stdout.write( fromLock );
73 changes: 49 additions & 24 deletions e2e/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ name: Run Playwright e2e Tests
description: Installs WordPress, starts the server, builds dependencies and run Playwright tests. Currently runs on PHP 8.0 with WordPress latest release.

inputs:
container-cache-key:
description: Container cache key. Used to cache wp-env container.
wp-env-cache-key:
description: Extra cache key fragment for the wp-env home directory. Use for inputs not reflected in .wp-env.json (e.g. WooCommerce version, Polylang Pro commit).
required: false
type: string
default: 'no-external-cache-key'
default: 'default'
wp-env-config-path:
description: Path to a custom wp-env config file passed to wp-env --config. Default is '' (wp-env default discovery).
required: false
Expand All @@ -28,22 +28,34 @@ runs:
with:
node-version: 22

- name: Get Node.js version
id: node-version
run: echo "NODE_VERSION=$(node -v)" >> "$GITHUB_OUTPUT"
shell: bash

# Repos without a committed lockfile (e.g. Polylang) need one for npm ci / cache key.
- name: Set up package-lock.json
run: |
npm install --package-lock-only --no-audit
if [ ! -f package-lock.json ]; then
npm install --package-lock-only --no-audit
fi
shell: bash

- name: Cache node_modules
id: cache-node_modules
uses: actions/cache@v4
with:
path: '**/node_modules'
key: node_modules-${{ runner.os }}-${{ runner.arch }}-${{ steps.node-version.outputs.NODE_VERSION }}-${{ hashFiles('package-lock.json') }}
key: node_modules-${{ runner.os }}-${{ runner.arch }}-${{ steps.node-version.outputs.NODE_VERSION }}-${{ hashFiles('package.json', 'package-lock.json') }}

- name: Install dependencies
if: ${{ steps.cache-node_modules.outputs.cache-hit != 'true' }}
run: |
if [ -f package-lock.json ]; then
npm ci
else
npm install --no-audit
fi
shell: bash

# Must install PHP deps before wp-env is running to prevent errors.
Expand All @@ -66,7 +78,8 @@ runs:
- name: Get installed Playwright version
id: playwright-version
run: |
echo "PLAYWRIGHT_VERSION=$(node -e "console.log(require('./package-lock.json').packages['node_modules/@playwright/test'].version)")" >> $GITHUB_ENV
PLAYWRIGHT_VERSION=$(node "${{ github.action_path }}/../bin/get-playwright-version.js")
echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> "$GITHUB_ENV"
shell: bash

- name: Cache Playwright binaries
Expand All @@ -76,6 +89,8 @@ runs:
path: |
~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}
restore-keys: |
${{ runner.os }}-playwright-

- name: Install Playwright binaries
# Only Chromium for the moment.
Expand All @@ -84,23 +99,40 @@ runs:
shell: bash
if: steps.playwright-cache.outputs.cache-hit != 'true'

# Pin home so CI does not depend on /snap detection (~/wp-env vs ~/.wp-env).
# Same WP_ENV_HOME behavior in @wordpress/env 10 and 11.
- name: Set wp-env home directory
run: echo "WP_ENV_HOME=${HOME}/.wp-env" >> "$GITHUB_ENV"
shell: bash

- name: Get @wordpress/env version
id: wp-env-version
run: |
echo "WP_ENV_VERSION=$(node -e "console.log(require('@wordpress/env/package.json').version)")" >> "$GITHUB_OUTPUT"
shell: bash

# When core is "latest" (null), bust the workdir cache when WordPress.org ships a new release.
- name: Get WordPress latest version
id: wp-version
run: |
echo "WP_VERSION=$(node -e "fetch('https://api.wordpress.org/core/version-check/1.7/').then(r => r.json()).then(data => console.log(data.offers[0].current))")" >> $GITHUB_ENV
echo "WP_VERSION=$(node -e "fetch('https://api.wordpress.org/core/version-check/1.7/').then(r => r.json()).then(data => console.log(data.offers[0].current))")" >> "$GITHUB_ENV"
shell: bash

- name: Cache wp-env Docker image
id: docker-cache
# Key: {os}-wp-env-home-{@wordpress/env}-{WP latest}-{hash(.wp-env.json)}-{hash(custom config)|no-custom-config}-{wp-env-cache-key}
- name: Cache wp-env home
id: wp-env-home-cache
uses: actions/cache@v4
with:
path: wp-env-image.tar
key: ${{ runner.os }}-wp-env-${{ env.WP_VERSION }}-${{ inputs.container-cache-key }}

- name: Load cached Docker image (if any)
if: steps.docker-cache.outputs.cache-hit == 'true'
run: |
docker load -i wp-env-image.tar
path: ${{ env.WP_ENV_HOME }}
key: ${{ runner.os }}-wp-env-home-${{ steps.wp-env-version.outputs.WP_ENV_VERSION }}-${{ env.WP_VERSION }}-${{ hashFiles('.wp-env.json') }}-${{ inputs.wp-env-config-path != '' && hashFiles(inputs.wp-env-config-path) || 'no-custom-config' }}-${{ inputs.wp-env-cache-key }}

# WP_ENV_HOME holds WordPress sources + wp-env-cache.json, not MySQL volumes.
# On a cache hit the config checksum makes wp-env skip `wp core install` against
# empty DB volumes → REST API / rewrite failures. Drop the checksum so start
# reconfigures while still reusing downloaded sources (wp-env 10 and 11).
- name: Force wp-env reconfigure after home cache restore
if: steps.wp-env-home-cache.outputs.cache-hit == 'true'
run: find "${WP_ENV_HOME}" -name 'wp-env-cache.json' -delete
shell: bash

- name: Install WordPress and start the server
Expand All @@ -112,14 +144,7 @@ runs:
fi
shell: bash

- name: Save Docker image to cache
if: steps.docker-cache.outputs.cache-hit != 'true'
run: |
docker image ls
docker save $(docker images --format '{{.Repository}}:{{.Tag}}') -o wp-env-image.tar
shell: bash

- name: Run Playwright tests with WordPress ${{ env.WP_VERSION }}
- name: Run Playwright tests
run: ${{ inputs.playwright-cmd }}
shell: bash

Expand Down