lib/store/src/ArgsStore.test.ts
import { ArgsStore } from './ArgsStore';
jest.mock('@storybook/client-logger');
const stringType = { type: { name: 'string' } };
const booleanType = { type: { name: 'boolean' } };
describe('ArgsStore', () => {
describe('setInitial / get', () => {
it('returns in a straightforward way', () => {
const store = new ArgsStore();
store.setInitial({ id: 'id', initialArgs: { foo: 'bar' } } as any);
expect(store.get('id')).toEqual({ foo: 'bar' });
});
it('throws if you try to get non-existent', () => {
const store = new ArgsStore();
expect(() => store.get('id')).toThrow(/No args known/);
});
describe('on second call for same story', () => {
describe('if initialArgs are unchanged', () => {
it('does nothing if the args are untouched', () => {
const store = new ArgsStore();
const previousStory = {
id: 'id',
initialArgs: { a: '1', b: '1' },
argTypes: { a: stringType, b: stringType },
} as any;
store.setInitial(previousStory);
const story = {
id: 'id',
initialArgs: { a: '1', b: '1' },
argTypes: { a: stringType, b: stringType },
} as any;
store.setInitial(story);
expect(store.get(story.id)).toEqual({ a: '1', b: '1' });
});
it('retains any arg changes', () => {
const store = new ArgsStore();
const previousStory = {
id: 'id',
initialArgs: { a: '1', b: false, c: 'unchanged' },
argTypes: { a: stringType, b: booleanType, c: stringType },
} as any;
store.setInitial(previousStory);
// NOTE: I'm not sure technically you should be allowed to set d here, but
// let's make sure we behave sensibly if you do
store.update('id', { a: 'update', b: true, d: 'update' });
const story = {
id: 'id',
initialArgs: { a: '1', b: false, c: 'unchanged' },
argTypes: { a: stringType, b: booleanType, c: stringType },
} as any;
store.setInitial(story);
// In any case c is not retained.
expect(store.get(story.id)).toEqual({ a: 'update', b: true, c: 'unchanged' });
});
});
describe('when initialArgs change', () => {
it('replaces old args with new if the args are untouched', () => {
const store = new ArgsStore();
const previousStory = {
id: 'id',
initialArgs: { a: '1', b: '1' },
argTypes: { a: stringType, b: stringType },
} as any;
store.setInitial(previousStory);
const story = {
id: 'id',
initialArgs: { a: '1', c: '1' },
argTypes: { a: stringType, c: stringType },
} as any;
store.setInitial(story);
expect(store.get(story.id)).toEqual({ a: '1', c: '1' });
});
it('applies the same delta if the args are changed', () => {
const store = new ArgsStore();
const previousStory = {
id: 'id',
initialArgs: { a: '1', b: '1' },
argTypes: { a: stringType, b: stringType },
} as any;
store.setInitial(previousStory);
// NOTE: I'm not sure technically you should be allowed to set c here
store.update('id', { a: 'update', c: 'update' });
const story = {
id: 'id',
initialArgs: { a: '2', d: '2' },
argTypes: { a: stringType, d: stringType },
} as any;
store.setInitial(story);
// In any case c is not retained.
expect(store.get(story.id)).toEqual({ a: 'update', d: '2' });
});
});
});
});
describe('update', () => {
it('overrides on a per-key basis', () => {
const store = new ArgsStore();
store.setInitial({ id: 'id', initialArgs: {} } as any);
store.update('id', { foo: 'bar' });
expect(store.get('id')).toEqual({ foo: 'bar' });
store.update('id', { baz: 'bing' });
expect(store.get('id')).toEqual({ foo: 'bar', baz: 'bing' });
});
it('does not merge objects', () => {
const store = new ArgsStore();
store.setInitial({ id: 'id', initialArgs: {} } as any);
store.update('id', { obj: { foo: 'bar' } });
expect(store.get('id')).toEqual({ obj: { foo: 'bar' } });
store.update('id', { obj: { baz: 'bing' } });
expect(store.get('id')).toEqual({ obj: { baz: 'bing' } });
});
it('does not set keys to undefined, it simply unsets them', () => {
const store = new ArgsStore();
store.setInitial({ id: 'id', initialArgs: { foo: 'bar' } } as any);
store.update('id', { foo: undefined });
expect('foo' in store.get('id')).toBe(false);
});
});
describe('updateFromPersisted', () => {
it('ensures the types of args are correct', () => {
const store = new ArgsStore();
store.setInitial({ id: 'id', initialArgs: {} } as any);
const story = {
id: 'id',
argTypes: { a: stringType },
} as any;
store.updateFromPersisted(story, { a: 'str' });
expect(store.get('id')).toEqual({ a: 'str' });
store.updateFromPersisted(story, { a: 42 });
expect(store.get('id')).toEqual({ a: '42' });
});
it('merges objects and sparse arrays', () => {
const store = new ArgsStore();
store.setInitial({ id: 'id', initialArgs: { a: { foo: 'bar' }, b: ['1', '2', '3'] } } as any);
const story = {
id: 'id',
argTypes: {
a: { type: { name: 'object', value: { name: 'string' } } },
b: { type: { name: 'array', value: { name: 'string' } } },
},
} as any;
store.updateFromPersisted(story, { a: { baz: 'bing' } });
expect(store.get('id')).toEqual({
a: { foo: 'bar', baz: 'bing' },
b: ['1', '2', '3'],
});
// eslint-disable-next-line no-sparse-arrays
store.updateFromPersisted(story, { b: [, , '4'] });
expect(store.get('id')).toEqual({
a: { foo: 'bar', baz: 'bing' },
b: ['1', '2', '4'],
});
});
it('checks args are allowed options', () => {
const store = new ArgsStore();
store.setInitial({ id: 'id', initialArgs: {} } as any);
const story = {
id: 'id',
argTypes: { a: { type: { name: 'string' }, options: ['a', 'b'] } },
} as any;
store.updateFromPersisted(story, { a: 'random' });
expect(store.get('id')).toEqual({});
store.updateFromPersisted(story, { a: 'a' });
expect(store.get('id')).toEqual({ a: 'a' });
});
});
});