mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-03-28 23:01:57 -04:00
feat(curriculum): add Cargo Manifest Validator Lab to JavaScript v9 (#66197)
Co-authored-by: LGH831 <luishernandez1@csumb.edu> Co-authored-by: Ilenia M <nethleen@gmail.com>
This commit is contained in:
@@ -4829,6 +4829,12 @@
|
||||
"In these lectures, you will learn the fundamentals of JavaScript objects, including how to create them, access their properties, and understand the difference between primitive and non-primitive data types."
|
||||
]
|
||||
},
|
||||
"lab-cargo-manifest-validator": {
|
||||
"title": "Build a Cargo Manifest Validator",
|
||||
"intro": [
|
||||
"In this lab, you will use JavaScript to normalize and validate cargo manifests."
|
||||
]
|
||||
},
|
||||
"lecture-working-with-json": {
|
||||
"title": "Working with JSON",
|
||||
"intro": [
|
||||
|
||||
@@ -0,0 +1,861 @@
|
||||
---
|
||||
id: 69a56b5069ca99f7317e6e19
|
||||
title: Build a Cargo Manifest Validator
|
||||
challengeType: 26
|
||||
dashedName: lab-cargo-manifest-validator
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In this lab, you will use JavaScript to normalize and validate cargo manifests. A cargo manifest is a document that typically lists goods being transported (for example, by ship or train) and includes details about those goods.
|
||||
|
||||
Each cargo manifest will be represented as an object with the following properties:
|
||||
|
||||
- `containerId`: a positive integer identifying the associated cargo container.
|
||||
- `destination`: a non-empty string (after trimming whitespace) denoting the cargo's target destination.
|
||||
- `weight`: a positive number representing the cargo's weight.
|
||||
- `unit`: a string describing the units for the cargo's weight property (either `"kg"` for kilograms or `"lb"` for pounds).
|
||||
- `hazmat`: a boolean value indicating whether hazardous material handling is needed.
|
||||
|
||||
Example cargo manifest object:
|
||||
|
||||
```js
|
||||
{
|
||||
containerId: 1,
|
||||
destination: "Monterey, California, USA",
|
||||
weight: 831,
|
||||
unit: "lb",
|
||||
hazmat: false
|
||||
}
|
||||
```
|
||||
|
||||
**Objective:** Fulfill the user stories below and get all the tests to pass to complete the lab.
|
||||
|
||||
**User Stories:**
|
||||
|
||||
1. You should implement a function named `normalizeUnits` with a `manifest` parameter.
|
||||
- The function must not mutate the original manifest object and must always return a new object where `weight` is normalized to kilograms and `unit` is set to `"kg"`.
|
||||
- If the weight of the manifest object is expressed in pounds (`unit: "lb"`), the function should convert the `weight` to kilograms using the approximate conversion, 1 lb = 0.45 kg, and update the `unit` accordingly.
|
||||
- If the weight is already expressed in kilograms (`unit: "kg"`), the `weight` and `unit` should remain unchanged.
|
||||
2. You should implement a function named `validateManifest` with a `manifest` parameter.
|
||||
- The function must not mutate the original manifest object and must always return a new object.
|
||||
- If the input manifest is valid (no missing or invalid properties), the function should return an empty object.
|
||||
- If the input manifest is not valid, the function should return an object containing entries for each missing or invalid property. Missing properties should have the value `"Missing"` and invalid properties should have the value `"Invalid"`.
|
||||
|
||||
Example return value where the input object is missing the `destination` property and has an invalid `weight` property:
|
||||
|
||||
```js
|
||||
{
|
||||
destination: "Missing",
|
||||
weight: "Invalid"
|
||||
}
|
||||
```
|
||||
|
||||
3. You should implement a function named `processManifest` with a `manifest` parameter. The function should log:
|
||||
- If the manifest object is valid, `Validation success: ${containerId}` and then the manifest's weight in kilograms as such, `Total weight: ${weight} kg`. Use `normalizeUnits()` for this conversion.
|
||||
- If the manifest object is not valid, `Validation error: ${containerId}` and then the object returned by calling `validateManifest()` with the manifest object.
|
||||
**Note:** each of these two cases should have two `console.log()` calls.
|
||||
|
||||
# --before-each--
|
||||
|
||||
```js
|
||||
const _validManifests = [
|
||||
{
|
||||
containerId: 4,
|
||||
destination: "Monterey, California, USA",
|
||||
weight: 831,
|
||||
unit: "lb",
|
||||
hazmat: false
|
||||
},
|
||||
{
|
||||
containerId: 5,
|
||||
destination: "Montreal, Quebec, Canada",
|
||||
weight: 151,
|
||||
unit: "kg",
|
||||
hazmat: false
|
||||
}
|
||||
];
|
||||
|
||||
const _invalidManifests = [
|
||||
{
|
||||
containerId: -6,
|
||||
destination: 123,
|
||||
weight: 0,
|
||||
unit: "pounds",
|
||||
hazmat: true
|
||||
},
|
||||
{
|
||||
containerId: 0,
|
||||
weight: -21,
|
||||
unit: "KG"
|
||||
},
|
||||
{
|
||||
containerId: "eight",
|
||||
destination: "Guadalajara, Jalisco, Mexico",
|
||||
weight: 9001,
|
||||
hazmat: "no"
|
||||
},
|
||||
{
|
||||
destination: " ",
|
||||
weight: NaN,
|
||||
unit: "kg",
|
||||
hazmat: 1
|
||||
},
|
||||
{
|
||||
containerId: 10,
|
||||
destination: " ",
|
||||
},
|
||||
];
|
||||
|
||||
const _testObj = {
|
||||
containerId: 1.51,
|
||||
destination: "Lavender Town",
|
||||
weight: NaN,
|
||||
};
|
||||
|
||||
console.log = () => {};
|
||||
```
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a function named `normalizeUnits` with a `manifest` parameter.
|
||||
|
||||
```js
|
||||
assert.isFunction(normalizeUnits);
|
||||
const funcString = normalizeUnits.toString();
|
||||
const params = __helpers.getFunctionParams(funcString);
|
||||
assert.equal(params.length, 1);
|
||||
assert.equal(params[0].name, 'manifest');
|
||||
```
|
||||
|
||||
Calling `normalizeUnits()` with `{ containerId: 68, destination: "Salinas", weight: 101, unit: "lb", hazmat: true }` should return the new object `{ containerId: 68, destination: "Salinas", weight: 45.45, unit: "kg", hazmat: true }` without mutating the source input.
|
||||
|
||||
```js
|
||||
const testObj = {
|
||||
containerId: 68,
|
||||
destination: "Salinas",
|
||||
weight: 101,
|
||||
unit: "lb",
|
||||
hazmat: true
|
||||
};
|
||||
|
||||
const expected = {
|
||||
containerId: 68,
|
||||
destination: "Salinas",
|
||||
weight: 45.45,
|
||||
unit: "kg",
|
||||
hazmat: true
|
||||
};
|
||||
|
||||
const copy = normalizeUnits(testObj);
|
||||
|
||||
assert.isObject(copy);
|
||||
assert.deepEqual(copy, expected, "normalizeUnits() did not return the expected normalized object");
|
||||
assert.notStrictEqual(copy, testObj, "normalizeUnits() should return a new object.");
|
||||
assert.strictEqual(testObj.containerId, 68, "Original object should not be mutated.");
|
||||
assert.strictEqual(testObj.destination, "Salinas", "Original object should not be mutated.");
|
||||
assert.strictEqual(testObj.weight, 101, "Original object should not be mutated.");
|
||||
assert.strictEqual(testObj.unit, "lb", "Original object should not be mutated.");
|
||||
assert.strictEqual(testObj.hazmat, true, "Original object should not be mutated.");
|
||||
assert.equal(Object.keys(testObj).length, 5, "Original object should not be mutated.");
|
||||
```
|
||||
|
||||
Your `normalizeUnits` function should return a copy of the input manifest object with its `weight` normalized to kilograms and its `unit` set to `"kg"`. Use the approximate conversion `1 lb = 0.45 kg` for the weight conversion.
|
||||
|
||||
```js
|
||||
_validManifests.forEach((obj, i) => {
|
||||
const result = normalizeUnits(obj);
|
||||
assert.isObject(result, "normalizeUnits should return an object");
|
||||
assert.notStrictEqual(result, obj, "normalizeUnits must return a new object, not modify the original");
|
||||
const expectedWeight = obj.unit === "lb" ? obj.weight * 0.45 : obj.weight;
|
||||
const expected = {
|
||||
...obj,
|
||||
weight: expectedWeight,
|
||||
unit: "kg"
|
||||
};
|
||||
|
||||
assert.deepEqual(
|
||||
result,
|
||||
expected,
|
||||
`normalizeUnits did not return the expected normalized object for manifest at index ${i}`
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
Your `normalizeUnits` function should return a new copy of the input `manifest` object without mutating the original.
|
||||
|
||||
```js
|
||||
const original = {
|
||||
containerId: 151,
|
||||
destination: "Saffron City",
|
||||
weight: 151,
|
||||
unit: "lb",
|
||||
hazmat: false
|
||||
};
|
||||
|
||||
const expected = {
|
||||
containerId: 151,
|
||||
destination: "Saffron City",
|
||||
weight: 67.95,
|
||||
unit: "kg",
|
||||
hazmat: false
|
||||
};
|
||||
|
||||
const copy = normalizeUnits(original);
|
||||
assert.isObject(copy);
|
||||
assert.deepEqual(copy, expected);
|
||||
assert.notStrictEqual(copy, original, "normalizeUnits() should return a new object.");
|
||||
assert.strictEqual(original.weight, 151, "Original object should not be mutated.");
|
||||
assert.strictEqual(original.unit, "lb", "Original object should not be mutated.");
|
||||
assert.equal(Object.keys(original).length, 5, "Original object should not be mutated.");
|
||||
```
|
||||
|
||||
You should have a function named `validateManifest` with a `manifest` parameter.
|
||||
|
||||
```js
|
||||
assert.isFunction(validateManifest);
|
||||
const funcString = validateManifest.toString();
|
||||
const params = __helpers.getFunctionParams(funcString);
|
||||
assert.equal(params.length, 1);
|
||||
assert.equal(params[0].name, 'manifest');
|
||||
```
|
||||
|
||||
Calling `validateManifest()` with `{ containerId: 1, destination: "Santa Cruz", weight: 304, unit: "kg", hazmat: false }` should return the new object `{}`.
|
||||
|
||||
```js
|
||||
const testObj = {
|
||||
containerId: 1,
|
||||
destination: "Santa Cruz",
|
||||
weight: 304,
|
||||
unit: "kg",
|
||||
hazmat: false
|
||||
};
|
||||
|
||||
const expected = {};
|
||||
|
||||
const copy = validateManifest(testObj);
|
||||
|
||||
assert.isObject(copy);
|
||||
assert.deepEqual(copy, expected, "validateManifest() did not return the expected object for a valid manifest");
|
||||
assert.notStrictEqual(copy, testObj, "validateManifest() should return a new object, not mutate the original");
|
||||
assert.strictEqual(testObj.containerId, 1, "Original object should not be mutated");
|
||||
assert.strictEqual(testObj.destination, "Santa Cruz", "Original object should not be mutated");
|
||||
assert.strictEqual(testObj.weight, 304, "Original object should not be mutated");
|
||||
assert.strictEqual(testObj.unit, "kg", "Original object should not be mutated");
|
||||
assert.strictEqual(testObj.hazmat, false, "Original object should not be mutated");
|
||||
```
|
||||
|
||||
If the input manifest object is valid, your `validateManifest` function should return an empty object `{}`.
|
||||
|
||||
```js
|
||||
const solution = {}
|
||||
for (const obj of _validManifests) {
|
||||
assert.deepEqual(
|
||||
validateManifest(obj),
|
||||
solution,
|
||||
"validateManifest() should return an empty object for valid manifests."
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Calling `validateManifest()` with `{}` should return the new object `{ containerId: "Missing", destination: "Missing", weight: "Missing", unit: "Missing", hazmat: "Missing" }` without mutating the source input.
|
||||
|
||||
```js
|
||||
const testObj = {
|
||||
};
|
||||
|
||||
const expected = {
|
||||
containerId: "Missing",
|
||||
destination: "Missing",
|
||||
weight: "Missing",
|
||||
unit: "Missing",
|
||||
hazmat: "Missing"
|
||||
};
|
||||
|
||||
const copy = validateManifest(testObj);
|
||||
assert.isObject(copy);
|
||||
assert.deepEqual(copy, expected, "validateManifest() did not return the expected object for missing properties");
|
||||
assert.notStrictEqual(copy, testObj, "validateManifest() should return a new object, not mutate the original");
|
||||
assert.equal(Object.keys(testObj).length, 0, "validateManifest() should return a new object, not mutate the original")
|
||||
```
|
||||
|
||||
Calling `validateManifest()` with `{ containerId: 0, destination: 405, weight: -84, unit: "pounds", hazmat: "no" }` should return the new object `{ containerId: "Invalid", destination: "Invalid", weight: "Invalid", unit: "Invalid", hazmat: "Invalid" }` without mutating the source input.
|
||||
|
||||
```js
|
||||
const testObj = {
|
||||
containerId: 0,
|
||||
destination: 405,
|
||||
weight: -84,
|
||||
unit: "pounds",
|
||||
hazmat: "no"
|
||||
};
|
||||
|
||||
const expected = {
|
||||
containerId: "Invalid",
|
||||
destination: "Invalid",
|
||||
weight: "Invalid",
|
||||
unit: "Invalid",
|
||||
hazmat: "Invalid"
|
||||
};
|
||||
|
||||
const copy = validateManifest(testObj);
|
||||
assert.isObject(copy);
|
||||
assert.deepEqual(copy, expected, "validateManifest() did not return the expected object for invalid properties");
|
||||
assert.notStrictEqual(copy, testObj, "validateManifest() should return a new object, not mutate the original");
|
||||
assert.strictEqual(testObj.containerId, 0, "Original object should not be mutated");
|
||||
assert.strictEqual(testObj.destination, 405, "Original object should not be mutated");
|
||||
assert.strictEqual(testObj.weight, -84, "Original object should not be mutated");
|
||||
assert.strictEqual(testObj.unit, "pounds", "Original object should not be mutated");
|
||||
assert.strictEqual(testObj.hazmat, "no", "Original object should not be mutated");
|
||||
```
|
||||
|
||||
Calling `validateManifest()` with `{ containerId: -2 }` should return the new object `{ containerId: "Invalid", destination: "Missing", weight: "Missing", unit: "Missing", hazmat: "Missing" }` without mutating the source input.
|
||||
|
||||
```js
|
||||
const testObj = {
|
||||
containerId: -2,
|
||||
};
|
||||
|
||||
const expected = {
|
||||
containerId: "Invalid",
|
||||
destination: "Missing",
|
||||
weight: "Missing",
|
||||
unit: "Missing",
|
||||
hazmat: "Missing"
|
||||
};
|
||||
|
||||
const copy = validateManifest(testObj);
|
||||
assert.isObject(copy);
|
||||
assert.deepEqual(copy, expected, "validateManifest() did not return the expected object for invalid and missing properties");
|
||||
assert.notStrictEqual(copy, testObj, "validateManifest() should return a new object, not mutate the original");
|
||||
assert.strictEqual(testObj.containerId, -2, "Original object should not be mutated");
|
||||
assert.equal(Object.keys(testObj).length, 1, "Original object should not be mutated");
|
||||
```
|
||||
|
||||
Calling `validateManifest()` with `{ containerId: 3.50 }` should return the new object `{ containerId: "Invalid", destination: "Missing", weight: "Missing", unit: "Missing", hazmat: "Missing" }` without mutating the source input. You can use `Number.isInteger()` to validate integer values.
|
||||
|
||||
```js
|
||||
const testObj = {
|
||||
containerId: 3.50,
|
||||
};
|
||||
|
||||
const expected = {
|
||||
containerId: "Invalid",
|
||||
destination: "Missing",
|
||||
weight: "Missing",
|
||||
unit: "Missing",
|
||||
hazmat: "Missing"
|
||||
};
|
||||
|
||||
const copy = validateManifest(testObj);
|
||||
assert.isObject(copy);
|
||||
assert.deepEqual(copy, expected, "validateManifest() did not return the expected object for invalid and missing properties");
|
||||
assert.notStrictEqual(copy, testObj, "validateManifest() should return a new object, not mutate the original");
|
||||
assert.strictEqual(testObj.containerId, 3.50, "Original object should not be mutated");
|
||||
assert.equal(Object.keys(testObj).length, 1, "Original object should not be mutated");
|
||||
```
|
||||
|
||||
Calling `validateManifest()` with `{ destination: " " }` should return the new object `{ containerId: "Missing", destination: "Invalid", weight: "Missing", unit: "Missing", hazmat: "Missing" }` without mutating the source input. You can use `String.trim()` to remove whitespace from a string.
|
||||
|
||||
```js
|
||||
const testObj = {
|
||||
destination: " ",
|
||||
};
|
||||
|
||||
const expected = {
|
||||
containerId: "Missing",
|
||||
destination: "Invalid",
|
||||
weight: "Missing",
|
||||
unit: "Missing",
|
||||
hazmat: "Missing"
|
||||
};
|
||||
|
||||
const copy = validateManifest(testObj);
|
||||
assert.isObject(copy);
|
||||
assert.deepEqual(copy, expected, "validateManifest() did not return the expected object for invalid and missing properties");
|
||||
assert.notStrictEqual(copy, testObj, "validateManifest() should return a new object, not mutate the original");
|
||||
assert.strictEqual(testObj.destination, " ", "Original object should not be mutated");
|
||||
assert.equal(Object.keys(testObj).length, 1, "Original object should not be mutated");
|
||||
```
|
||||
|
||||
Calling `validateManifest()` with `{ weight: NaN }` should return the new object `{ containerId: "Missing", destination: "Missing", weight: "Invalid", unit: "Missing", hazmat: "Missing" }` without mutating the source input. You can use `Number.isNaN()` to validate NaN values.
|
||||
|
||||
```js
|
||||
const testObj = {
|
||||
weight: NaN,
|
||||
};
|
||||
|
||||
const expected = {
|
||||
containerId: "Missing",
|
||||
destination: "Missing",
|
||||
weight: "Invalid",
|
||||
unit: "Missing",
|
||||
hazmat: "Missing"
|
||||
};
|
||||
|
||||
const copy = validateManifest(testObj);
|
||||
assert.isObject(copy);
|
||||
assert.deepEqual(copy, expected, "validateManifest() did not return the expected object for invalid and missing properties");
|
||||
assert.notStrictEqual(copy, testObj, "validateManifest() should return a new object, not mutate the original");
|
||||
assert.ok(Number.isNaN(testObj.weight), "Original object should not be mutated");
|
||||
assert.equal(Object.keys(testObj).length, 1, "Original object should not be mutated");
|
||||
```
|
||||
|
||||
If the input manifest object is not valid, your `validateManifest` function should return an object describing missing and/or invalid properties.
|
||||
|
||||
```js
|
||||
const solutions = [
|
||||
{ containerId: 'Invalid', destination: 'Invalid', weight: 'Invalid', unit: 'Invalid' },
|
||||
{ containerId: 'Invalid', destination: 'Missing', weight: 'Invalid', unit: 'Invalid', hazmat: 'Missing' },
|
||||
{ containerId: 'Invalid', unit: 'Missing', hazmat: 'Invalid' },
|
||||
{ containerId: 'Missing', destination: 'Invalid', weight: 'Invalid', hazmat: 'Invalid' },
|
||||
{ destination: 'Invalid', weight: 'Missing', unit: 'Missing', hazmat: 'Missing' }
|
||||
];
|
||||
|
||||
let i = 0;
|
||||
for (const obj of _invalidManifests) {
|
||||
const result = validateManifest(obj);
|
||||
assert.deepEqual(
|
||||
result,
|
||||
solutions[i],
|
||||
"validateManifest() should return an object with errors for invalid manifests."
|
||||
);
|
||||
i += 1;
|
||||
}
|
||||
```
|
||||
|
||||
Your `validateManifest` function should return a new object without mutating the original.
|
||||
|
||||
```js
|
||||
const invalidOriginal = {
|
||||
containerId: -151,
|
||||
destination: "",
|
||||
weight: -151,
|
||||
unit: "kilograms",
|
||||
hazmat: "idk"
|
||||
};
|
||||
|
||||
const validOriginal = {
|
||||
containerId: 151,
|
||||
destination: "Pallet Town",
|
||||
weight: 151,
|
||||
unit: "kg",
|
||||
hazmat: false
|
||||
};
|
||||
|
||||
const res0 = validateManifest(invalidOriginal);
|
||||
assert.isObject(res0);
|
||||
assert.notStrictEqual(res0, invalidOriginal, "validateManifest() should return a new object.");
|
||||
assert.strictEqual(invalidOriginal.containerId, -151, "Original object should not be mutated.");
|
||||
assert.strictEqual(invalidOriginal.destination, "", "Original object should not be mutated.");
|
||||
assert.strictEqual(invalidOriginal.weight, -151, "Original object should not be mutated.");
|
||||
assert.strictEqual(invalidOriginal.unit, "kilograms", "Original object should not be mutated.");
|
||||
assert.strictEqual(invalidOriginal.hazmat, "idk", "Original object should not be mutated.");
|
||||
assert.equal(Object.keys(invalidOriginal).length, 5, "Original object should not be mutated.");
|
||||
|
||||
const res1 = validateManifest(validOriginal);
|
||||
assert.isObject(res1);
|
||||
assert.notStrictEqual(res1, validOriginal, "validateManifest() should return a new object.");
|
||||
assert.strictEqual(validOriginal.containerId, 151, "Original object should not be mutated.");
|
||||
assert.strictEqual(validOriginal.destination, "Pallet Town", "Original object should not be mutated.");
|
||||
assert.strictEqual(validOriginal.weight, 151, "Original object should not be mutated.");
|
||||
assert.strictEqual(validOriginal.unit, "kg", "Original object should not be mutated.");
|
||||
assert.strictEqual(validOriginal.hazmat, false, "Original object should not be mutated.");
|
||||
assert.equal(Object.keys(validOriginal).length, 5, "Original object should not be mutated.");
|
||||
```
|
||||
|
||||
You should have a function named `processManifest` with a `manifest` parameter.
|
||||
|
||||
```js
|
||||
assert.isFunction(processManifest);
|
||||
const funcString = processManifest.toString();
|
||||
const params = __helpers.getFunctionParams(funcString);
|
||||
assert.equal(params.length, 1);
|
||||
assert.equal(params[0].name, 'manifest');
|
||||
```
|
||||
|
||||
Calling `processManifest()` with `{ containerId: 55, destination: "Carmel", weight: 400, unit: "lb", hazmat: false }` should first log `"Validation success: 55"` and then log `"Total weight: 180 kg"`.
|
||||
|
||||
```js
|
||||
const testObj = {
|
||||
containerId: 55,
|
||||
destination: "Carmel",
|
||||
weight: 400,
|
||||
unit: "lb",
|
||||
hazmat: false
|
||||
};
|
||||
|
||||
const spy = __helpers.spyOn(console, "log");
|
||||
|
||||
try {
|
||||
processManifest(testObj);
|
||||
|
||||
const expectedCalls = [
|
||||
["Validation success: 55"],
|
||||
["Total weight: 180 kg"]
|
||||
];
|
||||
assert.deepEqual(spy.calls[0], expectedCalls[0], "First log should be the validation success message");
|
||||
assert.deepEqual(spy.calls[1], expectedCalls[1], "Second log should be the normalized weight in kg");
|
||||
|
||||
} catch (err) {
|
||||
assert.fail(err);
|
||||
} finally {
|
||||
spy.restore();
|
||||
}
|
||||
```
|
||||
|
||||
If the input manifest object is valid, your `processManifest` function should first log the success message, `Validation success: ${containerId}`.
|
||||
|
||||
```js
|
||||
const testObj = {
|
||||
containerId: 97,
|
||||
destination: "Cerulean City",
|
||||
weight: 200,
|
||||
unit: "lb",
|
||||
hazmat: false
|
||||
}
|
||||
|
||||
const spy = __helpers.spyOn(console, "log");
|
||||
|
||||
try {
|
||||
processManifest(testObj);
|
||||
const expectedCalls = [
|
||||
["Validation success: 97"],
|
||||
["Total weight: 90 kg"],
|
||||
]
|
||||
assert.deepEqual(spy.calls[0], expectedCalls[0]);
|
||||
|
||||
} catch (err) {
|
||||
assert.fail(err);
|
||||
} finally {
|
||||
spy.restore();
|
||||
}
|
||||
```
|
||||
|
||||
If the input manifest object is valid, your `processManifest` function should normalize it to kilograms using `normalizeUnits()` and then log: `Total weight: ${weight} kg`.
|
||||
|
||||
```js
|
||||
const testObj = {
|
||||
containerId: 97,
|
||||
destination: "Cerulean City",
|
||||
weight: 200,
|
||||
unit: "lb",
|
||||
hazmat: false
|
||||
}
|
||||
|
||||
const spy = __helpers.spyOn(console, "log");
|
||||
|
||||
try {
|
||||
processManifest(testObj);
|
||||
const expectedCalls = [
|
||||
["Validation success: 97"],
|
||||
["Total weight: 90 kg"],
|
||||
]
|
||||
assert.deepEqual(spy.calls[1], expectedCalls[1]);
|
||||
|
||||
} catch (err) {
|
||||
assert.fail(err);
|
||||
} finally {
|
||||
spy.restore();
|
||||
}
|
||||
```
|
||||
|
||||
If the input manifest object is valid, your `processManifest` function should log a success message with the object's `containerId`, and then log the object's `weight` in kilograms. You should use `normalizeUnits()` for the conversion and have two `console.log()` calls.
|
||||
|
||||
```js
|
||||
const originalNormalize = normalizeUnits;
|
||||
let called = false;
|
||||
try {
|
||||
normalizeUnits = function (...args) {
|
||||
called = true;
|
||||
return originalNormalize(...args);
|
||||
};
|
||||
|
||||
processManifest(_validManifests[0]);
|
||||
|
||||
assert.isTrue(
|
||||
called,
|
||||
"processManifest should call normalizeUnits() when the manifest is valid"
|
||||
);
|
||||
|
||||
} catch (err) {
|
||||
assert.fail(err);
|
||||
} finally {
|
||||
normalizeUnits = originalNormalize;
|
||||
}
|
||||
|
||||
const spy = __helpers.spyOn(console, "log");
|
||||
|
||||
try {
|
||||
for (const obj of _validManifests) {
|
||||
processManifest(obj);
|
||||
}
|
||||
const expectedCalls = [
|
||||
["Validation success: 4"],
|
||||
["Total weight: 373.95 kg"],
|
||||
["Validation success: 5"],
|
||||
["Total weight: 151 kg"],
|
||||
]
|
||||
assert.deepEqual(spy.calls, expectedCalls);
|
||||
|
||||
} catch (err) {
|
||||
assert.fail(err);
|
||||
} finally {
|
||||
spy.restore();
|
||||
}
|
||||
```
|
||||
|
||||
Calling `processManifest()` with `{ containerId: -88, destination: "Soledad", weight: NaN }` should first log `Validation error: -88` and then log the object `{ containerId: "Invalid", weight: "Invalid", unit: "Missing", hazmat: "Missing" }`.
|
||||
|
||||
```js
|
||||
const testObj = {
|
||||
containerId: -88,
|
||||
destination: "Soledad",
|
||||
weight: NaN
|
||||
};
|
||||
|
||||
const spy = __helpers.spyOn(console, "log");
|
||||
|
||||
try {
|
||||
processManifest(testObj);
|
||||
const expectedCalls = [
|
||||
["Validation error: -88"],
|
||||
[{
|
||||
containerId: "Invalid",
|
||||
weight: "Invalid",
|
||||
unit: "Missing",
|
||||
hazmat: "Missing"
|
||||
}]
|
||||
];
|
||||
|
||||
assert.deepEqual(spy.calls[0], expectedCalls[0], "First log should be the validation error message");
|
||||
assert.deepEqual(spy.calls[1], expectedCalls[1], "Second log should be the validation result object");
|
||||
|
||||
} catch (err) {
|
||||
assert.fail(err);
|
||||
} finally {
|
||||
spy.restore();
|
||||
}
|
||||
```
|
||||
|
||||
Calling `processManifest()` with `{ destination: "Watsonville", hazmat: true }` should first log `Validation error: undefined` and then log the object `{ containerId: "Missing", weight: "Missing", unit: "Missing" }`.
|
||||
|
||||
```js
|
||||
const testObj = {
|
||||
destination: "Watsonville",
|
||||
hazmat: true
|
||||
};
|
||||
|
||||
const spy = __helpers.spyOn(console, "log");
|
||||
|
||||
try {
|
||||
processManifest(testObj);
|
||||
const expectedCalls = [
|
||||
["Validation error: undefined"],
|
||||
[{
|
||||
containerId: "Missing",
|
||||
weight: "Missing",
|
||||
unit: "Missing"
|
||||
}]
|
||||
];
|
||||
|
||||
assert.deepEqual(spy.calls[0], expectedCalls[0], "First log should be the validation error message");
|
||||
assert.deepEqual(spy.calls[1], expectedCalls[1], "Second log should be the validation result object");
|
||||
|
||||
} catch (err) {
|
||||
assert.fail(err);
|
||||
} finally {
|
||||
spy.restore();
|
||||
}
|
||||
```
|
||||
|
||||
If the input manifest object is not valid, your `processManifest` function should first log the error message, `Validation error: ${containerId}`.
|
||||
|
||||
```js
|
||||
const spy = __helpers.spyOn(console, "log");
|
||||
|
||||
try {
|
||||
processManifest(_testObj);
|
||||
const expectedCalls = [
|
||||
["Validation error: 1.51"],
|
||||
[{
|
||||
containerId: "Invalid",
|
||||
weight: "Invalid",
|
||||
unit: "Missing",
|
||||
hazmat: "Missing"
|
||||
}]
|
||||
];
|
||||
assert.deepEqual(spy.calls[0], expectedCalls[0]);
|
||||
|
||||
} catch (err) {
|
||||
assert.fail(err);
|
||||
} finally {
|
||||
spy.restore();
|
||||
}
|
||||
```
|
||||
|
||||
If the input manifest object is not valid, your `processManifest` function should also log the object returned by calling `validateManifest()` with the original manifest object. Call `console.log()` directly with the returned object.
|
||||
|
||||
```js
|
||||
const originalValidate = validateManifest;
|
||||
let called = false;
|
||||
|
||||
try {
|
||||
validateManifest = function (...args) {
|
||||
called = true;
|
||||
return originalValidate(...args);
|
||||
};
|
||||
|
||||
processManifest(_invalidManifests[0]);
|
||||
|
||||
assert.isTrue(
|
||||
called,
|
||||
"processManifest should call validateManifest() when the manifest is invalid"
|
||||
);
|
||||
|
||||
} catch (err) {
|
||||
assert.fail(err);
|
||||
} finally {
|
||||
validateManifest = originalValidate;
|
||||
}
|
||||
|
||||
const spy = __helpers.spyOn(console, "log");
|
||||
|
||||
try {
|
||||
processManifest(_testObj);
|
||||
const expectedCalls = [
|
||||
["Validation error: 1.51"],
|
||||
[{
|
||||
containerId: "Invalid",
|
||||
weight: "Invalid",
|
||||
unit: "Missing",
|
||||
hazmat: "Missing"
|
||||
}]
|
||||
];
|
||||
assert.deepEqual(spy.calls[1], expectedCalls[1]);
|
||||
|
||||
} catch (err) {
|
||||
assert.fail(err);
|
||||
} finally {
|
||||
spy.restore();
|
||||
}
|
||||
```
|
||||
|
||||
If the input manifest object is not valid, your `processManifest` function should log an error message with the object's `containerId`, and then log the object returned by calling `validateManifest()` with the input object. This should involve two `console.log()` calls.
|
||||
|
||||
```js
|
||||
const originalValidate = validateManifest;
|
||||
let called = false;
|
||||
|
||||
try {
|
||||
validateManifest = function (...args) {
|
||||
called = true;
|
||||
return originalValidate(...args);
|
||||
};
|
||||
|
||||
processManifest(_invalidManifests[0]);
|
||||
|
||||
assert.isTrue(
|
||||
called,
|
||||
"processManifest should call validateManifest() when the manifest is invalid"
|
||||
);
|
||||
|
||||
} catch (err) {
|
||||
assert.fail(err);
|
||||
} finally {
|
||||
validateManifest = originalValidate;
|
||||
}
|
||||
|
||||
const spy = __helpers.spyOn(console, "log");
|
||||
|
||||
try {
|
||||
processManifest(_testObj);
|
||||
const expectedCalls = [
|
||||
["Validation error: 1.51"],
|
||||
[{
|
||||
containerId: "Invalid",
|
||||
weight: "Invalid",
|
||||
unit: "Missing",
|
||||
hazmat: "Missing"
|
||||
}]
|
||||
];
|
||||
assert.deepEqual(spy.calls, expectedCalls);
|
||||
|
||||
} catch (err) {
|
||||
assert.fail(err);
|
||||
} finally {
|
||||
spy.restore();
|
||||
}
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
function normalizeUnits(manifest) {
|
||||
const normalized = {
|
||||
...manifest
|
||||
}
|
||||
if (normalized.unit === "lb") {
|
||||
normalized.weight = normalized.weight * 0.45;
|
||||
normalized.unit = "kg";
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function validateManifest(manifest) {
|
||||
const result = {}
|
||||
|
||||
if (manifest.containerId === undefined) {
|
||||
result.containerId = "Missing";
|
||||
} else if (
|
||||
typeof manifest.containerId !== "number" ||
|
||||
!Number.isInteger(manifest.containerId) ||
|
||||
manifest.containerId <= 0
|
||||
) {
|
||||
result.containerId = "Invalid";
|
||||
}
|
||||
if (manifest.destination === undefined) {
|
||||
result.destination = "Missing";
|
||||
} else if (typeof manifest.destination !== "string" || manifest.destination.trim() === "") {
|
||||
result.destination = "Invalid";
|
||||
}
|
||||
if (manifest.weight === undefined) {
|
||||
result.weight = "Missing";
|
||||
} else if (typeof manifest.weight !== "number" || manifest.weight <= 0 || Number.isNaN(manifest.weight)) {
|
||||
result.weight = "Invalid";
|
||||
}
|
||||
if (manifest.unit === undefined) {
|
||||
result.unit = "Missing";
|
||||
} else if (manifest.unit !== "kg" && manifest.unit !== "lb") {
|
||||
result.unit = "Invalid";
|
||||
}
|
||||
if (manifest.hazmat === undefined) {
|
||||
result.hazmat = "Missing";
|
||||
} else if (typeof manifest.hazmat !== "boolean") {
|
||||
result.hazmat = "Invalid";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function processManifest(manifest) {
|
||||
const validation = validateManifest(manifest);
|
||||
|
||||
if (Object.keys(validation).length > 0) {
|
||||
console.log(`Validation error: ${manifest.containerId}`);
|
||||
console.log(validation);
|
||||
} else {
|
||||
const normalized = normalizeUnits(manifest);
|
||||
console.log(`Validation success: ${manifest.containerId}`);
|
||||
console.log(`Total weight: ${normalized.weight} kg`);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"isUpcomingChange": false,
|
||||
"dashedName": "lab-cargo-manifest-validator",
|
||||
"helpCategory": "JavaScript",
|
||||
"blockLayout": "link",
|
||||
"challengeOrder": [
|
||||
{
|
||||
"id": "69a56b5069ca99f7317e6e19",
|
||||
"title": "Build a Cargo Manifest Validator"
|
||||
}
|
||||
],
|
||||
"blockLabel": "lab",
|
||||
"usesMultifileEditor": true
|
||||
}
|
||||
@@ -82,6 +82,7 @@
|
||||
"dashedName": "javascript-objects",
|
||||
"blocks": [
|
||||
"lecture-introduction-to-javascript-objects-and-their-properties",
|
||||
"lab-cargo-manifest-validator",
|
||||
"lecture-working-with-json",
|
||||
"lecture-working-with-optional-chaining-and-object-destructuring",
|
||||
"workshop-recipe-tracker",
|
||||
|
||||
Reference in New Issue
Block a user