|
| 1 | +# Platform Integration Tests |
| 2 | + |
| 3 | +Kirie platform integration tests live in a repo-level Godot project: |
| 4 | + |
| 5 | +```text |
| 6 | +tests/integration/ |
| 7 | +``` |
| 8 | + |
| 9 | +They are not Android instrumentation tests and are not part of |
| 10 | +`examples/basic-ipc`. Android and iOS should eventually run the same Godot test |
| 11 | +project; the platform only provides the WebView runtime and app launch |
| 12 | +mechanism. |
| 13 | + |
| 14 | +## Goals |
| 15 | + |
| 16 | +These tests cover the platform bridge path: |
| 17 | + |
| 18 | +```text |
| 19 | +Godot -> Kirie platform singleton -> platform WebView -> JavaScript -> Godot |
| 20 | +``` |
| 21 | + |
| 22 | +The current focus is: |
| 23 | + |
| 24 | +- WebView lifecycle behavior from Godot |
| 25 | +- raw JavaScript bridge IPC |
| 26 | +- resource loading through `res://` |
| 27 | +- exported app behavior, not editor-only behavior |
| 28 | + |
| 29 | +The tests intentionally do not depend on the browser-facing `@kirie/ipc` |
| 30 | +package. That package is a convenience SDK above the raw bridge contract and |
| 31 | +should be tested separately. |
| 32 | + |
| 33 | +## Project Layout |
| 34 | + |
| 35 | +```text |
| 36 | +tests/integration/ |
| 37 | + project.godot |
| 38 | + export_presets.cfg |
| 39 | + main.tscn |
| 40 | + addons/kirie -> ../../../packages/kirie/addon/addons/kirie |
| 41 | + scripts/ |
| 42 | + test_runner.gd |
| 43 | + test_probe.gd |
| 44 | + test_cases/ |
| 45 | + ipc_round_trip_probe.gd |
| 46 | + webview_lifecycle_probe.gd |
| 47 | + res_asset_loading_probe.gd |
| 48 | + web/ |
| 49 | + probe.html |
| 50 | +``` |
| 51 | + |
| 52 | +`web/probe.html` is a minimal fixture, not a web app project. There is no Vite |
| 53 | +project, package.json, TypeScript config, or generated web bundle under |
| 54 | +`tests/integration/web`. |
| 55 | + |
| 56 | +## Runner Contract |
| 57 | + |
| 58 | +The exported app runs one test per app session. |
| 59 | + |
| 60 | +On Android, the runner reads the test name from the launch option: |
| 61 | + |
| 62 | +```text |
| 63 | +kirie_test |
| 64 | +``` |
| 65 | + |
| 66 | +The Android plugin exposes this through: |
| 67 | + |
| 68 | +```gdscript |
| 69 | +Kirie.get_launch_option("kirie_test") |
| 70 | +``` |
| 71 | + |
| 72 | +The runner also supports `--kirie-test=<name>` from Godot command-line user |
| 73 | +args for local non-Android runs. |
| 74 | + |
| 75 | +`scripts/test_runner.gd` owns only: |
| 76 | + |
| 77 | +- resolving the test name |
| 78 | +- loading `res://scripts/test_cases/<test_name>.gd` |
| 79 | +- calling `run(kirie, tree, test_name)` |
| 80 | +- printing pass/fail markers |
| 81 | +- quitting the app |
| 82 | + |
| 83 | +Test cases return a `String`: |
| 84 | + |
| 85 | +- `""` means pass |
| 86 | +- non-empty string means fail reason |
| 87 | + |
| 88 | +The runner prints exactly one final marker: |
| 89 | + |
| 90 | +```text |
| 91 | +KIRIE_TEST_PASS <test_name> |
| 92 | +KIRIE_TEST_FAIL <test_name> <reason> |
| 93 | +``` |
| 94 | + |
| 95 | +## Test Case Contract |
| 96 | + |
| 97 | +Each test case owns its own lifecycle operations. A test should explicitly call |
| 98 | +the Kirie API it wants to exercise: |
| 99 | + |
| 100 | +- `create_webview()` |
| 101 | +- `load_html_string(...)` |
| 102 | +- `load_url(...)` |
| 103 | +- `send_ipc_message(...)` |
| 104 | +- `destroy_webview()` |
| 105 | + |
| 106 | +Shared waiting and probe observation lives in `scripts/test_probe.gd`. |
| 107 | +`KirieIntegrationProbe` may: |
| 108 | + |
| 109 | +- connect to Kirie signals |
| 110 | +- wait for `webview_ready` |
| 111 | +- collect `ipc_message_received` |
| 112 | +- wait for a specific probe message |
| 113 | +- read `web/probe.html` |
| 114 | + |
| 115 | +It should not decide which URL a test loads. Page URLs are test inputs and must |
| 116 | +be provided by the test case itself. |
| 117 | + |
| 118 | +## Web Fixture |
| 119 | + |
| 120 | +`web/probe.html` is a small raw bridge fixture. It uses the platform-level |
| 121 | +contract directly: |
| 122 | + |
| 123 | +- Android JavaScript to Godot: |
| 124 | + `globalThis.KirieAndroidBridge.postMessage(messageJson)` |
| 125 | +- iOS JavaScript to Godot: |
| 126 | + `globalThis.webkit.messageHandlers.kirie.postMessage(messageJson)` |
| 127 | +- Godot to JavaScript: |
| 128 | + `kirie:ipc-message` DOM events |
| 129 | + |
| 130 | +For tests that do not care about asset loading, the GDScript case reads |
| 131 | +`probe.html` and injects it with `load_html_string(...)`. This keeps the test |
| 132 | +case in Godot while preserving HTML/JavaScript syntax highlighting in the |
| 133 | +fixture file. |
| 134 | + |
| 135 | +For tests that do care about exported resources, the case loads |
| 136 | +`res://web/probe.html?...` with `load_url(...)`. |
| 137 | + |
| 138 | +## Test Coverage Shape |
| 139 | + |
| 140 | +Individual test behavior belongs in `scripts/test_cases/*.gd`, not in this |
| 141 | +architecture note. |
| 142 | + |
| 143 | +The suite should stay organized around a small number of platform-facing |
| 144 | +coverage categories: |
| 145 | + |
| 146 | +- IPC round trips through the raw JavaScript bridge |
| 147 | +- WebView lifecycle transitions driven from Godot |
| 148 | +- exported `res://` web resource loading |
| 149 | + |
| 150 | +New tests should add a focused case under `scripts/test_cases/` when they need |
| 151 | +different lifecycle operations, a different loaded URL, or a different platform |
| 152 | +bridge assertion. |
| 153 | + |
| 154 | +## Android Local Flow |
| 155 | + |
| 156 | +Build the test APK: |
| 157 | + |
| 158 | +```bash |
| 159 | +scripts/build_integration_android.sh |
| 160 | +``` |
| 161 | + |
| 162 | +Install it once: |
| 163 | + |
| 164 | +```bash |
| 165 | +adb install -r dist/integration/android_debug.apk |
| 166 | +``` |
| 167 | + |
| 168 | +Run one test: |
| 169 | + |
| 170 | +```bash |
| 171 | +scripts/run_integration_android_test.sh ipc_round_trip_probe |
| 172 | +``` |
| 173 | + |
| 174 | +The run script: |
| 175 | + |
| 176 | +- clears logcat |
| 177 | +- force-stops the package |
| 178 | +- clears app data |
| 179 | +- starts the exported app with `--es kirie_test <test_name>` |
| 180 | +- waits for `KIRIE_TEST_PASS` or `KIRIE_TEST_FAIL` |
| 181 | + |
| 182 | +The Android package defaults to: |
| 183 | + |
| 184 | +```text |
| 185 | +ai.moeru.kirie.integrationtests |
| 186 | +``` |
| 187 | + |
| 188 | +The Android launcher component defaults to: |
| 189 | + |
| 190 | +```text |
| 191 | +com.godot.game.GodotAppLauncher |
| 192 | +``` |
| 193 | + |
| 194 | +## Isolation Model |
| 195 | + |
| 196 | +Tests are isolated by app session: |
| 197 | + |
| 198 | +- export one test APK |
| 199 | +- install it once |
| 200 | +- run each test in a fresh app start |
| 201 | +- run `pm clear` before each test |
| 202 | + |
| 203 | +This avoids residual WebView, JavaScript, singleton, signal, and cache state |
| 204 | +without exporting a separate APK for every test. |
| 205 | + |
| 206 | +## CI Direction |
| 207 | + |
| 208 | +The intended GitHub Actions shape is: |
| 209 | + |
| 210 | +- set up Node and Java with `jdx/mise-action` |
| 211 | +- set up Godot and export templates with `chickensoft-games/setup-godot` |
| 212 | +- install pnpm dependencies |
| 213 | +- build the Android AAR |
| 214 | +- export `tests/integration` as an Android APK |
| 215 | +- start an emulator with `reactivecircus/android-emulator-runner` |
| 216 | +- install the APK once |
| 217 | +- run each test through `scripts/run_integration_android_test.sh` |
| 218 | + |
| 219 | +CI should reuse the same marker contract and app-session isolation used |
| 220 | +locally. |
0 commit comments