From 12e65d06926f75227bad757ee5498fe39f7d0dd9 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Fri, 6 Dec 2024 17:53:37 -0900 Subject: [PATCH] feat(feedback): more iteration on UI form and tests (#4600) --- .../iOS-ObjectiveC/AppDelegate.m | 128 ++++----- .../iOS-Swift-UITests/BaseUITest.swift | 6 +- .../UserFeedbackUITests.swift | 264 +++++++++++++++--- .../xcshareddata/xcschemes/iOS-Swift.xcscheme | 16 +- Samples/iOS-Swift/iOS-Swift/AppDelegate.swift | 23 +- .../iOS-Swift/ErrorsViewController.swift | 2 +- .../UserFeedback/SentryUserFeedbackForm.swift | 10 +- 7 files changed, 329 insertions(+), 120 deletions(-) diff --git a/Samples/iOS-ObjectiveC/iOS-ObjectiveC/AppDelegate.m b/Samples/iOS-ObjectiveC/iOS-ObjectiveC/AppDelegate.m index f02b19b7d2e..add7e76bfb4 100644 --- a/Samples/iOS-ObjectiveC/iOS-ObjectiveC/AppDelegate.m +++ b/Samples/iOS-ObjectiveC/iOS-ObjectiveC/AppDelegate.m @@ -40,75 +40,77 @@ - (BOOL)application:(UIApplication *)application return scope; }; - options.configureUserFeedback = ^(SentryUserFeedbackConfiguration *_Nonnull config) { - UIOffset layoutOffset = UIOffsetMake(25, 75); - if ([args containsObject:@"--io.sentry.iOS-Swift.user-feedback.all-defaults"]) { - config.configureWidget = ^(SentryUserFeedbackWidgetConfiguration *widget) { - widget.layoutUIOffset = layoutOffset; - }; - return; - } - config.useShakeGesture = YES; - config.showFormForScreenshots = YES; - config.configureWidget = ^(SentryUserFeedbackWidgetConfiguration *_Nonnull widget) { - if ([args - containsObject:@"--io.sentry.iOS-Swift.auto-inject-user-feedback-widget"]) { - widget.labelText = @"Report Jank"; - widget.widgetAccessibilityLabel = @"io.sentry.iOS-Swift.button.report-jank"; - widget.layoutUIOffset = layoutOffset; - } else { - widget.autoInject = NO; + if (@available(iOS 13.0, *)) { + options.configureUserFeedback = ^(SentryUserFeedbackConfiguration *_Nonnull config) { + UIOffset layoutOffset = UIOffsetMake(25, 75); + if ([args containsObject:@"--io.sentry.feedback.all-defaults"]) { + config.configureWidget = ^(SentryUserFeedbackWidgetConfiguration *widget) { + widget.layoutUIOffset = layoutOffset; + }; + return; } + config.useShakeGesture = YES; + config.showFormForScreenshots = YES; + config.configureWidget = ^(SentryUserFeedbackWidgetConfiguration *_Nonnull widget) { + if ([args containsObject:@"--io.sentry.feedback.auto-inject-widget"]) { + widget.labelText = @"Report Jank"; + widget.widgetAccessibilityLabel = @"io.sentry.iOS-Swift.button.report-jank"; + widget.layoutUIOffset = layoutOffset; + } else { + widget.autoInject = NO; + } - if ([args containsObject:@"--io.sentry.iOS-Swift.user-feedback.no-widget-text"]) { - widget.labelText = nil; - } - if ([args containsObject:@"--io.sentry.iOS-Swift.user-feedback.no-widget-icon"]) { - widget.showIcon = NO; - } - }; - config.configureForm = ^(SentryUserFeedbackFormConfiguration *_Nonnull uiForm) { - uiForm.formTitle = @"Jank Report"; - uiForm.submitButtonLabel = @"Report that jank"; - uiForm.addScreenshotButtonLabel = @"Show us the jank"; - uiForm.messagePlaceholder - = @"Describe the nature of the jank. Its essence, if you will."; - }; - config.configureTheme = ^(SentryUserFeedbackThemeConfiguration *_Nonnull theme) { - theme.font = [UIFont fontWithName:@"ChalkboardSE-Regular" size:25]; - }; - config.onSubmitSuccess = ^(NSDictionary *_Nonnull info) { - NSString *name = info[@"name"] ?: @"$shakespearean_insult_name"; - UIAlertController *alert = [UIAlertController - alertControllerWithTitle:@"Thanks?" - message:[NSString stringWithFormat: - @"We have enough jank of our own, we " - @"really didn't need yours too, %@", - name] - preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:@"Derp" - style:UIAlertActionStyleDefault - handler:nil]]; - [self.window.rootViewController presentViewController:alert - animated:YES - completion:nil]; - }; - config.onSubmitError = ^(NSError *_Nonnull error) { - UIAlertController *alert = [UIAlertController - alertControllerWithTitle:@"D'oh" - message:[NSString stringWithFormat: + if ([args containsObject:@"--io.sentry.feedback.no-widget-text"]) { + widget.labelText = nil; + } + if ([args containsObject:@"--io.sentry.feedback.no-widget-icon"]) { + widget.showIcon = NO; + } + }; + config.configureForm = ^(SentryUserFeedbackFormConfiguration *_Nonnull uiForm) { + uiForm.formTitle = @"Jank Report"; + uiForm.submitButtonLabel = @"Report that jank"; + uiForm.addScreenshotButtonLabel = @"Show us the jank"; + uiForm.messagePlaceholder + = @"Describe the nature of the jank. Its essence, if you will."; + }; + config.configureTheme = ^(SentryUserFeedbackThemeConfiguration *_Nonnull theme) { + theme.font = [UIFont fontWithName:@"ChalkboardSE-Regular" size:25]; + }; + config.onSubmitSuccess = ^(NSDictionary *_Nonnull info) { + NSString *name = info[@"name"] ?: @"$shakespearean_insult_name"; + UIAlertController *alert = [UIAlertController + alertControllerWithTitle:@"Thanks?" + message:[NSString stringWithFormat: + @"We have enough jank of our own, we " + @"really didn't need yours too, %@", + name] + preferredStyle:UIAlertControllerStyleAlert]; + [alert addAction:[UIAlertAction actionWithTitle:@"Derp" + style:UIAlertActionStyleDefault + handler:nil]]; + [self.window.rootViewController presentViewController:alert + animated:YES + completion:nil]; + }; + config.onSubmitError = ^(NSError *_Nonnull error) { + UIAlertController *alert = [UIAlertController + alertControllerWithTitle:@"D'oh" + message: + [NSString stringWithFormat: @"You tried to report jank, and encountered " @"more jank. The jank has you now: %@", error] - preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:@"Derp" - style:UIAlertActionStyleDefault - handler:nil]]; - [self.window.rootViewController presentViewController:alert - animated:YES - completion:nil]; + preferredStyle:UIAlertControllerStyleAlert]; + [alert addAction:[UIAlertAction actionWithTitle:@"Derp" + style:UIAlertActionStyleDefault + handler:nil]]; + [self.window.rootViewController presentViewController:alert + animated:YES + completion:nil]; + }; }; - }; + } }]; return YES; diff --git a/Samples/iOS-Swift/iOS-Swift-UITests/BaseUITest.swift b/Samples/iOS-Swift/iOS-Swift-UITests/BaseUITest.swift index ac7a449d348..e3abf0eacbf 100644 --- a/Samples/iOS-Swift/iOS-Swift-UITests/BaseUITest.swift +++ b/Samples/iOS-Swift/iOS-Swift-UITests/BaseUITest.swift @@ -32,7 +32,11 @@ extension BaseUITest { return app } - func launchApp() { + func launchApp(args: [String] = [], env: [String: String] = [:]) { + app.launchArguments.append(contentsOf: args) + for (k, v) in env { + app.launchEnvironment[k] = v + } app.launch() waitForExistenceOfMainScreen() } diff --git a/Samples/iOS-Swift/iOS-Swift-UITests/UserFeedbackUITests.swift b/Samples/iOS-Swift/iOS-Swift-UITests/UserFeedbackUITests.swift index 5f95d05f34f..4ca524f2ee9 100644 --- a/Samples/iOS-Swift/iOS-Swift-UITests/UserFeedbackUITests.swift +++ b/Samples/iOS-Swift/iOS-Swift-UITests/UserFeedbackUITests.swift @@ -7,15 +7,101 @@ class UserFeedbackUITests: BaseUITest { override func setUp() { super.setUp() + app.launchArguments.append(contentsOf: [ - "--io.sentry.iOS-Swift.auto-inject-user-feedback-widget", - "--io.sentry.iOS-Swift.user-feedback.all-defaults", - "--io.sentry.feedback.no-animations" + "--io.sentry.feedback.auto-inject-widget", + "--io.sentry.feedback.no-animations", + + // since the goal of these tests is only to exercise the UI of the widget and form, disable as much as possible from the SDK to avoid any confounding factors that might fail or crash a test case + "--disable-spotlight", + "--disable-automatic-session-tracking", + "--disable-metrickit-integration", + "--disable-session-replay", + "--disable-watchdog-tracking", + "--disable-tracing", + "--disable-swizzling", + "--disable-network-breadcrumbs", + "--disable-core-data-tracing", + "--disable-network-tracking", + "--disable-uiviewcontroller-tracing", + "--disable-automatic-breadcrumbs", + "--disable-anr-tracking", + "--disable-auto-performance-tracing", + "--disable-ui-tracing" ]) + continueAfterFailure = true + } + + // MARK: Tests ensuring correct appearance + + func testUIElementsWithDefaults() { + launchApp(args: ["--io.sentry.feedback.all-defaults"]) + // widget button text + XCTAssert(app.staticTexts["Report a Bug"].exists) + + widgetButton.tap() + + // Form title + XCTAssert(app.staticTexts["Report a Bug"].exists) + + // form buttons + XCTAssert(app.staticTexts["Add a screenshot"].exists) + XCTAssert(app.staticTexts["Cancel"].exists) + XCTAssert(app.staticTexts["Send Bug Report"].exists) + + addScreenshotButton.tap() + XCTAssert(app.staticTexts["Remove screenshot"].exists) + + // Input field placeholders + XCTAssertEqual(try XCTUnwrap(nameField.placeholderValue), "Your Name") + XCTAssertEqual(try XCTUnwrap(emailField.placeholderValue), "your.email@example.org") + XCTAssert(app.staticTexts["What's the bug? What did you expect?"].exists) + + // Input field labels + XCTAssert(app.staticTexts["Email"].exists) + XCTAssert(app.staticTexts["Name"].exists) + XCTAssert(app.staticTexts["Description (Required)"].exists) + XCTAssertFalse(app.staticTexts["Email (Required)"].exists) + XCTAssertFalse(app.staticTexts["Name (Required)"].exists) + } + + func testUIElementsWithCustomizations() { launchApp() + + // widget button text + XCTAssert(app.staticTexts["Report Jank"].exists) + + widgetButton.tap() + + // Form title + XCTAssert(app.staticTexts["Jank Report"].exists) + + // form buttons + XCTAssert(app.staticTexts["Report that jank"].exists) + XCTAssert(app.staticTexts["Show us the jank"].exists) + XCTAssert(app.staticTexts["What, me worry?"].exists) + + addScreenshotButton.tap() + XCTAssert(app.staticTexts["Oof too nsfl"].exists) + + // Input field placeholders + XCTAssertEqual(try XCTUnwrap(nameField.placeholderValue), "Yo name") + XCTAssertEqual(try XCTUnwrap(emailField.placeholderValue), "Yo email") + XCTAssert(app.staticTexts["Describe the nature of the jank. Its essence, if you will."].exists) + + // Input field labels + XCTAssert(app.staticTexts["Thine email"].exists) + XCTAssert(app.staticTexts["Thy name"].exists) + XCTAssert(app.staticTexts["Thy complaint (Required)"].exists) + XCTAssertFalse(app.staticTexts["Thine email (Required)"].exists) + XCTAssertFalse(app.staticTexts["Thy name (Required)"].exists) } + // MARK: Tests validating happy path / successful submission + func testSubmitFullyFilledForm() throws { + launchApp(args: ["--io.sentry.feedback.all-defaults"]) + widgetButton.tap() nameField.tap() @@ -27,58 +113,34 @@ class UserFeedbackUITests: BaseUITest { messageTextView.tap() messageTextView.typeText("UITest user feedback") - app.staticTexts["Send Bug Report"].tap() + sendButton.tap() // displaying the form again ensures the widget button still works afterwards; also assert that the fields are in their default state to ensure the entered data is not persisted between displays - widgetButton.tap() // the placeholder text is returned for XCUIElement.value XCTAssertEqual(try XCTUnwrap(nameField.value as? String), "Your Name") XCTAssertEqual(try XCTUnwrap(emailField.value as? String), "your.email@example.org") - // the UITextView doesn't hav a placeholder, it's a label on top of it. so it is actually empty - XCTAssertEqual(try XCTUnwrap(messageTextView.value as? String), "") - } - - func testSubmitWithNoFieldsFilled() throws { - widgetButton.tap() - - app.staticTexts["Send Bug Report"].tap() - - XCTAssert(app.staticTexts["Error"].exists) - - app.buttons["OK"].tap() + XCTAssertEqual(try XCTUnwrap(messageTextView.value as? String), "", "The UITextView shouldn't have any initial text functioning as a placeholder; as UITextView has no placeholder property, the \"placeholder\" is a label on top of it.") } func testSubmitWithOnlyRequiredFieldsFilled() { + launchApp(args: ["--io.sentry.feedback.all-defaults"]) widgetButton.tap() messageTextView.tap() messageTextView.typeText("UITest user feedback") - app.staticTexts["Send Bug Report"].tap() + sendButton.tap() XCTAssert(widgetButton.waitForExistence(timeout: 1)) } - func testSubmitOnlyWithOptionalFieldsFilled() throws { - widgetButton.tap() - - nameField.tap() - nameField.typeText("Andrew") - - emailField.tap() - emailField.typeText("andrew.mcknight@sentry.io") - - app.staticTexts["Send Bug Report"].tap() - - XCTAssert(app.staticTexts["Error"].exists) - - app.buttons["OK"].tap() - } + // MARK: Tests validating cancellation functions correctly func testCancelFromFormByButton() { + launchApp(args: ["--io.sentry.feedback.all-defaults"]) widgetButton.tap() // fill out the fields; we'll assert later that the entered data does not reappear on subsequent displays @@ -95,18 +157,17 @@ class UserFeedbackUITests: BaseUITest { cancelButton.tap() // displaying the form again ensures the widget button still works afterwards; also assert that the fields are in their default state to ensure the entered data is not persisted between displays - widgetButton.tap() // the placeholder text is returned for XCUIElement.value XCTAssertEqual(try XCTUnwrap(nameField.value as? String), "Your Name") XCTAssertEqual(try XCTUnwrap(emailField.value as? String), "your.email@example.org") - // the UITextView doesn't hav a placeholder, it's a label on top of it. so it is actually empty - XCTAssertEqual(try XCTUnwrap(messageTextView.value as? String), "") + XCTAssertEqual(try XCTUnwrap(messageTextView.value as? String), "", "The UITextView shouldn't have any initial text functioning as a placeholder; as UITextView has no placeholder property, the \"placeholder\" is a label on top of it.") } func testCancelFromFormBySwipeDown() { + launchApp(args: ["--io.sentry.feedback.all-defaults"]) widgetButton.tap() // fill out the fields; we'll assert later that the entered data does not reappear on subsequent displays @@ -118,27 +179,30 @@ class UserFeedbackUITests: BaseUITest { messageTextView.tap() messageTextView.typeText("UITest user feedback") - - // the cancel gesture + + // first swipe down dismisses the keyboard that's still visible from typing the above inputs app.swipeDown(velocity: .fast) + + // the modal cancel gesture app.swipeDown(velocity: .fast) // the swipe dismiss animation takes an extra moment, so we need to wait for the widget to be visible again XCTAssert(widgetButton.waitForExistence(timeout: 1)) // displaying the form again ensures the widget button still works afterwards; also assert that the fields are in their default state to ensure the entered data is not persisted between displays - widgetButton.tap() // the placeholder text is returned for XCUIElement.value XCTAssertEqual(try XCTUnwrap(nameField.value as? String), "Your Name") XCTAssertEqual(try XCTUnwrap(emailField.value as? String), "your.email@example.org") - // the UITextView doesn't hav a placeholder, it's a label on top of it. so it is actually empty - XCTAssertEqual(try XCTUnwrap(messageTextView.value as? String), "") + XCTAssertEqual(try XCTUnwrap(messageTextView.value as? String), "", "The UITextView shouldn't have any initial text functioning as a placeholder; as UITextView has no placeholder property, the \"placeholder\" is a label on top of it.") } + // MARK: Tests validating screenshot functionality + func testAddingAndRemovingScreenshots() { + launchApp(args: ["--io.sentry.feedback.all-defaults"]) widgetButton.tap() addScreenshotButton.tap() XCTAssert(removeScreenshotButton.isHittable) @@ -148,8 +212,126 @@ class UserFeedbackUITests: BaseUITest { XCTAssertFalse(removeScreenshotButton.isHittable) } + // MARK: Tests validating error cases + + func testSubmitWithNoFieldsFilledDefault() throws { + launchApp(args: ["--io.sentry.feedback.all-defaults"]) + + widgetButton.tap() + + sendButton.tap() + + XCTAssert(app.staticTexts["Error"].exists) + XCTAssert(app.staticTexts["You must provide all required information. Please check the following field: description."].exists) + + app.buttons["OK"].tap() + } + + func testSubmitWithNoFieldsFilledEmailAndMessageRequired() { + launchApp(args: ["--io.sentry.feedback.require-email"]) + + widgetButton.tap() + + XCTAssert(app.staticTexts["Thine email (Required)"].exists) + XCTAssert(app.staticTexts["Thy name"].exists) + XCTAssertFalse(app.staticTexts["Thy name (Required)"].exists) + XCTAssert(app.staticTexts["Thy complaint (Required)"].exists) + + sendButton.tap() + + XCTAssert(app.staticTexts["Error"].exists) + XCTAssert(app.staticTexts["You must provide all required information. Please check the following fields: thine email and thy complaint."].exists) + + app.buttons["OK"].tap() + } + + func testSubmitWithNoFieldsFilledAllRequired() throws { + launchApp(args: [ + "--io.sentry.feedback.require-email", + "--io.sentry.feedback.require-name" + ]) + + widgetButton.tap() + + XCTAssert(app.staticTexts["Thine email (Required)"].exists) + XCTAssert(app.staticTexts["Thy name (Required)"].exists) + XCTAssert(app.staticTexts["Thy complaint (Required)"].exists) + + sendButton.tap() + + XCTAssert(app.staticTexts["Error"].exists) + XCTAssert(app.staticTexts["You must provide all required information. Please check the following fields: thy name, thine email and thy complaint."].exists) + + app.buttons["OK"].tap() + } + + func testSubmitWithNoFieldsFilledAllRequiredCustomLabels() throws { + launchApp(args: [ + "--io.sentry.feedback.require-email", + "--io.sentry.feedback.require-name" + ]) + + widgetButton.tap() + + XCTAssert(app.staticTexts["Thine email (Required)"].exists) + XCTAssert(app.staticTexts["Thy name (Required)"].exists) + XCTAssert(app.staticTexts["Thy complaint (Required)"].exists) + + sendButton.tap() + + XCTAssert(app.staticTexts["Error"].exists) + XCTAssert(app.staticTexts["You must provide all required information. Please check the following fields: thy name, thine email and thy complaint."].exists) + + app.buttons["OK"].tap() + } + + func testSubmitOnlyWithOptionalFieldsFilled() throws { + launchApp(args: ["--io.sentry.feedback.all-defaults"]) + + widgetButton.tap() + + nameField.tap() + nameField.typeText("Andrew") + + emailField.tap() + emailField.typeText("andrew.mcknight@sentry.io") + + sendButton.tap() + + XCTAssert(app.staticTexts["Error"].exists) + XCTAssert(app.staticTexts["You must provide all required information. Please check the following field: description."].exists) + + app.buttons["OK"].tap() + } + + func testSubmissionErrorThenSuccessAfterFixingIssues() { + launchApp(args: ["--io.sentry.feedback.all-defaults"]) + widgetButton.tap() + + sendButton.tap() + + XCTAssert(app.staticTexts["Error"].exists) + + app.buttons["OK"].tap() + + messageTextView.tap() + messageTextView.typeText("UITest user feedback") + + sendButton.tap() + + XCTAssert(widgetButton.waitForExistence(timeout: 1)) + } + // MARK: Private + var cancelButton: XCUIElement { + app.buttons["io.sentry.feedback.form.cancel"] + } + + var sendButton: XCUIElement { + app.buttons["io.sentry.feedback.form.submit"] + } + var widgetButton: XCUIElement { app.otherElements["io.sentry.feedback.widget"] } diff --git a/Samples/iOS-Swift/iOS-Swift.xcodeproj/xcshareddata/xcschemes/iOS-Swift.xcscheme b/Samples/iOS-Swift/iOS-Swift.xcodeproj/xcshareddata/xcschemes/iOS-Swift.xcscheme index 6924b480e57..75721f0cd2a 100644 --- a/Samples/iOS-Swift/iOS-Swift.xcodeproj/xcshareddata/xcschemes/iOS-Swift.xcscheme +++ b/Samples/iOS-Swift/iOS-Swift.xcodeproj/xcshareddata/xcschemes/iOS-Swift.xcscheme @@ -73,20 +73,28 @@ argument = "--disable-file-io-tracing" isEnabled = "NO"> + + + + diff --git a/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift b/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift index 3d9e54c6486..523bdd81d85 100644 --- a/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift +++ b/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift @@ -1,6 +1,8 @@ import Sentry import UIKit +//swiftlint:disable type_body_length + @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { @@ -165,7 +167,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { options.configureUserFeedback = { config in let layoutOffset = UIOffset(horizontal: 25, vertical: 75) - guard !args.contains("--io.sentry.iOS-Swift.user-feedback.all-defaults") else { + guard !args.contains("--io.sentry.feedback.all-defaults") else { config.configureWidget = { widget in widget.layoutUIOffset = layoutOffset } @@ -175,7 +177,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { config.useShakeGesture = true config.showFormForScreenshots = true config.configureWidget = { widget in - if args.contains("--io.sentry.iOS-Swift.auto-inject-user-feedback-widget") { + if args.contains("--io.sentry.feedback.auto-inject-widget") { if Locale.current.languageCode == "ar" { // arabic widget.labelText = "﷽" } else if Locale.current.languageCode == "ur" { // urdu @@ -192,19 +194,27 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } else { widget.autoInject = false } - if args.contains("--io.sentry.iOS-Swift.user-feedback.no-widget-text") { + if args.contains("--io.sentry.feedback.no-widget-text") { widget.labelText = nil } - if args.contains("--io.sentry.iOS-Swift.user-feedback.no-widget-icon") { + if args.contains("--io.sentry.feedback.no-widget-icon") { widget.showIcon = false } } config.configureForm = { uiForm in uiForm.formTitle = "Jank Report" - uiForm.isEmailRequired = true + uiForm.isEmailRequired = args.contains("--io.sentry.feedback.require-email") + uiForm.isNameRequired = args.contains("--io.sentry.feedback.require-name") uiForm.submitButtonLabel = "Report that jank" uiForm.addScreenshotButtonLabel = "Show us the jank" + uiForm.removeScreenshotButtonLabel = "Oof too nsfl" + uiForm.cancelButtonLabel = "What, me worry?" uiForm.messagePlaceholder = "Describe the nature of the jank. Its essence, if you will." + uiForm.namePlaceholder = "Yo name" + uiForm.emailPlaceholder = "Yo email" + uiForm.messageLabel = "Thy complaint" + uiForm.emailLabel = "Thine email" + uiForm.nameLabel = "Thy name" } config.configureTheme = { theme in let fontFamily: String @@ -241,7 +251,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } } }) - } //swiftlint:enable function_body_length cyclomatic_complexity @@ -303,3 +312,5 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } } } + +//swiftlint:enable type_body_length diff --git a/Samples/iOS-Swift/iOS-Swift/ErrorsViewController.swift b/Samples/iOS-Swift/iOS-Swift/ErrorsViewController.swift index 209df3d3a91..aac9b370626 100644 --- a/Samples/iOS-Swift/iOS-Swift/ErrorsViewController.swift +++ b/Samples/iOS-Swift/iOS-Swift/ErrorsViewController.swift @@ -59,7 +59,7 @@ class ErrorsViewController: UIViewController { scope.setTag(value: "value", key: "myTag") } - if !ProcessInfo.processInfo.arguments.contains("--io.sentry.iOS-Swift.auto-inject-user-feedback-widget") { + if !ProcessInfo.processInfo.arguments.contains("--io.sentry.feedback.auto-inject-widget") { let alert = UIAlertController(title: "Uh-oh!", message: "There was an error. Would you like to tell us what happened?", preferredStyle: .alert) alert.addAction(.init(title: "Yes", style: .default, handler: { _ in SentrySDK.showUserFeedbackForm() diff --git a/Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackForm.swift b/Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackForm.swift index 72c6b55fcc3..b49555afcef 100644 --- a/Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackForm.swift +++ b/Sources/Swift/Integrations/UserFeedback/SentryUserFeedbackForm.swift @@ -114,20 +114,20 @@ class SentryUserFeedbackForm: UIViewController { var missing = [String]() if config.formConfig.isNameRequired && !fullNameTextField.hasText { - missing.append("name") + missing.append(config.formConfig.nameLabel.lowercased()) } if config.formConfig.isEmailRequired && !emailTextField.hasText { - missing.append("email") + missing.append(config.formConfig.emailLabel.lowercased()) } if !messageTextView.hasText { - missing.append("description") + missing.append(config.formConfig.messageLabel.lowercased()) } guard missing.isEmpty else { let list = missing.count == 1 ? missing[0] : missing[0 ..< missing.count - 1].joined(separator: ", ") + " and " + missing[missing.count - 1] - let alert = UIAlertController(title: "Error", message: "You must provide all required information. Please check the following fields: \(list).", preferredStyle: .alert) + let alert = UIAlertController(title: "Error", message: "You must provide all required information. Please check the following field\(missing.count > 1 ? "s" : ""): \(list).", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default)) present(alert, animated: config.animations) return @@ -334,6 +334,7 @@ class SentryUserFeedbackForm: UIViewController { button.backgroundColor = config.theme.submitBackground button.setTitleColor(config.theme.submitForeground, for: .normal) button.addTarget(self, action: #selector(submitFeedbackButtonTapped), for: .touchUpInside) + button.accessibilityIdentifier = "io.sentry.feedback.form.submit" return button }() @@ -341,6 +342,7 @@ class SentryUserFeedbackForm: UIViewController { let button = UIButton(frame: .zero) button.setTitle(config.formConfig.cancelButtonLabel, for: .normal) button.accessibilityLabel = config.formConfig.cancelButtonAccessibilityLabel + button.accessibilityIdentifier = "io.sentry.feedback.form.cancel" button.addTarget(self, action: #selector(cancelButtonTapped), for: .touchUpInside) return button }()