From 09d9ae80f0daf8e65736e3af100c50af7bedf72f Mon Sep 17 00:00:00 2001 From: Solange Calabrese Date: Mon, 7 Sep 2026 09:21:14 -0300 Subject: [PATCH 1/6] Add Home page e2e coverage for hero and feature cards --- playwright.config.ts | 2 +- tests/fixtures/index.ts | 12 +++++++ tests/home.spec.ts | 63 ++++++++++++++++++++++++++++++++++ tests/pages/base.page.ts | 17 ++++++++++ tests/pages/home.page.ts | 71 +++++++++++++++++++++++++++++++++++++++ ui/src/pages/HomePage.tsx | 1 + 6 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/index.ts create mode 100644 tests/home.spec.ts create mode 100644 tests/pages/base.page.ts create mode 100644 tests/pages/home.page.ts diff --git a/playwright.config.ts b/playwright.config.ts index a7bd8af4..f26a4911 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -64,7 +64,7 @@ export default defineConfig({ ], use: { - baseURL: rc.baseUrl || undefined, + baseURL: rc.baseUrl || localWebServerUrl, headless: rc.headed ? false : true, trace: rc.trace ?? 'on-first-retry', screenshot: rc.screenshot ?? 'only-on-failure', diff --git a/tests/fixtures/index.ts b/tests/fixtures/index.ts new file mode 100644 index 00000000..9f8d860a --- /dev/null +++ b/tests/fixtures/index.ts @@ -0,0 +1,12 @@ +import { test as base } from '@playwright/test'; +import { HomePage } from '../pages/home.page'; + +export const test = base.extend<{ + homePage: HomePage; +}>({ + homePage: async ({ page }, use) => { + await use(new HomePage(page)); + }, +}); + +export { expect } from '@playwright/test'; diff --git a/tests/home.spec.ts b/tests/home.spec.ts new file mode 100644 index 00000000..357ad05d --- /dev/null +++ b/tests/home.spec.ts @@ -0,0 +1,63 @@ +import { test, expect } from './fixtures'; + +test.describe('Home Page', () => { + + test.beforeEach(async ({ homePage }) => { + await homePage.goto(); + }); + + test.describe('Hero section', () => { + + test('should display the title', async ({ homePage }) => { + await expect(homePage.heroHeading).toBeVisible(); + }); + + test('should display the sub title', async ({ homePage }) => { + await expect(homePage.subTitle1).toBeVisible(); + }); + + test('should display the sub title 2', async ({ homePage }) => { + await expect(homePage.subTitle2).toBeVisible(); + }); + + }); + + test.describe('Features Cards', () => { + + test('should display Self-Healing Tests Cards', async ({ homePage }) => { + await expect(homePage.selfHealingTestsCard).toBeVisible(); + await expect(homePage.selfHealingTestsCardTitle).toBeVisible(); + await expect(homePage.selfHealingTestsCardDescription).toBeVisible(); + await expect(homePage.selfHealingTestCardIcon).toBeVisible(); + }); + + test('should display Smart Data Gen Card', async ({ homePage }) => { + await expect(homePage.smartDataGenCard).toBeVisible(); + await expect(homePage.smartDataGenCardTitle).toBeVisible(); + await expect(homePage.smartDataGenCardDescription).toBeVisible(); + await expect(homePage.smartDataGenCardIcon).toBeVisible(); + }); + + test('should display Auto Bug Triage Card', async ({ homePage }) => { + await expect(homePage.autoBugTriageCard).toBeVisible(); + await expect(homePage.autoBugTriageCardTitle).toBeVisible(); + await expect(homePage.autoBugTriageCardDescription).toBeVisible(); + await expect(homePage.autoBugTriageCardIcon).toBeVisible(); + }); + + test('should display Claude Powered Card', async ({ homePage }) => { + await expect(homePage.claudePoweredCard).toBeVisible(); + await expect(homePage.claudePoweredCardTitle).toBeVisible(); + await expect(homePage.claudePoweredCardDescription).toBeVisible(); + await expect(homePage.claudePoweredCardIcon).toBeVisible(); + }); + + test('should display Daily Missions Card', async ({ homePage }) => { + await expect(homePage.dailyMissionsCard).toBeVisible(); + await expect(homePage.dailyMissionsCardTitle).toBeVisible(); + await expect(homePage.dailyMissionsCardDescription).toBeVisible(); + }); + + }); + +}); diff --git a/tests/pages/base.page.ts b/tests/pages/base.page.ts new file mode 100644 index 00000000..554211d1 --- /dev/null +++ b/tests/pages/base.page.ts @@ -0,0 +1,17 @@ +import { type Page } from '@playwright/test'; + +export class BasePage { + + readonly page: Page; + readonly url: string; + + constructor(page: Page, url: string) { + this.page = page; + this.url = url; + } + + async goto() { + await this.page.goto(this.url); + } + +} diff --git a/tests/pages/home.page.ts b/tests/pages/home.page.ts new file mode 100644 index 00000000..1c63f705 --- /dev/null +++ b/tests/pages/home.page.ts @@ -0,0 +1,71 @@ +import { BasePage } from "./base.page"; +import { type Locator, type Page } from '@playwright/test'; + +export class HomePage extends BasePage { + + // Hero Section + readonly heroHeading: Locator; + readonly subTitle1: Locator; + readonly subTitle2: Locator; + + // Features Cards + readonly selfHealingTestsCard: Locator; + readonly selfHealingTestsCardTitle: Locator; + readonly selfHealingTestsCardDescription: Locator; + readonly selfHealingTestCardIcon: Locator; + readonly smartDataGenCard: Locator; + readonly smartDataGenCardTitle: Locator; + readonly smartDataGenCardDescription: Locator; + readonly smartDataGenCardIcon: Locator; + readonly autoBugTriageCard: Locator; + readonly autoBugTriageCardTitle: Locator; + readonly autoBugTriageCardDescription: Locator; + readonly autoBugTriageCardIcon: Locator; + readonly claudePoweredCard: Locator; + readonly claudePoweredCardTitle: Locator; + readonly claudePoweredCardDescription: Locator; + readonly claudePoweredCardIcon: Locator; + readonly dailyMissionsCard: Locator; + readonly dailyMissionsCardTitle: Locator; + readonly dailyMissionsCardDescription: Locator; + readonly dailyMissionsCardIcon: Locator; + + + constructor(page: Page) { + + super(page, '/'); + + // Hero Section + this.heroHeading = page.getByRole('heading', { name: 'OPEN-QA', exact: true }); + this.subTitle1 = page.getByText('The QA Toolkit AI Is Missing', { exact: true }); + this.subTitle2 = page.getByText('Ready to install, verified, and open source', { exact: true }); + + // Features Cards + this.selfHealingTestsCard = page.getByTestId('Self-Healing Tests'); + this.selfHealingTestsCardTitle = this.selfHealingTestsCard.getByRole('heading', { name: 'Self-Healing Tests', exact: true }); + this.selfHealingTestsCardDescription = this.selfHealingTestsCard.getByText('AI suggests new locators when your UI changes — no more brittle selectors.', { exact: true }); + this.selfHealingTestCardIcon = this.selfHealingTestsCard.getByTestId('BuildOutlinedIcon'); + + this.smartDataGenCard = page.getByTestId('Smart Data Gen'); + this.smartDataGenCardTitle = this.smartDataGenCard.getByRole('heading', { name: 'Smart Data Gen', exact: true }); + this.smartDataGenCardDescription = this.smartDataGenCard.getByText('Generates SQLi, XSS, nulls, RTL text and boundary payloads automatically.', { exact: true }); + this.smartDataGenCardIcon = this.smartDataGenCard.getByTestId('CasinoOutlinedIcon'); + + this.autoBugTriageCard = page.getByTestId('Auto Bug Triage'); + this.autoBugTriageCardTitle = this.autoBugTriageCard.getByRole('heading', { name: 'Auto Bug Triage', exact: true }); + this.autoBugTriageCardDescription = this.autoBugTriageCard.getByText('Reads error logs and writes Jira-ready bug reports with root-cause analysis.', { exact: true }); + this.autoBugTriageCardIcon = this.autoBugTriageCard.getByTestId('BugReportOutlinedIcon'); + + this.claudePoweredCard = page.getByTestId('Claude-Powered'); + this.claudePoweredCardTitle = this.claudePoweredCard.getByRole('heading', { name: 'Claude-Powered', exact: true }); + this.claudePoweredCardDescription = this.claudePoweredCard.getByText('Every agent and skill is powered by Anthropic Claude. MOCK mode requires no API key.', { exact: true }); + this.claudePoweredCardIcon = this.claudePoweredCard.getByTestId('SmartToyOutlinedIcon'); + + this.dailyMissionsCard = page.getByTestId('Daily Missions'); + this.dailyMissionsCardTitle = this.dailyMissionsCard.getByRole('heading', { name: 'Daily Missions', exact: true }); + this.dailyMissionsCardDescription = this.dailyMissionsCard.getByText('Gamified QA challenges that change every day. Earn XP, build streaks, sharpen your skills.', { exact: true }); + this.dailyMissionsCardIcon = this.dailyMissionsCard.getByTestId('AssignmentOutlinedIcon'); + + } + +} diff --git a/ui/src/pages/HomePage.tsx b/ui/src/pages/HomePage.tsx index 9c9effdf..1cf4b1ea 100644 --- a/ui/src/pages/HomePage.tsx +++ b/ui/src/pages/HomePage.tsx @@ -254,6 +254,7 @@ export default function HomePage() { {featureKeys.map(({ Icon, titleKey, descKey, color }) => (
From dc51193680e905872f9b02eb47a8da7c8a1dde25 Mon Sep 17 00:00:00 2001 From: Solange Calabrese Date: Mon, 7 Sep 2026 11:08:06 -0300 Subject: [PATCH 2/6] Add navbar GitHub coverage and stabilize Home feature locators --- tests/data/navbar.data.ts | 1 + tests/fixtures/index.ts | 6 +++ tests/home.spec.ts | 78 +++++++++++++++++++++++++++----------- tests/navbar.spec.ts | 30 +++++++++++++++ tests/pages/home.page.ts | 20 +++++----- tests/pages/navbar.page.ts | 17 +++++++++ ui/src/pages/HomePage.tsx | 6 ++- 7 files changed, 123 insertions(+), 35 deletions(-) create mode 100644 tests/data/navbar.data.ts create mode 100644 tests/navbar.spec.ts create mode 100644 tests/pages/navbar.page.ts diff --git a/tests/data/navbar.data.ts b/tests/data/navbar.data.ts new file mode 100644 index 00000000..aacb7c77 --- /dev/null +++ b/tests/data/navbar.data.ts @@ -0,0 +1 @@ +export const GITHUB_REPO_URL = 'https://github.com/MyNameIsEdi/open-qa'; diff --git a/tests/fixtures/index.ts b/tests/fixtures/index.ts index 9f8d860a..264b47ef 100644 --- a/tests/fixtures/index.ts +++ b/tests/fixtures/index.ts @@ -1,12 +1,18 @@ import { test as base } from '@playwright/test'; import { HomePage } from '../pages/home.page'; +import { NavbarPage } from '../pages/navbar.page'; export const test = base.extend<{ homePage: HomePage; + navbar: NavbarPage; }>({ homePage: async ({ page }, use) => { + await page.goto('/'); await use(new HomePage(page)); }, + navbar: async ({ page }, use) => { + await use(new NavbarPage(page)); + } }); export { expect } from '@playwright/test'; diff --git a/tests/home.spec.ts b/tests/home.spec.ts index 357ad05d..c9b73030 100644 --- a/tests/home.spec.ts +++ b/tests/home.spec.ts @@ -2,10 +2,6 @@ import { test, expect } from './fixtures'; test.describe('Home Page', () => { - test.beforeEach(async ({ homePage }) => { - await homePage.goto(); - }); - test.describe('Hero section', () => { test('should display the title', async ({ homePage }) => { @@ -25,37 +21,73 @@ test.describe('Home Page', () => { test.describe('Features Cards', () => { test('should display Self-Healing Tests Cards', async ({ homePage }) => { - await expect(homePage.selfHealingTestsCard).toBeVisible(); - await expect(homePage.selfHealingTestsCardTitle).toBeVisible(); - await expect(homePage.selfHealingTestsCardDescription).toBeVisible(); - await expect(homePage.selfHealingTestCardIcon).toBeVisible(); + + await test.step('should display Self-Healing Tests Cards', async () => { + await expect(homePage.selfHealingTestsCard).toBeVisible(); + }); + + await test.step('should display Self-Healing Tests Cards Title, description and icon', async () => { + await expect.soft(homePage.selfHealingTestsCardTitle).toBeVisible(); + await expect.soft(homePage.selfHealingTestsCardDescription).toBeVisible(); + await expect.soft(homePage.selfHealingTestCardIcon).toBeVisible(); + }); + }); test('should display Smart Data Gen Card', async ({ homePage }) => { - await expect(homePage.smartDataGenCard).toBeVisible(); - await expect(homePage.smartDataGenCardTitle).toBeVisible(); - await expect(homePage.smartDataGenCardDescription).toBeVisible(); - await expect(homePage.smartDataGenCardIcon).toBeVisible(); + + await test.step('should display Smart Data Gen Card', async () => { + await expect(homePage.smartDataGenCard).toBeVisible(); + }); + + await test.step('should display Smart Data Gen Card Title, description and icon', async () => { + await expect.soft(homePage.smartDataGenCardTitle).toBeVisible(); + await expect.soft(homePage.smartDataGenCardDescription).toBeVisible(); + await expect.soft(homePage.smartDataGenCardIcon).toBeVisible(); + }); + }); test('should display Auto Bug Triage Card', async ({ homePage }) => { - await expect(homePage.autoBugTriageCard).toBeVisible(); - await expect(homePage.autoBugTriageCardTitle).toBeVisible(); - await expect(homePage.autoBugTriageCardDescription).toBeVisible(); - await expect(homePage.autoBugTriageCardIcon).toBeVisible(); + + await test.step('should display Auto Bug Triage Card', async () => { + await expect(homePage.autoBugTriageCard).toBeVisible(); + }); + + await test.step('should display Auto Bug Triage Card Title, description and icon', async () => { + await expect.soft(homePage.autoBugTriageCardTitle).toBeVisible(); + await expect.soft(homePage.autoBugTriageCardDescription).toBeVisible(); + await expect.soft(homePage.autoBugTriageCardIcon).toBeVisible(); + }); + }); test('should display Claude Powered Card', async ({ homePage }) => { - await expect(homePage.claudePoweredCard).toBeVisible(); - await expect(homePage.claudePoweredCardTitle).toBeVisible(); - await expect(homePage.claudePoweredCardDescription).toBeVisible(); - await expect(homePage.claudePoweredCardIcon).toBeVisible(); + + await test.step('should display Claude Powered Card', async () => { + await expect(homePage.claudePoweredCard).toBeVisible(); + }); + + await test.step('should display Claude Powered Card Title, description and icon', async () => { + await expect.soft(homePage.claudePoweredCardTitle).toBeVisible(); + await expect.soft(homePage.claudePoweredCardDescription).toBeVisible(); + await expect.soft(homePage.claudePoweredCardIcon).toBeVisible(); + }); + }); test('should display Daily Missions Card', async ({ homePage }) => { - await expect(homePage.dailyMissionsCard).toBeVisible(); - await expect(homePage.dailyMissionsCardTitle).toBeVisible(); - await expect(homePage.dailyMissionsCardDescription).toBeVisible(); + + await test.step('should display Daily Missions Card', async () => { + await expect(homePage.dailyMissionsCard).toBeVisible(); + }); + + await test.step('should display Daily Missions Card Title, description and icon', async () => { + await expect.soft(homePage.dailyMissionsCardTitle).toBeVisible(); + await expect.soft(homePage.dailyMissionsCardDescription).toBeVisible(); + await expect.soft(homePage.dailyMissionsCardIcon).toBeVisible(); + }); + }); }); diff --git a/tests/navbar.spec.ts b/tests/navbar.spec.ts new file mode 100644 index 00000000..8431477f --- /dev/null +++ b/tests/navbar.spec.ts @@ -0,0 +1,30 @@ +import { test, expect } from './fixtures'; +import { GITHUB_REPO_URL } from './data/navbar.data'; + +test.describe('Test Navbar from Home Page', () => { + + test('should display github link', async ({ homePage: _homePage, navbar }) => { + await test.step('github link is visible', async () => { + await expect(navbar.githubLink).toBeVisible(); + }); + await test.step('github link attributes href, target and rel', async () => { + await expect.soft(navbar.githubLink).toHaveAttribute('href', GITHUB_REPO_URL); + await expect.soft(navbar.githubLink).toHaveAttribute('target', '_blank'); + await expect.soft(navbar.githubLink).toHaveAttribute('rel', /noopener/); + }); + }); + + test('should navigate to github repository', async ({ homePage: _homePage, navbar, page }) => { + const newTabPromise = page.context().waitForEvent('page'); + await navbar.clickGithubLink(); + const githubPage = await newTabPromise; + await expect(githubPage).toHaveURL(/github\.com\/MyNameIsEdi\/open-qa/); + await githubPage.close(); + }); + + test('GitHub link is hidden on mobile', async ({ homePage, navbar, page }) => { + await page.setViewportSize({ width: 375, height: 667 }); + await homePage.goto(); + await expect(navbar.githubLink).toBeHidden(); + }); +}); diff --git a/tests/pages/home.page.ts b/tests/pages/home.page.ts index 1c63f705..41a9388c 100644 --- a/tests/pages/home.page.ts +++ b/tests/pages/home.page.ts @@ -41,30 +41,30 @@ export class HomePage extends BasePage { this.subTitle2 = page.getByText('Ready to install, verified, and open source', { exact: true }); // Features Cards - this.selfHealingTestsCard = page.getByTestId('Self-Healing Tests'); + this.selfHealingTestsCard = page.getByTestId('home.feature_healing_title'); this.selfHealingTestsCardTitle = this.selfHealingTestsCard.getByRole('heading', { name: 'Self-Healing Tests', exact: true }); this.selfHealingTestsCardDescription = this.selfHealingTestsCard.getByText('AI suggests new locators when your UI changes — no more brittle selectors.', { exact: true }); - this.selfHealingTestCardIcon = this.selfHealingTestsCard.getByTestId('BuildOutlinedIcon'); + this.selfHealingTestCardIcon = this.selfHealingTestsCard.getByTestId('home.feature_healing_title_icon'); - this.smartDataGenCard = page.getByTestId('Smart Data Gen'); + this.smartDataGenCard = page.getByTestId('home.feature_datagen_title'); this.smartDataGenCardTitle = this.smartDataGenCard.getByRole('heading', { name: 'Smart Data Gen', exact: true }); this.smartDataGenCardDescription = this.smartDataGenCard.getByText('Generates SQLi, XSS, nulls, RTL text and boundary payloads automatically.', { exact: true }); - this.smartDataGenCardIcon = this.smartDataGenCard.getByTestId('CasinoOutlinedIcon'); + this.smartDataGenCardIcon = this.smartDataGenCard.getByTestId('home.feature_datagen_title_icon'); - this.autoBugTriageCard = page.getByTestId('Auto Bug Triage'); + this.autoBugTriageCard = page.getByTestId('home.feature_triage_title'); this.autoBugTriageCardTitle = this.autoBugTriageCard.getByRole('heading', { name: 'Auto Bug Triage', exact: true }); this.autoBugTriageCardDescription = this.autoBugTriageCard.getByText('Reads error logs and writes Jira-ready bug reports with root-cause analysis.', { exact: true }); - this.autoBugTriageCardIcon = this.autoBugTriageCard.getByTestId('BugReportOutlinedIcon'); + this.autoBugTriageCardIcon = this.autoBugTriageCard.getByTestId('home.feature_triage_title_icon'); - this.claudePoweredCard = page.getByTestId('Claude-Powered'); + this.claudePoweredCard = page.getByTestId('home.feature_claude_title'); this.claudePoweredCardTitle = this.claudePoweredCard.getByRole('heading', { name: 'Claude-Powered', exact: true }); this.claudePoweredCardDescription = this.claudePoweredCard.getByText('Every agent and skill is powered by Anthropic Claude. MOCK mode requires no API key.', { exact: true }); - this.claudePoweredCardIcon = this.claudePoweredCard.getByTestId('SmartToyOutlinedIcon'); + this.claudePoweredCardIcon = this.claudePoweredCard.getByTestId('home.feature_claude_title_icon'); - this.dailyMissionsCard = page.getByTestId('Daily Missions'); + this.dailyMissionsCard = page.getByTestId('home.feature_missions_title'); this.dailyMissionsCardTitle = this.dailyMissionsCard.getByRole('heading', { name: 'Daily Missions', exact: true }); this.dailyMissionsCardDescription = this.dailyMissionsCard.getByText('Gamified QA challenges that change every day. Earn XP, build streaks, sharpen your skills.', { exact: true }); - this.dailyMissionsCardIcon = this.dailyMissionsCard.getByTestId('AssignmentOutlinedIcon'); + this.dailyMissionsCardIcon = this.dailyMissionsCard.getByTestId('home.feature_missions_title_icon'); } diff --git a/tests/pages/navbar.page.ts b/tests/pages/navbar.page.ts new file mode 100644 index 00000000..26335db4 --- /dev/null +++ b/tests/pages/navbar.page.ts @@ -0,0 +1,17 @@ +import { BasePage } from "./base.page"; +import { type Locator, type Page } from '@playwright/test'; + +export class NavbarPage extends BasePage { + + readonly githubLink: Locator; + + constructor(page: Page) { + super(page, '/'); + this.githubLink = page.getByRole('link', { name: 'GitHub' }); + } + + async clickGithubLink() { + await this.githubLink.click(); + } + +} diff --git a/ui/src/pages/HomePage.tsx b/ui/src/pages/HomePage.tsx index 1cf4b1ea..b19d11c2 100644 --- a/ui/src/pages/HomePage.tsx +++ b/ui/src/pages/HomePage.tsx @@ -254,11 +254,13 @@ export default function HomePage() { {featureKeys.map(({ Icon, titleKey, descKey, color }) => (
- +

{t(titleKey)}

From 9ace2a276d59d48f0b8945d0dffc526c15ca4440 Mon Sep 17 00:00:00 2001 From: Solange Calabrese Date: Mon, 7 Sep 2026 11:52:55 -0300 Subject: [PATCH 3/6] Format Home and navbar Playwright files with Prettier --- tests/fixtures/index.ts | 2 +- tests/home.spec.ts | 28 -------------- tests/navbar.spec.ts | 3 -- tests/pages/base.page.ts | 2 - tests/pages/home.page.ts | 77 +++++++++++++++++++++++++++----------- tests/pages/navbar.page.ts | 4 +- 6 files changed, 58 insertions(+), 58 deletions(-) diff --git a/tests/fixtures/index.ts b/tests/fixtures/index.ts index 264b47ef..157bae4c 100644 --- a/tests/fixtures/index.ts +++ b/tests/fixtures/index.ts @@ -12,7 +12,7 @@ export const test = base.extend<{ }, navbar: async ({ page }, use) => { await use(new NavbarPage(page)); - } + }, }); export { expect } from '@playwright/test'; diff --git a/tests/home.spec.ts b/tests/home.spec.ts index c9b73030..80578c84 100644 --- a/tests/home.spec.ts +++ b/tests/home.spec.ts @@ -1,95 +1,67 @@ import { test, expect } from './fixtures'; test.describe('Home Page', () => { - test.describe('Hero section', () => { - test('should display the title', async ({ homePage }) => { await expect(homePage.heroHeading).toBeVisible(); }); - test('should display the sub title', async ({ homePage }) => { await expect(homePage.subTitle1).toBeVisible(); }); - test('should display the sub title 2', async ({ homePage }) => { await expect(homePage.subTitle2).toBeVisible(); }); - }); - test.describe('Features Cards', () => { - test('should display Self-Healing Tests Cards', async ({ homePage }) => { - await test.step('should display Self-Healing Tests Cards', async () => { await expect(homePage.selfHealingTestsCard).toBeVisible(); }); - await test.step('should display Self-Healing Tests Cards Title, description and icon', async () => { await expect.soft(homePage.selfHealingTestsCardTitle).toBeVisible(); await expect.soft(homePage.selfHealingTestsCardDescription).toBeVisible(); await expect.soft(homePage.selfHealingTestCardIcon).toBeVisible(); }); - }); - test('should display Smart Data Gen Card', async ({ homePage }) => { - await test.step('should display Smart Data Gen Card', async () => { await expect(homePage.smartDataGenCard).toBeVisible(); }); - await test.step('should display Smart Data Gen Card Title, description and icon', async () => { await expect.soft(homePage.smartDataGenCardTitle).toBeVisible(); await expect.soft(homePage.smartDataGenCardDescription).toBeVisible(); await expect.soft(homePage.smartDataGenCardIcon).toBeVisible(); }); - }); - test('should display Auto Bug Triage Card', async ({ homePage }) => { - await test.step('should display Auto Bug Triage Card', async () => { await expect(homePage.autoBugTriageCard).toBeVisible(); }); - await test.step('should display Auto Bug Triage Card Title, description and icon', async () => { await expect.soft(homePage.autoBugTriageCardTitle).toBeVisible(); await expect.soft(homePage.autoBugTriageCardDescription).toBeVisible(); await expect.soft(homePage.autoBugTriageCardIcon).toBeVisible(); }); - }); - test('should display Claude Powered Card', async ({ homePage }) => { - await test.step('should display Claude Powered Card', async () => { await expect(homePage.claudePoweredCard).toBeVisible(); }); - await test.step('should display Claude Powered Card Title, description and icon', async () => { await expect.soft(homePage.claudePoweredCardTitle).toBeVisible(); await expect.soft(homePage.claudePoweredCardDescription).toBeVisible(); await expect.soft(homePage.claudePoweredCardIcon).toBeVisible(); }); - }); - test('should display Daily Missions Card', async ({ homePage }) => { - await test.step('should display Daily Missions Card', async () => { await expect(homePage.dailyMissionsCard).toBeVisible(); }); - await test.step('should display Daily Missions Card Title, description and icon', async () => { await expect.soft(homePage.dailyMissionsCardTitle).toBeVisible(); await expect.soft(homePage.dailyMissionsCardDescription).toBeVisible(); await expect.soft(homePage.dailyMissionsCardIcon).toBeVisible(); }); - }); - }); - }); diff --git a/tests/navbar.spec.ts b/tests/navbar.spec.ts index 8431477f..5a09a402 100644 --- a/tests/navbar.spec.ts +++ b/tests/navbar.spec.ts @@ -2,7 +2,6 @@ import { test, expect } from './fixtures'; import { GITHUB_REPO_URL } from './data/navbar.data'; test.describe('Test Navbar from Home Page', () => { - test('should display github link', async ({ homePage: _homePage, navbar }) => { await test.step('github link is visible', async () => { await expect(navbar.githubLink).toBeVisible(); @@ -13,7 +12,6 @@ test.describe('Test Navbar from Home Page', () => { await expect.soft(navbar.githubLink).toHaveAttribute('rel', /noopener/); }); }); - test('should navigate to github repository', async ({ homePage: _homePage, navbar, page }) => { const newTabPromise = page.context().waitForEvent('page'); await navbar.clickGithubLink(); @@ -21,7 +19,6 @@ test.describe('Test Navbar from Home Page', () => { await expect(githubPage).toHaveURL(/github\.com\/MyNameIsEdi\/open-qa/); await githubPage.close(); }); - test('GitHub link is hidden on mobile', async ({ homePage, navbar, page }) => { await page.setViewportSize({ width: 375, height: 667 }); await homePage.goto(); diff --git a/tests/pages/base.page.ts b/tests/pages/base.page.ts index 554211d1..2ee8f47b 100644 --- a/tests/pages/base.page.ts +++ b/tests/pages/base.page.ts @@ -1,7 +1,6 @@ import { type Page } from '@playwright/test'; export class BasePage { - readonly page: Page; readonly url: string; @@ -13,5 +12,4 @@ export class BasePage { async goto() { await this.page.goto(this.url); } - } diff --git a/tests/pages/home.page.ts b/tests/pages/home.page.ts index 41a9388c..0f6708af 100644 --- a/tests/pages/home.page.ts +++ b/tests/pages/home.page.ts @@ -1,8 +1,7 @@ -import { BasePage } from "./base.page"; +import { BasePage } from './base.page'; import { type Locator, type Page } from '@playwright/test'; export class HomePage extends BasePage { - // Hero Section readonly heroHeading: Locator; readonly subTitle1: Locator; @@ -30,9 +29,7 @@ export class HomePage extends BasePage { readonly dailyMissionsCardDescription: Locator; readonly dailyMissionsCardIcon: Locator; - constructor(page: Page) { - super(page, '/'); // Hero Section @@ -42,30 +39,68 @@ export class HomePage extends BasePage { // Features Cards this.selfHealingTestsCard = page.getByTestId('home.feature_healing_title'); - this.selfHealingTestsCardTitle = this.selfHealingTestsCard.getByRole('heading', { name: 'Self-Healing Tests', exact: true }); - this.selfHealingTestsCardDescription = this.selfHealingTestsCard.getByText('AI suggests new locators when your UI changes — no more brittle selectors.', { exact: true }); - this.selfHealingTestCardIcon = this.selfHealingTestsCard.getByTestId('home.feature_healing_title_icon'); + this.selfHealingTestsCardTitle = this.selfHealingTestsCard.getByRole('heading', { + name: 'Self-Healing Tests', + exact: true, + }); + this.selfHealingTestsCardDescription = this.selfHealingTestsCard.getByText( + 'AI suggests new locators when your UI changes — no more brittle selectors.', + { exact: true }, + ); + this.selfHealingTestCardIcon = this.selfHealingTestsCard.getByTestId( + 'home.feature_healing_title_icon', + ); this.smartDataGenCard = page.getByTestId('home.feature_datagen_title'); - this.smartDataGenCardTitle = this.smartDataGenCard.getByRole('heading', { name: 'Smart Data Gen', exact: true }); - this.smartDataGenCardDescription = this.smartDataGenCard.getByText('Generates SQLi, XSS, nulls, RTL text and boundary payloads automatically.', { exact: true }); - this.smartDataGenCardIcon = this.smartDataGenCard.getByTestId('home.feature_datagen_title_icon'); + this.smartDataGenCardTitle = this.smartDataGenCard.getByRole('heading', { + name: 'Smart Data Gen', + exact: true, + }); + this.smartDataGenCardDescription = this.smartDataGenCard.getByText( + 'Generates SQLi, XSS, nulls, RTL text and boundary payloads automatically.', + { exact: true }, + ); + this.smartDataGenCardIcon = this.smartDataGenCard.getByTestId( + 'home.feature_datagen_title_icon', + ); this.autoBugTriageCard = page.getByTestId('home.feature_triage_title'); - this.autoBugTriageCardTitle = this.autoBugTriageCard.getByRole('heading', { name: 'Auto Bug Triage', exact: true }); - this.autoBugTriageCardDescription = this.autoBugTriageCard.getByText('Reads error logs and writes Jira-ready bug reports with root-cause analysis.', { exact: true }); - this.autoBugTriageCardIcon = this.autoBugTriageCard.getByTestId('home.feature_triage_title_icon'); + this.autoBugTriageCardTitle = this.autoBugTriageCard.getByRole('heading', { + name: 'Auto Bug Triage', + exact: true, + }); + this.autoBugTriageCardDescription = this.autoBugTriageCard.getByText( + 'Reads error logs and writes Jira-ready bug reports with root-cause analysis.', + { exact: true }, + ); + this.autoBugTriageCardIcon = this.autoBugTriageCard.getByTestId( + 'home.feature_triage_title_icon', + ); this.claudePoweredCard = page.getByTestId('home.feature_claude_title'); - this.claudePoweredCardTitle = this.claudePoweredCard.getByRole('heading', { name: 'Claude-Powered', exact: true }); - this.claudePoweredCardDescription = this.claudePoweredCard.getByText('Every agent and skill is powered by Anthropic Claude. MOCK mode requires no API key.', { exact: true }); - this.claudePoweredCardIcon = this.claudePoweredCard.getByTestId('home.feature_claude_title_icon'); + this.claudePoweredCardTitle = this.claudePoweredCard.getByRole('heading', { + name: 'Claude-Powered', + exact: true, + }); + this.claudePoweredCardDescription = this.claudePoweredCard.getByText( + 'Every agent and skill is powered by Anthropic Claude. MOCK mode requires no API key.', + { exact: true }, + ); + this.claudePoweredCardIcon = this.claudePoweredCard.getByTestId( + 'home.feature_claude_title_icon', + ); this.dailyMissionsCard = page.getByTestId('home.feature_missions_title'); - this.dailyMissionsCardTitle = this.dailyMissionsCard.getByRole('heading', { name: 'Daily Missions', exact: true }); - this.dailyMissionsCardDescription = this.dailyMissionsCard.getByText('Gamified QA challenges that change every day. Earn XP, build streaks, sharpen your skills.', { exact: true }); - this.dailyMissionsCardIcon = this.dailyMissionsCard.getByTestId('home.feature_missions_title_icon'); - + this.dailyMissionsCardTitle = this.dailyMissionsCard.getByRole('heading', { + name: 'Daily Missions', + exact: true, + }); + this.dailyMissionsCardDescription = this.dailyMissionsCard.getByText( + 'Gamified QA challenges that change every day. Earn XP, build streaks, sharpen your skills.', + { exact: true }, + ); + this.dailyMissionsCardIcon = this.dailyMissionsCard.getByTestId( + 'home.feature_missions_title_icon', + ); } - } diff --git a/tests/pages/navbar.page.ts b/tests/pages/navbar.page.ts index 26335db4..06b93155 100644 --- a/tests/pages/navbar.page.ts +++ b/tests/pages/navbar.page.ts @@ -1,8 +1,7 @@ -import { BasePage } from "./base.page"; +import { BasePage } from './base.page'; import { type Locator, type Page } from '@playwright/test'; export class NavbarPage extends BasePage { - readonly githubLink: Locator; constructor(page: Page) { @@ -13,5 +12,4 @@ export class NavbarPage extends BasePage { async clickGithubLink() { await this.githubLink.click(); } - } From 021c13af4dc077a10672b01bc106559e7d82a786 Mon Sep 17 00:00:00 2001 From: Solange Calabrese Date: Tue, 22 Sep 2026 16:19:48 -0300 Subject: [PATCH 4/6] Wrap Home hero assertions in test.step --- tests/home.spec.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/home.spec.ts b/tests/home.spec.ts index 80578c84..fac83773 100644 --- a/tests/home.spec.ts +++ b/tests/home.spec.ts @@ -3,13 +3,19 @@ import { test, expect } from './fixtures'; test.describe('Home Page', () => { test.describe('Hero section', () => { test('should display the title', async ({ homePage }) => { - await expect(homePage.heroHeading).toBeVisible(); + await test.step('hero title is visible', async () => { + await expect(homePage.heroHeading).toBeVisible(); + }); }); test('should display the sub title', async ({ homePage }) => { - await expect(homePage.subTitle1).toBeVisible(); + await test.step('hero subtitle is visible', async () => { + await expect(homePage.subTitle1).toBeVisible(); + }); }); test('should display the sub title 2', async ({ homePage }) => { - await expect(homePage.subTitle2).toBeVisible(); + await test.step('hero second subtitle is visible', async () => { + await expect(homePage.subTitle2).toBeVisible(); + }); }); }); test.describe('Features Cards', () => { From b74f6fad79d14068e3d34b54bfcecc6836eaa4f7 Mon Sep 17 00:00:00 2001 From: Solange Calabrese Date: Tue, 22 Sep 2026 16:49:13 -0300 Subject: [PATCH 5/6] Wrap navbar GitHub navigation and mobile checks in test.step --- tests/navbar.spec.ts | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/tests/navbar.spec.ts b/tests/navbar.spec.ts index 5a09a402..aa7a3b0f 100644 --- a/tests/navbar.spec.ts +++ b/tests/navbar.spec.ts @@ -1,3 +1,4 @@ +import { type Page } from '@playwright/test'; import { test, expect } from './fixtures'; import { GITHUB_REPO_URL } from './data/navbar.data'; @@ -12,16 +13,33 @@ test.describe('Test Navbar from Home Page', () => { await expect.soft(navbar.githubLink).toHaveAttribute('rel', /noopener/); }); }); - test('should navigate to github repository', async ({ homePage: _homePage, navbar, page }) => { - const newTabPromise = page.context().waitForEvent('page'); - await navbar.clickGithubLink(); - const githubPage = await newTabPromise; - await expect(githubPage).toHaveURL(/github\.com\/MyNameIsEdi\/open-qa/); - await githubPage.close(); + test('should navigate to github repository', async ({ homePage: _homePage, navbar }) => { + let newTabPromise: Promise; + + await test.step('wait for the new tab', async () => { + newTabPromise = navbar.page.context().waitForEvent('page'); + }); + await test.step('click the GitHub link', async () => { + await navbar.clickGithubLink(); + }); + const githubPage = await test.step('GitHub tab opens the repository', async () => { + const openedPage = await newTabPromise; + await expect(openedPage).toHaveURL(/github\.com\/MyNameIsEdi\/open-qa/); + return openedPage; + }); + await test.step('close the GitHub tab', async () => { + await githubPage.close(); + }); }); - test('GitHub link is hidden on mobile', async ({ homePage, navbar, page }) => { - await page.setViewportSize({ width: 375, height: 667 }); - await homePage.goto(); - await expect(navbar.githubLink).toBeHidden(); + test('GitHub link is hidden on mobile', async ({ homePage, navbar }) => { + await test.step('set a mobile viewport', async () => { + await homePage.page.setViewportSize({ width: 375, height: 667 }); + }); + await test.step('reload Home at the mobile viewport', async () => { + await homePage.goto(); + }); + await test.step('GitHub link is hidden', async () => { + await expect(navbar.githubLink).toBeHidden(); + }); }); }); From 7f08d37ac3ee19b871e8e5313dc7f9106e3724ce Mon Sep 17 00:00:00 2001 From: Solange Calabrese Date: Tue, 22 Sep 2026 17:14:02 -0300 Subject: [PATCH 6/6] Fix TypeScript error in the navbar test --- tests/navbar.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/navbar.spec.ts b/tests/navbar.spec.ts index aa7a3b0f..a5072787 100644 --- a/tests/navbar.spec.ts +++ b/tests/navbar.spec.ts @@ -14,7 +14,7 @@ test.describe('Test Navbar from Home Page', () => { }); }); test('should navigate to github repository', async ({ homePage: _homePage, navbar }) => { - let newTabPromise: Promise; + let newTabPromise!: Promise; await test.step('wait for the new tab', async () => { newTabPromise = navbar.page.context().waitForEvent('page');