summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/web-nfc/resources/nfc-helpers.js
blob: 10229f0b06d968cc82033ef5ad6bda44d449365f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
'use strict';
// These tests rely on the User Agent providing an implementation of
// platform nfc backends.
//
// In Chromium-based browsers this implementation is provided by a polyfill
// in order to reduce the amount of test-only code shipped to users. To enable
// these tests the browser must be run with these options:
//
//   --enable-blink-features=MojoJS,MojoJSTest

async function loadChromiumResources() {
  await loadScript('/resources/testdriver.js');
  await loadScript('/resources/testdriver-vendor.js');
  await import('/resources/chromium/nfc-mock.js');
}

async function initialize_nfc_tests() {
  if (typeof WebNFCTest === 'undefined') {
    const script = document.createElement('script');
    script.src = '/resources/test-only-api.js';
    script.async = false;
    const p = new Promise((resolve, reject) => {
      script.onload = () => { resolve(); };
      script.onerror = e => { reject(e); };
    })
    document.head.appendChild(script);
    await p;

    if (isChromiumBased) {
      await loadChromiumResources();
    }
  }
  assert_implements( WebNFCTest, 'WebNFC testing interface is unavailable.');
  let NFCTest = new WebNFCTest();
  await NFCTest.initialize();
  return NFCTest;
}

function nfc_test(func, name, properties) {
  promise_test(async t => {
    let NFCTest = await initialize_nfc_tests();
    t.add_cleanup(async () => {
      await NFCTest.reset();
    });
    await func(t, NFCTest.getMockNFC());
  }, name, properties);
}

const test_text_data = 'Test text data.';
const test_text_byte_array = new TextEncoder().encode(test_text_data);
const test_number_data = 42;
const test_json_data = {level: 1, score: 100, label: 'Game'};
const test_url_data = 'https://w3c.github.io/web-nfc/';
const test_message_origin = 'https://127.0.0.1:8443';
const test_buffer_data = new ArrayBuffer(test_text_byte_array.length);
const test_buffer_view = new Uint8Array(test_buffer_data);
test_buffer_view.set(test_text_byte_array);
const fake_tag_serial_number = 'c0:45:00:02';
const test_record_id = '/test_path/test_id';

const NFCHWStatus = {};
// OS-level NFC setting is ON
NFCHWStatus.ENABLED = 1;
// no NFC chip
NFCHWStatus.NOT_SUPPORTED = NFCHWStatus.ENABLED + 1;
// OS-level NFC setting OFF
NFCHWStatus.DISABLED = NFCHWStatus.NOT_SUPPORTED + 1;

function encodeTextToArrayBuffer(string, encoding) {
  // Only support 'utf-8', 'utf-16', 'utf-16be', and 'utf-16le'.
  assert_true(
      encoding === 'utf-8' || encoding === 'utf-16' ||
      encoding === 'utf-16be' || encoding === 'utf-16le');

  if (encoding === 'utf-8') {
    return new TextEncoder().encode(string).buffer;
  }

  if (encoding === 'utf-16') {
    let uint16array = new Uint16Array(string.length);
    for (let i = 0; i < string.length; i++) {
      uint16array[i] = string.codePointAt(i);
    }
    return uint16array.buffer;
  }

  const littleEndian = encoding === 'utf-16le';
  const buffer = new ArrayBuffer(string.length * 2);
  const view = new DataView(buffer);
  for (let i = 0; i < string.length; i++) {
    view.setUint16(i * 2, string.codePointAt(i), littleEndian);
  }
  return buffer;
}

function createMessage(records) {
  if (records !== undefined) {
    let message = {};
    message.records = records;
    return message;
  }
}

function createRecord(recordType, data, id, mediaType, encoding, lang) {
  let record = {};
  if (recordType !== undefined)
    record.recordType = recordType;
  if (id !== undefined)
    record.id = id;
  if (mediaType !== undefined)
    record.mediaType = mediaType;
  if (encoding !== undefined)
    record.encoding = encoding;
  if (lang !== undefined)
    record.lang = lang;
  if (data !== undefined)
    record.data = data;
  return record;
}

function createTextRecord(data, encoding, lang) {
  return createRecord('text', data, test_record_id, undefined, encoding, lang);
}

function createMimeRecordFromJson(json) {
  return createRecord(
      'mime', new TextEncoder().encode(JSON.stringify(json)),
      test_record_id, 'application/json');
}

function createMimeRecord(buffer) {
  return createRecord(
      'mime', buffer, test_record_id, 'application/octet-stream');
}

function createUnknownRecord(buffer) {
  return createRecord('unknown', buffer, test_record_id);
}

function createUrlRecord(url, isAbsUrl) {
  if (isAbsUrl) {
    return createRecord('absolute-url', url, test_record_id);
  }
  return createRecord('url', url, test_record_id);
}

// Compares NDEFMessageSource that was provided to the API
// (e.g. NDEFReader.write), and NDEFMessage that was received by the
// mock NFC service.
function assertNDEFMessagesEqual(providedMessage, receivedMessage) {
  // If simple data type is passed, e.g. String or ArrayBuffer or
  // ArrayBufferView, convert it to NDEFMessage before comparing.
  // https://w3c.github.io/web-nfc/#dom-ndefmessagesource
  let provided = providedMessage;
  if (providedMessage instanceof ArrayBuffer ||
      ArrayBuffer.isView(providedMessage))
    provided = createMessage([createRecord(
        'mime', providedMessage, undefined /* id */,
        'application/octet-stream')]);
  else if (typeof providedMessage === 'string')
    provided = createMessage([createRecord('text', providedMessage)]);

  assert_equals(provided.records.length, receivedMessage.data.length,
      'NDEFMessages must have same number of NDEFRecords');

  // Compare contents of each individual NDEFRecord
  for (let i = 0; i < provided.records.length; ++i)
    compareNDEFRecords(provided.records[i], receivedMessage.data[i]);
}

// Used to compare two NDEFMessage, one that is received from
// NDEFReader.onreading() EventHandler and another that is provided to mock NFC
// service.
function assertWebNDEFMessagesEqual(message, expectedMessage) {
  assert_equals(message.records.length, expectedMessage.records.length);

  for(let i in message.records) {
    let record = message.records[i];
    let expectedRecord = expectedMessage.records[i];
    assert_equals(record.recordType, expectedRecord.recordType);
    assert_equals(record.mediaType, expectedRecord.mediaType);
    assert_equals(record.id, expectedRecord.id);
    assert_equals(record.encoding, expectedRecord.encoding);
    assert_equals(record.lang, expectedRecord.lang);
    // Compares record data
    assert_array_equals(new Uint8Array(record.data),
          new Uint8Array(expectedRecord.data));
  }
}

function testMultiScanOptions(message, scanOptions, unmatchedScanOptions, desc) {
  nfc_test(async (t, mockNFC) => {
    const ndef1 = new NDEFReader();
    const ndef2 = new NDEFReader();
    const controller = new AbortController();

    // Reading from unmatched ndef will not be triggered
    ndef1.onreading = t.unreached_func("reading event should not be fired.");
    unmatchedScanOptions.signal = controller.signal;
    await ndef1.scan(unmatchedScanOptions);

    const ndefWatcher = new EventWatcher(t, ndef2, ["reading", "readingerror"]);
    const promise = ndefWatcher.wait_for("reading").then(event => {
      controller.abort();
      assertWebNDEFMessagesEqual(event.message, new NDEFMessage(message));
    });
    scanOptions.signal = controller.signal;
    await ndef2.scan(scanOptions);

    mockNFC.setReadingMessage(message);
    await promise;
  }, desc);
}

function testMultiMessages(message, scanOptions, unmatchedMessage, desc) {
  nfc_test(async (t, mockNFC) => {
    const ndef = new NDEFReader();
    const controller = new AbortController();
    const ndefWatcher = new EventWatcher(t, ndef, ["reading", "readingerror"]);
    const promise = ndefWatcher.wait_for("reading").then(event => {
      controller.abort();
      assertWebNDEFMessagesEqual(event.message, new NDEFMessage(message));
    });
    scanOptions.signal = controller.signal;
    await ndef.scan(scanOptions);

    // Unmatched message will not be read
    mockNFC.setReadingMessage(unmatchedMessage);
    mockNFC.setReadingMessage(message);
    await promise;
  }, desc);
}