Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
7 changes: 6 additions & 1 deletion Sprint-3/2-practice-tdd/count.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
function countChar(stringOfCharacters, findCharacter) {
return 5
let output = 0;
for( let i=0; i <= stringOfCharacters.length; i++ ) {
if(stringOfCharacters[i] === findCharacter)
output++;
}
return output;
}

module.exports = countChar;
6 changes: 6 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,9 @@ 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 return 0 if there are no occurrences at all", () => {
const str = "Hello World!";
const char = "a";
const count = countChar(str, char);
expect(count).toEqual(0);
});
25 changes: 24 additions & 1 deletion Sprint-3/2-practice-tdd/get-ordinal-number.js
Copy link
Contributor

Choose a reason for hiding this comment

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

The function should work for any positive integers.

Can you lookup the rules for representing ordinal numbers?

You should prepare tests in get-ordinal-number.test.js file first before implementing getOrdinalNumber().

For example, we can prepare a test for numbers 2, 22, 132, etc. as

test("append 'nd' to numbers ending in 2, except those ending in 12", () => {
    expect( getOrdinalNumber(2) ).toEqual("2nd");
    expect( getOrdinalNumber(22) ).toEqual("22nd");
    expect( getOrdinalNumber(132) ).toEqual("132nd");
});

Copy link
Author

Choose a reason for hiding this comment

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

Thank you for the feedback. I set the tests for the function and modified the function getOrdinalNumber() to meet the requirements and to handle all possible cases.

Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
function getOrdinalNumber(num) {
return "1st";
//
if (typeof num !== "number" || !Number.isInteger(num) || num <= 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This condition can be shorten. Can you figure out how?

Copy link
Author

@M-Abdoon M-Abdoon Oct 20, 2025

Choose a reason for hiding this comment

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

Yes, I think I one of these conditions typeof num !== "number" || !Number.isInteger(num) is not needed since they both check if the input is a valid number.

Copy link
Contributor

Choose a reason for hiding this comment

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

typeof num !== "number" and !Number.isInteger(num) are not equivalent. One of them is stricter than the other.

Both Infinity and NaN can still pass this check: typeof num !== "number".

Copy link
Author

@M-Abdoon M-Abdoon Oct 20, 2025

Choose a reason for hiding this comment

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

Yes, that's right. They are not equivalent. I tested them and learned that Infinity and NaN are actually type of numbers, that's why the rule typeof num !== "number" won't work here. So the best practice is to use Number.isInteger(), as this method excludes both NaN and Infinity.
I updated the function and added new test cases including NaN, Infinity, strings and arrays and instead of showing a string error message, I used throw an error with an error message

return "Input must be a positive integer.";
}

const lastTwoDigits = num % 100;
const lastDigit = num % 10;

if (lastTwoDigits >= 11 && lastTwoDigits <= 13) {
return `${num}th`;
}

switch (lastDigit) {
case 1:
return `${num}st`;
case 2:
return `${num}nd`;
case 3:
return `${num}rd`;
default:
return `${num}th`;
}
return num;
}

console.log(getOrdinalNumber(21)); // "1st"
module.exports = getOrdinalNumber;
50 changes: 50 additions & 0 deletions Sprint-3/2-practice-tdd/get-ordinal-number.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,54 @@ const getOrdinalNumber = require("./get-ordinal-number");

test("should return '1st' for 1", () => {
expect(getOrdinalNumber(1)).toEqual("1st");
expect(getOrdinalNumber(21)).toEqual("21st");
expect(getOrdinalNumber(101)).toEqual("101st");
});

// Case 2: Identify the ordinal number for 1
// except those ending in 12
test("append 'nd' to numbers ending in 2, except those ending in 12", () => {
expect( getOrdinalNumber(2) ).toEqual("2nd");
expect( getOrdinalNumber(22) ).toEqual("22nd");
expect( getOrdinalNumber(132) ).toEqual("132nd");
});

// Case 3: Identify the ordinal numbers for 3 to 12
test("should return correct ordinal suffixes for numbers 3 to 12", () => {
expect( getOrdinalNumber(3) ).toEqual("3rd");
expect( getOrdinalNumber(4) ).toEqual("4th");
expect( getOrdinalNumber(5) ).toEqual("5th");
expect( getOrdinalNumber(6) ).toEqual("6th");
expect( getOrdinalNumber(7) ).toEqual("7th");
expect( getOrdinalNumber(8) ).toEqual("8th");
expect( getOrdinalNumber(9) ).toEqual("9th");
expect( getOrdinalNumber(10) ).toEqual("10th");
expect( getOrdinalNumber(11) ).toEqual("11th");
expect( getOrdinalNumber(12) ).toEqual("12th");
});
// Case 4: Identify the ordinal numbers for numbers above 12
test("should return correct ordinal suffixes for numbers above 12", () => {
expect( getOrdinalNumber(13) ).toEqual("13th");
expect( getOrdinalNumber(19) ).toEqual("19th");
expect( getOrdinalNumber(23) ).toEqual("23rd");
expect( getOrdinalNumber(34) ).toEqual("34th");
expect( getOrdinalNumber(45) ).toEqual("45th");
expect( getOrdinalNumber(56) ).toEqual("56th");
expect( getOrdinalNumber(67) ).toEqual("67th");
});

// Case 5: Identify the ordinal numbers for numbers like 20 30 40 etc
test("should return correct ordinal suffixes for multiples of ten", () => {
expect( getOrdinalNumber(20) ).toEqual("20th");
expect( getOrdinalNumber(30) ).toEqual("30th");
expect( getOrdinalNumber(70) ).toEqual("70th");
expect( getOrdinalNumber(50) ).toEqual("50th");
});

// Case 6: dealing with big numbers
test("should return correct ordinal suffixes for big numbers", () => {
expect( getOrdinalNumber(111) ).toEqual("111th");
expect( getOrdinalNumber(4712) ).toEqual("4712th");
expect( getOrdinalNumber(10003) ).toEqual("10003rd");
expect( getOrdinalNumber(10012) ).toEqual("10012th");
});
Copy link
Contributor

@cjyuan cjyuan Oct 19, 2025

Choose a reason for hiding this comment

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

Why categorize the input number into 3-12, and >12? Why choose 12 as the cutting point?
Why do multiples of 10 need a separate category?

They way the possible input values are being categorized seem a bit illogical.

Copy link
Author

Choose a reason for hiding this comment

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

The categories (3–12, >12, multiples of 10) are not part of the logic itself, they’re just for organizing the test cases.

I grouped them that way to make sure the function covered different patterns

3 - 10 is for testing numbers that only consist of 1 number, maybe I should have categorized them from 0 to 9.

11 and 12 are special exceptions (11th, 12th), when the last number is 1 or 2 it will always be "st" and "nd" except for these cases.

Multiples of 10 are a separate group just to confirm that round numbers still get “th”.

It’s not about logic segmentation, just clearer test coverage.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally, the test description should help a developer determine which cases the function needs to handle.
When a test fails, the description should also help the developer identify which case the function missed.

While your categories do cover all numbers, here’s another way to categorize the input (for your reference):

  • Numbers that end with "st"
  • Numbers that end with "nd"
  • Numbers that end with "rd"
  • Numbers that end with "th"
  • Numbers whose last two digits are in the range 11–13 (special cases)

Copy link
Author

Choose a reason for hiding this comment

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

Thanks a lot for the explanation and for clearing things up! I’ve re-categorized the test cases like you suggested.

7 changes: 5 additions & 2 deletions Sprint-3/2-practice-tdd/repeat.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
function repeat() {
return "hellohellohello";
function repeat(str, count) {
if (typeof count !== "number" || isNaN(count) || count < 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can count be 0.5 or Infinity?

Copy link
Author

Choose a reason for hiding this comment

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

if count is 0.5, the function will treat it as 0, so an empty string will be returned.
If count is Infinity, the function will throw a RangeError because a string cannot be repeated infinitely.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why define a condition that rejects NaN but not Infinity?

Copy link
Author

Choose a reason for hiding this comment

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

I didn't know what really the difference was. but I changed the rule and used Number.isInteger() instead of isNaN.
Number.isInteger() is more general and can detect both NaN and Infinity.

Copy link
Contributor

Choose a reason for hiding this comment

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

If you have time, look up the characteristics of "number" type values in JS, what are some of the edge cases, and how to check them.

When writing programs, it is important to know the limits of each type of values.

throw new Error("Count must be a valid number");
}
return str.repeat(count);
}

module.exports = repeat;
26 changes: 26 additions & 0 deletions Sprint-3/2-practice-tdd/repeat.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,39 @@ test("should repeat the string count times", () => {
// Given a target string str and a count equal to 1,
// 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 only 1 time (no repetition)", () => {
const str = "hello";
const count = 1;
const repeatedStr = repeat(str, count);
expect(repeatedStr).toEqual("hello");
});

// 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 repeat the string 0 times ( means the output will be an empty string)", () => {
const str = "hello";
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 if count is not a valid number", () => {
const str = "hello";
const count = -8;
expect(() => repeat(str, count)).toThrow("Count must be a valid number");
});

test("should throw an error if count is not a valid number", () => {
const str = "hello";
const count = "abc";
expect(() => repeat(str, count)).toThrow("Count must be a valid number");
});