On this page10 sections
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't

const 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 to

Truthy / falsy

These six values are falsy; everything else is truthy:

false
0   ( -0,  0n )
""  ( '',  `` )
null
undefined
NaN
if (response.body) { /* runs only if non-empty */ }
 
// Convert anything to boolean
Boolean(value);  // or !!value

Strings & 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 1

push/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);                 // 1

Transforming

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 key

Destructuring

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 password

Optional 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 nullish

Object 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 scope

Default 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 args

Closures

function counter() {
  let n = 0;
  return () => ++n;
}
 
const next = counter();
next();   // 1
next();   // 2

IIFE

(() => {
  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 | false

Events

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) { /* ... */ }