Step-by-Step Guide to Integrate BDD Behavior in Playwright with POM

This guide explains how to integrate:

  • Playwright
  • BDD (Behavior Driven Development)
  • Cucumber
  • Page Object Model (POM)

using JavaScript/TypeScript.

The framework structure will support:

  • Readable feature files
  • Reusable page objects
  • Clean step definitions
  • Scalable automation framework
  • HTML reports
  • Cross-browser execution

1. What You Will Build

You will create a framework like this:

PlaywrightBDDFramework/

├── features/
│ ├── login.feature
│ └── step-definitions/
│ └── login.steps.ts

├── pages/
│ └── LoginPage.ts

├── hooks/
│ └── hooks.ts

├── utils/
│ └── helper.ts

├── reports/

├── playwright.config.ts
├── cucumber.js
├── package.json
└── tsconfig.json

2. Install Node.js

Install Node.js from:

Node.js Official Website

Verify installation:

node -v
npm -v

3. Create Project

mkdir PlaywrightBDDFramework
cd PlaywrightBDDFramework

Initialize project:

npm init -y

4. Install Required Dependencies

Install Playwright

npm install -D @playwright/test

Install browsers:

npx playwright install

Install Cucumber + TypeScript Support

npm install -D @cucumber/cucumber
npm install -D typescript ts-node
npm install -D @types/node

Install Reporting Packages

npm install -D multiple-cucumber-html-reporter
npm install -D cucumber-html-reporter

5. Configure TypeScript

Create:

tsconfig.json

{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"types": ["node"],
"strict": false,
"esModuleInterop": true,
"skipLibCheck": true
}
}

6. Configure Cucumber

Create:

cucumber.js

module.exports = {
default: {
require: [
"features/step-definitions/*.ts",
"hooks/*.ts"
],
format: [
"progress",
"json:reports/cucumber-report.json"
],
requireModule: ["ts-node/register"],
paths: ["features/*.feature"]
}
};

7. Create Playwright Config

playwright.config.ts

import { defineConfig } from '@playwright/test';

export default defineConfig({
testDir: './features',
timeout: 60000,
use: {
headless: false,
screenshot: 'only-on-failure',
video: 'retain-on-failure'
}
});

8. Create Feature File

features/login.feature

Feature: Login Functionality

Scenario: Successful Login

Given User launches the application
When User enters username and password
And User clicks on login button
Then User should navigate to dashboard

9. Create Page Object Model (POM)

pages/LoginPage.ts

import { Page } from '@playwright/test';

export class LoginPage {

constructor(private page: Page) {}

username = '#username';
password = '#password';
loginBtn = '#loginBtn';

async launchApplication() {
await this.page.goto('https://example.com');
}

async enterUsername(username: string) {
await this.page.fill(this.username, username);
}

async enterPassword(password: string) {
await this.page.fill(this.password, password);
}

async clickLogin() {
await this.page.click(this.loginBtn);
}
}

10. Create Hooks File

Hooks help initialize browser and cleanup.

hooks/hooks.ts

import {
Before,
After,
BeforeAll,
AfterAll
} from '@cucumber/cucumber';

import { chromium, Browser, Page } from '@playwright/test';

let browser: Browser;
let page: Page;

BeforeAll(async () => {
browser = await chromium.launch({
headless: false
});
});

Before(async function () {
const context = await browser.newContext();
page = await context.newPage();

this.page = page;
});

After(async function () {
await page.close();
});

AfterAll(async () => {
await browser.close();
});

11. Create Step Definitions

features/step-definitions/login.steps.ts

import { Given, When, Then } from '@cucumber/cucumber';
import { expect } from '@playwright/test';

import { LoginPage } from '../../pages/LoginPage';

let loginPage: LoginPage;

Given('User launches the application', async function () {

loginPage = new LoginPage(this.page);

await loginPage.launchApplication();
});

When('User enters username and password', async function () {

await loginPage.enterUsername('admin');

await loginPage.enterPassword('admin123');
});

When('User clicks on login button', async function () {

await loginPage.clickLogin();
});

Then('User should navigate to dashboard', async function () {

await expect(this.page).toHaveURL(/dashboard/);
});

12. Add Scripts in package.json

Update:

package.json

"scripts": {
"test": "cucumber-js",
"test:headed": "cucumber-js --tags '@SmokeTest'",
"report": "node report.js"
}

13. Execute Tests

Run:

npm test

14. Generate HTML Reports

Create:

report.js

const report = require('multiple-cucumber-html-reporter');

report.generate({
jsonDir: 'reports',
reportPath: 'reports/html-report',
metadata: {
browser: {
name: 'chrome',
version: 'latest'
},
device: 'Local Machine',
platform: {
name: 'windows',
version: '11'
}
}
});

Run:

node report.js

15. Add Tags in Feature Files

@SmokeTest
Feature: Login Functionality

Run specific tags:

npx cucumber-js --tags "@SmokeTest"

16. Add Environment Support

Create:

.env

BASE_URL=https://example.com
USERNAME=admin
PASSWORD=admin123

Install dotenv:

npm install dotenv

Use:

import dotenv from 'dotenv';

dotenv.config();

process.env.BASE_URL

17. Best Framework Structure

framework/

├── features/
├── pages/
├── hooks/
├── utils/
├── locators/
├── test-data/
├── reports/
├── screenshots/
├── videos/
├── logs/
└── config/

18. Advanced Improvements

You can enhance the framework with:

FeatureBenefit
Parallel ExecutionFaster execution
CI/CD IntegrationAutomated pipeline
Retry LogicStable execution
API + UI FrameworkHybrid testing
Docker SupportEasy setup
Allure ReportsBetter reporting
Faker DataDynamic test data
Database ValidationEnd-to-end validation

19. Real-Time Industry Best Practices

Use Separate Layers

Page Layer

  • Locators
  • Page actions

Step Definition Layer

  • BDD mapping only

Utility Layer

  • Reusable functions

Test Data Layer

  • JSON/CSV/Excel data

20. Recommended Design Pattern

Hybrid Framework

Combine:

  • POM
  • BDD
  • Data-Driven
  • Utility Helpers
  • API Helpers
  • Reporting
  • CI/CD

21. Sample Execution Flow

Feature File

Step Definition

Page Object

Playwright Actions

Browser Execution

22. Advantages of BDD with POM

BDDPOM
Readable scenariosReusable code
Business friendlyMaintainable
Better collaborationEasy scaling
Easy reportingCleaner structure

23. Common Interview Questions

What is BDD?

BDD is a development approach where application behavior is written in simple language using Gherkin syntax.


Why use POM?

POM improves:

  • Reusability
  • Maintainability
  • Scalability

Difference Between Playwright Test and Cucumber?

Playwright TestCucumber
TechnicalBusiness readable
Fast executionBDD support
Built-in fixturesGherkin support

24. Recommended Learning Path

  1. Playwright Basics
  2. TypeScript Fundamentals
  3. POM Design
  4. BDD Concepts
  5. Cucumber Framework
  6. Reporting
  7. CI/CD
  8. Docker
  9. API Automation
  10. AI-assisted Automation

25. Official Documentation

How to read data from CSV file in Playwright

1. Install CSV Parser

Run:

npm install csv-parser

2. Create CSV File

Create a file named testdata.csv

username,password
admin,admin123
testuser,test123
demo,demo123

3. Create Utility to Read CSV

Create readCSV.js

const fs = require('fs');
const csv = require('csv-parser');

function readCSV(filePath) {
return new Promise((resolve, reject) => {
const results = [];

fs.createReadStream(filePath)
.pipe(csv())
.on('data', (data) => results.push(data))
.on('end', () => resolve(results))
.on('error', (error) => reject(error));
});
}

module.exports = readCSV;

4. Use CSV Data in Playwright Test

Create example.spec.js

const { test, expect } = require('@playwright/test');
const readCSV = require('./readCSV');

let testData = [];

test.beforeAll(async () => {
testData = await readCSV('./testdata.csv');
});

test.describe('Login Tests Using CSV Data', () => {

for (const data of testData) {

test(`Login test for ${data.username}`, async ({ page }) => {

await page.goto('https://example.com/login');

await page.fill('#username', data.username);
await page.fill('#password', data.password);

await page.click('button[type="submit"]');

// Example validation
await expect(page).toHaveURL(/dashboard/);
});
}
});

Better Approach (Recommended)

Because Playwright loads tests before beforeAll, the loop may not work correctly.

Use this cleaner approach:

const { test, expect } = require('@playwright/test');
const readCSV = require('./readCSV');

test.describe('CSV Driven Tests', () => {

let testData;

test.beforeAll(async () => {
testData = await readCSV('./testdata.csv');
});

test('Run all CSV rows', async ({ page }) => {

for (const data of testData) {

console.log(`Testing with ${data.username}`);

await page.goto('https://example.com/login');

await page.fill('#username', data.username);
await page.fill('#password', data.password);

await page.click('button[type="submit"]');

await expect(page).toHaveURL(/dashboard/);
}
});
});

Best Practice for Large Frameworks

For scalable automation frameworks:

  • Keep test data in data/ folder
  • Use Page Object Model (POM)
  • Create reusable CSV utility
  • Separate:
    • Test Data
    • Test Logic
    • Page Actions

Example structure:

project/

├── tests/
├── pages/
├── utils/
│ └── readCSV.js
├── data/
│ └── testdata.csv
└── playwright.config.js

Alternative (Recommended for Playwright)

Playwright works better with JSON than CSV for parameterized tests.

You can convert CSV → JSON and use:

testData.forEach((data) => {
test(`Login ${data.username}`, async ({ page }) => {
// test steps
});
});

This gives:

  • Better reporting
  • Separate test cases
  • Parallel execution support
  • Easier debugging

Playwright Custom Fixtures with Examples

In Playwright, fixtures are reusable setup and teardown utilities that help you share test data, page objects, authentication, API clients, and configurations across tests.

Custom fixtures make your framework:

  • Reusable
  • Maintainable
  • Scalable
  • Cleaner and easier to read

1. What is a Fixture in Playwright?

A fixture is a setup environment provided to your test before execution.

Playwright already provides built-in fixtures such as:

  • page
  • browser
  • context
  • request

Example:

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

test('example test', async ({ page }) => {
await page.goto('https://example.com');
});

Here, page is a built-in fixture.


2. Why Use Custom Fixtures?

Custom fixtures help when you want to:

  • Reuse login steps
  • Share page objects
  • Create API clients
  • Initialize test data
  • Setup database connections
  • Handle authentication
  • Avoid duplicate code

3. Basic Structure of Custom Fixture

import { test as base } from '@playwright/test';

export const test = base.extend({

myFixture: async ({}, use) => {

// Setup
const data = "Hello Fixture";

// Provide fixture
await use(data);

// Teardown
console.log("Fixture cleanup");
}
});

4. Example 1 — Simple Custom Fixture

Folder Structure

project
├── tests
│ └── example.spec.ts

├── fixtures
│ └── testFixture.ts

Step 1: Create Fixture File

fixtures/testFixture.ts

import { test as base } from '@playwright/test';

type MyFixtures = {
userName: string;
};

export const test = base.extend<MyFixtures>({

userName: async ({}, use) => {

const name = 'Deepesh';

await use(name);

console.log('Test completed');
}
});

export { expect } from '@playwright/test';

Step 2: Use Fixture in Test

tests/example.spec.ts

import { test, expect } from '../fixtures/testFixture';

test('Custom Fixture Example', async ({ userName }) => {

console.log(userName);

expect(userName).toBe('Deepesh');
});

5. Example 2 — Page Object Fixture

This is the most common real-world usage.


Folder Structure

project
├── pages
│ └── LoginPage.ts

├── fixtures
│ └── pageFixture.ts

├── tests
│ └── login.spec.ts

Step 1: Create Page Object

pages/LoginPage.ts

import { Page } from '@playwright/test';

export class LoginPage {

constructor(private page: Page) {}

async navigate() {
await this.page.goto('https://example.com/login');
}

async login(username: string, password: string) {

await this.page.fill('#username', username);
await this.page.fill('#password', password);

await this.page.click('#loginBtn');
}
}

Step 2: Create Fixture

fixtures/pageFixture.ts

import { test as base } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';

type PageFixtures = {
loginPage: LoginPage;
};

export const test = base.extend<PageFixtures>({

loginPage: async ({ page }, use) => {

const loginPage = new LoginPage(page);

await use(loginPage);
}
});

export { expect } from '@playwright/test';

Step 3: Use Fixture in Test

tests/login.spec.ts

import { test, expect } from '../fixtures/pageFixture';

test('Login Test', async ({ loginPage }) => {

await loginPage.navigate();

await loginPage.login(
'admin',
'password123'
);
});

6. Example 3 — Authentication Fixture

This is extremely useful in enterprise frameworks.


Authentication Fixture

import { test as base } from '@playwright/test';

type AuthFixtures = {
authenticatedPage: any;
};

export const test = base.extend<AuthFixtures>({

authenticatedPage: async ({ page }, use) => {

await page.goto('https://example.com/login');

await page.fill('#username', 'admin');
await page.fill('#password', 'admin123');

await page.click('#login');

// Reuse logged-in page
await use(page);

// Optional cleanup
await page.close();
}
});

export { expect } from '@playwright/test';

Test File

import { test, expect } from '../fixtures/authFixture';

test('Dashboard Test', async ({ authenticatedPage }) => {

await authenticatedPage.goto(
'https://example.com/dashboard'
);

await expect(
authenticatedPage.locator('h1')
).toContainText('Dashboard');
});

7. Example 4 — API Fixture

Useful for API automation frameworks.


fixtures/apiFixture.ts

import { test as base, request } from '@playwright/test';

type ApiFixture = {
apiContext: any;
};

export const test = base.extend<ApiFixture>({

apiContext: async ({}, use) => {

const apiContext = await request.newContext({

baseURL: 'https://reqres.in/api',

extraHTTPHeaders: {
'Accept': 'application/json'
}
});

await use(apiContext);

await apiContext.dispose();
}
});

export { expect } from '@playwright/test';

API Test

import { test, expect } from '../fixtures/apiFixture';

test('Get Users API', async ({ apiContext }) => {

const response = await apiContext.get('/users?page=2');

expect(response.status()).toBe(200);

const body = await response.json();

console.log(body);
});

8. Worker Fixture vs Test Fixture

Playwright supports two fixture scopes.

Fixture TypeScopeRuns
Test FixturePer TestEvery test
Worker FixturePer WorkerOnce per worker

Example of Worker Fixture

import { test as base } from '@playwright/test';

type WorkerFixture = {
token: string;
};

export const test = base.extend<{}, WorkerFixture>({

token: [async ({}, use) => {

console.log('Generate Token');

const token = 'ABC123TOKEN';

await use(token);

}, { scope: 'worker' }]
});

9. Auto Fixtures

Auto fixtures execute automatically before every test.


Example

import { test as base } from '@playwright/test';

export const test = base.extend({

autoLogger: [async ({}, use) => {

console.log('Before Test');

await use();

console.log('After Test');

}, { auto: true }]
});

10. Best Practices

Use Fixtures For

  • Login handling
  • API clients
  • Database setup
  • Page objects
  • Environment setup
  • Test data generation

Avoid

  • Very large fixtures
  • Too much business logic
  • Circular dependencies
  • Hardcoded credentials

11. Real Enterprise Framework Structure

project
├── fixtures
│ ├── authFixture.ts
│ ├── apiFixture.ts
│ ├── pageFixture.ts

├── pages
│ ├── LoginPage.ts
│ ├── DashboardPage.ts

├── tests
│ ├── ui
│ ├── api

├── utils
├── test-data
├── playwright.config.ts

12. Advantages of Custom Fixtures

BenefitDescription
ReusabilityAvoid duplicate code
ScalabilityEasier framework growth
MaintainabilityCentralized setup
Clean TestsBetter readability
Faster DevelopmentReuse common logic

13. Interview Questions

Q1. What is a fixture in Playwright?

A reusable setup/teardown utility used in tests.


Q2. Difference between built-in and custom fixtures?

  • Built-in fixtures are provided by Playwright.
  • Custom fixtures are user-defined.

Q3. What is worker scope?

Fixture executes once per worker process.


Q4. Why use fixtures with Page Object Model?

To inject reusable page object instances into tests.


14. Recommended Real-World Usage

Most companies use fixtures for:

  • Authentication
  • Page Object initialization
  • API clients
  • Environment configuration
  • Test reporting
  • Database utilities
  • Logging

15. Summary

Custom fixtures are one of the most important concepts in Playwright frameworks because they:

  • Reduce code duplication
  • Improve maintainability
  • Support scalable automation frameworks
  • Work perfectly with POM and API automation

Most enterprise Playwright frameworks heavily depend on custom fixtures.

JavaScript Exception Handling

Exception handling in JavaScript is used to manage runtime errors without stopping the entire program. It helps developers write stable and reliable applications.


What is an Exception?

An exception is an unexpected error that occurs while the program is running.

Examples:

  • Dividing by invalid values
  • Accessing undefined variables
  • Invalid JSON parsing
  • Network/API failures
  • Custom validation errors

Why Exception Handling is Important

Exception handling helps to:

  • Prevent application crashes
  • Show meaningful error messages
  • Debug applications easily
  • Handle API/database failures gracefully
  • Improve user experience

JavaScript Error Types

1. Syntax Error

Occurs when JavaScript code has invalid syntax.

console.log("Hello"

Output:

SyntaxError: missing ) after argument list

2. Reference Error

Occurs when accessing an undefined variable.

console.log(userName);

Output:

ReferenceError: userName is not defined

3. Type Error

Occurs when an operation is performed on the wrong data type.

let num = 10;
num.toUpperCase();

Output:

TypeError: num.toUpperCase is not a function

4. Range Error

Occurs when a value is outside the allowed range.

let arr = new Array(-1);

Output:

RangeError: Invalid array length

5. URI Error

Occurs when URI functions are used incorrectly.

decodeURIComponent("%");

Output:

URIError: URI malformed

try…catch Statement

The try...catch block is the core of exception handling.

Syntax

try {
// risky code
} catch(error) {
// error handling code
}

Basic Example

try {
let result = 10 / 0;
console.log(result);
} catch(error) {
console.log("Something went wrong");
}

Example with Actual Error

try {
console.log(user);
} catch(error) {
console.log("Error Name:", error.name);
console.log("Error Message:", error.message);
}

Output:

Error Name: ReferenceError
Error Message: user is not defined

Error Object Properties

The error object contains useful information.

PropertyDescription
nameError type
messageError description
stackStack trace

finally Block

The finally block always executes whether an error occurs or not.

Syntax

try {
// code
} catch(error) {
// handle error
} finally {
// always runs
}

Example

try {
console.log("Start");
console.log(data);
} catch(error) {
console.log("Error handled");
} finally {
console.log("Execution completed");
}

Output:

Start
Error handled
Execution completed

throw Statement

The throw keyword is used to create custom exceptions.

Syntax

throw "Custom Error";

or

throw new Error("Invalid input");

Custom Error Example

let age = 15;

try {
if(age < 18) {
throw new Error("Age must be 18 or above");
}

console.log("Eligible");
} catch(error) {
console.log(error.message);
}

Output:

Age must be 18 or above

Nested try…catch

You can use multiple try-catch blocks.

try {
try {
console.log(userName);
} catch(error) {
console.log("Inner Catch");
}

console.log(data);
} catch(error) {
console.log("Outer Catch");
}

Exception Handling in Functions

function divide(a, b) {
try {
if(b === 0) {
throw new Error("Division by zero not allowed");
}

return a / b;

} catch(error) {
return error.message;
}
}

console.log(divide(10, 0));

Exception Handling with JSON

Very common in API automation and backend development.

let jsonData = '{"name":"Deepesh"}';

try {
let user = JSON.parse(jsonData);
console.log(user.name);

} catch(error) {
console.log("Invalid JSON");
}

Invalid JSON Example

let jsonData = '{"name":"Deepesh"';

try {
let user = JSON.parse(jsonData);

} catch(error) {
console.log("JSON Parsing Failed");
}

Async Exception Handling

Used with async/await.

Example

async function fetchData() {

try {
let response = await fetch("https://dummyjson.com/users/1");

let data = await response.json();

console.log(data);

} catch(error) {
console.log("API Error:", error.message);
}
}

fetchData();

Best Practices

1. Use Specific Error Messages

throw new Error("Password cannot be empty");

2. Avoid Empty catch Blocks

❌ Bad

catch(error) {

}

✅ Good

catch(error) {
console.log(error.message);
}

3. Use finally for Cleanup

Useful for:

  • Closing database connections
  • Clearing resources
  • Stopping loaders

4. Do Not Overuse try…catch

Use it only around risky code.


Real-World Example

Form Validation

function login(username, password) {

try {

if(!username) {
throw new Error("Username is required");
}

if(!password) {
throw new Error("Password is required");
}

console.log("Login Successful");

} catch(error) {
console.log(error.message);
}
}

login("", "12345");

Playwright Framework Read Data from a JSON File

Using JSON files in a Playwright framework is a common way to manage:

  • Test data
  • Environment configuration
  • User credentials
  • API payloads
  • Multiple datasets for data-driven testing

1. Project Structure Example

playwright-framework/

├── test-data/
│ ├── users.json
│ └── loginData.json

├── tests/
│ └── login.spec.ts

├── utils/
│ └── jsonReader.ts

├── playwright.config.ts
├── package.json
└── tsconfig.json

2. Create a JSON File

test-data/loginData.json

{
"validUser": {
"username": "admin",
"password": "admin123"
},
"invalidUser": {
"username": "wronguser",
"password": "wrongpass"
}
}

3. Read JSON Directly in Playwright Test

tests/login.spec.ts

import { test, expect } from '@playwright/test';
import loginData from '../test-data/loginData.json';

test('Login with valid user', async ({ page }) => {

await page.goto('https://example.com/login');

await page.fill('#username', loginData.validUser.username);

await page.fill('#password', loginData.validUser.password);

await page.click('#loginBtn');

await expect(page).toHaveURL(/dashboard/);
});

4. Enable JSON Imports in TypeScript

Update your tsconfig.json.

tsconfig.json

{
"compilerOptions": {
"resolveJsonModule": true,
"esModuleInterop": true
}
}

5. Reading JSON Using fs Module (Dynamic Read)

This approach is useful when:

  • File changes frequently
  • Large datasets
  • Runtime data loading
  • External config management

utils/jsonReader.ts

import fs from 'fs';

export function readJsonFile(path: string) {

const data = fs.readFileSync(path, 'utf-8');

return JSON.parse(data);
}

Usage in Test

import { test, expect } from '@playwright/test';
import { readJsonFile } from '../utils/jsonReader';

test('Login using JSON data', async ({ page }) => {

const testData = readJsonFile('./test-data/loginData.json');

await page.goto('https://example.com/login');

await page.fill('#username', testData.validUser.username);

await page.fill('#password', testData.validUser.password);

await page.click('#loginBtn');

await expect(page).toHaveURL(/dashboard/);
});

6. Data-Driven Testing Using JSON

test-data/users.json

[
{
"username": "user1",
"password": "pass1"
},
{
"username": "user2",
"password": "pass2"
},
{
"username": "user3",
"password": "pass3"
}
]

Data-Driven Playwright Test

import { test } from '@playwright/test';
import users from '../test-data/users.json';

users.forEach((user) => {

test(`Login test for ${user.username}`, async ({ page }) => {

await page.goto('https://example.com/login');

await page.fill('#username', user.username);

await page.fill('#password', user.password);

await page.click('#loginBtn');
});
});

7. Best Practice in Enterprise Playwright Framework

Keep Separate JSON Files For

PurposeExample File
Login DataloginData.json
API Payloadspayloads.json
Environment Dataenv.json
Test Usersusers.json
Configurationsconfig.json

8. Recommended Folder Structure

test-data/

├── ui/
│ ├── login.json
│ └── checkout.json

├── api/
│ ├── createUser.json
│ └── updateUser.json

└── environments/
├── qa.json
└── prod.json

9. Example: Environment Configuration

environments/qa.json

{
"baseURL": "https://qa.example.com",
"username": "qauser",
"password": "qapass"
}

Usage

import env from '../test-data/environments/qa.json';

test('Environment Test', async ({ page }) => {

await page.goto(env.baseURL);
});

10. Async JSON Read Example

import fs from 'fs/promises';

export async function readJson(path: string) {

const data = await fs.readFile(path, 'utf-8');

return JSON.parse(data);
}

11. Real-World Enterprise Utility Class

utils/dataManager.ts

import fs from 'fs';

export class DataManager {

static getTestData(filePath: string) {

const data = fs.readFileSync(filePath, 'utf-8');

return JSON.parse(data);
}
}

Usage

import { test } from '@playwright/test';
import { DataManager } from '../utils/dataManager';

test('Enterprise JSON Example', async ({ page }) => {

const data = DataManager.getTestData(
'./test-data/loginData.json'
);

await page.goto('https://example.com');

console.log(data.validUser.username);
});

12. Common Errors and Fixes

ErrorSolution
Cannot find JSON moduleAdd resolveJsonModule: true
Unexpected token JSONUse JSON.parse() properly
Path not foundUse correct relative path
Undefined valuesVerify JSON structure

13. Recommended Best Practices

Use JSON When

✅ Static test data
✅ Small-medium datasets
✅ Config management
✅ API request payloads

Playwright API Automation with POM

API Automation with Page Object Model (POM)

What is API Automation in Playwright?

Playwright is not only used for UI automation; it also supports powerful API testing using built-in HTTP request methods.

API automation helps to:

  • Validate backend services
  • Test REST APIs
  • Verify response data
  • Perform authentication testing
  • Integrate API + UI workflows

Why Use Page Object Model (POM) for API Testing?

Page Object Model is a design pattern used to:

  • Separate API logic from test logic
  • Improve code reusability
  • Make framework scalable
  • Reduce code duplication
  • Improve maintenance

Recommended Project Structure

playwright-api-framework/

├── tests/
│ ├── user.spec.ts
│ └── booking.spec.ts

├── api/
│ ├── UserAPI.ts
│ └── BookingAPI.ts

├── utils/
│ ├── apiClient.ts
│ └── testData.ts

├── fixtures/
│ └── auth.json

├── playwright.config.ts
├── package.json
└── tsconfig.json

Step 1: Install Playwright

Initialize Project

npm init -y

Install Playwright

npm init playwright@latest

Choose:

  • TypeScript
  • VS Code
  • GitHub Actions (optional)

Step 2: Configure Playwright

playwright.config.ts

import { defineConfig } from '@playwright/test';

export default defineConfig({
use: {
baseURL: 'https://reqres.in/api',
extraHTTPHeaders: {
'Content-Type': 'application/json'
}
}
});

Step 3: Create Base API Client

utils/apiClient.ts

This class handles common API operations.

import { APIRequestContext, request } from '@playwright/test';

export class APIClient {
private apiContext!: APIRequestContext;

async createContext() {
this.apiContext = await request.newContext({
baseURL: 'https://reqres.in/api',
extraHTTPHeaders: {
'Content-Type': 'application/json'
}
});
}

getContext() {
return this.apiContext;
}
}

Step 4: Create API Page Object

api/UserAPI.ts

import { APIRequestContext, expect } from '@playwright/test';

export class UserAPI {
readonly request: APIRequestContext;

constructor(request: APIRequestContext) {
this.request = request;
}

async getUsers(pageNumber: number) {
const response = await this.request.get(`/users?page=${pageNumber}`);

expect(response.status()).toBe(200);

return await response.json();
}

async createUser(name: string, job: string) {
const response = await this.request.post('/users', {
data: {
name,
job
}
});

expect(response.status()).toBe(201);

return await response.json();
}

async updateUser(id: number, name: string, job: string) {
const response = await this.request.put(`/users/${id}`, {
data: {
name,
job
}
});

expect(response.status()).toBe(200);

return await response.json();
}

async deleteUser(id: number) {
const response = await this.request.delete(`/users/${id}`);

expect(response.status()).toBe(204);
}
}

Step 5: Write API Test Cases

tests/user.spec.ts

import { test, request, expect } from '@playwright/test';
import { UserAPI } from '../api/UserAPI';

test.describe('User API Tests', () => {

let userAPI: UserAPI;

test.beforeEach(async () => {
const apiContext = await request.newContext({
baseURL: 'https://reqres.in/api'
});

userAPI = new UserAPI(apiContext);
});

test('Get Users List', async () => {

const response = await userAPI.getUsers(2);

expect(response.page).toBe(2);
expect(response.data.length).toBeGreaterThan(0);
});

test('Create New User', async () => {

const response = await userAPI.createUser(
'Deepesh',
'QA Engineer'
);

expect(response.name).toBe('Deepesh');
expect(response.job).toBe('QA Engineer');
});

test('Update Existing User', async () => {

const response = await userAPI.updateUser(
2,
'Deepesh Updated',
'Senior QA'
);

expect(response.name).toBe('Deepesh Updated');
});

test('Delete User', async () => {

await userAPI.deleteUser(2);
});

});

Step 6: Execute Tests

Run All Tests

npx playwright test

Run Specific File

npx playwright test tests/user.spec.ts

Run in Headed Mode

npx playwright test --headed

Generate HTML Report

npx playwright show-report

API Assertions Examples

Validate Status Code

expect(response.status()).toBe(200);

Validate Response Body

expect(responseBody.name).toBe('Deepesh');

Validate Headers

expect(response.headers()['content-type'])
.toContain('application/json');

Authentication Example

Login API

async login(email: string, password: string) {

const response = await this.request.post('/login', {
data: {
email,
password
}
});

return await response.json();
}

Token Handling Example

const token = loginResponse.token;

const apiContext = await request.newContext({
extraHTTPHeaders: {
Authorization: `Bearer ${token}`
}
});

Reusable Test Data

utils/testData.ts

export const users = {
adminUser: {
name: 'Deepesh',
job: 'Automation Engineer'
}
};

Advanced Framework Enhancements

You can extend this framework with:

FeaturePurpose
Environment ConfigDEV/UAT/PROD support
LoggerAPI request-response logs
Allure ReportsAdvanced reporting
Retry MechanismHandle flaky APIs
CI/CDJenkins/GitHub Actions
Database ValidationBackend verification
Schema ValidationValidate JSON structure
Faker LibraryDynamic test data
Parallel ExecutionFaster execution

Real-World Enterprise Structure

framework/

├── api/
├── tests/
├── fixtures/
├── utils/
├── helpers/
├── data/
├── reports/
├── logs/
├── schemas/
├── config/
└── .github/workflows/

Best Practices

1. Keep API Logic Separate

Do not write API calls directly inside test files.

✅ Correct:

userAPI.createUser()

❌ Wrong:

request.post(...)

inside every test.


2. Use Environment Variables

BASE_URL=https://uat-api.company.com

3. Validate Response Time

expect(response.ok()).toBeTruthy();

4. Use Soft Assertions Carefully

expect.soft(response.status()).toBe(200);

5. Create Reusable Helpers

Example:

  • Token manager
  • Payload builder
  • Common assertions

Advantages of Playwright API Automation

AdvantageDescription
Fast ExecutionNo browser required
Built-in AssertionsEasy validations
API + UI ComboEnd-to-end workflow
Parallel SupportFaster pipelines
TypeScript SupportBetter maintainability
Auto WaitingStable execution

API + UI Hybrid Testing Example

test('Create user via API and validate in UI', async ({ page }) => {

// Create user via API
await userAPI.createUser('Deepesh', 'QA');

// Validate in UI
await page.goto('/users');

await expect(
page.locator('text=Deepesh')
).toBeVisible();
});

Maintain Different Config Files for API and UI Tests

To maintain 2 separate config files for API and UI test cases in frameworks like Playwright Docs, the best approach is to create the following:

  • One config for UI tests
  • One config for API tests

This helps you:

  • Run tests independently
  • Use different base URLs
  • Use different reporters/timeouts
  • Execute API tests faster without browser launch
  • Maintain clean project structure

Recommended Project Structure

project-root/

├── tests/
│ ├── ui/
│ │ ├── login.spec.ts
│ │ └── dashboard.spec.ts
│ │
│ └── api/
│ ├── users.spec.ts
│ └── auth.spec.ts

├── playwright-ui.config.ts
├── playwright-api.config.ts

├── utils/
├── pages/
├── package.json
└── tsconfig.json

1. UI Config File

Create:

playwright-ui.config.ts

import { defineConfig } from '@playwright/test';

export default defineConfig({
testDir: './tests/ui',

timeout: 60000,

use: {
browserName: 'chromium',
headless: false,
baseURL: 'https://example-ui.com',

screenshot: 'only-on-failure',
video: 'retain-on-failure',
trace: 'on-first-retry'
},

reporter: [
['html'],
['list']
],

projects: [
{
name: 'chrome',
use: {
browserName: 'chromium'
}
}
]
});

2. API Config File

Create:

playwright-api.config.ts

import { defineConfig } from '@playwright/test';

export default defineConfig({
testDir: './tests/api',

timeout: 30000,

use: {
baseURL: 'https://api.example.com',

extraHTTPHeaders: {
Accept: 'application/json'
}
},

reporter: [
['html'],
['dot']
],

workers: 4
});

3. Run UI Tests

npx playwright test --config=playwright-ui.config.ts

4. Run API Tests

npx playwright test --config=playwright-api.config.ts

5. Add Package.json Scripts

package.json

{
"scripts": {
"test:ui": "playwright test --config=playwright-ui.config.ts",

"test:api": "playwright test --config=playwright-api.config.ts",

"test:all": "npm run test:ui && npm run test:api"
}
}

6. Environment-Specific Config (Recommended)

You can also maintain:

.env.dev
.env.qa
.env.stage

Install dotenv:

npm install dotenv

Example:

import dotenv from 'dotenv';

dotenv.config({ path: '.env.qa' });

export default defineConfig({
use: {
baseURL: process.env.BASE_URL
}
});

7. Better Enterprise Approach (Best Practice)

Many companies use:

configs/

├── ui/
│ ├── dev.config.ts
│ ├── qa.config.ts
│ └── stage.config.ts

└── api/
├── dev.config.ts
├── qa.config.ts
└── stage.config.ts

This supports:

  • Multiple environments
  • CI/CD pipelines
  • Parallel execution
  • Easy maintenance

8. Common Real-World Usage

TypeSeparate Config Needed?Why
UI TestsYesBrowser settings
API TestsYesNo browser needed
Mobile TestsYesDevice configs
Smoke TestsYesFaster execution
RegressionYesLarge suite handling

9. Advanced Approach Using Base Config

You can avoid duplication using a shared base config.

playwright.base.ts

import { defineConfig } from '@playwright/test';

export default defineConfig({
timeout: 30000,

reporter: 'html'
});

UI Config

import base from './playwright.base';

export default {
...base,

testDir: './tests/ui',

use: {
browserName: 'chromium',
baseURL: 'https://ui.example.com'
}
};

API Config

import base from './playwright.base';

export default {
...base,

testDir: './tests/api',

use: {
baseURL: 'https://api.example.com'
}
};

Best Practice Recommendation

Use:

✅ Separate config files
✅ Shared base config
✅ Environment variables
✅ Separate folders for API/UI
✅ Separate reports
✅ Package.json scripts

Step-by-Step Guide to Install Git on MacOS

Method 1: Install Git Using Homebrew (Recommended)

Step 1: Open Terminal

Press:

  • Command (⌘) + Space
  • Type Terminal
  • Press Enter

Step 2: Check if Git is Already Installed

Run:

git --version

If Git is installed, you’ll see output like:

git version 2.45.0

If not installed, continue below.


Step 3: Install Homebrew (If Not Installed)

Run this command in Terminal:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Wait for installation to complete.


Step 4: Verify Homebrew Installation

Run:

brew --version

Example output:

Homebrew 4.3.0

Step 5: Install Git Using Homebrew

Run:

brew install git

Homebrew downloads and installs the latest Git version.


Step 6: Verify Git Installation

Run:

git --version

Example:

git version 2.45.1

Step 7: Configure Git Username

Run:

git config --global user.name "Your Name"

Example:

git config --global user.name "Deepesh Yadav"

Step 8: Configure Git Email

Run:

git config --global user.email "your-email@example.com"

Example:

git config --global user.email "deepesh@gmail.com"

Step 9: Verify Git Configuration

Run:

git config --list

Example output:

user.name=Deepesh Yadav
user.email=deepesh@gmail.com

Playwright Page Object Model (POM) – Step by Step Guide

1. What is Page Object Model (POM)?

Page Object Model (POM) is a design pattern used in test automation where:

  • Each web page is represented as a class
  • Locators and methods are stored separately from test files
  • Tests become reusable, maintainable, and readable

2. Why Use POM?

Problems Without POM

Without POM:

  • Duplicate locators everywhere
  • Hard to maintain tests
  • UI changes break multiple tests
  • Large test files become messy

Benefits of POM

✅ Reusable code
✅ Easy maintenance
✅ Better readability
✅ Centralized locators
✅ Scalable framework
✅ Cleaner tests


3. Basic Folder Structure

project/

├── tests/
│ └── login.spec.ts

├── pages/
│ └── LoginPage.ts

├── utils/

├── fixtures/

├── playwright.config.ts

├── package.json

└── tsconfig.json

4. Install Playwright

Step 1: Create Project

mkdir playwright-pom
cd playwright-pom

Step 2: Initialize Node Project

npm init -y

Step 3: Install Playwright

npm init playwright@latest

Select:

  • TypeScript
  • Tests folder
  • GitHub Actions (optional)

5. Understanding POM Structure

In POM:

ComponentResponsibility
Page ClassStores locators + methods
Test FileContains test scenarios
FixturesShared setup
UtilsReusable helper methods

6. Create Your First Page Object


Example Website

We will automate:

  • Login page
  • Dashboard validation

7. Create Login Page Class

File

pages/LoginPage.ts

LoginPage.ts

import { Page, Locator, expect } from '@playwright/test';

export class LoginPage {

readonly page: Page;
readonly username: Locator;
readonly password: Locator;
readonly loginBtn: Locator;
readonly dashboardText: Locator;

constructor(page: Page) {
this.page = page;

this.username = page.locator('#username');
this.password = page.locator('#password');
this.loginBtn = page.locator('#loginBtn');
this.dashboardText = page.locator('text=Dashboard');
}

async gotoLoginPage() {
await this.page.goto('https://example.com/login');
}

async enterUsername(user: string) {
await this.username.fill(user);
}

async enterPassword(pass: string) {
await this.password.fill(pass);
}

async clickLogin() {
await this.loginBtn.click();
}

async login(user: string, pass: string) {
await this.enterUsername(user);
await this.enterPassword(pass);
await this.clickLogin();
}

async verifyDashboardVisible() {
await expect(this.dashboardText).toBeVisible();
}
}

8. Create Test File

File

tests/login.spec.ts

login.spec.ts

import { test } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';

test('User Login Test', async ({ page }) => {

const loginPage = new LoginPage(page);

await loginPage.gotoLoginPage();

await loginPage.login('admin', 'admin123');

await loginPage.verifyDashboardVisible();
});

9. Execute Tests

npx playwright test

10. Run Specific Test

npx playwright test tests/login.spec.ts

11. View HTML Report

npx playwright show-report

12. Improve POM Design

Basic POM is good, but enterprise frameworks need more structure.


Better Folder Structure

project/

├── pages/
│ ├── LoginPage.ts
│ ├── DashboardPage.ts
│ └── BasePage.ts

├── tests/

├── fixtures/

├── test-data/

├── utils/

├── hooks/

├── api/

├── constants/

└── playwright.config.ts

13. Create Base Page

Why Base Page?

To avoid duplicate common methods:

  • click
  • fill
  • wait
  • navigate
  • screenshot

BasePage.ts

import { Page, Locator } from '@playwright/test';

export class BasePage {

readonly page: Page;

constructor(page: Page) {
this.page = page;
}

async navigate(url: string) {
await this.page.goto(url);
}

async click(locator: Locator) {
await locator.click();
}

async fill(locator: Locator, value: string) {
await locator.fill(value);
}

async getTitle() {
return await this.page.title();
}

async wait(milliseconds: number) {
await this.page.waitForTimeout(milliseconds);
}
}

14. Extend Base Page

Updated LoginPage.ts

import { Page, Locator } from '@playwright/test';
import { BasePage } from './BasePage';

export class LoginPage extends BasePage {

readonly username: Locator;
readonly password: Locator;
readonly loginBtn: Locator;

constructor(page: Page) {
super(page);

this.username = page.locator('#username');
this.password = page.locator('#password');
this.loginBtn = page.locator('#loginBtn');
}

async login(user: string, pass: string) {

await this.navigate('https://example.com/login');

await this.fill(this.username, user);

await this.fill(this.password, pass);

await this.click(this.loginBtn);
}
}

15. Create Dashboard Page

DashboardPage.ts

import { Page, Locator, expect } from '@playwright/test';

export class DashboardPage {

readonly page: Page;
readonly dashboardHeader: Locator;

constructor(page: Page) {
this.page = page;

this.dashboardHeader = page.locator('h1');
}

async verifyDashboardLoaded() {
await expect(this.dashboardHeader).toHaveText('Dashboard');
}
}

16. Updated Test

import { test } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
import { DashboardPage } from '../pages/DashboardPage';

test('Verify Login', async ({ page }) => {

const loginPage = new LoginPage(page);
const dashboardPage = new DashboardPage(page);

await loginPage.login('admin', 'admin123');

await dashboardPage.verifyDashboardLoaded();
});

17. Best Practices for POM

1. Keep Locators Private to Page

❌ Bad

page.locator('#btn').click();

inside test files.


✅ Good

await loginPage.clickLogin();

2. Avoid Assertions Inside Page Methods

Page objects should perform actions.

Assertions should mostly remain in test files.


3. Use Meaningful Method Names

✅ Good

await loginPage.login();

❌ Bad

await loginPage.doAction1();

4. One Page = One Class

Each application page should have its own class.


5. Reuse Common Functions

Use:

  • BasePage
  • Utility classes
  • Helper functions

18. Real Enterprise POM Structure

project/

├── pages/
│ ├── auth/
│ │ ├── LoginPage.ts
│ │ └── ForgotPasswordPage.ts
│ │
│ ├── dashboard/
│ │ └── DashboardPage.ts
│ │
│ └── common/
│ └── BasePage.ts

├── tests/

├── fixtures/

├── api/

├── utils/

├── constants/

├── test-data/

├── reports/

└── screenshots/

19. Advanced POM Concepts

Component Object Model

Reusable UI components:

  • Navbar
  • Sidebar
  • Footer
  • Modal
  • Table

Example

export class HeaderComponent {

readonly profileIcon;
readonly logoutBtn;

constructor(page) {
this.profileIcon = page.locator('#profile');
this.logoutBtn = page.locator('#logout');
}

async logout() {
await this.profileIcon.click();
await this.logoutBtn.click();
}
}

20. POM with Fixtures

fixtures/baseTest.ts

import { test as base } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';

type MyFixtures = {
loginPage: LoginPage;
};

export const test = base.extend<MyFixtures>({
loginPage: async ({ page }, use) => {
await use(new LoginPage(page));
},
});

Usage

import { test } from '../fixtures/baseTest';

test('Login Test', async ({ loginPage }) => {

await loginPage.login('admin', 'admin123');
});

21. POM with Environment Variables

Install dotenv

npm install dotenv

.env

BASE_URL=https://example.com
USERNAME=admin
PASSWORD=admin123

playwright.config.ts

import dotenv from 'dotenv';

dotenv.config();

export default {
use: {
baseURL: process.env.BASE_URL
}
};

22. Common Mistakes in POM

❌ Huge Page Classes

Avoid:

  • 2000-line page classes

Split by:

  • Components
  • Features

❌ Hardcoded Waits

Avoid:

waitForTimeout(5000)

Use:

  • auto waiting
  • expect
  • waitForResponse

❌ Duplicate Locators

Centralize locators.


23. Industry-Level Recommendations

Recommended Stack

AreaTool
UI AutomationPlaywright
LanguageTypeScript
FrameworkPOM + Fixtures
ReportingAllure
CI/CDGitHub Actions
API TestingPlaywright API
DataJSON / Faker
Parallel RunPlaywright Workers

24. Final Enterprise Flow

Test File

Page Object

Base Page

Playwright Engine

Browser

25. Summary

POM Helps You

✅ Build scalable frameworks
✅ Reduce maintenance
✅ Improve readability
✅ Reuse code
✅ Create enterprise-level automation


26. Recommended Next Topics

After learning POM, learn:

  1. Playwright Fixtures
  2. API Testing
  3. Authentication Handling
  4. Parallel Execution
  5. Data-Driven Testing
  6. CI/CD Integration
  7. Retry Mechanism
  8. Playwright Hooks
  9. Allure Reporting
  10. Hybrid Framework Design

JavaScript Object Methods and Programs

In JavaScript, an Object is a non-primitive data type used to store data in key-value pairs.

Objects are one of the most important concepts in JavaScript because almost everything in JavaScript is based on objects.


1. Creating Objects

Using Object Literal (Most Common)

const person = {
name: "John",
age: 25,
city: "New York"
};

console.log(person);

Using new Object()

const person = new Object();

person.name = "John";
person.age = 25;

console.log(person);

2. Accessing Object Properties

Dot Notation

console.log(person.name);
console.log(person.age);

Bracket Notation

console.log(person["name"]);
console.log(person["age"]);

Useful when property names are dynamic.

let key = "name";

console.log(person[key]);

3. Adding Properties

person.country = "USA";

console.log(person);

4. Updating Properties

person.age = 30;

console.log(person.age);

5. Deleting Properties

delete person.city;

console.log(person);

6. Nested Objects

const student = {
name: "Sam",
marks: {
math: 90,
science: 85
}
};

console.log(student.marks.math);

7. Object Methods

Methods are functions inside objects.

const user = {
name: "Alex",

greet: function () {
console.log("Hello");
}
};

user.greet();

Important Object Methods


1. Object.keys()

Returns all object keys.

const person = {
name: "John",
age: 25
};

console.log(Object.keys(person));

Output:

["name", "age"]

2. Object.values()

Returns all values.

console.log(Object.values(person));

Output:

["John", 25]

3. Object.entries()

Returns key-value pairs as arrays.

console.log(Object.entries(person));

Output:

[
["name", "John"],
["age", 25]
]

4. Object.assign()

Copies properties from one object to another.

const obj1 = { a: 1 };
const obj2 = { b: 2 };

const result = Object.assign({}, obj1, obj2);

console.log(result);

Output:

{ a: 1, b: 2 }

5. Object.freeze()

Prevents modification.

const car = {
brand: "BMW"
};

Object.freeze(car);

car.brand = "Audi";

console.log(car.brand);

Output:

BMW

6. Object.seal()

Allows updating existing properties but prevents adding/removing.

const user = {
name: "John"
};

Object.seal(user);

user.name = "Sam"; // allowed
user.age = 30; // not allowed

console.log(user);

7. Object.hasOwn()

Checks if property exists.

const person = {
name: "John"
};

console.log(Object.hasOwn(person, "name"));

Output:

true

8. Object.create()

Creates a new object using another object as prototype.

const animal = {
sound: "Bark"
};

const dog = Object.create(animal);

console.log(dog.sound);

9. Object.fromEntries()

Converts array to object.

const arr = [
["name", "John"],
["age", 25]
];

console.log(Object.fromEntries(arr));

Output:

{
name: "John",
age: 25
}

10. Object.is()

Compares two values.

console.log(Object.is(10, 10));

Output:

true

Looping Through Objects

Using for...in

const person = {
name: "John",
age: 25
};

for (let key in person) {
console.log(key, person[key]);
}

Object Destructuring

const person = {
name: "John",
age: 25
};

const { name, age } = person;

console.log(name);
console.log(age);

Spread Operator with Objects

const obj1 = {
a: 1
};

const obj2 = {
...obj1,
b: 2
};

console.log(obj2);

Shallow Copy vs Reference

Reference Copy

const obj1 = {
name: "John"
};

const obj2 = obj1;

obj2.name = "Sam";

console.log(obj1.name);

Output:

Sam

Shallow Copy

const obj1 = {
name: "John"
};

const obj2 = { ...obj1 };

obj2.name = "Sam";

console.log(obj1.name);

Output:

John

Real-World Example

const employee = {
id: 101,
name: "David",
department: "QA",
skills: ["JavaScript", "Playwright"],

displayInfo() {
console.log(`${this.name} works in ${this.department}`);
}
};

employee.displayInfo();

Common Interview Questions

Difference Between Object and Array

ObjectArray
Stores key-value pairsStores ordered values
Uses keysUses indexes
{}[]

Difference Between == and Object.is()

console.log(NaN == NaN); // false
console.log(Object.is(NaN, NaN)); // true

Practice Programs

1. Count Object Properties

const user = {
name: "John",
age: 25,
city: "NY"
};

console.log(Object.keys(user).length);

2. Merge Two Objects

const a = { x: 1 };
const b = { y: 2 };

const c = { ...a, ...b };

console.log(c);

3. Convert Object to Array

const person = {
name: "John",
age: 25
};

console.log(Object.entries(person));

Summary

JavaScript objects are used to:

  • Store structured data
  • Create reusable methods
  • Represent real-world entities
  • Manage application state

Most commonly used object methods:

  • Object.keys()
  • Object.values()
  • Object.entries()
  • Object.assign()
  • Object.freeze()
  • Object.seal()
  • Object.hasOwn()

10 JavaScript Object Programs with Solutions


1. Count Number of Properties in an Object

Problem

Find total number of keys in an object.

Solution

const user = {
name: "John",
age: 25,
city: "New York"
};

const count = Object.keys(user).length;

console.log(count);

Output

3

2. Iterate Through Object Properties

Problem

Print all keys and values from an object.

Solution

const student = {
name: "Sam",
marks: 90,
grade: "A"
};

for (let key in student) {
console.log(key + " : " + student[key]);
}

Output

name : Sam
marks : 90
grade : A

3. Merge Two Objects

Problem

Combine two objects into one.

Solution

const obj1 = {
a: 1,
b: 2
};

const obj2 = {
c: 3,
d: 4
};

const merged = { ...obj1, ...obj2 };

console.log(merged);

Output

{
a: 1,
b: 2,
c: 3,
d: 4
}

4. Check if Property Exists

Problem

Check whether a key exists in object.

Solution

const employee = {
id: 101,
name: "David"
};

console.log("name" in employee);
console.log("salary" in employee);

Output

true
false

5. Remove Property from Object

Problem

Delete a property from object.

Solution

const car = {
brand: "BMW",
color: "Black"
};

delete car.color;

console.log(car);

Output

{
brand: "BMW"
}

6. Convert Object to Array

Problem

Convert object into array of key-value pairs.

Solution

const person = {
name: "John",
age: 30
};

const result = Object.entries(person);

console.log(result);

Output

[
["name", "John"],
["age", 30]
]

7. Find Sum of Object Values

Problem

Calculate sum of numeric values in object.

Solution

const marks = {
math: 90,
science: 80,
english: 85
};

let sum = 0;

for (let key in marks) {
sum += marks[key];
}

console.log(sum);

Output

255

8. Clone an Object

Problem

Create copy of object without affecting original.

Solution

const original = {
name: "Alex",
age: 28
};

const copy = { ...original };

copy.name = "Sam";

console.log(original);
console.log(copy);

Output

{ name: "Alex", age: 28 }

{ name: "Sam", age: 28 }

9. Freeze an Object

Problem

Prevent object modification.

Solution

const user = {
name: "John"
};

Object.freeze(user);

user.name = "Sam";

console.log(user.name);

Output

John

10. Nested Object Access

Problem

Access values inside nested objects.

Solution

const company = {
name: "TechSoft",
employee: {
id: 101,
details: {
name: "David",
role: "QA Engineer"
}
}
};

console.log(company.employee.details.name);
console.log(company.employee.details.role);

Output

David
QA Engineer

Bonus Program — Object Destructuring

Problem

Extract object properties into variables.

Solution

const user = {
name: "John",
age: 25
};

const { name, age } = user;

console.log(name);
console.log(age);

Output

John
25

Concepts Covered

These programs cover:

  • Object creation
  • Object iteration
  • Object methods
  • Nested objects
  • Destructuring
  • Spread operator
  • Property checking
  • Cloning
  • Freezing objects
  • Data transformation