core-app-api: add tests for new StorageApi design

Co-authored-by: Fredrik Adelöw <freben@gmail.com>
Co-authored-by: blam <ben@blam.sh>
Co-authored-by: Johan Haals <johan.haals@gmail.com>
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2021-12-16 11:52:49 +01:00
parent 78fa85cfa7
commit 61fa5d0171
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { WebStorage } from './WebStorage';
import { ErrorApi, StorageApi } from '@backstage/core-plugin-api';
@@ -33,6 +34,12 @@ describe('WebStorage Storage API', () => {
const storage = createWebStorage();
expect(storage.get('myfakekey')).toBeUndefined();
expect(storage.snapshot('myfakekey')).toEqual({
key: 'myfakekey',
presence: 'absent',
value: undefined,
newValue: undefined,
});
});
it('should allow the setting and getting of the simple data structures', async () => {
@@ -44,9 +51,27 @@ describe('WebStorage Storage API', () => {
expect(storage.get('myfakekey')).toBe('helloimastring');
expect(storage.get('mysecondfakekey')).toBe(1234);
expect(storage.get('mythirdfakekey')).toBe(true);
expect(storage.snapshot('myfakekey')).toEqual({
key: 'myfakekey',
presence: 'present',
value: 'helloimastring',
newValue: 'helloimastring',
});
expect(storage.snapshot('mysecondfakekey')).toEqual({
key: 'mysecondfakekey',
presence: 'present',
value: 1234,
newValue: 1234,
});
expect(storage.snapshot('mythirdfakekey')).toEqual({
key: 'mythirdfakekey',
presence: 'present',
value: true,
newValue: true,
});
});
it('should allow setting of complex datastructures', async () => {
it('should allow setting of complex data structures', async () => {
const storage = createWebStorage();
const mockData = {
@@ -57,6 +82,12 @@ describe('WebStorage Storage API', () => {
await storage.set('myfakekey', mockData);
expect(storage.get('myfakekey')).toEqual(mockData);
expect(storage.snapshot('myfakekey')).toEqual({
key: 'myfakekey',
presence: 'present',
value: mockData,
newValue: mockData,
});
});
it('should subscribe to key changes when setting a new value', async () => {
@@ -67,10 +98,12 @@ describe('WebStorage Storage API', () => {
const mockData = { hello: 'im a great new value' };
await new Promise<void>(resolve => {
storage.observe$<String>('correctKey').subscribe({
next: (...args) => {
selectedKeyNextHandler(...args);
resolve();
storage.observe$<typeof mockData>('correctKey').subscribe({
next: snapshot => {
selectedKeyNextHandler(snapshot);
if (snapshot.presence === 'present') {
resolve();
}
},
});
@@ -79,10 +112,12 @@ describe('WebStorage Storage API', () => {
storage.set('correctKey', mockData);
});
expect(wrongKeyNextHandler).not.toHaveBeenCalled();
expect(wrongKeyNextHandler).toHaveBeenCalledTimes(0);
expect(selectedKeyNextHandler).toHaveBeenCalledTimes(1);
expect(selectedKeyNextHandler).toHaveBeenCalledWith({
key: 'correctKey',
presence: 'present',
value: mockData,
newValue: mockData,
});
});
@@ -98,9 +133,11 @@ describe('WebStorage Storage API', () => {
await new Promise<void>(resolve => {
storage.observe$('correctKey').subscribe({
next: (...args) => {
selectedKeyNextHandler(...args);
resolve();
next: snapshot => {
selectedKeyNextHandler(snapshot);
if (snapshot.presence === 'absent') {
resolve();
}
},
});
@@ -109,10 +146,12 @@ describe('WebStorage Storage API', () => {
storage.remove('correctKey');
});
expect(wrongKeyNextHandler).not.toHaveBeenCalled();
expect(wrongKeyNextHandler).toHaveBeenCalledTimes(0);
expect(selectedKeyNextHandler).toHaveBeenCalledTimes(1);
expect(selectedKeyNextHandler).toHaveBeenCalledWith({
key: 'correctKey',
presence: 'absent',
value: undefined,
newValue: undefined,
});
});
@@ -130,9 +169,24 @@ describe('WebStorage Storage API', () => {
expect(firstStorage.get(keyName)).not.toBe(secondStorage.get(keyName));
expect(firstStorage.get(keyName)).toBe('boop');
expect(secondStorage.get(keyName)).toBe('deerp');
expect(firstStorage.snapshot(keyName)).not.toEqual(
secondStorage.snapshot(keyName),
);
expect(firstStorage.snapshot(keyName)).toEqual({
key: keyName,
presence: 'present',
value: 'boop',
newValue: 'boop',
});
expect(secondStorage.snapshot(keyName)).toEqual({
key: keyName,
presence: 'present',
value: 'deerp',
newValue: 'deerp',
});
});
it('should not clash with other namesapces when creating buckets', async () => {
it('should not clash with other namespaces when creating buckets', async () => {
const rootStorage = createWebStorage();
// when getting key test2 it will translate to /profile/something/deep/test2
@@ -145,7 +199,9 @@ describe('WebStorage Storage API', () => {
await firstStorage.set('test2', { error: true });
expect(secondStorage.get('deep/test2')).toBe(undefined);
expect(secondStorage.snapshot('deep/test2')).toMatchObject({
presence: 'absent',
});
});
it('should call the error api when the json can not be parsed in local storage', async () => {
@@ -155,9 +211,14 @@ describe('WebStorage Storage API', () => {
localStorage.setItem('/Test/Mock/Thing/key', '{smd: asdouindA}');
const value = rootStorage.get('key');
const value = rootStorage.snapshot('key');
expect(value).toBe(undefined);
expect(value).toEqual({
key: 'key',
presence: 'absent',
value: undefined,
newValue: undefined,
});
expect(mockErrorApi.post).toHaveBeenCalledWith(expect.any(Error));
expect(mockErrorApi.post).toHaveBeenCalledWith(
expect.objectContaining({
@@ -166,11 +227,35 @@ describe('WebStorage Storage API', () => {
);
});
it('should return a singleton for the same namespace and same bucket', async () => {
it('should return a stable reference for the same namespace and same bucket', async () => {
const rootStorage = createWebStorage({
namespace: '/Test/Mock/Thing/Thing ',
});
expect(rootStorage.forBucket('test')).toBe(rootStorage.forBucket('test'));
});
it('should freeze the snapshot value', async () => {
const storage = createWebStorage();
const data = { foo: 'bar', baz: [{ foo: 'bar' }] };
storage.set('foo', data);
const snapshot = storage.snapshot<typeof data>('foo');
expect(snapshot.value).not.toBe(data);
if (snapshot.presence !== 'present') {
throw new Error('Invalid presence');
}
expect(() => {
snapshot.value.foo = 'buzz';
}).toThrow(/Cannot assign to read only property/);
expect(() => {
snapshot.value.baz[0].foo = 'buzz';
}).toThrow(/Cannot assign to read only property/);
expect(() => {
snapshot.value.baz.push({ foo: 'buzz' });
}).toThrow(/Cannot add property 1, object is not extensible/);
});
});