From eda38f0eda3fc5bf000fcfcb94b532bbfef230ad Mon Sep 17 00:00:00 2001 From: "Nurul Huda (Apon)" Date: Tue, 23 Dec 2025 03:04:12 +0600 Subject: [PATCH] test: add html comparision for render test --- .gitattributes | 2 + build.zig.zon | 2 +- editors/vscode/package.json | 2 +- editors/zed/extension.toml | 2 +- packages/tree-sitter-zx/Cargo.toml | 2 +- packages/tree-sitter-zx/build.zig.zon | 2 +- packages/tree-sitter-zx/package.json | 2 +- packages/tree-sitter-zx/tree-sitter.json | 2 +- packages/ziex/package.json | 2 +- test/cli/fmt.zig | 2 +- test/data/attribute/builtin.html | 1 + test/data/attribute/builtin_escaping.html | 7 + test/data/attribute/component.html | 1 + test/data/attribute/dynamic.html | 1 + test/data/attribute/types.html | 1 + test/data/component/basic.html | 1 + test/data/component/children_only.html | 1 + test/data/component/csr_react.html | 1 + test/data/component/csr_react_multiple.html | 1 + test/data/component/csr_zig.html | 1 + test/data/component/import.html | 1 + test/data/component/multiple.html | 1 + test/data/component/nested.html | 1 + test/data/component/root_cmp.html | 1 + test/data/control_flow/for.html | 1 + test/data/control_flow/for_block.html | 1 + test/data/control_flow/for_capture.html | 1 + .../for_capture_to_component.html | 1 + test/data/control_flow/for_for.html | 1 + test/data/control_flow/for_if.html | 1 + test/data/control_flow/for_switch.html | 1 + test/data/control_flow/for_while.html | 1 + test/data/control_flow/if.html | 1 + test/data/control_flow/if_block.html | 1 + test/data/control_flow/if_capture.html | 1 + test/data/control_flow/if_else_if.html | 1 + test/data/control_flow/if_for.html | 1 + test/data/control_flow/if_for_if.html | 1 + test/data/control_flow/if_if.html | 1 + test/data/control_flow/if_if_only.html | 1 + test/data/control_flow/if_if_only_block.html | 1 + test/data/control_flow/if_only.html | 1 + test/data/control_flow/if_only_block.html | 1 + test/data/control_flow/if_switch.html | 1 + test/data/control_flow/if_while.html | 1 + test/data/control_flow/switch.html | 1 + test/data/control_flow/switch_block.html | 1 + test/data/control_flow/switch_for.html | 1 + test/data/control_flow/switch_if.html | 1 + test/data/control_flow/switch_switch.html | 1 + test/data/control_flow/switch_while.html | 1 + test/data/control_flow/while.html | 1 + test/data/control_flow/while_block.html | 1 + test/data/control_flow/while_for.html | 1 + test/data/control_flow/while_if.html | 1 + test/data/control_flow/while_switch.html | 1 + test/data/control_flow/while_while.html | 1 + test/data/element/empty.html | 1 + test/data/element/fragment.html | 1 + test/data/element/fragment_root.html | 1 + test/data/element/nested.html | 1 + test/data/element/void.html | 1 + test/data/escaping/pre.html | 7 + test/data/escaping/quotes.html | 1 + test/data/expression/component.html | 1 + test/data/expression/format.html | 1 + test/data/expression/function_call.html | 1 + test/data/expression/mixed.html | 1 + test/data/expression/multiline_string.html | 2 + test/data/expression/optional.html | 1 + test/data/expression/struct_access.html | 1 + test/data/expression/text.html | 1 + test/test_util.zig | 2 +- test/zx/ast.zig | 278 +++++++++++++----- 74 files changed, 299 insertions(+), 76 deletions(-) create mode 100644 test/data/attribute/builtin.html create mode 100644 test/data/attribute/builtin_escaping.html create mode 100644 test/data/attribute/component.html create mode 100644 test/data/attribute/dynamic.html create mode 100644 test/data/attribute/types.html create mode 100644 test/data/component/basic.html create mode 100644 test/data/component/children_only.html create mode 100644 test/data/component/csr_react.html create mode 100644 test/data/component/csr_react_multiple.html create mode 100644 test/data/component/csr_zig.html create mode 100644 test/data/component/import.html create mode 100644 test/data/component/multiple.html create mode 100644 test/data/component/nested.html create mode 100644 test/data/component/root_cmp.html create mode 100644 test/data/control_flow/for.html create mode 100644 test/data/control_flow/for_block.html create mode 100644 test/data/control_flow/for_capture.html create mode 100644 test/data/control_flow/for_capture_to_component.html create mode 100644 test/data/control_flow/for_for.html create mode 100644 test/data/control_flow/for_if.html create mode 100644 test/data/control_flow/for_switch.html create mode 100644 test/data/control_flow/for_while.html create mode 100644 test/data/control_flow/if.html create mode 100644 test/data/control_flow/if_block.html create mode 100644 test/data/control_flow/if_capture.html create mode 100644 test/data/control_flow/if_else_if.html create mode 100644 test/data/control_flow/if_for.html create mode 100644 test/data/control_flow/if_for_if.html create mode 100644 test/data/control_flow/if_if.html create mode 100644 test/data/control_flow/if_if_only.html create mode 100644 test/data/control_flow/if_if_only_block.html create mode 100644 test/data/control_flow/if_only.html create mode 100644 test/data/control_flow/if_only_block.html create mode 100644 test/data/control_flow/if_switch.html create mode 100644 test/data/control_flow/if_while.html create mode 100644 test/data/control_flow/switch.html create mode 100644 test/data/control_flow/switch_block.html create mode 100644 test/data/control_flow/switch_for.html create mode 100644 test/data/control_flow/switch_if.html create mode 100644 test/data/control_flow/switch_switch.html create mode 100644 test/data/control_flow/switch_while.html create mode 100644 test/data/control_flow/while.html create mode 100644 test/data/control_flow/while_block.html create mode 100644 test/data/control_flow/while_for.html create mode 100644 test/data/control_flow/while_if.html create mode 100644 test/data/control_flow/while_switch.html create mode 100644 test/data/control_flow/while_while.html create mode 100644 test/data/element/empty.html create mode 100644 test/data/element/fragment.html create mode 100644 test/data/element/fragment_root.html create mode 100644 test/data/element/nested.html create mode 100644 test/data/element/void.html create mode 100644 test/data/escaping/pre.html create mode 100644 test/data/escaping/quotes.html create mode 100644 test/data/expression/component.html create mode 100644 test/data/expression/format.html create mode 100644 test/data/expression/function_call.html create mode 100644 test/data/expression/mixed.html create mode 100644 test/data/expression/multiline_string.html create mode 100644 test/data/expression/optional.html create mode 100644 test/data/expression/struct_access.html create mode 100644 test/data/expression/text.html diff --git a/.gitattributes b/.gitattributes index dfc490d7..ee52542f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -19,6 +19,8 @@ *.gitattributes text eol=lf *.editorconfig text eol=lf +test/data/**/*.html linguist-generated + # Denote all files that are truly binary and should not be modified *.png binary *.jpg binary diff --git a/build.zig.zon b/build.zig.zon index 544bfda8..1cb1d21f 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = .zx, - .version = "0.1.0-dev.410", + .version = "0.1.0-dev.411", .description = "ZX is a framework for building web applications with Zig.", .repository = "https://github.com/nurulhudaapon/zx", .fingerprint = 0xcad77a8d2a3389f2, diff --git a/editors/vscode/package.json b/editors/vscode/package.json index ccd15d7f..598e2640 100644 --- a/editors/vscode/package.json +++ b/editors/vscode/package.json @@ -2,7 +2,7 @@ "name": "zx", "displayName": "ZX", "description": "ZX let's you write HTML within your Zig code.", - "version": "0.1.410", + "version": "0.1.411", "publisher": "nurulhudaapon", "icon": "images/zx-icon.png", "private": true, diff --git a/editors/zed/extension.toml b/editors/zed/extension.toml index a6bca801..fba08f41 100644 --- a/editors/zed/extension.toml +++ b/editors/zed/extension.toml @@ -1,7 +1,7 @@ id = "zx" name = "ZX" description = "ZX support." -version = "0.1.0-dev.410" +version = "0.1.0-dev.411" schema_version = 1 authors = ["Nurul Huda (Apon) "] repository = "https://github.com/nurulhudaapon/zx" diff --git a/packages/tree-sitter-zx/Cargo.toml b/packages/tree-sitter-zx/Cargo.toml index 6887468e..9c35f9c9 100644 --- a/packages/tree-sitter-zx/Cargo.toml +++ b/packages/tree-sitter-zx/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tree-sitter-zx" description = "ZX is a framework for building web applications with Zig." -version = "0.1.0-dev.410" +version = "0.1.0-dev.411" authors = ["Nurul Huda (Apon) "] license = "MIT" readme = "README.md" diff --git a/packages/tree-sitter-zx/build.zig.zon b/packages/tree-sitter-zx/build.zig.zon index 90e20f1d..980bc8c7 100644 --- a/packages/tree-sitter-zx/build.zig.zon +++ b/packages/tree-sitter-zx/build.zig.zon @@ -1,7 +1,7 @@ .{ .name = .tree_sitter_zx, .fingerprint = 0xd3a91e4ba933ca89, - .version = "0.1.0-dev.410", + .version = "0.1.0-dev.411", .dependencies = .{ .tree_sitter = .{ .url = "git+https://github.com/tree-sitter/zig-tree-sitter#a53dac49d66562006e84dd11211b7ae40db68263", diff --git a/packages/tree-sitter-zx/package.json b/packages/tree-sitter-zx/package.json index 923b6c0f..f9292202 100644 --- a/packages/tree-sitter-zx/package.json +++ b/packages/tree-sitter-zx/package.json @@ -1,6 +1,6 @@ { "name": "tree-sitter-zx", - "version": "0.1.0-dev.410", + "version": "0.1.0-dev.411", "description": "ZX is a framework for building web applications with Zig.", "repository": "https://github.com/nurulhudaapon/zx", "license": "MIT", diff --git a/packages/tree-sitter-zx/tree-sitter.json b/packages/tree-sitter-zx/tree-sitter.json index 71fb110c..17a49d46 100644 --- a/packages/tree-sitter-zx/tree-sitter.json +++ b/packages/tree-sitter-zx/tree-sitter.json @@ -14,7 +14,7 @@ } ], "metadata": { - "version": "0.1.0-dev.410", + "version": "0.1.0-dev.411", "license": "MIT", "description": "ZX is a framework for building web applications with Zig.", "authors": [ diff --git a/packages/ziex/package.json b/packages/ziex/package.json index 5108860e..cebfb8c9 100644 --- a/packages/ziex/package.json +++ b/packages/ziex/package.json @@ -1,6 +1,6 @@ { "name": "ziex", - "version": "0.1.0-dev.410", + "version": "0.1.0-dev.411", "private": true, "description": "ZX is a framework for building web applications with Zig.", "main": "src/index.ts", diff --git a/test/cli/fmt.zig b/test/cli/fmt.zig index 04bb53fb..3ac85af8 100644 --- a/test/cli/fmt.zig +++ b/test/cli/fmt.zig @@ -237,7 +237,7 @@ test "escaping_quotes" { try test_fmt("escaping/quotes"); } -test "performance" { +test "performance > fmt" { // if (true) return error.Todo; const MAX_TIME_MS = 50.0 * 8; // 50ms is on M1 Pro const MAX_TIME_PER_FILE_MS = 8.0 * 10; // 5ms is on M1 Pro diff --git a/test/data/attribute/builtin.html b/test/data/attribute/builtin.html new file mode 100644 index 00000000..462e1b08 --- /dev/null +++ b/test/data/attribute/builtin.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/data/attribute/builtin_escaping.html b/test/data/attribute/builtin_escaping.html new file mode 100644 index 00000000..ec3ac8cb --- /dev/null +++ b/test/data/attribute/builtin_escaping.html @@ -0,0 +1,7 @@ +
+ const data = name: + Test + Test 2 + + test ; +
\ No newline at end of file diff --git a/test/data/attribute/component.html b/test/data/attribute/component.html new file mode 100644 index 00000000..5633f54a --- /dev/null +++ b/test/data/attribute/component.html @@ -0,0 +1 @@ +
Hello!
Hello!
\ No newline at end of file diff --git a/test/data/attribute/dynamic.html b/test/data/attribute/dynamic.html new file mode 100644 index 00000000..61f53625 --- /dev/null +++ b/test/data/attribute/dynamic.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/data/attribute/types.html b/test/data/attribute/types.html new file mode 100644 index 00000000..3e784d4b --- /dev/null +++ b/test/data/attribute/types.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/data/component/basic.html b/test/data/component/basic.html new file mode 100644 index 00000000..2f1ef611 --- /dev/null +++ b/test/data/component/basic.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/data/component/children_only.html b/test/data/component/children_only.html new file mode 100644 index 00000000..215c5558 --- /dev/null +++ b/test/data/component/children_only.html @@ -0,0 +1 @@ +

Wrapped content

FirstSecond
\ No newline at end of file diff --git a/test/data/component/csr_react.html b/test/data/component/csr_react.html new file mode 100644 index 00000000..5edda4f2 --- /dev/null +++ b/test/data/component/csr_react.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/data/component/csr_react_multiple.html b/test/data/component/csr_react_multiple.html new file mode 100644 index 00000000..122ff0fa --- /dev/null +++ b/test/data/component/csr_react_multiple.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/data/component/csr_zig.html b/test/data/component/csr_zig.html new file mode 100644 index 00000000..67de7883 --- /dev/null +++ b/test/data/component/csr_zig.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/data/component/import.html b/test/data/component/import.html new file mode 100644 index 00000000..2f1ef611 --- /dev/null +++ b/test/data/component/import.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/data/component/multiple.html b/test/data/component/multiple.html new file mode 100644 index 00000000..d06397a7 --- /dev/null +++ b/test/data/component/multiple.html @@ -0,0 +1 @@ +
Score #1Points #2Rating #3
\ No newline at end of file diff --git a/test/data/component/nested.html b/test/data/component/nested.html new file mode 100644 index 00000000..84c64aab --- /dev/null +++ b/test/data/component/nested.html @@ -0,0 +1 @@ +

Welcome

\ No newline at end of file diff --git a/test/data/component/root_cmp.html b/test/data/component/root_cmp.html new file mode 100644 index 00000000..17ead3cf --- /dev/null +++ b/test/data/component/root_cmp.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/data/control_flow/for.html b/test/data/control_flow/for.html new file mode 100644 index 00000000..efb30094 --- /dev/null +++ b/test/data/control_flow/for.html @@ -0,0 +1 @@ +

John

Jane

Jim

Jill

\ No newline at end of file diff --git a/test/data/control_flow/for_block.html b/test/data/control_flow/for_block.html new file mode 100644 index 00000000..b1ac4a6a --- /dev/null +++ b/test/data/control_flow/for_block.html @@ -0,0 +1 @@ +
A
B
C
\ No newline at end of file diff --git a/test/data/control_flow/for_capture.html b/test/data/control_flow/for_capture.html new file mode 100644 index 00000000..d40fdaa2 --- /dev/null +++ b/test/data/control_flow/for_capture.html @@ -0,0 +1 @@ +

John - 20

Jane - 21

Jim - 22

Jill - 23

\ No newline at end of file diff --git a/test/data/control_flow/for_capture_to_component.html b/test/data/control_flow/for_capture_to_component.html new file mode 100644 index 00000000..d40fdaa2 --- /dev/null +++ b/test/data/control_flow/for_capture_to_component.html @@ -0,0 +1 @@ +

John - 20

Jane - 21

Jim - 22

Jill - 23

\ No newline at end of file diff --git a/test/data/control_flow/for_for.html b/test/data/control_flow/for_for.html new file mode 100644 index 00000000..00489fe0 --- /dev/null +++ b/test/data/control_flow/for_for.html @@ -0,0 +1 @@ +

John

Jane

Jim

Jill

\ No newline at end of file diff --git a/test/data/control_flow/for_if.html b/test/data/control_flow/for_if.html new file mode 100644 index 00000000..2cb6999a --- /dev/null +++ b/test/data/control_flow/for_if.html @@ -0,0 +1 @@ +

John (Active)

Jane (Inactive)

Jim (Active)

\ No newline at end of file diff --git a/test/data/control_flow/for_switch.html b/test/data/control_flow/for_switch.html new file mode 100644 index 00000000..cfdf48ec --- /dev/null +++ b/test/data/control_flow/for_switch.html @@ -0,0 +1 @@ +

John

Admin

Jane

Member

Jim

Guest
\ No newline at end of file diff --git a/test/data/control_flow/for_while.html b/test/data/control_flow/for_while.html new file mode 100644 index 00000000..fe987295 --- /dev/null +++ b/test/data/control_flow/for_while.html @@ -0,0 +1 @@ +

0 : A

1 : A

\ No newline at end of file diff --git a/test/data/control_flow/if.html b/test/data/control_flow/if.html new file mode 100644 index 00000000..42ad0a33 --- /dev/null +++ b/test/data/control_flow/if.html @@ -0,0 +1 @@ +

Please log in to continue.

\ No newline at end of file diff --git a/test/data/control_flow/if_block.html b/test/data/control_flow/if_block.html new file mode 100644 index 00000000..8564cb9b --- /dev/null +++ b/test/data/control_flow/if_block.html @@ -0,0 +1 @@ +

Please log in to continue.

\ No newline at end of file diff --git a/test/data/control_flow/if_capture.html b/test/data/control_flow/if_capture.html new file mode 100644 index 00000000..3d590de2 --- /dev/null +++ b/test/data/control_flow/if_capture.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/data/control_flow/if_else_if.html b/test/data/control_flow/if_else_if.html new file mode 100644 index 00000000..42ad0a33 --- /dev/null +++ b/test/data/control_flow/if_else_if.html @@ -0,0 +1 @@ +

Please log in to continue.

\ No newline at end of file diff --git a/test/data/control_flow/if_for.html b/test/data/control_flow/if_for.html new file mode 100644 index 00000000..efb30094 --- /dev/null +++ b/test/data/control_flow/if_for.html @@ -0,0 +1 @@ +

John

Jane

Jim

Jill

\ No newline at end of file diff --git a/test/data/control_flow/if_for_if.html b/test/data/control_flow/if_for_if.html new file mode 100644 index 00000000..a3c6e3e8 --- /dev/null +++ b/test/data/control_flow/if_for_if.html @@ -0,0 +1 @@ +
  • Apple
  • Banana
  • Cherry
\ No newline at end of file diff --git a/test/data/control_flow/if_if.html b/test/data/control_flow/if_if.html new file mode 100644 index 00000000..0616c87b --- /dev/null +++ b/test/data/control_flow/if_if.html @@ -0,0 +1 @@ +

Welcome, User!

\ No newline at end of file diff --git a/test/data/control_flow/if_if_only.html b/test/data/control_flow/if_if_only.html new file mode 100644 index 00000000..1b062a46 --- /dev/null +++ b/test/data/control_flow/if_if_only.html @@ -0,0 +1 @@ +

Welcome, User!

Welcome, Premium User!

\ No newline at end of file diff --git a/test/data/control_flow/if_if_only_block.html b/test/data/control_flow/if_if_only_block.html new file mode 100644 index 00000000..2ba124ee --- /dev/null +++ b/test/data/control_flow/if_if_only_block.html @@ -0,0 +1 @@ +

Welcome, Premium User

\ No newline at end of file diff --git a/test/data/control_flow/if_only.html b/test/data/control_flow/if_only.html new file mode 100644 index 00000000..3d590de2 --- /dev/null +++ b/test/data/control_flow/if_only.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/data/control_flow/if_only_block.html b/test/data/control_flow/if_only_block.html new file mode 100644 index 00000000..dc819d79 --- /dev/null +++ b/test/data/control_flow/if_only_block.html @@ -0,0 +1 @@ +

Welcome, User

\ No newline at end of file diff --git a/test/data/control_flow/if_switch.html b/test/data/control_flow/if_switch.html new file mode 100644 index 00000000..4748e615 --- /dev/null +++ b/test/data/control_flow/if_switch.html @@ -0,0 +1 @@ +

Admin

\ No newline at end of file diff --git a/test/data/control_flow/if_while.html b/test/data/control_flow/if_while.html new file mode 100644 index 00000000..241a7051 --- /dev/null +++ b/test/data/control_flow/if_while.html @@ -0,0 +1 @@ +

0

1

2

\ No newline at end of file diff --git a/test/data/control_flow/switch.html b/test/data/control_flow/switch.html new file mode 100644 index 00000000..d627c038 --- /dev/null +++ b/test/data/control_flow/switch.html @@ -0,0 +1 @@ +
Admin
\ No newline at end of file diff --git a/test/data/control_flow/switch_block.html b/test/data/control_flow/switch_block.html new file mode 100644 index 00000000..631a0a77 --- /dev/null +++ b/test/data/control_flow/switch_block.html @@ -0,0 +1 @@ +

Admin

\ No newline at end of file diff --git a/test/data/control_flow/switch_for.html b/test/data/control_flow/switch_for.html new file mode 100644 index 00000000..3e2cde46 --- /dev/null +++ b/test/data/control_flow/switch_for.html @@ -0,0 +1 @@ +

John

Jane

\ No newline at end of file diff --git a/test/data/control_flow/switch_if.html b/test/data/control_flow/switch_if.html new file mode 100644 index 00000000..ab313111 --- /dev/null +++ b/test/data/control_flow/switch_if.html @@ -0,0 +1 @@ +

Active Admin

\ No newline at end of file diff --git a/test/data/control_flow/switch_switch.html b/test/data/control_flow/switch_switch.html new file mode 100644 index 00000000..ab313111 --- /dev/null +++ b/test/data/control_flow/switch_switch.html @@ -0,0 +1 @@ +

Active Admin

\ No newline at end of file diff --git a/test/data/control_flow/switch_while.html b/test/data/control_flow/switch_while.html new file mode 100644 index 00000000..8208a280 --- /dev/null +++ b/test/data/control_flow/switch_while.html @@ -0,0 +1 @@ +

0

1

2

\ No newline at end of file diff --git a/test/data/control_flow/while.html b/test/data/control_flow/while.html new file mode 100644 index 00000000..8208a280 --- /dev/null +++ b/test/data/control_flow/while.html @@ -0,0 +1 @@ +

0

1

2

\ No newline at end of file diff --git a/test/data/control_flow/while_block.html b/test/data/control_flow/while_block.html new file mode 100644 index 00000000..40f9c185 --- /dev/null +++ b/test/data/control_flow/while_block.html @@ -0,0 +1 @@ +

0

1

2

\ No newline at end of file diff --git a/test/data/control_flow/while_for.html b/test/data/control_flow/while_for.html new file mode 100644 index 00000000..a497b84b --- /dev/null +++ b/test/data/control_flow/while_for.html @@ -0,0 +1 @@ +

a

b

a

b

\ No newline at end of file diff --git a/test/data/control_flow/while_if.html b/test/data/control_flow/while_if.html new file mode 100644 index 00000000..9ec6bee5 --- /dev/null +++ b/test/data/control_flow/while_if.html @@ -0,0 +1 @@ +

Even: 0

Odd: 1

Even: 2

\ No newline at end of file diff --git a/test/data/control_flow/while_switch.html b/test/data/control_flow/while_switch.html new file mode 100644 index 00000000..0f3c921b --- /dev/null +++ b/test/data/control_flow/while_switch.html @@ -0,0 +1 @@ +
Zero
One
Other
\ No newline at end of file diff --git a/test/data/control_flow/while_while.html b/test/data/control_flow/while_while.html new file mode 100644 index 00000000..b4aaa712 --- /dev/null +++ b/test/data/control_flow/while_while.html @@ -0,0 +1 @@ +

0

1

\ No newline at end of file diff --git a/test/data/element/empty.html b/test/data/element/empty.html new file mode 100644 index 00000000..ea2a8a83 --- /dev/null +++ b/test/data/element/empty.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/test/data/element/fragment.html b/test/data/element/fragment.html new file mode 100644 index 00000000..f8053c46 --- /dev/null +++ b/test/data/element/fragment.html @@ -0,0 +1 @@ +

First

Second

\ No newline at end of file diff --git a/test/data/element/fragment_root.html b/test/data/element/fragment_root.html new file mode 100644 index 00000000..cca0b3ec --- /dev/null +++ b/test/data/element/fragment_root.html @@ -0,0 +1 @@ +

First

Second

\ No newline at end of file diff --git a/test/data/element/nested.html b/test/data/element/nested.html new file mode 100644 index 00000000..dcf78c01 --- /dev/null +++ b/test/data/element/nested.html @@ -0,0 +1 @@ +

Deeply nested content

\ No newline at end of file diff --git a/test/data/element/void.html b/test/data/element/void.html new file mode 100644 index 00000000..7a6e94bb --- /dev/null +++ b/test/data/element/void.html @@ -0,0 +1 @@ +


Logo
\ No newline at end of file diff --git a/test/data/escaping/pre.html b/test/data/escaping/pre.html new file mode 100644 index 00000000..178f6138 --- /dev/null +++ b/test/data/escaping/pre.html @@ -0,0 +1,7 @@ +
                const data = 
+                
+                Test   
+                        Test 2
+                
+                 name: "test" ;
+            
\ No newline at end of file diff --git a/test/data/escaping/quotes.html b/test/data/escaping/quotes.html new file mode 100644 index 00000000..4366d935 --- /dev/null +++ b/test/data/escaping/quotes.html @@ -0,0 +1 @@ +
quote should be escaped "quote"
"quote"
\ No newline at end of file diff --git a/test/data/expression/component.html b/test/data/expression/component.html new file mode 100644 index 00000000..8f11cb5c --- /dev/null +++ b/test/data/expression/component.html @@ -0,0 +1 @@ +

Greeting: Hello!

Hello!
\ No newline at end of file diff --git a/test/data/expression/format.html b/test/data/expression/format.html new file mode 100644 index 00000000..edd680e2 --- /dev/null +++ b/test/data/expression/format.html @@ -0,0 +1 @@ +

Count: 42

Hex: 0xff

Percentage: 75%

Count: 42

Hex: 0x255

Percentage: 75%

Float: 3.14

Bool: true

\ No newline at end of file diff --git a/test/data/expression/function_call.html b/test/data/expression/function_call.html new file mode 100644 index 00000000..20a75eb6 --- /dev/null +++ b/test/data/expression/function_call.html @@ -0,0 +1 @@ +

Count: 42

Items: 3

Greeting: Hello!

\ No newline at end of file diff --git a/test/data/expression/mixed.html b/test/data/expression/mixed.html new file mode 100644 index 00000000..b573547a --- /dev/null +++ b/test/data/expression/mixed.html @@ -0,0 +1 @@ +

Hello Alice, you have 5apples in your cart.

Welcome back, Alice! Your order #5 is ready.

Item: apple (qty: 5)
\ No newline at end of file diff --git a/test/data/expression/multiline_string.html b/test/data/expression/multiline_string.html new file mode 100644 index 00000000..bd6e47a5 --- /dev/null +++ b/test/data/expression/multiline_string.html @@ -0,0 +1,2 @@ +
ZX + Multiline
\ No newline at end of file diff --git a/test/data/expression/optional.html b/test/data/expression/optional.html new file mode 100644 index 00000000..6de8166a --- /dev/null +++ b/test/data/expression/optional.html @@ -0,0 +1 @@ +

Name: Alice

Default: Anonymous

\ No newline at end of file diff --git a/test/data/expression/struct_access.html b/test/data/expression/struct_access.html new file mode 100644 index 00000000..bf283e18 --- /dev/null +++ b/test/data/expression/struct_access.html @@ -0,0 +1 @@ +

Name: Alice

Age: 25

Book

Price: $29.99
\ No newline at end of file diff --git a/test/data/expression/text.html b/test/data/expression/text.html new file mode 100644 index 00000000..eb2dc02c --- /dev/null +++ b/test/data/expression/text.html @@ -0,0 +1 @@ +

User: Alice & Bob

Safe HTML: <script>alert('XSS')</script>

Unsafe HTML: Test

\ No newline at end of file diff --git a/test/test_util.zig b/test/test_util.zig index c39f28a1..2607edcd 100644 --- a/test/test_util.zig +++ b/test/test_util.zig @@ -95,7 +95,7 @@ pub const TestFileCache = struct { const base_path = "test/data/"; - // Load both .zx and .zig files for each test file + // Load .zx and .zig files for each test file for (test_files) |file_path| { for ([_]struct { ext: []const u8 }{ .{ .ext = ".zx" }, .{ .ext = ".zig" } }) |ext_info| { const full_path = try std.fmt.allocPrint(allocator, "{s}{s}{s}", .{ base_path, file_path, ext_info.ext }); diff --git a/test/zx/ast.zig b/test/zx/ast.zig index c0a7676a..70343136 100644 --- a/test/zx/ast.zig +++ b/test/zx/ast.zig @@ -19,316 +19,316 @@ test "tests:afterAll" { // === If === test "if" { try test_transpile("control_flow/if"); - try test_render(@import("./../data/control_flow/if.zig").Page); + try test_render("control_flow/if", @import("./../data/control_flow/if.zig").Page); } test "if_block" { try test_transpile("control_flow/if_block"); - try test_render(@import("./../data/control_flow/if_block.zig").Page); + try test_render("control_flow/if_block", @import("./../data/control_flow/if_block.zig").Page); } test "if_if_only" { // if (true) return error.Todo; try test_transpile("control_flow/if_if_only"); - try test_render(@import("./../data/control_flow/if_if_only.zig").Page); + try test_render("control_flow/if_if_only", @import("./../data/control_flow/if_if_only.zig").Page); } test "if_if_only_block" { // if (true) return error.Todo; try test_transpile("control_flow/if_if_only_block"); - try test_render(@import("./../data/control_flow/if_if_only_block.zig").Page); + try test_render("control_flow/if_if_only_block", @import("./../data/control_flow/if_if_only_block.zig").Page); } test "if_only" { // if (true) return error.Todo; try test_transpile("control_flow/if_only"); - try test_render(@import("./../data/control_flow/if_only.zig").Page); + try test_render("control_flow/if_only", @import("./../data/control_flow/if_only.zig").Page); } test "if_only_block" { // if (true) return error.Todo; try test_transpile("control_flow/if_only_block"); - try test_render(@import("./../data/control_flow/if_only_block.zig").Page); + try test_render("control_flow/if_only_block", @import("./../data/control_flow/if_only_block.zig").Page); } test "if_while" { // if (true) return error.Todo; try test_transpile("control_flow/if_while"); - try test_render(@import("./../data/control_flow/if_while.zig").Page); + try test_render("control_flow/if_while", @import("./../data/control_flow/if_while.zig").Page); } test "if_if" { // if (true) return error.Todo; try test_transpile("control_flow/if_if"); - try test_render(@import("./../data/control_flow/if_if.zig").Page); + try test_render("control_flow/if_if", @import("./../data/control_flow/if_if.zig").Page); } test "if_for" { // if (true) return error.Todo; try test_transpile("control_flow/if_for"); - try test_render(@import("./../data/control_flow/if_for.zig").Page); + try test_render("control_flow/if_for", @import("./../data/control_flow/if_for.zig").Page); } test "if_switch" { // if (true) return error.Todo; try test_transpile("control_flow/if_switch"); - try test_render(@import("./../data/control_flow/if_switch.zig").Page); + try test_render("control_flow/if_switch", @import("./../data/control_flow/if_switch.zig").Page); } test "if_else_if" { try test_transpile("control_flow/if_else_if"); - try test_render(@import("./../data/control_flow/if_else_if.zig").Page); + try test_render("control_flow/if_else_if", @import("./../data/control_flow/if_else_if.zig").Page); } test "if_capture" { try test_transpile("control_flow/if_capture"); - try test_render(@import("./../data/control_flow/if_capture.zig").Page); + try test_render("control_flow/if_capture", @import("./../data/control_flow/if_capture.zig").Page); } // === For === test "for" { // if (true) return error.Todo; try test_transpile("control_flow/for"); - try test_render(@import("./../data/control_flow/for.zig").Page); + try test_render("control_flow/for", @import("./../data/control_flow/for.zig").Page); } test "for_capture" { // if (true) return error.Todo; - try test_render(@import("./../data/control_flow/for.zig").StructCapture); + try test_render("control_flow/for_capture", @import("./../data/control_flow/for.zig").StructCapture); } test "for_capture_to_component" { // if (true) return error.Todo; - try test_render(@import("./../data/control_flow/for.zig").StructCaptureToComponent); + try test_render("control_flow/for_capture_to_component", @import("./../data/control_flow/for.zig").StructCaptureToComponent); } test "for_block" { // if (true) return error.Todo; try test_transpile("control_flow/for_block"); - try test_render(@import("./../data/control_flow/for_block.zig").Page); + try test_render("control_flow/for_block", @import("./../data/control_flow/for_block.zig").Page); } test "for_if" { // if (true) return error.Todo; try test_transpile("control_flow/for_if"); - try test_render(@import("./../data/control_flow/for_if.zig").Page); + try test_render("control_flow/for_if", @import("./../data/control_flow/for_if.zig").Page); } test "for_for" { // if (true) return error.Todo; try test_transpile("control_flow/for_for"); - try test_render(@import("./../data/control_flow/for_for.zig").Page); + try test_render("control_flow/for_for", @import("./../data/control_flow/for_for.zig").Page); } test "for_switch" { // if (true) return error.Todo; try test_transpile("control_flow/for_switch"); - try test_render(@import("./../data/control_flow/for_switch.zig").Page); + try test_render("control_flow/for_switch", @import("./../data/control_flow/for_switch.zig").Page); } test "for_while" { // if (true) return error.Todo; try test_transpile("control_flow/for_while"); - try test_render(@import("./../data/control_flow/for_while.zig").Page); + try test_render("control_flow/for_while", @import("./../data/control_flow/for_while.zig").Page); } // === Switch === test "switch" { // if (true) return error.Todo; try test_transpile("control_flow/switch"); - try test_render(@import("./../data/control_flow/switch.zig").Page); + try test_render("control_flow/switch", @import("./../data/control_flow/switch.zig").Page); } test "switch_block" { // if (true) return error.Todo; try test_transpile("control_flow/switch_block"); - try test_render(@import("./../data/control_flow/switch_block.zig").Page); + try test_render("control_flow/switch_block", @import("./../data/control_flow/switch_block.zig").Page); } test "switch_if" { // if (true) return error.Todo; try test_transpile("control_flow/switch_if"); - try test_render(@import("./../data/control_flow/switch_if.zig").Page); + try test_render("control_flow/switch_if", @import("./../data/control_flow/switch_if.zig").Page); } test "switch_for" { // if (true) return error.Todo; try test_transpile("control_flow/switch_for"); - try test_render(@import("./../data/control_flow/switch_for.zig").Page); + try test_render("control_flow/switch_for", @import("./../data/control_flow/switch_for.zig").Page); } test "switch_switch" { // if (true) return error.Todo; try test_transpile("control_flow/switch_switch"); - try test_render(@import("./../data/control_flow/switch_switch.zig").Page); + try test_render("control_flow/switch_switch", @import("./../data/control_flow/switch_switch.zig").Page); } test "switch_while" { // if (true) return error.Todo; try test_transpile("control_flow/switch_while"); - try test_render(@import("./../data/control_flow/switch_while.zig").Page); + try test_render("control_flow/switch_while", @import("./../data/control_flow/switch_while.zig").Page); } // === While === test "while" { // if (true) return error.Todo; try test_transpile("control_flow/while"); - try test_render(@import("./../data/control_flow/while.zig").Page); + try test_render("control_flow/while", @import("./../data/control_flow/while.zig").Page); } test "while_block" { // if (true) return error.Todo; try test_transpile("control_flow/while_block"); - try test_render(@import("./../data/control_flow/while_block.zig").Page); + try test_render("control_flow/while_block", @import("./../data/control_flow/while_block.zig").Page); } test "while_while" { // if (true) return error.Todo; try test_transpile("control_flow/while_while"); - try test_render(@import("./../data/control_flow/while_while.zig").Page); + try test_render("control_flow/while_while", @import("./../data/control_flow/while_while.zig").Page); } test "while_if" { // if (true) return error.Todo; try test_transpile("control_flow/while_if"); - try test_render(@import("./../data/control_flow/while_if.zig").Page); + try test_render("control_flow/while_if", @import("./../data/control_flow/while_if.zig").Page); } test "while_for" { // if (true) return error.Todo; try test_transpile("control_flow/while_for"); - try test_render(@import("./../data/control_flow/while_for.zig").Page); + try test_render("control_flow/while_for", @import("./../data/control_flow/while_for.zig").Page); } test "while_switch" { // if (true) return error.Todo; try test_transpile("control_flow/while_switch"); - try test_render(@import("./../data/control_flow/while_switch.zig").Page); + try test_render("control_flow/while_switch", @import("./../data/control_flow/while_switch.zig").Page); } // === Deeply Nested Control Flow (3-level) === test "if_for_if" { try test_transpile("control_flow/if_for_if"); - try test_render(@import("./../data/control_flow/if_for_if.zig").Page); + try test_render("control_flow/if_for_if", @import("./../data/control_flow/if_for_if.zig").Page); } // === Miscellaneous === test "attribute_builtin" { // if (true) return error.Todo; try test_transpile("attribute/builtin"); - try test_render(@import("./../data/attribute/builtin.zig").Page); + try test_render("attribute/builtin", @import("./../data/attribute/builtin.zig").Page); } test "attribute_component" { // if (true) return error.Todo; try test_transpile("attribute/component"); - try test_render(@import("./../data/attribute/component.zig").Page); + try test_render("attribute/component", @import("./../data/attribute/component.zig").Page); } test "attribute_builtin_escaping" { // if (true) return error.Todo; try test_transpile("attribute/builtin_escaping"); - try test_render(@import("./../data/attribute/builtin_escaping.zig").Page); + try test_render("attribute/builtin_escaping", @import("./../data/attribute/builtin_escaping.zig").Page); } test "attribute_dynamic" { try test_transpile("attribute/dynamic"); - try test_render(@import("./../data/attribute/dynamic.zig").Page); + try test_render("attribute/dynamic", @import("./../data/attribute/dynamic.zig").Page); } test "attribute_types" { try test_transpile("attribute/types"); - try test_render(@import("./../data/attribute/types.zig").Page); + try test_render("attribute/types", @import("./../data/attribute/types.zig").Page); } // === Element === test "element_void" { try test_transpile("element/void"); - try test_render(@import("./../data/element/void.zig").Page); + try test_render("element/void", @import("./../data/element/void.zig").Page); } test "element_empty" { try test_transpile("element/empty"); - try test_render(@import("./../data/element/empty.zig").Page); + try test_render("element/empty", @import("./../data/element/empty.zig").Page); } test "element_nested" { try test_transpile("element/nested"); - try test_render(@import("./../data/element/nested.zig").Page); + try test_render("element/nested", @import("./../data/element/nested.zig").Page); } test "element_fragment" { // TODO: Fragment transpilation generates invalid Zig code (trailing comma in empty children) // if (true) return error.SkipZigTest; try test_transpile("element/fragment"); - try test_render(@import("./../data/element/fragment.zig").Page); + try test_render("element/fragment", @import("./../data/element/fragment.zig").Page); } test "element_fragment_root" { try test_transpile("element/fragment_root"); - try test_render(@import("./../data/element/fragment_root.zig").Page); + try test_render("element/fragment_root", @import("./../data/element/fragment_root.zig").Page); } test "escaping_pre" { // if (true) return error.Todo; try test_transpile("escaping/pre"); - try test_render(@import("./../data/escaping/pre.zig").Page); + try test_render("escaping/pre", @import("./../data/escaping/pre.zig").Page); } test "escaping_quotes" { // if (true) return error.Todo; try test_transpile("escaping/quotes"); - try test_render(@import("./../data/escaping/quotes.zig").Page); + try test_render("escaping/quotes", @import("./../data/escaping/quotes.zig").Page); } test "expression_text" { // if (true) return error.Todo; try test_transpile("expression/text"); - try test_render(@import("./../data/expression/text.zig").Page); + try test_render("expression/text", @import("./../data/expression/text.zig").Page); } test "expression_format" { // if (true) return error.Todo; try test_transpile("expression/format"); - try test_render(@import("./../data/expression/format.zig").Page); + try test_render("expression/format", @import("./../data/expression/format.zig").Page); } test "expression_component" { // if (true) return error.Todo; try test_transpile("expression/component"); - try test_render(@import("./../data/expression/component.zig").Page); + try test_render("expression/component", @import("./../data/expression/component.zig").Page); } test "expression_mixed" { try test_transpile("expression/mixed"); - try test_render(@import("./../data/expression/mixed.zig").Page); + try test_render("expression/mixed", @import("./../data/expression/mixed.zig").Page); } test "expression_optional" { try test_transpile("expression/optional"); - try test_render(@import("./../data/expression/optional.zig").Page); + try test_render("expression/optional", @import("./../data/expression/optional.zig").Page); } test "expression_struct_access" { try test_transpile("expression/struct_access"); - try test_render(@import("./../data/expression/struct_access.zig").Page); + try test_render("expression/struct_access", @import("./../data/expression/struct_access.zig").Page); } test "expression_function_call" { try test_transpile("expression/function_call"); - try test_render(@import("./../data/expression/function_call.zig").Page); + try test_render("expression/function_call", @import("./../data/expression/function_call.zig").Page); } test "expression_multiline_string" { try test_transpile("expression/multiline_string"); - try test_render(@import("./../data/expression/multiline_string.zig").Page); + try test_render("expression/multiline_string", @import("./../data/expression/multiline_string.zig").Page); } test "component_basic" { // if (true) return error.Todo; try test_transpile("component/basic"); - try test_render(@import("./../data/component/basic.zig").Page); + try test_render("component/basic", @import("./../data/component/basic.zig").Page); } test "component_multiple" { // if (true) return error.Todo; try test_transpile("component/multiple"); - try test_render(@import("./../data/component/multiple.zig").Page); + try test_render("component/multiple", @import("./../data/component/multiple.zig").Page); } test "component_nested" { try test_transpile("component/nested"); - try test_render(@import("./../data/component/nested.zig").Page); + try test_render("component/nested", @import("./../data/component/nested.zig").Page); } test "component_children_only" { try test_transpile("component/children_only"); - try test_render(@import("./../data/component/children_only.zig").Page); + try test_render("component/children_only", @import("./../data/component/children_only.zig").Page); } test "component_csr_react" { // if (true) return error.Todo; try test_transpile("component/csr_react"); - try test_render(@import("./../data/component/csr_react.zig").Page); + try test_render("component/csr_react", @import("./../data/component/csr_react.zig").Page); } test "component_csr_react_multiple" { // if (true) return error.Todo; try test_transpile("component/csr_react_multiple"); - try test_render(@import("./../data/component/csr_react_multiple.zig").Page); + try test_render("component/csr_react_multiple", @import("./../data/component/csr_react_multiple.zig").Page); } test "component_csr_zig" { // if (true) return error.Todo; try test_transpile("component/csr_zig"); - try test_render(@import("./../data/component/csr_zig.zig").Page); + try test_render("component/csr_zig", @import("./../data/component/csr_zig.zig").Page); } test "component_import" { // if (true) return error.Todo; try test_transpile("component/import"); - try test_render(@import("./../data/component/import.zig").Page); + try test_render("component/import", @import("./../data/component/import.zig").Page); } test "component_root_cmp" { // if (true) return error.Todo; try test_transpile("component/root_cmp"); - try test_render(@import("./../data/component/root_cmp.zig").Page); + try test_render("component/root_cmp", @import("./../data/component/root_cmp.zig").Page); } -test "performance" { +test "performance > transpile" { // if (true) return error.Todo; const MAX_TIME_MS = 50.0 * 8; // 50ms is on M1 Pro const MAX_TIME_PER_FILE_MS = 8.0 * 10; // 5ms is on M1 Pro @@ -352,6 +352,30 @@ test "performance" { try expectLessThan(MAX_TIME_PER_FILE_MS, average_time_ms); } +test "performance > render" { + // if (true) return error.Todo; + const MAX_TIME_MS = 5.0 * 8; // 3.5ms is on M1 Pro + const MAX_TIME_PER_FILE_MS = 0.10 * 10; // 0.06ms is on M1 Pro + + var total_time_ns: f64 = 0.0; + inline for (TestFileCache.test_files) |comptime_path| { + const start_time = std.time.nanoTimestamp(); + try test_render_inner(comptime_path, true); + const end_time = std.time.nanoTimestamp(); + const duration = @as(f64, @floatFromInt(end_time - start_time)); + total_time_ns += duration; + const duration_ms = duration / std.time.ns_per_ms; + try expectLessThan(MAX_TIME_PER_FILE_MS, duration_ms); + } + + const total_time_ms = total_time_ns / std.time.ns_per_ms; + const average_time_ms = total_time_ms / TestFileCache.test_files.len; + std.debug.print("\x1b[33m⏲\x1b[0m render \x1b[90m>\x1b[0m {d:.2}ms | Avg: {d:.2}ms\n", .{ total_time_ms, average_time_ms }); + + try expectLessThan(MAX_TIME_MS, total_time_ms); + try expectLessThan(MAX_TIME_PER_FILE_MS, average_time_ms); +} + fn test_transpile(comptime file_path: []const u8) !void { try test_transpile_inner(file_path, false); } @@ -364,6 +388,7 @@ fn test_transpile_inner(comptime file_path: []const u8, comptime no_expect: bool const source_path = file_path ++ ".zx"; const expected_source_path = file_path ++ ".zig"; const full_file_path = "test/data/" ++ file_path ++ ".zx"; + const output_zig_path = "test/data/" ++ file_path ++ ".zig"; // Get pre-loaded source file const source = cache.get(source_path) orelse return error.FileNotFound; @@ -374,6 +399,22 @@ fn test_transpile_inner(comptime file_path: []const u8, comptime no_expect: bool var result = try zx.Ast.parse(allocator, source_z, .{ .path = full_file_path, .version = .new }); defer result.deinit(allocator); + // Check for SNAPSHOT=1 environment variable + const snapshot_mode = std.posix.getenv("SNAPSHOT") != null; + if (snapshot_mode) { + // Save the transpiled output to .zig file + const file = std.fs.cwd().createFile(output_zig_path, .{}) catch |err| { + std.log.err("Failed to create snapshot file {s}: {}\n", .{ output_zig_path, err }); + return err; + }; + defer file.close(); + file.writeAll(result.zig_source) catch |err| { + std.log.err("Failed to write snapshot file {s}: {}\n", .{ output_zig_path, err }); + return err; + }; + return; // Skip comparison in snapshot mode + } + // Get pre-loaded expected file const expected_source = cache.get(expected_source_path) orelse { std.log.err("Expected file not found: {s}\n", .{expected_source_path}); @@ -388,7 +429,88 @@ fn test_transpile_inner(comptime file_path: []const u8, comptime no_expect: bool } } -fn test_render(comptime cmp: fn (allocator: std.mem.Allocator) zx.Component) !void { +fn test_render(comptime file_path: []const u8, comptime cmp: fn (allocator: std.mem.Allocator) zx.Component) !void { + try test_render_inner_with_cmp(file_path, cmp, false); +} + +fn test_render_inner(comptime file_path: []const u8, comptime no_expect: bool) !void { + const cmp = comptime getPageFn(file_path) orelse return; + try test_render_inner_with_cmp(file_path, cmp, no_expect); +} + +fn getPageFn(comptime path: []const u8) ?fn (std.mem.Allocator) zx.Component { + const imports = .{ + .{ "control_flow/if", @import("./../data/control_flow/if.zig") }, + .{ "control_flow/if_block", @import("./../data/control_flow/if_block.zig") }, + .{ "control_flow/if_only", @import("./../data/control_flow/if_only.zig") }, + .{ "control_flow/if_only_block", @import("./../data/control_flow/if_only_block.zig") }, + .{ "control_flow/for", @import("./../data/control_flow/for.zig") }, + .{ "control_flow/for_block", @import("./../data/control_flow/for_block.zig") }, + .{ "control_flow/switch", @import("./../data/control_flow/switch.zig") }, + .{ "control_flow/switch_block", @import("./../data/control_flow/switch_block.zig") }, + .{ "control_flow/while", @import("./../data/control_flow/while.zig") }, + .{ "control_flow/while_block", @import("./../data/control_flow/while_block.zig") }, + .{ "control_flow/if_if", @import("./../data/control_flow/if_if.zig") }, + .{ "control_flow/if_for", @import("./../data/control_flow/if_for.zig") }, + .{ "control_flow/if_switch", @import("./../data/control_flow/if_switch.zig") }, + .{ "control_flow/if_while", @import("./../data/control_flow/if_while.zig") }, + .{ "control_flow/if_if_only", @import("./../data/control_flow/if_if_only.zig") }, + .{ "control_flow/if_if_only_block", @import("./../data/control_flow/if_if_only_block.zig") }, + .{ "control_flow/if_else_if", @import("./../data/control_flow/if_else_if.zig") }, + .{ "control_flow/if_capture", @import("./../data/control_flow/if_capture.zig") }, + .{ "control_flow/for_if", @import("./../data/control_flow/for_if.zig") }, + .{ "control_flow/for_for", @import("./../data/control_flow/for_for.zig") }, + .{ "control_flow/for_switch", @import("./../data/control_flow/for_switch.zig") }, + .{ "control_flow/for_while", @import("./../data/control_flow/for_while.zig") }, + .{ "control_flow/switch_if", @import("./../data/control_flow/switch_if.zig") }, + .{ "control_flow/switch_for", @import("./../data/control_flow/switch_for.zig") }, + .{ "control_flow/switch_switch", @import("./../data/control_flow/switch_switch.zig") }, + .{ "control_flow/switch_while", @import("./../data/control_flow/switch_while.zig") }, + .{ "control_flow/while_if", @import("./../data/control_flow/while_if.zig") }, + .{ "control_flow/while_for", @import("./../data/control_flow/while_for.zig") }, + .{ "control_flow/while_switch", @import("./../data/control_flow/while_switch.zig") }, + .{ "control_flow/while_while", @import("./../data/control_flow/while_while.zig") }, + .{ "control_flow/if_for_if", @import("./../data/control_flow/if_for_if.zig") }, + .{ "expression/text", @import("./../data/expression/text.zig") }, + .{ "expression/format", @import("./../data/expression/format.zig") }, + .{ "expression/component", @import("./../data/expression/component.zig") }, + .{ "expression/mixed", @import("./../data/expression/mixed.zig") }, + .{ "expression/struct_access", @import("./../data/expression/struct_access.zig") }, + .{ "expression/function_call", @import("./../data/expression/function_call.zig") }, + .{ "expression/multiline_string", @import("./../data/expression/multiline_string.zig") }, + .{ "expression/optional", @import("./../data/expression/optional.zig") }, + .{ "component/basic", @import("./../data/component/basic.zig") }, + .{ "component/multiple", @import("./../data/component/multiple.zig") }, + .{ "component/nested", @import("./../data/component/nested.zig") }, + .{ "component/children_only", @import("./../data/component/children_only.zig") }, + .{ "component/csr_react", @import("./../data/component/csr_react.zig") }, + .{ "component/csr_react_multiple", @import("./../data/component/csr_react_multiple.zig") }, + .{ "component/csr_zig", @import("./../data/component/csr_zig.zig") }, + .{ "component/import", @import("./../data/component/import.zig") }, + .{ "component/root_cmp", @import("./../data/component/root_cmp.zig") }, + .{ "attribute/builtin", @import("./../data/attribute/builtin.zig") }, + .{ "attribute/component", @import("./../data/attribute/component.zig") }, + .{ "attribute/builtin_escaping", @import("./../data/attribute/builtin_escaping.zig") }, + .{ "attribute/dynamic", @import("./../data/attribute/dynamic.zig") }, + .{ "attribute/types", @import("./../data/attribute/types.zig") }, + .{ "element/void", @import("./../data/element/void.zig") }, + .{ "element/empty", @import("./../data/element/empty.zig") }, + .{ "element/nested", @import("./../data/element/nested.zig") }, + .{ "element/fragment", @import("./../data/element/fragment.zig") }, + .{ "element/fragment_root", @import("./../data/element/fragment_root.zig") }, + .{ "escaping/pre", @import("./../data/escaping/pre.zig") }, + .{ "escaping/quotes", @import("./../data/escaping/quotes.zig") }, + }; + + inline for (imports) |entry| { + if (std.mem.eql(u8, entry[0], path)) { + return entry[1].Page; + } + } + return null; +} + +fn test_render_inner_with_cmp(comptime file_path: []const u8, comptime cmp: fn (allocator: std.mem.Allocator) zx.Component, comptime no_expect: bool) !void { const gpa = std.testing.allocator; var aa = std.heap.ArenaAllocator.init(gpa); defer aa.deinit(); @@ -401,9 +523,33 @@ fn test_render(comptime cmp: fn (allocator: std.mem.Allocator) zx.Component) !vo const rendered = aw.written(); try testing.expect(rendered.len > 0); - // std.debug.print("\x1b[32m✓\x1b[0m Rendered: {s}\n", .{rendered}); + if (no_expect) return; - // try testing.expectEqualStrings(expected_source_z, rendered); + const html_path = "test/data/" ++ file_path ++ ".html"; + + // Check for SNAPSHOT=1 environment variable + const snapshot_mode = std.posix.getenv("SNAPSHOT") != null; + if (snapshot_mode) { + // Save the rendered output to .html file + const file = std.fs.cwd().createFile(html_path, .{}) catch |err| { + std.log.err("Failed to create snapshot file {s}: {}\n", .{ html_path, err }); + return err; + }; + defer file.close(); + file.writeAll(rendered) catch |err| { + std.log.err("Failed to write snapshot file {s}: {}\n", .{ html_path, err }); + return err; + }; + return; // Skip comparison in snapshot mode + } + + // Read expected HTML file directly + const expected_html = std.fs.cwd().readFileAlloc(allocator, html_path, std.math.maxInt(usize)) catch |err| { + std.log.err("Expected HTML file not found: {s}\n", .{html_path}); + return err; + }; + + try testing.expectEqualStrings(expected_html, rendered); } fn expectLessThan(expected: f64, actual: f64) !void {