Skip to content

Commit

Permalink
resolve merge conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
colebillys19 committed Jun 24, 2019
2 parents 5a5c32d + 9432a0d commit 94fc4d9
Show file tree
Hide file tree
Showing 45 changed files with 6,295 additions and 2,169 deletions.
7,162 changes: 5,100 additions & 2,062 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@
"not op_mini all"
],
"devDependencies": {
"@testing-library/react": "^8.0.1",
"husky": "^1.3.1",
"jest-dom": "^3.0.0",
"prettier": "1.15.3",
"pretty-quick": "^1.8.0",
"react-testing-library": "^5.4.2",
"pretty-quick": "^1.11.1",
"redux-mock-store": "^1.5.3"
}
}
138 changes: 137 additions & 1 deletion src/actions/action-creators.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,61 @@ import * as types from '../constants/action-types';
import thunkMiddleware from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
import panes from '../constants/pane-types';
import { initializeCalculator } from '../lib/calculator';

const middlewares = [thunkMiddleware];
const mockStore = configureMockStore(middlewares);

const initialState = {
images: {
frames: {},
frameIDs: [],
gifProgress: 0,
gifData: '',
caption: '',
fontColor: '#000000',
gifFileName: ''
},
settings: {
image: {
width: 300,
height: 300,
interval: 100,
oversample: false
},
bounds: {
left: -10,
right: 10,
bottom: -10,
top: 10
},
strategy: 'contain'
},
ui: {
expandedPane: panes.NONE,
previewIdx: 0,
playing: false,
error: ''
}
};

const desmosMock = {
GraphingCalculator: jest.fn(() => {
return {
asyncScreenshot: (opts, cb) => cb(''),
getExpressions: () => [
{ id: 1, type: 'expression', latex: 'x = 3' },
{ id: 2, latex: '' }
],
setExpression: () => null
};
})
};

const calcContainerMock = { current: undefined };

// Tests

describe('Action creators', () => {
describe('synchronous action creators', () => {
it('return an action to add a frame', () => {
Expand Down Expand Up @@ -127,6 +178,91 @@ describe('Action creators', () => {
});

describe('async action creators', () => {
// TODO
let store;

beforeEach(() => {
store = mockStore(initialState);
});

it('return an action to temporarily display error message', () => {
jest.useFakeTimers();
const expectedActions = [
{ type: types.SET_ERROR, payload: { message: 'error' } },
{ type: types.CLEAR_ERROR }
];
store.dispatch(actions.flashError('error'));
jest.runAllTimers();
expect(store.getActions()).toEqual(expectedActions);
});

it('return an action to request a frame from the calculator', () => {
const expectedActions = [
{
type: types.ADD_FRAME,
payload: { id: 2, imageData: expect.any(String) }
}
];
const opts = {
height: 300,
targetPixelRatio: 1,
width: 300
};
initializeCalculator(desmosMock, calcContainerMock);
return store.dispatch(actions.requestFrame(opts)).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});

it('return an action to request a burst of frames from the calculator', () => {
const opts = {
height: 300,
idx: 1,
max: 3,
min: -3,
oversample: false,
step: 1,
width: 300
};
initializeCalculator(desmosMock, calcContainerMock);
return store.dispatch(actions.requestBurst(opts)).then(() => {
// slide with min/max of -3/3 should dispatch addFrame 7 times
expect(store.getActions().length).toEqual(7);
});
});

it('return actions to play animation via frames in state', () => {
const expectedActions = [
{ type: types.PLAY_PREVIEW },
{ type: types.UPDATE_PREVIEW_IDX, payload: { idx: 1 } }
];
store.dispatch(actions.startAnimation());
expect(store.getActions()).toEqual(expectedActions);
});

it('return action to generate a GIF via frames in state', () => {
const expectedActions = [
{ type: types.UPDATE_GIF_PROGRESS, payload: { progress: 100 } },
{ type: types.ADD_GIF, payload: { imageData: expect.any(String) } }
];
const opts = {
gifHeight: 300,
gifWidth: 300,
images: ['img1', 'img2', 'img3'],
interval: 0.1,
progressCallback: jest.fn()
};
// gifshot mock
const gifshot = {
createGIF: (args, cb) => {
args.progressCallback(100);
return cb({ image: 'test' });
}
};
// download mock
const download = () => null;

store.dispatch(actions.generateGIF([], opts, gifshot, download));
expect(store.getActions()).toEqual(expectedActions);
});
});
});
75 changes: 70 additions & 5 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,25 @@
*/

import * as types from '../constants/action-types';
import { setSliderByIndex, getImageData } from '../lib/calc-helpers';
import {
setSliderByIndex,
getImageData,
loadSavedGraph,
saveCurrentGraph
} from '../lib/calc-helpers';
import { startTimer, clearTimer } from '../lib/timer';
import {
gifCreationProblem,
badBurstInput,
badSettingsInput
badSettingsInput,
badNameInput
} from '../lib/error-messages';
import { getBurstErrors, getSettingsErrors } from '../lib/input-helpers';
import download from 'downloadjs';
import {
getBurstErrors,
getSettingsErrors,
getSaveGraphErrors
} from '../lib/input-helpers';

const ERROR_DELAY = 3000;
let nextFrameID = 0;
Expand All @@ -48,11 +59,36 @@ export const addFrame = imageData => ({
}
});

export const addSavedFrame = (imageData, id) => ({
type: types.ADD_FRAME,
payload: {
id,
imageData
}
});

export const updateGIFProgress = progress => ({
type: types.UPDATE_GIF_PROGRESS,
payload: { progress }
});

export const updateText = text => ({
type: types.UPDATE_TEXT,
payload: { text }
});

export const updateTextColor = fontColor => ({
type: types.UPDATE_TEXT_COLOR,
payload: { fontColor }
});

export const updateGIFFileName = name => {
return {
type: types.UPDATE_GIF_FILENAME,
payload: { gifFileName: name }
};
};

export const addGIF = imageData => ({
type: types.ADD_GIF,
payload: { imageData }
Expand Down Expand Up @@ -195,9 +231,15 @@ export const startAnimation = () => (dispatch, getState) => {

// The gifshot library is loaded in index.html
const gifshot = window.gifshot;
export const generateGIF = (images, opts) => (dispatch, getState) => {
export const generateGIF = (
images,
opts,
gifMaker = gifshot,
downloadFn = download
) => (dispatch, getState) => {
// Have to check state interval and not opts because opts is in seconds
const { interval } = getState().settings.image;
const { gifFileName } = getState().images;
const settingsErrors = getSettingsErrors({ interval });
if (Object.keys(settingsErrors).length) {
dispatch(flashError(badSettingsInput(settingsErrors)));
Expand All @@ -209,11 +251,34 @@ export const generateGIF = (images, opts) => (dispatch, getState) => {
...opts,
progressCallback: progress => dispatch(updateGIFProgress(progress))
};
gifshot.createGIF(gifshotArgs, data => {

gifMaker.createGIF(gifshotArgs, data => {
if (data.error) {
dispatch(flashError(gifCreationProblem()));
} else {
dispatch(addGIF(data.image));
downloadFn(data.image, gifFileName || 'gifsmos.gif', 'image/gif');
}
});
};

export const loadFramesFromLocal = dateString => (dispatch, getState) => {
dispatch(reset());
const { frameIDs, frames } = loadSavedGraph(dateString);
for (let val = 0; val < frameIDs.length; val += 1) {
// get corresponding image
const id = frameIDs[val];
const imageData = frames[id];
dispatch(addSavedFrame(imageData, id));
}
};

export const saveGraph = (name, frames, frameIDs) => async dispatch => {
const saveErrors = getSaveGraphErrors(name);
if (saveErrors.name) {
dispatch(flashError(badNameInput(saveErrors.name)));
return;
}
const newGraph = await saveCurrentGraph(name, frames, frameIDs);
return newGraph;
};
10 changes: 4 additions & 6 deletions src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import SidebarContainer from '../containers/SidebarContainer';
import PreviewContainer from '../containers/PreviewContainer';
import BurstContainer from '../containers/BurstContainer';
import SettingsContainer from '../containers/SettingsContainer';
import FolderContainer from '../containers/FolderContainer';
import ErrorToastContainer from '../containers/ErrorToastContainer';
import CALCULATOR_OPTIONS from '../constants/calculator-options';
import { initializeCalculator } from '../lib/calculator';
import './App.css';

// The Desmos API is loaded in index.html
const Desmos = window.Desmos;
const calcContainer = React.createRef();
export let calculator;

class App extends Component {
constructor(props) {
Expand All @@ -25,11 +26,7 @@ class App extends Component {
}

componentDidMount() {
calculator = Desmos.GraphingCalculator(
calcContainer.current,
CALCULATOR_OPTIONS
);

initializeCalculator(Desmos, calcContainer, CALCULATOR_OPTIONS);
window.addEventListener('keydown', this.handleKeyDown);
}

Expand All @@ -41,6 +38,7 @@ class App extends Component {
<SettingsContainer />
<PreviewContainer />
<BurstContainer />
<FolderContainer />
<SidebarContainer />
<ErrorToastContainer />
</div>
Expand Down
7 changes: 5 additions & 2 deletions src/components/App.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import React from 'react';
import App from './App';
import { cleanup } from '@testing-library/react';

describe('App', () => {
afterEach(cleanup);

describe('<App/>', () => {
it('renders without crashing', () => {
renderWithRedux(<App />);
global.renderWithRedux(<App />);
});
});
8 changes: 4 additions & 4 deletions src/components/Burst.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class Burst extends Component {

return (
<div className={classNames('Burst', { 'Burst-expanded': expanded })}>
<div>Slider Index</div>
<div data-testid="Burst-slider-index-label">Slider Index</div>
<input
className={classNames('Burst-input', {
'Burst-input-error': !!errors.idx
Expand All @@ -56,7 +56,7 @@ class Burst extends Component {
value={isNaN(idx) ? '' : idx}
onChange={this.handleInputUpdate}
/>
<div>Slider Min</div>
<div data-testid="Burst-slider-min-label">Slider Min</div>
<input
className={classNames('Burst-input', {
'Burst-input-error': !!errors.min
Expand All @@ -67,7 +67,7 @@ class Burst extends Component {
value={isNaN(min) ? '' : min}
onChange={this.handleInputUpdate}
/>
<div>Slider Max</div>
<div data-testid="Burst-slider-max-label">Slider Max</div>
<input
className={classNames('Burst-input', {
'Burst-input-error': !!errors.max
Expand All @@ -78,7 +78,7 @@ class Burst extends Component {
value={isNaN(max) ? '' : max}
onChange={this.handleInputUpdate}
/>
<div>Slider Step</div>
<div data-testid="Burst-slider-step-label">Slider Step</div>
<input
className={classNames('Burst-input', {
'Burst-input-error': !!errors.step
Expand Down
Loading

0 comments on commit 94fc4d9

Please sign in to comment.