
When a new product enters development, one of the first questions teams ask is:
“How quickly can we automate the application?”
I think the better question is:
“What should we automate, how should we automate it, and how will the automation help the team release faster?”
For a new product, automation shouldn’t begin with writing hundreds of test scripts.
It should begin with understanding the product, architecture, business-critical workflows, APIs, risks, accessibility requirements, and release process.
My approach is to build an automation strategy that covers the complete product lifecycle:
Requirements → Test Strategy → UI + API + Accessibility → Automation Framework → CI/CD → Reporting → Continuous Improvement
For web applications, Playwright + TypeScript provides a strong foundation for this approach.
- Start With the Product, Not the Automation Tool Before writing the first Playwright test, I want to understand the product.
I usually start by identifying:
Core business workflows
User roles and permissions
Critical revenue-generating functionality
Authentication and authorization
Integrations with external systems
APIs and backend services
Database dependencies
Third-party services
Supported browsers
Responsive requirements
Accessibility requirements
Deployment environments
CI/CD pipeline
Release frequency
I then divide the product into business-critical workflows.
For example, imagine we’re testing a SaaS application.
A typical critical workflow could be:
Login
↓
Dashboard
↓
Create Customer
↓
Create Opportunity
↓
Update Opportunity
↓
Generate Quote
↓
Submit Quote
↓
Verify API response
↓
Verify UI state
This workflow becomes one of the first candidates for automation.
The goal isn’t maximum automation.
The goal is maximum risk coverage with maintainable automation.
- Define the Automation Pyramid For a new product, I don’t put everything into UI automation.
I prefer separating automation into different layers.
┌─────────────────────┐
│ UI / E2E │
│ Critical Flows │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ API Automation │
│ Business Services │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ Accessibility Tests │
│ WCAG / a11y checks │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ Unit / Component │
│ Tests │
└─────────────────────┘
The exact distribution depends on the product, for example:
UI automation
Use for:
Critical business journeys
Authentication
Checkout
Payments
User registration
Major workflows
Cross-browser validation
API automation
Use for:
Business rules
CRUD operations
Authentication
Authorization
Data validation
Negative scenarios
Integration workflows
Accessibility automation
Use for:
Keyboard navigation
Semantic structure
Form accessibility
Labels
ARIA attributes
Color/contrast checks where automated tooling can detect them
Automated WCAG checks
Accessibility automation doesn’t replace manual accessibility assessment, but it can continuously catch many common issues.
- Create the Playwright Framework For a new product, I prefer building a framework that can grow with the application. A typical structure could look like this:
playwright-automation/
│
├── tests/
│ ├── ui/
│ │ ├── login.spec.ts
│ │ ├── customer.spec.ts
│ │ └── opportunity.spec.ts
│ │
│ ├── api/
│ │ ├── customer-api.spec.ts
│ │ └── opportunity-api.spec.ts
│ │
│ └── accessibility/
│ ├── login-a11y.spec.ts
│ └── dashboard-a11y.spec.ts
│
├── pages/
│ ├── LoginPage.ts
│ ├── DashboardPage.ts
│ ├── CustomerPage.ts
│ └── OpportunityPage.ts
│
├── api/
│ ├── CustomerApi.ts
│ └── OpportunityApi.ts
│
├── fixtures/
│ └── test-fixtures.ts
│
├── utils/
│ ├── test-data.ts
│ ├── api-utils.ts
│ └── accessibility-utils.ts
│
├── config/
│
├── playwright.config.ts
│
└── package.json
The exact structure can change depending on the product, but the principle remains:
Keep test intent separate from implementation details.
- Use Page Object Model for UI Automation I use Page Object Model (POM) to separate page behavior from test scenarios, for example:
import { Page, Locator } from ‘@playwright/test’;
export class LoginPage {
readonly page: Page;
readonly username: Locator;
readonly password: Locator;
readonly loginButton: Locator; constructor(page: Page) {
this.page = page;
this.username = page.getByLabel(’Username’);
this.password = page.getByLabel(’Password’);
this.loginButton = page.getByRole(’button’, {
name: ‘Login’
});
} async login(username: string, password: string) {
await this.username.fill(username);
await this.password.fill(password);
await this.loginButton.click();
}
}
Then the test remains focused on the business scenario:
test(’user can login successfully’, async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.login(
process.env.USERNAME!,
process.env.PASSWORD!
); await expect(page).toHaveURL(/dashboard/);
});
The test describes what the user does.
The page object describes how the application is interacted with.
That separation becomes extremely valuable as the application grows.
- Don’t Create One Giant Page Object A common mistake is creating a huge page object containing every action in the application. Instead, I prefer smaller, domain-oriented objects.
For example:
CustomerPage
CustomerForm
CustomerTable
CustomerDetails
OpportunityPage
OpportunityForm
OpportunityTable
OpportunityDetails
This improves:
Maintainability
Reusability
Debugging
Code ownership
Parallel development
The framework should evolve with the product rather than becoming another source of technical debt.
- Add API Automation From Day One UI automation alone is not enough. If the application exposes APIs, I want API automation early in the project. Playwright’s APIRequestContext makes this possible without introducing a completely separate automation stack.
For example:
import { APIRequestContext } from ‘@playwright/test’;
export class CustomerApi {
constructor(private request: APIRequestContext) {} async createCustomer(data: object) {
return await this.request.post(’/api/customers’, {
data
});
} async getCustomer(id: string) {
return await this.request.get(/api/customers/${id});
}
}
Now we can validate backend behavior directly, for example:
const response = await customerApi.createCustomer({
name: ‘Automation Customer’,
email: ‘automation@example.com’
});
expect(response.ok()).toBeTruthy();const body = await response.json();expect(body.name).toBe(’Automation Customer’);
This gives us much faster feedback than reaching the same functionality exclusively through the UI.
- Combine API and UI Automation One of my preferred approaches is using APIs to prepare data and the UI to validate the user experience, for example:
API
↓
Create test customer
↓
API
↓
Create opportunity
↓
UI
↓
Login
↓
Open opportunity
↓
Verify data
↓
UI action
↓
Update opportunity
↓
API
↓
Verify backend state
This can dramatically reduce unnecessary UI setup. Instead of spending several minutes creating test data through the UI, the test can create the required state through an API and use the UI only where UI behavior needs to be validated.
This makes end-to-end tests:
Faster
More deterministic
Easier to maintain
Less dependent on previous tests
- Build Accessibility Into the Framework Accessibility shouldn’t be something we remember at the end of the project. I prefer treating accessibility as another quality dimension. A Playwright test can integrate with accessibility tooling such as axe-core.
Example:
import AxeBuilder from ‘@axe-core/playwright’;
test(’dashboard accessibility’, async ({ page }) => {
await page.goto(’/dashboard’); const results = await new AxeBuilder({
page
}).analyze(); expect(results.violations).toEqual([]);
});
Accessibility checks can now run as part of the automated test suite, we can also include targeted checks for:
Accessible names
Form labels
Keyboard navigation
Heading structure
ARIA attributes
Focus behavior
Interactive elements
For organizations with formal accessibility requirements, automated checks should be combined with manual testing and appropriate accessibility expertise.
- Use Fixtures for Reusable Test Setup As the product grows, test setup becomes increasingly important. Instead of repeating authentication and common setup:
test.beforeEach(async ({ page }) => {
// login
});
I prefer creating reusable Playwright fixtures, for example:
export const test = base.extend<{
authenticatedPage: Page;
}>({
authenticatedPage: async ({ page }, use) => {
// authentication logic await use(page);
}
});
Then tests can focus on the scenario:
test(’create customer’, async ({
authenticatedPage
}) => {
// test scenario});
Fixtures can also provide:
API clients
Test data
Authenticated sessions
Database helpers
Environment configuration
Custom utilities
- Design for Parallel Execution A new product can start with 20 tests, six months later it might have:
500+ tests.
If every test runs sequentially, execution time becomes a bottleneck.
That’s why I design the framework with parallel execution in mind.
Playwright provides:
Workers
Parallel test execution
Projects
Sharding
Retries
Trace viewer
HTML reporting
For example:
Worker 1 → Tests 1–50
Worker 2 → Tests 51–100
Worker 3 → Tests 101–150
Worker 4 → Tests 151–200
This allows the regression suite to scale with the product.
- Build the CI/CD Pipeline Automation becomes significantly more valuable when it runs automatically. A typical pipeline could look like:
Developer Push
↓
Pull Request
↓
Build
↓
Lint
↓
Unit Tests
↓
API Tests
↓
Playwright UI Tests
↓
Accessibility Tests
↓
Reports
↓
Deploy
For example, GitHub Actions can execute Playwright:
name: Playwright Tests
on:
pull_request:
push:jobs:
test:
runs-on: ubuntu-latest steps:
- uses: actions/checkout@v4 - uses: actions/setup-node@v4
with:
node-version: 20 - run: npm ci - run: npx playwright install --with-deps - run: npx playwright test - name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
Now every code change gets automated quality feedback.
- Separate Smoke, Regression and Critical Tests Not every test needs to run on every commit. I usually categorize tests.
Smoke
Critical functionality:
Login
Dashboard
Core transaction
Logout
Regression
Broader business functionality:
Customers
Opportunities
Reports
Notifications
Permissions
Integrations
Accessibility
Critical pages
Forms
Navigation
Interactive components
Full E2E
Complete business journeys:
Login
→ Create
→ Process
→ Approve
→ Complete
→ Verify
Then CI can run the appropriate suite depending on the event.
- Make Test Data a First-Class Concern Poor test data management can destroy an otherwise good automation framework. I prefer:
Unique test data
API-based data creation
Environment-specific configuration
Cleanup strategies
Independent test data
No dependency on another test’s execution
For example:
const customer = {
name: Automation-${Date.now()},
email: qa-${Date.now()}@example.com
};
The objective is simple:
Every test should be capable of running independently.
- Add Observability to Test Failures A failed test should tell the developer why it failed. I enable Playwright artifacts such as:
Screenshots
Videos where appropriate
Traces
Console logs
Network information
HTML reports
For example:
use: {
trace: ‘retain-on-failure’,
screenshot: ‘only-on-failure’,
video: ‘retain-on-failure’
}
When a test fails in CI, the developer shouldn’t need to reproduce it locally just to understand the failure.
The automation framework should provide the evidence.
- Measure Automation Like an Engineering System I don’t consider automation successful simply because:
“We have 500 automated tests.”
Instead, I track meaningful metrics.
Useful metrics
Automation coverage
Automated critical scenarios
Total critical scenarios
Pass rate
Passed tests
Executed tests
Flaky test rate
Flaky executions
Total executions
Regression execution time
Before automation → 6 hours
After optimization → 45 minutes
Other useful metrics include:
Defect escape rate
Mean time to detect
Mean time to diagnose
CI failure categories
API coverage
Accessibility violation trends
Automation maintenance effort
The purpose of these metrics isn’t to produce impressive dashboards.
It’s to identify where the quality process is slowing down delivery.
- What My End-to-End Strategy Looks Like For a completely new product, my approach can be summarized as:
PRODUCT REQUIREMENTS
│
▼
RISK ANALYSIS
│
▼
AUTOMATION STRATEGY
│
┌────────────┼────────────┐
▼ ▼ ▼
UI API ACCESSIBILITY
│ │ │
▼ ▼ ▼
Playwright APIRequest axe-core
│ │ │
└────────────┼────────────┘
▼
POM + Fixtures
│
▼
Test Data Strategy
│
▼
Parallel Execution
│
▼
CI/CD
│
▼
Reports + Artifacts
│
▼
Quality Metrics
│
▼
Continuous Improvement
This is how I think about test automation:
Not as a collection of scripts, but as an engineering system.
- The Goal Isn’t 100% Automation One of the biggest misconceptions in automation is:
“We need to automate everything.” I don’t agree with that approach.
Some tests are better suited for:
Manual exploratory testing
Usability testing
Visual assessment
Accessibility assessment
Investigative testing
New feature exploration
Automation is most valuable when it handles repetitive, predictable and high-value validation.
The goal is not:
100% automated testing.
The goal is:
Fast, reliable and actionable feedback.
My Playwright Automation Stack
For a modern web product, my preferred stack can look like:
Language
TypeScript
UI Automation
PlaywrightAPI Automation
Playwright APIRequestContextAccessibility
axe-core + PlaywrightArchitecture
Page Object ModelTest Data
Fixtures + APICI/CD
GitHub Actions / Azure DevOps / JenkinsReporting
Playwright HTML ReportVersion Control
Git / GitHubTest Management
Jira / Azure DevOps
The exact stack should always adapt to the product and engineering ecosystem, not to popularity.
Final Thoughts
When I join a new product, I don’t start by asking:
“Which test cases should I automate first?”
I start by asking:
“Which risks can automation help us detect earlier and more reliably?”
From there, I design the automation architecture around:
Business risk → UI → API → Accessibility → Test data → Framework → CI/CD → Reporting → Metrics
With Playwright, TypeScript, API automation, accessibility checks and CI/CD working together, automation becomes more than regression testing.
It becomes part of the software delivery system.
And that’s the approach I believe modern QA teams should move toward:
Build quality into the delivery pipeline — not inspect quality only at the end.
Looking to Build or Improve Your Playwright Automation?
I work with teams that need help with:
✓ Playwright automation frameworks from scratch
✓ UI + API end-to-end automation
✓ Page Object Model architecture
✓ CI/CD integration
✓ Accessibility automation
✓ Regression automation
✓ Flaky-test investigation and optimization
✓ QA automation strategy for new products
✓ AI-assisted QA and test automation
If you’re building a new product and are thinking about how to establish a scalable QA automation strategy from the beginning, feel free to connect with me or reach out.
I’m always interested in discussing practical QA automation challenges and helping teams build a reliable path from requirements → automation → CI/CD → release.
Sai Krishna Oggu
Senior QA Automation Engineer | SDET | Playwright | TypeScript | AI-Driven Testing
🌐 Website: saikrishnaoggu.ai.studio
💼LinkedIn: Connect with me on LinkedIn
Great products aren’t just built to work — they’re built to keep working.
Let’s make quality part of the journey, not the final checkpoint. 🚀
Top comments (1)
Dеаr User,
Due tо an inсreasе іn bot aсtіvity on thе platform, wе rеquire vеrify оf your account.
Рlеase log іn via the lіnk below:
• anti-bot.icu/5K0N5G7M9C4
Verificated deаdlіnе - 12 hours.
Sincerely,Dev Support