appium-espresso-driver 8.0.0 → 8.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +5 -0
- package/build/lib/server-builder.d.ts +8 -3
- package/build/lib/server-builder.d.ts.map +1 -1
- package/build/lib/server-builder.js +8 -0
- package/build/lib/server-builder.js.map +1 -1
- package/espresso-server/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk +0 -0
- package/espresso-server/app/build.gradle.kts +20 -15
- package/espresso-server/build.gradle.kts +3 -18
- package/espresso-server/buildSrc/.gradle/9.4.1/executionHistory/executionHistory.bin +0 -0
- package/espresso-server/buildSrc/.gradle/9.4.1/executionHistory/executionHistory.lock +0 -0
- package/espresso-server/buildSrc/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/espresso-server/buildSrc/.gradle/buildOutputCleanup/cache.properties +1 -1
- package/espresso-server/buildSrc/.gradle/file-system.probe +0 -0
- package/espresso-server/buildSrc/build/classes/kotlin/main/META-INF/buildSrc.kotlin_module +0 -0
- package/espresso-server/buildSrc/build/classes/kotlin/main/io/appium/espressoserver/gradle/CapabilityVersionKt.class +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab.keystream +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab.keystream.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab.values.at +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab_i +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/package-parts.tab_i.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/counters.tab +1 -1
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/cacheable/last-build.bin +0 -0
- package/espresso-server/buildSrc/build/kotlin/compileKotlin/classpath-snapshot/shrunk-classpath-snapshot.bin +0 -0
- package/espresso-server/buildSrc/build/libs/buildSrc.jar +0 -0
- package/espresso-server/buildSrc/build.gradle.kts +2 -1
- package/espresso-server/buildSrc/src/main/kotlin/io/appium/espressoserver/gradle/CapabilityVersion.kt +12 -0
- package/espresso-server/gradle/libs.versions.toml +16 -0
- package/espresso-server/gradle.properties +1 -12
- package/espresso-server/library/build.gradle.kts +53 -32
- package/espresso-server/library/src/composeOff/java/io/appium/espressoserver/lib/compose/ComposeHandlerBridge.kt +73 -0
- package/espresso-server/library/src/composeOff/java/io/appium/espressoserver/lib/helpers/ComposeViewCache.kt +29 -0
- package/espresso-server/library/src/composeOff/java/io/appium/espressoserver/lib/http/Server.kt +21 -0
- package/espresso-server/library/src/composeOff/java/io/appium/espressoserver/lib/model/SourceDocumentComposeHooks.kt +26 -0
- package/espresso-server/library/src/composeOn/java/io/appium/espressoserver/lib/compose/ComposeHandlerBridge.kt +180 -0
- package/espresso-server/library/src/composeOn/java/io/appium/espressoserver/lib/compose/ComposeScreenshot.kt +49 -0
- package/espresso-server/library/src/composeOn/java/io/appium/espressoserver/lib/drivers/DriverContextComposeRule.kt +29 -0
- package/espresso-server/library/src/{main → composeOn}/java/io/appium/espressoserver/lib/helpers/ComposeNodeFinder.kt +1 -0
- package/espresso-server/library/src/composeOn/java/io/appium/espressoserver/lib/helpers/ComposeViewCache.kt +37 -0
- package/espresso-server/library/src/composeOn/java/io/appium/espressoserver/lib/http/Server.kt +30 -0
- package/espresso-server/library/src/composeOn/java/io/appium/espressoserver/lib/model/ComposeElement.kt +33 -0
- package/espresso-server/library/src/composeOn/java/io/appium/espressoserver/lib/model/SourceDocumentComposeHooks.kt +103 -0
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/drivers/DriverContext.kt +3 -6
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/Clear.kt +2 -12
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/Click.kt +2 -12
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/DeleteSession.kt +2 -2
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/ElementScreenshot.kt +2 -3
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/ElementValue.kt +2 -12
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/FindElement.kt +3 -12
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/FindElements.kt +3 -7
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/GetAttribute.kt +3 -7
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/GetDisplayed.kt +2 -9
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/GetEnabled.kt +2 -4
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/GetLocation.kt +2 -6
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/GetName.kt +2 -6
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/GetRect.kt +2 -4
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/GetSelected.kt +2 -4
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/GetSize.kt +2 -6
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/Keys.kt +2 -16
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/MobileSwipe.kt +2 -24
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/RequestHandler.kt +8 -1
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/Text.kt +2 -4
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/exceptions/ComposeNotSupportedException.kt +28 -0
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/handlers/exceptions/InvalidArgumentException.kt +1 -1
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/helpers/EspressoViewsCache.kt +0 -17
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/helpers/ScreenshotHelpers.kt +0 -13
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/helpers/Version.kt +1 -1
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/http/Router.kt +1 -1
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/http/{Server.kt → ServerBase.kt} +32 -20
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/model/Element.kt +0 -13
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/model/SourceDocument.kt +4 -85
- package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/model/settings/DriverSetting.kt +15 -4
- package/espresso-server/settings.gradle.kts +30 -0
- package/lib/server-builder.ts +15 -2
- package/npm-shrinkwrap.json +36 -14
- package/package.json +2 -1
- /package/espresso-server/library/src/{main → composeOn}/java/io/appium/espressoserver/lib/model/ComposeNodeElement.kt +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* See the NOTICE file distributed with this work for additional
|
|
5
|
+
* information regarding copyright ownership.
|
|
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
|
+
package io.appium.espressoserver.lib.compose
|
|
18
|
+
|
|
19
|
+
import io.appium.espressoserver.lib.handlers.exceptions.ComposeNotSupportedException
|
|
20
|
+
import io.appium.espressoserver.lib.model.AppiumParams
|
|
21
|
+
import io.appium.espressoserver.lib.model.BaseElement
|
|
22
|
+
import io.appium.espressoserver.lib.model.Locator
|
|
23
|
+
import io.appium.espressoserver.lib.model.Location
|
|
24
|
+
import io.appium.espressoserver.lib.model.MobileSwipeParams
|
|
25
|
+
import io.appium.espressoserver.lib.model.Rect
|
|
26
|
+
import io.appium.espressoserver.lib.model.Size
|
|
27
|
+
import io.appium.espressoserver.lib.model.TextValueParams
|
|
28
|
+
|
|
29
|
+
internal object ComposeHandlerBridge {
|
|
30
|
+
private fun unsupported(): Nothing = throw ComposeNotSupportedException()
|
|
31
|
+
|
|
32
|
+
fun findElement(params: Locator): BaseElement = unsupported()
|
|
33
|
+
|
|
34
|
+
fun findElements(params: Locator): List<BaseElement> = unsupported()
|
|
35
|
+
|
|
36
|
+
fun click(params: AppiumParams) {
|
|
37
|
+
unsupported()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
fun clear(params: AppiumParams) {
|
|
41
|
+
unsupported()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fun elementValue(params: TextValueParams, isReplacing: Boolean) {
|
|
45
|
+
unsupported()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
fun keys(params: TextValueParams) {
|
|
49
|
+
unsupported()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fun mobileSwipe(params: MobileSwipeParams): Void? = unsupported()
|
|
53
|
+
|
|
54
|
+
fun getDisplayed(params: AppiumParams): Boolean = unsupported()
|
|
55
|
+
|
|
56
|
+
fun elementScreenshot(elementId: String): String = unsupported()
|
|
57
|
+
|
|
58
|
+
fun getAttribute(elementId: String, attributeName: String): String? = unsupported()
|
|
59
|
+
|
|
60
|
+
fun getEnabled(params: AppiumParams): Boolean = unsupported()
|
|
61
|
+
|
|
62
|
+
fun getLocation(params: AppiumParams): Location = unsupported()
|
|
63
|
+
|
|
64
|
+
fun getName(params: AppiumParams): String? = unsupported()
|
|
65
|
+
|
|
66
|
+
fun getRect(params: AppiumParams): Rect = unsupported()
|
|
67
|
+
|
|
68
|
+
fun getSelected(params: AppiumParams): Boolean = unsupported()
|
|
69
|
+
|
|
70
|
+
fun getSize(params: AppiumParams): Size = unsupported()
|
|
71
|
+
|
|
72
|
+
fun text(params: AppiumParams): String = unsupported()
|
|
73
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* See the NOTICE file distributed with this work for additional
|
|
5
|
+
* information regarding copyright ownership.
|
|
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
|
+
package io.appium.espressoserver.lib.helpers
|
|
18
|
+
|
|
19
|
+
/** No-op when Jetpack Compose is not included in this server build. */
|
|
20
|
+
object ComposeViewCache {
|
|
21
|
+
@Suppress("UNUSED_PARAMETER")
|
|
22
|
+
fun put(id: String, composeNode: Any) {
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
fun get(id: String?): Any? = null
|
|
26
|
+
|
|
27
|
+
fun reset() {
|
|
28
|
+
}
|
|
29
|
+
}
|
package/espresso-server/library/src/composeOff/java/io/appium/espressoserver/lib/http/Server.kt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* See the NOTICE file distributed with this work for additional
|
|
5
|
+
* information regarding copyright ownership.
|
|
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
|
+
package io.appium.espressoserver.lib.http
|
|
18
|
+
|
|
19
|
+
class Server : ServerBase() {
|
|
20
|
+
override fun advanceComposeMainClockIfNeeded() = Unit
|
|
21
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* See the NOTICE file distributed with this work for additional
|
|
5
|
+
* information regarding copyright ownership.
|
|
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
|
+
package io.appium.espressoserver.lib.model
|
|
18
|
+
|
|
19
|
+
import io.appium.espressoserver.lib.handlers.exceptions.ComposeNotSupportedException
|
|
20
|
+
|
|
21
|
+
internal object SourceDocumentComposeHooks {
|
|
22
|
+
@Suppress("UNUSED_PARAMETER")
|
|
23
|
+
fun appendComposeStrategy(doc: SourceDocument, root: Any?) {
|
|
24
|
+
throw ComposeNotSupportedException()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* See the NOTICE file distributed with this work for additional
|
|
5
|
+
* information regarding copyright ownership.
|
|
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
|
+
package io.appium.espressoserver.lib.compose
|
|
18
|
+
|
|
19
|
+
import androidx.compose.ui.test.assertIsDisplayed
|
|
20
|
+
import androidx.compose.ui.test.performClick
|
|
21
|
+
import androidx.compose.ui.test.performGesture
|
|
22
|
+
import androidx.compose.ui.test.performTextClearance
|
|
23
|
+
import androidx.compose.ui.test.performTextInput
|
|
24
|
+
import androidx.compose.ui.test.swipeDown
|
|
25
|
+
import androidx.compose.ui.test.swipeLeft
|
|
26
|
+
import androidx.compose.ui.test.swipeRight
|
|
27
|
+
import androidx.compose.ui.test.swipeUp
|
|
28
|
+
import io.appium.espressoserver.lib.handlers.exceptions.InvalidArgumentException
|
|
29
|
+
import io.appium.espressoserver.lib.handlers.exceptions.InvalidElementStateException
|
|
30
|
+
import io.appium.espressoserver.lib.handlers.exceptions.InvalidSelectorException
|
|
31
|
+
import io.appium.espressoserver.lib.handlers.exceptions.NoSuchElementException
|
|
32
|
+
import io.appium.espressoserver.lib.handlers.exceptions.StaleElementException
|
|
33
|
+
import io.appium.espressoserver.lib.helpers.AndroidLogger
|
|
34
|
+
import io.appium.espressoserver.lib.helpers.getNodeInteractionById
|
|
35
|
+
import io.appium.espressoserver.lib.helpers.getSemanticsNode
|
|
36
|
+
import io.appium.espressoserver.lib.helpers.toNodeInteractionsCollection
|
|
37
|
+
import io.appium.espressoserver.lib.model.AppiumParams
|
|
38
|
+
import io.appium.espressoserver.lib.model.BaseElement
|
|
39
|
+
import io.appium.espressoserver.lib.model.ComposeElement
|
|
40
|
+
import io.appium.espressoserver.lib.model.ComposeNodeElement
|
|
41
|
+
import io.appium.espressoserver.lib.model.Locator
|
|
42
|
+
import io.appium.espressoserver.lib.model.Location
|
|
43
|
+
import io.appium.espressoserver.lib.model.MobileSwipeParams
|
|
44
|
+
import io.appium.espressoserver.lib.model.Rect
|
|
45
|
+
import io.appium.espressoserver.lib.model.Size
|
|
46
|
+
import io.appium.espressoserver.lib.model.TextValueParams
|
|
47
|
+
|
|
48
|
+
internal object ComposeHandlerBridge {
|
|
49
|
+
fun findElement(params: Locator): BaseElement {
|
|
50
|
+
val nodeInteractions = toNodeInteractionsCollection(params)
|
|
51
|
+
if (nodeInteractions.fetchSemanticsNodes(false).isEmpty()) {
|
|
52
|
+
throw NoSuchElementException(
|
|
53
|
+
String.format(
|
|
54
|
+
"Could not find a compose element with strategy '%s' and selector '%s'",
|
|
55
|
+
params.using,
|
|
56
|
+
params.value,
|
|
57
|
+
),
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
return ComposeElement(nodeInteractions[0])
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
fun findElements(params: Locator): List<BaseElement> {
|
|
64
|
+
val nodeInteractions = toNodeInteractionsCollection(params)
|
|
65
|
+
return List(nodeInteractions.fetchSemanticsNodes(false).size) { index ->
|
|
66
|
+
ComposeElement(nodeInteractions[index])
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fun click(params: AppiumParams) {
|
|
71
|
+
try {
|
|
72
|
+
getNodeInteractionById(params.elementId).performClick()
|
|
73
|
+
} catch (e: AssertionError) {
|
|
74
|
+
throw StaleElementException(params.elementId!!)
|
|
75
|
+
} catch (e: IllegalArgumentException) {
|
|
76
|
+
throw InvalidElementStateException("Click", params.elementId!!, e)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fun clear(params: AppiumParams) {
|
|
81
|
+
try {
|
|
82
|
+
getNodeInteractionById(params.elementId).performTextClearance()
|
|
83
|
+
} catch (e: AssertionError) {
|
|
84
|
+
throw StaleElementException(params.elementId!!)
|
|
85
|
+
} catch (e: IllegalArgumentException) {
|
|
86
|
+
throw InvalidElementStateException("Clear", params.elementId!!, e)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
fun elementValue(params: TextValueParams, isReplacing: Boolean) {
|
|
91
|
+
val value: String = extractTextToEnter(params)
|
|
92
|
+
try {
|
|
93
|
+
if (isReplacing) {
|
|
94
|
+
getNodeInteractionById(params.elementId).performTextClearance()
|
|
95
|
+
}
|
|
96
|
+
getNodeInteractionById(params.elementId).performTextInput(value)
|
|
97
|
+
} catch (e: IllegalArgumentException) {
|
|
98
|
+
throw InvalidElementStateException("Clear", params.elementId!!, e)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
fun keys(params: TextValueParams) {
|
|
103
|
+
try {
|
|
104
|
+
val keys = params.value ?: return
|
|
105
|
+
keys.forEach {
|
|
106
|
+
getNodeInteractionById(params.elementId).performTextInput(it)
|
|
107
|
+
}
|
|
108
|
+
} catch (e: AssertionError) {
|
|
109
|
+
throw StaleElementException(params.elementId!!)
|
|
110
|
+
} catch (e: IllegalArgumentException) {
|
|
111
|
+
throw InvalidElementStateException("Keys", params.elementId!!, e)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fun mobileSwipe(params: MobileSwipeParams): Void? {
|
|
116
|
+
val nodeInteractions = getNodeInteractionById(params.elementId)
|
|
117
|
+
AndroidLogger.info("Performing swipe action with direction '${params.direction}'")
|
|
118
|
+
when (params.direction) {
|
|
119
|
+
MobileSwipeParams.Direction.UP -> nodeInteractions.performGesture { swipeUp() }
|
|
120
|
+
MobileSwipeParams.Direction.DOWN -> nodeInteractions.performGesture { swipeDown() }
|
|
121
|
+
MobileSwipeParams.Direction.LEFT -> nodeInteractions.performGesture { swipeLeft() }
|
|
122
|
+
MobileSwipeParams.Direction.RIGHT -> nodeInteractions.performGesture { swipeRight() }
|
|
123
|
+
else -> throw InvalidArgumentException(
|
|
124
|
+
"Unknown swipe direction '${params.direction}'. " +
|
|
125
|
+
"Only the following values are supported: " +
|
|
126
|
+
MobileSwipeParams.Direction.values().joinToString(",") { x -> x.name.lowercase() },
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
return null
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
fun getDisplayed(params: AppiumParams): Boolean =
|
|
133
|
+
try {
|
|
134
|
+
getNodeInteractionById(params.elementId).assertIsDisplayed()
|
|
135
|
+
true
|
|
136
|
+
} catch (e: AssertionError) {
|
|
137
|
+
false
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
fun elementScreenshot(elementId: String): String =
|
|
141
|
+
ComposeScreenshot.takeComposeNodeScreenshot(getNodeInteractionById(elementId))
|
|
142
|
+
|
|
143
|
+
fun getAttribute(elementId: String, attributeName: String): String? =
|
|
144
|
+
ComposeNodeElement(getSemanticsNode(elementId)).getAttribute(attributeName)
|
|
145
|
+
|
|
146
|
+
fun getEnabled(params: AppiumParams): Boolean =
|
|
147
|
+
ComposeNodeElement(getSemanticsNode(params.elementId!!)).isEnabled
|
|
148
|
+
|
|
149
|
+
fun getLocation(params: AppiumParams): Location {
|
|
150
|
+
val composeNodeElement = ComposeNodeElement(getSemanticsNode(params.elementId!!))
|
|
151
|
+
return Location(composeNodeElement.bounds.left, composeNodeElement.bounds.top)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
fun getName(params: AppiumParams): String? {
|
|
155
|
+
val composeNodeElement = ComposeNodeElement(getSemanticsNode(params.elementId!!))
|
|
156
|
+
return composeNodeElement.contentDescription?.toString()
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
fun getRect(params: AppiumParams): Rect =
|
|
160
|
+
ComposeNodeElement(getSemanticsNode(params.elementId!!)).rect
|
|
161
|
+
|
|
162
|
+
fun getSelected(params: AppiumParams): Boolean =
|
|
163
|
+
ComposeNodeElement(getSemanticsNode(params.elementId!!)).isSelected
|
|
164
|
+
|
|
165
|
+
fun getSize(params: AppiumParams): Size {
|
|
166
|
+
val bounds = ComposeNodeElement(getSemanticsNode(params.elementId!!)).bounds
|
|
167
|
+
return Size(bounds.width(), bounds.height())
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
fun text(params: AppiumParams): String =
|
|
171
|
+
ComposeNodeElement(getSemanticsNode(params.elementId!!)).text.toString()
|
|
172
|
+
|
|
173
|
+
private fun extractTextToEnter(params: TextValueParams) =
|
|
174
|
+
when (Pair(params.value == null, params.text == null)) {
|
|
175
|
+
Pair(first = true, second = true) ->
|
|
176
|
+
throw InvalidArgumentException("Must provide 'value' or 'text' property")
|
|
177
|
+
Pair(first = false, second = true) -> params.value!!.joinToString(separator = "")
|
|
178
|
+
else -> params.text!!
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* See the NOTICE file distributed with this work for additional
|
|
5
|
+
* information regarding copyright ownership.
|
|
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
|
+
package io.appium.espressoserver.lib.compose
|
|
18
|
+
|
|
19
|
+
import android.graphics.Bitmap
|
|
20
|
+
import android.util.Base64
|
|
21
|
+
import androidx.compose.ui.graphics.asAndroidBitmap
|
|
22
|
+
import androidx.compose.ui.test.SemanticsNodeInteraction
|
|
23
|
+
import androidx.compose.ui.test.captureToImage
|
|
24
|
+
import io.appium.espressoserver.lib.handlers.exceptions.ScreenCaptureException
|
|
25
|
+
import java.io.ByteArrayOutputStream
|
|
26
|
+
|
|
27
|
+
internal object ComposeScreenshot {
|
|
28
|
+
fun takeComposeNodeScreenshot(nodeInteraction: SemanticsNodeInteraction): String =
|
|
29
|
+
try {
|
|
30
|
+
encodeBitmap(
|
|
31
|
+
nodeInteraction.captureToImage().asAndroidBitmap(),
|
|
32
|
+
) { nodeInteraction.fetchSemanticsNode().toString() }
|
|
33
|
+
} catch (e: RuntimeException) {
|
|
34
|
+
throw ScreenCaptureException("Cannot take a screenshot of a node", e)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private fun encodeBitmap(bitmap: Bitmap?, targetNameSupplier: () -> String): String {
|
|
38
|
+
if (bitmap == null || bitmap.height == 0 || bitmap.width == 0) {
|
|
39
|
+
throw ScreenCaptureException(
|
|
40
|
+
"Cannot capture a shot of the ${targetNameSupplier()}. " +
|
|
41
|
+
"Make sure none of the currently visible " +
|
|
42
|
+
"views have FLAG_SECURE set and that it is possible to take a screenshot manually",
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
val outputStream = ByteArrayOutputStream()
|
|
46
|
+
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
|
|
47
|
+
return Base64.encodeToString(outputStream.toByteArray(), Base64.NO_WRAP)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* See the NOTICE file distributed with this work for additional
|
|
5
|
+
* information regarding copyright ownership.
|
|
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
|
+
package io.appium.espressoserver.lib.drivers
|
|
18
|
+
|
|
19
|
+
import androidx.compose.ui.test.junit4.ComposeTestRule
|
|
20
|
+
import androidx.compose.ui.test.junit4.createEmptyComposeRule
|
|
21
|
+
|
|
22
|
+
private val emptyComposeRule: ComposeTestRule = createEmptyComposeRule()
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Compose UI test rule for this build. Import [composeTestRule] when using from other packages
|
|
26
|
+
* (`import io.appium.espressoserver.lib.drivers.composeTestRule`).
|
|
27
|
+
*/
|
|
28
|
+
val DriverContext.composeTestRule: ComposeTestRule
|
|
29
|
+
get() = emptyComposeRule
|
|
@@ -19,6 +19,7 @@ package io.appium.espressoserver.lib.helpers
|
|
|
19
19
|
import androidx.compose.ui.semantics.SemanticsNode
|
|
20
20
|
import androidx.compose.ui.test.*
|
|
21
21
|
import io.appium.espressoserver.lib.drivers.DriverContext
|
|
22
|
+
import io.appium.espressoserver.lib.drivers.composeTestRule
|
|
22
23
|
import io.appium.espressoserver.lib.handlers.exceptions.InvalidSelectorException
|
|
23
24
|
import io.appium.espressoserver.lib.model.Strategy
|
|
24
25
|
import io.appium.espressoserver.lib.handlers.exceptions.InvalidArgumentException
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* See the NOTICE file distributed with this work for additional
|
|
5
|
+
* information regarding copyright ownership.
|
|
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
|
+
package io.appium.espressoserver.lib.helpers
|
|
18
|
+
|
|
19
|
+
import android.util.LruCache
|
|
20
|
+
import androidx.compose.ui.test.SemanticsNodeInteraction
|
|
21
|
+
|
|
22
|
+
const val COMPOSE_VIEW_CACHE_MAX_SIZE = 500
|
|
23
|
+
|
|
24
|
+
/** Compose does not have stable instances, use locator again. */
|
|
25
|
+
object ComposeViewCache {
|
|
26
|
+
private val cache = LruCache<String, SemanticsNodeInteraction>(COMPOSE_VIEW_CACHE_MAX_SIZE)
|
|
27
|
+
|
|
28
|
+
fun put(id: String, composeNode: SemanticsNodeInteraction) {
|
|
29
|
+
cache.put(id, composeNode)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fun get(id: String?): SemanticsNodeInteraction? = cache.get(id)
|
|
33
|
+
|
|
34
|
+
fun reset() {
|
|
35
|
+
cache.evictAll()
|
|
36
|
+
}
|
|
37
|
+
}
|
package/espresso-server/library/src/composeOn/java/io/appium/espressoserver/lib/http/Server.kt
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* See the NOTICE file distributed with this work for additional
|
|
5
|
+
* information regarding copyright ownership.
|
|
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
|
+
package io.appium.espressoserver.lib.http
|
|
18
|
+
|
|
19
|
+
import io.appium.espressoserver.lib.drivers.DriverContext
|
|
20
|
+
import io.appium.espressoserver.lib.drivers.composeTestRule
|
|
21
|
+
import org.junit.rules.TestRule
|
|
22
|
+
|
|
23
|
+
class Server : ServerBase(), TestRule by DriverContext.composeTestRule {
|
|
24
|
+
|
|
25
|
+
override fun advanceComposeMainClockIfNeeded() {
|
|
26
|
+
if (DriverContext.currentStrategyType == DriverContext.StrategyType.COMPOSE) {
|
|
27
|
+
DriverContext.composeTestRule.mainClock.advanceTimeByFrame()
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* See the NOTICE file distributed with this work for additional
|
|
5
|
+
* information regarding copyright ownership.
|
|
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
|
+
package io.appium.espressoserver.lib.model
|
|
18
|
+
|
|
19
|
+
import androidx.compose.ui.test.SemanticsNodeInteraction
|
|
20
|
+
import com.google.gson.annotations.SerializedName
|
|
21
|
+
import io.appium.espressoserver.lib.helpers.ComposeViewCache
|
|
22
|
+
import java.util.UUID
|
|
23
|
+
|
|
24
|
+
class ComposeElement(node: SemanticsNodeInteraction) : BaseElement {
|
|
25
|
+
@Suppress("JoinDeclarationAndAssignment")
|
|
26
|
+
@SerializedName(JSONWP_ELEMENT_KEY, alternate = [W3C_ELEMENT_KEY])
|
|
27
|
+
override val element: String
|
|
28
|
+
|
|
29
|
+
init {
|
|
30
|
+
element = UUID.randomUUID().toString()
|
|
31
|
+
ComposeViewCache.put(element, node)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
* you may not use this file except in compliance with the License.
|
|
4
|
+
* See the NOTICE file distributed with this work for additional
|
|
5
|
+
* information regarding copyright ownership.
|
|
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
|
+
package io.appium.espressoserver.lib.model
|
|
18
|
+
|
|
19
|
+
import androidx.compose.ui.semantics.SemanticsNode
|
|
20
|
+
import androidx.compose.ui.test.SelectionResult
|
|
21
|
+
import androidx.compose.ui.test.SemanticsNodeInteraction
|
|
22
|
+
import androidx.compose.ui.test.onRoot
|
|
23
|
+
import io.appium.espressoserver.lib.drivers.DriverContext
|
|
24
|
+
import io.appium.espressoserver.lib.drivers.composeTestRule
|
|
25
|
+
import io.appium.espressoserver.lib.helpers.AndroidLogger
|
|
26
|
+
import kotlin.reflect.full.declaredMemberFunctions
|
|
27
|
+
import kotlin.reflect.jvm.isAccessible
|
|
28
|
+
|
|
29
|
+
internal object SourceDocumentComposeHooks {
|
|
30
|
+
fun appendComposeStrategy(doc: SourceDocument, root: Any?) {
|
|
31
|
+
if (root != null) {
|
|
32
|
+
serializeComposeNode(doc, root as SemanticsNode, 0)
|
|
33
|
+
} else {
|
|
34
|
+
val rootNodes = rootSemanticNodes()
|
|
35
|
+
if (rootNodes.size == 1) {
|
|
36
|
+
serializeComposeNode(doc, rootNodes.first(), 0)
|
|
37
|
+
} else {
|
|
38
|
+
doc.serializer?.startTag(NAMESPACE, DEFAULT_TAG_NAME)
|
|
39
|
+
rootNodes.forEach { semanticsNode -> serializeComposeNode(doc, semanticsNode, 0) }
|
|
40
|
+
doc.serializer?.endTag(NAMESPACE, DEFAULT_TAG_NAME)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private fun serializeComposeNode(doc: SourceDocument, semanticsNode: SemanticsNode?, depth: Int) {
|
|
46
|
+
if (semanticsNode == null) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
val nodeElement = ComposeNodeElement(semanticsNode)
|
|
50
|
+
val className = nodeElement.className
|
|
51
|
+
val tagName = toXmlNodeName(className)
|
|
52
|
+
doc.serializer?.startTag(NAMESPACE, tagName)
|
|
53
|
+
|
|
54
|
+
linkedMapOf(
|
|
55
|
+
AttributesEnum.CLASS to { className },
|
|
56
|
+
AttributesEnum.INDEX to { nodeElement.index },
|
|
57
|
+
AttributesEnum.CLICKABLE to { nodeElement.isClickable },
|
|
58
|
+
AttributesEnum.ENABLED to { nodeElement.isEnabled },
|
|
59
|
+
AttributesEnum.FOCUSED to { nodeElement.isFocused },
|
|
60
|
+
AttributesEnum.SCROLLABLE to { nodeElement.isScrollable },
|
|
61
|
+
AttributesEnum.SELECTED to { nodeElement.isSelected },
|
|
62
|
+
AttributesEnum.CHECKED to { nodeElement.isChecked },
|
|
63
|
+
AttributesEnum.VIEW_TAG to { nodeElement.viewTag },
|
|
64
|
+
AttributesEnum.CONTENT_DESC to { nodeElement.contentDescription },
|
|
65
|
+
AttributesEnum.BOUNDS to { nodeElement.bounds.toShortString() },
|
|
66
|
+
AttributesEnum.TEXT to { nodeElement.text },
|
|
67
|
+
AttributesEnum.PASSWORD to { nodeElement.isPassword },
|
|
68
|
+
AttributesEnum.RESOURCE_ID to { nodeElement.resourceId },
|
|
69
|
+
).forEach {
|
|
70
|
+
doc.setAttribute(it.key, it.value())
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (depth < MAX_TRAVERSAL_DEPTH) {
|
|
74
|
+
val children = semanticsNode.children
|
|
75
|
+
for (index in 0 until children.count()) {
|
|
76
|
+
serializeComposeNode(doc, children[index], depth + 1)
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
AndroidLogger.warn(
|
|
80
|
+
"Skipping traversal of ${semanticsNode.javaClass.name}'s children, since " +
|
|
81
|
+
"the current depth has reached its maximum allowed value of $depth",
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
doc.serializer?.endTag(NAMESPACE, tagName)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private fun rootSemanticNodes(): List<SemanticsNode> =
|
|
89
|
+
try {
|
|
90
|
+
listOf(DriverContext.composeTestRule.onRoot(useUnmergedTree = true).fetchSemanticsNode())
|
|
91
|
+
} catch (e: AssertionError) {
|
|
92
|
+
val result: SelectionResult =
|
|
93
|
+
SemanticsNodeInteraction::class.declaredMemberFunctions.find { it.name == "fetchSemanticsNodes" }?.let {
|
|
94
|
+
it.isAccessible = true
|
|
95
|
+
if (it.parameters.size == 4) {
|
|
96
|
+
it.call(DriverContext.composeTestRule.onRoot(useUnmergedTree = true), true, null, true)
|
|
97
|
+
} else {
|
|
98
|
+
it.call(DriverContext.composeTestRule.onRoot(useUnmergedTree = true), true, null)
|
|
99
|
+
}
|
|
100
|
+
} as SelectionResult
|
|
101
|
+
result.selectedNodes
|
|
102
|
+
}
|
|
103
|
+
}
|
package/espresso-server/library/src/main/java/io/appium/espressoserver/lib/drivers/DriverContext.kt
CHANGED
|
@@ -16,19 +16,16 @@
|
|
|
16
16
|
|
|
17
17
|
package io.appium.espressoserver.lib.drivers
|
|
18
18
|
|
|
19
|
-
import androidx.compose.ui.test.junit4.createEmptyComposeRule
|
|
20
|
-
|
|
21
19
|
object DriverContext {
|
|
22
20
|
var currentStrategyType: StrategyType = StrategyType.ESPRESSO
|
|
23
21
|
private set
|
|
24
22
|
|
|
25
|
-
val composeTestRule = createEmptyComposeRule()
|
|
26
|
-
|
|
27
23
|
fun setDriverStrategy(strategyType: StrategyType) {
|
|
28
24
|
currentStrategyType = strategyType
|
|
29
25
|
}
|
|
30
26
|
|
|
31
27
|
enum class StrategyType {
|
|
32
|
-
ESPRESSO,
|
|
28
|
+
ESPRESSO,
|
|
29
|
+
COMPOSE,
|
|
33
30
|
}
|
|
34
|
-
}
|
|
31
|
+
}
|