Testar o Web Bluetooth com o Puppeteer

François Beaufort
François Beaufort

O Bluetooth da Web tem suporte desde o Chrome 56 e permite que os desenvolvedores criem apps da Web que se comunicam diretamente com os dispositivos Bluetooth dos usuários. A capacidade do editor da Web Espruino de fazer upload de código para dispositivos Bluetooth compatíveis é um exemplo disso. Agora é possível testar esses aplicativos com o Puppeteer.

Esta postagem de blog explica como usar o Puppeteer para operar e testar um app da Web com suporte a Bluetooth. A parte mais importante disso é a capacidade do Puppeteer de operar o seletor de dispositivos Bluetooth do Chrome.

Se você não sabe usar o Web Bluetooth no Chrome, o vídeo a seguir mostra a solicitação de Bluetooth no editor da Web Espruino:

O usuário seleciona um dispositivo Bluetooth Puck.js no editor da Web do Espruino.

Para acompanhar esta postagem do blog, você precisará de um app da Web com Bluetooth, um dispositivo Bluetooth com o qual ele possa se comunicar e estar usando o Puppeteer v21.4.0 ou posterior.

Iniciar o navegador

Como na maioria dos scripts do Puppeteer, comece iniciando o navegador com Puppeteer.launch(). Para acessar os recursos do Bluetooth, você precisa fornecer alguns argumentos extras:

  • Desative o modo headless: isso significa que o Puppeteer vai abrir uma janela visível do navegador Chrome para executar o teste. Use o novo modo headless se preferir executar sem a interface. O modo headless legado não mostra solicitações do Bluetooth.
  • Argumentos adicionais para o Chromium: transmita um argumento "enable Web Bluetooth" para ambientes Linux.
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({
  headless: false,
  args: ["--enable-features=WebBluetooth"],
});

Ao abrir a primeira página, geralmente é recomendável usar um contexto de navegação anônima. Isso ajuda a evitar o vazamento de permissões entre os testes executados com seu script, embora haja um estado compartilhado no nível do SO que não pode ser evitado pelo Puppeteer. O código abaixo demonstra isso:

const browserContext = await browser.createIncognitoBrowserContext();
const page = await browserContext.newPage();

Em seguida, acesse o URL do app da Web que você está testando com Page.goto().

Abrir a solicitação do dispositivo Bluetooth

Depois de usar o Puppeteer para abrir a página do app da Web, você pode se conectar ao dispositivo Bluetooth para ler os dados. Esta próxima etapa pressupõe que você tenha um botão no seu app da Web que execute algum código JavaScript, incluindo uma chamada para navigator.bluetooth.requestDevice().

Use Page.locator().click() para pressionar esse botão e Page.waitForDevicePrompt() para reconhecer quando o seletor de dispositivos Bluetooth aparece. É necessário chamar waitForDevicePrompt() antes de clicar no botão. Caso contrário, o prompt já vai ter sido aberto e não será possível detectá-lo.

Como esses dois métodos do Puppeteer retornam promessas, Promise.all() é uma maneira conveniente de chamá-los na ordem certa:

const [devicePrompt] = await Promise.all([
  page.waitForDevicePrompt(),
  page.locator("#start-test-button").click(),
]);

A promessa retornada por waitForDevicePrompt() é resolvida para um objeto DeviceRequestPrompt, que você vai usar em seguida para selecionar o dispositivo Bluetooth ao qual quer se conectar.

Selecionar um dispositivo

Use DeviceRequestPrompt.waitForDevice() e DeviceRequestPrompt.select() para encontrar e se conectar ao dispositivo Bluetooth correto.

DeviceRequestPrompt.waitForDevice() chama o callback fornecido sempre que o Chrome encontra um dispositivo Bluetooth com algumas informações básicas sobre o dispositivo. Na primeira vez que o callback retornar "true", waitForDevice() será resolvido para o DeviceRequestPromptDevice correspondente. Transmita esse dispositivo ao DeviceRequestPrompt.select() para que ele possa ser selecionado e conectado a ele.

const bluetoothDevice = await devicePrompt.waitForDevice(
  (d) => d.name == wantedDeviceName,
);
await devicePrompt.select(bluetoothDevice);

Quando o DeviceRequestPrompt.select() for resolvido, o Chrome será conectado ao dispositivo e a página da Web poderá acessá-lo.

Ler do dispositivo

Nesse ponto, o app da Web vai estar conectado ao dispositivo Bluetooth escolhido e poderá ler as informações dele. O resultado será algo como o seguinte:

const serviceId = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";

const device = await navigator.bluetooth.requestDevice({
  filters: [{ services: [serviceId] }],
});
const gattServer = await device.gatt.connect();
const service = await gattServer.getPrimaryService(serviceId);
const characteristic = await service.getCharacteristic(
  "0b30afd0-193e-11eb-adc1-0242ac120002",
);
const dataView = await characteristic.readValue();

Para conferir um tutorial mais detalhado sobre essa sequência de chamadas de API, consulte Como se comunicar com dispositivos Bluetooth usando JavaScript.

Neste ponto, você sabe como usar o Puppeteer para automatizar o uso de um app da Web compatível com Bluetooth substituindo a etapa humana de seleção de um dispositivo no menu de escolha de dispositivos Bluetooth. Embora isso possa ser útil em geral, é diretamente aplicável para escrever um teste completo para esse tipo de app da Web.

Criar um teste

A parte que falta para levar o código até agora para escrever um teste completo é extrair informações do app da Web e colocá-las no script do Puppeteer. Depois disso, é bastante simples usar uma biblioteca de testes (como TAP ou mocha) para verificar se os dados corretos foram lidos e informados.

Uma das maneiras mais fáceis de fazer isso é gravar dados no DOM. O JavaScript tem muitas maneiras de fazer isso sem bibliotecas adicionais. Voltando ao seu app da Web hipotético, ele pode mudar a cor de um indicador de status quando lê dados do dispositivo Bluetooth ou imprime os dados literais em um campo. Exemplo:

const dataDisplayElement = document.querySelector('#data-display');
dataDisplayElement.innerText = dataView.getUint8();

No Puppeteer, Page.$eval() oferece uma maneira de extrair esses dados do DOM da página e colocá-los em um script de teste. $eval() usa a mesma lógica de document.querySelector() para encontrar um elemento e, em seguida, executa a função de callback fornecida com esse elemento como argumento. Depois de ter isso como uma variável, use a biblioteca de declarações para testar se os dados são os esperados.

const dataText = await page.$eval('#data-display', (el) => el.innerText);
equal(17, dataText);

Outros recursos

Para conferir exemplos mais complexos de como escrever testes para apps da Web com Bluetooth com o Puppeteer, consulte este repositório: https://github.com/WebBluetoothCG/manual-tests/. O Web Bluetooth Community Group (link em inglês) mantém esse conjunto de testes, que podem ser executados em um navegador ou localmente. O teste de característica somente leitura é mais semelhante ao exemplo usado nesta postagem do blog.

Agradecimentos

Agradecemos a Vincent Scheib por iniciar este projeto e fornecer feedback valioso nesta publicação.