Added: GitHub Action for E2E Tests Workflow on PRs#1692
Added: GitHub Action for E2E Tests Workflow on PRs#1692sumitwebkul wants to merge 3 commits intoQloapps:developfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a Playwright-based E2E testing harness and a GitHub Actions workflow to automatically install QloApps in CI, start a PHP built-in server, and run browser tests on PRs/pushes—while also updating PHP version requirements.
Changes:
- Added Playwright config, env template, CI router, and an initial “Customer Login” E2E spec.
- Added a GitHub Actions workflow to provision MySQL, install QloApps, run Playwright, and upload reports/artifacts.
- Updated
composer.jsonPHP requirement and introduced a rootpackage.jsonfor Playwright tooling.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/e2e-tests.yml |
CI workflow to install QloApps, start PHP server, execute Playwright, upload artifacts |
composer.json |
Updates required PHP version range |
package.json |
Adds Node devDependencies and scripts for running Playwright E2E tests |
tests/e2e/playwright/playwright.config.ts |
Playwright test runner configuration + dotenv loading |
tests/e2e/playwright/.env.example |
Template env vars for local/CI E2E runs |
tests/e2e/playwright/ci-router.php |
Router for PHP built-in server to serve app + static files |
tests/e2e/playwright/specs/website/auth/login.spec.ts |
Login/logout E2E scenarios |
tests/e2e/**/index.php files |
Directory index redirects to prevent browsing test directories |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| { | ||
| "require": { | ||
| "php": ">=5.4 <8.0", | ||
| "php": ">=8.1 <8.6", |
There was a problem hiding this comment.
composer.json allows PHP <8.6, which implies compatibility with PHP 8.5.x as well. The README currently states support is "PHP 8.1+ to PHP 8.4"; consider tightening the constraint (e.g., <8.5) or updating the documentation so the supported PHP range is consistent.
| "php": ">=8.1 <8.6", | |
| "php": ">=8.1 <8.5", |
| # Base URL of the application under test | ||
| BASE_URL=http://127.0.0.1 |
There was a problem hiding this comment.
BASE_URL is documented as http://127.0.0.1 (no trailing slash), but the tests build URLs via string concatenation (e.g., BASE_URL + 'index.php?...'). With the example value, this produces an invalid URL (http://127.0.0.1index.php?...). Consider either normalizing BASE_URL to always end with / or constructing URLs via new URL() in the tests, and update the example accordingly.
| # Base URL of the application under test | |
| BASE_URL=http://127.0.0.1 | |
| # Base URL of the application under test (include trailing slash) | |
| BASE_URL=http://127.0.0.1/ |
| SQL_INJECTION_EMAIL=' OR '1'='1 | ||
| XSS_EMAIL=<script>alert('xss')</script> |
There was a problem hiding this comment.
The SQL_INJECTION_EMAIL example value is wrapped in single quotes but itself contains single quotes, which can cause dotenv parsers to terminate the value early (and produce an unintended value). Use a representation that dotenv can parse reliably (e.g., wrap the whole value in double quotes or escape the embedded quotes).
| SQL_INJECTION_EMAIL=' OR '1'='1 | |
| XSS_EMAIL=<script>alert('xss')</script> | |
| SQL_INJECTION_EMAIL="' OR '1'='1" | |
| XSS_EMAIL="<script>alert('xss')</script>" |
| test.describe('Customer Login', () => { | ||
|
|
||
| const BASE_URL = process.env.BASE_URL || 'http://127.0.0.1/'; | ||
| const AUTH_URL = BASE_URL + 'index.php?controller=authentication'; |
There was a problem hiding this comment.
AUTH_URL is built via string concatenation from BASE_URL. If BASE_URL is set without a trailing slash (as in the provided .env.example), the resulting URL is invalid. Normalize BASE_URL (ensure exactly one trailing /) or build AUTH_URL via new URL('index.php?...', BASE_URL) to avoid configuration-dependent failures.
| const AUTH_URL = BASE_URL + 'index.php?controller=authentication'; | |
| const AUTH_URL = new URL('index.php?controller=authentication', BASE_URL).toString(); |
| /* Fail the build on CI if you accidentally left test.only in the source code. */ | ||
| forbidOnly: !!process.env.CI, | ||
| /* Retry on CI only */ | ||
| retries: 0, |
There was a problem hiding this comment.
The comment says "Retry on CI only", but retries is set to 0, which also makes trace: 'on-first-retry' a no-op (no retries => no trace capture). Either enable retries on CI (and keep the trace setting) or change the trace setting to one that matches the no-retry policy.
| retries: 0, | |
| retries: process.env.CI ? 2 : 0, |
|
|
||
| if ($requestPath !== '/' && is_file($targetPath)) { |
There was a problem hiding this comment.
The router concatenates $rootDirectory and the raw request path to decide whether to serve a static file. Without normalizing/validating the path (e.g., rejecting .. segments and ensuring the resolved path stays under $rootDirectory), this can open path traversal / unintended file serving when used with the built-in PHP server. Consider using realpath() + prefix check (or a strict whitelist) before returning false.
| if ($requestPath !== '/' && is_file($targetPath)) { | |
| $realTargetPath = realpath($targetPath); | |
| if ( | |
| $requestPath !== '/' && | |
| $realTargetPath !== false && | |
| strncmp($realTargetPath, $rootDirectory . DIRECTORY_SEPARATOR, strlen($rootDirectory) + 1) === 0 && | |
| is_file($realTargetPath) | |
| ) { |
| cat > tests/e2e/playwright/.env << EOF | ||
| BASE_URL=$BASE_URL | ||
| CUSTOMER_EMAIL=$CUSTOMER_EMAIL | ||
| CUSTOMER_PASSWORD=$CUSTOMER_PASSWORD | ||
| INVALID_EMAIL=$INVALID_EMAIL | ||
| INVALID_PASSWORD=$INVALID_PASSWORD | ||
| INVALID_EMAIL_FORMAT=$INVALID_EMAIL_FORMAT | ||
| SQL_INJECTION_EMAIL=$SQL_INJECTION_EMAIL | ||
| XSS_EMAIL=$XSS_EMAIL | ||
| EOF | ||
|
|
There was a problem hiding this comment.
The generated .env file writes values without robust escaping/quoting. Values like SQL_INJECTION_EMAIL contain spaces and quotes, which can break dotenv parsing (or change the value) and make local/CI behavior diverge. Either (a) remove this step and rely on the job-level env: already set, or (b) emit correctly quoted dotenv values (e.g., wrap in double quotes and escape embedded characters).
| cat > tests/e2e/playwright/.env << EOF | |
| BASE_URL=$BASE_URL | |
| CUSTOMER_EMAIL=$CUSTOMER_EMAIL | |
| CUSTOMER_PASSWORD=$CUSTOMER_PASSWORD | |
| INVALID_EMAIL=$INVALID_EMAIL | |
| INVALID_PASSWORD=$INVALID_PASSWORD | |
| INVALID_EMAIL_FORMAT=$INVALID_EMAIL_FORMAT | |
| SQL_INJECTION_EMAIL=$SQL_INJECTION_EMAIL | |
| XSS_EMAIL=$XSS_EMAIL | |
| EOF | |
| python - << 'PY' | |
| import os | |
| import json | |
| from pathlib import Path | |
| env_path = Path("tests/e2e/playwright/.env") | |
| keys = [ | |
| "BASE_URL", | |
| "CUSTOMER_EMAIL", | |
| "CUSTOMER_PASSWORD", | |
| "INVALID_EMAIL", | |
| "INVALID_PASSWORD", | |
| "INVALID_EMAIL_FORMAT", | |
| "SQL_INJECTION_EMAIL", | |
| "XSS_EMAIL", | |
| ] | |
| env_path.parent.mkdir(parents=True, exist_ok=True) | |
| with env_path.open("w", encoding="utf-8") as f: | |
| for key in keys: | |
| value = os.environ.get(key, "") | |
| f.write(f"{key}={json.dumps(value)}\n") | |
| PY |
This pull request introduces a comprehensive end-to-end (E2E) testing setup for automating browser tests via GitHub Actions, ensuring robust CI/CD integration, and preparing the codebase for PHP 8.1+.