import DatabaseClient import Dependencies import Foundation import HtmlSnapshotTesting import SharedModels import SnapshotTesting import Testing import ViewControllerLive // NOTE: Passing routes as arguments doesn't work bc they are sometimes not in the same order. @Suite("ViewControllerTests") struct ViewControllerTests { let record: SnapshotTestingConfiguration.Record = .missing @Test func sanity() { #expect(Bool(true)) } @Test func employeeViews() async throws { try await withSnapshotTesting(record: record) { try await withDependencies { $0.viewController = .liveValue $0.database.employees = .mock } operation: { @Dependency(\.viewController) var viewController @Dependency(\.database) var database var htmlString = try await viewController.render(.employee(.index)) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.employee(.form)) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.employee(.get(id: UUID(0)))) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.employee(.select(context: .purchaseOrderForm))) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.employee(.select(context: .purchaseOrderSearch))) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.employee(.update(id: UUID(0), updates: .mock))) assertSnapshot(of: htmlString, as: .html) } } } @Test func loginViews() async throws { try await withSnapshotTesting(record: record) { try await withDependencies { $0.viewController = .liveValue } operation: { @Dependency(\.viewController) var viewController let htmlString = try await viewController.render(.login(.index(next: "/purchase-orders"))) assertSnapshot(of: htmlString, as: .html) } } } // @Test // func purchaseOrderViews() async throws { // try await withSnapshotTesting(record: record) { // try await withDependencies { // $0.database.purchaseOrders = .mock // $0.viewController = .liveValue // } operation: { // @Dependency(\.viewController) var viewController // // let htmlString = try await viewController.render(.login(.index(next: "/purchase-orders"))) // assertSnapshot(of: htmlString, as: .html) // } // } // } @Test func userViews() async throws { try await withDependencies { $0.dateFormatter = .liveValue $0.database.users = .mock $0.viewController = .liveValue } operation: { @Dependency(\.database) var database @Dependency(\.viewController) var viewController var htmlString = try await viewController.render(.user(.index)) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.user(.form)) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.user(.create(.mock))) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.user(.get(id: UUID(0)))) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.user(.update(id: UUID(0), updates: .mock))) assertSnapshot(of: htmlString, as: .html) } } @Test func vendorViews() async throws { try await withDependencies { $0.dateFormatter = .liveValue $0.database.vendors = .mock $0.viewController = .liveValue } operation: { @Dependency(\.database) var database @Dependency(\.viewController) var viewController var htmlString = try await viewController.render(.vendor(.index)) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.vendor(.form)) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.vendor(.create(.mock))) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.vendor(.get(id: UUID(0)))) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.vendor(.update(id: UUID(0), updates: .mock))) assertSnapshot(of: htmlString, as: .html) } } @Test func vendorBranchViews() async throws { try await withDependencies { $0.dateFormatter = .liveValue $0.database.vendors = .mock $0.database.vendorBranches = .mock $0.viewController = .liveValue } operation: { @Dependency(\.database) var database @Dependency(\.viewController) var viewController var htmlString = try await viewController.render(.vendorBranch(.index(for: UUID(0)))) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.vendorBranch(.select(context: .purchaseOrderSearch))) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.vendorBranch(.select(context: .purchaseOrderForm))) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.vendorBranch(.create(.mock))) assertSnapshot(of: htmlString, as: .html) } } } extension ViewController { func render(_ route: ViewRoute) async throws -> String { guard let html = try await view(for: route, isHtmxRequest: true, authenticate: { _ in }) else { throw TestError() } return html.renderFormatted() } } struct TestError: Error {} extension DatabaseClient.Employees { static var mock: Self { .init( create: { _ in .mock }, delete: { _ in }, fetchAll: { _ in [Employee.mock] }, get: { _ in Employee.mock }, update: { _, _ in Employee.mock } ) } } extension DatabaseClient.Users { static var mock: Self { .init( count: { 1 }, create: { _ in User.mock }, delete: { _ in }, fetchAll: { [User.mock] }, get: { _ in User.mock }, login: { _ in User.Token.mock }, logout: { _ in }, token: { _ in User.Token.mock }, update: { _, _ in User.mock } ) } } extension DatabaseClient.Vendors { static var mock: Self { .init( create: { _ in Vendor.mock }, delete: { _ in }, fetchAll: { _ in [Vendor.mock] }, get: { _, _ in Vendor.mock }, update: { _, _, _ in Vendor.mock } ) } } extension DatabaseClient.VendorBranches { static var mock: Self { .init( create: { _ in VendorBranch.mock }, delete: { _ in }, fetchAll: { _ in [VendorBranch.mock] }, fetchAllWithDetail: { [VendorBranch.Detail.mock] }, get: { _ in VendorBranch.mock }, update: { _, _ in VendorBranch.mock } ) } } extension Date { static var mock: Self { Date(timeIntervalSince1970: 1_234_567_890) } } extension Employee { static var mock: Self { Employee( id: UUID(0), createdAt: Date(timeIntervalSince1970: 1_234_567_890), firstName: "Testy", lastName: "McTestface", updatedAt: Date(timeIntervalSince1970: 1_234_567_890) ) } } extension Employee.Create { static var mock: Self { .init(firstName: "Testy", lastName: "McTestface") } func employeeMock() -> Employee { @Dependency(\.date.now) var now return .init( id: UUID(0), createdAt: Date(timeIntervalSince1970: 1_234_567_890), firstName: firstName, lastName: lastName, updatedAt: Date(timeIntervalSince1970: 1_234_567_890) ) } } extension Employee.Update { static var mock: Self { .init(firstName: "Testy", lastName: "McTestface", active: false) } } extension User { static var mock: Self { .init(id: UUID(0), email: "test@example.com", username: "test") } } extension User.Create { static var mock: Self { .init(username: "test", email: "test@example.com", password: "super-secret", confirmPassword: "super-secret") } } extension User.Token { static var mock: Self { .init(id: UUID(1), userID: UUID(0), value: "test-token") } } extension User.Update { static var mock: Self { User.Update(username: "test", email: "test@test.com") } } extension Vendor { static var mock: Self { .init(id: UUID(0), name: "Test", branches: nil, createdAt: .mock, updatedAt: .mock) } } extension Vendor.Create { static var mock: Self { .init(name: "Test") } } extension Vendor.Update { static var mock: Self { .init(name: "Test") } } extension VendorBranch { static var mock: Self { .init(id: UUID(1), name: "Mock", vendorID: UUID(0), createdAt: .mock, updatedAt: .mock) } } extension VendorBranch.Create { static var mock: Self { .init(name: "Mock", vendorID: UUID(0)) } } extension VendorBranch.Detail { static var mock: Self { .init(id: UUID(1), name: "Mock", vendor: .mock, createdAt: .mock, updatedAt: .mock) } } extension PurchaseOrder { static var mock: Self { .init( id: 1, workOrder: 12245, materials: "foo", customer: "Testy McTestface", truckStock: true, createdBy: .mock, createdFor: .mock, vendorBranch: .mock, createdAt: .mock, updatedAt: .mock ) } }