Skip to content

Commit

Permalink
updated UI Test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
Vladimir Espinola committed May 23, 2024
1 parent 53f326f commit 34d7cd1
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 29 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ on:
push:
branches:
- main
- develop
- feature/*
pull_request:
branches:
- main
- develop
- feature/*
workflow_dispatch:

Expand All @@ -26,6 +28,22 @@ jobs:
- name: "Run tests"
run: xcodebuild -project "attentive-ios-sdk.xcodeproj" -scheme "attentive-ios-sdk-framework" -destination "platform=iOS Simulator,OS=16.1,name=iPhone 13 Pro" test

ui-tests:
name: Run iOS UI Tests
runs-on: macOS-14
env:
DEVELOPER_DIR: "/Applications/Xcode_15.3.app/Contents/Developer"
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: "Run UI Tests"
run: xcodebuild -workspace "attentive-ios-sdk.xcworkspace" -scheme "CreativeUITest" -destination "platform=iOS Simulator,OS=17.4,name=iPhone 15 Pro" -derivedDataPath build/ -resultBundlePath ui-test-results.xcresult test | xcpretty
- name: Upload Test Results
uses: actions/upload-artifact@v3
with:
name: ui-test-results
path: ui-test-results.xcresult
lint:
name: Run lint
runs-on: ubuntu-20.04
Expand Down
4 changes: 4 additions & 0 deletions Example/CreativeUITest/CreativeUITest-Bridging-Header.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

60 changes: 31 additions & 29 deletions Example/CreativeUITest/CreativeUITest.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#import <WebKit/WebKit.h>
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import "CreativeUITest-Swift.h"

@interface CreativeUITest : XCTestCase

Expand Down Expand Up @@ -38,72 +39,74 @@ + (void)tearDown {

- (void)testLoadCreative_clickClose_closesCreative {
[self launchAppWithMode:@"production"];
[app.buttons[@"Push me for Creative!"] tap];
[app.buttons[@"Push me for Creative!"] tapOnElement];

sleep(2);

// Close the creative
XCTAssertTrue([app.webViews.buttons[@"Dismiss this popup"] waitForExistenceWithTimeout:5.0]);
[app.webViews.buttons[@"Dismiss this popup"] tap];
[app.webViews.buttons[@"Dismiss this popup"] tapOnElement];

// Assert that the creative is closed
XCTAssertTrue([app.buttons[@"Push me for Creative!"] waitForExistenceWithTimeout:5.0]);
XCTAssertTrue([app.buttons[@"Push me for Creative!"] elementExists]);
XCTAssertEqual(app.buttons[@"Push me for Creative!"].isHittable, true);
}


- (void)testLoadCreative_fillOutFormAndSubmit_launchesSmsAppWithPrePopulatedText {
[self launchAppWithMode:@"production"];
[app.buttons[@"Push me to clear the User!"] tap];
[app.buttons[@"Push me for Creative!"] tap];
[app.buttons[@"Push me to clear the User!"] tapOnElement];
[app.buttons[@"Push me for Creative!"] tapOnElement];

// Fill in the email
XCTAssertTrue([app.webViews.textFields[@"Email Address"] waitForExistenceWithTimeout:5.0]);
XCUIElement *emailField = app.webViews.textFields[@"Email Address"];
[emailField tap];
[emailField tapOnElement];
[emailField typeText:@"[email protected]"];

// Tap something else on the creative to dismiss the keyboard
[app.staticTexts[@"10% OFF"] tap];
[app.staticTexts[@"10% OFF"] tapOnElement];

// Submit email
XCTAssertTrue([app.buttons[@"CONTINUE"] waitForExistenceWithTimeout:5.0]);
[app.webViews.buttons[@"CONTINUE"] tap];
[app.webViews.buttons[@"CONTINUE"] tapOnElement];

// Click subscribe button
XCTAssertTrue([app.buttons[@"GET 10% OFF NOW when you sign up for email and texts"] waitForExistenceWithTimeout:5.0]);
[app.buttons[@"GET 10% OFF NOW when you sign up for email and texts"] tap];
[app.buttons[@"GET 10% OFF NOW when you sign up for email and texts"] tapOnElement];

// Assert that the SMS app is opened with prepopulated text if running locally
// (AWS Device Farm doesn't allow use of SMS apps)
NSString *envHost = [[[NSProcessInfo processInfo] environment] objectForKey:@"COM_ATTENTIVE_EXAMPLE_HOST"];
if ([envHost isEqualToString:@"local"]) {
XCUIApplication *smsApp = [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.MobileSMS"];
XCTAssertTrue([smsApp.textFields[@"Message"] waitForExistenceWithTimeout:5.0]);

// Close Info Screen when SMS App is opened for the first time
if ([smsApp.buttons[@"OK"] elementExists]) {
[smsApp.buttons[@"OK"] tapOnElement];
}

XCTAssertTrue([smsApp.textFields[@"Message"] elementExists]);
XCTAssertTrue([smsApp.textFields[@"Message"].value containsString:@"Send this text to subscribe to recurring automated personalized marketing alerts (e.g. cart reminders) from Attentive Mobile Apps Test"]);
}
}


- (void)testLoadCreative_clickPrivacyLink_opensPrivacyPageInWebApp {
[self launchAppWithMode:@"production"];
[app.buttons[@"Push me for Creative!"] tap];
[app.buttons[@"Push me for Creative!"] tapOnElement];

// Click privacy link
XCTAssertTrue([app.webViews.links[@"Privacy"] waitForExistenceWithTimeout:5.0]);
[app.webViews.links[@"Privacy"] tap];
[app.webViews.links[@"Privacy"] tapOnElement];

// Wait for a moment to allow the app to react
sleep(5);

// Check if the app is no longer active
XCTAssertFalse([app.webViews.links[@"Privacy"] waitForExistenceWithTimeout:5.0]);
XCTAssertFalse([app.webViews.links[@"Privacy"] elementExists]);

// AWS Device Farm doesn't always acknowledge separate apps, leading to flakiness here
NSString *envHost = [[[NSProcessInfo processInfo] environment] objectForKey:@"COM_ATTENTIVE_EXAMPLE_HOST"];
if ([envHost isEqualToString:@"local"]) {
// Verify that the privacy page is visible in the external browser
XCUIApplication *safariApp = [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.mobilesafari"];
BOOL privacyPolicyExists = [safariApp.staticTexts[@"Privacy Policy"] waitForExistenceWithTimeout:5.0];
BOOL messagingPrivacyPolicyExists = [safariApp.staticTexts[@"Messaging Privacy Policy"] waitForExistenceWithTimeout:5.0];
BOOL privacyPolicyExists = [safariApp.staticTexts[@"Privacy Policy"] elementExists];
BOOL messagingPrivacyPolicyExists = [safariApp.staticTexts[@"Messaging Privacy Policy"] elementExists];

XCTAssertTrue(privacyPolicyExists || messagingPrivacyPolicyExists);
}
Expand All @@ -112,26 +115,25 @@ - (void)testLoadCreative_clickPrivacyLink_opensPrivacyPageInWebApp {

- (void)testLoadCreative_inDebugMode_showsDebugMessage {
[self launchAppWithMode:@"debug"];
[app.buttons[@"Push me for Creative!"] tap];
[app.buttons[@"Push me for Creative!"] tapOnElement];

// Verify debug page shows
XCTAssertTrue([app.staticTexts[@"Debug output JSON"] waitForExistenceWithTimeout:5.0]);
XCTAssertTrue([app.staticTexts[@"Debug output JSON"] elementExists]);
}

- (void)testLoadCreative_clickProductPage_closesCreative {
[self launchAppWithMode:@"production"];
[app.buttons[@"Push me for Creative!"] tap];
[app.buttons[@"Push me for Creative!"] tapOnElement];

// Click privacy link
XCTAssertTrue([app.webViews.links[@"Privacy"] waitForExistenceWithTimeout:5.0]);
[app.tabBars.buttons[@"Product"] tap];
[app.tabBars.buttons[@"Product"] tapOnElement];

// Verify that the product page is visible
XCTAssertTrue([app.buttons[@"Add To Cart"] waitForExistenceWithTimeout:5.0]);
XCTAssertTrue([app.buttons[@"Add To Cart"] elementExists]);

// Nav back, and verify the creative is closed
[app.tabBars.buttons[@"Main"] tap];
XCTAssertTrue([app.buttons[@"Push me for Creative!"] waitForExistenceWithTimeout:5.0]);
[app.tabBars.buttons[@"Main"] tapOnElement];
XCTAssertTrue([app.buttons[@"Push me for Creative!"] elementExists]);
}


Expand Down
30 changes: 30 additions & 0 deletions Example/CreativeUITest/XCUIElement+Extension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// XCUIElement+Extension.swift
// CreativeUITest
//
// Created by Vladimir - Work on 2024-05-22.
//

import XCTest

extension XCUIElement {
/// Verify element existence and then proceed with tapping on it
@objc func tapOnElement() {
guard elementExists() else {
XCTFail("\(description) does not exists")
return
}
tap()
}

/// Verify element existence on the app view hierarchy
@objc func elementExists() -> Bool {
elementExists(timeout: 5)
}
}

fileprivate extension XCUIElement {
func elementExists(timeout: TimeInterval) -> Bool {
waitForExistence(timeout: timeout)
}
}
14 changes: 14 additions & 0 deletions Example/Example.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
69CCAF5F28DCDE5A007620BD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 69CCAF5D28DCDE5A007620BD /* LaunchScreen.storyboard */; };
69CCAF6228DCDE5A007620BD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 69CCAF6128DCDE5A007620BD /* main.m */; };
69D3C14C299EF2D10027934F /* CreativeUITest.m in Sources */ = {isa = PBXBuildFile; fileRef = 69D3C14B299EF2D10027934F /* CreativeUITest.m */; };
FB51061A2BFE4A9700D9A72D /* XCUIElement+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB5106192BFE4A9700D9A72D /* XCUIElement+Extension.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -76,6 +77,8 @@
69CCAF6128DCDE5A007620BD /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
69D3C149299EF2D10027934F /* CreativeUITest.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CreativeUITest.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
69D3C14B299EF2D10027934F /* CreativeUITest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CreativeUITest.m; sourceTree = "<group>"; };
FB5106182BFE4A9700D9A72D /* CreativeUITest-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CreativeUITest-Bridging-Header.h"; sourceTree = "<group>"; };
FB5106192BFE4A9700D9A72D /* XCUIElement+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCUIElement+Extension.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -168,6 +171,8 @@
isa = PBXGroup;
children = (
69D3C14B299EF2D10027934F /* CreativeUITest.m */,
FB5106192BFE4A9700D9A72D /* XCUIElement+Extension.swift */,
FB5106182BFE4A9700D9A72D /* CreativeUITest-Bridging-Header.h */,
);
path = CreativeUITest;
sourceTree = "<group>";
Expand Down Expand Up @@ -249,6 +254,7 @@
};
69D3C148299EF2D10027934F = {
CreatedOnToolsVersion = 14.2;
LastSwiftMigration = 1530;
TestTargetID = 58317964292EEEAA0003D6B0;
};
};
Expand Down Expand Up @@ -335,6 +341,7 @@
buildActionMask = 2147483647;
files = (
69D3C14C299EF2D10027934F /* CreativeUITest.m in Sources */,
FB51061A2BFE4A9700D9A72D /* XCUIElement+Extension.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -604,6 +611,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
Expand Down Expand Up @@ -632,6 +640,9 @@
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_OBJC_BRIDGING_HEADER = "CreativeUITest/CreativeUITest-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = "Example - Local";
};
Expand All @@ -641,6 +652,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
Expand Down Expand Up @@ -669,6 +681,8 @@
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_OBJC_BRIDGING_HEADER = "CreativeUITest/CreativeUITest-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = "Example - Local";
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1530"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "69D3C148299EF2D10027934F"
BuildableName = "CreativeUITest.xctest"
BlueprintName = "CreativeUITest"
ReferencedContainer = "container:Example.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

0 comments on commit 34d7cd1

Please sign in to comment.