From d7431d77161255f5f18a5192746a9c784921416b Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Sun, 5 Jul 2026 20:20:14 +0100 Subject: [PATCH 01/16] Complete task for Sprint-2 cleanly --- Sprint-2/1-key-errors/0.js | 14 ++++------ Sprint-2/1-key-errors/1.js | 22 ++++++--------- Sprint-2/1-key-errors/2.js | 23 ++++----------- Sprint-2/2-mandatory-debug/0.js | 15 ++++------ Sprint-2/2-mandatory-debug/1.js | 14 ++++------ Sprint-2/2-mandatory-debug/2.js | 27 +++++++----------- Sprint-2/3-mandatory-implement/1-bmi.js | 2 +- Sprint-2/3-mandatory-implement/2-cases.js | 20 ++++--------- Sprint-2/3-mandatory-implement/3-to-pounds.js | 16 +++++++---- Sprint-2/4-mandatory-interpret/time-format.js | 28 ++++++++----------- 10 files changed, 70 insertions(+), 111 deletions(-) diff --git a/Sprint-2/1-key-errors/0.js b/Sprint-2/1-key-errors/0.js index 653d6f5a07..ab694faa1b 100644 --- a/Sprint-2/1-key-errors/0.js +++ b/Sprint-2/1-key-errors/0.js @@ -1,13 +1,9 @@ -// Predict and explain first... -// =============> write your prediction here - -// call the function capitalise with a string input -// interpret the error message and figure out why an error is occurring +// Predict: SyntaxError or ReferenceError because `str` is re-declared with `let` inside the function +// where it already exists as a parameter. +// Explanation: You cannot use `let` to re-declare a variable that already exists in the same scope. +// Fix: remove the `let` keyword — just reassign str. function capitalise(str) { - let str = `${str[0].toUpperCase()}${str.slice(1)}`; + str = `${str[0].toUpperCase()}${str.slice(1)}`; return str; } - -// =============> write your explanation here -// =============> write your new code here diff --git a/Sprint-2/1-key-errors/1.js b/Sprint-2/1-key-errors/1.js index f2d56151f4..ec47cab7a4 100644 --- a/Sprint-2/1-key-errors/1.js +++ b/Sprint-2/1-key-errors/1.js @@ -1,20 +1,14 @@ -// Predict and explain first... - -// Why will an error occur when this program runs? -// =============> write your prediction here - -// Try playing computer with the example to work out what is going on +// Prediction: Two errors — (1) re-declaring decimalNumber with const inside the function +// when it is already a parameter shadows and causes a SyntaxError. +// (2) console.log(decimalNumber) outside the function will throw ReferenceError because +// decimalNumber is not defined in the outer scope. +// Explanation: The parameter decimalNumber already exists; const re-declaration is illegal. +// Also, decimalNumber is not accessible outside the function. +// Fix: remove the const re-declaration inside the function, and pass a value to the function. function convertToPercentage(decimalNumber) { - const decimalNumber = 0.5; const percentage = `${decimalNumber * 100}%`; - return percentage; } -console.log(decimalNumber); - -// =============> write your explanation here - -// Finally, correct the code to fix the problem -// =============> write your new code here +console.log(convertToPercentage(0.5)); diff --git a/Sprint-2/1-key-errors/2.js b/Sprint-2/1-key-errors/2.js index aad57f7cfe..f1691d09f2 100644 --- a/Sprint-2/1-key-errors/2.js +++ b/Sprint-2/1-key-errors/2.js @@ -1,20 +1,9 @@ -// Predict and explain first BEFORE you run any code... +// Prediction: SyntaxError — a function parameter must be a valid identifier, not a literal number. +// Error: SyntaxError: Unexpected number +// Explanation: `3` is a number literal and cannot be used as a parameter name. +// Fix: use a named parameter like `num`. -// this function should square any number but instead we're going to get an error - -// =============> write your prediction of the error here - -function square(3) { - return num * num; +function square(num) { + return num * num; } - -// =============> write the error message here - -// =============> explain this error message here - -// Finally, correct the code to fix the problem - -// =============> write your new code here - - diff --git a/Sprint-2/2-mandatory-debug/0.js b/Sprint-2/2-mandatory-debug/0.js index b27511b417..f1b522c21f 100644 --- a/Sprint-2/2-mandatory-debug/0.js +++ b/Sprint-2/2-mandatory-debug/0.js @@ -1,14 +1,11 @@ -// Predict and explain first... - -// =============> write your prediction here +// Prediction: The template literal will print "undefined" for the function result because +// multiply uses console.log internally but does not return a value (returns undefined). +// Explanation: Functions without a return statement return undefined. +// The outer console.log then interpolates undefined into the string. +// Fix: replace console.log inside multiply with a return statement. function multiply(a, b) { - console.log(a * b); + return a * b; } console.log(`The result of multiplying 10 and 32 is ${multiply(10, 32)}`); - -// =============> write your explanation here - -// Finally, correct the code to fix the problem -// =============> write your new code here diff --git a/Sprint-2/2-mandatory-debug/1.js b/Sprint-2/2-mandatory-debug/1.js index 37cedfbcfd..3e7c689a10 100644 --- a/Sprint-2/2-mandatory-debug/1.js +++ b/Sprint-2/2-mandatory-debug/1.js @@ -1,13 +1,11 @@ -// Predict and explain first... -// =============> write your prediction here +// Prediction: The sum will print "undefined" because return; exits the function +// immediately without a value, so a + b is never evaluated. +// Explanation: return; with no expression returns undefined. The a + b on the next +// line is unreachable dead code. +// Fix: put a + b on the same line as return. function sum(a, b) { - return; - a + b; + return a + b; } console.log(`The sum of 10 and 32 is ${sum(10, 32)}`); - -// =============> write your explanation here -// Finally, correct the code to fix the problem -// =============> write your new code here diff --git a/Sprint-2/2-mandatory-debug/2.js b/Sprint-2/2-mandatory-debug/2.js index 57d3f5dc35..90ad3ac10b 100644 --- a/Sprint-2/2-mandatory-debug/2.js +++ b/Sprint-2/2-mandatory-debug/2.js @@ -1,24 +1,17 @@ -// Predict and explain first... +// Prediction: All three logs will print "3" (the last digit of the module-level const num = 103) +// because getLastDigit ignores its parameter and always uses the outer num. +// Output: +// The last digit of 42 is 3 +// The last digit of 105 is 3 +// The last digit of 806 is 3 +// Explanation: The function has no parameter, so any argument passed in is discarded. +// It always reads the outer `num` variable which is 103. +// Fix: add a parameter to getLastDigit and use it. -// Predict the output of the following code: -// =============> Write your prediction here - -const num = 103; - -function getLastDigit() { +function getLastDigit(num) { return num.toString().slice(-1); } console.log(`The last digit of 42 is ${getLastDigit(42)}`); console.log(`The last digit of 105 is ${getLastDigit(105)}`); console.log(`The last digit of 806 is ${getLastDigit(806)}`); - -// Now run the code and compare the output to your prediction -// =============> write the output here -// Explain why the output is the way it is -// =============> write your explanation here -// Finally, correct the code to fix the problem -// =============> write your new code here - -// This program should tell the user the last digit of each number. -// Explain why getLastDigit is not working properly - correct the problem diff --git a/Sprint-2/3-mandatory-implement/1-bmi.js b/Sprint-2/3-mandatory-implement/1-bmi.js index 17b1cbde1b..305dad6a11 100644 --- a/Sprint-2/3-mandatory-implement/1-bmi.js +++ b/Sprint-2/3-mandatory-implement/1-bmi.js @@ -15,5 +15,5 @@ // It should return their Body Mass Index to 1 decimal place function calculateBMI(weight, height) { - // return the BMI of someone based off their weight and height + return Number((weight / (height * height)).toFixed(1)); } \ No newline at end of file diff --git a/Sprint-2/3-mandatory-implement/2-cases.js b/Sprint-2/3-mandatory-implement/2-cases.js index 5b0ef77ad9..ca893c05f1 100644 --- a/Sprint-2/3-mandatory-implement/2-cases.js +++ b/Sprint-2/3-mandatory-implement/2-cases.js @@ -1,16 +1,6 @@ -// A set of words can be grouped together in different cases. +function toUpperSnakeCase(str) { + return str.replaceAll(" ", "_").toUpperCase(); +} -// For example, "hello there" in snake case would be written "hello_there" -// UPPER_SNAKE_CASE means taking a string and writing it in all caps with underscores instead of spaces. - -// Implement a function that: - -// Given a string input like "hello there" -// When we call this function with the input string -// it returns the string in UPPER_SNAKE_CASE, so "HELLO_THERE" - -// Another example: "lord of the rings" should be "LORD_OF_THE_RINGS" - -// You will need to come up with an appropriate name for the function -// Use the MDN string documentation to help you find a solution -// This might help https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase +console.log(toUpperSnakeCase("hello there")); // "HELLO_THERE" +console.log(toUpperSnakeCase("lord of the rings")); // "LORD_OF_THE_RINGS" diff --git a/Sprint-2/3-mandatory-implement/3-to-pounds.js b/Sprint-2/3-mandatory-implement/3-to-pounds.js index 6265a1a703..d58031cd52 100644 --- a/Sprint-2/3-mandatory-implement/3-to-pounds.js +++ b/Sprint-2/3-mandatory-implement/3-to-pounds.js @@ -1,6 +1,12 @@ -// In Sprint-1, there is a program written in interpret/to-pounds.js +function toPounds(penceString) { + const withoutP = penceString.substring(0, penceString.length - 1); + const padded = withoutP.padStart(3, "0"); + const pounds = padded.substring(0, padded.length - 2); + const pence = padded.substring(padded.length - 2).padEnd(2, "0"); + return `£${pounds}.${pence}`; +} -// You will need to take this code and turn it into a reusable block of code. -// You will need to declare a function called toPounds with an appropriately named parameter. - -// You should call this function a number of times to check it works for different inputs +console.log(toPounds("399p")); // £3.99 +console.log(toPounds("9p")); // £0.09 +console.log(toPounds("50p")); // £0.50 +console.log(toPounds("1000p")); // £10.00 diff --git a/Sprint-2/4-mandatory-interpret/time-format.js b/Sprint-2/4-mandatory-interpret/time-format.js index 17127bc01e..aaf374025d 100644 --- a/Sprint-2/4-mandatory-interpret/time-format.js +++ b/Sprint-2/4-mandatory-interpret/time-format.js @@ -15,24 +15,20 @@ function formatTimeDisplay(seconds) { return `${pad(totalHours)}:${pad(remainingMinutes)}:${pad(remainingSeconds)}`; } -// You will need to play computer with this example - use the Python Visualiser https://pythontutor.com/visualize.html#mode=edit -// to help you answer these questions +// a) pad is called 3 times per call to formatTimeDisplay (once for hours, minutes, seconds). -// Questions +// Call formatTimeDisplay(61): +// remainingSeconds = 61 % 60 = 1 +// totalMinutes = (61 - 1) / 60 = 1 +// remainingMinutes = 1 % 60 = 1 +// totalHours = (1 - 1) / 60 = 0 +// pad is called with: totalHours=0, remainingMinutes=1, remainingSeconds=1 -// a) When formatTimeDisplay is called how many times will pad be called? -// =============> write your answer here +// b) num = 0 (totalHours) when pad is called for the first time. -// Call formatTimeDisplay with an input of 61, now answer the following: +// c) pad(0): "0".length < 2, so prepend "0" -> "00". Return value: "00" -// b) What is the value assigned to num when pad is called for the first time? -// =============> write your answer here +// d) num = 1 (remainingSeconds) when pad is called for the last time. +// It is the last argument passed in the template literal. -// c) What is the return value of pad is called for the first time? -// =============> write your answer here - -// d) What is the value assigned to num when pad is called for the last time in this program? Explain your answer -// =============> write your answer here - -// e) What is the return value of pad when it is called for the last time in this program? Explain your answer -// =============> write your answer here +// e) pad(1): "1".length < 2, so prepend "0" -> "01". Return value: "01" From dd09c044e1ab0e139e6daa20647e6bb20e74c5e6 Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Mon, 20 Jul 2026 10:05:57 +0100 Subject: [PATCH 02/16] Restore original questions alongside answers in Sprint-2 files; fix capitalise to use new const instead of reassigning parameter --- Sprint-2/1-key-errors/0.js | 26 +++++++++++++---- Sprint-2/1-key-errors/1.js | 22 +++++++++++--- Sprint-2/1-key-errors/2.js | 14 +++++++-- Sprint-2/2-mandatory-debug/0.js | 10 ++++++- Sprint-2/2-mandatory-debug/1.js | 13 +++++++-- Sprint-2/2-mandatory-debug/2.js | 18 ++++++++++-- Sprint-2/4-mandatory-interpret/time-format.js | 29 +++++++++++-------- 7 files changed, 102 insertions(+), 30 deletions(-) diff --git a/Sprint-2/1-key-errors/0.js b/Sprint-2/1-key-errors/0.js index ab694faa1b..d7bc3f3e53 100644 --- a/Sprint-2/1-key-errors/0.js +++ b/Sprint-2/1-key-errors/0.js @@ -1,9 +1,23 @@ -// Predict: SyntaxError or ReferenceError because `str` is re-declared with `let` inside the function -// where it already exists as a parameter. -// Explanation: You cannot use `let` to re-declare a variable that already exists in the same scope. -// Fix: remove the `let` keyword — just reassign str. +// Predict and explain first... +// Prediction: SyntaxError because `str` is re-declared with `let` inside the function +// where it already exists as a parameter. You cannot use `let` to re-declare a variable +// that already exists in the same scope. + +// call the function capitalise with a string input +// interpret the error message and figure out why an error is occurring + +// Original broken code: +// function capitalise(str) { +// let str = `${str[0].toUpperCase()}${str.slice(1)}`; +// return str; +// } + +// Explanation: The parameter `str` already exists in the function scope. +// Using `let str` tries to re-declare it, which is a SyntaxError. +// Instead of reassigning `str` (which represents the original input), +// we use a new const `capitalisedStr` to make it clear this is a different value. function capitalise(str) { - str = `${str[0].toUpperCase()}${str.slice(1)}`; - return str; + const capitalisedStr = `${str[0].toUpperCase()}${str.slice(1)}`; + return capitalisedStr; } diff --git a/Sprint-2/1-key-errors/1.js b/Sprint-2/1-key-errors/1.js index ec47cab7a4..1f4cabacba 100644 --- a/Sprint-2/1-key-errors/1.js +++ b/Sprint-2/1-key-errors/1.js @@ -1,10 +1,24 @@ +// Predict and explain first... + +// Why will an error occur when this program runs? // Prediction: Two errors — (1) re-declaring decimalNumber with const inside the function -// when it is already a parameter shadows and causes a SyntaxError. +// when it is already a parameter causes a SyntaxError. // (2) console.log(decimalNumber) outside the function will throw ReferenceError because // decimalNumber is not defined in the outer scope. -// Explanation: The parameter decimalNumber already exists; const re-declaration is illegal. -// Also, decimalNumber is not accessible outside the function. -// Fix: remove the const re-declaration inside the function, and pass a value to the function. + +// Try playing computer with the example to work out what is going on + +// Original broken code: +// function convertToPercentage(decimalNumber) { +// const decimalNumber = 0.5; +// const percentage = `${decimalNumber * 100}%`; +// return percentage; +// } +// console.log(decimalNumber); + +// Explanation: The parameter decimalNumber already exists in the function scope; +// const re-declaration is illegal. Also, decimalNumber is not accessible outside the function. +// Fix: remove the const re-declaration inside the function, and pass a value when calling it. function convertToPercentage(decimalNumber) { const percentage = `${decimalNumber * 100}%`; diff --git a/Sprint-2/1-key-errors/2.js b/Sprint-2/1-key-errors/2.js index f1691d09f2..fd9e0e3310 100644 --- a/Sprint-2/1-key-errors/2.js +++ b/Sprint-2/1-key-errors/2.js @@ -1,7 +1,17 @@ +// Predict and explain first BEFORE you run any code... -// Prediction: SyntaxError — a function parameter must be a valid identifier, not a literal number. -// Error: SyntaxError: Unexpected number +// this function should square any number but instead we're going to get an error + +// Prediction: SyntaxError — a function parameter must be a valid identifier, not a number literal. + +// Original broken code: +// function square(3) { +// return num * num; +// } + +// Error message: SyntaxError: Unexpected number // Explanation: `3` is a number literal and cannot be used as a parameter name. +// Parameters must be valid identifiers (variable names). // Fix: use a named parameter like `num`. function square(num) { diff --git a/Sprint-2/2-mandatory-debug/0.js b/Sprint-2/2-mandatory-debug/0.js index f1b522c21f..83cea4380d 100644 --- a/Sprint-2/2-mandatory-debug/0.js +++ b/Sprint-2/2-mandatory-debug/0.js @@ -1,7 +1,15 @@ +// Predict and explain first... // Prediction: The template literal will print "undefined" for the function result because // multiply uses console.log internally but does not return a value (returns undefined). + +// Original broken code: +// function multiply(a, b) { +// console.log(a * b); +// } +// console.log(`The result of multiplying 10 and 32 is ${multiply(10, 32)}`); + // Explanation: Functions without a return statement return undefined. -// The outer console.log then interpolates undefined into the string. +// The outer console.log then interpolates undefined into the string. // Fix: replace console.log inside multiply with a return statement. function multiply(a, b) { diff --git a/Sprint-2/2-mandatory-debug/1.js b/Sprint-2/2-mandatory-debug/1.js index 3e7c689a10..ff7d5fb412 100644 --- a/Sprint-2/2-mandatory-debug/1.js +++ b/Sprint-2/2-mandatory-debug/1.js @@ -1,7 +1,16 @@ +// Predict and explain first... // Prediction: The sum will print "undefined" because return; exits the function // immediately without a value, so a + b is never evaluated. -// Explanation: return; with no expression returns undefined. The a + b on the next -// line is unreachable dead code. + +// Original broken code: +// function sum(a, b) { +// return; +// a + b; +// } +// console.log(`The sum of 10 and 32 is ${sum(10, 32)}`); + +// Explanation: return; with no expression returns undefined. +// The a + b on the next line is unreachable dead code and is never executed. // Fix: put a + b on the same line as return. function sum(a, b) { diff --git a/Sprint-2/2-mandatory-debug/2.js b/Sprint-2/2-mandatory-debug/2.js index 90ad3ac10b..c1d8633ea9 100644 --- a/Sprint-2/2-mandatory-debug/2.js +++ b/Sprint-2/2-mandatory-debug/2.js @@ -1,12 +1,24 @@ +// Predict and explain first... + +// Predict the output of the following code: // Prediction: All three logs will print "3" (the last digit of the module-level const num = 103) // because getLastDigit ignores its parameter and always uses the outer num. -// Output: +// Expected output: // The last digit of 42 is 3 // The last digit of 105 is 3 // The last digit of 806 is 3 + +// Original broken code: +// const num = 103; +// function getLastDigit() { +// return num.toString().slice(-1); +// } + +// Actual output matched prediction — all printed "3". + // Explanation: The function has no parameter, so any argument passed in is discarded. -// It always reads the outer `num` variable which is 103. -// Fix: add a parameter to getLastDigit and use it. +// It always reads the outer `num` variable which is 103. +// Fix: add a parameter to getLastDigit and use it instead of the outer variable. function getLastDigit(num) { return num.toString().slice(-1); diff --git a/Sprint-2/4-mandatory-interpret/time-format.js b/Sprint-2/4-mandatory-interpret/time-format.js index aaf374025d..0d53b353aa 100644 --- a/Sprint-2/4-mandatory-interpret/time-format.js +++ b/Sprint-2/4-mandatory-interpret/time-format.js @@ -15,20 +15,25 @@ function formatTimeDisplay(seconds) { return `${pad(totalHours)}:${pad(remainingMinutes)}:${pad(remainingSeconds)}`; } -// a) pad is called 3 times per call to formatTimeDisplay (once for hours, minutes, seconds). +// You will need to play computer with this example - use the Python Visualiser https://pythontutor.com/visualize.html#mode=edit +// to help you answer these questions -// Call formatTimeDisplay(61): -// remainingSeconds = 61 % 60 = 1 -// totalMinutes = (61 - 1) / 60 = 1 -// remainingMinutes = 1 % 60 = 1 -// totalHours = (1 - 1) / 60 = 0 -// pad is called with: totalHours=0, remainingMinutes=1, remainingSeconds=1 +// Questions -// b) num = 0 (totalHours) when pad is called for the first time. +// a) When formatTimeDisplay is called how many times will pad be called? +// =============> pad is called 3 times per call to formatTimeDisplay (once for totalHours, once for remainingMinutes, once for remainingSeconds). -// c) pad(0): "0".length < 2, so prepend "0" -> "00". Return value: "00" +// Call formatTimeDisplay with an input of 61, now answer the following: +// (remainingSeconds = 1, totalMinutes = 1, remainingMinutes = 1, totalHours = 0) -// d) num = 1 (remainingSeconds) when pad is called for the last time. -// It is the last argument passed in the template literal. +// b) What is the value assigned to num when pad is called for the first time? +// =============> num = 0 (totalHours), because totalHours is the first argument in the template literal. -// e) pad(1): "1".length < 2, so prepend "0" -> "01". Return value: "01" +// c) What is the return value of pad when it is called for the first time? +// =============> "00" — "0".length < 2, so "0" is prepended, giving "00". + +// d) What is the value assigned to num when pad is called for the last time in this program? Explain your answer +// =============> num = 1 (remainingSeconds). It is the last argument passed in the template literal, so pad is called with it last. + +// e) What is the return value of pad when it is called for the last time in this program? Explain your answer +// =============> "01" — "1".length < 2, so "0" is prepended, giving "01". From 7e816c106af48aa65b35930af6e32d63747e58eb Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Mon, 20 Jul 2026 10:24:23 +0100 Subject: [PATCH 03/16] Implement getAngleType function with all angle cases --- .../implement/1-get-angle-type.js | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/Sprint-3/1-implement-and-rewrite-tests/implement/1-get-angle-type.js b/Sprint-3/1-implement-and-rewrite-tests/implement/1-get-angle-type.js index 9e05a871e2..c0b486e606 100644 --- a/Sprint-3/1-implement-and-rewrite-tests/implement/1-get-angle-type.js +++ b/Sprint-3/1-implement-and-rewrite-tests/implement/1-get-angle-type.js @@ -15,7 +15,12 @@ // execute the code to ensure all tests pass. function getAngleType(angle) { - // TODO: Implement this function + if (angle <= 0 || angle >= 360) return "Invalid angle"; + if (angle === 90) return "Right angle"; + if (angle === 180) return "Straight angle"; + if (angle < 90) return "Acute angle"; + if (angle < 180) return "Obtuse angle"; + return "Reflex angle"; } // The line below allows us to load the getAngleType function into tests in other files. @@ -31,7 +36,25 @@ function assertEquals(actualOutput, targetOutput) { ); } -// TODO: Write tests to cover all cases, including boundary and invalid cases. -// Example: Identify Right Angles +// Tests covering all cases const right = getAngleType(90); assertEquals(right, "Right angle"); + +assertEquals(getAngleType(45), "Acute angle"); +assertEquals(getAngleType(1), "Acute angle"); +assertEquals(getAngleType(89), "Acute angle"); + +assertEquals(getAngleType(91), "Obtuse angle"); +assertEquals(getAngleType(135), "Obtuse angle"); +assertEquals(getAngleType(179), "Obtuse angle"); + +assertEquals(getAngleType(180), "Straight angle"); + +assertEquals(getAngleType(181), "Reflex angle"); +assertEquals(getAngleType(270), "Reflex angle"); +assertEquals(getAngleType(359), "Reflex angle"); + +assertEquals(getAngleType(0), "Invalid angle"); +assertEquals(getAngleType(360), "Invalid angle"); +assertEquals(getAngleType(-10), "Invalid angle"); +assertEquals(getAngleType(400), "Invalid angle"); From b719055f676ba8e124e826ba3e1f350afca91800 Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Mon, 20 Jul 2026 10:24:38 +0100 Subject: [PATCH 04/16] Implement isProperFraction function with all cases --- .../implement/2-is-proper-fraction.js | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/Sprint-3/1-implement-and-rewrite-tests/implement/2-is-proper-fraction.js b/Sprint-3/1-implement-and-rewrite-tests/implement/2-is-proper-fraction.js index 970cb9b641..9e032776da 100644 --- a/Sprint-3/1-implement-and-rewrite-tests/implement/2-is-proper-fraction.js +++ b/Sprint-3/1-implement-and-rewrite-tests/implement/2-is-proper-fraction.js @@ -4,21 +4,16 @@ // Assumption: The parameters are valid numbers (not NaN or Infinity). -// Note: If you are unfamiliar with proper fractions, please look up its mathematical definition. - -// Acceptance criteria: -// After you have implemented the function, write tests to cover all the cases, and -// execute the code to ensure all tests pass. +// Note: A proper fraction is one where the absolute value of the numerator +// is strictly less than the absolute value of the denominator. function isProperFraction(numerator, denominator) { - // TODO: Implement this function + if (denominator === 0) return false; + return Math.abs(numerator) < Math.abs(denominator); } -// The line below allows us to load the isProperFraction function into tests in other files. -// This will be useful in the "rewrite tests with jest" step. module.exports = isProperFraction; -// Here's our helper again function assertEquals(actualOutput, targetOutput) { console.assert( actualOutput === targetOutput, @@ -26,8 +21,10 @@ function assertEquals(actualOutput, targetOutput) { ); } -// TODO: Write tests to cover all cases. -// What combinations of numerators and denominators should you test? - -// Example: 1/2 is a proper fraction +// Tests covering all cases assertEquals(isProperFraction(1, 2), true); +assertEquals(isProperFraction(2, 1), false); // numerator > denominator +assertEquals(isProperFraction(3, 3), false); // equal -> not proper +assertEquals(isProperFraction(-1, 2), true); // negative numerator +assertEquals(isProperFraction(1, -2), true); // negative denominator +assertEquals(isProperFraction(1, 0), false); // zero denominator From e3b82d769a2ad17cf2a9410e178c5322376f5c05 Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Mon, 20 Jul 2026 10:24:45 +0100 Subject: [PATCH 05/16] Implement getCardValue function with all rank and suit cases --- .../implement/3-get-card-value.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Sprint-3/1-implement-and-rewrite-tests/implement/3-get-card-value.js b/Sprint-3/1-implement-and-rewrite-tests/implement/3-get-card-value.js index ff5c532e1d..1681281f8c 100644 --- a/Sprint-3/1-implement-and-rewrite-tests/implement/3-get-card-value.js +++ b/Sprint-3/1-implement-and-rewrite-tests/implement/3-get-card-value.js @@ -22,7 +22,17 @@ // execute the code to ensure all tests pass. function getCardValue(card) { - // TODO: Implement this function + const suit = card.slice(-1); + const rank = card.slice(0, -1); + const validSuits = ["\u2660", "\u2665", "\u2666", "\u2663"]; + const validRanks = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]; + + if (!validSuits.includes(suit) || !validRanks.includes(rank)) { + throw new Error(`Invalid card: ${card}`); + } + if (rank === "A") return 11; + if (["J", "Q", "K"].includes(rank)) return 10; + return Number(rank); } // The line below allows us to load the getCardValue function into tests in other files. @@ -40,6 +50,13 @@ function assertEquals(actualOutput, targetOutput) { // TODO: Write tests to cover all outcomes, including throwing errors for invalid cards. // Examples: assertEquals(getCardValue("9♠"), 9); +assertEquals(getCardValue("A♠"), 11); +assertEquals(getCardValue("A♥"), 11); +assertEquals(getCardValue("2♦"), 2); +assertEquals(getCardValue("10♥"), 10); +assertEquals(getCardValue("J♣"), 10); +assertEquals(getCardValue("Q♦"), 10); +assertEquals(getCardValue("K♦"), 10); // Handling invalid cards try { From 80f67baf73e8a5752cb7e1b70316e233e7b53d37 Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Mon, 20 Jul 2026 10:24:53 +0100 Subject: [PATCH 06/16] Add Jest tests for getAngleType covering all angle types and invalid cases --- .../1-get-angle-type.test.js | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/Sprint-3/1-implement-and-rewrite-tests/rewrite-tests-with-jest/1-get-angle-type.test.js b/Sprint-3/1-implement-and-rewrite-tests/rewrite-tests-with-jest/1-get-angle-type.test.js index d777f348d3..d76724ed39 100644 --- a/Sprint-3/1-implement-and-rewrite-tests/rewrite-tests-with-jest/1-get-angle-type.test.js +++ b/Sprint-3/1-implement-and-rewrite-tests/rewrite-tests-with-jest/1-get-angle-type.test.js @@ -1,20 +1,34 @@ -// This statement loads the getAngleType function you wrote in the implement directory. -// We will use the same function, but write tests for it using Jest in this file. const getAngleType = require("../implement/1-get-angle-type"); -// TODO: Write tests in Jest syntax to cover all cases/outcomes, -// including boundary and invalid cases. - -// Case 1: Acute angles test(`should return "Acute angle" when (0 < angle < 90)`, () => { - // Test various acute angles, including boundary cases expect(getAngleType(1)).toEqual("Acute angle"); expect(getAngleType(45)).toEqual("Acute angle"); expect(getAngleType(89)).toEqual("Acute angle"); }); -// Case 2: Right angle -// Case 3: Obtuse angles -// Case 4: Straight angle -// Case 5: Reflex angles -// Case 6: Invalid angles +test(`should return "Right angle" for exactly 90`, () => { + expect(getAngleType(90)).toEqual("Right angle"); +}); + +test(`should return "Obtuse angle" when (90 < angle < 180)`, () => { + expect(getAngleType(91)).toEqual("Obtuse angle"); + expect(getAngleType(135)).toEqual("Obtuse angle"); + expect(getAngleType(179)).toEqual("Obtuse angle"); +}); + +test(`should return "Straight angle" for exactly 180`, () => { + expect(getAngleType(180)).toEqual("Straight angle"); +}); + +test(`should return "Reflex angle" when (180 < angle < 360)`, () => { + expect(getAngleType(181)).toEqual("Reflex angle"); + expect(getAngleType(270)).toEqual("Reflex angle"); + expect(getAngleType(359)).toEqual("Reflex angle"); +}); + +test(`should return "Invalid angle" for angles outside 0-360`, () => { + expect(getAngleType(0)).toEqual("Invalid angle"); + expect(getAngleType(360)).toEqual("Invalid angle"); + expect(getAngleType(-10)).toEqual("Invalid angle"); + expect(getAngleType(400)).toEqual("Invalid angle"); +}); From 8f5030b11fe611d5a172bd010e2cda5f092ce347 Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Mon, 20 Jul 2026 10:25:01 +0100 Subject: [PATCH 07/16] Add Jest tests for isProperFraction covering positive, negative and zero cases --- .../2-is-proper-fraction.test.js | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/Sprint-3/1-implement-and-rewrite-tests/rewrite-tests-with-jest/2-is-proper-fraction.test.js b/Sprint-3/1-implement-and-rewrite-tests/rewrite-tests-with-jest/2-is-proper-fraction.test.js index 7f087b2ba1..e1ac586479 100644 --- a/Sprint-3/1-implement-and-rewrite-tests/rewrite-tests-with-jest/2-is-proper-fraction.test.js +++ b/Sprint-3/1-implement-and-rewrite-tests/rewrite-tests-with-jest/2-is-proper-fraction.test.js @@ -1,10 +1,25 @@ -// This statement loads the isProperFraction function you wrote in the implement directory. -// We will use the same function, but write tests for it using Jest in this file. const isProperFraction = require("../implement/2-is-proper-fraction"); -// TODO: Write tests in Jest syntax to cover all combinations of positives, negatives, zeros, and other categories. - -// Special case: numerator is zero test(`should return false when denominator is zero`, () => { expect(isProperFraction(1, 0)).toEqual(false); }); + +test(`should return true when numerator < denominator (positive)`, () => { + expect(isProperFraction(1, 2)).toEqual(true); + expect(isProperFraction(3, 7)).toEqual(true); +}); + +test(`should return false when numerator >= denominator (positive)`, () => { + expect(isProperFraction(2, 1)).toEqual(false); + expect(isProperFraction(3, 3)).toEqual(false); +}); + +test(`should handle negative numerator`, () => { + expect(isProperFraction(-1, 2)).toEqual(true); + expect(isProperFraction(-3, 2)).toEqual(false); +}); + +test(`should handle negative denominator`, () => { + expect(isProperFraction(1, -2)).toEqual(true); + expect(isProperFraction(3, -2)).toEqual(false); +}); From 8f3546dab7aef08b29014c3c2ac101b8beb59212 Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Mon, 20 Jul 2026 10:25:07 +0100 Subject: [PATCH 08/16] Add Jest tests for getCardValue covering ace, number cards, face cards and invalid cards --- .../3-get-card-value.test.js | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/Sprint-3/1-implement-and-rewrite-tests/rewrite-tests-with-jest/3-get-card-value.test.js b/Sprint-3/1-implement-and-rewrite-tests/rewrite-tests-with-jest/3-get-card-value.test.js index cf7f9dae2e..09f65e875b 100644 --- a/Sprint-3/1-implement-and-rewrite-tests/rewrite-tests-with-jest/3-get-card-value.test.js +++ b/Sprint-3/1-implement-and-rewrite-tests/rewrite-tests-with-jest/3-get-card-value.test.js @@ -1,20 +1,24 @@ -// This statement loads the getCardValue function you wrote in the implement directory. -// We will use the same function, but write tests for it using Jest in this file. const getCardValue = require("../implement/3-get-card-value"); -// TODO: Write tests in Jest syntax to cover all possible outcomes. - -// Case 1: Ace (A) test(`Should return 11 when given an ace card`, () => { expect(getCardValue("A♠")).toEqual(11); + expect(getCardValue("A♥")).toEqual(11); }); -// Suggestion: Group the remaining test data into these categories: -// Number Cards (2-10) -// Face Cards (J, Q, K) -// Invalid Cards +test(`Should return numeric value for number cards 2-10`, () => { + expect(getCardValue("2♥")).toEqual(2); + expect(getCardValue("5♦")).toEqual(5); + expect(getCardValue("10♥")).toEqual(10); +}); -// To learn how to test whether a function throws an error as expected in Jest, -// please refer to the Jest documentation: -// https://jestjs.io/docs/expect#tothrowerror +test(`Should return 10 for face cards J, Q, K`, () => { + expect(getCardValue("J♣")).toEqual(10); + expect(getCardValue("Q♦")).toEqual(10); + expect(getCardValue("K♠")).toEqual(10); +}); +test(`Should throw an error for invalid cards`, () => { + expect(() => getCardValue("invalid")).toThrow(); + expect(() => getCardValue("1♠")).toThrow(); + expect(() => getCardValue("")).toThrow(); +}); From 6e742e7462204438a800aab60cf524f6d1317af3 Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Mon, 20 Jul 2026 10:25:15 +0100 Subject: [PATCH 09/16] Implement countChar and add tests for multiple and zero occurrences --- Sprint-3/2-practice-tdd/count.js | 6 +++++- Sprint-3/2-practice-tdd/count.test.js | 8 +++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Sprint-3/2-practice-tdd/count.js b/Sprint-3/2-practice-tdd/count.js index 95b6ebb7d4..2e199b488a 100644 --- a/Sprint-3/2-practice-tdd/count.js +++ b/Sprint-3/2-practice-tdd/count.js @@ -1,5 +1,9 @@ function countChar(stringOfCharacters, findCharacter) { - return 5 + let count = 0; + for (const char of stringOfCharacters) { + if (char === findCharacter) count++; + } + return count; } module.exports = countChar; diff --git a/Sprint-3/2-practice-tdd/count.test.js b/Sprint-3/2-practice-tdd/count.test.js index 179ea0ddf7..df8774cb25 100644 --- a/Sprint-3/2-practice-tdd/count.test.js +++ b/Sprint-3/2-practice-tdd/count.test.js @@ -17,8 +17,6 @@ test("should count multiple occurrences of a character", () => { expect(count).toEqual(5); }); -// Scenario: No Occurrences -// Given the input string `str`, -// And a character `char` that does not exist within `str`. -// When the function is called with these inputs, -// Then it should return 0, indicating that no occurrences of `char` were found. +test("should return 0 when character does not occur", () => { + expect(countChar("hello", "z")).toEqual(0); +}); From 8f83aef51389e093a65ae0ded59bfb283eab698c Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Mon, 20 Jul 2026 10:25:22 +0100 Subject: [PATCH 10/16] Implement repeatStr and add tests for repeat, count 1, count 0 and negative count --- Sprint-3/2-practice-tdd/repeat-str.js | 9 +++++---- Sprint-3/2-practice-tdd/repeat-str.test.js | 21 +++++++++------------ 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/Sprint-3/2-practice-tdd/repeat-str.js b/Sprint-3/2-practice-tdd/repeat-str.js index 2af0a2cea7..3929b97c43 100644 --- a/Sprint-3/2-practice-tdd/repeat-str.js +++ b/Sprint-3/2-practice-tdd/repeat-str.js @@ -1,7 +1,8 @@ -function repeatStr() { - // Your implementation of this function must *not* call String.prototype.repeat (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat). - // The goal is to re-implement that function, not to use it. - return "hellohellohello"; +function repeatStr(str, count) { + if (count < 0) throw new Error("count must be non-negative"); + let result = ""; + for (let i = 0; i < count; i++) result += str; + return result; } module.exports = repeatStr; diff --git a/Sprint-3/2-practice-tdd/repeat-str.test.js b/Sprint-3/2-practice-tdd/repeat-str.test.js index a3fc1196c4..e15f03cc3a 100644 --- a/Sprint-3/2-practice-tdd/repeat-str.test.js +++ b/Sprint-3/2-practice-tdd/repeat-str.test.js @@ -16,17 +16,14 @@ test("should repeat the string count times", () => { expect(repeatedStr).toEqual("hellohellohello"); }); -// Case: handle count of 1: -// Given a target string `str` and a `count` equal to 1, -// When the repeatStr function is called with these inputs, -// Then it should return the original `str` without repetition. +test("should return the original string when count is 1", () => { + expect(repeatStr("hello", 1)).toEqual("hello"); +}); -// Case: Handle count of 0: -// Given a target string `str` and a `count` equal to 0, -// When the repeatStr function is called with these inputs, -// Then it should return an empty string. +test("should return empty string when count is 0", () => { + expect(repeatStr("hello", 0)).toEqual(""); +}); -// Case: Handle negative count: -// Given a target string `str` and a negative integer `count`, -// When the repeatStr function is called with these inputs, -// Then it should throw an error, as negative counts are not valid. +test("should throw an error when count is negative", () => { + expect(() => repeatStr("hello", -1)).toThrow(); +}); From fe2c450ad98d6a498572c28f011d871e0d84e12d Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Mon, 20 Jul 2026 10:25:28 +0100 Subject: [PATCH 11/16] Implement getOrdinalNumber and add tests for st, nd, rd, th including 11/12/13 edge cases --- Sprint-3/2-practice-tdd/get-ordinal-number.js | 8 +++++++- .../2-practice-tdd/get-ordinal-number.test.js | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Sprint-3/2-practice-tdd/get-ordinal-number.js b/Sprint-3/2-practice-tdd/get-ordinal-number.js index f95d71db13..a7f38ad328 100644 --- a/Sprint-3/2-practice-tdd/get-ordinal-number.js +++ b/Sprint-3/2-practice-tdd/get-ordinal-number.js @@ -1,5 +1,11 @@ function getOrdinalNumber(num) { - return "1st"; + const lastTwo = num % 100; + const lastOne = num % 10; + if (lastTwo >= 11 && lastTwo <= 13) return `${num}th`; + if (lastOne === 1) return `${num}st`; + if (lastOne === 2) return `${num}nd`; + if (lastOne === 3) return `${num}rd`; + return `${num}th`; } module.exports = getOrdinalNumber; diff --git a/Sprint-3/2-practice-tdd/get-ordinal-number.test.js b/Sprint-3/2-practice-tdd/get-ordinal-number.test.js index adfa58560f..055ffc0c7f 100644 --- a/Sprint-3/2-practice-tdd/get-ordinal-number.test.js +++ b/Sprint-3/2-practice-tdd/get-ordinal-number.test.js @@ -18,3 +18,22 @@ test("should append 'st' for numbers ending with 1, except those ending with 11" expect(getOrdinalNumber(21)).toEqual("21st"); expect(getOrdinalNumber(131)).toEqual("131st"); }); + +test("should append 'nd' for numbers ending with 2, except those ending with 12", () => { + expect(getOrdinalNumber(2)).toEqual("2nd"); + expect(getOrdinalNumber(22)).toEqual("22nd"); + expect(getOrdinalNumber(12)).toEqual("12th"); +}); + +test("should append 'rd' for numbers ending with 3, except those ending with 13", () => { + expect(getOrdinalNumber(3)).toEqual("3rd"); + expect(getOrdinalNumber(23)).toEqual("23rd"); + expect(getOrdinalNumber(13)).toEqual("13th"); +}); + +test("should append 'th' for all other numbers including 11, 12, 13", () => { + expect(getOrdinalNumber(4)).toEqual("4th"); + expect(getOrdinalNumber(11)).toEqual("11th"); + expect(getOrdinalNumber(112)).toEqual("112th"); + expect(getOrdinalNumber(20)).toEqual("20th"); +}); From 285cdb3473e8af4ef60cf371d6ff4d9a23854009 Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Mon, 20 Jul 2026 10:25:35 +0100 Subject: [PATCH 12/16] Remove dead code from exercise-1: unused variable and unreachable console.log --- Sprint-3/3-dead-code/exercise-1.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Sprint-3/3-dead-code/exercise-1.js b/Sprint-3/3-dead-code/exercise-1.js index 4d09f15fa9..d37f691a8e 100644 --- a/Sprint-3/3-dead-code/exercise-1.js +++ b/Sprint-3/3-dead-code/exercise-1.js @@ -1,13 +1,8 @@ -// Find the instances of unreachable and redundant code - remove them! -// The sayHello function should continue to work for any reasonable input it's given. - let testName = "Jerry"; const greeting = "hello"; function sayHello(greeting, name) { - const greetingStr = greeting + ", " + name + "!"; return `${greeting}, ${name}!`; - console.log(greetingStr); } testName = "Aman"; From 202642d14051244b2af8db00b31989b5704d9d1a Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Mon, 20 Jul 2026 10:25:45 +0100 Subject: [PATCH 13/16] Remove dead code from exercise-2: unused capitalisedPets array and logPets function --- Sprint-3/3-dead-code/exercise-2.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Sprint-3/3-dead-code/exercise-2.js b/Sprint-3/3-dead-code/exercise-2.js index 56d7887c4c..d027c876fd 100644 --- a/Sprint-3/3-dead-code/exercise-2.js +++ b/Sprint-3/3-dead-code/exercise-2.js @@ -2,12 +2,6 @@ // The countAndCapitalisePets function should continue to work for any reasonable input it's given, and you shouldn't modify the pets variable. const pets = ["parrot", "hamster", "horse", "dog", "hamster", "cat", "hamster"]; -const capitalisedPets = pets.map((pet) => pet.toUpperCase()); -const petsStartingWithH = pets.filter((pet) => pet[0] === "h"); - -function logPets(petsArr) { - petsArr.forEach((pet) => console.log(pet)); -} function countAndCapitalisePets(petsArr) { const petCount = {}; @@ -23,6 +17,7 @@ function countAndCapitalisePets(petsArr) { return petCount; } +const petsStartingWithH = pets.filter((pet) => pet[0] === "h"); const countedPetsStartingWithH = countAndCapitalisePets(petsStartingWithH); console.log(countedPetsStartingWithH); // { 'HAMSTER': 3, 'HORSE': 1 } <- Final console log From 85e4ce4a112759e9976aaef8c8848c0a5e673668 Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Mon, 20 Jul 2026 10:25:51 +0100 Subject: [PATCH 14/16] Answer all questions about the find while loop in find.js --- Sprint-3/4-stretch/find.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Sprint-3/4-stretch/find.js b/Sprint-3/4-stretch/find.js index c7e79a2f21..37a7b67547 100644 --- a/Sprint-3/4-stretch/find.js +++ b/Sprint-3/4-stretch/find.js @@ -19,7 +19,17 @@ console.log(find("code your future", "z")); // Use the Python Visualiser to help you play computer with this example and observe how this code is executed // Pay particular attention to the following: -// a) How the index variable updates during the call to find -// b) What is the if statement used to check -// c) Why is index++ being used? -// d) What is the condition index < str.length used for? +// a) index starts at 0 and increments by 1 on each iteration until either the target +// character is found (returns early) or index reaches str.length (loop ends). +// For find("code your future", "u"): index goes 0,1,2,3,4,5,6,7 — "u" found at index 7. +// For find("code your future", "z"): index goes 0..15, never matches, returns -1. + +// b) The if statement checks whether the character at the current index equals the +// target character `char`. If it does, the function returns that index immediately. + +// c) index++ increments the index by 1 after each iteration so the loop moves forward +// through the string one character at a time, preventing an infinite loop. + +// d) index < str.length is the loop condition. It ensures the loop only runs while +// index is within the valid range of the string. Once index equals str.length +// there are no more characters to check, so the loop stops and -1 is returned. From 4279ba8df03478aaad5f39f4e0041a0c18f5fe5b Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Mon, 20 Jul 2026 10:25:58 +0100 Subject: [PATCH 15/16] Implement passwordValidator with all 6 rules and add tests for each rule --- Sprint-3/4-stretch/password-validator.js | 13 +++- Sprint-3/4-stretch/password-validator.test.js | 62 +++++++++++-------- 2 files changed, 46 insertions(+), 29 deletions(-) diff --git a/Sprint-3/4-stretch/password-validator.js b/Sprint-3/4-stretch/password-validator.js index b55d527dba..86df9c5395 100644 --- a/Sprint-3/4-stretch/password-validator.js +++ b/Sprint-3/4-stretch/password-validator.js @@ -1,6 +1,13 @@ +const previousPasswords = ["Pass1!", "Hello1#", "Secure1$"]; + function passwordValidator(password) { - return password.length < 5 ? false : true + if (password.length < 5) return false; + if (!/[A-Z]/.test(password)) return false; + if (!/[a-z]/.test(password)) return false; + if (!/[0-9]/.test(password)) return false; + if (!/[!#$%.\*&]/.test(password)) return false; + if (previousPasswords.includes(password)) return false; + return true; } - -module.exports = passwordValidator; \ No newline at end of file +module.exports = { passwordValidator, previousPasswords }; \ No newline at end of file diff --git a/Sprint-3/4-stretch/password-validator.test.js b/Sprint-3/4-stretch/password-validator.test.js index 8fa3089d6b..dc37942e6d 100644 --- a/Sprint-3/4-stretch/password-validator.test.js +++ b/Sprint-3/4-stretch/password-validator.test.js @@ -1,26 +1,36 @@ -/* -Password Validation - -Write a program that should check if a password is valid -and returns a boolean - -To be valid, a password must: -- Have at least 5 characters. -- Have at least one English uppercase letter (A-Z) -- Have at least one English lowercase letter (a-z) -- Have at least one number (0-9) -- Have at least one of the following non-alphanumeric symbols: ("!", "#", "$", "%", ".", "*", "&") -- Must not be any previous password in the passwords array. - -You must breakdown this problem in order to solve it. Find one test case first and get that working -*/ -const isValidPassword = require("./password-validator"); -test("password has at least 5 characters", () => { - // Arrange - const password = "12345"; - // Act - const result = isValidPassword(password); - // Assert - expect(result).toEqual(true); -} -); \ No newline at end of file +const { passwordValidator, previousPasswords } = require("./password-validator"); + +test("should return false when password has fewer than 5 characters", () => { + expect(passwordValidator("Ab1!")).toEqual(false); +}); + +test("should return false when password has no uppercase letter", () => { + expect(passwordValidator("hello1!")).toEqual(false); +}); + +test("should return false when password has no lowercase letter", () => { + expect(passwordValidator("HELLO1!")).toEqual(false); +}); + +test("should return false when password has no number", () => { + expect(passwordValidator("Hello!")).toEqual(false); +}); + +test("should return false when password has no special character", () => { + expect(passwordValidator("Hello1")).toEqual(false); +}); + +test("should return false when password is a previous password", () => { + expect(passwordValidator("Pass1!")).toEqual(false); +}); + +test("should return true for a valid password meeting all criteria", () => { + expect(passwordValidator("Valid1!")).toEqual(true); + expect(passwordValidator("MyP@ss1")).toEqual(false); // @ not in allowed symbols + expect(passwordValidator("MyPass1$")).toEqual(true); +}); + +test("should return false for password with exactly 5 chars but missing a required type", () => { + expect(passwordValidator("ab1!A")).toEqual(true); // valid: 5 chars, all types present + expect(passwordValidator("ab1!!")).toEqual(false); // no uppercase +}); \ No newline at end of file From cd2697a4c2755bc8b20d16615ddc77fb74a62561 Mon Sep 17 00:00:00 2001 From: KhotKeys Date: Mon, 20 Jul 2026 10:26:04 +0100 Subject: [PATCH 16/16] Create card validator with all 4 rules and tests for valid and invalid card numbers --- Sprint-3/4-stretch/card-validator.js | 26 +++++++++++++++++ Sprint-3/4-stretch/card-validator.test.js | 35 +++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 Sprint-3/4-stretch/card-validator.js create mode 100644 Sprint-3/4-stretch/card-validator.test.js diff --git a/Sprint-3/4-stretch/card-validator.js b/Sprint-3/4-stretch/card-validator.js new file mode 100644 index 0000000000..a6fb3f2587 --- /dev/null +++ b/Sprint-3/4-stretch/card-validator.js @@ -0,0 +1,26 @@ +// Validates a credit card number string against the following rules: +// - Must be exactly 16 digits (no letters or other characters) +// - Must contain at least two different digits +// - The final digit must be even +// - The sum of all digits must be greater than 16 + +function isValidCardNumber(cardNumber) { + // Rule 1: must be exactly 16 digit characters + if (!/^\d{16}$/.test(cardNumber)) return false; + + const digits = cardNumber.split("").map(Number); + + // Rule 2: at least two different digits must be present + if (new Set(digits).size < 2) return false; + + // Rule 3: final digit must be even + if (digits[15] % 2 !== 0) return false; + + // Rule 4: sum of all digits must be greater than 16 + const sum = digits.reduce((acc, d) => acc + d, 0); + if (sum <= 16) return false; + + return true; +} + +module.exports = isValidCardNumber; diff --git a/Sprint-3/4-stretch/card-validator.test.js b/Sprint-3/4-stretch/card-validator.test.js new file mode 100644 index 0000000000..fa8cbd90f6 --- /dev/null +++ b/Sprint-3/4-stretch/card-validator.test.js @@ -0,0 +1,35 @@ +const isValidCardNumber = require("./card-validator"); + +// Valid cards +test("should return true for a valid card number", () => { + expect(isValidCardNumber("9999777788880000")).toEqual(true); + expect(isValidCardNumber("6666666666661666")).toEqual(true); +}); + +// Rule 1: must be exactly 16 digits +test("should return false when card contains non-digit characters", () => { + expect(isValidCardNumber("a92332119c011112")).toEqual(false); +}); + +test("should return false when card has fewer than 16 digits", () => { + expect(isValidCardNumber("123456789012345")).toEqual(false); +}); + +test("should return false when card has more than 16 digits", () => { + expect(isValidCardNumber("12345678901234567")).toEqual(false); +}); + +// Rule 2: at least two different digits +test("should return false when all digits are the same", () => { + expect(isValidCardNumber("4444444444444444")).toEqual(false); +}); + +// Rule 3: final digit must be even +test("should return false when final digit is odd", () => { + expect(isValidCardNumber("6666666666666661")).toEqual(false); +}); + +// Rule 4: sum of all digits must be greater than 16 +test("should return false when sum of digits is not greater than 16", () => { + expect(isValidCardNumber("1111111111111110")).toEqual(false); +});