Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions WordPress/UITests/Tests/EditorGutenbergTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ class EditorGutenbergTests: XCTestCase {
.enterTextInTitle(text: postTitle)
.addParagraphBlock(withText: postContent)
.verifyContentStructure(blocks: 1, words: postContent.components(separatedBy: " ").count, characters: postContent.count)
.publish()
.viewPublishedPost(withTitle: postTitle)
.publishAndViewEpilogue()
.verifyEpilogueDisplays(postTitle: postTitle, siteAddress: WPUITestCredentials.testWPcomPaidSite)
.tapDone()
}
Expand All @@ -48,8 +47,7 @@ class EditorGutenbergTests: XCTestCase {
.selectCategory(name: category)
.addTag(name: tag)
.closePostSettings()
.publish()
.viewPublishedPost(withTitle: postTitle)
.publishAndViewEpilogue()
.verifyEpilogueDisplays(postTitle: postTitle, siteAddress: WPUITestCredentials.testWPcomPaidSite)
.tapDone()
}
Expand Down
3 changes: 1 addition & 2 deletions WordPress/UITests/Tests/PostTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ class PostTests: XCTestCase {
.updatePublishDateToFutureDate()
.closePublishDateSelector()
.closePostSettings()
.schedulePost()
.viewPublishedPost(withTitle: postTitle)
.schedulePostAndViewEpilogue()
.verifyEpilogueDisplays(postTitle: postTitle, siteAddress: WPUITestCredentials.testWPcomSiteForScheduledPost)
.tapDone()
}
Expand Down
65 changes: 39 additions & 26 deletions WordPress/UITestsFoundation/Screens/Editor/BlockEditorScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ public class BlockEditorScreen: ScreenObject {
$0.staticTexts["You have unsaved changes."]
}

private let noticeViewTitleGetter: (XCUIApplication) -> XCUIElement = {
$0.otherElements["notice_title_and_message"]
}

private let noticeViewButtonGetter: (XCUIApplication) -> XCUIElement = {
$0.buttons["View"]
}

var addBlockButton: XCUIElement { addBlockButtonGetter(app) }
var applyButton: XCUIElement { applyButtonGetter(app) }
var chooseFromDeviceButton: XCUIElement { chooseFromDeviceButtonGetter(app) }
Expand All @@ -104,6 +112,8 @@ public class BlockEditorScreen: ScreenObject {
var setRemindersButton: XCUIElement { setRemindersButtonGetter(app) }
var undoButton: XCUIElement { undoButtonGetter(app) }
var unsavedChangesLabel: XCUIElement { unsavedChangesLabelGetter(app) }
var noticeViewButton: XCUIElement { noticeViewButtonGetter(app) }
var noticeViewTitle: XCUIElement { noticeViewTitleGetter(app) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nitpick - one of the things i'm trying to do is to sort the vars here alphabetically so it's easier to scan through the list of available elements, do you mind moving this alphabetically too? 🙏

    var addBlockButton: XCUIElement { addBlockButtonGetter(app) }
    var applyButton: XCUIElement { applyButtonGetter(app) }
    var chooseFromDeviceButton: XCUIElement { chooseFromDeviceButtonGetter(app) }
    var closeButton: XCUIElement { closeButtonGetter(app) }
    var discardButton: XCUIElement { discardButtonGetter(app) }
    var dismissPopoverRegion: XCUIElement { dismissPopoverRegionGetter(app) }
    var editorCloseButton: XCUIElement { editorCloseButtonGetter(app) }
    var editorNavigationBar: XCUIElement { editorNavigationBarGetter(app) }
    var firstParagraphBlock: XCUIElement { firstParagraphBlockGetter(app) }
    var fullScreenImage: XCUIElement { fullScreenImageGetter(app) }
    var insertFromUrlButton: XCUIElement { insertFromUrlButtonGetter(app) }
    var keepEditingButton: XCUIElement { keepEditingButtonGetter(app) }
    var moreButton: XCUIElement { moreButtonGetter(app) }
    var noticeViewButton: XCUIElement { noticeViewButtonGetter(app) }
    var noticeViewTitle: XCUIElement { noticeViewTitleGetter(app) }
    var postSettingsButton: XCUIElement { postSettingsButtonGetter(app) }
    var postTitleView: XCUIElement { postTitleViewGetter(app) }
    var redoButton: XCUIElement { redoButtonGetter(app) }
    var setRemindersButton: XCUIElement { setRemindersButtonGetter(app) }
    var switchToHTMLModeButton: XCUIElement { switchToHTMLModeButtonGetter(app) }
    var undoButton: XCUIElement { undoButtonGetter(app) }
    var unsavedChangesLabel: XCUIElement { unsavedChangesLabelGetter(app) }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Moved these and a few others, as well as the getters, in f5e4ec4.


public init(app: XCUIApplication = XCUIApplication()) throws {
// The block editor has _many_ elements but most are loaded on-demand. To verify the screen
Expand Down Expand Up @@ -234,37 +244,43 @@ public class BlockEditorScreen: ScreenObject {
}
}

public func publish() throws -> EditorNoticeComponent {
return try post(action: "Publish")
private enum postAction: String {
case publish = "Publish"
case schedule = "Schedule"
}

public func publish() throws {
return try post(action: .publish)
}

public func publishAndViewEpilogue() throws -> EditorPublishEpilogueScreen {
try publish()
waitAndTap(noticeViewButton)
return try EditorPublishEpilogueScreen()
}

public func schedulePost() throws -> EditorNoticeComponent {
return try post(action: "Schedule")
public func schedulePost() throws {
try post(action: .schedule)
}

private func post(action: String) throws -> EditorNoticeComponent {
let postButton = app.buttons[action]
let postNowButton = app.buttons["\(action) Now"]
public func schedulePostAndViewEpilogue() throws -> EditorPublishEpilogueScreen {
try schedulePost()
waitAndTap(noticeViewButton)
return try EditorPublishEpilogueScreen()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we can DRY this further, possibly by moving the ViewEpilogue part of the method into the post(action: postAction)? this way we won't need to create publishAndViewEpilogue() and schedulePostAndViewEpilogue(). wdyt?

something like:

    public func publish() throws -> EditorPublishEpilogueScreen {
        return try postAndViewEpilogue(action: .publish)
    }

    public func schedulePost() throws -> EditorPublishEpilogueScreen {
        return try postAndViewEpilogue(action: .schedule)
    }

    private func postAndViewEpilogue(action: postAction) throws -> EditorPublishEpilogueScreen {
        let postButton = app.buttons[action.rawValue]
        let postNowButton = app.buttons["\(action.rawValue) Now"]
        var tries = 0
        // 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 confirmPost(button: postNowButton, action: action)

        waitAndTap(noticeViewButton)
        return try EditorPublishEpilogueScreen()
    }

or if we want to keep post and view epilogue separate, we can DRY it by calling post(action: postAction) directly in publishAndViewEpilogue():

    public func publishAndViewEpilogue() throws -> EditorPublishEpilogueScreen {
        try post(action: .publish)
        waitAndTap(noticeViewButton)
        return try EditorPublishEpilogueScreen()
    }

Copy link
Contributor Author

@tiagomar tiagomar Aug 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, @jostnes! While working on it I decided to keep publish() in order make it possible to publish without viewing the Epilogue and maybe go to Posts or somewhere else right away? Not deeply attached to it though. WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah think that makes sense and very possible that we'd want to use it without viewing Epilogue so I think that's fine to keep it that way.

my second suggestion above is about publishAndViewEpilogue(), where instead of calling publish() or schedulePost, we call try post(action: PostAction) directly to remove that one extra level. not sure if it'll make a big difference in terms of timing but i think keeping the calls as direct as possible (when/if possible) usually helps with timing issues in UI tests. wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally! Addressed in 0af55e8.


private func post(action: postAction) throws {
let postButton = app.buttons[action.rawValue]
let postNowButton = app.buttons["\(action.rawValue) Now"]
var tries = 0
// 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 confirmPost(button: postNowButton)

let actionInNotice: String

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)")
try confirmPost(button: postNowButton, action: action)
}

@discardableResult
Expand Down Expand Up @@ -402,13 +418,10 @@ public class BlockEditorScreen: ScreenObject {
.selectAlbum(atIndex: 0)
}

private func confirmPost(button: XCUIElement) throws {
if FancyAlertComponent.isLoaded() {
try FancyAlertComponent().acceptAlert()
Comment on lines -402 to -403
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know which FancyAlert we could expect here. I've run all the editor tests several times and haven't seen it. We are saving some time here but it doesn't impact the target issue as it comes before tapping the Schedule Now button.

} else {
button.tap()
dismissBloggingRemindersAlertIfNeeded()
}
private func confirmPost(button: XCUIElement, action: postAction) throws {
button.tap()
guard action == .publish else { return }
dismissBloggingRemindersAlertIfNeeded()
Comment on lines +405 to +406
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blogging Reminder is only displayed when a post is published, so we can save the 3 seconds timeout from dismissBloggingRemindersAlertIfNeeded() when scheduling a post. ⏱️

}

public func dismissBloggingRemindersAlertIfNeeded() {
Expand Down