Skip to content

More Director 8.5 fixes on top of #245: score, sound, inks, text, input, shaders #465

More Director 8.5 fixes on top of #245: score, sound, inks, text, input, shaders

More Director 8.5 fixes on top of #245: score, sound, inks, text, input, shaders #465

Workflow file for this run

name: E2E Tests

Check warning on line 1 in .github/workflows/e2e.yml

View workflow run for this annotation

GitHub Actions / E2E Tests

Workflow execution policy warning (evaluate mode)

On November 2, 2026, GitHub will restrict `pull_request_target` on public repositories by default. To continue allowing the event trigger, configure an Actions policy. Learn more: https://gh.io/securely-using-pull_request_target#default-policy-for-pull_request_target
on:
push:
branches: [main]
pull_request:
branches: [main]
pull_request_target:
branches: [main]
workflow_dispatch:
inputs:
ref:
description: 'Branch, tag, or commit SHA to test'
required: false
type: string
default: ''
pr_number:
description: 'PR number to test (for fork PRs — posts statuses on the PR head SHA)'
required: false
type: string
default: ''
rebase_on_main:
description: 'Rebase on main before running tests'
required: false
type: boolean
default: false
permissions:
contents: read
pull-requests: write
statuses: write
env:
CARGO_TERM_COLOR: always
TEST_ASSETS_URL: ${{ secrets.TEST_ASSETS_URL }}
HABBO_V7_USERNAME: ${{ secrets.HABBO_V7_USERNAME }}
HABBO_V7_PASSWORD: ${{ secrets.HABBO_V7_PASSWORD }}
HABBO_V7_INFO_HOST: ${{ secrets.HABBO_V7_INFO_HOST }}
HABBO_V7_INFO_PORT: ${{ secrets.HABBO_V7_INFO_PORT }}
HABBO_V7_WS_SSL: ${{ vars.HABBO_V7_WS_SSL }}
HABBO_V7_WS_PATH: ${{ vars.HABBO_V7_WS_PATH }}
HABBO_V26_SSO_TICKET: ${{ secrets.HABBO_V26_SSO_TICKET }}
HABBO_V26_INFO_HOST: ${{ secrets.HABBO_V26_INFO_HOST }}
HABBO_V26_INFO_PORT: ${{ secrets.HABBO_V26_INFO_PORT }}
HABBO_V26_WS_SSL: ${{ vars.HABBO_V26_WS_SSL }}
HABBO_V26_WS_PATH: ${{ vars.HABBO_V26_WS_PATH }}
HABBO_V31_SSO_TICKET: ${{ secrets.HABBO_V31_SSO_TICKET }}
HABBO_V31_INFO_HOST: ${{ secrets.HABBO_V31_INFO_HOST }}
HABBO_V31_INFO_PORT: ${{ secrets.HABBO_V31_INFO_PORT }}
HABBO_V31_WS_SSL: ${{ vars.HABBO_V31_WS_SSL }}
HABBO_V31_WS_PATH: ${{ vars.HABBO_V31_WS_PATH }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
S3_BUCKET: ${{ secrets.S3_BUCKET }}
S3_REGION: ${{ secrets.S3_REGION }}
jobs:
native:
name: Native tests
runs-on: ubuntu-latest
# Skip pull_request runs from forks (they have no secrets and would fail);
# those go through pull_request_target instead.
if: |
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository)
# pull_request_target from a fork pauses on this environment until a
environment: ${{ github.event_name == 'pull_request_target' && 'fork-pr-tests' || '' }}
steps:
- name: Resolve PR head
id: resolve_pr
if: inputs.pr_number != ''
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ inputs.pr_number }}
with:
script: |
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: parseInt(process.env.PR_NUMBER, 10),
});
core.setOutput('sha', pr.data.head.sha);
core.setOutput('ref', `refs/pull/${pr.data.number}/head`);
- uses: actions/checkout@v4
with:
# For pull_request_target, github.ref points at the BASE branch — we
# need to explicitly check out the PR's merge ref to test the PR code.
ref: ${{ github.event_name == 'pull_request_target' && format('refs/pull/{0}/merge', github.event.pull_request.number) || steps.resolve_pr.outputs.ref || inputs.ref || github.ref }}
submodules: true
fetch-depth: 0
allow-unsafe-pr-checkout: true
- name: Post pending status
if: inputs.pr_number != ''
uses: actions/github-script@v7
env:
PR_SHA: ${{ steps.resolve_pr.outputs.sha }}
with:
script: |
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: process.env.PR_SHA,
state: 'pending',
context: 'E2E / native',
target_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
});
- name: Rebase on main
if: inputs.rebase_on_main
run: |
git config user.name "github-actions"
git config user.email "github-actions@github.com"
git fetch origin main
git rebase origin/main
- name: Download test assets
run: |
if [ -z "$TEST_ASSETS_URL" ]; then
echo "::error::TEST_ASSETS_URL secret is not set"
exit 1
fi
curl -fSL "$TEST_ASSETS_URL" -o assets.zip
unzip -qo assets.zip -d public/
- name: Cargo fetch
working-directory: ./vm-rust
run: cargo fetch
# `run-native-tests.mjs` always runs `generate-snapshot-report.mjs`
# afterwards, and that imports `pngjs`. Without an install step this job
# ended every run on ERR_MODULE_NOT_FOUND, whatever the tests did.
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Install npm dependencies
run: npm ci
- name: Run native e2e tests
id: native_tests
continue-on-error: true
run: node scripts/run-native-tests.mjs
- name: Generate snapshot diff grid
if: always()
run: |
sudo apt-get install -yq imagemagick >/dev/null 2>&1 || true
bash scripts/build-diff-grid.sh vm-rust/tests/snapshots /tmp/diff-report
- name: Upload diff grid to S3
if: always()
run: |
if [ -z "$S3_BUCKET" ] || [ ! -f /tmp/diff-report/diff-grid.png ]; then exit 0; fi
KEY="dirplayer/snapshots/${GITHUB_RUN_ID}/native-$(sha256sum /tmp/diff-report/diff-grid.png | cut -c1-12).png"
aws s3 cp /tmp/diff-report/diff-grid.png "s3://${S3_BUCKET}/${KEY}" --content-type image/png
echo "https://${S3_BUCKET}.s3.${S3_REGION:-us-east-1}.amazonaws.com/${KEY}" > /tmp/diff-report/image-url.txt
- name: Upload snapshot report to S3
id: upload_report
if: always()
run: |
if [ -z "$S3_BUCKET" ] || [ ! -f test-results/snapshot-report/index.html ]; then exit 0; fi
PREFIX="dirplayer/snapshots/${GITHUB_RUN_ID}/native-report"
[ -d test-results/snapshot-report/images ] && \
aws s3 sync test-results/snapshot-report/images/ "s3://${S3_BUCKET}/${PREFIX}/images/"
[ -d test-results/snapshot-report/suites ] && \
aws s3 sync test-results/snapshot-report/suites/ "s3://${S3_BUCKET}/${PREFIX}/suites/" \
--content-type "text/html; charset=utf-8"
aws s3 cp test-results/snapshot-report/index.html \
"s3://${S3_BUCKET}/${PREFIX}/index.html" --content-type "text/html; charset=utf-8"
echo "url=https://${S3_BUCKET}.s3.${S3_REGION:-us-east-1}.amazonaws.com/${PREFIX}/index.html" >> "$GITHUB_OUTPUT"
- name: Upload test artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: native-test-results
path: |
vm-rust/tests/snapshots/output/
vm-rust/tests/snapshots/diff/
/tmp/diff-report/
test-results/
retention-days: 7
- name: Post snapshot test report on PR
if: always() && (github.event_name == 'pull_request' || github.event_name == 'pull_request_target' || inputs.pr_number != '')
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ inputs.pr_number }}
REPORT_URL: ${{ steps.upload_report.outputs.url }}
with:
script: |
const script = require('./scripts/post-snapshot-comment.cjs');
const prNumber = process.env.PR_NUMBER ? parseInt(process.env.PR_NUMBER, 10) : context.issue.number;
await script({ github, context, label: 'native', artifactName: 'native-test-results', prNumber, reportUrl: process.env.REPORT_URL || null });
- name: Post final status
if: always() && inputs.pr_number != ''
uses: actions/github-script@v7
env:
PR_SHA: ${{ steps.resolve_pr.outputs.sha }}
OUTCOME: ${{ steps.native_tests.outcome }}
REPORT_URL: ${{ steps.upload_report.outputs.url }}
with:
script: |
const state = process.env.OUTCOME === 'success' ? 'success' : 'failure';
const targetUrl = process.env.REPORT_URL ||
`${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: process.env.PR_SHA,
state,
context: 'E2E / native',
target_url: targetUrl,
});
- name: Fail if native tests failed
if: steps.native_tests.outcome == 'failure'
run: exit 1
browser:
name: Browser tests
runs-on: ubuntu-latest
if: |
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository)
environment: ${{ github.event_name == 'pull_request_target' && 'fork-pr-tests' || '' }}
steps:
- name: Resolve PR head
id: resolve_pr
if: inputs.pr_number != ''
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ inputs.pr_number }}
with:
script: |
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: parseInt(process.env.PR_NUMBER, 10),
});
core.setOutput('sha', pr.data.head.sha);
core.setOutput('ref', `refs/pull/${pr.data.number}/head`);
- uses: actions/checkout@v4
with:
ref: ${{ github.event_name == 'pull_request_target' && format('refs/pull/{0}/merge', github.event.pull_request.number) || steps.resolve_pr.outputs.ref || inputs.ref || github.ref }}
submodules: true
fetch-depth: 0
allow-unsafe-pr-checkout: true
- name: Post pending status
if: inputs.pr_number != ''
uses: actions/github-script@v7
env:
PR_SHA: ${{ steps.resolve_pr.outputs.sha }}
with:
script: |
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: process.env.PR_SHA,
state: 'pending',
context: 'E2E / browser',
target_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
});
- name: Rebase on main
if: inputs.rebase_on_main
run: |
git config user.name "github-actions"
git config user.email "github-actions@github.com"
git fetch origin main
git rebase origin/main
- name: Install wasm target
run: rustup target add wasm32-unknown-unknown
- name: Install wasm-bindgen-cli
run: |
WASM_BINDGEN_VERSION=$(cargo metadata --manifest-path vm-rust/Cargo.toml --format-version 1 | \
python3 -c "import sys,json; pkgs=json.load(sys.stdin)['packages']; print(next(p['version'] for p in pkgs if p['name']=='wasm-bindgen'))")
cargo install wasm-bindgen-cli --version "$WASM_BINDGEN_VERSION"
- name: Download test assets
run: |
if [ -z "$TEST_ASSETS_URL" ]; then
echo "::error::TEST_ASSETS_URL secret is not set"
exit 1
fi
curl -fSL "$TEST_ASSETS_URL" -o assets.zip
unzip -qo assets.zip -d public/
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Install npm dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps chromium
- name: Install Microsoft Core Fonts
run: |
echo "ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true" | sudo debconf-set-selections
sudo apt-get install -y ttf-mscorefonts-installer
sudo fc-cache -fv
- name: Cargo fetch
working-directory: ./vm-rust
run: cargo fetch
- name: Build and copy Ruffle
working-directory: ./vm-rust
run: npm run build-ruffle && npm run copy-ruffle
- name: Build xtras (bobba-xtra, example-xtra, …)
# The xtra registry resolves to wasms under public/ — built fresh
# here from xtras/<name>/ so browser-side `new(xtra "…")` calls
# have something to load.
run: npm run build-xtras
- name: Build and run browser e2e tests
id: browser_tests
continue-on-error: true
run: node scripts/run-browser-tests.mjs
- name: Generate snapshot diff grid
if: always()
run: |
sudo apt-get install -yq imagemagick >/dev/null 2>&1 || true
bash scripts/build-diff-grid.sh vm-rust/tests/snapshots /tmp/diff-report
- name: Upload diff grid to S3
if: always()
run: |
if [ -z "$S3_BUCKET" ] || [ ! -f /tmp/diff-report/diff-grid.png ]; then exit 0; fi
KEY="dirplayer/snapshots/${GITHUB_RUN_ID}/browser-$(sha256sum /tmp/diff-report/diff-grid.png | cut -c1-12).png"
aws s3 cp /tmp/diff-report/diff-grid.png "s3://${S3_BUCKET}/${KEY}" --content-type image/png
echo "https://${S3_BUCKET}.s3.${S3_REGION:-us-east-1}.amazonaws.com/${KEY}" > /tmp/diff-report/image-url.txt
- name: Upload snapshot report to S3
id: upload_report
if: always()
run: |
if [ -z "$S3_BUCKET" ] || [ ! -f test-results/snapshot-report/index.html ]; then exit 0; fi
PREFIX="dirplayer/snapshots/${GITHUB_RUN_ID}/browser-report"
[ -d test-results/snapshot-report/images ] && \
aws s3 sync test-results/snapshot-report/images/ "s3://${S3_BUCKET}/${PREFIX}/images/"
[ -d test-results/snapshot-report/suites ] && \
aws s3 sync test-results/snapshot-report/suites/ "s3://${S3_BUCKET}/${PREFIX}/suites/" \
--content-type "text/html; charset=utf-8"
aws s3 cp test-results/snapshot-report/index.html \
"s3://${S3_BUCKET}/${PREFIX}/index.html" --content-type "text/html; charset=utf-8"
echo "url=https://${S3_BUCKET}.s3.${S3_REGION:-us-east-1}.amazonaws.com/${PREFIX}/index.html" >> "$GITHUB_OUTPUT"
- name: Upload test artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: browser-test-results
path: |
vm-rust/tests/snapshots/output/
vm-rust/tests/snapshots/diff/
/tmp/diff-report/
test-results/
retention-days: 7
- name: Post snapshot test report on PR
if: always() && (github.event_name == 'pull_request' || github.event_name == 'pull_request_target' || inputs.pr_number != '')
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ inputs.pr_number }}
REPORT_URL: ${{ steps.upload_report.outputs.url }}
with:
script: |
const script = require('./scripts/post-snapshot-comment.cjs');
const prNumber = process.env.PR_NUMBER ? parseInt(process.env.PR_NUMBER, 10) : context.issue.number;
await script({ github, context, label: 'browser', artifactName: 'browser-test-results', prNumber, reportUrl: process.env.REPORT_URL || null });
- name: Post final status
if: always() && inputs.pr_number != ''
uses: actions/github-script@v7
env:
PR_SHA: ${{ steps.resolve_pr.outputs.sha }}
OUTCOME: ${{ steps.browser_tests.outcome }}
REPORT_URL: ${{ steps.upload_report.outputs.url }}
with:
script: |
const state = process.env.OUTCOME === 'success' ? 'success' : 'failure';
const targetUrl = process.env.REPORT_URL ||
`${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: process.env.PR_SHA,
state,
context: 'E2E / browser',
target_url: targetUrl,
});
- name: Fail if browser tests failed
if: steps.browser_tests.outcome == 'failure'
run: exit 1