From bcd978db652583b826806360e4f2a85730b61398 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:48:37 +0800 Subject: [PATCH 1/7] add scheduled post ui test --- .../mappings/wpcom/me/rest_v11_me_sites.json | 2 +- .../JetpackScreenshotGeneration.swift | 2 +- WordPress/UITests/Flows/LoginFlow.swift | 4 +- .../UITests/Tests/EditorGutenbergTests.swift | 4 +- WordPress/UITests/Tests/PostTests.swift | 36 ++++++++ WordPress/UITests/WPUITestCredentials.swift | 1 + .../Screens/Editor/BlockEditorScreen.swift | 36 ++++++-- .../Editor/EditorNoticeComponent.swift | 1 + .../Screens/Editor/EditorPostSettings.swift | 87 ++++++++++++++++--- .../Screens/MySiteScreen.swift | 7 +- .../Screens/PostsScreen.swift | 51 +++++++++-- .../Screens/TabNavComponent.swift | 2 +- WordPress/WordPress.xcodeproj/project.pbxproj | 6 ++ .../WordPressScreenshotGeneration.swift | 6 +- 14 files changed, 202 insertions(+), 43 deletions(-) create mode 100644 WordPress/UITests/Tests/PostTests.swift diff --git a/API-Mocks/WordPressMocks/src/main/assets/mocks/mappings/wpcom/me/rest_v11_me_sites.json b/API-Mocks/WordPressMocks/src/main/assets/mocks/mappings/wpcom/me/rest_v11_me_sites.json index 68c2dc010113..1a523d55e311 100644 --- a/API-Mocks/WordPressMocks/src/main/assets/mocks/mappings/wpcom/me/rest_v11_me_sites.json +++ b/API-Mocks/WordPressMocks/src/main/assets/mocks/mappings/wpcom/me/rest_v11_me_sites.json @@ -359,7 +359,7 @@ "ID": 181977606, "description": "Site with everything enabled", "name": "Weekend Bakes", - "URL": "yourjetpack.blog", + "URL": "weekendbakesblog.wordpress.com", "user_can_manage": false, "capabilities": { "edit_pages": true, diff --git a/WordPress/JetpackScreenshotGeneration/JetpackScreenshotGeneration.swift b/WordPress/JetpackScreenshotGeneration/JetpackScreenshotGeneration.swift index 6b0c6cbc9e71..00ecd1c24c0d 100644 --- a/WordPress/JetpackScreenshotGeneration/JetpackScreenshotGeneration.swift +++ b/WordPress/JetpackScreenshotGeneration/JetpackScreenshotGeneration.swift @@ -25,7 +25,7 @@ class JetpackScreenshotGeneration: XCTestCase { try LoginFlow.login(email: WPUITestCredentials.testWPcomUserEmail, password: WPUITestCredentials.testWPcomPassword, - selectedSiteTitle: "yourjetpack.blog") + selectedSiteTitle: "weekendbakesblog.wordpress.com") } override func tearDown() { diff --git a/WordPress/UITests/Flows/LoginFlow.swift b/WordPress/UITests/Flows/LoginFlow.swift index 794be5eff30c..8de93c5d448e 100644 --- a/WordPress/UITests/Flows/LoginFlow.swift +++ b/WordPress/UITests/Flows/LoginFlow.swift @@ -24,13 +24,13 @@ class LoginFlow { // Login with WP site via Site Address. @discardableResult - static func login(siteUrl: String, email: String, password: String) throws -> MySiteScreen { + static func login(siteUrl: String, email: String, password: String, title: String? = nil) throws -> MySiteScreen { return try PrologueScreen() .selectSiteAddress() .proceedWithWP(siteUrl: siteUrl) .proceedWith(email: email) .proceedWithValidPassword() - .continueWithSelectedSite() + .continueWithSelectedSite(title: title) } // Login with self-hosted site via Site Address. diff --git a/WordPress/UITests/Tests/EditorGutenbergTests.swift b/WordPress/UITests/Tests/EditorGutenbergTests.swift index 0dabfb3c009d..231d009717c4 100644 --- a/WordPress/UITests/Tests/EditorGutenbergTests.swift +++ b/WordPress/UITests/Tests/EditorGutenbergTests.swift @@ -12,7 +12,7 @@ class EditorGutenbergTests: XCTestCase { ) try TabNavComponent() - .gotoBlockEditorScreen() + .goToBlockEditorScreen() } override func tearDownWithError() throws { @@ -49,7 +49,7 @@ class EditorGutenbergTests: XCTestCase { .selectCategory(name: category) .addTag(name: tag) .closePostSettings() - try BlockEditorScreen().publish() + .publish() .viewPublishedPost(withTitle: title) .verifyEpilogueDisplays(postTitle: title, siteAddress: WPUITestCredentials.testWPcomSitePrimaryAddress) .done() diff --git a/WordPress/UITests/Tests/PostTests.swift b/WordPress/UITests/Tests/PostTests.swift new file mode 100644 index 000000000000..1edabff11e4b --- /dev/null +++ b/WordPress/UITests/Tests/PostTests.swift @@ -0,0 +1,36 @@ +import UITestsFoundation +import XCTest + +class PostTests: XCTestCase { + override func setUpWithError() throws { + setUpTestSuite() + try LoginFlow.login( + siteUrl: WPUITestCredentials.testWPcomSiteAddress, + email: WPUITestCredentials.testWPcomUserEmail, + password: WPUITestCredentials.testWPcomPassword, + title: WPUITestCredentials.testWPcomSiteForScheduledPost + ) + + try TabNavComponent() + .goToBlockEditorScreen() + } + + override func tearDownWithError() throws { + takeScreenshotOfFailedTest() + } + + let title = "Scheduled Post" + + func testCreateScheduledPost() throws { + try BlockEditorScreen() + .enterTextInTitle(text: title) + .openPostSettings() + .updatePublishDate() + .closePublishDateSelector() + .closePostSettings() + .schedulePost() + .viewPublishedPost(withTitle: title) + .verifyEpilogueDisplays(postTitle: title, siteAddress: WPUITestCredentials.testWPcomSiteForScheduledPost) + .done() + } +} diff --git a/WordPress/UITests/WPUITestCredentials.swift b/WordPress/UITests/WPUITestCredentials.swift index bb64302b01e7..f92a07c69aba 100644 --- a/WordPress/UITests/WPUITestCredentials.swift +++ b/WordPress/UITests/WPUITestCredentials.swift @@ -7,6 +7,7 @@ struct WPUITestCredentials { static let testWPcomPassword: String = "pw" static let testWPcomSiteAddress: String = "tricountyrealestate.wordpress.com" static let testWPcomSitePrimaryAddress: String = "tricountyrealestate.wordpress.com" + static let testWPcomSiteForScheduledPost: String = "weekendbakesblog.wordpress.com" static let selfHostedUsername: String = "e2eflowtestingmobile" static let selfHostedPassword: String = "mocked_password" static let selfHostedSiteAddress: String = "\(WireMock.URL().absoluteString)" diff --git a/WordPress/UITestsFoundation/Screens/Editor/BlockEditorScreen.swift b/WordPress/UITestsFoundation/Screens/Editor/BlockEditorScreen.swift index fbb2d566ca6f..e252daeffba4 100644 --- a/WordPress/UITestsFoundation/Screens/Editor/BlockEditorScreen.swift +++ b/WordPress/UITestsFoundation/Screens/Editor/BlockEditorScreen.swift @@ -174,20 +174,39 @@ public class BlockEditorScreen: ScreenObject { } public func publish() throws -> EditorNoticeComponent { - let publishButton = app.buttons["Publish"] - let publishNowButton = app.buttons["Publish Now"] + return try post(action: "Publish") + } + + public func schedulePost() throws -> EditorNoticeComponent { + return try post(action: "Schedule") + } + + private func post(action: String) throws -> EditorNoticeComponent { + let postButton = app.buttons[action] + let postNowButton = app.buttons["\(action) Now"] var tries = 0 // This loop to check for Publish Now Button is an attempt to confirm that the publishButton.tap() call took effect. // The tests would fail sometimes in the pipeline with no apparent reason. repeat { - publishButton.tap() + postButton.tap() tries += 1 - } while !publishNowButton.waitForIsHittable(timeout: 3) && tries <= 3 - try confirmPublish() + } while !postNowButton.waitForIsHittable(timeout: 3) && tries <= 3 + try confirmPublish(button: postNowButton) + + let actionInNotice: String - return try EditorNoticeComponent(withNotice: "Post published", andAction: "View") + if action == "Schedule" { + actionInNotice = "scheduled" + } else if action == "Publish" { + actionInNotice = "published" + } else { + throw NSError(domain: "InvalidAction", code: 0, userInfo: [NSLocalizedDescriptionKey: "Invalid action: \(action)"]) + } + + return try EditorNoticeComponent(withNotice: "Post \(actionInNotice)", andAction: "View") } + @discardableResult public func openPostSettings() throws -> EditorPostSettings { moreButton.tap() let postSettingsButton = app.buttons["Post Settings"] // Uses a localized string @@ -276,12 +295,11 @@ public class BlockEditorScreen: ScreenObject { .selectAlbum(atIndex: 0) } - private func confirmPublish() throws { + private func confirmPublish(button: XCUIElement) throws { if FancyAlertComponent.isLoaded() { try FancyAlertComponent().acceptAlert() } else { - let publishNowButton = app.buttons["Publish Now"] - publishNowButton.tap() + button.tap() dismissBloggingRemindersAlertIfNeeded() } } diff --git a/WordPress/UITestsFoundation/Screens/Editor/EditorNoticeComponent.swift b/WordPress/UITestsFoundation/Screens/Editor/EditorNoticeComponent.swift index 21d96321b673..571be7d16069 100644 --- a/WordPress/UITestsFoundation/Screens/Editor/EditorNoticeComponent.swift +++ b/WordPress/UITestsFoundation/Screens/Editor/EditorNoticeComponent.swift @@ -23,6 +23,7 @@ public class EditorNoticeComponent: ScreenObject { ) } + @discardableResult public func viewPublishedPost(withTitle postTitle: String) throws -> EditorPublishEpilogueScreen { // The publish notice has a joined accessibility label equal to: title + message // (the postTitle). It does not seem possible to target the specific postTitle label diff --git a/WordPress/UITestsFoundation/Screens/Editor/EditorPostSettings.swift b/WordPress/UITestsFoundation/Screens/Editor/EditorPostSettings.swift index 0ab2e404d0f4..15d40363c3ab 100644 --- a/WordPress/UITestsFoundation/Screens/Editor/EditorPostSettings.swift +++ b/WordPress/UITestsFoundation/Screens/Editor/EditorPostSettings.swift @@ -1,20 +1,57 @@ -import Nimble import ScreenObject import XCTest public class EditorPostSettings: ScreenObject { - // expectedElement comes from the superclass and gets the first expectedElementGetters result - var settingsTable: XCUIElement { expectedElement } + let settingsTableGetter: (XCUIApplication) -> XCUIElement = { + $0.tables["SettingsTable"] + } + + let categoriesSectionGetter: (XCUIApplication) -> XCUIElement = { + $0.cells["Categories"] + } + + let tagsSectionGetter: (XCUIApplication) -> XCUIElement = { + $0.cells["Tags"] + } + + let featuredImageButtonGetter: (XCUIApplication) -> XCUIElement = { + $0.cells["SetFeaturedImage"] + } - var categoriesSection: XCUIElement { settingsTable.cells["Categories"] } - var tagsSection: XCUIElement { settingsTable.cells["Tags"] } - var featuredImageButton: XCUIElement { settingsTable.cells["SetFeaturedImage"] } - var currentFeaturedImage: XCUIElement { settingsTable.cells["CurrentFeaturedImage"] } + let currentFeaturedImageGetter: (XCUIApplication) -> XCUIElement = { + $0.cells["CurrentFeaturedImage"] + } + + let publishDateButtonGetter: (XCUIApplication) -> XCUIElement = { + $0.staticTexts["Publish Date"] + } + + let dateSelectorGetter: (XCUIApplication) -> XCUIElement = { + $0.staticTexts["Immediately"] + } + + let dismissPopoverButtonGetter: (XCUIApplication) -> XCUIElement = { + $0.buttons["PopoverDismissRegion"] + } - init(app: XCUIApplication = XCUIApplication()) throws { + let doneButtonGetter: (XCUIApplication) -> XCUIElement = { + $0.buttons["Done"] + } + + var settingsTable: XCUIElement { settingsTableGetter(app) } + var categoriesSection: XCUIElement { categoriesSectionGetter(app) } + var tagsSection: XCUIElement { tagsSectionGetter(app) } + var featuredImageButton: XCUIElement { featuredImageButtonGetter(app) } + var currentFeaturedImage: XCUIElement { currentFeaturedImageGetter(app) } + var publishDateButton: XCUIElement { publishDateButtonGetter(app) } + var dateSelector: XCUIElement { dateSelectorGetter(app) } + var dismissPopoverButton: XCUIElement { dismissPopoverButtonGetter(app) } + var doneButton: XCUIElement { doneButtonGetter(app) } + + public init(app: XCUIApplication = XCUIApplication()) throws { try super.init( - expectedElementGetters: [ { $0.tables["SettingsTable"] } ], + expectedElementGetters: [ settingsTableGetter ], app: app ) } @@ -77,12 +114,40 @@ public class EditorPostSettings: ScreenObject { return try EditorPostSettings() } - /// - Note: Returns `Void` because the return screen depends on which editor the user is in. - public func closePostSettings() { + @discardableResult + public func closePostSettings() throws -> BlockEditorScreen { navigateBack() + + return try BlockEditorScreen() } public static func isLoaded() -> Bool { return (try? EditorPostSettings().isLoaded) ?? false } + + @discardableResult + public func updatePublishDate() -> Self { + publishDateButton.tap() + dateSelector.tap() + + let predicate = NSPredicate(format: "label CONTAINS[c] 'AM' OR label CONTAINS[c] 'PM'") + let timeButton = app.buttons.element(matching: predicate) + timeButton.tap() + + let currentHour = app.pickerWheels.firstMatch.value as? String + let currentHourValue = Int(currentHour?.components(separatedBy: " ").first ?? "") ?? 0 + let newHourValue = currentHourValue + 2 + + app.pickerWheels.firstMatch.adjust(toPickerWheelValue: "\(newHourValue)") + + dismissPopoverButton.tap() + doneButton.tap() + + return self + } + + public func closePublishDateSelector() -> Self { + navigateBack() + return self + } } diff --git a/WordPress/UITestsFoundation/Screens/MySiteScreen.swift b/WordPress/UITestsFoundation/Screens/MySiteScreen.swift index 6178c4e7cae8..fe72a8e7b273 100644 --- a/WordPress/UITestsFoundation/Screens/MySiteScreen.swift +++ b/WordPress/UITestsFoundation/Screens/MySiteScreen.swift @@ -166,12 +166,7 @@ public class MySiteScreen: ScreenObject { return try JetpackBackupScreen() } - public func gotoPostsScreen() throws -> PostsScreen { - // A hack for iPad, because sometimes tapping "posts" doesn't load it the first time - if XCUIDevice.isPad { - mediaButton.tap() - } - + public func goToPostsScreen() throws -> PostsScreen { postsButtonGetter(app).tap() return try PostsScreen() } diff --git a/WordPress/UITestsFoundation/Screens/PostsScreen.swift b/WordPress/UITestsFoundation/Screens/PostsScreen.swift index a74d99b906bd..c1dab0cf4375 100644 --- a/WordPress/UITestsFoundation/Screens/PostsScreen.swift +++ b/WordPress/UITestsFoundation/Screens/PostsScreen.swift @@ -10,9 +10,40 @@ public class PostsScreen: ScreenObject { private var currentlyFilteredPostStatus: PostStatus = .published - init(app: XCUIApplication = XCUIApplication()) throws { + let postsTableGetter: (XCUIApplication) -> XCUIElement = { + $0.tables["PostsTable"] + } + + let publishedButtonGetter: (XCUIApplication) -> XCUIElement = { + $0.buttons["published"] + } + + let draftsButtonGetter: (XCUIApplication) -> XCUIElement = { + $0.buttons["drafts"] + } + + let scheduledButtonGetter: (XCUIApplication) -> XCUIElement = { + $0.buttons["scheduled"] + } + + let createPostButtonGetter: (XCUIApplication) -> XCUIElement = { + $0.buttons["Create Post Button"] + } + + let autosaveAlertGetter: (XCUIApplication) -> XCUIElement = { + $0.alerts["autosave-options-alert"] + } + + var postsTable: XCUIElement { postsTableGetter(app) } + var publishedButton: XCUIElement { publishedButtonGetter(app) } + var draftsButton: XCUIElement { draftsButtonGetter(app) } + var scheduledButton: XCUIElement { scheduledButtonGetter(app) } + var createPostButton: XCUIElement { createPostButtonGetter(app) } + var autosaveAlert: XCUIElement { autosaveAlertGetter(app) } + + public init(app: XCUIApplication = XCUIApplication()) throws { try super.init( - expectedElementGetters: [ { $0.tables["PostsTable"] } ], + expectedElementGetters: [postsTableGetter], app: app ) showOnly(.published) @@ -22,9 +53,9 @@ public class PostsScreen: ScreenObject { public func showOnly(_ status: PostStatus) -> PostsScreen { switch status { case .published: - app.buttons["published"].tap() + publishedButton.tap() case .drafts: - app.buttons["drafts"].tap() + draftsButton.tap() } currentlyFilteredPostStatus = status @@ -51,12 +82,18 @@ public class PostsScreen: ScreenObject { return EditorScreen() } + public func goToScheduledPost() -> Self { + scheduledButton.tap() + createPostButton.tap() + + return self + } + /// If there are two versions of a local post, the app will ask which version we want to use when editing. /// We always want to use the local version (which is currently the first option) private func dismissAutosaveDialogIfNeeded() { - let autosaveDialog = app.alerts["autosave-options-alert"] - if autosaveDialog.exists { - autosaveDialog.buttons.firstMatch.tap() + if autosaveAlert.exists { + autosaveAlert.buttons.firstMatch.tap() } } } diff --git a/WordPress/UITestsFoundation/Screens/TabNavComponent.swift b/WordPress/UITestsFoundation/Screens/TabNavComponent.swift index 00e05583f4a6..39284b7ddc2f 100644 --- a/WordPress/UITestsFoundation/Screens/TabNavComponent.swift +++ b/WordPress/UITestsFoundation/Screens/TabNavComponent.swift @@ -56,7 +56,7 @@ public class TabNavComponent: ScreenObject { } @discardableResult - public func gotoBlockEditorScreen() throws -> BlockEditorScreen { + public func goToBlockEditorScreen() throws -> BlockEditorScreen { try goToMySiteScreen() .goToCreateSheet() .goToBlogPost() diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 1437352faa27..4f66a7a15a93 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -1713,6 +1713,8 @@ 8031F34B292FF46E00E8F95E /* ExtensionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 800035C0292307E8007D2D26 /* ExtensionConfiguration.swift */; }; 8031F34C29302A2500E8F95E /* ExtensionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 800035C229230A0B007D2D26 /* ExtensionConfiguration.swift */; }; 8031F34D29302C8100E8F95E /* ExtensionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 800035C0292307E8007D2D26 /* ExtensionConfiguration.swift */; }; + 80379C6E2A5C0D8F00D924AC /* PostTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80379C6D2A5C0D8F00D924AC /* PostTests.swift */; }; + 80379C6F2A5C0D8F00D924AC /* PostTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80379C6D2A5C0D8F00D924AC /* PostTests.swift */; }; 803BB9792959543D00B3F6D6 /* RootViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 803BB9782959543D00B3F6D6 /* RootViewCoordinator.swift */; }; 803BB97A2959543D00B3F6D6 /* RootViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 803BB9782959543D00B3F6D6 /* RootViewCoordinator.swift */; }; 803BB97C2959559500B3F6D6 /* RootViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 803BB97B2959559500B3F6D6 /* RootViewPresenter.swift */; }; @@ -7321,6 +7323,7 @@ 801D9519291AC0B00051993E /* OverlayFrequencyTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverlayFrequencyTracker.swift; sourceTree = ""; }; 801D951C291ADB7E0051993E /* OverlayFrequencyTrackerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverlayFrequencyTrackerTests.swift; sourceTree = ""; }; 80293CF6284450AD0083F946 /* WordPress-Swift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "WordPress-Swift.h"; sourceTree = ""; }; + 80379C6D2A5C0D8F00D924AC /* PostTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostTests.swift; sourceTree = ""; }; 803BB9782959543D00B3F6D6 /* RootViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootViewCoordinator.swift; sourceTree = ""; }; 803BB97B2959559500B3F6D6 /* RootViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootViewPresenter.swift; sourceTree = ""; }; 803BB97F295957CF00B3F6D6 /* WPTabBarController+RootViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WPTabBarController+RootViewPresenter.swift"; sourceTree = ""; }; @@ -15712,6 +15715,7 @@ EAD2BF4127594DAB00A847BB /* StatsTests.swift */, D82E087429EEB0B00098F500 /* DashboardTests.swift */, 01281E9B2A051EEA00464F8F /* MenuNavigationTests.swift */, + 80379C6D2A5C0D8F00D924AC /* PostTests.swift */, ); path = Tests; sourceTree = ""; @@ -23791,6 +23795,7 @@ buildActionMask = 2147483647; files = ( EA14532929AD874C001F3143 /* MainNavigationTests.swift in Sources */, + 80379C6F2A5C0D8F00D924AC /* PostTests.swift in Sources */, EA14532A29AD874C001F3143 /* ReaderTests.swift in Sources */, EA14532B29AD874C001F3143 /* EditorAztecTests.swift in Sources */, D82E087629EEB0B00098F500 /* DashboardTests.swift in Sources */, @@ -25747,6 +25752,7 @@ buildActionMask = 2147483647; files = ( FFA0B7D71CAC1F9F00533B9D /* MainNavigationTests.swift in Sources */, + 80379C6E2A5C0D8F00D924AC /* PostTests.swift in Sources */, EAB10E4027487F5D000DA4C1 /* ReaderTests.swift in Sources */, BED4D8301FF11DEF00A11345 /* EditorAztecTests.swift in Sources */, D82E087529EEB0B00098F500 /* DashboardTests.swift in Sources */, diff --git a/WordPress/WordPressScreenshotGeneration/WordPressScreenshotGeneration.swift b/WordPress/WordPressScreenshotGeneration/WordPressScreenshotGeneration.swift index 379e0109d919..a186fe387ab1 100644 --- a/WordPress/WordPressScreenshotGeneration/WordPressScreenshotGeneration.swift +++ b/WordPress/WordPressScreenshotGeneration/WordPressScreenshotGeneration.swift @@ -38,7 +38,7 @@ class WordPressScreenshotGeneration: XCTestCase { let postList = try MySiteScreen() .showSiteSwitcher() .switchToSite(withTitle: "fourpawsdoggrooming.wordpress.com") - .gotoPostsScreen() + .goToPostsScreen() .showOnly(.drafts) let postEditorScreenshot = try postList.selectPost(withSlug: "our-services") @@ -58,8 +58,8 @@ class WordPressScreenshotGeneration: XCTestCase { if XCUIDevice.isPad { let ipadScreenshot = try MySiteScreen() .showSiteSwitcher() - .switchToSite(withTitle: "yourjetpack.blog") - .gotoPostsScreen() + .switchToSite(withTitle: "weekendbakesblog.wordpress.com") + .goToPostsScreen() .showOnly(.drafts) .selectPost(withSlug: "easy-blueberry-muffins") try BlockEditorScreen().selectBlock(containingText: "Ingredients") From c6a0661d0954cd1750796ce6ec2535f9d429ba28 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Mon, 10 Jul 2023 19:06:22 +0800 Subject: [PATCH 2/7] fix hound errors --- WordPress/UITests/Tests/PostTests.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/WordPress/UITests/Tests/PostTests.swift b/WordPress/UITests/Tests/PostTests.swift index 1edabff11e4b..30a961b64ed4 100644 --- a/WordPress/UITests/Tests/PostTests.swift +++ b/WordPress/UITests/Tests/PostTests.swift @@ -3,7 +3,9 @@ import XCTest class PostTests: XCTestCase { override func setUpWithError() throws { + try super.setUpWithError() setUpTestSuite() + try LoginFlow.login( siteUrl: WPUITestCredentials.testWPcomSiteAddress, email: WPUITestCredentials.testWPcomUserEmail, @@ -16,6 +18,7 @@ class PostTests: XCTestCase { } override func tearDownWithError() throws { + try super.tearDownWithError() takeScreenshotOfFailedTest() } From f33dc987274622afc9b648ec46985ffc3d82ce8f Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Mon, 10 Jul 2023 19:10:37 +0800 Subject: [PATCH 3/7] properly handle hour value --- .../Screens/Editor/EditorPostSettings.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WordPress/UITestsFoundation/Screens/Editor/EditorPostSettings.swift b/WordPress/UITestsFoundation/Screens/Editor/EditorPostSettings.swift index 15d40363c3ab..d4ef317367c0 100644 --- a/WordPress/UITestsFoundation/Screens/Editor/EditorPostSettings.swift +++ b/WordPress/UITestsFoundation/Screens/Editor/EditorPostSettings.swift @@ -136,7 +136,10 @@ public class EditorPostSettings: ScreenObject { let currentHour = app.pickerWheels.firstMatch.value as? String let currentHourValue = Int(currentHour?.components(separatedBy: " ").first ?? "") ?? 0 - let newHourValue = currentHourValue + 2 + var newHourValue = currentHourValue + 2 + + // To handle hour as it is displayed in 12-hour format not 24-hour + if newHourValue > 12 { newHourValue = newHourValue - 12} app.pickerWheels.firstMatch.adjust(toPickerWheelValue: "\(newHourValue)") From 19d2b7ea2c21356e32c0b2bd397d90652659d9d3 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Tue, 11 Jul 2023 11:11:45 +0800 Subject: [PATCH 4/7] rename for clarity and remove unused --- .../Screens/Editor/BlockEditorScreen.swift | 6 +++--- .../Screens/Editor/EditorPostSettings.swift | 4 +++- WordPress/UITestsFoundation/Screens/PostsScreen.swift | 7 ------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/WordPress/UITestsFoundation/Screens/Editor/BlockEditorScreen.swift b/WordPress/UITestsFoundation/Screens/Editor/BlockEditorScreen.swift index e252daeffba4..598441ae64b7 100644 --- a/WordPress/UITestsFoundation/Screens/Editor/BlockEditorScreen.swift +++ b/WordPress/UITestsFoundation/Screens/Editor/BlockEditorScreen.swift @@ -185,13 +185,13 @@ public class BlockEditorScreen: ScreenObject { let postButton = app.buttons[action] let postNowButton = app.buttons["\(action) Now"] var tries = 0 - // This loop to check for Publish Now Button is an attempt to confirm that the publishButton.tap() call took effect. + // This loop to check for Publish/Schedule Now Button is an attempt to confirm that the postButton.tap() call took effect. // The tests would fail sometimes in the pipeline with no apparent reason. repeat { postButton.tap() tries += 1 } while !postNowButton.waitForIsHittable(timeout: 3) && tries <= 3 - try confirmPublish(button: postNowButton) + try confirmPost(button: postNowButton) let actionInNotice: String @@ -295,7 +295,7 @@ public class BlockEditorScreen: ScreenObject { .selectAlbum(atIndex: 0) } - private func confirmPublish(button: XCUIElement) throws { + private func confirmPost(button: XCUIElement) throws { if FancyAlertComponent.isLoaded() { try FancyAlertComponent().acceptAlert() } else { diff --git a/WordPress/UITestsFoundation/Screens/Editor/EditorPostSettings.swift b/WordPress/UITestsFoundation/Screens/Editor/EditorPostSettings.swift index d4ef317367c0..b47d5c6b62ff 100644 --- a/WordPress/UITestsFoundation/Screens/Editor/EditorPostSettings.swift +++ b/WordPress/UITestsFoundation/Screens/Editor/EditorPostSettings.swift @@ -136,9 +136,11 @@ public class EditorPostSettings: ScreenObject { let currentHour = app.pickerWheels.firstMatch.value as? String let currentHourValue = Int(currentHour?.components(separatedBy: " ").first ?? "") ?? 0 + + // Set new hour to be 2 hours in the future var newHourValue = currentHourValue + 2 - // To handle hour as it is displayed in 12-hour format not 24-hour + // To handle hour as picker wheel displays time in 12-hour format if newHourValue > 12 { newHourValue = newHourValue - 12} app.pickerWheels.firstMatch.adjust(toPickerWheelValue: "\(newHourValue)") diff --git a/WordPress/UITestsFoundation/Screens/PostsScreen.swift b/WordPress/UITestsFoundation/Screens/PostsScreen.swift index c1dab0cf4375..f3225025f046 100644 --- a/WordPress/UITestsFoundation/Screens/PostsScreen.swift +++ b/WordPress/UITestsFoundation/Screens/PostsScreen.swift @@ -82,13 +82,6 @@ public class PostsScreen: ScreenObject { return EditorScreen() } - public func goToScheduledPost() -> Self { - scheduledButton.tap() - createPostButton.tap() - - return self - } - /// If there are two versions of a local post, the app will ask which version we want to use when editing. /// We always want to use the local version (which is currently the first option) private func dismissAutosaveDialogIfNeeded() { From 77e9d12fd989408a4130044747592c3138327617 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:11:11 +0800 Subject: [PATCH 5/7] mark test as automated, remove unnecessary @discardableResult tag --- WordPress/UITests/README.md | 2 +- .../Screens/Editor/EditorNoticeComponent.swift | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/WordPress/UITests/README.md b/WordPress/UITests/README.md index e2d66a9a0e5d..b792693a8c72 100644 --- a/WordPress/UITests/README.md +++ b/WordPress/UITests/README.md @@ -33,7 +33,7 @@ The following flows are covered/planned to be covered by UI tests. Tests that ar - [x] Add and Remove Featured Image - [x] Add Gallery Block - [x] Add Media Blocks (Image, Video and Audio) - - [ ] Create Scheduled Post + - [x] Create Scheduled Post - [ ] Pages: - [ ] Create Page from Layout - [ ] Create Blank Page diff --git a/WordPress/UITestsFoundation/Screens/Editor/EditorNoticeComponent.swift b/WordPress/UITestsFoundation/Screens/Editor/EditorNoticeComponent.swift index 571be7d16069..21d96321b673 100644 --- a/WordPress/UITestsFoundation/Screens/Editor/EditorNoticeComponent.swift +++ b/WordPress/UITestsFoundation/Screens/Editor/EditorNoticeComponent.swift @@ -23,7 +23,6 @@ public class EditorNoticeComponent: ScreenObject { ) } - @discardableResult public func viewPublishedPost(withTitle postTitle: String) throws -> EditorPublishEpilogueScreen { // The publish notice has a joined accessibility label equal to: title + message // (the postTitle). It does not seem possible to target the specific postTitle label From 57879d5e40c2a59c34e42622c6f43725629d38af Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Wed, 12 Jul 2023 11:49:50 +0800 Subject: [PATCH 6/7] rename vars for clarity --- WordPress/UITests/Flows/LoginFlow.swift | 4 +- .../UITests/Tests/EditorGutenbergTests.swift | 40 +++++++++---------- WordPress/UITests/Tests/PostTests.swift | 14 +++---- .../Editor/EditorPublishEpilogueScreen.swift | 2 +- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/WordPress/UITests/Flows/LoginFlow.swift b/WordPress/UITests/Flows/LoginFlow.swift index 8de93c5d448e..5a78c1696d31 100644 --- a/WordPress/UITests/Flows/LoginFlow.swift +++ b/WordPress/UITests/Flows/LoginFlow.swift @@ -24,13 +24,13 @@ class LoginFlow { // Login with WP site via Site Address. @discardableResult - static func login(siteUrl: String, email: String, password: String, title: String? = nil) throws -> MySiteScreen { + static func login(siteUrl: String, email: String, password: String, selectedSiteTitle: String? = nil) throws -> MySiteScreen { return try PrologueScreen() .selectSiteAddress() .proceedWithWP(siteUrl: siteUrl) .proceedWith(email: email) .proceedWithValidPassword() - .continueWithSelectedSite(title: title) + .continueWithSelectedSite(title: selectedSiteTitle) } // Login with self-hosted site via Site Address. diff --git a/WordPress/UITests/Tests/EditorGutenbergTests.swift b/WordPress/UITests/Tests/EditorGutenbergTests.swift index 231d009717c4..13b1e7f6de49 100644 --- a/WordPress/UITests/Tests/EditorGutenbergTests.swift +++ b/WordPress/UITests/Tests/EditorGutenbergTests.swift @@ -19,21 +19,21 @@ class EditorGutenbergTests: XCTestCase { takeScreenshotOfFailedTest() } - let title = "Rich post title" - let content = "Some text, and more text" + let postTitle = "Rich post title" + let postContent = "Some text, and more text" let videoUrlPath = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" let audioUrlPath = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4" func testTextPostPublish() throws { try BlockEditorScreen() - .enterTextInTitle(text: title) - .addParagraphBlock(withText: content) - .verifyContentStructure(blocks: 1, words: content.components(separatedBy: " ").count, characters: content.count) + .enterTextInTitle(text: postTitle) + .addParagraphBlock(withText: postContent) + .verifyContentStructure(blocks: 1, words: postContent.components(separatedBy: " ").count, characters: postContent.count) .publish() - .viewPublishedPost(withTitle: title) - .verifyEpilogueDisplays(postTitle: title, siteAddress: WPUITestCredentials.testWPcomSitePrimaryAddress) - .done() + .viewPublishedPost(withTitle: postTitle) + .verifyEpilogueDisplays(postTitle: postTitle, siteAddress: WPUITestCredentials.testWPcomSitePrimaryAddress) + .tapDone() } func testBasicPostPublishWithCategoryAndTag() throws { @@ -41,26 +41,26 @@ class EditorGutenbergTests: XCTestCase { let category = getCategory() let tag = getTag() try BlockEditorScreen() - .enterTextInTitle(text: title) - .addParagraphBlock(withText: content) + .enterTextInTitle(text: postTitle) + .addParagraphBlock(withText: postContent) .addImage() - .verifyContentStructure(blocks: 2, words: content.components(separatedBy: " ").count, characters: content.count) + .verifyContentStructure(blocks: 2, words: postContent.components(separatedBy: " ").count, characters: postContent.count) .openPostSettings() .selectCategory(name: category) .addTag(name: tag) .closePostSettings() .publish() - .viewPublishedPost(withTitle: title) - .verifyEpilogueDisplays(postTitle: title, siteAddress: WPUITestCredentials.testWPcomSitePrimaryAddress) - .done() + .viewPublishedPost(withTitle: postTitle) + .verifyEpilogueDisplays(postTitle: postTitle, siteAddress: WPUITestCredentials.testWPcomSitePrimaryAddress) + .tapDone() } func testAddRemoveFeaturedImage() throws { try BlockEditorScreen() - .enterTextInTitle(text: title) - .addParagraphBlock(withText: content) - .verifyContentStructure(blocks: 1, words: content.components(separatedBy: " ").count, characters: content.count) + .enterTextInTitle(text: postTitle) + .addParagraphBlock(withText: postContent) + .verifyContentStructure(blocks: 1, words: postContent.components(separatedBy: " ").count, characters: postContent.count) .openPostSettings() .setFeaturedImage() .verifyPostSettings(hasImage: true) @@ -73,10 +73,10 @@ class EditorGutenbergTests: XCTestCase { func testAddGalleryBlock() throws { try BlockEditorScreen() - .enterTextInTitle(text: title) - .addParagraphBlock(withText: content) + .enterTextInTitle(text: postTitle) + .addParagraphBlock(withText: postContent) .addImageGallery() - .verifyContentStructure(blocks: 2, words: content.components(separatedBy: " ").count, characters: content.count) + .verifyContentStructure(blocks: 2, words: postContent.components(separatedBy: " ").count, characters: postContent.count) } func testAddMediaBlocks() throws { diff --git a/WordPress/UITests/Tests/PostTests.swift b/WordPress/UITests/Tests/PostTests.swift index 30a961b64ed4..b73667a3d241 100644 --- a/WordPress/UITests/Tests/PostTests.swift +++ b/WordPress/UITests/Tests/PostTests.swift @@ -10,7 +10,7 @@ class PostTests: XCTestCase { siteUrl: WPUITestCredentials.testWPcomSiteAddress, email: WPUITestCredentials.testWPcomUserEmail, password: WPUITestCredentials.testWPcomPassword, - title: WPUITestCredentials.testWPcomSiteForScheduledPost + selectedSiteTitle: WPUITestCredentials.testWPcomSiteForScheduledPost ) try TabNavComponent() @@ -22,18 +22,18 @@ class PostTests: XCTestCase { takeScreenshotOfFailedTest() } - let title = "Scheduled Post" + let postTitle = "Scheduled Post" func testCreateScheduledPost() throws { try BlockEditorScreen() - .enterTextInTitle(text: title) + .enterTextInTitle(text: postTitle) .openPostSettings() - .updatePublishDate() + .updatePublishDateToFutureDate() .closePublishDateSelector() .closePostSettings() .schedulePost() - .viewPublishedPost(withTitle: title) - .verifyEpilogueDisplays(postTitle: title, siteAddress: WPUITestCredentials.testWPcomSiteForScheduledPost) - .done() + .viewPublishedPost(withTitle: postTitle) + .verifyEpilogueDisplays(postTitle: postTitle, siteAddress: WPUITestCredentials.testWPcomSiteForScheduledPost) + .tapDone() } } diff --git a/WordPress/UITestsFoundation/Screens/Editor/EditorPublishEpilogueScreen.swift b/WordPress/UITestsFoundation/Screens/Editor/EditorPublishEpilogueScreen.swift index b8540f16bac7..70726a290558 100644 --- a/WordPress/UITestsFoundation/Screens/Editor/EditorPublishEpilogueScreen.swift +++ b/WordPress/UITestsFoundation/Screens/Editor/EditorPublishEpilogueScreen.swift @@ -27,7 +27,7 @@ public class EditorPublishEpilogueScreen: ScreenObject { } /// - Note: Returns `Void` since the return screen depends on which screen we started from. - public func done() { + public func tapDone() { doneButton.tap() } From e2740ab5f2374f94e4a6ed9e6977d646d10f49db Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Wed, 12 Jul 2023 11:50:55 +0800 Subject: [PATCH 7/7] update to future date the next month --- .../Screens/Editor/EditorPostSettings.swift | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/WordPress/UITestsFoundation/Screens/Editor/EditorPostSettings.swift b/WordPress/UITestsFoundation/Screens/Editor/EditorPostSettings.swift index b47d5c6b62ff..012f7d86b3dd 100644 --- a/WordPress/UITestsFoundation/Screens/Editor/EditorPostSettings.swift +++ b/WordPress/UITestsFoundation/Screens/Editor/EditorPostSettings.swift @@ -31,8 +31,12 @@ public class EditorPostSettings: ScreenObject { $0.staticTexts["Immediately"] } - let dismissPopoverButtonGetter: (XCUIApplication) -> XCUIElement = { - $0.buttons["PopoverDismissRegion"] + let nextMonthButtonGetter: (XCUIApplication) -> XCUIElement = { + $0.buttons["Next Month"] + } + + let firstCalendarDayLabelGetter: (XCUIApplication) -> XCUIElement = { + $0.staticTexts["1"] } let doneButtonGetter: (XCUIApplication) -> XCUIElement = { @@ -46,7 +50,8 @@ public class EditorPostSettings: ScreenObject { var currentFeaturedImage: XCUIElement { currentFeaturedImageGetter(app) } var publishDateButton: XCUIElement { publishDateButtonGetter(app) } var dateSelector: XCUIElement { dateSelectorGetter(app) } - var dismissPopoverButton: XCUIElement { dismissPopoverButtonGetter(app) } + var nextMonthButton: XCUIElement { nextMonthButtonGetter(app) } + var firstCalendarDayLabel: XCUIElement { firstCalendarDayLabelGetter(app) } var doneButton: XCUIElement { doneButtonGetter(app) } public init(app: XCUIApplication = XCUIApplication()) throws { @@ -126,28 +131,15 @@ public class EditorPostSettings: ScreenObject { } @discardableResult - public func updatePublishDate() -> Self { + public func updatePublishDateToFutureDate() -> Self { publishDateButton.tap() dateSelector.tap() - let predicate = NSPredicate(format: "label CONTAINS[c] 'AM' OR label CONTAINS[c] 'PM'") - let timeButton = app.buttons.element(matching: predicate) - timeButton.tap() - - let currentHour = app.pickerWheels.firstMatch.value as? String - let currentHourValue = Int(currentHour?.components(separatedBy: " ").first ?? "") ?? 0 - - // Set new hour to be 2 hours in the future - var newHourValue = currentHourValue + 2 + // Selects the first day of the next month + nextMonthButton.tap() + firstCalendarDayLabel.tap() - // To handle hour as picker wheel displays time in 12-hour format - if newHourValue > 12 { newHourValue = newHourValue - 12} - - app.pickerWheels.firstMatch.adjust(toPickerWheelValue: "\(newHourValue)") - - dismissPopoverButton.tap() doneButton.tap() - return self }