From 3453c9e841a406f80e48d9be81e5b33e2f257dfd Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Wed, 8 Jul 2026 10:11:39 +0100 Subject: [PATCH 01/18] Fix error in debug/address.js --- Sprint-2/debug/address.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..16001dcd9 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,4 +1,5 @@ // Predict and explain first... +// [MM] - The original code returns undefined because it is trying to call houseNumber by index, rather than by key. // This code should log out the houseNumber from the address object // but it isn't working... @@ -12,4 +13,4 @@ const address = { postcode: "XYZ 123", }; -console.log(`My house number is ${address[0]}`); +console.log(`My house number is ${address.houseNumber}`); From 4399fc1bca324323b51b39db0002017c9fc4f139 Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Wed, 8 Jul 2026 10:28:39 +0100 Subject: [PATCH 02/18] Fix bug in debug/author.js --- Sprint-2/debug/author.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..eba40efd3 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,4 +1,5 @@ // Predict and explain first... +// [MM] - The original code does not work because it is trying to loop through an array, rather than an object // This program attempts to log out all the property values in the object. // But it isn't working. Explain why first and then fix the problem @@ -11,6 +12,6 @@ const author = { alive: true, }; -for (const value of author) { +for (const value of Object.values(author)) { console.log(value); } From 4a01f99bd53b847337a46382d688b528f65a5742 Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Thu, 9 Jul 2026 02:16:20 +0100 Subject: [PATCH 03/18] Fix error in debug/recipe.js --- Sprint-2/debug/recipe.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..f5ba35eb0 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -1,4 +1,5 @@ // Predict and explain first... +// [MM] - I expect this programme to log out recipe object rather than the ingredients // This program should log out the title, how many it serves and the ingredients. // Each ingredient should be logged on a new line @@ -10,6 +11,5 @@ const recipe = { ingredients: ["olive oil", "tomatoes", "salt", "pepper"], }; -console.log(`${recipe.title} serves ${recipe.serves} - ingredients: -${recipe}`); +console.log(`${recipe.title} serves ${recipe.serves} ingredients: +${recipe.ingredients.join("\n")}`); From 3a207285afc384baf4c79a7ee6742d6b0209a3e7 Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Thu, 9 Jul 2026 14:29:58 +0100 Subject: [PATCH 04/18] write tests for implement/contains.test.js --- Sprint-2/implement/contains.test.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..983ec1639 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -16,20 +16,33 @@ as the object doesn't contains a key of 'c' // Given a contains function // When passed an object and a property name // Then it should return true if the object contains the property, false otherwise +test("given an object and a property name, returns true if object contains the property, otherwise return false", () => { + expect(contains({ a: 1, b: 2 }, "a")).toBe(true); + expect(contains({ a: 1, b: 2 }, "c")).toBe(false); +}); // Given an empty object // When passed to contains // Then it should return false -test.todo("contains on empty object returns false"); +test("given an empty object returns false", () => { + expect(contains({}, "a")).toBe(false); +}); // Given an object with properties // When passed to contains with an existing property name // Then it should return true - +test("given an object and a property name, returns true if object contains the property", () => { + expect(contains({ a: 1, b: 2 }, "a")).toBe(true); +}); // Given an object with properties // When passed to contains with a non-existent property name // Then it should return false - +test("given an object and a property name, returns false if object does not contain the property", () => { + expect(contains({ a: 1, b: 2 }, "c")).toBe(false); +}); // Given invalid parameters like an array // When passed to contains // Then it should return false or throw an error +test("given an invalid array parameter, returns false", () => { + expect(contains([1, 2, 3], 3)).toBe(false); +}); From 3ce1f746a9c995d4df804988f98989f4d253f144 Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Thu, 9 Jul 2026 14:54:24 +0100 Subject: [PATCH 05/18] complete function in implement/contains.js --- Sprint-2/implement/contains.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..3a615dd64 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,16 @@ -function contains() {} +function contains(object, value) { + const isNotObject = + typeof object !== "object" || + object === null || + Array.isArray(object) || + Object.keys(object).length < 1; + + if (isNotObject) return false; + + return Object.keys(object).includes(value); +} + +console.log(contains({ a: 1, b: 2 }, "a")); +console.log(contains(["a", "b"], "a")); module.exports = contains; From e0455d0d6e910f7dcf97e50ea24f2924e8e826f6 Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Fri, 10 Jul 2026 08:40:13 +0100 Subject: [PATCH 06/18] Refactor implement/contains.js --- Sprint-2/implement/contains.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index 3a615dd64..d7060e92e 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,13 +1,10 @@ function contains(object, value) { const isNotObject = - typeof object !== "object" || - object === null || - Array.isArray(object) || - Object.keys(object).length < 1; + typeof object !== "object" || object === null || Array.isArray(object); if (isNotObject) return false; - return Object.keys(object).includes(value); + return Object.hasOwn(object, value); } console.log(contains({ a: 1, b: 2 }, "a")); From b603cf44be84976427fb11b59c5ff9e4b54f6a74 Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Fri, 10 Jul 2026 09:25:33 +0100 Subject: [PATCH 07/18] Write first test for implement/lookup.test.js --- Sprint-2/implement/lookup.test.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..7bd50b4a6 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,7 +1,5 @@ const createLookup = require("./lookup.js"); -test.todo("creates a country currency code lookup for multiple codes"); - /* Create a lookup object of key value pairs from an array of code pairs @@ -33,3 +31,14 @@ It should return: 'CA': 'CAD' } */ +test("maps country codes to currency codes", () => { + expect( + createLookup([ + ["US", "USD"], + ["CA", "CAD"], + ]) + ).toEqual({ + US: "USD", + CA: "CAD", + }); +}); From 975a820c9c5970ba0e7002101c0754f069528190 Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Tue, 14 Jul 2026 15:08:41 +0100 Subject: [PATCH 08/18] Write tests for lookup.test.js --- Sprint-2/implement/lookup.test.js | 57 ++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 7bd50b4a6..a70de530b 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -31,14 +31,53 @@ It should return: 'CA': 'CAD' } */ -test("maps country codes to currency codes", () => { - expect( - createLookup([ - ["US", "USD"], - ["CA", "CAD"], - ]) - ).toEqual({ - US: "USD", - CA: "CAD", + +describe("createLookup", () => { + test("maps country codes to currency codes", () => { + expect( + createLookup([ + ["US", "USD"], + ["CA", "CAD"], + ]) + ).toEqual({ + US: "USD", + CA: "CAD", + }); + }); + + test.each([ + [ + "throws when input array is empty", + [], + "Input must not be an empty array", + ], + ["throws when input is not an array", "hello", "Input must be an array"], + [ + "throws when input pairs are invalid", + [1, 2, 3], + "Input must be valid text pairs", + ], + [ + "throws when duplicate pairs are provided", + [ + ["US", "USD"], + ["US", "USD"], + ], + "Input must not be duplicated", + ], + ])("%s", (_title, data, expecteMessage) => { + expect(() => createLookup(data).toThrow(expecteMessage)); + }); + + test("throws when country code format is incorrect", () => { + expect(() => createLookup([["us", "USD"]])).toThrow( + "Country code must be 2 uppercase letters" + ); + }); + + test("throws when currency code format is incorrect", () => { + expect(() => createLookup([["US", "usd"]])).toThrow( + "Currency code must be 3 uppercase letters" + ); }); }); From e7733131ec9af19f2bd0437cd0f60c82c8cc8f47 Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Tue, 14 Jul 2026 15:10:13 +0100 Subject: [PATCH 09/18] Complete lookup.js - all tests pass --- Sprint-2/implement/lookup.js | 83 +++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 2 deletions(-) diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..1c2ef44a0 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,84 @@ -function createLookup() { - // implementation here +function createLookup(data) { + validateData(data); + return data.reduce((lookup, [country, currency]) => { + lookup[country] = currency; + return lookup; + }, {}); } +// check for valid pairs +const isValidPair = (value) => + Array.isArray(value) && + value.length === 2 && + typeof value[0] === "string" && + typeof value[1] === "string"; + +// check for duplicates +const normaliseDataPair = ([country, currency]) => `${country}|${currency}`; +const isDuplicated = (data) => + new Set(data.map(normaliseDataPair)).size !== data.length; + +const validateData = (data) => { + // check that it is an array + if (!Array.isArray(data)) { + throw new Error("Input must be an array"); + } + // check that input is not empty + if (data.length === 0) { + throw new Error("Input must not be empty"); + } + + // check that the values are valid + if (data.some((value) => !isValidPair(value))) { + throw new Error("Input must be valid text pairs"); + } + + // check for duplicates + if (isDuplicated(data)) { + throw new Error("Input must not be duplicated"); + } + + // check country code has 2 uppercase letters + if (data.some(([country]) => !/^[A-Z]{2}$/.test(country))) { + throw new Error("Country code must be 2 uppercase letters"); + } + // check currency code has 3 uppercase letters + if (data.some(([, currency]) => !/^[A-Z]{3}$/.test(currency))) { + throw new Error("Currency code must be 3 uppercase letters"); + } +}; + +// Handle invalid cases +const INVALID_CASES = [ + [[], "Input must not be empty"], + ["hello", "Input must be an array"], + [[1, 2, 3], "Input must be valid text pairs"], + [ + [ + ["US", "USD"], + ["US", "USD"], + ], + "Input must not be duplicated", + ], +]; + +const printInvalidCases = () => { + for (const [data, expectedMessage] of INVALID_CASES) { + try { + createLookup(data); + console.log(`Unexpected success for ${JSON.stringify(data)}`); + } catch (error) { + console.log("-".repeat(50)); + console.log( + `Data: ${JSON.stringify(data)}\nExpected: ${expectedMessage}\nReceived: ${error.message}` + ); + } + } +}; + +if (require.main === module) { + console.log("DATA ERRORS"); + printInvalidCases(); + console.log("-".repeat(50)); +} module.exports = createLookup; From dab2b2470603ea96dc014b097e4ed1a1f9921a41 Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Wed, 15 Jul 2026 18:13:24 +0100 Subject: [PATCH 10/18] Complete implement/querysting.js so that all tests pass --- Sprint-2/implement/querystring.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..39e232015 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -6,10 +6,22 @@ function parseQueryString(queryString) { const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); - queryParams[key] = value; - } + if (pair === "") continue; + + const [keyPart, ...valueParts] = pair.split("="); + const rawKey = keyPart.trim("").replace(/\+/g, " "); + const rawValue = valueParts.join("=").trim("").replace(/\+/g, " "); + const key = decodeURIComponent(rawKey); + const value = decodeURIComponent(rawValue); + if (queryParams[key] === undefined) { + queryParams[key] = value; + } else if (Array.isArray(queryParams[key])) { + queryParams[key].push(value); + } else { + queryParams[key] = [queryParams[key], value]; + } + } return queryParams; } From 48482452cbc00a286f521ec48a6cd2fc6d77a304 Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Wed, 15 Jul 2026 18:19:18 +0100 Subject: [PATCH 11/18] Add edge test cases to querystring.test.js --- Sprint-2/implement/querystring.test.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 328b8df61..0a5f54ee9 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -3,7 +3,7 @@ // Below are some test cases the implementation doesn't handle well. // Fix the implementation for these tests, and try to think of as many other edge cases as possible - write tests and fix those too. -const parseQueryString = require("./querystring.js") +const parseQueryString = require("./querystring.js"); test("should parse values containing '='", () => { expect(parseQueryString("equation=a=b-2")).toEqual({ @@ -46,3 +46,17 @@ test("should store values of a key in an array when the key has 2 or more values foo: "bar", }); }); + +test("should return empty object for empty string", () => { + expect(parseQueryString("")).toEqual({}); +}); + +test("should ignore leading and trailing ampersands", () => { + expect(parseQueryString("&a=1&b=2&")).toEqual({ a: "1", b: "2" }); +}); + +test("should parse keys and values with plus signs and percent encoding", () => { + expect(parseQueryString("full+name=John+Doe%21")).toEqual({ + "full name": "John Doe!", + }); +}); From bbb2a56a9a06ba0a4b7dd13a5e8c749a1b1639ba Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Wed, 15 Jul 2026 23:51:55 +0100 Subject: [PATCH 12/18] write tests fo tally.test.js --- Sprint-2/implement/tally.test.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..c1bcac222 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -19,16 +19,28 @@ const tally = require("./tally.js"); // Given a function called tally // When passed an array of items // Then it should return an object containing the count for each unique item +test("return counts for each unique item", () => { + expect(tally(["a", "b", "c"])).toEqual({ a: 1, b: 1, c: 1 }); + expect(tally([1, 2, 3])).toEqual({ 1: 1, 2: 1, 3: 1 }); +}); // Given an empty array // When passed to tally // Then it should return an empty object -test.todo("tally on an empty array returns an empty object"); +test("tally on an empty array returns an empty object", () => { + expect(tally([])).toEqual({}); +}); // Given an array with duplicate items // When passed to tally // Then it should return counts for each unique item +test("return counts for each unique item of duplicate items", () => { + expect(tally(["a", "a", "a"])).toEqual({ a: 3 }); +}); // Given an invalid input like a string // When passed to tally // Then it should throw an error +test("throws when input is not an array", () => { + expect(() => tally("hello")).toThrow("Input must be an array"); +}); From 87eb907dae10c320b5e8b373c9e9aa274269211a Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Wed, 15 Jul 2026 23:55:05 +0100 Subject: [PATCH 13/18] complete tally.js - all tests pass --- Sprint-2/implement/tally.js | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..b537cdb1f 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,35 @@ -function tally() {} +function tally(array) { + if (!Array.isArray(array)) { + throw new Error("Input must be an array"); + } + + return array.reduce((acc, item) => { + acc[item] = (acc[item] ?? 0) + 1; + return acc; + }, {}); +} + +// Handle invalid cases +const INVALID_CASES = [["hello", "Input must be an array"]]; + +const printInvalidCases = () => { + for (const [data, expectedMessage] of INVALID_CASES) { + try { + tally(data); + console.log(`Unexpected success for ${JSON.stringify(data)}`); + } catch (error) { + console.log("-".repeat(50)); + console.log( + `Data: ${JSON.stringify(data)}\nExpected: ${expectedMessage}\nReceived: ${error.message}` + ); + } + } +}; + +if (require.main === module) { + console.log("ERROR MESSAGES"); + printInvalidCases(); + console.log("-".repeat(50)); +} module.exports = tally; From 23e876b89a2a26b3fff7c397425a512c3628e22f Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Wed, 15 Jul 2026 23:59:10 +0100 Subject: [PATCH 14/18] update trim method in querystring.js --- Sprint-2/implement/querystring.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 39e232015..c9fd781a7 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -9,8 +9,8 @@ function parseQueryString(queryString) { if (pair === "") continue; const [keyPart, ...valueParts] = pair.split("="); - const rawKey = keyPart.trim("").replace(/\+/g, " "); - const rawValue = valueParts.join("=").trim("").replace(/\+/g, " "); + const rawKey = keyPart.trim().replace(/\+/g, " "); + const rawValue = valueParts.join("=").trim().replace(/\+/g, " "); const key = decodeURIComponent(rawKey); const value = decodeURIComponent(rawValue); From 7e095824397e52f5e97d25a93e6f97a3daae0f4f Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Thu, 16 Jul 2026 15:43:59 +0100 Subject: [PATCH 15/18] fix issue in invert.js --- Sprint-2/interpret/invert.js | 80 +++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..3830725fc 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -7,23 +7,91 @@ // E.g. invert({x : 10, y : 20}), target output: {"10": "x", "20": "y"} function invert(obj) { + if (obj === null || typeof obj !== "object" || Array.isArray(obj)) { + throw new Error("Input must be an object"); + } + + if (Object.keys(obj).length === 0) { + return {}; + } + const invertedObj = {}; + const seenValues = new Set(); for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; + const isKeyValid = typeof key === "string" || typeof key === "number"; + const isValueValid = typeof value === "string" || typeof value === "number"; + + if (!isKeyValid || !isValueValid) { + throw new Error("Keys and values must be a string or a number"); + } + + if (seenValues.has(value)) { + throw new Error("Values cannot be duplicated"); + } + + seenValues.add(value); + invertedObj[value] = key; } return invertedObj; } -// a) What is the current return value when invert is called with { a : 1 } +// Handle invalid cases +const INVALID_CASES = [ + [{ a: {} }, "Keys and values must be a string or a number"], + [{ a: 1, b: 1 }, "Values cannot be duplicated"], + ["hello", "Input must be an object"], +]; -// b) What is the current return value when invert is called with { a: 1, b: 2 } +const printInvalidCases = () => { + for (const [data, expectedMessage] of INVALID_CASES) { + try { + invert(data); + console.log(`Unexpected success for ${JSON.stringify(data)}`); + } catch (error) { + console.log("-".repeat(50)); + console.log( + `Data: ${JSON.stringify(data)}\nExpected: ${expectedMessage}\nReceived: ${error.message}` + ); + } + } +}; -// c) What is the target return value when invert is called with {a : 1, b: 2} +if (require.main === module) { + console.log("ERROR MESSAGES"); + printInvalidCases(); + console.log("-".repeat(50)); -// c) What does Object.entries return? Why is it needed in this program? + const runExample = (input) => { + try { + const result = invert(input); + console.log( + `Input: ${JSON.stringify(input)} => ${JSON.stringify(result)}` + ); + } catch (error) { + console.log(`Input: ${JSON.stringify(input)} => ${error.message}`); + } + }; -// d) Explain why the current return value is different from the target output + // Example cases + runExample({ x: 10, x: 20 }); + runExample({ a: 1 }); + // Invalid cases + runExample({ x: 20, y: 20 }); + runExample({ a: {} }); +} + +module.exports = invert; +// a) What is the current return value when invert is called with { a : 1 } +// Current return value is {key: 1} +// b) What is the current return value when invert is called with { a: 1, b: 2 } +// Current return value is {key: 1, key: 2} +// c) What is the target return value when invert is called with {a : 1, b: 2} +// Target return value when invert is called with {a : 1, b: 2} is {key: 1, key: 2} +// c) What does Object.entries return? Why is it needed in this program? +// Object.entries returns the key / value pairs in nested arrays [["a", 1], "b", 2]]. It is needed in order for this programme to be able to work as it requires swapping of keys and values +// d) Explain why the current return value is different from the target output +// The current value is returning a key because it is using the key notation (invertedObj.key). It needs to be returned using brackets and the value and key swapped - inveretedObj[value] = key // e) Fix the implementation of invert (and write tests to prove it's fixed!) From 253201d7843113998e7db3ee005f55cac235561f Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Thu, 16 Jul 2026 15:50:36 +0100 Subject: [PATCH 16/18] create test file invert.test.js - all tests pass --- Sprint-2/interpret/invert.test.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Sprint-2/interpret/invert.test.js diff --git a/Sprint-2/interpret/invert.test.js b/Sprint-2/interpret/invert.test.js new file mode 100644 index 000000000..cd226a392 --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,27 @@ +const invert = require("./invert"); + +test("inverts valid keys and values", () => { + expect(invert({ x: 10, y: 20 })).toEqual({ 10: "x", 20: "y" }); +}); + +test("inverts last key if keys are duplicated", () => { + expect(invert({ x: 10, x: 20 })).toEqual({ 20: "x" }); +}); + +test("returns empty object if no input is provided", () => { + expect(invert({ x: 10, x: 20 })).toEqual({ 20: "x" }); +}); + +test("throws when input is not an object", () => { + expect(() => invert("hello")).toThrow("Input must be an object"); +}); + +test("throws when keys or values are not a string or number", () => { + expect(() => invert({ a: {} })).toThrow( + "Keys and values must be a string or a number" + ); +}); + +test("throws when values are duplicated", () => { + expect(() => invert({ a: 1, b: 1 })).toThrow("Values cannot be duplicated"); +}); From 8a80a2027af635c338c8beb689a66dfe08f38a53 Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Thu, 16 Jul 2026 15:57:29 +0100 Subject: [PATCH 17/18] Update empty object test in invert.test.js --- Sprint-2/interpret/invert.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sprint-2/interpret/invert.test.js b/Sprint-2/interpret/invert.test.js index cd226a392..dbec876a2 100644 --- a/Sprint-2/interpret/invert.test.js +++ b/Sprint-2/interpret/invert.test.js @@ -8,8 +8,8 @@ test("inverts last key if keys are duplicated", () => { expect(invert({ x: 10, x: 20 })).toEqual({ 20: "x" }); }); -test("returns empty object if no input is provided", () => { - expect(invert({ x: 10, x: 20 })).toEqual({ 20: "x" }); +test("returns empty object if input has no keys", () => { + expect(invert({})).toEqual({}); }); test("throws when input is not an object", () => { From 853d45b7653953a4d5088993d68c762d4a5ddce1 Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Fri, 17 Jul 2026 06:59:42 +0100 Subject: [PATCH 18/18] Fix bug on line 68 of lookup.test.js --- Sprint-2/implement/lookup.js | 4 ++-- Sprint-2/implement/lookup.test.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index 1c2ef44a0..e77b07032 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -25,7 +25,7 @@ const validateData = (data) => { } // check that input is not empty if (data.length === 0) { - throw new Error("Input must not be empty"); + throw new Error("Input must not be an empty array"); } // check that the values are valid @@ -50,7 +50,7 @@ const validateData = (data) => { // Handle invalid cases const INVALID_CASES = [ - [[], "Input must not be empty"], + [[], "Input must not be an empty array"], ["hello", "Input must be an array"], [[1, 2, 3], "Input must be valid text pairs"], [ diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index a70de530b..ca73a4259 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -66,7 +66,7 @@ describe("createLookup", () => { "Input must not be duplicated", ], ])("%s", (_title, data, expecteMessage) => { - expect(() => createLookup(data).toThrow(expecteMessage)); + expect(() => createLookup(data)).toThrow(expecteMessage); }); test("throws when country code format is incorrect", () => {