Salesforce + Playwright  ›  Course Overview
Advanced SDET Masterclass

Salesforce Test Automation
with Playwright

Historically, automating Salesforce UI was a nightmare due to dynamic namespaces, complex Shadow roots, nested frames, and slow execution. Playwright changes everything. This course compiles 10+ years of Salesforce and automation experience to help you master Salesforce QA concepts and Playwright engineering patterns for competitive interviews.

9
Modules
25+
Code Snippets
20
Interview Q&A
20
Quiz Questions
🎯
What You Will Learn
  • Salesforce Core Concepts: CRM fundamentals, SaaS vs PaaS platforms, multitenant metadata engines, standard objects lifecycles, and customization layers.
  • Salesforce QA Domain Knowledge: Sandboxes, Profile permissions, sharing hierarchies, validation UI alerts, and Governor Limit constraints.
  • Playwright Shadow DOM Piercing: Learn how Playwright locates components inside Salesforce LWC shadow roots natively.
  • Bypassing MFA & Caching Sessions: Implement high-performance storageState state reuse to skip logins.
  • API-First Test Seeding: Leverage Salesforce REST API within Playwright to seed test data in milliseconds.
  • Hands-On Coding Project: Initialize and scaffold a working Playwright framework from scratch using Page Objects and session caches.
💡
Interview Insight Interviewers look for candidates who understand that Salesforce automation is 80% architecture and domain context, and only 20% writing basic locator clicks. Demonstrating session reuse and API seeding will immediately separate you from standard applicants.
Platform Architecture

What is Salesforce & How It Works?

To test a Salesforce instance, you must understand what Salesforce is, how its unique cloud infrastructure operates, and the core CRM features that power business processes.

1. What is Salesforce?

Salesforce is the world's leading cloud-based Customer Relationship Management (CRM) platform. It operates primarily as a Software as a Service (SaaS) model, where out-of-the-box business apps are ready to run, and a Platform as a Service (PaaS) model (Force.com), enabling developers to write custom code and build bespoke applications.

SaaS Layer (OOTB Apps)

Ready-to-use business solutions like Sales Cloud (Lead and deal tracking), Service Cloud (Ticketing and customer cases), and Marketing Cloud (Journeys and email blasts).

PaaS Layer (Force.com)

Custom customization capability. Write backend apex triggers, design custom tables (objects), build Lightning Web Components, and configure complex automated Flows.

2. Under the Hood: Multitenant & Metadata Architecture

Understanding these two concepts separates junior automation engineers from true Salesforce SDET experts:

Tenant A Client Tenant B Client Security Router Checks Org ID Shared DB Storage Org A Records Org B Records Shared Schemas Metadata Engine Compiles UI Layout
Salesforce Multitenant Shared DB & Metadata Core Rendering
  • M
    Multitenant Database Architecture
    All customers (tenants) share the same physical server and database infrastructure. A strict runtime security engine ensures that one tenant's users can never see another tenant's data, even though they sit on the same server rows.
  • D
    Metadata-Driven Customization Engine
    In Salesforce, standard layout rendering, custom fields, validation rules, security levels, and sharing settings are stored entirely as metadata (declarative XML files). When a page loads, the compiler reads this metadata on-the-fly to render the custom LWC interface dynamically.
3. Standard Salesforce Objects & The CRM Lifecycle

Salesforce structures database tables as **Objects**. To test effectively, you must understand the standard Lead conversion lifecycle:

1. Prospect Lead Raw, unverified data 2. Qualification Criteria verification Lead Convert Apex Transaction Validation checks Account (Company) Contact (Individual) Opportunity (Deal)
Salesforce Standard Lead Conversion Pipeline
Standard Object Database Concept Business Purpose Lifecycle Role
Lead Table: Lead Raw prospect contact details. Unverified interest. The starting point. After qualification, converting it auto-creates Account, Contact, and Opportunity records.
Account Table: Account A company, customer, partner, or business entity. The central record. Stores all related contacts, deals, and service tickets.
Contact Table: Contact An individual person associated with an Account. Contains email, phone, and professional titles. Linked to the Account record.
Opportunity Table: Opportunity A specific active sales deal or revenue pipeline. Tracks deal amounts, close dates, and progressive stages (e.g. Prospecting -> Closed Won).
Domain Expertise

Salesforce QA & Domain Fundamentals

To build bulletproof Salesforce test automation, you must understand the architectural rules, sandboxes, and user sharing structures that govern the Salesforce CRM platform.

1. Sandbox Environments & Release Cycles

Salesforce development happens in isolated environments called Sandboxes. Automated suites are typically run against Developer or Partial sandboxes, and finally against Full Sandboxes during regression cycles.

Developer Sandbox 200MB limit. Daily refreshes QA Goal: Local Smoke Checks Partial Sandbox 5GB limit. 5-day refreshes QA Goal: CI Regression Runs Full Sandbox 100% Prod copy. 29-day QA Goal: Staging & Perf Production Org Live Customers. No direct tests
Salesforce Sandbox Tier Hierarchy & QA Automation Targets
Sandbox Type Data Limit Refresh Interval QA Automation Target
Developer 200 MB 1 Day Perfect for smoke tests and isolated feature branch validations.
Developer Pro 1 GB 1 Day Used for integration testing with external APIs.
Partial Copy 5 GB (incl. sample data) 5 Days Standard candidate for daily automated regression runs.
Full Sandbox Same as Production 29 Days Final stage validation. High-volume performance runs.
2. Persona-Based Testing & Security Roles

Salesforce enforces security via **Profiles** (what a user can see), **Permission Sets** (additional privileges), and **Sharing Rules** (record-level access). A vital part of QA is verifying that Standard Users cannot edit records owned by other departments, while Admins can.

🛠️
The Playwright Advantage: Context Isolation Instead of sequentially logging in and logging out to switch users—which wastes minutes—Playwright allows you to initialize multiple fully isolated BrowserContext instances in parallel, simulating the Admin, Approver, and Standard User concurrently!
3. Triggers, Flows & Governor Limits
  • 1
    Validation Rules & Error UI
    Salesforce developers write Validation Rules to prevent bad data. Automated tests must verify both the "happy path" (successful record saves) and the "error path" (ensuring warning toasts appear when fields are invalid).
  • 2
    Apex Triggers & Process Flows
    Updating a single field in UI can trigger a chain reaction: auto-creating related tasks, sending emails, or converting Leads. Automation should always do **UI+API verification**—checking both the front-end layout and the database side-effects.
  • 3
    Governor Limit Awareness
    Salesforce enforces strict multitenant limits (e.g., max 100 SOQL queries per transaction). Poorly designed automation that bombards pages or triggers bulk operations synchronously can crash the server instance, returning System.LimitException errors.
Technical Deep Dive

Salesforce Architecture & Shadow DOM

Salesforce Lightning Experience is built entirely on modern web components that encapsulate DOM trees inside shadow roots. Traditional automation tools struggle to touch these elements—Playwright handles them natively.

The Shadow DOM Piercing Difference

Salesforce LWC (Lightning Web Components) use **Shadow DOM** in `open` mode. The shadow boundary prevents external styles and standard query selectors from accessing nested fields.

Selenium WebDriver (Context Loops) 1. Query Root 2. executeScript Fragile context switching loop per element Playwright Native Piercing getByLabel('First Name') Click Element Automatic Shadow DOM traversal in 1 step
Playwright Direct Shadow DOM Piercing vs. Selenium Shadow Loops
Selenium WebDriver

Fails to locate shadow elements using normal selectors. You must write deep, fragile custom JavaScript scripts using shadowRoot.querySelector() to chain and pierce each boundary manually.

Playwright Engine

Pierces shadow boundaries automatically! Playwright locators automatically drill through open shadow roots by default. Standard locators work out of the box without any code tricks.

Shadow Root Locator Example

Below is a typical LWC shadow structure in a Salesforce standard contact form. In Playwright, you can locate the inner input directly.

LWC DOM Structure
<lightning-input class="slds-form-element">
  #shadow-root (open)
    <div class="slds-form-element__control">
      <input id="input-42" type="text" class="slds-input" />
    </div>
</lightning-input>
Playwright Selector Code
// Playwright ignores the Shadow root completely and targets the LWC element or the input directly!
await page.locator('lightning-input input').fill('John Doe');

// A much more resilient role-based selector (which also pierces shadow DOM):
await page.getByLabel('First Name').fill('John Doe');
💡
Interview Q&A Prep Question: "How do you configure Playwright to pierce Salesforce LWC shadow roots?"
Answer: "You don't need any configuration. Playwright's locator engines, including CSS selectors, text selectors, and role queries, bypass open shadow roots natively. It is built directly into the core locator architecture."
Performance Engineering

Bypassing MFA & Caching Sessions

Standard Salesforce UI logins are incredibly slow (taking 8 to 15 seconds) and are frequently blocked by Multi-Factor Authentication (MFA). Master high-performance session reuse.

1. Strategies for Bypassing Salesforce MFA

MFA is required in production, but in automated QA sandbox instances, you should bypass MFA using these techniques:

  • IP Relaxation: Add the test execution IP ranges (e.g., your GitHub Actions runners) to the Salesforce Network Access Allowed IPs to prevent security verification prompts.
  • API-based Auth: Authenticate via JWT or client-credentials API flows to retrieve an Access Token, then set this token directly into cookies/local storage.
  • Custom System Profile settings: Disable "MFA for User Interface Logins" on the dedicated Integration/Automation User profile within sandbox setups.
2. Playwright storageState Solution

Instead of logging into the Salesforce UI in each individual test spec, log in exactly once inside a **global setup** step, capture the resulting cookies and localStorage to a JSON file, and load this saved state instantly in all subsequent test context creations.

Traditional Slow E2E Strategy (No Caching) Test 1 Login (12s) Action 1 (3s) Test 2 Login (12s) Action 2 (3s) Optimized Playwright Session Reuse (storageState) Global Setup Spec Login Once & Save (12s) auth-state.json Test 1 Execution Loads auth JSON + Run (3s) Test 2 Execution Loads auth JSON + Run (3s)
Playwright storageState Session Reuse Workflow vs. Traditional Login
global-setup.ts
import { test as setup, expect } from '@playwright/test';

setup('login to salesforce and save session', async ({ page }) => {
  await page.goto('https://test.salesforce.com');
  await page.locator('#username').fill(process.env.SF_USERNAME!);
  await page.locator('#password').fill(process.env.SF_PASSWORD!);
  await page.locator('#Login').click();
  
  // Wait for home page dashboard loading to ensure auth fully completes
  await expect(page).toHaveURL(/lightning/);
  
  // Write the authenticated state to storage JSON
  await page.context().storageState({ path: 'playwright/.auth/salesforce-user.json' });
});
playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  projects: [
    // Step 1: Execute global auth setup
    {
      name: 'setup',
      testMatch: '**/*.setup.ts',
    },
    // Step 2: Main test suite reusing the saved auth
    {
      name: 'chromium',
      use: {
        browserName: 'chromium',
        storageState: 'playwright/.auth/salesforce-user.json',
      },
      dependencies: ['setup'],
    },
  ],
});
Engineering Design

Resilient Selectors & Page Objects

Salesforce class names are dynamically built by LWC bundlers, and standard HTML IDs auto-increment (e.g., input-241) on page redraws. Writing selectors requires specific patterns.

Selector DOs and DONTs in Salesforce
Pattern Fragile / BAD Selector Resilient / GOOD Selector
Direct Form Inputs #input-241 page.getByLabel('Opportunity Name')
LWC Dropdowns div.combobox-input > button page.locator('lightning-combobox').filter({ hasText: 'Stage' })
Visualforce Iframes iframe[id*='ext-gen'] page.frameLocator('iframe.vf-iframe-class')
Page Object Model: Salesforce Opportunity POM

This custom POM encapsulates finding opportunity elements inside the custom Salesforce Lightning interface.

1. E2E Test Spec opportunity.spec.ts (Clean assertions) 2. Page Object Class OpportunityPage.ts (Locators & Methods) 3. Salesforce UI Dynamic LWC / SLDS Form
Page Object Model (POM) Modular Clean Test Separation
OpportunityPage.ts
import { Locator, Page } from '@playwright/test';

export class OpportunityPage {
  private readonly page: Page;
  private readonly newButton: Locator;
  private readonly nameInput: Locator;
  private readonly stageCombobox: Locator;
  private readonly saveButton: Locator;

  constructor(page: Page) {
    this.page = page;
    this.newButton = page.getByRole('button', { name: 'New' });
    this.nameInput = page.getByLabel('*Opportunity Name'); // Salesforce adds '*' to mandatory fields
    this.stageCombobox = page.locator('lightning-combobox').filter({ hasText: 'Stage' }).locator('button');
    this.saveButton = page.getByRole('button', { name: 'Save', exact: true });
  }

  async createSimpleOpportunity(name: string, stage: string) {
    await this.newButton.click();
    await this.nameInput.fill(name);
    
    // Open combobox and select target stage option
    await this.stageCombobox.click();
    await this.page.getByRole('option', { name: stage }).click();
    
    await this.saveButton.click();
  }
}
Performance Engineering

API-First Data Seeding

A major flaw in most Salesforce UI suites is using the UI to create prereq test accounts, which wastes minutes. Learn how to seed records via APIs in milliseconds.

Why Seeding via REST APIs is Crucial

If your UI test tests Opp stages, it first needs an Account and a Contact. Doing this via the UI involves loading heavy layouts, saving, waiting for spinners, and taking up to 30 seconds.

1. HTTP POST Seed OAuth2 + REST request Creates Account (300ms) Captured ID 0018W000021c3pQ 2. Direct Page Load page.goto('/lightning/r/... Starts directly in record UI 3. HTTP DELETE Teardown Deletes record via REST Leaves database clean Total Setup Time: ~340ms (API-First) vs. ~32 seconds (Standard UI Creation)
Playwright Speed-Optimized API + UI Hybrid Automation Flow
UI-Based Setup
~ 32 Seconds
REST API Seeding
~ 340 Milliseconds
Integrating Salesforce REST APIs with Playwright

Playwright provides a built-in `request` fixture that makes it easy to issue backend REST calls. We authenticate, create our test Accounts, and launch our UI tests immediately.

SalesforceSeedingTest.ts
import { test, expect } from '@playwright/test';

test.describe('Speed Optimized Salesforce UI Test', () => {
  let accountId: string;

  // 1. Seed data before UI execution
  test.beforeAll(async ({ request }) => {
    const tokenResponse = await request.post('https://login.salesforce.com/services/oauth2/token', {
      form: {
        grant_type: 'password',
        client_id: process.env.SF_CONSUMER_KEY!,
        client_secret: process.env.SF_CONSUMER_SECRET!,
        username: process.env.SF_USERNAME!,
        password: process.env.SF_PASSWORD!
      }
    });
    const { access_token, instance_url } = await tokenResponse.json();

    // Create record via SObjects REST API
    const account = await request.post(`${instance_url}/services/data/v60.0/sobjects/Account`, {
      headers: {
        'Authorization': `Bearer ${access_token}`,
        'Content-Type': 'application/json'
      },
      data: {
        Name: 'Playwright Automation Account Inc.',
        Industry: 'Technology'
      }
    });
    const accountData = await account.json();
    accountId = accountData.id; // Capture the ID!
  });

  // 2. Run UI Test directly visiting the record ID!
  test('UI validation on created record', async ({ page }) => {
    await page.goto(`/lightning/r/Account/${accountId}/view`);
    
    // Validate UI loaded matching the API details
    await expect(page.getByText('Playwright Automation Account Inc.')).toBeVisible();
  });
});
Hands-On Practice

Build a Salesforce Test Suite From Scratch

Learn how to initialize, configure, and code a fully working Salesforce test automation project using Playwright, Page Objects, and Session Caching.

1. Project Initialization & Dependency Setup

Open your terminal inside a new folder and execute the command below to initialize Playwright. Choose **TypeScript** and **tests** folder when prompted:

📁 Directory Structure ├── .env ├── playwright.config.ts ├── tests/ ├── auth.setup.ts └── opportunity.spec.ts └── pages/ └── OpportunityPage.ts Module Interaction & Execution Pipeline 1. .env credentials Secures login fields 2. auth.setup.ts Authenticates & caches 3. OpportunityPage Locators & UI actions 4. opportunity.spec.ts (Execution) Loads auth.json, executes Page Objects
Salesforce Playwright Automation Project Architecture & Lifecycle
Terminal Commands
# Initialize a clean Playwright project
npm init playwright@latest

# Install dotenv to securely load sandbox credentials
npm install dotenv --save-dev
2. Securing Credentials & Configuring Config File

Create a .env file in your project root to secure credentials, then structure your Playwright configuration to run the auth setup first.

.env
SF_URL=https://test.salesforce.com
SF_USERNAME=qa-sdet-bot@example.com.sandbox
SF_PASSWORD=MySecurePassword123!
playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
import * as dotenv from 'dotenv';
dotenv.config();

export default defineConfig({
  testDir: './tests',
  fullyParallel: false, // Keep sequential per file to avoid record row locks
  workers: 1, // Restrict sandbox concurrency
  reporter: 'html',
  use: {
    baseURL: process.env.SF_URL,
    trace: 'on-first-retry',
  },
  projects: [
    {
      name: 'setup',
      testMatch: '**/*.setup.ts',
    },
    {
      name: 'e2e',
      dependencies: ['setup'],
      use: {
        ...devices['Desktop Chrome'],
        // Load the session cookies dynamically
        storageState: 'playwright/.auth/salesforce-user.json',
      },
    },
  ],
});
3. Coding the Authentication Setup Spec

Write the authentication script in tests/auth.setup.ts. It logs in once and caches cookies/localStorage to avoid rate limits.

tests/auth.setup.ts
import { test as setup, expect } from '@playwright/test';

const authFile = 'playwright/.auth/salesforce-user.json';

setup('authenticate salesforce', async ({ page }) => {
  await page.goto('/');
  await page.locator('#username').fill(process.env.SF_USERNAME!);
  await page.locator('#password').fill(process.env.SF_PASSWORD!);
  await page.locator('#Login').click();

  // Confirm home page has successfully loaded
  await expect(page).toHaveURL(/lightning/);

  // Cache the authenticated storage state
  await page.context().storageState({ path: authFile });
});
4. Building a Resilient Page Object for Leads

Create a class `LeadPage.ts` inside a `pages` directory. This utilizes stable labels and accessibility roles suitable for Lightning components.

pages/LeadPage.ts
import { Locator, Page } from '@playwright/test';

export class LeadPage {
  private readonly page: Page;
  private readonly lastNameInput: Locator;
  private readonly companyInput: Locator;
  private readonly leadStatusCombobox: Locator;
  private readonly saveButton: Locator;

  constructor(page: Page) {
    this.page = page;
    this.lastNameInput = page.getByLabel('*Last Name'); // Standard LWC label matching
    this.companyInput = page.getByLabel('*Company');
    this.leadStatusCombobox = page.locator('lightning-combobox').filter({ hasText: 'Lead Status' }).locator('button');
    this.saveButton = page.getByRole('button', { name: 'Save', exact: true });
  }

  async createLead(lastName: string, company: string, status: string) {
    // Fill text inputs
    await this.lastNameInput.fill(lastName);
    await this.companyInput.fill(company);

    // Expand LWC picklist and select value role
    await this.leadStatusCombobox.click();
    await this.page.getByRole('option', { name: status }).click();

    // Save Opportunity Record
    await this.saveButton.click();
  }
}
5. Writing the Lead UI Spec File

Now write your automated E2E test in tests/lead.spec.ts. It uses the `LeadPage` POM and asserts the successful save toast notification.

tests/lead.spec.ts
import { test, expect } from '@playwright/test';
import { LeadPage } from '../pages/LeadPage';

test.describe('Salesforce Lead Management Suite', () => {
  test('should successfully create a new Lead in Sandbox org', async ({ page }) => {
    const leadPage = new LeadPage(page);

    // Go directly to Lead Creation page layout using Salesforce direct routing
    await page.goto('/lightning/o/Lead/new');

    // Wait for the modal popup elements to be visible
    await expect(page.getByText('New Lead')).toBeVisible();

    // Interact using Page Object methods
    await leadPage.createLead('Smith', 'Acme Corp', 'Working - Contacted');

    // Assert that the success Toast alert displays correctly
    const toast = page.locator('.toastMessage');
    await expect(toast).toContainText('Lead "Smith" was created.');
  });
});
Crack The Interview

Top Salesforce + Playwright Interview Q&A

Review standard, challenging real-world interview scenarios compiled from top-tier tech SDET rounds.

Test Your Knowledge

Final Assessment Quiz

Test your understanding of Salesforce QA and Playwright automation frameworks. Complete the quiz to check your interview readiness tier.

© 2026 Rishabh Ranjan. All rights reserved.
Happy Testing! 🚀