Testar o Web Bluetooth com o Puppeteer

François Beaufort
François Beaufort

O Web Bluetooth é compatível 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. Agora é possível testar esses aplicativos com o Puppeteer.

Esta postagem do blog mostra como usar o Puppeteer para operar e testar um aplicativo da Web habilitado para Bluetooth. A parte principal é 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 Espruino.

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

Inicie o navegador

Como na maioria dos scripts Puppeteer, comece iniciando o navegador com Puppeteer.launch(). Para acessar os recursos do Bluetooth, é necessário fornecer alguns argumentos extras:

  • Desativar o modo headless: isso significa que o Puppeteer abrirá uma janela visível do navegador Chrome para executar o teste. Use o novo modo headless se você preferir executá-lo sem interface. O modo headless legado não é compatível com a exibição de solicitações por 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 navegador anônimo. Isso ajuda a evitar o vazamento de permissões entre os testes executados com o script (embora haja algum estado compartilhado no nível do SO que não pode ser impedido pelo Puppeteer). O código a seguir demonstra isso:

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

Em seguida, navegue até 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 aplicativo da Web com ele, conecte-se ao dispositivo Bluetooth para ler dados. Esta próxima etapa pressupõe que você tenha um botão no seu app da Web que executa 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 dispositivo Bluetooth aparece. É necessário chamar waitForDevicePrompt() antes de clicar no botão. Caso contrário, a solicitação já terá sido aberta e não será possível detectá-la.

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

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

A promessa retornada por waitForDevicePrompt() é resolvida em um objeto DeviceRequestPrompt, que você vai usar ao lado para selecionar o dispositivo Bluetooth a que você 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 toda vez 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 como DeviceRequestPromptDevice correspondente. Transmita esse dispositivo para o DeviceRequestPrompt.select() para selecionar e se conectar a ele.

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

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

Ler a partir do dispositivo

Nesse momento, seu aplicativo da Web estará conectado ao dispositivo Bluetooth escolhido e poderá ler informações a partir 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 mais detalhes sobre essa sequência de chamadas de API, consulte Como se comunicar com dispositivos Bluetooth em JavaScript.

Neste ponto, você sabe como usar o Puppeteer para automatizar o uso de um aplicativo da Web habilitado para Bluetooth, substituindo a etapa humana de selecionar um dispositivo no menu do seletor de dispositivo Bluetooth. Embora isso possa ser útil em geral, é diretamente aplicável à criação de um teste completo para esse aplicativo da Web.

Criar um teste

O que falta desde levar o código até aqui até escrever um teste completo é extrair informações do app da Web para o 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 aplicativo da Web hipotético, ele pode mudar a cor de um indicador de status ao ler dados do dispositivo Bluetooth ou imprimir os dados literais em um campo. Exemplo:

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

No Puppeteer, o Page.$eval() oferece uma maneira de extrair esses dados do DOM da página para 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 o argumento. Depois de definir isso como uma variável, use sua biblioteca de declarações para testar se os dados são o esperado.

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

Outros recursos

Para ver exemplos mais complexos de criação de testes para apps da Web ativados por Bluetooth com o Puppeteer, consulte este repositório: https://github.com/WebBluetoothCG/manual-tests/ (link em inglês). O Grupo da comunidade Web Bluetooth mantém esse conjunto de testes, que podem ser executados em um navegador ou localmente. O teste"Característica somente leitura" é mais parecido com o exemplo usado nesta postagem do blog.

Agradecimentos

Agradecemos a Vincent Scheib por iniciar este projeto e fornecer comentários valiosos sobre esta postagem.