packages/deprecated/appcache/appcache_tests-client.js
const manifestUrl = '/app.manifest';
function appcacheTest(name, cb) {
Tinytest.addAsync(`appcache - ${name}`, test => {
return fetch(manifestUrl).then(
res => cb(test, res),
err => test.fail(err)
);
});
}
// Verify that the code status of the HTTP response is "OK"
appcacheTest('presence', (test, manifest) =>
test.equal(manifest.status, 200, 'manifest not served'));
// Verify the content-type HTTP header
appcacheTest('content type', (test, manifest) =>
test.equal(manifest.headers.get('content-type'), 'text/cache-manifest'));
// Verify that each section header is only set once.
appcacheTest('sections uniqueness', async (test, manifest) => {
const content = await manifest.text();
const mandatorySectionHeaders = ['CACHE:', 'NETWORK:', 'FALLBACK:'];
const optionalSectionHeaders = ['SETTINGS'];
const allSectionHeaders = [
...mandatorySectionHeaders,
...optionalSectionHeaders.filter(
header => !mandatorySectionHeaders.includes(header)
),
];
allSectionHeaders.forEach(sectionHeader => {
const globalSearch = new RegExp(sectionHeader, "g");
const matches = content.match(globalSearch) || [];
test.isTrue(matches.length <= 1, `${sectionHeader} is set twice`);
if (mandatorySectionHeaders.includes(sectionHeader)) {
test.isTrue(matches.length == 1, `${sectionHeader} is not set`);
}
});
});
// Verify the content of the header and of each section of the manifest using
// regular expressions. Regular expressions matches malformed URIs but that's
// not what we're trying to catch here (the user is free to add its own content
// in the manifest -- even malformed).
appcacheTest('sections validity', async (test, manifest) => {
const lines = (await manifest.text()).split('\n');
let i = 0;
let currentRegex = null;
let line = null;
const nextLine = () => lines[i++];
const eof = () => i >= lines.length;
const nextLineMatches = (expected, n) => {
n = n || 1;
for(let j = 0; j < n; j++) {
const testFunc = toString.call(expected) === '[object RegExp]' ?
'matches' :
'equal';
test[testFunc](nextLine(), expected);
}
};
// Verify header validity
nextLineMatches('CACHE MANIFEST');
nextLineMatches('');
nextLineMatches(/^# [a-z0-9]+$/i);
nextLineMatches('');
// Verify body validity
while (! eof()) {
line = nextLine();
// There are three distinct sections: 'CACHE', 'FALLBACK', and 'NETWORK'.
// A section start with its name suffixed by a colon. When we read a new
// section header, we update the currentRegex expression for the next lines
// of the section.
// XXX There is also a 'SETTINGS' section, not used by this package. If this
// section is used, the test will fail.
if (line === 'CACHE:' || line === 'NETWORK:')
currentRegex = /^\S+$/;
else if (line === 'FALLBACK:')
currentRegex = /^\S+ \S+$/;
// Blank lines and lines starting with a `#` (comments) are valid
else if (line == '' || line.match(/^#.+/))
continue;
// Outside sections, only blanks lines and comments are valid
else if (currentRegex === null)
test.fail(`Invalid line ${i}: ${line}`);
// Inside a section, a star is a valid expression
else if (line === '*')
continue;
// If it is not a blank line, not a comment, and not a header it must
// match the current section format
else
test.matches(line, currentRegex, `line ${i}`);
}
});
// Verify that resources declared on the server with the `onlineOnly` parameter
// are present in the network section of the manifest. The `appcache` package
// also automatically add the manifest (`app.manifest`) add the star symbol to
// this list and therefore we also check the presence of these two elements.
appcacheTest('network section content', async (test, manifest) => {
const shouldBePresentInNetworkSection = [
"/app.manifest",
"/online/",
"/bigimage.jpg",
"/largedata.json",
"*"
];
const lines = (await manifest.text()).split('\n');
const startNetworkSection = lines.indexOf('NETWORK:');
// We search the end of the 'NETWORK:' section by looking at the beginning
// of any potential other section. By default we set this value to
// `lines.length - 1` which is the index of the last line.
const otherSections = ['CACHE:', 'FALLBACK:', 'SETTINGS'];
const endNetworkSection = otherSections.reduce((min, sectionName) => {
const position = lines.indexOf(sectionName);
return position > startNetworkSection && position < min ? position : min;
}, lines.length - 1);
// We remove the first line because it's the 'NETWORK:' header line.
const networkLines = lines.slice(startNetworkSection + 1, endNetworkSection);
shouldBePresentInNetworkSection.forEach(
item => test.include(networkLines, item)
);
});