commonMain.androidx.compose.ui.test.TestOwner.kt Maven / Gradle / Ivy
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.ui.test
import androidx.compose.ui.node.RootForTest
import androidx.compose.ui.semantics.SemanticsNode
import androidx.compose.ui.semantics.getAllSemanticsNodes
import androidx.compose.ui.text.input.EditCommand
import androidx.compose.ui.text.input.ImeAction
/**
* Provides necessary services to facilitate testing.
*
* This is typically implemented by entities like test rule.
*/
@InternalTestApi
interface TestOwner {
/**
* Clock that drives frames and recompositions in compose tests.
*/
val mainClock: MainTestClock
/**
* Sends the given list of text commands to the given semantics node.
*/
fun sendTextInputCommand(node: SemanticsNode, command: List)
/**
* Sends the given IME action to the given semantics node.
*/
fun sendImeAction(node: SemanticsNode, actionSpecified: ImeAction)
/**
* Runs the given [action] on the ui thread.
*
* This is a blocking call.
*/
// TODO: Does ui-test really need it? Can it use coroutine context on Owner?
fun runOnUiThread(action: () -> T): T
/**
* Collects all [RootForTest]s from all compose hierarchies.
*
* This is a blocking call. Returns only after compose is idle.
*
* Can crash in case it hits time out. This is not supposed to be handled as it
* surfaces only in incorrect tests.
*
* @param atLeastOneRootExpected Whether the caller expects that at least one compose root is
* present in the tested app. This affects synchronization efforts / timeouts of this API.
*/
fun getRoots(atLeastOneRootExpected: Boolean): Set
}
@InternalTestApi
fun createTestContext(owner: TestOwner): TestContext {
return TestContext(owner)
}
@OptIn(InternalTestApi::class)
class TestContext internal constructor(internal val testOwner: TestOwner) {
/**
* Stores the [InputDispatcherState] of each [RootForTest]. The state will be restored in an
* [InputDispatcher] when it is created for an owner that has a state stored.
*/
internal val states = mutableMapOf()
/**
* Collects all [SemanticsNode]s from all compose hierarchies.
*
* This is a blocking call. Returns only after compose is idle.
*
* Can crash in case it hits time out. This is not supposed to be handled as it
* surfaces only in incorrect tests.
*/
internal fun getAllSemanticsNodes(
atLeastOneRootRequired: Boolean,
useUnmergedTree: Boolean
): Iterable {
val roots = testOwner.getRoots(atLeastOneRootRequired).also {
check(!atLeastOneRootRequired || it.isNotEmpty()) {
"No compose views found in the app. Is your Activity resumed?"
}
}
return roots.flatMap {
it.semanticsOwner.getAllSemanticsNodes(mergingEnabled = !useUnmergedTree)
}
}
}