Browse Source

More UI testing setup and API mocks

master
Shadowfacts 3 weeks ago
parent
commit
18c3c3c434
Signed by: Shadowfacts <me@shadowfacts.net> GPG Key ID: 94A5AB95422746E5

+ 32
- 0
Tusker.xcodeproj/project.pbxproj View File

@@ -183,6 +183,10 @@
183 183
 		D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC882321F79B00FD64D5 /* AccountTableViewCell.swift */; };
184 184
 		D6A3BC8B2321F79B00FD64D5 /* AccountTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A3BC892321F79B00FD64D5 /* AccountTableViewCell.xib */; };
185 185
 		D6A3BC8F2321FFB900FD64D5 /* StatusActionAccountListTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC8D2321FFB900FD64D5 /* StatusActionAccountListTableViewController.swift */; };
186
+		D6A5BB2B23BAEF61003BF21D /* APIMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2A23BAEF61003BF21D /* APIMocks.swift */; };
187
+		D6A5BB2D23BBA9C4003BF21D /* MyProfileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */; };
188
+		D6A5BB2F23BBAC97003BF21D /* DelegatingResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */; };
189
+		D6A5BB3123BBAD87003BF21D /* JSONResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB3023BBAD87003BF21D /* JSONResponse.swift */; };
186 190
 		D6A5FAF1217B7E05003DB2D9 /* ComposeViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A5FAF0217B7E05003DB2D9 /* ComposeViewController.xib */; };
187 191
 		D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */; };
188 192
 		D6AEBB412321642700E5038B /* SendMesasgeActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */; };
@@ -465,6 +469,10 @@
465 469
 		D6A3BC882321F79B00FD64D5 /* AccountTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountTableViewCell.swift; sourceTree = "<group>"; };
466 470
 		D6A3BC892321F79B00FD64D5 /* AccountTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountTableViewCell.xib; sourceTree = "<group>"; };
467 471
 		D6A3BC8D2321FFB900FD64D5 /* StatusActionAccountListTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusActionAccountListTableViewController.swift; sourceTree = "<group>"; };
472
+		D6A5BB2A23BAEF61003BF21D /* APIMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIMocks.swift; sourceTree = "<group>"; };
473
+		D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyProfileTests.swift; sourceTree = "<group>"; };
474
+		D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelegatingResponse.swift; sourceTree = "<group>"; };
475
+		D6A5BB3023BBAD87003BF21D /* JSONResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONResponse.swift; sourceTree = "<group>"; };
468 476
 		D6A5FAF0217B7E05003DB2D9 /* ComposeViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ComposeViewController.xib; sourceTree = "<group>"; };
469 477
 		D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIActivity+Types.swift"; sourceTree = "<group>"; };
470 478
 		D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMesasgeActivity.swift; sourceTree = "<group>"; };
@@ -1127,6 +1135,23 @@
1127 1135
 			path = "Status Action Account List";
1128 1136
 			sourceTree = "<group>";
1129 1137
 		};
1138
+		D6A5BB2623BAC88E003BF21D /* Preferences */ = {
1139
+			isa = PBXGroup;
1140
+			children = (
1141
+			);
1142
+			path = Preferences;
1143
+			sourceTree = "<group>";
1144
+		};
1145
+		D6A5BB2923BAEF51003BF21D /* API Mocks */ = {
1146
+			isa = PBXGroup;
1147
+			children = (
1148
+				D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */,
1149
+				D6A5BB3023BBAD87003BF21D /* JSONResponse.swift */,
1150
+				D6A5BB2A23BAEF61003BF21D /* APIMocks.swift */,
1151
+			);
1152
+			path = "API Mocks";
1153
+			sourceTree = "<group>";
1154
+		};
1130 1155
 		D6AEBB3F2321640F00E5038B /* Activities */ = {
1131 1156
 			isa = PBXGroup;
1132 1157
 			children = (
@@ -1274,8 +1299,11 @@
1274 1299
 		D6D4DDEE212518A200E1C4BB /* TuskerUITests */ = {
1275 1300
 			isa = PBXGroup;
1276 1301
 			children = (
1302
+				D6A5BB2923BAEF51003BF21D /* API Mocks */,
1277 1303
 				D6D4DDEF212518A200E1C4BB /* TuskerUITests.swift */,
1278 1304
 				D65F613323AEAB6600F3CFD3 /* OnboardingTests.swift */,
1305
+				D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */,
1306
+				D6A5BB2623BAC88E003BF21D /* Preferences */,
1279 1307
 				D6D4DDF1212518A200E1C4BB /* Info.plist */,
1280 1308
 			);
1281 1309
 			path = TuskerUITests;
@@ -1811,7 +1839,11 @@
1811 1839
 			isa = PBXSourcesBuildPhase;
1812 1840
 			buildActionMask = 2147483647;
1813 1841
 			files = (
1842
+				D6A5BB3123BBAD87003BF21D /* JSONResponse.swift in Sources */,
1814 1843
 				D65F613423AEAB6600F3CFD3 /* OnboardingTests.swift in Sources */,
1844
+				D6A5BB2F23BBAC97003BF21D /* DelegatingResponse.swift in Sources */,
1845
+				D6A5BB2D23BBA9C4003BF21D /* MyProfileTests.swift in Sources */,
1846
+				D6A5BB2B23BAEF61003BF21D /* APIMocks.swift in Sources */,
1815 1847
 				D6D4DDF0212518A200E1C4BB /* TuskerUITests.swift in Sources */,
1816 1848
 			);
1817 1849
 			runOnlyForDeploymentPostprocessing = 0;

+ 7
- 0
Tusker/LocalData.swift View File

@@ -17,6 +17,13 @@ class LocalData {
17 17
     private init() {
18 18
         if ProcessInfo.processInfo.environment.keys.contains("UI_TESTING") {
19 19
             defaults = UserDefaults(suiteName: "\(Bundle.main.bundleIdentifier!).uitesting")!
20
+            if ProcessInfo.processInfo.environment.keys.contains("UI_TESTING_LOGIN") {
21
+                defaults.set(true, forKey: onboardingCompleteKey)
22
+                defaults.set(URL(string: "http://localhost:8080")!, forKey: instanceURLKey)
23
+                defaults.set("client_id", forKey: clientIDKey)
24
+                defaults.set("client_secret", forKey: clientSecretKey)
25
+                defaults.set("access_token", forKey: accessTokenKey)
26
+            }
20 27
         } else {
21 28
             defaults = UserDefaults()
22 29
         }

+ 1
- 2
Tusker/Screens/Profile/MyProfileTableViewController.swift View File

@@ -22,9 +22,8 @@ class MyProfileTableViewController: ProfileTableViewController {
22 22
             self.accountID = account.id
23 23
             
24 24
             ImageCache.avatars.get(account.avatar, completion: { (data) in
25
-                guard let data = data else { return }
25
+                guard let data = data, let image = UIImage(data: data) else { return }
26 26
                 DispatchQueue.main.async {
27
-                    let image = UIImage(data: data)!
28 27
                     let size = CGSize(width: 30, height: 30)
29 28
                     let tabBarImage = UIGraphicsImageRenderer(size: size).image { (_) in
30 29
                         image.draw(in: CGRect(origin: .zero, size: size))

+ 77
- 0
TuskerUITests/API Mocks/APIMocks.swift View File

@@ -0,0 +1,77 @@
1
+//
2
+//  APIMocks.swift
3
+//  TuskerUITests
4
+//
5
+//  Created by Shadowfacts on 12/30/19.
6
+//  Copyright © 2019 Shadowfacts. All rights reserved.
7
+//
8
+
9
+import Foundation
10
+import Ambassador
11
+
12
+fileprivate let notFound = ["error": "Record not found"]
13
+
14
+extension Router {
15
+    func allRoutes() {
16
+        instanceRoutes()
17
+        accountRoutes()
18
+        timelineRoutes()
19
+        notificationRoutes()
20
+    }
21
+    
22
+    func instanceRoutes() {
23
+        self["/api/v1/instance"] = JSONResponse(handler: { (_) in
24
+            return [
25
+                "description": "An instance description",
26
+                "max_toot_chars": 500,
27
+                "thumbnail": "http://localhost:8080/thumbnail.png",
28
+                "title": "Localhost",
29
+                "uri": "http://localhost:8080",
30
+                "version": "2.7.2",
31
+                "urls": [:]
32
+            ]
33
+        })
34
+    }
35
+    
36
+    func accountRoutes() {
37
+        let selfAccount: [String: Any] = [
38
+            "id": "1",
39
+            "username": "admin",
40
+            "acct": "admin",
41
+            "display_name": "Admin Account",
42
+            "locked": false,
43
+            "created_at": "2019-12-31T11:13:42.0Z",
44
+            "followers_count": 0,
45
+            "following_count": 0,
46
+            "statuses_count": 0,
47
+            "note": "My profile description.",
48
+            "url": "http://localhost:8080/users/admin",
49
+            "avatar": "http://localhost:8080/avatar/admin.jpg",
50
+            "avatar_static": "http://localhost:8080/avatar/admin.jpg",
51
+            "header": "http://localhost:8080/header/admin.jpg",
52
+            "header_static": "http://localhost:8080/header/admin.jpg",
53
+            "emojis": []
54
+        ]
55
+        self["/api/v1/accounts/verify_credentials"] = JSONResponse(result: selfAccount)
56
+        self["/api/v1/accounts/\\d+/statuses"] = JSONResponse(result: [])
57
+        self["/api/v1/accounts/(\\d+)"] = DelegatingResponse { (env) in
58
+            let captures = env["ambassador.router_captures"] as! [String]
59
+            if captures[0] == "1" {
60
+                return JSONResponse(result: selfAccount)
61
+            } else {
62
+                return JSONResponse(statusCode: 404, statusMessage: "Not Found", result: notFound)
63
+            }
64
+        }
65
+    }
66
+    
67
+    func timelineRoutes() {
68
+        let emptyTimeline: [Any] = []
69
+        self["/api/v1/timelines/home"] = JSONResponse(result: emptyTimeline)
70
+        self["/api/v1/timelines/public"] = JSONResponse(result: emptyTimeline)
71
+    }
72
+    
73
+    func notificationRoutes() {
74
+        let emptyTimeline: [Any] = []
75
+        self["/api/v1/notifications"] = JSONResponse(result: emptyTimeline)
76
+    }
77
+}

+ 18
- 0
TuskerUITests/API Mocks/DelegatingResponse.swift View File

@@ -0,0 +1,18 @@
1
+//
2
+//  DelegatingResponse.swift
3
+//  TuskerUITests
4
+//
5
+//  Created by Shadowfacts on 12/31/19.
6
+//  Copyright © 2019 Shadowfacts. All rights reserved.
7
+//
8
+
9
+import Foundation
10
+import Ambassador
11
+
12
+struct DelegatingResponse: WebApp {
13
+    let handler: (_ environ: [String: Any]) -> WebApp
14
+    
15
+    func app(_ environ: [String : Any], startResponse: @escaping ((String, [(String, String)]) -> Void), sendBody: @escaping ((Data) -> Void)) {
16
+        handler(environ).app(environ, startResponse: startResponse, sendBody: sendBody)
17
+    }
18
+}

+ 18
- 0
TuskerUITests/API Mocks/JSONResponse.swift View File

@@ -0,0 +1,18 @@
1
+//
2
+//  JSONResponse.swift
3
+//  TuskerUITests
4
+//
5
+//  Created by Shadowfacts on 12/31/19.
6
+//  Copyright © 2019 Shadowfacts. All rights reserved.
7
+//
8
+
9
+import Foundation
10
+import Ambassador
11
+
12
+extension JSONResponse {
13
+    init(statusCode: Int = 200, statusMessage: String = "OK", result: Any) {
14
+        self.init(statusCode: statusCode, statusMessage: statusMessage, handler: { (_) in
15
+            return result
16
+        })
17
+    }
18
+}

+ 26
- 0
TuskerUITests/MyProfileTests.swift View File

@@ -0,0 +1,26 @@
1
+//
2
+//  MyProfileTests.swift
3
+//  TuskerUITests
4
+//
5
+//  Created by Shadowfacts on 12/31/19.
6
+//  Copyright © 2019 Shadowfacts. All rights reserved.
7
+//
8
+
9
+import XCTest
10
+
11
+class MyProfileTests: TuskerUITests {
12
+
13
+    override func setUp() {
14
+        super.setUp()
15
+        
16
+        router.allRoutes()
17
+
18
+        app.launchEnvironment["UI_TESTING_LOGIN"] = "true"
19
+        app.launch()
20
+    }
21
+
22
+    func testExample() {
23
+        sleep(10000000)
24
+    }
25
+
26
+}

+ 6
- 12
TuskerUITests/OnboardingTests.swift View File

@@ -14,20 +14,14 @@ class OnboardingTests: TuskerUITests {
14 14
     override func setUp() {
15 15
         super.setUp()
16 16
         
17
-        router["/api/v1/instance"] = JSONResponse(handler: { (_) in
18
-            return [
19
-                "description": "An instance description",
20
-                "max_toot_chars": 500,
21
-                "thumbnail": "http://localhost:8080/thumbnail.png",
22
-                "title": "Localhost",
23
-                "uri": "http://localhost:8080",
24
-                "version": "2.7.2",
25
-                "urls": [:]
26
-            ]
27
-        })
17
+        router.instanceRoutes()
18
+        
19
+        app.launch()
28 20
     }
21
+
22
+    // can't test logging in because there's no way of interacting with the safari VC that's used in the OAuth flow
29 23
     
30
-    func testExample() {
24
+    func testCustomInstanceAppears() {
31 25
         let searchField = app.searchFields.element
32 26
         searchField.tap()
33 27
         searchField.typeText("http://localhost:8080")

+ 0
- 1
TuskerUITests/TuskerUITests.swift View File

@@ -45,7 +45,6 @@ class TuskerUITests: XCTestCase {
45 45
         continueAfterFailure = false
46 46
         app = XCUIApplication()
47 47
         app.launchEnvironment["UI_TESTING"] = "true"
48
-        app.launch()
49 48
     }
50 49
 
51 50
     override func tearDown() {

Loading…
Cancel
Save