feat: Test running overhaul, switch to Prettier & reformat everything (#1407)

* chore: Switch to Node 20 + Vitest

* chore: migrate to vitest mock functions

* chore: code style (switch to prettier)

* test: re-enable long-running test

Seems the switch to Node 20 and Vitest has vastly improved the code's and / or the test's runtime!

see #1193

* chore: code style

* chore: fix failing tests

* Updated Documentation in README.md

* Update contribution guidelines to state usage of Prettier

* fix: set prettier printWidth back to 80

* chore: apply updated code style automatically

* fix: set prettier line endings to lf again

* chore: apply updated code style automatically

---------

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
Co-authored-by: Lars Müller <34514239+appgurueu@users.noreply.github.com>
This commit is contained in:
Roland Hummel
2023-10-03 23:08:19 +02:00
committed by GitHub
parent 0ca18c2b2c
commit 86d333ee94
392 changed files with 5849 additions and 16622 deletions

View File

@ -22,14 +22,14 @@
*/
class Combinations {
constructor (n, k) {
constructor(n, k) {
this.n = n
this.k = k
this.current = [] // will be used for storing current combination
this.combinations = []
}
findCombinations (high = this.n, total = this.k, low = 1) {
findCombinations(high = this.n, total = this.k, low = 1) {
if (total === 0) {
this.combinations.push([...this.current])
return this.combinations

View File

@ -10,14 +10,14 @@
*/
const swap = (arr, i, j) => {
const newArray = [...arr];
const newArray = [...arr]
[newArray[i], newArray[j]] = [newArray[j], newArray[i]] // Swapping elements ES6 way
;[newArray[i], newArray[j]] = [newArray[j], newArray[i]] // Swapping elements ES6 way
return newArray
}
const permutations = arr => {
const permutations = (arr) => {
const P = []
const permute = (arr, low, high) => {
if (low === high) {

View File

@ -1,12 +1,12 @@
// Wikipedia: https://en.wikipedia.org/wiki/Knight%27s_tour
class OpenKnightTour {
constructor (size) {
constructor(size) {
this.board = new Array(size).fill(0).map(() => new Array(size).fill(0))
this.size = size
}
getMoves ([i, j]) {
getMoves([i, j]) {
// helper function to get the valid moves of the knight from the current position
const moves = [
[i + 2, j - 1],
@ -19,15 +19,17 @@ class OpenKnightTour {
[i - 1, j + 2]
]
return moves.filter(([y, x]) => y >= 0 && y < this.size && x >= 0 && x < this.size)
return moves.filter(
([y, x]) => y >= 0 && y < this.size && x >= 0 && x < this.size
)
}
isComplete () {
isComplete() {
// helper function to check if the board is complete
return !this.board.map(row => row.includes(0)).includes(true)
return !this.board.map((row) => row.includes(0)).includes(true)
}
solve () {
solve() {
// function to find the solution for the given board
for (let i = 0; i < this.size; i++) {
for (let j = 0; j < this.size; j++) {
@ -37,7 +39,7 @@ class OpenKnightTour {
return false
}
solveHelper ([i, j], curr) {
solveHelper([i, j], curr) {
// helper function for the main computation
if (this.isComplete()) return true
@ -52,7 +54,7 @@ class OpenKnightTour {
return false
}
printBoard (output = value => console.log(value)) {
printBoard(output = (value) => console.log(value)) {
// utility function to display the board
for (const row of this.board) {
let string = ''

View File

@ -1,5 +1,5 @@
class NQueens {
constructor (size) {
constructor(size) {
if (size < 0) {
throw RangeError('Invalid board size')
}
@ -8,7 +8,7 @@ class NQueens {
this.solutionCount = 0
}
isValid ([row, col]) {
isValid([row, col]) {
// function to check if the placement of the queen in the given location is valid
// checking the left of the current row
@ -29,15 +29,15 @@ class NQueens {
return true
}
placeQueen (row, col) {
placeQueen(row, col) {
this.board[row][col] = 'Q'
}
removeQueen (row, col) {
removeQueen(row, col) {
this.board[row][col] = '.'
}
solve (col = 0) {
solve(col = 0) {
if (col >= this.size) {
this.solutionCount++
return true
@ -54,7 +54,7 @@ class NQueens {
return false
}
printBoard (output = value => console.log(value)) {
printBoard(output = (value) => console.log(value)) {
if (!output._isMockFunction) {
output('\n')
}

View File

@ -21,19 +21,23 @@
* @param grid The grid to check.
* @throws TypeError When the given grid is invalid.
*/
function validateGrid (grid) {
if (!Array.isArray(grid) || grid.length === 0) throw new TypeError('Grid must be a non-empty array')
function validateGrid(grid) {
if (!Array.isArray(grid) || grid.length === 0)
throw new TypeError('Grid must be a non-empty array')
const allRowsHaveCorrectLength = grid.every(row => row.length === grid.length)
const allRowsHaveCorrectLength = grid.every(
(row) => row.length === grid.length
)
if (!allRowsHaveCorrectLength) throw new TypeError('Grid must be a square')
const allCellsHaveValidValues = grid.every(row => {
return row.every(cell => cell === 0 || cell === 1)
const allCellsHaveValidValues = grid.every((row) => {
return row.every((cell) => cell === 0 || cell === 1)
})
if (!allCellsHaveValidValues) throw new TypeError('Grid must only contain 0s and 1s')
if (!allCellsHaveValidValues)
throw new TypeError('Grid must only contain 0s and 1s')
}
function isSafe (grid, x, y) {
function isSafe(grid, x, y) {
const n = grid.length
return x >= 0 && x < n && y >= 0 && y < n && grid[y][x] === 1
}
@ -48,7 +52,7 @@ function isSafe (grid, x, y) {
* @param path The path we took to get from the source cell to the current location.
* @returns {string|boolean} Either the path to the target cell or false.
*/
function getPathPart (grid, x, y, solution, path) {
function getPathPart(grid, x, y, solution, path) {
const n = grid.length
// are we there yet?
@ -89,7 +93,7 @@ function getPathPart (grid, x, y, solution, path) {
return false
}
function getPath (grid) {
function getPath(grid) {
// grid dimensions
const n = grid.length
@ -108,7 +112,7 @@ function getPath (grid) {
* Creates an instance of the "rat in a maze" based on a given grid (maze).
*/
export class RatInAMaze {
constructor (grid) {
constructor(grid) {
// first, let's do some error checking on the input
validateGrid(grid)

View File

@ -1,10 +1,10 @@
class Sudoku {
// Sudoku Class to hold the board and related functions
constructor (board) {
constructor(board) {
this.board = board
}
findEmptyCell () {
findEmptyCell() {
// Find a empty cell in the board (returns [-1, -1] if all cells are filled)
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
@ -14,7 +14,7 @@ class Sudoku {
return [-1, -1]
}
check ([y, x], value) {
check([y, x], value) {
// checks if the value to be added in the board is an acceptable value for the cell
// checking through the row
@ -29,8 +29,8 @@ class Sudoku {
// checking through the 3x3 block of the cell
const secRow = Math.floor(y / 3)
const secCol = Math.floor(x / 3)
for (let i = (secRow * 3); i < ((secRow * 3) + 3); i++) {
for (let j = (secCol * 3); j < ((secCol * 3) + 3); j++) {
for (let i = secRow * 3; i < secRow * 3 + 3; i++) {
for (let j = secCol * 3; j < secCol * 3 + 3; j++) {
if (y !== i && x !== j && this.board[i][j] === value) return false
}
}
@ -38,7 +38,7 @@ class Sudoku {
return true
}
solve () {
solve() {
const [y, x] = this.findEmptyCell()
// checking if the board is complete
@ -56,20 +56,23 @@ class Sudoku {
return false
}
getSection (row, [start, end]) {
getSection(row, [start, end]) {
return this.board[row].slice(start, end)
}
printBoard (output = (...v) => console.log(...v)) {
printBoard(output = (...v) => console.log(...v)) {
// helper function to display board
for (let i = 0; i < 9; i++) {
if (i % 3 === 0 && i !== 0) {
output('- - - - - - - - - - - -')
}
output(
...this.getSection(i, [0, 3]), ' | ',
...this.getSection(i, [3, 6]), ' | ',
...this.getSection(i, [6, 9]))
...this.getSection(i, [0, 3]),
' | ',
...this.getSection(i, [3, 6]),
' | ',
...this.getSection(i, [6, 9])
)
}
}
}

View File

@ -3,11 +3,22 @@ import { Combinations } from '../AllCombinationsOfSizeK'
describe('AllCombinationsOfSizeK', () => {
it('should return 3x2 matrix solution for n = 3 and k = 2', () => {
const test1 = new Combinations(3, 2)
expect(test1.findCombinations()).toEqual([[1, 2], [1, 3], [2, 3]])
expect(test1.findCombinations()).toEqual([
[1, 2],
[1, 3],
[2, 3]
])
})
it('should return 6x2 matrix solution for n = 4 and k = 2', () => {
const test2 = new Combinations(4, 2)
expect(test2.findCombinations()).toEqual([[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]])
expect(test2.findCombinations()).toEqual([
[1, 2],
[1, 3],
[1, 4],
[2, 3],
[2, 4],
[3, 4]
])
})
})

View File

@ -1,5 +1,11 @@
import { generateParentheses } from '../generateParentheses'
test('generate all valid parentheses of input 3', () => {
expect(generateParentheses(3)).toStrictEqual(['((()))', '(()())', '(())()', '()(())', '()()()'])
expect(generateParentheses(3)).toStrictEqual([
'((()))',
'(()())',
'(())()',
'()(())',
'()()()'
])
})

View File

@ -14,6 +14,8 @@ describe('NQueens', () => {
})
it('should throw RangeError for negative size board', () => {
expect(() => { return new NQueens(-1) }).toThrow(RangeError)
expect(() => {
return new NQueens(-1)
}).toThrow(RangeError)
})
})

View File

@ -7,14 +7,18 @@ describe('RatInAMaze', () => {
for (const value of values) {
// we deliberately want to check whether this constructor call fails or not
// eslint-disable-next-line no-new
expect(() => { new RatInAMaze(value) }).toThrow()
expect(() => {
new RatInAMaze(value)
}).toThrow()
}
})
it('should fail for an empty array', () => {
// we deliberately want to check whether this constructor call fails or not
// eslint-disable-next-line no-new
expect(() => { new RatInAMaze([]) }).toThrow()
expect(() => {
new RatInAMaze([])
}).toThrow()
})
it('should fail for a non-square array', () => {
@ -25,7 +29,9 @@ describe('RatInAMaze', () => {
// we deliberately want to check whether this constructor call fails or not
// eslint-disable-next-line no-new
expect(() => { new RatInAMaze(array) }).toThrow()
expect(() => {
new RatInAMaze(array)
}).toThrow()
})
it('should fail for arrays containing invalid values', () => {
@ -34,7 +40,9 @@ describe('RatInAMaze', () => {
for (const value of values) {
// we deliberately want to check whether this constructor call fails or not
// eslint-disable-next-line no-new
expect(() => { new RatInAMaze(value) }).toThrow()
expect(() => {
new RatInAMaze(value)
}).toThrow()
}
})
@ -51,13 +59,20 @@ describe('RatInAMaze', () => {
})
it('should work for a simple 3x3 maze', () => {
const maze = new RatInAMaze([[1, 1, 0], [0, 1, 0], [0, 1, 1]])
const maze = new RatInAMaze([
[1, 1, 0],
[0, 1, 0],
[0, 1, 1]
])
expect(maze.solved).toBe(true)
expect(maze.path).toBe('RDDR')
})
it('should work for a simple 2x2 that can not be solved', () => {
const maze = new RatInAMaze([[1, 0], [0, 1]])
const maze = new RatInAMaze([
[1, 0],
[0, 1]
])
expect(maze.solved).toBe(false)
expect(maze.path).toBe('')
})

View File

@ -28,7 +28,9 @@ describe('Sudoku', () => {
it('should create a valid board successfully', () => {
// we deliberately want to check whether this constructor call fails or not
// eslint-disable-next-line no-new
expect(() => { new Sudoku(data) }).not.toThrow()
expect(() => {
new Sudoku(data)
}).not.toThrow()
})
it('should find an empty cell', () => {