Skip to content
11 changes: 9 additions & 2 deletions Sprint-3/2-practice-tdd/count.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
function countChar(stringOfCharacters, findCharacter) {
return 5
function countChar(str, char) {
if (!char || char.length !== 1) return 0; // ensure char is a single character
let count = 0;
for (let i = 0; i < str.length; i++) {
if (str[i] === char) {
count++;
}
}
return count;
}

module.exports = countChar;
41 changes: 41 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,44 @@ 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 when character does not exist in the string", () => {
const str = "mnopqrst";
const char = "z";
const count = countChar(str, char);
expect(count).toEqual(0);
});

// we can add more test cases to make sure our function is working as expected
// Scenario: Case Sensitivity
// Given the input string str,
// And a character char that exists in str but with different casing (e.g., 'A' in 'aAaAa'),
// When the function is called with these inputs,
// Then it should treat char as case-sensitive and only count occurrences that match the exact casing (e.g., 'A' appears two times in 'aAaAa').

test("should treat character as case-sensitive", () => {
const str = "aAaAa";
const char = "A";
const count = countChar(str, char);
expect(count).toEqual(2);
});

// we can also add empty string test case
// Scenario: Empty String
// Given an empty input string str,
// And any character char,
// When the function is called with these inputs,
// Then it should return 0, indicating that no occurrences of char can be found in an empty str.

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

// We can run this test file using the command `npx jest count.test.js`
// in the terminal. Making sure we are in the directory where this file is located.
// If we have Jest installed globally, you can simply run `jest count.test.js`
// instead. If you have added a test script to your package.json file, you can also run
// `npm test count.test.js` to execute the tests.
19 changes: 18 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,22 @@
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.

Can you think of a more concise way to express the condition of this if statement?

Copy link
Author

Choose a reason for hiding this comment

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

Hello,

Thank you for the feedback.

Yes the If statement can be made more concise. Here’s a more compact version:

if (!Number.isInteger(num) || num <= 0) {

return "Invalid input";
}
if (num % 100 >= 11 && num % 100 <= 13) {
return num + "th";
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Indentation is a bit off.

Copy link
Author

Choose a reason for hiding this comment

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

Indentation is now fixed

switch (num % 10) {
case 1:
return num + "st";
case 2:
return num + "nd";
case 3:
return num + "rd";
default:
return num + "th";
}

}

module.exports = getOrdinalNumber;
64 changes: 64 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 @@ -11,3 +11,67 @@ const getOrdinalNumber = require("./get-ordinal-number");
test("should return '1st' for 1", () => {
expect(getOrdinalNumber(1)).toEqual("1st");
});

// Case 2: Identify the ordinal number for 2
// When the number is 2,
// Then the function should return "2nd"

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

// Case 3: Identify the ordinal number for 3
// When the number is 3,
// Then the function should return "3rd"

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

// Case 4: Identify the ordinal number for 4
// When the number is 4,
// Then the function should return "4th"

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

// Case 5: Identify the ordinal number for 11
// When the number is 11,
// Then the function should return "11th"

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

// Case 6: Identify the ordinal number for 21
// When the number is 21,
// Then the function should return "21st"

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

// Case 7: Identify the ordinal number for 102
// When the number is 102,
// Then the function should return "102nd"

test("should return '102nd' for 102", () => {
expect(getOrdinalNumber(102)).toEqual("102nd");
});
Copy link
Contributor

Choose a reason for hiding this comment

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

To ensure thorough testing, we need broad scenario coverage. Listing individual values, however, can quickly lead to an unmanageable number of test cases.
Instead of writing tests for individual numbers, consider grouping all possible input values into meaningful categories. Then, select representative samples from each category to test. This approach improves coverage and makes our tests easier to maintain.

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(102) ).toEqual("102nd");
});

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 have grouped all the different teste-cases to categories.

some of the benefits of this approach are:

  • Scalability — We don't need to test every number, just representatives of each category.
  • Maintainability — It is easier to read and update tests when logic changes.
  • Comprehensive — It covers all the functional branches in the logic.
  • Avoids Redundancy — We won't waste time testing repetitive patterns unnecessarily.


// Case 8: Identify the ordinal number for 111
// When the number is 111,
// Then the function should return "111th"

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

// case 9: identify the ordinal number for 211
// when the number is 211
// then the function should return "211th"

test("should return '33rd' for 33", () => {
expect(getOrdinalNumber(33)).toEqual("33rd");
});
28 changes: 27 additions & 1 deletion Sprint-3/2-practice-tdd/repeat.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
function repeat() {
return "hellohellohello";
if (arguments.length !== 2) {
throw new Error("Function requires exactly two arguments: str and count.");
}

const [str, count] = arguments;

Copy link
Contributor

Choose a reason for hiding this comment

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

Why not just declare the parameters as

function repeat(str, count) {
  ...

?

Copy link
Author

Choose a reason for hiding this comment

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

Yes we can declare the parameters in the function call and we will not need to declare const [str, count] = arguments;.

if (typeof str !== "string") {
throw new Error("First argument must be a string.");
}

if (typeof count !== "number" || !Number.isInteger(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.

Similar to the case in get-ordinal-number.js,
can you think of a more concise way to express the condition of this if statement?

Copy link
Author

Choose a reason for hiding this comment

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

There are two other ways that this condition can be expressed concisely.

concise (but potentially less readable):
if (!(Number.isInteger(count) && count >= 0))

and the other concise but more readable option:
if (!Number.isSafeInteger(count) || count < 0)

throw new Error("Second argument must be a non-negative integer.");
}

if (count === 0) {
return "";
}

if (count === 1) {
return str;
}

let result = "";
for (let i = 0; i < count; i++) {
result += str;
}
return result;
}

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 return the original string when count is 1", () => {
const str = "world";
const count = 1;
const repeatedStr = repeat(str, count);
expect(repeatedStr).toEqual("world");
});

// 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 when count is 0", () => {
const str = "test";
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 negative count", () => {
const str = "error";
const count = -2;
expect(() => repeat(str, count)).toThrow(
"Second argument must be a non-negative integer."
);
});