Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion Sprint-3/2-practice-tdd/count.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
function countChar(stringOfCharacters, findCharacter) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anosidium ✅ Great job! Your countChar function correctly counts occurrences and passes all test scenarios.
Using reduce here is neat and expressive. For clarity or performance, you could also consider a for...of loop, but your current solution is clean and idiomatic.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anosidium How should we handle cases where the first argument (stringOfCharacters) is an integer and the second (findCharacter) is a string — essentially when the parameters are of the wrong data types?

I believe adding a check for this would make the function more robust. What do you think?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree and updated the countChar() function.

return 5
if (typeof stringOfCharacters !== "string") {
throw new TypeError(
"The argument for parameter stringOfCharacters is not a String type"
);
}

if (typeof findCharacter !== "string") {
throw new TypeError(
"The argument for parameter findCharacter is not a String type"
);
}

if (findCharacter.length !== 1) {
throw new Error(
"Parameter findCharacter is must contain a single character"
);
}

return stringOfCharacters.split("").reduce((accumulator, character) => {
return accumulator + (character === findCharacter ? 1 : 0);
}, 0);
}

module.exports = countChar;
46 changes: 46 additions & 0 deletions Sprint-3/2-practice-tdd/count.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,49 @@ test("should count multiple occurrences of a character", () => {
// And a character char that does not exist within the case-sensitive str,
// When the function is called with these inputs,
// Then it should return 0, indicating that no occurrences of the char were found in the case-sensitive str.

test("should count no occurrences of z characters", () => {
const str = "aaaaa";
const char = "z";
const count = countChar(str, char);
expect(count).toEqual(0);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anosidium I think we can add more test cases to cover additional scenarios — such as how the function behaves with an empty string, a single occurrence, or when the character case differs (e.g., 'A' vs 'a').

It’s good practice to include these kinds of edge cases when writing tests to ensure the function handles all possible inputs robustly.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I've added more test cases.


test("should return 0 for an empty string and a non-empty character", () => {
const str = "";
const char = "z";
const count = countChar(str, char);
expect(count).toEqual(0);
});

test("should return 0 for an empty string and a whitespace character", () => {
const str = "";
const char = " ";
const count = countChar(str, char);
expect(count).toEqual(0);
});

test("should return 0 when the string contains the character in a different case", () => {
const str = "aBcDeF";
const char = "A";
const count = countChar(str, char);
expect(count).toEqual(0);
});

test("throws error when the first argument is not a string", () => {
expect(() => countChar(1234, "1")).toThrow(
"The argument for parameter stringOfCharacters is not a String type"
);
});

test("throws error when the second argument is not a string", () => {
expect(() => countChar("1234", 1)).toThrow(
"The argument for parameter findCharacter is not a String type"
);
});

test("throws error when second argument is longer than one character", () => {
expect(() => countChar("1234", "12")).toThrow(
"Parameter findCharacter is must contain a single character"
);
});
25 changes: 24 additions & 1 deletion Sprint-3/2-practice-tdd/get-ordinal-number.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
function getOrdinalNumber(num) {
return "1st";
if (isNaN(num)) {
return "";
}

const numberString = String(num);
const lastDigit = Number(numberString.slice(-1));
const lastDigits = Number(numberString.slice(-2));
let ordinal;

if (num === 0) {
ordinal = "";
} else if ([11, 12, 13].includes(lastDigits)) {
ordinal = "th";
} else if (lastDigit === 1) {
ordinal = "st";
} else if (lastDigit === 2) {
ordinal = "nd";
} else if (lastDigit === 3) {
ordinal = "rd";
} else {
ordinal = "th";
}

return `${num}${ordinal}`;
}

module.exports = getOrdinalNumber;
60 changes: 58 additions & 2 deletions Sprint-3/2-practice-tdd/get-ordinal-number.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,62 @@ const getOrdinalNumber = require("./get-ordinal-number");
// When the number is 1,
// Then the function should return "1st"

test("should return '1st' for 1", () => {
expect(getOrdinalNumber(1)).toEqual("1st");
test("should return an empty string for a non-numerical character", () => {
expect(getOrdinalNumber("@")).toEqual("");
});

test("should return '0' for 0", () => {
expect(getOrdinalNumber(0)).toEqual("0");
});

test("should return '11th' for 11", () => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one!!
how would this current implementation handle 12 and 13
@anosidium

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops!

expect(getOrdinalNumber(11)).toEqual("11th");
});

test("should return '12th' for 12", () => {
expect(getOrdinalNumber(12)).toEqual("12th");
});

test("should return '13th' for 13", () => {
expect(getOrdinalNumber(13)).toEqual("13th");
});

test("should return '-11th' for -11", () => {
expect(getOrdinalNumber(-11)).toEqual("-11th");
});

test("should return `22nd` for 22", () => {
expect(getOrdinalNumber(22)).toEqual("22nd");
});

test("should return `123rd` for 123", () => {
expect(getOrdinalNumber(123)).toEqual("123rd");
});

test("should return '1234th' for 1234", () => {
expect(getOrdinalNumber(1234)).toEqual("1234th");
});

test("should return '12345th' for 12345", () => {
expect(getOrdinalNumber(12345)).toEqual("12345th");
});

test("should return '123456th' for 123456", () => {
expect(getOrdinalNumber(123456)).toEqual("123456th");
});

test("should return '1234567th' for 1234567", () => {
expect(getOrdinalNumber(1234567)).toEqual("1234567th");
});

test("should return '12345678th' for 12345678", () => {
expect(getOrdinalNumber(12345678)).toEqual("12345678th");
});

test("should return '123456789th' for 123456789", () => {
expect(getOrdinalNumber(123456789)).toEqual("123456789th");
});

test("should return '1234567890' for 1234567890th", () => {
expect(getOrdinalNumber(1234567890)).toEqual("1234567890th");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know about using describe() blocks in Jest? It can help organize related tests and make your test output cleaner and easier to read.

describe("getOrdinalNumber", () => {
  describe("valid inputs", () => {
    test("returns '1st' for 1", () => {
      expect(getOrdinalNumber(1)).toEqual("1st");
    });
  });

  describe("invalid inputs", () => {
    test("returns empty string for non-numeric input", () => {
      expect(getOrdinalNumber('@')).toEqual('');
    });
  });
});

It’s a good practice to group similar tests like this — especially as your test suite grows.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I did not know about describe(name, fn). I will keep this in mind for future tests.

});
14 changes: 12 additions & 2 deletions Sprint-3/2-practice-tdd/repeat.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
function repeat() {
return "hellohellohello";
function repeat(string, count) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use a different argument name other than string

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion! I named the parameter string to indicate that the first argument is expected to be a string. I kept it simple to move forward, but I’m happy to rename it later if needed for clarity.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function name repeat isn’t very descriptive — we can give it a clearer name that better communicates its purpose and aligns with the file and test naming for consistency.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion! The function name repeat wasn’t chosen by me, it came from the existing code. I kept it as-is to avoid breaking references.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could also help to add a short comment above the function describing what it does and the expected parameter types — this improves readability for anyone new to the code.

if (isNaN(count)) {
throw new Error("count is not a number");
} else if (count < 0) {
throw new Error("negative numbers are not valid");
} else if (count === 0) {
return "";
} else if (count === 1) {
return string;
} else {
return string.repeat(count);
}
}

module.exports = repeat;
23 changes: 23 additions & 0 deletions Sprint-3/2-practice-tdd/repeat.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,35 @@ test("should repeat the string count times", () => {
// When the repeat function is called with these inputs,
// Then it should return the original str without repetition, ensuring that a count of 1 results in no repetition.

test("should repeat the string 1 times", () => {
const str = "Hello, World!";
const count = 1;
const repeatedStr = repeat(str, count);
expect(repeatedStr).toEqual(str);
});

// case: Handle Count of 0:
// Given a target string str and a count equal to 0,
// When the repeat function is called with these inputs,
// Then it should return an empty string, ensuring that a count of 0 results in an empty output.

test("should return an empty string for 0 times", () => {
const str = "Bye, World!";
const count = 0;
const repeatedStr = repeat(str, count);
expect(repeatedStr).toEqual("");
});

// case: Negative Count:
// Given a target string str and a negative integer count,
// When the repeat function is called with these inputs,
// Then it should throw an error or return an appropriate error message, as negative counts are not valid.

test("should throw an error for a negative number", () => {
expect(() => repeat("Ello!", -1)).toThrow("negative numbers are not valid");
});

// case: count is not a number:
test("should throw an error when count is not a number", () => {
expect(() => repeat("Hello?", "@")).toThrow("count is not a number");
});
30 changes: 30 additions & 0 deletions Sprint-3/3-stretch/credit-card-validator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
function creditCardValidator(cardNumber) {
// Check that the card number contains numbers only
if (isNaN(cardNumber)) return false;

// Convert the card number to a string so we can check its digits easily
let cardNumberString = String(cardNumber);

// Check that the card number has exactly 16 digits
if (cardNumberString.length != 16) return false;

// Check that the last digit is even
if (cardNumberString[15] % 2 != 0) return false;

// Calculate the sum of all digits
const sum = cardNumberString
.split("")
.reduce((sum, digit) => (sum += Number(digit)), 0);

// If the sum of all digits is 16 or less, the card is invalid
if (sum <= 16) return false;

// Use Set to filter out duplicate digits
let uniqueDigits = new Set(cardNumberString);

// Make sure the card number isn’t made up of all the same digit
if (uniqueDigits.size < 2) return false;

// If all conditions pass, the card number is valid
return true;
}
16 changes: 13 additions & 3 deletions Sprint-3/3-stretch/password-validator.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
const previousPasswords = [];

function passwordValidator(password) {
return password.length < 5 ? false : true
}
if (previousPasswords.includes(password)) return false;
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;

previousPasswords.push(password);

return true;
}

module.exports = passwordValidator;
module.exports = passwordValidator;
54 changes: 45 additions & 9 deletions Sprint-3/3-stretch/password-validator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,48 @@ 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
*/
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);
}
);

test("invalid password (numbers only)", () => {
// Arrange
const password = "12345";
// Act
const result = isValidPassword(password);
// Assert
expect(result).toEqual(false);
});

test("invalid password (lowercase letters only)", () => {
const password = "abcde";
const result = isValidPassword(password);
expect(result).toEqual(false);
});

test("invalid password (uppercase letters only)", () => {
const password = "QWERTY";
const result = isValidPassword(password);
expect(result).toEqual(false);
});

test("invalid password (non-alphanumeric symbols only)", () => {
const password = "!#$%.*&!";
const result = isValidPassword(password);
expect(result).toEqual(false);
});

test("valid password", () => {
const password = "aB1!cD";
const result = isValidPassword(password);
expect(result).toEqual(true);
});

test("duplicate password", () => {
const password = "aB1!cD";
const result = isValidPassword(password);
expect(result).toEqual(false);
});

test("new valid password", () => {
const password = "zX1$2£€";
const result = isValidPassword(password);
expect(result).toEqual(true);
});