| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import <PassKit/PassKit.h> |
| |
| #import <memory> |
| |
| #import "base/functional/bind.h" |
| #import "base/test/ios/wait_util.h" |
| #import "ios/chrome/browser/download/model/download_test_util.h" |
| #import "ios/chrome/browser/infobars/ui_bundled/banners/infobar_banner_constants.h" |
| #import "ios/chrome/browser/shared/model/utils/mime_type_util.h" |
| #import "ios/chrome/grit/ios_strings.h" |
| #import "ios/chrome/test/earl_grey/chrome_earl_grey.h" |
| #import "ios/chrome/test/earl_grey/chrome_test_case.h" |
| #import "ios/chrome/test/scoped_eg_synchronization_disabler.h" |
| #import "ios/testing/earl_grey/app_launch_manager.h" |
| #import "ios/testing/earl_grey/earl_grey_test.h" |
| #import "net/test/embedded_test_server/embedded_test_server.h" |
| #import "net/test/embedded_test_server/http_request.h" |
| #import "net/test/embedded_test_server/http_response.h" |
| #import "ui/base/l10n/l10n_util_mac.h" |
| |
| using base::test::ios::kWaitForDownloadTimeout; |
| using base::test::ios::WaitUntilConditionOrTimeout; |
| |
| namespace { |
| |
| // Returns matcher for PassKit error infobar. |
| id<GREYMatcher> PassKitErrorInfobarLabels() { |
| return grey_allOf( |
| grey_accessibilityID(kInfobarBannerLabelsStackViewIdentifier), |
| grey_accessibilityLabel( |
| l10n_util::GetNSString(IDS_IOS_GENERIC_PASSKIT_ERROR)), |
| nil); |
| } |
| |
| // PassKit landing page and download request handler. |
| std::unique_ptr<net::test_server::HttpResponse> GetResponse( |
| const net::test_server::HttpRequest& request) { |
| auto result = std::make_unique<net::test_server::BasicHttpResponse>(); |
| result->set_code(net::HTTP_OK); |
| |
| if (request.GetURL().path() == "/single") { |
| result->set_content("<a id='bad' href='/single-bad'>Bad</a>" |
| "<a id='good' href='/single-good'>Good</a>"); |
| } else if (request.GetURL().path() == "/bundle") { |
| result->set_content("<a id='bad' href='/bundle-bad'>Bad</a>" |
| "<a id='good' href='/bundle-good'>Good</a>" |
| "<a id='semi' href='/bundle-semi'>Semi</a>"); |
| } else if (request.GetURL().path() == "/single-bad") { |
| result->AddCustomHeader("Content-Type", kPkPassMimeType); |
| result->set_content("corrupted"); |
| } else if (request.GetURL().path() == "/single-good") { |
| result->AddCustomHeader("Content-Type", kPkPassMimeType); |
| result->set_content(testing::GetTestFileContents(testing::kPkPassFilePath)); |
| } else if (request.GetURL().path() == "/bundle-bad") { |
| result->AddCustomHeader("Content-Type", kPkBundledPassMimeType); |
| // Returning a valid pass unzipped is invalid for a bundled pass. |
| result->set_content(testing::GetTestFileContents(testing::kPkPassFilePath)); |
| } else if (request.GetURL().path() == "/bundle-good") { |
| result->AddCustomHeader("Content-Type", kPkBundledPassMimeType); |
| result->set_content( |
| testing::GetTestFileContents(testing::kBundledPkPassFilePath)); |
| } else if (request.GetURL().path() == "/bundle-semi") { |
| result->AddCustomHeader("Content-Type", kPkBundledPassMimeType); |
| result->set_content( |
| testing::GetTestFileContents(testing::kSemiValidBundledPkPassFilePath)); |
| } |
| |
| return result; |
| } |
| |
| } // namespace |
| |
| // Tests PassKit file download. |
| @interface PassKitEGTest : ChromeTestCase |
| @end |
| |
| @implementation PassKitEGTest { |
| } |
| |
| - (void)setUp { |
| [super setUp]; |
| |
| self.testServer->RegisterRequestHandler(base::BindRepeating(&GetResponse)); |
| GREYAssertTrue(self.testServer->Start(), @"Test server failed to start."); |
| } |
| |
| // Tests that Chrome presents PassKit error infobar if pkpass file cannot be |
| // parsed. |
| - (void)testPassKitParsingError { |
| [ChromeEarlGrey loadURL:self.testServer->GetURL("/single")]; |
| [ChromeEarlGrey waitForWebStateContainingText:"Bad"]; |
| [ChromeEarlGrey tapWebStateElementWithID:@"bad"]; |
| |
| bool infobarShown = WaitUntilConditionOrTimeout(kWaitForDownloadTimeout, ^{ |
| NSError* error = nil; |
| [[EarlGrey selectElementWithMatcher:PassKitErrorInfobarLabels()] |
| assertWithMatcher:grey_notNil() |
| error:&error]; |
| return (error == nil); |
| }); |
| GREYAssert(infobarShown, @"PassKit error infobar was not shown"); |
| } |
| |
| // Tests that Chrome PassKit dialog is shown for sucessfully downloaded pkpass |
| // file. |
| - (void)testPassKitDownload { |
| if ([ChromeEarlGrey isIPadIdiom]) { |
| EARL_GREY_TEST_SKIPPED(@"Wallet app is not supported on iPads."); |
| } |
| |
| [ChromeEarlGrey loadURL:self.testServer->GetURL("/single")]; |
| [ChromeEarlGrey waitForWebStateContainingText:"Good"]; |
| [ChromeEarlGrey tapWebStateElementWithID:@"good"]; |
| |
| // PKAddPassesViewController UI is rendered out of host process so EarlGrey |
| // matcher can not find PassKit Dialog UI. |
| // EG2 test can use XCUIApplication API to check for PassKit dialog UI |
| // presentation. |
| XCUIApplication* app = [[XCUIApplication alloc] init]; |
| XCUIElement* title = nil; |
| title = app.staticTexts[@"Toy Town"]; |
| GREYAssert( |
| [title waitForExistenceWithTimeout:kWaitForDownloadTimeout.InSecondsF()], |
| @"PassKit dialog UI was not presented"); |
| } |
| |
| // Tests that Chrome PassKit dialog is shown for sucessfully downloaded bundle |
| // pkpasses file. |
| - (void)testBundlePassKitDownload { |
| if ([ChromeEarlGrey isIPadIdiom]) { |
| EARL_GREY_TEST_SKIPPED(@"Wallet app is not supported on iPads."); |
| } |
| |
| [ChromeEarlGrey loadURL:self.testServer->GetURL("/bundle")]; |
| [ChromeEarlGrey waitForWebStateContainingText:"Good"]; |
| [ChromeEarlGrey tapWebStateElementWithID:@"good"]; |
| |
| { |
| ScopedSynchronizationDisabler synchronizationDisabler; |
| // PKAddPassesViewController UI is rendered out of host process so EarlGrey |
| // matcher can not find PassKit Dialog UI. |
| // EG2 test can use XCUIApplication API to check for PassKit dialog UI |
| // presentation. |
| XCUIApplication* app = [[XCUIApplication alloc] init]; |
| XCUIElement* title = nil; |
| title = app.staticTexts[@"Toy Town"]; |
| GREYAssert( |
| [title |
| waitForExistenceWithTimeout:kWaitForDownloadTimeout.InSecondsF()], |
| @"PassKit dialog UI was not presented"); |
| |
| // It is flaky to swipe to show the other pass, so check that there is the |
| // title saying there are 2 passes. |
| title = app.staticTexts[@"2 Passes"]; |
| GREYAssert( |
| [title |
| waitForExistenceWithTimeout:kWaitForDownloadTimeout.InSecondsF()], |
| @"There aren't two passes."); |
| } |
| } |
| |
| // Tests that Chrome PassKit dialog is shown for sucessfully downloaded |
| // semi-valid bundle pkpasses file (containing one good pkpass and a non-valid |
| // one). |
| - (void)testSemiValidBundlePassKitDownload { |
| if ([ChromeEarlGrey isIPadIdiom]) { |
| EARL_GREY_TEST_SKIPPED(@"Wallet app is not supported on iPads."); |
| } |
| |
| [ChromeEarlGrey loadURL:self.testServer->GetURL("/bundle")]; |
| [ChromeEarlGrey waitForWebStateContainingText:"Good"]; |
| [ChromeEarlGrey tapWebStateElementWithID:@"semi"]; |
| |
| { |
| ScopedSynchronizationDisabler synchronizationDisabler; |
| // PKAddPassesViewController UI is rendered out of host process so EarlGrey |
| // matcher can not find PassKit Dialog UI. |
| // EG2 test can use XCUIApplication API to check for PassKit dialog UI |
| // presentation. |
| XCUIApplication* app = [[XCUIApplication alloc] init]; |
| XCUIElement* title = nil; |
| title = app.staticTexts[@"Toy Town"]; |
| GREYAssert( |
| [title |
| waitForExistenceWithTimeout:kWaitForDownloadTimeout.InSecondsF()], |
| @"PassKit dialog UI was not presented"); |
| |
| // Swipe left, nothing should happen. |
| [title swipeLeft]; |
| GREYAssertTrue(title.hittable, @"PassKit dialog UI was not scrolled"); |
| } |
| } |
| |
| // Tests that Chrome presents PassKit error infobar if bundle pkpass file cannot |
| // be parsed. |
| - (void)testInvalidBundlePassKitDownload { |
| if ([ChromeEarlGrey isIPadIdiom]) { |
| EARL_GREY_TEST_SKIPPED(@"Wallet app is not supported on iPads."); |
| } |
| |
| [ChromeEarlGrey loadURL:self.testServer->GetURL("/bundle")]; |
| [ChromeEarlGrey waitForWebStateContainingText:"Good"]; |
| [ChromeEarlGrey tapWebStateElementWithID:@"bad"]; |
| |
| bool infobarShown = WaitUntilConditionOrTimeout(kWaitForDownloadTimeout, ^{ |
| NSError* error = nil; |
| [[EarlGrey selectElementWithMatcher:PassKitErrorInfobarLabels()] |
| assertWithMatcher:grey_notNil() |
| error:&error]; |
| return (error == nil); |
| }); |
| GREYAssert(infobarShown, @"PassKit error infobar was not shown"); |
| } |
| |
| @end |