NDEFReader_write.https.html (19356B)
1 <!DOCTYPE html> 2 <meta charset=utf-8> 3 <title>Web NFC: NDEFReader.write Tests</title> 4 <link rel="author" title="Intel" href="http://www.intel.com"/> 5 <link rel="help" href="https://w3c.github.io/web-nfc/"/> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 <script src="resources/nfc-helpers.js"></script> 9 <script> 10 11 "use strict"; 12 13 const invalid_type_messages = 14 [ 15 // Invalid NDEFMessageSource type 16 undefined, 17 18 // NDEFMessage.records: should have at least 1 valid record. 19 // https://w3c.github.io/web-nfc/#the-write-method - Step 8. 20 createMessage([{}]), 21 22 // NDEFMessageSource: not NDEF-formatable. 23 // https://w3c.github.io/web-nfc/#the-write-method - Step 8. 24 createMessage([]), 25 26 // https://w3c.github.io/web-nfc/#dfn-map-text-to-ndef 27 // NDEFRecord must have data. 28 createMessage([createTextRecord()]), 29 30 // NDEFRecord.data for 'text' record must be either a string, 31 // an arrayBuffer, or an arrayBufferView. 32 createMessage([createTextRecord(test_json_data)]), 33 34 // NDEFRecord.encoding for 'text' record must be either "utf-8", 35 // "utf-16", "utf-16le" or "utf-16be". 36 createMessage([createTextRecord(test_buffer_data, "chinese")]), 37 38 // https://w3c.github.io/web-nfc/#dfn-map-a-url-to-ndef 39 // NDEFRecord must have data. 40 createMessage([createUrlRecord()]), 41 42 // https://w3c.github.io/web-nfc/#dfn-map-a-url-to-ndef 43 // NDEFRecord must have data. 44 createMessage([createUrlRecord(undefined, true)]), 45 46 // NDEFRecord.data for 'url' record must be string. 47 createMessage([createUrlRecord(test_buffer_data)]), 48 createMessage([createUrlRecord(test_json_data)]), 49 50 // NDEFRecord.data for 'absolute-url' record must be string. 51 createMessage([createUrlRecord(test_buffer_data, true)]), 52 createMessage([createUrlRecord(test_json_data, true)]), 53 54 // https://w3c.github.io/web-nfc/#dfn-map-binary-data-to-ndef 55 // NDEFRecord must have data. 56 createMessage([createMimeRecord()]), 57 58 // NDEFRecord.data for 'mime' record must be BufferSource. 59 createMessage([createMimeRecord(test_text_data)]), 60 createMessage([createMimeRecord(test_number_data)]), 61 createMessage([createMimeRecord(test_json_data)]), 62 63 // NDEFRecord must have data. 64 createMessage([createUnknownRecord()]), 65 66 // NDEFRecord.data for 'unknown' record must be BufferSource. 67 createMessage([createUnknownRecord(test_text_data)]), 68 createMessage([createUnknownRecord(test_number_data)]), 69 createMessage([createUnknownRecord(test_json_data)]), 70 71 // https://w3c.github.io/web-nfc/#dfn-map-external-data-to-ndef 72 // NDEFRecord must have data. 73 createMessage([createRecord('w3.org:xyz')]), 74 75 // NDEFRecord.data for external record must be a BufferSource or NDEFMessageInit. 76 createMessage([createRecord('w3.org:xyz', test_text_data)]), 77 createMessage([createRecord('w3.org:xyz', test_number_data)]), 78 createMessage([createRecord('w3.org:xyz', test_json_data)]), 79 80 // https://w3c.github.io/web-nfc/#dfn-map-local-type-to-ndef 81 // NDEFRecord must have data. 82 createMessage([createRecord(':xyz')]), 83 84 // NDEFRecord.data for local type record must be a BufferSource or NDEFMessageInit. 85 createMessage([createRecord(':xyz', test_text_data)]), 86 createMessage([createRecord(':xyz', test_number_data)]), 87 createMessage([createRecord(':xyz', test_json_data)]), 88 89 // https://w3c.github.io/web-nfc/#dfn-map-smart-poster-to-ndef 90 // NDEFRecord must have data. 91 createMessage([createRecord('smart-poster')]), 92 93 // NDEFRecord.data for smart-poster record must be a NDEFMessageInit. 94 createMessage([createRecord('smart-poster', test_text_data)]), 95 createMessage([createRecord('smart-poster', test_number_data)]), 96 createMessage([createRecord('smart-poster', test_json_data)]), 97 createMessage([createRecord('smart-poster', test_buffer_data)]), 98 99 // https://w3c.github.io/web-nfc/#ndef-record-types 100 // The record type is neither a known type ('text', 'mime' etc.) nor a 101 // valid external/local type. 102 createMessage([createRecord('unmatched_type', test_buffer_data)]) 103 ]; 104 105 const invalid_syntax_messages = 106 [ 107 // Data for 'url' or 'absolute-url' record, must be a valid URL. 108 createMessage([createUrlRecord('Invalid URL:// Data')]), 109 createMessage([createUrlRecord('Invalid URL:// Data', true)]), 110 111 // NDEFRecord.lang length for 'text' record must be lower than 64. 112 createMessage([createTextRecord(test_text_data, undefined /* encoding */, 113 [...Array(64)].map(_ => 'a'))]), 114 ]; 115 116 const invalid_signals = [ 117 "string", 118 123, 119 {}, 120 true, 121 Symbol(), 122 () => {}, 123 self 124 ]; 125 126 nfc_test(async t => { 127 const ndef = new NDEFReader(); 128 const promises = []; 129 invalid_type_messages.forEach(message => { 130 promises.push( 131 promise_rejects_js(t, TypeError, ndef.write(message))); 132 }); 133 await Promise.all(promises); 134 }, "Test that promise is rejected with TypeError if NDEFMessageSource is invalid."); 135 136 nfc_test(async t => { 137 const ndef = new NDEFReader(); 138 const promises = []; 139 invalid_syntax_messages.forEach(message => { 140 promises.push( 141 promise_rejects_dom(t, 'SyntaxError', ndef.write(message))); 142 }); 143 await Promise.all(promises); 144 }, "Test that promise is rejected with SyntaxError if NDEFMessageSource contains\ 145 invalid records."); 146 147 nfc_test(async t => { 148 await test_driver.set_permission({ name: 'nfc' }, 'denied'); 149 const ndef = new NDEFReader(); 150 await promise_rejects_dom(t, 'NotAllowedError', ndef.write(test_text_data)); 151 }, 'NDEFReader.write should fail if user permission is not granted.'); 152 153 // We do not provide NFC mock here to simulate that there has no available 154 // implementation for NFC Mojo interface. 155 nfc_test(async (t, mockNFC) => { 156 mockNFC.simulateClosedPipe(); 157 const ndef = new NDEFReader(); 158 await promise_rejects_dom(t, 'NotSupportedError', ndef.write(test_text_data)); 159 }, 'NDEFReader.write should fail if no implementation for NFC Mojo interface is available.'); 160 161 nfc_test(async (t, mockNFC) => { 162 const ndef = new NDEFReader(); 163 const controller = new AbortController(); 164 165 //Make sure push is pending 166 mockNFC.setPendingPushCompleted(false); 167 const p = ndef.write(test_text_data, { signal: controller.signal }); 168 const rejected = promise_rejects_dom(t, 'AbortError', p); 169 let callback_called = false; 170 await new Promise(resolve => { 171 t.step_timeout(() => { 172 callback_called = true; 173 controller.abort(); 174 resolve(); 175 }, 10); 176 }); 177 await rejected; 178 assert_true(callback_called, 'timeout should have caused the abort'); 179 }, "NDEFReader.write should fail if abort write request before write happends."); 180 181 nfc_test(async t => { 182 const ndef = new NDEFReader(); 183 const controller = new AbortController(); 184 assert_false(controller.signal.aborted); 185 controller.abort(); 186 assert_true(controller.signal.aborted); 187 await promise_rejects_dom(t, 'AbortError', 188 ndef.write(test_text_data, { signal: controller.signal })); 189 }, "NDEFReader.write should fail if signal's aborted flag is set."); 190 191 nfc_test(async t => { 192 const ndef = new NDEFReader(); 193 const promises = []; 194 invalid_signals.forEach(invalid_signal => { 195 promises.push(promise_rejects_js(t, TypeError, 196 ndef.write(test_text_data, { signal: invalid_signal }))); 197 }); 198 await Promise.all(promises); 199 }, "NDEFReader.write should fail if signal is not an AbortSignal."); 200 201 nfc_test(async (t, mockNFC) => { 202 const ndef1 = new NDEFReader(); 203 const ndef2 = new NDEFReader(); 204 const controller = new AbortController(); 205 const p1 = ndef1.write(test_text_data, { signal: controller.signal }); 206 207 // Even though write request is grantable, 208 // this abort should be processed synchronously. 209 controller.abort(); 210 await promise_rejects_dom(t, 'AbortError', p1); 211 212 await ndef2.write(test_text_data); 213 assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage()); 214 }, "Synchronously signaled abort."); 215 216 nfc_test(async (t, mockNFC) => { 217 const ndef = new NDEFReader(); 218 mockNFC.setHWStatus(NFCHWStatus.DISABLED); 219 await promise_rejects_dom(t, 'NotReadableError', ndef.write(test_text_data)); 220 }, "NDEFReader.write should fail when NFC HW is disabled."); 221 222 nfc_test(async (t, mockNFC) => { 223 const ndef = new NDEFReader(); 224 mockNFC.setHWStatus(NFCHWStatus.NOT_SUPPORTED); 225 await promise_rejects_dom(t, 'NotSupportedError', ndef.write(test_text_data)); 226 }, "NDEFReader.write should fail when NFC HW is not supported."); 227 228 nfc_test(async () => { 229 await new Promise((resolve,reject) => { 230 const iframe = document.createElement('iframe'); 231 iframe.srcdoc = `<script> 232 window.onmessage = async (message) => { 233 if (message.data === "Ready") { 234 try { 235 const ndef = new NDEFReader(); 236 await ndef.write("Test"); 237 parent.postMessage("Failure", "*"); 238 } catch (error) { 239 if (error.name == "InvalidStateError") { 240 parent.postMessage("Success", "*"); 241 } else { 242 parent.postMessage("Failure", "*"); 243 } 244 } 245 } 246 }; 247 <\/script>`; 248 iframe.onload = () => iframe.contentWindow.postMessage('Ready', '*'); 249 document.body.appendChild(iframe); 250 window.onmessage = message => { 251 if (message.data == 'Success') { 252 resolve(); 253 } else if (message.data == 'Failure') { 254 reject(); 255 } 256 } 257 }); 258 }, 'Test that WebNFC API is not accessible from iframe context.'); 259 260 nfc_test(async () => { 261 const ndef = new NDEFReader(); 262 await ndef.write(test_text_data); 263 }, 'NDEFReader.write should succeed when NFC HW is enabled'); 264 265 nfc_test(async (t, mockNFC) => { 266 const ndef = new NDEFReader(); 267 const message = createMessage([createTextRecord(test_text_data), 268 createMimeRecordFromJson(test_json_data), 269 createMimeRecord(test_buffer_data), 270 createUnknownRecord(test_buffer_data), 271 createUrlRecord(test_url_data), 272 createUrlRecord(test_url_data, true), 273 createRecord('w3.org:xyz', test_buffer_data)], 274 test_message_origin); 275 await ndef.write(message); 276 assertNDEFMessagesEqual(message, mockNFC.pushedMessage()); 277 }, "NDEFReader.write NDEFMessage containing text, mime, unknown, url, absolute-url \ 278 and external records with default NDEFWriteOptions."); 279 280 nfc_test(async (t, mockNFC) => { 281 const messageContainText = createMessage([createTextRecord(test_text_data)]); 282 283 // Prepare a local type record that uses |messageContainText| as its payload. 284 const messageContainLocal = createMessage([createRecord(':containsTextRecord', messageContainText)]); 285 286 // Prepare an external type record that uses |messageContainLocal| as its payload. 287 const message = createMessage([createRecord('example.com:containsLocalRecord', messageContainLocal)]); 288 289 const ndef = new NDEFReader(); 290 await ndef.write(message); 291 const pushed_message = mockNFC.pushedMessage(); 292 293 // The mojom message received by mock nfc contains only the external type record. 294 assert_equals(pushed_message.data.length, 1); 295 assert_equals(pushed_message.data[0].recordType, 'example.com:containsLocalRecord', 'recordType'); 296 297 // The external type record's payload is from the original |messageContainLocal|, 298 // containing only the local type record. 299 assert_array_equals(pushed_message.data[0].data, new Uint8Array(0), 300 'payloadMessage is used instead'); 301 assert_equals(pushed_message.data[0].payloadMessage.data.length, 1); 302 assert_equals(pushed_message.data[0].payloadMessage.data[0].recordType, ':containsTextRecord', 'recordType'); 303 304 // The local type record's payload is from the original |messageContainText|, 305 // containing only the text record. 306 assert_array_equals(pushed_message.data[0].payloadMessage.data[0].data, new Uint8Array(0), 307 'payloadMessage is used instead'); 308 assertNDEFMessagesEqual(messageContainText, pushed_message.data[0].payloadMessage.data[0].payloadMessage); 309 }, "NDEFReader.write NDEFMessage containing embedded records."); 310 311 nfc_test(async (t, mockNFC) => { 312 // A smart-poster record contains a uri record, text record. 313 const uri_record = createUrlRecord(test_url_data); 314 const text_record = createTextRecord(test_text_data); 315 const payload_message = createMessage([uri_record, text_record]); 316 const message = createMessage([createRecord( 317 'smart-poster', payload_message, "dummy_record_id")]); 318 319 const ndef = new NDEFReader(); 320 await ndef.write(message); 321 const pushed_message = mockNFC.pushedMessage(); 322 323 // The mojom message received by mock nfc contains only the smart-poster record. 324 assert_equals(pushed_message.data.length, 1); 325 assert_equals(pushed_message.data[0].recordType, 'smart-poster', 'recordType'); 326 assert_equals(pushed_message.data[0].mediaType, null, 'mediaType'); 327 assert_equals(pushed_message.data[0].id, 'dummy_record_id', 'id'); 328 329 const embedded_records = pushed_message.data[0].payloadMessage.data; 330 assert_equals(embedded_records.length, 2); 331 332 let embedded_record_types = []; 333 for(let record of embedded_records) { 334 embedded_record_types.push(record.recordType); 335 switch(record.recordType) { 336 case 'url': 337 compareNDEFRecords(uri_record, record); 338 break; 339 case 'text': 340 compareNDEFRecords(text_record, record); 341 break; 342 default: 343 assert_unreached("Unknown recordType"); 344 } 345 } 346 assert_array_equals(embedded_record_types.sort(), ['text', 'url'], 347 'smart-poster record\'s contained record types'); 348 }, "NDEFReader.write NDEFMessage containing smart-poster record."); 349 350 nfc_test(async (t, mockNFC) => { 351 const ndef = new NDEFReader(); 352 await ndef.write(test_text_data); 353 assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage()); 354 }, "Test that NDEFReader.write succeeds when message is DOMString."); 355 356 nfc_test(async (t, mockNFC) => { 357 const ndef = new NDEFReader(); 358 await ndef.write(test_buffer_data); 359 assertNDEFMessagesEqual(test_buffer_data, mockNFC.pushedMessage()); 360 }, "Test that NDEFReader.write succeeds when message is ArrayBuffer."); 361 362 nfc_test(async (t, mockNFC) => { 363 let buffer_view = new Uint8Array(test_buffer_data, 2, 5); 364 const ndef = new NDEFReader(); 365 await ndef.write(buffer_view); 366 assertNDEFMessagesEqual(buffer_view, mockNFC.pushedMessage()); 367 }, "Test that NDEFReader.write succeeds when message is ArrayBufferView."); 368 369 nfc_test(async (t, mockNFC) => { 370 const ndef = new NDEFReader(); 371 await ndef.write(createMessage([createRecord('empty')])); 372 const receivedMessage = mockNFC.pushedMessage(); 373 assert_equals(receivedMessage.data.length, 1); 374 assert_equals(receivedMessage.data[0].recordType, 'empty', 'recordType'); 375 }, "NDEFReader.write with 'empty' record should succeed."); 376 377 nfc_test(async (t, mockNFC) => { 378 const ndef = new NDEFReader(); 379 await ndef.write(test_text_data); 380 assertNDEFWriteOptionsEqual({overwrite: true}, mockNFC.writeOptions()); 381 }, "Check that default NDEFWriteOptions values are correctly set."); 382 383 nfc_test(async (t, mockNFC) => { 384 const ndef = new NDEFReader(); 385 await ndef.write(test_text_data, {overwrite: false}); 386 assertNDEFWriteOptionsEqual({overwrite: false}, mockNFC.writeOptions()); 387 }, "Check that provided NDEFWriteOptions values are correctly converted."); 388 389 nfc_test(async (t, mockNFC) => { 390 const ndef1 = new NDEFReader(); 391 const ndef2 = new NDEFReader(); 392 393 const p1 = ndef1.write(test_text_data, {overwrite: false}); 394 const p2 = ndef2.write(test_url_data, {overwrite: true}); 395 396 await new Promise((resolve, reject) => { 397 // Make first push pending 398 mockNFC.setPendingPushCompleted(false); 399 let err = ""; 400 p1.then(() => { 401 reject("pending push should not be fulfilled"); 402 }).catch(e => { 403 err = e.name; 404 }); 405 p2.then(() => { 406 assertNDEFMessagesEqual(test_url_data, mockNFC.pushedMessage()); 407 assertNDEFWriteOptionsEqual( {overwrite: true}, mockNFC.writeOptions()); 408 assert_equals(err, "AbortError", "the pending push should be aborted"); 409 resolve(); 410 }); 411 }); 412 }, "NDEFReader.write should replace all previously configured write operations."); 413 414 nfc_test(async () => { 415 const ndef = new NDEFReader(); 416 417 const controller1 = new AbortController(); 418 await ndef.write(test_text_data, {signal: controller1.signal}); 419 420 const controller2 = new AbortController(); 421 const promise = ndef.write(test_text_data, {signal: controller2.signal}); 422 controller1.abort(); 423 await promise; 424 }, 'NDEFReader.write signals are independent.'); 425 426 nfc_test(async (t, mockNFC) => { 427 const ndef = new NDEFReader(); 428 await ndef.write({ records: [{ recordType: "mime", data: test_buffer_data }] }); 429 assertNDEFMessagesEqual(test_buffer_data, mockNFC.pushedMessage()); 430 }, "Test that mediaType should be set to 'application/octet-stream' if \ 431 NDEFRecordInit.record's recordType is 'mime' and NDEFRecordInit.record's \ 432 mediaType is undefined."); 433 434 nfc_test(async (t, mockNFC) => { 435 // Make sure the push will be pending in the mock. 436 mockNFC.setPendingPushCompleted(false); 437 438 const ndef1 = new NDEFReader(); 439 const promise = ndef1.write(test_text_data); 440 441 // Just to make sure the write() request has already reached to the mock. 442 const ndef2 = new NDEFReader(); 443 await ndef2.scan(); 444 445 mockNFC.simulateNonNDEFTagDiscovered(); 446 await promise_rejects_dom(t, 'NotSupportedError', promise); 447 }, "NDEFReader.write should fail when the NFC device coming up does not expose \ 448 NDEF technology."); 449 450 nfc_test(async (t, mockNFC) => { 451 const ndef = new NDEFReader(); 452 await ndef.write(test_text_data, {overwrite: false}); 453 assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage()); 454 }, "NDEFReader.write should succeed to write data to an unformatted NFC device \ 455 when the NDEFWriteOptions.overwrite is false."); 456 457 nfc_test(async (t, mockNFC) => { 458 const ndef = new NDEFReader(); 459 await ndef.write(test_buffer_data); 460 assertNDEFMessagesEqual(test_buffer_data, mockNFC.pushedMessage()); 461 await ndef.write(test_text_data, {overwrite: true}); 462 assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage()); 463 }, "NDEFReader.write should succeed to overwrite the existing data \ 464 when the NDEFWriteOptions.overwrite is true."); 465 466 nfc_test(async (t, mockNFC) => { 467 const ndef = new NDEFReader(); 468 const p = ndef.write(test_text_data, {overwrite: false}); 469 mockNFC.setIsFormattedTag(true); 470 await promise_rejects_dom(t, 'NotAllowedError', p); 471 }, "NDEFReader.write should fail when there are NDEF records on the NFC device \ 472 and NDEFWriteOptions.overwrite is false."); 473 474 nfc_test(async (t, mockNFC) => { 475 const ndef = new NDEFReader(); 476 mockNFC.simulateDataTransferFails(); 477 await promise_rejects_dom(t, 'NetworkError', ndef.write(test_text_data)); 478 }, "NDEFReader.write should fail with NetworkError when NFC data transfer fails."); 479 480 </script>