From cefefb1d0c7e66273230998828fa68df3b89c2e1 Mon Sep 17 00:00:00 2001 From: munderseth Date: Mon, 1 Jun 2026 14:53:14 -0700 Subject: [PATCH 1/4] Initial project setup with Playwright, including CI configuration, environment support, and example test --- .env.example | 1 + .github/workflows/ci.yml | 55 ++++++++++++++ .gitignore | 9 +++ README.md | 151 ++++++++++++++++++++++++++++++++++++++ package-lock.json | 92 +++++++++++++++++++++++ package.json | 19 +++++ playwright.config.js | 41 +++++++++++ tests/hello-world.spec.js | 8 ++ 8 files changed, 376 insertions(+) create mode 100644 .env.example create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 playwright.config.js create mode 100644 tests/hello-world.spec.js diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ae1d7bd --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +BASE_URL=https://example.com \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e0979e2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,55 @@ +name: Playwright CI + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Install Playwright browser + run: npx playwright install --with-deps chromium + + - name: Run Playwright tests + run: npm test + + - name: Setup Testspace client + uses: testspace-com/setup-testspace@v1 + with: + domain: ${{ github.repository_owner }}.stridespace.com + + - name: Publish results to Testspace + run: testspace test-results/junit/results.xml + + - name: Upload JUnit report + if: always() + uses: actions/upload-artifact@v4 + with: + name: junit-report + path: test-results/junit/results.xml + if-no-files-found: ignore + + - name: Upload HTML report + if: always() + uses: actions/upload-artifact@v4 + with: + name: playwright-html-report + path: playwright-report/ + if-no-files-found: ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..116ed43 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +node_modules/ +playwright-report/ +test-results/ +.playwright/ +.env +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..516b94d --- /dev/null +++ b/README.md @@ -0,0 +1,151 @@ +# JavaScript Playwright Scenarios + +Minimal Playwright setup for JavaScript-based browser scenarios with: + +- HTML reporting +- JUnit XML reporting +- Optional `.env` support via `dotenv` +- A hello-world smoke test +- Common npm scripts for local execution + +## Prerequisites + +- Node.js 20+ +- npm 10+ + +## Initial setup + +If starting from an empty folder, create a project manifest: + +```bash +npm init -y +``` + +Install project dependencies: + +```bash +npm install --save-dev @playwright/test dotenv +``` + +Optional: create a local environment file from the example: + +```bash +cp .env.example .env +``` + +Install the browser used by this starter project: + +```bash +npx playwright install chromium +``` + +Run the hello-world verification test: + +```bash +npm run test:hello +``` + +## Running tests + +Run all tests: + +```bash +npm test +``` + +Run a single file directly: + +```bash +npx playwright test tests/hello-world.spec.js +``` + +Run headed mode: + +```bash +npm run test:headed +``` + +Run with Playwright UI mode: + +```bash +npm run test:ui +``` + +Run in debug mode: + +```bash +npm run test:debug +``` + +## Reports + +Every test run writes: + +- HTML report to `playwright-report/` +- JUnit XML to `test-results/junit/results.xml` + +Open the HTML report after a run: + +```bash +npm run report:html +``` + +## CI (GitHub Actions) + +A minimal workflow is included at `.github/workflows/ci.yml`. + +It runs on: + +- push to `main` or `master` +- pull requests +- manual trigger via `workflow_dispatch` +- repository dispatch event type `workspace_dispatch` + +The workflow uploads both: + +- JUnit XML report +- Playwright HTML report + +### Testspace publishing + +The workflow can also publish JUnit results to Testspace. + +Configure these repository settings in GitHub: + +- Variable: TESTSPACE_DOMAIN + Example value: your-org.testspace.com +- Secret: TESTSPACE_TOKEN + Required for private repositories + +When TESTSPACE_DOMAIN is set, CI runs the Testspace setup action and publishes: + +- test-results/junit/results.xml + +## Changing the target URL + +The sample test uses `BASE_URL` and defaults to `https://example.com`. + +You can store it in a `.env` file: + +```bash +BASE_URL=https://example.com +``` + +Example: + +```bash +BASE_URL=https://playwright.dev npm run test:hello +``` + +If you change `BASE_URL`, update the sample assertions to match that page. + +## Project structure + +```text +. +|-- package.json +|-- playwright.config.js +|-- README.md +`-- tests/ + `-- hello-world.spec.js +``` diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8131575 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,92 @@ +{ + "name": "javascript.playwright-scenarios", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "javascript.playwright-scenarios", + "version": "1.0.0", + "devDependencies": { + "@playwright/test": "^1.60.0", + "dotenv": "^16.6.1" + } + }, + "node_modules/@playwright/test": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.60.0.tgz", + "integrity": "sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.60.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/playwright": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz", + "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.60.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz", + "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..ecb0659 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "javascript.playwright-scenarios", + "version": "1.0.0", + "private": true, + "description": "Starter setup for running JavaScript Playwright scenarios with HTML and JUnit reporting.", + "scripts": { + "test": "playwright test", + "test:headed": "playwright test --headed", + "test:ui": "playwright test --ui", + "test:debug": "playwright test --debug", + "test:hello": "playwright test tests/hello-world.spec.js", + "report:html": "playwright show-report playwright-report", + "install:browsers": "playwright install chromium" + }, + "devDependencies": { + "@playwright/test": "^1.60.0", + "dotenv": "^16.6.1" + } +} diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 0000000..b80e667 --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,41 @@ +require('dotenv').config(); + +const { defineConfig, devices } = require('@playwright/test'); +const isWSL = !!process.env.WSL_DISTRO_NAME; + +module.exports = defineConfig({ + testDir: './tests', + timeout: 30_000, + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: [ + ['list'], + ['html', { outputFolder: 'playwright-report', open: 'never' }], + ['junit', { outputFile: 'test-results/junit/results.xml' }] + ], + use: { + baseURL: process.env.BASE_URL || 'https://example.com', + trace: 'on-first-retry', + screenshot: 'only-on-failure', + video: 'retain-on-failure' + }, + projects: [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + launchOptions: { + args: [ + '--window-position=80,80', + '--window-size=1440,900', + '--start-maximized', + ...(isWSL ? ['--ozone-platform=x11', '--disable-gpu'] : []) + ] + } + } + } + ], + outputDir: 'test-results/artifacts' +}); \ No newline at end of file diff --git a/tests/hello-world.spec.js b/tests/hello-world.spec.js new file mode 100644 index 0000000..b54b497 --- /dev/null +++ b/tests/hello-world.spec.js @@ -0,0 +1,8 @@ +const { test, expect } = require('@playwright/test'); + +test('hello world smoke test', async ({ page, baseURL }) => { + await page.goto(baseURL); + await page.waitForTimeout(5000); + await expect(page).toHaveTitle(/Example Domain/); + await expect(page.getByRole('heading', { name: 'Example Domain' })).toBeVisible(); +}); \ No newline at end of file From 8246192f28c9e419304a1b3a25fdc78360ca1e0b Mon Sep 17 00:00:00 2001 From: munderseth Date: Mon, 1 Jun 2026 15:01:34 -0700 Subject: [PATCH 2/4] Fix subdomain --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e0979e2..ae92760 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: - name: Setup Testspace client uses: testspace-com/setup-testspace@v1 with: - domain: ${{ github.repository_owner }}.stridespace.com + domain: samples.stridespace.com - name: Publish results to Testspace run: testspace test-results/junit/results.xml From 4263f42574139686ec38b8d4f5f0bf0b5818c6a9 Mon Sep 17 00:00:00 2001 From: munderseth Date: Mon, 1 Jun 2026 15:24:36 -0700 Subject: [PATCH 3/4] Copilot PR review fixes --- .github/workflows/ci.yml | 8 ++++++++ README.md | 11 +++++++---- tests/hello-world.spec.js | 5 ++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ae92760..385fb43 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,8 +4,11 @@ on: push: branches: - main + - master pull_request: workflow_dispatch: + repository_dispatch: + types: [workspace_dispatch] jobs: test: @@ -34,8 +37,13 @@ jobs: uses: testspace-com/setup-testspace@v1 with: domain: samples.stridespace.com + # Optional for public repositories; required for private repositories. + token: ${{ secrets.TESTSPACE_TOKEN }} - name: Publish results to Testspace + env: + # Optional for public repositories; required for private repositories. + TESTSPACE_TOKEN: ${{ secrets.TESTSPACE_TOKEN }} run: testspace test-results/junit/results.xml - name: Upload JUnit report diff --git a/README.md b/README.md index 516b94d..8b19007 100644 --- a/README.md +++ b/README.md @@ -110,14 +110,17 @@ The workflow uploads both: The workflow can also publish JUnit results to Testspace. -Configure these repository settings in GitHub: +The workflow is configured to publish to the Testspace staging server: + +- `samples.stridespace.com` + +Configure this repository secret in GitHub: -- Variable: TESTSPACE_DOMAIN - Example value: your-org.testspace.com - Secret: TESTSPACE_TOKEN + Optional for public repositories Required for private repositories -When TESTSPACE_DOMAIN is set, CI runs the Testspace setup action and publishes: +CI publishes: - test-results/junit/results.xml diff --git a/tests/hello-world.spec.js b/tests/hello-world.spec.js index b54b497..e5c510c 100644 --- a/tests/hello-world.spec.js +++ b/tests/hello-world.spec.js @@ -1,8 +1,7 @@ const { test, expect } = require('@playwright/test'); -test('hello world smoke test', async ({ page, baseURL }) => { - await page.goto(baseURL); - await page.waitForTimeout(5000); +test('hello world smoke test', async ({ page }) => { + await page.goto('/'); await expect(page).toHaveTitle(/Example Domain/); await expect(page.getByRole('heading', { name: 'Example Domain' })).toBeVisible(); }); \ No newline at end of file From ae9413d3420414d79e3a56cc5d2f19d88ef4f9ca Mon Sep 17 00:00:00 2001 From: munderseth Date: Mon, 1 Jun 2026 15:32:09 -0700 Subject: [PATCH 4/4] Remove tokens, causes failure --- .github/workflows/ci.yml | 6 +----- README.md | 6 ------ 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 385fb43..bee3913 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,14 +37,10 @@ jobs: uses: testspace-com/setup-testspace@v1 with: domain: samples.stridespace.com - # Optional for public repositories; required for private repositories. - token: ${{ secrets.TESTSPACE_TOKEN }} - name: Publish results to Testspace - env: - # Optional for public repositories; required for private repositories. - TESTSPACE_TOKEN: ${{ secrets.TESTSPACE_TOKEN }} run: testspace test-results/junit/results.xml + if: always() - name: Upload JUnit report if: always() diff --git a/README.md b/README.md index 8b19007..110c767 100644 --- a/README.md +++ b/README.md @@ -114,12 +114,6 @@ The workflow is configured to publish to the Testspace staging server: - `samples.stridespace.com` -Configure this repository secret in GitHub: - -- Secret: TESTSPACE_TOKEN - Optional for public repositories - Required for private repositories - CI publishes: - test-results/junit/results.xml