Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions __e2e__/authFlow.harness.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import { screen, userEvent } from '@react-native-harness/ui';
import React from 'react';
import PrewarmAuthApp, { authServer } from './PrewarmAuthApp';

// `render` gives up after 1s by default, and the first render in a process creates the first
// native WebView, which on the CI Android emulator takes longer than that.
const MOUNT_OPTIONS = { timeout: 10000 };

describe('live SDK multi-screen auth flow', () => {
for (const passiveSiteKey of [false, true]) {
test(`signs up and logs in after destroying the warmed WebView (passiveSiteKey=${passiveSiteKey})`, async () => {
await fetch(`${authServer}/reset`, { method: 'POST' });
await render(<PrewarmAuthApp destroyOnLeave destroyAfterToken passiveSiteKey={passiveSiteKey} />);
await render(<PrewarmAuthApp destroyOnLeave destroyAfterToken passiveSiteKey={passiveSiteKey} />, MOUNT_OPTIONS);
await userEvent.type(await screen.findByTestId('email'), 'destroy@example.test');
for (const [attempt, mode] of ['signup', 'login'].entries()) {
await userEvent.press(await screen.findByTestId(mode));
Expand All @@ -30,7 +34,7 @@ describe('live SDK multi-screen auth flow', () => {
for (const passiveSiteKey of [false, true]) {
test(`prewarms, signs up, and logs in without reloading (passiveSiteKey=${passiveSiteKey})`, async () => {
await fetch(`${authServer}/reset`, { method: 'POST' });
await render(<PrewarmAuthApp passiveSiteKey={passiveSiteKey} />);
await render(<PrewarmAuthApp passiveSiteKey={passiveSiteKey} />, MOUNT_OPTIONS);
await waitFor(
async () => {
const state = await (await fetch(`${authServer}/state`)).json();
Expand Down Expand Up @@ -75,7 +79,7 @@ describe('live SDK multi-screen auth flow', () => {

test('cancels queued verification and reuses the warmed SDK on retry', async () => {
await fetch(`${authServer}/reset`, { method: 'POST' });
await render(<PrewarmAuthApp slowSdk passiveSiteKey />);
await render(<PrewarmAuthApp slowSdk passiveSiteKey />, MOUNT_OPTIONS);
await userEvent.type(await screen.findByTestId('email'), 'cancel@example.test');
await userEvent.press(await screen.findByTestId('signup'));
await userEvent.press(await screen.findByTestId('cancel'));
Expand All @@ -96,7 +100,7 @@ describe('live SDK multi-screen auth flow', () => {
test('fetches a fresh token after the backend rejects credentials', async () => {
await fetch(`${authServer}/reset`, { method: 'POST' });
const measurements = { attempts: [], errors: [], authErrors: [] };
await render(<PrewarmAuthApp passiveSiteKey measurements={measurements} />);
await render(<PrewarmAuthApp passiveSiteKey measurements={measurements} />, MOUNT_OPTIONS);
await userEvent.type(await screen.findByTestId('email'), 'retry@example.test');
for (const [mode, password, tokenCount] of [['signup', 'Password-123', 1], ['login', 'Password', 2]]) {
await userEvent.press(await screen.findByTestId(mode));
Expand Down
8 changes: 6 additions & 2 deletions __e2e__/example.harness.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import App from '../App';
import AutopassExample from './ExampleAutopass';
import { authServer } from './PrewarmAuthApp';

// `render` gives up after 1s by default, and the first render in a process creates the first
// native WebView, which on the CI Android emulator takes longer than that.
const MOUNT_OPTIONS = { timeout: 10000 };

async function saveScreenshot(phase) {
const screenshot = await screen.screenshot();
expect(screenshot).not.toBeNull();
Expand Down Expand Up @@ -35,7 +39,7 @@ describe('original repository example', () => {
afterEach(() => log.mockRestore());

test('opens, dismisses, and reopens the live demo challenge', async () => {
await render(<App />);
await render(<App />, MOUNT_OPTIONS);
for (const attempt of [1, 2]) {
await launchExample(log);
await waitFor(() => expect(log.mock.calls.filter(([message]) => message === 'Visual challenge opened')).toHaveLength(attempt), { timeout: 30000 });
Expand All @@ -56,7 +60,7 @@ describe('original repository example', () => {
// Generated from Example.App.js; only the sitekey changes to the public auto-pass key.
test('displays valid tokens and allows a second launch with the auto-pass key', async () => {
await fetch(`${authServer}/reset`, { method: 'POST' });
await render(<AutopassExample />);
await render(<AutopassExample />, MOUNT_OPTIONS);
for (const attempt of [1, 2]) {
await launchExample(log);
await waitFor(() => expect(log.mock.calls.filter(([message]) => message === 'Verified code from hCaptcha')).toHaveLength(attempt), { timeout: 30000 });
Expand Down
23 changes: 13 additions & 10 deletions __e2e__/prewarm.harness.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const sdk = `
window.hCaptchaOnLoad();
`;
const jsSrc = `data:application/javascript,${encodeURIComponent(sdk)}#`;
// `render` gives up after 1s by default, and the first render in a process creates the first
// native WebView, which on the CI Android emulator takes longer than that.
const MOUNT_OPTIONS = { timeout: 10000 };
// Native process startup is separate from the library's 15-second loading deadline.
const sdkReadyOptions = { timeout: 30000 };
const waitForSdk = (testId) => waitFor(() => expect(screen.queryByTestId(testId)).not.toBeNull(), sdkReadyOptions);
Expand Down Expand Up @@ -115,7 +118,7 @@ describe('native WebView prewarm', () => {
const lateSdk = `setTimeout(function() { ${sdk} }, 16000);`;
await render(<PrewarmFixture key="late-ready" captchaRef={ref}
sdkSource={`data:application/javascript,${encodeURIComponent(lateSdk)}#`}
onEvent={(event) => events.push(event)} />);
onEvent={(event) => events.push(event)} />, MOUNT_OPTIONS);
await waitForSdk('loads-1');
expect(events.map((event) => event.nativeEvent.data)).toEqual(['fixture-ready']);
ref.current.execute();
Expand All @@ -130,7 +133,7 @@ describe('native WebView prewarm', () => {
const events = [];
await render(<PrewarmFixture key={`render-retry-${variant}`} captchaRef={ref}
inline={variant === 'inline'} passiveSiteKey={variant === 'passive'} onEvent={(event) => events.push(event)}
sdkSource={`data:application/javascript,${encodeURIComponent('window.fixtureRenderFailures = 2;\n' + sdk)}#`} />);
sdkSource={`data:application/javascript,${encodeURIComponent('window.fixtureRenderFailures = 2;\n' + sdk)}#`} />, MOUNT_OPTIONS);
await waitFor(async () => {
const state = await (await fetch(`${authServer}/state`)).json();
expect(state.sdkReady).toBe(1);
Expand All @@ -151,7 +154,7 @@ describe('native WebView prewarm', () => {
const key = `hidden-reset-${variant}`;
const props = { captchaRef: ref, delayedTokens: true,
inline: variant === 'inline', passiveSiteKey: variant === 'passive', onEvent: (event) => events.push(event) };
const fixture = await render(<PrewarmFixture key={key} {...props} />);
const fixture = await render(<PrewarmFixture key={key} {...props} />, MOUNT_OPTIONS);
await waitForSdk('loads-1');
ref.current.execute();
await waitFor(() => expect(events.filter((event) => event.markUsed)).toHaveLength(1), { timeout: 10000 });
Expand Down Expand Up @@ -180,7 +183,7 @@ describe('native WebView prewarm', () => {
const props = { captchaRef: ref, inline: variant === 'inline', passiveSiteKey: variant === 'passive',
onEvent: (event) => events.push(event) };
const fixture = await render(<PrewarmFixture key={`loading-${variant}`} {...props}
sdkSource={`${authServer}/slow/1/api.js`} />);
sdkSource={`${authServer}/slow/1/api.js`} />, MOUNT_OPTIONS);
await waitFor(async () => {
const state = await (await fetch(`${authServer}/state`)).json();
expect(state.sdkLoads).toBe(1);
Expand All @@ -203,7 +206,7 @@ describe('native WebView prewarm', () => {
const events = [];
await render(<PrewarmFixture key={`destroy-${variant}-${phase}`} captchaRef={ref}
inline={variant === 'inline'} passiveSiteKey={variant === 'passive'}
delayedTokens={phase === 'pending'} hideAfterToken={false} onEvent={(event) => events.push(event)} />);
delayedTokens={phase === 'pending'} hideAfterToken={false} onEvent={(event) => events.push(event)} />, MOUNT_OPTIONS);
await waitForSdk('loads-1');
if (phase !== 'ready') {
ref.current.execute();
Expand Down Expand Up @@ -231,7 +234,7 @@ describe('native WebView prewarm', () => {

for (const passiveSiteKey of [false, true]) {
test(`loads while hidden and reuses the widget (passiveSiteKey=${passiveSiteKey})`, async () => {
await render(<PrewarmFixture key={String(passiveSiteKey)} passiveSiteKey={passiveSiteKey} />);
await render(<PrewarmFixture key={String(passiveSiteKey)} passiveSiteKey={passiveSiteKey} />, MOUNT_OPTIONS);
await waitForSdk('ready');
for (const attempt of [1, 2]) {
await userEvent.press(await screen.findByTestId('execute'));
Expand All @@ -241,7 +244,7 @@ describe('native WebView prewarm', () => {
});

test(`rejects a cancelled outcome after re-execute (passiveSiteKey=${passiveSiteKey})`, async () => {
await render(<PrewarmFixture key={`cancel-${passiveSiteKey}`} passiveSiteKey={passiveSiteKey} delayedTokens cancelAndRetry />);
await render(<PrewarmFixture key={`cancel-${passiveSiteKey}`} passiveSiteKey={passiveSiteKey} delayedTokens cancelAndRetry />, MOUNT_OPTIONS);
await waitForSdk('ready');
await userEvent.press(await screen.findByTestId('execute'));
await new Promise((resolve) => setTimeout(resolve, 2500));
Expand All @@ -251,7 +254,7 @@ describe('native WebView prewarm', () => {
});

test(`preserves a held token across presentation change (passiveSiteKey=${passiveSiteKey})`, async () => {
const fixture = await render(<PrewarmFixture key={`presentation-${passiveSiteKey}`} passiveSiteKey={passiveSiteKey} hideAfterToken={false} />);
const fixture = await render(<PrewarmFixture key={`presentation-${passiveSiteKey}`} passiveSiteKey={passiveSiteKey} hideAfterToken={false} />, MOUNT_OPTIONS);
await waitForSdk('ready');
await userEvent.press(await screen.findByTestId('execute'));
await waitFor(() => screen.findByTestId('fixture-token-widget-1-attempt-1-000000000000'));
Expand All @@ -263,7 +266,7 @@ describe('native WebView prewarm', () => {
}

test('recreates the native document after a theme change', async () => {
const fixture = await render(<PrewarmFixture key="theme" passiveSiteKey />);
const fixture = await render(<PrewarmFixture key="theme" passiveSiteKey />, MOUNT_OPTIONS);
await waitForSdk('loads-1');
await userEvent.press(await screen.findByTestId('execute'));
await waitFor(() => screen.findByTestId('fixture-token-widget-1-attempt-1-000000000000'));
Expand All @@ -278,7 +281,7 @@ describe('native WebView prewarm', () => {
test(`updates verification data with the expected reload behavior (legacy=${legacy})`, async () => {
const initial = legacy ? { rqdata: 'initial' } : { verifyParams: { rqdata: 'initial' } };
const updated = legacy ? { rqdata: 'current-000000000000' } : { verifyParams: { rqdata: 'current-000000000000' } };
const fixture = await render(<PrewarmFixture key={`payload-${legacy}`} passiveSiteKey {...initial} />);
const fixture = await render(<PrewarmFixture key={`payload-${legacy}`} passiveSiteKey {...initial} />, MOUNT_OPTIONS);
await waitForSdk('loads-1');
await fixture.rerender(<PrewarmFixture key={`payload-${legacy}`} passiveSiteKey {...updated} />);
const loads = legacy ? 2 : 1;
Expand Down
Loading