# Free Salesforce JavaScript Developer Practice Questions

*Last updated September 2026 · https://www.certifyforce.com/salesforce-javascript-developer-1-practice-exam/free-questions*

18 free JavaScript Developer practice questions from the CertifyForce bank of 300 reviewed questions, spread across the 7 official exam sections in proportion to their weight. Each has the correct answer, an explanation, and the official documentation it was verified against.

## Variables, Types, and Collections (23%)

### Question 1

Ursa Major Solar receives panel quantities as strings, and a developer converts them with a one-line `map()` call. Refer to the code below:
```javascript
const quantities = ['10', '10', '10'].map(parseInt);
console.log(quantities);
```
What is logged to the console?

- A. `[ 10, 10, 10 ]`
- B. `[ 10, NaN, 2 ]`
- C. `[ 10, NaN, NaN ]`
- D. `[ '10', '10', '10' ]`

**Correct answer:** B. `[ 10, NaN, 2 ]`

**Explanation:** `map()` calls its callback with three arguments (the element, its index, and the array), so each call is really `parseInt('10', index)`. Index 0 means the radix is treated as unspecified, giving `10`; radix 1 is invalid, giving `NaN`; and radix 2 parses `'10'` as binary, giving `2`. `[ 10, 10, 10 ]` is what `map(Number)` or `map(s => parseInt(s, 10))` would produce. The all-string result assumes `map()` returns the original values, and `[ 10, NaN, NaN ]` misses that `'10'` is a valid binary number.

**Sources:** [Array - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array), [Number - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)

### Question 2

Users of a Cumulus Financial app in California report that due dates display one day early. The due date arrives from the API as a date-only string.
Refer to the code below:
```javascript
const dueDate = new Date('2025-07-04');
console.log(dueDate.getDate());
```
The code logs `3` for these users. What is the cause?

- A. Date-only ISO 8601 strings are parsed as UTC midnight, and `getDate()` returns the day in the user's local time zone, which is still July 3 in California.
- B. `getDate()` returns a zero-based day of the month, so the value 3 represents the fourth day.
- C. Date-only strings are parsed in local time, but `getDate()` always returns the day in UTC.
- D. `getDate()` returns the day of the week rather than the day of the month.

**Correct answer:** A. Date-only ISO 8601 strings are parsed as UTC midnight, and `getDate()` returns the day in the user's local time zone, which is still July 3 in California.

**Explanation:** Under the ECMAScript date time string format, a date-only string such as `'2025-07-04'` is interpreted as UTC, while a date-time string without an offset is interpreted as local time. UTC midnight on July 4 is 5 PM on July 3 in California, and `getDate()` reports the local day, so it returns 3. `getDate()` is one-based (1-31), so the zero-based explanation is wrong, and it returns the day of the month; the day of the week comes from `getDay()`. The explanation that parsing is local and the getter is UTC reverses the actual behavior. Constructing the date with `new Date(2025, 6, 4)` or `'2025-07-04T00:00:00'` avoids the shift.

**Sources:** [Date - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date)

### Question 3

A Universal Containers developer serializes a case payload before sending it to an external system. Refer to the code below:
```javascript
const payload = {
    id: 42,
    note: undefined,
    closedAt: null,
    format() {
        return 'x';
    },
    tags: ['vip', undefined],
    created: new Date(0)
};
const json = JSON.stringify(payload);
```
Which three statements about the value of `json` are true? Choose 3 answers.

- A. `tags` is serialized as `["vip"]`.
- B. The `note` property does not appear in the string.
- C. `created` is serialized as the number `0`.
- D. `closedAt` is serialized as `null`.
- E. The `format` property does not appear in the string.

**Correct answers:** B. The `note` property does not appear in the string.; D. `closedAt` is serialized as `null`.; E. The `format` property does not appear in the string.

**Explanation:** `JSON.stringify()` omits object properties whose values are `undefined` or functions, so neither `note` nor `format` appears in the output. `null` is a valid JSON value, so `closedAt` is kept as `null`. Inside arrays, `undefined` is not dropped but converted to `null`, so `tags` becomes `["vip",null]` rather than `["vip"]`. `Date` objects are serialized through their `toJSON()` method, which returns an ISO 8601 string (`"1970-01-01T00:00:00.000Z"`), not a number.

**Sources:** [JSON - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON)

### Question 4

A developer at Ursa Major Solar writes code to choose a tax rate, but the script fails before any line runs.
Refer to the code below:
```javascript
const region = 'EU';
const taxRate;
if (region === 'EU') {
    taxRate = 0.2;
} else {
    taxRate = 0.08;
}
console.log(taxRate);
```
Which two changes allow the code to log the correct tax rate? Choose 2 answers.

- A. Replace line 2 with `const taxRate = null;` and keep the `if...else` assignments.
- B. Replace line 2 with `let taxRate;` and keep the `if...else` assignments.
- C. Remove line 2 and declare `const taxRate;` inside each branch of the `if...else` statement.
- D. Replace lines 2 through 7 with `const taxRate = region === 'EU' ? 0.2 : 0.08;`
- E. Add `'use strict';` as the first line of the script.

**Correct answers:** B. Replace line 2 with `let taxRate;` and keep the `if...else` assignments.; D. Replace lines 2 through 7 with `const taxRate = region === 'EU' ? 0.2 : 0.08;`

**Explanation:** A `const` declaration must be initialized in the same statement, so `const taxRate;` raises `SyntaxError: Missing initializer in const declaration` and the script never runs. Declaring with `let` allows the variable to be created without a value and assigned later in either branch, and a conditional (ternary) expression lets `const` be initialized with the correct value in a single statement. Initializing the constant to `null` removes the syntax error, but the later assignments throw `TypeError: Assignment to constant variable`. Declaring `const taxRate;` inside each branch still lacks an initializer, and even with one the block-scoped variable would not be visible to `console.log`. Strict mode does not change the rule that `const` requires an initializer.

**Sources:** [const - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const), [let - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let)

## Objects, Functions, and Classes (25%)

### Question 5

A developer at Universal Containers loads a dashboard with `<script type="module" src="dashboard.js"></script>` instead of a classic script tag. Which three statements are true about how the browser handles dashboard.js? Choose 3 answers.

- A. The script is deferred automatically, so it runs after the HTML document has been parsed even without a `defer` attribute.
- B. Top-level `this` in dashboard.js refers to the `window` object.
- C. The code runs in strict mode even without a `'use strict'` directive.
- D. The page works when it is opened directly from the local file system with a `file://` URL.
- E. A `var` declared at the top level of dashboard.js is scoped to the module and does not become a property of `window`.

**Correct answers:** A. The script is deferred automatically, so it runs after the HTML document has been parsed even without a `defer` attribute.; C. The code runs in strict mode even without a `'use strict'` directive.; E. A `var` declared at the top level of dashboard.js is scoped to the module and does not become a property of `window`.

**Explanation:** Module scripts are deferred by default, so they execute after the document is parsed, and module code always runs in strict mode. Top-level declarations in a module, including `var`, are scoped to that module rather than attached to the global object, so other scripts cannot read them unless the module exports them or assigns them to `window` explicitly. Top-level `this` in a module is undefined, not window. Browsers apply CORS rules to module scripts, so opening the page from a `file://` URL fails and a local web server is needed for testing.

**Sources:** [JavaScript modules - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)

### Question 6

A Universal Containers integration builds an update payload whose key comes from a variable.
```javascript
const field = 'status';
const value = 'Closed';
const update = { [field]: value, field };
console.log(update);
```
What is logged to the console?

- A. `{ field: 'Closed', status: 'status' }`
- B. `{ status: 'Closed', field: 'status' }`
- C. `{ status: 'Closed' }`
- D. A SyntaxError, because object keys cannot come from variables

**Correct answer:** B. `{ status: 'Closed', field: 'status' }`

**Explanation:** Square brackets in an object literal create a computed property name, so `[field]` evaluates the variable and uses its value, `'status'`, as the key. The shorthand `field` is equivalent to `field: field`, which adds a property named `field` whose value is the string `'status'`. The option with `field: 'Closed'` reverses the two features, `{ status: 'Closed' }` ignores the shorthand property, and both syntaxes have been valid since ES2015, so no SyntaxError is thrown.

**Sources:** [Working with objects - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_objects)

### Question 7

Cumulus Financial requires that an account balance can be changed only through the `deposit` method. A developer uses a private class field and then tests it.
```javascript
class Wallet {
    #balance = 0;
    deposit(amount) {
        this.#balance += amount;
        return this.#balance;
    }
}
const wallet = new Wallet();
console.log(wallet.deposit(50));
console.log(wallet.#balance);
```
What is the result of running this script?

- A. `50` is logged, and then `undefined` is logged.
- B. `50` is logged twice.
- C. `50` is logged, and then a `TypeError` is thrown.
- D. A `SyntaxError` is reported, and nothing is logged.

**Correct answer:** D. A `SyntaxError` is reported, and nothing is logged.

**Explanation:** Referencing a private name such as `#balance` outside the class body that declares it is an early error, so the script fails to parse and a SyntaxError is reported before any line runs. Because parsing fails, even the valid `deposit` call never executes and nothing is logged. Private fields are not ordinary properties that evaluate to `undefined` or expose their value outside the class, and the error is not a runtime TypeError raised after the first log.

**Sources:** [Classes - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes), [Using classes - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_classes)

### Question 8

The development lead at AW Computing is reviewing a pull request that converts every function in a legacy library to an arrow function. Which two behaviors of arrow functions should the lead consider before approving the change? Choose 2 answers.

- A. Arrow functions are hoisted together with their body, so they can be called before their definition.
- B. Arrow functions cannot be called with `new`, so any function used as a constructor must stay a regular function.
- C. Arrow functions receive `this` from the object that calls them, just like regular methods.
- D. Arrow functions do not support default parameter values.
- E. Arrow functions do not have their own `arguments` object, so code that reads `arguments` must switch to rest parameters.

**Correct answers:** B. Arrow functions cannot be called with `new`, so any function used as a constructor must stay a regular function.; E. Arrow functions do not have their own `arguments` object, so code that reads `arguments` must switch to rest parameters.

**Explanation:** Arrow functions cannot be used as constructors, so calling one with `new` throws a TypeError, and they do not have their own `arguments` object, so rest parameters are the replacement. Arrow functions are expressions assigned to variables, so they are not hoisted with their body the way function declarations are. They take `this` lexically from the surrounding scope rather than from the caller, which is the opposite of how regular methods behave. Default parameters work the same in arrow functions as in regular functions.

**Sources:** [Arrow function expressions - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions)

### Question 9

A developer at AW Computing writes a function factory for pricing adjustments:
```javascript
function makeMultiplier(factor) {
    return function (value) {
        factor = factor + 1;
        return value * factor;
    };
}
const scale = makeMultiplier(2);
const scaleAgain = makeMultiplier(2);
console.log(scale(10), scale(10), scaleAgain(10));
```
What is logged to the console?

- A. `20 20 20`
- B. `30 30 30`
- C. `30 40 30`
- D. `30 40 50`

**Correct answer:** C. `30 40 30`

**Explanation:** The parameter factor is a variable in makeMultiplier's scope, and the returned closure can both read and reassign it. Each call to scale increments the same factor, so the first call computes 10 * 3 = 30 and the second computes 10 * 4 = 40. scaleAgain comes from a separate call to makeMultiplier, so it has its own factor starting at 2 and returns 30. `20 20 20` ignores the increment, `30 30 30` assumes factor resets on each call, and `30 40 50` assumes both closures share one factor variable.

**Sources:** [Closures - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures)

## Browser and Events (17%)

### Question 10

Refer to the HTML and code below, which Get Cloudy Consulting uses on an approval screen:
```html
<div id="panel">
    <button id="approve">Approve</button>
</div>
```
```javascript
const panel = document.querySelector('#panel');
const btn = document.querySelector('#approve');
panel.addEventListener('click', () => console.log('panel'));
btn.addEventListener('click', (event) => {
    console.log('first');
    event.stopImmediatePropagation();
});
btn.addEventListener('click', () => console.log('second'));
```
What is logged when the Approve button is clicked?

- A. `first`
- B. `first`, `second`
- C. `first`, `second`, `panel`
- D. `first`, `panel`

**Correct answer:** A. `first`

**Explanation:** `stopImmediatePropagation()` prevents any remaining listeners from being called, both the other listeners on the same element and those on ancestors, so only `first` is logged. `first`, `second` is what `stopPropagation()` would produce, because that method lets the remaining listeners on the current element run but blocks ancestors. `first`, `second`, `panel` ignores the call entirely. `first`, `panel` reverses the behavior by skipping the sibling listener while still bubbling to the ancestor.

**Sources:** [Event - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/Event), [Event: stopImmediatePropagation() method | MDN](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopImmediatePropagation)

### Question 11

Refer to the HTML and code below from a Northern Trail Outfitters task page:
```html
<ul id="tasks">
    <li class="item">Call</li>
    <li class="item">Email</li>
</ul>
```
```javascript
const list = document.querySelector('#tasks');
const live = list.getElementsByClassName('item');
const snapshot = list.querySelectorAll('.item');
const li = document.createElement('li');
li.className = 'item';
list.appendChild(li);
console.log(live.length, snapshot.length);
```
What is logged to the console?

- A. `2 2`
- B. `3 3`
- C. `3 2`
- D. `2 3`

**Correct answer:** C. `3 2`

**Explanation:** `getElementsByClassName()` returns a live `HTMLCollection` that always reflects the current DOM, so the appended item appears in it immediately and it reports 3. `querySelectorAll()` returns a static `NodeList`, a snapshot taken when it was called, so it still reports 2. `2 2` and `3 3` treat both collections as the same kind, and `2 3` reverses which one is live.

**Sources:** [Document Object Model (DOM) - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model), [Element: getElementsByClassName() method | MDN](https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByClassName), [Document: querySelectorAll() method | MDN](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll)

### Question 12

Cumulus Financial's single-page account app switches between tabs without reloading the page. Refer to the code below:
```javascript
function showTab(tab) {
    renderTab(tab);
    history.pushState({ tab: tab }, '', `/account/${tab}`);
}

window.addEventListener('popstate', (event) => {
    renderTab(event.state ? event.state.tab : 'summary');
});
```
Which two statements about this code are true? Choose 2 answers.

- A. Calling `showTab('billing')` changes the address bar to `/account/billing` without the browser loading that URL.
- B. Calling `showTab('billing')` immediately fires a `popstate` event, so `renderTab()` runs twice.
- C. When the user clicks the browser's Back button, the `popstate` listener runs and `event.state` holds the state of the history entry being returned to, or `null` for an entry created without `pushState()`.
- D. `pushState()` can set the URL to a different origin, such as `https://partner.example.com/account`.
- E. The third argument to `pushState()` must be an absolute URL, so the relative path `/account/billing` throws an error.

**Correct answers:** A. Calling `showTab('billing')` changes the address bar to `/account/billing` without the browser loading that URL.; C. When the user clicks the browser's Back button, the `popstate` listener runs and `event.state` holds the state of the history entry being returned to, or `null` for an entry created without `pushState()`.

**Explanation:** `pushState()` adds a history entry and updates the URL, and the browser does not attempt to load that URL, which is why single-page apps use it for client-side routing. Calling `pushState()` does not trigger `popstate`; the event fires when the user navigates the session history, for example with the Back or Forward button, and `event.state` carries a copy of that entry's state object. The new URL must have the same origin as the current page, otherwise `pushState()` throws an exception. A relative URL is allowed and is resolved against the current URL, so `/account/billing` works.

**Sources:** [History: pushState() method - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState), [Window: popstate event - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event), [Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API)

## Debugging and Error Handling (7%)

### Question 13

Universal Containers receives cart data from a partner API as a string. Testing found two failure cases: the partner sometimes sends a truncated payload such as `{"cart": {"items": [` and sometimes sends `{}`.
```javascript
function countItems(payload) {
    try {
        const data = JSON.parse(payload);
        return data.cart.items.length;
    } catch (err) {
        console.log(err.name);
        return 0;
    }
}
```
Which two values does the `catch` block log for these two failure cases? Choose 2 answers.

- A. `SyntaxError`
- B. `ReferenceError`
- C. `RangeError`
- D. `TypeError`
- E. `JSONError`

**Correct answers:** A. `SyntaxError`; D. `TypeError`

**Explanation:** `JSON.parse()` throws a `SyntaxError` when the string is not valid JSON, which is what happens with the truncated payload. With `{}`, parsing succeeds and returns an empty object, so `data.cart` is `undefined` and reading `items` from `undefined` throws a `TypeError`. A `ReferenceError` is thrown when code refers to an undeclared identifier, but every identifier in the function is declared. A `RangeError` signals a numeric value outside its allowed range, which neither payload produces. There is no built-in `JSONError` type; JSON parse failures are reported as `SyntaxError`.

**Sources:** [JSON.parse() - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse), [Error - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)

## Asynchronous Programming (13%)

### Question 14

Northern Trail Outfitters is building a trail dashboard that calls three independent APIs: weather, trail conditions, and permits. Each API call returns a Promise. The dashboard must wait until every request has finished, render each widget whose request succeeded, and show an error message only in the widgets whose request failed. Which method should the developer use to combine the three promises?

- A. `Promise.all()`
- B. `Promise.race()`
- C. `Promise.allSettled()`
- D. `Promise.any()`

**Correct answer:** C. `Promise.allSettled()`

**Explanation:** Promise.allSettled() waits for every input promise to settle and fulfills with an array of outcome objects, each with `status: 'fulfilled'` and a `value` or `status: 'rejected'` and a `reason`, so the dashboard can render successes and failures individually. Promise.all() rejects as soon as one request fails, so the results of the successful requests are not available in its rejection. Promise.race() settles with whichever request settles first, so the other two outcomes are lost. Promise.any() fulfills with only the first successful result and rejects with an AggregateError only when all three fail.

**Sources:** [Promise.allSettled() - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled), [Promise - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)

### Question 15

Northern Trail Outfitters logs the name of each trail when a timer fires. The developer declared the counter with `let`, expecting each callback to capture its own value. Refer to the code below:
```javascript
const trails = ['Ridge', 'Lake', 'Pine'];
let i = 0;
while (i < trails.length) {
    setTimeout(() => console.log(trails[i]), 100);
    i++;
}
```
The code logs `undefined` three times instead of `Ridge`, `Lake`, and `Pine`. Which three changes make the code log the three trail names? Choose 3 answers.

- A. Change `let i = 0;` to `var i = 0;`.
- B. Replace the `while` loop with `for (const trail of trails) { setTimeout(() => console.log(trail), 100); }`.
- C. Add `const index = i;` as the first statement in the loop body and change the callback to `() => console.log(trails[index])`.
- D. Change the delay from `100` to `i * 100` so that each callback runs at a different time.
- E. Replace the `while` loop with `trails.forEach((trail) => setTimeout(() => console.log(trail), 100));`.

**Correct answers:** B. Replace the `while` loop with `for (const trail of trails) { setTimeout(() => console.log(trail), 100); }`.; C. Add `const index = i;` as the first statement in the loop body and change the callback to `() => console.log(trails[index])`.; E. Replace the `while` loop with `trails.forEach((trail) => setTimeout(() => console.log(trail), 100));`.

**Explanation:** `let` creates a new binding for each iteration only when it is declared in a `for` loop header; here `i` is declared once outside the loop, so all three callbacks close over the same variable, which is 3 by the time the timers fire after the loop has finished, and `trails[3]` is `undefined`. A `for...of` loop with a `const` variable, a `const` copy declared inside the loop body, and the parameter of a `forEach()` callback each give every callback its own binding that holds the current trail or index. Switching to `var` keeps a single shared, function-scoped variable. Staggering the delays changes only when each callback runs, not which variable it reads, so every callback still reads 3.

**Sources:** [for - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for), [Closures - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures), [Window: setTimeout() method - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout)

## Server Side JavaScript (8%)

### Question 16

Ursa Major Solar's inventory service uses an `EventEmitter` from the core `events` module to notify other modules when stock levels change. A new developer asks how the emitter behaves. Which two statements are true? Choose 2 answers.

- A. `emit()` returns a Promise that resolves after every listener has finished.
- B. Adding more than 10 listeners for the same event makes the emitter remove the oldest listeners automatically.
- C. When `emit()` is called, the listeners run synchronously in the order in which they were registered.
- D. A listener registered with `on()` is removed automatically after the event fires once.
- E. Emitting an `'error'` event when no `'error'` listener is registered throws the error, and the process exits if nothing catches it.

**Correct answers:** C. When `emit()` is called, the listeners run synchronously in the order in which they were registered.; E. Emitting an `'error'` event when no `'error'` listener is registered throws the error, and the process exits if nothing catches it.

**Explanation:** The events docs state that an `EventEmitter` calls all listeners synchronously in registration order, and that emitting `'error'` without at least one `'error'` listener throws the error, prints a stack trace and exits the process, which is why services should always register an `'error'` handler. `emit()` returns a boolean (`true` if the event had listeners, `false` otherwise), not a Promise. Exceeding the default of 10 listeners only prints a possible memory-leak warning; no listener is removed. Automatic removal after one call is the behaviour of `once()`, while listeners added with `on()` stay registered until they are removed.

**Sources:** [Events | Node.js Documentation](https://nodejs.org/api/events.html)

### Question 17

A Northern Trail Outfitters app runs fine on developer laptops. In the production container, `npm install` completes without errors, but the app crashes at startup with `Error: Cannot find module 'dayjs'`. Which two situations could explain the failure? Choose 2 answers.

- A. `dayjs` is listed under `devDependencies`, and the container sets `NODE_ENV=production` before running `npm install`.
- B. The developer ran `npm install dayjs` without adding the `--save` flag, so npm did not record it in package.json.
- C. The developer installed it with `npm install dayjs --no-save`, so it exists only in their local `node_modules` folder.
- D. package.json lists `dayjs` with the caret range `^1.11.10` instead of an exact version.
- E. The team committed package-lock.json to source control alongside package.json.

**Correct answers:** A. `dayjs` is listed under `devDependencies`, and the container sets `NODE_ENV=production` before running `npm install`.; C. The developer installed it with `npm install dayjs --no-save`, so it exists only in their local `node_modules` folder.

**Explanation:** When `NODE_ENV` is set to `production`, npm does not install modules listed in `devDependencies` (the `omit` config defaults to `dev`), so a runtime package saved as a dev dependency is missing in production. `--no-save` prevents saving to `dependencies`, so the package sits in the developer's local `node_modules` but never reaches package.json or the container. npm saves installed packages to `dependencies` by default (the `save` config defaults to `true`), so leaving out `--save` does not explain the failure. A caret range still installs a matching version, and package-lock.json is intended to be committed so every environment installs the same tree.

**Sources:** [npm install | npm Docs](https://docs.npmjs.com/cli/v12/commands/npm-install), [package-lock.json | npm Docs](https://docs.npmjs.com/cli/v12/configuring-npm/package-lock-json)

## Testing (7%)

### Question 18

Cloud Kicks offers a loyalty program to customers who are 18 or older. A developer wrote the function and unit test below, and the test passes.
```javascript
const assert = require('node:assert/strict');

function isEligible(age) {
    return age > 18;
}

assert.strictEqual(isEligible(30), true);
assert.strictEqual(isEligible(12), false);
```
Which additional assertion makes the test effective at catching the defect in `isEligible()`?

- A. `assert.strictEqual(isEligible(65), true);`
- B. `assert.strictEqual(isEligible(17), false);`
- C. `assert.strictEqual(isEligible(18), true);`
- D. `assert.ok(typeof isEligible(30) === 'boolean');`

**Correct answer:** C. `assert.strictEqual(isEligible(18), true);`

**Explanation:** The requirement includes customers who are exactly 18, but `age > 18` returns `false` for 18, so an assertion at the boundary that expects `isEligible(18)` to be `true` fails and exposes the off-by-one defect (the fix is `age >= 18`). Checking 65 repeats the adult case already covered by 30 and passes with the defective code. Checking 17 is a sensible boundary on the other side, but `17 > 18` is `false`, so it also passes with the defective code. Checking that the result is a boolean verifies the return type, not the eligibility rule. Effective unit tests exercise the values at and around each boundary of a condition.

**Sources:** [Assert: assert.strictEqual() | Node.js Documentation](https://nodejs.org/api/assert.html#assertstrictequalactual-expected-message)

## Related pages

- [Salesforce JavaScript Developer practice exam](https://www.certifyforce.com/salesforce-javascript-developer-1-practice-exam)
- [Practice exam packs](https://www.certifyforce.com/practice-exams)
- [Official JavaScript Developer exam guide](https://help.salesforce.com/s/articleView?id=005298945&language=en_US&type=1)
