feat: Moves vendor branch views to their own files, starts to implement snapshot testing for html
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"originHash" : "dab5887e7f33b2dba9c9b86598c47d541464dda5c29e084c8d38dec82923c953",
|
"originHash" : "20332ed810f0f8bda6b1a104968eae13e98b479c32a983c890e6526a4940c7ad",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "async-http-client",
|
"identity" : "async-http-client",
|
||||||
@@ -316,6 +316,15 @@
|
|||||||
"version" : "1.1.0"
|
"version" : "1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "swift-snapshot-testing",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/pointfreeco/swift-snapshot-testing.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "2e6a85b73fc14e27d7542165ae73b1a10516ca9a",
|
||||||
|
"version" : "1.17.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "swift-syntax",
|
"identity" : "swift-syntax",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ let package = Package(
|
|||||||
.executable(name: "App", targets: ["App"]),
|
.executable(name: "App", targets: ["App"]),
|
||||||
.library(name: "SharedModels", targets: ["SharedModels"]),
|
.library(name: "SharedModels", targets: ["SharedModels"]),
|
||||||
.library(name: "DatabaseClient", targets: ["DatabaseClient"]),
|
.library(name: "DatabaseClient", targets: ["DatabaseClient"]),
|
||||||
.library(name: "DatabaseClientLive", targets: ["DatabaseClientLive"])
|
.library(name: "DatabaseClientLive", targets: ["DatabaseClientLive"]),
|
||||||
|
.library(name: "HtmlSnapshotTesting", targets: ["HtmlSnapshotTesting"])
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
// 💧 A server-side Swift web framework.
|
// 💧 A server-side Swift web framework.
|
||||||
@@ -26,7 +27,8 @@ let package = Package(
|
|||||||
.package(url: "https://github.com/sliemeobn/elementary-htmx.git", from: "0.4.0"),
|
.package(url: "https://github.com/sliemeobn/elementary-htmx.git", from: "0.4.0"),
|
||||||
.package(url: "https://github.com/vapor-community/vapor-elementary.git", from: "0.1.0"),
|
.package(url: "https://github.com/vapor-community/vapor-elementary.git", from: "0.1.0"),
|
||||||
.package(url: "https://github.com/pointfreeco/swift-url-routing.git", from: "0.6.2"),
|
.package(url: "https://github.com/pointfreeco/swift-url-routing.git", from: "0.6.2"),
|
||||||
.package(url: "https://github.com/pointfreeco/vapor-routing.git", from: "0.1.3")
|
.package(url: "https://github.com/pointfreeco/vapor-routing.git", from: "0.1.3"),
|
||||||
|
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing.git", from: "1.17.7")
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.executableTarget(
|
.executableTarget(
|
||||||
@@ -89,6 +91,20 @@ let package = Package(
|
|||||||
.product(name: "FluentSQLiteDriver", package: "fluent-sqlite-driver")
|
.product(name: "FluentSQLiteDriver", package: "fluent-sqlite-driver")
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
.target(
|
||||||
|
name: "HtmlSnapshotTesting",
|
||||||
|
dependencies: [
|
||||||
|
.product(name: "Elementary", package: "elementary"),
|
||||||
|
.product(name: "SnapshotTesting", package: "swift-snapshot-testing")
|
||||||
|
]
|
||||||
|
),
|
||||||
|
.testTarget(
|
||||||
|
name: "HtmlSnapshotTestingTests",
|
||||||
|
dependencies: [
|
||||||
|
.target(name: "App"),
|
||||||
|
.target(name: "HtmlSnapshotTesting")
|
||||||
|
]
|
||||||
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "SharedModels",
|
name: "SharedModels",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ extension SharedModels.ViewRoute {
|
|||||||
|
|
||||||
var middleware: [any Middleware]? {
|
var middleware: [any Middleware]? {
|
||||||
switch self {
|
switch self {
|
||||||
|
case .index: return viewProtectedMiddleware
|
||||||
case let .employee(route): return route.middleware
|
case let .employee(route): return route.middleware
|
||||||
case .login: return nil
|
case .login: return nil
|
||||||
case let .purchaseOrder(route): return route.middleware
|
case let .purchaseOrder(route): return route.middleware
|
||||||
@@ -28,6 +29,9 @@ extension SharedModels.ViewRoute {
|
|||||||
func handle(request: Request) async throws -> any AsyncResponseEncodable {
|
func handle(request: Request) async throws -> any AsyncResponseEncodable {
|
||||||
@Dependency(\.database.users) var users
|
@Dependency(\.database.users) var users
|
||||||
switch self {
|
switch self {
|
||||||
|
case .index:
|
||||||
|
return request.redirect(to: Self.router.path(for: .purchaseOrder(.index)))
|
||||||
|
|
||||||
case let .employee(route):
|
case let .employee(route):
|
||||||
return try await route.handle(request: request)
|
return try await route.handle(request: request)
|
||||||
|
|
||||||
@@ -348,6 +352,17 @@ extension SharedModels.ViewRoute.VendorBranchRoute {
|
|||||||
@Dependency(\.database) var database
|
@Dependency(\.database) var database
|
||||||
|
|
||||||
switch self {
|
switch self {
|
||||||
|
case let .index(for: vendorID):
|
||||||
|
guard let vendorID else {
|
||||||
|
throw Abort(.badRequest, reason: "Vendor id not supplied")
|
||||||
|
}
|
||||||
|
return try await request.render {
|
||||||
|
try await VendorBranchList(
|
||||||
|
vendorID: vendorID,
|
||||||
|
branches: database.vendorBranches.fetchAll(.for(vendorID: vendorID))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
case let .select(context: context):
|
case let .select(context: context):
|
||||||
return try await request.render {
|
return try await request.render {
|
||||||
try await context.toHTML(branches: database.vendorBranches.fetchAllWithDetail())
|
try await context.toHTML(branches: database.vendorBranches.fetchAllWithDetail())
|
||||||
@@ -355,7 +370,7 @@ extension SharedModels.ViewRoute.VendorBranchRoute {
|
|||||||
|
|
||||||
case let .create(branch):
|
case let .create(branch):
|
||||||
return try await request.render {
|
return try await request.render {
|
||||||
try await VendorDetail.BranchRow(branch: database.vendorBranches.create(branch))
|
try await VendorBranchList.Row(branch: database.vendorBranches.create(branch))
|
||||||
}
|
}
|
||||||
|
|
||||||
case let .delete(id: id):
|
case let .delete(id: id):
|
||||||
|
|||||||
@@ -117,11 +117,13 @@ enum IDKey: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum Branch: CustomStringConvertible {
|
enum Branch: CustomStringConvertible {
|
||||||
|
case list
|
||||||
case form
|
case form
|
||||||
case row(id: VendorBranch.ID)
|
case row(id: VendorBranch.ID)
|
||||||
|
|
||||||
var description: String {
|
var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
|
case .list: return "list"
|
||||||
case .form: return "form"
|
case .form: return "form"
|
||||||
case let .row(id): return id.uuidString
|
case let .row(id): return id.uuidString
|
||||||
}
|
}
|
||||||
@@ -148,7 +150,7 @@ enum IDKey: CustomStringConvertible {
|
|||||||
|
|
||||||
var description: String {
|
var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
case let .content: return "content"
|
case .content: return "content"
|
||||||
case let .row(id): return "\(id)"
|
case let .row(id): return "\(id)"
|
||||||
case .search: return "search"
|
case .search: return "search"
|
||||||
case .table: return "table"
|
case .table: return "table"
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import Elementary
|
import Elementary
|
||||||
|
import SharedModels
|
||||||
|
import URLRouting
|
||||||
|
|
||||||
struct ToggleFormButton: HTML {
|
struct ToggleFormButton: HTML {
|
||||||
var content: some HTML<HTMLTag.a> {
|
var content: some HTML<HTMLTag.a> {
|
||||||
@@ -24,6 +26,13 @@ enum Button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func close(id: IDKey, resetURL route: ViewRoute? = nil) -> some HTML<HTMLTag.button> {
|
||||||
|
close(
|
||||||
|
id: id.description,
|
||||||
|
resetURL: route != nil ? ViewRoute.router.path(for: route!) : nil
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
static func update() -> some HTML<HTMLTag.button> {
|
static func update() -> some HTML<HTMLTag.button> {
|
||||||
button(.class("btn-update")) { "Update" }
|
button(.class("btn-update")) { "Update" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,4 +10,9 @@ enum Img {
|
|||||||
static func search(width: Int = 30, height: Int = 30) -> some HTML<HTMLTag.img> {
|
static func search(width: Int = 30, height: Int = 30) -> some HTML<HTMLTag.img> {
|
||||||
img(.src("/images/search.svg"), .width(width), .height(height))
|
img(.src("/images/search.svg"), .width(width), .height(height))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Sendable
|
||||||
|
static func trashCan(width: Int = 30, height: Int = 30) -> some HTML<HTMLTag.img> {
|
||||||
|
img(.src("/images/trash-can.svg"), .width(width), .height(height))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
34
Sources/App/Views/VendorBranches/VendorBranchForm.swift
Normal file
34
Sources/App/Views/VendorBranches/VendorBranchForm.swift
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import Elementary
|
||||||
|
import ElementaryHTMX
|
||||||
|
import SharedModels
|
||||||
|
|
||||||
|
struct VendorBranchForm: HTML {
|
||||||
|
let vendorID: Vendor.ID
|
||||||
|
|
||||||
|
var content: some HTML {
|
||||||
|
form(
|
||||||
|
.id(.branch(.form)),
|
||||||
|
.hx.post(route: .vendorBranch(.index())),
|
||||||
|
.hx.target(.id(.branch(.list))),
|
||||||
|
.hx.swap(.beforeEnd),
|
||||||
|
.hx.on(.afterRequest, .ifSuccessful(.resetForm))
|
||||||
|
) {
|
||||||
|
input(.type(.hidden), .name("vendorID"), .value(vendorID.uuidString))
|
||||||
|
input(
|
||||||
|
.type(.text), .class("col-9"), .name("name"), .placeholder("Add branch..."), .required,
|
||||||
|
// .hx.post(route: .vendorBranch(.index())),
|
||||||
|
.hx.trigger(.event(.keyup).changed().delay("800ms")) // ,
|
||||||
|
// .hx.target(.id(.branch(.list))),
|
||||||
|
// .hx.swap(.beforeEnd),
|
||||||
|
// .custom(name: "hx-on::after-request", value: "if(event.detail.successful) this.reset();")
|
||||||
|
)
|
||||||
|
button(
|
||||||
|
.type(.submit),
|
||||||
|
.class("btn-secondary"),
|
||||||
|
.style("float: right; padding: 10px 50px;"),
|
||||||
|
.hx.target(.id(.branch(.list))),
|
||||||
|
.hx.swap(.beforeEnd)
|
||||||
|
) { "+" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
46
Sources/App/Views/VendorBranches/VendorBranchList.swift
Normal file
46
Sources/App/Views/VendorBranches/VendorBranchList.swift
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import Elementary
|
||||||
|
import ElementaryHTMX
|
||||||
|
import SharedModels
|
||||||
|
|
||||||
|
struct VendorBranchList: HTML {
|
||||||
|
let vendorID: Vendor.ID
|
||||||
|
let branches: [VendorBranch]?
|
||||||
|
|
||||||
|
var content: some HTML {
|
||||||
|
if let branches {
|
||||||
|
ul(.id(.branch(.list))) {
|
||||||
|
for branch in branches {
|
||||||
|
Row(branch: branch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
div(
|
||||||
|
.hx.get(route: .vendorBranch(.index(for: vendorID))),
|
||||||
|
.hx.target(.this),
|
||||||
|
.hx.indicator(".hx-indicator"),
|
||||||
|
.hx.trigger(.event(.revealed))
|
||||||
|
) {
|
||||||
|
Img.spinner().attributes(.class("hx-indicator"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Row: HTML {
|
||||||
|
let branch: VendorBranch
|
||||||
|
|
||||||
|
var content: some HTML<HTMLTag.li> {
|
||||||
|
li(.id(.branch(.row(id: branch.id))), .class("branch-row")) {
|
||||||
|
span(.class("label")) { branch.name.capitalized }
|
||||||
|
button(
|
||||||
|
.class("btn"),
|
||||||
|
.hx.delete(route: .vendorBranch(.delete(id: branch.id))),
|
||||||
|
.hx.target(.id(.branch(.row(id: branch.id)))),
|
||||||
|
.hx.swap(.outerHTML.transition(true).swap("0.5s"))
|
||||||
|
) {
|
||||||
|
Img.trashCan().attributes(.style("margin-top: 5px;"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ import Elementary
|
|||||||
import ElementaryHTMX
|
import ElementaryHTMX
|
||||||
import SharedModels
|
import SharedModels
|
||||||
|
|
||||||
// TODO: Lazy Load branches when view appears.
|
|
||||||
struct VendorDetail: HTML {
|
struct VendorDetail: HTML {
|
||||||
|
|
||||||
let vendor: Vendor
|
let vendor: Vendor
|
||||||
@@ -11,8 +10,8 @@ struct VendorDetail: HTML {
|
|||||||
Float(shouldDisplay: true) {
|
Float(shouldDisplay: true) {
|
||||||
VendorForm(.formOnly(vendor))
|
VendorForm(.formOnly(vendor))
|
||||||
h2(.style("margin-left: 20px; font-size: 1.5em;"), .class("label")) { "Branches" }
|
h2(.style("margin-left: 20px; font-size: 1.5em;"), .class("label")) { "Branches" }
|
||||||
branchForm
|
VendorBranchForm(vendorID: vendor.id)
|
||||||
branches
|
VendorBranchList(vendorID: vendor.id, branches: nil)
|
||||||
} closeButton: {
|
} closeButton: {
|
||||||
Button.close(id: "float")
|
Button.close(id: "float")
|
||||||
.attributes(
|
.attributes(
|
||||||
@@ -23,61 +22,4 @@ struct VendorDetail: HTML {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: What route for here??
|
|
||||||
var branchForm: some HTML {
|
|
||||||
form(
|
|
||||||
.id(.branch(.form)),
|
|
||||||
.hx.post("/vendors/branches"),
|
|
||||||
.hx.target("#branches"),
|
|
||||||
.hx.swap(.beforeEnd),
|
|
||||||
.hx.on(.afterRequest, .ifSuccessful(.resetForm))
|
|
||||||
// .custom(name: "hx-on::after-request", value: "if(event.detail.successful) this.reset();")
|
|
||||||
) {
|
|
||||||
input(.type(.hidden), .name("vendorID"), .value(vendor.id.uuidString))
|
|
||||||
input(
|
|
||||||
.type(.text), .class("col-9"), .name("name"), .placeholder("Add branch..."), .required,
|
|
||||||
// FIX: route
|
|
||||||
// .hx.post(route: .vendorBranch(.index(for: vendor.id))),
|
|
||||||
.hx.trigger(.event(.keyup).changed().delay("800ms")),
|
|
||||||
.hx.target("#branches"),
|
|
||||||
.hx.swap(.beforeEnd) // ,
|
|
||||||
// .custom(name: "hx-on::after-request", value: "if(event.detail.successful) this.reset();")
|
|
||||||
)
|
|
||||||
button(
|
|
||||||
.type(.submit),
|
|
||||||
.class("btn-secondary"),
|
|
||||||
.style("float: right; padding: 10px 50px;"),
|
|
||||||
.hx.target("#branch-table"),
|
|
||||||
.hx.swap(.beforeEnd)
|
|
||||||
) { "+" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var branches: some HTML {
|
|
||||||
ul(.id("branches")) {
|
|
||||||
for branch in vendor.branches ?? [] {
|
|
||||||
BranchRow(branch: branch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BranchRow: HTML {
|
|
||||||
let branch: VendorBranch
|
|
||||||
|
|
||||||
var content: some HTML<HTMLTag.li> {
|
|
||||||
li(.id(.branch(.row(id: branch.id))), .class("branch-row")) {
|
|
||||||
span(.class("label")) { branch.name.capitalized }
|
|
||||||
button(
|
|
||||||
.class("btn"),
|
|
||||||
.hx.delete(route: .vendorBranch(.delete(id: branch.id))),
|
|
||||||
.hx.target(.id(.branch(.row(id: branch.id)))),
|
|
||||||
.hx.swap(.outerHTML.transition(true).swap("0.5s"))
|
|
||||||
) {
|
|
||||||
img(.src("/images/trash-can.svg"), .width(30), .height(30), .style("margin-top: 5px;"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,13 @@ public func configure(_ app: Application) async throws {
|
|||||||
|
|
||||||
app.mount(
|
app.mount(
|
||||||
SiteRoute.router,
|
SiteRoute.router,
|
||||||
middleware: { $0.middleware() },
|
middleware: {
|
||||||
|
if app.environment == .testing {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return $0.middleware()
|
||||||
|
}
|
||||||
|
},
|
||||||
use: siteHandler
|
use: siteHandler
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
12
Sources/HtmlSnapshotTesting/HtmlSnapshotTesting.swift
Normal file
12
Sources/HtmlSnapshotTesting/HtmlSnapshotTesting.swift
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import Elementary
|
||||||
|
@preconcurrency import SnapshotTesting
|
||||||
|
|
||||||
|
public extension Snapshotting where Value == (any HTML), Format == String {
|
||||||
|
static var html: Snapshotting {
|
||||||
|
var snapshotting = SimplySnapshotting.lines
|
||||||
|
.pullback { (html: any HTML) in html.renderFormatted() }
|
||||||
|
|
||||||
|
snapshotting.pathExtension = "html"
|
||||||
|
return snapshotting
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,11 +2,9 @@ import CasePathsCore
|
|||||||
import Foundation
|
import Foundation
|
||||||
@preconcurrency import URLRouting
|
@preconcurrency import URLRouting
|
||||||
|
|
||||||
// swiftlint:disable file_length
|
|
||||||
// TODO: Need vendor branch index route, to load branches when vendor form is displayed.
|
|
||||||
// Also need a home / index route that will redirect to login or purchase orders.
|
|
||||||
public enum ViewRoute: Sendable, Equatable {
|
public enum ViewRoute: Sendable, Equatable {
|
||||||
|
|
||||||
|
case index
|
||||||
case employee(EmployeeRoute)
|
case employee(EmployeeRoute)
|
||||||
case login(LoginRoute)
|
case login(LoginRoute)
|
||||||
case purchaseOrder(PurchaseOrderRoute)
|
case purchaseOrder(PurchaseOrderRoute)
|
||||||
@@ -15,6 +13,9 @@ public enum ViewRoute: Sendable, Equatable {
|
|||||||
case vendorBranch(VendorBranchRoute)
|
case vendorBranch(VendorBranchRoute)
|
||||||
|
|
||||||
public static let router = OneOf {
|
public static let router = OneOf {
|
||||||
|
Route(.case(Self.index)) {
|
||||||
|
Method.get
|
||||||
|
}
|
||||||
Route(.case(Self.employee)) { EmployeeRoute.router }
|
Route(.case(Self.employee)) { EmployeeRoute.router }
|
||||||
Route(.case(Self.login)) { LoginRoute.router }
|
Route(.case(Self.login)) { LoginRoute.router }
|
||||||
Route(.case(Self.purchaseOrder)) { PurchaseOrderRoute.router }
|
Route(.case(Self.purchaseOrder)) { PurchaseOrderRoute.router }
|
||||||
@@ -388,6 +389,7 @@ public extension ViewRoute {
|
|||||||
enum VendorBranchRoute: Sendable, Equatable {
|
enum VendorBranchRoute: Sendable, Equatable {
|
||||||
case create(VendorBranch.Create)
|
case create(VendorBranch.Create)
|
||||||
case delete(id: VendorBranch.ID)
|
case delete(id: VendorBranch.ID)
|
||||||
|
case index(for: Vendor.ID? = nil)
|
||||||
case select(context: ViewRoute.SelectContext)
|
case select(context: ViewRoute.SelectContext)
|
||||||
|
|
||||||
public static let router = OneOf {
|
public static let router = OneOf {
|
||||||
@@ -406,6 +408,13 @@ public extension ViewRoute {
|
|||||||
Path { "vendors"; "branches"; VendorBranch.ID.parser() }
|
Path { "vendors"; "branches"; VendorBranch.ID.parser() }
|
||||||
Method.delete
|
Method.delete
|
||||||
}
|
}
|
||||||
|
Route(.case(Self.index(for:))) {
|
||||||
|
Path { "vendors"; "branches" }
|
||||||
|
Method.get
|
||||||
|
Query {
|
||||||
|
Optionally { Field("vendorID") { Vendor.ID.parser() } }
|
||||||
|
}
|
||||||
|
}
|
||||||
Route(.case(Self.select(context:))) {
|
Route(.case(Self.select(context:))) {
|
||||||
Path { "vendors"; "branches"; "select" }
|
Path { "vendors"; "branches"; "select" }
|
||||||
Method.get
|
Method.get
|
||||||
@@ -416,5 +425,3 @@ public extension ViewRoute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// swiftlint:enable file_length
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
@testable import App
|
||||||
|
import Elementary
|
||||||
|
import HtmlSnapshotTesting
|
||||||
|
import SnapshotTesting
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
final class SnapshotTestingTests: XCTestCase {
|
||||||
|
func testSimple() {
|
||||||
|
let doc = MainPage.loggedIn(next: nil)
|
||||||
|
assertSnapshot(of: doc, as: .html)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Purchase Orders</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
|
||||||
|
<script src="/js/main.js"></script>
|
||||||
|
<link rel="stylesheet" href="/css/main.css">
|
||||||
|
<link rel="icon" href="/images/favicon.ico" type="image/x-icon">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="header">
|
||||||
|
<div id="logo">HHE - Purchase Orders</div>
|
||||||
|
<div class="sidepanel" id="sidepanel">
|
||||||
|
<a href="javascript:void(0)" class="closebtn" onclick="closeSidepanel()">x</a><a hx-get="/purchase-orders?page=1&limit=50" hx-target="body" hx-push-url="true">Purchase Orders</a><a hx-get="/users" hx-target="body" hx-push-url="true">Users</a><a hx-get="/employees" hx-target="body" hx-push-url="true">Employees</a><a hx-get="/vendors" hx-target="body" hx-push-url="true">Vendors</a>
|
||||||
|
<div style="border-bottom: 1px solid grey; margin-bottom: 5px;"></div>
|
||||||
|
Logout<a hx-post="/logout" hx-target="#content" hx-swap="outerHTML" hx-trigger="click"></a>
|
||||||
|
</div>
|
||||||
|
<button class="openbtn" onclick="openSidepanel()">
|
||||||
|
<img src="/images/menu.svg" style="width: 30px;, height: 30px;">
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
<div class="container" style="padding: 20px 20px;">
|
||||||
|
<h1>Purchase Orders</h1>
|
||||||
|
<br>
|
||||||
|
<p class="secondary"><i></i></p>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
<div hx-get="/purchase-orders" hx-push-url="true" hx-target="body" hx-trigger="revealed" hx-indicator=".hx-indicator">
|
||||||
|
<img src="/images/spinner.svg" width="30" height="30" class="hx-indicator">
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -31,6 +31,18 @@ struct VendorBranchViewRouteTests {
|
|||||||
#expect(route == .vendorBranch(.delete(id: id)))
|
#expect(route == .vendorBranch(.delete(id: id)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func index() throws {
|
||||||
|
let id = UUID(0)
|
||||||
|
var request = URLRequestData(
|
||||||
|
method: "GET",
|
||||||
|
path: "/vendors/branches",
|
||||||
|
query: ["vendorID": ["\(id)"]]
|
||||||
|
)
|
||||||
|
let route = try router.parse(&request)
|
||||||
|
#expect(route == .vendorBranch(.index(for: id)))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func select() throws {
|
func select() throws {
|
||||||
var request = URLRequestData(
|
var request = URLRequestData(
|
||||||
|
|||||||
Reference in New Issue
Block a user