source/UberRidesTests/RequestButtonTests.swift (415 lines of code) (raw):

// // RequestButtonTests.swift // UberRidesTests // // Copyright © 2015 Uber Technologies, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import XCTest import OHHTTPStubs import OHHTTPStubsSwift import CoreLocation import WebKit import UberCore @testable import UberRides class RequestButtonTests: XCTestCase { var client: RidesClient! var button: RideRequestButton! weak var expectation: XCTestExpectation? weak var errorExpectation: XCTestExpectation? var rideButtonError: UberError! let timeout: Double = 5 override func setUp() { super.setUp() Configuration.plistName = "testInfo" Configuration.restoreDefaults() Configuration.shared.isSandbox = true client = RidesClient() } override func tearDown() { Configuration.restoreDefaults() HTTPStubs.removeAllStubs() super.tearDown() } /** Test that title is initialized properly to default value. */ func testInitRequestButtonDefaultText() { button = RideRequestButton(client: client) XCTAssertEqual(button.uberTitleLabel.text!, "Ride there with Uber") } func testCorrectSource_whenRideRequestViewRequestingBehavior() { let testExpectation = expectation(description: "Test RideRequestView source parameter") let expectationClosure: (URLRequest) -> () = { request in testExpectation.fulfill() guard let url = request.url, let components = URLComponents(url: url, resolvingAgainstBaseURL: false), let items = components.queryItems else { XCTAssert(false) return } XCTAssertTrue(items.count > 0) var foundUserAgent = false for item in items { if (item.name == "user-agent") { if let value = item.value { foundUserAgent = true XCTAssertTrue(value.contains(RideRequestButton.sourceString)) break } } } XCTAssert(foundUserAgent) } let testIdentifier = "testAccessTokenIdentifier" let testToken = AccessToken(tokenString: "testTokenString") _ = TokenManager.save(accessToken: testToken, tokenIdentifier: testIdentifier) defer { _ = TokenManager.deleteToken(identifier: testIdentifier) } let baseViewController = UIViewControllerMock() let requestBehavior = RideRequestViewRequestingBehavior(presentingViewController: baseViewController) let button = RideRequestButton(rideParameters: RideParametersBuilder().build(), requestingBehavior: requestBehavior) let loginManger = LoginManager(accessTokenIdentifier: testIdentifier) let rideRequestVC = RideRequestViewController(rideParameters: RideParametersBuilder().build(), loginManager: loginManger) XCTAssertNotNil(rideRequestVC.view) let webViewMock = WebViewMock(frame: CGRect.zero, configuration: WKWebViewConfiguration(), testClosure: expectationClosure) rideRequestVC.rideRequestView.webView = webViewMock requestBehavior.modalRideRequestViewController.rideRequestViewController = rideRequestVC button.uberButtonTapped(button) waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) XCTAssertFalse(rideRequestVC.rideRequestView.isHidden) }) } func testCorrectSource_whenDeeplinkRequestingBehavior() { let testExpectation = expectation(description: "Test Deeplink source parameter") let expectationClosure: (URL?) -> (Bool) = { url in testExpectation.fulfill() guard let url = url, let components = URLComponents(url: url, resolvingAgainstBaseURL: false), let items = components.queryItems else { XCTAssert(false) return false } XCTAssertTrue(items.count > 0) var foundUserAgent = false for item in items { if (item.name == "user-agent") { if let value = item.value { foundUserAgent = true XCTAssertTrue(value.contains(RideRequestButton.sourceString)) break } } } XCTAssert(foundUserAgent) return false } let requestBehavior = DeeplinkRequestingBehaviorMock(testClosure: expectationClosure) let button = RideRequestButton(rideParameters: RideParametersBuilder().build(), requestingBehavior: requestBehavior) button.uberButtonTapped(button) waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) }) } /** Test that product ID is set on metadata. */ func testSetProductID() { let builder = RideParametersBuilder() builder.productID = productID let rideParams = builder.build() button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) button.loadRideInformation() XCTAssertEqual(button.metadata.productID, productID) } /** Test that pickup location lat/long is set on metadata. */ func testSetPickupLocation() { let location = CLLocation(latitude: pickupLat, longitude: pickupLong) let builder = RideParametersBuilder() builder.pickupLocation = location let rideParams = builder.build() button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) button.loadRideInformation() XCTAssertEqual(button.metadata.pickupLatitude, pickupLat) XCTAssertEqual(button.metadata.pickupLongitude, pickupLong) } /** Test that dropoff location lat/long is set on metadata. */ func testSetDropoffLocation() { let location = CLLocation(latitude: dropoffLat, longitude: dropoffLong) let builder = RideParametersBuilder() builder.dropoffLocation = location let rideParams = builder.build() button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) button.loadRideInformation() XCTAssertEqual(button.metadata.dropoffLatitude, dropoffLat) XCTAssertEqual(button.metadata.dropoffLongitude, dropoffLong) } /** Test get metadata with productID and pickup location only. Expected only time estimate label. */ func testGetMetadataSimple() { stub(condition: isHost("sandbox-api.uber.com")) { _ in return HTTPStubsResponse(fileAtPath:OHPathForFile("getTimeEstimateProduct.json", type(of: self))!, statusCode:200, headers:nil) } expectation = expectation(description: "information loaded") let location = CLLocation(latitude: dropoffLat, longitude: pickupLong) let builder = RideParametersBuilder() builder.pickupLocation = location builder.productID = productID let rideParams = builder.build() button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) XCTAssertEqual(self.button.uberTitleLabel.text!, "Get a ride") XCTAssertEqual(self.button.uberMetadataLabel.text!, "4 MINS AWAY") }) } /** Test get metadata with productID, pickup, and dropoff locations. Expected time and price estimates on label. */ func testGetMetadataDetailed() { stub(condition: isHost("sandbox-api.uber.com")) { urlRequest in if isPath("/v1.2/estimates/price")(urlRequest) { return HTTPStubsResponse(fileAtPath:OHPathForFile("getPriceEstimates.json", type(of: self))!, statusCode:200, headers:nil) } else if isPath("/v1.2/estimates/time")(urlRequest) { return HTTPStubsResponse(fileAtPath:OHPathForFile("getTimeEstimateProduct.json", type(of: self))!, statusCode:200, headers:nil) } else { XCTAssert(false) return HTTPStubsResponse() } } expectation = expectation(description: "information loaded") let pickupLocation = CLLocation(latitude: pickupLat, longitude: pickupLong) let dropoffLocation = CLLocation(latitude: dropoffLat, longitude: dropoffLong) let builder = RideParametersBuilder() builder.pickupLocation = pickupLocation builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) XCTAssertEqual(self.button.uberTitleLabel.text!, "Get a ride") XCTAssertEqual(self.button.uberMetadataLabel.text!, "4 MINS AWAY\n$15 for uberX") }) } func testErrorGettingPriceEstimates() { stub(condition: isHost("sandbox-api.uber.com")) { urlRequest in if isPath("/v1.2/estimates/time")(urlRequest) { return HTTPStubsResponse(fileAtPath:OHPathForFile("getTimeEstimateProduct.json", type(of: self))!, statusCode:200, headers: [ "Authorization" : "Bearer token" ]) } else if isPath("/v1.2/estimates/price")(urlRequest) { let obj = ["code":"price_estimate_error"] return HTTPStubsResponse(jsonObject: obj, statusCode: 404, headers: nil) } else { XCTAssert(false) return HTTPStubsResponse() } } errorExpectation = expectation(description: "price estimate error") let pickupLocation = CLLocation(latitude: pickupLat, longitude: pickupLong) let dropoffLocation = CLLocation(latitude: dropoffLat, longitude: dropoffLong) let builder = RideParametersBuilder() builder.pickupLocation = pickupLocation builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) XCTAssertEqual(self.button.uberTitleLabel.text!, "Get a ride") XCTAssertEqual(self.button.uberMetadataLabel.text!, "4 MINS AWAY") XCTAssertEqual(self.rideButtonError.code, "price_estimate_error") }) } func testErrorGettingTimeEstimates() { stub(condition: isHost("sandbox-api.uber.com")) { urlRequest in if isPath("/v1.2/estimates/price")(urlRequest) { return HTTPStubsResponse(fileAtPath:OHPathForFile("getPriceEstimates.json", type(of: self))!, statusCode:200, headers:nil) } else if isPath("/v1.2/estimates/time")(urlRequest) { let obj = ["code":"time_estimate_error"] return HTTPStubsResponse(jsonObject: obj, statusCode: 404, headers: nil) } else { XCTAssert(false) return HTTPStubsResponse() } } errorExpectation = expectation(description: "time estimate error") let pickupLocation = CLLocation(latitude: pickupLat, longitude: pickupLong) let dropoffLocation = CLLocation(latitude: dropoffLat, longitude: dropoffLong) let builder = RideParametersBuilder() builder.pickupLocation = pickupLocation builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) XCTAssertEqual(self.button.uberTitleLabel.text!, "Get a ride") XCTAssertEqual(self.button.uberMetadataLabel.text!, "$15 for uberX") XCTAssertEqual(self.rideButtonError.code, "time_estimate_error") }) } func testEmptyTimeEstimatesCallsDelegateValidPriceEstimates() { stub(condition: isHost("sandbox-api.uber.com")) { urlRequest in if isPath("/v1.2/estimates/price")(urlRequest) { return HTTPStubsResponse(fileAtPath:OHPathForFile("getPriceEstimates.json", type(of: self))!, statusCode:200, headers:nil) } else if isPath("/v1.2/estimates/time")(urlRequest) { return HTTPStubsResponse(fileAtPath:OHPathForFile("getTimeEstimatesEmpty.json", type(of: self))!, statusCode:200, headers:nil) } else { XCTAssert(false) return HTTPStubsResponse() } } expectation = expectation(description: "information loaded") let pickupLocation = CLLocation(latitude: pickupLat, longitude: pickupLong) let dropoffLocation = CLLocation(latitude: dropoffLat, longitude: dropoffLong) let builder = RideParametersBuilder() builder.pickupLocation = pickupLocation builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) XCTAssertEqual(self.button.uberTitleLabel.text!, "Get a ride") XCTAssertEqual(self.button.uberMetadataLabel.text!, "$15 for uberX") }) } func testEmptyPriceEstimatesValidTimeEstimates() { stub(condition: isHost("sandbox-api.uber.com")) { urlRequest in if isPath("/v1.2/estimates/price")(urlRequest) { return HTTPStubsResponse(fileAtPath:OHPathForFile("getPriceEstimatesEmpty.json", type(of: self))!, statusCode:200, headers:nil) } else if isPath("/v1.2/estimates/time")(urlRequest) { return HTTPStubsResponse(fileAtPath:OHPathForFile("getTimeEstimateProduct.json", type(of: self))!, statusCode:200, headers:nil) } else { XCTAssert(false) return HTTPStubsResponse() } } expectation = expectation(description: "information loaded") let pickupLocation = CLLocation(latitude: pickupLat, longitude: pickupLong) let dropoffLocation = CLLocation(latitude: dropoffLat, longitude: dropoffLong) let builder = RideParametersBuilder() builder.pickupLocation = pickupLocation builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) XCTAssertEqual(self.button.uberTitleLabel.text!, "Get a ride") XCTAssertEqual(self.button.uberMetadataLabel.text!, "4 MINS AWAY") }) } func testEmptyPriceEstimatesEmptyTimeEstimates() { stub(condition: isHost("sandbox-api.uber.com")) { urlRequest in if isPath("/v1.2/estimates/price")(urlRequest) { return HTTPStubsResponse(fileAtPath:OHPathForFile("getPriceEstimatesEmpty.json", type(of: self))!, statusCode:200, headers:nil) } else if isPath("/v1.2/estimates/time")(urlRequest) { return HTTPStubsResponse(fileAtPath:OHPathForFile("getTimeEstimatesEmpty.json", type(of: self))!, statusCode:200, headers:nil) } else { XCTAssert(false) return HTTPStubsResponse() } } expectation = expectation(description: "information loaded") let pickupLocation = CLLocation(latitude: pickupLat, longitude: pickupLong) let dropoffLocation = CLLocation(latitude: dropoffLat, longitude: dropoffLong) let builder = RideParametersBuilder() builder.pickupLocation = pickupLocation builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in XCTAssertNil(error) XCTAssertEqual(self.button.uberTitleLabel.text!, "Ride there with Uber") XCTAssertNil(self.button.uberMetadataLabel.text) }) } func testMissingClientTriggersErrorDelegate() { errorExpectation = expectation(description: "Expected to receive 422 error") let pickupLocation = CLLocation(latitude: pickupLat, longitude: pickupLong) let dropoffLocation = CLLocation(latitude: dropoffLat, longitude: dropoffLong) let builder = RideParametersBuilder() builder.pickupLocation = pickupLocation builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.client = nil button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in guard let ridesError = self.rideButtonError else { XCTFail("Expected to receive 422 error") return } XCTAssertEqual(ridesError.status, 422) XCTAssertEqual(ridesError.code, "validation_failed") XCTAssertEqual(ridesError.title, "Invalid Request") }) } func testMissingPickupTriggersErrorDelegate() { errorExpectation = expectation(description: "Expected to receive 422 error") let dropoffLocation = CLLocation(latitude: dropoffLat, longitude: dropoffLong) let builder = RideParametersBuilder() builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in guard let ridesError = self.rideButtonError else { XCTFail("Expected to receive 422 error") return } XCTAssertEqual(ridesError.status, 422) XCTAssertEqual(ridesError.code, "validation_failed") XCTAssertEqual(ridesError.title, "Invalid Request") }) } func testUseCurrentLocationTriggersErrorDelegate() { errorExpectation = expectation(description: "Expected to receive 422 error") let dropoffLocation = CLLocation(latitude: dropoffLat, longitude: dropoffLong) let builder = RideParametersBuilder() builder.dropoffLocation = dropoffLocation builder.productID = productID let rideParams = builder.build() button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) button.delegate = self button.loadRideInformation() waitForExpectations(timeout: timeout, handler: { error in guard let ridesError = self.rideButtonError else { XCTFail("Expected to receive 422 error") return } XCTAssertEqual(ridesError.status, 422) XCTAssertEqual(ridesError.code, "validation_failed") XCTAssertEqual(ridesError.title, "Invalid Request") }) } /** Test that button defaults to "Get a Ride" when no productID is set. */ func testMetadataSimpleWithNoProductID() { let pickupLocation = CLLocation(latitude: pickupLat, longitude: pickupLong) let dropoffLocation = CLLocation(latitude: dropoffLat, longitude: dropoffLong) let builder = RideParametersBuilder() builder.pickupLocation = pickupLocation builder.dropoffLocation = dropoffLocation let rideParams = builder.build() button = RideRequestButton(client: client, rideParameters:rideParams, requestingBehavior: DeeplinkRequestingBehavior()) button.loadRideInformation() XCTAssertEqual(self.button.uberTitleLabel.text!, "Ride there with Uber") } } private class UIViewControllerMock : UIViewController { override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?) { viewControllerToPresent.viewWillAppear(flag) viewControllerToPresent.viewDidAppear(flag) if let modal = viewControllerToPresent as? ModalRideRequestViewController { modal.rideRequestViewController.viewWillAppear(flag) modal.rideRequestViewController.viewDidAppear(flag) } return } } // MARK: RequestButtonDelegate extension RequestButtonTests: RideRequestButtonDelegate { func rideRequestButtonDidLoadRideInformation(_ button: RideRequestButton) { expectation?.fulfill() } func rideRequestButton(_ button: RideRequestButton, didReceiveError error: UberError) { self.rideButtonError = error errorExpectation?.fulfill() } }