VulpesOS/gaia/apps/system/test/unit/nfc_utils_test.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

942 lines
33 KiB
JavaScript
Executable file

'use strict';
/* globals MocksHelper, MozNDEFRecord, NDEF,
NfcUtils, NDEFUtils, NfcBuffer */
require('/shared/test/unit/mocks/mock_moz_ndefrecord.js');
require('/shared/js/nfc_utils.js');
requireApp('system/js/ndef_utils.js');
var mocksForNfcUtils = new MocksHelper([
'MozNDEFRecord'
]).init();
suite('NFC Utils', function() {
function assertArraysEqual(test, expected, msg) {
for (var i = 0; i < test.length; i++ ) {
assert.equal(test[i], expected[i],
(msg || 'Arrays not equal') + ' at position: ' + i);
}
}
mocksForNfcUtils.attachTestHelpers();
var nfcUtils;
var string1;
var uint8array1;
var urlU8a; // Uint8Array
setup(function() {
nfcUtils = new NfcUtils();
string1 = 'StringTestString ABCDEFGHIJKLMNOPQRSTUVWXYZ ą嗨è©';
uint8array1 = new Uint8Array([0x53, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x54, 0x65, 0x73, 0x74,
0x53, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x20,
0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52,
0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5a,
0x20,
0xC4, 0x85, 0xE5, 0x97, 0xA8, 0xC3,
0xA8, 0xC2, 0xA9]);
// SR = 1, TNF = 0x01 (NFC Forum Well Known Type),
// One record only: ME=1, MB=1
urlU8a = new Uint8Array([0xd1, // TNF and header
0x01, // Record type length
0x0c, // payload length
0x55, // 'U', NDEF.RTD_URI type
0x03, // NDEF.URIS[0x03] = 'http://';
0x6d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c, 0x61,
0x2e,
0x6f, 0x72, 0x67]); // mozilla.org
});
test('transitive', function() {
var u8a = nfcUtils.fromUTF8(string1);
var str = nfcUtils.toUTF8(uint8array1);
var backStr = nfcUtils.toUTF8(u8a);
var backU8a = nfcUtils.fromUTF8(str);
var nullObj = nfcUtils.toUTF8(null);
var nullStr = nfcUtils.fromUTF8(null);
var u1 = nfcUtils.equalArrays(u8a, uint8array1);
var s1 = nfcUtils.equalArrays(str, string1);
var bs1 = nfcUtils.equalArrays(string1, backStr);
var bs2 = nfcUtils.equalArrays(str, backStr);
var bu1 = nfcUtils.equalArrays(u8a, backU8a);
var bu2 = nfcUtils.equalArrays(uint8array1, backU8a);
assert.equal(u1, true);
assert.equal(s1, true);
assert.equal(bs1, true);
assert.equal(bs2, true);
assert.equal(bu1, true);
assert.equal(bu2, true);
assert.equal(nullObj, null);
assert.equal(nullStr, null);
});
suite('equalArrays', function() {
test('Accepts null arguments', function() {
assert.isFalse(nfcUtils.equalArrays());
assert.isFalse(nfcUtils.equalArrays(null));
assert.isFalse(nfcUtils.equalArrays(null, null));
assert.isFalse(nfcUtils.equalArrays(undefined, null));
});
test('Accepts non-array objects', function() {
assert.isFalse(nfcUtils.equalArrays({}, [1]));
});
test('Compares arrays', function() {
assert.isFalse(nfcUtils.equalArrays([1, 2, 3], [1, 3, 2]));
assert.isFalse(nfcUtils.equalArrays([1, 2, 3], [1]));
assert.isTrue(nfcUtils.equalArrays([1, 2, 3], [1, 2, 3]));
assert.isTrue(nfcUtils.equalArrays([1, 2, 3], new Uint8Array([1, 2, 3])));
});
});
suite('String <> byte conversions', function() {
var cprght = '©';
var cprghtUTF16BE = new Uint8Array([0x00, 0xA9]);
var cprghtUTF16BEBOM = new Uint8Array([0xFE, 0xFF, 0x00, 0xA9]);
var cprghtUTF16LEBOM = new Uint8Array([0xFF, 0xFE, 0xA9, 0x00]);
var plchr = 'ą';
var plchrUTF16BE = new Uint8Array([0x01, 0x05]);
var hai = '嗨';
var haiUTF16BE = new Uint8Array([0x55, 0xE8]);
var foxTxt = 'Big brown fox! 大棕狐狸?';
var foxTxtUTF16BE = new Uint8Array(
[0x00, 0x42, 0x00, 0x69, 0x00, 0x67, 0x00, 0x20, 0x00, 0x62, 0x00, 0x72,
0x00, 0x6F, 0x00, 0x77, 0x00, 0x6E, 0x00, 0x20, 0x00, 0x66, 0x00, 0x6F,
0x00, 0x78, 0x00, 0x21, 0x00, 0x20, 0x59, 0x27, 0x68, 0xD5, 0x72, 0xD0,
0x72, 0xF8, 0x00, 0x3F]
);
test('UTF16BytesToStr', function() {
assert.equal(cprght, nfcUtils.UTF16BytesToStr(cprghtUTF16BE));
assert.equal(cprght, nfcUtils.UTF16BytesToStr(cprghtUTF16BEBOM));
assert.equal(cprght, nfcUtils.UTF16BytesToStr(cprghtUTF16LEBOM));
assert.equal(plchr, nfcUtils.UTF16BytesToStr(plchrUTF16BE));
assert.equal(hai, nfcUtils.UTF16BytesToStr(haiUTF16BE));
assert.equal(foxTxt, nfcUtils.UTF16BytesToStr(foxTxtUTF16BE));
});
test('strToUTF16Bytes', function() {
assert.deepEqual(cprghtUTF16BE, nfcUtils.strToUTF16Bytes(cprght));
assert.deepEqual(plchrUTF16BE, nfcUtils.strToUTF16Bytes(plchr));
assert.deepEqual(haiUTF16BE, nfcUtils.strToUTF16Bytes(hai));
assert.deepEqual(foxTxtUTF16BE, nfcUtils.strToUTF16Bytes(foxTxt));
});
});
suite('encodeNDEF', function() {
var urlNDEF; // MozNDEFRecord
setup(function() {
var tnf = NDEF.TNF_WELL_KNOWN;
var type = NDEF.RTD_URI;
// Short Record, 0x3 or "http://"
var payload = new Uint8Array(nfcUtils.fromUTF8(
'\u0003mozilla.org'));
urlNDEF = new MozNDEFRecord({tnf: tnf, type: type, payload: payload});
});
test('Subrecord', function() {
var encodedNdefU8a = nfcUtils.encodeNDEF([urlNDEF]);
// MozNDEFRecord is abstract, and does not contain some extra bits in the
// header for NDEF payload subrecords:
var cpUrlU8a = new Uint8Array(encodedNdefU8a);
cpUrlU8a[0] = cpUrlU8a[0] & NDEF.TNF;
var equals1 = nfcUtils.equalArrays(encodedNdefU8a, urlU8a);
assert.equal(equals1, true);
});
test('Short record', function() {
var ndefMsg = new Uint8Array([0xd1, 0x01, 0x11, 0x55, 0x04, 0x77, 0x69,
0x6b, 0x69, 0x2e, 0x6d, 0x6f, 0x7a, 0x69,
0x6c, 0x6c, 0x61, 0x2e, 0x6f, 0x72, 0x67]);
// "wiki.mozilla.org" with abbreviation "https://" (0x04)
var payload = new Uint8Array([0x04, 0x77, 0x69, 0x6b, 0x69, 0x2e, 0x6d,
0x6f, 0x7a, 0x69, 0x6c, 0x6c, 0x61, 0x2e,
0x6f, 0x72, 0x67]);
var ndefRecord = new MozNDEFRecord({tnf: NDEF.TNF_WELL_KNOWN,
type: NDEF.RTD_URI,
payload: payload});
var result = nfcUtils.encodeNDEF([ndefRecord]);
assertArraysEqual(result, ndefMsg);
});
test('Long record', function() {
// Excerpt from Wikipedia about Mozilla Foundation, UTF-16 encoded.
var payloadArray =
[0x85, 0x70, 0x6c, 0x5f, 0x70, 0x6c, 0xff, 0xfe, 0x46, 0x00, 0x75,
0x00, 0x6e, 0x00, 0x64, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6a, 0x00,
0x61, 0x00, 0x20, 0x00, 0x4d, 0x00, 0x6f, 0x00, 0x7a, 0x00, 0x69,
0x00, 0x6c, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x20, 0x00, 0x28, 0x00,
0x61, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x2e, 0x00, 0x20, 0x00, 0x54,
0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x4d, 0x00, 0x6f, 0x00,
0x7a, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x20,
0x00, 0x46, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00,
0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x29,
0x00, 0x20, 0x00, 0x13, 0x20, 0x20, 0x00, 0x6f, 0x00, 0x72, 0x00,
0x67, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x7a, 0x00, 0x61,
0x00, 0x63, 0x00, 0x6a, 0x00, 0x61, 0x00, 0x20, 0x00, 0x6e, 0x00,
0x6f, 0x00, 0x6e, 0x00, 0x2d, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f,
0x00, 0x66, 0x00, 0x69, 0x00, 0x74, 0x00, 0x20, 0x00, 0x70, 0x00,
0x6f, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x42, 0x01, 0x61, 0x00, 0x6e,
0x00, 0x61, 0x00, 0x20, 0x00, 0x31, 0x00, 0x35, 0x00, 0x20, 0x00,
0x6c, 0x00, 0x69, 0x00, 0x70, 0x00, 0x63, 0x00, 0x61, 0x00, 0x20,
0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x2c, 0x00,
0x20, 0x00, 0x6b, 0x00, 0x74, 0x00, 0xf3, 0x00, 0x72, 0x00, 0x65,
0x00, 0x6a, 0x00, 0x20, 0x00, 0x63, 0x00, 0x65, 0x00, 0x6c, 0x00,
0x65, 0x00, 0x6d, 0x00, 0x20, 0x00, 0x6a, 0x00, 0x65, 0x00, 0x73,
0x00, 0x74, 0x00, 0x20, 0x00, 0x7a, 0x00, 0x61, 0x00, 0x70, 0x00,
0x65, 0x00, 0x77, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x65, 0x00, 0x6e,
0x00, 0x69, 0x00, 0x65, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x72, 0x00,
0x67, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x7a, 0x00, 0x61,
0x00, 0x63, 0x00, 0x79, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x65, 0x00,
0x67, 0x00, 0x6f, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x70, 0x00, 0x72,
0x00, 0x61, 0x00, 0x77, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x67, 0x00,
0x6f, 0x00, 0x20, 0x00, 0x69, 0x00, 0x20, 0x00, 0x66, 0x00, 0x69,
0x00, 0x6e, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x6f, 0x00,
0x77, 0x00, 0x65, 0x00, 0x67, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x77,
0x00, 0x73, 0x00, 0x70, 0x00, 0x61, 0x00, 0x72, 0x00, 0x63, 0x00,
0x69, 0x00, 0x61, 0x00, 0x20, 0x00, 0x64, 0x00, 0x6c, 0x00, 0x61,
0x00, 0x20, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x6a, 0x00,
0x65, 0x00, 0x6b, 0x00, 0x74, 0x00, 0x75, 0x00, 0x20, 0x00, 0x4d,
0x00, 0x6f, 0x00, 0x7a, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x6c, 0x00,
0x61, 0x00, 0x20, 0x00, 0x69, 0x00, 0x20, 0x00, 0x6a, 0x00, 0x65,
0x00, 0x67, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x70, 0x00, 0x6f, 0x00,
0x63, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x6e, 0x00, 0x79,
0x00, 0x63, 0x00, 0x68, 0x00, 0x2e, 0x00];
var ndefMsg = new Uint8Array([0xc1, 0x01, 0x00, 0x00, 0x01, 0xb4, 0x54]
.concat(payloadArray));
var payload = new Uint8Array(payloadArray);
var ndefRecord = new MozNDEFRecord({tnf: NDEF.TNF_WELL_KNOWN,
type: NDEF.RTD_TEXT,
payload: payload});
var result = nfcUtils.encodeNDEF([ndefRecord]);
assertArraysEqual(result, ndefMsg);
});
test('No type, no paylod, no id', function() {
var ndefMsg = new Uint8Array([0xd0, 0x00, 0x00]);
var result = nfcUtils.encodeNDEF([new MozNDEFRecord()]);
assertArraysEqual(result, ndefMsg);
});
test('Multiple records', function() {
var ndefMsg = new Uint8Array([0x91, 0x01, 0x11, 0x55, 0x04, 0x77, 0x69,
0x6b, 0x69, 0x2e, 0x6d, 0x6f, 0x7a, 0x69,
0x6c, 0x6c, 0x61, 0x2e, 0x6f, 0x72, 0x67,
0x51, 0x01, 0x0c, 0x55, 0x03, 0x6d, 0x6f,
0x7a, 0x69, 0x6c, 0x6c, 0x61, 0x2e, 0x6f,
0x72, 0x67]);
var payload1 = new Uint8Array([0x04, 0x77, 0x69, 0x6b, 0x69, 0x2e, 0x6d,
0x6f, 0x7a, 0x69, 0x6c, 0x6c, 0x61, 0x2e,
0x6f, 0x72, 0x67]);
var rec1 = new MozNDEFRecord({tnf: NDEF.TNF_WELL_KNOWN,
type: NDEF.RTD_URI,
payload: payload1});
var payload2 = new Uint8Array([0x03, 0x6d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c,
0x61, 0x2e, 0x6f, 0x72, 0x67]);
var rec2 = new MozNDEFRecord({tnf: NDEF.TNF_WELL_KNOWN,
type: NDEF.RTD_URI,
payload: payload2});
var result = nfcUtils.encodeNDEF([rec1, rec2]);
assertArraysEqual(result, ndefMsg);
});
});
suite('parseNDEF', function() {
var msg1;
setup(function() {
// These record payloads aren't valid, but that's fine because
// NDEF doesn't care about the payload.
msg1 = [
// TNF = 1; MB | SR bits; type=[0x55]; payload length = 3
0x91, 0x01, 0x03, 0x55, 0x03, 0x6D, 0x6F,
// TNF = 3; SR bit; type=[0xAA, 0xBB]; payload length = 5
0x13, 0x02, 0x05, 0xAA, 0xBB, 0x02, 0xAD, 0x8F, 0x71, 0x96,
// TNF = 4; ME | SR bits; type=[0x55]; payload length = 7
0x54, 0x01, 0x07, 0x55, 0xFF, 0x12, 0x9A, 0xA7, 0x11, 0xDC, 0xA8];
});
test('Subrecord', function() {
var buf = new NfcBuffer(urlU8a);
var ndefrecords = nfcUtils.parseNDEF(buf);
var equal;
// There is only one record here:
assert.equal(ndefrecords[0].tnf, NDEF.TNF_WELL_KNOWN);
equal = nfcUtils.equalArrays(ndefrecords[0].type, NDEF.RTD_URI);
assert.equal(equal, true, 'type');
equal = nfcUtils.equalArrays(ndefrecords[0].id, new Uint8Array());
assert.equal(equal, true, 'id');
equal = nfcUtils.equalArrays(ndefrecords[0].payload,
nfcUtils.fromUTF8('\u0003mozilla.org'));
assert.equal(equal, true, 'payload');
});
test('Returns null when first record has no MB bit set',
function() {
msg1[0] ^= 128;
var parsed = nfcUtils.parseNDEF(new NfcBuffer(new Uint8Array(msg1)));
assert.isNull(parsed);
});
test('Returns null when last record has no ME bit set',
function() {
msg1[17] ^= 64;
var parsed = nfcUtils.parseNDEF(new NfcBuffer(new Uint8Array(msg1)));
assert.isNull(parsed);
});
test('Returns null when not only last record has ME bit set',
function() {
msg1[7] ^= 64;
var parsed = nfcUtils.parseNDEF(new NfcBuffer(new Uint8Array(msg1)));
assert.isNull(parsed);
});
test('Returns null when no record has ME bit set', function() {
msg1[17] ^= 64;
var parsed = nfcUtils.parseNDEF(new NfcBuffer(new Uint8Array(msg1)));
assert.isNull(parsed);
});
test('Returns null if MB is set for record other than first',
function() {
msg1[7] ^= 128;
var parsed = nfcUtils.parseNDEF(new NfcBuffer(new Uint8Array(msg1)));
assert.isNull(parsed);
});
test('Returns null if CF is set (not supported)',
function() {
msg1[0] ^= 32;
var parsed = nfcUtils.parseNDEF(new NfcBuffer(new Uint8Array(msg1)));
assert.isNull(parsed);
});
test('Decodes multi record messages', function() {
var parsed = nfcUtils.parseNDEF(new NfcBuffer(new Uint8Array(msg1)));
assert.isNotNull(parsed);
assert.equal(parsed.length, 3);
assert.equal(parsed[0].tnf, NDEF.TNF_WELL_KNOWN);
assert.equal(parsed[1].tnf, NDEF.TNF_ABSOLUTE_URI);
assert.equal(parsed[2].tnf, NDEF.TNF_EXTERNAL_TYPE);
assertArraysEqual(parsed[0].type, [0x55], 'type 0');
assertArraysEqual(parsed[1].type, [0xAA, 0xBB], 'type 1');
assertArraysEqual(parsed[2].type, [0x55], 'type 2');
assertArraysEqual(parsed[0].payload,
[0x03, 0x6D, 0x6F], 'payload 0');
assertArraysEqual(parsed[1].payload,
[0x02, 0xAD, 0x8F, 0x71, 0x96], 'payload 1');
assertArraysEqual(parsed[2].payload,
[0xFF, 0x12, 0x9A, 0xA7, 0x11, 0xDC, 0xA8], 'payload 2');
});
});
suite('parseNDEFRecord', function() {
var record;
setup(function() {
// URI record, "http://mozilla.com"
record = [0xD1, 0x01, 0x0C, 0x55,
0x03, 0x6D, 0x6F, 0x7A,
0x69, 0x6C, 0x6C, 0x61,
0x2E, 0x6F, 0x72, 0x67];
});
test('Supports records with ID', function() {
// URI record, "http://mozilla.com"
// id = [0x01, 0x02, 0x03, 0x04]
var record = [0xD9, 0x01, 0x0C, 0x04,
0x55, 0x01, 0x02, 0x03,
0x04, 0x03, 0x6D, 0x6F,
0x7A, 0x69, 0x6C, 0x6C,
0x61, 0x2E, 0x6F, 0x72,
0x67];
var parsed = nfcUtils._parseNDEFRecord(new NfcBuffer(record));
assert.isNotNull(parsed);
assertArraysEqual(parsed.id, [0x01, 0x02, 0x03, 0x04]);
});
test('Supports records without ID', function() {
var parsed = nfcUtils._parseNDEFRecord(new NfcBuffer(record));
assert.isNotNull(parsed);
assertArraysEqual(parsed.id, []);
});
test('Supports long records (no SR bit set)', function() {
var record = [0xC1, 0x01, 0x00, 0x00, 0x01, 0x1C, 0x55];
var payload = new Array(284);
for (var i = 0; i < payload.length; i += 1) {
payload[i] = 7;
}
record = record.concat(payload);
var parsed = nfcUtils._parseNDEFRecord(new NfcBuffer(record));
assert.isNotNull(parsed);
assertArraysEqual(parsed.payload, payload);
});
test('Decodes TNF', function() {
var parsed = nfcUtils._parseNDEFRecord(new NfcBuffer(record));
assert.isNotNull(parsed);
assert.equal(parsed.tnf, NDEF.TNF_WELL_KNOWN);
record[0] ^= 1;
record[0] |= 5;
parsed = nfcUtils._parseNDEFRecord(new NfcBuffer(record));
assert.isNotNull(parsed);
assert.equal(parsed.tnf, NDEF.TNF_UNKNOWN);
});
test('Decodes type', function() {
var parsed = nfcUtils._parseNDEFRecord(new NfcBuffer(record));
assert.isNotNull(parsed);
assertArraysEqual(parsed.type, [0x55]);
});
test('Decodes payload', function() {
var parsed = nfcUtils._parseNDEFRecord(new NfcBuffer(record));
assert.isNotNull(parsed);
assertArraysEqual(parsed.payload, [0x03, 0x6D, 0x6F, 0x7A, 0x69,
0x6C, 0x6C, 0x61, 0x2E, 0x6F,
0x72, 0x67]);
});
});
suite('NDEFUtils', function() {
test('Encode and Parse Handover Request', function() {
var mac = '01:02:03:04:05:06';
var cps = 0x2;
var hrNDEFs1 = NDEFUtils.encodeHandoverRequest(mac, cps);
assert.equal(!!hrNDEFs1, true);
var hrNDEFU8a1 = nfcUtils.encodeNDEF(hrNDEFs1);
assert.equal(!!hrNDEFU8a1, true);
var buf = new NfcBuffer(hrNDEFU8a1);
var hrNDEFs2 = nfcUtils.parseNDEF(buf);
assert.equal(!!hrNDEFs2, true);
var hrNDEFU8a2 = nfcUtils.encodeNDEF(hrNDEFs2);
assert.equal(!!hrNDEFU8a2, true);
var equal1 = nfcUtils.equalArrays(hrNDEFU8a2, hrNDEFU8a1);
assert.equal(equal1, true);
});
test('Encode and Parse Handover Select', function() {
var mac = '01:02:03:04:05:06';
var cps = 0x2;
var hsNDEFs1 = NDEFUtils.encodeHandoverSelect(mac, cps);
assert.equal(!!hsNDEFs1, true);
var hsNDEFU8a1 = nfcUtils.encodeNDEF(hsNDEFs1);
assert.equal(!!hsNDEFU8a1, true);
var buf = new NfcBuffer(hsNDEFU8a1);
var hsNDEFs2 = nfcUtils.parseNDEF(buf);
assert.equal(!!hsNDEFs2, true);
var hsNDEFU8a2 = nfcUtils.encodeNDEF(hsNDEFs2);
assert.equal(!!hsNDEFU8a2, true);
var equal1 = nfcUtils.equalArrays(hsNDEFU8a2, hsNDEFU8a1);
assert.equal(equal1, true);
});
});
});
suite('NfcBuffer', function() {
var arr;
var buf;
function assertArraysEqual(test, expected, msg) {
for (var i = 0; i < test.length; i++ ) {
assert.equal(test[i], expected[i],
(msg || 'Arrays not equal') + ' at position: ' + i);
}
}
setup(function() {
arr = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
buf = new NfcBuffer(arr);
});
test('getOctet()', function() {
assert.equal(0, buf.getOctet());
assert.equal(1, buf.getOctet());
assert.equal(2, buf.getOctet());
assert.equal(3, buf.getOctet());
});
test('getOctetArray()', function() {
assertArraysEqual([0, 1, 2], buf.getOctetArray(3));
assertArraysEqual([3], buf.getOctetArray(1));
assertArraysEqual([4, 5, 6, 7], buf.getOctetArray(4));
assertArraysEqual([], buf.getOctetArray(0));
});
test('skip()', function() {
buf.skip(2);
assert.equal(2, buf.getOctet());
buf.skip(3);
assert.equal(6, buf.getOctet());
buf.skip(0);
assert.equal(7, buf.getOctet());
});
test('peek()', function() {
assert.equal(buf.peek(), 0);
assert.equal(buf.peek(), 0);
assert.equal(buf.peek(), 0);
buf.getOctet();
assert.equal(buf.peek(), 1);
assert.equal(buf.peek(), 1);
});
test('len is required', function() {
assert.throws(function() {
buf.getOctetArray();
});
assert.throws(function() {
buf.skip();
});
});
test('getOctetArray() throws when len is negative', function() {
assert.throws(function() {
buf.getOctetArray(-1);
}, Error);
});
test('skip() throws when len is negative', function() {
assert.throws(function() {
buf.skip(-1);
});
});
test('getOctetArray() throws on attempt to read too many octets',
function() {
assert.throws(function() {
buf.getOctetArray(arr.length + 1);
}, Error);
});
test('getOctet() throws on attempt to read after last octet', function() {
buf.skip(arr.length);
assert.throws(function() {
buf.getOctet();
}, Error);
});
test('skip() throws when after last octet', function() {
buf.skip(arr.length);
assert.throws(function() {
buf.skip(1);
}, Error);
});
});
suite('NDEF.payload', function() {
var nfcUtils;
mocksForNfcUtils.attachTestHelpers();
setup(function() {
nfcUtils = new NfcUtils();
});
suite('decode()', function() {
test('TNF empty', function() {
var decoded = NDEF.payload.decode(NDEF.TNF_EMPTY, undefined, undefined);
assert.deepEqual(decoded, {type: 'empty'});
});
test('TNF well known', function() {
var stubDecodeWellKnown = this.sinon.stub(NDEF.payload,
'decodeWellKnown');
NDEF.payload.decode(NDEF.TNF_WELL_KNOWN, NDEF.RTD_URI, 'payload');
assert.isTrue(stubDecodeWellKnown.withArgs(NDEF.RTD_URI, 'payload')
.calledOnce);
});
test('TNF mime media-type', function() {
// text/plain
var type = new Uint8Array([0x74, 0x65, 0x78, 0x74,
0x2F, 0x70, 0x6C, 0x61,
0x69, 0x6E]);
// What up?!
var payload = new Uint8Array([0x57, 0x68, 0x61, 0x74,
0x20, 0x75, 0x70, 0x3F,
0x21]);
var stubDecodeMIME = this.sinon.stub(NDEF.payload, 'decodeMIME');
NDEF.payload.decode(NDEF.TNF_MIME_MEDIA, type, payload);
assert.isTrue(stubDecodeMIME.withArgs(type, payload).calledOnce);
});
test('TNF absolute uri', function() {
// TNF_ABSOLUTE_URI has uri in the type
var type = new Uint8Array([0x68, 0x74, 0x74, 0x70,
0x3A, 0x2F, 0x2F, 0x6D,
0x6F, 0x7A, 0x69, 0x6C,
0x6C, 0x61, 0x2E, 0x6F,
0x72, 0x67]);
var decoded = NDEF.payload.decode(NDEF.TNF_ABSOLUTE_URI, type, null);
assert.deepEqual(decoded, { type: 'http://mozilla.org' });
});
test('TNF external type', function() {
var type = new Uint8Array([0x6D, 0x6F, 0x7A, 0x69,
0x6C, 0x6C, 0x61, 0x2E,
0x6F, 0x72, 0x67, 0x3A,
0x62, 0x75, 0x67]);
var decoded = NDEF.payload.decode(NDEF.TNF_EXTERNAL_TYPE, type, null);
assert.deepEqual(decoded, { type: 'mozilla.org:bug' });
});
test('TNF unknown', function() {
var decUnknown = NDEF.payload.decode(NDEF.TNF_UNKNOWN, null, 'data1');
assert.deepEqual(decUnknown, {}, 'should be empty initialized object');
});
test('TNF unchanged', function() {
var decoded = NDEF.payload.decode(NDEF.TNF_UNCHANGED, null, 'data');
assert.equal(decoded, null);
});
}),
suite('decodeWellKnown()', function() {
test('RTD Text', function() {
var stubDecodeText = this.sinon.stub(NDEF.payload, 'decodeText');
NDEF.payload.decodeWellKnown(NDEF.RTD_TEXT, 'payload');
assert.isTrue(stubDecodeText.withArgs('payload').calledOnce);
});
test('RTD URI', function() {
var stubDecodeURI = this.sinon.stub(NDEF.payload, 'decodeURI');
NDEF.payload.decodeWellKnown(NDEF.RTD_URI, 'payload');
assert.isTrue(stubDecodeURI.withArgs('payload').calledOnce);
});
test('RTD Smart Poster', function() {
var stubDecodeSmartPoster = this.sinon.stub(NDEF.payload,
'decodeSmartPoster');
NDEF.payload.decodeWellKnown(NDEF.RTD_SMART_POSTER, 'payload');
assert.isTrue(stubDecodeSmartPoster.withArgs('payload').calledOnce);
});
test('Wrong RTD', function() {
var decoded = NDEF.payload.decodeWellKnown('fake', 'payload');
assert.equal(decoded, null);
});
}),
suite('decodeText()', function() {
test('UTF-8 en', function() {
var payload = new Uint8Array([0x02, 0x65, 0x6E, 0x48,
0x65, 0x79, 0x21, 0x20,
0x55, 0x54, 0x46, 0x2D,
0x38, 0x20, 0x65, 0x6E]);
var decoded = NDEF.payload.decodeText(payload);
assert.deepEqual(decoded, {
type: 'text',
text: 'Hey! UTF-8 en',
language: 'en',
encoding: 'UTF-8'
});
});
test('UTF-16 en', function() {
var payload = new Uint8Array([0x82, 0x65, 0x6E, 0xFF,
0xFE, 0x48, 0x00, 0x6F,
0x00, 0x21, 0x00, 0x20,
0x00, 0x55, 0x00, 0x54,
0x00, 0x46, 0x00, 0x2D,
0x00, 0x31, 0x00, 0x36,
0x00, 0x20, 0x00, 0x65,
0x00, 0x6E, 0x00]);
var decoded = NDEF.payload.decodeText(payload);
assert.deepEqual(decoded, {
type: 'text',
text: 'Ho! UTF-16 en',
language: 'en',
encoding: 'UTF-16'
});
});
}),
suite('decodeURI()', function() {
test('https://', function() {
var payload = new Uint8Array([0x04, 0x77, 0x69, 0x6B,
0x69, 0x2E, 0x6D, 0x6F,
0x7A, 0x69, 0x6C, 0x6C,
0x61, 0x2E, 0x6F, 0x72,
0x67]);
var decoded = NDEF.payload.decodeURI(payload);
assert.deepEqual(decoded, {
type: 'uri',
uri: 'https://wiki.mozilla.org'
});
});
test('mailto:', function() {
// \u0006 is short for mailto:
var payload = nfcUtils.fromUTF8('\u0006jorge@borges.ar');
var decoded = NDEF.payload.decodeURI(payload);
assert.deepEqual(decoded, {
type: 'uri',
uri: 'mailto:jorge@borges.ar'
});
});
test('tel:', function() {
// \u0005 is short for tel:
var payload = nfcUtils.fromUTF8('\u00050054267437');
var decoded = NDEF.payload.decodeURI(payload);
assert.deepEqual(decoded, {
type: 'uri',
uri: 'tel:0054267437'
});
});
test('unabbreviated', function() {
var payload = nfcUtils.fromUTF8('\u0000http://mozilla.com');
var decoded = NDEF.payload.decodeURI(payload);
assert.deepEqual(decoded, {
type: 'uri',
uri: 'http://mozilla.com'
});
});
}),
suite('decodeSmartPoster()', function() {
/**
* Constructs a NDEF message with one record. This record
* is a smart poster, so it contains multiple subrecords
* in the payload, encoded as bytes.
*
* Accepts 3*N arguments, where:
* 3*(N + 0) argument - TNF of the record
* 3*(N + 1) argument - type given as string
* 3*(N + 2) argument - payload, given as Uint8Array
*/
var makePoster = function() {
var records = [];
for (var arg = 0; arg < arguments.length; arg += 3) {
records.push({
tnf: arguments[arg],
type: nfcUtils.fromUTF8(arguments[arg + 1]),
payload: arguments[arg + 2],
id: new Uint8Array()
});
}
var payload = nfcUtils.encodeNDEF(records);
return [{
tnf: NDEF.TNF_WELL_KNOWN,
type: NDEF.RTD_SMART_POSTER,
id: new Uint8Array(),
payload: new Uint8Array(payload)
}];
};
var recordURI,
recordTextEnglish,
recordTextPolish,
recordAction,
recordIcon;
setup(function() {
// URI "http://www.youtube.com" with abbreviation
// for "http://www."
recordURI = [
0x01, // Payload: abbreviation
0x79, 0x6F, 0x75, 0x74, // 'yout'
0x75, 0x62, 0x65, 0x2E, // 'ube.'
0x63, 0x6F, 0x6D // 'com'
];
// Text record with contents: "Best page ever! q#@"
// and language code: "en"
recordTextEnglish = [
0x02, // Status byte: UTF-8, two byte lang code
0x65, 0x6E, // ISO language code: 'en'
0x42, 0x65, 0x73, 0x74, // 'Best'
0x20, 0x70, 0x61, 0x67, // ' pag'
0x65, 0x20, 0x65, 0x76, // 'e ev'
0x65, 0x72, 0x21, 0x20, // 'er! '
0x20, 0x71, 0x23, 0x40 // ' q#@'
];
// Text record with contents: "ąćńó"
// and language code: "pl"
recordTextPolish = [
0x02, // Status byte: UTF-8, two byte lang code
0x70, 0x6C, // ISO language code: 'pl'
0xC4, 0x85, 0xC4, 0x87, // 'ąć'
0xC5, 0x84, 0xC3, 0xB3 // 'ńó'
];
// Action record with action = 0
recordAction = [0x00];
// Icon record with simple 4x4 PNG image.
recordIcon = [
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04,
0x08, 0x02, 0x00, 0x00, 0x00, 0x26, 0x93, 0x09,
0x29, 0x00, 0x00, 0x00, 0x1b, 0x49, 0x44, 0x41,
0x54, 0x08, 0xd7, 0x63, 0xf8, 0xff, 0xff, 0x3f,
0x03, 0x0c, 0x30, 0xc2, 0x39, 0x8c, 0x8c, 0x8c,
0x4c, 0x10, 0x0a, 0x2a, 0x85, 0xac, 0x0c, 0x00,
0x26, 0x0b, 0x09, 0x01, 0xc3, 0xd1, 0x9a, 0x7b,
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44,
0xae, 0x42, 0x60, 0x82
];
});
test('Decodes simple poster', function() {
var poster = makePoster(NDEF.TNF_WELL_KNOWN, 'U', recordURI);
var decoded = NDEF.payload.decodeSmartPoster(poster[0].payload);
assert.equal(decoded.type, 'smartposter');
assert.equal(decoded.uri, 'http://www.youtube.com');
});
test('Decodes extra records', function() {
var poster = makePoster(NDEF.TNF_WELL_KNOWN, 'U', recordURI,
NDEF.TNF_WELL_KNOWN, 'T', recordTextEnglish,
NDEF.TNF_WELL_KNOWN, 'T', recordTextPolish,
NDEF.TNF_WELL_KNOWN, 'act', recordAction,
NDEF.TNF_MIME_MEDIA, 'image/png', recordIcon);
var decoded = NDEF.payload.decodeSmartPoster(poster[0].payload);
assert.equal(decoded.uri, 'http://www.youtube.com');
assert.equal(decoded.text.en, 'Best page ever! q#@');
assert.equal(decoded.text.pl, 'ąćńó');
assert.equal(decoded.icons.length, 1);
assert.equal(decoded.icons[0].type, 'image/png');
assert.isTrue(nfcUtils.equalArrays(decoded.icons[0].bytes,
recordIcon));
});
test('Does not handle poster with multiple URIs', function() {
var poster = makePoster(NDEF.TNF_WELL_KNOWN, 'U', recordURI,
NDEF.TNF_WELL_KNOWN, 'U', recordURI);
var decoded = NDEF.payload.decodeSmartPoster(poster[0].payload);
assert.equal(decoded, null);
});
test('Does not handle poster with no URI', function() {
var poster = makePoster(NDEF.TNF_WELL_KNOWN, 'T', recordTextEnglish,
NDEF.TNF_WELL_KNOWN, 'act', recordAction);
var decoded = NDEF.payload.decodeSmartPoster(poster[0].payload);
assert.equal(decoded, null);
});
test('Does not handle poster with multiple title ' +
'records in the same language', function() {
var poster = makePoster(NDEF.TNF_WELL_KNOWN, 'U', recordURI,
NDEF.TNF_WELL_KNOWN, 'T', recordTextEnglish,
NDEF.TNF_WELL_KNOWN, 'T', recordTextEnglish);
var decoded = NDEF.payload.decodeSmartPoster(poster[0].payload);
assert.equal(decoded, null);
});
});
suite('decodeMIME()', function() {
suite('vcards', function() {
var vcardTxt = 'BEGIN:VCARD\nVERSION:2.1\nN:J;\nEND:VCARD';
var payload;
var decoded;
setup(function() {
payload = nfcUtils.fromUTF8(vcardTxt);
decoded = {
type: 'text/vcard',
blob: new Blob([vcardTxt], {type: 'text/vcard'})
};
});
test('text/vcard', function() {
var vcard = NDEF.payload.decodeMIME(nfcUtils.fromUTF8('text/vcard'),
payload);
assert.deepEqual(decoded, vcard);
});
test('text/x-vCard', function() {
var vcard = NDEF.payload.decodeMIME(nfcUtils.fromUTF8('text/x-vCard'),
payload);
assert.deepEqual(decoded, vcard);
});
test('text/x-vcard', function() {
var vcard = NDEF.payload.decodeMIME(nfcUtils.fromUTF8('text/x-vcard'),
payload);
assert.deepEqual(decoded, vcard);
});
});
test('other', function() {
var decoded = NDEF.payload.decodeMIME(nfcUtils.fromUTF8('text/plain'),
'payload');
assert.deepEqual(decoded, { type: 'text/plain' });
});
});
});