1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
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 androidx.compose.ui.test
18 
19 import androidx.compose.ui.semantics.SemanticsNode
20 
21 internal val SemanticsNode.siblings: List<SemanticsNode>
22     get() {
23         val node = this
<lambda>null24         return parent?.run { this.children.filter { it.id != node.id } } ?: emptyList()
25     }
26 
27 /**
28  * Returns a parent of this node.
29  *
30  * Any subsequent operation on its result will expect exactly one element found (unless
31  * [SemanticsNodeInteraction.assertDoesNotExist] is used) and will throw [AssertionError] if none or
32  * more than one element is found.
33  */
SemanticsNodeInteractionnull34 fun SemanticsNodeInteraction.onParent(): SemanticsNodeInteraction {
35     return SemanticsNodeInteraction(
36         testContext,
37         useUnmergedTree,
38         selector.addSelectionFromSingleNode("parent") { listOfNotNull(it.parent) }
39     )
40 }
41 
42 /** Returns children of this node. */
SemanticsNodeInteractionnull43 fun SemanticsNodeInteraction.onChildren(): SemanticsNodeInteractionCollection {
44     return SemanticsNodeInteractionCollection(
45         testContext,
46         useUnmergedTree,
47         selector.addSelectionFromSingleNode("children") { it.children }
48     )
49 }
50 
51 /**
52  * Returns exactly one child of this node.
53  *
54  * Use this only if this node has exactly one child.
55  *
56  * Any subsequent operation on its result will expect exactly one element found (unless
57  * [SemanticsNodeInteraction.assertDoesNotExist] is used) and will throw [AssertionError] if none or
58  * more than one element is found.
59  */
SemanticsNodeInteractionnull60 fun SemanticsNodeInteraction.onChild(): SemanticsNodeInteraction {
61     return SemanticsNodeInteraction(
62         testContext,
63         useUnmergedTree,
64         selector.addSelectionFromSingleNode("child") { it.children }
65     )
66 }
67 
68 /**
69  * Returns child of this node at the given index.
70  *
71  * This is just a shortcut for "children[index]".
72  */
onChildAtnull73 fun SemanticsNodeInteraction.onChildAt(index: Int): SemanticsNodeInteraction = onChildren()[index]
74 
75 /**
76  * Returns all siblings of this node.
77  *
78  * Example: For the following tree
79  *
80  * ```
81  * |-A
82  *   |-B1
83  *   |-B2 <- this node
84  *   |-B3
85  * Returns B1, B3
86  * ```
87  */
88 fun SemanticsNodeInteraction.onSiblings(): SemanticsNodeInteractionCollection {
89     return SemanticsNodeInteractionCollection(
90         testContext,
91         useUnmergedTree,
92         selector.addSelectionFromSingleNode("siblings") { it.siblings }
93     )
94 }
95 
96 /**
97  * Returns exactly one sibling of this node.
98  *
99  * Use this only if this node has exactly one sibling.
100  *
101  * Any subsequent operation on its result will expect exactly one element found (unless
102  * [SemanticsNodeInteraction.assertDoesNotExist] is used) and will throw [AssertionError] if none or
103  * more than one element is found.
104  */
SemanticsNodeInteractionnull105 fun SemanticsNodeInteraction.onSibling(): SemanticsNodeInteraction {
106     return SemanticsNodeInteraction(
107         testContext,
108         useUnmergedTree,
109         selector.addSelectionFromSingleNode("sibling") { it.siblings }
110     )
111 }
112 
113 /**
114  * Returns all the ancestors of this node.
115  *
116  * Example: For the following tree
117  *
118  * ```
119  * |-A
120  *   |-B
121  *     |-C <- this node
122  * Returns B, A
123  * ```
124  */
SemanticsNodeInteractionnull125 fun SemanticsNodeInteraction.onAncestors(): SemanticsNodeInteractionCollection {
126     return SemanticsNodeInteractionCollection(
127         testContext,
128         useUnmergedTree,
129         selector.addSelectionFromSingleNode("ancestors") { it.ancestors.toList() }
130     )
131 }
132 
133 /**
134  * Returns the first node in this collection.
135  *
136  * Any subsequent operation on its result will expect exactly one element found (unless
137  * [SemanticsNodeInteraction.assertDoesNotExist] is used) and will throw [AssertionError] if no
138  * element is found.
139  */
onFirstnull140 fun SemanticsNodeInteractionCollection.onFirst(): SemanticsNodeInteraction {
141     return get(0)
142 }
143 
144 /**
145  * Returns the last node in this collection.
146  *
147  * Any subsequent operation on its result will expect exactly one element found (unless
148  * [SemanticsNodeInteraction.assertDoesNotExist] is used) and will throw [AssertionError] if no
149  * element is found.
150  */
SemanticsNodeInteractionCollectionnull151 fun SemanticsNodeInteractionCollection.onLast(): SemanticsNodeInteraction {
152     return SemanticsNodeInteraction(testContext, useUnmergedTree, selector.addLastNodeSelector())
153 }
154 
155 /**
156  * Returns all the nodes matching the given [matcher].
157  *
158  * @param matcher Matcher to use for the filtering.
159  */
filternull160 fun SemanticsNodeInteractionCollection.filter(
161     matcher: SemanticsMatcher
162 ): SemanticsNodeInteractionCollection {
163     return SemanticsNodeInteractionCollection(
164         testContext,
165         useUnmergedTree,
166         selector.addSelectorViaMatcher("filter", matcher)
167     )
168 }
169 
170 /**
171  * Expects to return exactly one node matching the given [matcher].
172  *
173  * Any subsequent operation on its result will expect exactly one element found (unless
174  * [SemanticsNodeInteraction.assertDoesNotExist] is used) and will throw [AssertionError] if no
175  * element is found.
176  *
177  * @param matcher Matcher to use for the filtering.
178  */
filterToOnenull179 fun SemanticsNodeInteractionCollection.filterToOne(
180     matcher: SemanticsMatcher
181 ): SemanticsNodeInteraction {
182     return SemanticsNodeInteraction(
183         testContext,
184         useUnmergedTree,
185         selector.addSelectorViaMatcher("filterToOne", matcher)
186     )
187 }
188