@@ -27,32 +27,16 @@ class AtbIntegrationTests: XCTestCase {
2727 // 5 should be good enough. 10 for some padding
2828 static let defaultTimeout : Double = 10
2929
30- static let initialAtb = " v100-1 "
31- static let searchRetentionAtb = " v102-7 "
32- static let appRetentionAtb = " v102-6 "
33-
3430 static let devmode = " test "
3531 static let atbParam = " atb "
3632 static let setAtbParam = " set_atb "
3733 static let activityType = " at "
3834 }
39-
40- enum StatisticsRequestType {
41- case atb
42- case exti
43- }
44-
45- struct StatisticsRequest {
46- let type : StatisticsRequestType
47- let httpRequest : HttpRequest
48- }
49-
35+
5036 let app = XCUIApplication ( )
5137 let server = HttpServer ( )
52- var statisticsRequests = [ StatisticsRequest] ( )
53- var searchRequests = [ HttpRequest] ( )
54- var atbToSet = Constants . initialAtb
55-
38+ var requests = 0
39+
5640 override func setUp( ) {
5741 super. setUp ( )
5842 continueAfterFailure = false
@@ -73,134 +57,39 @@ class AtbIntegrationTests: XCTestCase {
7357 } catch {
7458 fatalError ( " Could not start server " )
7559 }
76-
60+
61+ requests = 0
7762 app. launch ( )
7863 }
7964
8065 override func tearDown( ) {
8166 super. tearDown ( )
8267 server. stop ( )
83- statisticsRequests. removeAll ( )
84- searchRequests. removeAll ( )
85- }
86-
87- func test( ) throws {
88- try assertWhenAppIsInstalledAndLaunchedThenExtiIsCalledAndInitialAtbIsRetrieved ( )
89- clearRequests ( )
90-
91- assertWhenAppLaunchedAgainThenAppAtbIsUpdated ( )
92- clearRequests ( )
93-
94- assertWhenUserSearchesWithOldAtbThenAtbIsUpdated ( )
95- clearRequests ( )
96-
97- try assertWhenSearchPerformedThenAtbIsAddedToRequest ( )
98- clearRequests ( )
99-
100- assertWhenUserEntersSearchDirectlyThenAtbIsAddedToRequest ( )
101- clearRequests ( )
10268 }
103-
104- func assertWhenAppIsInstalledAndLaunchedThenExtiIsCalledAndInitialAtbIsRetrieved( ) throws {
105-
106- waitFor ( searchRequestsCount: 0 , statisticRequestsCount: 3 , timeout: 30 )
107-
108- assertSearchRequestCount ( count: 0 )
109- assertStatisticsRequestCount ( count: 3 )
110- assertAtb ( expectedAtb: nil , expectedSetAtb: nil , expectedType: nil )
111- assertExti ( )
112- assertAtb ( expectedAtb: Constants . initialAtb, expectedSetAtb: Constants . initialAtb, expectedType: " app_use " )
113- }
114-
115- func assertWhenSearchPerformedThenAtbIsAddedToRequest( ) throws {
116- search ( forText: " oranges " )
11769
118- assertSearchRequestCount ( count : 1 )
119- assertSearch ( text : " oranges " , atb : Constants . initialAtb )
70+ func testAppUsageCausesAtbRequests ( ) {
71+ waitForRequests ( )
12072 }
12173
122- func assertWhenUserSearchesWithOldAtbThenAtbIsUpdated( ) {
123- atbToSet = Constants . searchRetentionAtb
124-
74+ func testSearchCausesAtbRequests( ) {
75+ requests = 0 // Reset to ensure test launch of the app doesn't register as a false positive
12576 search ( forText: " lemons " )
126- search ( forText: " pears " )
127-
128- waitFor ( searchRequestsCount: 2 , statisticRequestsCount: 0 , timeout: 30 )
129- assertSearchRequestCount ( count: 2 )
130- assertSearch ( text: " lemons " , atb: Constants . initialAtb)
131- assertSearch ( text: " pears " , atb: Constants . initialAtb)
132-
133- waitFor ( searchRequestsCount: 0 , statisticRequestsCount: 2 , timeout: 30 )
134- assertStatisticsRequestCount ( count: 2 )
135- assertAtb ( expectedAtb: Constants . initialAtb, expectedSetAtb: Constants . initialAtb, expectedType: nil )
136- assertAtb ( expectedAtb: Constants . initialAtb, expectedSetAtb: Constants . searchRetentionAtb, expectedType: nil )
77+ waitForRequests ( )
13778 }
138-
139- func assertWhenUserEntersSearchDirectlyThenAtbIsAddedToRequest( ) {
140- search ( forText: " http://localhost:8080?q=beagles " )
141-
142- assertSearchRequestCount ( count: 1 )
143- assertSearch ( text: " beagles " , atb: Constants . initialAtb)
144- }
145-
146- func assertWhenAppLaunchedAgainThenAppAtbIsUpdated( ) {
147- atbToSet = Constants . appRetentionAtb
148-
149- backgroundRelaunch ( ) // this launch gets new atb
150- backgroundRelaunch ( ) // this launch sends it
15179
152- waitFor ( searchRequestsCount: 0 , statisticRequestsCount: 2 , timeout: 30 )
153- assertSearchRequestCount ( count: 0 )
154- assertStatisticsRequestCount ( count: 2 )
155- assertAtb ( expectedAtb: Constants . initialAtb, expectedSetAtb: Constants . initialAtb, expectedType: " app_use " )
156- assertAtb ( expectedAtb: Constants . initialAtb, expectedSetAtb: Constants . appRetentionAtb, expectedType: " app_use " )
80+ func testRelaunchCausesAtbRequests( ) {
81+ requests = 0 // Reset to ensure test launch of the app doesn't register as a false positive
82+ backgroundRelaunch ( )
83+ waitForRequests ( )
15784 }
158-
159- func clearRequests( ) {
160- statisticsRequests. removeAll ( )
161- searchRequests. removeAll ( )
162- }
163-
85+
16486 func backgroundRelaunch( ) {
16587 XCUIDevice . shared. press ( . home)
16688 app. activate ( )
16789 if !app. searchFields [ " searchEntry " ] . waitForExistence ( timeout: Constants . defaultTimeout) {
16890 fatalError ( " Can not find search field. Has the app launched? " )
16991 }
17092 }
171-
172- func assertStatisticsRequestCount( count: Int , file: StaticString = #file, line: UInt = #line) {
173- XCTAssertEqual ( count, statisticsRequests. count, file: file, line: line)
174- }
175-
176- func assertExti( file: StaticString = #file, line: UInt = #line) {
177- let request = statisticsRequests. removeFirst ( )
178- XCTAssertEqual ( StatisticsRequestType . exti, request. type, file: file, line: line)
179- XCTAssertEqual ( Constants . initialAtb, request. httpRequest. queryParam ( Constants . atbParam) , file: file, line: line)
180- }
181-
182- func assertAtb( expectedAtb: String ? = nil , expectedSetAtb: String ? = nil , expectedType: String ? = nil ,
183- file: StaticString = #file, line: UInt = #line) {
184- let request = statisticsRequests. removeFirst ( )
185- XCTAssertEqual ( StatisticsRequestType . atb, request. type, file: file, line: line)
186-
187- let httpRequest = request. httpRequest
188- XCTAssertEqual ( expectedAtb, httpRequest. queryParam ( Constants . atbParam) , file: file, line: line)
189- XCTAssertEqual ( expectedSetAtb, httpRequest. queryParam ( Constants . setAtbParam) , file: file, line: line)
190- XCTAssertEqual ( expectedType, httpRequest. queryParam ( Constants . activityType) , file: file, line: line)
191- XCTAssertEqual ( " 1 " , httpRequest. queryParam ( Constants . devmode) , file: file, line: line)
192- }
193-
194- func assertSearchRequestCount( count: Int , file: StaticString = #file, line: UInt = #line) {
195- XCTAssertEqual ( count, searchRequests. count, file: file, line: line)
196- }
197-
198- func assertSearch( text: String , atb: String , file: StaticString = #file, line: UInt = #line) {
199- let request = searchRequests. removeFirst ( )
200-
201- XCTAssertEqual ( text, request. queryParam ( " q " ) , file: file, line: line)
202- XCTAssertEqual ( atb, request. queryParam ( Constants . atbParam) , file: file, line: line)
203- }
20493
20594 private func search( forText text: String ) {
20695
@@ -211,40 +100,32 @@ class AtbIntegrationTests: XCTestCase {
211100 Snapshot . waitForLoadingIndicatorToDisappear ( within: Constants . defaultTimeout)
212101
213102 }
214-
215- private func waitFor( searchRequestsCount: Int , statisticRequestsCount: Int , timeout: TimeInterval ) {
103+
104+ /// We don't care which requests, as long as it's one of the expected endpoints. The actual logic is tested in
105+ /// the StatisticsLoader tests
106+ private func waitForRequests( timeout: TimeInterval = Constants . defaultTimeout) {
216107 let start = Date ( )
217-
218108 while ( start. timeIntervalSinceNow * - 1 ) < timeout {
219109 _ = app. buttons [ " _wait_ " ] . waitForExistence ( timeout: 1 )
220- if searchRequests . count >= searchRequestsCount && statisticsRequests . count >= statisticRequestsCount {
110+ if requests > 0 {
221111 return
222112 }
223113 }
224-
225- if searchRequests. count != searchRequests. count || statisticRequestsCount != statisticsRequests. count {
226- XCTFail ( " \( searchRequests. count) vs \( searchRequestsCount) , \( statisticRequestsCount) vs \( statisticsRequests. count) " )
227- }
114+
115+ XCTFail ( " No requests detected " )
228116 }
229117
230118 private func addRequestHandlers( ) {
231-
232- server [ " / " ] = {
233- self . searchRequests . append ( $0 )
119+
120+ server [ " /exti/ " ] = { _ in
121+ self . requests += 1
234122 return . accepted
235123 }
236124
237- server [ " /exti/ " ] = {
238- self . statisticsRequests . append ( StatisticsRequest ( type : StatisticsRequestType . exti , httpRequest : $0 ) )
125+ server [ " /atb.js " ] = { _ in
126+ self . requests += 1
239127 return . accepted
240128 }
241-
242- server [ " /atb.js " ] = {
243- self . statisticsRequests. append ( StatisticsRequest ( type: StatisticsRequestType . atb, httpRequest: $0) )
244- return . ok( . json( [
245- " version " : self . atbToSet
246- ] as AnyObject ) )
247- }
248129 }
249130
250131 private func waitForButtonThenTap( _ named: String ) {
@@ -258,52 +139,9 @@ class AtbIntegrationTests: XCTestCase {
258139}
259140
260141fileprivate extension HttpRequest {
261-
142+
262143 func queryParam( _ named: String ) -> String ? {
263144 return queryParams. first ( where: { $0. 0 == named } ) ? . 1
264145 }
265-
266- }
267146
268- // from: https://stackoverflow.com/a/36168101/73479
269- class Springboard {
270-
271- static let springboard = XCUIApplication ( bundleIdentifier: " com.apple.springboard " )
272-
273- /**
274- Terminate and delete the app via springboard
275- */
276- class func deleteMyApp( ) {
277- XCUIApplication ( ) . terminate ( )
278-
279- // Resolve the query for the springboard rather than launching it
280- springboard. activate ( )
281-
282- // Force delete the app from the springboard
283- let icon = springboard. icons [ " DuckDuckGo " ]
284- if icon. exists {
285- let iconFrame = icon. frame
286- let springboardFrame = springboard. frame
287- icon. press ( forDuration: 1.3 )
288-
289- let rearrangeButton = springboard. buttons [ " Rearrange Apps " ]
290- if rearrangeButton. waitForExistence ( timeout: 2 ) {
291- rearrangeButton. tap ( )
292-
293- sleep ( 1 )
294- // Tap the little "X" button at approximately where it is. The X is not exposed directly
295-
296- springboard. coordinate ( withNormalizedOffset: CGVector ( dx: ( iconFrame. minX + 3 ) / springboardFrame. maxX,
297- dy: ( iconFrame. minY + 3 ) / springboardFrame. maxY) ) . tap ( )
298- } else {
299- // Buttons have been rearranged for iOS 13.1+ version
300- let deleteButton = springboard. buttons [ " Delete App " ]
301- deleteButton. tap ( )
302- }
303-
304- let deleteButton = springboard. alerts. buttons [ " Delete " ]
305- _ = deleteButton. waitForExistence ( timeout: AtbIntegrationTests . Constants. defaultTimeout)
306- deleteButton. tap ( )
307- }
308- }
309147}
0 commit comments