Skip to content

Commit

Permalink
Add tests and use injected store by default
Browse files Browse the repository at this point in the history
  • Loading branch information
agubler committed Jun 13, 2019
1 parent 6cd319f commit f78a33f
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 7 deletions.
23 changes: 16 additions & 7 deletions src/core/middleware/store.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,36 @@
import { destroy, invalidator, create } from '../vdom';
import injector from '../middleware/injector';
import Store, { StatePaths, Path } from '../../stores/Store';
import { Process } from '../../stores/process';

const factory = create({ destroy, invalidator });
const factory = create({ destroy, invalidator, injector });

export const createStoreMiddleware = <S = any>(initial?: Function) => {
let store = new Store();
export const createStoreMiddleware = <S = any>(initial?: (store: Store<S>) => void) => {
let store = new Store<S>();
let initialized = false;
initial && initial(store);
const storeMiddleware = factory(({ middleware: { destroy, invalidator } }) => {
const storeMiddleware = factory(({ middleware: { destroy, invalidator, injector } }) => {
const handles: any[] = [];
destroy(() => {
let handle: any;
while ((handle = handles.pop())) {
handle();
}
});
if (!initialized) {
const injectedStore = injector.get<Store<S>>('state');
if (injectedStore) {
store = injectedStore;
}
initialized = true;
}
const registeredPaths: string[] = [];
const path: StatePaths<S> = (path: any, ...segments: any) => {
return (store as any).path(path, ...segments);
};
return {
get<U = any>(path: Path<S, U>): U {
if (registeredPaths.indexOf(path.path) === -1) {
get<U = any>(path: Path<S, U>, subscribe = true): U {
if (subscribe && registeredPaths.indexOf(path.path) === -1) {
const handle = store.onChange(path, () => {
invalidator();
});
Expand All @@ -42,4 +51,4 @@ export const createStoreMiddleware = <S = any>(initial?: Function) => {
return storeMiddleware;
};

export default createStoreMiddleware();
export default createStoreMiddleware;
1 change: 1 addition & 0 deletions tests/core/unit/middleware/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ import './icache';
import './injector';
import './intersection';
import './resize';
import './store';
import './theme';
182 changes: 182 additions & 0 deletions tests/core/unit/middleware/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
const { it, describe, afterEach, beforeEach } = intern.getInterface('bdd');
const { assert } = intern.getPlugin('chai');
import { sandbox } from 'sinon';

import { createProcess } from '../../../../src/stores/process';
import { replace } from '../../../../src/stores/state/operations';
import Store from '../../../../src/stores/Store';
import createStoreMiddleware from '../../../../src/core/middleware/store';

const sb = sandbox.create();
const destroyStub = sb.stub();
const invalidatorStub = sb.stub();
const injectorStub = {
get: sb.stub(),
subscribe: sb.stub()
};
let storeMiddleware = createStoreMiddleware();

describe('store middleware', () => {
beforeEach(() => {
storeMiddleware = createStoreMiddleware();
});

afterEach(() => {
sb.resetHistory();
});

it('Should return data from store and subscribe to data changes for the path', () => {
const { callback } = storeMiddleware();
const store = callback({
id: 'test',
middleware: {
destroy: destroyStub,
invalidator: invalidatorStub,
injector: injectorStub
},
properties: {}
});
let result = store.get(store.path('my-state'));
assert.isUndefined(result);
const testProcess = createProcess('test', [
({ path }) => {
return [replace(path('my-state'), 'test-data')];
}
]);
store.executor(testProcess)({});
assert.isTrue(invalidatorStub.calledOnce);
result = store.get(store.path('my-state'));
assert.strictEqual(result, 'test-data');
});

it('should not subscribe to store changes when subscribe options is false', () => {
const { callback } = storeMiddleware();
const store = callback({
id: 'test',
middleware: {
destroy: destroyStub,
invalidator: invalidatorStub,
injector: injectorStub
},
properties: {}
});
let result = store.get(store.path('my-state'), false);
assert.isUndefined(result);
const testProcess = createProcess('test', [
({ path }) => {
return [replace(path('my-state'), 'test-data')];
}
]);
store.executor(testProcess)({});
assert.isTrue(invalidatorStub.notCalled);
result = store.get(store.path('my-state'));
assert.strictEqual(result, 'test-data');
});

it('Should be able to work with arrays in the store', () => {
const { callback } = storeMiddleware();
const store = callback({
id: 'test',
middleware: {
destroy: destroyStub,
invalidator: invalidatorStub,
injector: injectorStub
},
properties: {}
});
let result = store.get(store.path('my-state'));
assert.isUndefined(result);
let testProcess = createProcess('test', [
({ path }) => {
return [replace(path('my-state'), [1])];
}
]);
store.executor(testProcess)({});
assert.isTrue(invalidatorStub.calledOnce);
result = store.get(store.at(store.path('my-state'), 0));
assert.deepEqual(result, 1);
testProcess = createProcess('test', [
({ path, at }) => {
return [replace(at(path('my-state'), 1), 2)];
}
]);
store.executor(testProcess)({});
assert.isTrue(invalidatorStub.calledTwice);
result = store.get(store.at(store.path('my-state'), 1));
assert.deepEqual(result, 2);
});

it('Should remove subscription handles when destroyed', () => {
const { callback } = storeMiddleware();
const store = callback({
id: 'test',
middleware: {
destroy: destroyStub,
invalidator: invalidatorStub,
injector: injectorStub
},
properties: {}
});
let result = store.get(store.path('my-state'));
assert.isUndefined(result);
let testProcess = createProcess('test', [
({ path }) => {
return [replace(path('my-state'), 'test-data')];
}
]);
store.executor(testProcess)({});
assert.isTrue(invalidatorStub.calledOnce);
result = store.get(store.path('my-state'));
assert.strictEqual(result, 'test-data');
destroyStub.getCall(0).callArg(0);
testProcess = createProcess('test', [
({ path }) => {
return [replace(path('my-state'), 'test')];
}
]);
store.executor(testProcess)({});
assert.isTrue(invalidatorStub.calledOnce);
result = store.get(store.path('my-state'));
assert.strictEqual(result, 'test');
});

it('Should use an injected store if available', () => {
const { callback } = storeMiddleware();
const injectedStore = new Store();
let testProcess = createProcess('test', [
({ path }) => {
return [replace(path('my', 'nested', 'state'), 'existing-data')];
}
]);
testProcess(injectedStore)({});
injectorStub.get.returns(injectedStore);
const store = callback({
id: 'test',
middleware: {
destroy: destroyStub,
invalidator: invalidatorStub,
injector: injectorStub
},
properties: {}
});
const result = store.get(store.path('my', 'nested', 'state'));
assert.strictEqual(result, 'existing-data');
const otherStore = callback({
id: 'test',
middleware: {
destroy: destroyStub,
invalidator: invalidatorStub,
injector: injectorStub
},
properties: {}
});
const resultTwo = otherStore.get(store.path('my', 'nested', 'state'));
assert.strictEqual(resultTwo, 'existing-data');
});

it('Should run initialize function on creation', () => {
const init = sb.stub();
storeMiddleware = createStoreMiddleware(init);
assert.isTrue(init.calledOnce);
});
});

0 comments on commit f78a33f

Please sign in to comment.