This repository has been archived by the owner. It is now read-only.
Permalink
Cannot retrieve contributors at this time
675 lines (555 sloc)
29.2 KB
| /* This Source Code Form is subject to the terms of the Mozilla Public | |
| * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
| import Foundation | |
| import GCDWebServers | |
| import Storage | |
| import WebKit | |
| import SwiftKeychainWrapper | |
| import Shared | |
| import EarlGrey | |
| @testable import Client | |
| let LabelAddressAndSearch = "Address and Search" | |
| extension XCTestCase { | |
| func tester(_ file: String = #file, _ line: Int = #line) -> KIFUITestActor { | |
| return KIFUITestActor(inFile: file, atLine: line, delegate: self) | |
| } | |
| func system(_ file: String = #file, _ line: Int = #line) -> KIFSystemTestActor { | |
| return KIFSystemTestActor(inFile: file, atLine: line, delegate: self) | |
| } | |
| } | |
| extension KIFUITestActor { | |
| /// Looks for a view with the given accessibility hint. | |
| func tryFindingViewWithAccessibilityHint(_ hint: String) -> Bool { | |
| let element = UIApplication.shared.accessibilityElement { element in | |
| return element?.accessibilityHint! == hint | |
| } | |
| return element != nil | |
| } | |
| func waitForViewWithAccessibilityHint(_ hint: String) -> UIView? { | |
| var view: UIView? = nil | |
| autoreleasepool { | |
| wait(for: nil, view: &view, withElementMatching: NSPredicate(format: "accessibilityHint = %@", hint), tappable: false) | |
| } | |
| return view | |
| } | |
| func viewExistsWithLabel(_ label: String) -> Bool { | |
| do { | |
| try self.tryFindingView(withAccessibilityLabel: label) | |
| return true | |
| } catch { | |
| return false | |
| } | |
| } | |
| func viewExistsWithLabelPrefixedBy(_ prefix: String) -> Bool { | |
| let element = UIApplication.shared.accessibilityElement { element in | |
| return element?.accessibilityLabel?.hasPrefix(prefix) ?? false | |
| } | |
| return element != nil | |
| } | |
| /// Waits for and returns a view with the given accessibility value. | |
| func waitForViewWithAccessibilityValue(_ value: String) -> UIView { | |
| var element: UIAccessibilityElement! | |
| run { _ in | |
| element = UIApplication.shared.accessibilityElement { element in | |
| return element?.accessibilityValue == value | |
| } | |
| return (element == nil) ? KIFTestStepResult.wait : KIFTestStepResult.success | |
| } | |
| return UIAccessibilityElement.viewContaining(element) | |
| } | |
| /// Wait for and returns a view with the given accessibility label as an | |
| /// attributed string. See the comment in ReadingListPanel.swift about | |
| /// using attributed strings as labels. (It lets us set the pitch) | |
| func waitForViewWithAttributedAccessibilityLabel(_ label: NSAttributedString) -> UIView { | |
| var element: UIAccessibilityElement! | |
| run { _ in | |
| element = UIApplication.shared.accessibilityElement { element in | |
| if let elementLabel = element?.value(forKey: "accessibilityLabel") as? NSAttributedString { | |
| return elementLabel.isEqual(to: label) | |
| } | |
| return false | |
| } | |
| return (element == nil) ? KIFTestStepResult.wait : KIFTestStepResult.success | |
| } | |
| return UIAccessibilityElement.viewContaining(element) | |
| } | |
| /// There appears to be a KIF bug where waitForViewWithAccessibilityLabel returns the parent | |
| /// UITableView instead of the UITableViewCell with the given label. | |
| /// As a workaround, retry until KIF gives us a cell. | |
| /// Open issue: https://github.com/kif-framework/KIF/issues/336 | |
| func waitForCellWithAccessibilityLabel(_ label: String) -> UITableViewCell { | |
| var cell: UITableViewCell! | |
| run { _ in | |
| let view = self.waitForView(withAccessibilityLabel: label) | |
| cell = view as? UITableViewCell | |
| return (cell == nil) ? KIFTestStepResult.wait : KIFTestStepResult.success | |
| } | |
| return cell | |
| } | |
| /** | |
| * Finding views by accessibility label doesn't currently work with WKWebView: | |
| * https://github.com/kif-framework/KIF/issues/460 | |
| * As a workaround, inject a KIFHelper class that iterates the document and finds | |
| * elements with the given textContent or title. | |
| */ | |
| func waitForWebViewElementWithAccessibilityLabel(_ text: String) { | |
| run { error in | |
| if self.hasWebViewElementWithAccessibilityLabel(text) { | |
| return KIFTestStepResult.success | |
| } | |
| return KIFTestStepResult.wait | |
| } | |
| } | |
| /** | |
| * Sets the text for a WKWebView input element with the given name. | |
| */ | |
| func enterText(_ text: String, intoWebViewInputWithName inputName: String) { | |
| let webView = getWebViewWithKIFHelper() | |
| var stepResult = KIFTestStepResult.wait | |
| let escaped = text.replacingOccurrences(of: "\"", with: "\\\"") | |
| webView.evaluateJavaScript("KIFHelper.enterTextIntoInputWithName(\"\(escaped)\", \"\(inputName)\");") { success, _ in | |
| stepResult = (success as! Bool) ? KIFTestStepResult.success : KIFTestStepResult.failure | |
| } | |
| run { error in | |
| if stepResult == KIFTestStepResult.failure { | |
| error?.pointee = NSError(domain: "KIFHelper", code: 0, userInfo: [NSLocalizedDescriptionKey: "Input element not found in webview: \(escaped)"]) | |
| } | |
| return stepResult | |
| } | |
| } | |
| /** | |
| * Clicks a WKWebView element with the given label. | |
| */ | |
| func tapWebViewElementWithAccessibilityLabel(_ text: String) { | |
| let webView = getWebViewWithKIFHelper() | |
| var stepResult = KIFTestStepResult.wait | |
| let escaped = text.replacingOccurrences(of: "\"", with: "\\\"") | |
| webView.evaluateJavaScript("KIFHelper.tapElementWithAccessibilityLabel(\"\(escaped)\")") { success, _ in | |
| stepResult = (success as! Bool) ? KIFTestStepResult.success : KIFTestStepResult.failure | |
| } | |
| run { error in | |
| if stepResult == KIFTestStepResult.failure { | |
| error?.pointee = NSError(domain: "KIFHelper", code: 0, userInfo: [NSLocalizedDescriptionKey: "Accessibility label not found in webview: \(escaped)"]) | |
| } | |
| return stepResult | |
| } | |
| } | |
| /** | |
| * Determines whether an element in the page exists. | |
| */ | |
| func hasWebViewElementWithAccessibilityLabel(_ text: String) -> Bool { | |
| let webView = getWebViewWithKIFHelper() | |
| var stepResult = KIFTestStepResult.wait | |
| var found = false | |
| let escaped = text.replacingOccurrences(of: "\"", with: "\\\"") | |
| webView.evaluateJavaScript("KIFHelper.hasElementWithAccessibilityLabel(\"\(escaped)\")") { success, _ in | |
| found = success as? Bool ?? false | |
| stepResult = KIFTestStepResult.success | |
| } | |
| run { _ in return stepResult } | |
| return found | |
| } | |
| fileprivate func getWebViewWithKIFHelper() -> WKWebView { | |
| let webView = waitForView(withAccessibilityLabel: "Web content") as! WKWebView | |
| // Wait for the web view to stop loading. | |
| run { _ in | |
| return webView.isLoading ? KIFTestStepResult.wait : KIFTestStepResult.success | |
| } | |
| var stepResult = KIFTestStepResult.wait | |
| webView.evaluateJavaScript("typeof KIFHelper") { result, _ in | |
| if result as! String == "undefined" { | |
| let bundle = Bundle(for: BrowserTests.self) | |
| let path = bundle.path(forResource: "KIFHelper", ofType: "js")! | |
| let source = try! String(contentsOfFile: path, encoding: .utf8) | |
| webView.evaluateJavaScript(source, completionHandler: nil) | |
| } | |
| stepResult = KIFTestStepResult.success | |
| } | |
| run { _ in return stepResult } | |
| return webView | |
| } | |
| public func deleteCharacterFromFirstResponser() { | |
| self.enterText(intoCurrentFirstResponder: "\u{0008}") | |
| } | |
| } | |
| class BrowserUtils { | |
| // Needs to be in sync with Client Clearables. | |
| enum Clearable: String { | |
| case History = "Browsing History" | |
| case Cache = "Cache" | |
| case OfflineData = "Offline Website Data" | |
| case Cookies = "Cookies" | |
| case TrackingProtection = "Tracking Protection" | |
| } | |
| internal static let AllClearables = Set([Clearable.History, Clearable.Cache, Clearable.OfflineData, Clearable.Cookies, Clearable.TrackingProtection]) | |
| class func resetToAboutHome() { | |
| var error: NSError? | |
| // If there is a popup dialog, close. Otherwise, ignore the error and continue | |
| EarlGrey.selectElement(with: grey_accessibilityLabel("Cancel")).perform(grey_tap(), error: &error) | |
| error = nil | |
| if iPad() { | |
| EarlGrey.selectElement(with: grey_accessibilityID("TopTabsViewController.tabsButton")).perform(grey_tap()) | |
| } else { | |
| EarlGrey.selectElement(with: grey_accessibilityID("TabToolbar.tabsButton")).perform(grey_tap()) | |
| } | |
| let goPrivateModeBtn = grey_allOf([grey_accessibilityID("TabTrayController.maskButton"), grey_accessibilityValue("Off")]) | |
| let goNormalModeBtn = grey_allOf([grey_accessibilityID("TabTrayController.maskButton"), grey_accessibilityValue("On")]) | |
| let closeAllBtn = grey_allOf([grey_accessibilityID("TabTrayController.deleteButton.closeAll"), grey_kindOfClass(NSClassFromString("_UIAlertControllerActionView")!)]) | |
| // Clear all Private and normal tabs | |
| EarlGrey.selectElement(with: goPrivateModeBtn).assert(grey_notNil(), error: &error) | |
| if (error == nil) { /* in normal mode now, go to Private mode */ | |
| EarlGrey.selectElement(with: goPrivateModeBtn).perform(grey_tap()) | |
| } | |
| EarlGrey.selectElement(with: grey_accessibilityID("TabTrayController.removeTabsButton")).perform(grey_tap()) | |
| EarlGrey.selectElement(with: closeAllBtn).perform(grey_tap()) | |
| /* go to Normal mode */ | |
| EarlGrey.selectElement(with: goNormalModeBtn).perform(grey_tap()) | |
| EarlGrey.selectElement(with: grey_accessibilityID("TabTrayController.removeTabsButton")).perform(grey_tap()) | |
| EarlGrey.selectElement(with: closeAllBtn).perform(grey_tap()) | |
| let topsiteAppeared = GREYCondition(name: "Wait for the topsite view", block: { | |
| var errorOrNil: NSError? | |
| let matcher = grey_allOf([grey_accessibilityLabel("Show Tabs"), | |
| grey_sufficientlyVisible()]) | |
| EarlGrey.selectElement(with: matcher) | |
| .assert(grey_notNil(), error: &errorOrNil) | |
| let success = errorOrNil == nil | |
| return success | |
| }).wait(withTimeout: 10) | |
| GREYAssertTrue(topsiteAppeared, reason: "Failed to return to topsite view") | |
| } | |
| class func configEarlGrey() { | |
| GREYConfiguration.sharedInstance().setValue(2, forConfigKey: kGREYConfigKeyCALayerMaxAnimationDuration) | |
| GREYConfiguration.sharedInstance().setValue(false, forConfigKey: kGREYConfigKeyActionConstraintsEnabled) | |
| GREYConfiguration.sharedInstance().setValue(2, forConfigKey: kGREYConfigKeyDelayedPerformMaxTrackableDuration) | |
| GREYConfiguration.sharedInstance().setValue(100, forConfigKey: kGREYConfigKeyInteractionTimeoutDuration) | |
| GREYConfiguration.sharedInstance().setValue(["."], forConfigKey: kGREYConfigKeyURLBlacklistRegex) | |
| } | |
| class func dismissFirstRunUI() { | |
| var error: NSError? | |
| let matcher = grey_allOf([ | |
| grey_accessibilityID("IntroViewController.scrollView"), grey_sufficientlyVisible()]) | |
| EarlGrey.selectElement(with: matcher).assert(grey_notNil(), error: &error) | |
| if error == nil { | |
| EarlGrey.selectElement(with: matcher).perform(grey_swipeFastInDirection(GREYDirection.left)) | |
| let buttonMatcher = grey_allOf([ | |
| grey_accessibilityID("IntroViewController.startBrowsingButton"), grey_sufficientlyVisible()]) | |
| EarlGrey.selectElement(with: buttonMatcher).assert(grey_notNil(), error: &error) | |
| if error == nil { | |
| EarlGrey.selectElement(with: buttonMatcher).perform(grey_tap()) | |
| } | |
| } | |
| } | |
| class func iPad() -> Bool { | |
| return UIDevice.current.userInterfaceIdiom == .pad | |
| } | |
| /// Injects a URL and title into the browser's history database. | |
| class func addHistoryEntry(_ title: String, url: URL) { | |
| let info: [AnyHashable: Any] = [ | |
| "url": url, | |
| "title": title, | |
| "visitType": VisitType.link.rawValue] | |
| NotificationCenter.default.post(name: .OnLocationChange, object: self, userInfo: info) | |
| } | |
| fileprivate class func clearHistoryItemAtIndex(_ index: IndexPath, tester: KIFUITestActor) { | |
| if let row = tester.waitForCell(at: index, inTableViewWithAccessibilityIdentifier: "History List") { | |
| tester.swipeView(withAccessibilityLabel: row.accessibilityLabel, value: row.accessibilityValue, in: KIFSwipeDirection.left) | |
| tester.tapView(withAccessibilityLabel: "Remove") | |
| } | |
| } | |
| class func openClearPrivateDataDialog(_ swipe: Bool) { | |
| let settings_button = grey_allOf([grey_accessibilityLabel("Settings"), | |
| grey_accessibilityID("menu-Settings")]) | |
| EarlGrey.selectElement(with: grey_accessibilityLabel("Menu")).perform(grey_tap()) | |
| // Need this for simulator only | |
| if swipe { | |
| EarlGrey.selectElement(with: grey_accessibilityLabel("Set Homepage")) | |
| .perform(grey_swipeFastInDirection(GREYDirection.left)) | |
| } | |
| EarlGrey.selectElement(with: settings_button).perform(grey_tap()) | |
| EarlGrey.selectElement(with: grey_accessibilityLabel("Clear Private Data")) | |
| .using(searchAction: grey_scrollInDirection(.down, 200), | |
| onElementWithMatcher: grey_accessibilityID("AppSettingsTableViewController.tableView")) | |
| .assert(grey_notNil()) | |
| EarlGrey.selectElement(with: grey_accessibilityLabel("Clear Private Data")).perform(grey_tap()) | |
| } | |
| class func closeClearPrivateDataDialog() { | |
| let back_button = grey_allOf([grey_accessibilityLabel("Settings"), | |
| grey_kindOfClass(NSClassFromString("_UIButtonBarButton")!)]) | |
| EarlGrey.selectElement(with: back_button).perform(grey_tap()) | |
| EarlGrey.selectElement(with: grey_accessibilityID("AppSettingsTableViewController.navigationItem.leftBarButtonItem")) | |
| .perform(grey_tap()) | |
| } | |
| fileprivate class func acceptClearPrivateData() { | |
| EarlGrey.selectElement(with: grey_allOf([grey_accessibilityLabel("OK"), grey_kindOfClass(NSClassFromString("_UIAlertControllerActionView")!)])).perform(grey_tap()) | |
| } | |
| fileprivate class func cancelClearPrivateData() { | |
| EarlGrey.selectElement(with: grey_accessibilityLabel("Cancel")).perform(grey_tap()) | |
| EarlGrey.selectElement(with: grey_accessibilityLabel("Clear Private Data")).perform(grey_tap()) | |
| } | |
| class func clearPrivateData(_ clearables: Set<Clearable>? = AllClearables, swipe: Bool? = false) { | |
| openClearPrivateDataDialog(swipe!) | |
| // Disable all items that we don't want to clear. | |
| for clearable in AllClearables { | |
| let switchControl = grey_allOf([grey_accessibilityLabel(clearable.rawValue), | |
| grey_kindOfClass(UISwitch.self)]) | |
| let clearablePresent = GREYCondition(name: "Wait for URL field", block: { | |
| var errorOrNil: NSError? | |
| EarlGrey.selectElement(with: switchControl) | |
| .assert(grey_notNil(), error: &errorOrNil) | |
| return errorOrNil == nil | |
| }).wait(withTimeout: 10) | |
| GREYAssertTrue(clearablePresent, reason: "Failed to find clearable") | |
| EarlGrey.selectElement(with: switchControl).perform(grey_turnSwitchOn(clearables!.contains(clearable))) | |
| } | |
| EarlGrey.selectElement(with: grey_accessibilityID("ClearPrivateData")).perform(grey_tap()) | |
| acceptClearPrivateData() | |
| closeClearPrivateDataDialog() | |
| } | |
| class func clearHistoryItems(_ tester: KIFUITestActor, numberOfTests: Int = -1) { | |
| resetToAboutHome() | |
| tester.tapView(withAccessibilityLabel: "History") | |
| let historyTable = tester.waitForView(withAccessibilityIdentifier: "History List") as! UITableView | |
| var index = 0 | |
| for _ in 0 ..< historyTable.numberOfSections { | |
| for _ in 0 ..< historyTable.numberOfRows(inSection: 0) { | |
| clearHistoryItemAtIndex(IndexPath(row: 0, section: 0), tester: tester) | |
| if numberOfTests > -1 { | |
| index += 1 | |
| if index == numberOfTests { | |
| return | |
| } | |
| } | |
| } | |
| } | |
| tester.tapView(withAccessibilityLabel: "Top sites") | |
| } | |
| class func ensureAutocompletionResult(_ tester: KIFUITestActor, textField: UITextField, prefix: String, completion: String) { | |
| // searches are async (and debounced), so we have to wait for the results to appear. | |
| tester.waitForViewWithAccessibilityValue(prefix + completion) | |
| let autocompleteFieldlabel = textField.subviews.first { $0.accessibilityIdentifier == "autocomplete" } as? UILabel | |
| if completion == "" { | |
| XCTAssertTrue(autocompleteFieldlabel == nil, "The autocomplete was empty but the label still exists.") | |
| return | |
| } | |
| XCTAssertTrue(autocompleteFieldlabel != nil, "The autocomplete was not found") | |
| XCTAssertEqual(completion, autocompleteFieldlabel!.text, "Expected prefix matches actual prefix") | |
| } | |
| } | |
| class SimplePageServer { | |
| class func getPageData(_ name: String, ext: String = "html") -> String { | |
| let pageDataPath = Bundle(for: self).path(forResource: name, ofType: ext)! | |
| return try! String(contentsOfFile: pageDataPath, encoding: .utf8) | |
| } | |
| static var useLocalhostInsteadOfIP = false | |
| class func start() -> String { | |
| let webServer: GCDWebServer = GCDWebServer() | |
| webServer.addHandler(forMethod: "GET", path: "/image.png", request: GCDWebServerRequest.self) { (request) -> GCDWebServerResponse? in | |
| let img = UIImagePNGRepresentation(UIImage(named: "goBack")!) | |
| return GCDWebServerDataResponse(data: img, contentType: "image/png") | |
| } | |
| for page in ["findPage", "noTitle", "readablePage", "JSPrompt"] { | |
| webServer.addHandler(forMethod: "GET", path: "/\(page).html", request: GCDWebServerRequest.self) { (request) -> GCDWebServerResponse? in | |
| return GCDWebServerDataResponse(html: self.getPageData(page)) | |
| } | |
| } | |
| // we may create more than one of these but we need to give them uniquie accessibility ids in the tab manager so we'll pass in a page number | |
| webServer.addHandler(forMethod: "GET", path: "/scrollablePage.html", request: GCDWebServerRequest.self) { (request) -> GCDWebServerResponse? in | |
| var pageData = self.getPageData("scrollablePage") | |
| let page = Int((request?.query["page"] as! String))! | |
| pageData = pageData.replacingOccurrences(of: "{page}", with: page.description) | |
| return GCDWebServerDataResponse(html: pageData as String) | |
| } | |
| webServer.addHandler(forMethod: "GET", path: "/numberedPage.html", request: GCDWebServerRequest.self) { (request) -> GCDWebServerResponse? in | |
| var pageData = self.getPageData("numberedPage") | |
| let page = Int((request?.query["page"] as! String))! | |
| pageData = pageData.replacingOccurrences(of: "{page}", with: page.description) | |
| return GCDWebServerDataResponse(html: pageData as String) | |
| } | |
| webServer.addHandler(forMethod: "GET", path: "/readerContent.html", request: GCDWebServerRequest.self) { (request) -> GCDWebServerResponse? in | |
| return GCDWebServerDataResponse(html: self.getPageData("readerContent")) | |
| } | |
| webServer.addHandler(forMethod: "GET", path: "/loginForm.html", request: GCDWebServerRequest.self) { _ in | |
| return GCDWebServerDataResponse(html: self.getPageData("loginForm")) | |
| } | |
| webServer.addHandler(forMethod: "GET", path: "/navigationDelegate.html", request: GCDWebServerRequest.self) { _ in | |
| return GCDWebServerDataResponse(html: self.getPageData("navigationDelegate")) | |
| } | |
| webServer.addHandler(forMethod: "GET", path: "/localhostLoad.html", request: GCDWebServerRequest.self) { _ in | |
| return GCDWebServerDataResponse(html: self.getPageData("localhostLoad")) | |
| } | |
| webServer.addHandler(forMethod: "GET", path: "/auth.html", request: GCDWebServerRequest.self) { (request: GCDWebServerRequest?) in | |
| // "user:pass", Base64-encoded. | |
| let expectedAuth = "Basic dXNlcjpwYXNz" | |
| let response: GCDWebServerDataResponse | |
| if request?.headers["Authorization"] as? String == expectedAuth && request?.query["logout"] == nil { | |
| response = GCDWebServerDataResponse(html: "<html><body>logged in</body></html>") | |
| } else { | |
| // Request credentials if the user isn't logged in. | |
| response = GCDWebServerDataResponse(html: "<html><body>auth fail</body></html>") | |
| response.statusCode = 401 | |
| response.setValue("Basic realm=\"test\"", forAdditionalHeader: "WWW-Authenticate") | |
| } | |
| return response | |
| } | |
| func htmlForImageBlockingTest(imageURL: String) -> String{ | |
| let html = | |
| """ | |
| <html><head><script> | |
| function testImage(URL) { | |
| var tester = new Image(); | |
| tester.onload = imageFound; | |
| tester.onerror = imageNotFound; | |
| tester.src = URL; | |
| } | |
| function imageFound() { | |
| alert('image loaded.'); | |
| } | |
| function imageNotFound() { | |
| alert('image not loaded.'); | |
| } | |
| window.onload = function(e) { | |
| // Disabling TP stats reporting using JS execution on the wkwebview happens async; | |
| // setTimeout(1 sec) is plenty of delay to ensure the JS has executed. | |
| setTimeout(() => { testImage('\(imageURL)'); }, 1000); | |
| } | |
| </script></head> | |
| <body>TEST IMAGE BLOCKING</body></html> | |
| """ | |
| return html | |
| } | |
| // Add tracking protection check page | |
| webServer.addHandler(forMethod: "GET", path: "/tracking-protection-test.html", request: GCDWebServerRequest.self) { (request: GCDWebServerRequest?) in | |
| return GCDWebServerDataResponse(html: htmlForImageBlockingTest(imageURL: "http://ymail.com/favicon.ico")) | |
| } | |
| // Add image blocking test page | |
| webServer.addHandler(forMethod: "GET", path: "/hide-images-test.html", request: GCDWebServerRequest.self) { (request: GCDWebServerRequest?) in | |
| return GCDWebServerDataResponse(html: htmlForImageBlockingTest(imageURL: "https://www.mozilla.com/favicon.ico")) | |
| } | |
| if !webServer.start(withPort: 0, bonjourName: nil) { | |
| XCTFail("Can't start the GCDWebServer") | |
| } | |
| // We use 127.0.0.1 explicitly here, rather than localhost, in order to avoid our | |
| // history exclusion code (Bug 1188626). | |
| let webRoot = "http://\(useLocalhostInsteadOfIP ? "localhost" : "127.0.0.1"):\(webServer.port)" | |
| return webRoot | |
| } | |
| } | |
| class SearchUtils { | |
| static func navigateToSearchSettings(_ tester: KIFUITestActor) { | |
| let engine = SearchUtils.getDefaultEngine().shortName | |
| tester.tapView(withAccessibilityLabel: "Menu") | |
| tester.waitForAnimationsToFinish() | |
| tester.tapView(withAccessibilityLabel: "Settings") | |
| tester.waitForView(withAccessibilityLabel: "Settings") | |
| tester.tapView(withAccessibilityLabel: "Search, \(engine)") | |
| tester.waitForView(withAccessibilityIdentifier: "Search") | |
| } | |
| static func navigateFromSearchSettings(_ tester: KIFUITestActor) { | |
| tester.tapView(withAccessibilityLabel: "Settings") | |
| tester.tapView(withAccessibilityLabel: "Done") | |
| } | |
| // Given that we're at the Search Settings sheet, select the named search engine as the default. | |
| // Afterwards, we're still at the Search Settings sheet. | |
| static func selectDefaultSearchEngineName(_ tester: KIFUITestActor, engineName: String) { | |
| tester.tapView(withAccessibilityLabel: "Default Search Engine", traits: UIAccessibilityTraitButton) | |
| tester.waitForView(withAccessibilityLabel: "Default Search Engine") | |
| tester.tapView(withAccessibilityLabel: engineName) | |
| tester.waitForView(withAccessibilityLabel: "Search") | |
| } | |
| // Given that we're at the Search Settings sheet, return the default search engine's name. | |
| static func getDefaultSearchEngineName(_ tester: KIFUITestActor) -> String { | |
| let view = tester.waitForCellWithAccessibilityLabel("Default Search Engine") | |
| return view.accessibilityValue! | |
| } | |
| static func getDefaultEngine() -> OpenSearchEngine! { | |
| guard let userProfile = (UIApplication.shared.delegate as? AppDelegate)?.profile else { | |
| XCTFail("Unable to get a reference to the user's profile") | |
| return nil | |
| } | |
| return userProfile.searchEngines.defaultEngine | |
| } | |
| /* | |
| static func youTubeSearchEngine() -> OpenSearchEngine { | |
| return mockSearchEngine("YouTube", template: "https://m.youtube.com/#/results?q={searchTerms}&sm=", icon: "youtube")! | |
| } | |
| static func mockSearchEngine(_ name: String, template: String, icon: String) -> OpenSearchEngine? { | |
| guard let imagePath = Bundle(for: self).path(forResource: icon, ofType: "ico"), | |
| let imageData = Data(contentsOfFile: imagePath), | |
| let image = UIImage(data: imageData) else { | |
| XCTFail("Unable to load search engine image named \(icon).ico") | |
| return nil | |
| } | |
| return OpenSearchEngine(engineID: nil, shortName: name, image: image, searchTemplate: template, suggestTemplate: nil, isCustomEngine: true) | |
| } | |
| */ | |
| static func addCustomSearchEngine(_ engine: OpenSearchEngine) { | |
| guard let userProfile = (UIApplication.shared.delegate as? AppDelegate)?.profile else { | |
| XCTFail("Unable to get a reference to the user's profile") | |
| return | |
| } | |
| userProfile.searchEngines.addSearchEngine(engine) | |
| } | |
| static func removeCustomSearchEngine(_ engine: OpenSearchEngine) { | |
| guard let userProfile = (UIApplication.shared.delegate as? AppDelegate)?.profile else { | |
| XCTFail("Unable to get a reference to the user's profile") | |
| return | |
| } | |
| userProfile.searchEngines.deleteCustomEngine(engine) | |
| } | |
| } | |
| // From iOS 10, below methods no longer works | |
| class DynamicFontUtils { | |
| // Need to leave time for the notification to propagate | |
| static func bumpDynamicFontSize(_ tester: KIFUITestActor) { | |
| let value = UIContentSizeCategory.accessibilityExtraLarge | |
| UIApplication.shared.setValue(value, forKey: "preferredContentSizeCategory") | |
| tester.wait(forTimeInterval: 0.3) | |
| } | |
| static func lowerDynamicFontSize(_ tester: KIFUITestActor) { | |
| let value = UIContentSizeCategory.extraSmall | |
| UIApplication.shared.setValue(value, forKey: "preferredContentSizeCategory") | |
| tester.wait(forTimeInterval: 0.3) | |
| } | |
| static func restoreDynamicFontSize(_ tester: KIFUITestActor) { | |
| let value = UIContentSizeCategory.medium | |
| UIApplication.shared.setValue(value, forKey: "preferredContentSizeCategory") | |
| tester.wait(forTimeInterval: 0.3) | |
| } | |
| } | |
| class PasscodeUtils { | |
| static func resetPasscode() { | |
| KeychainWrapper.sharedAppContainerKeychain.setAuthenticationInfo(nil) | |
| } | |
| static func setPasscode(_ code: String, interval: PasscodeInterval) { | |
| let info = AuthenticationKeychainInfo(passcode: code) | |
| info.updateRequiredPasscodeInterval(interval) | |
| KeychainWrapper.sharedAppContainerKeychain.setAuthenticationInfo(info) | |
| } | |
| static func enterPasscode(_ tester: KIFUITestActor, digits: String) { | |
| tester.tapView(withAccessibilityLabel: String(digits[digits.startIndex])) | |
| tester.tapView(withAccessibilityLabel: String(digits[digits.index(digits.startIndex, offsetBy: 1)])) | |
| tester.tapView(withAccessibilityLabel: String(digits[digits.index(digits.startIndex, offsetBy: 2)])) | |
| tester.tapView(withAccessibilityLabel: String(digits[digits.index(digits.startIndex, offsetBy: 3)])) | |
| } | |
| } | |
| class HomePageUtils { | |
| static func navigateToHomePageSettings(_ tester: KIFUITestActor) { | |
| tester.waitForAnimationsToFinish() | |
| tester.tapView(withAccessibilityLabel: "Menu") | |
| tester.tapView(withAccessibilityLabel: "Settings") | |
| tester.tapView(withAccessibilityIdentifier: "Homepage") | |
| } | |
| static func homePageSetting(_ tester: KIFUITestActor) -> String? { | |
| let view = tester.waitForView(withAccessibilityIdentifier: "HomePageSettingTextField") | |
| guard let textField = view as? UITextField else { | |
| XCTFail("View is not a textField: view is \(String(describing: view))") | |
| return nil | |
| } | |
| return textField.text | |
| } | |
| static func navigateFromHomePageSettings(_ tester: KIFUITestActor) { | |
| tester.tapView(withAccessibilityLabel: "Settings") | |
| tester.tapView(withAccessibilityLabel: "Done") | |
| } | |
| } |