or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

accessibility-testing.mdcoordinate-utilities.mdindex.mdios-uikit-testing.mdtouch-simulation.md

accessibility-testing.mddocs/

0

# Accessibility Testing

1

2

iOS accessibility system integration with VoiceOver support, accessibility tree validation, and comprehensive testing utilities for ensuring app accessibility compliance.

3

4

## Capabilities

5

6

### Accessibility Tree Construction

7

8

Functions for building and analyzing the accessibility tree from the UI hierarchy.

9

10

```kotlin { .api }

11

/**

12

* Constructs accessibility tree from current UI hierarchy

13

* @return Root AccessibilityTestNode representing the complete accessibility tree

14

*/

15

internal fun UIKitInstrumentedTest.getAccessibilityTree(): AccessibilityTestNode

16

17

/**

18

* Normalizes accessibility tree by removing non-essential elements

19

* @return Normalized accessibility tree with only meaningful nodes, or null if empty

20

*/

21

internal fun AccessibilityTestNode.normalized(): AccessibilityTestNode?

22

```

23

24

**Usage Example:**

25

26

```kotlin

27

runUIKitInstrumentedTest {

28

setContentWithAccessibilityEnabled {

29

Column {

30

Text("Welcome")

31

Button(onClick = {}) {

32

Text("Submit")

33

}

34

}

35

}

36

37

val tree = getAccessibilityTree()

38

val normalizedTree = tree.normalized()

39

40

println("Accessibility tree: $normalizedTree")

41

}

42

```

43

44

### Accessibility Node Search

45

46

Functions for finding specific nodes within the accessibility tree based on various criteria.

47

48

```kotlin { .api }

49

/**

50

* Finds accessibility node by identifier tag

51

* @param tag - Identifier tag to search for

52

* @return AccessibilityTestNode if found

53

* @throws AssertionError if node with tag is not found

54

*/

55

internal fun UIKitInstrumentedTest.findNodeWithTag(tag: String): AccessibilityTestNode

56

57

/**

58

* Finds accessibility node by accessibility label

59

* @param label - Accessibility label to search for

60

* @return AccessibilityTestNode if found

61

* @throws AssertionError if node with label is not found

62

*/

63

internal fun UIKitInstrumentedTest.findNodeWithLabel(label: String): AccessibilityTestNode

64

65

/**

66

* Finds first accessibility element in the tree

67

* @return AccessibilityTestNode of first accessible element

68

* @throws AssertionError if no accessibility element is found

69

*/

70

internal fun UIKitInstrumentedTest.firstAccessibleNode(): AccessibilityTestNode

71

72

/**

73

* Finds accessibility node matching a condition

74

* @param isValid - Predicate function to test nodes

75

* @return AccessibilityTestNode if found, null otherwise

76

*/

77

internal fun UIKitInstrumentedTest.findNodeOrNull(

78

isValid: (AccessibilityTestNode) -> Boolean

79

): AccessibilityTestNode?

80

```

81

82

**Usage Examples:**

83

84

```kotlin

85

runUIKitInstrumentedTest {

86

setContentWithAccessibilityEnabled {

87

Button(

88

onClick = {},

89

modifier = Modifier.testTag("submit_button")

90

) {

91

Text("Submit Form")

92

}

93

}

94

95

// Find by test tag

96

val submitButton = findNodeWithTag("submit_button")

97

requireNotNull(submitButton) { "Submit button not found" }

98

99

// Find by accessibility label

100

val labeledNode = findNodeWithLabel("Submit Form")

101

requireNotNull(labeledNode) { "Node with label not found" }

102

}

103

```

104

105

### Accessibility Tree Validation

106

107

Comprehensive validation functions for asserting accessibility tree structure and content.

108

109

```kotlin { .api }

110

/**

111

* Asserts that accessibility tree matches expected structure

112

* @param block - DSL block defining expected tree structure

113

* @throws AssertionError if tree structure doesn't match expectations

114

*/

115

fun UIKitInstrumentedTest.assertAccessibilityTree(

116

block: AccessibilityTestNode.() -> Unit

117

)

118

```

119

120

**Usage Example:**

121

122

```kotlin

123

runUIKitInstrumentedTest {

124

setContentWithAccessibilityEnabled {

125

Column {

126

Text("Header Text")

127

Row {

128

Text("Label:")

129

Text("Value")

130

}

131

Button(onClick = {}) {

132

Text("Action")

133

}

134

}

135

}

136

137

assertAccessibilityTree {

138

node {

139

label = "Header Text"

140

children = listOf(

141

node {

142

label = "Label:"

143

children = listOf(

144

node { label = "Value" }

145

)

146

},

147

node {

148

label = "Action"

149

role = "button"

150

}

151

)

152

}

153

}

154

}

155

```

156

157

### Accessibility Node Interactions

158

159

Functions for interacting with accessibility nodes through accessibility actions.

160

161

```kotlin { .api }

162

/**

163

* Simulates tap on accessibility element using accessibility actions

164

* @throws IllegalStateException if node is not accessible or tappable

165

*/

166

fun AccessibilityTestNode.tap()

167

168

/**

169

* Simulates double tap on accessibility element

170

* @throws IllegalStateException if node doesn't support double tap action

171

*/

172

fun AccessibilityTestNode.doubleTap()

173

```

174

175

**Usage Examples:**

176

177

```kotlin

178

runUIKitInstrumentedTest {

179

setContentWithAccessibilityEnabled {

180

Button(onClick = { println("Button clicked!") }) {

181

Text("Click Me")

182

}

183

}

184

185

val button = findNodeWithLabel("Click Me")

186

requireNotNull(button) { "Button not found" }

187

188

// Tap using accessibility action

189

button.tap()

190

191

// Double tap if supported

192

button.doubleTap()

193

}

194

```

195

196

## AccessibilityTestNode Data Class

197

198

Represents a node in the accessibility tree with comprehensive accessibility information.

199

200

```kotlin { .api }

201

/**

202

* Represents a node in the accessibility tree for testing purposes

203

* @param isAccessibilityElement - Whether element is exposed to accessibility services

204

* @param identifier - Unique identifier for the accessibility element

205

* @param label - Accessibility label for VoiceOver

206

* @param value - Current value of the accessibility element

207

* @param frame - Bounding rectangle in screen coordinates

208

* @param children - Child accessibility nodes

209

* @param traits - Accessibility traits as UIAccessibilityTraits values

210

* @param element - Reference to the underlying NSObject

211

* @param parent - Parent accessibility node

212

*/

213

internal data class AccessibilityTestNode(

214

var isAccessibilityElement: Boolean? = null,

215

var identifier: String? = null,

216

var label: String? = null,

217

var value: String? = null,

218

var frame: DpRect? = null,

219

var children: List<AccessibilityTestNode>? = null,

220

var traits: List<UIAccessibilityTraits>? = null,

221

var element: NSObject? = null,

222

var parent: AccessibilityTestNode? = null

223

) {

224

fun validate(actualNode: AccessibilityTestNode?)

225

fun node(builder: AccessibilityTestNode.() -> Unit)

226

fun traits(vararg trait: UIAccessibilityTraits)

227

fun printTree(): String

228

val hasAccessibilityComponents: Boolean

229

}

230

```

231

232

**Usage Example:**

233

234

```kotlin

235

val node = AccessibilityTestNode(

236

identifier = "user_profile_button",

237

label = "User Profile",

238

hint = "Double tap to open profile",

239

role = "button",

240

traits = setOf("button", "enabled"),

241

frame = DpRect(

242

offset = DpOffset(10.dp, 10.dp),

243

size = DpSize(100.dp, 44.dp)

244

)

245

)

246

```

247

248

## Advanced Accessibility Testing Patterns

249

250

### Testing Complex UI Hierarchies

251

252

```kotlin

253

runUIKitInstrumentedTest {

254

setContentWithAccessibilityEnabled {

255

NavigationView {

256

List {

257

ForEach(items) { item ->

258

NavigationLink(destination = DetailView(item)) {

259

VStack {

260

Text(item.title)

261

Text(item.subtitle)

262

}

263

}

264

}

265

}

266

}

267

}

268

269

assertAccessibilityTree {

270

node {

271

role = "list"

272

children = items.map { item ->

273

node {

274

label = item.title

275

role = "button"

276

traits = setOf("button", "link")

277

children = listOf(

278

node { label = item.subtitle }

279

)

280

}

281

}

282

}

283

}

284

}

285

```

286

287

### Testing Dynamic Content Updates

288

289

```kotlin

290

runUIKitInstrumentedTest {

291

var counter by remember { mutableStateOf(0) }

292

293

setContentWithAccessibilityEnabled {

294

Column {

295

Text("Count: $counter")

296

Button(onClick = { counter++ }) {

297

Text("Increment")

298

}

299

}

300

}

301

302

// Test initial state

303

val initialCount = findNodeWithLabel("Count: 0")

304

requireNotNull(initialCount)

305

306

// Interact and test updated state

307

val button = findNodeWithLabel("Increment")

308

button?.tap()

309

310

waitUntil {

311

findNodeWithLabel("Count: 1") != null

312

}

313

}

314

```

315

316

## VoiceOver Integration

317

318

- **Full VoiceOver Support**: Complete integration with iOS VoiceOver screen reader

319

- **Focus Management**: Proper accessibility focus handling and navigation

320

- **Announcements**: Support for dynamic accessibility announcements

321

- **Custom Actions**: Integration with custom accessibility actions

322

- **Grouping**: Proper accessibility element grouping and container support

323

324

## Error Handling

325

326

- **AssertionError**: Thrown when accessibility tree assertions fail

327

- **IllegalStateException**: Thrown when attempting to interact with inaccessible elements

328

- **TimeoutException**: Thrown when waiting for accessibility tree changes times out

329

- **Graceful Degradation**: Fallback behaviors when accessibility services are disabled