node/test/fixtures/wpt/resources/web-bluetooth-bidi-test.js
Filip Skokan 8e5c5b3b04
test: update WPT resources,WebCryptoAPI,webstorage
PR-URL: https://github.com/nodejs/node/pull/59311
Refs: https://github.com/nodejs/node/issues/58987
Refs: https://github.com/nodejs/node/issues/59310
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
2025-08-03 11:31:39 +00:00

408 lines
15 KiB
JavaScript

'use strict'
// Convert `manufacturerData` to an array of bluetooth.BluetoothManufacturerData
// defined in
// https://webbluetoothcg.github.io/web-bluetooth/#bluetooth-bidi-definitions.
function convertToBidiManufacturerData(manufacturerData) {
const bidiManufacturerData = [];
for (const key in manufacturerData) {
bidiManufacturerData.push({
key: parseInt(key),
data: btoa(String.fromCharCode(...manufacturerData[key]))
})
}
return bidiManufacturerData;
}
function ArrayToMojoCharacteristicProperties(arr) {
const struct = {};
arr.forEach(property => {
struct[property] = true;
});
return struct;
}
class FakeBluetooth {
constructor() {
this.fake_central_ = null;
}
// Returns a promise that resolves with a FakeCentral that clients can use
// to simulate events that a device in the Central/Observer role would
// receive as well as monitor the operations performed by the device in the
// Central/Observer role.
//
// A "Central" object would allow its clients to receive advertising events
// and initiate connections to peripherals i.e. operations of two roles
// defined by the Bluetooth Spec: Observer and Central.
// See Bluetooth 4.2 Vol 3 Part C 2.2.2 "Roles when Operating over an
// LE Physical Transport".
async simulateCentral({state}) {
if (this.fake_central_) {
throw 'simulateCentral() should only be called once';
}
await test_driver.bidi.bluetooth.simulate_adapter({state: state});
this.fake_central_ = new FakeCentral();
return this.fake_central_;
}
}
// FakeCentral allows clients to simulate events that a device in the
// Central/Observer role would receive as well as monitor the operations
// performed by the device in the Central/Observer role.
class FakeCentral {
constructor() {
this.peripherals_ = new Map();
}
// Simulates a peripheral with |address|, |name|, |manufacturerData| and
// |known_service_uuids| that has already been connected to the system. If the
// peripheral existed already it updates its name, manufacturer data, and
// known UUIDs. |known_service_uuids| should be an array of
// BluetoothServiceUUIDs
// https://webbluetoothcg.github.io/web-bluetooth/#typedefdef-bluetoothserviceuuid
//
// Platforms offer methods to retrieve devices that have already been
// connected to the system or weren't connected through the UA e.g. a user
// connected a peripheral through the system's settings. This method is
// intended to simulate peripherals that those methods would return.
async simulatePreconnectedPeripheral(
{address, name, manufacturerData = {}, knownServiceUUIDs = []}) {
await test_driver.bidi.bluetooth.simulate_preconnected_peripheral({
address: address,
name: name,
manufacturerData: convertToBidiManufacturerData(manufacturerData),
knownServiceUuids:
knownServiceUUIDs.map(uuid => BluetoothUUID.getService(uuid))
});
return this.fetchOrCreatePeripheral_(address);
}
// Create a fake_peripheral object from the given address.
fetchOrCreatePeripheral_(address) {
let peripheral = this.peripherals_.get(address);
if (peripheral === undefined) {
peripheral = new FakePeripheral(address);
this.peripherals_.set(address, peripheral);
}
return peripheral;
}
}
class FakePeripheral {
constructor(address) {
this.address = address;
}
// Adds a fake GATT Service with |uuid| to be discovered when discovering
// the peripheral's GATT Attributes. Returns a FakeRemoteGATTService
// corresponding to this service. |uuid| should be a BluetoothServiceUUIDs
// https://webbluetoothcg.github.io/web-bluetooth/#typedefdef-bluetoothserviceuuid
async addFakeService({uuid}) {
const service_uuid = BluetoothUUID.getService(uuid);
await test_driver.bidi.bluetooth.simulate_service({
address: this.address,
uuid: service_uuid,
type: 'add',
});
return new FakeRemoteGATTService(service_uuid, this.address);
}
// Sets the next GATT Connection request response to |code|. |code| could be
// an HCI Error Code from BT 4.2 Vol 2 Part D 1.3 List Of Error Codes or a
// number outside that range returned by specific platforms e.g. Android
// returns 0x101 to signal a GATT failure
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE
async setNextGATTConnectionResponse({code}) {
const remove_handler =
test_driver.bidi.bluetooth.gatt_connection_attempted.on((event) => {
if (event.address != this.address) {
return;
}
remove_handler();
test_driver.bidi.bluetooth.simulate_gatt_connection_response({
address: event.address,
code,
});
});
}
async setNextGATTDiscoveryResponse({code}) {
// No-op for Web Bluetooth Bidi test, it will be removed when migration
// completes.
return Promise.resolve();
}
// Simulates a GATT connection response with |code| from the peripheral.
async simulateGATTConnectionResponse(code) {
await test_driver.bidi.bluetooth.simulate_gatt_connection_response(
{address: this.address, code});
}
// Simulates a GATT disconnection in the peripheral.
async simulateGATTDisconnection() {
await test_driver.bidi.bluetooth.simulate_gatt_disconnection(
{address: this.address});
}
}
class FakeRemoteGATTService {
constructor(service_uuid, peripheral_address) {
this.service_uuid_ = service_uuid;
this.peripheral_address_ = peripheral_address;
}
// Adds a fake GATT Characteristic with |uuid| and |properties|
// to this fake service. The characteristic will be found when discovering
// the peripheral's GATT Attributes. Returns a FakeRemoteGATTCharacteristic
// corresponding to the added characteristic.
async addFakeCharacteristic({uuid, properties}) {
const characteristic_uuid = BluetoothUUID.getCharacteristic(uuid);
await test_driver.bidi.bluetooth.simulate_characteristic({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: characteristic_uuid,
characteristicProperties: ArrayToMojoCharacteristicProperties(properties),
type: 'add'
});
return new FakeRemoteGATTCharacteristic(
characteristic_uuid, this.service_uuid_, this.peripheral_address_);
}
// Removes the fake GATT service from its fake peripheral.
async remove() {
await test_driver.bidi.bluetooth.simulate_service({
address: this.peripheral_address_,
uuid: this.service_uuid_,
type: 'remove'
});
}
}
class FakeRemoteGATTCharacteristic {
constructor(characteristic_uuid, service_uuid, peripheral_address) {
this.characteristic_uuid_ = characteristic_uuid;
this.service_uuid_ = service_uuid;
this.peripheral_address_ = peripheral_address;
this.last_written_value_ = {lastValue: null, lastWriteType: 'none'};
}
// Adds a fake GATT Descriptor with |uuid| to be discovered when
// discovering the peripheral's GATT Attributes. Returns a
// FakeRemoteGATTDescriptor corresponding to this descriptor. |uuid| should
// be a BluetoothDescriptorUUID
// https://webbluetoothcg.github.io/web-bluetooth/#typedefdef-bluetoothdescriptoruuid
async addFakeDescriptor({uuid}) {
const descriptor_uuid = BluetoothUUID.getDescriptor(uuid);
await test_driver.bidi.bluetooth.simulate_descriptor({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: this.characteristic_uuid_,
descriptorUuid: descriptor_uuid,
type: 'add'
});
return new FakeRemoteGATTDescriptor(
descriptor_uuid, this.characteristic_uuid_, this.service_uuid_,
this.peripheral_address_);
}
// Simulate a characteristic for operation |type| with response |code| and
// |data|.
async simulateResponse(type, code, data) {
await test_driver.bidi.bluetooth.simulate_characteristic_response({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: this.characteristic_uuid_,
type,
code,
data,
});
}
// Simulate a characteristic response for read operation with response |code|
// and |data|.
async simulateReadResponse(code, data) {
await this.simulateResponse('read', code, data);
}
// Simulate a characteristic response for write operation with response
// |code|.
async simulateWriteResponse(code) {
await this.simulateResponse('write', code);
}
// Sets the next read response for characteristic to |code| and |value|.
// |code| could be a GATT Error Response from
// BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range
// returned by specific platforms e.g. Android returns 0x101 to signal a GATT
// failure.
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE
async setNextReadResponse(gatt_code, value = null) {
if (gatt_code === 0 && value === null) {
throw '|value| can\'t be null if read should success.';
}
if (gatt_code !== 0 && value !== null) {
throw '|value| must be null if read should fail.';
}
const remove_handler =
test_driver.bidi.bluetooth.characteristic_event_generated.on(
(event) => {
if (event.address != this.peripheral_address_) {
return;
}
remove_handler();
this.simulateReadResponse(gatt_code, value);
});
}
// Sets the next write response for this characteristic to |code|. If
// writing to a characteristic that only supports 'write-without-response'
// the set response will be ignored.
// |code| could be a GATT Error Response from
// BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range
// returned by specific platforms e.g. Android returns 0x101 to signal a GATT
// failure.
async setNextWriteResponse(gatt_code) {
const remove_handler =
test_driver.bidi.bluetooth.characteristic_event_generated.on(
(event) => {
if (event.address != this.peripheral_address_) {
return;
}
this.last_written_value_ = {
lastValue: event.data,
lastWriteType: event.type
};
remove_handler();
if (event.type == 'write-with-response') {
this.simulateWriteResponse(gatt_code);
}
});
}
// Gets the last successfully written value to the characteristic and its
// write type. Write type is one of 'none', 'default-deprecated',
// 'with-response', 'without-response'. Returns {lastValue: null,
// lastWriteType: 'none'} if no value has yet been written to the
// characteristic.
async getLastWrittenValue() {
return this.last_written_value_;
}
// Removes the fake GATT Characteristic from its fake service.
async remove() {
await test_driver.bidi.bluetooth.simulate_characteristic({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: this.characteristic_uuid_,
characteristicProperties: undefined,
type: 'remove'
});
}
}
class FakeRemoteGATTDescriptor {
constructor(
descriptor_uuid, characteristic_uuid, service_uuid, peripheral_address) {
this.descriptor_uuid_ = descriptor_uuid;
this.characteristic_uuid_ = characteristic_uuid;
this.service_uuid_ = service_uuid;
this.peripheral_address_ = peripheral_address;
this.last_written_value_ = null;
}
// Simulate a descriptor for operation |type| with response |code| and
// |data|.
async simulateResponse(type, code, data) {
await test_driver.bidi.bluetooth.simulate_descriptor_response({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: this.characteristic_uuid_,
descriptorUuid: this.descriptor_uuid_,
type,
code,
data,
});
}
// Simulate a descriptor response for read operation with response |code| and
// |data|.
async simulateReadResponse(code, data) {
await this.simulateResponse('read', code, data);
}
// Simulate a descriptor response for write operation with response |code|.
async simulateWriteResponse(code) {
await this.simulateResponse('write', code);
}
// Sets the next read response for descriptor to |code| and |value|.
// |code| could be a GATT Error Response from
// BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range
// returned by specific platforms e.g. Android returns 0x101 to signal a GATT
// failure.
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE
async setNextReadResponse(gatt_code, value = null) {
if (gatt_code === 0 && value === null) {
throw '|value| can\'t be null if read should success.';
}
if (gatt_code !== 0 && value !== null) {
throw '|value| must be null if read should fail.';
}
const remove_handler =
test_driver.bidi.bluetooth.descriptor_event_generated.on((event) => {
if (event.address != this.peripheral_address_) {
return;
}
remove_handler();
this.simulateReadResponse(gatt_code, value);
});
}
// Sets the next write response for this descriptor to |code|.
// |code| could be a GATT Error Response from
// BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range
// returned by specific platforms e.g. Android returns 0x101 to signal a GATT
// failure.
async setNextWriteResponse(gatt_code) {
const remove_handler =
test_driver.bidi.bluetooth.descriptor_event_generated.on((event) => {
if (event.address != this.peripheral_address_) {
return;
}
this.last_written_value_ = {
lastValue: event.data,
lastWriteType: event.type
};
remove_handler();
if (event.type == 'write-with-response') {
this.simulateWriteResponse(gatt_code);
}
});
}
// Gets the last successfully written value to the descriptor.
// Returns null if no value has yet been written to the descriptor.
async getLastWrittenValue() {
return this.last_written_value_;
}
// Removes the fake GATT Descriptor from its fake characteristic.
async remove() {
await test_driver.bidi.bluetooth.simulate_descriptor({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: this.characteristic_uuid_,
descriptorUuid: this.descriptor_uuid_,
type: 'remove'
});
}
}
function initializeBluetoothBidiResources() {
navigator.bluetooth.test = new FakeBluetooth();
}