Skip to content

Commit

Permalink
Increase test coverage for frontend (#7)
Browse files Browse the repository at this point in the history
* Increase test coverage

* Increase test coverage
  • Loading branch information
neelsomani committed Apr 24, 2020
1 parent 9cf3c98 commit fecc999
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 13 deletions.
2 changes: 1 addition & 1 deletion app.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def static_file(path):

@sockets.route('/submit')
def submit(ws):
""" Receives incoming chat messages, sends to all clients. """
""" Receive incoming messages. """
while not ws.closed:
gevent.sleep(0.1)
msg = ws.receive()
Expand Down
2 changes: 1 addition & 1 deletion backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def handle_message(self, message):
MOVE: self._move,
SWITCH_TEAM: self._switch_team
}
fn = action_map[message.get('action', '')]
fn = action_map.get(message['action'], None)
if fn is None:
self.logger.exception(
'Received bad action for message: {}'
Expand Down
2 changes: 1 addition & 1 deletion src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class App extends Component {
this.lastMove(data.payload)
break;
default:
console.log('Unhandled action: ' + data.action);
throw 'Unhandled action: ' + data.action;
}
}

Expand Down
138 changes: 133 additions & 5 deletions src/App.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,140 @@ import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

class MockReconnectingWebSocket {
const PLAYER_KEY = '123';

class MockSocketWrapper {
socketClass() {
const parent = this;
class MockReconnectingWebSocket {
constructor(props) {
if (props.includes('receive')) {
parent.socket = this;
}
}

send(msg) {
parent.msg = msg;
}
}
return MockReconnectingWebSocket;
}
}

function serialize(payload) {
return {
data: JSON.stringify(payload)
};
}

function initSixPlayerGame() {
socketWrapper.socket.onmessage(serialize(
{
action: 'register',
payload: {
success: true,
uuid: PLAYER_KEY,
player_n: 0,
n_players: 6,
time_limit: 30
}
}));
}

function _lastMove() {
return {
action: 'last_move',
payload: {
nCards: {
1: 1,
2: 1,
3: 1,
4: 1,
5: 1
},
moveTimestamp: 0,
turn: 0,
success: false,
card: 'KC',
respondent: 0,
interrogator: 1
}
}
}

it('renders without crashing', () => {
const div = document.createElement('div');
window.ReconnectingWebSocket = MockReconnectingWebSocket;
let socketWrapper;
let container;

beforeEach(() => {
socketWrapper = new MockSocketWrapper();
window.ReconnectingWebSocket = socketWrapper.socketClass();
window.cards = {}
ReactDOM.render(<App />, div);
container = document.createElement('div');
document.body.appendChild(container);
ReactDOM.render(<App />, container);
});

afterEach(() => {
socketWrapper = null;
container = null;
});

it('handles visitor correctly', () => {
socketWrapper.socket.onmessage(serialize(
{
action: 'register',
payload: {
success: false,
player_n: -1,
n_players: 4,
time_limit: 30
}
}));
expect(container.getElementsByClassName('Player')).toHaveLength(4);
});

it('handles player correctly', () => {
initSixPlayerGame();
expect(container.getElementsByClassName('Player')).toHaveLength(5);
socketWrapper.socket.onmessage(serialize(
{
action: 'hand',
payload: ['AC', '2C']
}));
expect(container.getElementsByClassName('card')).toHaveLength(2);
socketWrapper.socket.onmessage(serialize(_lastMove()));
expect(container.getElementsByClassName('MoveDisplay')[0]).toHaveProperty(
'innerHTML',
'Failure: Player 1 KC from Player 0'
);
});

it('handles making moves correctly', () => {
initSixPlayerGame();
socketWrapper.socket.onmessage(serialize(
{
action: 'hand',
payload: ['AC']
}));
expect(container.getElementsByClassName('MakeMoveCover')).toHaveLength(0);
const players = container.getElementsByClassName('Player');
// The modal should not show unless it is the player's turn
for (let i = 0; i < players.length; i++) {
players[i].click();
expect(container.getElementsByClassName('MakeMoveCover')).toHaveLength(0);
}
socketWrapper.socket.onmessage(serialize(_lastMove()));
// Test showing and hiding the make move modal works
players[0].click();
expect(container.getElementsByClassName('MakeMoveCover')).toHaveLength(1);
container.getElementsByClassName('MakeMoveCover')[0].children[0].click();
expect(container.getElementsByClassName('MakeMoveCover')).toHaveLength(0);
// Move serialization
players[0].click();
container.getElementsByClassName('MakeMoveCover')[0]
.getElementsByClassName('card')[0].click();
const move = JSON.parse(socketWrapper.msg);
expect(move.action).toBe('move');
expect(move.payload.key).toBe(PLAYER_KEY);
expect(move.payload.respondent).toBe(1);
})
5 changes: 2 additions & 3 deletions src/components/Card.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,8 @@ export default class Card extends Component {

render() {
return <img
class='card'
onClick={() => { this.props.playCard(this.props.card) }}
aria-card-name={this.props.card}
className='card'
onClick={() => { this.props.playCard && this.props.playCard(this.props.card) }}
src={this.cardMap[this.props.card]} />
}
}
4 changes: 2 additions & 2 deletions src/components/MakeMoveModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default class MakeMoveModal extends Component {
}

render() {
return <div class='MakeMoveCover'>
return <div className='MakeMoveCover'>
<div style={{
width: '100%',
height: '100%',
Expand All @@ -40,7 +40,7 @@ export default class MakeMoveModal extends Component {
left: 0,
zIndex: 5
}} onClick={this.props.hideModal}></div>
<div class='CardSelector' style={{ zIndex: 10 }}>
<div className='CardSelector' style={{ zIndex: 10 }}>
<VerticalCards
playCard={this.props.playCard}
suitClass='active-hand'
Expand Down
1 change: 1 addition & 0 deletions src/components/Players.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default class Players extends Component {
].map((p) => {
if (p != this.props.playerN) {
return <Player
key={'player_' + p}
showModal={this.props.showModal}
turn={this.props.turn}
playerN={p}
Expand Down
4 changes: 4 additions & 0 deletions src/components/VerticalCards.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,25 @@ export default class VerticalCards extends Component {
<div className={this.props.handClass}>
<div className={suitClass}>
{suited['C'].map((c) => <Card
key={'card-' + c}
playCard={this.props.playCard}
card={c} />)}
</div>
<div className={suitClass}>
{suited['D'].map((c) => <Card
key={'card-' + c}
playCard={this.props.playCard}
card={c} />)}
</div>
<div className={suitClass}>
{suited['H'].map((c) => <Card
key={'card-' + c}
playCard={this.props.playCard}
card={c} />)}
</div>
<div className={suitClass}>
{suited['S'].map((c) => <Card
key={'card-' + c}
playCard={this.props.playCard}
card={c} />)}
</div>
Expand Down

0 comments on commit fecc999

Please sign in to comment.