mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-23 14:02:50 -05:00
214 lines
8.3 KiB
JavaScript
214 lines
8.3 KiB
JavaScript
/* eslint-env jest */
|
|
import undoReducer, {
|
|
undoSnapshot, undo, redo, clearUndoState, MAX_STACK_SIZE
|
|
} from '../../src/reducers/undo';
|
|
|
|
test('initialState', () => {
|
|
let defaultState;
|
|
|
|
expect(undoReducer(defaultState /* state */, {type: 'anything'} /* action */)).toBeDefined();
|
|
expect(undoReducer(defaultState /* state */, {type: 'anything'} /* action */).pointer).toEqual(-1);
|
|
expect(undoReducer(defaultState /* state */, {type: 'anything'} /* action */).stack).toHaveLength(0);
|
|
});
|
|
|
|
test('snapshot', () => {
|
|
let defaultState;
|
|
const state1 = {state: 1};
|
|
const state2 = {state: 2};
|
|
|
|
let reduxState = undoReducer(defaultState /* state */, undoSnapshot([state1]) /* action */);
|
|
expect(reduxState.pointer).toEqual(0);
|
|
expect(reduxState.stack).toHaveLength(1);
|
|
expect(reduxState.stack[0]).toEqual(state1);
|
|
|
|
reduxState = undoReducer(reduxState /* state */, undoSnapshot([state2]) /* action */);
|
|
expect(reduxState.pointer).toEqual(1);
|
|
expect(reduxState.stack).toHaveLength(2);
|
|
expect(reduxState.stack[0]).toEqual(state1);
|
|
expect(reduxState.stack[1]).toEqual(state2);
|
|
});
|
|
|
|
test('invalidSnapshot', () => {
|
|
let defaultState;
|
|
const state1 = {state: 1};
|
|
|
|
const reduxState = undoReducer(defaultState /* state */, undoSnapshot([state1]) /* action */);
|
|
const newReduxState = undoReducer(reduxState /* state */, undoSnapshot() /* action */); // No snapshot provided
|
|
expect(reduxState).toEqual(newReduxState);
|
|
});
|
|
|
|
test('clearUndoState', () => {
|
|
let defaultState;
|
|
const state1 = {state: 1};
|
|
const state2 = {state: 2};
|
|
|
|
// Push 2 states then clear
|
|
const reduxState = undoReducer(defaultState /* state */, undoSnapshot([state1]) /* action */);
|
|
undoReducer(reduxState /* state */, undoSnapshot([state2]) /* action */);
|
|
const newReduxState = undoReducer(reduxState /* state */, clearUndoState() /* action */);
|
|
|
|
expect(newReduxState.pointer).toEqual(-1);
|
|
expect(newReduxState.stack).toHaveLength(0);
|
|
});
|
|
|
|
test('cantUndo', () => {
|
|
let defaultState;
|
|
const state1 = {state: 1};
|
|
|
|
// Undo when there's no undo stack
|
|
let reduxState = undoReducer(defaultState /* state */, undo() /* action */);
|
|
|
|
expect(reduxState.pointer).toEqual(-1);
|
|
expect(reduxState.stack).toHaveLength(0);
|
|
|
|
// Undo when there's only one state
|
|
reduxState = undoReducer(reduxState /* state */, undoSnapshot([state1]) /* action */);
|
|
reduxState = undoReducer(reduxState /* state */, undo() /* action */);
|
|
|
|
expect(reduxState.pointer).toEqual(0);
|
|
expect(reduxState.stack).toHaveLength(1);
|
|
});
|
|
|
|
test('cantRedo', () => {
|
|
let defaultState;
|
|
const state1 = {state: 1};
|
|
|
|
let reduxState = undoReducer(defaultState /* state */, undoSnapshot([state1]) /* action */);
|
|
|
|
// Redo when there's no redo stack
|
|
reduxState = undoReducer(reduxState /* state */, redo() /* action */);
|
|
|
|
expect(reduxState.pointer).toEqual(0);
|
|
expect(reduxState.stack).toHaveLength(1);
|
|
});
|
|
|
|
test('undo', () => {
|
|
let defaultState;
|
|
const state1 = {state: 1};
|
|
const state2 = {state: 2};
|
|
|
|
// Push 2 states then undo one
|
|
let reduxState = undoReducer(defaultState /* state */, undoSnapshot([state1]) /* action */);
|
|
reduxState = undoReducer(reduxState /* state */, undoSnapshot([state2]) /* action */);
|
|
reduxState = undoReducer(reduxState /* state */, undo() /* action */);
|
|
|
|
expect(reduxState.pointer).toEqual(0);
|
|
expect(reduxState.stack).toHaveLength(2);
|
|
expect(reduxState.stack[0]).toEqual(state1);
|
|
expect(reduxState.stack[1]).toEqual(state2);
|
|
});
|
|
|
|
test('redo', () => {
|
|
let defaultState;
|
|
const state1 = {state: 1};
|
|
const state2 = {state: 2};
|
|
|
|
// Push 2 states then undo one
|
|
let reduxState = undoReducer(defaultState /* state */, undoSnapshot([state1]) /* action */);
|
|
reduxState = undoReducer(reduxState /* state */, undoSnapshot([state2]) /* action */);
|
|
let newReduxState = undoReducer(reduxState /* state */, undo() /* action */);
|
|
|
|
// Now redo and check equality with previous state
|
|
newReduxState = undoReducer(newReduxState /* state */, redo() /* action */);
|
|
expect(newReduxState.pointer).toEqual(reduxState.pointer);
|
|
expect(newReduxState.stack).toHaveLength(reduxState.stack.length);
|
|
expect(newReduxState.stack[0]).toEqual(reduxState.stack[0]);
|
|
expect(reduxState.stack[1]).toEqual(reduxState.stack[1]);
|
|
});
|
|
|
|
test('undoSnapshotCantRedo', () => {
|
|
let defaultState;
|
|
const state1 = {state: 1};
|
|
const state2 = {state: 2};
|
|
const state3 = {state: 3};
|
|
|
|
// Push 2 states then undo
|
|
let reduxState = undoReducer(defaultState /* state */, undoSnapshot([state1]) /* action */);
|
|
reduxState = undoReducer(reduxState /* state */, undoSnapshot([state2]) /* action */);
|
|
reduxState = undoReducer(reduxState /* state */, undo() /* action */);
|
|
|
|
expect(reduxState.pointer).toEqual(0);
|
|
expect(reduxState.stack).toHaveLength(2);
|
|
|
|
// Snapshot
|
|
reduxState = undoReducer(reduxState /* state */, undoSnapshot([state3]) /* action */);
|
|
// Redo should do nothing
|
|
const newReduxState = undoReducer(reduxState /* state */, redo() /* action */);
|
|
|
|
expect(newReduxState.pointer).toEqual(reduxState.pointer);
|
|
expect(newReduxState.stack).toHaveLength(reduxState.stack.length);
|
|
expect(newReduxState.stack[0]).toEqual(reduxState.stack[0]);
|
|
expect(newReduxState.stack[1]).toEqual(state3);
|
|
});
|
|
|
|
test('snapshotAtMaxStackSize', () => {
|
|
let defaultState;
|
|
const getState = function (num) {
|
|
return {state: num};
|
|
};
|
|
// Push MAX_STACK_SIZE states
|
|
let num = 1;
|
|
let reduxState = undoReducer(defaultState /* state */, undoSnapshot([getState(num)]) /* action */);
|
|
for (num = 2; num <= MAX_STACK_SIZE; num++) {
|
|
reduxState = undoReducer(reduxState /* state */, undoSnapshot([getState(num)]) /* action */);
|
|
}
|
|
|
|
expect(reduxState.pointer).toEqual(MAX_STACK_SIZE - 1);
|
|
expect(reduxState.stack).toHaveLength(MAX_STACK_SIZE);
|
|
expect(reduxState.stack[0].state).toEqual(1);
|
|
|
|
// Push one more
|
|
reduxState = undoReducer(reduxState /* state */, undoSnapshot([getState(num)]) /* action */);
|
|
|
|
// Stack size stays the same
|
|
expect(reduxState.pointer).toEqual(MAX_STACK_SIZE - 1);
|
|
expect(reduxState.stack).toHaveLength(MAX_STACK_SIZE);
|
|
expect(reduxState.stack[0].state).toEqual(2); // State 1 was cut off
|
|
expect(reduxState.stack[MAX_STACK_SIZE - 1].state).toEqual(MAX_STACK_SIZE + 1); // Newest added state is at end
|
|
});
|
|
|
|
test('undoRedoAtMaxStackSize', () => {
|
|
let defaultState;
|
|
const getState = function (num) {
|
|
return {state: num};
|
|
};
|
|
// Push MAX_STACK_SIZE states
|
|
let num = 1;
|
|
let reduxState = undoReducer(defaultState /* state */, undoSnapshot([getState(num)]) /* action */);
|
|
for (num = 2; num <= MAX_STACK_SIZE; num++) {
|
|
reduxState = undoReducer(reduxState /* state */, undoSnapshot([getState(num)]) /* action */);
|
|
}
|
|
|
|
// Undo twice and redo
|
|
reduxState = undoReducer(reduxState /* state */, undo() /* action */);
|
|
reduxState = undoReducer(reduxState /* state */, undo() /* action */);
|
|
reduxState = undoReducer(reduxState /* state */, redo() /* action */);
|
|
|
|
expect(reduxState.pointer).toEqual(MAX_STACK_SIZE - 2);
|
|
expect(reduxState.stack).toHaveLength(MAX_STACK_SIZE);
|
|
expect(reduxState.stack[0].state).toEqual(1);
|
|
});
|
|
|
|
test('undoSnapshotAtMaxStackSize', () => {
|
|
let defaultState;
|
|
const getState = function (num) {
|
|
return {state: num};
|
|
};
|
|
// Push MAX_STACK_SIZE states
|
|
let num = 1;
|
|
let reduxState = undoReducer(defaultState /* state */, undoSnapshot([getState(num)]) /* action */);
|
|
for (num = 2; num <= MAX_STACK_SIZE; num++) {
|
|
reduxState = undoReducer(reduxState /* state */, undoSnapshot([getState(num)]) /* action */);
|
|
}
|
|
|
|
// Undo twice and then take a snapshot
|
|
reduxState = undoReducer(reduxState /* state */, undo() /* action */);
|
|
reduxState = undoReducer(reduxState /* state */, undo() /* action */);
|
|
reduxState = undoReducer(reduxState /* state */, undoSnapshot([getState(num)]) /* action */);
|
|
|
|
expect(reduxState.pointer).toEqual(MAX_STACK_SIZE - 2);
|
|
expect(reduxState.stack).toHaveLength(MAX_STACK_SIZE - 1);
|
|
expect(reduxState.stack[0].state).toEqual(1);
|
|
expect(reduxState.stack[MAX_STACK_SIZE - 2].state).toEqual(MAX_STACK_SIZE + 1); // Newest added state is at end
|
|
expect(reduxState.stack[MAX_STACK_SIZE - 3].state).toEqual(MAX_STACK_SIZE - 2); // Old redo state is gone
|
|
});
|