Skip to content

Commit

Permalink
Update tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hsjoberg committed Mar 5, 2020
1 parent f55cd8e commit fed3330
Show file tree
Hide file tree
Showing 15 changed files with 1,504 additions and 127 deletions.
4 changes: 0 additions & 4 deletions jestSetup.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
global.fetch = require('jest-fetch-mock');
fetch.mockResponse(JSON.stringify({
scores: [],
}));
jest.mock("react-native-camera", () => require("./mocks/react-native-camera"));
jest.mock("@react-native-community/async-storage", () => require("./mocks/@react-native-community/async-storage"));
jest.mock("react-native-sqlite-storage", () => require("./mocks/react-native-sqlite-storage"));
Expand Down
4 changes: 2 additions & 2 deletions mocks/lndmobile/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ export const listChannels = jest.fn(async (): Promise<lnrpc.ListChannelsResponse

export const channelBalance = jest.fn(async (): Promise<lnrpc.ChannelBalanceResponse> => {
const response = lnrpc.ChannelBalanceResponse.create({
balance: 0, // TODO
pendingOpenBalance: 0,
balance: Long.fromValue(0), // TODO
pendingOpenBalance: Long.fromValue(0),
});
return response;
});
Expand Down
4 changes: 2 additions & 2 deletions src/components/FooterNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ export default () => {
return (
<Footer>
<FooterTab>
<Button onPress={() => navigation.navigate("Receive")}>
<Button testID="FOOTER_RECEIVE" onPress={() => navigation.navigate("Receive")}>
{<Icon type="AntDesign" name="qrcode" />}
<Text>Receive</Text>
</Button>
</FooterTab>
<FooterTab>
<Button onPress={() => navigation.navigate("Send")}>
<Button testID="FOOTER_SEND" onPress={() => navigation.navigate("Send")}>
<Icon type="AntDesign" name="camerao" />
<Text>Send</Text>
</Button>
Expand Down
175 changes: 103 additions & 72 deletions src/state/Receive.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Action, action, Thunk, thunk } from "easy-peasy";
import { DeviceEventEmitter } from "react-native";
import { DeviceEventEmitter, EmitterSubscription } from "react-native";
import Long from "long";

import { IStoreModel } from "./index";
Expand All @@ -8,7 +8,7 @@ import { ITransaction } from "../storage/database/transaction";
import { lnrpc } from "../../proto/proto";
import { setupDescription } from "../utils/NameDesc";
import { valueFiat } from "../utils/bitcoin-units";
import { timeout, uint8ArrayToString, bytesToString, decodeTLVRecord, stringToUint8Array, bytesToHexString } from "../utils";
import { timeout, uint8ArrayToString, bytesToString, decodeTLVRecord, stringToUint8Array, bytesToHexString, toast } from "../utils";
import { TLV_RECORD_NAME } from "../utils/constants.ts";

import logger from "./../utils/log";
Expand All @@ -31,15 +31,18 @@ interface IReceiveModelAddInvoicePayload {

export interface IReceiveModel {
initialize: Thunk<IReceiveModel, void, IStoreInjections>;
deinitialize: Thunk<IReceiveModel>;

addInvoice: Thunk<IReceiveModel, IReceiveModelAddInvoicePayload, IStoreInjections, IStoreModel, Promise<lnrpc.AddInvoiceResponse>>;
subscribeInvoice: Thunk<IReceiveModel, void, IStoreInjections, IStoreModel>;
setInvoiceSubscriptionStarted: Action<IReceiveModel, boolean>;
setInvoiceSubscriptionResource: Action<IReceiveModel, EmitterSubscription | undefined>;

setInvoiceTmpData: Action<IReceiveModel, IInvoiceTempData>;
deleteInvoiceTmpData: Action<IReceiveModel, string>;

invoiceSubscriptionStarted: boolean;
invoiceSubscriptionStarted: boolean; // TODO make computed value
invoiceSubscription?: EmitterSubscription;
invoiceTempData: { [key: string]: IInvoiceTempData };
}

Expand All @@ -52,6 +55,16 @@ export const receive: IReceiveModel = {
return true;
}),

deinitialize: thunk((actions, _, { getState }) => {
const invoiceSubscription = getState().invoiceSubscription;
if (invoiceSubscription) {
log.i("Removing invoice subscription");
invoiceSubscription.remove();
actions.setInvoiceSubscriptionStarted(false);
actions.setInvoiceSubscriptionResource(undefined);
}
}),

addInvoice: thunk(async (actions, payload, { injections, getStoreState }) => {
log.d("addInvoice()");
const addInvoice = injections.lndMobile.index.addInvoice;
Expand All @@ -77,101 +90,119 @@ export const receive: IReceiveModel = {
log.d("Receive.subscribeInvoice() called when subsription already started");
return;
}
DeviceEventEmitter.addListener("SubscribeInvoices", async (e: any) => {
log.i("New invoice event");
const invoiceSubscription = DeviceEventEmitter.addListener("SubscribeInvoices", async (e: any) => {
try {
log.i("New invoice event");

const invoice = decodeInvoiceResult(e.data);
const invoice = decodeInvoiceResult(e.data);
const rHash = bytesToHexString(invoice.rHash);

log.d("invoice", [invoice]);
const paymentRequest = await decodePayReq(invoice.paymentRequest);
log.d("invoice", [invoice]);
const paymentRequest = invoice.paymentRequest
? await decodePayReq(invoice.paymentRequest)
: undefined;

// TODO in the future we should handle
// both value (the requested amount in the payreq)
// and amtPaidMsat (the actual amount paid)
// TODO in the future we should handle
// both value (the requested amount in the payreq)
// and amtPaidMsat (the actual amount paid)

if (!Long.isLong(invoice.value)) {
invoice.value = Long.fromValue(invoice.value);
}
if (!Long.isLong(invoice.valueMsat)) {
invoice.amtPaidSat = Long.fromValue(invoice.amtPaidSat);
}
if (!Long.isLong(invoice.amtPaidMsat)) {
invoice.amtPaidMsat = Long.fromValue(invoice.amtPaidMsat);
}
if (!Long.isLong(invoice.value)) {
invoice.value = Long.fromValue(invoice.value);
}
if (!Long.isLong(invoice.valueMsat)) {
invoice.amtPaidSat = Long.fromValue(invoice.amtPaidSat);
}
if (!Long.isLong(invoice.amtPaidMsat)) {
invoice.amtPaidMsat = Long.fromValue(invoice.amtPaidMsat);
}

const tmpData: IInvoiceTempData = getState().invoiceTempData[bytesToHexString(invoice.rHash)] || {
rHash: bytesToHexString(invoice.rHash),
payer: null,
type: "NORMAL",
website: null,
};

const transaction: ITransaction = {
description: invoice.memo,
value: invoice.value || Long.fromInt(0),
valueMsat: (invoice.value && invoice.value.mul(1000)) || Long.fromInt(0),
amtPaidSat: invoice.amtPaidSat,
amtPaidMsat: invoice.amtPaidMsat,
fee: null,
feeMsat: null,
date: invoice.creationDate,
expire: invoice.creationDate.add(paymentRequest.expiry),
remotePubkey: paymentRequest.destination,
status: decodeInvoiceState(invoice.state),
paymentRequest: invoice.paymentRequest,
rHash: paymentRequest.paymentHash,
nodeAliasCached: null,
valueUSD: null,
valueFiat: null,
valueFiatCurrency: null,
tlvRecordName: null,

payer: tmpData.payer,
website: tmpData.website,
type: tmpData.type,
locationLat: null,
locationLong: null,

hops: [],
};

if (invoice.state === lnrpc.Invoice.InvoiceState.SETTLED) {
transaction.valueUSD = valueFiat(invoice.amtPaidSat, getStoreState().fiat.fiatRates.USD.last);
transaction.valueFiat = valueFiat(invoice.amtPaidSat, getStoreState().fiat.currentRate);
transaction.valueFiatCurrency = getStoreState().settings.fiatUnit;

// Loop through known TLV records
for (const htlc of invoice.htlcs) {
if (htlc.customRecords) {
for (const [customRecordKey, customRecordValue] of Object.entries(htlc.customRecords)) {
if (decodeTLVRecord(customRecordKey) === TLV_RECORD_NAME) {
const tlvRecordName = uint8ArrayToString(customRecordValue);
log.i("Found TLV_RECORD_NAME 🎉", [tlvRecordName]);
transaction.tlvRecordName = tlvRecordName;
const tmpData: IInvoiceTempData = getState().invoiceTempData[rHash] || {
rHash,
payer: null,
type: "NORMAL",
website: null,
};

const transaction: ITransaction = {
description: invoice.memo,
value: invoice.value || Long.fromInt(0),
valueMsat: (invoice.value && invoice.value.mul(1000)) || Long.fromInt(0),
amtPaidSat: invoice.amtPaidSat,
amtPaidMsat: invoice.amtPaidMsat,
fee: null,
feeMsat: null,
date: invoice.creationDate,
expire: paymentRequest ? invoice.creationDate.add(paymentRequest.expiry) : Long.fromNumber(0),
remotePubkey: paymentRequest ? paymentRequest.destination : "", // TODO
status: decodeInvoiceState(invoice.state),
paymentRequest: invoice.paymentRequest,
rHash,
nodeAliasCached: null,
valueUSD: null,
valueFiat: null,
valueFiatCurrency: null,
tlvRecordName: null,

payer: tmpData.payer,
website: tmpData.website,
type: tmpData.type,
locationLat: null,
locationLong: null,

hops: [],
};

if (invoice.state === lnrpc.Invoice.InvoiceState.SETTLED) {
transaction.valueUSD = valueFiat(invoice.amtPaidSat, getStoreState().fiat.fiatRates.USD.last);
transaction.valueFiat = valueFiat(invoice.amtPaidSat, getStoreState().fiat.currentRate);
transaction.valueFiatCurrency = getStoreState().settings.fiatUnit;

// Loop through known TLV records
for (const htlc of invoice.htlcs) {
if (htlc.customRecords) {
for (const [customRecordKey, customRecordValue] of Object.entries(htlc.customRecords)) {
if (decodeTLVRecord(customRecordKey) === TLV_RECORD_NAME) {
const tlvRecordName = uint8ArrayToString(customRecordValue);
log.i("Found TLV_RECORD_NAME 🎉", [tlvRecordName]);
transaction.tlvRecordName = tlvRecordName;
}
}
}
}
}

// We can now delete the temp data
// as the invoice has been settled
actions.deleteInvoiceTmpData(paymentRequest.paymentHash);
actions.deleteInvoiceTmpData(rHash);

await dispatch.channel.getBalance();
await dispatch.transaction.syncTransaction(transaction);
} catch (e) {
console.error(e)
log.i("Error receiving invoice", [e]);
toast("Error receiving payment: " + e.message, undefined, "danger");
}
await dispatch.transaction.syncTransaction(transaction);
});
actions.setInvoiceSubscriptionStarted(true);
actions.setInvoiceSubscriptionResource(invoiceSubscription);
log.i("Transaction subscription started");
}),

setInvoiceSubscriptionStarted: action((state, payload) => { state.invoiceSubscriptionStarted = payload }),

setInvoiceSubscriptionResource: action((state, payload) => { state.invoiceSubscription = payload }),

setInvoiceTmpData: action((state, payload) => {
state.invoiceTempData = {
...state.invoiceTempData,
[payload.rHash!]: payload,
}
}),

deleteInvoiceTmpData: action((state, payload) => {
delete state.invoiceTempData[payload];
const tmpData = { ...state.invoiceTempData };
delete tmpData[payload];
state.invoiceTempData = tmpData;
}),

invoiceSubscriptionStarted: false,
Expand Down
4 changes: 4 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ export const bytesToHexString = (bytes: ArrayLike<number>) => {
}, "");
}

export const hexToUint8Array = (hexString: string) => {
return new Uint8Array(hexString.match(/.{1,2}/g)!.map(byte => parseInt(byte, 16)));
};

export const toast = (message: string, period = 3000, type: "danger" | "success" | "warning" = "success", button?: string) => {
console.log(message);
Toast.show({
Expand Down
9 changes: 8 additions & 1 deletion src/windows/OnChain/Withdraw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,14 @@ export default ({ navigation }: IOpenChannelProps) => {
),
}]}
buttons={[
<Button testID="SEND_COINS" key="WITHDRAW" onPress={onWithdrawClick} block={true} primary={true}>
<Button
testID="SEND_COINS"
key="WITHDRAW"
block={true}
primary={true}
onPress={onWithdrawClick}
disabled={sending}
>
{!sending && <Text>Withdraw</Text>}
{sending && <Spinner color={blixtTheme.light} />}
</Button>
Expand Down
3 changes: 2 additions & 1 deletion src/windows/Overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ const Overview = ({ navigation }: IOverviewProps) => {
scrollEventThrottle={16}
refreshControl={refreshControl}
onScroll={transactionListOnScroll}
testID="TX_LIST"
>
{txs}
</ScrollView>
Expand Down Expand Up @@ -166,7 +167,7 @@ const Overview = ({ navigation }: IOverviewProps) => {
</View>

{/* Big header */}
<Animated.Text onPress={onPressBalanceHeader} style={{...headerInfo.btc, fontSize: headerBtcFontSize}}>
<Animated.Text testID="BIG_BALANCE_HEADER" onPress={onPressBalanceHeader} style={{...headerInfo.btc, fontSize: headerBtcFontSize}}>
{!preferFiat && bitcoinBalance}
{preferFiat && fiatBalance}
</Animated.Text>
Expand Down
2 changes: 1 addition & 1 deletion src/windows/Receive/ReceiveQr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export default ({ navigation, route }: IReceiveQRProps) => {
<Container testID="qr">
<Header iosBarStyle="light-content" translucent={false}>
<Left>
<Button transparent={true} onPress={() => navigation.pop()}>
<Button testID="GO_BACK" transparent={true} onPress={() => navigation.pop()}>
<Icon name="arrow-back" />
</Button>
</Left>
Expand Down
Loading

0 comments on commit fed3330

Please sign in to comment.