Tests/UI/ListlessiOSUITests.swift (5455B)
1import XCTest
2
3final class ListlessiOSUITests: XCTestCase {
4 var app: XCUIApplication!
5
6 override func setUpWithError() throws {
7 continueAfterFailure = false
8 app = XCUIApplication()
9 app.launchArguments = ["UI_TESTING"]
10 app.launch()
11 }
12
13 override func tearDownWithError() throws {
14 app.terminate()
15 }
16
17
18
19
20 var emptyStateLabel: XCUIElement {
21 app.staticTexts["Pull down to create"]
22 }
23
24
25 var itemListScrollView: XCUIElement {
26 app.scrollViews["item-list-scrollview"]
27 }
28
29
30
31 var draftTextField: XCUIElement {
32 app.textViews.matching(identifier: "draft-row-append").firstMatch
33 }
34
35
36 func itemText(_ title: String) -> XCUIElement {
37 app.staticTexts.matching(
38 NSPredicate(format: "identifier BEGINSWITH 'item-text-' AND label == %@", title)
39 ).firstMatch
40 }
41
42
43
44 func createItem(_ title: String) {
45 let textView = draftTextField
46 if !textView.exists {
47 itemListScrollView.tap()
48 if !textView.waitForExistence(timeout: 2) {
49 XCTFail("Draft text view should appear after tapping empty space")
50 return
51 }
52 }
53 textView.tap()
54 textView.typeText(title + "\n")
55 }
56
57
58 func itemCheckbox(at index: Int) -> XCUIElement {
59 app.buttons.matching(identifier: "item-checkbox").element(boundBy: index)
60 }
61
62
63
64
65 func exitEditingMode() {
66 let draft = draftTextField
67 if draft.exists {
68 draft.typeText("\n")
69 }
70
71 itemListScrollView.tap()
72 }
73
74
75
76 func testLaunchShowsEmptyState() {
77 XCTAssertTrue(
78 emptyStateLabel.waitForExistence(timeout: 2),
79 "Empty state label should be visible on launch"
80 )
81 }
82
83 func testEmptyStateDisappearsAfterCreatingItem() {
84 createItem("First item")
85 exitEditingMode()
86 XCTAssertFalse(emptyStateLabel.exists, "Empty state should disappear after creating a item")
87 }
88
89
90
91 func testCreateItemViaTap() {
92 createItem("Buy groceries")
93 exitEditingMode()
94 XCTAssertTrue(
95 itemText("Buy groceries").waitForExistence(timeout: 2),
96 "Item should appear with the typed title"
97 )
98 }
99
100 func testReturnChainsNewItem() {
101 createItem("First item")
102 XCTAssertTrue(
103 draftTextField.waitForExistence(timeout: 2),
104 "New draft text view should appear after Return"
105 )
106 }
107
108 func testCreateMultipleItems() {
109 createItem("Alpha")
110 createItem("Bravo")
111 createItem("Charlie")
112 exitEditingMode()
113
114 XCTAssertTrue(itemText("Alpha").waitForExistence(timeout: 2))
115 XCTAssertTrue(itemText("Bravo").exists)
116 XCTAssertTrue(itemText("Charlie").exists)
117 }
118
119 func testEmptyItemDeletedOnCommit() {
120 itemListScrollView.tap()
121 XCTAssertTrue(draftTextField.waitForExistence(timeout: 2))
122 draftTextField.typeText("\n")
123 XCTAssertTrue(
124 emptyStateLabel.waitForExistence(timeout: 2),
125 "Empty state should reappear when empty item is discarded"
126 )
127 }
128
129
130
131 func testCompleteItemViaCheckbox() {
132 createItem("Finish report")
133 exitEditingMode()
134
135 let checkbox = itemCheckbox(at: 0)
136 XCTAssertTrue(checkbox.waitForExistence(timeout: 2))
137 XCTAssertEqual(checkbox.value as? String, "circle")
138 checkbox.tap()
139
140 let completed = app.buttons.matching(
141 NSPredicate(format: "identifier == 'item-checkbox' AND value == 'checkmark.circle.fill'")
142 ).firstMatch
143 XCTAssertTrue(
144 completed.waitForExistence(timeout: 3),
145 "Checkbox should show checkmark after tapping"
146 )
147 }
148
149 func testUncompleteItem() {
150 createItem("Finish report")
151 exitEditingMode()
152 usleep(500_000)
153
154 itemCheckbox(at: 0).tap()
155
156 let completed = app.buttons.matching(
157 NSPredicate(format: "identifier == 'item-checkbox' AND value == 'checkmark.circle.fill'")
158 ).firstMatch
159 XCTAssertTrue(completed.waitForExistence(timeout: 3))
160 completed.tap()
161
162 let uncompleted = app.buttons.matching(
163 NSPredicate(format: "identifier == 'item-checkbox' AND value == 'circle'")
164 ).firstMatch
165 XCTAssertTrue(
166 uncompleted.waitForExistence(timeout: 3),
167 "Checkbox should revert to circle after uncompleting"
168 )
169 }
170}