Skip to content

Commit a96e5b1

Browse files
Attaching app log to failed tests
1 parent fdc5036 commit a96e5b1

File tree

9 files changed

+80
-4
lines changed

9 files changed

+80
-4
lines changed

ios/Configurations/UITests.xcconfig.template

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ PARTNER_API_TOKEN =
1212
// Mullvad accounts used by UI tests
1313
HAS_TIME_ACCOUNT_NUMBER[config=Debug] =
1414
HAS_TIME_ACCOUNT_NUMBER[config=Staging] =
15-
FIVE_WIREGUARD_KEYS_ACCOUNT_NUMBER =
1615

1716
// Ad serving domain used when testing ad blocking. Note that we are assuming there's an HTTP server running on the host.
1817
AD_SERVING_DOMAIN = vpnlist.to

ios/MullvadVPN.xcodeproj/project.pbxproj

+4
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,7 @@
616616
850201DB2B503D7700EF8C96 /* RelayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850201DA2B503D7700EF8C96 /* RelayTests.swift */; };
617617
850201DD2B503D8C00EF8C96 /* SelectLocationPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850201DC2B503D8C00EF8C96 /* SelectLocationPage.swift */; };
618618
850201DF2B5040A500EF8C96 /* TunnelControlPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850201DE2B5040A500EF8C96 /* TunnelControlPage.swift */; };
619+
85021CAE2BDBC4290098B400 /* AppLogsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85021CAD2BDBC4290098B400 /* AppLogsPage.swift */; };
619620
85139B2D2B84B4A700734217 /* OutOfTimePage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85139B2C2B84B4A700734217 /* OutOfTimePage.swift */; };
620621
852969282B4D9C1F007EAD4C /* AccountTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 852969272B4D9C1F007EAD4C /* AccountTests.swift */; };
621622
852969332B4E9232007EAD4C /* Page.swift in Sources */ = {isa = PBXBuildFile; fileRef = 852969322B4E9232007EAD4C /* Page.swift */; };
@@ -1874,6 +1875,7 @@
18741875
850201DC2B503D8C00EF8C96 /* SelectLocationPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectLocationPage.swift; sourceTree = "<group>"; };
18751876
850201DE2B5040A500EF8C96 /* TunnelControlPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelControlPage.swift; sourceTree = "<group>"; };
18761877
850201E22B51A93C00EF8C96 /* SettingsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPage.swift; sourceTree = "<group>"; };
1878+
85021CAD2BDBC4290098B400 /* AppLogsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLogsPage.swift; sourceTree = "<group>"; };
18771879
85139B2C2B84B4A700734217 /* OutOfTimePage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutOfTimePage.swift; sourceTree = "<group>"; };
18781880
8518F6372B60157E009EB113 /* LoggedInWithoutTimeUITestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggedInWithoutTimeUITestCase.swift; sourceTree = "<group>"; };
18791881
852969252B4D9C1F007EAD4C /* MullvadVPNUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MullvadVPNUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -3664,6 +3666,7 @@
36643666
850201DE2B5040A500EF8C96 /* TunnelControlPage.swift */,
36653667
8542CE232B95F7B9006FCA14 /* VPNSettingsPage.swift */,
36663668
85FB5A0B2B6903990015DCED /* WelcomePage.swift */,
3669+
85021CAD2BDBC4290098B400 /* AppLogsPage.swift */,
36673670
);
36683671
path = Pages;
36693672
sourceTree = "<group>";
@@ -5649,6 +5652,7 @@
56495652
8529693C2B4F0257007EAD4C /* Alert.swift in Sources */,
56505653
850201DD2B503D8C00EF8C96 /* SelectLocationPage.swift in Sources */,
56515654
85D039982BA4711800940E7F /* SettingsMigrationTests.swift in Sources */,
5655+
85021CAE2BDBC4290098B400 /* AppLogsPage.swift in Sources */,
56525656
850201DB2B503D7700EF8C96 /* RelayTests.swift in Sources */,
56535657
85139B2D2B84B4A700734217 /* OutOfTimePage.swift in Sources */,
56545658
85A42B862BB1D627007BABF7 /* XCUIElement+Extensions.swift in Sources */,

ios/MullvadVPN/Classes/AccessbilityIdentifier.swift

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public enum AccessibilityIdentifier: String {
1313
case accountButton
1414
case agreeButton
1515
case alertOkButton
16+
case appLogsDoneButton
17+
case appLogsShareButton
1618
case applyButton
1719
case cancelButton
1820
case connectionPanelButton
@@ -74,6 +76,7 @@ public enum AccessibilityIdentifier: String {
7476
case addLocationsView
7577
case alertContainerView
7678
case alertTitle
79+
case appLogsView
7780
case changeLogAlert
7881
case deviceManagementView
7982
case headerBarView
@@ -101,6 +104,7 @@ public enum AccessibilityIdentifier: String {
101104
case dnsSettingsEnterIPAddressTextField
102105
case loginTextField
103106
case selectLocationSearchTextField
107+
case problemReportAppLogsTextView
104108
case problemReportEmailTextField
105109
case problemReportMessageTextView
106110
case deleteAccountTextField

ios/MullvadVPN/View controllers/ProblemReport/ProblemReportReviewViewController.swift

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class ProblemReportReviewViewController: UIViewController {
2424
override func viewDidLoad() {
2525
super.viewDidLoad()
2626

27+
view.accessibilityIdentifier = .appLogsView
28+
2729
navigationItem.title = NSLocalizedString(
2830
"NAVIGATION_TITLE",
2931
tableName: "ProblemReportReview",
@@ -37,6 +39,7 @@ class ProblemReportReviewViewController: UIViewController {
3739
self?.dismiss(animated: true)
3840
})
3941
)
42+
navigationItem.rightBarButtonItem?.accessibilityIdentifier = .appLogsDoneButton
4043

4144
#if DEBUG
4245
navigationItem.leftBarButtonItem = UIBarButtonItem(
@@ -45,8 +48,10 @@ class ProblemReportReviewViewController: UIViewController {
4548
self?.share()
4649
})
4750
)
51+
navigationItem.leftBarButtonItem?.accessibilityIdentifier = .appLogsShareButton
4852
#endif
4953

54+
textView.accessibilityIdentifier = .problemReportAppLogsTextView
5055
textView.translatesAutoresizingMaskIntoConstraints = false
5156
textView.text = reportString
5257
textView.isEditable = false

ios/MullvadVPN/View controllers/ProblemReport/ProblemReportViewController+ViewManagement.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ extension ProblemReportViewController {
8888

8989
func makeViewLogsButton() -> AppButton {
9090
let button = AppButton(style: .default)
91+
button.accessibilityIdentifier = .problemReportAppLogsButton
9192
button.translatesAutoresizingMaskIntoConstraints = false
9293
button.setTitle(Self.persistentViewModel.viewLogsButtonTitle, for: .normal)
9394
button.addTarget(self, action: #selector(handleViewLogsButtonTap), for: .touchUpInside)
@@ -96,7 +97,7 @@ extension ProblemReportViewController {
9697

9798
func makeSendButton() -> AppButton {
9899
let button = AppButton(style: .success)
99-
button.accessibilityIdentifier = AccessibilityIdentifier.problemReportSendButton
100+
button.accessibilityIdentifier = .problemReportSendButton
100101
button.translatesAutoresizingMaskIntoConstraints = false
101102
button.setTitle(Self.persistentViewModel.sendLogsButtonTitle, for: .normal)
102103
button.addTarget(self, action: #selector(handleSendButtonTap), for: .touchUpInside)

ios/MullvadVPNUITests/AccountTests.swift

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class AccountTests: LoggedOutUITestCase {
5454
}
5555

5656
func testLogin() throws {
57+
XCTFail("test")
5758
let hasTimeAccountNumber = getAccountWithTime()
5859

5960
addTeardownBlock {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// AppLogsPage.swift
3+
// MullvadVPNUITests
4+
//
5+
// Created by Niklas Berglund on 2024-04-26.
6+
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
7+
//
8+
9+
import XCTest
10+
11+
class AppLogsPage: Page {
12+
override init(_ app: XCUIApplication) {
13+
super.init(app)
14+
15+
self.pageAccessibilityIdentifier = .appLogsView
16+
waitForPageToBeShown()
17+
}
18+
19+
@discardableResult func tapShareButton() -> Self {
20+
app.buttons[AccessibilityIdentifier.appLogsShareButton].tap()
21+
return self
22+
}
23+
24+
@discardableResult func tapDoneButton() -> Self {
25+
app.buttons[AccessibilityIdentifier.appLogsDoneButton].tap()
26+
return self
27+
}
28+
29+
func getAppLogText() -> String {
30+
guard let logText = app.textViews[AccessibilityIdentifier.problemReportAppLogsTextView].value as? String else {
31+
XCTFail("Failed to extract app log text")
32+
return String()
33+
}
34+
35+
return logText
36+
}
37+
}

ios/MullvadVPNUITests/Pages/ProblemReportPage.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class ProblemReportPage: Page {
3333
}
3434

3535
@discardableResult func tapViewAppLogsButton() -> Self {
36-
app.otherElements[AccessibilityIdentifier.problemReportAppLogsButton]
36+
app.buttons[AccessibilityIdentifier.problemReportAppLogsButton]
3737
.tap()
3838

3939
return self

ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift

+26-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class BaseUITestCase: XCTestCase {
2525
.infoDictionary?["IOSDevicePinCode"] as! String
2626
// swiftlint:enable force_cast
2727

28+
/// Get an account number with time. If an account with time is specified in the configuration file that account will be used, else a temporary account will be created.
2829
func getAccountWithTime() -> String {
2930
if let configuredAccountWithTime = bundleHasTimeAccountNumber, !configuredAccountWithTime.isEmpty {
3031
return configuredAccountWithTime
@@ -36,12 +37,14 @@ class BaseUITestCase: XCTestCase {
3637
}
3738
}
3839

40+
/// Return account with time after done using it This is neccessary because if a temporary account was created we want to delete it.
3941
func returnAccountWithTime(accountNumber: String) {
4042
if bundleHasTimeAccountNumber?.isEmpty == true {
4143
PartnerAPIClient().deleteAccount(accountNumber: accountNumber)
4244
}
4345
}
4446

47+
/// Get an account number without time. If an account without time is specified in the configuration file that account will be used, else a temporary account will be created.
4548
func getAccountWithoutTime() -> String {
4649
if let configuredAccountWithoutTime = bundleNoTimeAccountNumber, !configuredAccountWithoutTime.isEmpty {
4750
return configuredAccountWithoutTime
@@ -107,7 +110,29 @@ class BaseUITestCase: XCTestCase {
107110
app.terminate()
108111

109112
if let testRun = self.testRun, testRun.failureCount > 0 {
110-
// TODO: extract and store log
113+
app.launch()
114+
115+
HeaderBar(app)
116+
.tapSettingsButton()
117+
118+
SettingsPage(app)
119+
.tapReportAProblemCell()
120+
121+
ProblemReportPage(app)
122+
.tapViewAppLogsButton()
123+
124+
let logText = AppLogsPage(app)
125+
.getAppLogText()
126+
127+
// Attach app log to result
128+
let dateFormatter = DateFormatter()
129+
dateFormatter.dateFormat = "yyyy-MM-dd_HH-mm-ss"
130+
let dateString = dateFormatter.string(from: Date())
131+
let attachment = XCTAttachment(string: logText)
132+
attachment.name = "app-log-\(dateString).log"
133+
add(attachment)
134+
135+
app.terminate()
111136
}
112137
}
113138

0 commit comments

Comments
 (0)