From d8176f54574e7bc6ea3166204d5f36da0b2ed1d4 Mon Sep 17 00:00:00 2001 From: Arnab Ghosh Date: Fri, 9 Oct 2026 10:33:49 +0530 Subject: [PATCH] ci: add deployed Playwright workflow --- .github/workflows/commitlint.yml | 4 +- .../workflows/playwright-deployed-branch.yml | 76 +++++++++++++++++++ projects/angular-test-app/tests/config.js | 6 +- .../tests/e2e/MediaCo/embedded.spec.js | 7 +- .../tests/e2e/MediaCo/portal.spec.js | 5 +- 5 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/playwright-deployed-branch.yml diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index 7def7a795..1ecbf193c 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -19,12 +19,12 @@ jobs: steps: # Full history is required so the PR base and head commits are both available. - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 + uses: actions/setup-node@v7 with: node-version: ${{ matrix.node-version }} cache: npm diff --git a/.github/workflows/playwright-deployed-branch.yml b/.github/workflows/playwright-deployed-branch.yml new file mode 100644 index 000000000..e96ab8021 --- /dev/null +++ b/.github/workflows/playwright-deployed-branch.yml @@ -0,0 +1,76 @@ +name: Playwright against deployed branch + +on: + workflow_dispatch: + inputs: + server_url: + description: 'Base URL of the deployed app; tests append /portal and /embedded' + required: true + type: string + +permissions: + contents: read + +concurrency: + # The portal tests create and hand off one case, so runs against the same deployment must not overlap. + group: deployed-playwright-${{ inputs.server_url }} + cancel-in-progress: false + +jobs: + playwright: + runs-on: ubuntu-latest + timeout-minutes: 30 + env: + PLAYWRIGHT_SERVER_URL: ${{ inputs.server_url }} + + steps: + - name: Check out selected branch + uses: actions/checkout@v7 + with: + ref: ${{ github.ref }} + + - name: Set up Node.js + uses: actions/setup-node@v7 + with: + node-version: 24.x + cache: npm + + - name: Validate server URL + run: | + node <<'NODE' + const url = new URL(process.env.PLAYWRIGHT_SERVER_URL); + + if ( + !['http:', 'https:'].includes(url.protocol) || + url.username || + url.password || + url.search || + url.hash + ) { + throw new Error('Provide a valid HTTP(S) base URL without credentials, query, or fragment'); + } + NODE + + - name: Check deployed app availability + run: >- + curl --fail --silent --show-error --max-time 30 --retry 5 + --retry-delay 5 --retry-all-errors --output /dev/null + "${PLAYWRIGHT_SERVER_URL%/}/portal" + + - name: Install dependencies + run: npm ci + + - name: Install Playwright Chromium + run: npx playwright install --with-deps chromium + + - name: Run MediaCo portal and embedded tests + run: npm test + + - name: Upload Playwright report + if: always() + uses: actions/upload-artifact@v7 + with: + name: playwright-report + path: tests/playwright-report/ + if-no-files-found: ignore + retention-days: 1 diff --git a/projects/angular-test-app/tests/config.js b/projects/angular-test-app/tests/config.js index 84a4eface..07334a228 100644 --- a/projects/angular-test-app/tests/config.js +++ b/projects/angular-test-app/tests/config.js @@ -1,6 +1,8 @@ +const serverUrl = process.env.PLAYWRIGHT_SERVER_URL?.replace(/\/+$/, ''); + const config = { - baseUrl: 'http://localhost:3500/portal', - baseEmbedUrl: 'http://localhost:3500/embedded', + baseUrl: serverUrl ? `${serverUrl}/portal` : 'http://localhost:3500/portal', + baseEmbedUrl: serverUrl ? `${serverUrl}/embedded` : 'http://localhost:3500/embedded', apps: { mediaCo: { rep: { diff --git a/projects/angular-test-app/tests/e2e/MediaCo/embedded.spec.js b/projects/angular-test-app/tests/e2e/MediaCo/embedded.spec.js index 33206123c..017a25aeb 100644 --- a/projects/angular-test-app/tests/e2e/MediaCo/embedded.spec.js +++ b/projects/angular-test-app/tests/e2e/MediaCo/embedded.spec.js @@ -2,6 +2,9 @@ const { test, expect } = require('@playwright/test'); const common = require('../../common'); const config = require('../../config'); +// Orders can persist in the shared deployment, so use a fresh email per worker run. +const testEmail = `john-${Date.now()}-${process.pid}@example.com`; + test.beforeEach(async ({ page }) => { await page.setViewportSize({ width: 1920, height: 1080 }); await page.goto(config.config.baseEmbedUrl, { waitUntil: 'networkidle' }); @@ -37,7 +40,7 @@ test.describe('E2E test', () => { await common.selectDateFromPicker(page, '6', 'June', '2000'); - await common.fillTextInput(page, '643a860f992333b8600ea264aca7c4fc', 'Johndoe@gmail.com'); + await common.fillTextInput(page, '643a860f992333b8600ea264aca7c4fc', testEmail); const phoneControl = page.locator('mat-tel-input[data-test-id="1e4dbc7eaa78468a3bc1448a3d68d906"]'); const countrySelector = phoneControl.locator('button'); @@ -73,7 +76,7 @@ test.describe('E2E test', () => { const paragraphElement = page.locator('.resolution-card p'); await expect(paragraphElement).toBeVisible(); await expect(paragraphElement).toHaveText( - ' We have received your order of a Oceonix 25. It will ship out within 1 business day to Apt 4. Your tracking information will be sent to Johndoe@gmail.com. Thank you for your business! ' + ` We have received your order of a Oceonix 25. It will ship out within 1 business day to Apt 4. Your tracking information will be sent to ${testEmail}. Thank you for your business! ` ); }, 10000); }); diff --git a/projects/angular-test-app/tests/e2e/MediaCo/portal.spec.js b/projects/angular-test-app/tests/e2e/MediaCo/portal.spec.js index 32d304f9b..9a5c59a4c 100644 --- a/projects/angular-test-app/tests/e2e/MediaCo/portal.spec.js +++ b/projects/angular-test-app/tests/e2e/MediaCo/portal.spec.js @@ -3,6 +3,9 @@ const { test, expect } = require('@playwright/test'); const config = require('../../config'); const common = require('../../common'); +// Distinct emails keep repeated and retried runs from colliding in a shared deployment. +const testEmail = `john-${Date.now()}-${process.pid}@example.com`; + test.beforeEach(async ({ page }) => { await page.setViewportSize({ width: 1920, height: 1080 }); await page.goto(config.config.baseUrl, { waitUntil: 'networkidle' }); @@ -35,7 +38,7 @@ test.describe('E2E test', () => { const emailInput = page.locator('input[data-test-id="CE8AE9DA5B7CD6C3DF2929543A9AF92D"]'); await emailInput.click(); - await emailInput.fill('john@doe.com'); + await emailInput.fill(testEmail); const serviceDateInput = page.locator('input[data-test-id="E0BA356AE552ACD4326D51E61F4279AC"]'); await serviceDateInput.click();