swiftbricks/Tests/SwiftBricksTests/SwiftBricksTests.swift
Hermes Agent 36967370fd SwiftBricks: Bricks JSON UI framework native Swift/SwiftUI implementation
- Core: Schema (Codable), Store, EventBus, RPC, Engine, I18n
- Controls: Text, Title1-6, Label, Input, Textarea, Number, Date, Button, Select
  VBox, HBox, ScrollPanel, DynamicColumn, Tabular, Form, InlineForm,
  TabView, Menu, PopupWindow, Html, Image, UrlWidget
- Renderer: BricksView (main entry), ControlRenderer (recursive dispatch)
- 30+ widget types, 9 bind actiontypes
- Form validation (required/minlength/maxlength/min/max/email/number/pattern)
- i18n with multi-language JSON files
- 18 tests covering schema parsing, store, i18n, events
2026-06-21 12:12:03 +08:00

196 lines
6.9 KiB
Swift

import XCTest
@testable import SwiftBricks
final class SwiftBricksTests: XCTestCase {
// MARK: - Schema
func testParseSimpleSchema() throws {
let json = """
{
"widgettype": "Text",
"options": {
"text": "Hello World",
"i18n": false
}
}
"""
let data = json.data(using: .utf8)!
let schema = try JSONDecoder().decode(ControlSchema.self, from: data)
XCTAssertEqual(schema.widgettype, "Text")
XCTAssertEqual(schema.options.text, "Hello World")
XCTAssertEqual(schema.options.i18n, false)
}
func testParseVBoxWithSubwidgets() throws {
let json = """
{
"widgettype": "VBox",
"options": { "spacing": 10, "css": "card" },
"subwidgets": [
{ "widgettype": "Title2", "options": { "text": "" } },
{ "widgettype": "Text", "options": { "otext": "" } },
{ "widgettype": "Button", "id": "btn1", "options": { "label": "" },
"binds": [{ "wid": "self", "event": "click", "actiontype": "urlwidget",
"target": "content", "options": { "url": "/api/page.ui" } }] }
]
}
"""
let data = json.data(using: .utf8)!
let schema = try JSONDecoder().decode(ControlSchema.self, from: data)
XCTAssertEqual(schema.widgettype, "VBox")
XCTAssertEqual(schema.options.spacing, 10)
XCTAssertEqual(schema.subwidgets?.count, 3)
XCTAssertEqual(schema.subwidgets?[0].widgettype, "Title2")
XCTAssertEqual(schema.subwidgets?[2].binds?.count, 1)
XCTAssertEqual(schema.subwidgets?[2].binds?[0].actiontype, "urlwidget")
}
func testParseFormSchema() throws {
let json = """
{
"widgettype": "Form",
"options": {
"title": "",
"submit_url": "/api/submit.dspy",
"fields": [
{ "name": "username", "label": "", "uitype": "str", "required": true,
"rules": [{ "type": "required", "message": "" }] },
{ "name": "role", "label": "", "uitype": "code",
"codes": [{"value": "admin", "text": ""}, {"value": "user", "text": ""}] }
]
}
}
"""
let data = json.data(using: .utf8)!
let schema = try JSONDecoder().decode(ControlSchema.self, from: data)
XCTAssertEqual(schema.widgettype, "Form")
XCTAssertEqual(schema.options.fields?.count, 2)
XCTAssertEqual(schema.options.fields?[0].rules?.count, 1)
XCTAssertEqual(schema.options.fields?[1].codes?.count, 2)
}
func testParseTabularSchema() throws {
let json = """
{
"widgettype": "Tabular",
"options": {
"data_url": "/api/list.dspy",
"page_rows": 10,
"row_options": {
"fields": [
{ "name": "id", "label": "ID", "uitype": "str" },
{ "name": "name", "label": "", "uitype": "str" }
],
"browserfields": {
"exclouded": ["id"]
}
}
}
}
"""
let data = json.data(using: .utf8)!
let schema = try JSONDecoder().decode(ControlSchema.self, from: data)
XCTAssertEqual(schema.widgettype, "Tabular")
XCTAssertEqual(schema.options.page_rows, 10)
XCTAssertEqual(schema.options.row_options?.fields?.count, 2)
XCTAssertEqual(schema.options.row_options?.browserfields?.exclouded, ["id"])
}
// MARK: - ActionType
func testValidActionTypes() {
XCTAssertEqual(ActionType.allCases.count, 9)
XCTAssertNotNil(ActionType(rawValue: "urlwidget"))
XCTAssertNotNil(ActionType(rawValue: "urldata"))
XCTAssertNotNil(ActionType(rawValue: "method"))
XCTAssertNotNil(ActionType(rawValue: "event"))
XCTAssertNil(ActionType(rawValue: "fetch")) //
XCTAssertNil(ActionType(rawValue: "ajax")) //
}
// MARK: - WidgetType
func testWidgetTypeCategories() {
XCTAssertTrue(WidgetType.vbox.isLayout)
XCTAssertTrue(WidgetType.hbox.isLayout)
XCTAssertTrue(WidgetType.text.isText)
XCTAssertTrue(WidgetType.title2.isText)
XCTAssertTrue(WidgetType.input.isInput)
XCTAssertFalse(WidgetType.button.isLayout)
}
// MARK: - Store
@MainActor
func testStoreFormValues() {
let store = BricksStore()
store.setFormField(formId: "form1", field: "name", value: "Alice")
store.setFormField(formId: "form1", field: "email", value: "alice@test.com")
XCTAssertEqual(store.getFormField(formId: "form1", field: "name"), "Alice")
XCTAssertEqual(store.getFormField(formId: "form1", field: "email"), "alice@test.com")
let all = store.getFormValues(formId: "form1")
XCTAssertEqual(all.count, 2)
store.clearForm(formId: "form1")
XCTAssertEqual(store.getFormField(formId: "form1", field: "name"), "")
}
@MainActor
func testStoreTableData() {
let store = BricksStore()
let rows: [[String: Any]] = [
["id": "1", "name": "Alice"],
["id": "2", "name": "Bob"]
]
store.setTableData(id: "tbl1", rows: rows)
store.selectRow(tabularId: "tbl1", index: 1)
XCTAssertEqual(store.getTableData(id: "tbl1").count, 2)
XCTAssertEqual(store.getSelectedRow(tabularId: "tbl1"), 1)
let selected = store.getSelectedRowData(tabularId: "tbl1")
XCTAssertEqual(selected?["name"] as? String, "Bob")
}
// MARK: - I18n
@MainActor
func testI18nTranslation() {
let i18n = BricksI18n(locale: "en")
i18n.loadMessages([
"你好": "Hello",
"提交": "Submit"
])
XCTAssertEqual(i18n.t("你好"), "Hello")
XCTAssertEqual(i18n.t("提交"), "Submit")
XCTAssertEqual(i18n.t("未知"), "未知") //
}
// MARK: - EventBus
@MainActor
func testEventBus() async {
let bus = BricksEventBus()
var received = false
bus.on("test.click") { _ in
received = true
}
await bus.dispatch("test.click")
XCTAssertTrue(received)
}
// MARK: - Engine
@MainActor
func testTemplateResolve() {
let engine = BricksEngine()
let result = engine.resolveTemplate("/api/detail.dspy?id=${id}$", params: ["id": "12345"])
XCTAssertEqual(result, "/api/detail.dspy?id=12345")
}
}