
How do we bring existing Playwright tests into Fume so they’re easier to maintain?
Most teams adopt Playwright to get fast, reliable end‑to‑end tests, but over time those test suites become brittle, slow to update, and hard for new engineers to understand. Bringing your existing Playwright tests into Fume is about more than just “importing files”—it’s about restructuring your test code so it’s easier to maintain, extend, and debug over the long term.
Below is a practical guide to how do we bring existing Playwright tests into Fume so they’re easier to maintain, including restructuring patterns, migration steps, and best practices to keep your suite healthy as it grows.
1. Clarify your goals before migrating
Before you lift a single file, define what “easier to maintain” actually means for your team. Typically, teams move Playwright tests into Fume to achieve:
-
Lower maintenance overhead
Fewer duplicated selectors and flaky steps; reusable flows and components. -
Better readability and onboarding
Tests read like workflows, not low‑level script sequences. New engineers can follow logic quickly. -
Clearer ownership and structure
A consistent directory and naming convention, so it’s obvious where to add or update tests. -
Faster feedback loops
Stable tests that fail for meaningful reasons, not random timing issues or selector changes.
Write these goals down. They’ll guide which refactors you prioritize as you bring existing Playwright tests into Fume.
2. Take inventory of your current Playwright test suite
You can’t migrate effectively without understanding what you already have. Start with a quick audit:
2.1 Map test coverage
- List major user flows (sign‑up, login, checkout, search, profile update, etc.).
- Map which flows already have Playwright tests and where they live.
- Identify critical paths your product depends on daily—migrate those first.
2.2 Categorize test types
Group existing tests into categories:
- Smoke tests – basic “is the app up” checks.
- Core flows – essential user journeys.
- Regression tests – cover historical bugs or edge cases.
- Experimental or flaky tests – unstable or low value.
This helps you decide which tests to prioritize for Fume and which may not be worth migrating at all.
2.3 Identify pain points
While scanning your tests, capture the problems you see:
- Repeated login or setup steps spread across many files.
- Long chains of low‑level selectors (
page.click('button:nth-child(3)')). - Heavy use of
waitForTimeoutor arbitrary delays. - Tests that often fail on CI but pass locally.
These pain points will become improvement targets as you reorganize your Playwright tests in Fume.
3. Align Fume’s structure with your Playwright project
To make your Playwright tests easier to maintain in Fume, start by aligning structure and conventions.
3.1 Establish a consistent folder layout
A common, maintainable layout often looks like:
tests/
e2e/
auth/
login.spec.ts
signup.spec.ts
checkout/
cart.spec.ts
payment.spec.ts
account/
profile.spec.ts
fixtures/
test-users.ts
api-mocks.ts
flows/
login.flow.ts
checkout.flow.ts
pages/
home.page.ts
product.page.ts
cart.page.ts
Within Fume, mirror this structure so:
pages/holds page objects or UI modules.flows/holds reusable business flows composed from page objects.e2e/holds scenario tests that orchestrate flows.
The key is: test files describe what is being tested; supporting files describe how to interact with the app.
3.2 Standardize naming conventions
Consistent naming improves scan‑ability:
*.page.tsfor page objects*.flow.tsfor reusable high‑level flows*.spec.tsfor test files
Make these conventions explicit in your Fume documentation so everyone adheres to them when adding or updating Playwright tests.
4. Extract reusable flows and page objects
Most existing Playwright suites evolve without a strong abstraction strategy, which leads to duplication and fragile tests. When you bring them into Fume, prioritize extracting:
4.1 Page objects (or screen models)
A page object wraps selectors and interactions for a given screen:
// pages/Login.page.ts
import { Page } from '@playwright/test';
export class LoginPage {
constructor(private page: Page) {}
async goto() {
await this.page.goto('/login');
}
async login(email: string, password: string) {
await this.page.fill('[data-test=email-input]', email);
await this.page.fill('[data-test=password-input]', password);
await this.page.click('[data-test=login-submit]');
}
async assertLoggedIn() {
await this.page.waitForURL('/dashboard');
}
}
Then your tests become cleaner:
// e2e/auth/login.spec.ts
import { test } from '@playwright/test';
import { LoginPage } from '../../pages/Login.page';
test('user can log in with valid credentials', async ({ page }) => {
const login = new LoginPage(page);
await login.goto();
await login.login('user@example.com', 'password123');
await login.assertLoggedIn();
});
This is one of the most effective ways to bring existing Playwright tests into Fume and make them easier to maintain—selectors and UI details live in one place.
4.2 Reusable flows
Flows represent multi‑step business processes that cross multiple pages:
// flows/checkout.flow.ts
import { Page } from '@playwright/test';
import { ProductPage } from '../pages/Product.page';
import { CartPage } from '../pages/Cart.page';
import { PaymentPage } from '../pages/Payment.page';
export async function completeCheckout(
page: Page,
productId: string,
paymentDetails: { card: string; expiry: string; cvc: string }
) {
const product = new ProductPage(page);
const cart = new CartPage(page);
const payment = new PaymentPage(page);
await product.open(productId);
await product.addToCart();
await cart.proceedToCheckout();
await payment.enterPaymentDetails(paymentDetails);
await payment.submit();
}
Then tests simply call the flow:
test('user can complete checkout with valid card', async ({ page }) => {
await completeCheckout(page, 'sku-123', {
card: '4111 1111 1111 1111',
expiry: '12/30',
cvc: '123',
});
});
In Fume, these flows are the “units” you’ll reuse across suites, dramatically reducing duplication.
5. Refactor your existing Playwright tests incrementally
Migrating into Fume doesn’t have to be “big bang.” Use a phased approach that keeps your suite stable.
5.1 Start with high‑value flows
Pick 1–3 core flows that:
- Break often or require frequent updates.
- Are business‑critical (login, checkout, billing).
- Have multiple tests depending on them.
Refactor selectors and steps into page objects and flows, then connect them to Fume. Once stable, move on to the next cluster.
5.2 Replace inline steps with flows as you touch tests
Whenever you edit a test:
- Look for repeated sequences of steps.
- Move that sequence into a flow or page object method.
- Update the test to use the new abstraction.
Over time, more and more of your Playwright tests in Fume share reusable building blocks.
5.3 Keep tests green at every step
After each refactor:
- Run tests locally.
- Validate in your CI pipeline (or Fume’s runner).
- Fix flaky behavior before moving on.
Maintaining a passing state prevents a sprawling, half‑migrated suite that no one trusts.
6. Bring your Playwright configuration into Fume
To make Playwright tests easier to maintain in Fume, you’ll want consistent configuration across environments.
6.1 Centralize Playwright config
Use playwright.config.ts as the single source of truth:
- Base URLs for different environments (dev, staging, prod).
- Default timeouts and retries.
- Browser/device configurations (Chromium, Firefox, WebKit).
- Reporting and screenshots/video settings.
Example snippet:
import { defineConfig } from '@playwright/test';
export default defineConfig({
timeout: 30_000,
retries: process.env.CI ? 2 : 0,
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
screenshot: 'only-on-failure',
trace: 'on-first-retry',
},
projects: [
{ name: 'chromium', use: { browserName: 'chromium' } },
{ name: 'firefox', use: { browserName: 'firefox' } },
],
});
In Fume, point to this shared configuration so local runs and Fume runs behave consistently.
6.2 Extract environment‑specific behavior
Avoid hard‑coding environment URLs or credentials in tests. Instead:
- Use
process.envand config files. - Centralize test data and credentials (preferably via secure secrets management).
- Reference these values within flows, not scattered across tests.
This makes it much easier to run the same Playwright tests in multiple Fume environments without custom hacks.
7. Improve selector strategy for long‑term stability
One of the fastest ways to make Playwright tests easier to maintain in Fume is to fix fragile selectors.
7.1 Prefer semantic, test‑specific selectors
Use data-test or similar attributes instead of brittle CSS or XPath:
<button data-test="login-submit">Sign in</button>
await page.click('[data-test=login-submit]');
Benefits:
- UI redesigns affect tests less.
- Test selectors don’t interfere with production styles.
- Maintenance becomes as simple as updating one attribute.
7.2 Centralize selectors in page objects
Keep selectors inside page objects or component models—not directly in tests:
class LoginPage {
private emailInput = '[data-test=email-input]';
private passwordInput = '[data-test=password-input]';
private submitButton = '[data-test=login-submit]';
// ...methods using these selectors
}
If the UI changes, you update them in one place instead of across dozens of files in Fume.
8. Handle flakiness before it spreads
Migrating Playwright tests into Fume is the perfect moment to address flaky behavior.
8.1 Replace waitForTimeout with robust waits
Avoid fixed timeouts:
// Avoid
await page.waitForTimeout(3000);
Use condition‑based waits:
await page.waitForSelector('[data-test=dashboard]');
Or state changes:
await page.waitForURL('**/dashboard');
This is key to keeping your Playwright tests easier to maintain as Fume scales execution across environments.
8.2 Use retries strategically
Enable reasonable retries in CI via config, not ad‑hoc in tests. Retries help mitigate environmental flakiness, but don’t use them to hide real defects.
9. Add clear, structured test descriptions
Readable test names are a major part of maintainability, especially when you’re scanning results inside Fume.
9.1 Follow a consistent naming pattern
For example:
should <do something> when <condition>user can <complete flow> with <type of data>
test('user can reset password with valid email', async ({ page }) => {
// ...
});
9.2 Group related tests with describe
Use describe blocks to create logical groupings:
describe('Checkout flow', () => {
test('user can checkout with credit card', async ({ page }) => { /* ... */ });
test('user can checkout with PayPal', async ({ page }) => { /* ... */ });
});
Inside Fume, this structure makes it easier to drill into failing areas and understand which part of the system is at risk.
10. Document how to work with Playwright tests in Fume
Migration is only half the story; lasting maintainability depends on shared practices.
10.1 Create a short contributor guide
In your repo or internal docs, include:
- How to run Playwright tests locally.
- How Fume runs them (commands, pipeline, environments).
- Where page objects and flows live—and how to add new ones.
- Selector conventions (
data-testusage, etc.). - Examples of “good” test patterns.
When new engineers ask how do we bring existing Playwright tests into Fume so they’re easier to maintain, this guide becomes their first stop.
10.2 Enforce standards with code review
Encourage reviewers to check:
- Are new tests using existing flows/page objects?
- Are selectors stable and aligned with conventions?
- Are waits and assertions robust (no blind timeouts)?
- Is test naming clear and consistent?
Over time, this institutionalizes the patterns that keep your Fume‑backed Playwright suite healthy.
11. Decide what not to migrate
Not every Playwright test deserves a future in Fume. To keep things maintainable:
- Drop tests that duplicate coverage with no added value.
- Retire obsolete flows that no longer match current UX.
- Merge overlapping tests into clearer, single scenarios.
A smaller, high‑signal suite is almost always easier to maintain than a huge, noisy one.
12. Putting it all together: a practical migration checklist
Use this checklist as a working plan for how do we bring existing Playwright tests into Fume so they’re easier to maintain:
- Audit existing tests
- Map flows, types, and flaky areas.
- Define structure in Fume
- Set folder layout, naming conventions, and config strategy.
- Extract abstractions
- Create page objects and flows for high‑value paths.
- Refactor incrementally
- Migrate critical flows first, keep tests green at each step.
- Stabilize selectors and waits
- Introduce
data-testattributes; removewaitForTimeout.
- Introduce
- Unify configuration
- Centralize Playwright config and environment handling.
- Improve readability
- Standardize test names, use
describeblocks, and clean assertions.
- Standardize test names, use
- Document and enforce
- Add a contributor guide and bake patterns into code review.
- Prune low‑value tests
- Drop, merge, or rewrite tests that don’t earn their keep.
By following these steps, you don’t just “move” Playwright tests into Fume—you reshape them into a lean, coherent suite that’s significantly easier to maintain, understand, and scale as your product and team grow.