#40: Add basic AuthenticationService and missing UI tests.

* Add MockAuthenticationService and ServerSelectionUITests.
* Add tests covering the Authentication flow.
This commit is contained in:
Doug
2022-07-04 10:00:27 +01:00
committed by GitHub
parent 0a199ee611
commit 26d8affaa8
29 changed files with 641 additions and 203 deletions
@@ -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.")
}
}
+23 -8
View File
@@ -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)
}
+2 -6
View File
@@ -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.")
}
}
+1 -4
View File
@@ -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.")