Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8abce51
feat(icons): add nostr-wallet-connect SVG icon
esaugomez31 Mar 26, 2026
68f22cd
feat(i18n): add nostrWalletConnect settings label
esaugomez31 Mar 26, 2026
8969cd8
feat(settings): add Nostr Wallet Connect entry point in Advanced section
esaugomez31 Mar 26, 2026
32e3260
fix(dropdown): align minHeight and padding with InputField component
esaugomez31 Mar 27, 2026
8e3505a
refactor(dropdown): replace inline empty object with conditional style
esaugomez31 Mar 27, 2026
09937e8
feat(icons): add NWC indicator SVGs and register in GaloyIcon
esaugomez31 Mar 27, 2026
5add912
feat(i18n): add NostrWalletConnect translation namespace
esaugomez31 Mar 27, 2026
2e11b30
feat(nwc): add IconHero component
esaugomez31 Mar 27, 2026
cdce602
feat(nwc): add dashed line flow animation hook
esaugomez31 Mar 27, 2026
6bd474c
feat(nwc): add NWC connection hooks
esaugomez31 Mar 27, 2026
a51192a
feat(nwc): add NWC screens
esaugomez31 Mar 27, 2026
9e8222d
feat(nwc): register NWC screens in navigation and update settings entry
esaugomez31 Mar 27, 2026
a94e113
test(settings): add NWC settings row test
esaugomez31 Mar 27, 2026
bf23f8a
test(nwc): add IconHero component tests
esaugomez31 Mar 27, 2026
807fb96
test(nwc): add dashed line flow animation tests
esaugomez31 Mar 27, 2026
e73a76f
test(nwc): add NWC connections hook tests
esaugomez31 Mar 27, 2026
12344d9
test(nwc): add new connection hook tests
esaugomez31 Mar 27, 2026
486b48e
test(nwc): add empty state screen tests
esaugomez31 Mar 27, 2026
deab740
test(nwc): add new connection form screen tests
esaugomez31 Mar 27, 2026
b3a6112
test(nwc): add connection created screen tests
esaugomez31 Mar 27, 2026
969384f
test(nwc): add connected apps list screen tests
esaugomez31 Mar 27, 2026
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
41 changes: 41 additions & 0 deletions __tests__/components/animations/dashed-line-flow.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { renderHook } from "@testing-library/react-native"

import { useDashedLineFlow } from "@app/components/animations/dashed-line-flow"

jest.mock("react-native-reanimated", () => ({
__esModule: true,
default: {},
useSharedValue: (initial: number) => ({ value: initial }),
useAnimatedProps: (factory: () => Record<string, number>) => factory(),
withRepeat: jest.fn(),
withTiming: jest.fn(),
Easing: { linear: "linear" },
}))

describe("useDashedLineFlow", () => {
it("returns dashArray with default values '5 5'", () => {
const { result } = renderHook(() => useDashedLineFlow())

expect(result.current.dashArray).toBe("5 5")
})

it("returns custom dashArray when params are provided", () => {
const { result } = renderHook(() =>
useDashedLineFlow({ dashLength: 10, gapLength: 3 }),
)

expect(result.current.dashArray).toBe("10 3")
})

it("returns animatedProps", () => {
const { result } = renderHook(() => useDashedLineFlow())

expect(result.current.animatedProps).toBeDefined()
})

it("returns animatedProps with strokeDashoffset", () => {
const { result } = renderHook(() => useDashedLineFlow())

expect(result.current.animatedProps).toEqual({ strokeDashoffset: 0 })
})
})
42 changes: 42 additions & 0 deletions __tests__/components/icon-hero.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from "react"
import { render } from "@testing-library/react-native"
import { loadLocale } from "@app/i18n/i18n-util.sync"

import { IconHero } from "@app/components/icon-hero"
import { ContextForScreen } from "../screens/helper"

loadLocale("en")

describe("IconHero", () => {
it("renders the title", async () => {
const { getByText } = render(
<ContextForScreen>
<IconHero icon="nostr-wallet-connect" title="Test Title" />
</ContextForScreen>,
)

expect(getByText("Test Title")).toBeTruthy()
})

it("renders subtitle when provided", async () => {
const { getByText } = render(
<ContextForScreen>
<IconHero icon="nostr-wallet-connect" title="Title" subtitle="Sub text" />
</ContextForScreen>,
)

expect(getByText("Title")).toBeTruthy()
expect(getByText("Sub text")).toBeTruthy()
})

it("does not render subtitle when not provided", async () => {
const { getByText, queryByText } = render(
<ContextForScreen>
<IconHero icon="nostr-wallet-connect" title="Only Title" />
</ContextForScreen>,
)

expect(getByText("Only Title")).toBeTruthy()
expect(queryByText("Sub text")).toBeNull()
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import React from "react"
import { render, fireEvent, act } from "@testing-library/react-native"
import { loadLocale } from "@app/i18n/i18n-util.sync"
import { i18nObject } from "@app/i18n/i18n-util"

import { NwcConnectedAppsListScreen } from "@app/screens/nostr-wallet-connect/connected-apps-list-screen"
import { ContextForScreen } from "../helper"

loadLocale("en")
const LL = i18nObject("en")

const mockNavigate = jest.fn()
const mockReplace = jest.fn()

jest.mock("@react-navigation/native", () => {
const actualNav = jest.requireActual("@react-navigation/native")
return {
...actualNav,
useNavigation: () => ({
navigate: mockNavigate,
replace: mockReplace,
}),
}
})

const mockRemoveConnection = jest.fn()
const mockConnections = [
{
id: "conn-1",
appName: "Amethyst",
dailyBudgetSats: 10_000,
connectionString: "nostr+walletconnect://abc",
createdAt: 1700000000000,
},
{
id: "conn-2",
appName: "Damus",
dailyBudgetSats: 1_000,
connectionString: "nostr+walletconnect://xyz",
createdAt: 1700000001000,
},
]

jest.mock("@app/screens/nostr-wallet-connect/hooks", () => ({
useNwcConnections: () => ({
connections: mockConnections,
removeConnection: mockRemoveConnection,
hasConnections: true,
}),
}))

jest.mock("@app/hooks/use-display-currency", () => ({
useDisplayCurrency: () => ({
formatMoneyAmount: ({ moneyAmount }: { moneyAmount: { amount: number } }) =>
`${moneyAmount.amount} SAT`,
}),
}))

jest.mock("@app/components/custom-modal/custom-modal", () => ({
__esModule: true,
default: ({
isVisible,
title,
body,
primaryButtonTitle,
primaryButtonOnPress,
secondaryButtonTitle,
secondaryButtonOnPress,
}: {
isVisible: boolean
title: string
body: React.ReactNode
primaryButtonTitle: string
primaryButtonOnPress: () => void
secondaryButtonTitle: string
secondaryButtonOnPress: () => void
}) => {
const { Text, View, Pressable } = jest.requireActual("react-native")
if (!isVisible) return null
return (
<View testID="delete-modal">
<Text>{title}</Text>
{body}
<Pressable onPress={primaryButtonOnPress}>
<Text>{primaryButtonTitle}</Text>
</Pressable>
<Pressable onPress={secondaryButtonOnPress} testID="confirm-delete">
<Text>{secondaryButtonTitle}</Text>
</Pressable>
</View>
)
},
}))

describe("NwcConnectedAppsListScreen", () => {
beforeEach(() => {
loadLocale("en")
jest.clearAllMocks()
})

it("renders connection cards", async () => {
const { getByText } = render(
<ContextForScreen>
<NwcConnectedAppsListScreen />
</ContextForScreen>,
)

await act(async () => {})

expect(getByText("Amethyst")).toBeTruthy()
expect(getByText("Damus")).toBeTruthy()
})

it("renders budget for each connection", async () => {
const { getByText } = render(
<ContextForScreen>
<NwcConnectedAppsListScreen />
</ContextForScreen>,
)

await act(async () => {})

expect(getByText(LL.NostrWalletConnect.budget({ amount: "10000 SAT" }))).toBeTruthy()
expect(getByText(LL.NostrWalletConnect.budget({ amount: "1000 SAT" }))).toBeTruthy()
})

it("shows delete modal on close icon press", async () => {
const { getByText, queryByTestId, getAllByTestId } = render(
<ContextForScreen>
<NwcConnectedAppsListScreen />
</ContextForScreen>,
)

await act(async () => {})

expect(queryByTestId("delete-modal")).toBeNull()

const closeIcons = getAllByTestId("icon-close")
await act(async () => {
fireEvent.press(closeIcons[0])
})

expect(getByText(LL.NostrWalletConnect.deleteConfirmTitle())).toBeTruthy()
})

it("removes connection on confirm delete", async () => {
const { getByTestId, getAllByTestId } = render(
<ContextForScreen>
<NwcConnectedAppsListScreen />
</ContextForScreen>,
)

await act(async () => {})

const closeIcons = getAllByTestId("icon-close")
await act(async () => {
fireEvent.press(closeIcons[0])
})

const confirmButton = getByTestId("confirm-delete")
await act(async () => {
fireEvent.press(confirmButton)
})

expect(mockRemoveConnection).toHaveBeenCalledWith("conn-1")
})

it("renders threshold field", async () => {
const { getByText } = render(
<ContextForScreen>
<NwcConnectedAppsListScreen />
</ContextForScreen>,
)

await act(async () => {})

expect(getByText(LL.NostrWalletConnect.doNotNotifyBelow())).toBeTruthy()
})

it("renders the new connection button", async () => {
const { getByText } = render(
<ContextForScreen>
<NwcConnectedAppsListScreen />
</ContextForScreen>,
)

await act(async () => {})

expect(getByText(LL.NostrWalletConnect.newConnection())).toBeTruthy()
})

it("navigates to nwcNewConnection on new connection press", async () => {
const { getByText } = render(
<ContextForScreen>
<NwcConnectedAppsListScreen />
</ContextForScreen>,
)

await act(async () => {})

const button = getByText(LL.NostrWalletConnect.newConnection())
await act(async () => {
fireEvent.press(button)
})

expect(mockNavigate).toHaveBeenCalledWith("nwcNewConnection")
})
})
Loading
Loading