JavaScript Function Practice Programs with Solutions (32 Programs)

Practise JavaScript functions with 32 programs, each with a full solution, the exact output and a short explanation. The programs start with the basics, move to intermediate problems and finish with real-world scenarios from test automation, such as test data, prices, form inputs and test results. They are written for testers learning JavaScript for Playwright.

Advertisement

How to practise: read the task, write your own solution first, then open Show solution to compare. Run any program with node program.js.

New to this topic? Read the JavaScript Functions lesson first.

JavaScript Beginner Programs

1. Function that greets a userBeginner

Write a function greet(name) that returns Hello, <name>! and print it for two names.

Show solution
function greet(name) {
  return `Hello, ${name}!`;
}
console.log(greet('Asha'));
console.log(greet('Ravi'));

Output

Hello, Asha!
Hello, Ravi!

Explanation: a function declaration takes parameters and uses return to send a value back to the caller.

2. Add two numbersBeginner

Write add(a, b) and print the sum of 12 and 30.

Show solution
function add(a, b) {
  return a + b;
}
const result = add(12, 30);
console.log('Result:', result);

Output

Result: 42

Explanation: the returned value can be stored in a variable or used directly.

3. Arrow functionBeginner

Rewrite a square function as an arrow function and print the squares of 4 and 9.

Show solution
const square = n => n * n;
console.log(square(4));
console.log(square(9));

Output

16
81

Explanation: arrow functions with one expression return it automatically, without return or braces.

4. Default parametersBeginner

Write openPage(url, timeout = 30000) that prints the URL and timeout, and call it with and without a timeout.

Show solution
function openPage(url, timeout = 30000) {
  console.log(`Opening ${url} (timeout ${timeout} ms)`);
}
openPage('/login');
openPage('/reports', 60000);

Output

Opening /login (timeout 30000 ms)
Opening /reports (timeout 60000 ms)

Explanation: default parameters are used when the argument is missing or undefined.

5. Function without returnBeginner

Show what a function without a return statement gives back.

Show solution
function logStep(step) {
  console.log('Step:', step);
}
const value = logStep('Click login');
console.log('Returned:', value);

Output

Step: Click login
Returned: undefined

Explanation: a function with no return returns undefined.

6. Even checkerBeginner

Write isEven(n) that returns true or false, and test it with 4 and 7.

Show solution
const isEven = n => n % 2 === 0;
console.log(isEven(4));
console.log(isEven(7));

Output

true
false

Explanation: functions that return booleans read naturally in conditions, for example if (isEven(x)).

7. Celsius to FahrenheitBeginner

Convert 0, 37 and 100 °C to Fahrenheit with a function.

Show solution
function toFahrenheit(celsius) {
  return (celsius * 9) / 5 + 32;
}
for (const c of [0, 37, 100]) console.log(`${c}°C = ${toFahrenheit(c)}°F`);

Output

0°C = 32°F
37°C = 98.6°F
100°C = 212°F

Explanation: wrapping a formula in a function makes it reusable and easy to test.

8. Function expressionBeginner

Store an anonymous function in a constant and call it.

Show solution
const multiply = function (a, b) {
  return a * b;
};
console.log(multiply(6, 7));
console.log(typeof multiply);

Output

42
function

Explanation: functions are values in JavaScript, so they can be stored in variables.

9. Rest parametersBeginner

Write sum(...numbers) that adds any number of arguments.

Show solution
function sum(...numbers) {
  let total = 0;
  for (const n of numbers) total += n;
  return total;
}
console.log(sum(1, 2));
console.log(sum(5, 10, 15, 20));

Output

3
50

Explanation: ...numbers collects all arguments into a real array.

10. Return multiple valuesBeginner

Return the min and max of a list as an object and destructure the result.

Show solution
function minMax(values) {
  return { min: Math.min(...values), max: Math.max(...values) };
}
const { min, max } = minMax([42, 7, 19, 88]);
console.log('min:', min, 'max:', max);

Output

min: 7 max: 88

Explanation: return an object or array when a function produces more than one value.

JavaScript Intermediate Programs

11. Hoisting of function declarationsIntermediate

Call a function declaration before it is defined, and compare with a function expression.

Show solution
console.log(double(5));
function double(n) { return n * 2; }
try {
  triple(5);
} catch (error) {
  console.log(error.name);
}
const triple = n => n * 3;

Output

10
ReferenceError

Explanation: function declarations are hoisted with their body. A const function is in the temporal dead zone until its line runs.

12. Callback functionIntermediate

Write runTest(name, callback) that prints the name and calls the callback with a result.

Show solution
function runTest(name, callback) {
  console.log('Running', name);
  callback('passed');
}
runTest('login', result => console.log('Result:', result));

Output

Running login
Result: passed

Explanation: a callback is a function passed to another function to be called later.

13. Higher-order function that returns a functionIntermediate

Write makeMultiplier(factor) that returns a function, and create double and triple.

Show solution
function makeMultiplier(factor) {
  return n => n * factor;
}
const double = makeMultiplier(2);
const triple = makeMultiplier(3);
console.log(double(10), triple(10));

Output

20 30

Explanation: functions can return functions. The returned function remembers factor through a closure.

14. Closure counterIntermediate

Create a counter function that keeps its own private count.

Show solution
function createCounter() {
  let count = 0;
  return () => ++count;
}
const next = createCounter();
console.log(next(), next(), next());
const other = createCounter();
console.log(other());

Output

1 2 3
1

Explanation: each call to createCounter creates a new count that only its returned function can access.

15. Recursion: factorialIntermediate

Calculate 6! with a recursive function.

Show solution
function factorial(n) {
  if (n <= 1) return 1;
  return n * factorial(n - 1);
}
console.log(factorial(6));

Output

720

Explanation: a recursive function calls itself with a smaller input until it reaches a base case.

16. Recursion: sum nested arraysIntermediate

Sum all numbers in [1, [2, 3], [4, [5]]] with recursion.

Show solution
function deepSum(items) {
  let total = 0;
  for (const item of items) {
    total += Array.isArray(item) ? deepSum(item) : item;
  }
  return total;
}
console.log(deepSum([1, [2, 3], [4, [5]]]));

Output

15

Explanation: recursion handles data whose nesting depth is not known in advance.

17. Pure vs impure functionsIntermediate

Compare a pure function with one that changes outside state.

Show solution
let calls = 0;
const impureAdd = (a, b) => { calls++; return a + b; };
const pureAdd = (a, b) => a + b;
console.log(pureAdd(2, 3), pureAdd(2, 3));
console.log(impureAdd(2, 3), impureAdd(2, 3), 'calls:', calls);

Output

5 5
5 5 calls: 2

Explanation: a pure function always returns the same output for the same input and changes nothing outside itself, which makes it easy to test.

18. Immediately invoked functionIntermediate

Use an IIFE to compute a value without leaking variables.

Show solution
const config = (() => {
  const env = 'qa';
  const port = 8080;
  return `${env}:${port}`;
})();
console.log(config);
console.log(typeof env);

Output

qa:8080
undefined

Explanation: an IIFE runs immediately and keeps its variables private.

19. Memoize a slow functionIntermediate

Cache results of a function so repeated calls are not recalculated.

Show solution
function memoize(fn) {
  const cache = new Map();
  return n => {
    if (cache.has(n)) return `${cache.get(n)} (cached)`;
    const value = fn(n);
    cache.set(n, value);
    return value;
  };
}
const slowSquare = n => n * n;
const fastSquare = memoize(slowSquare);
console.log(fastSquare(12));
console.log(fastSquare(12));

Output

144
144 (cached)

Explanation: a closure keeps the cache between calls. Memoization trades memory for speed.

20. Arguments with destructured optionsIntermediate

Write createUser({ name, role = 'viewer', active = true }) and call it with partial options.

Show solution
function createUser({ name, role = 'viewer', active = true }) {
  return `${name} | ${role} | ${active ? 'active' : 'inactive'}`;
}
console.log(createUser({ name: 'Asha' }));
console.log(createUser({ name: 'Ravi', role: 'admin', active: false }));

Output

Asha | viewer | active
Ravi | admin | inactive

Explanation: an options object with destructuring and defaults keeps calls readable when a function has many settings.

JavaScript Real-World QA Scenario Programs

21. Reusable assertion helperReal-world

Write assertEqual(actual, expected, message) that prints PASS or FAIL.

Show solution
function assertEqual(actual, expected, message) {
  const ok = actual === expected;
  console.log(`${ok ? 'PASS' : 'FAIL'}: ${message}${ok ? '' : ` (expected ${expected}, got ${actual})`}`);
  return ok;
}
assertEqual(2 + 2, 4, 'adds numbers');
assertEqual('Login'.toLowerCase(), 'login', 'lower-cases text');
assertEqual(10 / 4, 2, 'divides numbers');

Output

PASS: adds numbers
PASS: lower-cases text
FAIL: divides numbers (expected 2, got 2.5)

Explanation: small helpers make checks consistent. Real tests use Playwright’s expect, which works the same way.

22. Build a URL helperReal-world

Write buildUrl(base, path, params) that joins a base URL, a path and query parameters.

Show solution
function buildUrl(base, path, params = {}) {
  const url = new URL(path, base);
  for (const [key, value] of Object.entries(params)) url.searchParams.set(key, value);
  return url.toString();
}
console.log(buildUrl('https://shop.test', '/search', { q: 'laptop', page: 2 }));
console.log(buildUrl('https://shop.test', '/cart'));

Output

https://shop.test/search?q=laptop&page=2
https://shop.test/cart

Explanation: a helper avoids hand-building URLs, and URL takes care of encoding query values.

23. Generate unique test emailsReal-world

Write uniqueEmail(prefix, id) that returns a unique email address.

Show solution
const uniqueEmail = (prefix, id) => `${prefix}.${id}@example.com`;
for (let i = 1; i <= 3; i++) console.log(uniqueEmail('qa.signup', 1000 + i));

Output

qa.signup.1001@example.com
qa.signup.1002@example.com
qa.signup.1003@example.com

Explanation: a tiny pure function keeps test-data rules in one place.

24. Retry wrapperReal-world

Write retry(fn, attempts) that calls a function until it succeeds or attempts run out.

Show solution
function retry(fn, attempts) {
  for (let i = 1; i <= attempts; i++) {
    try {
      return fn(i);
    } catch (error) {
      console.log(`Attempt ${i} failed: ${error.message}`);
    }
  }
  throw new Error('All attempts failed');
}
const flaky = attempt => {
  if (attempt < 3) throw new Error('element not ready');
  return 'clicked';
};
console.log(retry(flaky, 5));

Output

Attempt 1 failed: element not ready
Attempt 2 failed: element not ready
clicked

Explanation: wrapping retry logic in a function means every flaky step can reuse the same behaviour.

25. Format test durationReal-world

Write formatDuration(ms) that returns values like 850 ms, 2.5 s or 1m 5s.

Show solution
function formatDuration(ms) {
  if (ms < 1000) return `${ms} ms`;
  if (ms < 60000) return `${(ms / 1000).toFixed(1)} s`;
  const minutes = Math.floor(ms / 60000);
  const seconds = Math.round((ms % 60000) / 1000);
  return `${minutes}m ${seconds}s`;
}
for (const ms of [850, 2500, 65000]) console.log(formatDuration(ms));

Output

850 ms
2.5 s
1m 5s

Explanation: one formatting function keeps reports consistent.

26. Validate form inputReal-world

Write validateSignup(data) that returns a list of error messages.

Show solution
function validateSignup({ email = '', password = '', age }) {
  const errors = [];
  if (!email.includes('@')) errors.push('Email is invalid');
  if (password.length < 8) errors.push('Password is too short');
  if (!(age >= 18)) errors.push('Must be 18 or older');
  return errors;
}
const show = errors => console.log(errors.length ? errors.join('; ') : 'No errors');
show(validateSignup({ email: 'qa@test.com', password: 'Secret123', age: 25 }));
show(validateSignup({ email: 'qa', password: '123', age: 16 }));

Output

No errors
Email is invalid; Password is too short; Must be 18 or older

Explanation: returning all errors at once lets a test assert every validation message on the form.

27. Debounce a searchReal-world

Write a debounce function so that only the last of several quick calls runs.

Show solution
function debounce(fn, delay) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
}
const search = debounce(term => console.log('Searching for:', term), 30);
search('p');
search('pl');
search('play');
await new Promise(resolve => setTimeout(resolve, 60));

Output

Searching for: play

Explanation: debouncing waits until calls stop, which is how search boxes avoid one request per keystroke.

28. Compose small functionsReal-world

Combine trim, lowercase and slugify steps into one pipeline function.

Show solution
const pipe = (...fns) => value => fns.reduce((acc, fn) => fn(acc), value);
const trim = s => s.trim();
const lower = s => s.toLowerCase();
const slugify = s => s.replace(/\s+/g, '-');
const toSlug = pipe(trim, lower, slugify);
console.log(toSlug('  Playwright Mock Test  '));

Output

playwright-mock-test

Explanation: composing small single-purpose functions builds bigger behaviour that is easy to test piece by piece.

29. Price calculator with optionsReal-world

Write calculateTotal(items, { taxRate, coupon }) and test two carts.

Show solution
function calculateTotal(items, { taxRate = 0.18, coupon = 0 } = {}) {
  const subtotal = items.reduce((sum, item) => sum + item.price * item.qty, 0);
  const discounted = Math.max(subtotal - coupon, 0);
  return Math.round(discounted * (1 + taxRate));
}
const cart = [{ price: 500, qty: 2 }, { price: 250, qty: 1 }];
console.log(calculateTotal(cart));
console.log(calculateTotal(cart, { coupon: 250, taxRate: 0.05 }));

Output

1475
1050

Explanation: an options object with defaults keeps the function easy to call for common cases.

30. Callback for each test resultReal-world

Write runSuite(tests, onResult) that runs simple checks and calls onResult for each.

Show solution
function runSuite(tests, onResult) {
  for (const test of tests) {
    const passed = test.check();
    onResult(test.name, passed);
  }
}
const tests = [
  { name: 'title is set', check: () => 'Shop'.length > 0 },
  { name: 'cart is empty', check: () => [1].length === 0 }
];
runSuite(tests, (name, passed) => console.log(`${passed ? '✔' : '✘'} ${name}`));

Output

✔ title is set
✘ cart is empty

Explanation: passing a callback lets the caller decide how results are reported, such as in the console, a file or a dashboard.

31. Run a function only onceReal-world

Write a once function that lets a warning print only the first time.

Show solution
function once(fn) {
  let called = false;
  return (...args) => {
    if (called) return;
    called = true;
    fn(...args);
  };
}
const warnDeprecated = once(() => console.log('Warning: page.$() is discouraged, use locators'));
warnDeprecated();
warnDeprecated();
warnDeprecated();

Output

Warning: page.$() is discouraged, use locators

Explanation: a closure flag prevents the same message from flooding the logs.

32. Page-object style functionReal-world

Write a factory createLoginPage() that returns an object with methods that record actions.

Show solution
function createLoginPage() {
  const actions = [];
  return {
    fill(field, value) { actions.push(`fill ${field}=${field === 'password' ? '***' : value}`); return this; },
    submit() { actions.push('click submit'); return this; },
    history() { return actions.join(' -> '); }
  };
}
const page = createLoginPage().fill('email', 'qa@test.com').fill('password', 'secret').submit();
console.log(page.history());

Output

fill email=qa@test.com -> fill password=*** -> click submit

Explanation: returning this from each method allows chaining. This is the same idea as a page object with readable steps.

Keep Practising

Leave a Comment