From e3e959ba229e9d2a58370210d959cea98b3e675a Mon Sep 17 00:00:00 2001 From: SalahAlsabhi Date: Fri, 23 Oct 2026 20:12:54 +0100 Subject: [PATCH 1/5] Implement password validator with full rules and tests --- .../password-validator.js | 21 +++++++++- .../password-validator.test.js | 39 ++++++++++++++----- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/sprint-3-stretch-exercises/password-validator.js b/sprint-3-stretch-exercises/password-validator.js index ac70324a5..20613831a 100644 --- a/sprint-3-stretch-exercises/password-validator.js +++ b/sprint-3-stretch-exercises/password-validator.js @@ -1,3 +1,22 @@ +const previousPasswords = ["password", "12345", "qwerty"]; + export function isValidPassword(password) { - return password.length >= 5; + if (password.length < 5) { + return false; + } + + const upperCase = /[A-Z]/.test(password); + const lowerCase = /[a-z]/.test(password); + const number = /[0-9]/.test(password); + const symbol = /[!#$%.*&]/.test(password); + + if (!upperCase || !lowerCase || !number || !symbol) { + return false; + } + + if (previousPasswords.includes(password)) { + return false; + } + + return true; } diff --git a/sprint-3-stretch-exercises/password-validator.test.js b/sprint-3-stretch-exercises/password-validator.test.js index 2a58a0fb6..9498807c5 100644 --- a/sprint-3-stretch-exercises/password-validator.test.js +++ b/sprint-3-stretch-exercises/password-validator.test.js @@ -15,12 +15,33 @@ To be valid, a password must: You must breakdown this problem in order to solve it. Find one test case first and get that working */ import { isValidPassword } from "./password-validator.js"; -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 + +test("password has at least 5 characters and meets all rules", () => { + const password = "Abc1!"; + const result = isValidPassword(password); + expect(result).toEqual(true); +}); + +test("rejects password shorter than 5 characters", () => { + expect(isValidPassword("Ab1!")).toEqual(false); +}); + +test("rejects password without uppercase", () => { + expect(isValidPassword("abc1!")).toEqual(false); +}); + +test("rejects password without lowercase", () => { + expect(isValidPassword("ABC1!")).toEqual(false); +}); + +test("rejects password without number", () => { + expect(isValidPassword("Abcde!")).toEqual(false); +}); + +test("rejects password without symbol", () => { + expect(isValidPassword("Abc12")).toEqual(false); +}); + +test("rejects previous password", () => { + expect(isValidPassword("password")).toEqual(false); +}); From aab021a03da50d67fb92cd49ebd8894d2c4deea7 Mon Sep 17 00:00:00 2001 From: SalahAlsabhi Date: Fri, 23 Oct 2026 20:51:54 +0100 Subject: [PATCH 2/5] Add credit card validator with tests --- sprint-3-stretch-exercises/card-validator.js | 29 +++++++++++++++++++ .../card-validator.test.js | 25 ++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 sprint-3-stretch-exercises/card-validator.js create mode 100644 sprint-3-stretch-exercises/card-validator.test.js diff --git a/sprint-3-stretch-exercises/card-validator.js b/sprint-3-stretch-exercises/card-validator.js new file mode 100644 index 000000000..f5a0cdb6e --- /dev/null +++ b/sprint-3-stretch-exercises/card-validator.js @@ -0,0 +1,29 @@ +export function isValidCardNumber(cardNumber) { + //The card number must be exactly 16 digits + if (!/^\d{16}$/.test(cardNumber)) { + return false; + } + + //It should contains at least two different digits + const uniqueDigits = new Set(cardNumber); + if (uniqueDigits.size < 2) { + return false; + } + + //It's last digit must be even + const lastDigit = Number(cardNumber[15]); + if (lastDigit % 2 !== 0) { + return false; + } + + //It's sum of all digits must be greater than 16 + let sum = 0; + for (const digit of cardNumber) { + sum += Number(digit); + } + if (sum <= 16) { + return false; + } + + return true; +} diff --git a/sprint-3-stretch-exercises/card-validator.test.js b/sprint-3-stretch-exercises/card-validator.test.js new file mode 100644 index 000000000..f22b16087 --- /dev/null +++ b/sprint-3-stretch-exercises/card-validator.test.js @@ -0,0 +1,25 @@ +import { isValidCardNumber } from "./card-validator.js"; + +test("accepts a valid card number", () => { + expect(isValidCardNumber("9999777788880000")).toEqual(true); +}); + +test("accepts another valid card number", () => { + expect(isValidCardNumber("6666666666661666")).toEqual(true); +}); + +test("rejects non-digit characters", () => { + expect(isValidCardNumber("a92332119c011112")).toEqual(false); +}); + +test("rejects all same digits", () => { + expect(isValidCardNumber("4444444444444444")).toEqual(false); +}); + +test("rejects sum of 16 or less", () => { + expect(isValidCardNumber("1111111111111110")).toEqual(false); +}); + +test("rejects odd final digit", () => { + expect(isValidCardNumber("6666666666666661")).toEqual(false); +}); From bd6a8fbb92b222346c385856347e6fee8143d7b6 Mon Sep 17 00:00:00 2001 From: SalahAlsabhi Date: Sun, 25 Oct 2026 06:46:09 +0000 Subject: [PATCH 3/5] Address review: test names, length cases, previousPasswords --- .../card-validator.test.js | 20 +++++++++++++------ .../password-validator.js | 17 +++++++++------- .../password-validator.test.js | 18 ++++++++++------- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/sprint-3-stretch-exercises/card-validator.test.js b/sprint-3-stretch-exercises/card-validator.test.js index f22b16087..54d01f38a 100644 --- a/sprint-3-stretch-exercises/card-validator.test.js +++ b/sprint-3-stretch-exercises/card-validator.test.js @@ -1,25 +1,33 @@ import { isValidCardNumber } from "./card-validator.js"; -test("accepts a valid card number", () => { +test("should accept a valid 16-digit card number", () => { expect(isValidCardNumber("9999777788880000")).toEqual(true); }); -test("accepts another valid card number", () => { +test("should accept another valid 16-digit card number", () => { expect(isValidCardNumber("6666666666661666")).toEqual(true); }); -test("rejects non-digit characters", () => { +test("should reject a card number with non-digit characters", () => { expect(isValidCardNumber("a92332119c011112")).toEqual(false); }); -test("rejects all same digits", () => { +test("should reject a card number where all digits are the same", () => { expect(isValidCardNumber("4444444444444444")).toEqual(false); }); -test("rejects sum of 16 or less", () => { +test("should reject a card number whose digits sum to 16 or less", () => { expect(isValidCardNumber("1111111111111110")).toEqual(false); }); -test("rejects odd final digit", () => { +test("should reject a card number with an odd final digit", () => { expect(isValidCardNumber("6666666666666661")).toEqual(false); }); + +test("should reject a card number shorter than 16 digits", () => { + expect(isValidCardNumber("123456789012345")).toEqual(false); +}); + +test("should reject a card number longer than 16 digits", () => { + expect(isValidCardNumber("12345678901234567")).toEqual(false); +}); diff --git a/sprint-3-stretch-exercises/password-validator.js b/sprint-3-stretch-exercises/password-validator.js index 20613831a..bb5b54bba 100644 --- a/sprint-3-stretch-exercises/password-validator.js +++ b/sprint-3-stretch-exercises/password-validator.js @@ -1,16 +1,19 @@ -const previousPasswords = ["password", "12345", "qwerty"]; +const defaultPreviousPasswords = ["password", "12345", "qwerty"]; -export function isValidPassword(password) { +export function isValidPassword( + password, + previousPasswords = defaultPreviousPasswords +) { if (password.length < 5) { return false; } - const upperCase = /[A-Z]/.test(password); - const lowerCase = /[a-z]/.test(password); - const number = /[0-9]/.test(password); - const symbol = /[!#$%.*&]/.test(password); + const hasUpper = /[A-Z]/.test(password); + const hasLower = /[a-z]/.test(password); + const hasDigit = /[0-9]/.test(password); + const hasSymbol = /[!#$%.*&]/.test(password); - if (!upperCase || !lowerCase || !number || !symbol) { + if (!hasUpper || !hasLower || !hasDigit || !hasSymbol) { return false; } diff --git a/sprint-3-stretch-exercises/password-validator.test.js b/sprint-3-stretch-exercises/password-validator.test.js index 9498807c5..63596d050 100644 --- a/sprint-3-stretch-exercises/password-validator.test.js +++ b/sprint-3-stretch-exercises/password-validator.test.js @@ -16,32 +16,36 @@ You must breakdown this problem in order to solve it. Find one test case first a */ import { isValidPassword } from "./password-validator.js"; -test("password has at least 5 characters and meets all rules", () => { +test("password should have at least 5 characters and meets all rules", () => { const password = "Abc1!"; const result = isValidPassword(password); expect(result).toEqual(true); }); -test("rejects password shorter than 5 characters", () => { +test("should rejects password shorter than 5 characters", () => { expect(isValidPassword("Ab1!")).toEqual(false); }); -test("rejects password without uppercase", () => { +test("should rejects password without uppercase", () => { expect(isValidPassword("abc1!")).toEqual(false); }); -test("rejects password without lowercase", () => { +test("should rejects password without lowercase", () => { expect(isValidPassword("ABC1!")).toEqual(false); }); -test("rejects password without number", () => { +test("should rejects password without number", () => { expect(isValidPassword("Abcde!")).toEqual(false); }); -test("rejects password without symbol", () => { +test("should rejects password without symbol", () => { expect(isValidPassword("Abc12")).toEqual(false); }); -test("rejects previous password", () => { +test("should rejects previous password", () => { expect(isValidPassword("password")).toEqual(false); }); + +test("should reject a password listed in the previousPasswords argument", () => { + expect(isValidPassword("Abc1!", ["Abc1!"])).toEqual(false); +}); From 987378dc33e57144b9f79e399a05e16090983c41 Mon Sep 17 00:00:00 2001 From: SalahAlsabhi Date: Sun, 25 Oct 2026 13:06:04 +0000 Subject: [PATCH 4/5] Isolate password test cases; use empty default for previousPasswords --- .../password-validator.js | 7 +----- .../password-validator.test.js | 23 ++++++++++--------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/sprint-3-stretch-exercises/password-validator.js b/sprint-3-stretch-exercises/password-validator.js index bb5b54bba..9b37ecfb9 100644 --- a/sprint-3-stretch-exercises/password-validator.js +++ b/sprint-3-stretch-exercises/password-validator.js @@ -1,9 +1,4 @@ -const defaultPreviousPasswords = ["password", "12345", "qwerty"]; - -export function isValidPassword( - password, - previousPasswords = defaultPreviousPasswords -) { +export function isValidPassword(password, previousPasswords = []) { if (password.length < 5) { return false; } diff --git a/sprint-3-stretch-exercises/password-validator.test.js b/sprint-3-stretch-exercises/password-validator.test.js index 63596d050..69deaf832 100644 --- a/sprint-3-stretch-exercises/password-validator.test.js +++ b/sprint-3-stretch-exercises/password-validator.test.js @@ -16,34 +16,35 @@ You must breakdown this problem in order to solve it. Find one test case first a */ import { isValidPassword } from "./password-validator.js"; -test("password should have at least 5 characters and meets all rules", () => { - const password = "Abc1!"; - const result = isValidPassword(password); - expect(result).toEqual(true); +const previousPasswords = ["password", "12345", "qwerty"]; + +test("should accept a password that meets all rules", () => { + expect(isValidPassword("Abc1!")).toEqual(true); }); -test("should rejects password shorter than 5 characters", () => { +test("should reject a password shorter than 5 characters", () => { + // only length fails: has upper, lower, digit, symbol expect(isValidPassword("Ab1!")).toEqual(false); }); -test("should rejects password without uppercase", () => { +test("should reject a password without an uppercase letter", () => { expect(isValidPassword("abc1!")).toEqual(false); }); -test("should rejects password without lowercase", () => { +test("should reject a password without a lowercase letter", () => { expect(isValidPassword("ABC1!")).toEqual(false); }); -test("should rejects password without number", () => { +test("should reject a password without a digit", () => { expect(isValidPassword("Abcde!")).toEqual(false); }); -test("should rejects password without symbol", () => { +test("should reject a password without a required symbol", () => { expect(isValidPassword("Abc12")).toEqual(false); }); -test("should rejects previous password", () => { - expect(isValidPassword("password")).toEqual(false); +test("should reject a previous password", () => { + expect(isValidPassword("password", previousPasswords)).toEqual(false); }); test("should reject a password listed in the previousPasswords argument", () => { From e378e8d15a8c694d9aec39778372e9cb7023beb5 Mon Sep 17 00:00:00 2001 From: SalahAlsabhi Date: Sun, 25 Oct 2026 18:29:10 +0000 Subject: [PATCH 5/5] Isolate card tests; single previousPasswords test --- .../card-validator.test.js | 21 ++++++++++--------- .../password-validator.test.js | 16 ++++---------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/sprint-3-stretch-exercises/card-validator.test.js b/sprint-3-stretch-exercises/card-validator.test.js index 54d01f38a..2d28f41ba 100644 --- a/sprint-3-stretch-exercises/card-validator.test.js +++ b/sprint-3-stretch-exercises/card-validator.test.js @@ -8,7 +8,15 @@ test("should accept another valid 16-digit card number", () => { expect(isValidCardNumber("6666666666661666")).toEqual(true); }); -test("should reject a card number with non-digit characters", () => { +test("should reject a card number shorter than 16 digits", () => { + expect(isValidCardNumber("999999999999998")).toEqual(false); +}); + +test("should reject a card number longer than 16 digits", () => { + expect(isValidCardNumber("99999999999999998")).toEqual(false); +}); + +test("should reject a card number with a non-digit character", () => { expect(isValidCardNumber("a92332119c011112")).toEqual(false); }); @@ -21,13 +29,6 @@ test("should reject a card number whose digits sum to 16 or less", () => { }); test("should reject a card number with an odd final digit", () => { - expect(isValidCardNumber("6666666666666661")).toEqual(false); -}); - -test("should reject a card number shorter than 16 digits", () => { - expect(isValidCardNumber("123456789012345")).toEqual(false); -}); - -test("should reject a card number longer than 16 digits", () => { - expect(isValidCardNumber("12345678901234567")).toEqual(false); + // same style as a valid number, only last digit is odd + expect(isValidCardNumber("9999777788880001")).toEqual(false); }); diff --git a/sprint-3-stretch-exercises/password-validator.test.js b/sprint-3-stretch-exercises/password-validator.test.js index 69deaf832..34c2d6d6c 100644 --- a/sprint-3-stretch-exercises/password-validator.test.js +++ b/sprint-3-stretch-exercises/password-validator.test.js @@ -1,23 +1,18 @@ -/* +/* 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. - +- 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 */ import { isValidPassword } from "./password-validator.js"; -const previousPasswords = ["password", "12345", "qwerty"]; - test("should accept a password that meets all rules", () => { expect(isValidPassword("Abc1!")).toEqual(true); }); @@ -43,10 +38,7 @@ test("should reject a password without a required symbol", () => { expect(isValidPassword("Abc12")).toEqual(false); }); -test("should reject a previous password", () => { - expect(isValidPassword("password", previousPasswords)).toEqual(false); -}); - -test("should reject a password listed in the previousPasswords argument", () => { +test("should reject a password listed in previousPasswords", () => { + // meets all other rules; fails only because it is in the list expect(isValidPassword("Abc1!", ["Abc1!"])).toEqual(false); });