← XpressFunda

JavaScript Tutorial

JavaScript is a lightweight, cross-platform, object-oriented language — and the foundation of modern browser automation.

🟨

Get Started

Welcome to the JavaScript Tutorial series on XpressFunda! JavaScript is the primary language for browser-based test automation — powering Playwright, Cypress, and the entire Node.js testing ecosystem.

🚀

This tutorial is designed for QA engineers who want to master JavaScript specifically for automation — from fundamentals to writing robust end-to-end tests using Playwright.

What You'll Learn

  • Core language features: variables, functions, loops, and objects
  • Modern ES6+ syntax used in Playwright and Cypress
  • Asynchronous programming with async / await
  • Working with arrays and objects for test data management
  • OOP patterns including the Page Object Model (POM)

To Begin Learning

  1. Select a lesson from the sidebar on the left
  2. Work through the lessons in order for the best learning experience
  3. Each lesson contains detailed explanations and runnable examples

Your First JavaScript Program

Create a file called hello.js and paste the following:

hello.js
// Your first JavaScript program
console.log("Hello, World!");
console.log("Welcome to JavaScript for QA Automation!");

Run it with Node.js:

terminal
node hello.js

Hello, World!
Welcome to JavaScript for QA Automation!

💡 Note: You'll need Node.js installed to run JavaScript outside the browser. See the Setting Up Node.js lesson in this chapter.

Why JavaScript for QA?

JavaScript has become the dominant language for browser-based test automation. Here's why every QA engineer should invest in learning it:

1. Playwright & Cypress Are JS-First

The two most popular end-to-end testing frameworks — Playwright and Cypress — are built on Node.js and use JavaScript/TypeScript natively. Writing tests in the same language as the framework gives you full access to its API.

playwright-test.js — a real Playwright test
const { test, expect } = require('@playwright/test');

test('Homepage title is correct', async ({ page }) => {
  await page.goto('https://xpressfunda.com');
  await expect(page).toHaveTitle(/XpressFunda/);
});

2. The npm Ecosystem

Node Package Manager (npm) gives you instant access to thousands of testing utilities:

  • @playwright/test — End-to-end browser automation
  • jest — Unit and integration testing
  • supertest — HTTP API testing without a browser
  • @faker-js/faker — Generate realistic test data
  • dotenv — Manage environment variables securely

3. Async/Await for Browser Testing

Browser interactions are asynchronous — clicking a button or navigating to a URL takes time. JavaScript's async/await makes handling these actions clean and readable:

async-playwright.js
test('Login flow works', async ({ page }) => {
  await page.goto('/login');
  await page.fill('#email', 'user@example.com');
  await page.fill('#password', 'secret123');
  await page.click('[type="submit"]');
  await expect(page.locator('.dashboard')).toBeVisible();
});

4. TypeScript Compatibility

JavaScript is the foundation of TypeScript. Once you know JS, moving to TypeScript — which adds static typing for safer test code — is straightforward. Most enterprise Playwright projects use TypeScript.

Setting Up Node.js

Node.js is the JavaScript runtime that lets you run JS outside the browser. It's required for Playwright, Cypress, and almost every modern JavaScript testing tool.

Step 1 — Install Node.js

Download the LTS (Long Term Support) version from nodejs.org. The installer also includes npm (Node Package Manager).

📥

Always choose the LTS version — not "Current" — for production and automation projects. Node.js 22 LTS is recommended as of 2025.

Step 2 — Verify Installation

terminal
node --version
v22.x.x

npm --version
10.x.x

Step 3 — Create Your First Project

terminal
mkdir js-automation-practice
cd js-automation-practice
npm init -y

This generates a package.json file that tracks your project's dependencies and scripts.

Step 4 — Install Playwright (Optional)

To follow the automation examples later in this tutorial, install Playwright:

terminal
npm init playwright@latest

💡 Tip: Install VS Code and the Playwright Test for VS Code extension for the best development and debugging experience.

Variables & Data Types

Variables store data values. In modern JavaScript, use const and let — never var.

const vs let

variables.js
// const — cannot be reassigned
const BASE_URL   = 'https://xpressfunda.com';
const MAX_RETRIES = 3;

// let — value can change
let currentPage = 1;
let isLoggedIn  = false;

currentPage = 2;      // ✅ OK
// BASE_URL = '...'; // ❌ TypeError — const cannot be reassigned
💡

Best practice: Default to const. Only use let when the value genuinely needs to change. Never use var in modern code.

Primitive Data Types

data-types.js
const name     = 'Playwright';   // String
const version  = 4.2;            // Number
const isActive = true;           // Boolean
const config   = null;           // Null — intentional absence
let   unset;                      // Undefined — not yet assigned

console.log(typeof name);        // "string"
console.log(typeof version);     // "number"
console.log(typeof isActive);    // "boolean"
console.log(typeof unset);       // "undefined"

Template Literals

Use backticks to embed variables directly in strings — far cleaner than concatenation:

template-literals.js
const browser = 'chromium';
const env     = 'staging';

// Old way
console.log('Running ' + browser + ' on ' + env);

// Modern template literal
console.log(`Running ${browser} on ${env}`);
// Running chromium on staging

Arrays & Objects (Preview)

complex-types.js
// Array — ordered list
const browsers = ['chromium', 'firefox', 'webkit'];
console.log(browsers[0]);  // chromium

// Object — key/value pairs
const testConfig = {
  browser:  'chromium',
  headless: true,
  timeout:  30000,
  baseURL:  'https://xpressfunda.com'
};
console.log(testConfig.browser);      // chromium
console.log(testConfig['timeout']);  // 30000

Operators

Operators perform calculations, comparisons, and logical checks — all essential for writing assertions and conditional test logic.

Arithmetic Operators

arithmetic.js
const total   = 10 + 5;   // 15 — addition
const diff    = 10 - 5;   // 5  — subtraction
const product = 10 * 5;   // 50 — multiplication
const quot    = 10 / 4;   // 2.5 — division
const remain  = 10 % 3;   // 1   — modulus (remainder)
const power   = 2 ** 8;   // 256 — exponentiation

Comparison Operators

comparison.js
// Always use === (strict equality) — checks value AND type
console.log(5 === 5);     // true
console.log(5 === '5');   // false — type mismatch!
console.log(5 !== '5');   // true

// Loose equality == — avoid in tests!
console.log(5 == '5');    // true ← unexpected coercion

console.log(10 > 5);     // true
console.log(3 <= 3);     // true

Logical Operators

logical.js
const isAdmin   = true;
const hasAccess = false;

console.log(isAdmin && hasAccess);  // false — AND: both must be true
console.log(isAdmin || hasAccess);  // true  — OR:  at least one true
console.log(!isAdmin);              // false — NOT: inverts the value

// Nullish coalescing — use default if null or undefined
const timeout = null ?? 30000;  // 30000

Control Flow

Control flow determines which code runs and when — essential for building conditional test logic and iterating over test data sets.

if / else if / else

conditionals.js
const statusCode = 404;

if (statusCode === 200) {
  console.log('✅ Request successful');
} else if (statusCode === 401) {
  console.log('🔐 Unauthorised — check credentials');
} else if (statusCode === 404) {
  console.log('🔍 Resource not found');
} else {
  console.log(`❌ Unexpected status: ${statusCode}`);
}

Loops

loops.js
const browsers = ['chromium', 'firefox', 'webkit'];

// Classic for loop
for (let i = 0; i < browsers.length; i++) {
  console.log(`Testing: ${browsers[i]}`);
}

// for...of — cleaner for arrays
for (const browser of browsers) {
  console.log(`Testing: ${browser}`);
}

// forEach with arrow function
browsers.forEach(b => console.log(`Testing: ${b}`));

Switch Statement

switch.js
const env = 'staging';

switch (env) {
  case 'dev':
    console.log('Using dev config');    break;
  case 'staging':
    console.log('Using staging config'); break;
  case 'prod':
    console.log('Using prod config');    break;
  default:
    throw new Error(`Unknown environment: ${env}`);
}

Functions & Arrow Functions

Functions let you package reusable logic. In test automation, functions power helper utilities, page actions, and test data factories.

Function Declarations

functions.js
// Classic function declaration
function greet(name) {
  return `Hello, ${name}!`;
}
console.log(greet('Playwright'));  // Hello, Playwright!

// Default parameters
function buildUrl(path, base = 'https://xpressfunda.com') {
  return `${base}${path}`;
}
console.log(buildUrl('/login'));
// https://xpressfunda.com/login

Arrow Functions

Arrow functions are the concise modern syntax — you'll see them throughout every Playwright test file:

arrow-functions.js
// Traditional function
function add(a, b) { return a + b; }

// Arrow function — equivalent
const add = (a, b) => a + b;

// Single parameter — no parentheses needed
const double = n => n * 2;

// Multi-line body needs { } and explicit return
const getPort = (env) => {
  const ports = { dev: 3000, staging: 8080, prod: 443 };
  return ports[env] ?? 80;
};

console.log(double(5));           // 10
console.log(getPort('staging')); // 8080

Higher-Order Functions for Test Data

higher-order.js
const users = [
  { username: 'admin',  role: 'admin',  active: true  },
  { username: 'viewer', role: 'viewer', active: true  },
  { username: 'legacy', role: 'admin',  active: false },
];

// filter — keep only matching items
const active = users.filter(u => u.active);

// map — transform each item
const names = users.map(u => u.username);

// find — first matching item
const firstAdmin = users.find(u => u.role === 'admin');

console.log(active.length);          // 2
console.log(names);                  // ['admin', 'viewer', 'legacy']
console.log(firstAdmin.username);    // admin

Async/Await & Promises

🚧

This lesson is being written. Check back soon! In the meantime, explore the lessons above.

Arrays & Array Methods

📚

Coming Soon

This lesson covers map, filter, reduce, find, some, every, and flat — all used heavily in test data management.

Objects & Destructuring

📚

Coming Soon

Covers object creation, spread operator, destructuring, and optional chaining — essential for test config management.

Maps & Sets

📚

Coming Soon

Maps and Sets for managing unique test data collections.

Object Creation Patterns

📚

Coming Soon

Factory functions, constructor functions, and Object.create().

Prototype Chain

📚

Coming Soon

How JavaScript objects inherit properties through the prototype chain.

ES6 Classes

📚

Coming Soon

Class syntax, constructors, methods, and inheritance — the foundation of the Page Object Model.

Selecting Elements

📚

Coming Soon

querySelector, getElementById, and how Playwright's locators relate to browser DOM selection.

Events & Listeners

📚

Coming Soon

addEventListener, event objects, and how browser events map to Playwright's click/fill actions.

Fetch API

📚

Coming Soon

Make HTTP requests with fetch() and async/await — directly relevant to API testing in Playwright.

Encapsulation

📚

Coming Soon

Private fields (#field), getters/setters, and hiding implementation details in test helpers.

Inheritance

📚

Coming Soon

extends, super(), and building a hierarchy of Page Object classes from a shared BasePage.

Page Object Model

🏆

Coming Soon

The final lesson — putting it all together to build a full POM test framework with Playwright and JavaScript classes.