blob: 147ea612c2f99b41375b643160da49598cc6603c [file] [log] [blame]
// Adapter for testharness.js-style tests with Service Workers
function service_worker_unregister_and_register(test, url, scope) {
if (!scope || scope.length == 0)
return Promise.reject(new Error('tests must define a scope'));
var options = { scope: scope };
return service_worker_unregister(test, scope)
.then(function() {
return navigator.serviceWorker.register(url, options);
})
.catch(unreached_rejection(test,
'unregister and register should not fail'));
}
function service_worker_unregister(test, documentUrl) {
return navigator.serviceWorker.getRegistration(documentUrl)
.then(function(registration) {
if (registration)
return registration.unregister();
})
.catch(unreached_rejection(test, 'unregister should not fail'));
}
function service_worker_unregister_and_done(test, scope) {
return service_worker_unregister(test, scope)
.then(test.done.bind(test));
}
function unreached_fulfillment(test, prefix) {
return test.step_func(function(result) {
var error_prefix = prefix || 'unexpected fulfillment';
assert_unreached(error_prefix + ': ' + result);
});
}
// Rejection-specific helper that provides more details
function unreached_rejection(test, prefix) {
return test.step_func(function(error) {
var reason = error.message || error.name || error;
var error_prefix = prefix || 'unexpected rejection';
assert_unreached(error_prefix + ': ' + reason);
});
}
// Adds an iframe to the document and returns a promise that resolves to the
// iframe when it finishes loading. The caller is responsible for removing the
// iframe later if needed.
function with_iframe(url) {
return new Promise(function(resolve) {
var frame = document.createElement('iframe');
frame.src = url;
frame.onload = function() { resolve(frame); };
document.body.appendChild(frame);
});
}
function normalizeURL(url) {
return new URL(url, self.location).toString().replace(/#.*$/, '');
}
function wait_for_update(test, registration) {
if (!registration || registration.unregister == undefined) {
return Promise.reject(new Error(
'wait_for_update must be passed a ServiceWorkerRegistration'));
}
return new Promise(test.step_func(function(resolve) {
registration.addEventListener('updatefound', test.step_func(function() {
resolve(registration.installing);
}));
}));
}
function wait_for_state(test, worker, state) {
if (!worker || worker.state == undefined) {
return Promise.reject(new Error(
'wait_for_state must be passed a ServiceWorker'));
}
if (worker.state === state)
return Promise.resolve(state);
if (state === 'installing') {
switch (worker.state) {
case 'installed':
case 'activating':
case 'activated':
case 'redundant':
return Promise.reject(new Error(
'worker is ' + worker.state + ' but waiting for ' + state));
}
}
if (state === 'installed') {
switch (worker.state) {
case 'activating':
case 'activated':
case 'redundant':
return Promise.reject(new Error(
'worker is ' + worker.state + ' but waiting for ' + state));
}
}
if (state === 'activating') {
switch (worker.state) {
case 'activated':
case 'redundant':
return Promise.reject(new Error(
'worker is ' + worker.state + ' but waiting for ' + state));
}
}
if (state === 'activated') {
switch (worker.state) {
case 'redundant':
return Promise.reject(new Error(
'worker is ' + worker.state + ' but waiting for ' + state));
}
}
return new Promise(test.step_func(function(resolve) {
worker.addEventListener('statechange', test.step_func(function() {
if (worker.state === state)
resolve(state);
}));
}));
}
// Declare a test that runs entirely in the ServiceWorkerGlobalScope. The |url|
// is the service worker script URL. This function:
// - Instantiates a new test with the description specified in |description|.
// The test will succeed if the specified service worker can be successfully
// registered and installed.
// - Creates a new ServiceWorker registration with a scope unique to the current
// document URL. Note that this doesn't allow more than one
// service_worker_test() to be run from the same document.
// - Waits for the new worker to begin installing.
// - Imports tests results from tests running inside the ServiceWorker.
function service_worker_test(url, description) {
// If the document URL is https://example.com/document and the script URL is
// https://example.com/script/worker.js, then the scope would be
// https://example.com/script/scope/document.
var scope = new URL('scope' + window.location.pathname,
new URL(url, window.location)).toString();
promise_test(function(test) {
return service_worker_unregister_and_register(test, url, scope)
.then(function(registration) {
add_completion_callback(function() {
registration.unregister();
});
return wait_for_update(test, registration)
.then(function(worker) {
return fetch_tests_from_worker(worker);
});
});
}, description);
}
function get_host_info() {
var ORIGINAL_HOST = '127.0.0.1';
var REMOTE_HOST = 'localhost';
var UNAUTHENTICATED_HOST = 'example.test';
var HTTP_PORT = 8000;
var HTTPS_PORT = 8443;
try {
// In W3C test, we can get the hostname and port number in config.json
// using wptserve's built-in pipe.
// http://wptserve.readthedocs.org/en/latest/pipes.html#built-in-pipes
HTTP_PORT = eval('{{ports[http][0]}}');
HTTPS_PORT = eval('{{ports[https][0]}}');
ORIGINAL_HOST = eval('\'{{host}}\'');
REMOTE_HOST = 'www1.' + ORIGINAL_HOST;
} catch (e) {
}
return {
HTTP_ORIGIN: 'http://' + ORIGINAL_HOST + ':' + HTTP_PORT,
HTTPS_ORIGIN: 'https://' + ORIGINAL_HOST + ':' + HTTPS_PORT,
HTTP_REMOTE_ORIGIN: 'http://' + REMOTE_HOST + ':' + HTTP_PORT,
HTTPS_REMOTE_ORIGIN: 'https://' + REMOTE_HOST + ':' + HTTPS_PORT,
UNAUTHENTICATED_ORIGIN: 'http://' + UNAUTHENTICATED_HOST + ':' + HTTP_PORT
};
}
function base_path() {
return location.pathname.replace(/\/[^\/]*$/, '/');
}
function test_login(test, origin, username, password, cookie) {
return new Promise(function(resolve, reject) {
with_iframe(
origin +
'/serviceworker/resources/fetch-access-control-login.html')
.then(test.step_func(function(frame) {
var channel = new MessageChannel();
channel.port1.onmessage = test.step_func(function() {
frame.remove();
resolve();
});
frame.contentWindow.postMessage(
{username: username, password: password, cookie: cookie},
origin, [channel.port2]);
}));
});
}
function login(test) {
return test_login(test, 'http://127.0.0.1:8000',
'username1', 'password1', 'cookie1')
.then(function() {
return test_login(test, 'http://localhost:8000',
'username2', 'password2', 'cookie2');
});
}
function login_https(test) {
return test_login(test, 'https://127.0.0.1:8443',
'username1s', 'password1s', 'cookie1')
.then(function() {
return test_login(test, 'https://localhost:8443',
'username2s', 'password2s', 'cookie2');
});
}