diff --git a/.github/actions/db-sync-server/action.yml b/.github/actions/db-sync-server/action.yml index caf74ea7..d5b5cfff 100644 --- a/.github/actions/db-sync-server/action.yml +++ b/.github/actions/db-sync-server/action.yml @@ -1,13 +1,15 @@ name: Start db-sync server description: >- - Check out logseq@feature/native-mobile-app, build the db-sync node adapter, + Check out the native-compatible Logseq revision, build the db-sync node adapter, and start it on the given port (default 8787). The app e2e flows create sync graphs against it. logseq/logseq is public, so no credential is needed. inputs: ref: description: logseq/logseq ref to check out. required: false - default: feature/native-mobile-app + # This revision includes the snapshot transaction cursor required by the + # shared native bootstrap protocol. The old feature branch omitted it. + default: a63da5cb7993302f8c58ccf42eaf0083b77cf42b port: description: Port the server binds. required: false diff --git a/android/app/src/androidTest/kotlin/com/logseq/app/AndroidNativeSurfacesTest.kt b/android/app/src/androidTest/kotlin/com/logseq/app/AndroidNativeSurfacesTest.kt index 8eddda05..5ef81a71 100644 --- a/android/app/src/androidTest/kotlin/com/logseq/app/AndroidNativeSurfacesTest.kt +++ b/android/app/src/androidTest/kotlin/com/logseq/app/AndroidNativeSurfacesTest.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.test.swipeRight import androidx.compose.ui.unit.dp import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry import com.logseq.app.runtime.logseqIconResolver import dev.lui.LuiBackend import dev.lui.LuiEvent @@ -135,6 +136,55 @@ class AndroidNativeSurfacesTest { ui.runOnIdle { assertEquals(listOf(LuiEvent.Dismiss(2)), events) } } + private fun assertModalControlsExposeResourceIds(kind: String) { + render(kind) + ui.runOnIdle { + val ops = JSONArray() + listOf("text-field", "switch", "button").forEachIndexed { index, control -> + val id = index + 3 + ops.put(op("create-node", "id" to id, "kind" to control)) + .put(op("set-prop", "id" to id, "property" to "accessibility-identifier", "value" to "modal.$control")) + .put(op("set-prop", "id" to id, "property" to "text", "value" to control)) + .put(op("insert-child", "parent" to 2, "child" to id, "index" to index)) + } + apply(ops) + } + val automation = InstrumentationRegistry.getInstrumentation().uiAutomation + val originalInfo = automation.serviceInfo + automation.serviceInfo = android.accessibilityservice.AccessibilityServiceInfo().apply { + eventTypes = originalInfo.eventTypes + feedbackType = originalInfo.feedbackType + flags = originalInfo.flags or + android.accessibilityservice.AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS or + android.accessibilityservice.AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS + } + fun resourceIds(): Set { + val pending = java.util.ArrayDeque() + automation.windows.forEach { window -> window.root?.let { pending.add(it) } } + val ids = mutableSetOf() + while (pending.isNotEmpty()) { + val node = pending.removeFirst() + node.viewIdResourceName?.let { ids.add(it) } + repeat(node.childCount) { index -> node.getChild(index)?.let { pending.add(it) } } + } + return ids + } + val expected = setOf("modal.text-field", "modal.switch", "modal.button") + try { + ui.waitUntil(5_000) { resourceIds().containsAll(expected) } + } finally { + automation.serviceInfo = originalInfo + } + } + + @Test fun dialogControlsExposeResourceIdsInTheirOwnNativeWindow() { + assertModalControlsExposeResourceIds("dialog") + } + + @Test fun sheetControlsExposeResourceIdsInTheirOwnNativeWindow() { + assertModalControlsExposeResourceIds("sheet") + } + @Test fun selectedListItemsExposeNativeSingleSelection() { render("list-item", mapOf("text" to "Example", "selected" to true, "press-enabled" to true)) ui.onNodeWithText("Example").assertIsSelected().performClick() diff --git a/apple/Package.resolved b/apple/Package.resolved index ccb47c81..77e5e272 100644 --- a/apple/Package.resolved +++ b/apple/Package.resolved @@ -15,7 +15,7 @@ "kind" : "remoteSourceControl", "location" : "ssh://git@github.com/logseq/lui.git", "state" : { - "revision" : "db5cca5056a76775b2ce12d085c9eadde44cc42d" + "revision" : "52aae8634c9add1e0c11fecfdda102b3f3644b08" } }, { diff --git a/apple/Package.swift b/apple/Package.swift index 00c4f810..3b7d2922 100644 --- a/apple/Package.swift +++ b/apple/Package.swift @@ -44,7 +44,7 @@ let package = Package( .library(name: "LogseqModel", type: .dynamic, targets: ["LogseqModel"]), ], dependencies: [ - .package(url: "ssh://git@github.com/logseq/lui.git", revision: "db5cca5056a76775b2ce12d085c9eadde44cc42d"), + .package(url: "ssh://git@github.com/logseq/lui.git", revision: "52aae8634c9add1e0c11fecfdda102b3f3644b08"), .package(url: "https://github.com/gonzalezreal/swiftui-math", from: "0.1.0"), .package(url: "https://github.com/appstefan/highlightswift.git", from: "1.1.0") ], diff --git a/logseq.opam b/logseq.opam index c0d532e0..3f826fc7 100644 --- a/logseq.opam +++ b/logseq.opam @@ -34,7 +34,7 @@ pin-depends: [ ["melange-transit-core.0.1.2" "git+https://github.com/logseq/melange-transit.git#main"] ["melange-transit-native.0.1.2" "git+https://github.com/logseq/melange-transit.git#main"] ["melange-transit-melange.0.1.2" "git+https://github.com/logseq/melange-transit.git#main"] - ["lui.0.1.0" "git+ssh://git@github.com/logseq/lui.git#db5cca5056a76775b2ce12d085c9eadde44cc42d"] + ["lui.0.1.0" "git+ssh://git@github.com/logseq/lui.git#52aae8634c9add1e0c11fecfdda102b3f3644b08"] ["drive.dev" "git+https://github.com/logseq/drive.git#dd735a65ed525d1c36db38ac8e8114aa81d314a1"] ["ocaml-signal.0.1.0" "git+https://github.com/logseq/ocaml-signal.git#02e18cdc5e0ad672aa223f638eaa3ba52fc9421e"] ["mldoc.dev" "git+https://github.com/logseq/mldoc.git#f11230c367964af2b575524d00779cfb1ffc7147"] diff --git a/scripts/test-android-e2e-runner.sh b/scripts/test-android-e2e-runner.sh index 167c5f49..781391d5 100755 --- a/scripts/test-android-e2e-runner.sh +++ b/scripts/test-android-e2e-runner.sh @@ -259,6 +259,8 @@ PATH="$mock_bin:$PATH" \ "$runner" connect >/dev/null grep -Fxq -- '-s test-device reverse tcp:8787 tcp:8787' "$adb_args" \ || die "Android E2E runner did not expose the host-local backend to the device" +grep -Eq 'run-as com\.logseq\.app cp .* shared_prefs/logseq\.xml$' "$adb_args" \ + || die "Android E2E server override did not use the app's SharedPreferences file" PATH="$mock_bin:$PATH" \ ANDROID_SERIAL=test-device \ diff --git a/scripts/test-android-e2e.sh b/scripts/test-android-e2e.sh index fa6d99ed..27479904 100755 --- a/scripts/test-android-e2e.sh +++ b/scripts/test-android-e2e.sh @@ -380,7 +380,7 @@ if [[ -n ${LOGSEQ_E2E_BASE_URL:-} ]]; then '' >"$preferences_file" adb -s "$device" push "$preferences_file" "$remote_preferences" >/dev/null adb -s "$device" shell run-as "$app_id" mkdir -p shared_prefs - adb -s "$device" shell run-as "$app_id" cp "$remote_preferences" shared_prefs/defaults.xml + adb -s "$device" shell run-as "$app_id" cp "$remote_preferences" shared_prefs/logseq.xml adb -s "$device" shell rm -f "$remote_preferences" fi diff --git a/tests/e2e/android-capture-search.yaml b/tests/e2e/android-capture-search.yaml index 29d5004e..479209b3 100644 --- a/tests/e2e/android-capture-search.yaml +++ b/tests/e2e/android-capture-search.yaml @@ -59,6 +59,9 @@ appId: com.logseq.app - assertVisible: id: "screen.search" - assertVisible: "Android E2E capture" +# Returning to search restores focus to its field. Back first dismisses the +# Android IME; close the keyboard explicitly before testing route dismissal. +- hideKeyboard - pressKey: BACK - assertVisible: id: "button.composer.expand" diff --git a/tests/e2e/android-local-graph-setup.yaml b/tests/e2e/android-local-graph-setup.yaml index 8155539a..07b7529d 100644 --- a/tests/e2e/android-local-graph-setup.yaml +++ b/tests/e2e/android-local-graph-setup.yaml @@ -15,28 +15,34 @@ appId: com.logseq.app - tapOn: "Use without an account" - extendedWaitUntil: visible: - id: "signInFormUsername" + id: "signInFormUsername|screen.graph-picker|button.composer.expand" timeout: 30000 - - tapOn: - id: "signInFormUsername" - - inputText: ${USERNAME} + # A retained browser session can complete sign-in without the form. - runFlow: when: - visible: "Try out your stylus" + visible: + id: "signInFormUsername" commands: - - tapOn: "Cancel" - - tapOn: - id: "signInFormPassword" - - inputText: ${PASSWORD} - - hideKeyboard - - extendedWaitUntil: - visible: "submit" - timeout: 10000 - - tapOn: "submit" - - extendedWaitUntil: - visible: - id: "screen.graph-picker" - timeout: 60000 + - tapOn: + id: "signInFormUsername" + - inputText: ${USERNAME} + - runFlow: + when: + visible: "Try out your stylus" + commands: + - tapOn: "Cancel" + - tapOn: + id: "signInFormPassword" + - inputText: ${PASSWORD} + - hideKeyboard + - extendedWaitUntil: + visible: "submit" + timeout: 10000 + - tapOn: "submit" + - extendedWaitUntil: + visible: + id: "screen.graph-picker|button.composer.expand" + timeout: 60000 - runFlow: when: visible: @@ -46,8 +52,15 @@ appId: com.logseq.app id: "button.graph-add" - extendedWaitUntil: visible: - id: "field.graph-name" + # Login can finish opening the bootstrap graph after the picker + # was observed. Wait for either stable outcome of that transition. + id: "field.graph-name|button.composer.expand" timeout: 30000 +- runFlow: + when: + visible: + id: "field.graph-name" + commands: - tapOn: id: "field.graph-name" - inputText: "android-e2e-smoke" @@ -65,5 +78,5 @@ appId: com.logseq.app timeout: 60000 - extendedWaitUntil: visible: - id: "button.sidebar" + id: "button.composer.expand" timeout: 60000 diff --git a/tests/e2e/android-search-navigation.yaml b/tests/e2e/android-search-navigation.yaml index 0a10fee7..800970d8 100644 --- a/tests/e2e/android-search-navigation.yaml +++ b/tests/e2e/android-search-navigation.yaml @@ -27,6 +27,7 @@ appId: com.logseq.app - assertVisible: id: "screen.search" - assertVisible: "E2E Page Target 20260816" +- hideKeyboard - pressKey: BACK - assertVisible: id: "button.search" diff --git a/tests/e2e/ios-capture-responsive.yaml b/tests/e2e/ios-capture-responsive.yaml index cadc22ad..4a959714 100644 --- a/tests/e2e/ios-capture-responsive.yaml +++ b/tests/e2e/ios-capture-responsive.yaml @@ -15,13 +15,16 @@ appId: com.logseq.logseq - assertVisible: id: "button.composer.expand" - tapOn: - id: "button.connection" -- tapOn: "Settings" + id: "button.sidebar" +- tapOn: + id: "button.sidebar.settings" - assertVisible: "Sync server" - assertVisible: id: "field.base-url" - tapOn: id: "button.connection.cancel" +- tapOn: + id: "button.sidebar.dismiss" - assertVisible: id: "button.composer.expand" - tapOn: