|
|
|
import { ObservableStore } from '@metamask/obs-store';
|
|
|
|
import {
|
|
|
|
BaseController,
|
|
|
|
BaseControllerV2,
|
|
|
|
ControllerMessenger,
|
|
|
|
} from '@metamask/controllers';
|
|
|
|
import ComposableObservableStore from './ComposableObservableStore';
|
|
|
|
|
|
|
|
class OldExampleController extends BaseController {
|
|
|
|
name = 'OldExampleController';
|
|
|
|
|
|
|
|
defaultState = {
|
|
|
|
baz: 'baz',
|
|
|
|
};
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
this.initialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
updateBaz(contents) {
|
|
|
|
this.update({ baz: contents });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
class ExampleController extends BaseControllerV2 {
|
|
|
|
static defaultState = {
|
|
|
|
bar: 'bar',
|
|
|
|
};
|
|
|
|
|
|
|
|
static metadata = {
|
|
|
|
bar: { persist: true, anonymous: true },
|
|
|
|
};
|
|
|
|
|
|
|
|
constructor({ messenger }) {
|
|
|
|
super({
|
|
|
|
messenger,
|
|
|
|
name: 'ExampleController',
|
|
|
|
metadata: ExampleController.metadata,
|
|
|
|
state: ExampleController.defaultState,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
updateBar(contents) {
|
|
|
|
this.update(() => {
|
|
|
|
return { bar: contents };
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
describe('ComposableObservableStore', () => {
|
|
|
|
it('should register initial state', () => {
|
|
|
|
const controllerMessenger = new ControllerMessenger();
|
|
|
|
const store = new ComposableObservableStore({
|
|
|
|
controllerMessenger,
|
|
|
|
state: 'state',
|
|
|
|
});
|
|
|
|
expect(store.getState()).toStrictEqual('state');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should register initial structure', () => {
|
|
|
|
const controllerMessenger = new ControllerMessenger();
|
|
|
|
const testStore = new ObservableStore();
|
|
|
|
const store = new ComposableObservableStore({
|
|
|
|
config: { TestStore: testStore },
|
|
|
|
controllerMessenger,
|
|
|
|
});
|
|
|
|
testStore.putState('state');
|
|
|
|
expect(store.getState()).toStrictEqual({ TestStore: 'state' });
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should update structure with observable store', () => {
|
|
|
|
const controllerMessenger = new ControllerMessenger();
|
|
|
|
const testStore = new ObservableStore();
|
|
|
|
const store = new ComposableObservableStore({ controllerMessenger });
|
|
|
|
store.updateStructure({ TestStore: testStore });
|
|
|
|
testStore.putState('state');
|
|
|
|
expect(store.getState()).toStrictEqual({ TestStore: 'state' });
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should update structure with BaseController-based controller', () => {
|
|
|
|
const controllerMessenger = new ControllerMessenger();
|
|
|
|
const oldExampleController = new OldExampleController();
|
|
|
|
const store = new ComposableObservableStore({ controllerMessenger });
|
|
|
|
store.updateStructure({ OldExample: oldExampleController });
|
|
|
|
oldExampleController.updateBaz('state');
|
|
|
|
expect(store.getState()).toStrictEqual({ OldExample: { baz: 'state' } });
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should update structure with BaseControllerV2-based controller', () => {
|
|
|
|
const controllerMessenger = new ControllerMessenger();
|
|
|
|
const exampleController = new ExampleController({
|
|
|
|
messenger: controllerMessenger,
|
|
|
|
});
|
|
|
|
const store = new ComposableObservableStore({ controllerMessenger });
|
|
|
|
store.updateStructure({ Example: exampleController });
|
|
|
|
exampleController.updateBar('state');
|
|
|
|
expect(store.getState()).toStrictEqual({ Example: { bar: 'state' } });
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should update structure with all three types of stores', () => {
|
|
|
|
const controllerMessenger = new ControllerMessenger();
|
|
|
|
const exampleStore = new ObservableStore();
|
|
|
|
const exampleController = new ExampleController({
|
|
|
|
messenger: controllerMessenger,
|
|
|
|
});
|
|
|
|
const oldExampleController = new OldExampleController();
|
|
|
|
const store = new ComposableObservableStore({ controllerMessenger });
|
|
|
|
store.updateStructure({
|
|
|
|
Example: exampleController,
|
|
|
|
OldExample: oldExampleController,
|
|
|
|
Store: exampleStore,
|
|
|
|
});
|
|
|
|
exampleStore.putState('state');
|
|
|
|
exampleController.updateBar('state');
|
|
|
|
oldExampleController.updateBaz('state');
|
|
|
|
expect(store.getState()).toStrictEqual({
|
|
|
|
Example: { bar: 'state' },
|
|
|
|
OldExample: { baz: 'state' },
|
|
|
|
Store: 'state',
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return flattened state', () => {
|
|
|
|
const controllerMessenger = new ControllerMessenger();
|
|
|
|
const fooStore = new ObservableStore({ foo: 'foo' });
|
|
|
|
const barController = new ExampleController({
|
|
|
|
messenger: controllerMessenger,
|
|
|
|
});
|
|
|
|
const bazController = new OldExampleController();
|
|
|
|
const store = new ComposableObservableStore({
|
|
|
|
config: {
|
|
|
|
FooStore: fooStore,
|
|
|
|
BarStore: barController,
|
|
|
|
BazStore: bazController,
|
|
|
|
},
|
|
|
|
controllerMessenger,
|
|
|
|
state: {
|
|
|
|
FooStore: fooStore.getState(),
|
|
|
|
BarStore: barController.state,
|
|
|
|
BazStore: bazController.state,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
expect(store.getFlatState()).toStrictEqual({
|
|
|
|
foo: 'foo',
|
|
|
|
bar: 'bar',
|
|
|
|
baz: 'baz',
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return empty flattened state when not configured', () => {
|
|
|
|
const controllerMessenger = new ControllerMessenger();
|
|
|
|
const store = new ComposableObservableStore({ controllerMessenger });
|
|
|
|
expect(store.getFlatState()).toStrictEqual({});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should throw if the controller messenger is omitted and the config includes a BaseControllerV2 controller', () => {
|
|
|
|
const controllerMessenger = new ControllerMessenger();
|
|
|
|
const exampleController = new ExampleController({
|
|
|
|
messenger: controllerMessenger,
|
|
|
|
});
|
|
|
|
expect(
|
|
|
|
() =>
|
|
|
|
new ComposableObservableStore({
|
|
|
|
config: {
|
|
|
|
Example: exampleController,
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
).toThrow(`Cannot read property 'subscribe' of undefined`);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should throw if the controller messenger is omitted and updateStructure called with a BaseControllerV2 controller', () => {
|
|
|
|
const controllerMessenger = new ControllerMessenger();
|
|
|
|
const exampleController = new ExampleController({
|
|
|
|
messenger: controllerMessenger,
|
|
|
|
});
|
|
|
|
const store = new ComposableObservableStore({});
|
|
|
|
expect(() => store.updateStructure({ Example: exampleController })).toThrow(
|
|
|
|
`Cannot read property 'subscribe' of undefined`,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should throw if initialized with undefined config entry', () => {
|
|
|
|
const controllerMessenger = new ControllerMessenger();
|
|
|
|
expect(
|
|
|
|
() =>
|
|
|
|
new ComposableObservableStore({
|
|
|
|
config: {
|
|
|
|
Example: undefined,
|
|
|
|
},
|
|
|
|
controllerMessenger,
|
|
|
|
}),
|
|
|
|
).toThrow(`Undefined 'Example'`);
|
|
|
|
});
|
|
|
|
});
|