fetch.https.window.js (10725B)
1 // META: script=/common/get-host-info.sub.js 2 // META: script=/service-workers/service-worker/resources/test-helpers.sub.js 3 // META: script=resources/utils.js 4 5 'use strict'; 6 7 // Covers basic functionality provided by BackgroundFetchManager.fetch(). 8 // https://wicg.github.io/background-fetch/#background-fetch-manager-fetch 9 10 const wait = milliseconds => 11 new Promise(resolve => step_timeout(resolve, milliseconds)); 12 13 promise_test(async test => { 14 // 6.3.1.9.2: If |registration|’s active worker is null, then reject promise 15 // with a TypeError and abort these steps. 16 const script = 'service_workers/sw.js'; 17 const scope = 'service_workers/' + location.pathname; 18 19 const serviceWorkerRegistration = 20 await service_worker_unregister_and_register(test, script, scope); 21 22 assert_equals( 23 serviceWorkerRegistration.active, null, 24 'There must not be an activated worker'); 25 26 await promise_rejects_js( 27 test, TypeError, 28 serviceWorkerRegistration.backgroundFetch.fetch( 29 uniqueId(), ['resources/feature-name.txt']), 30 'fetch() must reject on pending and installing workers'); 31 32 }, 'Background Fetch requires an activated Service Worker'); 33 34 backgroundFetchTest(async (test, backgroundFetch) => { 35 // 6.3.1.6: If |requests| is empty, then return a promise rejected with a 36 // TypeError. 37 await promise_rejects_js( 38 test, TypeError, backgroundFetch.fetch(uniqueId(), []), 39 'Empty sequences are treated as NULL'); 40 41 // 6.3.1.7.1: Let |internalRequest| be the request of the result of invoking 42 // the Request constructor with |request|. If this throws an 43 // exception, return a promise rejected with the exception. 44 await promise_rejects_js( 45 test, TypeError, 46 backgroundFetch.fetch(uniqueId(), 'https://user:pass@domain/secret.txt'), 47 'Exceptions thrown in the Request constructor are rethrown'); 48 49 // 6.3.1.7.2: If |internalRequest|’s mode is "no-cors", then return a 50 // promise rejected with a TypeError. 51 { 52 const request = 53 new Request('resources/feature-name.txt', {mode: 'no-cors'}); 54 55 await promise_rejects_js( 56 test, TypeError, backgroundFetch.fetch(uniqueId(), request), 57 'Requests must not be in no-cors mode'); 58 } 59 60 }, 'Argument verification is done for BackgroundFetchManager.fetch()'); 61 62 backgroundFetchTest(async (test, backgroundFetch) => { 63 // 6.3.1.9.2: If |bgFetchMap[id]| exists, reject |promise| with a TypeError 64 // and abort these steps. 65 return promise_rejects_js(test, TypeError, Promise.all([ 66 backgroundFetch.fetch('my-id', 'resources/feature-name.txt?1'), 67 backgroundFetch.fetch('my-id', 'resources/feature-name.txt?2') 68 ])); 69 70 }, 'IDs must be unique among active Background Fetch registrations'); 71 72 backgroundFetchTest(async (test, backgroundFetch) => { 73 const registrationId = uniqueId(); 74 const registration = 75 await backgroundFetch.fetch(registrationId, ''); 76 77 assert_equals(registration.id, registrationId); 78 79 const {type, eventRegistration, results} = await getMessageFromServiceWorker(); 80 81 assert_equals('backgroundfetchsuccess', type); 82 assert_equals(eventRegistration.result, 'success'); 83 assert_equals(eventRegistration.failureReason, ''); 84 85 }, 'Empty URL is OK.'); 86 87 backgroundFetchTest(async (test, backgroundFetch) => { 88 const registrationId = uniqueId(); 89 const registration = 90 await backgroundFetch.fetch(registrationId, 91 new Request('https://example/com', { 92 method: 'PUT', 93 })); 94 95 assert_equals(registration.id, registrationId); 96 97 const {type, eventRegistration, results} = await getMessageFromServiceWorker(); 98 99 assert_equals(type, 'backgroundfetchsuccess'); 100 assert_equals(eventRegistration.result, 'success'); 101 assert_equals(eventRegistration.failureReason, ''); 102 }, 'Requests with PUT method require CORS Preflight and succeed.'); 103 104 backgroundFetchTest(async (test, backgroundFetch) => { 105 const registrationId = uniqueId(); 106 const registration = 107 await backgroundFetch.fetch(registrationId, 108 new Request('https://example/com', { 109 method: 'POST', 110 headers: {'Content-Type': 'text/json'} 111 })); 112 113 assert_equals(registration.id, registrationId); 114 115 const {type, eventRegistration, results} = await getMessageFromServiceWorker(); 116 117 assert_equals(type, 'backgroundfetchsuccess'); 118 assert_equals(eventRegistration.result, 'success'); 119 assert_equals(eventRegistration.failureReason, ''); 120 }, 'Requests with text/json content type require CORS Preflight and succeed.'); 121 122 backgroundFetchTest(async (test, backgroundFetch) => { 123 const registrationId = uniqueId(); 124 const registration = 125 await backgroundFetch.fetch(registrationId, 'resources/feature-name.txt'); 126 127 assert_equals(registration.id, registrationId); 128 assert_equals(registration.uploadTotal, 0); 129 assert_equals(registration.uploaded, 0); 130 assert_equals(registration.downloadTotal, 0); 131 assert_equals(registration.result, ''); 132 assert_equals(registration.failureReason, ''); 133 assert_true(registration.recordsAvailable); 134 // Skip `downloaded`, as the transfer may have started already. 135 136 const {type, eventRegistration, results} = await getMessageFromServiceWorker(); 137 assert_equals('backgroundfetchsuccess', type); 138 assert_equals(results.length, 1); 139 140 assert_equals(eventRegistration.id, registration.id); 141 assert_equals(eventRegistration.result, 'success'); 142 assert_equals(eventRegistration.failureReason, ''); 143 144 assert_true(results[0].url.includes('resources/feature-name.txt')); 145 assert_equals(results[0].status, 200); 146 assert_equals(results[0].text, 'Background Fetch'); 147 148 }, 'Using Background Fetch to successfully fetch a single resource'); 149 150 backgroundFetchTest(async (test, backgroundFetch) => { 151 const registrationId = uniqueId(); 152 const registration = 153 await backgroundFetch.fetch(registrationId, 'resources/feature-name.txt'); 154 155 assert_equals(registration.result, ''); 156 assert_equals(registration.failureReason, ''); 157 158 const {type, eventRegistration, results} = 159 await getMessageFromServiceWorker(); 160 assert_equals('backgroundfetchsuccess', type); 161 162 assert_equals(eventRegistration.id, registration.id); 163 assert_equals(registration.result, 'success'); 164 assert_equals(registration.failureReason, ''); 165 166 }, 'Registration object gets updated values when a background fetch completes.'); 167 168 backgroundFetchTest(async (test, backgroundFetch) => { 169 const registrationId = uniqueId(); 170 171 // Very large download total that will definitely exceed the quota. 172 const options = {downloadTotal: Number.MAX_SAFE_INTEGER}; 173 await promise_rejects_quotaexceedederror( 174 test, backgroundFetch.fetch(registrationId, 'resources/feature-name.txt', options), 175 null, null 176 ); 177 178 }, 'Background Fetch that exceeds the quota throws a QuotaExceededError'); 179 180 backgroundFetchTest(async (test, backgroundFetch) => { 181 const registration = await backgroundFetch.fetch( 182 'my-id', ['resources/feature-name.txt', 'resources/feature-name.txt']); 183 184 const {type, eventRegistration, results} = await getMessageFromServiceWorker(); 185 assert_equals('backgroundfetchsuccess', type); 186 assert_equals(results.length, 2); 187 188 assert_equals(eventRegistration.id, registration.id); 189 assert_equals(eventRegistration.result, 'success'); 190 assert_equals(eventRegistration.failureReason, ''); 191 192 for (const result of results) { 193 assert_true(result.url.includes('resources/feature-name.txt')); 194 assert_equals(result.status, 200); 195 assert_equals(result.text, 'Background Fetch'); 196 } 197 198 }, 'Fetches can have requests with duplicate URLs'); 199 200 backgroundFetchTest(async (test, backgroundFetch) => { 201 const registrationId = uniqueId(); 202 const registration = 203 await backgroundFetch.fetch(registrationId, 'resources/feature-name.txt'); 204 assert_true(registration.recordsAvailable); 205 206 const {type, eventRegistration, results} = await getMessageFromServiceWorker(); 207 assert_equals('backgroundfetchsuccess', type); 208 assert_equals(results.length, 1); 209 210 // Wait for up to 5 seconds for the |eventRegistration|'s recordsAvailable 211 // flag to be set to false, which happens after the successful invocation 212 // of the ServiceWorker event has finished. 213 for (let i = 0; i < 50; ++i) { 214 if (!registration.recordsAvailable) 215 break; 216 await wait(100); 217 } 218 219 assert_false(registration.recordsAvailable); 220 }, 'recordsAvailable is false after onbackgroundfetchsuccess finishes execution.'); 221 222 backgroundFetchTest(async (test, backgroundFetch) => { 223 const registrationId = uniqueId(); 224 const registration = 225 await backgroundFetch.fetch(registrationId, 'resources/missing-cat.txt'); 226 227 assert_equals(registration.id, registrationId); 228 assert_equals(registration.result, ''); 229 assert_equals(registration.failureReason, ''); 230 231 const {type, eventRegistration, results} = await getMessageFromServiceWorker(); 232 assert_equals(type, 'backgroundfetchfail'); 233 assert_equals(results.length, 1); 234 assert_true(results[0].url.includes('resources/missing-cat.txt')); 235 assert_equals(results[0].status, 404); 236 assert_equals(results[0].text, ''); 237 238 assert_equals(eventRegistration.id, registration.id); 239 assert_equals(eventRegistration.result, 'failure'); 240 assert_equals(eventRegistration.failureReason, 'bad-status'); 241 242 assert_equals(registration.result, 'failure'); 243 assert_equals(registration.failureReason, 'bad-status'); 244 245 }, 'Using Background Fetch to fetch a non-existent resource should fail.'); 246 247 backgroundFetchTest(async (test, backgroundFetch) => { 248 const registration = await backgroundFetch.fetch( 249 'my-id', 250 [location.origin, location.origin.replace('https', 'http')]); 251 252 const {type, eventRegistration, results} = await getMessageFromServiceWorker(); 253 254 assert_equals('backgroundfetchfail', type); 255 assert_equals(eventRegistration.failureReason, 'fetch-error'); 256 257 assert_equals(results.length, 2); 258 259 const validResponse = results[0] ? results[0] : results[1]; 260 const nullResponse = !results[0] ? results[0] : results[1]; 261 262 assert_true(validResponse.url.includes(location.origin)); 263 assert_equals(nullResponse, null); 264 265 }, 'Fetches with mixed content should fail.'); 266 267 backgroundFetchTest(async (test, backgroundFetch) => { 268 const filePath = '/background-fetch/resources/feature-name.txt'; 269 const registration = await backgroundFetch.fetch( 270 uniqueId(), 271 `https://${get_host_info().REMOTE_HOST}${filePath}`); 272 273 const {type, eventRegistration, results} = await getMessageFromServiceWorker(); 274 assert_equals(type, 'backgroundfetchfail'); 275 assert_equals(results.length, 1); 276 277 assert_equals(results[0], null); 278 assert_equals(eventRegistration.id, registration.id); 279 assert_equals(eventRegistration.downloaded, 0); 280 }, 'Responses failing CORS checks are not leaked');