CommandsIntermediate8-10 min reference
JavaScript for Testers
The JavaScript features you'll reach for in Cypress, Playwright, Jest, Vitest, and Node-based API testing — modern syntax, async patterns, and idioms.
Variables & Data Types
let, const, var
const url = 'https://api.example.com'; // immutable binding (the value can mutate)
let count = 0; // mutable
count++;
// Avoid var — function-scoped + hoisted, leads to bugs
var x = 1; // ✗ don'tconst is block-scoped and prevents reassignment. Use it by default; switch to let only when you reassign.
Primitive types
typeof "hello" // "string"
typeof 42 // "number"
typeof 9007199254740993n // "bigint"
typeof true // "boolean"
typeof undefined // "undefined"
typeof null // "object" ← well-known JS quirk
typeof Symbol() // "symbol"
typeof {} // "object"
typeof [] // "object"
typeof (() => {}) // "function"Template literals
const name = 'Ada';
const greeting = `Hello, ${name}! Today is ${new Date().toDateString()}.`;
const multiline = `line 1
line 2
line 3`;== vs ===
1 == '1'; // true — coerces types
1 === '1'; // false — strict equality
null == undefined; // true
null === undefined; // false
// Always use === unless you have a specific reason not toTruthy / falsy
These six values are falsy; everything else is truthy:
false
0 ( -0, 0n )
"" ( '', `` )
null
undefined
NaNif (response.body) { /* runs only if non-empty */ }
// Convert anything to boolean
Boolean(value); // or !!valueStrings & Numbers
String methods
const s = ' Hello World ';
s.length; // 15
s.trim(); // 'Hello World'
s.trimStart(); s.trimEnd();
s.toLowerCase();
s.toUpperCase();
s.includes('World'); // true
s.startsWith(' H'); // true
s.endsWith(' '); // true
s.indexOf('World'); // 8
s.slice(2, 7); // 'Hello'
s.substring(2, 7); // 'Hello'
s.split(' '); // ['', '', 'Hello', 'World', '', '']
s.replace('World', 'QA'); // first match only
s.replaceAll('l', 'L'); // every match
s.repeat(2);
s.padStart(20, '·');
s.padEnd(20, '·');
s.match(/\w+/g); // ['Hello', 'World']Number methods and Math
const n = 3.14159;
n.toFixed(2); // '3.14' (string!)
parseInt('123abc'); // 123
parseFloat('3.14abc'); // 3.14
Number.isNaN(0/0); // true
Number.isInteger(3); // true
Number.isFinite(Infinity); // false
Number.MAX_SAFE_INTEGER; // 9007199254740991
Math.floor(3.7); // 3
Math.ceil(3.2); // 4
Math.round(3.5); // 4
Math.trunc(-3.7); // -3
Math.abs(-5); // 5
Math.max(1, 2, 3); // 3
Math.min(...[1, 2, 3]); // 1
Math.random(); // [0, 1)
Math.floor(Math.random() * 100); // [0, 99]Arrays
Creating
const arr = [1, 2, 3];
const empty = [];
Array.of(1, 2, 3); // [1, 2, 3]
Array.from('abc'); // ['a', 'b', 'c']
Array.from({ length: 5 }, (_, i) => i * 2); // [0, 2, 4, 6, 8]
[...arr, 4, 5]; // spread to copy
new Array(3).fill(0); // [0, 0, 0]Adding and removing
const a = [1, 2, 3];
a.push(4); // → 4 (length), a = [1,2,3,4]
a.pop(); // → 4, a = [1,2,3]
a.shift(); // → 1, a = [2,3]
a.unshift(0); // → 3 (length), a = [0,2,3]
a.splice(1, 1); // remove 1 item at index 1, returns removed
a.splice(1, 0, 9); // insert 9 at index 1push/pop/shift/unshift/splice mutate. Prefer non-mutating versions (toSorted, toReversed, toSpliced in Node 20+) or spread copy when you need immutability.
Searching
const users = [{ id: 1, name: 'Ada' }, { id: 2, name: 'Bob' }];
users.find(u => u.id === 1); // { id: 1, name: 'Ada' }
users.findIndex(u => u.name === 'Bob'); // 1
users.includes({ id: 1 }); // false (reference compare)
users.some(u => u.id > 1); // true
users.every(u => u.name); // true
[1, 2, 3].indexOf(2); // 1Transforming
const nums = [1, 2, 3, 4, 5];
nums.map(n => n * 2); // [2, 4, 6, 8, 10]
nums.filter(n => n % 2 === 0); // [2, 4]
nums.reduce((sum, n) => sum + n, 0); // 15
nums.flat(); // [1, 2, 3, 4, 5]
[[1, 2], [3, 4]].flat(); // [1, 2, 3, 4]
[1, 2, 3].flatMap(n => [n, n * 10]); // [1, 10, 2, 20, 3, 30]
[3, 1, 2].sort(); // [1, 2, 3]
[10, 2, 1].sort((a, b) => a - b); // [1, 2, 10] (numeric)
[3, 1, 2].toReversed(); // [2, 1, 3] (non-mutating)Iterating
const xs = ['a', 'b', 'c'];
xs.forEach((x, i) => console.log(i, x));
for (const x of xs) console.log(x);
for (const [i, x] of xs.entries()) {
console.log(i, x);
}Destructuring
const [first, second, ...rest] = [1, 2, 3, 4];
// first=1, second=2, rest=[3, 4]
const [, , third] = [1, 2, 3];
// third=3 — skip with empty slots
const [a = 'default'] = [];
// a='default'Objects
Creating and accessing
const user = {
name: 'Ada',
age: 36,
'full-name': 'Ada Lovelace',
};
user.name; // 'Ada' — dot notation
user['full-name']; // 'Ada Lovelace' — bracket required for hyphenated keys
user[someVariable]; // dynamic keyDestructuring
const { name, age } = user;
const { name: userName } = user; // rename
const { country = 'UK' } = user; // default
const { address: { city } = {} } = user; // nested with default
// Function parameters
function greet({ name, greeting = 'Hello' }) {
return `${greeting}, ${name}`;
}
greet({ name: 'Ada' }); // 'Hello, Ada'Spread
const updated = { ...user, age: 37 }; // shallow copy + override
const merged = { ...defaults, ...overrides };
const { password, ...safeUser } = user; // omit passwordOptional chaining and nullish coalescing
user?.address?.city; // undefined if any link is null/undefined
user?.greet?.(); // call only if greet exists
const port = config.port ?? 3000; // use 3000 if port is null/undefined
const name = response.user?.name ?? 'Anonymous';
// ?? differs from || — only nullish, not falsy
0 || 100; // 100 — 0 is falsy
0 ?? 100; // 0 — 0 isn't nullishObject methods
Object.keys(user); // ['name', 'age', 'full-name']
Object.values(user); // ['Ada', 36, 'Ada Lovelace']
Object.entries(user); // [['name', 'Ada'], ['age', 36], ...]
Object.fromEntries([['a', 1], ['b', 2]]); // { a: 1, b: 2 }
Object.assign({}, defaults, overrides); // pre-spread alternative
Object.freeze(user); // shallow-immutable
'name' in user; // true
user.hasOwnProperty('name'); // true (older API)
Object.hasOwn(user, 'name'); // true (modern, ES2022)Computed property names
const key = 'status';
const result = { [key]: 'ok' }; // { status: 'ok' }Functions
Arrow functions
const add = (a, b) => a + b;
const square = n => n * n; // single param, no parens
const noop = () => {};
const wrap = x => ({ value: x }); // ()-wrap to return an object literal
// Arrow functions don't have their own `this` — they inherit from enclosing scopeDefault and rest params
function fetch(url, options = {}) { /* ... */ }
function sum(...nums) { return nums.reduce((a, b) => a + b, 0); }
sum(1, 2, 3); // 6
sum(...[1, 2, 3]); // spread an array as argsClosures
function counter() {
let n = 0;
return () => ++n;
}
const next = counter();
next(); // 1
next(); // 2IIFE
(() => {
const internal = 'private';
console.log(internal);
})();Higher-order functions
const retry = (fn, attempts = 3) => async (...args) => {
let lastErr;
for (let i = 0; i < attempts; i++) {
try { return await fn(...args); }
catch (e) { lastErr = e; }
}
throw lastErr;
};
const safeFetch = retry(fetch, 3);Async / Await & Promises
Creating a Promise
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const fetchUser = id => new Promise((resolve, reject) => {
if (!id) return reject(new Error('id required'));
setTimeout(() => resolve({ id, name: 'Ada' }), 100);
});.then().catch().finally()
fetch('/api/users')
.then(res => res.json())
.then(users => console.log(users))
.catch(err => console.error(err))
.finally(() => console.log('done'));async / await
async function loadUsers() {
try {
const res = await fetch('/api/users');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const users = await res.json();
return users.filter(u => u.active);
} catch (err) {
console.error('loadUsers failed', err);
throw err;
} finally {
console.log('done');
}
}Promise.all / allSettled / race / any
// Run in parallel — fail fast on any rejection
const [users, orders, products] = await Promise.all([
fetch('/users'), fetch('/orders'), fetch('/products')
]);
// Wait for all — never rejects, returns { status, value/reason } per item
const results = await Promise.allSettled(promises);
const ok = results.filter(r => r.status === 'fulfilled').map(r => r.value);
// First settled (fulfilled OR rejected) wins
const firstResponse = await Promise.race([fetch(url), sleep(5000)]);
// First fulfilled wins, ignoring rejections
const firstOk = await Promise.any([fetch(a), fetch(b), fetch(c)]);Fetch API
const res = await fetch('https://api.example.com/users/42', {
method: 'POST',
headers: { 'Content-Type': 'application/json',
Authorization: `Bearer ${token}` },
body: JSON.stringify({ name: 'Ada' }),
});
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
const data = await res.json();
const text = await res.text();
const blob = await res.blob();DOM Manipulation (UI testing context)
Querying
document.getElementById('submit');
document.querySelector('[data-testid="submit"]');
document.querySelectorAll('.error'); // NodeList (array-like)
[...document.querySelectorAll('li')].map(li => li.textContent);Element properties
el.textContent = 'Hello';
el.innerHTML = '<strong>Hello</strong>';
el.value; // input/select/textarea
el.checked; // checkbox/radio
el.classList.add('active');
el.classList.remove('disabled');
el.classList.toggle('open');
el.classList.contains('open'); // true | falseEvents
el.addEventListener('click', e => {
e.preventDefault();
console.log('clicked', e.target);
});
const handler = () => {};
el.addEventListener('input', handler);
el.removeEventListener('input', handler);
el.dispatchEvent(new Event('input', { bubbles: true }));Attributes
el.getAttribute('data-id');
el.setAttribute('aria-label', 'Submit');
el.removeAttribute('disabled');
el.dataset.userId; // <div data-user-id="42"> → "42"Creating elements
const div = document.createElement('div');
div.className = 'banner';
div.textContent = 'New version available';
document.body.appendChild(div);Error Handling
try / catch / finally
try {
const data = await loadConfig();
return parse(data);
} catch (err) {
console.error('config load failed', err);
throw err; // rethrow
} finally {
cleanup();
}Throwing
throw new Error('something failed');
throw new TypeError('expected a string');
throw new RangeError('out of range');Custom error classes
class ApiError extends Error {
constructor(status, body) {
super(`HTTP ${status}`);
this.name = 'ApiError';
this.status = status;
this.body = body;
}
}
try {
await call();
} catch (err) {
if (err instanceof ApiError && err.status === 404) {
return null;
}
throw err;
}Built-in error types
Error — base type
TypeError — wrong type for an operation
ReferenceError — variable not defined
SyntaxError — parse error
RangeError — value out of range
URIError — bad encodeURI argument
Modules
ES Modules (modern)
// users.js
export const VERSION = '1.0.0';
export function loadUser(id) { /* ... */ }
export default class UserApi { /* ... */ }// app.js
import UserApi, { VERSION, loadUser } from './users.js';
import * as users from './users.js';
import { loadUser as load } from './users.js'; // rename
// Dynamic import (lazy)
const { default: UserApi } = await import('./users.js');CommonJS (Node, older)
// users.js
const VERSION = '1.0.0';
function loadUser(id) { /* ... */ }
module.exports = { VERSION, loadUser };
module.exports.UserApi = class { /* ... */ };// app.js
const { VERSION, loadUser } = require('./users.js');Useful Patterns for Testing
Deep clone
const clone = structuredClone(complexObj); // modern, handles Map/Set/Date
// Older fallback (loses Date/Map/undefined)
const clone2 = JSON.parse(JSON.stringify(obj));Debounce / throttle
function debounce(fn, ms) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), ms);
};
}
function throttle(fn, ms) {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= ms) {
last = now;
return fn(...args);
}
};
}Retry with exponential backoff
async function retry(fn, { attempts = 5, baseMs = 100 } = {}) {
let lastErr;
for (let i = 0; i < attempts; i++) {
try { return await fn(); }
catch (err) {
lastErr = err;
const delay = baseMs * 2 ** i;
await new Promise(r => setTimeout(r, delay));
}
}
throw lastErr;
}
const data = await retry(() => fetch(url).then(r => r.json()));Date handling
const now = new Date();
Date.now(); // ms since epoch
new Date('2024-06-15T10:00:00Z');
now.toISOString(); // '2024-06-15T10:00:00.000Z'
now.toJSON(); // same as toISOString
now.getTime(); // ms since epoch
now.getFullYear();
now.getMonth(); // 0-indexed: 0 = January
now.getDate(); // day-of-month 1–31
// Diff in seconds
const seconds = (Date.now() - then.getTime()) / 1000;JSON
JSON.parse('{"a":1}'); // { a: 1 }
JSON.stringify({ a: 1, b: 2 }); // '{"a":1,"b":2}'
JSON.stringify(obj, null, 2); // pretty-print
// Replacer
JSON.stringify(obj, (key, value) =>
key === 'password' ? undefined : value);Regular expressions
const emailRe = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
emailRe.test('a@b.co'); // true
'order-123-abc'.match(/order-(\d+)-(\w+)/);
// → ['order-123-abc', '123', 'abc', ...]
'foo BAR baz'.replace(/[A-Z]+/g, m => m.toLowerCase());
// → 'foo bar baz'
const re = /(\w+)=(\w+)/g;
for (const m of 'a=1&b=2'.matchAll(re)) {
console.log(m[1], m[2]);
}Set and Map
const seen = new Set();
seen.add('a'); seen.add('a'); seen.add('b');
seen.size; // 2
seen.has('a'); // true
[...new Set(['a', 'a', 'b'])]; // ['a', 'b']
const cache = new Map();
cache.set('user:42', { name: 'Ada' });
cache.get('user:42');
cache.has('user:42');
cache.delete('user:42');
cache.size;
for (const [k, v] of cache) { /* ... */ }