1
This commit is contained in:
@@ -4,51 +4,119 @@ import SwiftUI
|
||||
@main
|
||||
struct SpreadsheetLayoutTests {
|
||||
@MainActor static func main() throws {
|
||||
let source = URL(fileURLWithPath: CommandLine.arguments[1])
|
||||
let layout = try JSONDecoder().decode(SpreadsheetLayout.self, from: Data(contentsOf: source)).validated()
|
||||
_ = NSApplication.shared
|
||||
NSApplication.shared.appearance = NSAppearance(named: .aqua)
|
||||
let data = try Data(contentsOf: URL(fileURLWithPath: CommandLine.arguments[1]))
|
||||
let layout = try JSONDecoder().decode(SpreadsheetLayout.self, from: data).validated()
|
||||
let sheet = layout.sheets[0]
|
||||
let narrow = SpreadsheetZoom.width.scale(sheet: sheet, viewport: CGSize(width: 800, height: 500))
|
||||
let wide = SpreadsheetZoom.width.scale(sheet: sheet, viewport: CGSize(width: 1800, height: 900))
|
||||
precondition(narrow * sheet.width <= 752)
|
||||
precondition(wide == 1)
|
||||
let page = SpreadsheetZoom.page.scale(sheet: sheet, viewport: CGSize(width: 800, height: 500))
|
||||
precondition(page * sheet.height <= 452)
|
||||
precondition(SpreadsheetZoom.actual.scale(sheet: sheet, viewport: .zero) == 1)
|
||||
precondition(sheet.cells.first { $0.reference == "H30" }?.style.rotation == 255)
|
||||
precondition(sheet.cells.first { $0.reference == "C25" }?.text == "001234567890")
|
||||
precondition(sheet.cells.first { $0.reference == "F27" }?.text.trimmingCharacters(in: .whitespaces) == "544.30")
|
||||
let signature = sheet.cells.first { $0.reference == "A30" }!
|
||||
precondition(signature.textClip.width > signature.width)
|
||||
print("Workbook layout, zoom bounds, vertical text and exact values passed")
|
||||
if CommandLine.arguments.count > 2 {
|
||||
let folder = URL(fileURLWithPath: CommandLine.arguments[2], isDirectory: true)
|
||||
try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
|
||||
_ = NSApplication.shared
|
||||
NSApplication.shared.appearance = NSAppearance(named: .aqua)
|
||||
let canvas = SpreadsheetCanvasView(sheet: sheet, scale: 1)
|
||||
try capture(canvas, to: folder.appendingPathComponent("sheet.png"))
|
||||
for size in [CGSize(width: 1080, height: 620), CGSize(width: 820, height: 480)] {
|
||||
let host = NSHostingView(rootView: ExpenseSpreadsheetPreview(layout: layout))
|
||||
host.frame = NSRect(origin: .zero, size: size)
|
||||
let window = NSWindow(contentRect: host.frame, styleMask: [.borderless], backing: .buffered, defer: false)
|
||||
window.contentView = host
|
||||
host.layoutSubtreeIfNeeded()
|
||||
RunLoop.current.run(until: Date().addingTimeInterval(0.5))
|
||||
try capture(host, to: folder.appendingPathComponent("preview-\(Int(size.width)).png"))
|
||||
window.orderOut(nil)
|
||||
}
|
||||
print("Native preview snapshots written to \(folder.path)")
|
||||
let cells = Dictionary(uniqueKeysWithValues: sheet.cells.map { ($0.reference, $0) })
|
||||
let canvas = SpreadsheetCanvasView(sheet: sheet, scale: 1)
|
||||
let placements = sheet.cells.filter { !$0.text.isEmpty }.map { canvas.placement(for: $0) }
|
||||
let overflow = placements.compactMap(\.overflow)
|
||||
for ref in ["A30", "D30", "A31"] {
|
||||
let cell = cells[ref]!
|
||||
precondition(canvas.verticalSegments(x: cell.rect.maxX, top: cell.rect.minY,
|
||||
bottom: cell.rect.maxY, overflow: overflow).isEmpty,
|
||||
"A vertical line still crosses \(ref)")
|
||||
}
|
||||
let boundary = cells["B30"]!
|
||||
precondition(!canvas.verticalSegments(x: boundary.rect.maxX, top: boundary.rect.minY,
|
||||
bottom: boundary.rect.maxY, overflow: overflow).isEmpty,
|
||||
"An unrelated border was removed")
|
||||
let anchor = cells["A30"]!
|
||||
let shortLabel = SpreadsheetCell(reference: anchor.reference, x: anchor.x, y: anchor.y,
|
||||
width: anchor.width, height: anchor.height, text: "签",
|
||||
style: anchor.style, overflowLeft: anchor.overflowLeft,
|
||||
overflowRight: anchor.overflowRight)
|
||||
precondition(canvas.placement(for: shortLabel).overflow == nil)
|
||||
let blockedLabel = SpreadsheetCell(reference: anchor.reference, x: anchor.x, y: anchor.y,
|
||||
width: anchor.width, height: anchor.height, text: anchor.text,
|
||||
style: anchor.style, overflowLeft: anchor.x,
|
||||
overflowRight: anchor.x + anchor.width)
|
||||
precondition(canvas.placement(for: blockedLabel).overflow == nil)
|
||||
for ref in ["F27", "H29"] {
|
||||
let placed = canvas.placement(for: cells[ref]!)
|
||||
let paragraph = placed.text.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as! NSParagraphStyle
|
||||
precondition(paragraph.alignment == .right)
|
||||
precondition(placed.text.string == "447.00")
|
||||
}
|
||||
let signature = canvas.placement(for: cells["H30"]!)
|
||||
precondition(canvas.placement(for: cells["A29"]!).text.string.contains("\nTOTAL PAYMENT (A)"))
|
||||
precondition(signature.text.string == "收\n款\n人\n签\n字")
|
||||
precondition(abs(signature.box.maxY - cells["H30"]!.rect.maxY) < 2)
|
||||
precondition(cells["H30"]!.style.borders["right"]?.style == "medium")
|
||||
for size in [CGSize(width: 800, height: 500), CGSize(width: 1200, height: 760)] {
|
||||
let scale = SpreadsheetZoom.width.scale(sheet: sheet, viewport: size)
|
||||
precondition(scale * sheet.width <= size.width - 48)
|
||||
let page = SpreadsheetZoom.page.scale(sheet: sheet, viewport: size)
|
||||
precondition(page * sheet.height <= size.height - 48)
|
||||
}
|
||||
let expanded = SpreadsheetPreviewWindow()
|
||||
expanded.show(layout: layout, title: "Test")
|
||||
let first = expanded.window!
|
||||
precondition(first.styleMask.contains(.resizable))
|
||||
expanded.show(layout: layout, title: "Template")
|
||||
precondition(expanded.window === first)
|
||||
expanded.close()
|
||||
precondition(expanded.window == nil)
|
||||
print("Overflow borders, accounting alignment, stacked signature, merge edges, zoom and window cleanup passed")
|
||||
|
||||
let folder = URL(fileURLWithPath: CommandLine.arguments[2], isDirectory: true)
|
||||
try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
|
||||
let bitmap = try capture(canvas, to: folder.appendingPathComponent("sheet.png"))
|
||||
let ratio = CGFloat(bitmap.pixelsWide) / canvas.bounds.width
|
||||
func luminance(x: CGFloat, y: CGFloat) -> CGFloat {
|
||||
let color = bitmap.colorAt(x: Int(x * ratio), y: Int(y * ratio))!.usingColorSpace(.deviceRGB)!
|
||||
return (color.redComponent + color.greenComponent + color.blueComponent) / 3
|
||||
}
|
||||
// Pixel checks away from glyphs catch border regressions in actual drawing,
|
||||
// not just matching JSON models or two instances of the same renderer.
|
||||
for ref in ["A30", "D30", "A31"] {
|
||||
let cell = cells[ref]!
|
||||
precondition(luminance(x: cell.rect.maxX, y: cell.rect.minY + 15) > 0.97)
|
||||
}
|
||||
precondition(luminance(x: boundary.rect.maxX, y: boundary.rect.minY + 15) < 0.9)
|
||||
for width in [620, 820, 1200] {
|
||||
let host = NSHostingView(rootView: ExpenseDocumentPreview(layout: layout))
|
||||
let window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: width, height: 700),
|
||||
styleMask: [.borderless], backing: .buffered, defer: false)
|
||||
window.contentView = host
|
||||
host.layoutSubtreeIfNeeded()
|
||||
RunLoop.current.run(until: Date().addingTimeInterval(0.3))
|
||||
_ = try capture(host, to: folder.appendingPathComponent("preview-\(width).png"))
|
||||
window.orderOut(nil)
|
||||
}
|
||||
if CommandLine.arguments.count > 3 {
|
||||
let alternateData = try Data(contentsOf: URL(fileURLWithPath: CommandLine.arguments[3]))
|
||||
let alternate = try JSONDecoder().decode(SpreadsheetLayout.self, from: alternateData).validated()
|
||||
let host = NSHostingView(rootView: ExpenseDocumentPreview(layout: alternate,
|
||||
title: "报销单模板", preferredSheet: "说明页"))
|
||||
let window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 620, height: 500),
|
||||
styleMask: [.borderless], backing: .buffered, defer: false)
|
||||
window.contentView = host
|
||||
host.layoutSubtreeIfNeeded()
|
||||
RunLoop.current.run(until: Date().addingTimeInterval(0.3))
|
||||
_ = try capture(host, to: folder.appendingPathComponent("template-selected-sheet.png"))
|
||||
precondition(containsCanvas(host, named: "说明页"), "Template sheet selection was ignored")
|
||||
host.rootView = ExpenseDocumentPreview(layout: alternate, title: "报销单模板", preferredSheet: "横向费用单")
|
||||
host.layoutSubtreeIfNeeded()
|
||||
RunLoop.current.run(until: Date().addingTimeInterval(0.3))
|
||||
_ = try capture(host, to: folder.appendingPathComponent("template-alternate.png"))
|
||||
precondition(containsCanvas(host, named: "横向费用单"), "Changing the mapping sheet did not change the preview")
|
||||
window.orderOut(nil)
|
||||
}
|
||||
print("Rendered pixel checks passed; screenshots written to \(folder.path)")
|
||||
}
|
||||
|
||||
@MainActor static func capture(_ view: NSView, to file: URL) throws {
|
||||
guard let bitmap = view.bitmapImageRepForCachingDisplay(in: view.bounds) else {
|
||||
throw ExpenseTemplateFailure.message("Cannot create preview snapshot")
|
||||
}
|
||||
@MainActor static func containsCanvas(_ view: NSView, named: String) -> Bool {
|
||||
if let canvas = view as? SpreadsheetCanvasView, canvas.sheet.name == named { return true }
|
||||
return view.subviews.contains { containsCanvas($0, named: named) }
|
||||
}
|
||||
|
||||
@MainActor static func capture(_ view: NSView, to url: URL) throws -> NSBitmapImageRep {
|
||||
let bitmap = view.bitmapImageRepForCachingDisplay(in: view.bounds)!
|
||||
view.cacheDisplay(in: view.bounds, to: bitmap)
|
||||
guard let png = bitmap.representation(using: .png, properties: [:]) else {
|
||||
throw ExpenseTemplateFailure.message("Cannot encode preview snapshot")
|
||||
}
|
||||
try png.write(to: file)
|
||||
try bitmap.representation(using: .png, properties: [:])!.write(to: url)
|
||||
return bitmap
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user