From cde8e3d6e9f88cf29ece4f9c825dc9adcc90bea8 Mon Sep 17 00:00:00 2001 From: Sirajvind <99573323+sirajvind@users.noreply.github.com> Date: Tue, 3 Dec 2024 00:30:39 +0800 Subject: [PATCH 1/6] feat: enhance Playwright setup with environment variable management and new tests --- .github/workflows/playwright.yml | 4 +++ .gitignore | 2 ++ package-lock.json | 15 ++++++++ package.json | 3 ++ .../ui-tests/playwright/pw-example.spec.ts} | 0 .../playwright/pw-pom-example.spec.ts | 25 +++++++++++++ .../ui-tests/swag-labs/swg-login-form.spec.js | 17 +++++++++ tests/helpers/loadEnv.js | 28 +++++++++++++++ tests/pages/playwright-dev-page.ts | 35 +++++++++++++++++++ tests/pages/swag-labs-dev-page.js | 30 ++++++++++++++++ 10 files changed, 159 insertions(+) rename tests/{example.spec.ts => e2e/ui-tests/playwright/pw-example.spec.ts} (100%) create mode 100644 tests/e2e/ui-tests/playwright/pw-pom-example.spec.ts create mode 100644 tests/e2e/ui-tests/swag-labs/swg-login-form.spec.js create mode 100644 tests/helpers/loadEnv.js create mode 100644 tests/pages/playwright-dev-page.ts create mode 100644 tests/pages/swag-labs-dev-page.js diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index e73ddf0..e706c7e 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -17,6 +17,10 @@ jobs: run: npm ci - name: Install Playwright Browsers run: npx playwright install --with-deps + - name: Set environment variables from JSON + run: | + echo "${{ secrets.SECRETS_JSON }}" > secrets.json + cat secrets.json | jq -r 'to_entries | .[] | "echo \(.key)=\(.value) >> $GITHUB_ENV"' | sh - name: Run Playwright tests run: npx playwright test - uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index 6eb6b06..983511e 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,8 @@ Thumbs.db .env .env.local .env.*.local +.nprmc +secrets.json # Playwright Specific playwright-report/ diff --git a/package-lock.json b/package-lock.json index b097922..0eeb009 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "playwright-automation", "version": "1.0.0", "license": "ISC", + "dependencies": { + "dotenv": "^16.4.5" + }, "devDependencies": { "@playwright/test": "^1.49.0", "@types/node": "^22.10.1" @@ -39,6 +42,18 @@ "undici-types": "~6.20.0" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "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", diff --git a/package.json b/package.json index 7901c6c..dc8f423 100644 --- a/package.json +++ b/package.json @@ -16,5 +16,8 @@ "devDependencies": { "@playwright/test": "^1.49.0", "@types/node": "^22.10.1" + }, + "dependencies": { + "dotenv": "^16.4.5" } } diff --git a/tests/example.spec.ts b/tests/e2e/ui-tests/playwright/pw-example.spec.ts similarity index 100% rename from tests/example.spec.ts rename to tests/e2e/ui-tests/playwright/pw-example.spec.ts diff --git a/tests/e2e/ui-tests/playwright/pw-pom-example.spec.ts b/tests/e2e/ui-tests/playwright/pw-pom-example.spec.ts new file mode 100644 index 0000000..0c65189 --- /dev/null +++ b/tests/e2e/ui-tests/playwright/pw-pom-example.spec.ts @@ -0,0 +1,25 @@ +import { test, expect } from '@playwright/test'; +import { PlaywrightDevPage } from '../../../pages/playwright-dev-page'; + +test('getting started should contain table of contents', async ({ page }) => { + const playwrightDev = new PlaywrightDevPage(page); + await playwrightDev.goto(); + await playwrightDev.getStarted(); + await expect(playwrightDev.tocList).toHaveText([ + `How to install Playwright`, + `What's Installed`, + `How to run the example test`, + `How to open the HTML test report`, + `Write tests using web first assertions, page fixtures and locators`, + `Run single test, multiple tests, headed mode`, + `Generate tests with Codegen`, + `See a trace of your tests` + ]); +}); + +test('should show Page Object Model article', async ({ page }) => { + const playwrightDev = new PlaywrightDevPage(page); + await playwrightDev.goto(); + await playwrightDev.pageObjectModel(); + await expect(page.locator('article')).toContainText('Page Object Model is a common pattern'); +}); \ No newline at end of file diff --git a/tests/e2e/ui-tests/swag-labs/swg-login-form.spec.js b/tests/e2e/ui-tests/swag-labs/swg-login-form.spec.js new file mode 100644 index 0000000..1441fa3 --- /dev/null +++ b/tests/e2e/ui-tests/swag-labs/swg-login-form.spec.js @@ -0,0 +1,17 @@ +import { test } from '@playwright/test'; +import { SwagLabsDevPage } from '../../../pages/swag-labs-dev-page'; +import loadEnv from '../../../helpers/loadEnv'; + +loadEnv(); + +test.beforeEach('goto baseURL', async ({ page }) => { + const swagLabsBaseURL = new SwagLabsDevPage(page); + await swagLabsBaseURL.goto(); +}); + +test('validate login form', async ({ page }) => { + const swagLabsModule = new SwagLabsDevPage(page); + await swagLabsModule.fillUsername(process.env.SWAG_LABS_USERNAME); + await swagLabsModule.fillPassword(process.env.SWAG_LABS_PASSWORD); + await swagLabsModule.clickLoginButton(); +}); \ No newline at end of file diff --git a/tests/helpers/loadEnv.js b/tests/helpers/loadEnv.js new file mode 100644 index 0000000..e8225aa --- /dev/null +++ b/tests/helpers/loadEnv.js @@ -0,0 +1,28 @@ +const dotenv = require('dotenv'); +const fs = require('fs'); +const path = require('path'); + +function loadEnv() { + const envPath = path.resolve(__dirname, '../../.env'); + if (fs.existsSync(envPath)) { + dotenv.config({ path: envPath }); + } else { + const secretsPath = path.resolve(__dirname, '../../secrets.json'); + if (fs.existsSync(secretsPath)) { + const secrets = JSON.parse(fs.readFileSync(secretsPath, 'utf8')); + for (const [key, value] of Object.entries(secrets)) { + process.env[key] = value; + } + } else { + const secretsJson = process.env.SECRETS_JSON; + if (secretsJson) { + const envConfig = JSON.parse(secretsJson); + for (const [key, value] of Object.entries(envConfig)) { + process.env[key] = value; + } + } + } + } +} + +module.exports = loadEnv; \ No newline at end of file diff --git a/tests/pages/playwright-dev-page.ts b/tests/pages/playwright-dev-page.ts new file mode 100644 index 0000000..118c387 --- /dev/null +++ b/tests/pages/playwright-dev-page.ts @@ -0,0 +1,35 @@ +import { expect, type Locator, type Page } from '@playwright/test'; + +export class PlaywrightDevPage { + readonly page: Page; + readonly getStartedLink: Locator; + readonly gettingStartedHeader: Locator; + readonly pomLink: Locator; + readonly tocList: Locator; + + constructor(page: Page) { + this.page = page; + this.getStartedLink = page.locator('a', { hasText: 'Get started' }); + this.gettingStartedHeader = page.locator('h1', { hasText: 'Installation' }); + this.pomLink = page.locator('li', { + hasText: 'Guides', + }).locator('a', { + hasText: 'Page Object Model', + }); + this.tocList = page.locator('article div.markdown ul > li > a'); + } + + async goto() { + await this.page.goto('https://playwright.dev'); + } + + async getStarted() { + await this.getStartedLink.first().click(); + await expect(this.gettingStartedHeader).toBeVisible(); + } + + async pageObjectModel() { + await this.getStarted(); + await this.pomLink.click(); + } +} \ No newline at end of file diff --git a/tests/pages/swag-labs-dev-page.js b/tests/pages/swag-labs-dev-page.js new file mode 100644 index 0000000..b7f05f6 --- /dev/null +++ b/tests/pages/swag-labs-dev-page.js @@ -0,0 +1,30 @@ +import { expect } from "@playwright/test"; + +export class SwagLabsDevPage { + + constructor(page) { + this.page = page; + this.username = page.locator('[data-test="username"]'); + this.password = page.locator('[data-test="password"]'); + this.loginButton = page.locator('[data-test="login-button"]'); + } + + async goto() { + await this.page.goto('https://www.saucedemo.com/'); + await expect(this.page.locator('#root')).toContainText('Swag Labs'); + } + + async clickLoginButton() { + await this.loginButton.click(); + await expect(this.page.locator('[data-test="title"]')).toContainText('Products'); + await expect(this.page.locator('[data-test="inventory-container"]')).toBeVisible(); + } + + async fillUsername(username){ + await this.username.fill(username); + } + + async fillPassword(password){ + await this.password.fill(password); + } +} \ No newline at end of file From caab55f4651277536b210af75d66deb9c2a079c9 Mon Sep 17 00:00:00 2001 From: Sirajvind <99573323+sirajvind@users.noreply.github.com> Date: Tue, 3 Dec 2024 00:51:15 +0800 Subject: [PATCH 2/6] fix: add validation step for environment variables in Playwright workflow --- .github/workflows/playwright.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index e706c7e..3ac9789 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -20,6 +20,7 @@ jobs: - name: Set environment variables from JSON run: | echo "${{ secrets.SECRETS_JSON }}" > secrets.json + cat secrets.json | jq empty cat secrets.json | jq -r 'to_entries | .[] | "echo \(.key)=\(.value) >> $GITHUB_ENV"' | sh - name: Run Playwright tests run: npx playwright test From 5f19d6f90464b46e6b719e25ba5a7bc8b72b3e10 Mon Sep 17 00:00:00 2001 From: Sirajvind <99573323+sirajvind@users.noreply.github.com> Date: Tue, 3 Dec 2024 00:56:09 +0800 Subject: [PATCH 3/6] fix: update environment variable handling in Playwright workflow to use toJson function --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 3ac9789..5af3600 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -19,7 +19,7 @@ jobs: run: npx playwright install --with-deps - name: Set environment variables from JSON run: | - echo "${{ secrets.SECRETS_JSON }}" > secrets.json + echo "${{ toJson(secrets.SECRETS_JSON) }}" > secrets.json cat secrets.json | jq empty cat secrets.json | jq -r 'to_entries | .[] | "echo \(.key)=\(.value) >> $GITHUB_ENV"' | sh - name: Run Playwright tests From 9de920d02f3787e0061f5082db4f895d8122d0d9 Mon Sep 17 00:00:00 2001 From: Sirajvind <99573323+sirajvind@users.noreply.github.com> Date: Tue, 3 Dec 2024 01:11:08 +0800 Subject: [PATCH 4/6] fix: simplify environment variable handling in Playwright workflow by removing toJson function --- .github/workflows/playwright.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 5af3600..e706c7e 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -19,8 +19,7 @@ jobs: run: npx playwright install --with-deps - name: Set environment variables from JSON run: | - echo "${{ toJson(secrets.SECRETS_JSON) }}" > secrets.json - cat secrets.json | jq empty + echo "${{ secrets.SECRETS_JSON }}" > secrets.json cat secrets.json | jq -r 'to_entries | .[] | "echo \(.key)=\(.value) >> $GITHUB_ENV"' | sh - name: Run Playwright tests run: npx playwright test From 39e8f8c565038fa73a2f4556f57018df173a25e9 Mon Sep 17 00:00:00 2001 From: Sirajvind <99573323+sirajvind@users.noreply.github.com> Date: Wed, 4 Dec 2024 00:01:40 +0800 Subject: [PATCH 5/6] fix: streamline environment variable management in Playwright workflow by removing JSON handling and using dotenv --- .github/workflows/playwright.yml | 7 ++--- .../ui-tests/swag-labs/swg-login-form.spec.js | 4 +-- tests/helpers/loadEnv.js | 28 ------------------- 3 files changed, 5 insertions(+), 34 deletions(-) delete mode 100644 tests/helpers/loadEnv.js diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index e706c7e..5e73985 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -17,11 +17,10 @@ jobs: run: npm ci - name: Install Playwright Browsers run: npx playwright install --with-deps - - name: Set environment variables from JSON - run: | - echo "${{ secrets.SECRETS_JSON }}" > secrets.json - cat secrets.json | jq -r 'to_entries | .[] | "echo \(.key)=\(.value) >> $GITHUB_ENV"' | sh - name: Run Playwright tests + env: + SWAG_LABS_USERNAME: ${{ secrets.SWAG_LABS_USERNAME }} + SWAG_LABS_PASSWORD: ${{ secrets.SWAG_LABS_PASSWORD }} run: npx playwright test - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} diff --git a/tests/e2e/ui-tests/swag-labs/swg-login-form.spec.js b/tests/e2e/ui-tests/swag-labs/swg-login-form.spec.js index 1441fa3..393a7d7 100644 --- a/tests/e2e/ui-tests/swag-labs/swg-login-form.spec.js +++ b/tests/e2e/ui-tests/swag-labs/swg-login-form.spec.js @@ -1,8 +1,8 @@ import { test } from '@playwright/test'; import { SwagLabsDevPage } from '../../../pages/swag-labs-dev-page'; -import loadEnv from '../../../helpers/loadEnv'; +import dotenv from 'dotenv'; -loadEnv(); +dotenv.config({ path: '.env' }); test.beforeEach('goto baseURL', async ({ page }) => { const swagLabsBaseURL = new SwagLabsDevPage(page); diff --git a/tests/helpers/loadEnv.js b/tests/helpers/loadEnv.js deleted file mode 100644 index e8225aa..0000000 --- a/tests/helpers/loadEnv.js +++ /dev/null @@ -1,28 +0,0 @@ -const dotenv = require('dotenv'); -const fs = require('fs'); -const path = require('path'); - -function loadEnv() { - const envPath = path.resolve(__dirname, '../../.env'); - if (fs.existsSync(envPath)) { - dotenv.config({ path: envPath }); - } else { - const secretsPath = path.resolve(__dirname, '../../secrets.json'); - if (fs.existsSync(secretsPath)) { - const secrets = JSON.parse(fs.readFileSync(secretsPath, 'utf8')); - for (const [key, value] of Object.entries(secrets)) { - process.env[key] = value; - } - } else { - const secretsJson = process.env.SECRETS_JSON; - if (secretsJson) { - const envConfig = JSON.parse(secretsJson); - for (const [key, value] of Object.entries(envConfig)) { - process.env[key] = value; - } - } - } - } -} - -module.exports = loadEnv; \ No newline at end of file From 62105d871c55e84236cc2f1649fb88d251c7206d Mon Sep 17 00:00:00 2001 From: Sirajvind <99573323+sirajvind@users.noreply.github.com> Date: Wed, 4 Dec 2024 00:20:49 +0800 Subject: [PATCH 6/6] fix: adjust worker count in Playwright configuration for improved parallelism in CI --- playwright.config.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/playwright.config.ts b/playwright.config.ts index be0ff7b..d54731d 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -5,12 +5,11 @@ export default defineConfig({ fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, - workers: process.env.CI ? 1 : undefined, + workers: process.env.CI ? 5 : undefined, reporter: 'html', use: { trace: 'on-first-retry', }, - projects: [ { name: 'chromium', @@ -27,7 +26,6 @@ export default defineConfig({ use: { ...devices['Desktop Safari'] }, }, ], - /* Run your local dev server before starting the tests */ // webServer: { // command: 'npm run start',