Enjoy this full spectrum of essential web development topics (Data Structures, Strings, DOM, Events, Storage, OOP, Timers, and Async APIs).
The dummy database file.
{
"users":[
{ "id": 1, "name": "Alice", "role": "admin", "email": "alice@test.com" },
{ "id": 2, "name": "Bob", "role": "user", "email": "bob@test.com" },
{ "id": 3, "name": "Charlie", "role": "admin", "email": "charlie@test.com" }
],
"products":[
{ "id": 101, "name": "Laptop", "price": 999 },
{ "id": 102, "name": "Mouse", "price": 25 },
{ "id": 103, "name": "Monitor", "price": 200 }
],
"orders":[
{ "id": 1, "userId": 1, "total": 1024 },
{ "id": 2, "userId": 2, "total": 25 }
]
}The UI layout displaying all 50 questions.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ultimate Web Dev Assignment</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Ultimate Web Developer Assignment</h1>
<p class="rules"><strong>Rule:</strong> Implement everything from Array methods to DOM manipulation and Promises.</p>
<div id="score-board">Score: <span id="pass-count">0</span> / 50</div>
</header>
<main id="app-container">
<!-- Arrays & Objects -->
<div class="question-box" id="q1"><h3>1. Get Admins</h3><p>Write <code>getAdminNames(users)</code>. Return an array of names for users with the 'admin' role.</p></div>
<div class="question-box" id="q2"><h3>2. Cart Value</h3><p>Write <code>totalCartValue(items)</code>. Items have <code>price</code> and <code>qty</code>. Return the total value.</p></div>
<div class="question-box" id="q3"><h3>3. Group by Role</h3><p>Write <code>groupByRole(users)</code>. Return an object grouping users by their role.</p></div>
<div class="question-box" id="q4"><h3>4. Most Expensive</h3><p>Write <code>getMostExpensiveProduct(products)</code>. Return the product object with the highest price.</p></div>
<div class="question-box" id="q5"><h3>5. Flatten Array</h3><p>Write <code>flattenArray(arr)</code>. Deeply flatten a nested array.</p></div>
<div class="question-box" id="q6"><h3>6. Remove Duplicates</h3><p>Write <code>removeDuplicates(arr)</code>. Return a new array with no duplicate values.</p></div>
<div class="question-box" id="q7"><h3>7. Deep Merge</h3><p>Write <code>deepMerge(obj1, obj2)</code>. Return a new object that merges both recursively.</p></div>
<div class="question-box" id="q8"><h3>8. Deep Clone</h3><p>Write <code>deepClone(obj)</code>. Return a deep copy of the provided object without reference ties.</p></div>
<div class="question-box" id="q9"><h3>9. Find By Email</h3><p>Write <code>findUserByEmail(users, email)</code>. Return the user object matching the email.</p></div>
<div class="question-box" id="q10"><h3>10. Sort Products</h3><p>Write <code>sortProductsByPrice(products, order)</code>. Return products sorted by price. Order: 'asc' or 'desc'.</p></div>
<!-- Strings & Formatting -->
<div class="question-box" id="q11"><h3>11. Capitalize</h3><p>Write <code>capitalizeWords(str)</code>. Capitalize the first letter of every word.</p></div>
<div class="question-box" id="q12"><h3>12. Kebab Case</h3><p>Write <code>toKebabCase(str)</code>. Convert "Hello World" to "hello-world".</p></div>
<div class="question-box" id="q13"><h3>13. Validate Email</h3><p>Write <code>validateEmail(email)</code>. Return true if valid format, false otherwise.</p></div>
<div class="question-box" id="q14"><h3>14. Extract Mentions</h3><p>Write <code>extractMentions(text)</code>. Return an array of words starting with '@'.</p></div>
<div class="question-box" id="q15"><h3>15. Format Currency</h3><p>Write <code>formatCurrency(amt, currency)</code>. e.g. formatCurrency(1000, 'USD') -> $1,000.00.</p></div>
<div class="question-box" id="q16"><h3>16. Truncate Text</h3><p>Write <code>truncateText(text, length)</code>. If longer than length, slice and append '...'.</p></div>
<!-- DOM & Events -->
<div class="question-box" id="q17"><h3>17. Create Button</h3><p>Write <code>createButton(text)</code>. Return a new <code><button></code> element with the given text.</p></div>
<div class="question-box" id="q18"><h3>18. Toggle Theme</h3><p>Write <code>toggleTheme(element)</code>. Toggle the 'dark-theme' class on the element.</p></div>
<div class="question-box" id="q19"><h3>19. Count Children</h3><p>Write <code>countChildElements(element)</code>. Return the number of child elements.</p></div>
<div class="question-box" id="q20"><h3>20. Click Tracker</h3><p>Write <code>attachClickTracker(btn)</code>. On click, increment <code>btn.dataset.clicks</code>.</p></div>
<div class="question-box" id="q21"><h3>21. Create List</h3><p>Write <code>createList(items)</code>. Return a <code><ul></code> element containing <code><li></code>s for each string.</p></div>
<div class="question-box" id="q22"><h3>22. Form Data to JSON</h3><p>Write <code>getFormData(form)</code>. Parse a form element into a JS object.</p></div>
<div class="question-box" id="q23"><h3>23. Clear DOM</h3><p>Write <code>removeAllChildren(element)</code>. Safely remove all child nodes.</p></div>
<div class="question-box" id="q24"><h3>24. Event Delegation</h3><p>Write <code>addDelegatedListener(parent, selector, event, handler)</code>.</p></div>
<!-- Storage & Web APIs -->
<div class="question-box" id="q25"><h3>25. Save to Local</h3><p>Write <code>saveToLocal(key, value)</code>. Stringify and save data to localStorage.</p></div>
<div class="question-box" id="q26"><h3>26. Get from Local</h3><p>Write <code>getFromLocal(key)</code>. Retrieve and parse data from localStorage.</p></div>
<div class="question-box" id="q27"><h3>27. Parse Query Params</h3><p>Write <code>getQueryParams(url)</code>. Return an object of query string parameters.</p></div>
<div class="question-box" id="q28"><h3>28. Set Cookie</h3><p>Write <code>setCookie(name, value, days)</code>. Set a cookie expiring in X days.</p></div>
<div class="question-box" id="q29"><h3>29. Delete Cookie</h3><p>Write <code>deleteCookie(name)</code>. Delete an existing cookie.</p></div>
<!-- Functions, Closures & OOP -->
<div class="question-box" id="q30"><h3>30. Counter Closure</h3><p>Write <code>createCounter(init)</code>. Return obj with <code>inc()</code>, <code>dec()</code>, <code>get()</code> methods.</p></div>
<div class="question-box" id="q31"><h3>31. Curry Addition</h3><p>Write <code>curryAdd(a)</code>. Return a function taking b that returns a + b.</p></div>
<div class="question-box" id="q32"><h3>32. Memoize</h3><p>Write <code>memoize(fn)</code>. Cache the result of fn based on its argument.</p></div>
<div class="question-box" id="q33"><h3>33. Compose</h3><p>Write <code>compose(f, g)</code>. Return a function executing g(x), then passing result to f(x).</p></div>
<div class="question-box" id="q34"><h3>34. Run Once</h3><p>Write <code>once(fn)</code>. Return a function that runs at most once, caching the output.</p></div>
<div class="question-box" id="q35"><h3>35. User Class</h3><p>Write ES6 <code>UserClass(name, age)</code> with a <code>greet()</code> method returning 'Hi, I am [name]'.</p></div>
<!-- Timers -->
<div class="question-box" id="q36"><h3>36. Delay Callback</h3><p>Write <code>delayCallback(cb, ms)</code>. Run the callback after ms.</p></div>
<div class="question-box" id="q37"><h3>37. Interval Call</h3><p>Write <code>intervalCounter(cb, ms, times)</code>. Call cb repeatedly up to 'times' times.</p></div>
<div class="question-box" id="q38"><h3>38. Debounce</h3><p>Write <code>debounce(fn, ms)</code>. Delay execution until ms after last call.</p></div>
<div class="question-box" id="q39"><h3>39. Throttle</h3><p>Write <code>throttle(fn, ms)</code>. Ensure fn is called at most once every ms.</p></div>
<!-- Promises & Fetch -->
<div class="question-box" id="q40"><h3>40. Promisify</h3><p>Write <code>promisify(fn)</code>. Convert callback API (data, cb(err, res)) into Promise.</p></div>
<div class="question-box" id="q41"><h3>41. Sleep</h3><p>Write <code>sleep(ms)</code>. Return a promise that resolves after ms.</p></div>
<div class="question-box" id="q42"><h3>42. Fetch JSON</h3><p>Write <code>fetchJSON(url)</code>. Fetch and return parsed JSON.</p></div>
<div class="question-box" id="q43"><h3>43. Fetch Retry</h3><p>Write <code>fetchWithRetry(url, retries)</code>. Retry fetch if failure occurs.</p></div>
<div class="question-box" id="q44"><h3>44. Fetch Any</h3><p>Write <code>fetchAny(urls)</code>. Return the first successful fetch result using Promise.any.</p></div>
<div class="question-box" id="q45"><h3>45. Fetch All</h3><p>Write <code>fetchAll(urls)</code>. Return array of all successful fetch results concurrently.</p></div>
<div class="question-box" id="q46"><h3>46. Fetch Sequentially</h3><p>Write <code>fetchSequentially(urls)</code>. Await each url strictly one by one.</p></div>
<div class="question-box" id="q47"><h3>47. Timeout Promise</h3><p>Write <code>timeoutPromise(promise, ms)</code>. Reject if promise takes longer than ms.</p></div>
<div class="question-box" id="q48"><h3>48. Fetch with Auth</h3><p>Write <code>fetchWithAuth(url, token)</code>. Fetch passing Authorization Bearer header.</p></div>
<div class="question-box" id="q49"><h3>49. Poll Endpoint</h3><p>Write <code>pollForCondition(url, conditionFn, ms)</code>. Poll until conditionFn(data) is true.</p></div>
<div class="question-box" id="q50"><h3>50. Batch Fetch</h3><p>Write <code>batchFetch(urls, batchSize)</code>. Fetch in chunks to avoid overloading network.</p></div>
</main>
<script src="solution.js"></script>
<script src="tester.js"></script>
</body>
</html>The workspace file for the student.
// --- 1. Arrays & Objects ---
function getAdminNames(users) {}
function totalCartValue(items) {}
function groupByRole(users) {}
function getMostExpensiveProduct(products) {}
function flattenArray(arr) {}
function removeDuplicates(arr) {}
function deepMerge(obj1, obj2) {}
function deepClone(obj) {}
function findUserByEmail(users, email) {}
function sortProductsByPrice(products, order) {}
// --- 2. Strings & Formatting ---
function capitalizeWords(str) {}
function toKebabCase(str) {}
function validateEmail(email) {}
function extractMentions(text) {}
function formatCurrency(amt, currency) {}
function truncateText(text, length) {}
// --- 3. DOM & Events ---
function createButton(text) {}
function toggleTheme(element) {}
function countChildElements(element) {}
function attachClickTracker(btn) {}
function createList(items) {}
function getFormData(form) {}
function removeAllChildren(element) {}
function addDelegatedListener(parent, selector, event, handler) {}
// --- 4. Storage & Web APIs ---
function saveToLocal(key, value) {}
function getFromLocal(key) {}
function getQueryParams(url) {}
function setCookie(name, value, days) {}
function deleteCookie(name) {}
// --- 5. Functions, Closures & OOP ---
function createCounter(init) {}
function curryAdd(a) {}
function memoize(fn) {}
function compose(f, g) {}
function once(fn) {}
class UserClass {}
// --- 6. Timers ---
function delayCallback(cb, ms) {}
function intervalCounter(cb, ms, times) {}
function debounce(fn, ms) {}
function throttle(fn, ms) {}
// --- 7. Promises & Fetch ---
function promisify(fn) {}
async function sleep(ms) {}
async function fetchJSON(url) {}
async function fetchWithRetry(url, retries) {}
async function fetchAny(urls) {}
async function fetchAll(urls) {}
async function fetchSequentially(urls) {}
async function timeoutPromise(promise, ms) {}
async function fetchWithAuth(url, token) {}
async function pollForCondition(url, conditionFn, ms) {}
async function batchFetch(urls, batchSize) {}The robust testing engine. Designed with careful timeouts, deep equality checks, mocked networks, and detached DOM logic to reliably validate right answers without flaking.
window.addEventListener('load', async () => {
let passed = 0;
const total = 50;
const updateScore = () => document.getElementById('pass-count').innerText = passed;
// --- MOCK SERVER ENGINE ---
const originalFetch = window.fetch;
let retryCounter = 0;
let pollCounter = 0;
window.fetch = async (url, options = {}) => {
const _delay = ms => new Promise(r => setTimeout(r, ms));
await _delay(5); // Simulate fast network
const ok = data => ({ ok: true, status: 200, json: async () => data });
const err = (status, msg) => ({ ok: false, status, json: async () => ({ error: msg }) });
if (url === '/api/flaky') {
retryCounter++;
if (retryCounter < 3) throw new TypeError('Network Error');
return ok({ success: true });
}
if (url === '/api/poll') {
pollCounter++;
return pollCounter >= 3 ? ok({ status: 'ready' }) : ok({ status: 'pending' });
}
if (url === '/api/auth' && options.headers?.Authorization === 'Bearer xyz') {
return ok({ authorized: true });
}
if (url.includes('/api/batch')) {
await _delay(20);
return ok({ url });
}
if (url.includes('slow')) {
await _delay(100);
return ok({ done: true });
}
if (url.includes('fast')) {
return ok({ done: true });
}
return ok({ path: url });
};
// --- UTILS ---
const isEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
const delay = ms => new Promise(r => setTimeout(r, ms));
// --- MOCK DATA ---
const mockUsers =[
{ id: 1, name: "Alice", role: "admin", email: "alice@test.com" },
{ id: 2, name: "Bob", role: "user", email: "bob@test.com" }
];
const mockProducts =[
{ id: 101, name: "Laptop", price: 1000 },
{ id: 102, name: "Mouse", price: 25 }
];
// --- TEST SUITE ---
const tests =[
// Arrays & Objects
{ id: 'q1', run: () => isEqual(getAdminNames(mockUsers), ["Alice"]) },
{ id: 'q2', run: () => totalCartValue([{price: 10, qty: 2}, {price: 5, qty: 1}]) === 25 },
{ id: 'q3', run: () => {
const res = groupByRole(mockUsers);
return res.admin.length === 1 && res.user.length === 1 && res.admin[0].name === 'Alice';
}},
{ id: 'q4', run: () => getMostExpensiveProduct(mockProducts).id === 101 },
{ id: 'q5', run: () => isEqual(flattenArray([1, [2,[3, 4]]]), [1, 2, 3, 4]) },
{ id: 'q6', run: () => isEqual(removeDuplicates([1, 2, 2, 3]),[1, 2, 3]) },
{ id: 'q7', run: () => isEqual(deepMerge({a: 1}, {b: 2}), {a: 1, b: 2}) },
{ id: 'q8', run: () => {
const obj = { a: { b: 1 } };
const cloned = deepClone(obj);
cloned.a.b = 2;
return obj.a.b === 1;
}},
{ id: 'q9', run: () => findUserByEmail(mockUsers, "bob@test.com").name === "Bob" },
{ id: 'q10', run: () => sortProductsByPrice(mockProducts, "desc")[0].id === 101 },
// Strings
{ id: 'q11', run: () => capitalizeWords("hello world") === "Hello World" },
{ id: 'q12', run: () => toKebabCase("Hello World") === "hello-world" },
{ id: 'q13', run: () => validateEmail("a@b.com") === true && validateEmail("ab.com") === false },
{ id: 'q14', run: () => isEqual(extractMentions("Hey @alice and @bob"), ["@alice", "@bob"]) },
{ id: 'q15', run: () => {
const res = formatCurrency(1000, "USD");
return res.includes("1,000") && res.includes("$");
}},
{ id: 'q16', run: () => truncateText("Hello World", 5) === "Hello..." },
// DOM
{ id: 'q17', run: () => {
const btn = createButton("Click");
return btn.tagName === 'BUTTON' && btn.textContent === "Click";
}},
{ id: 'q18', run: () => {
const div = document.createElement("div");
toggleTheme(div);
return div.classList.contains('dark-theme');
}},
{ id: 'q19', run: () => {
const div = document.createElement("div");
div.innerHTML = "<p></p><span></span>";
return countChildElements(div) === 2;
}},
{ id: 'q20', run: () => {
const btn = document.createElement("button");
attachClickTracker(btn);
btn.click(); btn.click();
return btn.dataset.clicks === "2";
}},
{ id: 'q21', run: () => {
const ul = createList(["A", "B"]);
return ul.tagName === 'UL' && ul.children.length === 2 && ul.children[0].textContent === "A";
}},
{ id: 'q22', run: () => {
const form = document.createElement("form");
form.innerHTML = '<input name="user" value="alice">';
return getFormData(form).user === "alice";
}},
{ id: 'q23', run: () => {
const div = document.createElement("div");
div.innerHTML = "<p>Text</p>";
removeAllChildren(div);
return div.children.length === 0 && div.innerHTML === "";
}},
{ id: 'q24', run: () => {
let hit = false;
const parent = document.createElement("div");
parent.innerHTML = '<span class="target">A</span><span class="other">B</span>';
addDelegatedListener(parent, '.target', 'click', () => hit = true);
parent.querySelector('.other').click();
if (hit) return false;
parent.querySelector('.target').click();
return hit === true;
}},
// Storage & APIs
{ id: 'q25', run: () => { saveToLocal('test', {x: 1}); return localStorage.getItem('test') === '{"x":1}'; }},
{ id: 'q26', run: () => { localStorage.setItem('test2', '{"y":2}'); return getFromLocal('test2').y === 2; }},
{ id: 'q27', run: () => getQueryParams("http://a.com?x=1&y=2").x === "1" },
{ id: 'q28', run: () => { setCookie('test_cookie', '123', 1); return document.cookie.includes('test_cookie=123'); }},
{ id: 'q29', run: () => { deleteCookie('test_cookie'); return !document.cookie.includes('test_cookie=123'); }},
// Closures & OOP
{ id: 'q30', run: () => {
const c = createCounter(5);
c.inc(); c.inc(); c.dec();
return c.get() === 6;
}},
{ id: 'q31', run: () => curryAdd(5)(10) === 15 },
{ id: 'q32', run: () => {
let calls = 0;
const memoed = memoize(x => { calls++; return x * 2; });
memoed(5); memoed(5);
return calls === 1 && memoed(5) === 10;
}},
{ id: 'q33', run: () => compose(x => x * 2, x => x + 1)(5) === 12 },
{ id: 'q34', run: () => {
let x = 0;
const runOnce = once(() => ++x);
runOnce(); runOnce();
return x === 1;
}},
{ id: 'q35', run: () => new UserClass("Alice", 25).greet() === "Hi, I am Alice" },
// Timers
{ id: 'q36', run: async () => { let x=0; delayCallback(()=>x=1, 20); await delay(40); return x===1; }},
{ id: 'q37', run: async () => { let x=0; intervalCounter(()=>x++, 20, 3); await delay(100); return x===3; }},
{ id: 'q38', run: async () => {
let x=0; const fn = debounce(()=>x++, 20);
fn(); fn(); fn(); await delay(40); return x===1;
}},
{ id: 'q39', run: async () => {
let x=0; const fn = throttle(()=>x++, 30);
fn(); fn(); await delay(20); fn(); await delay(40); fn();
return x===2;
}},
// Promises
{ id: 'q40', run: async () => {
const dummyApi = (val, cb) => setTimeout(() => cb(null, val * 2), 10);
const pApi = promisify(dummyApi);
return (await pApi(5)) === 10;
}},
{ id: 'q41', run: async () => {
const start = Date.now();
await sleep(30);
return (Date.now() - start) >= 25; // Tolerance
}},
{ id: 'q42', run: async () => (await fetchJSON('/api/test')).path === '/api/test' },
{ id: 'q43', run: async () => { retryCounter = 0; return (await fetchWithRetry('/api/flaky', 5)).success === true; }},
{ id: 'q44', run: async () => (await fetchAny(['/api/slow', '/api/fast'])).done === true },
{ id: 'q45', run: async () => (await fetchAll(['/api/fast', '/api/fast'])).length === 2 },
{ id: 'q46', run: async () => {
let timeline =[];
const _fetchSequentially = async () => {
await fetchSequentially(['/api/slow', '/api/fast']);
timeline.push('done');
}
_fetchSequentially();
await delay(50);
if(timeline.length > 0) return false; // Should still be waiting on slow
await delay(100);
return timeline.length === 1;
}},
{ id: 'q47', run: async () => {
try { await timeoutPromise(sleep(100), 20); return false; }
catch(e) { return true; }
}},
{ id: 'q48', run: async () => (await fetchWithAuth('/api/auth', 'xyz')).authorized === true },
{ id: 'q49', run: async () => {
pollCounter = 0;
const res = await pollForCondition('/api/poll', data => data.status === 'ready', 10);
return res.status === 'ready';
}},
{ id: 'q50', run: async () => {
const urls =['/api/batch1', '/api/batch2', '/api/batch3'];
const res = await batchFetch(urls, 2);
return res.length === 3 && res[2].url === '/api/batch3';
}}
];
const runAsyncTest = async (id, testFn) => {
const box = document.getElementById(id);
try {
const success = await testFn();
if (success) { box.classList.add('pass'); passed++; }
else { box.classList.add('fail'); console.warn(`Test Failed: ${id}`); }
} catch (e) {
box.classList.add('fail');
console.error(`Test Error in ${id}:`, e);
}
updateScore();
};
// Run sequentially to prevent shared state interference (especially in DOM/Storage)
for (const test of tests) {
await runAsyncTest(test.id, test.run);
}
});The responsive UI stylesheet that elegantly arranges 50 challenges.
:root {
--bg: #f8fafc;
--card-bg: #ffffff;
--text: #0f172a;
--primary: #2563eb;
--success: #16a34a;
--error: #e11d48;
}
body {
font-family: 'Inter', -apple-system, sans-serif;
background: var(--bg);
color: var(--text);
padding: 20px;
margin: 0;
}
header { text-align: center; margin-bottom: 40px; }
h1 { color: var(--primary); margin-bottom: 8px; font-weight: 800; font-size: 2.5rem;}
.rules { color: #475569; font-size: 1.1rem; }
#score-board {
background: #0f172a;
color: white;
padding: 12px 28px;
border-radius: 30px;
display: inline-block;
font-size: 1.4rem;
font-weight: bold;
margin-top: 15px;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
#app-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
max-width: 1600px;
margin: 0 auto;
}
.question-box {
background: var(--card-bg);
padding: 20px;
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
border-left: 6px solid #cbd5e1;
transition: transform 0.2s, border-color 0.3s;
display: flex;
flex-direction: column;
}
.question-box:hover {
transform: translateY(-3px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
.question-box h3 {
margin-top: 0;
font-size: 1.15rem;
color: #1e293b;
margin-bottom: 10px;
}
.question-box p {
font-size: 0.95rem;
line-height: 1.5;
color: #475569;
margin-bottom: 0;
}
.question-box code {
background: #f1f5f9;
color: #be185d;
padding: 3px 6px;
border-radius: 4px;
font-size: 0.85rem;
}
/* Dynamic Test States */
.pass { border-left-color: var(--success); }
.fail { border-left-color: var(--error); background-color: #fff1f2; }