Skip to content

Commit 7b38faa

Browse files
chore: capacitor integration tests (#976)
Co-authored-by: Simon Binder <simon@journeyapps.com>
1 parent 16007e2 commit 7b38faa

73 files changed

Lines changed: 1773 additions & 68 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@powersync/capacitor': patch
3+
---
4+
5+
Fix Capacitor batch operations so they do not start a nested native transaction when executed inside PowerSync's write transaction wrapper.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: Setup Android Emulator
2+
description: Configure KVM, Java, Gradle, and a cached Android emulator snapshot.
3+
inputs:
4+
avd-name:
5+
description: Name of the Android Virtual Device to create or reuse.
6+
required: true
7+
cache-key:
8+
description: Cache key for the Android Virtual Device files.
9+
required: true
10+
api-level:
11+
description: Android API level for the emulator.
12+
required: false
13+
default: '31'
14+
target:
15+
description: Android system image target.
16+
required: false
17+
default: google_apis
18+
arch:
19+
description: Android system image architecture.
20+
required: false
21+
default: x86_64
22+
java-version:
23+
description: JDK version to install for Android builds.
24+
required: false
25+
default: '17'
26+
runs:
27+
using: composite
28+
steps:
29+
- name: Enable KVM group perms
30+
shell: bash
31+
run: |
32+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
33+
sudo udevadm control --reload-rules
34+
sudo udevadm trigger --name-match=kvm
35+
36+
- name: Setup Gradle
37+
uses: gradle/actions/setup-gradle@v4
38+
39+
- name: AVD Cache
40+
uses: actions/cache@v3
41+
id: avd-cache
42+
with:
43+
path: |
44+
~/.android/avd/*
45+
~/.android/adb*
46+
key: ${{ inputs.cache-key }}
47+
48+
- name: Set up JDK
49+
uses: actions/setup-java@v5
50+
with:
51+
java-version: ${{ inputs.java-version }}
52+
distribution: adopt
53+
cache: gradle
54+
55+
- name: Initialize Android Folder
56+
shell: bash
57+
run: mkdir -p ~/.android/avd
58+
59+
- name: Create AVD and generate snapshot for caching
60+
if: steps.avd-cache.outputs.cache-hit != 'true'
61+
uses: reactivecircus/android-emulator-runner@v2.28.0
62+
with:
63+
api-level: ${{ inputs.api-level }}
64+
force-avd-creation: false
65+
target: ${{ inputs.target }}
66+
arch: ${{ inputs.arch }}
67+
disable-animations: false
68+
avd-name: ${{ inputs.avd-name }}
69+
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
70+
script: echo "Generated AVD snapshot for caching."

.github/workflows/test-simulators.yaml

Lines changed: 86 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@ name: Test Simulators/Emulators
33
on:
44
pull_request: # triggered for any PR updates (including new pushes to PR branch)
55

6+
permissions:
7+
contents: read
8+
69
jobs:
710
check-changes:
811
name: Check for relevant changes
912
runs-on: ubuntu-latest
1013
outputs:
11-
should_run: ${{ steps.check.outputs.should_run }}
14+
react_native_simulator_should_run: ${{ steps.check.outputs.react_native_simulator_should_run }}
15+
capacitor_should_run: ${{ steps.check.outputs.capacitor_should_run }}
1216
steps:
1317
- uses: actions/checkout@v6
1418
with:
@@ -18,15 +22,21 @@ jobs:
1822
run: |
1923
git fetch origin ${{ github.base_ref }}
2024
if git diff --quiet origin/${{ github.base_ref }} -- packages/common packages/powersync-op-sqlite tools/powersynctests; then
21-
echo "should_run=false" >> $GITHUB_OUTPUT
25+
echo "react_native_simulator_should_run=false" >> $GITHUB_OUTPUT
26+
else
27+
echo "react_native_simulator_should_run=true" >> $GITHUB_OUTPUT
28+
fi
29+
30+
if git diff --quiet origin/${{ github.base_ref }} -- packages/capacitor pnpm-lock.yaml .github/workflows/test-simulators.yaml; then
31+
echo "capacitor_should_run=false" >> $GITHUB_OUTPUT
2232
else
23-
echo "should_run=true" >> $GITHUB_OUTPUT
33+
echo "capacitor_should_run=true" >> $GITHUB_OUTPUT
2434
fi
2535
2636
test-android:
2737
name: Test Android
2838
needs: check-changes
29-
if: ${{ needs.check-changes.outputs.should_run == 'true' }}
39+
if: ${{ needs.check-changes.outputs.react_native_simulator_should_run == 'true' }}
3040
runs-on: ubuntu-xl
3141
timeout-minutes: 30
3242
env:
@@ -36,30 +46,11 @@ jobs:
3646
with:
3747
persist-credentials: false
3848

39-
- name: Enable KVM group perms
40-
run: |
41-
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
42-
sudo udevadm control --reload-rules
43-
sudo udevadm trigger --name-match=kvm
44-
45-
- name: Setup Gradle
46-
uses: gradle/actions/setup-gradle@v4
47-
48-
- name: AVD Cache
49-
uses: actions/cache@v3
50-
id: avd-cache
51-
with:
52-
path: |
53-
~/.android/avd/*
54-
~/.android/adb*
55-
key: avd-31
56-
57-
- name: Set up JDK 17
58-
uses: actions/setup-java@v3
49+
- name: Setup Android emulator
50+
uses: ./.github/actions/setup-android-emulator
5951
with:
60-
java-version: 17
61-
distribution: 'adopt'
62-
cache: 'gradle'
52+
avd-name: ${{ env.AVD_NAME }}
53+
cache-key: avd-31
6354

6455
- name: Enable Corepack
6556
run: corepack enable
@@ -80,22 +71,6 @@ jobs:
8071
run: |
8172
pnpx detox clean-framework-cache && pnpx detox build-framework-cache
8273
83-
- name: Initialize Android Folder
84-
run: mkdir -p ~/.android/avd
85-
86-
- name: create AVD and generate snapshot for caching
87-
if: steps.avd-cache.outputs.cache-hit != 'true'
88-
uses: reactivecircus/android-emulator-runner@v2.28.0
89-
with:
90-
api-level: 31
91-
force-avd-creation: false
92-
target: google_apis
93-
arch: x86_64
94-
disable-animations: false
95-
avd-name: $AVD_NAME
96-
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
97-
script: echo "Generated AVD snapshot for caching."
98-
9974
- name: Android Emulator Build
10075
working-directory: ./tools/powersynctests
10176
run: pnpx detox build --configuration android.emu.release
@@ -106,18 +81,84 @@ jobs:
10681
api-level: 31
10782
target: google_apis
10883
arch: x86_64
109-
avd-name: $AVD_NAME
84+
avd-name: ${{ env.AVD_NAME }}
11085
script: cd tools/powersynctests && pnpx detox test --configuration android.emu.release --headless
11186
force-avd-creation: false
11287
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
11388
disable-animations: true
11489

90+
test-capacitor-android:
91+
name: Test Capacitor Android
92+
needs: check-changes
93+
if: ${{ needs.check-changes.outputs.capacitor_should_run == 'true' }}
94+
runs-on: ubuntu-xl
95+
timeout-minutes: 30
96+
env:
97+
AVD_NAME: capacitor-avd-x86_64-35
98+
TEST_PLATFORM: android
99+
TEST_TARGET: emulator-5554
100+
steps:
101+
- uses: actions/checkout@v6
102+
with:
103+
persist-credentials: false
104+
105+
- name: Setup Android emulator
106+
uses: ./.github/actions/setup-android-emulator
107+
with:
108+
avd-name: ${{ env.AVD_NAME }}
109+
cache-key: capacitor-avd-35
110+
api-level: '35'
111+
java-version: '21'
112+
113+
- name: Enable Corepack
114+
run: corepack enable
115+
116+
- name: Setup Node.js
117+
uses: actions/setup-node@v6
118+
with:
119+
node-version-file: '.nvmrc'
120+
cache: pnpm
121+
122+
- name: Install dependencies
123+
run: pnpm install
124+
125+
- name: Build
126+
run: pnpm run -r --filter "@powersync/capacitor..." build
127+
128+
- name: Install Capacitor example dependencies
129+
working-directory: ./packages/capacitor/example-app
130+
run: pnpm install
131+
132+
- name: Sync and build Capacitor Android app
133+
working-directory: ./packages/capacitor/example-app
134+
# Prebuild the native app so the later browser-test action can start quickly.
135+
# This dummy Vitest server URL is only used for the prebuild; the test run
136+
# updates Capacitor with the actual Vitest URL before launching the app.
137+
env:
138+
CAPACITOR_VITEST_SERVER_URL: http://10.0.2.2
139+
run: |
140+
pnpm exec cap sync android
141+
cd android
142+
./gradlew assembleDebug
143+
144+
- name: Run Capacitor Android browser tests
145+
uses: reactivecircus/android-emulator-runner@v2.28.0
146+
with:
147+
api-level: 35
148+
target: google_apis
149+
arch: x86_64
150+
avd-name: ${{ env.AVD_NAME }}
151+
script: pnpm --dir packages/capacitor exec vitest run --config vitest.config.ts
152+
force-avd-creation: false
153+
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
154+
disable-animations: true
155+
115156
test-ios:
116157
name: Test iOS
117158
needs: check-changes
118159
# TODO: Re-enable iOS tests. They have been disabled because they are failing extremely frequently without
119160
# any apparent cause. In particular, it seems like even starting the simulator times out most of the time.
120-
if: ${{ false && needs.check-changes.outputs.should_run == 'true' }}
161+
if: ${{ false && needs.check-changes.outputs.react_native_simulator_should_run == 'true' }}
121162
runs-on: macOS-15
122163
timeout-minutes: 30
123164

demos/example-capacitor/ios/App/CapApp-SPM/Package.resolved

Lines changed: 41 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/capacitor/DEVELOP.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Developing the Capacitor SDK
2+
3+
## Native browser integration tests
4+
5+
The Capacitor package uses Vitest browser mode with a custom provider. Vitest starts a browser test server, then the provider runs the example Capacitor app and points the app at that server.
6+
7+
Build the workspace before running these tests so the Capacitor package can resolve generated workspace package outputs:
8+
9+
```sh
10+
pnpm install
11+
pnpm build:packages
12+
cd packages/capacitor/example-app
13+
pnpm install
14+
cd ..
15+
```
16+
17+
The test provider reads these environment variables:
18+
19+
- `TEST_PLATFORM`: Native platform to run. Use `ios` or `android`.
20+
- `TEST_TARGET`: Simulator/emulator target id passed to `cap run --target`.
21+
- `TEST_SERVER_HOST`: Hostname the native app should use to reach the Vitest server. Android defaults to `10.0.2.2`; iOS uses the Vitest URL host as-is.
22+
23+
### iOS
24+
25+
List available iOS simulator targets:
26+
27+
```sh
28+
cd packages/capacitor/example-app
29+
pnpm exec cap run ios --list
30+
```
31+
32+
Run the integration tests on a simulator:
33+
34+
```sh
35+
cd packages/capacitor
36+
TEST_PLATFORM=ios \
37+
TEST_TARGET=<ios-simulator-id> \
38+
pnpm exec vitest run --config vitest.config.ts
39+
```
40+
41+
### Android
42+
43+
List available Android emulator/device targets:
44+
45+
```sh
46+
adb devices
47+
```
48+
49+
For the default Android emulator, the target is usually `emulator-5554`.
50+
51+
Run the integration tests on Android:
52+
53+
```sh
54+
cd packages/capacitor
55+
TEST_PLATFORM=android \
56+
TEST_TARGET=emulator-5554 \
57+
pnpm exec vitest run --config vitest.config.ts
58+
```
59+
60+
Android defaults `TEST_SERVER_HOST` to `10.0.2.2`, which lets the emulator reach the host machine without `adb reverse`. The Vitest config also binds the test server to `0.0.0.0` for this reason.
Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
## Created with Capacitor Create App
1+
# PowerSync Capacitor Example App
22

3-
This app was created using [`@capacitor/create-app`](https://github.com/ionic-team/create-capacitor-app),
4-
and comes with a very minimal shell for building an app.
3+
This app is a minimal Capacitor host for the `@powersync/capacitor` package. It is used as the native app shell for the package's Vitest browser integration tests: Vitest starts a browser test server, then the custom provider launches this app and points the WebView at that server.
54

6-
### Running this example
5+
The `ios/` and `android/` folders are committed intentionally. Capacitor treats them as app source folders, and they contain native configuration such as `Info.plist`, `AndroidManifest.xml`, plugin setup, and platform-specific project files.
76

8-
To run the provided example, you can use `npm start` command.
7+
## Refresh Native Projects
8+
9+
After changing Capacitor config, native dependencies, or plugin setup, refresh the native projects:
910

1011
```bash
11-
npm start
12+
pnpm exec cap sync ios
13+
pnpm exec cap sync android
1214
```
15+
16+
See `../DEVELOP.md` for the full native integration test workflow.

0 commit comments

Comments
 (0)