mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +00:00
#40: Add basic AuthenticationService and missing UI tests.
* Add MockAuthenticationService and ServerSelectionUITests. * Add tests covering the Authentication flow.
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// AuthenticationCoordinatorUITests.swift
|
||||
// UITests
|
||||
//
|
||||
// Created by Doug on 30/06/2022.
|
||||
// Copyright © 2022 Element. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
@testable import ElementX
|
||||
|
||||
@MainActor
|
||||
class AuthenticationCoordinatorUITests: XCTestCase {
|
||||
func testLoginWithPassword() {
|
||||
// Given the authentication flow.
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.authenticationFlow)
|
||||
|
||||
// Splash Screen: Tap get started button
|
||||
app.buttons["getStartedButton"].tap()
|
||||
|
||||
// Login Screen: Enter valid credentials
|
||||
app.textFields["usernameTextField"].tap()
|
||||
app.typeText("alice\n")
|
||||
app.secureTextFields["passwordTextField"].tap()
|
||||
app.typeText("12345678")
|
||||
|
||||
// Login Screen: Tap next
|
||||
app.buttons["nextButton"].tap()
|
||||
|
||||
// Then login should succeed.
|
||||
XCTAssertFalse(app.alerts.element.exists, "No alert should be shown when logging in with valid credentials.")
|
||||
}
|
||||
|
||||
func testLoginWithIncorrectPassword() {
|
||||
// Given the authentication flow.
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.authenticationFlow)
|
||||
|
||||
// Splash Screen: Tap get started button
|
||||
app.buttons["getStartedButton"].tap()
|
||||
|
||||
// Login Screen: Enter invalid credentials
|
||||
app.textFields["usernameTextField"].tap()
|
||||
app.typeText("alice\n")
|
||||
app.typeText("87654321\n")
|
||||
|
||||
// Then login should fail.
|
||||
XCTAssertTrue(app.alerts.element.exists, "An error alert should be shown when attempting login with invalid credentials.")
|
||||
}
|
||||
|
||||
func testSelectingOIDCServer() {
|
||||
// Given the authentication flow.
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.authenticationFlow)
|
||||
|
||||
// Splash Screen: Tap get started button
|
||||
app.buttons["getStartedButton"].tap()
|
||||
|
||||
// Login Screen: Tap edit server button.
|
||||
XCTAssertFalse(app.buttons["oidcButton"].exists, "The OIDC button shouldn't be shown before entering a supported homeserver.")
|
||||
app.buttons["editServerButton"].tap()
|
||||
|
||||
// Server Selection: Clear the default and enter OIDC server.
|
||||
app.textFields["addressTextField"].tap()
|
||||
app.textFields["addressTextField"].buttons.element.tap()
|
||||
app.typeText("company.com")
|
||||
|
||||
// Dismiss server screen.
|
||||
app.buttons["confirmButton"].tap()
|
||||
|
||||
// Then the login form should be updated for OIDC.
|
||||
XCTAssertTrue(app.buttons["oidcButton"].waitForExistence(timeout: 1), "The OIDC button should be shown after selecting a homeserver with OIDC.")
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ class LoginScreenUITests: XCTestCase {
|
||||
}
|
||||
|
||||
func testMatrixDotOrg() {
|
||||
// Given the initial login screen which defaults to matrix.org.
|
||||
app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.login)
|
||||
|
||||
@@ -37,35 +38,49 @@ class LoginScreenUITests: XCTestCase {
|
||||
validateNextButtonIsDisabled(for: state)
|
||||
validateUnsupportedServerTextIsHidden(for: state)
|
||||
|
||||
// When typing in a username and password.
|
||||
app.textFields.element.tap()
|
||||
app.typeText("@test:server.com")
|
||||
app.typeText("@test:matrix.org")
|
||||
|
||||
app.secureTextFields.element.tap()
|
||||
app.typeText("12345678")
|
||||
|
||||
// Then the form should be ready to submit.
|
||||
validateNextButtonIsEnabled(for: "matrix.org with credentials entered")
|
||||
}
|
||||
|
||||
func testOIDC() {
|
||||
// Given the initial login screen.
|
||||
app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.loginOIDC)
|
||||
app.goToScreenWithIdentifier(.login)
|
||||
|
||||
// When entering a username on a homeserver that only supports OIDC.
|
||||
app.textFields.element.tap()
|
||||
app.typeText("@test:company.com\n")
|
||||
|
||||
// Then the screen should be configured for OIDC.
|
||||
let state = "an OIDC only server"
|
||||
validateOIDCButtonIsShown(for: state)
|
||||
validateServerDescriptionIsHidden(for: state)
|
||||
validateLoginFormIsHidden(for: state)
|
||||
validateOIDCButtonIsShown(for: state)
|
||||
validateUnsupportedServerTextIsHidden(for: state)
|
||||
}
|
||||
|
||||
func testUnsupported() {
|
||||
// Given the initial login screen.
|
||||
app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.loginUnsupported)
|
||||
app.goToScreenWithIdentifier(.login)
|
||||
|
||||
// When entering a username on a homeserver with an unsupported flow.
|
||||
app.textFields.element.tap()
|
||||
app.typeText("@test:server.net\n")
|
||||
|
||||
// Then the screen should not allow login to continue.
|
||||
let state = "an unsupported server"
|
||||
validateUnsupportedServerTextIsShown(for: state)
|
||||
validateServerDescriptionIsHidden(for: state)
|
||||
validateLoginFormIsHidden(for: state)
|
||||
validateOIDCButtonIsHidden(for: state)
|
||||
validateUnsupportedServerTextIsShown(for: state)
|
||||
}
|
||||
|
||||
/// Checks that the server description label is shown.
|
||||
@@ -78,7 +93,7 @@ class LoginScreenUITests: XCTestCase {
|
||||
/// Checks that the server description label is hidden.
|
||||
func validateServerDescriptionIsHidden(for state: String) {
|
||||
let descriptionLabel = app.staticTexts["serverDescriptionText"]
|
||||
XCTAssertFalse(descriptionLabel.exists, "The server description should be shown for \(state).")
|
||||
XCTAssertFalse(descriptionLabel.exists, "The server description should be hidden for \(state).")
|
||||
}
|
||||
|
||||
/// Checks that the username and password text fields are shown along with the next button.
|
||||
@@ -123,7 +138,7 @@ class LoginScreenUITests: XCTestCase {
|
||||
/// Checks that the OIDC button is shown on the screen.
|
||||
func validateOIDCButtonIsShown(for state: String) {
|
||||
let oidcButton = app.buttons["oidcButton"]
|
||||
XCTAssertTrue(oidcButton.exists, "The OIDC button should be shown for \(state).")
|
||||
XCTAssertTrue(oidcButton.waitForExistence(timeout: 1), "The OIDC button should be shown for \(state).")
|
||||
XCTAssertEqual(oidcButton.label, ElementL10n.loginContinue)
|
||||
}
|
||||
|
||||
@@ -136,7 +151,7 @@ class LoginScreenUITests: XCTestCase {
|
||||
/// Checks that the unsupported homeserver text is shown on the screen.
|
||||
func validateUnsupportedServerTextIsShown(for state: String) {
|
||||
let unsupportedText = app.staticTexts["unsupportedServerText"]
|
||||
XCTAssertTrue(unsupportedText.exists, "The unsupported homeserver text should be shown for \(state).")
|
||||
XCTAssertTrue(unsupportedText.waitForExistence(timeout: 1), "The unsupported homeserver text should be shown for \(state).")
|
||||
XCTAssertEqual(unsupportedText.label, ElementL10n.autodiscoverWellKnownError)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,23 +20,19 @@ import ElementX
|
||||
@MainActor
|
||||
class RoomScreenUITests: XCTestCase {
|
||||
|
||||
func testPlainNoAvatar() async throws {
|
||||
func testPlainNoAvatar() {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.roomPlainNoAvatar)
|
||||
|
||||
try await Task.sleep(nanoseconds: 400_000_000)
|
||||
|
||||
XCTAssert(app.staticTexts["roomNameLabel"].exists)
|
||||
XCTAssert(app.staticTexts["roomAvatarPlaceholderImage"].exists)
|
||||
XCTAssertFalse(app.images["encryptionBadgeIcon"].exists)
|
||||
}
|
||||
|
||||
func testEncryptedWithAvatar() async throws {
|
||||
func testEncryptedWithAvatar() {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.roomEncryptedWithAvatar)
|
||||
|
||||
try await Task.sleep(nanoseconds: 400_000_000)
|
||||
|
||||
XCTAssert(app.staticTexts["roomNameLabel"].exists)
|
||||
XCTAssert(app.images["roomAvatarImage"].exists)
|
||||
XCTAssert(app.images["encryptionBadgeIcon"].exists)
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import ElementX
|
||||
|
||||
@MainActor
|
||||
class ServerSelectionUITests: XCTestCase {
|
||||
|
||||
let textFieldIdentifier = "addressTextField"
|
||||
|
||||
func testNormalState() async {
|
||||
// Given the initial server selection screen as a modal.
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.serverSelection)
|
||||
|
||||
// Then it should be configured for matrix.org and with a cancel button
|
||||
let serverTextField = app.textFields.element
|
||||
XCTAssertEqual(serverTextField.value as? String, "matrix.org", "The server shown should be matrix.org with the https scheme hidden.")
|
||||
|
||||
let confirmButton = app.buttons["confirmButton"]
|
||||
XCTAssertEqual(confirmButton.label, ElementL10n.actionConfirm, "The confirm button should say Confirm when in modal presentation.")
|
||||
XCTAssertTrue(confirmButton.exists, "The confirm button should always be shown.")
|
||||
XCTAssertTrue(confirmButton.isEnabled, "The confirm button should be enabled when there is an address.")
|
||||
|
||||
let textFieldFooter = app.staticTexts[textFieldIdentifier]
|
||||
XCTAssertTrue(textFieldFooter.exists)
|
||||
XCTAssertEqual(textFieldFooter.label, ElementL10n.serverSelectionServerFooter)
|
||||
|
||||
let dismissButton = app.buttons["dismissButton"]
|
||||
XCTAssertTrue(dismissButton.exists, "The dismiss button should be shown during modal presentation.")
|
||||
}
|
||||
|
||||
func testEmptyAddress() async {
|
||||
// Given the initial server selection screen as a modal.
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.serverSelection)
|
||||
|
||||
// When clearing the server address text field.
|
||||
app.textFields.element.tap()
|
||||
app.textFields.element.buttons.element.tap()
|
||||
|
||||
// Then the screen should not allow the user to continue.
|
||||
let serverTextField = app.textFields.element
|
||||
XCTAssertEqual(serverTextField.value as? String, ElementL10n.serverSelectionServerUrl, "The text field should show placeholder text in this state.")
|
||||
|
||||
let confirmButton = app.buttons["confirmButton"]
|
||||
XCTAssertTrue(confirmButton.exists, "The confirm button should always be shown.")
|
||||
XCTAssertFalse(confirmButton.isEnabled, "The confirm button should be disabled when the address is empty.")
|
||||
}
|
||||
|
||||
func testInvalidAddress() {
|
||||
// Given the initial server selection screen as a modal.
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.serverSelection)
|
||||
|
||||
// When typing in an invalid homeserver
|
||||
app.textFields.element.tap()
|
||||
app.textFields.element.buttons.element.tap()
|
||||
app.typeText("thisisbad\n") // The tests only accept an address from LoginHomeserver.mockXYZ
|
||||
|
||||
// Then an error should be shown and the confirmation button disabled.
|
||||
let serverTextField = app.textFields.element
|
||||
XCTAssertEqual(serverTextField.value as? String, "thisisbad", "The text field should show the entered server.")
|
||||
|
||||
let confirmButton = app.buttons["confirmButton"]
|
||||
XCTAssertTrue(confirmButton.exists, "The confirm button should always be shown.")
|
||||
XCTAssertFalse(confirmButton.isEnabled, "The confirm button should be disabled when there is an error.")
|
||||
|
||||
let textFieldFooter = app.staticTexts[textFieldIdentifier]
|
||||
XCTAssertTrue(textFieldFooter.exists)
|
||||
XCTAssertEqual(textFieldFooter.label, ElementL10n.loginErrorHomeserverNotFound)
|
||||
}
|
||||
|
||||
func testNonModalPresentation() {
|
||||
// Given the initial server selection screen pushed onto the stack.
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.serverSelectionNonModal)
|
||||
|
||||
// Then the screen should be tweaked slightly to reflect the change of navigation.
|
||||
let dismissButton = app.buttons["dismissButton"]
|
||||
XCTAssertFalse(dismissButton.exists, "The dismiss button should be hidden when not in modal presentation.")
|
||||
|
||||
let confirmButton = app.buttons["confirmButton"]
|
||||
XCTAssertEqual(confirmButton.label, ElementL10n.actionNext, "The confirm button should say Next when not in modal presentation.")
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ class SplashScreenUITests: XCTestCase {
|
||||
XCTAssertEqual(getStartedButton.label, ElementL10n.loginSplashSubmit)
|
||||
}
|
||||
|
||||
func testSwipingBetweenPages() async throws {
|
||||
func testSwipingBetweenPages() {
|
||||
let app = Application.launch()
|
||||
app.goToScreenWithIdentifier(.splash)
|
||||
|
||||
@@ -42,7 +42,6 @@ class SplashScreenUITests: XCTestCase {
|
||||
|
||||
// When swiping to the next screen.
|
||||
page1TitleText.swipeLeft()
|
||||
try await Task.sleep(nanoseconds: 200_000_000) // Wait for the animation.
|
||||
|
||||
// Then the second screen should be shown.
|
||||
XCTAssertFalse(page1TitleText.isHittable, "The title from the first page of the carousel should be offscreen.")
|
||||
@@ -50,7 +49,6 @@ class SplashScreenUITests: XCTestCase {
|
||||
|
||||
// When swiping back to the previous screen.
|
||||
page2TitleText.swipeRight()
|
||||
try await Task.sleep(nanoseconds: 200_000_000) // Wait for the animation.
|
||||
|
||||
// Then the first screen should be shown again.
|
||||
XCTAssertTrue(page1TitleText.isHittable, "The title from the first page of the carousel should be onscreen.")
|
||||
@@ -58,7 +56,6 @@ class SplashScreenUITests: XCTestCase {
|
||||
|
||||
// When swiping back to the previous screen.
|
||||
page1TitleText.swipeRight()
|
||||
try await Task.sleep(nanoseconds: 200_000_000) // Wait for the animation.
|
||||
|
||||
// Then the screen shouldn't change and the hidden screen should be ignored.
|
||||
XCTAssertTrue(page1TitleText.isHittable, "The title from the first page of the carousel should be still be onscreen.")
|
||||
|
||||
Reference in New Issue
Block a user