feat(aio): make the ServiceWorker update process more intuitive/user-friendly (#16170)

Fixes #16093
This commit is contained in:
Georgios Kalpakas 2017-04-19 19:29:36 +03:00 committed by Miško Hevery
parent e07b7ea114
commit 24c34385ee
2 changed files with 20 additions and 41 deletions

View File

@ -11,6 +11,8 @@ import { SwUpdatesService } from './sw-updates.service';
describe('SwUpdateNotificationsService', () => { describe('SwUpdateNotificationsService', () => {
const UPDATE_AVAILABLE_MESSAGE = 'New update for angular.io is available.';
const UPDATE_FAILED_MESSAGE = 'Update activation failed :(';
let injector: ReflectiveInjector; let injector: ReflectiveInjector;
let service: SwUpdateNotificationsService; let service: SwUpdateNotificationsService;
let swUpdates: MockSwUpdatesService; let swUpdates: MockSwUpdatesService;
@ -65,8 +67,8 @@ describe('SwUpdateNotificationsService', () => {
swUpdates.$$isUpdateAvailableSubj.next(true); swUpdates.$$isUpdateAvailableSubj.next(true);
expect(snackBar.$$lastRef.$$message).toContain('ServiceWorker update available'); expect(snackBar.$$lastRef.$$message).toBe(UPDATE_AVAILABLE_MESSAGE);
expect(snackBar.$$lastRef.$$action).toBe('Activate'); expect(snackBar.$$lastRef.$$action).toBe('Update now');
expect(snackBar.$$lastRef.$$config.duration).toBeUndefined(); expect(snackBar.$$lastRef.$$config.duration).toBeUndefined();
}); });
@ -75,7 +77,7 @@ describe('SwUpdateNotificationsService', () => {
expect(snackBar.$$lastRef).toBeUndefined(); expect(snackBar.$$lastRef).toBeUndefined();
}); });
it('should activate the update when clicking on `Activate`', () => { it('should activate the update when clicking on `Update now`', () => {
spyOn(swUpdates, 'activateUpdate').and.callThrough(); spyOn(swUpdates, 'activateUpdate').and.callThrough();
swUpdates.$$isUpdateAvailableSubj.next(true); swUpdates.$$isUpdateAvailableSubj.next(true);
@ -85,30 +87,19 @@ describe('SwUpdateNotificationsService', () => {
expect(swUpdates.activateUpdate).toHaveBeenCalled(); expect(swUpdates.activateUpdate).toHaveBeenCalled();
}); });
it('should report a successful activation', fakeAsync(() => { it('should reload the page after a successful activation', fakeAsync(() => {
const global = injector.get(Global);
expect(global.location.reload).not.toHaveBeenCalled();
activateUpdate(true); activateUpdate(true);
expect(global.location.reload).toHaveBeenCalled();
expect(snackBar.$$lastRef.$$message).toContain('Update activated successfully');
expect(snackBar.$$lastRef.$$action).toBe('Reload');
expect(snackBar.$$lastRef.$$config.duration).toBeUndefined();
})); }));
it('should reload the page when clicking on `Reload` (after a successful activation)',
fakeAsync(() => {
const global = injector.get(Global);
activateUpdate(true);
expect(global.location.reload).not.toHaveBeenCalled();
snackBar.$$lastRef.$$onActionSubj.next();
expect(global.location.reload).toHaveBeenCalled();
})
);
it('should report a failed activation', fakeAsync(() => { it('should report a failed activation', fakeAsync(() => {
activateUpdate(false); activateUpdate(false);
expect(snackBar.$$lastRef.$$message).toContain('Update activation failed'); expect(snackBar.$$lastRef.$$message).toBe(UPDATE_FAILED_MESSAGE);
expect(snackBar.$$lastRef.$$action).toBe('Dismiss'); expect(snackBar.$$lastRef.$$action).toBe('Dismiss');
expect(snackBar.$$lastRef.$$config.duration).toBeGreaterThan(0); expect(snackBar.$$lastRef.$$config.duration).toBeGreaterThan(0);
})); }));
@ -128,22 +119,13 @@ describe('SwUpdateNotificationsService', () => {
it('should dismiss open update notification', () => { it('should dismiss open update notification', () => {
swUpdates.$$isUpdateAvailableSubj.next(true); swUpdates.$$isUpdateAvailableSubj.next(true);
expect(snackBar.$$lastRef.$$message).toContain('ServiceWorker update available'); expect(snackBar.$$lastRef.$$message).toBe(UPDATE_AVAILABLE_MESSAGE);
expect(snackBar.$$lastRef.$$dismissed).toBe(false); expect(snackBar.$$lastRef.$$dismissed).toBe(false);
service.disable(); service.disable();
expect(snackBar.$$lastRef.$$dismissed).toBe(true); expect(snackBar.$$lastRef.$$dismissed).toBe(true);
}); });
it('should dismiss open activation notification', fakeAsync(() => {
activateUpdate(true);
expect(snackBar.$$lastRef.$$message).toContain('Update activated successfully');
expect(snackBar.$$lastRef.$$dismissed).toBe(false);
service.disable();
expect(snackBar.$$lastRef.$$dismissed).toBe(true);
}));
it('should ignore further updates', () => { it('should ignore further updates', () => {
service.disable(); service.disable();
swUpdates.$$isUpdateAvailableSubj.next(true); swUpdates.$$isUpdateAvailableSubj.next(true);
@ -157,7 +139,7 @@ describe('SwUpdateNotificationsService', () => {
expect(snackBar.$$lastRef).toBeUndefined(); expect(snackBar.$$lastRef).toBeUndefined();
swUpdates.$$isUpdateAvailableSubj.next(true); swUpdates.$$isUpdateAvailableSubj.next(true);
expect(snackBar.$$lastRef.$$message).toContain('ServiceWorker update available'); expect(snackBar.$$lastRef.$$message).toBe(UPDATE_AVAILABLE_MESSAGE);
}); });
it('should not ignore pending updates if re-enabled', () => { it('should not ignore pending updates if re-enabled', () => {
@ -166,7 +148,7 @@ describe('SwUpdateNotificationsService', () => {
expect(snackBar.$$lastRef).toBeUndefined(); expect(snackBar.$$lastRef).toBeUndefined();
service.enable(); service.enable();
expect(snackBar.$$lastRef.$$message).toContain('ServiceWorker update available'); expect(snackBar.$$lastRef.$$message).toBe(UPDATE_AVAILABLE_MESSAGE);
}); });
}); });
}); });
@ -174,7 +156,7 @@ describe('SwUpdateNotificationsService', () => {
// Mocks // Mocks
class MockGlobal { class MockGlobal {
location = { location = {
reload: jasmine.createSpy('MockGlobal.reload') reload: jasmine.createSpy('MockGlobal.location.reload')
}; };
} }

View File

@ -12,9 +12,8 @@ import { SwUpdatesService } from './sw-updates.service';
* *
* @description * @description
* Once enabled: * Once enabled:
* 1. Subscribes to ServiceWorker updates and prompts the user to activate. * 1. Subscribes to ServiceWorker updates and prompts the user to update.
* 2. When the user confirms, it activates the update and notifies the user (upon activation success * 2. When the user confirms, it activates the update and reloads the page upon activation success.
* or failure).
* 3. Continues to listen for available ServiceWorker updates. * 3. Continues to listen for available ServiceWorker updates.
* *
* @method * @method
@ -64,7 +63,7 @@ export class SwUpdateNotificationsService {
} }
private notifyForUpdate() { private notifyForUpdate() {
this.openSnackBar('ServiceWorker update available.', 'Activate') this.openSnackBar('New update for angular.io is available.', 'Update now')
.onAction().subscribe(() => this.activateUpdate()); .onAction().subscribe(() => this.activateUpdate());
} }
@ -74,9 +73,7 @@ export class SwUpdateNotificationsService {
} }
private onActivateSuccess() { private onActivateSuccess() {
const message = 'Update activated successfully! Reload the page to see the latest content.'; this.reloadPage();
this.openSnackBar(message, 'Reload')
.onAction().subscribe(() => this.reloadPage());
} }
private openSnackBar(message: string, action?: string, config?: MdSnackBarConfig): MdSnackBarRef<any> { private openSnackBar(message: string, action?: string, config?: MdSnackBarConfig): MdSnackBarRef<any> {