Files
Authenticator/AuthenticatorTests/TokenListViewControllerTest.swift
2023-02-12 13:46:12 -05:00

146 lines
7.0 KiB
Swift

//
// TokenListViewControllerTest.swift
// Authenticator
//
// Copyright (c) 2016-2023 Authenticator authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import XCTest
import OneTimePassword
@testable import Authenticator
class TokenListViewControllerTest: XCTestCase {
private let defaultDigitGroupSize = 2
let tokenList = TokenList()
let displayTime = DisplayTime(date: Date())
lazy var testWindow: UIWindow = {
let window = UIWindow(frame: UIScreen.main.bounds)
window.makeKeyAndVisible()
return window
}()
func emptyListViewModel() -> TokenList.ViewModel {
return tokenList.viewModel(with: [], at: displayTime, digitGroupSize: defaultDigitGroupSize).viewModel
}
// Test that inserting a new token will produce the expected changes to the table view.
func testTokenListInsertsNewToken() {
// Set up a view controller with a mock table view.
let initialViewModel = emptyListViewModel()
let controller = TokenListViewController(viewModel: initialViewModel, dispatchAction: { _ in })
let tableView = MockTableView()
controller.tableView = tableView
// Update the view controller.
let persistentTokens = mockPersistentTokens([
("Service", "email@example.com"),
])
let (updatedViewModel, _) = tokenList.viewModel(with: persistentTokens,
at: displayTime,
digitGroupSize: defaultDigitGroupSize)
controller.update(with: updatedViewModel)
// Check the table view.
let expectedChanges: [MockTableView.ChangeType] = [
.beginUpdates,
.insert(indexPath: IndexPath(row: 0, section: 0)),
.endUpdates,
.scroll(indexPath: IndexPath(row: 0, section: 0)),
]
XCTAssertEqual(tableView.changes, expectedChanges)
}
// Test that updating an existing token will produce the expected changes to the table view.
func testUpdatesExistingToken() {
// Set up a view controller with a mock table view.
let initialPersistentToken = mockPersistentToken(name: "account@example.com", issuer: "Issuer")
let (initialTokenListViewModel, _) = tokenList.viewModel(with: [initialPersistentToken],
at: displayTime,
digitGroupSize: defaultDigitGroupSize)
let controller = TokenListViewController(viewModel: initialTokenListViewModel, dispatchAction: { _ in })
let tableView = MockTableView()
controller.tableView = tableView
// Add the view controller to a test window so it will create cells as if it were visible.
testWindow.rootViewController = controller
// Update the view controller.
let updatedPersistentToken = initialPersistentToken.updated(with: mockToken(name: "name", issuer: "issuer"))
let (updatedTokenListViewModel, _) = tokenList.viewModel(with: [updatedPersistentToken],
at: displayTime,
digitGroupSize: defaultDigitGroupSize)
controller.update(with: updatedTokenListViewModel)
// Check the changes to the table view.
// Updates to existing rows should be applied directly to the cells, without changing the table view.
let expectedChanges: [MockTableView.ChangeType] = []
XCTAssertEqual(tableView.changes, expectedChanges)
// Check that the table view contains the expected cells.
XCTAssertEqual(tableView.numberOfSections, 1)
XCTAssertEqual(tableView.numberOfRows(inSection: 0), 1)
let indexPath = IndexPath(row: 0, section: 0)
let expectedText = updatedPersistentToken.token.issuer + " " + updatedPersistentToken.token.name
XCTAssert(cellAt: indexPath, in: tableView, containsText: expectedText)
}
// Test that the view controller will display the expected number of rows and sections for a given view model.
func testNumberOfRowsAndSection() {
let persistentTokens = mockPersistentTokens([
("Service", "example@google.com"),
("Service", "username"),
])
let (viewModel, _) = tokenList.viewModel(with: persistentTokens,
at: displayTime,
digitGroupSize: defaultDigitGroupSize)
let controller = TokenListViewController(viewModel: viewModel, dispatchAction: { _ in })
addToTestViewHierarchy(controller)
// Check that the table view contains the expected cells.
XCTAssertEqual(controller.tableView.numberOfSections, 1)
XCTAssertEqual(controller.tableView.numberOfRows(inSection: 0), viewModel.rowModels.count)
let visibleCells = controller.tableView.visibleCells
XCTAssertEqual(visibleCells.count, 2)
for (rowModel, cell) in zip(viewModel.rowModels, visibleCells) {
let expectedText = rowModel.issuer + " " + rowModel.name
XCTAssert(cell, containsText: expectedText)
}
}
}
func XCTAssert(_ cell: UITableViewCell, containsText expectedText: String,
file: StaticString = #file, line: UInt = #line) {
let textInCellLabels = cell.contentView.subviews.compactMap({ ($0 as? UILabel)?.text })
XCTAssert(textInCellLabels.contains(expectedText), "Expected \(textInCellLabels) to contain \"\(expectedText)\"",
file: file, line: line)
}
func XCTAssert(cellAt indexPath: IndexPath, in tableView: UITableView, containsText expectedText: String,
file: StaticString = #file, line: UInt = #line) {
guard let cell = tableView.cellForRow(at: indexPath) else {
XCTFail("Expected cell at index path \(indexPath)", file: file, line: line)
return
}
XCTAssert(cell, containsText: expectedText, file: file, line: line)
}