Skip to content

Commit 41481c1

Browse files
committed
tests: enable automatic tests on Android.
1 parent 7d540b4 commit 41481c1

File tree

9 files changed

+20379
-0
lines changed

9 files changed

+20379
-0
lines changed

.github/workflows/ci.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,57 @@ jobs:
7070
path: /github/home/go/src/github.com/BitBoxSwiss/bitbox-wallet-app/frontends/android/BitBoxApp/app/build/outputs/apk/debug/app-debug.apk
7171
name: BitBoxApp-android-${{github.sha}}.apk
7272
if-no-files-found: error
73+
74+
e2e-android:
75+
name: Android E2E Tests
76+
needs: android
77+
runs-on: ubuntu-latest
78+
strategy:
79+
matrix:
80+
api-level: [30, 31, 32, 33, 34, 35]
81+
steps:
82+
- name: Checkout repo
83+
uses: actions/checkout@v4
84+
85+
- name: Set up Node.js
86+
uses: actions/setup-node@v4
87+
with:
88+
node-version: '20.x'
89+
90+
- name: Download APK
91+
uses: actions/download-artifact@v4
92+
with:
93+
name: BitBoxApp-android-${{ github.sha }}.apk
94+
path: frontends/mobiletests/apk
95+
96+
- name: Install dependencies
97+
working-directory: frontends/mobiletests
98+
run: npm ci
99+
100+
- name: Enable KVM group perms
101+
run: |
102+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
103+
sudo udevadm control --reload-rules
104+
sudo udevadm trigger --name-match=kvm
105+
106+
- name: Install Appium & drivers
107+
run: |
108+
npm install -g [email protected]
109+
appium driver install uiautomator2
110+
111+
- name: Start Android emulator and run tests
112+
uses: reactivecircus/android-emulator-runner@v2
113+
env:
114+
PLATFORM: Android
115+
ANDROID_EMULATOR_WAIT_TIME_BEFORE_KILL: 5
116+
with:
117+
api-level: ${{ matrix.api-level }}
118+
arch: x86_64
119+
force-avd-creation: true
120+
emulator-boot-timeout: 600
121+
working-directory: frontends/mobiletests
122+
emulator-options: "-no-window -no-audio -no-boot-anim -no-snapshot-load -no-snapshot-save"
123+
script: ./run.sh
73124
qt-linux:
74125
runs-on: ubuntu-22.04
75126
needs: setup-env

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
SHELL := /bin/bash
1616
WEBROOT := frontends/web
17+
MOBILETESTROOT := frontends/mobiletests
1718

1819
catch:
1920
@echo "Choose a make target."
@@ -57,6 +58,8 @@ webserve:
5758
cd ${WEBROOT} && $(MAKE) serve
5859
webe2etest:
5960
cd ${WEBROOT} && $(MAKE) test-e2e
61+
mobilee2etest:
62+
cd ${MOBILETESTROOT} && ./run.sh
6063
qt-linux: # run inside dockerdev
6164
$(MAKE) buildweb
6265
cd frontends/qt && $(MAKE) linux

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ Playwright is used to perform automatic test on some use cases on the webdev ver
103103
Tests are located under [`frontends/web/tests`](/frontends/web/tests) and can be run with
104104
`make webe2etest`
105105

106+
Appium is used to perform automatic test on Android emulators.
107+
The test runs automatically on PRs and push to master in the Github CI.
108+
109+
`make mobilee2etest` can be used to launch the tests; however, when ran locally, this requires an Android emulator
110+
to be already running. In CI, the emulator is automatically spawned from a Github Action.
111+
106112
#### Run the HTTP API
107113

108114
Run `make servewallet` to compile the code and run `servewallet`. `servewallet` is a devtool which

frontends/mobiletests/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/node_modules/

frontends/mobiletests/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Mobile e2e tests
2+
3+
This folder contains the necessary files to setup and run tests against Android (and in the future iOS) devices.
4+
5+
The tests are contained in the [e2e](./e2e) directory and use [Appium](https://github.com/appium/appium) to automate execution of actions on the app.
6+
7+
To run the test manually, one can either run `make mobilee2etest` from the [root repo](../..) or execute [run.sh](./run.sh) directly.
8+
The script will start an Appium server, wait for it to be ready, and then call `npm run tests` which uses the testing framework [Mocha](https://mochajs.org/).
9+
10+
Note that, in order for the test to start, an Android emulator must be running. The script doesn't start one as steps to do so tend to be somewhat platform-dependant. When running in CI, an emulator is automatically started through a Github Action.
11+
12+
## Development
13+
To create a new test, simply add a new file called `<test-name>.test.js` to the `e2e` folder.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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+
import { remote } from 'webdriverio';
17+
import { expect } from 'chai';
18+
19+
// --- Test ---
20+
describe('BitBoxApp Base Test', function () {
21+
this.timeout(180000);
22+
23+
let driver;
24+
before(async () => {
25+
26+
const opts = {
27+
path: '/',
28+
port: 4723,
29+
capabilities: {
30+
platformName: 'Android',
31+
'appium:deviceName': 'Android Emulator',
32+
'appium:automationName': 'UiAutomator2',
33+
'appium:app': './apk/app-debug.apk',
34+
}
35+
};
36+
37+
driver = await remote(opts);
38+
39+
// Switch to WebView if present
40+
const contexts = await driver.getContexts();
41+
console.log('Available contexts:', contexts);
42+
const webview = contexts.find((c) => c.startsWith('WEBVIEW_'));
43+
if (webview) await driver.switchContext(webview);
44+
45+
});
46+
47+
after(async () => {
48+
if (driver) await driver.deleteSession();
49+
});
50+
51+
it('App main page loads', async () => {
52+
const body = await driver.$('body');
53+
const bodyText = await body.getText();
54+
expect(bodyText).to.include(
55+
'Please connect your BitBox and tap the side to continue.'
56+
);
57+
});
58+
});

0 commit comments

Comments
 (0)