Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: handle lnurl success action #3362

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
788c041
feat(lnurl): implement successAction to display message on send-bitco…
esaugomez31 Feb 14, 2025
9f9479f
revert(config): restore default endOfLine setting to 'lf'
esaugomez31 Feb 14, 2025
1a77f0f
fix(lnurl): ensure successAction updates persist in confirmation screen
esaugomez31 Feb 14, 2025
cba0563
Merge branch 'main' into feat--handle-lnurl-success-action
esaugomez31 Feb 17, 2025
9046c4f
chore: improve code structure and optimization
esaugomez31 Feb 21, 2025
da0627a
feat: url success action - LUD-09
esaugomez31 Feb 21, 2025
e655e1c
revert: restore previous duration for Send Bitcoin Completed screen
esaugomez31 Feb 21, 2025
8645fd8
feat: LUD-10 AES implemented using decipherAES
esaugomez31 Feb 24, 2025
8633633
test: send bitcoin completed screen
esaugomez31 Feb 24, 2025
7c06888
fix(success-action): open link button UI
esaugomez31 Feb 26, 2025
a4fe8b0
test(send bitcoin screen): LUD-09 tests (message|url|url + message)
esaugomez31 Feb 26, 2025
d6626cb
fix: graphql lnInvoicePaymentSend transaction object added
esaugomez31 Feb 26, 2025
ad8a973
fix(tsc): preimage validated to SettlementViaLn type
esaugomez31 Feb 27, 2025
6b7c942
chore: code refatoring in the send bitcoin completed and send bitcoin…
esaugomez31 Feb 28, 2025
728fecd
test: LUD-10 (AES) to send bitcoin completed screen
esaugomez31 Feb 28, 2025
a10bb25
feat: description support added to LUD-10 AES
esaugomez31 Feb 28, 2025
7dee4af
feat(send-bitcoin-completed): disable auto-redirect after successful …
esaugomez31 Feb 28, 2025
ff70e8d
test: send bitcoin confirmation screen test
esaugomez31 Feb 28, 2025
d1600df
chore: updating language labels to send bitcoin completed screen and …
esaugomez31 Feb 28, 2025
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
135 changes: 135 additions & 0 deletions __tests__/screens/send-bitcoin-completed-screen.spec.tsx
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are missing aes test... you can use this data

    successAction: {
      tag: "aes",
      description: "Here is your redeem code",
      ciphertext: "564u3BEMRefWUV1098gJ5w==",
      iv: "IhkC5ktKB9LG91FvlbN2kg==",
    },
    preimage: "25004cd52960a3bac983e3f95c432341a7052cef37b9253b0b0b1256d754559b",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thanks!

Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import React from "react"
import { fireEvent, render, screen, waitFor } from "@testing-library/react-native"
import { loadLocale } from "@app/i18n/i18n-util.sync"
import { i18nObject } from "@app/i18n/i18n-util"
import {
Success,
Queued,
Pending,
SuccessLUD09Message,
SuccessLUD09URL,
SuccessLUD09URLWithDesc,
SuccessLUD10AES,
} from "@app/screens/send-bitcoin-screen/send-bitcoin-completed-screen.stories"
import { ContextForScreen } from "./helper"
import { Linking } from "react-native"

jest.mock("react-native-in-app-review", () => ({
isAvailable: () => true,
RequestInAppReview: jest.fn(),
}))

describe("SendBitcoinCompletedScreen", () => {
let LL: ReturnType<typeof i18nObject>

beforeEach(() => {
loadLocale("en")
LL = i18nObject("en")
})

it("renders the Success state correctly", async () => {
render(
<ContextForScreen>
<Success />
</ContextForScreen>,
)

const successTextElement = await waitFor(() => screen.findByTestId("Success Text"))
expect(successTextElement.props.children).toContain(
"Payment has been sent successfully",
)
})

it("renders the Queued state correctly", async () => {
render(
<ContextForScreen>
<Queued />
</ContextForScreen>,
)

const queuedTextElement = await waitFor(() => screen.findByTestId("Success Text"))
expect(queuedTextElement.props.children).toEqual(
expect.stringContaining("Your transaction is queued"),
)
})

it("renders the Pending state correctly", async () => {
render(
<ContextForScreen>
<Pending />
</ContextForScreen>,
)

const pendingTextElement = await waitFor(() => screen.findByTestId("Success Text"))
expect(pendingTextElement.props.children).toEqual(
expect.stringContaining("The payment has been sent"),
)
})

it("render successAction - LUD 09 - message", async () => {
const message = "Thanks for your support."

render(
<ContextForScreen>
<SuccessLUD09Message />
</ContextForScreen>,
)

expect(screen.getByText(message)).toBeTruthy()
expect(screen.getByText(LL.SendBitcoinScreen.note())).toBeTruthy()
})

it("render successAction - LUD 09 - URL", async () => {
const url = "https://es.blink.sv"

render(
<ContextForScreen>
<SuccessLUD09URL />
</ContextForScreen>,
)

const button = screen.getByText(LL.ScanningQRCodeScreen.openLinkTitle())

expect(button).toBeTruthy()

fireEvent.press(button)

expect(Linking.openURL).toHaveBeenCalledWith(url)
})

it("render successAction - LUD 09 - URL with description", async () => {
const url = "https://es.blink.sv"
const description = "Example URL + description"

render(
<ContextForScreen>
<SuccessLUD09URLWithDesc />
</ContextForScreen>,
)

expect(screen.getByText(description)).toBeTruthy()

const button = screen.getByText(LL.ScanningQRCodeScreen.openLinkTitle())

expect(button).toBeTruthy()

fireEvent.press(button)

expect(Linking.openURL).toHaveBeenCalledWith(url)
})

it("render successAction - LUD 10 - message", async () => {
const description = "Here is your redeem code"
const encryptedMessage = "131313"

render(
<ContextForScreen>
<SuccessLUD10AES />
</ContextForScreen>,
)

expect(screen.getByText(description)).toBeTruthy()
expect(screen.getByText(encryptedMessage)).toBeTruthy()
expect(screen.getByText(LL.SendBitcoinScreen.note())).toBeTruthy()
})
})
102 changes: 81 additions & 21 deletions __tests__/screens/send-confirmation.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,86 @@
import React from "react"
import { act, render, screen } from "@testing-library/react-native"
import { loadLocale } from "@app/i18n/i18n-util.sync"
import { i18nObject } from "@app/i18n/i18n-util"
import {
Intraledger,
LightningLnURL,
} from "@app/screens/send-bitcoin-screen/send-bitcoin-confirmation-screen.stories"
import { ContextForScreen } from "./helper"

import { act, render } from "@testing-library/react-native"
jest.mock("@app/graphql/generated", () => ({
...jest.requireActual("@app/graphql/generated"),
useSendBitcoinConfirmationScreenQuery: jest.fn(() => ({
data: {
me: {
id: "mocked-user-id",
defaultAccount: {
id: "mocked-account-id",
wallets: [
{
id: "btc-wallet-id",
balance: 500000,
walletCurrency: "BTC",
},
{
id: "usd-wallet-id",
balance: 10000,
walletCurrency: "USD",
},
],
},
},
},
})),
}))

import { Intraledger } from "../../app/screens/send-bitcoin-screen/send-bitcoin-confirmation-screen.stories"
import { ContextForScreen } from "./helper"
describe("SendBitcoinConfirmationScreen", () => {
let LL: ReturnType<typeof i18nObject>

beforeEach(() => {
loadLocale("en")
LL = i18nObject("en")
})

it("Send Screen Confirmation - Intraledger Payment", async () => {
const { findByLabelText } = render(
<ContextForScreen>
<Intraledger />
</ContextForScreen>,
)

// it seems we need multiple act because the component re-render multiple times
// probably this could be debug with why-did-you-render
await act(
() =>
new Promise((resolve) => {
setTimeout(resolve, 10)
}),
)

const { children } = await findByLabelText("Successful Fee")
expect(children).toEqual(["₦0 ($0.00)"])
})

it("Send Screen Confirmation - Lightning lnurl Payment", async () => {
const lnurl = "lnurl1dp68gurn8ghj7mr..."

render(
<ContextForScreen>
<LightningLnURL />
</ContextForScreen>,
)

await act(
() =>
new Promise((resolve) => {
setTimeout(resolve, 10)
}),
)

it("SendScreen Confirmation", async () => {
const { findByLabelText } = render(
<ContextForScreen>
<Intraledger />
</ContextForScreen>,
)

// it seems we need multiple act because the component re-render multiple times
// probably this could be debug with why-did-you-render
await act(
() =>
new Promise((resolve) => {
setTimeout(resolve, 10)
}),
)

const { children } = await findByLabelText("Successful Fee")
expect(children).toEqual(["₦0 ($0.00)"])
expect(screen.getByText(lnurl)).toBeTruthy()
expect(screen.getByText("₦100 ($100.00)")).toBeTruthy()
expect(screen.getByTestId("slider")).toBeTruthy()
expect(LL.SendBitcoinConfirmationScreen.slideToConfirm()).toBeTruthy()
})
})
10 changes: 10 additions & 0 deletions app/graphql/generated.gql
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,16 @@ mutation lnInvoicePaymentSend($input: LnInvoicePaymentInput!) {
__typename
}
status
transaction {
settlementVia {
... on SettlementViaLn {
preImage
__typename
}
__typename
}
__typename
}
__typename
}
}
Expand Down
9 changes: 8 additions & 1 deletion app/graphql/generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2954,7 +2954,7 @@ export type LnInvoicePaymentSendMutationVariables = Exact<{
}>;


export type LnInvoicePaymentSendMutation = { readonly __typename: 'Mutation', readonly lnInvoicePaymentSend: { readonly __typename: 'PaymentSendPayload', readonly status?: PaymentSendResult | null, readonly errors: ReadonlyArray<{ readonly __typename: 'GraphQLApplicationError', readonly message: string }> } };
export type LnInvoicePaymentSendMutation = { readonly __typename: 'Mutation', readonly lnInvoicePaymentSend: { readonly __typename: 'PaymentSendPayload', readonly status?: PaymentSendResult | null, readonly errors: ReadonlyArray<{ readonly __typename: 'GraphQLApplicationError', readonly message: string }>, readonly transaction?: { readonly __typename: 'Transaction', readonly settlementVia: { readonly __typename: 'SettlementViaIntraLedger' } | { readonly __typename: 'SettlementViaLn', readonly preImage?: string | null } | { readonly __typename: 'SettlementViaOnChain' } } | null } };

export type LnNoAmountUsdInvoicePaymentSendMutationVariables = Exact<{
input: LnNoAmountUsdInvoicePaymentInput;
Expand Down Expand Up @@ -6533,6 +6533,13 @@ export const LnInvoicePaymentSendDocument = gql`
message
}
status
transaction {
settlementVia {
... on SettlementViaLn {
preImage
}
}
}
}
}
`;
Expand Down
5 changes: 5 additions & 0 deletions app/i18n/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2306,6 +2306,11 @@ const en: BaseTranslation = {
copiedDestination: "Copied destination to clipboard",
pendingPayment:
"The payment has been sent, but hasn't confirmed yet.\n\nIt's possible the payment will not confirm, in which case the funds will be returned to your account.",
copySuccessMessage: "Copy success message to clipboard",
openSuccessUrl: "Open success URL in browser",
copySecretMessage: "Copy secret message to clipboard",
copiedSuccessMessage: "Message copied successfully",
copiedSecretMessage: "Secret message copied successfully"
},
SettingsScreen: {
staticQr: "Printable Static QR Code",
Expand Down
40 changes: 40 additions & 0 deletions app/i18n/i18n-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7195,6 +7195,26 @@ type RootTranslation = {
​I​t​'​s​ ​p​o​s​s​i​b​l​e​ ​t​h​e​ ​p​a​y​m​e​n​t​ ​w​i​l​l​ ​n​o​t​ ​c​o​n​f​i​r​m​,​ ​i​n​ ​w​h​i​c​h​ ​c​a​s​e​ ​t​h​e​ ​f​u​n​d​s​ ​w​i​l​l​ ​b​e​ ​r​e​t​u​r​n​e​d​ ​t​o​ ​y​o​u​r​ ​a​c​c​o​u​n​t​.
*/
pendingPayment: string
/**
* C​o​p​y​ ​s​u​c​c​e​s​s​ ​m​e​s​s​a​g​e​ ​t​o​ ​c​l​i​p​b​o​a​r​d
*/
copySuccessMessage: string
/**
* O​p​e​n​ ​s​u​c​c​e​s​s​ ​U​R​L​ ​i​n​ ​b​r​o​w​s​e​r
*/
openSuccessUrl: string
/**
* C​o​p​y​ ​s​e​c​r​e​t​ ​m​e​s​s​a​g​e​ ​t​o​ ​c​l​i​p​b​o​a​r​d
*/
copySecretMessage: string
/**
* M​e​s​s​a​g​e​ ​c​o​p​i​e​d​ ​s​u​c​c​e​s​s​f​u​l​l​y
*/
copiedSuccessMessage: string
/**
* S​e​c​r​e​t​ ​m​e​s​s​a​g​e​ ​c​o​p​i​e​d​ ​s​u​c​c​e​s​s​f​u​l​l​y
*/
copiedSecretMessage: string
}
SettingsScreen: {
/**
Expand Down Expand Up @@ -16208,6 +16228,26 @@ export type TranslationFunctions = {
It's possible the payment will not confirm, in which case the funds will be returned to your account.
*/
pendingPayment: () => LocalizedString
/**
* Copy success message to clipboard
*/
copySuccessMessage: () => LocalizedString
/**
* Open success URL in browser
*/
openSuccessUrl: () => LocalizedString
/**
* Copy secret message to clipboard
*/
copySecretMessage: () => LocalizedString
/**
* Message copied successfully
*/
copiedSuccessMessage: () => LocalizedString
/**
* Secret message copied successfully
*/
copiedSecretMessage: () => LocalizedString
}
SettingsScreen: {
/**
Expand Down
7 changes: 6 additions & 1 deletion app/i18n/raw-i18n/source/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2235,7 +2235,12 @@
"confirmButton": "I'm 100% sure"
},
"copiedDestination": "Copied destination to clipboard",
"pendingPayment": "The payment has been sent, but hasn't confirmed yet.\n\nIt's possible the payment will not confirm, in which case the funds will be returned to your account."
"pendingPayment": "The payment has been sent, but hasn't confirmed yet.\n\nIt's possible the payment will not confirm, in which case the funds will be returned to your account.",
"copySuccessMessage": "Copy success message to clipboard",
"openSuccessUrl": "Open success URL in browser",
"copySecretMessage": "Copy secret message to clipboard",
"copiedSuccessMessage": "Message copied successfully",
"copiedSecretMessage": "Secret message copied successfully"
},
"SettingsScreen": {
"staticQr": "Printable Static QR Code",
Expand Down
3 changes: 3 additions & 0 deletions app/navigation/stack-param-lists.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { LNURLPaySuccessAction } from "lnurl-pay/dist/types/types"
import { PhoneCodeChannelType, UserContact, WalletCurrency } from "@app/graphql/generated"
import { EarnSectionType } from "@app/screens/earns-screen/sections"
import { PhoneLoginInitiateType } from "@app/screens/phone-auth-screen"
Expand Down Expand Up @@ -52,6 +53,8 @@ export type RootStackParamList = {
sendBitcoinCompleted: {
arrivalAtMempoolEstimate?: number
status: PaymentSendCompletedStatus
successAction?: LNURLPaySuccessAction
preimage?: string
}
language: undefined
currency: undefined
Expand Down
Loading
Loading