From 1c6e464a4c1ac41ec39fee7c57b9e1bff42a5069 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sun, 13 Sep 2020 13:19:56 -0400 Subject: [PATCH] Start Compose screen tests --- Tusker.xcodeproj/project.pbxproj | 4 + .../Compose/ComposeCurrentAccount.swift | 2 + .../Compose/ComposeHostingController.swift | 20 +++- Tusker/Screens/Compose/ComposeView.swift | 1 + TuskerUITests/ComposeTests.swift | 99 +++++++++++++++++++ 5 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 TuskerUITests/ComposeTests.swift diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index 8e8611a928..a4421c03cd 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -253,6 +253,7 @@ D6D3FDE024F41B8400FF50A5 /* ComposeContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D3FDDF24F41B8400FF50A5 /* ComposeContainerView.swift */; }; D6D3FDE224F46A8D00FF50A5 /* ComposeUIState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D3FDE124F46A8D00FF50A5 /* ComposeUIState.swift */; }; D6D4CC91250D2C3100FCCF8D /* UIAccessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4CC90250D2C3100FCCF8D /* UIAccessibility.swift */; }; + D6D4CC94250DB86A00FCCF8D /* ComposeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4CC93250DB86A00FCCF8D /* ComposeTests.swift */; }; D6D4DDD0212518A000E1C4BB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4DDCF212518A000E1C4BB /* AppDelegate.swift */; }; D6D4DDD7212518A200E1C4BB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D6D4DDD6212518A200E1C4BB /* Assets.xcassets */; }; D6D4DDDA212518A200E1C4BB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D6D4DDD8212518A200E1C4BB /* LaunchScreen.storyboard */; }; @@ -575,6 +576,7 @@ D6D3FDDF24F41B8400FF50A5 /* ComposeContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeContainerView.swift; sourceTree = ""; }; D6D3FDE124F46A8D00FF50A5 /* ComposeUIState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeUIState.swift; sourceTree = ""; }; D6D4CC90250D2C3100FCCF8D /* UIAccessibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAccessibility.swift; sourceTree = ""; }; + D6D4CC93250DB86A00FCCF8D /* ComposeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeTests.swift; sourceTree = ""; }; D6D4DDCC212518A000E1C4BB /* Tusker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Tusker.app; sourceTree = BUILT_PRODUCTS_DIR; }; D6D4DDCF212518A000E1C4BB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; D6D4DDD6212518A200E1C4BB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -1387,6 +1389,7 @@ D6D4DDEF212518A200E1C4BB /* TuskerUITests.swift */, D65F613323AEAB6600F3CFD3 /* OnboardingTests.swift */, D6A5BB2C23BBA9C4003BF21D /* MyProfileTests.swift */, + D6D4CC93250DB86A00FCCF8D /* ComposeTests.swift */, D6A5BB2623BAC88E003BF21D /* Preferences */, D6D4DDF1212518A200E1C4BB /* Info.plist */, ); @@ -1950,6 +1953,7 @@ D6A5BB2F23BBAC97003BF21D /* DelegatingResponse.swift in Sources */, D6A5BB2D23BBA9C4003BF21D /* MyProfileTests.swift in Sources */, D6A5BB2B23BAEF61003BF21D /* APIMocks.swift in Sources */, + D6D4CC94250DB86A00FCCF8D /* ComposeTests.swift in Sources */, D6D4DDF0212518A200E1C4BB /* TuskerUITests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Tusker/Screens/Compose/ComposeCurrentAccount.swift b/Tusker/Screens/Compose/ComposeCurrentAccount.swift index 49121d1236..82da83def9 100644 --- a/Tusker/Screens/Compose/ComposeCurrentAccount.swift +++ b/Tusker/Screens/Compose/ComposeCurrentAccount.swift @@ -19,6 +19,8 @@ struct ComposeCurrentAccount: View { var body: some View { HStack(alignment: .top) { ComposeAvatarImageView(url: account.avatar) + .accessibility(label: Text("\(account.displayName) avatar")) + VStack(alignment: .leading) { AccountDisplayNameLabel(account: mastodonController.persistentContainer.account(for: account.id)!, fontSize: 20) .lineLimit(1) diff --git a/Tusker/Screens/Compose/ComposeHostingController.swift b/Tusker/Screens/Compose/ComposeHostingController.swift index 0e3c2b6557..5a9665770c 100644 --- a/Tusker/Screens/Compose/ComposeHostingController.swift +++ b/Tusker/Screens/Compose/ComposeHostingController.swift @@ -85,8 +85,22 @@ class ComposeHostingController: UIHostingController { super.viewWillAppear(animated) // can't do this in viewDidLoad because viewDidLoad isn't called for UIHostingController - if mainToolbar.superview == nil { - view.addSubview(mainToolbar) +// if mainToolbar.superview == nil { +// view.addSubview(mainToolbar) +// NSLayoutConstraint.activate([ +// mainToolbar.leadingAnchor.constraint(equalTo: view.leadingAnchor), +// mainToolbar.trailingAnchor.constraint(equalTo: view.trailingAnchor), +// // use the top anchor of the toolbar so our additionalSafeAreaInsets (which has the bottom as the toolbar height) don't affect it +// mainToolbar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), +// ]) +// } + } + + override func didMove(toParent parent: UIViewController?) { + super.didMove(toParent: parent) + + if let parent = parent { + parent.view.addSubview(mainToolbar) NSLayoutConstraint.activate([ mainToolbar.leadingAnchor.constraint(equalTo: view.leadingAnchor), mainToolbar.trailingAnchor.constraint(equalTo: view.trailingAnchor), @@ -108,6 +122,7 @@ class ComposeHostingController: UIHostingController { private func createToolbar() -> UIToolbar { let toolbar = UIToolbar() toolbar.translatesAutoresizingMaskIntoConstraints = false + toolbar.isAccessibilityElement = true let visibilityAction: Selector? if #available(iOS 14.0, *) { @@ -204,6 +219,7 @@ class ComposeHostingController: UIHostingController { private func visibilityChanged(_ newVisibility: Status.Visibility) { for item in visibilityBarButtonItems { item.image = UIImage(systemName: newVisibility.imageName) + item.image!.accessibilityLabel = String(format: NSLocalizedString("Visibility: %@", comment: "compose visiblity accessibility label"), draft.visibility.displayName) item.accessibilityLabel = String(format: NSLocalizedString("Visibility: %@", comment: "compose visiblity accessibility label"), draft.visibility.displayName) if #available(iOS 14.0, *) { let elements = Status.Visibility.allCases.map { (visibility) -> UIMenuElement in diff --git a/Tusker/Screens/Compose/ComposeView.swift b/Tusker/Screens/Compose/ComposeView.swift index 7fa549837c..604acdefb5 100644 --- a/Tusker/Screens/Compose/ComposeView.swift +++ b/Tusker/Screens/Compose/ComposeView.swift @@ -117,6 +117,7 @@ struct ComposeView: View { Text(verbatim: charactersRemaining.description) .foregroundColor(charactersRemaining < 0 ? .red : .secondary) .font(Font.body.monospacedDigit()) + .accessibility(label: Text(charactersRemaining < 0 ? "\(-charactersRemaining) characters too many" : "\(charactersRemaining) characters remaining")) }.frame(height: 50) } diff --git a/TuskerUITests/ComposeTests.swift b/TuskerUITests/ComposeTests.swift new file mode 100644 index 0000000000..9a6f64199a --- /dev/null +++ b/TuskerUITests/ComposeTests.swift @@ -0,0 +1,99 @@ +// +// ComposeTests.swift +// TuskerUITests +// +// Created by Shadowfacts on 9/12/20. +// Copyright © 2020 Shadowfacts. All rights reserved. +// + +import XCTest +import Pachyderm + +class ComposeTests: TuskerUITests { + + override func setUp() { + super.setUp() + + router.allRoutes() + + app.launchEnvironment["UI_TESTING_LOGIN"] = "true" + app.launch() + + app.tabBars.buttons["Compose"].tap() + } + + func testCurrentAccount() { + XCTAssertTrue(app.images["Admin Account avatar"].exists, "avatar image exists") + XCTAssertTrue(app.staticTexts["Admin Account"].exists, "display name label exists") + XCTAssertTrue(app.staticTexts["@admin"].exists, "acct label exists") + } + + func testBodyPlaceholder() { + XCTAssertTrue(app.staticTexts["What's on your mind?"].exists, "placeholder exists") + app.textViews.firstMatch.typeText("Blah") + XCTAssertFalse(app.staticTexts["What's on your mind?"].exists, "placeholder does not exist") + } + + func testCharacterCounter() { + XCTAssertTrue(app.staticTexts["500 characters remaining"].exists, "initial character count is 500") + let textView = app.textViews.firstMatch + + let fragments = [ + "Hello", + "World", + "@admin", + "@admin@example.com", + "https://foo.example.com/?bar=baz#qux", + ] + + var remaining = 500 + for s in fragments { + let length = CharacterCounter.count(text: s) + // add 1 for newline + remaining -= length + 1 + + textView.typeText(s + "\n") + XCTAssertTrue(app.staticTexts["\(remaining) characters remaining"].exists, "subsequent character count is \(remaining)") + } + } + +// func testToolbarSwitching() { +// // text view is automatically focused, so unfocus +// app.swipeDown() +// +// XCTAssertEqual(app.toolbars.count, 1) +// XCTAssertEqual(app.toolbars.buttons.count, 3) +// XCTAssertTrue(app.toolbars.buttons["CW"].exists) +// XCTAssertTrue(app.toolbars.buttons["Visibility: Public"].exists) +// XCTAssertTrue(app.toolbars.buttons["Drafts"].exists) +// +// } + + func testContentWarning() { + let toolbar = app.toolbars.firstMatch + XCTAssertTrue(toolbar.waitForExistence(timeout: 0.1)) + XCTAssertEqual(app.toolbars.count, 1, "there is only 1 toolbar") + let cwButton = toolbar.buttons["CW"] + XCTAssertTrue(cwButton.exists, "the CW button exists") + + cwButton.tap() + let cwField = app.textFields.firstMatch + XCTAssertEqual(cwField.placeholderValue, "Write your warning here") + + XCTAssertTrue(app.staticTexts["500 characters remaining"].exists) + cwField.tap() + let str: String + // on iOS 14, the first type text is typed into a SwiftUI TextField, the 2nd character is inexplicably dropped >.< + if #available(iOS 14.0, *) { + str = "fooo" + } else { + str = "foo" + } + cwField.typeText(str) + XCTAssertTrue(app.staticTexts["497 characters remaining"].exists) + // CharacterCounter is not used => '@' is counted + cwField.typeText(" @bar") + XCTAssertTrue(app.staticTexts["492 characters remaining"].exists) + } + +}