Playwright Test Control Methods đŽ
The Traffic Light Analogy đŚ
Imagine youâre a traffic controller at a busy intersection. You have special buttons that tell cars when to go, when to stop, when to slow down, and when to take a detour. Playwrightâs test control methods work exactly like those buttonsâthey tell your tests what to do!
What Are Test Control Methods?
When you write tests, sometimes you need to:
- Skip a test (like closing a road for repairs)
- Run only one test (like letting just one car through)
- Mark a test as broken (like putting up a âRoad Closedâ sign)
- Mark a test to fix later (like scheduling road repairs)
- Slow down a test (like a school zone speed limit)
- Tag tests (like labeling roads by type)
- Run tests conditionally (like opening roads only on certain days)
Letâs explore each one!
1. test.skip() - The âClosed Roadâ Sign đ§
What Is It?
test.skip() tells Playwright: âDonât run this test right now.â
Itâs like putting a âRoad Closedâ signâcars (tests) see the sign and take another route.
When Do We Use It?
- A feature isnât ready yet
- A test works on some browsers but not others
- Youâre waiting for a bug fix
Simple Example
import { test } from '@playwright/test';
// This test will NOT run
test.skip('login with magic link', async ({ page }) => {
// This code won't execute
await page.goto('/magic-login');
});
Skip With a Reason
test.skip('payment checkout', async ({ page }) => {
test.skip(true, 'Payment API not ready');
await page.goto('/checkout');
});
Skip Based on Condition
test('mobile menu', async ({ page, isMobile }) => {
test.skip(!isMobile, 'Only runs on mobile');
await page.click('.hamburger-menu');
});
2. test.only() - The âVIP Laneâ đ
What Is It?
test.only() tells Playwright: âRun ONLY this test, ignore all others.â
Itâs like a VIP laneâonly special cars get through while everyone else waits.
When Do We Use It?
- Debugging a specific test
- Working on one feature at a time
- Quick testing during development
Simple Example
import { test } from '@playwright/test';
test('normal test 1', async ({ page }) => {
// This will NOT run
});
// Only this test runs!
test.only('my special test', async ({ page }) => {
await page.goto('/special-page');
});
test('normal test 2', async ({ page }) => {
// This will NOT run either
});
Multiple Only Tests
test.only('first VIP test', async ({ page }) => {
// This runs
});
test.only('second VIP test', async ({ page }) => {
// This also runs
});
test('regular test', async ({ page }) => {
// This does NOT run
});
Warning! Donât commit
test.only()to your codebase. Itâs for local development only!
3. test.fail() - The âExpected Potholeâ Sign â ď¸
What Is It?
test.fail() tells Playwright: âThis test SHOULD fail. If it passes, somethingâs wrong!â
Itâs like marking a pothole on the roadâyou EXPECT cars to bump there. If they donât bump, the pothole must be fixed (which is great news!).
When Do We Use It?
- You know thereâs a bug
- You want the test to track when a bug gets fixed
- Documenting known issues
Simple Example
import { test } from '@playwright/test';
test.fail('broken logout button', async ({ page }) => {
// This test is EXPECTED to fail
await page.goto('/app');
await page.click('#logout');
// Bug: logout doesn't work yet
});
Fail With Condition
test('download feature', async ({ page, browserName }) => {
test.fail(browserName === 'firefox',
'Download broken in Firefox');
await page.click('#download-btn');
});
What Happens?
- If test fails â Playwright says âOK, expected!â
- If test passes â Playwright says âWait, this should fail!â
4. test.fixme() - The âUnder Constructionâ Sign đ§
What Is It?
test.fixme() tells Playwright: âSkip this test. Someone needs to fix it later.â
Itâs like putting up an âUnder Constructionâ signâthe road is closed, and we need workers to fix it.
When Do We Use It?
- The test is incomplete
- You found a problem but canât fix it now
- Marking tests for future attention
Simple Example
import { test } from '@playwright/test';
test.fixme('user profile editing', async ({ page }) => {
// TODO: Complete this test
// The profile page needs refactoring
});
Fixme With Condition
test('video player', async ({ page, browserName }) => {
test.fixme(browserName === 'webkit',
'Video controls broken in Safari');
await page.click('#play-video');
});
Difference from skip()
| test.skip() | test.fixme() |
|---|---|
| âDonât run thisâ | âDonât run this AND fix it laterâ |
| For intentional skipping | For incomplete/broken tests |
| Permanent or temporary | Always signals âneeds workâ |
5. test.slow() - The âSchool Zoneâ Sign đ˘
What Is It?
test.slow() tells Playwright: âThis test needs more time. Give it 3x the normal timeout.â
Itâs like a school zoneâcars slow down because kids might be crossing. Some tests need extra patience!
When Do We Use It?
- Tests with lots of data loading
- Tests that involve file uploads/downloads
- Tests running on slow environments
Simple Example
import { test } from '@playwright/test';
test.slow('upload large file', async ({ page }) => {
// Gets 3x more time to complete
await page.setInputFiles('#upload', 'huge-file.zip');
await page.click('#submit');
});
Slow With Condition
test('data export', async ({ page, browserName }) => {
test.slow(browserName === 'webkit',
'Safari is slower with exports');
await page.click('#export-all-data');
});
How It Works
Normal timeout: 30 seconds
With test.slow(): 90 seconds (30 Ă 3)
6. Test Tags - The âRoad Labelsâ đˇď¸
What Are They?
Test tags are like labels on roadsââHighwayâ, âLocal Roadâ, âScenic Routeâ. They help you organize and run specific groups of tests.
How to Add Tags
import { test } from '@playwright/test';
test('quick login check',
{ tag: '@smoke' },
async ({ page }) => {
await page.goto('/login');
});
test('full checkout flow',
{ tag: ['@slow', '@e2e'] },
async ({ page }) => {
// Complex test with multiple tags
});
test('admin panel access',
{ tag: '@admin' },
async ({ page }) => {
await page.goto('/admin');
});
Running Tests by Tag
# Run only smoke tests
npx playwright test --grep @smoke
# Run slow tests
npx playwright test --grep @slow
# Run everything EXCEPT admin tests
npx playwright test --grep-invert @admin
Common Tag Ideas
| Tag | Purpose |
|---|---|
@smoke |
Quick sanity checks |
@slow |
Long-running tests |
@e2e |
End-to-end flows |
@api |
API-related tests |
@mobile |
Mobile-specific tests |
@critical |
Must-pass tests |
7. Conditional Test Execution - The âSmart Traffic Lightâ đ§
What Is It?
Run or skip tests based on conditionsâlike a smart traffic light that changes based on traffic patterns.
Skip Based on Browser
test('copy to clipboard', async ({ page, browserName }) => {
test.skip(browserName === 'firefox',
'Clipboard API differs in Firefox');
await page.click('#copy-btn');
});
Skip Based on Device
test('hover tooltip', async ({ page, isMobile }) => {
test.skip(isMobile, 'No hover on mobile');
await page.hover('#info-icon');
});
Skip Based on Environment
test('production smoke test', async ({ page }) => {
test.skip(process.env.ENV !== 'production',
'Only runs in production');
await page.goto('https://mysite.com');
});
Using test.info()
test('platform-specific test', async ({ page }) => {
const info = test.info();
if (info.project.name === 'Mobile Safari') {
test.skip(true, 'Not supported on iOS');
}
await page.click('.desktop-only-feature');
});
Quick Visual Summary
graph LR A["Test Control Methods"] --> B["test.skip"] A --> C["test.only"] A --> D["test.fail"] A --> E["test.fixme"] A --> F["test.slow"] A --> G["Test Tags"] A --> H["Conditional Execution"] B --> B1[Don't run this test] C --> C1["Run ONLY this test"] D --> D1["Expect this to fail"] E --> E1["Skip + needs fixing"] F --> F1["3x timeout"] G --> G1["Organize & filter"] H --> H1["Smart conditions"]
Real-World Example: E-Commerce Test Suite
import { test, expect } from '@playwright/test';
// Smoke test - always run
test('homepage loads',
{ tag: '@smoke' },
async ({ page }) => {
await page.goto('/');
await expect(page).toHaveTitle(/Shop/);
});
// Skip on mobile - no hover
test('product hover preview', async ({ page, isMobile }) => {
test.skip(isMobile, 'No hover on mobile');
await page.hover('.product-card');
await expect(page.locator('.preview')).toBeVisible();
});
// Known bug - expected to fail
test.fail('discount code bug', async ({ page }) => {
await page.fill('#discount', 'SAVE20');
await page.click('#apply');
// Bug: Discount not applying correctly
});
// Slow test - needs extra time
test.slow('full checkout with payment', async ({ page }) => {
await page.goto('/cart');
await page.click('#checkout');
await page.fill('#card', '4242424242424242');
// ... more steps
});
// Fix later - incomplete test
test.fixme('wishlist sync', async ({ page }) => {
// TODO: Implement after API is ready
});
Remember! đŻ
| Method | Traffic Analogy | Use When |
|---|---|---|
test.skip() |
Road Closed | Feature not ready |
test.only() |
VIP Lane | Debugging one test |
test.fail() |
Expected Pothole | Known bug exists |
test.fixme() |
Under Construction | Test needs work |
test.slow() |
School Zone | Test needs more time |
| Tags | Road Labels | Organizing tests |
| Conditional | Smart Traffic Light | Browser/device specific |
Now youâre ready to control your tests like a pro traffic controller! đŚâ¨
