Playwright Organizing and Running Test Suites in Playwright Estimated reading: 2 minutes 72 views In this detailed guide, I’ll walk you through step-by-step how to effectively organize and run your test suites in Playwright, helping your test code remain clean, maintainable, and scalable.Structuring Your Test Files by FeaturesTo start organizing your Playwright tests, group related tests by application features or pages. Here’s a recommended folder structure:Project Folder Structuretests/ authentication/ login.spec.js unlock_account.spec.js forms/ basic_form_test.spec.js modals/ complete_registration_form.spec.jsThis method clearly associates tests with their corresponding functionalities, enhancing readability and ease of maintenance.Implementing Test Tags and AnnotationsUsing tags or annotations allows you to group or categorize tests within your suites. This practice helps manage test execution more efficiently.Here’s how you can use test.describe() to group related tests:Basic Form Test (Multiple Tests)import { test, expect } from '@playwright/test'; test.describe('Basic Form Validation Tests', () => { test('should display error messages when required fields are empty | @smoketest', async ({ page }) => { // Navigate to the form validation page await page.goto('https://jahmalrichard.github.io/mqa-demo-test-app/forms-validation.html'); // Define the form section clearly const formSection = page.locator('section.card:has-text("Basic Form Validation")'); // Submit the form without filling in the required fields await formSection.locator('button.btn-primary').click(); // Validate error messages for each required field const fullNameInput = formSection.locator('input[name="fullname"]'); const emailInput = formSection.locator('input[name="email"]'); const skillsTextarea = formSection.locator('textarea[name="skills"]'); // Check that validation messages are triggered (native HTML validation) expect(await fullNameInput.evaluate(el => el.validationMessage)).not.toBe(''); expect(await emailInput.evaluate(el => el.validationMessage)).not.toBe(''); expect(await skillsTextarea.evaluate(el => el.validationMessage)).not.toBe(''); }); test('should allow submission with valid inputs | @uptest', async ({ page }) => { await page.goto('https://jahmalrichard.github.io/mqa-demo-test-app/forms-validation.html'); const formSection = page.locator('section.card:has-text("Basic Form Validation")'); await formSection.locator('input[name="fullname"]').fill('John Doe'); await formSection.locator('input[name="email"]').fill('john@masteringqa.com'); await formSection.locator('input[name="github"]').fill('https://github.com/johndoe'); await formSection.locator('textarea[name="skills"]').fill('Playwright, JavaScript, Testing'); // Submit the form await formSection.locator('button.btn-primary').click(); }); }); To temporarily skip tests or focus exclusively on specific tests during debugging, utilize these commands:Basic Form Tests – Skip & Only Annotationsimport { test, expect } from '@playwright/test'; test.describe('Basic Form Validation Tests', () => { test.skip('should display error messages when required fields are empty | @smoketest', async ({ page }) => { // Navigate to the form validation page await page.goto('https://jahmalrichard.github.io/mqa-demo-test-app/forms-validation.html'); // Define the form section clearly const formSection = page.locator('section.card:has-text("Basic Form Validation")'); // Submit the form without filling in the required fields await formSection.locator('button.btn-primary').click(); // Validate error messages for each required field const fullNameInput = formSection.locator('input[name="fullname"]'); const emailInput = formSection.locator('input[name="email"]'); const skillsTextarea = formSection.locator('textarea[name="skills"]'); // Check that validation messages are triggered (native HTML validation) expect(await fullNameInput.evaluate(el => el.validationMessage)).not.toBe(''); expect(await emailInput.evaluate(el => el.validationMessage)).not.toBe(''); expect(await skillsTextarea.evaluate(el => el.validationMessage)).not.toBe(''); }); test.only('should allow submission with valid inputs | @uptest', async ({ page }) => { await page.goto('https://jahmalrichard.github.io/mqa-demo-test-app/forms-validation.html'); const formSection = page.locator('section.card:has-text("Basic Form Validation")'); await formSection.locator('input[name="fullname"]').fill('John Doe'); await formSection.locator('input[name="email"]').fill('john@masteringqa.com'); await formSection.locator('input[name="github"]').fill('https://github.com/johndoe'); await formSection.locator('textarea[name="skills"]').fill('Playwright, JavaScript, Testing'); // Submit the form await formSection.locator('button.btn-primary').click(); }); }); Running Specific Tests and SuitesPlaywright allows targeted execution of test files, groups, or specific tagged tests. To run a specific test file:Running Basic Form Test (Headless Mode)npx playwright test tests/forms/basic_form_test.spec.js To run tests by tags or descriptive names:Running Specific Test By Tagnpx playwright test --grep "@uptest"This targeted execution significantly reduces feedback time during development and debugging.ConclusionNow you’ve mastered the essentials of organizing and running your Playwright test suites effectively. Following these steps ensures your tests remain organized, scalable, and easy to maintain. Next, explore how to further optimize your testing by leveraging parallel execution and CI integration in our next guide: Parallelization and CI Integration in Playwright. Playwright - Previous Handling Authentication and Sessions in Playwright Next - Playwright Parallelization and CI Integration in Playwright