Skip to content

Commit

Permalink
added full test for React config
Browse files Browse the repository at this point in the history
  • Loading branch information
mangs committed Jan 5, 2024
1 parent 0316adb commit 82c1dca
Show file tree
Hide file tree
Showing 8 changed files with 211 additions and 11 deletions.
49 changes: 41 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"list:todo-comments": "rg --only-matching '(TODO|FIXME):[a-zA-Z0-9\\t .,;?]+'",
"reinstall": "npm run --silent delete:node_modules && npm run --silent delete:package-lock && npm i",
"reinstall:use-lock-file": "npm run --silent delete:node_modules && npm ci",
"test": "eslint --max-warnings 0 --report-unused-disable-directives test/**/*.{js,mjs}",
"test": "eslint --max-warnings 0 --report-unused-disable-directives test/**/*.{js,jsx,mjs}",
"validate:formatting": "prettier --check --no-editorconfig .",
"validate:linting:eslint": "eslint-config-prettier ./lib/eslintBaseConfig.json"
},
Expand Down Expand Up @@ -66,6 +66,9 @@
"eslint": "8.56.0",
"jest": "29.7.0",
"prettier": "3.1.0",
"prop-types": "15.8.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"typescript": "5.3.3"
},
"peerDependencies": {
Expand Down
8 changes: 7 additions & 1 deletion test/eslintReactConfig/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
{
"root": true,
"extends": ["../../lib/eslintReactConfig.json"]
"extends": ["../../lib/eslintReactConfig.json", "../../lib/eslintBrowserConfig.json"],
"overrides": [
{
"files": "./components/*.jsx",
"rules": { "unicorn/filename-case": ["error", { "case": "pascalCase" }] }
}
]
}
73 changes: 73 additions & 0 deletions test/eslintReactConfig/components/Board.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// External Imports
import PropTypes from 'prop-types';
import React from 'react';

// Internal Imports
import { Square } from './Square.jsx';

// Local Functions
function calculateWinner(squares) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
for (const [a, b, c] of lines) {
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}
// eslint-disable-next-line unicorn/no-null -- return an explicit null value
return null;
}

// Component Definition
function Board({ onPlay, squares, xIsNext }) {
function handleClick(index) {
if (calculateWinner(squares) || squares[index]) {
return;
}
const nextSquares = [...squares];
nextSquares[index] = xIsNext ? 'X' : 'O';
onPlay(nextSquares);
}

const winner = calculateWinner(squares);
const status = winner ? `Winner: ${winner}` : `Next player: ${xIsNext ? 'X' : 'O'}`;

return (
<>
<div className="status">{status}</div>
<div className="board-row">
<Square value={squares[0]} onSquareClick={() => handleClick(0)} />
<Square value={squares[1]} onSquareClick={() => handleClick(1)} />
<Square value={squares[2]} onSquareClick={() => handleClick(2)} />
</div>
<div className="board-row">
<Square value={squares[3]} onSquareClick={() => handleClick(3)} />
<Square value={squares[4]} onSquareClick={() => handleClick(4)} />
<Square value={squares[5]} onSquareClick={() => handleClick(5)} />
</div>
<div className="board-row">
<Square value={squares[6]} onSquareClick={() => handleClick(6)} />
<Square value={squares[7]} onSquareClick={() => handleClick(7)} />
<Square value={squares[8]} onSquareClick={() => handleClick(8)} />
</div>
</>
);
}

// Prop Types
Board.propTypes = {
onPlay: PropTypes.func.isRequired,
squares: PropTypes.arrayOf(PropTypes.number).isRequired,
xIsNext: PropTypes.bool.isRequired,
};

// Module Exports
export { Board };
53 changes: 53 additions & 0 deletions test/eslintReactConfig/components/Game.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// External Imports
import React, { useCallback, useState } from 'react';

// Internal Imports
import { Board } from './Board.jsx';

// Component Definition
function Game() {
// eslint-disable-next-line unicorn/no-null -- the game expects explicit null values
const [history, setHistory] = useState([Array.from({ length: 9 }).fill(null)]);
const [currentMove, setCurrentMove] = useState(0);
const handlePlay = useCallback(
(nextSquares) => {
const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];
setHistory(nextHistory);
setCurrentMove(nextHistory.length - 1);
},
[currentMove, history],
);

function jumpTo(nextMove) {
setCurrentMove(nextMove);
}

const xIsNext = currentMove % 2 === 0;
const currentSquares = history[currentMove];

const moves = history.map((squares, move) => {
const description = move > 0 ? `Go to move #${move}` : 'Go to game start';
return (
// eslint-disable-next-line react/no-array-index-key -- this is the best unique value
<li key={move}>
<button type="button" onClick={() => jumpTo(move)}>
{description}
</button>
</li>
);
});

return (
<div className="game">
<div className="game-board">
<Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} />
</div>
<div className="game-info">
<ol>{moves}</ol>
</div>
</div>
);
}

// Module Exports
export { Game };
21 changes: 21 additions & 0 deletions test/eslintReactConfig/components/Square.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// External Imports
import PropTypes from 'prop-types';
import React from 'react';

// Component Definition
function Square({ onSquareClick, value }) {
return (
<button type="button" className="square" onClick={onSquareClick}>
{value}
</button>
);
}

// Prop Types
Square.propTypes = {
onSquareClick: PropTypes.func.isRequired,
value: PropTypes.string.isRequired,
};

// Module Export
export { Square };
12 changes: 12 additions & 0 deletions test/eslintReactConfig/exampleReactConfigEntrypoint.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Code adapted from here: https://react.dev/learn/tutorial-tic-tac-toe#what-are-you-building

// External Imports
import { createRoot } from 'react-dom/client';
import React from 'react';

// Internal Imports
import { Game } from './components/Game.jsx';

// Render the Application
const reactRoot = createRoot(document.querySelector('#react-root'));
reactRoot.render(<Game />);
1 change: 0 additions & 1 deletion test/eslintReactConfig/exampleReactConfigEntrypoint.mjs

This file was deleted.

0 comments on commit 82c1dca

Please sign in to comment.