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 $0.dateFormatter = .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 $0.dateFormatter = .mock } 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.dateFormatter = .mock $0.database.purchaseOrders = .mock $0.viewController = .liveValue } operation: { @Dependency(\.viewController) var viewController var htmlString = try await viewController.render(.purchaseOrder(.index)) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.purchaseOrder(.form)) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.purchaseOrder(.create(.mock))) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.purchaseOrder(.get(id: 1))) assertSnapshot(of: htmlString, as: .html) htmlString = try await viewController.render(.purchaseOrder(.page(page: 1, limit: 25))) assertSnapshot(of: htmlString, as: .html) for context in SiteRoute.View.PurchaseOrderRoute.Search.Context.allCases { htmlString = try await viewController.render(.purchaseOrder(.search(.index( context: context, table: true )))) assertSnapshot(of: htmlString, as: .html) } } } } @Test func userViews() async throws { try await withSnapshotTesting(record: record) { try await withDependencies { $0.dateFormatter = .mock $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 withSnapshotTesting(record: record) { try await withDependencies { $0.dateFormatter = .mock $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 withSnapshotTesting(record: record) { try await withDependencies { $0.dateFormatter = .mock $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: SiteRoute.View) async throws -> String { let html = try await view( for: route, isHtmxRequest: true, logger: .init(label: "tests"), authenticate: { _ in } ) 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.PurchaseOrders { static var mock: Self { .init( create: { _ in .mock }, fetchAll: { [.mock] }, fetchPage: { _ in .init(items: [.mock], metadata: .init(page: 1, per: 1, total: 1)) }, get: { _ in .mock }, delete: { _ in }, search: { _, _ in .init(items: [.mock], metadata: .init(page: 1, per: 1, total: 1)) } ) } } 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 }, resetPassword: { _, _ 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 { let formatter = ISO8601DateFormatter() return formatter.date(from: "2025-01-31T02:22:40Z")! } } extension DateFormatterKey { static var mock: Self { .init(string: { _ in "01/31/2025" }) } } extension Employee { static var mock: Self { Employee( id: UUID(0), createdAt: .mock, firstName: "Testy", lastName: "McTestface", updatedAt: .mock ) } } 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: .mock, firstName: firstName, lastName: lastName, updatedAt: .mock ) } } 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 ) } } extension PurchaseOrder.Create { static var mock: Self { .init( materials: "bar", customer: "Testy McTestface", createdByID: UUID(0), createdForID: UUID(0), vendorBranchID: UUID(0) ) } }