Skip to content
Merged
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
10 changes: 5 additions & 5 deletions apps/web/e2e/axe-devices.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { type Browser, type Page, expect, test } from '@playwright/test';

import { THEMES, WIDTHS, audit } from './axe';
import { createVault, startScreen } from './create';
import { checkSameNetwork, createVault, startScreen } from './create';
import { PAIRING_URL } from '../playwright.config';
import { fillPassword } from './password';

Expand Down Expand Up @@ -66,7 +66,7 @@ for (const [layout, width] of WIDTHS) {
await settings(a);
await button(a, 'Pair a device…').click();
await audit(a, 'the pairing password');
await a.getByTestId('pairing-same-network').check();
await checkSameNetwork(a);
await fillPassword(a.getByTestId('invite-password'), PASSWORD_A);
await button(a, 'Start pairing').click();
const offer = a.getByTestId('pairing-text');
Expand All @@ -77,7 +77,7 @@ for (const [layout, width] of WIDTHS) {
await button(b, 'Pair with an existing device').click();
await fillPassword(b.getByTestId('new-password'), PASSWORD_B);
await fillPassword(b.getByTestId('confirm-password'), PASSWORD_B);
await b.getByTestId('pairing-same-network').check();
await checkSameNetwork(b);
await button(b, 'Start pairing').click();
await expect(b.getByTestId('pairing-paste')).toBeVisible({ timeout: 30_000 });
await audit(b, "B waiting for A's text");
Expand Down Expand Up @@ -109,14 +109,14 @@ for (const [layout, width] of WIDTHS) {
await a.getByTestId('device-sync').click();
const syncA = a.getByTestId('sync');
await audit(a, 'the sync choice');
await syncA.getByTestId('pairing-same-network').check();
await checkSameNetwork(syncA);
await syncA.getByTestId('sync-first').click();
const code = syncA.getByTestId('pairing-text');
await expect(code).not.toHaveValue('', { timeout: 30_000 });
await audit(a, "A's sync code");
await b.getByTestId('device-sync').click();
const syncB = b.getByTestId('sync');
await syncB.getByTestId('pairing-same-network').check();
await checkSameNetwork(syncB);
await syncB.getByTestId('sync-read').click();
await syncB.getByTestId('pairing-paste').fill(await code.inputValue());
await button(b, 'Show my answer').click();
Expand Down
2 changes: 1 addition & 1 deletion apps/web/e2e/axe.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ for (const [layout, width] of WIDTHS) {

await button(page, 'Pair with an existing device').click();
await audit(page, 'joining a vault');
await button(page, 'Back').click();
await button(page, 'Cancel').click();
await button(page, 'Restore from a backup').click();
await audit(page, 'restoring a backup');
await button(page, 'Cancel').click();
Expand Down
6 changes: 3 additions & 3 deletions apps/web/e2e/backup-merge.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { type Browser, type BrowserContext, type Page, expect, test } from '@pla

import { FUTURE_CLOCK_BOUND_MS } from '@blindvault/vault';

import { createVault, openSettingsPage, openVault, startScreen } from './create';
import { checkSameNetwork, createVault, openSettingsPage, openVault, startScreen } from './create';
import { PAIRING_URL } from '../playwright.config';
import { fillPassword } from './password';

Expand Down Expand Up @@ -275,7 +275,7 @@ test.describe('devices a merge brings', () => {
async function pair(browser: Browser, a: Page, logged: string[]) {
await openSettingsPage(a, 'devices');
await button(a, 'Pair a device…').click();
await a.getByTestId('pairing-same-network').check();
await checkSameNetwork(a);
await fillPassword(a.getByTestId('invite-password'), PASSWORD);
await button(a, 'Start pairing').click();
const offer = a.getByTestId('pairing-text');
Expand All @@ -286,7 +286,7 @@ test.describe('devices a merge brings', () => {
await button(b, 'Pair with an existing device').click();
await fillPassword(b.getByTestId('new-password'), B_PASSWORD);
await fillPassword(b.getByTestId('confirm-password'), B_PASSWORD);
await b.getByTestId('pairing-same-network').check();
await checkSameNetwork(b);
await button(b, 'Start pairing').click();
await b.getByTestId('pairing-paste').fill(await offer.inputValue(), { timeout: 30_000 });
await button(b, 'Show my answer').click();
Expand Down
16 changes: 15 additions & 1 deletion apps/web/e2e/create.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Vault creation as a user goes through it (§13.3, D179): the Recovery Phrase, its confirmation,
// then the local password.

import { type Page, expect } from '@playwright/test';
import { type Locator, type Page, expect } from '@playwright/test';

/**
* Waits for the three starts. Playwright's WebKit is Safari in a plain tab, where the first screen
Expand Down Expand Up @@ -113,3 +113,17 @@ export async function switchSettingsPage(page: Page, name: SettingsPageName): Pr
await page.getByRole('button', { name: 'Back', exact: true }).click();
await page.getByTestId(`settings-${name}`).click();
}

/** Opens the advanced options of a device wizard's first step, unless open already (D253). */
export async function openAdvanced(scope: Page | Locator): Promise<void> {
const box = scope.getByTestId('pairing-same-network');
if (!(await box.isVisible()))
await scope.getByTestId('advanced-options').locator('summary').click();
await expect(box).toBeVisible();
}

/** Checks "Same network only" in the advanced options (D203, D253). */
export async function checkSameNetwork(scope: Page | Locator): Promise<void> {
await openAdvanced(scope);
await scope.getByTestId('pairing-same-network').check();
}
5 changes: 4 additions & 1 deletion apps/web/e2e/onboarding.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,11 @@ test('says that no server can reset the vault, then offers the three starts (§1
await expect(page.getByRole('heading', { name: 'Pair with an existing device' })).toBeVisible();
await expect(page.getByText(/First create this device’s local password/)).toBeVisible();
await expect(page.getByTestId('new-password')).toBeVisible();
await expect(page.getByTestId('wizard-step')).toHaveText(
'Step 1 of 5 · Create the local password',
);
await expect(page.getByTestId('pairing-same-network')).not.toBeChecked();
await button(page, 'Back').click();
await button(page, 'Cancel').click();
await expect(button(page, 'Pair with an existing device')).toBeVisible();
await button(page, 'Restore from a backup').click();
await expect(page.getByRole('heading', { name: 'Restore from a backup' })).toBeVisible();
Expand Down
6 changes: 3 additions & 3 deletions apps/web/e2e/pairing-qr.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type Browser, type Page, expect, test } from '@playwright/test';

import { deniedCamera, expectCameraReleased, fakeQrCamera, noCamera, showQr } from './camera';
import { createVault, openVault, startScreen, openSettingsPage } from './create';
import { checkSameNetwork, createVault, openSettingsPage, openVault, startScreen } from './create';
import { PAIRING_URL } from '../playwright.config';
import { fillPassword } from './password';

Expand Down Expand Up @@ -61,7 +61,7 @@ async function invite(a: Page): Promise<void> {
await expect(a.getByRole('heading', { name: TITLE })).toBeVisible();
await openSettingsPage(a, 'devices');
await button(a, 'Pair a device…').click();
await a.getByTestId('pairing-same-network').check();
await checkSameNetwork(a);
await fillPassword(a.getByTestId('invite-password'), PASSWORD_A);
await button(a, 'Start pairing').click();
await expect(a.getByTestId('pairing-qr')).toBeVisible({ timeout: 30_000 });
Expand All @@ -73,7 +73,7 @@ async function join(b: Page): Promise<void> {
await button(b, 'Pair with an existing device').click();
await fillPassword(b.getByTestId('new-password'), PASSWORD_B);
await fillPassword(b.getByTestId('confirm-password'), PASSWORD_B);
await b.getByTestId('pairing-same-network').check();
await checkSameNetwork(b);
await button(b, 'Start pairing').click();
await expect(b.getByTestId('pairing-paste')).toBeVisible({ timeout: 30_000 });
}
Expand Down
14 changes: 11 additions & 3 deletions apps/web/e2e/pairing-stun.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { type Browser, type Page, expect, test } from '@playwright/test';

import { createVault, startScreen, openSettingsPage } from './create';
import {
checkSameNetwork,
openAdvanced,
createVault,
openSettingsPage,
startScreen,
} from './create';
import { PAIRING_URL } from '../playwright.config';
import { fillPassword } from './password';

Expand Down Expand Up @@ -66,6 +72,7 @@ test('a STUN list of its own: refused when invalid, saved, used by the pairing,
await createVault(a, PASSWORD_A);
await openSettingsPage(a, 'devices');
await button(a, 'Pair a device…').click();
await openAdvanced(a);
const hint = a.getByTestId('same-network-hint');
await expect(hint).toContainText('(stun.l.google.com, stun.cloudflare.com)');
await a.getByTestId('stun-open').click();
Expand Down Expand Up @@ -115,6 +122,7 @@ test('a STUN list of its own: refused when invalid, saved, used by the pairing,

// Cancelled, the screen reads the list again: it stays, until the defaults are restored.
await expect(a.getByTestId('invite-password')).toBeVisible();
await openAdvanced(a);
await a.getByTestId('stun-open').click();
await expect(list).toHaveText(['stun:127.0.0.1:3478', 'stun:stun.invalid:3479']);
await a.getByTestId('stun-reset').click();
Expand All @@ -123,7 +131,7 @@ test('a STUN list of its own: refused when invalid, saved, used by the pairing,
expect(await stored(a)).toBeNull();

// The same network only hides the list and uses no STUN server.
await a.getByTestId('pairing-same-network').check();
await checkSameNetwork(a);
await expect(a.getByTestId('stun')).toHaveCount(0);
await fillPassword(a.getByTestId('invite-password'), PASSWORD_A);
await button(a, 'Start pairing').click();
Expand All @@ -146,7 +154,7 @@ test('an unreadable stored list: the same network only, locked on, and the pairi
await createVault(a, PASSWORD_A);
await openSettingsPage(a, 'devices');
await button(a, 'Pair a device…').click();
await a.getByTestId('pairing-same-network').check();
await checkSameNetwork(a);
await fillPassword(a.getByTestId('invite-password'), PASSWORD_A);
await button(a, 'Start pairing').click();
const offerText = a.getByTestId('pairing-text');
Expand Down
21 changes: 15 additions & 6 deletions apps/web/e2e/pairing.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type Browser, type Page, expect, test } from '@playwright/test';

import { corruptRecords } from './corrupt';
import {
checkSameNetwork,
createVault,
openSettingsPage,
openVault,
Expand Down Expand Up @@ -62,11 +63,16 @@ async function invite(a: Page): Promise<string> {

await openSettingsPage(a, 'devices');
await button(a, 'Pair a device…').click();
await a.getByTestId('pairing-same-network').check();
// One step at a time, with what to do on the other device (D253).
await expect(a.getByTestId('wizard-step')).toHaveText('Step 1 of 4 · Confirm it is you');
await expect(a.getByTestId('wizard-other')).toContainText('Pair with an existing device');
await expect(a.getByTestId('pairing-same-network')).toBeHidden();
await checkSameNetwork(a);
await fillPassword(a.getByTestId('invite-password'), PASSWORD_A);
await button(a, 'Start pairing').click();
const text = a.getByTestId('pairing-text');
await expect(text).not.toHaveValue('', { timeout: 30_000 });
await expect(a.getByTestId('wizard-step')).toHaveText('Step 2 of 4 · Exchange the codes');
// What the code or the text carries shows this device's network addresses (D208).
await expect(a.getByText(CHANNEL)).toBeVisible();
return text.inputValue();
Expand All @@ -78,12 +84,13 @@ async function join(b: Page, offer: string): Promise<string> {
await button(b, 'Pair with an existing device').click();
await fillPassword(b.getByTestId('new-password'), PASSWORD_B);
await fillPassword(b.getByTestId('confirm-password'), PASSWORD_B);
await b.getByTestId('pairing-same-network').check();
await checkSameNetwork(b);
await button(b, 'Start pairing').click();
await b.getByTestId('pairing-paste').fill(offer, { timeout: 30_000 });
await button(b, 'Show my answer').click();
const text = b.getByTestId('pairing-text');
await expect(text).not.toHaveValue('', { timeout: 30_000 });
await expect(b.getByTestId('wizard-step')).toHaveText('Step 3 of 5 · Show the answer');
await expect(b.getByText(CHANNEL)).toBeVisible();
return text.inputValue();
}
Expand All @@ -103,6 +110,8 @@ async function toSas(browser: Browser, clockA = false) {
await expect(sasB).toHaveText(/^\d{3} \d{3}$/, { timeout: 30_000 });
const sas = await sasA.textContent();
expect(await sasB.textContent()).toBe(sas);
await expect(a.getByTestId('wizard-step')).toHaveText('Step 3 of 4 · Compare the numbers');
await expect(b.getByTestId('wizard-step')).toHaveText('Step 4 of 5 · Compare the numbers');
return { a, b };
}

Expand Down Expand Up @@ -286,7 +295,7 @@ test('a hidden-app lock at 0 says it locks at once, in Settings, the export and

await switchSettingsPage(a, 'devices');
await button(a, 'Pair a device…').click();
await a.getByTestId('pairing-same-network').check();
await checkSameNetwork(a);
await fillPassword(a.getByTestId('invite-password'), PASSWORD_A);
await button(a, 'Start pairing').click();
await expect(a.getByTestId('pairing-text')).not.toHaveValue('', { timeout: 30_000 });
Expand All @@ -306,7 +315,7 @@ test('a hidden-app lock at 0 says it locks at once, in Settings, the export and
await expect(a.getByText(exportHidden)).toHaveCount(0);
await switchSettingsPage(a, 'devices');
await button(a, 'Pair a device…').click();
await a.getByTestId('pairing-same-network').check();
await checkSameNetwork(a);
await fillPassword(a.getByTestId('invite-password'), PASSWORD_A);
await button(a, 'Start pairing').click();
await expect(a.getByTestId('pairing-text')).not.toHaveValue('', { timeout: 30_000 });
Expand Down Expand Up @@ -336,7 +345,7 @@ async function replacing(browser: Browser) {
);
await fillPassword(b.getByTestId('new-password'), PASSWORD_NEW);
await fillPassword(b.getByTestId('confirm-password'), PASSWORD_NEW);
await b.getByTestId('pairing-same-network').check();
await checkSameNetwork(b);
// Nothing starts before the user confirms the replacement (D183, D204).
await expect(button(b, 'Start pairing')).toBeDisabled();
await b.getByTestId('replace-understood').check();
Expand Down Expand Up @@ -382,7 +391,7 @@ test('numbers said to differ leave the vault being replaced intact (D150, D153)'
await button(b, 'No, it differs').click();
await expect(a.getByTestId('pairing-failed')).toHaveAttribute('data-reason', 'sas-mismatch');
await button(b, 'Start again').click();
await button(b, 'Back').click();
await button(b, 'Cancel').click();
await fillPassword(b.getByTestId('unlock-password'), PASSWORD_B);
await button(b, 'Unlock').click();
await expect(b.getByTestId('lock-state')).toHaveText('Unlocked', { timeout: 30_000 });
Expand Down
10 changes: 5 additions & 5 deletions apps/web/e2e/readmission.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { RecordType } from '@blindvault/crypto';
import { type Browser, type Page, expect, test } from '@playwright/test';

import { corruptRecords } from './corrupt';
import { createVault, openSettingsPage, openVault, startScreen } from './create';
import { checkSameNetwork, createVault, openSettingsPage, openVault, startScreen } from './create';
import { PAIRING_URL } from '../playwright.config';
import { fillPassword } from './password';

Expand Down Expand Up @@ -58,7 +58,7 @@ async function addItem(page: Page, title: string): Promise<void> {
async function startReadmission(page: Page, first: boolean): Promise<void> {
await openSettingsPage(page, 'devices');
await button(page, 'Re-admit with another device of this vault…').click();
await page.getByTestId('readmit-device').getByTestId('pairing-same-network').check();
await checkSameNetwork(page.getByTestId('readmit-device'));
await page.getByTestId(first ? 'readmission-first' : 'readmission-paste').click();
}

Expand Down Expand Up @@ -162,7 +162,7 @@ test('a pairing answered by a device of the vault is refused, and a restored dev
// A invites; B answers from its re-admission, as a device that already holds the vault.
await openSettingsPage(a, 'devices');
await button(a, 'Pair a device…').click();
await a.getByTestId('pairing-same-network').check();
await checkSameNetwork(a);
await fillPassword(a.getByTestId('invite-password'), PASSWORD_A);
await button(a, 'Start pairing').click();
const offer = a.getByTestId('pairing-text');
Expand All @@ -184,7 +184,7 @@ test('a pairing answered by a device of the vault is refused, and a restored dev

// B, restored and unregistered, pairs a new device C: the others know neither yet.
await button(b, 'Pair a device…').click();
await b.getByTestId('invite').getByTestId('pairing-same-network').check();
await checkSameNetwork(b.getByTestId('invite'));
await fillPassword(b.getByTestId('invite-password'), PASSWORD_B);
await button(b, 'Start pairing').click();
const invite = b.getByTestId('pairing-text');
Expand All @@ -194,7 +194,7 @@ test('a pairing answered by a device of the vault is refused, and a restored dev
await button(c, 'Pair with an existing device').click();
await fillPassword(c.getByTestId('new-password'), PASSWORD_A);
await fillPassword(c.getByTestId('confirm-password'), PASSWORD_A);
await c.getByTestId('pairing-same-network').check();
await checkSameNetwork(c);
await button(c, 'Start pairing').click();
await c.getByTestId('pairing-paste').fill(await invite.inputValue(), { timeout: 30_000 });
await button(c, 'Show my answer').click();
Expand Down
Loading
Loading