aboutsummaryrefslogtreecommitdiffstats
path: root/test/e2e/custodial-send-lightning.spec.ts
blob: 88f87f403d027c53074318deaaf61f6523a265f6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { expect, test } from "@playwright/test";
import {
  aliceUsername,
  alicePassword,
  bobUsername,
  bobPassword,
  apiBaseUrl,
  loginNewContext,
  pasteAndSend,
  waitForSentRedirect,
  waitForPaidRedirect,
} from "./helpers";

test("custodial-to-custodial via lightning bolt11 sends internally and notifies recipient", async ({
  browser,
}) => {
  test.setTimeout(90_000);

  // --- Alice: log in and create a lightning invoice with amount via API ---
  const { context: aliceContext, page: alicePage } = await loginNewContext(
    browser,
    aliceUsername,
    alicePassword,
  );

  // Get auth token
  const tokenRes = await alicePage.request.post(`${apiBaseUrl}/login`, {
    data: { username: aliceUsername, password: alicePassword },
  });
  const { token } = await tokenRes.json();

  // Create lightning invoice with amount 100 sats
  const invoiceRes = await alicePage.request.post(`${apiBaseUrl}/invoice`, {
    headers: { authorization: `Bearer ${token}` },
    data: {
      invoice: { type: "lightning", amount: 100 },
      user: { username: aliceUsername },
    },
  });
  const { id: invoiceId } = await invoiceRes.json();
  expect(invoiceId).toBeTruthy();

  // Get the bolt11 payreq
  const invDetailRes = await alicePage.request.get(`${apiBaseUrl}/invoice/${invoiceId}`);
  const invDetail = await invDetailRes.json();
  const bolt11 = invDetail.text;
  expect(bolt11).toBeTruthy();
  console.log(`[e2e] Alice bolt11 invoice: ${invoiceId}, bolt11: ${bolt11.substring(0, 40)}...`);

  // Alice navigates to invoice page (subscribes to websocket notifications)
  await alicePage.goto(`/invoice/${invoiceId}`);
  await alicePage.waitForURL(new RegExp(`/invoice/${invoiceId}`));

  // --- Bob: paste bolt11 into /send ---
  const { context: bobContext, page: bobPage } = await loginNewContext(
    browser,
    bobUsername,
    bobPassword,
  );

  await pasteAndSend(bobPage, bolt11);

  let bobUrl = bobPage.url();
  console.log(`[e2e] Bob navigated to: ${bobUrl}`);

  // Handle tip page if present (Alice may have tip prompt enabled)
  if (bobUrl.includes("/tip")) {
    console.log("[e2e] Tip page detected, clicking 'No' to skip tip...");
    const noTipButton = bobPage.locator("button", { hasText: "No" });
    await expect(noTipButton).toBeVisible({ timeout: 5_000 });
    await noTipButton.click();

    // After skipping tip, redirects to /invoice/{id} → then to /send/invoice/{id}
    await bobPage.waitForURL((url) => !url.pathname.includes("/tip"), { timeout: 15_000 });
    bobUrl = bobPage.url();
    console.log(`[e2e] After tip skip: ${bobUrl}`);
  }

  // Now Bob should be on /send/invoice/{id} or similar
  if (bobUrl.includes("/send/invoice/")) {
    // Internal path — amount is set on invoice, Send button should be visible
    const sendBtn = bobPage.locator('button[type="submit"]:visible');
    await expect(sendBtn).toBeVisible({ timeout: 5_000 });
    await sendBtn.click();
  } else if (bobUrl.includes("/send/lightning/")) {
    // External lightning path — amount is embedded, Send button visible
    const sendBtn = bobPage.locator('button[type="submit"]:visible');
    await expect(sendBtn).toBeVisible({ timeout: 5_000 });
    await sendBtn.click();
  } else if (bobUrl.includes("/invoice/")) {
    // On invoice view — navigate to send page
    const id = bobUrl.match(/\/invoice\/([^/?#]+)/)?.[1];
    if (id) {
      await bobPage.goto(`/send/invoice/${id}`);
      await bobPage.waitForURL(/\/send\/invoice\//);
    }
    const sendBtn = bobPage.locator('button[type="submit"]:visible');
    await expect(sendBtn).toBeVisible({ timeout: 5_000 });
    await sendBtn.click();
  }

  await waitForSentRedirect(bobPage, 30_000);
  console.log(`[e2e] Bob payment sent: ${bobPage.url()}`);

  // --- Verify Alice receives notification ---
  await waitForPaidRedirect(alicePage, invoiceId, 30_000);
  console.log(`[e2e] Alice redirected to: ${alicePage.url()}`);

  const successText = await alicePage.locator("h1").first().innerText({ timeout: 5_000 });
  expect(
    successText.toLowerCase().includes("payment") ||
      successText.toLowerCase().includes("success") ||
      successText.toLowerCase().includes("detected"),
    `Expected payment success text, got: ${successText}`,
  ).toBeTruthy();

  await aliceContext.close();
  await bobContext.close();
});