Skip to content

Commit

Permalink
added ui test and ui yml action
Browse files Browse the repository at this point in the history
  • Loading branch information
vespinola committed Jun 28, 2024
1 parent 438e7c7 commit b7fd009
Show file tree
Hide file tree
Showing 10 changed files with 288 additions and 15 deletions.
37 changes: 37 additions & 0 deletions .github/workflows/ui.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Run UI Test Cases

on:
pull_request:
branches:
- main
- "feature/*"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
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: Reset Simulators
run: xcrun simctl erase all
- name: Run UI Tests
run: |
set -o pipefail
xcodebuild -project "mobile-courier-app.xcodeproj" -scheme "mobile-courier-appUITests" -destination "platform=iOS Simulator,OS=17.4,name=iPhone 15 Pro" -derivedDataPath build/ -resultBundlePath ui-test-results.xcresult test | xcpretty
- name: Compress UI Test Results
if: failure()
run: tar -czf ui-test-results.xcresult.tar.gz ui-test-results.xcresult
- name: Upload UI Test Results
if: failure()
uses: actions/upload-artifact@v3
with:
name: ui-test-results
path: ui-test-results.xcresult.tar.gz
32 changes: 32 additions & 0 deletions mobile-courier-app.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@
1BE63F6F2BE972A60016A26E /* AuthRepositoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BE63F6E2BE972A60016A26E /* AuthRepositoryProtocol.swift */; };
1BE63F712BE980D60016A26E /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BE63F702BE980D60016A26E /* Storage.swift */; };
1BE7837B2C2E503000D2093E /* AccessibilityIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BE7837A2C2E503000D2093E /* AccessibilityIdentifiers.swift */; };
1BE7837E2C2E61BE00D2093E /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BE7837D2C2E61BE00D2093E /* LoginScreen.swift */; };
1BE783802C2E61CA00D2093E /* HomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BE7837F2C2E61CA00D2093E /* HomeScreen.swift */; };
1BE783822C2E61D400D2093E /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BE783812C2E61D400D2093E /* Screen.swift */; };
1BE783852C2E630000D2093E /* XCUIElement+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BE783842C2E630000D2093E /* XCUIElement+Extensions.swift */; };
1BEE841F2C17D6FF00B8A367 /* RowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BEE841E2C17D6FF00B8A367 /* RowView.swift */; };
1BFD1EAF2C2A688B002E4232 /* SettingsViewModelMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BFD1EAE2C2A688B002E4232 /* SettingsViewModelMock.swift */; };
1BFD1EB12C2A7907002E4232 /* GroupedPackageEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BFD1EB02C2A7907002E4232 /* GroupedPackageEntity.swift */; };
Expand Down Expand Up @@ -188,6 +192,10 @@
1BE63F702BE980D60016A26E /* Storage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = "<group>"; };
1BE63F722BE98ADD0016A26E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
1BE7837A2C2E503000D2093E /* AccessibilityIdentifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityIdentifiers.swift; sourceTree = "<group>"; };
1BE7837D2C2E61BE00D2093E /* LoginScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreen.swift; sourceTree = "<group>"; };
1BE7837F2C2E61CA00D2093E /* HomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreen.swift; sourceTree = "<group>"; };
1BE783812C2E61D400D2093E /* Screen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = "<group>"; };
1BE783842C2E630000D2093E /* XCUIElement+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCUIElement+Extensions.swift"; sourceTree = "<group>"; };
1BEE841E2C17D6FF00B8A367 /* RowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RowView.swift; sourceTree = "<group>"; };
1BFD1EAE2C2A688B002E4232 /* SettingsViewModelMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModelMock.swift; sourceTree = "<group>"; };
1BFD1EB02C2A7907002E4232 /* GroupedPackageEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupedPackageEntity.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -318,6 +326,8 @@
1B58EF912BE6BBEA0066F447 /* mobile-courier-appUITests */ = {
isa = PBXGroup;
children = (
1BE783832C2E61E100D2093E /* Extensions */,
1BE7837C2C2E619A00D2093E /* Screens */,
1B58EF922BE6BBEA0066F447 /* MobileCourierAppUITests.swift */,
);
path = "mobile-courier-appUITests";
Expand Down Expand Up @@ -573,6 +583,24 @@
path = Home;
sourceTree = "<group>";
};
1BE7837C2C2E619A00D2093E /* Screens */ = {
isa = PBXGroup;
children = (
1BE7837D2C2E61BE00D2093E /* LoginScreen.swift */,
1BE7837F2C2E61CA00D2093E /* HomeScreen.swift */,
1BE783812C2E61D400D2093E /* Screen.swift */,
);
path = Screens;
sourceTree = "<group>";
};
1BE783832C2E61E100D2093E /* Extensions */ = {
isa = PBXGroup;
children = (
1BE783842C2E630000D2093E /* XCUIElement+Extensions.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
1BFD1EB42C2A79CB002E4232 /* Extensions */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -856,6 +884,10 @@
buildActionMask = 2147483647;
files = (
1B58EF932BE6BBEA0066F447 /* MobileCourierAppUITests.swift in Sources */,
1BE7837E2C2E61BE00D2093E /* LoginScreen.swift in Sources */,
1BE783802C2E61CA00D2093E /* HomeScreen.swift in Sources */,
1BE783822C2E61D400D2093E /* Screen.swift in Sources */,
1BE783852C2E630000D2093E /* XCUIElement+Extensions.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1540"
version = "2.2">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<AutocreatedTestPlanReference>
</AutocreatedTestPlanReference>
</BuildActionEntry>
</BuildActionEntries>
</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 = "1B58EF8D2BE6BBEA0066F447"
BuildableName = "mobile-courier-appUITests.xctest"
BlueprintName = "mobile-courier-appUITests"
ReferencedContainer = "container:mobile-courier-app.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">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1B58EF8D2BE6BBEA0066F447"
BuildableName = "mobile-courier-appUITests.xctest"
BlueprintName = "mobile-courier-appUITests"
ReferencedContainer = "container:mobile-courier-app.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ enum AccessibilityIdentifiers {
}

enum HeaderView {
static var usernameLabel: String { "Home-UsernameLabel" }
static var usernameLabel: String { "HeaderView-UsernameLabel" }
}

enum GroupedPackageRowView {
Expand Down
6 changes: 3 additions & 3 deletions mobile-courier-app/Presentation/Login/LoginView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,15 @@ struct LoginView: View {
.foregroundStyle(.white)
.cornerRadius(16)
.disabled(!viewModel.buttonIsEnabled)
.accessibilityIdentifier(
AccessibilityIdentifiers.Login.loginButton
)

Spacer()
}
.padding(20)
.showRippleSpinner(isLoading: $viewModel.isLoading)
.toast(message: $viewModel.toastMessage)
.accessibilityIdentifier(
AccessibilityIdentifiers.Login.loginButton
)
}

private func navigateToLogin() {
Expand Down
36 changes: 36 additions & 0 deletions mobile-courier-appUITests/Extensions/XCUIElement+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// XCUIElement+Extensions.swift
// mobile-courier-appUITests
//
// Created by Vladimir Espinola on 2024-06-27.
//

import XCTest

enum Timeout {
static var `default`: TimeInterval { 10 }
static var max: TimeInterval { 15 }
}

extension XCUIElement {
func tapOnElement(timeout: TimeInterval = Timeout.default) {
guard elementExists(timeout: timeout) else {
XCTFail("\(description) does not exists")
return
}
tap()
}

func elementExists(timeout: TimeInterval = Timeout.default) -> Bool {
waitForExistence(timeout: timeout)
}

func fillTextField(_ text: String) {
guard elementExists() else {
XCTFail("\(description) does not exists")
return
}

typeText(text)
}
}
35 changes: 24 additions & 11 deletions mobile-courier-appUITests/MobileCourierAppUITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,36 @@ final class MobileCourierAppUITests: XCTestCase {

override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
deleteApp()
try super.tearDownWithError()
}

func testExample() throws {
// UI tests must launch the application that they test.
func testLogin_givenUserData_whenAuthenticate_shouldLandOnHome() throws {
let app = XCUIApplication()
app.launch()

// Use XCTAssert and related functions to verify your tests produce the correct results.
}
LoginScreen
.performLogin(email: "john.doe@mail.com", password: "123ABC123")

func testLaunchPerformance() throws {
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
// This measures how long it takes to launch your application.
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
}
HomeScreen
.verifyUsername()
.verifyShipment(by: 2179)
.verifyShipment(by: 2182)
}
}

extension MobileCourierAppUITests {
func deleteApp() {
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")

let icon = springboard.icons["JustACourierApp"]

guard icon.exists else { return }

icon.press(forDuration: 1)

springboard.buttons["Remove App"].tapOnElement()
springboard.buttons["Delete App"].tapOnElement()
springboard.buttons["Delete"].tapOnElement()
}
}
30 changes: 30 additions & 0 deletions mobile-courier-appUITests/Screens/HomeScreen.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// HomeScreen.swift
// mobile-courier-appUITests
//
// Created by Vladimir Espinola on 2024-06-27.
//

import XCTest

struct HomeScreen: Screen {
private init() {}

@discardableResult
static func verifyUsername() -> Self.Type {
XCTAssertTrue(headerText.elementExists())
return self
}

@discardableResult
static func verifyShipment(by id: Int) -> Self.Type {
XCTAssertTrue(app.staticTexts["GroupedPackageRowView-\(id)"].elementExists())
return self
}
}

private extension HomeScreen {
static var headerText: XCUIElement {
app.staticTexts["HeaderView-UsernameLabel"]
}
}
34 changes: 34 additions & 0 deletions mobile-courier-appUITests/Screens/LoginScreen.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// LoginScreen.swift
// mobile-courier-appUITests
//
// Created by Vladimir Espinola on 2024-06-27.
//

import XCTest

struct LoginScreen: Screen {
private init() {}

@discardableResult
static func performLogin(email: String, password: String) -> Self.Type {
sleep(2)
emailTextField.fillTextField(email)
passwordTextField.fillTextField(password)
loginButton.tapOnElement()
return self
}
}

private extension LoginScreen {
static var loginButton: XCUIElement { app.buttons["Login-Button"]
}

static var emailTextField: XCUIElement {
app.descendants(matching: .any)["Login-Email"]
}

static var passwordTextField: XCUIElement {
app.descendants(matching: .any)["Login-Password"]
}
}
16 changes: 16 additions & 0 deletions mobile-courier-appUITests/Screens/Screen.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// Screen.swift
// mobile-courier-appUITests
//
// Created by Vladimir Espinola on 2024-06-27.
//

import XCTest

protocol Screen { }

extension Screen {
static var app: XCUIApplication {
.init()
}
}

0 comments on commit b7fd009

Please sign in to comment.