blob: 019bbd694c1c626e769503cf8a90ea4d11cdb77c [file] [log] [blame]
<!DOCTYPE html>
<meta charset=utf-8>
<title>Web NFC: NDEFWriter.write Tests</title>
<link rel="author" title="Intel" href="http://www.intel.com"/>
<link rel="help" href="https://w3c.github.io/web-nfc/"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/nfc-helpers.js"></script>
<script>
"use strict";
const invalid_type_messages =
[
// Invalid NDEFMessageSource type
undefined,
// NDEFMessage.records: should have at least 1 valid record.
// https://w3c.github.io/web-nfc/#the-write-method - Step 8.
createMessage([{}]),
// NDEFMessageSource: not NDEF-formatable.
// https://w3c.github.io/web-nfc/#the-write-method - Step 8.
createMessage([]),
// https://w3c.github.io/web-nfc/#dfn-map-text-to-ndef
// NDEFRecord must have data.
createMessage([createTextRecord()]),
// NDEFRecord.data for 'text' record must be either a string,
// an arrayBuffer, or an arrayBufferView.
createMessage([createTextRecord(test_json_data)]),
// NDEFRecord.encoding for 'text' record must be either "utf-8",
// "utf-16", "utf-16le" or "utf-16be".
createMessage([createTextRecord(test_buffer_data, "chinese")]),
// https://w3c.github.io/web-nfc/#dfn-map-a-url-to-ndef
// NDEFRecord must have data.
createMessage([createUrlRecord()]),
// https://w3c.github.io/web-nfc/#dfn-map-a-url-to-ndef
// NDEFRecord must have data.
createMessage([createUrlRecord(undefined, true)]),
// NDEFRecord.data for 'url' record must be string.
createMessage([createUrlRecord(test_buffer_data)]),
createMessage([createUrlRecord(test_json_data)]),
// NDEFRecord.data for 'absolute-url' record must be string.
createMessage([createUrlRecord(test_buffer_data, true)]),
createMessage([createUrlRecord(test_json_data, true)]),
// https://w3c.github.io/web-nfc/#dfn-map-binary-data-to-ndef
// NDEFRecord must have data.
createMessage([createMimeRecord()]),
// NDEFRecord.data for 'mime' record must be BufferSource.
createMessage([createMimeRecord(test_text_data)]),
createMessage([createMimeRecord(test_number_data)]),
createMessage([createMimeRecord(test_json_data)]),
// NDEFRecord must have data.
createMessage([createUnknownRecord()]),
// NDEFRecord.data for 'unknown' record must be BufferSource.
createMessage([createUnknownRecord(test_text_data)]),
createMessage([createUnknownRecord(test_number_data)]),
createMessage([createUnknownRecord(test_json_data)]),
// https://w3c.github.io/web-nfc/#dfn-map-external-data-to-ndef
// NDEFRecord must have data.
createMessage([createRecord('w3.org:xyz')]),
// NDEFRecord.data for external record must be a BufferSource or NDEFMessageInit.
createMessage([createRecord('w3.org:xyz', test_text_data)]),
createMessage([createRecord('w3.org:xyz', test_number_data)]),
createMessage([createRecord('w3.org:xyz', test_json_data)]),
// https://w3c.github.io/web-nfc/#dfn-map-local-type-to-ndef
// NDEFRecord must have data.
createMessage([createRecord(':xyz')]),
// NDEFRecord.data for local type record must be a BufferSource or NDEFMessageInit.
createMessage([createRecord(':xyz', test_text_data)]),
createMessage([createRecord(':xyz', test_number_data)]),
createMessage([createRecord(':xyz', test_json_data)]),
// https://w3c.github.io/web-nfc/#ndef-record-types
// The record type is neither a known type ('text', 'mime' etc.) nor a
// valid external/local type.
createMessage([createRecord('unmatched_type', test_buffer_data)])
];
const invalid_syntax_messages =
[
// Data for 'url' or 'absolute-url' record, must be a valid URL.
createMessage([createUrlRecord('Invalid URL:// Data')]),
createMessage([createUrlRecord('Invalid URL:// Data', true)]),
// NDEFRecord.lang length for 'text' record must be lower than 64.
createMessage([createTextRecord(test_text_data, undefined /* encoding */,
[...Array(64)].map(_ => 'a'))]),
];
const invalid_signals = [
"string",
123,
{},
true,
Symbol(),
() => {},
self
];
promise_test(async t => {
const writer = new NDEFWriter();
const promises = [];
invalid_type_messages.forEach(message => {
promises.push(
promise_rejects_js(t, TypeError, writer.write(message)));
});
await Promise.all(promises);
}, "Test that promise is rejected with TypeError if NDEFMessageSource is invalid.");
promise_test(async t => {
const writer = new NDEFWriter();
const promises = [];
invalid_syntax_messages.forEach(message => {
promises.push(
promise_rejects(t, 'SyntaxError', writer.write(message)));
});
await Promise.all(promises);
}, "Test that promise is rejected with SyntaxError if NDEFMessageSource contains\
invalid records.");
promise_test(async t => {
if (window.testRunner) {
// Deny nfc permissions for Chromium testrunner.
window.testRunner.setPermission('nfc', 'denied',
location.origin, location.origin);
}
const writer = new NDEFWriter();
await promise_rejects(t, 'NotAllowedError', writer.write(test_text_data));
}, 'NDEFWriter.write should fail if user permission is not granted.');
// We do not provide NFC mock here to simulate that there has no available
// implementation for NFC Mojo interface.
promise_test(async t => {
if (window.testRunner) {
// Deny nfc permissions for Chromium testrunner.
window.testRunner.setPermission('nfc', 'granted',
location.origin, location.origin);
}
const writer = new NDEFWriter();
await promise_rejects(t, 'NotSupportedError', writer.write(test_text_data));
}, 'NDEFWriter.write should fail if no implementation for NFC Mojo interface is available.');
nfc_test(async (t, mockNFC) => {
const writer = new NDEFWriter();
const controller = new AbortController();
//Make sure push is pending
mockNFC.setPendingPushCompleted(false);
const p = writer.write(test_text_data, { signal: controller.signal });
const rejected = promise_rejects(t, 'AbortError', p);
let callback_called = false;
await new Promise(resolve => {
t.step_timeout(() => {
callback_called = true;
controller.abort();
resolve();
}, 10);
});
await rejected;
assert_true(callback_called, 'timeout should have caused the abort');
}, "NDEFWriter.write should fail if abort write request before write happends.");
promise_test(async t => {
const writer = new NDEFWriter();
const controller = new AbortController();
assert_false(controller.signal.aborted);
controller.abort();
assert_true(controller.signal.aborted);
await promise_rejects(t, 'AbortError',
writer.write(test_text_data, { signal: controller.signal }));
}, "NDEFWriter.write should fail if signal's aborted flag is set.");
promise_test(async t => {
const writer = new NDEFWriter();
const promises = [];
invalid_signals.forEach(invalid_signal => {
promises.push(promise_rejects_js(t, TypeError,
writer.write(test_text_data, { signal: invalid_signal })));
});
await Promise.all(promises);
}, "NDEFWriter.write should fail if signal is not an AbortSignal.");
nfc_test(async (t, mockNFC) => {
const writer1 = new NDEFWriter();
const writer2 = new NDEFWriter();
const controller = new AbortController();
const p1 = writer1.write(test_text_data, { signal: controller.signal });
// Even though write request is grantable,
// this abort should be processed synchronously.
controller.abort();
await promise_rejects(t, 'AbortError', p1);
await writer2.write(test_text_data);
assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage());
}, "Synchronously signaled abort.");
nfc_test(async (t, mockNFC) => {
const writer = new NDEFWriter();
mockNFC.setHWStatus(NFCHWStatus.DISABLED);
await promise_rejects(t, 'NotReadableError', writer.write(test_text_data));
}, "NDEFWriter.write should fail when NFC HW is disabled.");
nfc_test(async (t, mockNFC) => {
const writer = new NDEFWriter();
mockNFC.setHWStatus(NFCHWStatus.NOT_SUPPORTED);
await promise_rejects(t, 'NotSupportedError', writer.write(test_text_data));
}, "NDEFWriter.write should fail when NFC HW is not supported.");
promise_test(async () => {
await new Promise((resolve,reject) => {
const iframe = document.createElement('iframe');
iframe.srcdoc = `<script>
if (window.testRunner) {
// Grant nfc permissions for Chromium testrunner.
window.testRunner.setPermission('nfc', 'granted',
location.origin, location.origin);
}
window.onmessage = message => {
if (message.data === "Ready") {
const onSuccess = () => {
parent.postMessage("Failure", "*");
};
const onError = error => {
if (error.name == "NotAllowedError") {
parent.postMessage("Success", "*");
} else {
parent.postMessage("Failure", "*");
}
};
try {
const writer = new NDEFWriter();
writer.write("Test").then(onSuccess, onError);
} catch(e) {
parent.postMessage("Failure", "*");
}
}
};
<\/script>`;
iframe.onload = () => iframe.contentWindow.postMessage('Ready', '*');
document.body.appendChild(iframe);
window.onmessage = message => {
if (message.data == 'Success') {
resolve();
} else if (message.data == 'Failure') {
reject();
}
}
});
}, 'Test that WebNFC API is not accessible from iframe context.');
nfc_test(async () => {
const writer = new NDEFWriter();
await writer.write(test_text_data);
}, 'NDEFWriter.write should succeed when NFC HW is enabled');
nfc_test(async (t, mockNFC) => {
const writer = new NDEFWriter();
const message = createMessage([createTextRecord(test_text_data),
createMimeRecordFromJson(test_json_data),
createMimeRecord(test_buffer_data),
createUnknownRecord(test_buffer_data),
createUrlRecord(test_url_data),
createUrlRecord(test_url_data, true),
createRecord('w3.org:xyz', test_buffer_data)],
test_message_origin);
await writer.write(message);
assertNDEFMessagesEqual(message, mockNFC.pushedMessage());
}, "NDEFWriter.write NDEFMessage containing text, mime, unknown, url, absolute-url \
and external records with default NDEFWriteOptions.");
nfc_test(async (t, mockNFC) => {
const messageContainText = createMessage([createTextRecord(test_text_data)]);
// Prepare a local type record that uses |messageContainText| as its payload.
const messageContainLocal = createMessage([createRecord(':containsTextRecord', messageContainText)]);
// Prepare an external type record that uses |messageContainLocal| as its payload.
const message = createMessage([createRecord('example.com:containsLocalRecord', messageContainLocal)]);
const writer = new NDEFWriter();
await writer.write(message);
const pushed_message = mockNFC.pushedMessage();
// The mojom message received by mock nfc contains only the external type record.
assert_equals(pushed_message.data.length, 1);
assert_equals(pushed_message.data[0].recordType, 'example.com:containsLocalRecord', 'recordType');
// The external type record's payload is from the original |messageContainLocal|,
// containing only the local type record.
assert_array_equals(pushed_message.data[0].data, new Uint8Array(0),
'payloadMessage is used instead');
assert_equals(pushed_message.data[0].payloadMessage.data.length, 1);
assert_equals(pushed_message.data[0].payloadMessage.data[0].recordType, ':containsTextRecord', 'recordType');
// The local type record's payload is from the original |messageContainText|,
// containing only the text record.
assert_array_equals(pushed_message.data[0].payloadMessage.data[0].data, new Uint8Array(0),
'payloadMessage is used instead');
assertNDEFMessagesEqual(messageContainText, pushed_message.data[0].payloadMessage.data[0].payloadMessage);
}, "NDEFWriter.write NDEFMessage containing embedded records.");
nfc_test(async (t, mockNFC) => {
const writer = new NDEFWriter();
await writer.write(test_text_data);
assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage());
}, "Test that NDEFWriter.write succeeds when message is DOMString.");
nfc_test(async (t, mockNFC) => {
const writer = new NDEFWriter();
await writer.write(test_buffer_data);
assertNDEFMessagesEqual(test_buffer_data, mockNFC.pushedMessage());
}, "Test that NDEFWriter.write succeeds when message is ArrayBuffer.");
nfc_test(async (t, mockNFC) => {
let buffer_view = new Uint8Array(test_buffer_data, 2, 5);
const writer = new NDEFWriter();
await writer.write(buffer_view);
assertNDEFMessagesEqual(buffer_view, mockNFC.pushedMessage());
}, "Test that NDEFWriter.write succeeds when message is ArrayBufferView.");
nfc_test(async (t, mockNFC) => {
const writer = new NDEFWriter();
await writer.write(createMessage([createRecord('empty')]));
const receivedMessage = mockNFC.pushedMessage();
assert_equals(receivedMessage.data.length, 1);
assert_equals(receivedMessage.data[0].recordType, 'empty', 'recordType');
}, "NDEFWriter.write with 'empty' record should succeed.");
nfc_test(async (t, mockNFC) => {
const writer = new NDEFWriter();
await writer.write(test_text_data);
assertNDEFWriteOptionsEqual(createNDEFWriteOptions(true),
mockNFC.writeOptions());
}, "Check that default NDEFWriteOptions values are correctly set.");
nfc_test(async (t, mockNFC) => {
const writer = new NDEFWriter();
let ndefWriteOptions = createNDEFWriteOptions(false);
await writer.write(test_text_data, ndefWriteOptions);
assertNDEFWriteOptionsEqual(ndefWriteOptions, mockNFC.writeOptions());
}, "Check that provided NDEFWriteOptions values are correctly converted.");
nfc_test(async (t, mockNFC) => {
const reader = new NDEFReader();
const message = createMessage([createTextRecord(test_text_data)]);
const controller = new AbortController();
const readerWatcher = new EventWatcher(t, reader, ["reading", "error"]);
await reader.scan({ signal: controller.signal });
const writer = new NDEFWriter();
await writer.write(test_text_data, { ignoreRead: false });
assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage());
mockNFC.setReadingMessage(message);
await readerWatcher.wait_for("reading").then(event => {
controller.abort();
assertWebNDEFMessagesEqual(event.message, new NDEFMessage(message));
});
}, "NDEFWriter.write should read data when ignoreRead is false.");
nfc_test(async (t, mockNFC) => {
const reader = new NDEFReader();
const message = createMessage([createTextRecord(test_text_data)]);
// Ignore reading if NDEFWriteOptions.ignoreRead is true
reader.onreading = t.unreached_func("reading event should not be fired.");
await reader.scan();
const writer = new NDEFWriter();
await writer.write(test_text_data, { ignoreRead: true });
mockNFC.setReadingMessage(message);
assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage());
}, "NDEFWriter.write should ignore reading data when ignoreRead is true.");
nfc_test(async (t, mockNFC) => {
const writer1 = new NDEFWriter();
const writer2 = new NDEFWriter();
const ndefWriteOptions1 = createNDEFWriteOptions( false);
const ndefWriteOptions2 = createNDEFWriteOptions(true);
const p1 = writer1.write(test_text_data, ndefWriteOptions1);
const p2 = writer2.write(test_url_data, ndefWriteOptions2);
await new Promise((resolve, reject) => {
// Make first push pending
mockNFC.setPendingPushCompleted(false);
let err = "";
p1.then(() => {
reject("pending push should not be fulfilled");
}).catch(e => {
err = e.name;
});
p2.then(() => {
assertNDEFMessagesEqual(test_url_data, mockNFC.pushedMessage());
assertNDEFWriteOptionsEqual(ndefWriteOptions2, mockNFC.writeOptions());
assert_equals(err, "AbortError", "the pending push should be aborted");
resolve();
});
});
}, "NDEFWriter.write should replace all previously configured write operations.");
nfc_test(async (t, mockNFC) => {
const writer = new NDEFWriter();
await writer.write({ records: [{ recordType: "mime", data: test_buffer_data }] });
assertNDEFMessagesEqual(test_buffer_data, mockNFC.pushedMessage());
}, "Test that mediaType should be set to 'application/octet-stream' if \
NDEFRecordInit.record's recordType is 'mime' and NDEFRecordInit.record's \
mediaType is undefined.");
nfc_test(async (t, mockNFC) => {
// Make sure the push will be pending in the mock.
mockNFC.setPendingPushCompleted(false);
const writer = new NDEFWriter();
const promise = writer.write(test_text_data);
// Just to make sure the write() request has already reached to the mock.
const reader = new NDEFReader();
await reader.scan();
mockNFC.simulateNonNDEFTagDiscovered();
await promise_rejects(t, 'NotSupportedError', promise);
}, "NDEFWriter.write should fail when the NFC device coming up does not expose \
NDEF technology.");
nfc_test(async (t, mockNFC) => {
const writer = new NDEFWriter();
await writer.write(test_text_data, { overwrite: false });
assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage());
}, "NDEFWriter.write should succeed to write data to an unformatted NFC device \
when the NDEFWriteOptions.overwrite is false.");
nfc_test(async (t, mockNFC) => {
const writer = new NDEFWriter();
await writer.write(test_buffer_data);
assertNDEFMessagesEqual(test_buffer_data, mockNFC.pushedMessage());
await writer.write(test_text_data, { overwrite: true });
assertNDEFMessagesEqual(test_text_data, mockNFC.pushedMessage());
}, "NDEFWriter.write should succeed to overwrite the existing data \
when the NDEFWriteOptions.overwrite is true.");
nfc_test(async (t, mockNFC) => {
const writer = new NDEFWriter();
const p = writer.write(test_text_data, { overwrite: false });
mockNFC.setIsFormattedTag(true);
await promise_rejects(t, 'NotAllowedError', p);
}, "NDEFWriter.write should fail when there are NDEF records on the NFC device \
and NDEFWriteOptions.overwrite is false.");
nfc_test(async (t, mockNFC) => {
const writer = new NDEFWriter();
mockNFC.simulateDataTransferFails();
await promise_rejects(t, 'NetworkError', writer.write(test_text_data));
}, "NDEFWriter.write should fail with NetworkError when NFC data transfer fails.");
</script>