/*
* Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
import * as React from 'react';
import {expect, assert} from 'chai';
import * as sinon from 'sinon';
import {mountWithRoute, shallowWithStore} from '../globals';
import ConnectedWheel, {Wheel} from '../../src/components/wheel';
import * as H from 'history';
import '../globals';
import {shimData} from '../shim_data';
import {Button} from 'react-bootstrap';
describe('Wheel', function() {
const wheelId = 'wheel_id_0';
const participants = shimData.participants.filter((e) => e.wheel_id === shimData.wheels[0].id);
// Wheel uses localStorage which doesn't exist in tests. Mock it
// Open Question: do we want to test localStorage logic? If so, need to create a basic
// implementation and add tests
if (!global.window.localStorage) {
global.window.localStorage = {
getItem() { return ''; },
setItem() {}
};
}
const props = {
location: H.createLocation('/'),
match: {
params: {wheel_id: wheelId},
isExact: true,
path: `/wheel/${wheelId}`,
url: 'localhost/'
},
history: H.createHashHistory(),
};
const shallowProps = {
wheelFetch: {
fulfilled: true,
rejected: false,
pending: false,
value: shimData.wheels[0],
},
allParticipantsFetch: {
fulfilled: true,
rejected: false,
pending: false,
value: participants,
},
participantSuggestFetch: {
fulfilled: true,
rejected: false,
pending: false,
value: {participant_id: participants[1].id},
},
participantSelectFetch: {
fulfilled: false,
rejected: false,
pending: false,
},
match: {
params: {wheel_id: wheelId},
},
};
const sandbox = sinon.createSandbox();
const dispatchProps = {
dispatchWheelGet: sandbox.spy(),
dispatchAllParticipantsGet: sandbox.spy(),
dispatchParticipantSelectPost: sandbox.spy(),
dispatchParticipantSuggestGet: sandbox.spy(),
};
afterEach(() => {
sandbox.reset();
});
it('Should render before and after loading completely', (done) => {
const wrapper = mountWithRoute();
// The wheel should be be loading when initially mounted
expect(wrapper.html()).to.contain('Loading the Wheel and its Participants...');
// After 50ms we should have retrieved wheel and participant data from the nocks
setTimeout(() => {
expect(wrapper.find('div').first().html()).to.contain('wheel_name_0');
done();
}, 50);
});
it(' appropriately upon initial update with suggested participant', () => {
const testSelectedParticipant = Object.assign({}, shallowProps.participantSuggestFetch.value, participants[1]);
const wrapper = shallowWithStore();
// Let's strip out the drawing and animation stuff
wrapper.instance().drawInitialWheel = sinon.spy();
wrapper.instance().componentDidUpdate();
wrapper.instance().spinTicker = {add: sinon.spy(), remove: sinon.spy(), stop: sinon.spy()};
wrapper.instance().setState({isSpinning: true});
wrapper.instance().componentDidUpdate();
expect(wrapper.instance().state.wheel).to.equal(shallowProps.wheelFetch.value);
expect(wrapper.instance().state.participants).to.equal(shallowProps.allParticipantsFetch.value);
expect(wrapper.instance().state.fetching).to.be.false;
expect(wrapper.instance().state.fetching).to.be.false;
expect(wrapper.instance().drawInitialWheel.calledOnce).to.be.true;
expect(wrapper.instance().state.selectedParticipant).to.deep.equal(testSelectedParticipant);
});
it('Should update state appropriately upon initial update with rigged suggested participant', () => {
const testProps = {
participantSuggestFetch: {
fulfilled: true,
rejected: false,
pending: false,
value: {
participant_id: participants[1].id,
rigged: true,
},
},
}
const testSelectedParticipant = Object.assign({}, testProps.participantSuggestFetch.value, participants[1]);
const wrapper = shallowWithStore();
// Let's strip out the drawing and animation stuff
wrapper.instance().drawInitialWheel = sinon.spy();
wrapper.instance().componentDidUpdate();
wrapper.instance().spinTicker = {add: sinon.spy(), remove: sinon.spy(), stop: sinon.spy()};
wrapper.instance().setState({isSpinning: true});
wrapper.instance().componentDidUpdate();
expect(wrapper.instance().state.wheel).to.equal(shallowProps.wheelFetch.value);
expect(wrapper.instance().state.participants).to.equal(shallowProps.allParticipantsFetch.value);
expect(wrapper.instance().state.fetching).to.be.false;
expect(wrapper.instance().state.fetching).to.be.false;
expect(wrapper.instance().drawInitialWheel.calledOnce).to.be.true;
expect(wrapper.instance().state.selectedParticipant).to.deep.equal(testSelectedParticipant);
});
it('Should update state appropriately and call dispatchParticipantSuggestGet when Spin is clicked', () => {
const testProps = {
participantSuggestFetch: {
fulfilled: false,
rejected: false,
pending: false,
value: {
participant_id: participants[1].id,
rigged: true,
},
},
}
const wrapper = shallowWithStore();
// Let's strip out the drawing and animation stuff
wrapper.instance().drawInitialWheel = sinon.spy();
wrapper.instance().spinTicker = {add: sinon.spy(), remove: sinon.spy(), stop: sinon.spy()};
wrapper.instance().componentDidUpdate();
wrapper.find('#btnSpin').first().simulate('click');
expect(wrapper.instance().state.isSpinning).to.be.true;
expect(wrapper.instance().state.selectedParticipant).to.be.undefined;
expect(dispatchProps.dispatchParticipantSuggestGet.calledWith(wheelId)).to.be.true;
// Try to spin again; state should remain unchanged
wrapper.instance().startSpinningWheel();
expect(wrapper.instance().state.selectedParticipant).to.be.undefined;
expect(dispatchProps.dispatchParticipantSuggestGet.calledWith(wheelId)).to.be.true;
});
it('Should set time fields and call drawWheel upon calls to spin()', () => {
const testTime = 4;
const wrapper = shallowWithStore();
// Let's strip out the drawing and animation stuff
wrapper.instance().drawInitialWheel = sinon.spy();
wrapper.instance().drawWheel = sinon.spy();
wrapper.instance().componentDidUpdate();
wrapper.instance().spinTicker = {add: sinon.spy(), remove: sinon.spy(), stop: sinon.spy()};
wrapper.instance().setState({isSpinning: true});
wrapper.instance().componentDidUpdate();
wrapper.instance().spin(testTime);
expect(wrapper.instance().currentAnimationTime).to.equal(testTime);
expect(wrapper.instance().drawWheel.calledOnce).to.be.true;
// 2nd spin with time increased by 16 results in a call to drawWheel and requestAnimationFrame
wrapper.instance().spin(1);
expect(wrapper.instance().drawWheel.calledTwice).to.be.true;
expect(wrapper.instance().currentAnimationTime).to.equal(testTime + 1);
});
it('Should call appropriate methods and eventually spinTicker.remove upon calls to riggedSpin()', () => {
const testTime = 16;
const testProps = {
participantSuggestFetch: {
fulfilled: true,
rejected: false,
pending: false,
value: {
participant_id: participants[1].id,
rigged: true,
},
},
}
const wrapper = shallowWithStore();
// Let's strip out the drawing and animation stuff
wrapper.instance().drawInitialWheel = sinon.spy();
wrapper.instance().drawWheel = sinon.spy();
wrapper.instance().componentDidUpdate();
wrapper.instance().spinTicker = {add: sinon.spy(), remove: sinon.spy(), stop: sinon.spy()};
wrapper.instance().setState({isSpinning: true});
wrapper.instance().componentDidUpdate();
wrapper.instance().currentAnimationTime = 0;
// First riggedSpin call should result in immediate exit due to not enough time elapsed
wrapper.instance().riggedSpin(0);
expect(wrapper.instance().lastClickTime).to.equal(0);
// Second riggedSpin call should result in a call to drawWheel
wrapper.instance().riggedSpin(50);
expect(wrapper.instance().drawWheel.calledOnce).to.be.true;
// Third riggedSpin call we add a ton of time so we can hit the base case
// This will call spinTicker.remove, drawWheel, and stop spinning
wrapper.instance().riggedSpin(1000);
expect(wrapper.instance().spinTicker.remove.calledOnce).to.be.true;
expect(wrapper.instance().drawWheel.calledTwice).to.be.true;
expect(wrapper.instance().state.isSpinning).to.be.false;
expect(wrapper.instance().state.targetAngle).to.equal(undefined);
expect(wrapper.instance().currentAnimationTime).to.equal(undefined);
});
it('Should render loading message if wheel and participant fetches arent fulfilled', () => {
const testProps = {
wheelFetch: {
fulfilled: true,
rejected: false,
pending: true,
value: shimData.wheels[0],
},
allParticipantsFetch: {
fulfilled: false,
rejected: false,
pending: true,
value: participants,
},
};
const wrapper = shallowWithStore();
expect(wrapper.html()).to.contain('Loading the Wheel and its Participants...');
});
it('Should render an error message if wheel fetch is rejected', () => {
const testProps = {
wheelFetch: {
fulfilled: false,
rejected: true,
pending: false,
value: shimData.wheels[0],
},
};
const wrapper = shallowWithStore();
expect(wrapper.html()).to.contain('Error: Wheel or wheel participants could not be loaded!');
});
it('Should render an error message if wheel fetch is rejected', () => {
const testProps = {
participantSuggestFetch: {
fulfilled: false,
rejected: true,
pending: false,
value: participants,
},
};
const wrapper = shallowWithStore();
expect(wrapper.html()).to.contain('Error: Participant Selection could not be loaded!');
});
it('Should call the appropriate functions and set state appropriately on call to openParticipantPage', () => {
const testProps = {
participantSuggestFetch: {
fulfilled: false,
rejected: true,
pending: false,
value: participants,
},
};
const wrapper = shallowWithStore();
window.open = sinon.spy();
wrapper.instance().setState({selectedParticipant: {wheel_id: 'test_wheel_id', id: 'test_id', url: 'test_url'}});
wrapper.instance().openParticipantPage();
expect(dispatchProps.dispatchParticipantSelectPost.calledWith('test_wheel_id', 'test_id')).to.be.true;
expect(window.open.calledWith('test_url')).to.be.true;
});
it('Should not do anything on openParticipantPage if the wheel is still spinning', () => {
const testProps = {
participantSuggestFetch: {
fulfilled: false,
rejected: true,
pending: false,
value: participants,
},
};
const wrapper = shallowWithStore();
window.open = sinon.spy();
wrapper.instance().setState({isSpinning: true});
wrapper.instance().openParticipantPage();
expect(dispatchProps.dispatchParticipantSelectPost.called).to.be.false;
expect(window.open.called).to.be.false;
});
it('Should mute audio when the mute button is pressed', () => {
const wrapper = shallowWithStore();
// stub the audio control since refs don't work in the test
wrapper.instance().refs = {clickSound: {volume:1}};
const clickSound = wrapper.instance().refs.clickSound;
let btnSoundToggle = wrapper.find('#btnSoundToggle').first();
btnSoundToggle.simulate('click');
expect(clickSound.volume).to.equal(0);
btnSoundToggle.simulate('click');
expect(clickSound.volume).to.equal(1);
btnSoundToggle.simulate('click');
expect(clickSound.volume).to.equal(0);
});
});