diff --git a/package.json b/package.json index 5e1ee22..546fd40 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "repository": "https://github.com/ericmackrodt/staat", "devDependencies": { "@types/jest": "^23.3.10", + "@types/node": "^10.12.18", "coveralls": "^3.0.2", "jest": "^23.6.0", "lerna": "^3.8.5", diff --git a/packages/time-travel/src/__tests__/time-travel.test.ts b/packages/time-travel/src/__tests__/time-travel.test.ts index 9bfac08..8ea562f 100644 --- a/packages/time-travel/src/__tests__/time-travel.test.ts +++ b/packages/time-travel/src/__tests__/time-travel.test.ts @@ -1,5 +1,169 @@ -describe('TimeTravel', () => { - it('does', () => { - expect(true).toBe(true); +import timeTravel from '../time-travel'; +import { scope } from 'staat'; +import { TimeTravelContainer } from '../time-travel-container'; + +type State = { + argument0: string; + argument1: string; + argument2: number; + scope: { + scopeArgument: string; + }; +}; + +const baseState: State = { + argument0: 'str', + argument1: '', + argument2: 0, + scope: { scopeArgument: '0' }, +}; + +function transformer( + state: State, + argument1: string, + argument2: number, +): State { + return { ...state, argument1, argument2 }; +} + +function transformerPromised( + state: State, + argument1: string, + argument2: number, +): Promise { + return Promise.resolve({ ...state, argument1, argument2 }); +} + +describe('timeTravel', () => { + let setPresentStub: jest.Mock<{}>; + let undoStub: jest.Mock<{}>; + let redoStub: jest.Mock<{}>; + beforeEach(() => { + setPresentStub = jest.fn(TimeTravelContainer.prototype.setPresent); + undoStub = jest.fn(TimeTravelContainer.prototype.undo); + redoStub = jest.fn(TimeTravelContainer.prototype.redo); + TimeTravelContainer.prototype.setPresent = setPresentStub; + TimeTravelContainer.prototype.undo = undoStub; + TimeTravelContainer.prototype.redo = redoStub; + }); + + it('retains transformer functionality', () => { + const sut = timeTravel({ transformer }); + expect(transformer(baseState, 'a', 1)).not.toBe( + sut.transformer(baseState, 'a', 1), + ); + expect(sut.transformer(baseState, 'a', 1)).toEqual( + transformer(baseState, 'a', 1), + ); + }); + + it('retains transformer functionality with promise', async () => { + const sut = timeTravel({ transformerPromised }); + expect(await sut.transformerPromised(baseState, 'a', 1)).not.toBe( + await transformerPromised(baseState, 'a', 1), + ); + expect(await sut.transformerPromised(baseState, 'a', 1)).toEqual( + await transformerPromised(baseState, 'a', 1), + ); + }); + + it('builds object with time travel functions', () => { + const sut = timeTravel({ transformer }); + expect(sut).toHaveProperty('undo'); + expect(sut).toHaveProperty('redo'); + }); + + it('sets up scoped transformers', () => { + const testScope = scope('scope'); + const scopedTransformer = testScope.transformer( + (currentScope, scopeArgument: string) => { + return { ...currentScope, scopeArgument }; + }, + ); + const sut = timeTravel({ scopedTransformer }, testScope); + expect(sut.scopedTransformer(baseState, 'scoped_value')).not.toBe( + scopedTransformer(baseState, 'scoped_value'), + ); + expect(sut.scopedTransformer(baseState, 'scoped_value')).toEqual( + scopedTransformer(baseState, 'scoped_value'), + ); + }); + + it('calls setPresent when calling transformer', () => { + const sut = timeTravel({ transformer }); + sut.transformer(baseState, 'a', 1); + expect(setPresentStub).toHaveBeenCalledTimes(1); + }); + + it('calls scoped setPresent when calling transformer', () => { + const testScope = scope('scope'); + const scopedTransformer = testScope.transformer( + (currentScope, scopeArgument: string) => { + return { ...currentScope, scopeArgument }; + }, + ); + const sut = timeTravel({ scopedTransformer }, testScope); + sut.scopedTransformer(baseState, 'scoped_value'); + expect(setPresentStub).toHaveBeenCalledTimes(1); + expect(setPresentStub).toHaveBeenCalledWith( + { scopeArgument: '0' }, + { scopeArgument: 'scoped_value' }, + ); + }); + + it('calls undo', () => { + const sut = timeTravel({ transformer }); + const newState = sut.transformer(baseState, 'new_value', 10) as State; + sut.undo(newState); + expect(undoStub).toHaveBeenCalledTimes(1); + expect(undoStub).toHaveBeenCalledWith({ + argument0: 'str', + argument1: 'new_value', + argument2: 10, + scope: { scopeArgument: '0' }, + }); + }); + + it('calls scoped undo', () => { + const testScope = scope('scope'); + const scopedTransformer = testScope.transformer( + (currentScope, scopeArgument: string) => { + return { ...currentScope, scopeArgument }; + }, + ); + const sut = timeTravel({ scopedTransformer }, testScope); + const newState = sut.scopedTransformer(baseState, 'scoped_value') as State; + sut.undo(newState); + expect(undoStub).toHaveBeenCalledTimes(1); + expect(undoStub).toHaveBeenCalledWith({ scopeArgument: 'scoped_value' }); + }); + + it('calls redo', () => { + const sut = timeTravel({ transformer }); + let newState = sut.transformer(baseState, 'new_value', 10) as State; + newState = sut.undo(newState) as State; + sut.redo(newState); + expect(redoStub).toHaveBeenCalledTimes(1); + expect(redoStub).toHaveBeenCalledWith({ + argument0: 'str', + argument1: '', + argument2: 0, + scope: { scopeArgument: '0' }, + }); + }); + + it('calls scoped redo', () => { + const testScope = scope('scope'); + const scopedTransformer = testScope.transformer( + (currentScope, scopeArgument: string) => { + return { ...currentScope, scopeArgument }; + }, + ); + const sut = timeTravel({ scopedTransformer }, testScope); + let newState = sut.scopedTransformer(baseState, 'scoped_value') as State; + newState = sut.undo(newState) as State; + sut.redo(newState); + expect(redoStub).toHaveBeenCalledTimes(1); + expect(redoStub).toHaveBeenCalledWith({ scopeArgument: '0' }); }); }); diff --git a/packages/time-travel/src/__tests__/utils.test.ts b/packages/time-travel/src/__tests__/utils.test.ts new file mode 100644 index 0000000..7f010ac --- /dev/null +++ b/packages/time-travel/src/__tests__/utils.test.ts @@ -0,0 +1,18 @@ +import * as utils from '../utils'; + +describe('timeTravel/utils', () => { + test('getKeys', () => { + expect(utils.getKeys({ prop1: '', prop2: 0 })).toEqual(['prop1', 'prop2']); + expect(utils.getKeys({ keyA: new Date(), keyB: ['array'] })).toEqual([ + 'keyA', + 'keyB', + ]); + }); + + test('asArray', () => { + expect(utils.asArray('test')).toEqual(['test']); + expect(utils.asArray(['test'])).toEqual(['test']); + expect(utils.asArray(1)).toEqual([1]); + expect(utils.asArray([1])).toEqual([1]); + }); +}); diff --git a/yarn.lock b/yarn.lock index 7128596..80d3659 100644 --- a/yarn.lock +++ b/yarn.lock @@ -605,6 +605,11 @@ version "23.3.10" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.10.tgz#4897974cc317bf99d4fe6af1efa15957fa9c94de" +"@types/node@^10.12.18": + version "10.12.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" + integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== + "@types/prop-types@*": version "15.5.8" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.8.tgz#8ae4e0ea205fe95c3901a5a1df7f66495e3a56ce"