fix(service-worker): clean up caches from old SW versions (#26319)
Since the SW immediately takes over all clients, it is safe to delete caches used by older (e.g. beta) `@angular/service-worker` versions to avoid running into browser storage quota limitations. PR Close #26319
This commit is contained in:

committed by
Miško Hevery

parent
96f38562bd
commit
00b5c7b49b
@ -210,10 +210,56 @@ const manifestUpdateHash = sha1(JSON.stringify(manifestUpdate));
|
||||
driver = new Driver(scope, scope, new CacheDatabase(scope, scope));
|
||||
});
|
||||
|
||||
async_it('initializes prefetched content correctly, after activation', async() => {
|
||||
expect(await scope.startup(true)).toEqual(true);
|
||||
async_it('activates without waiting', async() => {
|
||||
const skippedWaiting = await scope.startup(true);
|
||||
expect(skippedWaiting).toBe(true);
|
||||
});
|
||||
|
||||
async_it('claims all clients, after activation', async() => {
|
||||
const claimSpy = spyOn(scope.clients, 'claim');
|
||||
|
||||
await scope.startup(true);
|
||||
expect(claimSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
async_it('cleans up old `@angular/service-worker` caches, after activation', async() => {
|
||||
const claimSpy = spyOn(scope.clients, 'claim');
|
||||
const cleanupOldSwCachesSpy = spyOn(driver, 'cleanupOldSwCaches');
|
||||
|
||||
// Automatically advance time to trigger idle tasks as they are added.
|
||||
scope.autoAdvanceTime = true;
|
||||
await scope.startup(true);
|
||||
await scope.resolveSelfMessages();
|
||||
await driver.initialized;
|
||||
scope.autoAdvanceTime = false;
|
||||
|
||||
expect(cleanupOldSwCachesSpy).toHaveBeenCalledTimes(1);
|
||||
expect(claimSpy).toHaveBeenCalledBefore(cleanupOldSwCachesSpy);
|
||||
});
|
||||
|
||||
async_it(
|
||||
'does not blow up if cleaning up old `@angular/service-worker` caches fails', async() => {
|
||||
spyOn(driver, 'cleanupOldSwCaches').and.callFake(() => Promise.reject('Ooops'));
|
||||
|
||||
// Automatically advance time to trigger idle tasks as they are added.
|
||||
scope.autoAdvanceTime = true;
|
||||
await scope.startup(true);
|
||||
await scope.resolveSelfMessages();
|
||||
scope.autoAdvanceTime = false;
|
||||
|
||||
server.clearRequests();
|
||||
|
||||
expect(driver.state).toBe(DriverReadyState.NORMAL);
|
||||
expect(await makeRequest(scope, '/foo.txt')).toBe('this is foo');
|
||||
server.assertNoOtherRequests();
|
||||
});
|
||||
|
||||
async_it('initializes prefetched content correctly, after activation', async() => {
|
||||
// Automatically advance time to trigger idle tasks as they are added.
|
||||
scope.autoAdvanceTime = true;
|
||||
await scope.startup(true);
|
||||
await scope.resolveSelfMessages();
|
||||
scope.autoAdvanceTime = false;
|
||||
|
||||
server.assertSawRequestFor('ngsw.json');
|
||||
server.assertSawRequestFor('/foo.txt');
|
||||
server.assertSawRequestFor('/bar.txt');
|
||||
@ -825,6 +871,41 @@ const manifestUpdateHash = sha1(JSON.stringify(manifestUpdate));
|
||||
});
|
||||
});
|
||||
|
||||
describe('cleanupOldSwCaches()', () => {
|
||||
async_it('should delete the correct caches', async() => {
|
||||
const oldSwCacheNames = ['ngsw:active', 'ngsw:staged', 'ngsw:manifest:a1b2c3:super:duper'];
|
||||
const otherCacheNames = [
|
||||
'ngsuu:active',
|
||||
'not:ngsw:active',
|
||||
'ngsw:staged:not',
|
||||
'NgSw:StAgEd',
|
||||
'ngsw:manifest',
|
||||
];
|
||||
const allCacheNames = oldSwCacheNames.concat(otherCacheNames);
|
||||
|
||||
await Promise.all(allCacheNames.map(name => scope.caches.open(name)));
|
||||
expect(await scope.caches.keys()).toEqual(allCacheNames);
|
||||
|
||||
await driver.cleanupOldSwCaches();
|
||||
expect(await scope.caches.keys()).toEqual(otherCacheNames);
|
||||
});
|
||||
|
||||
async_it('should delete other caches even if deleting one of them fails', async() => {
|
||||
const oldSwCacheNames = ['ngsw:active', 'ngsw:staged', 'ngsw:manifest:a1b2c3:super:duper'];
|
||||
const deleteSpy = spyOn(scope.caches, 'delete')
|
||||
.and.callFake(
|
||||
(cacheName: string) =>
|
||||
Promise.reject(`Failed to delete cache '${cacheName}'.`));
|
||||
|
||||
await Promise.all(oldSwCacheNames.map(name => scope.caches.open(name)));
|
||||
const error = await driver.cleanupOldSwCaches().catch(err => err);
|
||||
|
||||
expect(error).toBe('Failed to delete cache \'ngsw:active\'.');
|
||||
expect(deleteSpy).toHaveBeenCalledTimes(3);
|
||||
oldSwCacheNames.forEach(name => expect(deleteSpy).toHaveBeenCalledWith(name));
|
||||
});
|
||||
});
|
||||
|
||||
describe('bugs', () => {
|
||||
async_it('does not crash with bad index hash', async() => {
|
||||
scope = new SwTestHarnessBuilder().withServerState(brokenServer).build();
|
||||
|
Reference in New Issue
Block a user