Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eyes example - screenshot comparison #8

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"jsx-a11y/no-static-element-interactions": "off",
"jsx-a11y/click-events-have-key-events": "off",
"no-case-declarations": "off",
"no-debugger": "off",
"no-console": "off",
"no-mixed-operators": "off",
"no-multi-assign": "off",
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.env
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ The end result will be something like this:
1. Complete game winning logic: write unit tests for the different game winning scenarios (all rows, columns/diagonals/tie).
2. Write a component test verifiying a user cannot press a non empty cell.
3. Write a component test for a tie (show a "It's a tie!" message).
4. Write a browser test for marking next user in a special color (show 'X' and 'O' users and mark next user in special color).
5. Write a component test for displaying the number of wins next to each user: Win a game. Press a "new game" button, and win the game again.

4. Write a browser/component test for marking next user in a special color (show 'X' and 'O' users and mark next user in special color).
5. Write a browser/component test for hiding registration form after game starts.
6. Write a browser/component test for hiding game board before game starts.
7. Bonus: Write a browser test for user saving / loading existing game state (use local storage for that)
7. Bonus: Write a component test for displaying the number of wins next to each user: Win a game. Press a "new game" button, and win the game again. (Use local storage for that as well)
108 changes: 108 additions & 0 deletions package-lock.json

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

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,5 +104,8 @@
},
"eslintConfig": {
"extends": "react-app"
},
"devDependencies": {
"puppeteer-eyes.it": "^1.0.27"
}
}
4 changes: 4 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,8 @@ td {
border: 1px solid black;
height: 50px;
width: 50px;
}

.next {
border: 2px blue solid;
}
4 changes: 3 additions & 1 deletion src/App.driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const appDriver = () => {
let wrapper;
return {
render: node => {
wrapper = mount(node, { attachTo: document.createElement('div') });
wrapper = mount(node);
return wrapper;
},
newGame: (p1Name, p2Name) => {
Expand All @@ -23,6 +23,8 @@ const appDriver = () => {
.at(index)
.text(),
getWinnerMessage: () => wrapper.find('[data-hook="winner-message"]').text(),
isGameVisible: () => wrapper.find('[data-hook="cell"]').exists(),
isRegistrationVisible: () => wrapper.find('[data-hook="p1-input"]').exists(),
};
};

Expand Down
54 changes: 40 additions & 14 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import Registration from './Registration';
import Game from './Game';
import WinnerMessage from './WinnerMessage';
import { gameStatus } from './gameService';
import './App.css';

Expand All @@ -13,36 +14,61 @@ class App extends React.Component {
board: [['', '', ''], ['', '', ''], ['', '', '']],
winner: '',
currentPlayer: 'X',
gameStarted: false,
};
}
onNewGame = ({ p1Name, p2Name }) => {
this.setState({ p1Name, p2Name });
this.setState({ p1Name, p2Name, gameStarted: true });
};

loadGame = () => {
const gameState = localStorage.getItem('game');
this.setState(JSON.parse(gameState));
};
saveGame = () => {
const { p1Name, p2Name, board, gameStarted, currentPlayer } = this.state;
localStorage.setItem(
'game',
JSON.stringify({ p1Name, p2Name, board, gameStarted, currentPlayer }),
);
};

handleCellClick = (rIndex, cIndex) => {
const board = this.state.board.map(row => [...row]);
if (board[rIndex][cIndex]) {
return;
}
board[rIndex][cIndex] = this.state.currentPlayer;
if (gameStatus(board) === this.state.currentPlayer) {
this.setState({ winner: this.state.currentPlayer });
const winner = gameStatus(board);
if (winner) {
this.setState({ winner });
}
const nextPlayer = this.state.currentPlayer === 'X' ? 'O' : 'X';
this.setState({ board, currentPlayer: nextPlayer });
};
render() {
const { p1Name, p2Name, winner, board, currentPlayer, gameStarted } = this.state;
return (
<div className="App">
<Registration onNewGame={this.onNewGame} />
<Game
onCellClicked={this.handleCellClick}
board={this.state.board}
p1Name={this.state.p1Name}
p2Name={this.state.p2Name}
/>
{this.state.winner && (
<div data-hook="winner-message">
{`${this.state.winner === 'X' ? this.state.p1Name : this.state.p2Name} won!`}
</div>
{!gameStarted && <Registration onNewGame={this.onNewGame} />}
{gameStarted && (
<Game
onCellClicked={this.handleCellClick}
board={board}
p1Name={p1Name}
p2Name={p2Name}
currentPlayer={currentPlayer}
/>
)}
<WinnerMessage p1Name={p1Name} p2Name={p2Name} winner={winner} />
<div>
<button data-hook="save-game" onClick={this.saveGame}>
Save
</button>
<button data-hook="load-game" onClick={this.loadGame}>
Load
</button>
</div>
</div>
);
}
Expand Down
40 changes: 40 additions & 0 deletions src/App.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,43 @@ test('"O" should win the game', () => {
driver.clickACellAt(2);
expect(driver.getWinnerMessage()).toBe(`${p2Name} won!`);
});

test('cannot click a cell twice', () => {
const p1Name = 'Yaniv';
const p2Name = 'Computer';
driver.render(<App />);
driver.newGame(p1Name, p2Name);
driver.clickACellAt(4);
driver.clickACellAt(4);
expect(driver.getACellAt(4)).toBe('X');
});

test('should have a tie', () => {
const p1Name = 'Yaniv';
const p2Name = 'Computer';
driver.render(<App />);
driver.newGame(p1Name, p2Name);
driver.clickACellAt(0);
driver.clickACellAt(1);
driver.clickACellAt(2);
driver.clickACellAt(3);
driver.clickACellAt(4);
driver.clickACellAt(6);
driver.clickACellAt(5);
driver.clickACellAt(8);
driver.clickACellAt(7);
expect(driver.getWinnerMessage()).toBe(`it is a tie!`);
});

test('should hide game before registration', () => {
driver.render(<App />);
expect(driver.isGameVisible()).toBe(false);
});

test('should hide regsitration after a game started', () => {
const p1Name = 'Yaniv';
const p2Name = 'Computer';
driver.render(<App />);
driver.newGame(p1Name, p2Name);
expect(driver.isRegistrationVisible()).toBe(false);
});
11 changes: 8 additions & 3 deletions src/Game.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import React from 'react';
import PropTypes from 'prop-types';

const Game = ({ p1Name, p2Name, board, onCellClicked }) => {
const Game = ({ p1Name, p2Name, board, onCellClicked, currentPlayer }) => {
return (
<div>
<span data-hook="p1-name">{p1Name}</span>
<span data-hook="p2-name">{p2Name}</span>
<span className={currentPlayer === 'X' ? 'next' : ''} data-hook="p1-name">
{p1Name}
</span>
<span className={currentPlayer === 'O' ? 'next' : ''} data-hook="p2-name">
{p2Name}
</span>
<table role="grid">
<tbody>
{board.map((row, rIndex) => (
Expand Down Expand Up @@ -33,5 +37,6 @@ Game.propTypes = {
p2Name: PropTypes.string.isRequired,
board: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)).isRequired,
onCellClicked: PropTypes.func.isRequired,
currentPlayer: PropTypes.string.isRequired,
};
export default Game;
20 changes: 20 additions & 0 deletions src/WinnerMessage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import PropTypes from 'prop-types';

const WinnerMessage = ({ p1Name, p2Name, winner }) => (
<div>
{winner && (
<div data-hook="winner-message">
{winner === '-' ? 'it is a tie!' : `${winner === 'X' ? p1Name : p2Name} won!`}
</div>
)}
</div>
);

WinnerMessage.propTypes = {
p1Name: PropTypes.string.isRequired,
p2Name: PropTypes.string.isRequired,
winner: PropTypes.string.isRequired,
};

export default WinnerMessage;
Loading