fix(docs-infra): do not break when cookies are disabled in the browser (#35557)

Whenever cookies are disabled in the browser, `window.localStorage` is
not avaialable to the app (i.e. even trying to access
`window.localStorage` throws an error).

To avoid breaking the app, we use a no-op `Storage` implementation if
`window.localStorage` is not available.

(This is similar to #33829, but for `localStorage` instead of
`sessionStorage`.)

Fixes #35555

PR Close #35557
This commit is contained in:
George Kalpakas 2020-02-19 18:58:33 +02:00 committed by Miško Hevery
parent 9c01ca42d3
commit 975a11b37f
2 changed files with 36 additions and 5 deletions

View File

@ -111,6 +111,22 @@ describe('NotificationComponent', () => {
expect(getItemSpy).toHaveBeenCalledWith('aio-notification/survey-january-2018'); expect(getItemSpy).toHaveBeenCalledWith('aio-notification/survey-january-2018');
expect(component.showNotification).toBe('hide'); expect(component.showNotification).toBe('hide');
}); });
it('should not break when cookies are disabled in the browser', () => {
configTestingModule();
// Simulate `window.localStorage` being inaccessible, when cookies are disabled.
const mockWindow: MockWindow = TestBed.inject(WindowToken);
Object.defineProperty(mockWindow, 'localStorage', {
get() { throw new Error('The operation is insecure'); },
});
expect(() => createComponent()).not.toThrow();
expect(component.showNotification).toBe('show');
component.dismiss();
expect(component.showNotification).toBe('hide');
});
}); });
@Component({ @Component({

View File

@ -20,7 +20,7 @@ const LOCAL_STORAGE_NAMESPACE = 'aio-notification/';
] ]
}) })
export class NotificationComponent implements OnInit { export class NotificationComponent implements OnInit {
private get localStorage() { return this.window.localStorage; } private storage: Storage;
@Input() dismissOnContentClick: boolean; @Input() dismissOnContentClick: boolean;
@Input() notificationId: string; @Input() notificationId: string;
@ -31,12 +31,27 @@ export class NotificationComponent implements OnInit {
showNotification: 'show'|'hide'; showNotification: 'show'|'hide';
constructor( constructor(
@Inject(WindowToken) private window: Window, @Inject(WindowToken) window: Window,
@Inject(CurrentDateToken) private currentDate: Date @Inject(CurrentDateToken) private currentDate: Date
) {} ) {
try {
this.storage = window.localStorage;
} catch {
// When cookies are disabled in the browser, even trying to access
// `window.localStorage` throws an error. Use a no-op storage.
this.storage = {
length: 0,
clear: () => undefined,
getItem: () => null,
key: () => null,
removeItem: () => undefined,
setItem: () => undefined
};
}
}
ngOnInit() { ngOnInit() {
const previouslyHidden = this.localStorage.getItem(LOCAL_STORAGE_NAMESPACE + this.notificationId) === 'hide'; const previouslyHidden = this.storage.getItem(LOCAL_STORAGE_NAMESPACE + this.notificationId) === 'hide';
const expired = this.currentDate > new Date(this.expirationDate); const expired = this.currentDate > new Date(this.expirationDate);
this.showNotification = previouslyHidden || expired ? 'hide' : 'show'; this.showNotification = previouslyHidden || expired ? 'hide' : 'show';
} }
@ -48,7 +63,7 @@ export class NotificationComponent implements OnInit {
} }
dismiss() { dismiss() {
this.localStorage.setItem(LOCAL_STORAGE_NAMESPACE + this.notificationId, 'hide'); this.storage.setItem(LOCAL_STORAGE_NAMESPACE + this.notificationId, 'hide');
this.showNotification = 'hide'; this.showNotification = 'hide';
this.dismissed.next(); this.dismissed.next();
} }