From 322d2fd8247ad069aeee87016bc81aa89c00da76 Mon Sep 17 00:00:00 2001 From: brunomachadors Date: Sat, 18 Jan 2025 06:45:48 +0000 Subject: [PATCH 1/6] feat: update Playwright workflow and refactor locators - Updated Playwright GitHub Actions workflow to build the application, start it, and wait for localhost before running tests. - Refactored `FooterPage`, `HomePage`, `AboutPage`, and `ResumePage` classes to use `getByTestId` locators for improved readability and maintainability. - Enhanced YAML configuration to ensure proper application build and readiness before test execution. --- .github/workflows/playwright.yml | 9 +++++ playwright.config.ts | 2 +- src/app/about/page.tsx | 12 +++---- .../AboutSections/AboutSections.tsx | 22 ++++++------ src/app/components/Button/SkillButton.tsx | 8 ++--- src/app/components/Footer/Footer.tsx | 16 ++++----- src/app/contacts/page.tsx | 26 +++++++------- .../{experiences.ts => experiences.tsx} | 0 src/app/experience/[companyYear]/page.tsx | 26 +++++++------- src/app/page.tsx | 10 +++--- src/app/posts/page.tsx | 36 +++++++++---------- src/app/projects/page.tsx | 26 +++++++------- src/app/resume/page.tsx | 26 +++++++------- src/app/skills/page.tsx | 20 +++++------ tests/data/test-data.ts | 2 +- tests/pages/AboutPage.ts | 30 ++++++---------- tests/pages/FooterPage.ts | 10 +++--- tests/pages/HomePage.ts | 8 ++--- tests/pages/ResumePage.ts | 30 ++++++---------- 19 files changed, 152 insertions(+), 167 deletions(-) rename src/app/content/{experiences.ts => experiences.tsx} (100%) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index f893cbf..63b7f34 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -22,6 +22,15 @@ jobs: - name: Install Playwright Browsers run: npx playwright install --with-deps + - name: Build application + run: npm run build + + - name: Start application + run: npm start & + + - name: Wait for localhost to be ready + run: npx wait-on http://localhost:3000 + - name: Run Playwright tests run: npx playwright test diff --git a/playwright.config.ts b/playwright.config.ts index 7e90a35..49842ff 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -20,7 +20,7 @@ export default defineConfig({ }, use: { - baseURL: process.env.BASE_URL || 'https://bruno-portfolio-eight.vercel.app', + baseURL: 'http://localhost:3000/', trace: 'on-first-retry', screenshot: 'only-on-failure', video: 'retain-on-failure', diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx index 673d46f..b8542f5 100644 --- a/src/app/about/page.tsx +++ b/src/app/about/page.tsx @@ -27,16 +27,16 @@ export default function About() { return (
-
+
diff --git a/src/app/components/AboutSections/AboutSections.tsx b/src/app/components/AboutSections/AboutSections.tsx index a69d7d7..104a492 100644 --- a/src/app/components/AboutSections/AboutSections.tsx +++ b/src/app/components/AboutSections/AboutSections.tsx @@ -12,18 +12,18 @@ export default function AboutSections() { return (

About Me

I'm Bruno Machado, a QA Engineer passionate about software development. @@ -33,7 +33,7 @@ export default function AboutSections() {

toggleSection(index)} >

{section.title} @@ -55,7 +55,7 @@ export default function AboutSections() { className={`transform transition-transform duration-300 ${ openSection === index ? 'rotate-180' : '' }`} - data-test-id={`toggle-icon-${index}`} + data-testid={`toggle-icon-${index}`} > ▼ @@ -63,19 +63,19 @@ export default function AboutSections() { {openSection === index && (
{typeof section.content === 'string' ? ( -

{section.content}

+

{section.content}

) : ( -
+
{React.Children.map( section.content.props.children, (child, childIndex) => (
{child}
diff --git a/src/app/components/Button/SkillButton.tsx b/src/app/components/Button/SkillButton.tsx index af36dbe..3b45e92 100644 --- a/src/app/components/Button/SkillButton.tsx +++ b/src/app/components/Button/SkillButton.tsx @@ -29,7 +29,7 @@ export default function SkillButton({ alignItems: 'center', justifyContent: 'center', }} - data-test-id={testId} + data-testid={testId} > @@ -89,13 +91,17 @@ export default function SkillsPage() {

{subcategory.name}

{subcategory.items.map(({ text, description }, itemIndex) => ( handleSkillClick(text)} - testId={`skill-button-${text.toLowerCase()}`} aria-pressed={activeSkill === text} /> ))} diff --git a/tests/pages/skillsPage.ts b/tests/pages/skillsPage.ts new file mode 100644 index 0000000..ae844a7 --- /dev/null +++ b/tests/pages/skillsPage.ts @@ -0,0 +1,63 @@ +import { expect, Page } from '@playwright/test'; + +export class SkillsPage { + readonly page: Page; + + constructor(page: Page) { + this.page = page; + } + + async navigateToSkills() { + await this.page.goto('/skills', { waitUntil: 'commit' }); + } + + async validatePageLoaded() { + const tabAll = this.page.getByTestId('tab-all'); + await expect(tabAll).toBeVisible(); + } + + async validateCategoryTab(category: string) { + if (category.toLowerCase() === 'all') { + const tabAll = this.page.getByTestId('tab-all'); + await expect(tabAll).toBeVisible(); + await tabAll.click(); + } else { + const tabCategory = this.page.getByTestId( + `tab-${category.toLowerCase().replace(/\s+/g, '-')}` + ); + await expect(tabCategory).toBeVisible(); + await tabCategory.click(); + } + } + + async validateSkill(skill: string, description: string) { + const skillButton = this.page.getByTestId( + `skill-${skill.toLowerCase().replace(/\s+/g, '-')}` + ); + await expect(skillButton).toBeVisible(); + await skillButton.click(); + + const skillDescriptionLocator = this.page.locator( + `[data-testid="${skill.toLowerCase().replace(/\s+/g, '-')}-description"]` + ); + await expect(skillDescriptionLocator).toBeVisible(); + await expect(skillDescriptionLocator).toHaveText(description); + } + + async validateAllSkills(skills: { text: string; description: string }[]) { + for (const { text, description } of skills) { + await this.validateSkill(text, description); + } + } + + async validateSubCategory(subcategory: string) { + const subcategoryTestId = `subcategory-title-${subcategory + .toLowerCase() + .replace(/\s+/g, '-')}`; + await expect(this.page.getByTestId(subcategoryTestId)).toBeVisible(); + } + + async validateProjectButton() { + await expect(this.page.getByTestId('projects-button')).toBeVisible(); + } +} diff --git a/tests/skills.spec.ts b/tests/skills.spec.ts new file mode 100644 index 0000000..21be06e --- /dev/null +++ b/tests/skills.spec.ts @@ -0,0 +1,38 @@ +import test from 'playwright/test'; +import { SkillsPage } from './pages/skillsPage'; +import { SKILLS } from '@/app/content/skills'; + +test.describe('Skills Page', () => { + test('Content', async ({ page }) => { + const skillsPage = new SkillsPage(page); + + await test.step('Navigate', async () => { + await skillsPage.navigateToSkills(); + await skillsPage.validatePageLoaded(); + }); + + await test.step('Filters', async () => { + for (const { category } of SKILLS) { + await skillsPage.validateCategoryTab(category); + } + }); + + for (const { category, subcategories } of SKILLS) { + await test.step(`Category: ${category}`, async () => { + await skillsPage.validateCategoryTab(category); + + for (const { name, items } of subcategories) { + await test.step(`Subcategory: ${name}`, async () => { + await skillsPage.validateSubCategory(name); + }); + + for (const { text, description } of items) { + await test.step(`Skill: ${text}`, async () => { + await skillsPage.validateSkill(text, description); + }); + } + } + }); + } + }); +}); From 8f39436eeea338e8826bf8b6d715a947e2712c13 Mon Sep 17 00:00:00 2001 From: brunomachadors Date: Sat, 18 Jan 2025 08:16:20 +0000 Subject: [PATCH 3/6] Update test step text --- tests/resume.spec.ts | 4 ++-- tests/skills.spec.ts | 41 +++++++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/tests/resume.spec.ts b/tests/resume.spec.ts index b02c0f2..30a450f 100644 --- a/tests/resume.spec.ts +++ b/tests/resume.spec.ts @@ -2,10 +2,10 @@ import { test } from '@playwright/test'; import { ResumePage } from './pages/ResumePage'; test.describe('Resume', () => { - test('Validate Resume Page content', async ({ page }) => { + test('Content', async ({ page }) => { const resumePage = new ResumePage(page); - await test.step('Resume Page', async () => { + await test.step('Page', async () => { await resumePage.navigateToResume(); await resumePage.validatePageLoaded(); }); diff --git a/tests/skills.spec.ts b/tests/skills.spec.ts index 21be06e..27d5573 100644 --- a/tests/skills.spec.ts +++ b/tests/skills.spec.ts @@ -2,7 +2,7 @@ import test from 'playwright/test'; import { SkillsPage } from './pages/skillsPage'; import { SKILLS } from '@/app/content/skills'; -test.describe('Skills Page', () => { +test.describe('Skills', () => { test('Content', async ({ page }) => { const skillsPage = new SkillsPage(page); @@ -16,23 +16,28 @@ test.describe('Skills Page', () => { await skillsPage.validateCategoryTab(category); } }); - - for (const { category, subcategories } of SKILLS) { - await test.step(`Category: ${category}`, async () => { - await skillsPage.validateCategoryTab(category); - - for (const { name, items } of subcategories) { - await test.step(`Subcategory: ${name}`, async () => { - await skillsPage.validateSubCategory(name); + await test.step(`Category`, async () => { + for (const { category, subcategories } of SKILLS) { + await test.step(`${category}`, async () => { + await test.step(`Visible`, async () => { + await skillsPage.validateCategoryTab(category); }); - - for (const { text, description } of items) { - await test.step(`Skill: ${text}`, async () => { - await skillsPage.validateSkill(text, description); - }); - } - } - }); - } + await test.step(`Subcategories`, async () => { + for (const { name, items } of subcategories) { + await test.step(`${name}`, async () => { + await skillsPage.validateSubCategory(name); + }); + await test.step(`Skills`, async () => { + for (const { text, description } of items) { + await test.step(` ${text}`, async () => { + await skillsPage.validateSkill(text, description); + }); + } + }); + } + }); + }); + } + }); }); }); From 4fbc85d3527427a98fd46ab33e6214a43db66651 Mon Sep 17 00:00:00 2001 From: brunomachadors Date: Sat, 18 Jan 2025 09:09:06 +0000 Subject: [PATCH 4/6] feat: integrate Allure report and update workflow configuration - Removed default Playwright HTML report and replaced it with Allure report generation. - Updated GitHub Actions workflow to generate and upload Allure reports as artifacts. - Added `test:allure:generate` script to ensure Allure reports are clean and properly generated. - Workflow now runs Playwright tests and generates an Allure report automatically after tests complete. - Simplified report handling for CI/CD pipelines with Allure integration. --- .github/workflows/playwright.yml | 7 ++- .gitignore | 2 + package-lock.json | 82 ++++++++++++++++++++++++++++++++ package.json | 10 +++- playwright.config.ts | 6 +-- 5 files changed, 99 insertions(+), 8 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 63b7f34..76b5c92 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -34,9 +34,12 @@ jobs: - name: Run Playwright tests run: npx playwright test + - name: Generate Allure Report + run: npm run test:allure:generate + - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} with: - name: playwright-report - path: playwright-report/ + name: allure-report + path: allure-report/ retention-days: 30 diff --git a/.gitignore b/.gitignore index 0d769c7..fba7a3f 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,5 @@ node_modules/ /playwright-report/ /blob-report/ /playwright/.cache/ +/allure-report/ +/allure-results/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f3591ac..8ed5d07 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,8 @@ "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "allure-commandline": "^2.32.0", + "allure-playwright": "^3.0.9", "autoprefixer": "^10.4.20", "eslint": "^9", "eslint-config-next": "15.1.3", @@ -1252,6 +1254,47 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/allure-commandline": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/allure-commandline/-/allure-commandline-2.32.0.tgz", + "integrity": "sha512-W03ors+ks8uy0SgQILHQvtvR0iadAfDYmTFC3p8Pk4pi8KXUW1cF+z8FN2+7deH3FE2cuYgjhhA+CdLdJfzOMQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "allure": "bin/allure" + } + }, + "node_modules/allure-js-commons": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/allure-js-commons/-/allure-js-commons-3.0.9.tgz", + "integrity": "sha512-ll7h66zkBAwKSuMv2ZIqa9vXztz/I1Q+oUzxMgg7EErwd6ooO04E4nquXE+rS9mJErYs9K1qTVo3KgnPUzCsQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "md5": "^2.3.0" + }, + "peerDependencies": { + "allure-playwright": "3.0.9" + }, + "peerDependenciesMeta": { + "allure-playwright": { + "optional": true + } + } + }, + "node_modules/allure-playwright": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/allure-playwright/-/allure-playwright-3.0.9.tgz", + "integrity": "sha512-5NBVJ50b9iYbzhkLJF0iSWefV4/eWSv+OioRv4kpLKXbnhrQk+wUH/wVt7oxWnZPSrQAuxilckWxZW5BIP3sNQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "allure-js-commons": "3.0.9" + }, + "peerDependencies": { + "@playwright/test": ">=1.36.0" + } + }, "node_modules/ansi-regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", @@ -1759,6 +1802,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -1880,6 +1933,16 @@ "node": ">= 8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -3394,6 +3457,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "license": "MIT" + }, "node_modules/is-bun-module": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.3.0.tgz", @@ -3963,6 +4033,18 @@ "node": ">= 0.4" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", diff --git a/package.json b/package.json index 7c64d36..f11483e 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,13 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "test": "playwright test && npm run test:allure:open", + "test:headed": "playwright test --headed", + "test:debug": "playwright test --debug", + "test:report": "playwright show-report", + "test:allure:generate": "allure generate allure-results --clean -o allure-report", + "test:allure:open": "npm run test:allure:generate && allure open allure-report" }, "dependencies": { "dotenv": "^16.4.7", @@ -21,6 +27,8 @@ "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "allure-commandline": "^2.32.0", + "allure-playwright": "^3.0.9", "autoprefixer": "^10.4.20", "eslint": "^9", "eslint-config-next": "15.1.3", diff --git a/playwright.config.ts b/playwright.config.ts index 49842ff..a1dcaba 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -10,11 +10,7 @@ export default defineConfig({ retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, - reporter: [ - ['line'], - ['github'], - ['html', { outputFolder: 'playwright-report', open: 'always' }], - ], + reporter: [['line'], ['github'], ['list'], ['allure-playwright']], expect: { timeout: 10_000, }, From 2e47ddf66ef70d56f9133c3f5677fdb00e01a40b Mon Sep 17 00:00:00 2001 From: brunomachadors Date: Sat, 18 Jan 2025 09:19:03 +0000 Subject: [PATCH 5/6] update yml --- .github/workflows/playwright.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 76b5c92..f39dba6 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -12,6 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: lts/* @@ -37,9 +38,12 @@ jobs: - name: Generate Allure Report run: npm run test:allure:generate - - uses: actions/upload-artifact@v4 - if: ${{ !cancelled() }} - with: - name: allure-report - path: allure-report/ - retention-days: 30 + - name: Deploy Allure Report to GitHub Pages + if: ${{ success() }} + run: | + git config --global user.name "GitHub Actions" + git config --global user.email "actions@github.com" + git checkout --orphan gh-pages + git --work-tree=allure-report add --all + git --work-tree=allure-report commit -m "Deploy Allure Report" + git push -f origin gh-pages From e1762b6178fdd53d799d3923d9f58ed8a13bf583 Mon Sep 17 00:00:00 2001 From: brunomachadors Date: Sat, 18 Jan 2025 09:29:17 +0000 Subject: [PATCH 6/6] Fix yml gh pages --- .github/workflows/playwright.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index f39dba6..2e7bc4e 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -43,6 +43,7 @@ jobs: run: | git config --global user.name "GitHub Actions" git config --global user.email "actions@github.com" + git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git git checkout --orphan gh-pages git --work-tree=allure-report add --all git --work-tree=allure-report commit -m "Deploy Allure Report"