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
- Select a lesson from the sidebar on the left
- Work through the lessons in order for the best learning experience
- Each lesson contains detailed explanations and runnable examples
Your First JavaScript Program
Create a file called hello.js and paste the following:
// Your first JavaScript program console.log("Hello, World!"); console.log("Welcome to JavaScript for QA Automation!");
Run it with Node.js:
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.
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:
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
node --version v22.x.x npm --version 10.x.x
Step 3 — Create Your First Project
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:
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
// 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
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:
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)
// 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
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
// 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
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
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
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
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
// 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:
// 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
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.