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
52 changes: 47 additions & 5 deletions recording/playwright/fixtures/browser-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ export const SEL = {
// Setup (first run, no password set)
passwordInput: 'input[type="password"]',
setupConfirm: 'button:has-text("Confirm")',
providerPubkeyInput: "#provider-pubkey",
submitKycButton: 'button:has-text("Submit KYC"):not([disabled])',
kycNameInput: "#kyc-name",
kycPasswordInput: "#kyc-password",

// Add wallet (after setup)
importTab: 'button:has-text("Import")',
Expand Down Expand Up @@ -402,11 +406,18 @@ export async function createPrivacyChannel(

/**
* Open channel picker sheet, add a provider, connect (which navigates to
* the in-popup sign-request page → password → Approve).
* the in-popup sign-request page → password → Approve). After connect, if
* the wallet reports the entity is not yet APPROVED, complete the KYC step.
*/
export async function addAndConnectProvider(
page: Page,
opts: { providerUrl: string; providerName: string; password: string },
opts: {
providerUrl: string;
providerName: string;
providerPubkey: string;
kycEntityName: string;
password: string;
},
): Promise<void> {
await page.bringToFront();
// Open the channel picker sheet (shows PrivateChannelManager).
Expand All @@ -418,7 +429,7 @@ export async function addAndConnectProvider(
await addToggle.waitFor({ timeout: 10_000 });
await clickWithPause(addToggle);

// Fill provider form.
// Fill provider form (url + name + pubkey).
await typeSlowly(
page.locator(SEL.providerUrlInput).first(),
opts.providerUrl,
Expand All @@ -427,6 +438,10 @@ export async function addAndConnectProvider(
page.locator(SEL.providerNameInput).first(),
opts.providerName,
);
await typeSlowly(
page.locator(SEL.providerPubkeyInput).first(),
opts.providerPubkey,
);

// Submit add — picks the form button (not the toggle we just used).
// Both have text "Add Provider" — prefer the visible submit inside the form card.
Expand All @@ -450,8 +465,35 @@ export async function addAndConnectProvider(
);
await clickWithPause(page.locator(SEL.signRequestApproveButton).first());

// Success: navigates back to home with provider connected. The action
// buttons enable (Receive visible) and the header dot turns green.
// After Approve the popup navigates back to home with the channel-picker
// sheet closed. The KYC prompt lives inside that sheet, so re-open it,
// then probe for the "Submit KYC" button and complete the form if shown.
await page.locator(SEL.channelPickerTrigger).first().waitFor({
timeout: 30_000,
});
await clickWithPause(page.locator(SEL.channelPickerTrigger).first());
const kycButton = page.locator(SEL.submitKycButton).first();
if (await kycButton.isVisible({ timeout: 10_000 }).catch(() => false)) {
await clickWithPause(kycButton);
await typeSlowly(
page.locator(SEL.kycNameInput).first(),
opts.kycEntityName,
);
await typeSlowly(
page.locator(SEL.kycPasswordInput).first(),
opts.password,
);
// The button label is identical, but only the form's submit is enabled
// when both inputs are filled — pick the enabled one.
await clickWithPause(page.locator(SEL.submitKycButton).last());
}
// Close the sheet (whether the KYC step ran or not) so home (with
// Receive/Send/Ramp) is in view and not blocked by the sheet overlay.
await page.keyboard.press("Escape");
await page.waitForTimeout(500);

// Success: action buttons enable (Receive visible) and the header dot
// turns green.
await page.locator(SEL.receiveButton).first().waitFor({ timeout: 60_000 });
await page
.locator('header span.bg-green-400, header [class*="bg-green-400"]')
Expand Down
33 changes: 31 additions & 2 deletions recording/playwright/specs/03-private-transfer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,31 @@ test.describe.configure({ mode: "serial" });
test("03 — private transfer (Bob receive → Alice deposit + send → Alice withdraw)", async () => {
const env = loadRunEnv();
const channelId = requireValue(env, "PRIVACY_CHANNEL_ID");
const councilId = requireValue(env, "CHANNEL_AUTH_ID");

// env.PP_PK is the operator pubkey from setup-recording-keys. The PP that
// spec 02 registered with the platform has its own server-derived pubkey,
// which is what /providers/:ppPublicKey/... URLs use. Look it up via the
// council-platform's public listing, matching on the spec-02 label.
const providerLabel = getProviderName();
const provListRes = await fetch(
`${env.COUNCIL_PLATFORM_URL}/api/v1/public/providers?councilId=${councilId}`,
);
if (!provListRes.ok) {
throw new Error(
`Failed to list providers: HTTP ${provListRes.status}`,
);
}
const provListBody = await provListRes.json() as {
data?: { publicKey: string; label: string }[];
};
const match = provListBody.data?.find((p) => p.label === providerLabel);
if (!match?.publicKey) {
throw new Error(
`Could not find provider "${providerLabel}" on council ${councilId}`,
);
}
const providerPubkey = match.publicKey;

const bobHandle = await launchWalletContext({
section: "03-bob",
Expand Down Expand Up @@ -71,7 +96,9 @@ test("03 — private transfer (Bob receive → Alice deposit + send → Alice wi
});
await addAndConnectProvider(bobWallet, {
providerUrl: env.PROVIDER_PLATFORM_URL,
providerName: getProviderName(),
providerName: providerLabel,
providerPubkey,
kycEntityName: "Bob",
password: RECORDING_PASSWORD,
});

Expand Down Expand Up @@ -108,7 +135,9 @@ test("03 — private transfer (Bob receive → Alice deposit + send → Alice wi
});
await addAndConnectProvider(aliceWallet, {
providerUrl: env.PROVIDER_PLATFORM_URL,
providerName: getProviderName(),
providerName: providerLabel,
providerPubkey,
kycEntityName: "Alice",
password: RECORDING_PASSWORD,
});

Expand Down
Loading