From 7a387703e8399e01ea5eba366255687cc52b12b7 Mon Sep 17 00:00:00 2001 From: Zaafir Dar Date: Wed, 19 Nov 2025 09:39:41 -0500 Subject: [PATCH 1/6] Integration Playwright MCP server --- .github/copilot-instructions.md | 119 +++++++++++++++ FRAMEWORK_RULES.md | 253 ++++++++++++++++++++++++++++++++ 2 files changed, 372 insertions(+) create mode 100644 .github/copilot-instructions.md create mode 100644 FRAMEWORK_RULES.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..3247096 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,119 @@ +# Copilot Instructions for PlaywrightTypeScriptDemo + +## Project Architecture + +This is a Playwright TypeScript test automation framework using the **Page Object Model (POM)** pattern with a custom fixture-based architecture. Tests run against external practice sites. + +### Key Structure +- `tests/specs/` - Test specifications using extended fixtures +- `tests/pageObejcts/` - Page object classes (note: typo in directory name is intentional) +- `tests/locators/` - JSON files containing element selectors +- `tests/data/` - JSON test data files +- `tests/utilities/setup.ts` - Custom Playwright fixture extensions + +## Critical Framework Pattern: Custom Fixtures + +**This project uses a custom test fixture pattern in `tests/utilities/setup.ts` that automatically instantiates page objects.** + +```typescript +// In tests, import from setup.ts, NOT from @playwright/test +import { test } from '../utilities/setup'; + +// Page objects are injected as fixtures: +test('example', async ({ loginPage, examplePage }) => { + // loginPage and examplePage are already instantiated + await loginPage.loginValidCredentials(user, pass); +}); +``` + +**Never import `{ test }` directly from '@playwright/test' in specs** - always use the extended test from `setup.ts`. + +## Locator Management + +All selectors live in JSON files in `tests/locators/`: + +```json +{ + "username": "input[id='username']", + "loginButton": "button[id='submit']", + "loginSuccessHeader": ".post-title:has-text('Logged In Successfully')" +} +``` + +Import and use in page objects: +```typescript +import loginLocator from '../locators/example.json'; +await this.page.fill(loginLocator.username, username); +``` + +**Never hardcode selectors in page objects or tests.** Use Playwright's `:has-text()` for text-based selectors with specific text content. + +## Page Object Pattern + +Constructor signature: +```typescript +export class LoginPage { + constructor(private page: Page) {} + // Methods use this.page +} +``` + +Use `private page: Page` in constructor (TypeScript shorthand). Methods should be async and focused on user actions, not assertions. + +## Test Data + +Centralized in JSON files at `tests/data/`. Contains URLs, credentials, and expected values: +```typescript +import testData from '../data/example.json'; +await examplePage.navigateTo(testData.BASE_URL); +``` + +## Test Execution + +**Primary test command (from package.json):** +```bash +npm run test # Runs example.spec.ts in headed mode +``` + +Standard Playwright commands work but respect `testDir: './tests'` in config: +```bash +npx playwright test # Run all tests +npx playwright test example.spec.ts # Specific test +npx playwright show-report # View HTML report +``` + +Tests run in Chromium only (Firefox/WebKit commented out in config). + +## Adding New Features + +### New Page Object +1. Create class in `tests/pageObejcts/` with `constructor(private page: Page)` +2. Create locators JSON in `tests/locators/` +3. Add fixture to `tests/utilities/setup.ts`: +```typescript +const test = base.extend<{ + newPage: NewPage; +}>({ + newPage: async ({ page }, use) => { + await use(new NewPage(page)); + }, +}); +``` + +### New Test +1. Import extended test: `import { test } from '../utilities/setup'` +2. Use page object fixtures in test signatures +3. Reference `FRAMEWORK_RULES.md` for error handling, screenshots, and resilience patterns + +## Common Patterns + +- **Waits**: Use `await this.page.waitForTimeout(3000)` sparingly; prefer `waitForSelector` or `expect().toBeVisible()` +- **Assertions**: Import `expect` from setup.ts, use in page objects when verifying page state +- **BeforeEach**: Used for navigation and common setup, receives page objects as fixtures + +## Important Context + +- Directory `tests/pageObejcts/` has intentional typo - maintain consistency +- Tests target external site: for example `https://practicetestautomation.com/practice-test-login/` +- Framework follows strict separation: locators → page objects → tests → data +- See `FRAMEWORK_RULES.md` for comprehensive patterns on resilience, error handling, TypeScript best practices, and framework conventions diff --git a/FRAMEWORK_RULES.md b/FRAMEWORK_RULES.md new file mode 100644 index 0000000..93b2497 --- /dev/null +++ b/FRAMEWORK_RULES.md @@ -0,0 +1,253 @@ +# Rule Set for Playwright Automation Framework + +## Code Organization and Structure + +### Page Object Model (POM) +- **Always follow the Page Object Model pattern** + - Keep page interactions in page classes (`tests/pageObejcts/` directory - note the intentional typo) + - Store locators separately in JSON files (`tests/locators/` directory) + - Implement tests in the specs directory (`tests/specs/` directory) + - Centralize test data in JSON files (`tests/data/` directory) + - Define custom fixtures in `tests/utilities/setup.ts` + +### Class Naming +- Page classes should match the page name (e.g., `login.ts`, `example.ts`) +- Test files should end with `.spec.ts` +- Locator files should be JSON format (e.g., `example.json`) +- Test data files should be JSON format (e.g., `example.json`) + +## Locator Management + +### Centralized Locators in JSON +- All selectors must be defined in JSON files in `tests/locators/` directory +- Import locators as JSON objects in page classes +- Never hardcode selectors in page objects or test files + +**Example locator file (`tests/locators/example.json`):** +```json +{ + "username": "input[id='username']", + "loginButton": "button[id='submit']", + "loginSuccessHeader": ".post-title:has-text('Logged In Successfully')" +} +``` + +**Import and use in page objects:** +```typescript +import loginLocator from '../locators/example.json'; +await this.page.fill(loginLocator.username, username); +``` + +### Resilient Selectors +- Use Playwright's `:has-text()` pseudo-class for text-based selectors +- Prefer ID selectors when available (e.g., `input[id='username']`) +- Use CSS class selectors with text matching for dynamic content +- Keep selectors simple and maintainable + +## Page Object Implementation + +### Constructor Pattern +Each page class should: +- Accept a Playwright `Page` object in the constructor using TypeScript shorthand +- Use `private page: Page` for automatic property declaration + +**Example from actual codebase:** +```typescript +export class LoginPage { + constructor(private page: Page) {} + + async loginValidCredentials(username: string, password: string) { + await this.page.fill(loginLocator.username, username); + await this.page.fill(loginLocator.password, password); + await this.page.click(loginLocator.loginButton); + await this.page.waitForTimeout(3000); + } +} +``` + +### Method Documentation +- Keep method names descriptive and action-oriented +- Methods should be async when performing page interactions +- Include assertions within page methods when verifying page state + +**Example from actual codebase:** +```typescript +async verifyLoginSucess() { + await expect(this.page.locator(loginLocator.loginSuccessHeader)).toBeVisible(); +} + +async userLogout() { + await this.page.click(loginLocator.logoutButton); +} +``` + +### Test Data Management +- Store all test data in JSON files in `tests/data/` directory +- Import test data as JSON objects in tests and page objects +- Include URLs, credentials, expected values, and validation data + +**Example test data file (`tests/data/example.json`):** +```json +{ + "BASE_URL": "https://practicetestautomation.com/practice-test-login/", + "username": "student", + "password": "Password123", + "Header": "Test login", + "pageTitle": "Test Login | Practice Test Automation" +} +``` + +## Test Implementation + +### Custom Fixture Pattern (CRITICAL) +- **Always import `test` from `../utilities/setup.ts`, NOT from `@playwright/test`** +- Page objects are automatically instantiated as fixtures +- Access page objects directly in test parameters + +**Example from actual codebase:** +```typescript +import { test } from '../utilities/setup'; // Import extended test +import testData from '../data/example.json'; + +test('Login Success and verify', async ({ loginPage }) => { + // loginPage is already instantiated via fixture + await loginPage.loginValidCredentials(testData.username, testData.password); + await loginPage.verifyLoginSucess(); +}); +``` + +### Test Setup with Fixtures +- Use `test.beforeEach()` for common setup operations +- Page object fixtures are injected automatically +- Import test data from JSON files + +**Example:** +```typescript +test.beforeEach('Verify Header and title', async ({ examplePage }) => { + await examplePage.navigateTo(testData.BASE_URL); + await examplePage.verifyHeader(); + await examplePage.verifyTitle(); +}); +``` + +### Adding New Page Object Fixtures +When creating a new page object, add it to `tests/utilities/setup.ts`: + +```typescript +import { test as base, expect } from '@playwright/test'; +import { NewPage } from '../pageObejcts/newPage'; + +const test = base.extend<{ + newPage: NewPage; +}>({ + newPage: async ({ page }, use) => { + const newPage = new NewPage(page); + await use(newPage); + }, +}); + +export { test, expect }; +``` + +### Assertions and Waits +- Import `expect` from `tests/utilities/setup.ts` alongside `test` +- Use `expect().toBeVisible()` for element visibility checks +- Use `expect().toContain()` for text content validation +- Use `waitForTimeout()` sparingly; prefer built-in waits when possible + +**Example from actual codebase:** +```typescript +import { test, expect } from '../utilities/setup'; + +// In page object +async verifyHeader() { + let header = await this.page.textContent(locators.homePageLogo); + expect(header).toContain(testData.Header); +} + +async verifyLoginSucess() { + await expect(this.page.locator(loginLocator.loginSuccessHeader)).toBeVisible(); +} +``` + +## Test Execution + +### Running Tests +- **Primary command**: `npm run test` - Runs `example.spec.ts` in headed mode +- Standard Playwright commands work with `testDir: './tests'` configuration: + - `npx playwright test` - Run all tests + - `npx playwright test example.spec.ts` - Run specific test + - `npx playwright show-report` - View HTML report + +### Browser Configuration +- Tests run in Chromium only (Firefox/WebKit commented out in config) +- Configured in `playwright.config.ts` +- HTML reporter enabled by default + +## Best Practices + +### TypeScript Patterns +- Use `constructor(private page: Page)` shorthand for page objects +- Define `readonly page: Page` when using explicit property declaration +- Type method parameters appropriately (e.g., `username: string`) +- Use `Promise` or `Promise` for async return types + +**Example:** +```typescript +async getText(selector: string): Promise { + const text = await this.page.textContent(selector); + return text ?? ''; +} +``` + +### Code Organization +- Keep page interaction methods focused and single-purpose +- Import locators and test data at the top of files +- Group related methods together in page objects +- Use descriptive variable names (e.g., `loginPage`, `examplePage`) + +## Important Notes + +- The directory `tests/pageObejcts/` has an intentional typo - maintain consistency +- Tests target external site: `https://practicetestautomation.com/practice-test-login/` +- Framework uses strict separation: locators (JSON) → page objects → fixtures → tests → data (JSON) +- Always extend fixtures in `setup.ts` when adding new page objects + +## Actual Project Structure + +``` +/ +├── tests/ +│ ├── specs/ +│ │ └── example.spec.ts +│ ├── pageObejcts/ # Note: intentional typo +│ │ ├── example.ts +│ │ └── login.ts +│ ├── locators/ +│ │ └── example.json # JSON format, not .ts +│ ├── data/ +│ │ └── example.json # Test data in JSON +│ └── utilities/ +│ └── setup.ts # Custom fixture definitions +├── playwright.config.ts +├── FRAMEWORK_RULES.md +├── package.json +└── README.md +``` + +## Checklist for New Tests + +- [ ] Locators defined in JSON file in `tests/locators/` +- [ ] Test data defined in JSON file in `tests/data/` +- [ ] Page object created in `tests/pageObejcts/` with `constructor(private page: Page)` +- [ ] Page object added as fixture in `tests/utilities/setup.ts` +- [ ] Test imports `test` from `../utilities/setup`, NOT from `@playwright/test` +- [ ] Test uses page object fixtures (e.g., `async ({ loginPage }))` +- [ ] Test imports data from JSON files +- [ ] Descriptive test names that explain behavior +- [ ] Assertions use `expect` imported from setup.ts +- [ ] No hardcoded selectors or test data in test files + +--- + +**Last Updated:** November 19, 2025 From b3d43eee1731b7c634ffff477743978733264459 Mon Sep 17 00:00:00 2001 From: Zaafir Dar Date: Wed, 19 Nov 2025 10:23:00 -0500 Subject: [PATCH 2/6] updating test --- tests/locators/{example.json => locators.json} | 0 tests/pageObejcts/example.ts | 2 +- tests/pageObejcts/login.ts | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename tests/locators/{example.json => locators.json} (100%) diff --git a/tests/locators/example.json b/tests/locators/locators.json similarity index 100% rename from tests/locators/example.json rename to tests/locators/locators.json diff --git a/tests/pageObejcts/example.ts b/tests/pageObejcts/example.ts index 2d77779..d128c88 100644 --- a/tests/pageObejcts/example.ts +++ b/tests/pageObejcts/example.ts @@ -1,6 +1,6 @@ import { Page } from '@playwright/test'; import { test, expect } from '@playwright/test'; -import locators from '../locators/example.json'; +import locators from '../locators/locators.json'; import testData from '../data/example.json'; export class ExamplePage { diff --git a/tests/pageObejcts/login.ts b/tests/pageObejcts/login.ts index 6cde08c..f104d9e 100644 --- a/tests/pageObejcts/login.ts +++ b/tests/pageObejcts/login.ts @@ -1,5 +1,5 @@ import { Page, expect } from '@playwright/test'; -import loginLocator from '../locators/example.json'; +import loginLocator from '../locators/locators.json'; export class LoginPage { constructor(private page: Page) {} From f24a82aa2918238abc16516fb80bc1420516362b Mon Sep 17 00:00:00 2001 From: Zaafir Dar Date: Wed, 19 Nov 2025 10:24:46 -0500 Subject: [PATCH 3/6] framework update --- .github/copilot-instructions.md | 4 ++-- FRAMEWORK_RULES.md | 6 +++--- package.json | 2 +- tests/specs/{example.spec.ts => login.spec.ts} | 0 4 files changed, 6 insertions(+), 6 deletions(-) rename tests/specs/{example.spec.ts => login.spec.ts} (100%) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 3247096..ca116cf 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -72,13 +72,13 @@ await examplePage.navigateTo(testData.BASE_URL); **Primary test command (from package.json):** ```bash -npm run test # Runs example.spec.ts in headed mode +npm run test # Runs login.spec.ts in headed mode ``` Standard Playwright commands work but respect `testDir: './tests'` in config: ```bash npx playwright test # Run all tests -npx playwright test example.spec.ts # Specific test +npx playwright test login.spec.ts # Specific test npx playwright show-report # View HTML report ``` diff --git a/FRAMEWORK_RULES.md b/FRAMEWORK_RULES.md index 93b2497..1aac22d 100644 --- a/FRAMEWORK_RULES.md +++ b/FRAMEWORK_RULES.md @@ -173,10 +173,10 @@ async verifyLoginSucess() { ## Test Execution ### Running Tests -- **Primary command**: `npm run test` - Runs `example.spec.ts` in headed mode +- **Primary command**: `npm run test` - Runs `login.spec.ts` in headed mode - Standard Playwright commands work with `testDir: './tests'` configuration: - `npx playwright test` - Run all tests - - `npx playwright test example.spec.ts` - Run specific test + - `npx playwright test login.spec.ts` - Run specific test - `npx playwright show-report` - View HTML report ### Browser Configuration @@ -219,7 +219,7 @@ async getText(selector: string): Promise { / ├── tests/ │ ├── specs/ -│ │ └── example.spec.ts +│ │ └── login.spec.ts │ ├── pageObejcts/ # Note: intentional typo │ │ ├── example.ts │ │ └── login.ts diff --git a/package.json b/package.json index 4a3ff56..6a49078 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "", "main": "index.js", "scripts": { - "test": "playwright test example.spec.ts --headed" + "test": "playwright test login.spec.ts --headed" }, "repository": { "type": "git", diff --git a/tests/specs/example.spec.ts b/tests/specs/login.spec.ts similarity index 100% rename from tests/specs/example.spec.ts rename to tests/specs/login.spec.ts From 1728a61380a1799b82536a3f2a52c36428bb5133 Mon Sep 17 00:00:00 2001 From: Zaafir Dar Date: Wed, 19 Nov 2025 10:39:34 -0500 Subject: [PATCH 4/6] updating framework --- tests/pageObejcts/example.ts | 39 ------------------------------ tests/pageObejcts/login.ts | 16 ++++++++----- tests/specs/login.spec.ts | 10 ++++---- tests/utilities/helpers.ts | 46 ++++++++++++++++++++++++++++++++++++ tests/utilities/setup.ts | 9 ------- 5 files changed, 62 insertions(+), 58 deletions(-) delete mode 100644 tests/pageObejcts/example.ts create mode 100644 tests/utilities/helpers.ts diff --git a/tests/pageObejcts/example.ts b/tests/pageObejcts/example.ts deleted file mode 100644 index d128c88..0000000 --- a/tests/pageObejcts/example.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Page } from '@playwright/test'; -import { test, expect } from '@playwright/test'; -import locators from '../locators/locators.json'; -import testData from '../data/example.json'; - -export class ExamplePage { - readonly page: Page; - - constructor(page: Page) { - this.page = page; - } - - async navigateTo(url: string) { - await this.page.goto(url); - } - - async clickButton(selector: string) { - await this.page.click(selector); - } - - async enterText(selector: string, text: string) { - await this.page.fill(selector, text); - } - - async getText(selector: string): Promise { - const text = await this.page.textContent(selector); - return text ?? ''; - } - - async verifyHeader() { - let header = await this.page.textContent(locators.homePageLogo); - expect(header).toContain(testData.Header) - } - - async verifyTitle() { - let title = await this.page.title() - expect(title).toContain(testData.pageTitle) - } -} \ No newline at end of file diff --git a/tests/pageObejcts/login.ts b/tests/pageObejcts/login.ts index f104d9e..c9b6dec 100644 --- a/tests/pageObejcts/login.ts +++ b/tests/pageObejcts/login.ts @@ -1,14 +1,18 @@ import { Page, expect } from '@playwright/test'; import loginLocator from '../locators/locators.json'; +import { enterText, clickButton } from '../utilities/helpers'; export class LoginPage { - constructor(private page: Page) {} + + constructor(private page: Page) { + + } async loginValidCredentials(username: string, password: string) { - await this.page.fill(loginLocator.username, username); - await this.page.fill(loginLocator.password, password); - await this.page.click(loginLocator.loginButton); - await this.page.waitForTimeout(3000) + await enterText(this.page, loginLocator.username, username); + await enterText(this.page, loginLocator.password, password); + await clickButton(this.page, loginLocator.loginButton); + await this.page.waitForTimeout(3000); } async verifyLoginSucess() { @@ -16,6 +20,6 @@ export class LoginPage { } async userLogout() { - await this.page.click(loginLocator.logoutButton); + await clickButton(this.page, loginLocator.logoutButton); } } diff --git a/tests/specs/login.spec.ts b/tests/specs/login.spec.ts index 2f86dd8..b4281a8 100644 --- a/tests/specs/login.spec.ts +++ b/tests/specs/login.spec.ts @@ -1,10 +1,12 @@ import { test } from '../utilities/setup'; // Import extended test import testData from '../data/example.json'; +import locators from '../locators/locators.json'; +import { navigateTo, verifyTextContains, verifyTitle } from '../utilities/helpers'; -test.beforeEach('Verify Header and title', async ({ examplePage }) => { - await examplePage.navigateTo(testData.BASE_URL); - await examplePage.verifyHeader(); - await examplePage.verifyTitle() +test.beforeEach('Verify Header and title', async ({ page }) => { + await navigateTo(page, testData.BASE_URL); + await verifyTextContains(page, locators.homePageLogo, testData.Header); + await verifyTitle(page, testData.pageTitle); }); test('Login Success and verify', async ({ loginPage }) => { diff --git a/tests/utilities/helpers.ts b/tests/utilities/helpers.ts new file mode 100644 index 0000000..f265846 --- /dev/null +++ b/tests/utilities/helpers.ts @@ -0,0 +1,46 @@ +import { Page, expect } from '@playwright/test'; + +/** + * Navigate to a specified URL + */ +export async function navigateTo(page: Page, url: string): Promise { + await page.goto(url); +} + +/** + * Click a button or element by selector + */ +export async function clickButton(page: Page, selector: string): Promise { + await page.click(selector); +} + +/** + * Enter text into an input field + */ +export async function enterText(page: Page, selector: string, text: string): Promise { + await page.fill(selector, text); +} + +/** + * Get text content from an element + */ +export async function getText(page: Page, selector: string): Promise { + const text = await page.textContent(selector); + return text ?? ''; +} + +/** + * Verify text content contains expected value + */ +export async function verifyTextContains(page: Page, selector: string, expectedText: string): Promise { + const text = await page.textContent(selector); + expect(text).toContain(expectedText); +} + +/** + * Verify page title contains expected value + */ +export async function verifyTitle(page: Page, expectedTitle: string): Promise { + const title = await page.title(); + expect(title).toContain(expectedTitle); +} diff --git a/tests/utilities/setup.ts b/tests/utilities/setup.ts index 6605609..78506a1 100644 --- a/tests/utilities/setup.ts +++ b/tests/utilities/setup.ts @@ -1,23 +1,14 @@ import { test as base, expect } from '@playwright/test'; -import { ExamplePage } from '../pageObejcts/example'; import { LoginPage } from '../pageObejcts/login'; // Extend Playwright's test object const test = base.extend<{ - examplePage: ExamplePage; loginPage: LoginPage; - }>({ - examplePage: async ({ page }, use) => { - const examplePage = new ExamplePage(page); - await use(examplePage); - }, - loginPage: async ({ page }, use) => { const loginPage = new LoginPage(page); await use(loginPage); }, - }); export { test, expect }; From d5b455bda2115fa6f7d726eaa7324e657f61d557 Mon Sep 17 00:00:00 2001 From: Zaafir Dar Date: Wed, 19 Nov 2025 11:00:49 -0500 Subject: [PATCH 5/6] add reporting --- .gitignore | 5 +++ package.json | 9 ++++- playwright.config.ts | 28 +++++++++++----- tests/pageObejcts/login.ts | 5 ++- tests/specs/login.spec.ts | 4 +-- tests/utilities/helpers.ts | 67 ++++++++++++++++---------------------- 6 files changed, 64 insertions(+), 54 deletions(-) diff --git a/.gitignore b/.gitignore index ae383f1..19065a7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,8 @@ node_modules/ /playwright-report/ /blob-report/ /playwright/.cache/ + +# Test artifacts +*.mp4 +*.webm +test-results.json diff --git a/package.json b/package.json index 6a49078..b0b032f 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,14 @@ "description": "", "main": "index.js", "scripts": { - "test": "playwright test login.spec.ts --headed" + "test": "playwright test login.spec.ts --headed", + "test:all": "playwright test", + "test:chromium": "playwright test --project=chromium", + "test:firefox": "playwright test --project=firefox", + "test:webkit": "playwright test --project=webkit", + "test:headed": "playwright test --headed", + "test:debug": "playwright test --debug", + "report": "playwright show-report" }, "repository": { "type": "git", diff --git a/playwright.config.ts b/playwright.config.ts index 91850c9..1ef3d5e 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -22,7 +22,11 @@ export default defineConfig({ /* Opt out of parallel tests on CI. */ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'html', + reporter: [ + ['html', { open: 'never' }], + ['list'], + ['json', { outputFile: 'test-results/test-results.json' }], + ], /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ @@ -30,6 +34,12 @@ export default defineConfig({ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', + + /* Screenshot on failure */ + screenshot: 'only-on-failure', + + /* Video on failure */ + video: 'retain-on-failure', }, /* Configure projects for major browsers */ @@ -39,15 +49,15 @@ export default defineConfig({ use: { ...devices['Desktop Chrome'] }, }, - // { - // name: 'firefox', - // use: { ...devices['Desktop Firefox'] }, - // }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, - // { - // name: 'webkit', - // use: { ...devices['Desktop Safari'] }, - // }, + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, /* Test against mobile viewports. */ // { diff --git a/tests/pageObejcts/login.ts b/tests/pageObejcts/login.ts index c9b6dec..b92d9c6 100644 --- a/tests/pageObejcts/login.ts +++ b/tests/pageObejcts/login.ts @@ -1,6 +1,5 @@ -import { Page, expect } from '@playwright/test'; +import { Page, enterText, clickButton, verifyElementVisible } from '../utilities/helpers'; import loginLocator from '../locators/locators.json'; -import { enterText, clickButton } from '../utilities/helpers'; export class LoginPage { @@ -16,7 +15,7 @@ export class LoginPage { } async verifyLoginSucess() { - await expect(this.page.locator(loginLocator.loginSuccessHeader)).toBeVisible(); + await verifyElementVisible(this.page, loginLocator.loginSuccessHeader); } async userLogout() { diff --git a/tests/specs/login.spec.ts b/tests/specs/login.spec.ts index b4281a8..ae0e9d4 100644 --- a/tests/specs/login.spec.ts +++ b/tests/specs/login.spec.ts @@ -1,7 +1,7 @@ -import { test } from '../utilities/setup'; // Import extended test +import { test } from '../utilities/setup'; +import { navigateTo, verifyTextContains, verifyTitle } from '../utilities/helpers'; import testData from '../data/example.json'; import locators from '../locators/locators.json'; -import { navigateTo, verifyTextContains, verifyTitle } from '../utilities/helpers'; test.beforeEach('Verify Header and title', async ({ page }) => { await navigateTo(page, testData.BASE_URL); diff --git a/tests/utilities/helpers.ts b/tests/utilities/helpers.ts index f265846..ae95367 100644 --- a/tests/utilities/helpers.ts +++ b/tests/utilities/helpers.ts @@ -1,46 +1,35 @@ import { Page, expect } from '@playwright/test'; -/** - * Navigate to a specified URL - */ -export async function navigateTo(page: Page, url: string): Promise { +export { Page, expect }; + +// Navigation +export const navigateTo = async (page: Page, url: string): Promise => { await page.goto(url); -} +}; -/** - * Click a button or element by selector - */ -export async function clickButton(page: Page, selector: string): Promise { +// Interactions +export const clickButton = async (page: Page, selector: string): Promise => { await page.click(selector); -} +}; -/** - * Enter text into an input field - */ -export async function enterText(page: Page, selector: string, text: string): Promise { +export const enterText = async (page: Page, selector: string, text: string): Promise => { await page.fill(selector, text); -} - -/** - * Get text content from an element - */ -export async function getText(page: Page, selector: string): Promise { - const text = await page.textContent(selector); - return text ?? ''; -} - -/** - * Verify text content contains expected value - */ -export async function verifyTextContains(page: Page, selector: string, expectedText: string): Promise { - const text = await page.textContent(selector); - expect(text).toContain(expectedText); -} - -/** - * Verify page title contains expected value - */ -export async function verifyTitle(page: Page, expectedTitle: string): Promise { - const title = await page.title(); - expect(title).toContain(expectedTitle); -} +}; + +// Getters +export const getText = async (page: Page, selector: string): Promise => { + return (await page.textContent(selector)) ?? ''; +}; + +// Assertions +export const verifyTextContains = async (page: Page, selector: string, expectedText: string): Promise => { + expect(await page.textContent(selector)).toContain(expectedText); +}; + +export const verifyTitle = async (page: Page, expectedTitle: string): Promise => { + expect(await page.title()).toContain(expectedTitle); +}; + +export const verifyElementVisible = async (page: Page, selector: string): Promise => { + await expect(page.locator(selector)).toBeVisible(); +}; From cfd7d46918e53989cf31ef327eca6e14f0219e82 Mon Sep 17 00:00:00 2001 From: Zaafir Dar Date: Wed, 19 Nov 2025 11:05:17 -0500 Subject: [PATCH 6/6] updating yml --- .github/workflows/playwright.yml | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 3eb1314..1d6fce1 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -4,10 +4,12 @@ on: branches: [ main, master ] pull_request: branches: [ main, master ] + types: [ opened, synchronize, closed ] jobs: test: timeout-minutes: 60 runs-on: ubuntu-latest + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.merged == true) steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -19,9 +21,26 @@ jobs: run: npx playwright install --with-deps - name: Run Playwright tests run: npx playwright test - - uses: actions/upload-artifact@v4 - if: ${{ !cancelled() }} + - name: Upload HTML Report + uses: actions/upload-artifact@v4 + if: always() with: - name: playwright-report + name: playwright-html-report path: playwright-report/ retention-days: 30 + - name: Upload Test Results JSON + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-results-json + path: test-results/test-results.json + retention-days: 30 + - name: Upload Screenshots and Videos + uses: actions/upload-artifact@v4 + if: failure() + with: + name: test-artifacts + path: | + test-results/**/*.png + test-results/**/*.webm + retention-days: 30