Skip to content

Commit 6ca0b7a

Browse files
authored
Merge 619b72e into 18c5312
2 parents 18c5312 + 619b72e commit 6ca0b7a

File tree

7 files changed

+584
-0
lines changed

7 files changed

+584
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/**
2+
* @license
3+
* Copyright 2019 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import * as sinon from 'sinon';
19+
import { PersistenceType } from '.';
20+
import { expect } from 'chai';
21+
import { browserLocalPersistence, browserSessionPersistence } from './browser';
22+
import { User } from '../../model/user';
23+
import { testUser } from '../../../test/mock_auth';
24+
25+
describe('core/persistence/browser', () => {
26+
beforeEach(() => {
27+
localStorage.clear();
28+
sessionStorage.clear();
29+
});
30+
31+
afterEach(() => sinon.restore());
32+
33+
describe('browserLocalPersistence', () => {
34+
const persistence = browserLocalPersistence;
35+
36+
it('should work with persistence type', async () => {
37+
const key = 'my-super-special-persistence-type';
38+
const value = PersistenceType.LOCAL;
39+
expect(await persistence.get(key)).to.be.null;
40+
await persistence.set(key, value);
41+
expect(await persistence.get(key)).to.be.eq(value);
42+
expect(await persistence.get('other-key')).to.be.null;
43+
await persistence.remove(key);
44+
expect(await persistence.get(key)).to.be.null;
45+
});
46+
47+
it('should call instantiator function if provided', async () => {
48+
const key = 'my-super-special-user';
49+
const value = testUser('some-uid');
50+
51+
expect(await persistence.get(key)).to.be.null;
52+
await persistence.set(key, value);
53+
const out = await persistence.get<User>(key, blob =>
54+
testUser(`test-${blob.uid}`)
55+
);
56+
expect(out?.uid).to.eql('test-some-uid');
57+
await persistence.remove(key);
58+
expect(await persistence.get(key)).to.be.null;
59+
});
60+
61+
describe('#isAvailable', () => {
62+
it('should emit false if localStorage setItem throws', async () => {
63+
sinon.stub(localStorage, 'setItem').throws(new Error('nope'));
64+
expect(await persistence.isAvailable()).to.be.false;
65+
});
66+
67+
it('should emit false if localStorage removeItem throws', async () => {
68+
sinon.stub(localStorage, 'removeItem').throws(new Error('nope'));
69+
expect(await persistence.isAvailable()).to.be.false;
70+
});
71+
72+
it('should emit true if everything works properly', async () => {
73+
expect(await persistence.isAvailable()).to.be.true;
74+
});
75+
});
76+
});
77+
78+
describe('browserSessionPersistence', () => {
79+
const persistence = browserSessionPersistence;
80+
81+
it('should work with persistence type', async () => {
82+
const key = 'my-super-special-persistence-type';
83+
const value = PersistenceType.SESSION;
84+
expect(await persistence.get(key)).to.be.null;
85+
await persistence.set(key, value);
86+
expect(await persistence.get(key)).to.be.eq(value);
87+
expect(await persistence.get('other-key')).to.be.null;
88+
await persistence.remove(key);
89+
expect(await persistence.get(key)).to.be.null;
90+
});
91+
92+
it('should call instantiator function if provided', async () => {
93+
const key = 'my-super-special-user';
94+
const value = testUser('some-uid');
95+
96+
expect(await persistence.get(key)).to.be.null;
97+
await persistence.set(key, value);
98+
const out = await persistence.get<User>(key, blob =>
99+
testUser(`test-${blob.uid}`)
100+
);
101+
expect(out?.uid).to.eql('test-some-uid');
102+
await persistence.remove(key);
103+
expect(await persistence.get(key)).to.be.null;
104+
});
105+
106+
describe('#isAvailable', () => {
107+
it('should emit false if sessionStorage setItem throws', async () => {
108+
sinon.stub(sessionStorage, 'setItem').throws(new Error('nope'));
109+
expect(await persistence.isAvailable()).to.be.false;
110+
});
111+
112+
it('should emit false if sessionStorage removeItem throws', async () => {
113+
sinon.stub(sessionStorage, 'removeItem').throws(new Error('nope'));
114+
expect(await persistence.isAvailable()).to.be.false;
115+
});
116+
117+
it('should emit true if everything works properly', async () => {
118+
expect(await persistence.isAvailable()).to.be.true;
119+
});
120+
});
121+
});
122+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* @license
3+
* Copyright 2019 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import {
19+
Persistence,
20+
PersistenceType,
21+
PersistenceValue,
22+
Instantiator
23+
} from '.';
24+
25+
const STORAGE_AVAILABLE_KEY_ = '__sak';
26+
27+
class BrowserPersistence implements Persistence {
28+
type: PersistenceType = PersistenceType.LOCAL;
29+
30+
constructor(private readonly storage: Storage) {}
31+
32+
async isAvailable(): Promise<boolean> {
33+
try {
34+
if (!this.storage) {
35+
return false;
36+
}
37+
this.storage.setItem(STORAGE_AVAILABLE_KEY_, '1');
38+
this.storage.removeItem(STORAGE_AVAILABLE_KEY_);
39+
return true;
40+
} catch {
41+
return false;
42+
}
43+
}
44+
45+
async set(key: string, value: PersistenceValue): Promise<void> {
46+
this.storage.setItem(key, JSON.stringify(value));
47+
}
48+
49+
async get<T extends PersistenceValue>(
50+
key: string,
51+
instantiator?: Instantiator<T>
52+
): Promise<T | null> {
53+
const json = this.storage.getItem(key);
54+
const obj = json ? JSON.parse(json) : null;
55+
return instantiator && obj ? instantiator(obj) : obj;
56+
}
57+
58+
async remove(key: string): Promise<void> {
59+
this.storage.removeItem(key);
60+
}
61+
}
62+
63+
export const browserLocalPersistence: Persistence = new BrowserPersistence(
64+
localStorage
65+
);
66+
export const browserSessionPersistence: Persistence = new BrowserPersistence(
67+
sessionStorage
68+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* @license
3+
* Copyright 2019 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { inMemoryPersistence as persistence } from './in_memory';
19+
import { PersistenceType } from '.';
20+
import { expect } from 'chai';
21+
import { User } from '../../model/user';
22+
import { testUser } from '../../../test/mock_auth';
23+
24+
describe('core/persistence/in_memory', () => {
25+
it('should work with persistence type', async () => {
26+
const key = 'my-super-special-persistence-type';
27+
const value = PersistenceType.LOCAL;
28+
expect(await persistence.get(key)).to.be.null;
29+
await persistence.set(key, value);
30+
expect(await persistence.get(key)).to.be.eq(value);
31+
expect(await persistence.get('other-key')).to.be.null;
32+
await persistence.remove(key);
33+
expect(await persistence.get(key)).to.be.null;
34+
});
35+
36+
it('should work with user', async () => {
37+
const key = 'my-super-special-user';
38+
const value = testUser('uid');
39+
40+
expect(await persistence.get(key)).to.be.null;
41+
await persistence.set(key, value);
42+
expect(await persistence.get<User>(key)).to.eql(value);
43+
expect(await persistence.get('other-key')).to.be.null;
44+
await persistence.remove(key);
45+
expect(await persistence.get(key)).to.be.null;
46+
});
47+
48+
it('isAvailable returns true', async () => {
49+
expect(await persistence.isAvailable()).to.be.true;
50+
});
51+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* @license
3+
* Copyright 2019 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { Persistence, PersistenceType, PersistenceValue } from '../persistence';
19+
20+
class InMemoryPersistence implements Persistence {
21+
type: PersistenceType = PersistenceType.NONE;
22+
storage: {
23+
[key: string]: PersistenceValue;
24+
} = {};
25+
26+
async isAvailable(): Promise<boolean> {
27+
return true;
28+
}
29+
30+
async set(key: string, value: PersistenceValue): Promise<void> {
31+
this.storage[key] = value;
32+
}
33+
34+
async get<T extends PersistenceValue>(key: string): Promise<T | null> {
35+
const value = this.storage[key];
36+
return value === undefined ? null : (value as T);
37+
}
38+
39+
async remove(key: string): Promise<void> {
40+
delete this.storage[key];
41+
}
42+
}
43+
44+
export const inMemoryPersistence: Persistence = new InMemoryPersistence();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* @license
3+
* Copyright 2019 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { User } from '../../model/user';
19+
20+
export enum PersistenceType {
21+
SESSION = 'SESSION',
22+
LOCAL = 'LOCAL',
23+
NONE = 'NONE'
24+
}
25+
26+
export interface Instantiator<T> {
27+
(blob: { [key: string]: unknown }): T;
28+
}
29+
30+
export type PersistenceValue = PersistenceType | User;
31+
32+
export interface Persistence {
33+
type: PersistenceType;
34+
isAvailable(): Promise<boolean>;
35+
set(key: string, value: PersistenceValue): Promise<void>;
36+
get<T extends PersistenceValue>(
37+
key: string,
38+
instantiator?: Instantiator<T>
39+
): Promise<T | null>;
40+
remove(key: string): Promise<void>;
41+
}

0 commit comments

Comments
 (0)