Skip to content

Commit

Permalink
fix(elements): add check if localStorage is available (#1073)
Browse files Browse the repository at this point in the history
  • Loading branch information
hugo-vrijswijk authored May 3, 2021
1 parent 4b2358d commit 9e57c0a
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 2 deletions.
5 changes: 3 additions & 2 deletions packages/elements/src/components/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import theme from './theme.scss';
import { createCustomEvent } from '../../lib/custom-events';
import { FileUnderTestModel, Metrics, MutationTestMetricsResult, TestFileModel, TestMetrics } from 'mutation-testing-metrics/src/model';
import { toAbsoluteUrl } from '../../lib/htmlHelpers';
import { isLocalStorageAvailable } from '../../lib/browser';

interface BaseContext {
path: string[];
Expand Down Expand Up @@ -74,7 +75,7 @@ export class MutationTestReportAppComponent extends LitElement {
// Set the theme when no theme is selected (light vs dark)
if (!this.theme) {
// 1. check local storage
const theme = localStorage.getItem('mutation-testing-elements-theme');
const theme = isLocalStorageAvailable() && localStorage.getItem('mutation-testing-elements-theme');
if (theme) {
this.theme = theme;
// 2. check for user's OS preference
Expand Down Expand Up @@ -154,7 +155,7 @@ export class MutationTestReportAppComponent extends LitElement {
public themeSwitch = (event: CustomEvent<string>) => {
this.theme = event.detail;

localStorage.setItem('mutation-testing-elements-theme', this.theme);
isLocalStorageAvailable() && localStorage.setItem('mutation-testing-elements-theme', this.theme);
};

public static styles = [globals, unsafeCSS(theme), bootstrap, unsafeCSS(style)];
Expand Down
13 changes: 13 additions & 0 deletions packages/elements/src/lib/browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Test if localStorage exists and is enabled
*/
export function isLocalStorageAvailable() {
const test = 'test';
try {
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch (e) {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,21 @@ describe(MutationTestReportAppComponent.name, () => {
expect(localStorage.getItem('mutation-testing-elements-theme'), 'dark');
});

it('should not set theme to local storage if localStorage is not available', async () => {
// Arrange
sut.element.report = createReport();
const setItemStub = sinon.stub(localStorage, 'setItem').throws(new Error());
await sut.whenStable();

// Act
sut.$('mte-theme-switch').dispatchEvent(createCustomEvent('theme-switch', 'dark'));
await sut.whenStable();

// Assert
expect(sut.element.theme).eq('dark');
expect(setItemStub.notCalled).false;
});

describe('themeBackgroundColor', () => {
it('should show light theme-color on light theme', async () => {
// Arrange
Expand All @@ -198,6 +213,16 @@ describe(MutationTestReportAppComponent.name, () => {
});
});

it('should use fallbacks if localStorage is not available', async () => {
sinon.stub(localStorage, 'setItem').throws(new Error());
matchMediaStub.withArgs('(prefers-color-scheme: dark)').returns({ matches: true } as MediaQueryList);
sut.element.report = createReport();
await sut.whenStable();

// Assert
expect(sut.element.theme).eq('dark');
});

it('should choose attribute value over local storage', async () => {
// Arrange
localStorage.setItem('mutation-testing-elements-theme', 'dark');
Expand Down
35 changes: 35 additions & 0 deletions packages/elements/test/unit/lib/browser.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { isLocalStorageAvailable } from '../../../src/lib/browser';
import sinon from 'sinon';
import { expect } from 'chai';

describe(isLocalStorageAvailable.name, () => {
let setItemStub: sinon.SinonStub<Parameters<Storage['setItem']>, ReturnType<Storage['setItem']>>;
let removeItemStub: sinon.SinonStub<Parameters<Storage['removeItem']>, ReturnType<Storage['removeItem']>>;

beforeEach(() => {
setItemStub = sinon.stub(localStorage, 'setItem');
removeItemStub = sinon.stub(localStorage, 'removeItem');
});

it(`should be false if setItem throws`, () => {
setItemStub.throws(new Error('Quota exceeded'));

expect(isLocalStorageAvailable()).to.be.false;
});

it(`should be false if removeItem throws`, () => {
removeItemStub.throws(new Error('Quota exceeded'));

expect(isLocalStorageAvailable()).to.be.false;
});

it(`should be false if localStorage is undefined`, () => {
sinon.replaceGetter(window, 'localStorage', () => (undefined as unknown) as Storage);

expect(isLocalStorageAvailable()).to.be.false;
});

it('should be true if localStorage works', () => {
expect(isLocalStorageAvailable()).to.be.true;
});
});

0 comments on commit 9e57c0a

Please sign in to comment.