Pular para o conteúdo principal

Testando lógica de WebView

Se o seu app usa react-native-webview para executar HTML + JavaScript, a parte mais difícil de testar costuma ser a lógica interna: os scripts que consultam ou mutam o DOM antes de retornar um resultado via postMessage.

Testar o componente WebView em si (o onLoad dispara? o onMessage recebe os dados?) é uma questão de UI. Mas a lógica embutida no injectedJavaScript é JS + DOM puro, e o react-native-nitro-jsdom acaba sendo uma boa opção para executá-la de forma isolada.

O problema com as alternativas​

Jest (Node.js) não consegue rodar módulos nativos, então qualquer sandbox nativo está fora de cogitação.

Testar um WebView real com um runner nativo funciona, mas tem um custo: cada instância de WebView leva de 500ms a 2s para carregar, você só consegue observar o estado via postMessage, e a limpeza entre testes é frágil.

A abordagem​

Use react-native-nitro-jsdom para executar o JS embutido diretamente, sem nenhum WebView:

import { JSDOM } from 'react-native-nitro-jsdom'

// o mesmo HTML que sua WebView receberia
const dom = JSDOM.create(`
<html>
<body>
<div id="price" data-original="100">100</div>
</body>
</html>
`)

// o mesmo script que você passaria como injectedJavaScript
const result = await dom.evaluate(`
const el = document.getElementById('price')
const original = parseFloat(el.dataset.original)
el.textContent = String((original * 0.9).toFixed(2))
el.textContent
`)

// assertiva direta, sem precisar de um round-trip via postMessage
expect(result).toBe('90.00')

dom.dispose()

Cada teste recebe um DOM limpo e isolado que inicializa e encerra em milissegundos.

Com react-native-harness​

O react-native-harness da Callstack executa testes no estilo Jest (describe / it / expect) em um ambiente nativo real: simulador, emulador ou dispositivo físico. Isso o torna compatível com Nitro Modules, incluindo esta biblioteca.

import { describe, it, expect, beforeEach } from 'react-native-harness'
import { JSDOM } from 'react-native-nitro-jsdom'

describe('script do badge de preço', () => {
let dom: ReturnType<typeof JSDOM.create>

beforeEach(() => {
dom = JSDOM.create(`
<html>
<body>
<div id="price" data-original="100">100</div>
</body>
</html>
`)
})

afterEach(() => {
dom.dispose()
})

it('aplica 10% de desconto', async () => {
const result = await dom.evaluate(`
const el = document.getElementById('price')
el.textContent = String((parseFloat(el.dataset.original) * 0.9).toFixed(2))
el.textContent
`)
expect(result).toBe('90.00')
})

it('preserva o valor original no data attribute', async () => {
const result = await dom.evaluate(
`document.getElementById('price').dataset.original`
)
expect(result).toBe('100')
})
})

O que essa abordagem cobre e o que não cobre​

Essa abordagem é ideal para testar a lógica JavaScript que roda dentro de uma WebView: consultas ao DOM, mutações, valores computados e efeitos colaterais de scripts.

Ela não substitui testes de componente. Se você precisa verificar que o onMessage dispara, que o injectedJavaScriptBeforeContentLoaded executa no momento certo, ou que a WebView monta e renderiza corretamente, use o react-native-harness com um componente WebView real.