-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[presentation-api] Add tests to check a newly created receiving brows…
…ing context (#5046) The new manual test runs a series of checks on the receiving side to make sure the created receiving browsing context complies with restrictions set forth in the Presentation API specification. The test starts from the controlling side and runs as follows: 1. The controlling page adds entries to all "storages" (cookie, IndexedDB, localStorage, sessionStorage, cache) and installs a Service Worker. 2. The user starts the test and selects the receiver screen to be tested 3. Presentation page checks the browsing context it runs in, making sure that all "storages" are empty (and in particular that it cannot see the entries added by the controlling side) 4. Presentation page also checks nested contexts and the impossibility to navigate the context 5. Presentation page then reports the results to the controlling page. 6. Controlling page terminates the presentation and asks user to click on the button one more time, to re-start the same presentation in a different receiving browsing context. 7. The new presentation page runs the same tests as before, making sure that the storages are still empty (and in particular that it cannot see the entries added during the first run) 8. Presentation page then reports the results to the controlling page. 9. That's it!
- Loading branch information
1 parent
96d9921
commit 16bd452
Showing
8 changed files
with
904 additions
and
0 deletions.
There are no files selected for viewing
293 changes: 293 additions & 0 deletions
293
presentation-api/receiving-ua/PresentationReceiver_create-manual.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,293 @@ | ||
<!DOCTYPE html> | ||
<meta charset="utf-8"> | ||
<title>Creating a receiving browsing context</title> | ||
<link rel="author" title="Tomoyuki Shimizu" href="https://github.com/tomoyukilabs"> | ||
<link rel="help" href="https://w3c.github.io/presentation-api/#creating-a-receiving-browsing-context"> | ||
<script src="/resources/testharness.js"></script> | ||
<script src="/resources/testharnessreport.js"></script> | ||
<script src="common.js"></script> | ||
<script src="support/stash.js"></script> | ||
|
||
<p id="notice"> | ||
Click the button below and select the available presentation display, to start the manual test. The test passes if a "PASS" result appears.<br> | ||
This test asks you to click the button twice, unless the test fails.<br> | ||
<button id="presentBtn">Start Presentation Test</button> | ||
</p> | ||
|
||
<script> | ||
setup({explicit_timeout: true}); | ||
|
||
let receiverStack; | ||
add_completion_callback(() => { | ||
// overwrite a stack written in the test result | ||
if (receiverStack) { | ||
document.querySelector('#log pre').textContent = receiverStack; | ||
} | ||
}); | ||
|
||
let connection, db; | ||
const presentBtn = document.getElementById('presentBtn'); | ||
const main = () => { | ||
promise_test(t => { | ||
presentBtn.disabled = true; | ||
const stash = new Stash(stashIds.toController, stashIds.toReceiver); | ||
const request = new PresentationRequest('support/PresentationReceiver_create_receiving-ua.html'); | ||
|
||
t.add_cleanup(() => { | ||
const notice = document.getElementById('notice'); | ||
notice.parentNode.removeChild(notice); | ||
stash.stop(); | ||
|
||
//history.back(); | ||
document.cookie = 'PresentationApiTest=true; Expires=' + new Date().toUTCString(); | ||
sessionStorage.removeItem('presentation_api_test'); | ||
localStorage.removeItem('presentation_api_test'); | ||
|
||
if (db) | ||
db.close(); | ||
indexedDB.deleteDatabase(dbName); | ||
|
||
if (connection) { | ||
if (connection.state === 'connecting') { | ||
connection.onconnect = () => { | ||
connection.terminate(); | ||
} | ||
} | ||
else if (connection.state === 'closed') { | ||
request.reconnect(connection.id).then(c => { | ||
c.terminate(); | ||
}); | ||
} | ||
else | ||
connection.terminate(); | ||
} | ||
|
||
if ('serviceWorker' in navigator) { | ||
navigator.serviceWorker.getRegistrations().then(registrations => { | ||
return Promise.all(registrations.map(reg => reg.unregister())); | ||
}); | ||
} | ||
if ('caches' in window) { | ||
caches.keys().then(keys => { | ||
return Promise.all(keys.map(key => caches.delete(key))); | ||
}); | ||
} | ||
}); | ||
|
||
history.pushState(null, 'test', 'PresentationReceiver_create-manual.html'); | ||
document.cookie = 'PresentationApiTest=Controlling-UA'; | ||
|
||
const storageName = 'presentation_api_test'; | ||
const storageValue = 'receiving-ua'; | ||
sessionStorage.setItem(storageName, storageValue); | ||
localStorage.setItem(storageName, storageValue); | ||
|
||
const dbName = 'db-presentation-api'; | ||
const storeName = 'store-controlling-ua'; | ||
const storeData = { | ||
id: 'controller', | ||
data: 'controlling user agent' | ||
}; | ||
const openIndexedDB = () => { | ||
if ('indexedDB' in window) { | ||
const dbReq = indexedDB.open(dbName, 1); | ||
const eventWatcher = new EventWatcher(t, dbReq, ['upgradeneeded', 'success']); | ||
return eventWatcher.wait_for('upgradeneeded').then(() => { | ||
db = dbReq.result; | ||
const store = db.createObjectStore(storeName, { keyPath: 'id' }); | ||
store.add(storeData); | ||
return eventWatcher.wait_for('success'); | ||
}).then(() => { | ||
db = dbReq.result; | ||
db.close(); | ||
db = null; | ||
}); | ||
} | ||
else | ||
return Promise.resolve(); | ||
}; | ||
|
||
const cacheName = 'controlling-ua'; | ||
let clientUrls; | ||
const getClientUrls = () => { | ||
return new Promise(resolve => { | ||
navigator.serviceWorker.getRegistration().then(reg => { | ||
if (reg) { | ||
const channel = new MessageChannel(); | ||
channel.port1.onmessage = event => { | ||
resolve(event.data); | ||
}; | ||
reg.active.postMessage('', [channel.port2]); | ||
} | ||
}); | ||
}); | ||
}; | ||
const registerServiceWorker = () => { | ||
return ('serviceWorker' in navigator ? | ||
navigator.serviceWorker.register('serviceworker.js').then(registration => { | ||
return new Promise((resolve, reject) => { | ||
if (registration.installing) { | ||
registration.installing.addEventListener('statechange', event => { | ||
if(event.target.state === 'installed') | ||
resolve(); | ||
}); | ||
} | ||
else | ||
resolve(); | ||
}); | ||
}) : Promise.resolve()).then(getClientUrls).then(urls => { | ||
clientUrls = urls; | ||
}); | ||
}; | ||
const openCaches = () => { | ||
return 'caches' in window ? caches.open(cacheName).then(cache => cache.add('cache.txt')) : Promise.resolve(); | ||
}; | ||
|
||
const checkUpdates = () => { | ||
// Cookie | ||
assert_equals(document.cookie, 'PresentationApiTest=Controlling-UA', 'A cookie store is not shared with a receiving user agent.'); | ||
|
||
// Web Storage | ||
assert_equals(sessionStorage.length, 1, 'Session storage is not shared with a receiving user agent.'); | ||
assert_equals(sessionStorage.getItem(storageName), storageValue, 'Session storage is not shared with a receiving user agent.'); | ||
assert_equals(localStorage.length, 1, 'Local storage is not shared with a receiving user agent.'); | ||
assert_equals(localStorage.getItem(storageName), storageValue, 'Local storage is not shared with a receiving user agent.'); | ||
}; | ||
|
||
// Indexed Database | ||
const checkIndexedDB = t => { | ||
if ('indexedDB' in window) { | ||
const message = 'Indexed Database is not shared with a receiving user agent.'; | ||
let req = indexedDB.open(dbName, 1), store; | ||
let eventWatcher = new EventWatcher(t, req, 'success'); | ||
return eventWatcher.wait_for('success').then(() => { | ||
db = req.result; | ||
const transaction = db.transaction(storeName, 'readwrite'); | ||
store = transaction.objectStore(storeName); | ||
req = store.openCursor(); | ||
eventWatcher = new EventWatcher(t, req, 'success'); | ||
return eventWatcher.wait_for('success'); | ||
}).then(() => { | ||
assert_true(req.result instanceof IDBCursorWithValue, message); | ||
const cursor = req.result; | ||
const item = cursor.value; | ||
assert_equals(item.id, storeData.id, message); | ||
assert_equals(item.data, storeData.data, message); | ||
cursor.continue(); | ||
return eventWatcher.wait_for('success'); | ||
}).then(() => { | ||
assert_equals(req.result, null, message); | ||
db.close(); | ||
db = null; | ||
}); | ||
} | ||
else | ||
return Promise.resolve(); | ||
}; | ||
|
||
// Service Workers | ||
const checkServiceWorkers = () => { | ||
return 'serviceWorker' in navigator ? navigator.serviceWorker.getRegistrations().then(registrations => { | ||
const message = 'List of registered service worker registrations is not shared with a receiving user agent.'; | ||
assert_equals(registrations.length, 1, message); | ||
assert_equals(registrations[0].active.scriptURL, new Request('serviceworker.js').url, message); | ||
}) : Promise.resolve(); | ||
}; | ||
const checkCaches = () => { | ||
const message = 'Cache storage is not shared with a receiving user agent.'; | ||
return 'caches' in window ? caches.keys().then(keys => { | ||
assert_equals(keys.length, 1, message); | ||
assert_equals(keys[0], cacheName, message); | ||
return caches.open(keys[0]); | ||
}).then(cache => cache.matchAll()) | ||
.then(responses => { | ||
assert_equals(responses.length, 1, message); | ||
assert_equals(responses[0].url, new Request('cache.txt').url, message); | ||
}) : Promise.resolve(); | ||
}; | ||
|
||
let timeout; | ||
const enableTimeout = () => { | ||
timeout = t.step_timeout(() => { | ||
t.force_timeout(); | ||
t.done(); | ||
}, 5000); | ||
}; | ||
const disableTimeout = () => { | ||
clearTimeout(timeout); | ||
}; | ||
const cancelWait = () => { | ||
connection.removeEventListener('close', onTerminate); | ||
connection.removeEventListener('terminate', onTerminate); | ||
}; | ||
const onTerminate = (reject, event) => { | ||
cancelWait(); | ||
reject('A receiving user agent unexpectedly ' + event.type + 'd a presentation. '); | ||
}; | ||
const waitForTerminate = () => { | ||
return new Promise((resolve, reject) => { | ||
connection.addEventListener('close', onTerminate.bind(this, reject)); | ||
connection.addEventListener('terminate', onTerminate.bind(this, reject)); | ||
}); | ||
}; | ||
|
||
// Start a presentation for receiving user agent tests | ||
return request.start().then(c => { | ||
connection = c; | ||
enableTimeout(); | ||
// This Promise.race will be rejected if a receiving side terminates/closes the connection when window.close() is invoked | ||
return Promise.race([ | ||
openIndexedDB() | ||
.then(registerServiceWorker) | ||
.then(openCaches) | ||
.then(() => { return stash.init(); }) | ||
.then(() => { return stash.receive(); }), | ||
waitForTerminate()]); | ||
}).then(result => { | ||
// terminate and connect again if the result is PASS | ||
cancelWait(); | ||
const json = JSON.parse(result); | ||
if (json.test.status !== 0) | ||
return json; | ||
|
||
// Check accessibility to window clients before terminating a presentation | ||
return getClientUrls().then(urls => { | ||
assert_true(urls.length === clientUrls.length && urls.every((value, index) => { return clientUrls[index] === value}), | ||
'A window client in a receiving user agent is not accessible to a service worker on a controlling user agent.') | ||
const eventWatcher = new EventWatcher(t, connection, 'terminate'); | ||
connection.terminate(); | ||
return eventWatcher.wait_for('terminate'); | ||
}).then(() => { | ||
connection = null; | ||
disableTimeout(); | ||
presentBtn.removeEventListener('click', main); | ||
presentBtn.textContent = 'Continue Presentation Test'; | ||
presentBtn.disabled = false; | ||
const eventWatcher = new EventWatcher(t, presentBtn, 'click'); | ||
return eventWatcher.wait_for('click'); | ||
}).then(() => { | ||
presentBtn.disabled = true; | ||
return request.start(); | ||
}).then(c => { | ||
connection = c; | ||
enableTimeout(); | ||
return stash.receive(); | ||
}).then(result => { | ||
return JSON.parse(result); | ||
}); | ||
}).then(json => { | ||
if (json.test.status === 0) { | ||
checkUpdates(); | ||
return checkIndexedDB(t) | ||
.then(checkServiceWorkers) | ||
.then(checkCaches); | ||
} | ||
else { | ||
receiverStack = json.test.stack; | ||
parseResult(json.test.message); | ||
} | ||
}); | ||
}); | ||
}; | ||
presentBtn.addEventListener('click', main); | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Hello, Presentation API! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
self.addEventListener('install', () => { | ||
// activate this service worker immediately | ||
self.skipWaiting(); | ||
}); | ||
|
||
self.addEventListener('activate', event => { | ||
// let this service worker control window clients immediately | ||
event.waitUntil(self.clients.claim()); | ||
}); | ||
|
||
self.addEventListener('message', event => { | ||
event.waitUntil(clients.matchAll().then(windows => { | ||
event.ports[0].postMessage(windows.map(w => { return w.url; }).sort()); | ||
})); | ||
}); |
Oops, something went wrong.