// // Copyright 2026 Element Creations Ltd. // // SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. // Please see LICENSE files in the repository root for full details. // @testable import ElementX import Foundation import Testing final class HealthChatProfileEditScreenViewModelTests { private let apiService = HealthChatAPIServiceMock() private let clientProxy = ClientProxyMock(.init()) @Test func loadsProfileIntoBindings() async throws { apiService.ownProfileReturnValue = try .success(makeProfile()) let viewModel = makeViewModel() try await waitForLoad(of: viewModel) #expect(viewModel.context.firstName == "Alice") #expect(viewModel.context.businessTel == "+41 44 123 45 67") #expect(viewModel.context.furtherInfo == "Old info") #expect(viewModel.context.viewState.canEditAvatar) #expect(!viewModel.context.viewState.canSave) } @Test func saveSendsOnlyChangedFields() async throws { apiService.ownProfileReturnValue = try .success(makeProfile()) apiService.updateOwnProfileReturnValue = .success(()) let viewModel = makeViewModel() try await waitForLoad(of: viewModel) viewModel.context.businessTel = "+41 79 111 22 33" viewModel.context.furtherInfo = "" let deferred = deferFulfillment(viewModel.actionsPublisher) { action in guard case .dismiss = action else { return false } return true } viewModel.context.send(viewAction: .save) try await deferred.fulfill() let update = try #require(apiService.updateOwnProfileReceivedUpdate) #expect(update.businessTel == "+41 79 111 22 33") #expect(update.furtherInfo == "") #expect(update.firstName == nil) #expect(update.title == nil) } @Test func missingRequiredFieldBlocksSaving() async throws { apiService.ownProfileReturnValue = try .success(makeProfile()) let viewModel = makeViewModel() try await waitForLoad(of: viewModel) viewModel.context.firstName = " " #expect(!viewModel.context.viewState.canSave) } @Test func saveFailureShowsAlert() async throws { apiService.ownProfileReturnValue = try .success(makeProfile()) apiService.updateOwnProfileReturnValue = .failure(.networkFailure) let viewModel = makeViewModel() try await waitForLoad(of: viewModel) viewModel.context.businessTel = "+41 79 111 22 33" let deferred = deferFulfillment(viewModel.context.observe(\.viewState.bindings.alertInfo)) { $0 != nil } viewModel.context.send(viewAction: .save) try await deferred.fulfill() } @Test func cancelWithChangesShowsUnsavedChangesAlert() async throws { apiService.ownProfileReturnValue = try .success(makeProfile()) let viewModel = makeViewModel() try await waitForLoad(of: viewModel) viewModel.context.businessTel = "+41 79 111 22 33" viewModel.context.send(viewAction: .cancel) #expect(viewModel.context.viewState.bindings.alertInfo != nil) } @Test func loadFailureOffersRetry() async throws { apiService.ownProfileReturnValue = .failure(.networkFailure) let viewModel = makeViewModel() let deferredFailure = deferFulfillment(viewModel.context.observe(\.viewState.loadFailed)) { $0 } try await deferredFailure.fulfill() apiService.ownProfileReturnValue = try .success(makeProfile()) let deferredLoad = deferFulfillment(viewModel.context.observe(\.viewState.initialProfile)) { $0 != nil } viewModel.context.send(viewAction: .retry) try await deferredLoad.fulfill() } // MARK: - Helpers private func makeViewModel() -> HealthChatProfileEditScreenViewModel { HealthChatProfileEditScreenViewModel(userSession: UserSessionMock(.init(clientProxy: clientProxy)), apiService: apiService, mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: .volatile()), userIndicatorController: UserIndicatorControllerMock()) } private func waitForLoad(of viewModel: HealthChatProfileEditScreenViewModel) async throws { let deferred = deferFulfillment(viewModel.context.observe(\.viewState.initialProfile)) { $0 != nil } try await deferred.fulfill() } private func makeProfile() throws -> HealthChatMember { try JSONDecoder().decode(HealthChatMember.self, from: Data(""" { "userId": "@alice:healthchat.ch", "displayName": "Dr. Alice Alpha", "firstName": "Alice", "lastName": "Alpha", "specialty": "Physiotherapie", "practiceName": "Praxis Alpha", "businessCity": "Zürich", "businessTel": "+41 44 123 45 67", "furtherInfo": "Old info" } """.utf8)) } }