feat: Migrate testcafe tests to playwright [WIP]#5463
feat: Migrate testcafe tests to playwright [WIP]#5463tiagoapolo wants to merge 13 commits intomainfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Docker builds report
|
Uffizzi Ephemeral Environment Deploying☁️ https://app.uffizzi.com/github.com/Flagsmith/flagsmith/pull/5463 ⚙️ Updating now by workflow run 15077923522. What is Uffizzi? Learn more! |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5463 +/- ##
=======================================
Coverage 98.07% 98.07%
=======================================
Files 1294 1294
Lines 46535 46537 +2
=======================================
+ Hits 45637 45639 +2
Misses 898 898 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| }; | ||
|
|
||
| export const toggleFeature = async (page: Page, index: number, toValue: boolean) => { | ||
| await click(page, byId(`feature-switch-${index}${toValue ? '-off' : 'on'}`)); |
There was a problem hiding this comment.
Missing hyphen in toggleFeature selector breaks false case
The toggleFeature and setUserPermissions functions have a missing hyphen in the selector construction. When toValue is false, the selector becomes feature-switch-${index}on instead of feature-switch-${index}-on. This will cause tests that attempt to toggle a feature off to fail because the selector won't match any element.
Additional Locations (1)
| await setText(page, byId(`featureVariationValue${i}`), v.value); | ||
| await setText(page, byId(`featureVariationWeight${v.value}`), `${v.weight}`); | ||
| }), | ||
| ); |
There was a problem hiding this comment.
Race condition with Promise.all for sequential UI clicks
The createRemoteConfig function uses Promise.all with map to add multiple variations, but each iteration clicks add-variation which creates new UI elements. Running these in parallel causes a race condition where multiple clicks can occur before form fields are populated, leading to incorrect indices or values being set in wrong fields. The createSegment function correctly uses a sequential for loop pattern for similar operations. This will cause test flakiness.
| log('User with CREATE_ENVIRONMENT can create an environment'); | ||
| await login(page, E2E_NON_ADMIN_USER_WITH_PROJECT_PERMISSIONS, PASSWORD); | ||
| await click(page, '#project-select-0'); | ||
| await createEnvironment(page, 'Staging'); |
There was a problem hiding this comment.
Missing navigation step before createEnvironment call
The test calls createEnvironment directly after selecting a project, but the createEnvironment helper function expects to already be on the create environment page. Other tests like environment-test.spec.ts correctly navigate by clicking #create-env-link before calling createEnvironment. Without this navigation step, the test will fail because it tries to fill a form that doesn't exist on the current page.
| log('User without VIEW_AUDIT_LOG cannot view the audit log'); | ||
| await login(page, E2E_NON_ADMIN_USER_WITH_PROJECT_PERMISSIONS, PASSWORD); | ||
| await click(page, '#project-select-0'); | ||
| await waitForElementNotExist(page, 'audit-log-link'); |
There was a problem hiding this comment.
Missing byId wrapper causes incorrect element selector
The selector 'audit-log-link' is passed as a raw string to waitForElementNotExist, but it needs to be wrapped with byId() to generate the correct [data-test="audit-log-link"] selector. The correct pattern is shown in environment-permission-test.spec.ts which uses byId('audit-log-link'). Without the wrapper, this will look for an HTML element with tag name audit-log-link, which doesn't exist.
- Update Node.js version from 16.20.1 to 22.12.0 in Dockerfile-base.e2e (Playwright requires Node.js 18+) - Remove orphaned steps block from workflow YAML - Fix byId() wrapper in deleteSegment and deleteFeature functions - Fix compareVersion copy-paste bug (was checking old-value instead of new-value) - Add wait for page load in gotoSegments function - Add overflow menu handling in goToFeatureVersions function 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The pre-built base image (ghcr.io/flagsmith/e2e-frontend-base:latest) still has Node.js 16, which doesn't support Playwright. This change inlines the base image build steps with Node.js 22.12.0 directly in Dockerfile.e2e to unblock CI until the base image is rebuilt. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add test-framework parameter to reusable E2E workflow - Split run-e2e-tests into run-e2e-tests-playwright and run-e2e-tests-testcafe jobs - Add npm scripts test:playwright and test:testcafe - Add Makefile targets test-playwright and test-testcafe - Fix Playwright strict mode violations by using .first() on locators 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| - api-image: ${{ needs.docker-build-api.outputs.image }} | ||
| args: --meta-filter category=smoke | ||
| - api-image: ${{ needs.docker-build-private-cloud-api.outputs.image }} | ||
| args: --meta-filter category=smoke --meta-filter category=enterprise |
There was a problem hiding this comment.
Testcafe workflow uses nonexistent test category and wrong format
The new run-e2e-tests-testcafe job passes --meta-filter category=smoke but no testcafe tests define category: 'smoke' - all tests use category: 'oss' or category: 'enterprise'. This causes zero tests to run for the OSS API. Additionally, the second job uses multiple --meta-filter flags (space-separated) instead of the expected comma-separated format (--meta-filter category=oss,category=enterprise). The testcafe code calls .split(',') on args['meta-filter'], which would throw a TypeError when minimist returns an array from multiple flags. The testcafe CI job will either crash or silently pass with no tests running.
Split the PR workflow E2E jobs to run both test frameworks in parallel: - run-e2e-tests-playwright (OSS) - run-e2e-tests-testcafe (OSS) - run-e2e-tests-private-cloud-playwright (Enterprise) - run-e2e-tests-private-cloud-testcafe (Enterprise) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| if: github.event.pull_request.draft == false && !cancelled() | ||
| needs: [permissions-check, docker-build-api, docker-build-e2e] | ||
| uses: ./.github/workflows/.reusable-docker-e2e-tests.yml | ||
| with: | ||
| runs-on: ${{ matrix.runs-on }} | ||
| e2e-image: ${{ needs.docker-build-e2e.outputs.image }} | ||
| api-image: ${{ needs.docker-build-api.outputs.image }} | ||
| test-framework: testcafe | ||
| args: --meta-filter category=smoke | ||
| secrets: | ||
| GCR_TOKEN: ${{ needs.permissions-check.outputs.can-write == 'true' && secrets.GITHUB_TOKEN || '' }} | ||
| SLACK_TOKEN: ${{ needs.permissions-check.outputs.can-write == 'true' && secrets.SLACK_TOKEN || '' }} | ||
|
|
||
| strategy: | ||
| matrix: | ||
| runs-on: [depot-ubuntu-latest-16, depot-ubuntu-latest-arm-16] | ||
|
|
||
| run-e2e-tests-private-cloud-playwright: |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 1 month ago
In general: add an explicit permissions: block to the workflow (preferably at the root so it applies to all jobs) that restricts GITHUB_TOKEN to the minimal scopes required, typically contents: read for CI that just needs to fetch code and read workflow files.
Best minimal fix here: add a top‑level permissions: section after the on: block, setting contents: read. This will apply to all jobs in platform-pull-request.yml, including run-e2e-tests-testcafe, and will satisfy CodeQL’s requirement that the workflow explicitly limit GITHUB_TOKEN permissions, without changing any job logic. If other jobs in this workflow need elevated permissions, they can override permissions: individually, but we are not asked to change that, and there is no evidence in the provided snippet of a need for broader scopes.
Concretely:
-
Edit
.github/workflows/platform-pull-request.yml. -
Insert:
permissions: contents: read
between the
on:block (lines 3–8) andjobs:(line 10). -
No imports or additional methods are needed, since this is a YAML configuration change only.
| @@ -7,6 +7,9 @@ | ||
| - docs/** | ||
| - infrastructure/** | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| permissions-check: | ||
| name: Check actor permissions |
| if: github.event.pull_request.draft == false && !cancelled() && needs.permissions-check.outputs.can-write == 'true' | ||
| needs: [permissions-check, docker-build-private-cloud, docker-build-e2e] | ||
| uses: ./.github/workflows/.reusable-docker-e2e-tests.yml | ||
| with: | ||
| runs-on: ${{ matrix.runs-on }} | ||
| e2e-image: ${{ needs.docker-build-e2e.outputs.image }} | ||
| api-image: ${{ needs.docker-build-private-cloud.outputs.image }} | ||
| test-framework: playwright | ||
| args: --project=oss --project=enterprise | ||
| secrets: | ||
| GCR_TOKEN: ${{ needs.permissions-check.outputs.can-write == 'true' && secrets.GITHUB_TOKEN || '' }} | ||
| SLACK_TOKEN: ${{ needs.permissions-check.outputs.can-write == 'true' && secrets.SLACK_TOKEN || '' }} | ||
|
|
||
| strategy: | ||
| matrix: | ||
| runs-on: [depot-ubuntu-latest-16, depot-ubuntu-latest-arm-16] | ||
|
|
||
| run-e2e-tests-private-cloud-testcafe: |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 1 month ago
In general, the fix is to explicitly declare permissions: in the workflow so the GITHUB_TOKEN has the minimum required scopes. The safest and simplest change is to add a root-level permissions: block (right under name: or on:) so that all jobs default to least-privilege read access, and then selectively elevate permissions for any jobs that genuinely need writes. From the provided snippet, the jobs are building Docker images and running E2E tests via reusable workflows, and they use secrets.GITHUB_TOKEN as GCR_TOKEN. For typical container build/test workflows, contents: read is sufficient; no explicit write scopes are obviously required.
The single best fix here without changing any behavior is:
- Add a root-level
permissions:block (near the top of.github/workflows/platform-pull-request.yml) settingcontents: read. - Do not add any additional write permissions unless strictly necessary. From the context provided, we don’t see any direct need for write permissions, so limiting to
contents: readshould preserve behavior while restricting potential damage.
Concretely:
-
In
.github/workflows/platform-pull-request.yml, insert:permissions: contents: read
between the
on:block andjobs:(or just undername:; both are valid, but placing it betweenon:andjobs:is common and clear).
No additional imports, methods, or definitions are required, since this is purely a YAML configuration change.
| @@ -7,6 +7,9 @@ | ||
| - docs/** | ||
| - infrastructure/** | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| permissions-check: | ||
| name: Check actor permissions |
- Fix deleteFeature: change `remove-feature-btn-${index}` to `feature-remove-${index}`
- Fix deleteSegment: add `segment-action-${index}` click to open menu,
change `remove-segment-btn-${index}` to `segment-remove-${index}`
These selectors now match the actual UI element IDs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| && nvm use default | ||
|
|
||
| # add node and npm to path so the commands are available | ||
| ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules |
There was a problem hiding this comment.
Incorrect NODE_PATH missing versions/node directory segment
The NODE_PATH environment variable uses path $NVM_DIR/v$NODE_VERSION/lib/node_modules which is inconsistent with NVM's actual directory structure. Line 38 correctly uses $NVM_DIR/versions/node/v$NODE_VERSION/bin for PATH, showing the correct pattern includes versions/node/ before the version. The NODE_PATH is missing this segment and will point to a non-existent directory, which could affect module resolution for globally installed packages.
Thanks for submitting a PR! Please check the boxes below:
docs/if required so people know about the feature!Changes
Pending:
How did you test this code?
Please describe.