Skip to content

Commit 20470f1

Browse files
author
Madelyn Kasula
authored
Merge pull request #342 from code-dot-org/tests
Unit test <Train/> component
2 parents e205ba0 + 5acba48 commit 20470f1

File tree

6 files changed

+145
-20
lines changed

6 files changed

+145
-20
lines changed

src/oceans/models/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {init as initLoading} from './loading';
22
import {init as initWords} from './words';
3-
import {init as initTraining} from './train';
3+
import train from './train';
44
import {init as initPredicting} from './predict';
55
import {init as initPond} from './pond';
66
import {Modes} from '../constants';
@@ -17,7 +17,7 @@ export const init = state => {
1717
initWords();
1818
break;
1919
case Modes.Training:
20-
initTraining();
20+
train.init();
2121
break;
2222
case Modes.Predicting:
2323
initPredicting();

src/oceans/models/train.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import SimpleTrainer from '../../utils/SimpleTrainer';
55
import SVMTrainer from '../../utils/SVMTrainer';
66
import {generateOcean} from '../../utils/generateOcean';
77

8-
export const init = () => {
8+
const init = () => {
99
const state = getState();
1010

1111
let trainer = state.trainer;
@@ -38,7 +38,7 @@ export const init = () => {
3838
});
3939
};
4040

41-
export const onClassifyFish = doesLike => {
41+
const onClassifyFish = doesLike => {
4242
const state = getState();
4343

4444
// No-op if animation is currently in progress.
@@ -73,3 +73,8 @@ export const onClassifyFish = doesLike => {
7373

7474
return true;
7575
};
76+
77+
export default {
78+
init,
79+
onClassifyFish
80+
};

src/oceans/state.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ const initialState = {
3636
pondFishMaxExplainValue: 1,
3737
pondRecallFishMaxExplainValue: 1,
3838
guideDismissals: [],
39-
guideShowing: false
39+
guideShowing: false,
40+
showConfirmationDialog: false,
41+
confirmationDialogOnYes: null
4042
};
4143
let state = {...initialState};
4244

src/oceans/ui.jsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
resetTraining,
1313
friendlyNameForFishPart
1414
} from './helpers';
15-
import {onClassifyFish} from './models/train';
15+
import train from './models/train';
1616
import {arrangeFish} from './models/pond';
1717
import colors from './colors';
1818
import aiBotHead from '@public/images/ai-bot/ai-bot-head.png';
@@ -875,7 +875,7 @@ let UnwrappedWords = class Words extends React.Component {
875875
};
876876
export const Words = Radium(UnwrappedWords); // Exported for unit tests.
877877

878-
let Train = class Train extends React.Component {
878+
let UnwrappedTrain = class Train extends React.Component {
879879
state = {
880880
headOpen: false
881881
};
@@ -906,7 +906,7 @@ let Train = class Train extends React.Component {
906906
</div>
907907
<div style={styles.counter}>
908908
<img src={counterIcon} style={styles.counterImg} />
909-
<span style={styles.counterNum}>
909+
<span style={styles.counterNum} id="uitest-train-count">
910910
{Math.min(999, state.yesCount + state.noCount)}
911911
</span>
912912
</div>
@@ -927,7 +927,7 @@ let Train = class Train extends React.Component {
927927
style={styles.trainButtonNo}
928928
onClick={() => {
929929
this.setState({headOpen: true});
930-
return onClassifyFish(false);
930+
return train.onClassifyFish(false);
931931
}}
932932
sound={'no'}
933933
>
@@ -939,7 +939,7 @@ let Train = class Train extends React.Component {
939939
style={styles.trainButtonYes}
940940
onClick={() => {
941941
this.setState({headOpen: true});
942-
return onClassifyFish(true);
942+
return train.onClassifyFish(true);
943943
}}
944944
sound={'yes'}
945945
>
@@ -958,7 +958,7 @@ let Train = class Train extends React.Component {
958958
);
959959
}
960960
};
961-
Train = Radium(Train);
961+
export const Train = Radium(UnwrappedTrain); // Exported for unit tests.
962962

963963
const defaultTimeScale = 1;
964964
const timeScales = [1, 2];
@@ -1590,10 +1590,6 @@ let Guide = class Guide extends React.Component {
15901590
Guide = Radium(Guide);
15911591

15921592
export default class UI extends React.Component {
1593-
constructor(props) {
1594-
super(props);
1595-
}
1596-
15971593
render() {
15981594
const state = getState();
15991595
const currentMode = getState().currentMode;

test/unit/oceans/models/train.test.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const {initFishData} = require('@ml/utils/fishData');
22
import {setState, getState, resetState} from '@ml/oceans/state';
33
import {ClassType, Modes} from '@ml/oceans/constants';
4-
import {init, onClassifyFish} from '@ml/oceans/models/train.js';
4+
import train from '@ml/oceans/models/train.js';
55

66
describe('Model quality test', () => {
77
beforeAll(() => {
@@ -17,7 +17,7 @@ describe('Model quality test', () => {
1717
});
1818

1919
test('init state', () => {
20-
init();
20+
train.init();
2121
const state = getState();
2222
expect(state).toBeTruthy();
2323
expect(state.trainer).toBeTruthy();
@@ -26,11 +26,11 @@ describe('Model quality test', () => {
2626
});
2727

2828
test('state changes on classify', () => {
29-
init();
29+
train.init();
3030
// Set isRunning to false to simulate animation ending
3131
setState({isRunning: false});
3232
const previousState = getState();
33-
expect(onClassifyFish(true)).toBe(true);
33+
expect(train.onClassifyFish(true)).toBe(true);
3434
const newState = getState();
3535
expect(newState.trainingIndex).toBe(previousState.trainingIndex + 1);
3636
expect(newState.yesCount).toBe(previousState.yesCount + 1);

test/unit/oceans/ui.test.js

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import React from 'react';
22
import ReactDOM from 'react-dom';
33
import {shallow} from 'enzyme';
44
import sinon from 'sinon';
5-
import {Button, ConfirmationDialog, Words, wordSet} from '@ml/oceans/ui';
5+
import {Button, ConfirmationDialog, Words, wordSet, Train} from '@ml/oceans/ui';
66
import guide from '@ml/oceans/models/guide';
77
import soundLibrary from '@ml/oceans/models/soundLibrary';
8+
import train from '@ml/oceans/models/train';
89
import modeHelpers from '@ml/oceans/modeHelpers';
910
import {setState, getState, resetState} from '@ml/oceans/state';
1011
import {AppMode, Modes} from '@ml/oceans/constants';
@@ -197,3 +198,124 @@ describe('Words', () => {
197198
});
198199
});
199200
});
201+
202+
describe('Train', () => {
203+
let classifyFishStub;
204+
205+
beforeEach(() => {
206+
classifyFishStub = sinon.stub(train, 'onClassifyFish');
207+
});
208+
209+
afterEach(() => {
210+
train.onClassifyFish.restore();
211+
});
212+
213+
it('displays the current training count', () => {
214+
setState({yesCount: 10, noCount: 23});
215+
const wrapper = shallow(<Train {...DEFAULT_PROPS} />);
216+
const trainCount = wrapper.find('#uitest-train-count');
217+
218+
expect(trainCount.text()).toEqual('33');
219+
});
220+
221+
it('displays 999 if current training count is greater than 999', () => {
222+
setState({yesCount: 1000, noCount: 1});
223+
const wrapper = shallow(<Train {...DEFAULT_PROPS} />);
224+
const trainCount = wrapper.find('#uitest-train-count');
225+
226+
expect(trainCount.text()).toEqual('999');
227+
});
228+
229+
it('sets state to display confirmation dialog when erase icon is clicked', () => {
230+
const initialState = getState();
231+
expect(initialState.showConfirmationDialog).toBeFalsy();
232+
expect(initialState.confirmationDialogOnYes).toBeNull();
233+
234+
const wrapper = shallow(<Train {...DEFAULT_PROPS} />);
235+
const eraseIcon = wrapper.find('FontAwesomeIcon').at(0);
236+
eraseIcon.simulate('click');
237+
238+
const newState = getState();
239+
expect(newState.showConfirmationDialog).toBeTruthy();
240+
expect(newState.confirmationDialogOnYes).not.toBeNull();
241+
});
242+
243+
describe('train "no" button', () => {
244+
const getNoButton = wrapper => wrapper.find('Button').at(0);
245+
246+
it('displays the current word when not in AppMode.CreaturesVTrash', () => {
247+
setState({appMode: 'not-creatures-v-trash', word: 'Spooky'});
248+
const wrapper = shallow(<Train {...DEFAULT_PROPS} />);
249+
const noButton = getNoButton(wrapper).render();
250+
251+
expect(noButton.text().trimLeft()).toEqual('Not Spooky');
252+
});
253+
254+
it('displays "no" in AppMode.CreaturesVTrash', () => {
255+
setState({appMode: AppMode.CreaturesVTrash, word: 'Spooky'});
256+
const wrapper = shallow(<Train {...DEFAULT_PROPS} />);
257+
const noButton = getNoButton(wrapper).render();
258+
259+
expect(noButton.text().trimLeft()).toEqual('No');
260+
});
261+
262+
it('classifies fish on click', () => {
263+
const wrapper = shallow(<Train {...DEFAULT_PROPS} />);
264+
getNoButton(wrapper).simulate('click');
265+
266+
expect(classifyFishStub.withArgs(false).callCount).toEqual(1);
267+
});
268+
269+
it('opens bot head on click', () => {
270+
const wrapper = shallow(<Train {...DEFAULT_PROPS} />);
271+
272+
expect(wrapper.state().headOpen).toBeFalsy();
273+
getNoButton(wrapper).simulate('click');
274+
expect(wrapper.state().headOpen).toBeTruthy();
275+
});
276+
});
277+
278+
describe('train "yes" button', () => {
279+
const getYesButton = wrapper => wrapper.find('Button').at(1);
280+
281+
it('displays the current word when not in AppMode.CreaturesVTrash', () => {
282+
setState({appMode: 'not-creatures-v-trash', word: 'Spooky'});
283+
const wrapper = shallow(<Train {...DEFAULT_PROPS} />);
284+
const noButton = getYesButton(wrapper).render();
285+
286+
expect(noButton.text().trimLeft()).toEqual('Spooky');
287+
});
288+
289+
it('displays "yes" in AppMode.CreaturesVTrash', () => {
290+
setState({appMode: AppMode.CreaturesVTrash, word: 'Spooky'});
291+
const wrapper = shallow(<Train {...DEFAULT_PROPS} />);
292+
const noButton = getYesButton(wrapper).render();
293+
294+
expect(noButton.text().trimLeft()).toEqual('Yes');
295+
});
296+
297+
it('classifies fish on click', () => {
298+
const wrapper = shallow(<Train {...DEFAULT_PROPS} />);
299+
getYesButton(wrapper).simulate('click');
300+
301+
expect(classifyFishStub.withArgs(true).callCount).toEqual(1);
302+
});
303+
304+
it('opens bot head on click', () => {
305+
const wrapper = shallow(<Train {...DEFAULT_PROPS} />);
306+
307+
expect(wrapper.state().headOpen).toBeFalsy();
308+
getYesButton(wrapper).simulate('click');
309+
expect(wrapper.state().headOpen).toBeTruthy();
310+
});
311+
});
312+
313+
it('transitions to Modes.Predicting when continue button is clicked', () => {
314+
const toModeStub = sinon.stub(modeHelpers, 'toMode');
315+
const wrapper = shallow(<Train {...DEFAULT_PROPS} />);
316+
const continueButton = wrapper.find('Button').at(2);
317+
continueButton.simulate('click');
318+
319+
expect(toModeStub.withArgs(Modes.Predicting).callCount).toEqual(1);
320+
});
321+
});

0 commit comments

Comments
 (0)