Skip to content

Commit 9c2f4cc

Browse files
committed
tests: add test for sending coins.
1 parent 3eb51c6 commit 9c2f4cc

File tree

4 files changed

+176
-2
lines changed

4 files changed

+176
-2
lines changed

frontends/web/src/components/transactions/transaction.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export const Transaction = ({
5252
onShowDetail(internalID);
5353
}
5454
}}>
55-
<div className={styles.txContent}>
55+
<div className={styles.txContent} data-testid="transaction">
5656
<span className={styles.txIcon}>
5757
<Arrow status={status} type={type} />
5858
</span>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Copyright 2025 Shift Crypto AG
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import type { Page } from '@playwright/test';
18+
import { expect } from '@playwright/test';
19+
20+
export async function assertNthTransactionIsOfType (
21+
page: Page,
22+
n: number,
23+
expectedType: 'sent' | 'received' | 'self'
24+
) {
25+
const tx = page.getByTestId('transaction').nth(n);
26+
let imgSrcLocator: string;
27+
switch (expectedType) {
28+
case 'sent':
29+
imgSrcLocator = 'img[src="/src/components/icon/assets/icons/arrow-floor-up-red.svg"]';
30+
break;
31+
case 'received':
32+
imgSrcLocator = 'img[src="/src/components/icon/assets/icons/arrow-floor-down-green.svg"]';
33+
break;
34+
case 'self':
35+
imgSrcLocator = 'img[src="/src/components/icon/assets/icons/arrow-u-turn.svg"]';
36+
break;
37+
}
38+
await expect(tx.locator(imgSrcLocator)).toHaveCount(1);
39+
}

frontends/web/tests/helpers/regtest.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export async function sendCoins(address: string, amount: number | string): Promi
7878

7979

8080
export function launchRegtest(): Promise<ChildProcess> {
81-
const PROJECT_ROOT = process.env.GITHUB_WORKSPACE || path.resolve(__dirname, '../../..');
81+
const PROJECT_ROOT = process.env.GITHUB_WORKSPACE || path.resolve(__dirname, '../../../..');
8282
// First, clean up cache and headers.
8383
try {
8484
const basePath = path.join(PROJECT_ROOT, 'appfolder.dev/cache');

frontends/web/tests/send.test.ts

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/**
2+
* Copyright 2025 Shift Crypto AG
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { expect } from '@playwright/test';
18+
import { test } from './helpers/fixtures';
19+
import { ServeWallet } from './helpers/servewallet';
20+
import { launchRegtest, setupRegtestWallet, sendCoins, mineBlocks, cleanupRegtest } from './helpers/regtest';
21+
import { ChildProcess } from 'child_process';
22+
import { deleteAccountsFile } from './helpers/fs';
23+
import { assertNthTransactionIsOfType } from './helpers/accounts';
24+
25+
let servewallet: ServeWallet;
26+
let regtest: ChildProcess;
27+
28+
test('Send BTC', async ({ page, host, frontendPort, servewalletPort }) => {
29+
30+
await test.step('Start regtest and init wallet', async () => {
31+
regtest = await launchRegtest();
32+
// Give regtest some time to start
33+
await new Promise((resolve) => setTimeout(resolve, 3000));
34+
await setupRegtestWallet();
35+
});
36+
37+
38+
await test.step('Start servewallet', async () => {
39+
servewallet = new ServeWallet(page, servewalletPort, frontendPort, host, { regtest: true, testnet: false });
40+
await servewallet.start();
41+
});
42+
43+
await test.step('Verify there are no transactions yet', async () => {
44+
page.locator('td[data-label="Account name"]').nth(0).click();
45+
await expect(page.getByTestId('transaction')).toHaveCount(0);
46+
});
47+
48+
49+
let recvAdd: string;
50+
await test.step('Grab receive address', async () => {
51+
await page.getByRole('button', { name: 'Test wallet' }).click();
52+
await page.getByRole('button', { name: 'Unlock' }).click();
53+
await page.getByRole('link', { name: 'Bitcoin Regtest Bitcoin' }).click();
54+
await page.getByRole('button', { name: 'Receive RBTC' }).click();
55+
await page.getByRole('button', { name: 'Verify address on BitBox' }).click();
56+
const addressLocator = page.locator('[name="receive-address"]');
57+
recvAdd = await addressLocator.inputValue();
58+
console.log(`Receive address: ${recvAdd}`);
59+
});
60+
61+
await test.step('Add second RBTC account', async () => {
62+
await page.goto('/');
63+
await page.getByRole('link', { name: 'Settings' }).click();
64+
await page.getByRole('link', { name: 'Manage Accounts' }).click();
65+
await page.getByRole('button', { name: 'Add account' }).click();
66+
await page.getByRole('button', { name: 'Add account' }).click();
67+
await page.getByRole('button', { name: 'Done' }).click();
68+
});
69+
70+
await test.step('Send RBTC to receive address', async () => {
71+
await page.waitForTimeout(2000);
72+
const sendAmount = '10';
73+
sendCoins(recvAdd, sendAmount);
74+
mineBlocks(12);
75+
});
76+
77+
await test.step('Verify that the first account has a transaction', async () => {
78+
await page.goto('/');
79+
page.locator('td[data-label="Account name"]').nth(0).click();
80+
await expect(page.getByTestId('transaction')).toHaveCount(1);
81+
82+
// It should be an incoming tx
83+
await assertNthTransactionIsOfType(page, 0, 'received');
84+
85+
});
86+
87+
await test.step('Grab receive address for second account', async () => {
88+
await page.goto('/');
89+
await page.getByRole('link', { name: 'Bitcoin Regtest 2' }).click();
90+
91+
await page.getByRole('button', { name: 'Receive RBTC' }).click();
92+
await page.getByRole('button', { name: 'Verify address on BitBox' }).click();
93+
const addressLocator = page.locator('[name="receive-address"]');
94+
recvAdd = await addressLocator.inputValue();
95+
console.log(`Receive address: ${recvAdd}`);
96+
});
97+
98+
await test.step('Send RBTC to second account receive address', async () => {
99+
await page.goto('/');
100+
await page.getByRole('link', { name: 'Bitcoin Regtest Bitcoin' }).click();
101+
console.log('Sending RBTC to second account');
102+
await page.getByRole('link', { name: 'Send' }).click();
103+
await page.fill('#recipientAddress', recvAdd);
104+
await page.click('#sendAll');
105+
await page.getByRole('button', { name: 'Review' }).click();
106+
await page.getByRole('button', { name: 'Done' }).click();
107+
mineBlocks(12);
108+
});
109+
110+
await test.step('Verify that first account now has two transactions', async () => {
111+
await page.goto('/');
112+
page.locator('td[data-label="Account name"]').nth(0).click();
113+
await expect(page.getByTestId('transaction')).toHaveCount(2);
114+
// Verify that the second one is outgoing
115+
await assertNthTransactionIsOfType(page, 0, 'sent');
116+
await assertNthTransactionIsOfType(page, 1, 'received');
117+
});
118+
119+
await test.step('Verify that the second account has a transaction', async () => {
120+
await page.goto('/');
121+
page.locator('td[data-label="Account name"]').nth(1).click();
122+
await expect(page.getByTestId('transaction')).toHaveCount(1);
123+
await assertNthTransactionIsOfType(page, 0, 'received');
124+
});
125+
126+
});
127+
128+
test.beforeEach(async () => {
129+
deleteAccountsFile();
130+
});
131+
132+
test.afterAll(async () => {
133+
await servewallet.stop();
134+
await cleanupRegtest(regtest);
135+
});

0 commit comments

Comments
 (0)