debut TP2

This commit is contained in:
Pauline Srifi 2024-04-18 09:43:42 +02:00
parent a17920df41
commit e092272bd4
31 changed files with 696 additions and 37 deletions

View File

@ -23,8 +23,8 @@
}, },
"dependencies": { "dependencies": {
"nodemon": "^3.1.0", "nodemon": "^3.1.0",
"reflect-metadata": "^0.2.1", "reflect-metadata": "^0.2.2",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"tsyringe": "^4.8.0" "tsyringe": "^4.8.0"
} }
} }

View File

@ -0,0 +1,57 @@
import { fibo } from "./index";
describe("fibo", () => {
it("should return 0 when given 0", () => {
// arrange
const input = 0;
const expected = 0;
// act
const result = fibo(input);
// assert
expect(result).toBe(expected);
});
it("should return 1 when given 1", () => {
// arrange
const input = 1;
const expected = 1;
// act
const result = fibo(input);
// assert
expect(result).toBe(expected);
});
it.each([
{
input: 2,
expected: 1,
},
{
input: 3,
expected: 2,
},
{
input: 4,
expected: 3,
},
{
input: 5,
expected: 5,
},
{
input: 6,
expected: 8,
},
])("should return fibo(n) when given n", ({ input, expected }) => {
// arrange
// act
const result = fibo(input);
// assert
expect(result).toBe(expected);
});
});

View File

@ -0,0 +1,5 @@
export function fibo(n: number): number {
if (n <= 1) return n;
return fibo(n - 2) + fibo(n - 1);
}

View File

@ -0,0 +1,64 @@
import { fizzBuzz } from "./index";
describe("fizzBuzz", () => {
it.each([
{
input: 1,
expected: "1",
},
{
input: 2,
expected: "2",
},
{
input: 4,
expected: "4",
},
])(
"should return the number as string when given a non multiple of 3, 5 or 15",
({ input, expected }) => {
// arrange
// act
const result = fizzBuzz(input);
// assert
expect(result).toBe(expected);
}
);
it.each([3, 6, 9])(
"should return 'Fizz' when given a multiple of 3",
(input) => {
// arrange
// act
const result = fizzBuzz(input);
// assert
expect(result).toBe("Fizz");
}
);
it.each([5, 10, 20])(
'should return "Buzz" when given a multiple of 5',
(input) => {
// arrange
// act
const result = fizzBuzz(input);
// assert
expect(result).toBe("Buzz");
}
);
it.each([15, 30, 45])(
'should return "FizzBuzz" when given a multiple of 15',
(input) => {
// arrange
// act
const result = fizzBuzz(input);
// assert
expect(result).toBe("FizzBuzz");
}
);
});

View File

@ -0,0 +1,7 @@
export function fizzBuzz(input: number): string {
if (input % 15 === 0) return "FizzBuzz";
if (input % 5 === 0) return "Buzz";
if (input % 3 === 0) return "Fizz";
return input.toString();
}

View File

@ -1,32 +0,0 @@
import { divide } from "./index";
describe("Test divide", () => {
it.each([
{ foo: "", bar: "", expected: "" },
])(
"should $foo $bar",
({ foo, bar, expected }) => {
// act
// const result = function (foo, bar);
const result = 0;
// assert
expect(result).toEqual(expected);
console.log(`Test with foo = ${foo}, bar = ${bar}, expected = ${expected}, result = ${result}`);
}
);
/*
*
* If exception test
*
* */
it("should throw an exception when dividing by zero", () => {
// arrange
const foo = "";
const bar = "";
// act & assert
///TODO
// expect(() => function (foo, bar).toThrow("E");
console.log(foo + bar);
});
});

View File

@ -1,3 +0,0 @@
export function name(foo : number) : number {
return foo + 1;
}

View File

@ -0,0 +1,70 @@
import { addStringOfNumbers } from "./index";
describe("addStringOfNumbers", () => {
it("should return 0 when given an empty string", () => {
// arrange
const input = "";
const expected = 0;
// act
const result = addStringOfNumbers(input);
// assert
expect(result).toBe(expected);
});
it.each([
{ input: "12", expected: 12 },
{ input: "1.5", expected: 1.5 },
{ input: "-2", expected: -2 },
{ input: "-2.333", expected: -2.333 },
])(
"should return n when given the string representation of n",
({ input, expected }) => {
// arrange
// act
const result = addStringOfNumbers(input);
// assert
expect(result).toBe(expected);
}
);
it.each(["--4", "1-23", "3..5"])(
"should throw an error when given a string that is not a number",
(input) => {
// arrange
// act
const act = () => addStringOfNumbers(input);
// assert
expect(act).toThrow("Not valid number");
}
);
it.each([
{ input: "2,3", expected: 5 },
{ input: "-2.5,4", expected: 1.5 },
])(
"should add two comma separated numbers in a string",
({ input, expected }) => {
// arrange
// act
const result = addStringOfNumbers(input);
// assert
expect(result).toBe(expected);
}
);
it("should throw an error when given a string with wrong separator", () => {
// arrange
const input = "-2.5;3";
// act
const act = () => addStringOfNumbers(input);
// assert
expect(act).toThrow("Invalid characters");
});
});

View File

@ -0,0 +1,21 @@
export function addStringOfNumbers(input: string) {
if (input === "") return 0;
// Regex to catch any character that is not a digit, comma, dot or minus sign
const isContainingBadCharacters = /[^-,.\d]/.test(input);
if (isContainingBadCharacters) throw new Error("Invalid characters");
if (!input.includes(",")) {
const num = Number(input);
if (isNaN(num)) throw new Error("Not valid number");
return num;
}
const arr = input.split(",");
return arr.reduce((acc, str) => {
const num = Number(str);
if (isNaN(num)) throw new Error("Not valid numbers");
return acc + num;
}, 0);
}

View File

@ -0,0 +1,19 @@
import { convertToLowercase } from "./index";
describe("convertToLowercase", () => {
it.each([
{ input: "ToTO", expected: "toto" },
{ input: "ToTo", expected: "toto" },
{ input: "TOTo", expected: "toto" },
])(
"should return the input in lowercase when all letters are uppercases",
({ input, expected }) => {
// Arrange
// Act
const result = convertToLowercase(input);
// Assert
expect(result).toStrictEqual(expected);
}
);
});

View File

@ -0,0 +1,3 @@
export function convertToLowercase(input: string): string {
return input.toLowerCase();
}

View File

@ -0,0 +1,37 @@
import { isBeforeNow } from "./index";
const dateBefore = new Date("2022-01-01T00:00:00.000Z");
const dateNow = new Date("2023-01-01T00:00:00.000Z");
const dateAfter = new Date("2024-01-01T00:00:00.000Z");
describe("isBeforeNow", () => {
beforeEach(() => {
jest.useFakeTimers({
now: dateNow,
});
// or
// jest.spyOn(Date, "now").mockReturnValue(dateNow.getTime());
});
afterEach(() => {
jest.useRealTimers();
// or
// jest.restoreAllMocks();
});
it("returns true if the date is before now", () => {
expect(isBeforeNow(dateBefore)).toBe(true);
});
it("returns false if the date is now", () => {
expect(isBeforeNow(dateNow)).toBe(false);
});
it("returns false if the date is after now", () => {
expect(isBeforeNow(dateAfter)).toBe(false);
});
});

View File

@ -0,0 +1,3 @@
export function isBeforeNow(date: Date): boolean {
return date.getTime() < Date.now();
}

View File

@ -0,0 +1,18 @@
import { square } from "./index";
describe("square", () => {
it("returns the number pow 2", () => {
// Arrange
const num = 3;
Math.pow = jest.fn(); //.mockReturnValue(2);
// Act
square(num);
// Assert
expect(Math.pow).toHaveBeenCalledTimes(1);
expect(Math.pow).toHaveBeenCalledWith(3, 2);
// expect(square(num)).toBe(2);
});
});

View File

@ -0,0 +1,3 @@
export function square(value: number): number {
return Math.pow(value, 2);
}

View File

@ -0,0 +1,18 @@
import { toUpperCase } from "./index";
describe("toUpperCase", () => {
it("returns the string in uppercase", () => {
// Arrange
const input = "hello";
String.prototype.toUpperCase = jest.fn(); //.mockReturnValue("BLABLA");
// Act
toUpperCase(input);
// Assert
expect(input.toUpperCase).toHaveBeenCalledTimes(1);
expect(input.toUpperCase).toHaveBeenCalledWith();
// expect(toUpperCase(input)).toBe("BLABLA");
});
});

View File

@ -0,0 +1,3 @@
export function toUpperCase(value: string): string {
return value.toUpperCase();
}

View File

@ -0,0 +1,47 @@
import { multiplyArrays } from "./index";
describe("multiplyArrays", () => {
it.each([
{
arr1: [1, 2, 3],
arr2: [4, 5, 6],
expected: [4, 10, 18],
},
{
arr1: [3, 2, 3],
arr2: [4, 5, 3],
expected: [12, 10, 9],
},
])("should multiply all numbers of 2 arrays", ({ arr1, arr2, expected }) => {
// Arrange
// Act
const result = multiplyArrays(arr1, arr2);
// Assert
expect(result).toStrictEqual(expected);
});
it("should throw when one array is empty", () => {
// Arrange
const arr1 = [1, 2, 3];
const arr2: number[] = [];
// Act
const act = () => multiplyArrays(arr1, arr2);
// Assert
expect(act).toThrow("Arrays must have at least one element");
});
it("should throw when arrays length is different", () => {
// Arrange
const arr1 = [1, 2, 3];
const arr2 = [4, 5];
// Act
const act = () => multiplyArrays(arr1, arr2);
// Assert
expect(act).toThrow("Arrays must have the same length");
});
});

View File

@ -0,0 +1,16 @@
export function multiplyArrays(a: number[], b: number[]): number[] {
if (a.length === 0 || b.length === 0) {
throw new Error("Arrays must have at least one element");
}
if (a.length !== b.length) {
throw new Error("Arrays must have the same length");
}
const output: number[] = new Array(a.length);
for (let i = 0; i < output.length; i++) {
output[i] = a[i] * b[i];
}
return output;
}

View File

@ -0,0 +1,44 @@
import { multiplyArrays } from "./index";
describe("multiplyArrays", () => {
it.each([
{ arr1: [1, 2, 3], arr2: [4, 5, 6], expected: [4, 10, 18] },
{ arr1: [-1, -2, -3], arr2: [4, -5, 6], expected: [-4, 10, -18] },
{
arr1: [-1.1, -2.1, -3.1],
arr2: [4, -5, 6],
expected: [-4.4, 10.5, -18.6],
},
])("should multiply two arrays", ({ arr1, arr2, expected }) => {
// arrange
// act
const result = multiplyArrays(arr1, arr2);
// assert
expect(result).toStrictEqual(expected);
});
it("should throw an error if arrays are not the same length", () => {
// arrange
const arr1 = [1, 2, 3];
const arr2 = [4, 5];
// act
const act = () => multiplyArrays(arr1, arr2);
// assert
expect(act).toThrow("Arrays must be the same length");
});
it("should throw an error if arrays are empty", () => {
// arrange
const arr1: number[] = [];
const arr2: number[] = [];
// act
const act = () => multiplyArrays(arr1, arr2);
// assert
expect(act).toThrow("Arrays must not be empty");
});
});

View File

@ -0,0 +1,10 @@
export function multiplyArrays(arr1: number[], arr2: number[]): number[] {
const isSameLength = arr1.length === arr2.length;
if (!isSameLength) throw new Error("Arrays must be the same length");
const isOneEmpty = arr1.length === 0 || arr2.length === 0;
if (isOneEmpty) throw new Error("Arrays must not be empty");
return arr1.map((num, index) => num * arr2[index]);
}

View File

@ -0,0 +1,14 @@
import { removeDuplicates } from "./index";
describe("removeDuplicates", () => {
it("should multiply all numbers of 2 arrays", () => {
// Arrange
const arr = [1, 2, 2, 2, 3, 3, 4];
// Act
const result = removeDuplicates(arr);
// Assert
expect(result).toStrictEqual([1, 2, 3, 4]);
});
});

View File

@ -0,0 +1,3 @@
export function removeDuplicates(a: number[]): number[] {
return Array.from(new Set(a));
}

View File

@ -0,0 +1,14 @@
import { removeDuplicates } from "./index";
describe("removeDuplicates", () => {
it("should remove duplicates from an array", () => {
// arrange
const arr = [1, 2, 2, 3, 4, 4, 5];
// act
const result = removeDuplicates(arr);
// assert
expect(result).toStrictEqual([1, 2, 3, 4, 5]);
});
});

View File

@ -0,0 +1,3 @@
export function removeDuplicates(arr: number[]): number[] {
return Array.from(new Set(arr));
}

View File

@ -0,0 +1,14 @@
import { throwAnException } from "./index";
describe("testAnException", () => {
it("should throw an exception", () => {
// Arrange
// Act
const act = () => {
throwAnException();
};
// Assert
expect(act).toThrow("I am an exception");
});
});

View File

@ -0,0 +1,3 @@
export function throwAnException(): void {
throw new Error("I am an exception");
}

View File

@ -0,0 +1,37 @@
import "reflect-metadata";
import { container } from "tsyringe";
import { AgeValidation } from "./index";
describe("AgeValidation", () => {
const ageValidation = container.resolve(AgeValidation);
describe("validateAge", () => {
it.each([
{ input: "1", expected: 1 },
{ input: "18", expected: 18 },
{ input: "100", expected: 100 },
])(
"should return a number when given a string if represents a valid age",
({ input, expected }) => {
// arrange
// act
const result = ageValidation.validateAge(input);
// assert
expect(result).toBe(expected);
}
);
it.each([{ input: "-2", expected: "Input must be a valid age" }])(
"should throw an error if input does not represent a valid age",
({ input, expected }) => {
// arrange
// act
const act = () => ageValidation.validateAge(input);
// assert
expect(act).toThrow(expected);
}
);
});
});

View File

@ -0,0 +1,18 @@
import { singleton } from "tsyringe";
import { NumberValidation } from "../number";
@singleton()
export class AgeValidation {
public constructor(private numberValidation: NumberValidation) {}
public validateAge(input: string) {
try {
let number: number = this.numberValidation.validateInteger(input);
number = this.numberValidation.validatePositive(input);
return number;
} catch (error) {
throw new Error("Input must be a valid age");
}
}
}

View File

@ -0,0 +1,109 @@
import "reflect-metadata"
import { container } from "tsyringe";
import { NumberValidation } from "./index";
describe("NumberValidation", () => {
const numberValidation = container.resolve(NumberValidation);
describe("validateNumber", () => {
it.each([
{ input: "0", expected: 0 },
{ input: "1", expected: 1 },
{ input: "-1", expected: -1 },
{ input: "1.67", expected: 1.67 },
{ input: "-1.67", expected: -1.67 },
{ input: "-1e7", expected: -1e7 },
])(
"should return a number when given a string if valid",
({ input, expected }) => {
// arrange
// act
const result = numberValidation.validateNumber(input);
// assert
expect(result).toBe(expected);
}
);
it("should throw an error if input is not a number", () => {
// arrange
const input = "a";
// act
const act = () => numberValidation.validateNumber(input);
// assert
expect(act).toThrow("Input must be a number");
});
});
describe("validateInteger", () => {
it.each([
{ input: "0", expected: 0 },
{ input: "1", expected: 1 },
{ input: "-1", expected: -1 },
{ input: "1e3", expected: 1e3 },
])(
"should return an integer when given a string if valid",
({ input, expected }) => {
// arrange
// act
const result = numberValidation.validateInteger(input);
// assert
expect(result).toBe(expected);
}
);
it.each([
{ input: "1.67", expected: "Input must be an integer" },
{ input: "-1.67", expected: "Input must be an integer" },
{ input: "1e-3", expected: "Input must be an integer" },
])(
"should throw an error if input is not an integer",
({ input, expected }) => {
// arrange
// act
const act = () => numberValidation.validateInteger(input);
// assert
expect(act).toThrow(expected);
}
);
});
describe("validatePositive", () => {
it.each([
{ input: "0", expected: 0 },
{ input: "1", expected: 1 },
{ input: "1.67", expected: 1.67 },
{ input: "1e3", expected: 1e3 },
])(
"should return a positive number when given a string if valid",
({ input, expected }) => {
// arrange
// act
const result = numberValidation.validatePositive(input);
// assert
expect(result).toBe(expected);
}
);
it.each([
{ input: "-1", expected: "Input must be positive" },
{ input: "-1.67", expected: "Input must be positive" },
{ input: "-1e3", expected: "Input must be positive" },
])(
"should throw an error if input is not positive",
({ input, expected }) => {
// arrange
// act
const act = () => numberValidation.validatePositive(input);
// assert
expect(act).toThrow(expected);
}
);
});
});

View File

@ -0,0 +1,34 @@
import { singleton } from "tsyringe";
@singleton()
export class NumberValidation {
public validateNumber(input: string): number {
const number = Number(input);
if (isNaN(number)) {
throw new Error("Input must be a number");
}
return number;
}
public validateInteger(input: string): number {
const number = this.validateNumber(input);
if (!Number.isInteger(number)) {
throw new Error("Input must be an integer");
}
return number;
}
public validatePositive(input: string): number {
const number = this.validateNumber(input);
if (number < 0) {
throw new Error("Input must be positive");
}
return number;
}
}