diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index e73ddf0..5e73985 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -18,6 +18,9 @@ jobs: - name: Install Playwright Browsers run: npx playwright install --with-deps - 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/.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/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', 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..393a7d7 --- /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 dotenv from 'dotenv'; + +dotenv.config({ path: '.env' }); + +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/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