123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- import React from 'react';
- import {mountWithIntl} from '../../helpers/intl-helpers.jsx';
- import configureStore from 'redux-mock-store';
- import mockAudioBufferPlayer from '../../__mocks__/audio-buffer-player.js';
- import mockAudioEffects from '../../__mocks__/audio-effects.js';
- import SoundEditor from '../../../src/containers/sound-editor';
- import SoundEditorComponent from '../../../src/components/sound-editor/sound-editor';
- jest.mock('react-ga');
- jest.mock('../../../src/lib/audio/audio-buffer-player', () => mockAudioBufferPlayer);
- jest.mock('../../../src/lib/audio/audio-effects', () => mockAudioEffects);
- describe('Sound Editor Container', () => {
- const mockStore = configureStore();
- let store;
- let soundIndex;
- let soundBuffer;
- const samples = new Float32Array([0, 0, 0]); // eslint-disable-line no-undef
- let vm;
- beforeEach(() => {
- soundIndex = 0;
- soundBuffer = {
- sampleRate: 0,
- getChannelData: jest.fn(() => samples)
- };
- vm = {
- getSoundBuffer: jest.fn(() => soundBuffer),
- renameSound: jest.fn(),
- updateSoundBuffer: jest.fn(),
- editingTarget: {
- sprite: {
- sounds: [{name: 'first name', id: 'first id'}]
- }
- }
- };
- store = mockStore({scratchGui: {vm: vm}});
- });
- test('should pass the correct data to the component from the store', () => {
- const wrapper = mountWithIntl(
- <SoundEditor
- soundIndex={soundIndex}
- store={store}
- />
- );
- const componentProps = wrapper.find(SoundEditorComponent).props();
- // Data retreived and processed by the `connect` with the store
- expect(componentProps.name).toEqual('first name');
- expect(componentProps.chunkLevels).toEqual([0]);
- expect(mockAudioBufferPlayer.instance.samples).toEqual(samples);
- // Initial data
- expect(componentProps.playhead).toEqual(null);
- expect(componentProps.trimStart).toEqual(null);
- expect(componentProps.trimEnd).toEqual(null);
- });
- test('it plays when clicked and stops when clicked again', () => {
- const wrapper = mountWithIntl(
- <SoundEditor
- soundIndex={soundIndex}
- store={store}
- />
- );
- let component = wrapper.find(SoundEditorComponent);
- // Ensure rendering doesn't start playing any sounds
- expect(mockAudioBufferPlayer.instance.play.mock.calls).toEqual([]);
- expect(mockAudioBufferPlayer.instance.stop.mock.calls).toEqual([]);
- component.props().onPlay();
- expect(mockAudioBufferPlayer.instance.play).toHaveBeenCalled();
- // Mock the audio buffer player calling onUpdate
- mockAudioBufferPlayer.instance.onUpdate(0.5);
- wrapper.update();
- component = wrapper.find(SoundEditorComponent);
- expect(component.props().playhead).toEqual(0.5);
- component.props().onStop();
- wrapper.update();
- component = wrapper.find(SoundEditorComponent);
- expect(mockAudioBufferPlayer.instance.stop).toHaveBeenCalled();
- expect(component.props().playhead).toEqual(null);
- });
- test('it submits name changes to the vm', () => {
- const wrapper = mountWithIntl(
- <SoundEditor
- soundIndex={soundIndex}
- store={store}
- />
- );
- const component = wrapper.find(SoundEditorComponent);
- component.props().onChangeName('hello');
- expect(vm.renameSound).toHaveBeenCalledWith(soundIndex, 'hello');
- });
- test('it handles an effect by submitting the result and playing', async () => {
- const wrapper = mountWithIntl(
- <SoundEditor
- soundIndex={soundIndex}
- store={store}
- />
- );
- const component = wrapper.find(SoundEditorComponent);
- component.props().onReverse(); // Could be any of the effects, just testing the end result
- await mockAudioEffects.instance._finishProcessing(soundBuffer);
- expect(mockAudioBufferPlayer.instance.play).toHaveBeenCalled();
- expect(vm.updateSoundBuffer).toHaveBeenCalled();
- });
- test('it handles reverse effect correctly', () => {
- const wrapper = mountWithIntl(
- <SoundEditor
- soundIndex={soundIndex}
- store={store}
- />
- );
- const component = wrapper.find(SoundEditorComponent);
- component.props().onReverse();
- expect(mockAudioEffects.instance.name).toEqual(mockAudioEffects.effectTypes.REVERSE);
- expect(mockAudioEffects.instance.process).toHaveBeenCalled();
- });
- test('it handles louder effect correctly', () => {
- const wrapper = mountWithIntl(
- <SoundEditor
- soundIndex={soundIndex}
- store={store}
- />
- );
- const component = wrapper.find(SoundEditorComponent);
- component.props().onLouder();
- expect(mockAudioEffects.instance.name).toEqual(mockAudioEffects.effectTypes.LOUDER);
- expect(mockAudioEffects.instance.process).toHaveBeenCalled();
- });
- test('it handles softer effect correctly', () => {
- const wrapper = mountWithIntl(
- <SoundEditor
- soundIndex={soundIndex}
- store={store}
- />
- );
- const component = wrapper.find(SoundEditorComponent);
- component.props().onSofter();
- expect(mockAudioEffects.instance.name).toEqual(mockAudioEffects.effectTypes.SOFTER);
- expect(mockAudioEffects.instance.process).toHaveBeenCalled();
- });
- test('it handles faster effect correctly', () => {
- const wrapper = mountWithIntl(
- <SoundEditor
- soundIndex={soundIndex}
- store={store}
- />
- );
- const component = wrapper.find(SoundEditorComponent);
- component.props().onFaster();
- expect(mockAudioEffects.instance.name).toEqual(mockAudioEffects.effectTypes.FASTER);
- expect(mockAudioEffects.instance.process).toHaveBeenCalled();
- });
- test('it handles slower effect correctly', () => {
- const wrapper = mountWithIntl(
- <SoundEditor
- soundIndex={soundIndex}
- store={store}
- />
- );
- const component = wrapper.find(SoundEditorComponent);
- component.props().onSlower();
- expect(mockAudioEffects.instance.name).toEqual(mockAudioEffects.effectTypes.SLOWER);
- expect(mockAudioEffects.instance.process).toHaveBeenCalled();
- });
- test('it handles echo effect correctly', () => {
- const wrapper = mountWithIntl(
- <SoundEditor
- soundIndex={soundIndex}
- store={store}
- />
- );
- const component = wrapper.find(SoundEditorComponent);
- component.props().onEcho();
- expect(mockAudioEffects.instance.name).toEqual(mockAudioEffects.effectTypes.ECHO);
- expect(mockAudioEffects.instance.process).toHaveBeenCalled();
- });
- test('it handles robot effect correctly', () => {
- const wrapper = mountWithIntl(
- <SoundEditor
- soundIndex={soundIndex}
- store={store}
- />
- );
- const component = wrapper.find(SoundEditorComponent);
- component.props().onRobot();
- expect(mockAudioEffects.instance.name).toEqual(mockAudioEffects.effectTypes.ROBOT);
- expect(mockAudioEffects.instance.process).toHaveBeenCalled();
- });
- test('undo/redo stack state', async () => {
- const wrapper = mountWithIntl(
- <SoundEditor
- soundIndex={soundIndex}
- store={store}
- />
- );
- let component = wrapper.find(SoundEditorComponent);
- // Undo and redo should be disabled initially
- expect(component.prop('canUndo')).toEqual(false);
- expect(component.prop('canRedo')).toEqual(false);
- // Submitting new samples should make it possible to undo
- component.props().onFaster();
- await mockAudioEffects.instance._finishProcessing(soundBuffer);
- wrapper.update();
- component = wrapper.find(SoundEditorComponent);
- expect(component.prop('canUndo')).toEqual(true);
- expect(component.prop('canRedo')).toEqual(false);
- // Undoing should make it possible to redo and not possible to undo again
- await component.props().onUndo();
- wrapper.update();
- component = wrapper.find(SoundEditorComponent);
- expect(component.prop('canUndo')).toEqual(false);
- expect(component.prop('canRedo')).toEqual(true);
- // Redoing should make it possible to undo and not possible to redo again
- await component.props().onRedo();
- wrapper.update();
- component = wrapper.find(SoundEditorComponent);
- expect(component.prop('canUndo')).toEqual(true);
- expect(component.prop('canRedo')).toEqual(false);
- // New submission should clear the redo stack
- await component.props().onUndo(); // Undo to go back to a state where redo is enabled
- wrapper.update();
- component = wrapper.find(SoundEditorComponent);
- expect(component.prop('canRedo')).toEqual(true);
- component.props().onFaster();
- await mockAudioEffects.instance._finishProcessing(soundBuffer);
- wrapper.update();
- component = wrapper.find(SoundEditorComponent);
- expect(component.prop('canRedo')).toEqual(false);
- });
- test('undo and redo submit new samples and play the sound', async () => {
- const wrapper = mountWithIntl(
- <SoundEditor
- soundIndex={soundIndex}
- store={store}
- />
- );
- let component = wrapper.find(SoundEditorComponent);
- // Set up an undoable state
- component.props().onFaster();
- await mockAudioEffects.instance._finishProcessing(soundBuffer);
- wrapper.update();
- component = wrapper.find(SoundEditorComponent);
- // Undo should update the sound buffer and play the new samples
- await component.props().onUndo();
- expect(mockAudioBufferPlayer.instance.play).toHaveBeenCalled();
- expect(vm.updateSoundBuffer).toHaveBeenCalled();
- // Clear the mocks call history to assert again for redo.
- vm.updateSoundBuffer.mockClear();
- mockAudioBufferPlayer.instance.play.mockClear();
- // Undo should update the sound buffer and play the new samples
- await component.props().onRedo();
- expect(mockAudioBufferPlayer.instance.play).toHaveBeenCalled();
- expect(vm.updateSoundBuffer).toHaveBeenCalled();
- });
- });
|