Skip to content

Commit

Permalink
Add UI tests for generic views, fix unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Supereg committed Jun 27, 2024
1 parent c2ac2d7 commit 833e57d
Show file tree
Hide file tree
Showing 12 changed files with 301 additions and 53 deletions.
45 changes: 0 additions & 45 deletions Sources/SpeziDevicesUI/Measurements/CloseButtonLayer.swift

This file was deleted.

3 changes: 0 additions & 3 deletions Sources/SpeziDevicesUI/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,6 @@
}
}
},
"Close" : {
"comment" : "For closing sheets."
},
"Connected" : {
"localizations" : {
"en" : {
Expand Down
3 changes: 3 additions & 0 deletions Tests/SpeziDevicesTests/HealthMeasurementsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ final class HealthMeasurementsTests: XCTestCase {

let measurement1 = try XCTUnwrap(device.weightScale.weightMeasurement)
device.weightScale.$weightMeasurement.inject(measurement1)

try await Task.sleep(for: .milliseconds(50))

let measurement0 = try XCTUnwrap(device.bloodPressure.bloodPressureMeasurement)
device.bloodPressure.$bloodPressureMeasurement.inject(measurement0)

Expand Down
8 changes: 8 additions & 0 deletions Tests/SpeziDevicesTests/PairedDevicesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ final class PairedDevicesTests: XCTestCase {
devices
}

device.isInPairingMode = true


XCTAssertFalse(devices.isConnected(device: device.id))
XCTAssertFalse(devices.isPaired(device))
Expand Down Expand Up @@ -106,6 +108,8 @@ final class PairedDevicesTests: XCTestCase {
devices
}

device.isInPairingMode = true

device.$nearby.inject(false)
try await XCTAssertThrowsErrorAsync(await devices.pair(with: device)) { error in
XCTAssertEqual(try XCTUnwrap(error as? DevicePairingError), .invalidState)
Expand Down Expand Up @@ -141,6 +145,8 @@ final class PairedDevicesTests: XCTestCase {
devices
}

device.isInPairingMode = true

let task = Task {
try await devices.pair(with: device)
}
Expand Down Expand Up @@ -168,6 +174,8 @@ final class PairedDevicesTests: XCTestCase {
devices
}

device.isInPairingMode = true

devices.configure(device: device, accessing: device.$state, device.$advertisementData, device.$nearby)

let task = Task {
Expand Down
4 changes: 2 additions & 2 deletions Tests/SpeziOmronTests/SpeziOmronTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,14 @@ final class SpeziOmronTests: XCTestCase {
CBAdvertisementDataManufacturerDataKey: manufacturerData.encode()
]))

XCTAssertTrue(device.isInPairingMode)
XCTAssertEqual(device.manufacturerData?.pairingMode, .pairingMode)

let manufacturerData0 = OmronManufacturerData(pairingMode: .transferMode, users: [.init(id: 1, sequenceNumber: 3, recordsNumber: 8)])
device.$advertisementData.inject(AdvertisementData([
CBAdvertisementDataManufacturerDataKey: manufacturerData0.encode()
]))

XCTAssertFalse(device.isInPairingMode)
XCTAssertEqual(device.manufacturerData?.pairingMode, .transferMode)


device.deviceInformation.$modelNumber.inject(OmronModel.bp5250.rawValue)
Expand Down
55 changes: 55 additions & 0 deletions Tests/UITests/TestApp/BluetoothViewsTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

@_spi(TestingSupport) import SpeziDevices
import SpeziDevicesUI
import SwiftUI


struct BluetoothViewsTest: View {
@State private var device = MockDevice.createMockDevice()
@State private var presentDeviceDetails = false

var body: some View {
NavigationStack {
List {
BluetoothUnavailableSection()

Section {
NearbyDeviceRow(peripheral: device, primaryAction: tapAction) {
presentDeviceDetails = true
}
} header: {
LoadingSectionHeader("Devices", loading: true)
}
}
.navigationTitle("Views")
.navigationDestination(isPresented: $presentDeviceDetails) {
MockDeviceDetailsView(device)
}
}
}


@MainActor
private func tapAction() {
Task {
switch device.state {
case .disconnected, .disconnecting:
await device.connect()
case .connecting, .connected:
await device.disconnect()
}
}
}
}


#Preview {
BluetoothViewsTest()
}
4 changes: 4 additions & 0 deletions Tests/UITests/TestApp/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ struct ContentView: View {
.tabItem {
Label("Measurements", systemImage: "list.bullet.clipboard.fill")
}
BluetoothViewsTest()
.tabItem {
Label("Views", systemImage: "macwindow")
}
}
}
}
Expand Down
42 changes: 42 additions & 0 deletions Tests/UITests/TestApp/Views/BluetoothUnavailableSection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import SpeziDevicesUI
import SwiftUI


struct BluetoothUnavailableSection: View {
var body: some View {
Section("Bluetooth Unavailable") {
NavigationLink("Bluetooth Powered Off") {
BluetoothUnavailableView(.poweredOff)
}
NavigationLink("Bluetooth Powered On") {
BluetoothUnavailableView(.poweredOn)
}
NavigationLink("Bluetooth Unauthorized") {
BluetoothUnavailableView(.unauthorized)
}
NavigationLink("Bluetooth Unsupported") {
BluetoothUnavailableView(.unsupported)
}
NavigationLink("Bluetooth Unknown") {
BluetoothUnavailableView(.unknown)
}
}
}
}


#Preview {
NavigationStack {
List {
BluetoothUnavailableSection()
}
}
}
54 changes: 54 additions & 0 deletions Tests/UITests/TestApp/Views/MockDeviceDetailsView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

@_spi(TestingSupport) import SpeziDevices
import SpeziDevicesUI
import SpeziViews
import SwiftUI


struct MockDeviceDetailsView: View {
private let device: MockDevice

var body: some View {
List {
ListRow("Name") {
Text(device.label)
}
if let model = device.deviceInformation.modelNumber {
ListRow("Model") {
Text(model)
}
}
if let firmwareVersion = device.deviceInformation.firmwareRevision {
ListRow("Firmware Version") {
Text(firmwareVersion)
}
}
if let battery = device.battery.batteryLevel {
ListRow("Battery") {
BatteryIcon(percentage: Int(battery))
.labelStyle(.reverse)
}
}
}
.navigationTitle(device.label)
.navigationBarTitleDisplayMode(.inline)
}

init(_ device: MockDevice) {
self.device = device
}
}


#Preview {
NavigationStack {
MockDeviceDetailsView(MockDevice.createMockDevice())
}
}
75 changes: 75 additions & 0 deletions Tests/UITests/TestAppUITests/BluetoothViewsTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import XCTest


class BluetoothViewsTests: XCTestCase {
override func setUpWithError() throws {
try super.setUpWithError()

continueAfterFailure = false
}

@MainActor
func testBluetoothUnavailableViews() {
let app = XCUIApplication()
app.launch()

XCTAssert(app.buttons["Views"].waitForExistence(timeout: 2.0))
app.buttons["Views"].tap()

func navigateUnavailableView(name: String, expected: String?, back: Bool = true) {
XCTAssert(app.buttons[name].waitForExistence(timeout: 2.0))
app.buttons[name].tap()
if let expected {
XCTAssert(app.staticTexts[expected].waitForExistence(timeout: 2.0))
}
if back {
XCTAssert(app.navigationBars.buttons["Views"].exists)
app.navigationBars.buttons["Views"].tap()
}
}

navigateUnavailableView(name: "Bluetooth Powered On", expected: nil)
navigateUnavailableView(name: "Bluetooth Unauthorized", expected: "Bluetooth Prohibited")
navigateUnavailableView(name: "Bluetooth Unsupported", expected: "Bluetooth Unsupported")
navigateUnavailableView(name: "Bluetooth Unknown", expected: "Bluetooth Failure")
navigateUnavailableView(name: "Bluetooth Powered Off", expected: "Bluetooth Off", back: false)

XCTAssert(app.buttons["Open Settings"].exists)
app.buttons["Open Settings"].tap()

let settingsApp = XCUIApplication(bundleIdentifier: "com.apple.Preferences")
XCTAssertEqual(settingsApp.state, .runningForeground)
}

@MainActor
func testNearbyDeviceRow() {
let app = XCUIApplication()
app.launch()

XCTAssert(app.buttons["Views"].waitForExistence(timeout: 2.0))
app.buttons["Views"].tap()

XCTAssert(app.staticTexts["DEVICES"].exists)

XCTAssert(app.buttons["Mock Device"].exists)
app.buttons["Mock Device"].tap()

XCTAssert(app.buttons["Mock Device, Connected"].waitForExistence(timeout: 5.0))
XCTAssert(app.buttons["Device Details"].exists)
app.buttons["Device Details"].tap()

XCTAssert(app.navigationBars.staticTexts["Mock Device"].waitForExistence(timeout: 2.0))
XCTAssert(app.staticTexts["Name, Mock Device"].exists)
XCTAssert(app.staticTexts["Model, MD1"].exists)
XCTAssert(app.staticTexts["Firmware Version, 1.0"].exists)
XCTAssert(app.staticTexts["Battery, 85 %"].exists)
}
}
33 changes: 32 additions & 1 deletion Tests/UITests/TestAppUITests/PairedDevicesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,5 +162,36 @@ class PairedDevicesTests: XCTestCase {
XCTAssert(app.staticTexts["Pair Accessory"].waitForExistence(timeout: 2.0))
}

// TODO: forget devices test
@MainActor
func testPairingFailed() {
let app = XCUIApplication()
app.launch()

XCTAssert(app.buttons["Devices"].waitForExistence(timeout: 2.0))
app.buttons["Devices"].tap()

XCTAssert(app.navigationBars.buttons["More"].exists)
app.navigationBars.buttons["More"].tap()
XCTAssert(app.buttons["Connect"].exists)
app.buttons["Connect"].tap()

XCTAssert(app.navigationBars.buttons["More"].exists)
app.navigationBars.buttons["More"].tap()
XCTAssert(app.buttons["Discover Device"].exists)
app.buttons["Discover Device"].tap()

XCTAssert(app.staticTexts["Pair Accessory"].waitForExistence(timeout: 2.0))
XCTAssert(app.buttons["Pair"].exists)
app.buttons["Pair"].tap()

XCTAssert(app.staticTexts["Pairing Failed"].waitForExistence(timeout: 2.0))
XCTAssert(app.staticTexts["Failed to pair with device. Please try again."].exists)
XCTAssert(app.buttons["OK"].exists)
app.buttons["OK"].tap()

XCTAssert(app.navigationBars.buttons["Add Device"].waitForExistence(timeout: 0.5))
app.navigationBars.buttons["Add Device"].tap()

XCTAssert(app.staticTexts["Pair Accessory"].waitForExistence(timeout: 2.0))
}
}
Loading

0 comments on commit 833e57d

Please sign in to comment.