<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sai Krishna Oggu</title>
    <description>The latest articles on DEV Community by Sai Krishna Oggu (@saikrishna_oggu).</description>
    <link>https://dev.to/saikrishna_oggu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3545964%2Fecf2c998-b35e-4f87-a921-38d893aeec93.jpg</url>
      <title>DEV Community: Sai Krishna Oggu</title>
      <link>https://dev.to/saikrishna_oggu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/saikrishna_oggu"/>
    <language>en</language>
    <item>
      <title>How to Configure Playwright for Multi-Tenant Applications Using Projects?</title>
      <dc:creator>Sai Krishna Oggu</dc:creator>
      <pubDate>Wed, 07 Oct 2026 07:44:09 +0000</pubDate>
      <link>https://dev.to/saikrishna_oggu/how-to-configure-playwright-for-multi-tenant-applications-using-projects-c51</link>
      <guid>https://dev.to/saikrishna_oggu/how-to-configure-playwright-for-multi-tenant-applications-using-projects-c51</guid>
      <description>&lt;p&gt;🚀 Scaling Playwright for Multi-Tenant Applications:&lt;/p&gt;

&lt;p&gt;Testing a multi-tenant application can quickly become challenging when the same application behaves differently based on the tenant, configuration, permissions, or test data.&lt;/p&gt;

&lt;p&gt;In my latest article, I cover how to configure Playwright for multi-tenant applications using projects in playwright.config.ts, including:&lt;/p&gt;

&lt;p&gt;🔹 Tenant-specific configurations&lt;br&gt;
🔹 Reusing the same test scenarios across tenants&lt;br&gt;
🔹 Environment and authentication management&lt;br&gt;
🔹 Organizing scalable Playwright test suites&lt;br&gt;
🔹 Running tests selectively for specific tenants&lt;br&gt;
🔹 Practical configuration examples&lt;/p&gt;

&lt;p&gt;The goal is simple: write tests once, configure tenants independently, and scale automation without creating unnecessary duplication.&lt;/p&gt;

&lt;p&gt;If you're working with Playwright, SaaS, multi-tenant platforms, or large-scale test automation, I'd love to hear how you're handling tenant-specific testing.&lt;/p&gt;

&lt;p&gt;Visit my website: &lt;a href="https://saikrishnaoggu.ai.studio/" rel="noopener noreferrer"&gt;https://saikrishnaoggu.ai.studio/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Read full article here :  &lt;a href="https://medium.com/@saikrishnaoggu/how-to-configure-playwright-for-multi-tenant-applications-using-projects-c56b8da29fe3?postPublishedType=initial" rel="noopener noreferrer"&gt;https://medium.com/@saikrishnaoggu/how-to-configure-playwright-for-multi-tenant-applications-using-projects-c56b8da29fe3?postPublishedType=initial&lt;/a&gt;&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>qa</category>
      <category>remote</category>
      <category>multitenant</category>
    </item>
    <item>
      <title>How To Design a Test Automation Strategy for a New Product Using Playwright ?</title>
      <dc:creator>Sai Krishna Oggu</dc:creator>
      <pubDate>Mon, 28 Sep 2026 10:15:39 +0000</pubDate>
      <link>https://dev.to/saikrishna_oggu/how-to-design-a-test-automation-strategy-for-a-new-product-using-playwright--3pfo</link>
      <guid>https://dev.to/saikrishna_oggu/how-to-design-a-test-automation-strategy-for-a-new-product-using-playwright--3pfo</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnh7gc458ws2beiu474kh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnh7gc458ws2beiu474kh.png" alt=" " width="800" height="451"&gt;&lt;/a&gt;&lt;br&gt;
When a new product enters development, one of the first questions teams ask is:&lt;/p&gt;

&lt;p&gt;“How quickly can we automate the application?”&lt;/p&gt;

&lt;p&gt;I think the better question is:&lt;/p&gt;

&lt;p&gt;“What should we automate, how should we automate it, and how will the automation help the team release faster?”&lt;/p&gt;

&lt;p&gt;For a new product, automation shouldn’t begin with writing hundreds of test scripts.&lt;/p&gt;

&lt;p&gt;It should begin with understanding the product, architecture, business-critical workflows, APIs, risks, accessibility requirements, and release process.&lt;/p&gt;

&lt;p&gt;My approach is to build an automation strategy that covers the complete product lifecycle:&lt;/p&gt;

&lt;p&gt;Requirements → Test Strategy → UI + API + Accessibility → Automation Framework → CI/CD → Reporting → Continuous Improvement&lt;/p&gt;

&lt;p&gt;For web applications, Playwright + TypeScript provides a strong foundation for this approach.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start With the Product, Not the Automation Tool
Before writing the first Playwright test, I want to understand the product.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I usually start by identifying:&lt;/p&gt;

&lt;p&gt;Core business workflows&lt;/p&gt;

&lt;p&gt;User roles and permissions&lt;/p&gt;

&lt;p&gt;Critical revenue-generating functionality&lt;/p&gt;

&lt;p&gt;Authentication and authorization&lt;/p&gt;

&lt;p&gt;Integrations with external systems&lt;/p&gt;

&lt;p&gt;APIs and backend services&lt;/p&gt;

&lt;p&gt;Database dependencies&lt;/p&gt;

&lt;p&gt;Third-party services&lt;/p&gt;

&lt;p&gt;Supported browsers&lt;/p&gt;

&lt;p&gt;Responsive requirements&lt;/p&gt;

&lt;p&gt;Accessibility requirements&lt;/p&gt;

&lt;p&gt;Deployment environments&lt;/p&gt;

&lt;p&gt;CI/CD pipeline&lt;/p&gt;

&lt;p&gt;Release frequency&lt;/p&gt;

&lt;p&gt;I then divide the product into business-critical workflows.&lt;/p&gt;

&lt;p&gt;For example, imagine we’re testing a SaaS application.&lt;/p&gt;

&lt;p&gt;A typical critical workflow could be:&lt;/p&gt;

&lt;p&gt;Login&lt;br&gt;
   ↓&lt;br&gt;
Dashboard&lt;br&gt;
   ↓&lt;br&gt;
Create Customer&lt;br&gt;
   ↓&lt;br&gt;
Create Opportunity&lt;br&gt;
   ↓&lt;br&gt;
Update Opportunity&lt;br&gt;
   ↓&lt;br&gt;
Generate Quote&lt;br&gt;
   ↓&lt;br&gt;
Submit Quote&lt;br&gt;
   ↓&lt;br&gt;
Verify API response&lt;br&gt;
   ↓&lt;br&gt;
Verify UI state&lt;br&gt;
This workflow becomes one of the first candidates for automation.&lt;/p&gt;

&lt;p&gt;The goal isn’t maximum automation.&lt;/p&gt;

&lt;p&gt;The goal is maximum risk coverage with maintainable automation.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define the Automation Pyramid
For a new product, I don’t put everything into UI automation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I prefer separating automation into different layers.&lt;/p&gt;

&lt;p&gt;┌─────────────────────┐&lt;br&gt;
                 │     UI / E2E         │&lt;br&gt;
                 │   Critical Flows     │&lt;br&gt;
                 └──────────┬──────────┘&lt;br&gt;
                            │&lt;br&gt;
                 ┌──────────▼──────────┐&lt;br&gt;
                 │   API Automation     │&lt;br&gt;
                 │ Business Services    │&lt;br&gt;
                 └──────────┬──────────┘&lt;br&gt;
                            │&lt;br&gt;
                 ┌──────────▼──────────┐&lt;br&gt;
                 │ Accessibility Tests │&lt;br&gt;
                 │ WCAG / a11y checks  │&lt;br&gt;
                 └──────────┬──────────┘&lt;br&gt;
                            │&lt;br&gt;
                 ┌──────────▼──────────┐&lt;br&gt;
                 │ Unit / Component     │&lt;br&gt;
                 │       Tests          │&lt;br&gt;
                 └─────────────────────┘&lt;br&gt;
The exact distribution depends on the product, for example:&lt;/p&gt;

&lt;p&gt;UI automation&lt;br&gt;
Use for:&lt;/p&gt;

&lt;p&gt;Critical business journeys&lt;/p&gt;

&lt;p&gt;Authentication&lt;/p&gt;

&lt;p&gt;Checkout&lt;/p&gt;

&lt;p&gt;Payments&lt;/p&gt;

&lt;p&gt;User registration&lt;/p&gt;

&lt;p&gt;Major workflows&lt;/p&gt;

&lt;p&gt;Cross-browser validation&lt;/p&gt;

&lt;p&gt;API automation&lt;br&gt;
Use for:&lt;/p&gt;

&lt;p&gt;Business rules&lt;/p&gt;

&lt;p&gt;CRUD operations&lt;/p&gt;

&lt;p&gt;Authentication&lt;/p&gt;

&lt;p&gt;Authorization&lt;/p&gt;

&lt;p&gt;Data validation&lt;/p&gt;

&lt;p&gt;Negative scenarios&lt;/p&gt;

&lt;p&gt;Integration workflows&lt;/p&gt;

&lt;p&gt;Accessibility automation&lt;br&gt;
Use for:&lt;/p&gt;

&lt;p&gt;Keyboard navigation&lt;/p&gt;

&lt;p&gt;Semantic structure&lt;/p&gt;

&lt;p&gt;Form accessibility&lt;/p&gt;

&lt;p&gt;Labels&lt;/p&gt;

&lt;p&gt;ARIA attributes&lt;/p&gt;

&lt;p&gt;Color/contrast checks where automated tooling can detect them&lt;/p&gt;

&lt;p&gt;Automated WCAG checks&lt;/p&gt;

&lt;p&gt;Accessibility automation doesn’t replace manual accessibility assessment, but it can continuously catch many common issues.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;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:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;playwright-automation/&lt;br&gt;
│&lt;br&gt;
├── tests/&lt;br&gt;
│   ├── ui/&lt;br&gt;
│   │   ├── login.spec.ts&lt;br&gt;
│   │   ├── customer.spec.ts&lt;br&gt;
│   │   └── opportunity.spec.ts&lt;br&gt;
│   │&lt;br&gt;
│   ├── api/&lt;br&gt;
│   │   ├── customer-api.spec.ts&lt;br&gt;
│   │   └── opportunity-api.spec.ts&lt;br&gt;
│   │&lt;br&gt;
│   └── accessibility/&lt;br&gt;
│       ├── login-a11y.spec.ts&lt;br&gt;
│       └── dashboard-a11y.spec.ts&lt;br&gt;
│&lt;br&gt;
├── pages/&lt;br&gt;
│   ├── LoginPage.ts&lt;br&gt;
│   ├── DashboardPage.ts&lt;br&gt;
│   ├── CustomerPage.ts&lt;br&gt;
│   └── OpportunityPage.ts&lt;br&gt;
│&lt;br&gt;
├── api/&lt;br&gt;
│   ├── CustomerApi.ts&lt;br&gt;
│   └── OpportunityApi.ts&lt;br&gt;
│&lt;br&gt;
├── fixtures/&lt;br&gt;
│   └── test-fixtures.ts&lt;br&gt;
│&lt;br&gt;
├── utils/&lt;br&gt;
│   ├── test-data.ts&lt;br&gt;
│   ├── api-utils.ts&lt;br&gt;
│   └── accessibility-utils.ts&lt;br&gt;
│&lt;br&gt;
├── config/&lt;br&gt;
│&lt;br&gt;
├── playwright.config.ts&lt;br&gt;
│&lt;br&gt;
└── package.json&lt;br&gt;
The exact structure can change depending on the product, but the principle remains:&lt;/p&gt;

&lt;p&gt;Keep test intent separate from implementation details.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use Page Object Model for UI Automation
I use Page Object Model (POM) to separate page behavior from test scenarios, for example:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;import { Page, Locator } from ‘@playwright/test’;&lt;br&gt;
export class LoginPage {&lt;br&gt;
  readonly page: Page;&lt;br&gt;
  readonly username: Locator;&lt;br&gt;
  readonly password: Locator;&lt;br&gt;
  readonly loginButton: Locator;  constructor(page: Page) {&lt;br&gt;
    this.page = page;&lt;br&gt;
    this.username = page.getByLabel(’Username’);&lt;br&gt;
    this.password = page.getByLabel(’Password’);&lt;br&gt;
    this.loginButton = page.getByRole(’button’, {&lt;br&gt;
      name: ‘Login’&lt;br&gt;
    });&lt;br&gt;
  }  async login(username: string, password: string) {&lt;br&gt;
    await this.username.fill(username);&lt;br&gt;
    await this.password.fill(password);&lt;br&gt;
    await this.loginButton.click();&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
Then the test remains focused on the business scenario:&lt;/p&gt;

&lt;p&gt;test(’user can login successfully’, async ({ page }) =&amp;gt; {&lt;br&gt;
  const loginPage = new LoginPage(page);&lt;br&gt;
  await loginPage.login(&lt;br&gt;
    process.env.USERNAME!,&lt;br&gt;
    process.env.PASSWORD!&lt;br&gt;
  );  await expect(page).toHaveURL(/dashboard/);&lt;br&gt;
});&lt;br&gt;
The test describes what the user does.&lt;/p&gt;

&lt;p&gt;The page object describes how the application is interacted with.&lt;/p&gt;

&lt;p&gt;That separation becomes extremely valuable as the application grows.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;CustomerPage&lt;br&gt;
CustomerForm&lt;br&gt;
CustomerTable&lt;br&gt;
CustomerDetails&lt;br&gt;
OpportunityPage&lt;br&gt;
OpportunityForm&lt;br&gt;
OpportunityTable&lt;br&gt;
OpportunityDetails&lt;br&gt;
This improves:&lt;/p&gt;

&lt;p&gt;Maintainability&lt;/p&gt;

&lt;p&gt;Reusability&lt;/p&gt;

&lt;p&gt;Debugging&lt;/p&gt;

&lt;p&gt;Code ownership&lt;/p&gt;

&lt;p&gt;Parallel development&lt;/p&gt;

&lt;p&gt;The framework should evolve with the product rather than becoming another source of technical debt.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;import { APIRequestContext } from ‘@playwright/test’;&lt;br&gt;
export class CustomerApi {&lt;br&gt;
  constructor(private request: APIRequestContext) {}  async createCustomer(data: object) {&lt;br&gt;
    return await this.request.post(’/api/customers’, {&lt;br&gt;
      data&lt;br&gt;
    });&lt;br&gt;
  }  async getCustomer(id: string) {&lt;br&gt;
    return await this.request.get(&lt;code&gt;/api/customers/${id}&lt;/code&gt;);&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
Now we can validate backend behavior directly, for example:&lt;/p&gt;

&lt;p&gt;const response = await customerApi.createCustomer({&lt;br&gt;
  name: ‘Automation Customer’,&lt;br&gt;
  email: ‘&lt;a href="mailto:automation@example.com"&gt;automation@example.com&lt;/a&gt;’&lt;br&gt;
});&lt;br&gt;
expect(response.ok()).toBeTruthy();const body = await response.json();expect(body.name).toBe(’Automation Customer’);&lt;br&gt;
This gives us much faster feedback than reaching the same functionality exclusively through the UI.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;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:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;API&lt;br&gt;
 ↓&lt;br&gt;
Create test customer&lt;br&gt;
 ↓&lt;br&gt;
API&lt;br&gt;
 ↓&lt;br&gt;
Create opportunity&lt;br&gt;
 ↓&lt;br&gt;
UI&lt;br&gt;
 ↓&lt;br&gt;
Login&lt;br&gt;
 ↓&lt;br&gt;
Open opportunity&lt;br&gt;
 ↓&lt;br&gt;
Verify data&lt;br&gt;
 ↓&lt;br&gt;
UI action&lt;br&gt;
 ↓&lt;br&gt;
Update opportunity&lt;br&gt;
 ↓&lt;br&gt;
API&lt;br&gt;
 ↓&lt;br&gt;
Verify backend state&lt;br&gt;
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.&lt;/p&gt;

&lt;p&gt;This makes end-to-end tests:&lt;/p&gt;

&lt;p&gt;Faster&lt;/p&gt;

&lt;p&gt;More deterministic&lt;/p&gt;

&lt;p&gt;Easier to maintain&lt;/p&gt;

&lt;p&gt;Less dependent on previous tests&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;p&gt;import AxeBuilder from ‘@axe-core/playwright’;&lt;br&gt;
test(’dashboard accessibility’, async ({ page }) =&amp;gt; {&lt;br&gt;
  await page.goto(’/dashboard’);  const results = await new AxeBuilder({&lt;br&gt;
    page&lt;br&gt;
  }).analyze();  expect(results.violations).toEqual([]);&lt;br&gt;
});&lt;br&gt;
Accessibility checks can now run as part of the automated test suite, we can also include targeted checks for:&lt;/p&gt;

&lt;p&gt;Accessible names&lt;/p&gt;

&lt;p&gt;Form labels&lt;/p&gt;

&lt;p&gt;Keyboard navigation&lt;/p&gt;

&lt;p&gt;Heading structure&lt;/p&gt;

&lt;p&gt;ARIA attributes&lt;/p&gt;

&lt;p&gt;Focus behavior&lt;/p&gt;

&lt;p&gt;Interactive elements&lt;/p&gt;

&lt;p&gt;For organizations with formal accessibility requirements, automated checks should be combined with manual testing and appropriate accessibility expertise.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use Fixtures for Reusable Test Setup
As the product grows, test setup becomes increasingly important. Instead of repeating authentication and common setup:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;test.beforeEach(async ({ page }) =&amp;gt; {&lt;br&gt;
  // login&lt;br&gt;
});&lt;br&gt;
I prefer creating reusable Playwright fixtures, for example:&lt;/p&gt;

&lt;p&gt;export const test = base.extend&amp;lt;{&lt;br&gt;
  authenticatedPage: Page;&lt;br&gt;
}&amp;gt;({&lt;br&gt;
  authenticatedPage: async ({ page }, use) =&amp;gt; {&lt;br&gt;
    // authentication logic    await use(page);&lt;br&gt;
  }&lt;br&gt;
});&lt;br&gt;
Then tests can focus on the scenario:&lt;/p&gt;

&lt;p&gt;test(’create customer’, async ({&lt;br&gt;
  authenticatedPage&lt;br&gt;
}) =&amp;gt; {&lt;br&gt;
  // test scenario});&lt;br&gt;
Fixtures can also provide:&lt;/p&gt;

&lt;p&gt;API clients&lt;/p&gt;

&lt;p&gt;Test data&lt;/p&gt;

&lt;p&gt;Authenticated sessions&lt;/p&gt;

&lt;p&gt;Database helpers&lt;/p&gt;

&lt;p&gt;Environment configuration&lt;/p&gt;

&lt;p&gt;Custom utilities&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Design for Parallel Execution
A new product can start with 20 tests, six months later it might have:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;500+ tests.&lt;/p&gt;

&lt;p&gt;If every test runs sequentially, execution time becomes a bottleneck.&lt;/p&gt;

&lt;p&gt;That’s why I design the framework with parallel execution in mind.&lt;/p&gt;

&lt;p&gt;Playwright provides:&lt;/p&gt;

&lt;p&gt;Workers&lt;/p&gt;

&lt;p&gt;Parallel test execution&lt;/p&gt;

&lt;p&gt;Projects&lt;/p&gt;

&lt;p&gt;Sharding&lt;/p&gt;

&lt;p&gt;Retries&lt;/p&gt;

&lt;p&gt;Trace viewer&lt;/p&gt;

&lt;p&gt;HTML reporting&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Worker 1 → Tests 1–50&lt;br&gt;
Worker 2 → Tests 51–100&lt;br&gt;
Worker 3 → Tests 101–150&lt;br&gt;
Worker 4 → Tests 151–200&lt;br&gt;
This allows the regression suite to scale with the product.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build the CI/CD Pipeline
Automation becomes significantly more valuable when it runs automatically. A typical pipeline could look like:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Developer Push&lt;br&gt;
      ↓&lt;br&gt;
Pull Request&lt;br&gt;
      ↓&lt;br&gt;
Build&lt;br&gt;
      ↓&lt;br&gt;
Lint&lt;br&gt;
      ↓&lt;br&gt;
Unit Tests&lt;br&gt;
      ↓&lt;br&gt;
API Tests&lt;br&gt;
      ↓&lt;br&gt;
Playwright UI Tests&lt;br&gt;
      ↓&lt;br&gt;
Accessibility Tests&lt;br&gt;
      ↓&lt;br&gt;
Reports&lt;br&gt;
      ↓&lt;br&gt;
Deploy&lt;br&gt;
For example, GitHub Actions can execute Playwright:&lt;/p&gt;

&lt;p&gt;name: Playwright Tests&lt;br&gt;
on:&lt;br&gt;
  pull_request:&lt;br&gt;
  push:jobs:&lt;br&gt;
  test:&lt;br&gt;
    runs-on: ubuntu-latest    steps:&lt;br&gt;
      - uses: actions/checkout@v4      - uses: actions/setup-node@v4&lt;br&gt;
        with:&lt;br&gt;
          node-version: 20      - run: npm ci      - run: npx playwright install --with-deps      - run: npx playwright test      - name: Upload report&lt;br&gt;
        if: always()&lt;br&gt;
        uses: actions/upload-artifact@v4&lt;br&gt;
        with:&lt;br&gt;
          name: playwright-report&lt;br&gt;
          path: playwright-report/&lt;br&gt;
Now every code change gets automated quality feedback.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Separate Smoke, Regression and Critical Tests
Not every test needs to run on every commit. I usually categorize tests.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Smoke&lt;br&gt;
Critical functionality:&lt;/p&gt;

&lt;p&gt;Login&lt;br&gt;
Dashboard&lt;br&gt;
Core transaction&lt;br&gt;
Logout&lt;br&gt;
Regression&lt;br&gt;
Broader business functionality:&lt;/p&gt;

&lt;p&gt;Customers&lt;br&gt;
Opportunities&lt;br&gt;
Reports&lt;br&gt;
Notifications&lt;br&gt;
Permissions&lt;br&gt;
Integrations&lt;br&gt;
Accessibility&lt;br&gt;
Critical pages&lt;br&gt;
Forms&lt;br&gt;
Navigation&lt;br&gt;
Interactive components&lt;br&gt;
Full E2E&lt;br&gt;
Complete business journeys:&lt;/p&gt;

&lt;p&gt;Login&lt;br&gt;
→ Create&lt;br&gt;
→ Process&lt;br&gt;
→ Approve&lt;br&gt;
→ Complete&lt;br&gt;
→ Verify&lt;br&gt;
Then CI can run the appropriate suite depending on the event.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make Test Data a First-Class Concern
Poor test data management can destroy an otherwise good automation framework. I prefer:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Unique test data&lt;/p&gt;

&lt;p&gt;API-based data creation&lt;/p&gt;

&lt;p&gt;Environment-specific configuration&lt;/p&gt;

&lt;p&gt;Cleanup strategies&lt;/p&gt;

&lt;p&gt;Independent test data&lt;/p&gt;

&lt;p&gt;No dependency on another test’s execution&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;const customer = {&lt;br&gt;
  name: &lt;code&gt;Automation-${Date.now()}&lt;/code&gt;,&lt;br&gt;
  email: &lt;code&gt;qa-${Date.now()}@example.com&lt;/code&gt;&lt;br&gt;
};&lt;br&gt;
The objective is simple:&lt;/p&gt;

&lt;p&gt;Every test should be capable of running independently.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add Observability to Test Failures
A failed test should tell the developer why it failed. I enable Playwright artifacts such as:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Screenshots&lt;/p&gt;

&lt;p&gt;Videos where appropriate&lt;/p&gt;

&lt;p&gt;Traces&lt;/p&gt;

&lt;p&gt;Console logs&lt;/p&gt;

&lt;p&gt;Network information&lt;/p&gt;

&lt;p&gt;HTML reports&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;use: {&lt;br&gt;
  trace: ‘retain-on-failure’,&lt;br&gt;
  screenshot: ‘only-on-failure’,&lt;br&gt;
  video: ‘retain-on-failure’&lt;br&gt;
}&lt;br&gt;
When a test fails in CI, the developer shouldn’t need to reproduce it locally just to understand the failure.&lt;/p&gt;

&lt;p&gt;The automation framework should provide the evidence.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Measure Automation Like an Engineering System
I don’t consider automation successful simply because:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;“We have 500 automated tests.”&lt;/p&gt;

&lt;p&gt;Instead, I track meaningful metrics.&lt;/p&gt;

&lt;p&gt;Useful metrics&lt;br&gt;
Automation coverage&lt;/p&gt;

&lt;h2&gt;
  
  
  Automated critical scenarios
&lt;/h2&gt;

&lt;p&gt;Total critical scenarios&lt;br&gt;
Pass rate&lt;/p&gt;

&lt;h2&gt;
  
  
  Passed tests
&lt;/h2&gt;

&lt;p&gt;Executed tests&lt;br&gt;
Flaky test rate&lt;/p&gt;

&lt;h2&gt;
  
  
  Flaky executions
&lt;/h2&gt;

&lt;p&gt;Total executions&lt;br&gt;
Regression execution time&lt;/p&gt;

&lt;p&gt;Before automation → 6 hours&lt;br&gt;
After optimization → 45 minutes&lt;br&gt;
Other useful metrics include:&lt;/p&gt;

&lt;p&gt;Defect escape rate&lt;/p&gt;

&lt;p&gt;Mean time to detect&lt;/p&gt;

&lt;p&gt;Mean time to diagnose&lt;/p&gt;

&lt;p&gt;CI failure categories&lt;/p&gt;

&lt;p&gt;API coverage&lt;/p&gt;

&lt;p&gt;Accessibility violation trends&lt;/p&gt;

&lt;p&gt;Automation maintenance effort&lt;/p&gt;

&lt;p&gt;The purpose of these metrics isn’t to produce impressive dashboards.&lt;/p&gt;

&lt;p&gt;It’s to identify where the quality process is slowing down delivery.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What My End-to-End Strategy Looks Like
For a completely new product, my approach can be summarized as:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;PRODUCT REQUIREMENTS&lt;br&gt;
                        │&lt;br&gt;
                        ▼&lt;br&gt;
                 RISK ANALYSIS&lt;br&gt;
                        │&lt;br&gt;
                        ▼&lt;br&gt;
              AUTOMATION STRATEGY&lt;br&gt;
                        │&lt;br&gt;
           ┌────────────┼────────────┐&lt;br&gt;
           ▼            ▼            ▼&lt;br&gt;
          UI           API       ACCESSIBILITY&lt;br&gt;
           │            │            │&lt;br&gt;
           ▼            ▼            ▼&lt;br&gt;
       Playwright   APIRequest    axe-core&lt;br&gt;
           │            │            │&lt;br&gt;
           └────────────┼────────────┘&lt;br&gt;
                        ▼&lt;br&gt;
                  POM + Fixtures&lt;br&gt;
                        │&lt;br&gt;
                        ▼&lt;br&gt;
                Test Data Strategy&lt;br&gt;
                        │&lt;br&gt;
                        ▼&lt;br&gt;
               Parallel Execution&lt;br&gt;
                        │&lt;br&gt;
                        ▼&lt;br&gt;
                     CI/CD&lt;br&gt;
                        │&lt;br&gt;
                        ▼&lt;br&gt;
              Reports + Artifacts&lt;br&gt;
                        │&lt;br&gt;
                        ▼&lt;br&gt;
                 Quality Metrics&lt;br&gt;
                        │&lt;br&gt;
                        ▼&lt;br&gt;
               Continuous Improvement&lt;br&gt;
This is how I think about test automation:&lt;/p&gt;

&lt;p&gt;Not as a collection of scripts, but as an engineering system.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Goal Isn’t 100% Automation
One of the biggest misconceptions in automation is:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;“We need to automate everything.” I don’t agree with that approach.&lt;/p&gt;

&lt;p&gt;Some tests are better suited for:&lt;/p&gt;

&lt;p&gt;Manual exploratory testing&lt;/p&gt;

&lt;p&gt;Usability testing&lt;/p&gt;

&lt;p&gt;Visual assessment&lt;/p&gt;

&lt;p&gt;Accessibility assessment&lt;/p&gt;

&lt;p&gt;Investigative testing&lt;/p&gt;

&lt;p&gt;New feature exploration&lt;/p&gt;

&lt;p&gt;Automation is most valuable when it handles repetitive, predictable and high-value validation.&lt;/p&gt;

&lt;p&gt;The goal is not:&lt;/p&gt;

&lt;p&gt;100% automated testing.&lt;/p&gt;

&lt;p&gt;The goal is:&lt;/p&gt;

&lt;p&gt;Fast, reliable and actionable feedback.&lt;/p&gt;

&lt;p&gt;My Playwright Automation Stack&lt;br&gt;
For a modern web product, my preferred stack can look like:&lt;/p&gt;

&lt;p&gt;Language&lt;br&gt;
TypeScript&lt;br&gt;
UI Automation&lt;br&gt;
PlaywrightAPI Automation&lt;br&gt;
Playwright APIRequestContextAccessibility&lt;br&gt;
axe-core + PlaywrightArchitecture&lt;br&gt;
Page Object ModelTest Data&lt;br&gt;
Fixtures + APICI/CD&lt;br&gt;
GitHub Actions / Azure DevOps / JenkinsReporting&lt;br&gt;
Playwright HTML ReportVersion Control&lt;br&gt;
Git / GitHubTest Management&lt;br&gt;
Jira / Azure DevOps&lt;br&gt;
The exact stack should always adapt to the product and engineering ecosystem, not to popularity.&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;br&gt;
When I join a new product, I don’t start by asking:&lt;/p&gt;

&lt;p&gt;“Which test cases should I automate first?”&lt;/p&gt;

&lt;p&gt;I start by asking:&lt;/p&gt;

&lt;p&gt;“Which risks can automation help us detect earlier and more reliably?”&lt;/p&gt;

&lt;p&gt;From there, I design the automation architecture around:&lt;/p&gt;

&lt;p&gt;Business risk → UI → API → Accessibility → Test data → Framework → CI/CD → Reporting → Metrics&lt;/p&gt;

&lt;p&gt;With Playwright, TypeScript, API automation, accessibility checks and CI/CD working together, automation becomes more than regression testing.&lt;/p&gt;

&lt;p&gt;It becomes part of the software delivery system.&lt;/p&gt;

&lt;p&gt;And that’s the approach I believe modern QA teams should move toward:&lt;/p&gt;

&lt;p&gt;Build quality into the delivery pipeline — not inspect quality only at the end.&lt;/p&gt;

&lt;p&gt;Looking to Build or Improve Your Playwright Automation?&lt;br&gt;
I work with teams that need help with:&lt;/p&gt;

&lt;p&gt;✓ Playwright automation frameworks from scratch&lt;br&gt;
✓ UI + API end-to-end automation&lt;br&gt;
✓ Page Object Model architecture&lt;br&gt;
✓ CI/CD integration&lt;br&gt;
✓ Accessibility automation&lt;br&gt;
✓ Regression automation&lt;br&gt;
✓ Flaky-test investigation and optimization&lt;br&gt;
✓ QA automation strategy for new products&lt;br&gt;
✓ AI-assisted QA and test automation&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;I’m always interested in discussing practical QA automation challenges and helping teams build a reliable path from requirements → automation → CI/CD → release.&lt;/p&gt;

&lt;p&gt;Sai Krishna Oggu&lt;br&gt;
Senior QA Automation Engineer | SDET | Playwright | TypeScript | AI-Driven Testing&lt;/p&gt;

&lt;p&gt;🌐 Website: saikrishnaoggu.ai.studio&lt;/p&gt;

&lt;p&gt;💼LinkedIn: Connect with me on LinkedIn&lt;/p&gt;

&lt;p&gt;Great products aren’t just built to work — they’re built to keep working.&lt;br&gt;
Let’s make quality part of the journey, not the final checkpoint. 🚀&lt;/p&gt;

&lt;h1&gt;
  
  
  Playwright #TestAutomation #QAAutomation #SDET #TypeScript #APITesting #E2ETesting #AccessibilityTesting #CICD #QualityEngineering #SoftwareTesting #AITesting #TestAutomationEngineer
&lt;/h1&gt;

</description>
      <category>playwright</category>
      <category>automation</category>
      <category>testing</category>
      <category>ai</category>
    </item>
    <item>
      <title>Why Do Playwright Tests Pass Locally but Fail in CI/CD?</title>
      <dc:creator>Sai Krishna Oggu</dc:creator>
      <pubDate>Sat, 19 Sep 2026 08:09:42 +0000</pubDate>
      <link>https://dev.to/saikrishna_oggu/why-do-playwright-tests-pass-locally-but-fail-in-cicd-9ca</link>
      <guid>https://dev.to/saikrishna_oggu/why-do-playwright-tests-pass-locally-but-fail-in-cicd-9ca</guid>
      <description>&lt;p&gt;“It works perfectly on my machine.”&lt;/p&gt;

&lt;p&gt;Every automation engineer has said it.&lt;/p&gt;

&lt;p&gt;Your Playwright test passes 20 times locally.&lt;br&gt;
You push the code.&lt;br&gt;
The CI pipeline starts.&lt;br&gt;
And suddenly:&lt;/p&gt;

&lt;p&gt;❌ Test failed.&lt;/p&gt;

&lt;p&gt;You run it again locally.&lt;/p&gt;

&lt;p&gt;✅ Passed.&lt;/p&gt;

&lt;p&gt;So, what changed?&lt;/p&gt;

&lt;p&gt;Usually, the test didn’t suddenly become bad. The environment changed.&lt;/p&gt;

&lt;p&gt;Here are the most common reasons behind this frustrating problem — and how to debug them.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Timing Issues — The Classic Flaky Test
Your local machine may give the application enough time to render.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;CI may be under CPU or memory pressure, making everything slightly slower.&lt;/p&gt;

&lt;p&gt;A fragile test might look like:&lt;/p&gt;

&lt;p&gt;await page.click('#submit');&lt;br&gt;
await page.waitForTimeout(1000);&lt;br&gt;
expect(await page.locator('.success-message').isVisible()).toBeTruthy();&lt;br&gt;
Instead, let Playwright wait for the expected state:&lt;/p&gt;

&lt;p&gt;await page.getByRole('button', { name: 'Submit' }).click();&lt;br&gt;
await expect(&lt;br&gt;
  page.getByText('Success')&lt;br&gt;
).toBeVisible();&lt;br&gt;
Rule:&lt;br&gt;
Don’t wait for time. Wait for state.&lt;/p&gt;

&lt;p&gt;Avoid using waitForTimeout() as a solution for flaky tests.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your Local Environment ≠ CI Environment
This is one of the first things I check. Your local environment might use:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;QA1&lt;br&gt;
while the pipeline is configured for:&lt;/p&gt;

&lt;p&gt;QA2&lt;br&gt;
Or your local .env may contain variables that aren't available in CI.&lt;/p&gt;

&lt;p&gt;Check:&lt;/p&gt;

&lt;p&gt;baseURL&lt;br&gt;
API endpoints&lt;br&gt;
environment variables&lt;br&gt;
feature flags&lt;br&gt;
tenant configuration&lt;br&gt;
test credentials&lt;br&gt;
database state&lt;br&gt;
A test can be perfectly written and still fail because it is talking to the wrong environment.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Browser or Playwright Version Differences
Your local machine may be running one browser version while CI runs another.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Check your Playwright version:&lt;/p&gt;

&lt;p&gt;npx playwright --version&lt;br&gt;
Make sure your project uses a consistent dependency version and that CI installs the expected browsers.&lt;/p&gt;

&lt;p&gt;For CI environments, browser installation should be explicit when needed:&lt;/p&gt;

&lt;p&gt;npx playwright install --with-deps&lt;br&gt;
Consistency matters.&lt;/p&gt;

&lt;p&gt;Same code + different browser/runtime = potentially different behavior.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Headless vs Headed Execution
Most CI pipelines run Playwright in headless mode.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Locally, you may be running:&lt;/p&gt;

&lt;p&gt;npx playwright test --headed&lt;br&gt;
while CI runs:&lt;/p&gt;

&lt;p&gt;npx playwright test&lt;br&gt;
This can expose problems involving:&lt;/p&gt;

&lt;p&gt;animations&lt;br&gt;
responsive layouts&lt;br&gt;
viewport assumptions&lt;br&gt;
visual elements&lt;br&gt;
timing&lt;br&gt;
Try reproducing the pipeline locally in headless mode.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parallel Execution Can Expose Hidden Dependencies
This is a big one, you might run locally:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;npx playwright test --workers=1&lt;br&gt;
But your CI pipeline may execute multiple workers, now imagine two tests using the same user:&lt;/p&gt;

&lt;p&gt;Test A → Update User&lt;br&gt;
Test B → Delete User&lt;br&gt;
Run independently:&lt;/p&gt;

&lt;p&gt;PASS ✅&lt;/p&gt;

&lt;p&gt;Run simultaneously:&lt;/p&gt;

&lt;p&gt;FAIL ❌&lt;/p&gt;

&lt;p&gt;Shared test data is often the real culprit, watch for dependencies involving:&lt;/p&gt;

&lt;p&gt;users&lt;br&gt;
database records&lt;br&gt;
files&lt;br&gt;
API data&lt;br&gt;
tenants&lt;br&gt;
orders&lt;br&gt;
accounts&lt;br&gt;
Tests should be as isolated as possible.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Test Data Exists Locally — but Not in CI
Your local database may already contain:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;User: testuser123&lt;br&gt;
Order: 45678&lt;br&gt;
Tenant: ABC&lt;br&gt;
Your pipeline may not.&lt;/p&gt;

&lt;p&gt;Download the Medium app&lt;br&gt;
If your test assumes that data exists, it can fail immediately.&lt;/p&gt;

&lt;p&gt;Instead of relying on existing state, create the required data as part of the test setup.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;test.beforeEach(async ({ request }) =&amp;gt; {&lt;br&gt;
  await createTestUser(request);&lt;br&gt;
});&lt;br&gt;
A reliable test should control the data it depends on.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Authentication &amp;amp; Storage State
Another common issue:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Local&lt;br&gt;
↓&lt;br&gt;
Valid storageState&lt;br&gt;
↓&lt;br&gt;
Authenticated&lt;br&gt;
↓&lt;br&gt;
Test passes&lt;br&gt;
CI:&lt;/p&gt;

&lt;p&gt;Missing/expired storageState&lt;br&gt;
↓&lt;br&gt;
Login/session fails&lt;br&gt;
↓&lt;br&gt;
Test fails&lt;br&gt;
Check:&lt;/p&gt;

&lt;p&gt;cookies&lt;br&gt;
access tokens&lt;br&gt;
session state&lt;br&gt;
authentication setup&lt;br&gt;
CI secrets&lt;br&gt;
storage state files&lt;br&gt;
Don’t assume authentication works simply because it works locally.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;CI Secrets Are Different
You may have:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;process.env.USERNAME&lt;br&gt;
process.env.PASSWORD&lt;br&gt;
process.env.BASE_URL&lt;br&gt;
working locally because they’re defined in your .env.&lt;/p&gt;

&lt;p&gt;But CI needs those values configured separately.&lt;/p&gt;

&lt;p&gt;A missing environment variable can create a failure that looks like a Playwright problem — but isn’t.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Timezone Differences
Here’s a subtle one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your machine:&lt;/p&gt;

&lt;p&gt;IST&lt;br&gt;
CI server:&lt;/p&gt;

&lt;p&gt;UTC&lt;br&gt;
Now consider a test involving:&lt;/p&gt;

&lt;p&gt;Today's date&lt;br&gt;
Expiry date&lt;br&gt;
Scheduled job&lt;br&gt;
Booking time&lt;br&gt;
Timestamp&lt;br&gt;
Your test may behave differently.&lt;/p&gt;

&lt;p&gt;If dates matter, make timezone assumptions explicit rather than relying on the machine’s local timezone.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Windows Works. Linux Doesn’t.
This catches many automation engineers; your local machine may be Windows.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your CI runner may be Linux.&lt;/p&gt;

&lt;p&gt;Windows filesystem handling can be different from Linux, particularly around filename casing.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;import LoginPage from './pages/loginpage';&lt;br&gt;
while the actual file is:&lt;/p&gt;

&lt;p&gt;LoginPage.ts&lt;br&gt;
It may work locally and fail in a Linux pipeline.&lt;/p&gt;

&lt;p&gt;Treat filenames and imports as case-sensitive.&lt;/p&gt;

&lt;p&gt;The Most Powerful Debugging Tool: Trace Viewer&lt;br&gt;
When a test fails in CI, don’t immediately increase the timeout.&lt;/p&gt;

&lt;p&gt;First, find out why it failed.&lt;/p&gt;

&lt;p&gt;Configure Playwright:&lt;/p&gt;

&lt;p&gt;use: {&lt;br&gt;
  trace: 'retain-on-failure',&lt;br&gt;
  screenshot: 'only-on-failure',&lt;br&gt;
  video: 'retain-on-failure'&lt;br&gt;
}&lt;br&gt;
Then inspect the trace:&lt;/p&gt;

&lt;p&gt;npx playwright show-trace trace.zip&lt;br&gt;
You can investigate:&lt;/p&gt;

&lt;p&gt;the exact action that failed&lt;br&gt;
DOM state&lt;br&gt;
screenshots&lt;br&gt;
network activity&lt;br&gt;
console messages&lt;br&gt;
timing&lt;br&gt;
locator behavior&lt;br&gt;
Instead of guessing:&lt;/p&gt;

&lt;p&gt;“Maybe the page wasn’t loaded?”&lt;/p&gt;

&lt;p&gt;You can actually see what happened.&lt;/p&gt;

&lt;p&gt;My CI Debugging Checklist&lt;br&gt;
When a Playwright test passes locally but fails in CI, I check these in roughly this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Environment / Base URL
    ↓&lt;/li&gt;
&lt;li&gt;Test data
    ↓&lt;/li&gt;
&lt;li&gt;Authentication
    ↓&lt;/li&gt;
&lt;li&gt;Timing / synchronization
    ↓&lt;/li&gt;
&lt;li&gt;Browser &amp;amp; Playwright versions
    ↓&lt;/li&gt;
&lt;li&gt;Headless execution
    ↓&lt;/li&gt;
&lt;li&gt;Parallel workers
    ↓&lt;/li&gt;
&lt;li&gt;Viewport / responsive behavior
    ↓&lt;/li&gt;
&lt;li&gt;Timezone
    ↓&lt;/li&gt;
&lt;li&gt;CI resources / network
Then I reproduce the pipeline conditions locally:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;npx playwright test --workers=1&lt;br&gt;
and inspect the trace if the failure persists.&lt;/p&gt;

&lt;p&gt;The Bigger Lesson&lt;br&gt;
A CI failure isn’t necessarily a Playwright problem.&lt;/p&gt;

&lt;p&gt;It can reveal a weakness in your:&lt;/p&gt;

&lt;p&gt;test → data → environment → application → infrastructure&lt;/p&gt;

&lt;p&gt;chain.&lt;/p&gt;

&lt;p&gt;That’s why good automation isn’t just about writing locators.&lt;/p&gt;

&lt;p&gt;It’s about creating tests that are:&lt;/p&gt;

&lt;p&gt;Reliable. Isolated. Reproducible. Observable.&lt;/p&gt;

&lt;p&gt;The goal isn’t to make CI green by adding more waits.&lt;/p&gt;

&lt;p&gt;The goal is to understand why it wasn’t green in the first place.&lt;/p&gt;

&lt;p&gt;Final Thought&lt;br&gt;
Local passing tells you the test can work.&lt;br&gt;
CI passing tells you the test can be trusted.&lt;/p&gt;

&lt;p&gt;And that’s the real difference between a test that runs and an automation suite you can rely on.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>cicd</category>
      <category>automation</category>
      <category>ai</category>
    </item>
    <item>
      <title>Integrating Playwright with CI/CD Using GitHub Actions</title>
      <dc:creator>Sai Krishna Oggu</dc:creator>
      <pubDate>Fri, 18 Sep 2026 10:20:54 +0000</pubDate>
      <link>https://dev.to/saikrishna_oggu/integrating-playwright-with-cicd-using-github-actions-3nl9</link>
      <guid>https://dev.to/saikrishna_oggu/integrating-playwright-with-cicd-using-github-actions-3nl9</guid>
      <description>&lt;p&gt;Automating Playwright tests locally is useful, but running them automatically in a CI/CD pipeline gives teams faster feedback on every code change.&lt;/p&gt;

&lt;p&gt;In this guide, we'll integrate Playwright with &lt;strong&gt;GitHub Actions&lt;/strong&gt; using a simple YAML workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Project Setup
&lt;/h2&gt;

&lt;p&gt;Assuming you already have a Playwright project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;playwright-project/
├── tests/
├── playwright.config.ts
├── package.json
└── package-lock.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure the tests run locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx playwright &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Create the GitHub Actions Workflow
&lt;/h2&gt;

&lt;p&gt;Create the following file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.github/workflows/playwright.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Playwright Tests&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Playwright browsers&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx playwright install --with-deps&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Playwright tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx playwright test&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload Playwright report&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always()&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;playwright-report&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;playwright-report/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. How It Works
&lt;/h2&gt;

&lt;p&gt;Whenever code is pushed to &lt;code&gt;main&lt;/code&gt; or a pull request is created, GitHub Actions will:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Code Push / Pull Request
          ↓
   Checkout Repository
          ↓
     Install Node.js
          ↓
    Install Dependencies
          ↓
 Install Playwright Browsers
          ↓
    Run Playwright Tests
          ↓
    Generate Test Report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a test fails, the workflow fails and the Playwright report is still uploaded because of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes it easier to investigate failures directly from GitHub Actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Why Integrate Playwright with CI/CD?
&lt;/h2&gt;

&lt;p&gt;CI/CD integration helps teams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run tests automatically&lt;/li&gt;
&lt;li&gt;Catch regressions early&lt;/li&gt;
&lt;li&gt;Validate pull requests&lt;/li&gt;
&lt;li&gt;Execute tests consistently&lt;/li&gt;
&lt;li&gt;Store reports and artifacts&lt;/li&gt;
&lt;li&gt;Reduce manual testing effort&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For larger frameworks, you can extend the pipeline with &lt;strong&gt;parallel execution, environment variables, secrets, scheduled regression runs, test sharding, and deployment gates&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Thought
&lt;/h3&gt;

&lt;p&gt;Playwright becomes much more valuable when automation is part of the development pipeline—not just something executed manually on a QA engineer's machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write the tests once. Run them continuously. Get feedback early.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>automation</category>
      <category>cicd</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>🚀 1,200+ Test Cases Migrated from Tosca to Playwright – A Major QA Transformation Success! 🚀</title>
      <dc:creator>Sai Krishna Oggu</dc:creator>
      <pubDate>Mon, 08 Jun 2026 03:46:01 +0000</pubDate>
      <link>https://dev.to/saikrishna_oggu/1200-test-cases-migrated-from-tosca-to-playwright-a-major-qa-transformation-success-5f3g</link>
      <guid>https://dev.to/saikrishna_oggu/1200-test-cases-migrated-from-tosca-to-playwright-a-major-qa-transformation-success-5f3g</guid>
      <description>&lt;p&gt;Excited to share the successful migration of 1,200+ automated test cases from Tosca to Playwright, helping our customer modernize their automation landscape and build a faster, more scalable, and cost-effective testing ecosystem.&lt;/p&gt;

&lt;p&gt;Why Move from Tosca to Playwright?&lt;br&gt;
✅ No Licensing Costs – Open-source framework that eliminates licensing fees and reduces automation costs.&lt;br&gt;
✅ Faster Test Execution – Native parallel execution enables significantly quicker regression cycles.&lt;br&gt;
✅ Better CI/CD Integration – Seamlessly integrates with modern DevOps and continuous testing pipelines.&lt;br&gt;
✅ Superior Web Application Support – Auto-waiting and robust locators improve test stability and reliability.&lt;br&gt;
✅ Rich Debugging &amp;amp; Reporting – Built-in traces, screenshots, videos, and logs simplify failure analysis.&lt;br&gt;
✅ Cross-Browser Testing – Supports Chromium, Firefox, and WebKit from a single automation framework.&lt;br&gt;
.&lt;br&gt;
Value Delivered:&lt;/p&gt;

&lt;p&gt;🎯 Successfully migrated 1,200+ automated test cases&lt;br&gt;
🎯 Improved execution speed and test stability&lt;br&gt;
🎯 Reduced long-term automation costs&lt;br&gt;
🎯 Enhanced maintainability and scalability&lt;br&gt;
🎯 Accelerated feedback cycles for development teams&lt;br&gt;
🎯 Established a modern automation foundation for future growth&lt;/p&gt;

&lt;p&gt;Large-scale automation transformations require the right strategy, technical expertise, and a strong focus on quality. Proud to have contributed to this journey and to the measurable value delivered to the customer.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>🚀 1,200+ Test Cases Migrated from Tosca to Playwright – A Major QA Transformation Success! 🚀

Excited to share the successful migration of 1,200+ automated test cases from Tosca to Playwright, helping our customer modernize their automation landscape</title>
      <dc:creator>Sai Krishna Oggu</dc:creator>
      <pubDate>Mon, 08 Jun 2026 03:44:41 +0000</pubDate>
      <link>https://dev.to/saikrishna_oggu/1200-test-cases-migrated-from-tosca-to-playwright-a-major-qa-transformation-success-5499</link>
      <guid>https://dev.to/saikrishna_oggu/1200-test-cases-migrated-from-tosca-to-playwright-a-major-qa-transformation-success-5499</guid>
      <description></description>
      <category>automation</category>
      <category>testing</category>
      <category>tooling</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Testing the product as an end user.</title>
      <dc:creator>Sai Krishna Oggu</dc:creator>
      <pubDate>Sun, 05 Oct 2025 02:11:05 +0000</pubDate>
      <link>https://dev.to/saikrishna_oggu/testing-the-product-as-an-end-user-2hll</link>
      <guid>https://dev.to/saikrishna_oggu/testing-the-product-as-an-end-user-2hll</guid>
      <description>&lt;p&gt;🧪 Testing isn’t about ticking boxes. It’s about thinking like a user.&lt;/p&gt;

&lt;p&gt;Requirements are written with assumptions; they tell you what’s expected.&lt;br&gt;
Users arrive with expectations, and they need experience they really don't care about user stories in Jira.&lt;/p&gt;

&lt;p&gt;The gap between the two? That’s where startups succeed… or stumble.&lt;/p&gt;

&lt;p&gt;I test like your first customer, your 100th customer, and your toughest investor. 🐞🚀&lt;/p&gt;

&lt;p&gt;Great startups don’t just launch features; they launch experiences people believe in. We identify the gap between user expectations and user stories of your product.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>testing</category>
      <category>playwright</category>
      <category>qa</category>
    </item>
  </channel>
</rss>
