Skip to content

Commit

Permalink
refactor: rewrite with modern standard
Browse files Browse the repository at this point in the history
  • Loading branch information
saahil-mahato committed Oct 1, 2024
1 parent e727eb6 commit bade28f
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 52 deletions.
88 changes: 70 additions & 18 deletions Geometry/Test/Triangle.test.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,94 @@
import Triangle from '../Triangle'

describe('Triangle', () => {
describe('Triangle created with sides', () => {
const triangle = new Triangle(3, 4, 5)
describe('Constructor', () => {
test('creates a triangle with base and height', () => {
const triangle = new Triangle(5, 10)
expect(triangle).toBeInstanceOf(Triangle)
expect(triangle.base).toBe(5)
expect(triangle.height).toBe(10)
expect(triangle.sides).toEqual([5, null, null])
})

test('creates a triangle with three sides', () => {
const triangle = new Triangle(3, 4, 5)
expect(triangle).toBeInstanceOf(Triangle)
expect(triangle.sides).toEqual([3, 4, 5])
expect(triangle.base).toBe(3) // Assuming base is one of the sides
expect(triangle.height).toBe(null)
})

test('The area of a triangle with sides 3, 4, 5', () => {
expect(parseFloat(triangle.area().toFixed(2))).toEqual(6.0)
test('throws an error if invalid number of arguments', () => {
expect(() => new Triangle()).toThrow(
'Invalid number of arguments. Use either (base, height) or (sideA, sideB, sideC).'
)
expect(() => new Triangle(1)).toThrow(
'Invalid number of arguments. Use either (base, height) or (sideA, sideB, sideC).'
)
})

test('The perimeter of a triangle with sides 3, 4, 5', () => {
expect(parseFloat(triangle.perimeter().toFixed(2))).toEqual(12.0)
test('throws an error if the triangle is invalid', () => {
expect(() => new Triangle(1, 2, 3)).toThrow(
'Invalid triangle: The sum of any two sides must be greater than the third side.'
)
})
})

describe('Triangle created with base and height', () => {
const triangle = new Triangle(6, 4)
describe('Area Calculation', () => {
test('calculates area correctly using base and height', () => {
const triangle = new Triangle(5, 10)
expect(triangle.area()).toBe(25)
})

test("calculates area correctly using Heron's formula", () => {
const triangle = new Triangle(3, 4, 5)
expect(triangle.area()).toBe(6) // Area of a 3-4-5 triangle
})
})

test('The area of a triangle with base 6 and height 4', () => {
expect(parseFloat(triangle.area().toFixed(2))).toEqual(12.0)
describe('Perimeter Calculation', () => {
test('calculates perimeter correctly for three sides', () => {
const triangle = new Triangle(3, 4, 5)
expect(triangle.perimeter()).toBe(12) // 3 + 4 + 5
})

test('The perimeter calculation throws an error for base-height triangle', () => {
test('throws an error if not all sides are known', () => {
const triangle = new Triangle(5, 10)
expect(() => triangle.perimeter()).toThrow(
'Cannot calculate perimeter: not all sides are known.'
)
})
})

describe('Invalid triangle creation', () => {
test('Creating a triangle with invalid sides throws an error', () => {
expect(() => new Triangle(1, 1, 10)).toThrow(
'Invalid triangle: The sum of any two sides must be greater than the third side.'
describe('Getters', () => {
test('base getter returns correct value', () => {
const triangle = new Triangle(5, 10)
expect(triangle.base).toBe(5)
})

test('height getter returns correct value', () => {
const triangle = new Triangle(5, 10)
expect(triangle.height).toBe(10)
})

test('sides getter returns correct values', () => {
const triangle = new Triangle(3, 4, 5)
expect(triangle.sides).toEqual([3, 4, 5])
})
})

describe('String Representation', () => {
test('returns correct string representation for base and height', () => {
const triangle = new Triangle(5, 10)
expect(triangle.toString()).toBe(
'Triangle: base = 5, height = 10, sides = 5, unknown, unknown, area = 25, perimeter = unknown'
)
})

test('Creating a triangle with invalid number of arguments throws an error', () => {
expect(() => new Triangle(1)).toThrow(
'Invalid number of arguments. Use either (base, height) or (sideA, sideB, sideC).'
test('returns correct string representation for three sides', () => {
const triangle = new Triangle(3, 4, 5)
expect(triangle.toString()).toBe(
'Triangle: base = 3, height = unknown, sides = 3, 4, 5, area = 6, perimeter = 12'
)
})
})
Expand Down
130 changes: 96 additions & 34 deletions Geometry/Triangle.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
/**
* This class represents a Triangle and can calculate its area and perimeter with either all three sides or the base and height.
* https://en.wikipedia.org/wiki/Triangle
* Represents a Triangle and provides methods to calculate its area and perimeter
* based on either all three sides or the base and height.
* @see {@link https://en.wikipedia.org/wiki/Triangle|Triangle}
* @class
*/
export default class Triangle {
/** @private */
#base

/** @private */
#height

/** @private */
#sides

/**
* Create a triangle.
* Creates a triangle instance.
* @constructor
* @param {...number} args - The triangle dimensions.
* @param {...number} args - The dimensions of the triangle.
* @throws {Error} Will throw an error if the number of arguments is invalid or if the triangle is invalid.
*/
constructor(...args) {
if (args.length === 2) {
this.initializeFromBaseAndHeight(...args)
this.#initializeFromBaseAndHeight(...args)
} else if (args.length === 3) {
this.initializeFromSides(...args)
this.#initializeFromSides(...args)
} else {
throw new Error(
'Invalid number of arguments. Use either (base, height) or (sideA, sideB, sideC).'
Expand All @@ -23,73 +33,125 @@ export default class Triangle {
}

/**
* Initialize the triangle from base and height.
* Initializes the triangle from base and height.
* @private
* @param {number} base - The base of the triangle.
* @param {number} height - The height of the triangle.
* @private
*/
initializeFromBaseAndHeight = (base, height) => {
this.base = base
this.height = height
this.sides = [base, null, null]
#initializeFromBaseAndHeight(base, height) {
this.#base = base
this.#height = height
this.#sides = [base, null, null]
}

/**
* Initialize the triangle from three sides.
* Initializes the triangle from three sides.
* @private
* @param {number} sideA - The length of side A.
* @param {number} sideB - The length of side B.
* @param {number} sideC - The length of side C.
* @throws {Error} Will throw an error if the triangle is invalid.
* @private
*/
initializeFromSides = (sideA, sideB, sideC) => {
if (!this.isValidTriangle(sideA, sideB, sideC)) {
#initializeFromSides(sideA, sideB, sideC) {
if (!this.#isValidTriangle(sideA, sideB, sideC)) {
throw new Error(
'Invalid triangle: The sum of any two sides must be greater than the third side.'
)
}
this.sides = [sideA, sideB, sideC]
this.base = sideA
this.height = null
this.#sides = [sideA, sideB, sideC]
this.#base = sideA // Assuming base is one of the sides for consistency
this.#height = null
}

/**
* Check if three sides can form a valid triangle.
* Checks if three sides can form a valid triangle.
* @private
* @param {number} a - The length of side A.
* @param {number} b - The length of side B.
* @param {number} c - The length of side C.
* @returns {boolean} True if the triangle is valid, false otherwise.
* @private
* @returns {boolean} True if the triangle is valid; false otherwise.
*/
isValidTriangle = (a, b, c) => {
#isValidTriangle(a, b, c) {
return a + b > c && b + c > a && c + a > b
}

/**
* Calculate the area of the triangle.
* @returns {number} The area of the triangle.
* Calculates the area of the triangle.
* @public
* @returns {number} The area of the triangle.
*/
area = () => {
if (this.height !== null) {
return 0.5 * this.base * this.height
area() {
if (this.#height !== null) {
return 0.5 * this.#base * this.#height
}
// Using Heron's formula
const [a, b, c] = this.sides
const [a, b, c] = this.#sides
const s = (a + b + c) / 2
return Math.sqrt(s * (s - a) * (s - b) * (s - c))
}

/**
* Calculate the perimeter of the triangle.
* Calculates the perimeter of the triangle.
* @public
* @returns {number} The perimeter of the triangle.
* @throws {Error} Will throw an error if not all sides are known.
* @public
*/
perimeter = () => {
if (this.sides.some((side) => side === null)) {
perimeter() {
if (this.#sides.some((side) => side === null)) {
throw new Error('Cannot calculate perimeter: not all sides are known.')
}
return this.sides.reduce((sum, side) => sum + side, 0)
return this.#sides.reduce((sum, side) => sum + side, 0)
}

/**
* Returns a string representation of the triangle.
* @public
* @returns {string} A string describing the triangle's dimensions and area/perimeter.
*/
toString() {
const areaValue = this.area()
let perimeterValue

// Check if all sides are known for perimeter calculation
if (this.#sides.some((side) => side === null)) {
perimeterValue = 'unknown'
} else {
perimeterValue = this.perimeter()
}

return (
`Triangle: base = ${this.#base}, height = ${
this.#height ?? 'unknown'
}, ` +
`sides = ${this.#sides.map((side) => side ?? 'unknown').join(', ')}, ` +
`area = ${areaValue}, perimeter = ${perimeterValue}`
)
}

/**
* Gets the base of the triangle.
* @public
* @returns {number} The base of the triangle.
*/
get base() {
return this.#base
}

/**
* Gets the height of the triangle.
* @public
* @returns {number|null} The height of the triangle or null if not defined.
*/
get height() {
return this.#height
}

/**
* Gets the lengths of all sides of the triangle.
* @public
* @returns {Array<number|null>} An array containing lengths of all three sides or null for unknown sides.
*/
get sides() {
return this.#sides
}
}

0 comments on commit bade28f

Please sign in to comment.