Skip to content
2 changes: 1 addition & 1 deletion .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ jobs:
# - { name: onchain_boost_receive_widgets, grep: "@onchain|@boost|@receive|@widgets" }
# - { name: settings, grep: "@settings" }
# - { name: security, grep: "@security" }
- { name: e2e, grep: '@send|@lnurl|@lightning|@backup|@onboarding|@onchain_1|@onchain_2|@numberpad|@widgets|@boost|@receive|@settings|@security' }
- { name: e2e, grep: '@transfer|@send|@lnurl|@lightning|@backup|@onboarding|@onchain_1|@onchain_2|@numberpad|@widgets|@boost|@receive|@settings|@security' }

name: e2e-tests - ${{ matrix.shard.name }}

Expand Down
3 changes: 2 additions & 1 deletion Bitkit/Components/NumberPadTextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ struct NumberPadTextField: View {
}
.contentShape(Rectangle())
.animation(springAnimation, value: currency.primaryDisplay)
.accessibilityElement(children: .contain)
.accessibilityIdentifierIfPresent(testIdentifier)
}

@ViewBuilder
Expand All @@ -87,7 +89,6 @@ struct NumberPadTextField: View {
+ Text(viewModel.getPlaceholder(currency: currency))
.foregroundColor(isFocused ? .textSecondary : .textPrimary))
.font(.custom(Fonts.black, size: 44))
.accessibilityIdentifierIfPresent(testIdentifier)
}
}
}
34 changes: 29 additions & 5 deletions Bitkit/ViewModels/ChannelDetailsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ class ChannelDetailsViewModel: ObservableObject {
@Published var error: Error? = nil

private let coreService: CoreService
private let transferStorage: TransferStorage

/// Private initializer for the singleton instance
private init(coreService: CoreService = .shared) {
private init(coreService: CoreService = .shared, transferStorage: TransferStorage = .shared) {
self.coreService = coreService
self.transferStorage = transferStorage
}

/// Find a channel by ID, checking open channels, pending channels, pending orders, then closed channels
Expand Down Expand Up @@ -124,15 +126,31 @@ class ChannelDetailsViewModel: ObservableObject {
connections.append(contentsOf: channels.filter { !$0.isChannelReady })
}

// Only show pending orders that have been paid (aligns with Android/RN behavior)
let paidOrderIds: Set<String> = {
guard let activeTransfers = try? transferStorage.getActiveTransfers() else {
return []
}
return Set(
activeTransfers
.filter { $0.type.isToSpending() }
.compactMap(\.lspOrderId)
)
}()

if paidOrderIds.isEmpty {
return connections
}

// Create fake channels from pending orders
guard let orders = try? await coreService.blocktank.orders(refresh: false) else {
return connections
}

let pendingOrders = orders.filter { order in
// Include orders that are created or paid but not yet opened
order.state2 == .created || order.state2 == .paid
}
let pendingOrders = Self.pendingOrders(
orders: orders,
paidOrderIds: paidOrderIds
)

for order in pendingOrders {
let fakeChannel = createFakeChannel(from: order)
Expand All @@ -142,6 +160,12 @@ class ChannelDetailsViewModel: ObservableObject {
return connections
}

static func pendingOrders(orders: [IBtOrder], paidOrderIds: Set<String>) -> [IBtOrder] {
orders.filter { order in
paidOrderIds.contains(order.id) && (order.state2 == .created || order.state2 == .paid)
}
}

/// Creates a fake channel from a Blocktank order for UI display purposes
private func createFakeChannel(from order: IBtOrder) -> ChannelDetails {
return ChannelDetails(
Expand Down
4 changes: 4 additions & 0 deletions Bitkit/ViewModels/TransferViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ class TransferViewModel: ObservableObject {
uiState.isAdvanced = true
}

func displayOrder(for order: IBtOrder) -> IBtOrder {
uiState.order ?? order
}

func payOrder(order: IBtOrder, speed: TransactionSpeed) async throws {
var fees = try? await coreService.blocktank.fees(refresh: true)
if fees == nil {
Expand Down
9 changes: 6 additions & 3 deletions Bitkit/Views/Settings/Advanced/LightningConnectionsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,13 @@ struct LightningConnectionsView: View {
.padding(.top, 16)

ForEach(Array(pendingConnections.enumerated()), id: \.element.channelId) { index, channel in
let labelIndex = pendingConnections.count - index
Button {
navigation.navigate(.connectionDetail(channelId: channel.channelIdString))
} label: {
VStack(spacing: 0) {
HStack {
SubtitleText("\(t("lightning__connection")) \(index + 1)")
SubtitleText("\(t("lightning__connection")) \(labelIndex)")
Spacer()
Image("chevron")
.resizable()
Expand Down Expand Up @@ -109,12 +110,13 @@ struct LightningConnectionsView: View {
.padding(.top, 16)

ForEach(Array(openChannels.enumerated()), id: \.element.channelId) { index, channel in
let labelIndex = openChannels.count - index
Button {
navigation.navigate(.connectionDetail(channelId: channel.channelIdString))
} label: {
VStack(spacing: 0) {
HStack {
SubtitleText("\(t("lightning__connection")) \(index + 1)")
SubtitleText("\(t("lightning__connection")) \(labelIndex)")
Spacer()
Image("chevron")
.resizable()
Expand Down Expand Up @@ -147,12 +149,13 @@ struct LightningConnectionsView: View {
.padding(.top, 16)

ForEach(Array(closedChannels.enumerated()), id: \.element.channelId) { index, channel in
let labelIndex = closedChannels.count - index
Button {
navigation.navigate(.connectionDetail(channelId: channel.channelIdString))
} label: {
VStack(spacing: 0) {
HStack {
SubtitleText("\(t("lightning__connection")) \(index + 1)")
SubtitleText("\(t("lightning__connection")) \(labelIndex)")
Spacer()
Image("chevron")
.resizable()
Expand Down
1 change: 1 addition & 0 deletions Bitkit/Views/Transfer/FundManualAmountView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ struct FundManualAmountView: View {
isDisabled: amountSats == 0,
destination: FundManualConfirmView(lnPeer: lnPeer, amountSats: amountSats)
)
.accessibilityIdentifier("ExternalAmountContinue")
}
}
.navigationBarHidden(true)
Expand Down
1 change: 1 addition & 0 deletions Bitkit/Views/Transfer/SavingsAvailabilityView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct SavingsAvailabilityView: View {
CustomButton(title: t("common__continue")) {
navigation.navigate(.savingsConfirm)
}
.accessibilityIdentifier("AvailabilityContinue")
}
}
.navigationBarHidden(true)
Expand Down
2 changes: 2 additions & 0 deletions Bitkit/Views/Transfer/SavingsProgressView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ struct SavingsProgressContentView: View {
.frame(width: 256, height: 256)
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.accessibilityIdentifierIfPresent(progressState == .success ? "TransferSuccess" : nil)
}

Spacer()
Expand All @@ -109,6 +110,7 @@ struct SavingsProgressContentView: View {
) {
navigation.reset()
}
.accessibilityIdentifierIfPresent(progressState == .success ? "TransferSuccess-button" : nil)
}
.navigationBarHidden(true)
.padding(.horizontal, 16)
Expand Down
3 changes: 3 additions & 0 deletions Bitkit/Views/Transfer/SettingUpView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,15 @@ struct SettingUpView: View {

if isTransferring {
SettingUpLoadingView()
.accessibilityIdentifier("LightningSettingUp")
} else {
Image("check")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 256, height: 256)
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.accessibilityIdentifier("TransferSuccess")
}

Spacer()
Expand All @@ -188,6 +190,7 @@ struct SettingUpView: View {
CustomButton(title: buttonTitle) {
navigation.reset()
}
.accessibilityIdentifier("TransferSuccess-button")
}
}
.navigationBarHidden(true)
Expand Down
18 changes: 13 additions & 5 deletions Bitkit/Views/Transfer/SpendingAdvancedView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@ struct SpendingAdvancedView: View {
DisplayText(t("lightning__spending_advanced__title"), accentColor: .purpleAccent)
.fixedSize(horizontal: false, vertical: true)

NumberPadTextField(viewModel: amountViewModel, showConversion: false)
.onTapGesture {
amountViewModel.togglePrimaryDisplay(currency: currency)
}
.padding(.top, 32)
NumberPadTextField(
viewModel: amountViewModel,
showConversion: false,
testIdentifier: "SpendingAdvancedNumberField"
)
.onTapGesture {
amountViewModel.togglePrimaryDisplay(currency: currency)
}
.padding(.top, 32)

// Fee estimate
HStack(spacing: 4) {
Expand Down Expand Up @@ -91,6 +95,7 @@ struct SpendingAdvancedView: View {
app.toast(error)
}
}
.accessibilityIdentifier("SpendingAdvancedContinue")
}
}
.navigationBarHidden(true)
Expand Down Expand Up @@ -118,18 +123,21 @@ struct SpendingAdvancedView: View {
NumberPadActionButton(text: t("common__min")) {
amountViewModel.updateFromSats(transfer.transferValues.minLspBalance, currency: currency)
}
.accessibilityIdentifier("SpendingAdvancedMin")

Spacer()

NumberPadActionButton(text: t("common__default")) {
amountViewModel.updateFromSats(transfer.transferValues.defaultLspBalance, currency: currency)
}
.accessibilityIdentifier("SpendingAdvancedDefault")

Spacer()

NumberPadActionButton(text: t("common__max")) {
amountViewModel.updateFromSats(transfer.transferValues.maxLspBalance, currency: currency)
}
.accessibilityIdentifier("SpendingAdvancedMax")
}
}

Expand Down
3 changes: 3 additions & 0 deletions Bitkit/Views/Transfer/SpendingAmount.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ struct SpendingAmount: View {
) {
await onContinue()
}
.accessibilityIdentifier("SpendingAmountContinue")
}
.navigationBarHidden(true)
.padding(.horizontal, 16)
Expand All @@ -96,11 +97,13 @@ struct SpendingAmount: View {
let quarter = UInt64(wallet.spendableOnchainBalanceSats) / 4
amountViewModel.updateFromSats(min(quarter, max), currency: currency)
}
.accessibilityIdentifier("SpendingAmountQuarter")

NumberPadActionButton(text: t("common__max")) {
guard let max = maxTransferAmount else { return }
amountViewModel.updateFromSats(max, currency: currency)
}
.accessibilityIdentifier("SpendingAmountMax")
}
}

Expand Down
31 changes: 20 additions & 11 deletions Bitkit/Views/Transfer/SpendingConfirm.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ struct SpendingConfirm: View {
@State private var hideSwipeButton = false
@State private var transactionFee: UInt64 = 0

private var currentOrder: IBtOrder {
transfer.displayOrder(for: order)
}

var lspFee: UInt64 {
order.feeSat - order.clientBalanceSat
currentOrder.feeSat - currentOrder.clientBalanceSat
}

var total: UInt64 {
order.feeSat + transactionFee
currentOrder.feeSat + transactionFee
}

var body: some View {
Expand Down Expand Up @@ -47,7 +51,7 @@ struct SpendingConfirm: View {
HStack {
FeeDisplayRow(
label: t("lightning__spending_confirm__amount"),
amount: order.clientBalanceSat
amount: currentOrder.clientBalanceSat
)
.frame(maxWidth: .infinity)

Expand All @@ -62,12 +66,14 @@ struct SpendingConfirm: View {

if transfer.uiState.isAdvanced {
LightningChannel(
capacity: order.lspBalanceSat + order.clientBalanceSat,
localBalance: order.clientBalanceSat,
remoteBalance: order.lspBalanceSat,
capacity: currentOrder.lspBalanceSat + currentOrder.clientBalanceSat,
localBalance: currentOrder.clientBalanceSat,
remoteBalance: currentOrder.lspBalanceSat,
status: .open,
showLabels: true
)
.accessibilityElement(children: .ignore)
.accessibilityIdentifier("SpendingConfirmChannel")
.padding(.vertical, 16)
}

Expand All @@ -87,17 +93,20 @@ struct SpendingConfirm: View {

HStack(spacing: 16) {
CustomButton(title: t("common__learn_more"), size: .small) {
navigation.navigate(.transferLearnMore(order: order))
navigation.navigate(.transferLearnMore(order: currentOrder))
}
.accessibilityIdentifier("SpendingConfirmMore")

if transfer.uiState.isAdvanced {
CustomButton(title: t("lightning__spending_confirm__default"), size: .small) {
transfer.onDefaultClick()
}
.accessibilityIdentifier("SpendingConfirmDefault")
} else {
CustomButton(title: t("common__advanced"), size: .small) {
navigation.navigate(.spendingAdvanced(order: order))
navigation.navigate(.spendingAdvanced(order: currentOrder))
}
.accessibilityIdentifier("SpendingConfirmAdvanced")
}
}
.frame(maxWidth: .infinity, alignment: .leading)
Expand Down Expand Up @@ -125,7 +134,7 @@ struct SpendingConfirm: View {
isPaying = true

do {
try await transfer.payOrder(order: order, speed: .fast)
try await transfer.payOrder(order: currentOrder, speed: .fast)
try await Task.sleep(nanoseconds: 1_000_000_000)

navigation.navigate(.settingUp)
Expand All @@ -148,13 +157,13 @@ struct SpendingConfirm: View {
if let feeRates = try await coreService.blocktank.fees(refresh: true) {
let fastFeeRate = TransactionSpeed.fast.getFeeRate(from: feeRates)

guard let address = order.payment?.onchain?.address else {
guard let address = currentOrder.payment?.onchain?.address else {
throw AppError(message: "Order payment onchain address is nil", debugMessage: nil)
}

let fee = try await wallet.calculateTotalFee(
address: address,
amountSats: order.feeSat,
amountSats: currentOrder.feeSat,
satsPerVByte: fastFeeRate
)

Expand Down
1 change: 1 addition & 0 deletions Bitkit/Views/Transfer/TransferLearnMoreView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ struct TransferLearnMoreView: View {
dismiss()
}
.padding(.top, 32)
.accessibilityIdentifier("LiquidityContinue")
}
.navigationBarHidden(true)
.padding(.horizontal, 16)
Expand Down
Loading
Loading