Skip to content
Merged
18 changes: 1 addition & 17 deletions WordPress/UITestsFoundation/BaseScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,24 +69,8 @@ extension BaseScreen {
}
}

/// Scroll an element into view within another element.
/// scrollView can be a UIScrollView, or anything that subclasses it like UITableView
///
/// TODO: The implementation of this could use work:
/// - What happens if the element is above the current scroll view position?
/// - What happens if it's a really long scroll view?
public func scrollElementIntoView(element: XCUIElement, within scrollView: XCUIElement, threshold: Int = 1000) {

var iteration = 0

while !element.isFullyVisibleOnScreen && iteration < threshold {
scrollView.scroll(byDeltaX: 0, deltaY: 100)
iteration += 1
}

if !element.isFullyVisibleOnScreen {
XCTFail("Unable to scroll element into view")
}
element.scrollIntoView(within: scrollView, threshold: threshold)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

You might notice that this code is part of BaseScreen, which this train of work aims to remove.

This change here moved the logic to a dedicated extension (feb932e) so that both BaseScreen and ScreenObject could share it in the context of this project.

Better than copy-pasting, in particular because this code is fit to live in the XCUITestHelpers project.

}

@discardableResult
Expand Down
2 changes: 1 addition & 1 deletion WordPress/UITestsFoundation/FancyAlertComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class FancyAlertComponent: ScreenObject {
defaultAlertButton.tap()
}

func cancelAlert() {
public func cancelAlert() {
cancelAlertButton.tap()
}

Expand Down
16 changes: 16 additions & 0 deletions WordPress/UITestsFoundation/Globals.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,20 @@ extension ScreenObject {
safari.scrollViews.element(boundBy: 0).buttons.element(boundBy: 1).tap()
}
}

@discardableResult
public func dismissNotificationAlertIfNeeded(
_ action: FancyAlertComponent.Action = .cancel
) throws -> Self {
guard FancyAlertComponent.isLoaded() else { return self }

switch action {
case .accept:
try FancyAlertComponent().acceptAlert()
case .cancel:
try FancyAlertComponent().cancelAlert()
}

return self
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ public class AztecEditorScreen: BaseScreen {
Select Image from Camera Roll by its ID. Starts with 0
Simulator range: 0..4
*/
func addImageByOrder(id: Int) -> AztecEditorScreen {
func addImageByOrder(id: Int) throws -> AztecEditorScreen {
tapToolbarButton(button: mediaButton)

// Allow access to device media
Expand All @@ -221,7 +221,7 @@ public class AztecEditorScreen: BaseScreen {
}

// Inject the first picture
MediaPickerAlbumScreen().selectImage(atIndex: 0)
try MediaPickerAlbumScreen().selectImage(atIndex: 0)
insertMediaButton.tap()

// Wait for upload to finish
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ public class BlockEditorScreen: BaseScreen {
/**
Adds an image block with latest image from device.
*/
public func addImage() -> BlockEditorScreen {
public func addImage() throws -> BlockEditorScreen {
addBlock("Image block")
addImageByOrder(id: 0)
try addImageByOrder(id: 0)

return self
}
Expand Down Expand Up @@ -126,14 +126,14 @@ public class BlockEditorScreen: BaseScreen {
/*
Select Image from Camera Roll by its ID. Starts with 0
*/
private func addImageByOrder(id: Int) {
private func addImageByOrder(id: Int) throws {
imageDeviceButton.tap()

// Allow access to device media
app.tap() // trigger the media permissions alert handler

// Inject the first picture
MediaPickerAlbumListScreen()
try MediaPickerAlbumListScreen()
.selectAlbum(atIndex: 0)
.selectImage(atIndex: 0)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class EditorPostSettings: ScreenObject {

public func setFeaturedImage() throws -> EditorPostSettings {
featuredImageButton.tap()
MediaPickerAlbumListScreen()
try MediaPickerAlbumListScreen()
.selectAlbum(atIndex: 0) // Select media library
.selectImage(atIndex: 0) // Select latest uploaded image

Expand Down
39 changes: 19 additions & 20 deletions WordPress/UITestsFoundation/Screens/Login/LoginEmailScreen.swift
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import ScreenObject
import XCTest

// TODO: remove when unifiedAuth is permanent.

private struct ElementStringIDs {
static let emailTextField = "Login Email Address"
static let nextButton = "Login Email Next Button"
static let siteAddressButton = "Self Hosted Login Button"
}
public class LoginEmailScreen: ScreenObject {

let emailTextFieldGetter: (XCUIApplication) -> XCUIElement = {
$0.textFields["Login Email Address"]
}

public class LoginEmailScreen: BaseScreen {
let emailTextField: XCUIElement
let nextButton: XCUIElement
let siteAddressButton: XCUIElement
let nextButtonGetter: (XCUIApplication) -> XCUIElement = {
$0.buttons["Login Email Next Button"]
}

init() {
let app = XCUIApplication()
emailTextField = app.textFields[ElementStringIDs.emailTextField]
nextButton = app.buttons[ElementStringIDs.nextButton]
siteAddressButton = app.buttons[ElementStringIDs.siteAddressButton]
var emailTextField: XCUIElement { emailTextFieldGetter(app) }
var nextButton: XCUIElement { nextButtonGetter(app) }

super.init(element: emailTextField)
init(app: XCUIApplication = XCUIApplication()) throws {
try super.init(
expectedElementGetters: [emailTextFieldGetter, nextButtonGetter],
app: app
)
}

public func proceedWith(email: String) -> LinkOrPasswordScreen {
Expand All @@ -31,18 +32,16 @@ public class LoginEmailScreen: BaseScreen {
}

func goToSiteAddressLogin() -> LoginSiteAddressScreen {
siteAddressButton.tap()
app.buttons["Self Hosted Login Button"].tap()

return LoginSiteAddressScreen()
}

static func isLoaded() -> Bool {
let expectedElement = XCUIApplication().textFields[ElementStringIDs.emailTextField]
return expectedElement.exists && expectedElement.isHittable
(try? LoginEmailScreen().isLoaded) ?? false
}

static func isEmailEntered() -> Bool {
let emailTextField = XCUIApplication().textFields[ElementStringIDs.emailTextField]
return emailTextField.value != nil
(try? LoginEmailScreen().emailTextField.value != nil) ?? false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ public class LoginSiteAddressScreen: BaseScreen {
super.init(element: siteAddressTextField)
}

public func proceedWith(siteUrl: String) -> LoginUsernamePasswordScreen {
public func proceedWith(siteUrl: String) throws -> LoginUsernamePasswordScreen {
siteAddressTextField.tap()
siteAddressTextField.typeText(siteUrl)
nextButton.tap()

return LoginUsernamePasswordScreen()
return try LoginUsernamePasswordScreen()
}

public func proceedWithWP(siteUrl: String) throws -> GetStartedScreen {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import ScreenObject
import XCTest
import XCUITestHelpers

Expand All @@ -16,18 +17,35 @@ private struct ElementStringIDs {
static let nextButton = "Continue Button"
}

public class LoginUsernamePasswordScreen: BaseScreen {
let usernameTextField: XCUIElement
let passwordTextField: XCUIElement
let nextButton: XCUIElement
public class LoginUsernamePasswordScreen: ScreenObject {

init() {
let app = XCUIApplication()
usernameTextField = app.textFields[ElementStringIDs.usernameTextField]
passwordTextField = app.secureTextFields[ElementStringIDs.passwordTextField]
nextButton = app.buttons[ElementStringIDs.nextButton]
let usernameTextFieldGetter: (XCUIApplication) -> XCUIElement = {
$0.textFields[ElementStringIDs.usernameTextField]
}

let passwordTextFieldGetter: (XCUIApplication) -> XCUIElement = {
$0.secureTextFields[ElementStringIDs.passwordTextField]
}

let nextButtonGetter: (XCUIApplication) -> XCUIElement = {
$0.buttons[ElementStringIDs.nextButton]
}

var usernameTextField: XCUIElement { usernameTextFieldGetter(app) }
var passwordTextField: XCUIElement { passwordTextFieldGetter(app) }
var nextButton: XCUIElement { nextButtonGetter(app) }

super.init(element: passwordTextField)
init(app: XCUIApplication = XCUIApplication()) throws {
// Notice that we don't use the "next button" getter because, at the time the screen loads,
// that element is disabled. `ScreenObject` uses `isEnabled == true` on the elements we
// pass at `init`.
try super.init(
expectedElementGetters: [
usernameTextFieldGetter,
passwordTextFieldGetter
],
app: app
)
}

public func proceedWith(username: String, password: String) -> LoginEpilogueScreen {
Expand All @@ -47,6 +65,6 @@ public class LoginUsernamePasswordScreen: BaseScreen {
}

public static func isLoaded() -> Bool {
return XCUIApplication().buttons[ElementStringIDs.nextButton].exists
(try? LoginUsernamePasswordScreen().isLoaded) ?? false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ public class WelcomeScreenLoginComponent: BaseScreen {
super.init(element: emailLoginButton)
}

public func selectEmailLogin() -> LoginEmailScreen {
public func selectEmailLogin() throws -> LoginEmailScreen {
emailLoginButton.tap()

return LoginEmailScreen()
return try LoginEmailScreen()
}

func goToSiteAddressLogin() -> LoginSiteAddressScreen {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import ScreenObject
import XCTest

public class MediaPickerAlbumListScreen: BaseScreen {
let albumList: XCUIElement
public class MediaPickerAlbumListScreen: ScreenObject {

public init() {
let app = XCUIApplication()
albumList = app.tables["AlbumTable"]
private let albumListGetter: (XCUIApplication) -> XCUIElement = {
$0.tables["AlbumTable"]
}

super.init(element: albumList)
public init(app: XCUIApplication = XCUIApplication()) throws {
try super.init(
expectedElementGetter: albumListGetter,
app: app
)
}

public func selectAlbum(atIndex index: Int) -> MediaPickerAlbumScreen {
let selectedAlbum = albumList.cells.element(boundBy: index)
public func selectAlbum(atIndex index: Int) throws -> MediaPickerAlbumScreen {
let selectedAlbum = albumListGetter(app).cells.element(boundBy: index)
XCTAssertTrue(selectedAlbum.waitForExistence(timeout: 5), "Selected album did not load")
selectedAlbum.tap()

return MediaPickerAlbumScreen()
return try MediaPickerAlbumScreen()
}

public static func isLoaded() -> Bool {
return XCUIApplication().tables["AlbumTable"].exists
(try? MediaPickerAlbumListScreen().isLoaded) ?? false
}
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,32 @@
import ScreenObject
import XCTest

public class MediaPickerAlbumScreen: BaseScreen {
let mediaCollection: XCUIElement
let insertButton: XCUIElement

public init() {
let app = XCUIApplication()
mediaCollection = app.collectionViews["MediaCollection"]
insertButton = app.buttons["SelectedActionButton"]
public class MediaPickerAlbumScreen: ScreenObject {
let mediaCollectionGetter: (XCUIApplication) -> XCUIElement = {
$0.collectionViews["MediaCollection"]
}

super.init(element: mediaCollection)
public init(app: XCUIApplication = XCUIApplication()) throws {
try super.init(expectedElementGetters: [mediaCollectionGetter], app: app)
}

public func selectImage(atIndex index: Int) {
let selectedImage = mediaCollection.cells.element(boundBy: index)
let selectedImage = mediaCollectionGetter(app).cells.element(boundBy: index)
XCTAssertTrue(selectedImage.waitForExistence(timeout: 5), "Selected image did not load")
selectedImage.tap()
}

func insertSelectedImage() {
insertButton.tap()
app.buttons["SelectedActionButton"].tap()
}

public static func isLoaded() -> Bool {
public static func isLoaded(app: XCUIApplication = XCUIApplication()) -> Bool {
// Check if the media picker is loaded as a component within the editor
// and only return true if the media picker is a full screen
if XCUIApplication().navigationBars["Azctec Editor Navigation Bar"].exists {
if app.navigationBars["Azctec Editor Navigation Bar"].exists {
return false
}

return XCUIApplication().collectionViews["MediaCollection"].exists
return (try? MediaPickerAlbumScreen().isLoaded) ?? false
}
}
4 changes: 2 additions & 2 deletions WordPress/UITestsFoundation/Screens/MySiteScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,14 @@ public class MySiteScreen: BaseScreen {
return try JetpackBackupScreen()
}

public func gotoPostsScreen() -> PostsScreen {
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()
}

postsButton.tap()
return PostsScreen()
return try PostsScreen()
}

public func gotoMediaScreen() -> MediaScreen {
Expand Down
Loading