More UI testing setup and API mocks

This commit is contained in:
Shadowfacts 2019-12-31 11:40:56 -05:00
parent b9e359ba83
commit 18c3c3c434
Signed by untrusted user: shadowfacts
GPG Key ID: 94A5AB95422746E5
9 changed files with 185 additions and 15 deletions

View File

@ -183,6 +183,10 @@
D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC882321F79B00FD64D5 /* AccountTableViewCell.swift */; }; D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC882321F79B00FD64D5 /* AccountTableViewCell.swift */; };
D6A3BC8B2321F79B00FD64D5 /* AccountTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A3BC892321F79B00FD64D5 /* AccountTableViewCell.xib */; }; D6A3BC8B2321F79B00FD64D5 /* AccountTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A3BC892321F79B00FD64D5 /* AccountTableViewCell.xib */; };
D6A3BC8F2321FFB900FD64D5 /* StatusActionAccountListTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC8D2321FFB900FD64D5 /* StatusActionAccountListTableViewController.swift */; }; D6A3BC8F2321FFB900FD64D5 /* StatusActionAccountListTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC8D2321FFB900FD64D5 /* StatusActionAccountListTableViewController.swift */; };
D6A5BB2B23BAEF61003BF21D /* APIMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2A23BAEF61003BF21D /* APIMocks.swift */; };
D6A5BB2D23BBA9C4003BF21D /* MyProfileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */; };
D6A5BB2F23BBAC97003BF21D /* DelegatingResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */; };
D6A5BB3123BBAD87003BF21D /* JSONResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A5BB3023BBAD87003BF21D /* JSONResponse.swift */; };
D6A5FAF1217B7E05003DB2D9 /* ComposeViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A5FAF0217B7E05003DB2D9 /* ComposeViewController.xib */; }; D6A5FAF1217B7E05003DB2D9 /* ComposeViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A5FAF0217B7E05003DB2D9 /* ComposeViewController.xib */; };
D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */; }; D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */; };
D6AEBB412321642700E5038B /* SendMesasgeActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */; }; D6AEBB412321642700E5038B /* SendMesasgeActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */; };
@ -465,6 +469,10 @@
D6A3BC882321F79B00FD64D5 /* AccountTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountTableViewCell.swift; sourceTree = "<group>"; }; D6A3BC882321F79B00FD64D5 /* AccountTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountTableViewCell.swift; sourceTree = "<group>"; };
D6A3BC892321F79B00FD64D5 /* AccountTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountTableViewCell.xib; sourceTree = "<group>"; }; D6A3BC892321F79B00FD64D5 /* AccountTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountTableViewCell.xib; sourceTree = "<group>"; };
D6A3BC8D2321FFB900FD64D5 /* StatusActionAccountListTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusActionAccountListTableViewController.swift; sourceTree = "<group>"; }; D6A3BC8D2321FFB900FD64D5 /* StatusActionAccountListTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusActionAccountListTableViewController.swift; sourceTree = "<group>"; };
D6A5BB2A23BAEF61003BF21D /* APIMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIMocks.swift; sourceTree = "<group>"; };
D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyProfileTests.swift; sourceTree = "<group>"; };
D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelegatingResponse.swift; sourceTree = "<group>"; };
D6A5BB3023BBAD87003BF21D /* JSONResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONResponse.swift; sourceTree = "<group>"; };
D6A5FAF0217B7E05003DB2D9 /* ComposeViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ComposeViewController.xib; sourceTree = "<group>"; }; D6A5FAF0217B7E05003DB2D9 /* ComposeViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ComposeViewController.xib; sourceTree = "<group>"; };
D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIActivity+Types.swift"; sourceTree = "<group>"; }; D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIActivity+Types.swift"; sourceTree = "<group>"; };
D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMesasgeActivity.swift; sourceTree = "<group>"; }; D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMesasgeActivity.swift; sourceTree = "<group>"; };
@ -1127,6 +1135,23 @@
path = "Status Action Account List"; path = "Status Action Account List";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D6A5BB2623BAC88E003BF21D /* Preferences */ = {
isa = PBXGroup;
children = (
);
path = Preferences;
sourceTree = "<group>";
};
D6A5BB2923BAEF51003BF21D /* API Mocks */ = {
isa = PBXGroup;
children = (
D6A5BB2E23BBAC97003BF21D /* DelegatingResponse.swift */,
D6A5BB3023BBAD87003BF21D /* JSONResponse.swift */,
D6A5BB2A23BAEF61003BF21D /* APIMocks.swift */,
);
path = "API Mocks";
sourceTree = "<group>";
};
D6AEBB3F2321640F00E5038B /* Activities */ = { D6AEBB3F2321640F00E5038B /* Activities */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1274,8 +1299,11 @@
D6D4DDEE212518A200E1C4BB /* TuskerUITests */ = { D6D4DDEE212518A200E1C4BB /* TuskerUITests */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D6A5BB2923BAEF51003BF21D /* API Mocks */,
D6D4DDEF212518A200E1C4BB /* TuskerUITests.swift */, D6D4DDEF212518A200E1C4BB /* TuskerUITests.swift */,
D65F613323AEAB6600F3CFD3 /* OnboardingTests.swift */, D65F613323AEAB6600F3CFD3 /* OnboardingTests.swift */,
D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */,
D6A5BB2623BAC88E003BF21D /* Preferences */,
D6D4DDF1212518A200E1C4BB /* Info.plist */, D6D4DDF1212518A200E1C4BB /* Info.plist */,
); );
path = TuskerUITests; path = TuskerUITests;
@ -1811,7 +1839,11 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
D6A5BB3123BBAD87003BF21D /* JSONResponse.swift in Sources */,
D65F613423AEAB6600F3CFD3 /* OnboardingTests.swift in Sources */, D65F613423AEAB6600F3CFD3 /* OnboardingTests.swift in Sources */,
D6A5BB2F23BBAC97003BF21D /* DelegatingResponse.swift in Sources */,
D6A5BB2D23BBA9C4003BF21D /* MyProfileTests.swift in Sources */,
D6A5BB2B23BAEF61003BF21D /* APIMocks.swift in Sources */,
D6D4DDF0212518A200E1C4BB /* TuskerUITests.swift in Sources */, D6D4DDF0212518A200E1C4BB /* TuskerUITests.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;

View File

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

View File

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

View File

@ -0,0 +1,77 @@
//
// APIMocks.swift
// TuskerUITests
//
// Created by Shadowfacts on 12/30/19.
// Copyright © 2019 Shadowfacts. All rights reserved.
//
import Foundation
import Ambassador
fileprivate let notFound = ["error": "Record not found"]
extension Router {
func allRoutes() {
instanceRoutes()
accountRoutes()
timelineRoutes()
notificationRoutes()
}
func instanceRoutes() {
self["/api/v1/instance"] = JSONResponse(handler: { (_) in
return [
"description": "An instance description",
"max_toot_chars": 500,
"thumbnail": "http://localhost:8080/thumbnail.png",
"title": "Localhost",
"uri": "http://localhost:8080",
"version": "2.7.2",
"urls": [:]
]
})
}
func accountRoutes() {
let selfAccount: [String: Any] = [
"id": "1",
"username": "admin",
"acct": "admin",
"display_name": "Admin Account",
"locked": false,
"created_at": "2019-12-31T11:13:42.0Z",
"followers_count": 0,
"following_count": 0,
"statuses_count": 0,
"note": "My profile description.",
"url": "http://localhost:8080/users/admin",
"avatar": "http://localhost:8080/avatar/admin.jpg",
"avatar_static": "http://localhost:8080/avatar/admin.jpg",
"header": "http://localhost:8080/header/admin.jpg",
"header_static": "http://localhost:8080/header/admin.jpg",
"emojis": []
]
self["/api/v1/accounts/verify_credentials"] = JSONResponse(result: selfAccount)
self["/api/v1/accounts/\\d+/statuses"] = JSONResponse(result: [])
self["/api/v1/accounts/(\\d+)"] = DelegatingResponse { (env) in
let captures = env["ambassador.router_captures"] as! [String]
if captures[0] == "1" {
return JSONResponse(result: selfAccount)
} else {
return JSONResponse(statusCode: 404, statusMessage: "Not Found", result: notFound)
}
}
}
func timelineRoutes() {
let emptyTimeline: [Any] = []
self["/api/v1/timelines/home"] = JSONResponse(result: emptyTimeline)
self["/api/v1/timelines/public"] = JSONResponse(result: emptyTimeline)
}
func notificationRoutes() {
let emptyTimeline: [Any] = []
self["/api/v1/notifications"] = JSONResponse(result: emptyTimeline)
}
}

View File

@ -0,0 +1,18 @@
//
// DelegatingResponse.swift
// TuskerUITests
//
// Created by Shadowfacts on 12/31/19.
// Copyright © 2019 Shadowfacts. All rights reserved.
//
import Foundation
import Ambassador
struct DelegatingResponse: WebApp {
let handler: (_ environ: [String: Any]) -> WebApp
func app(_ environ: [String : Any], startResponse: @escaping ((String, [(String, String)]) -> Void), sendBody: @escaping ((Data) -> Void)) {
handler(environ).app(environ, startResponse: startResponse, sendBody: sendBody)
}
}

View File

@ -0,0 +1,18 @@
//
// JSONResponse.swift
// TuskerUITests
//
// Created by Shadowfacts on 12/31/19.
// Copyright © 2019 Shadowfacts. All rights reserved.
//
import Foundation
import Ambassador
extension JSONResponse {
init(statusCode: Int = 200, statusMessage: String = "OK", result: Any) {
self.init(statusCode: statusCode, statusMessage: statusMessage, handler: { (_) in
return result
})
}
}

View File

@ -0,0 +1,26 @@
//
// MyProfileTests.swift
// TuskerUITests
//
// Created by Shadowfacts on 12/31/19.
// Copyright © 2019 Shadowfacts. All rights reserved.
//
import XCTest
class MyProfileTests: TuskerUITests {
override func setUp() {
super.setUp()
router.allRoutes()
app.launchEnvironment["UI_TESTING_LOGIN"] = "true"
app.launch()
}
func testExample() {
sleep(10000000)
}
}

View File

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

View File

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