// META: script=resources/nfc-helpers.js // NDEFRecord constructor // https://w3c.github.io/web-nfc/#dom-ndefrecord test(() => { assert_equals(NDEFRecord.length, 1); assert_throws_js(TypeError, () => new NDEFRecord()); }, 'NDEFRecord constructor without init dict'); test(() => { assert_throws_js( TypeError, () => new NDEFRecord(null), 'NDEFRecordInit#recordType is a required field.'); }, 'NDEFRecord constructor with null init dict'); test(() => { assert_throws_js( TypeError, () => new NDEFRecord({id: test_record_id, data: test_text_data}), 'NDEFRecordInit#recordType is a required field.'); }, 'NDEFRecord constructor without NDEFRecordInit#recordType field'); test(() => { assert_throws_js( TypeError, () => new NDEFRecord(createRecord('empty', test_text_data, test_record_id)), 'id does not apply for empty record type.'); }, 'NDEFRecord constructor with empty record type and id'); test(() => { assert_throws_js( TypeError, () => new NDEFRecord( createRecord('empty', test_text_data, test_record_id, 'text/plain')), 'mediaType does not apply for empty record type.'); assert_throws_js( TypeError, () => new NDEFRecord( createRecord('text', test_text_data, test_record_id, 'text/plain')), 'mediaType does not apply for text record type.'); assert_throws_js( TypeError, () => new NDEFRecord( createRecord('url', test_url_data, test_record_id, 'text/plain')), 'mediaType does not apply for url record type.'); assert_throws_js( TypeError, () => new NDEFRecord(createRecord( 'absolute-url', test_url_data, test_record_id, 'text/plain')), 'mediaType does not apply for absolute-url record type.'); assert_throws_js( TypeError, () => new NDEFRecord(createRecord( 'unknown', test_buffer_data, test_record_id, 'application/octet-stream')), 'mediaType does not apply for unknown record type.'); assert_throws_js( TypeError, () => new NDEFRecord(createRecord( 'foo.example.com:bar', test_buffer_data, test_record_id, 'application/octet-stream')), 'mediaType does not apply for external record type.'); }, 'NDEFRecord constructor should only accept mediaType for mime record type'); test( () => { { const record = new NDEFRecord(createRecord('text', test_text_data)); assert_equals(record.id, null, 'id'); } {const record = new NDEFRecord(createRecord('text', test_text_data, '')); assert_equals(record.id, '', 'id');} { const dummy_id = 'https://dummy_host/mypath/myid'; const record = new NDEFRecord(createRecord('text', test_text_data, dummy_id)); assert_equals(record.id, dummy_id, 'id'); } {const dummy_id = 'http://dummy_host/mypath/myid'; const record = new NDEFRecord(createRecord('text', test_text_data, dummy_id)); assert_equals(record.id, dummy_id, 'id');} { const dummy_id = 'mypath/myid'; const record = new NDEFRecord(createRecord('text', test_text_data, dummy_id)); assert_equals(record.id, dummy_id, 'id'); } }, 'NDEFRecord constructor with custom record ids'); test(() => { const record = new NDEFRecord(createRecord('empty')); assert_equals(record.recordType, 'empty', 'recordType'); assert_equals(record.mediaType, null, 'mediaType'); assert_equals(record.id, null, 'id'); assert_equals(record.encoding, null, 'encoding'); assert_equals(record.lang, null, 'lang'); assert_equals(record.data, null, 'data'); assert_throws_dom( 'NotSupportedError', () => record.toRecords(), 'Only smart-poster, external type and local type records could have embedded records.'); }, 'NDEFRecord constructor with empty record type'); test(() => { const record = new NDEFRecord(createTextRecord(test_text_data)); assert_equals(record.recordType, 'text', 'recordType'); assert_equals(record.mediaType, null, 'mediaType'); assert_equals(record.id, test_record_id, 'id'); assert_equals(record.encoding, 'utf-8', 'encoding'); assert_equals(record.lang, 'en', 'lang'); const decoder = new TextDecoder(); assert_equals( decoder.decode(record.data), test_text_data, 'data has the same content with the original dictionary'); assert_throws_dom( 'NotSupportedError', () => record.toRecords(), 'Only smart-poster, external type and local type records could have embedded records.'); }, 'NDEFRecord constructor with text record type and string data'); test(() => { const encoder = new TextEncoder(); const uint8Array = encoder.encode(test_text_data); const record = new NDEFRecord(createTextRecord(uint8Array.buffer)); assert_equals(record.recordType, 'text', 'recordType'); assert_equals(record.mediaType, null, 'mediaType'); assert_equals(record.id, test_record_id, 'id'); // By default, 'utf-8'. assert_equals(record.encoding, 'utf-8', 'encoding'); assert_equals(record.lang, 'en', 'lang'); const decoder = new TextDecoder(); assert_equals( decoder.decode(record.data), test_text_data, 'data has the same content with the original dictionary'); assert_throws_dom( 'NotSupportedError', () => record.toRecords(), 'Only smart-poster, external type and local type records could have embedded records.'); }, 'NDEFRecord constructor with text record type and arrayBuffer data'); test(() => { const encoder = new TextEncoder(); const uint8Array = encoder.encode(test_text_data); const record = new NDEFRecord(createTextRecord(uint8Array)); assert_equals(record.recordType, 'text', 'recordType'); assert_equals(record.mediaType, null, 'mediaType'); assert_equals(record.id, test_record_id, 'id'); // By default, 'utf-8'. assert_equals(record.encoding, 'utf-8', 'encoding'); assert_equals(record.lang, 'en', 'lang'); const decoder = new TextDecoder(); assert_equals( decoder.decode(record.data), test_text_data, 'data has the same content with the original dictionary'); assert_throws_dom( 'NotSupportedError', () => record.toRecords(), 'Only smart-poster, external type and local type records could have embedded records.'); }, 'NDEFRecord constructor with text record type and arrayBufferView data'); test(() => { assert_throws_js( TypeError, () => new NDEFRecord(createTextRecord(test_text_data, 'random-encoding'))); assert_throws_js( TypeError, () => new NDEFRecord(createTextRecord(test_text_data, 'utf-16'))); // Only 'utf-8' is OK for a DOMString data source. const record = new NDEFRecord(createTextRecord(test_text_data, 'utf-8', 'fr')); assert_equals(record.recordType, 'text', 'recordType'); assert_equals(record.encoding, 'utf-8', 'encoding'); assert_equals(record.lang, 'fr', 'lang'); const decoder = new TextDecoder(); assert_equals( decoder.decode(record.data), test_text_data, 'data has the same content with the original text'); assert_throws_js( TypeError, () => new NDEFRecord(createTextRecord( encodeTextToArrayBuffer(test_text_data, 'utf-8'), 'random-encoding'))); // The encoding list valid for a BufferSource data source. const encodings = ['utf-8', 'utf-16', 'utf-16be', 'utf-16le']; for (const encoding of encodings) { const record = new NDEFRecord(createTextRecord( encodeTextToArrayBuffer(test_text_data, encoding), encoding, 'fr')); assert_equals(record.recordType, 'text', 'recordType'); assert_equals(record.encoding, encoding, 'encoding'); assert_equals(record.lang, 'fr', 'lang'); const decoder = new TextDecoder(record.encoding); assert_equals( decoder.decode(record.data), test_text_data, 'data has the same content with the original text. encoding: ' + encoding); } }, 'NDEFRecord constructor with text record type, encoding, and lang'); test(t => { const previous_lang = document.querySelector('html').getAttribute('lang'); const test_lang = 'fr'; document.querySelector('html').setAttribute('lang', test_lang); t.add_cleanup(() => { document.querySelector('html').setAttribute('lang', previous_lang); }); const record = new NDEFRecord(createTextRecord(test_text_data)); assert_equals(record.recordType, 'text', 'recordType'); assert_equals(record.mediaType, null, 'mediaType'); assert_equals(record.id, test_record_id, 'id'); assert_equals(record.encoding, 'utf-8', 'encoding'); assert_equals(record.lang, test_lang, 'lang'); const decoder = new TextDecoder(); assert_equals( decoder.decode(record.data), test_text_data, 'data has the same content with the original dictionary'); }, 'NDEFRecord constructor with text record type and custom document language'); test(() => { const record = new NDEFRecord(createUrlRecord(test_url_data)); assert_equals(record.recordType, 'url', 'recordType'); assert_equals(record.mediaType, null, 'mediaType'); assert_equals(record.id, test_record_id, 'id'); const decoder = new TextDecoder(); assert_equals( decoder.decode(record.data), test_url_data, 'data has the same content with the original dictionary'); assert_throws_dom( 'NotSupportedError', () => record.toRecords(), 'Only smart-poster, external type and local type records could have embedded records.'); }, 'NDEFRecord constructor with url record type'); test(() => { const record = new NDEFRecord(createUrlRecord(test_url_data, true)); assert_equals(record.recordType, 'absolute-url', 'recordType'); assert_equals(record.mediaType, null, 'mediaType'); assert_equals(record.id, test_record_id, 'id'); const decoder = new TextDecoder(); assert_equals( decoder.decode(record.data), test_url_data, 'data has the same content with the original dictionary'); assert_throws_dom( 'NotSupportedError', () => record.toRecords(), 'Only smart-poster, external type and local type records could have embedded records.'); }, 'NDEFRecord constructor with absolute-url record type'); test(() => { assert_throws_js( TypeError, () => new NDEFRecord(createMimeRecord('A string is not a BufferSource')), 'Only BufferSource is allowed to be the record data.'); let buffer = new ArrayBuffer(4); new Uint8Array(buffer).set([1, 2, 3, 4]); // Feed ArrayBuffer. { const record = new NDEFRecord(createMimeRecord(buffer)); assert_equals(record.recordType, 'mime', 'recordType'); assert_equals(record.mediaType, 'application/octet-stream', 'mediaType'); assert_equals(record.id, test_record_id, 'id'); assert_array_equals( new Uint8Array(record.data.buffer), [1, 2, 3, 4], 'data has the same content with the original buffer'); assert_throws_dom( 'NotSupportedError', () => record.toRecords(), 'Only smart-poster, external type and local type records could have embedded records.'); } // Feed ArrayBufferView. { let buffer_view = new Uint8Array(buffer, 1); const record = new NDEFRecord(createMimeRecord(buffer_view)); assert_equals(record.recordType, 'mime', 'recordType'); assert_equals(record.id, test_record_id, 'id'); assert_array_equals( new Uint8Array(record.data.buffer), [2, 3, 4], 'data has the same content with the original buffer view'); assert_throws_dom( 'NotSupportedError', () => record.toRecords(), 'Only smart-poster, external type and local type records could have embedded records.'); } }, 'NDEFRecord constructor with mime record type and stream data'); test(() => { const record = new NDEFRecord(createMimeRecordFromJson(test_json_data)); assert_equals(record.recordType, 'mime', 'recordType'); assert_equals(record.mediaType, 'application/json', 'mediaType'); assert_equals(record.id, test_record_id, 'id'); assert_object_equals( JSON.parse(new TextDecoder().decode(record.data)), test_json_data, 'data has the same content with the original json'); assert_throws_dom( 'NotSupportedError', () => record.toRecords(), 'Only smart-poster, external type and local type records could have embedded records.'); }, 'NDEFRecord constructor with mime record type and json data'); test(() => { assert_throws_js( TypeError, () => new NDEFRecord(createUnknownRecord('A string is not a BufferSource')), 'Only BufferSource is allowed to be the record data.'); let buffer = new ArrayBuffer(4); new Uint8Array(buffer).set([1, 2, 3, 4]); // Feed ArrayBuffer. { const record = new NDEFRecord(createUnknownRecord(buffer)); assert_equals(record.recordType, 'unknown', 'recordType'); assert_equals(record.id, test_record_id, 'id'); assert_array_equals( new Uint8Array(record.data.buffer), [1, 2, 3, 4], 'data has the same content with the original buffer'); assert_throws_dom( 'NotSupportedError', () => record.toRecords(), 'Only smart-poster, external type and local type records could have embedded records.'); } // Feed ArrayBufferView. { let buffer_view = new Uint8Array(buffer, 1); const record = new NDEFRecord(createUnknownRecord(buffer_view)); assert_equals(record.recordType, 'unknown', 'recordType'); assert_equals(record.id, test_record_id, 'id'); assert_array_equals( new Uint8Array(record.data.buffer), [2, 3, 4], 'data has the same content with the original buffer view'); assert_throws_dom( 'NotSupportedError', () => record.toRecords(), 'Only smart-poster, external type and local type records could have embedded records.'); } }, 'NDEFRecord constructor with unknown record type'); test(() => { assert_throws_js( TypeError, () => new NDEFRecord(createRecord( 'foo.eXamPle.com:bAr*-', 'A string is not a BufferSource or NDEFMessageInit')), 'Only BufferSource and NDEFMessageInit are allowed to be the record data.'); let buffer = new ArrayBuffer(4); new Uint8Array(buffer).set([1, 2, 3, 4]); // Feed ArrayBuffer. { const record = new NDEFRecord( createRecord('foo.eXamPle.com:bAr*-', buffer, test_record_id)); assert_equals(record.recordType, 'foo.eXamPle.com:bAr*-', 'recordType'); assert_equals(record.mediaType, null, 'mediaType'); assert_equals(record.id, test_record_id, 'id'); assert_array_equals( new Uint8Array(record.data.buffer), [1, 2, 3, 4], 'data has the same content with the original buffer'); assert_equals( record.toRecords(), null, 'toRecords() returns null if the payload is not an NDEF message.'); } // Feed ArrayBufferView. { let buffer_view = new Uint8Array(buffer, 1); const record = new NDEFRecord( createRecord('foo.eXamPle.com:bAr*-', buffer_view, test_record_id)); assert_equals(record.recordType, 'foo.eXamPle.com:bAr*-', 'recordType'); assert_equals(record.mediaType, null, 'mediaType'); assert_equals(record.id, test_record_id, 'id'); assert_array_equals( new Uint8Array(record.data.buffer), [2, 3, 4], 'data has the same content with the original buffer view'); assert_equals( record.toRecords(), null, 'toRecords() returns null if the payload is not an NDEF message.'); } // Feed NDEFMessageInit. { const payload_message = createMessage([createTextRecord(test_text_data)]); const record = new NDEFRecord(createRecord( 'foo.eXamPle.com:bAr*-', payload_message, 'dummy_record_id')); assert_equals(record.recordType, 'foo.eXamPle.com:bAr*-', 'recordType'); assert_equals(record.mediaType, null, 'mediaType'); assert_equals(record.id, 'dummy_record_id', 'id'); const embedded_records = record.toRecords(); assert_equals(embedded_records.length, 1, 'Only one embedded record.'); // The only one embedded record has correct content. assert_equals(embedded_records[0].recordType, 'text', 'recordType'); assert_equals(embedded_records[0].mediaType, null, 'mediaType'); assert_equals(embedded_records[0].id, test_record_id, 'id'); const decoder = new TextDecoder(); assert_equals( decoder.decode(embedded_records[0].data), test_text_data, 'data has the same content with the original dictionary'); } }, 'NDEFRecord constructor with external record type'); test(() => { assert_throws_js( TypeError, () => new NDEFRecord(createRecord(':xyz', test_buffer_data)), 'The local type record must be embedded in the payload of another record (smart-poster, external, or local)'); // The following test cases use an external type record embedding our target // local type record. const local_record = createRecord(':xyz', undefined /* data */, 'dummy_id_for_local_type'); const payload_message = createMessage([local_record]); const external_record_embedding_local_record = createRecord('example.com:foo', payload_message); local_record.data = 'A string is not a BufferSource or NDEFMessageInit'; assert_throws_js( TypeError, () => new NDEFRecord(external_record_embedding_local_record), 'Only BufferSource and NDEFMessageInit are allowed to be the record data.'); let buffer = new ArrayBuffer(4); new Uint8Array(buffer).set([1, 2, 3, 4]); // Feed ArrayBuffer. { local_record.data = buffer; const record = new NDEFRecord(external_record_embedding_local_record); const embedded_records = record.toRecords(); assert_equals( embedded_records.length, 1, 'Only the one embedded local record.'); // The embedded local record is actually from |local_record|. assert_equals(embedded_records[0].recordType, ':xyz', 'recordType'); assert_equals(embedded_records[0].mediaType, null, 'mediaType'); assert_equals(embedded_records[0].id, 'dummy_id_for_local_type', 'id'); assert_array_equals( new Uint8Array(embedded_records[0].data.buffer), [1, 2, 3, 4], 'data has the same content with the original buffer'); assert_equals( embedded_records[0].toRecords(), null, 'toRecords() returns null if the payload is not an NDEF message.'); } // Feed ArrayBufferView. { let buffer_view = new Uint8Array(buffer, 1); local_record.data = buffer_view; const record = new NDEFRecord(external_record_embedding_local_record); const embedded_records = record.toRecords(); assert_equals( embedded_records.length, 1, 'Only the one embedded local record.'); // The embedded local record is actually from |local_record|. assert_equals(embedded_records[0].recordType, ':xyz', 'recordType'); assert_equals(embedded_records[0].mediaType, null, 'mediaType'); assert_equals(embedded_records[0].id, 'dummy_id_for_local_type', 'id'); assert_array_equals( new Uint8Array(embedded_records[0].data.buffer), [2, 3, 4], 'data has the same content with the original buffer view'); assert_equals( embedded_records[0].toRecords(), null, 'toRecords() returns null if the payload is not an NDEF message.'); } // Feed NDEFMessageInit. { const payload_message = createMessage([createTextRecord(test_text_data)]); local_record.data = payload_message; const record = new NDEFRecord(external_record_embedding_local_record); const embedded_records = record.toRecords(); assert_equals( embedded_records.length, 1, 'Only the one embedded local record.'); // The embedded local record is actually from |local_record|. assert_equals(embedded_records[0].recordType, ':xyz', 'recordType'); assert_equals(embedded_records[0].mediaType, null, 'mediaType'); assert_equals(embedded_records[0].id, 'dummy_id_for_local_type', 'id'); // The embedded local record embeds another text record that's from // |payload_message|. const embedded_records_in_local_record = embedded_records[0].toRecords(); assert_equals( embedded_records_in_local_record.length, 1, 'Only one embedded record.'); // The only one embedded record has correct content. assert_equals( embedded_records_in_local_record[0].recordType, 'text', 'recordType'); assert_equals( embedded_records_in_local_record[0].mediaType, null, 'mediaType'); assert_equals(embedded_records_in_local_record[0].id, test_record_id, 'id'); const decoder = new TextDecoder(); assert_equals( decoder.decode(embedded_records_in_local_record[0].data), test_text_data, 'data has the same content with the original dictionary'); } }, 'NDEFRecord constructor with local record type'); test(() => { let buffer = new ArrayBuffer(4); new Uint8Array(buffer).set([1, 2, 3, 4]); const encoder = new TextEncoder(); const uri_record = createUrlRecord(test_url_data); const title_record = createTextRecord(test_text_data, 'utf-8', 'en'); const type_record = createRecord(':t', encoder.encode('image/gif')); const size_record = createRecord(':s', new Uint32Array([4096])); const action_record = createRecord(':act', new Uint8Array([3])); const icon_record = createRecord('mime', buffer, test_record_id, 'image/gif'); const payload_message = createMessage([ uri_record, title_record, type_record, size_record, action_record, icon_record ]); const smart_poster_record = createRecord('smart-poster', payload_message, 'dummy_record_id'); const record = new NDEFRecord(smart_poster_record); assert_equals(record.recordType, 'smart-poster', 'recordType'); assert_equals(record.mediaType, null, 'mediaType'); assert_equals(record.id, 'dummy_record_id', 'id'); const embedded_records = record.toRecords(); assert_equals(embedded_records.length, 6, 'length'); const decoder = new TextDecoder(); let embedded_record_types = []; for (let record of embedded_records) { embedded_record_types.push(record.recordType); switch (record.recordType) { case 'url': assert_equals(record.mediaType, null, 'uri record\'s mediaType'); assert_equals(record.id, test_record_id, 'uri record\'s id'); assert_equals( decoder.decode(record.data), test_url_data, 'uri record\'s data'); break; case 'text': assert_equals(record.mediaType, null, 'title record\'s mediaType'); assert_equals(record.id, test_record_id, 'title record\'s id'); assert_equals(record.encoding, 'utf-8', 'title record\'s encoding'); assert_equals(record.lang, 'en', 'title record\'s lang'); assert_equals( decoder.decode(record.data), test_text_data, 'title record\'s data'); break; case ':t': assert_equals(record.mediaType, null, 'type record\'s mediaType'); assert_equals(record.id, null, 'type record\'s id'); assert_equals( decoder.decode(record.data), 'image/gif', 'type record\'s data'); break; case ':s': assert_equals(record.mediaType, null, 'size record\'s mediaType'); assert_equals(record.id, null, 'size record\'s id'); assert_equals( record.data.byteLength, 4, 'byteLength of size record\'s data'); assert_equals( new Uint32Array(record.data.buffer)[0], 4096, 'value of size record\'s data'); break; case ':act': assert_equals(record.mediaType, null, 'action record\'s mediaType'); assert_equals(record.id, null, 'action record\'s id'); assert_equals( record.data.byteLength, 1, 'byteLength of action record\'s data'); assert_equals( new Uint8Array(record.data.buffer)[0], 3, 'value of action record\'s data'); break; case 'mime': assert_equals( record.mediaType, 'image/gif', 'icon record\'s mediaType'); assert_equals(record.id, test_record_id, 'icon record\'s id'); assert_array_equals( new Uint8Array(record.data.buffer), [1, 2, 3, 4], 'icon record\'s mediaType'); break; default: assert_unreached('Unknown recordType'); } } assert_array_equals( embedded_record_types.sort(), [':act', ':s', ':t', 'mime', 'text', 'url'], 'smart-poster record\'s contained record types'); }, 'NDEFRecord constructor with smart-poster record type'); test(() => { const uri_record = createUrlRecord(test_url_data); const smart_poster_record = createRecord( 'smart-poster', createMessage([uri_record]), 'dummy_record_id'); const record = new NDEFRecord(smart_poster_record); assert_equals(record.recordType, 'smart-poster', 'recordType'); assert_equals(record.mediaType, null, 'mediaType'); assert_equals(record.id, 'dummy_record_id', 'id'); const embedded_records = record.toRecords(); // smart-poster record only contains a uri record. assert_equals(embedded_records.length, 1, 'length'); const decoder = new TextDecoder(); assert_equals( embedded_records[0].recordType, 'url', 'uri record\'s recordType'); assert_equals(embedded_records[0].mediaType, null, 'uri record\'s mediaType'); assert_equals(embedded_records[0].id, test_record_id, 'uri record\'s id'); assert_equals( decoder.decode(embedded_records[0].data), test_url_data, 'uri record\'s data'); }, 'NDEFRecord constructor with smart-poster record type that contains only a mandatory uri record'); test(() => { assert_throws_js( TypeError, () => new NDEFRecord(createRecord('EMptY')), 'Unknown record type.'); assert_throws_js( TypeError, () => new NDEFRecord(createRecord('TeXt', test_text_data)), 'Unknown record type.'); assert_throws_js( TypeError, () => new NDEFRecord(createRecord('uRL', test_url_data)), 'Unknown record type.'); assert_throws_js( TypeError, () => new NDEFRecord(createRecord('Mime', test_buffer_data)), 'Unknown record type.'); assert_throws_js( TypeError, () => new NDEFRecord(createRecord('sMart-PosTER', test_url_data)), 'Unknown record type.'); }, 'NDEFRecord constructor with record type string being treated as case sensitive'); test(() => { assert_throws_js( TypeError, () => new NDEFRecord(createRecord('example.com:hellö', test_buffer_data)), 'The external type must be an ASCII string.'); // Length of the external type is 255, OK. const record = new NDEFRecord(createRecord( [...Array(251)].map(_ => 'a').join('') + ':xyz', test_buffer_data)); // Exceeding 255, Throws. assert_throws_js( TypeError, () => new NDEFRecord(createRecord( [...Array(252)].map(_ => 'a').join('') + ':xyz', test_buffer_data)), 'The external type should not be longer than 255.'); assert_throws_js( TypeError, () => new NDEFRecord(createRecord('xyz', test_buffer_data)), 'The external type must have a \':\'.'); assert_throws_js( TypeError, () => new NDEFRecord(createRecord(':xyz', test_buffer_data)), 'The domain should not be empty.'); assert_throws_js( TypeError, () => new NDEFRecord(createRecord('example.com:', test_buffer_data)), 'The type should not be empty.'); assert_throws_js( TypeError, () => new NDEFRecord(createRecord('example.com:xyz[', test_buffer_data)), 'The type should not contain \'[\'.'); assert_throws_js( TypeError, () => new NDEFRecord(createRecord('example.com:xyz~', test_buffer_data)), 'The type should not contain \'~\'.'); assert_throws_js( TypeError, () => new NDEFRecord(createRecord('example.com:xyz/', test_buffer_data)), 'The type should not contain \'/\'.'); }, 'NDEFRecord constructor with invalid external record type'); test(() => { const encoder = new TextEncoder(); const uri_record = createUrlRecord(test_url_data); const title_record = createTextRecord(test_text_data, 'utf-8', 'en'); const type_record = createRecord(':t', encoder.encode('image/gif')); const size_record = createRecord(':s', new Uint32Array([4096])); const action_record = createRecord(':act', new Uint8Array([0])); const icon_record = createRecord('mime', test_buffer_data, test_record_id, 'image/gif'); const invalid_data_list = [ { data: 'A string is not a NDEFMessageInit', message: 'A string is not allowed.' }, {data: test_buffer_data, message: 'An ArrayBuffer is not allowed.'}, { data: createMessage( [title_record, type_record, size_record, action_record, icon_record]), message: 'Must contain a URI record.' }, { data: createMessage([ uri_record, title_record, type_record, size_record, action_record, icon_record, uri_record ]), message: 'Must not contain more than one uri record.' }, { data: createMessage([ uri_record, title_record, type_record, size_record, action_record, icon_record, type_record ]), message: 'Must not contain more than one type record.' }, { data: createMessage([ uri_record, title_record, type_record, size_record, action_record, icon_record, size_record ]), message: 'Must not contain more than one size record.' }, { data: createMessage([ uri_record, title_record, type_record, size_record, action_record, icon_record, action_record ]), message: 'Must not contain more than one action record.' }, { data: createMessage([ uri_record, title_record, type_record, action_record, icon_record, createRecord(':s', new Uint8Array([1])) ]), message: 'Size record must have payload as 4-byte 32 bit unsigned integer.' }, { data: createMessage([ uri_record, title_record, type_record, size_record, icon_record, createRecord(':act', new Uint32Array([0])) ]), message: 'Action record must have payload as 1-byte 8 bit unsigned integer.' } ]; invalid_data_list.forEach(entry => { assert_throws_js( TypeError, () => new NDEFRecord(createRecord('smart-poster', entry.data)), entry.message); }); }, 'NDEFRecord constructor for smart-poster record with invalid embedded records.'); test(() => { assert_throws_js( TypeError, () => new NDEFRecord(createRecord(':xyz', test_buffer_data)), 'The local type record must be embedded in the payload of another record (smart-poster, external, or local)'); // The following test cases use an external type record embedding our target // local type record. const local_record = createRecord(':xyz', test_buffer_data); const payload_message = createMessage([local_record]); const external_record_embedding_local_record = createRecord('example.com:foo', payload_message); // OK. new NDEFRecord(external_record_embedding_local_record); local_record.recordType = ':xyZ123'; new NDEFRecord(external_record_embedding_local_record); local_record.recordType = ':123XYz'; new NDEFRecord(external_record_embedding_local_record); local_record.recordType = ':hellö'; assert_throws_js( TypeError, () => new NDEFRecord(external_record_embedding_local_record), 'The local type must be an ASCII string.'); // Length of the local type excluding the prefix ':' is 255, OK. local_record.recordType = ':' + [...Array(255)].map(_ => 'a').join(''); const record_255 = new NDEFRecord(external_record_embedding_local_record); // Exceeding 255, Throws. local_record.recordType = ':' + [...Array(256)].map(_ => 'a').join(''); assert_throws_js( TypeError, () => new NDEFRecord(external_record_embedding_local_record), 'The local type excluding the prefix \':\' should not be longer than 255.'); local_record.recordType = 'xyz'; assert_throws_js( TypeError, () => new NDEFRecord(external_record_embedding_local_record), 'The local type must start with a \':\'.'); local_record.recordType = ':Xyz'; assert_throws_js( TypeError, () => new NDEFRecord(external_record_embedding_local_record), 'The local type must have a lower case letter or digit following the prefix \':\'.'); local_record.recordType = ':-xyz'; assert_throws_js( TypeError, () => new NDEFRecord(external_record_embedding_local_record), 'The local type must have a lower case letter or digit following the prefix \':\'.'); }, 'NDEFRecord constructor with various local record types');