TypeScript is a programming language built on JavaScript. It helps you find errors early and makes your code easier to write, understand, and maintain.

//In JavaScript:

let age = 25;

age = "Twenty Five"; // Allowed

JavaScript allows age to change from a number to a string.

//But In TypeScript:

let age: number = 25;

age = "Twenty Five"; // Error

Here, age: number means age can contain only numbers. 
TypeScript will show an error if you try to assign text to it.

In simple words:
TypeScript = JavaScript + Type Safety
It helps find coding mistakes early and makes code easier to maintain.

TypeScript is useful with Playwright because it makes your automation code safer, easier to write, and easier to maintain.

Main benefits:

Safer functions and test data
You can define exactly what type of data a function should accept.

Catches mistakes early
TypeScript can identify type-related errors before you run your tests.

Better code suggestions
Editors like VS Code can provide better autocomplete for Playwright methods.

Easy to maintain
Larger Page Object Model (POM) frameworks easier to understand and modify.

All three are used to create variables, but they behave differently.

1. let – value can change

Use let when you know the value may change.

let score = 10;
score = 20;

console.log(score); // 20

Easy meaning: let = I may change this value later.

2. const – value cannot be reassigned

Use const when you don’t want to assign a different value later.

const browser = "Chrome";

browser = "Firefox"; //Error

Easy meaning: const = Keep this value fixed.

3. var – old way of declaring variables.

var name = "Keshav";
name = "Rahul"; // Allowed

var name = "Kapoor"; // Also allowed

var behaves differently from let and const, particularly regarding scope and redeclaration, so modern TypeScript code generally prefers let and const.

KeywordCan change value?Can declare again?Recommended?
letYesNoYes
constNoNoYes
varYesYesUsually avoid

let and const are block-scoped, while var is function-scoped. const cannot be reassigned after initialization.

An interface is like a rule or structure that tells TypeScript what properties an object should have and what type of data those properties can contain.

Interface = Blueprint / Rules for an object

Modules allow us to divide code into separate files and share functionality using export and import.

any disables type checking for a variable. It allows assigning any type of value.

A return value is the result that a function sends back.

function add(a: number, b: number): number {
    return a + b;
}

== allows type conversion during comparison.

=== checks values without that type coercion.

forEach() executes a function for every element in an array.

let browsers = ["Chrome", "Firefox", "Edge"];

browsers.forEach(browser => {
    console.log(browser);
});

An enum is a collection of named constants. It improves readability and avoids hardcoded values.

length tells us how many elements are in an array or characters are in a string.

let browsers = ["Chrome", "Firefox", "Edge"];

console.log(browsers.length); // 3

A class is a blueprint for creating objects. It contains properties and methods.

Inheritance allows a child class to acquire properties and methods from a parent class. It promotes code reusability.

Encapsulation means keeping data and the methods that work with that data together inside a class, while controlling access where needed.

Access modifiers control member visibility in a class. The three types are public, private, and protected.

An abstract class cannot be instantiated directly. It serves as a base class for other classes.

Method overloading allows multiple method signatures with a single implementation. It improves flexibility in method usage.

extends is used when one class inherits from another class.

Implements is used when a class follows the structure defined by an interface.

interface User {
    login(): void;
}

class Admin implements User {
    login(): void {
        console.log("Login");
    }
}

An object is an instance of a class.

const user = new User();

A constructor is a special method that runs when an object is created. It is commonly used to initialize properties.

This refers to the current object of the class.

class User {
    name = "Keshav";

    display() {
        console.log(this.name);
    }
}

Inheritance helps reuse common code and reduce duplication.

Optional parameters are not mandatory during function calls. They are marked using the ? symbol.

Polymorphism means the same method/interface can have different behavior depending on the object.

Method overriding happens when a child class provides its own implementation of a method inherited from its parent.

Playwright is an open-source automation framework developed by Microsoft for web application testing. It supports Chromium, Firefox, and WebKit browsers.

Playwright provides auto-waiting, faster execution, built-in parallel execution, and supports multiple browser engines. It also handles modern web applications more effectively.

Playwright supports Chromium, Firefox, and WebKit. It can also automate Google Chrome and Microsoft Edge.

A Browser Context is an isolated browser session. Multiple contexts can run within a single browser instance without sharing cookies or storage.

A Browser is the actual browser instance, while a Browser Context is an independent session within that browser. Contexts help run parallel and isolated tests.

POM is a design pattern where page elements and actions are stored in separate classes. It improves maintainability and code reusability.

Locators are used to identify web elements on a page. They provide auto-waiting and retry mechanisms before performing actions.

getByRole() is the most recommended locator because it mimics how users interact with the application. It is stable and accessible.

Playwright automatically waits for elements to become visible, enabled, and actionable before performing operations. This reduces flaky tests.

locator() supports auto-waiting and retries. page.$() returns the element immediately and may fail if the element is not available.

test.describe() is used to group related test cases together. It improves test organization and readability.

This hook runs before every test case in a test suite. It is commonly used for login and setup operations.

This hook runs after every test case execution. It is used for cleanup activities and logging.

beforeAll executes once before all tests. beforeEach executes before every individual test.

Playwright uses the expect() library for assertions. It verifies text, visibility, URL, title, and other conditions.

Use selectOption() to select dropdown values. Values can be selected by label, value, or index.

Playwright handles alerts using the dialog event. You can accept or dismiss the alert through event handlers.

File upload is handled using setInputFiles(). It directly uploads files without opening the OS file picker.

Use the browser context’s waitForEvent('page') method. It captures and switches control to the new tab.

Use the page.screenshot() method. Screenshots can be taken for the full page or a specific element.

Use page.on('request') and page.on('response') events. They allow monitoring of API and network traffic.

Playwright provides APIRequestContext for REST API testing. It allows GET, POST, PUT, and DELETE operations without a browser.

Set the workers property in playwright.config.ts. Playwright automatically distributes tests across workers.

Trace Viewer is a debugging tool that records test execution details, screenshots, network calls, and actions. It helps analyze test failures.

Execute the tests using the HTML reporter configuration. After execution, open the report with npx playwright show-report.

Playwright uses frameLocator() to work with elements inside an iframe. It provides direct access to frame content.

Authentication state can be saved using storageState(). This avoids logging in before every test execution.

Fixtures provide reusable setup and teardown logic for tests. They help reduce code duplication and improve maintainability.

Hooks are methods such as beforeAll, beforeEach, afterEach, and afterAll. They manage preconditions and cleanup activities.

Playwright offers cross-browser support, faster execution, auto-waiting, API testing, tracing, and parallel execution. These features make automation reliable and scalable.

npx playwright test –last-failed

Skips execution of a particular test.

Runs only the specified test.

test.only('Login Test', async () => {
});

Playwright supports cross-browser testing, which means the same test can run on Chromium, Firefox, and WebKit without changing the test code.

You configure multiple browsers using the projects section in playwright.config.ts.

projects: [
{ name: ‘chromium’ },
{ name: ‘firefox’ },
{ name: ‘webkit’ }
]

readonly prevents a property from being modified after initialization.

class User {
  readonly id = 101;
}

Overloading: Same method/function name with different call signatures.

Overriding: Child class replaces an inherited method’s behavior.

Abstraction means exposing necessary behavior while hiding implementation details.

No. An abstract class cannot be instantiated directly.

Yes. An abstract class can contain both abstract and implemented methods.

Yes.

Yes

A static method is called using the class itself.

Yes. A child class can inherit methods and also define additional methods.

super is used in a child class to access the parent class constructor or methods.

Common methods can be stored in a base class and inherited by page classes.

You can keep internal page details private and expose only the operations a test needs.

Assertions validate whether an expected result matches the actual result.

They confirm that the application behaves correctly.

Assertions can validate:

Error messages

Page titles

URLs

Success messages

npx playwright test --last-failed

toBeVisible, toHaveText, toHaveURL, toHaveCount, toContainText, and toBeEnabled.

// Check current page URL after login
await expect(page).toHaveURL('/dashboard');

// Check page title
await expect(page).toHaveTitle('My App - Dashboard');

// Check element is visible on screen
await expect(page.locator('.success-msg')).toBeVisible();

// Check element has specific text
await expect(page.locator('h1')).toHaveText('Welcome, Rahul');

// Check element does NOT exist or is NOT visible
await expect(page.locator('.error-msg')).not.toBeVisible();

page.waitForTimeout(2000) pauses for a fixed duration. It is the test-automation equivalent of Thread.sleep, and it causes flaky tests for 2 reasons:

export default defineConfig({
  use: {
    screenshot: 'only-on-failure', // 'on' | 'off' | 'only-on-failure'
    video: 'retain-on-failure',    // 'on' | 'off' | 'retain-on-failure' | 'on-first-retry'
    trace: 'on-first-retry',       // 'on' | 'off' | 'retain-on-failure' | 'on-first-retry'
  },
});

// Use test.only() to run just this one test
test.only('Login test', async ({ page }) => {
  // only this test will run in this file
});

// Remember to remove .only before committing to Git!

Playwright runs tests in parallel by default. Each worker is a separate Node.js process running its own browser. The runner distributes test files across workers.

// playwright.config.ts
export default defineConfig({
  fullyParallel: true,                       // run tests within a file in parallel too
  workers: process.env.CI ? 4 : undefined,   // 4 in CI, half your CPU cores locally
});

// By role — most recommended, works with accessibility
page.getByRole('button', { name: 'Login' })
page.getByRole('textbox', { name: 'Email' })

// By visible text on screen
page.getByText('Welcome to STAD Solution')

// By placeholder text inside an input
page.getByPlaceholder('Enter your email')

// By label text (for form fields)
page.getByLabel('Password')

// By test ID — most stable for automation
page.getByTestId('submit-button')

// By CSS selector
page.locator('#email-input')
page.locator('.btn-primary')

Playwright provides two methods — isVisible() returns true or false, while toBeVisible() is an assertion that fails the test if the element is not visible.

// Check visibility — returns true or false
const isVisible = await page.locator('.success-msg').isVisible();
console.log(isVisible); // true or false

// Assert element IS visible — test fails if not
await expect(page.locator('.success-msg')).toBeVisible();

// Assert element is NOT visible
await expect(page.locator('.error-msg')).not.toBeVisible();

// Check if element exists in DOM (even if hidden)
const exists = await page.locator('#modal').count() > 0;
console.log(exists); // true if present in HTML

MethodHow it worksSpeedUse when
fill()Clears the field and sets value directlyFastMost form inputs — standard text, email, password
type()Simulates actual key press for each characterSlowSearch inputs with live autocomplete suggestions
// fill() — use for most inputs
await page.fill('#email', 'kapoor@gmail.com');

// type() — use for autocomplete search inputs
// Each keypress triggers a suggestion list update
await page.locator('#city-search').type('Noida');
await page.locator('.suggestion-item').first().click();

Playwright automatically scrolls to elements before clicking them. But sometimes you need to scroll manually — like to load lazy-loaded content or to scroll inside a specific container.

// Scroll a specific element into view
await page.locator('#footer-section').scrollIntoViewIfNeeded();

// Scroll to bottom of page (for lazy loading)
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));

// Scroll to top of page
await page.evaluate(() => window.scrollTo(0, 0));

Tags help you organize and selectively run tests. Add tags using @tag in the test name, then filter by tag from the terminal.

// Add @tags to test names
test('Login test @smoke', async ({ page }) => { });
test('Checkout test @smoke @regression', async ({ page }) => { });
test('Profile update @regression', async ({ page }) => { });
test('Payment @regression @payment', async ({ page }) => { });

# Run only smoke tests (quick sanity check)
npx playwright test --grep @smoke

# Run full regression suite
npx playwright test --grep @regression

# Run only payment-related tests
npx playwright test --grep @payment

# Skip smoke tests, run everything else
npx playwright test --grep-invert @smoke

Playwright has two powerful built-in tools for understanding test results:

HTML Report — A visual report showing all test results with pass/fail status, screenshots, error messages, and test duration. You can share this with your team or manager after a test run.

Trace Viewer — A step-by-step recording of everything that happened during a test — every click, every network call, every DOM snapshot. When a test fails, you open the trace to see exactly what went wrong.

//playwright.config.js — Enable trace and report

reporter: ['html'],  // generates HTML report

use: {
  trace: 'retain-on-failure',       // save trace only when test fails
  screenshot: 'only-on-failure',    // save screenshot on failure
  video: 'retain-on-failure',        // save video on failure
}

//Terminal

# Open HTML report in browser
npx playwright show-report

# Open trace file to debug a failure
npx playwright show-trace trace.zip

Playwright provides two methods — isVisible() returns true or false, while toBeVisible() is an assertion that

MethodWhat it doesWhen to use
test.skip()Test is skipped — not run at allFeature not yet built, or test environment issue
test.only()Only this test runs — all others are skippedTemporarily run one test during development
test.fixme()Test runs but is expected to fail — shown separatelyKnown bug tracked in JIRA — test documents the bug
// Skip this test completely
test.skip('Payment test — gateway is down in staging', async ({ page }) => {
  // will not run
});

// Run ONLY this test (remove before committing!)
test.only('Login test', async ({ page }) => {
  // only this runs
});

// Known bug — test runs but failure is expected
test.fixme('Cart count is wrong — BUG-1234', async ({ page }) => {
  // documents the known bug
});

// Conditional skip — skip on Firefox only
test('Chrome-only feature test', async ({ page, browserName }) => {
  test.skip(browserName !== 'chromium', 'This feature is Chrome-only');
  // rest of test
});

CAPTCHA is designed to prevent automated access, so I would not try to automate or solve it directly. In the test or staging environment, I would work with the development/security team to disable CAPTCHA.

Before performing an action such as click, Playwright checks whether the target is ready.

For example, it checks that the element is visible, stable, enabled and able to receive the event.

Timeout ensures that before marking test execution as a failure the amount of time the Playwright needs to wait.

The playwright provides specific commands to navigate backward and forward.

page.goForward() command can be used to navigate forward from the browser history

page.goBack() command can be used to navigate backward from the browser history

In Playwright, you can perform these mouse actions using locator methods.

//1. Single Click
await page.getByRole('button').click();


//2. Double Click
await page.getByText('Item').dblclick();

//3. Right Click
await page.getByText('Item').click({ button: 'right' });

void means a function does not return a useful value.

function message(): void {
console.log(“Hello”);
}

await page.getByText(‘Products’).hover();

await page.reload();

const title = await page.title();
console.log(title);

await waits for an asynchronous operation to complete before the next statement continues.

await page.goto(‘https://logicnextgen.com/‘);
await page.getByRole(‘button’).click();

await expect(page.getByRole(‘button’)).toBeEnabled();

await page.reload();

test() defines a test case.

test(‘Login Test’, async ({ page }) => {
// test steps
});

expect() is used to verify expected behavior.

await expect(page.getByText(‘Welcome’)).toBeVisible();

await page.keyboard.press(‘Enter’);