Skip to content
This repository has been archived by the owner on Aug 31, 2021. It is now read-only.

Refactors the tests of cache expiration options. #187

Merged
merged 3 commits into from
Sep 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
264 changes: 86 additions & 178 deletions test/browser-tests/options/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,209 +20,117 @@

describe('Test Options Parameters', function() {
const swUtils = window.goog.swUtils;
const serviceWorkersFolder = '/test/browser-tests/options/serviceworkers';

const pausePromise = timeout => {
return new Promise(function(resolve) {
setTimeout(resolve, timeout);
});
const serviceWorkersFolder = '/test/browser-tests/options/serviceworkers/';

/**
* @param {Number} timeout The number of milliseconds to pause for.
* @return {Promise} A promise that resolves after a specified delay.
*/
const pause = timeout => {
return new Promise(resolve => setTimeout(resolve, timeout));
};

const cleanUpIDB = () => {
return new Promise(resolve => {
const req = indexedDB.deleteDatabase('sw-toolbox-options-test');
req.onsuccess = () => resolve();
req.onerror = () => resolve();
req.onblocked = () => resolve();
});
/**
* Performs a series of fetch() calls on an iframe, then pauses.
* @param {iframe} iframe The iframe whose contentWindow will be used to fetch().
* @param {Array.<String>} urls The URLs to fetch.
* @return {Promise} A promise that resolves following the fetches and a delay.
*/
const sequentialFetch = (iframe, urls) => {
return urls.reduce((chain, url) => {
return chain.then(() => iframe.contentWindow.fetch(url));
}, Promise.resolve()).then(() => pause(500));
};

beforeEach(function() {
// Clear IndexDB - Used for max age / max Entries
return cleanUpIDB();
/**
* Prepends a common prefix to several partial URLs, and returns the absolute URLs.
* @param {Array.<String>} urls The partial URLs.
* @return {Array.<String>} The absolute URLs.
*/
const absoluteTestDataFileUrls = urls => urls.map(url => {
return String(new URL(url, `${location.origin}/test/data/files/`));
});

after(function() {
// Clear IndexDB - Used for max age / max Entries
return cleanUpIDB();
});
/**
* Asserts that the keys in cachedAssets match exactly the list of expected URLs.
* @param {Object} cachedAssets The result from a call to swUtils.getAllCachedAssets().
* @param {Array.<String>} expectedUrls The expected cache contents.
*/
const assertCacheContents = (cachedAssets, expectedUrls) => {
const expectedLength = expectedUrls.length;
const cachedUrls = Object.keys(cachedAssets);
const filteredUrls = cachedUrls.filter(url => expectedUrls.includes(url));
Copy link

@gauntface gauntface Sep 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure of when filteredUrls != cachedUrls and that would be OK.

Would it make sense to invert the includes check and ensure filteredUrls.length === 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of the tests in this file assume that the cached contents need to be exactly equal to the expected contents. I.e., if there's something in cached contents that's not in expected, or vice versa, then it's a failure. This logic seemed like the easiest way to check that bijection.

It's not really a general-purpose check that's applicable to other tests, since other tests might be implemented in such a way that extra entries in the cached contents that aren't expected wouldn't be considered a failure.

filteredUrls.should.have.lengthOf(expectedLength);
cachedUrls.should.have.lengthOf(expectedLength);
};

describe('options.cache.maxEntries', function() {
it('should cache according to global maxEntries option', function() {
const urls = [
'/test/data/files/text-1.txt',
'/test/data/files/text-2.txt',
'/test/data/files/text-3.txt'
];

return swUtils.activateSW(serviceWorkersFolder + '/max-entries-global.js')
.then(iframe => {
return urls.reduce((promiseChain, url) => {
return promiseChain
.then(() => {
// Pause is to ensure the cache has had time to finish.
return iframe.contentWindow.fetch(url)
.then(pausePromise.bind(null, 500));
});
}, Promise.resolve());
})
.then(() => {
return swUtils.getAllCachedAssets('options-test');
})
.then(cachedAssets => {
Object.keys(cachedAssets).length.should.equal(2);
Object.keys(cachedAssets).indexOf(location.origin + '/test/data/files/text-2.txt').should.not.equal(-1);
Object.keys(cachedAssets).indexOf(location.origin + '/test/data/files/text-3.txt').should.not.equal(-1);
});
const urls = absoluteTestDataFileUrls([
'text-1.txt', 'text-2.txt', 'text-3.txt']);

const swFile = `${serviceWorkersFolder}max-entries-global.js`;
return swUtils.activateSW(swFile).then(iframe => {
return sequentialFetch(iframe, urls)
.then(() => swUtils.getAllCachedAssets(iframe.src));
}).then(cachedAssets => assertCacheContents(cachedAssets, urls.slice(1)));
});

it('should cache according to route specific maxEntries option', function() {
const urls = [
'/test/data/files/text-1.txt',
'/test/data/files/text-2.txt',
'/test/data/files/text-3.txt'
];

return swUtils.activateSW(serviceWorkersFolder + '/max-entries-route.js')
.then(iframe => {
return urls.reduce((promiseChain, url) => {
return promiseChain
.then(() => {
// Pause is to ensure the cache has had time to finish.
return iframe.contentWindow.fetch(url)
.then(pausePromise.bind(null, 500));
});
}, Promise.resolve());
})
.then(() => {
return swUtils.getAllCachedAssets('options-test');
})
.then(cachedAssets => {
Object.keys(cachedAssets).length.should.equal(2);
Object.keys(cachedAssets).indexOf(location.origin + '/test/data/files/text-2.txt').should.not.equal(-1);
Object.keys(cachedAssets).indexOf(location.origin + '/test/data/files/text-3.txt').should.not.equal(-1);
});
const urls = absoluteTestDataFileUrls([
'text-1.txt', 'text-2.txt', 'text-3.txt']);

const swFile = `${serviceWorkersFolder}max-entries-route.js`;
return swUtils.activateSW(swFile).then(iframe => {
return sequentialFetch(iframe, urls)
.then(() => swUtils.getAllCachedAssets(iframe.src));
}).then(cachedAssets => assertCacheContents(cachedAssets, urls.slice(1)));
});
});

describe('options.cache.maxAgeSeconds', function() {
it('should cache according to global maxAgeSeconds option', function() {
const urls = [
'/test/data/files/text-1.txt',
'/test/data/files/text-2.txt',
'/test/data/files/text-3.txt'
];

return swUtils.activateSW(serviceWorkersFolder + '/max-cache-age-global.js')
.then(iframe => {
return urls.reduce((promiseChain, url, index) => {
return promiseChain
.then(() => {
return iframe.contentWindow.fetch(url)
.then(() => {
if (index === 0) {
return pausePromise(1500);
}
});
});
}, Promise.resolve());
})
.then(() => {
// Give cache time to settle
return pausePromise(500);
})
.then(() => {
return swUtils.getAllCachedAssets('options-test');
})
.then(cachedAssets => {
Object.keys(cachedAssets).length.should.equal(2);
Object.keys(cachedAssets).indexOf(location.origin + '/test/data/files/text-2.txt').should.not.equal(-1);
Object.keys(cachedAssets).indexOf(location.origin + '/test/data/files/text-3.txt').should.not.equal(-1);
});
const urls = absoluteTestDataFileUrls([
'text-1.txt', 'text-2.txt', 'text-3.txt']);

const swFile = `${serviceWorkersFolder}max-cache-age-global.js`;
return swUtils.activateSW(swFile).then(iframe => {
return iframe.contentWindow.fetch(urls[0])
.then(() => pause(1500))
.then(() => sequentialFetch(iframe, urls.slice(1)))
.then(() => swUtils.getAllCachedAssets(iframe.src));
}).then(cachedAssets => assertCacheContents(cachedAssets, urls.slice(1)));
});

it('should cache according to route specific maxAgeSeconds option', function() {
const urls = [
'/test/data/files/text-1.txt',
'/test/data/files/text-2.txt',
'/test/data/files/text-3.txt'
];

return swUtils.activateSW(serviceWorkersFolder + '/max-cache-age-route.js')
.then(iframe => {
return urls.reduce((promiseChain, url, index) => {
return promiseChain
.then(() => {
// Pause is to ensure the cache has had time to finish.
return iframe.contentWindow.fetch(url)
.then(() => {
if (index === 0) {
return pausePromise(1500);
}
});
});
}, Promise.resolve());
})
.then(() => {
// Give cache time to settle
return pausePromise(500);
})
.then(() => {
return swUtils.getAllCachedAssets('options-test');
})
.then(cachedAssets => {
Object.keys(cachedAssets).length.should.equal(2);
Object.keys(cachedAssets).indexOf(location.origin + '/test/data/files/text-2.txt').should.not.equal(-1);
Object.keys(cachedAssets).indexOf(location.origin + '/test/data/files/text-3.txt').should.not.equal(-1);
});
const urls = absoluteTestDataFileUrls([
'text-1.txt', 'text-2.txt', 'text-3.txt']);

const swFile = `${serviceWorkersFolder}max-cache-age-route.js`;
return swUtils.activateSW(swFile).then(iframe => {
return iframe.contentWindow.fetch(urls[0])
.then(() => pause(1500))
.then(() => sequentialFetch(iframe, urls.slice(1)))
.then(() => swUtils.getAllCachedAssets(iframe.src));
}).then(cachedAssets => assertCacheContents(cachedAssets, urls.slice(1)));
});
});

describe('options.cache.maxEntries && options.cache.maxAgeSeconds', function() {
it('should cache according to global maxEntries & maxAgeSeconds option', function() {
this.timeout(8000);
const urls = [
'/test/data/files/text-1.txt',
'/test/data/files/text-2.txt',
'/test/data/files/text-3.txt'
];

let iframe;
return swUtils.activateSW(serviceWorkersFolder + '/max-cache-age-global.js')
.then(newIframe => {
iframe = newIframe;
return urls.reduce((promiseChain, url, index) => {
return promiseChain
.then(() => {
return iframe.contentWindow.fetch(url)
.then(() => {
if (index === 0) {
return pausePromise(1500);
}
});
});
}, Promise.resolve());
})
.then(() => pausePromise(500))
.then(() => {
return swUtils.getAllCachedAssets('options-test');
})
.then(cachedAssets => {
Object.keys(cachedAssets).length.should.equal(2);
Object.keys(cachedAssets).indexOf(location.origin + '/test/data/files/text-2.txt').should.not.equal(-1);
Object.keys(cachedAssets).indexOf(location.origin + '/test/data/files/text-3.txt').should.not.equal(-1);
})
.then(() => pausePromise(1500))
.then(() => {
return iframe.contentWindow.fetch('/test/data/files/text-4.txt');
})
.then(() => pausePromise(500))
.then(() => {
return swUtils.getAllCachedAssets('options-test');
})
.then(cachedAssets => {
Object.keys(cachedAssets).length.should.equal(1);
Object.keys(cachedAssets).indexOf(location.origin + '/test/data/files/text-4.txt').should.not.equal(-1);
const urls = absoluteTestDataFileUrls([
'text-1.txt', 'text-2.txt', 'text-3.txt', 'text-4.txt']);

const swFile = `${serviceWorkersFolder}max-entries-cache-age-global.js`;
return swUtils.activateSW(swFile).then(iframe => {
return iframe.contentWindow.fetch(urls[0])
.then(() => pause(1500))
.then(() => sequentialFetch(iframe, urls.slice(1, 3)))
.then(() => swUtils.getAllCachedAssets(iframe.src))
.then(cachedAssets => assertCacheContents(cachedAssets, urls.slice(1, 3)))
.then(() => sequentialFetch(iframe, urls.slice(2, 4)))
.then(() => swUtils.getAllCachedAssets(iframe.src))
.then(cachedAssets => assertCacheContents(cachedAssets, urls.slice(2, 4)));
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ importScripts('/build/sw-toolbox.js');
importScripts('/test/data/skip-and-claim.js');

self.toolbox.options.cache = {
name: 'options-test',
name: self.registration.scope,
maxAgeSeconds: 1
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ self.toolbox.options.debug = true;

self.toolbox.router.get('/test/data/files/:foo', self.toolbox.networkFirst, {
cache: {
name: 'options-test',
name: self.registration.scope,
maxAgeSeconds: 1
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ importScripts('/build/sw-toolbox.js');
importScripts('/test/data/skip-and-claim.js');

self.toolbox.options.cache = {
name: 'options-test',
name: self.registration.scope,
maxEntries: 2,
maxAgeSeconds: 1
};
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ importScripts('/build/sw-toolbox.js');
importScripts('/test/data/skip-and-claim.js');

self.toolbox.options.cache = {
name: 'options-test',
name: self.registration.scope,
maxEntries: 2
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ importScripts('/test/data/skip-and-claim.js');

self.toolbox.router.get('/test/data/files/:foo', self.toolbox.networkFirst, {
cache: {
name: 'options-test',
name: self.registration.scope,
maxEntries: 2
}
});