diff --git a/.gitignore b/.gitignore
index d256e605..de2009aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@ hosts/ocaml/test-results/
shared/static/wasm/sx_browser.bc.wasm.assets/
.claude/worktrees/
tests/playwright/test-results/
+test-results/
test-case-define.sx
test-case-define.txt
test_all.js
diff --git a/hosts/ocaml/bin/mcp_tree.ml b/hosts/ocaml/bin/mcp_tree.ml
index 0d269a00..ebcb1f50 100644
--- a/hosts/ocaml/bin/mcp_tree.ml
+++ b/hosts/ocaml/bin/mcp_tree.ml
@@ -155,6 +155,192 @@ let register_mcp_jit_hook () =
| None -> None))
| _ -> None)
+(* ------------------------------------------------------------------ *)
+(* Native tree-tools helpers — avoid CEK overhead on big trees. *)
+(* Mirror the SX semantics from lib/tree-tools.sx but run as direct *)
+(* OCaml recursion. Used by read-subtree / validate / find-all / *)
+(* find-across / comp-usage handlers. *)
+(* ------------------------------------------------------------------ *)
+
+let native_path_str path =
+ "[" ^ String.concat "," (List.map string_of_int path) ^ "]"
+
+let rec native_node_display (node : value) : string =
+ match node with
+ | Nil -> "nil"
+ | Symbol s -> s
+ | Keyword k -> ":" ^ k
+ | String s -> "\"" ^ s ^ "\""
+ | Number n ->
+ if Float.is_integer n then string_of_int (int_of_float n)
+ else Printf.sprintf "%g" n
+ | Bool true -> "true"
+ | Bool false -> "false"
+ | List [] | ListRef { contents = [] } -> "()"
+ | List (h :: t) | ListRef { contents = h :: t } ->
+ if t = [] then "(" ^ native_node_display h ^ ")"
+ else "(" ^ native_node_display h ^ " ...)"
+ | Dict _ -> "{...}"
+ | v -> Sx_runtime.value_to_str v
+
+(* node-summary-short: "(head)" for singletons, "(head second ...)" for >3,
+ "(all node-displays joined)" for small lists. Mirrors lib/tree-tools.sx. *)
+let native_node_summary_short node =
+ match node with
+ | List [] | ListRef { contents = [] } -> "()"
+ | List items | ListRef { contents = items } ->
+ let n = List.length items in
+ if n > 3 then
+ let head = native_node_display (List.hd items) in
+ let second = native_node_display (List.nth items 1) in
+ Printf.sprintf "(%s %s ...)" head second
+ else
+ "(" ^ String.concat " " (List.map native_node_display items) ^ ")"
+ | _ -> native_node_display node
+
+let rec native_node_matches node pattern =
+ match node with
+ | Symbol s | String s ->
+ (try ignore (Str.search_forward (Str.regexp_string pattern) s 0); true
+ with Not_found -> false)
+ | List items | ListRef { contents = items } ->
+ List.exists (fun c -> native_node_matches c pattern) items
+ | _ -> false
+
+(* Raw find: returns reversed list of (path, summary) pairs — caller reverses. *)
+let native_find_all_raw exprs pattern =
+ let nodes = match exprs with
+ | List xs | ListRef { contents = xs } -> xs
+ | x -> [x] in
+ let acc = ref [] in
+ let rec go node path =
+ if native_node_matches node pattern then
+ acc := (List.rev path, native_node_summary_short node) :: !acc;
+ match node with
+ | List items | ListRef { contents = items } ->
+ List.iteri (fun i child -> go child (i :: path)) items
+ | _ -> ()
+ in
+ List.iteri (fun i node -> go node [i]) nodes;
+ List.rev !acc
+
+(* SX-value-returning wrapper — kept for any SX code that still calls find-all. *)
+let native_find_all_sx exprs pattern =
+ let pairs = native_find_all_raw exprs pattern in
+ List (List.map (fun (path, summary) ->
+ List [List (List.map (fun i -> Number (float_of_int i)) path);
+ String summary]
+ ) pairs)
+
+(* navigate: walk a tree by a list of indices. Wraps exprs into a list on entry
+ (mirrors SX navigate). Returns Nil if any index is out of range. *)
+let native_navigate exprs path =
+ let init = match exprs with
+ | List _ | ListRef _ -> exprs
+ | x -> List [x] in
+ let rec step current = function
+ | [] -> current
+ | i :: rest ->
+ (match current with
+ | List items when i >= 0 && i < List.length items ->
+ step (List.nth items i) rest
+ | ListRef { contents = items } when i >= 0 && i < List.length items ->
+ step (List.nth items i) rest
+ | _ -> Nil) in
+ step init path
+
+(* annotate-tree: render a list of exprs as an indented path-annotated string.
+ Mirrors the `annotate-node` dispatch from lib/tree-tools.sx:
+ - small lists (len<=4, no list children) render inline
+ - others render head on first line, children indented, closing ")"
+ Path annotations use [i,j,k,...] form. *)
+let native_annotate_tree exprs =
+ let nodes = match exprs with
+ | List xs | ListRef { contents = xs } -> xs
+ | x -> [x] in
+ let out = Buffer.create 1024 in
+ let first = ref true in
+ let emit s =
+ if !first then first := false else Buffer.add_char out '\n';
+ Buffer.add_string out s in
+ let rec ann node path depth =
+ let indent = String.make (depth * 2) ' ' in
+ let label = native_path_str (List.rev path) in
+ match node with
+ | List [] | ListRef { contents = [] } ->
+ emit (indent ^ label ^ " ()")
+ | List items | ListRef { contents = items } ->
+ let n = List.length items in
+ let rest = List.tl items in
+ let any_child_list = List.exists
+ (fun c -> match c with List _ | ListRef _ -> true | _ -> false) rest in
+ if n <= 4 && not any_child_list then
+ emit (indent ^ label ^ " (" ^
+ String.concat " " (List.map native_node_display items) ^ ")")
+ else begin
+ let head_str = native_node_display (List.hd items) in
+ emit (indent ^ label ^ " (" ^ head_str);
+ List.iteri (fun i child ->
+ if i > 0 then ann child (i :: path) (depth + 1)
+ ) items;
+ emit (indent ^ " )")
+ end
+ | _ ->
+ emit (indent ^ label ^ " " ^ native_node_display node)
+ in
+ List.iteri (fun i node -> ann node [i] 0) nodes;
+ Buffer.contents out
+
+let native_read_subtree exprs path =
+ let node = native_navigate exprs path in
+ match node with
+ | Nil -> "Error: path " ^ native_path_str path ^ " not found"
+ | _ -> native_annotate_tree (List [node])
+
+(* validate: walk the tree, emit WARNING for malformed letrec bindings and
+ ERROR for defisland/defcomp with fewer than 3 args. *)
+let native_validate exprs =
+ let errors = ref [] in
+ let emit s = errors := s :: !errors in
+ let rec go node path =
+ (match node with
+ | List items | ListRef { contents = items } ->
+ (match items with
+ | [] -> ()
+ | head :: _ ->
+ let head_name = match head with Symbol s -> Some s | _ -> None in
+ (match head_name with
+ | Some "letrec" when List.length items >= 2 ->
+ let bindings = List.nth items 1 in
+ (match bindings with
+ | List pairs | ListRef { contents = pairs } ->
+ List.iteri (fun i pair ->
+ let ok = match pair with
+ | List (Symbol _ :: _ :: _) -> true
+ | ListRef { contents = Symbol _ :: _ :: _ } -> true
+ | _ -> false in
+ if not ok then
+ emit (Printf.sprintf
+ "WARNING %s: letrec binding %d is not a (name value) pair: %s"
+ (native_path_str (List.rev (i :: 1 :: path)))
+ i
+ (native_node_display pair))
+ ) pairs
+ | _ -> ())
+ | Some (("defisland" | "defcomp") as nm) when List.length items < 4 ->
+ emit (Printf.sprintf
+ "ERROR %s: %s has fewer than 3 args (name params b..."
+ (native_path_str (List.rev path)) nm)
+ | _ -> ()));
+ List.iteri (fun i child -> go child (i :: path)) items
+ | _ -> ())
+ in
+ let nodes = match exprs with
+ | List xs | ListRef { contents = xs } -> xs
+ | x -> [x] in
+ List.iteri (fun i node -> go node [i]) nodes;
+ if !errors = [] then "OK" else String.concat "\n" (List.rev !errors)
+
let setup_env () =
let e = make_env () in
(* Primitives are auto-registered at module init *)
@@ -439,6 +625,30 @@ let setup_env () =
try load_sx_file e (Filename.concat lib_dir "compiler.sx");
register_mcp_jit_hook ()
with exn -> Printf.eprintf "[mcp] Warning: compiler.sx load failed (JIT disabled): %s\n%!" (Printexc.to_string exn));
+
+ (* Native-impl the hot tree-tools ops — replaces SX versions to avoid CEK
+ overhead on big trees. See native_* helpers defined above setup_env. *)
+ ignore (Sx_types.env_bind e "find-all" (NativeFn ("find-all", fun args ->
+ match args with
+ | [exprs; String pattern] -> native_find_all_sx exprs pattern
+ | _ -> List [])));
+ ignore (Sx_types.env_bind e "read-subtree" (NativeFn ("read-subtree", fun args ->
+ match args with
+ | [exprs; List path] | [exprs; ListRef { contents = path }] ->
+ let ints = List.map (fun v -> match v with Number n -> int_of_float n | _ -> 0) path in
+ String (native_read_subtree exprs ints)
+ | _ -> String "")));
+ ignore (Sx_types.env_bind e "validate" (NativeFn ("validate", fun args ->
+ match args with
+ | [exprs] -> String (native_validate exprs)
+ | _ -> String "")));
+ ignore (Sx_types.env_bind e "path-str" (NativeFn ("path-str", fun args ->
+ match args with
+ | [List path] | [ListRef { contents = path }] ->
+ let ints = List.map (fun v -> match v with Number n -> int_of_float n | _ -> 0) path in
+ String (native_path_str ints)
+ | _ -> String "[]")));
+
Printf.eprintf "[mcp] Ready in %.0fms\n%!" ((Unix.gettimeofday () -. t0) *. 1000.0);
env := e
@@ -804,7 +1014,11 @@ let handle_sx_read_subtree args =
let open Yojson.Safe.Util in
let tree = parse_file (require_file args "file") in
let path = resolve_path tree (args |> member "path" |> to_string) in
- text_result (value_to_string (call_sx "read-subtree" [tree; path]))
+ let ints = match path with
+ | List xs | ListRef { contents = xs } ->
+ List.map (fun v -> match v with Number n -> int_of_float n | _ -> 0) xs
+ | _ -> [] in
+ text_result (native_read_subtree tree ints)
let handle_sx_get_context args =
let open Yojson.Safe.Util in
@@ -816,17 +1030,8 @@ let handle_sx_find_all args =
let open Yojson.Safe.Util in
let tree = parse_file (require_file args "file") in
let pattern = args |> member "pattern" |> to_string in
- let results = call_sx "find-all" [tree; String pattern] in
- let lines = match results with
- | List items | ListRef { contents = items } ->
- List.map (fun item ->
- match item with
- | List [p; s] | ListRef { contents = [p; s] } ->
- value_to_string (call_sx "path-str" [p]) ^ " " ^ value_to_string s
- | _ -> value_to_string item
- ) items
- | _ -> [value_to_string results]
- in
+ let results = native_find_all_raw tree pattern in
+ let lines = List.map (fun (p, s) -> native_path_str p ^ " " ^ s) results in
text_result (String.concat "\n" lines)
let handle_sx_get_siblings args =
@@ -843,7 +1048,7 @@ let handle_sx_get_siblings args =
let handle_sx_validate args =
let tree = parse_file (require_file args "file") in
- text_result (value_to_string (call_sx "validate" [tree]))
+ text_result (native_validate tree)
let handle_sx_replace_node args =
let open Yojson.Safe.Util in
@@ -1912,16 +2117,8 @@ let handle_sx_find_across args =
let rel = relative_path ~base:dir path in
try
let tree = parse_file path in
- let results = call_sx "find-all" [tree; String pattern] in
- (match results with
- | List items | ListRef { contents = items } ->
- List.map (fun item ->
- match item with
- | List [p; s] | ListRef { contents = [p; s] } ->
- rel ^ " " ^ value_to_string (call_sx "path-str" [p]) ^ " " ^ value_to_string s
- | _ -> rel ^ " " ^ value_to_string item
- ) items
- | _ -> [])
+ let results = native_find_all_raw tree pattern in
+ List.map (fun (p, s) -> rel ^ " " ^ native_path_str p ^ " " ^ s) results
with _ -> []
) files in
if all_lines = [] then text_result "(no matches)"
@@ -1948,16 +2145,8 @@ let handle_sx_comp_usage args =
let rel = relative_path ~base:dir path in
try
let tree = parse_file path in
- let results = call_sx "find-all" [tree; String name] in
- (match results with
- | List items | ListRef { contents = items } ->
- List.map (fun item ->
- match item with
- | List [p; s] | ListRef { contents = [p; s] } ->
- rel ^ " " ^ value_to_string (call_sx "path-str" [p]) ^ " " ^ value_to_string s
- | _ -> rel ^ " " ^ value_to_string item
- ) items
- | _ -> [])
+ let results = native_find_all_raw tree name in
+ List.map (fun (p, s) -> rel ^ " " ^ native_path_str p ^ " " ^ s) results
with _ -> []
) files in
if all_lines = [] then text_result "(no usages found)"
diff --git a/hosts/ocaml/bin/run_tests.ml b/hosts/ocaml/bin/run_tests.ml
index c9372942..823df835 100644
--- a/hosts/ocaml/bin/run_tests.ml
+++ b/hosts/ocaml/bin/run_tests.ml
@@ -25,6 +25,9 @@ open Sx_ref
let pass_count = ref 0
let fail_count = ref 0
let suite_stack : string list ref = ref []
+(* Test filter: when Some, only run tests (suite, name) in the set.
+ Populated by --only-failing=FILE from lines like "FAIL: suite > name: error". *)
+let suite_filter : (string * string, unit) Hashtbl.t option ref = ref None
(* ====================================================================== *)
(* Deep equality — SX structural comparison *)
@@ -176,6 +179,17 @@ let make_test_env () =
suite_stack := (match !suite_stack with _ :: t -> t | [] -> []);
Nil);
+ bind "test-allowed?" (fun args ->
+ match !suite_filter with
+ | None -> Bool true
+ | Some filter ->
+ let name = match args with
+ | [String s] -> s
+ | [v] -> Sx_types.value_to_string v
+ | _ -> "" in
+ let suite = match !suite_stack with [] -> "" | s :: _ -> s in
+ Bool (Hashtbl.mem filter (suite, name)));
+
(* --- Test helpers --- *)
bind "sx-parse" (fun args ->
@@ -1563,6 +1577,131 @@ let run_spec_tests env test_files =
| _ -> child
in
+ (* Minimal HTML parser for test mock.
+ Parses an HTML string into mock child elements and appends them to `parent`.
+ Handles: content, nested elements,
+ self-closing tags, text content. No comments, CDATA, DOCTYPE, or entities. *)
+ let parse_html_into parent_d html =
+ let len = String.length html in
+ let pos = ref 0 in
+ let is_name_char c =
+ (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') || c = '-' || c = '_' || c = ':'
+ in
+ let skip_ws () =
+ while !pos < len && (let c = html.[!pos] in c = ' ' || c = '\n' || c = '\t' || c = '\r') do
+ incr pos
+ done
+ in
+ let parse_name () =
+ let start = !pos in
+ while !pos < len && is_name_char html.[!pos] do incr pos done;
+ String.sub html start (!pos - start)
+ in
+ let parse_attr_value () =
+ if !pos >= len then ""
+ else if html.[!pos] = '"' then begin
+ incr pos;
+ let start = !pos in
+ while !pos < len && html.[!pos] <> '"' do incr pos done;
+ let v = String.sub html start (!pos - start) in
+ if !pos < len then incr pos;
+ v
+ end
+ else if html.[!pos] = '\'' then begin
+ incr pos;
+ let start = !pos in
+ while !pos < len && html.[!pos] <> '\'' do incr pos done;
+ let v = String.sub html start (!pos - start) in
+ if !pos < len then incr pos;
+ v
+ end
+ else begin
+ let start = !pos in
+ while !pos < len && (let c = html.[!pos] in
+ c <> ' ' && c <> '\t' && c <> '\n' && c <> '\r'
+ && c <> '>' && c <> '/') do
+ incr pos
+ done;
+ String.sub html start (!pos - start)
+ end
+ in
+ let parse_attrs (elem : (string, Sx_types.value) Hashtbl.t) =
+ skip_ws ();
+ while !pos < len && html.[!pos] <> '>' && html.[!pos] <> '/' do
+ let name = parse_name () in
+ if name = "" then begin
+ (* Avoid infinite loop on unexpected char *)
+ if !pos < len then incr pos
+ end else begin
+ let value =
+ if !pos < len && html.[!pos] = '=' then begin
+ incr pos; parse_attr_value ()
+ end else ""
+ in
+ let attrs = match Hashtbl.find_opt elem "attributes" with
+ | Some (Dict a) -> a
+ | _ -> let a = Hashtbl.create 4 in Hashtbl.replace elem "attributes" (Dict a); a in
+ Hashtbl.replace attrs name (String value);
+ if name = "id" then Hashtbl.replace elem "id" (String value);
+ if name = "class" then Hashtbl.replace elem "className" (String value);
+ if name = "value" then Hashtbl.replace elem "value" (String value);
+ skip_ws ()
+ end
+ done
+ in
+ let void_tags = ["br"; "hr"; "img"; "input"; "meta"; "link"; "area";
+ "base"; "col"; "embed"; "source"; "track"; "wbr"] in
+ let rec parse_children parent_elem =
+ while !pos < len && not (!pos + 1 < len && html.[!pos] = '<' && html.[!pos + 1] = '/') do
+ if !pos < len && html.[!pos] = '<' && !pos + 1 < len && is_name_char html.[!pos + 1] then
+ parse_element parent_elem
+ else if !pos < len && html.[!pos] = '<' then begin
+ (* Unknown/comment — skip to next '>' *)
+ while !pos < len && html.[!pos] <> '>' do incr pos done;
+ if !pos < len then incr pos
+ end
+ else begin
+ let start = !pos in
+ while !pos < len && html.[!pos] <> '<' do incr pos done;
+ let text = String.sub html start (!pos - start) in
+ if String.trim text <> "" then begin
+ let cur = match Hashtbl.find_opt parent_elem "textContent" with
+ | Some (String s) -> s | _ -> "" in
+ Hashtbl.replace parent_elem "textContent" (String (cur ^ text))
+ end
+ end
+ done
+ and parse_element parent_elem =
+ incr pos; (* skip '<' *)
+ let tag = parse_name () in
+ if tag = "" then () else begin
+ let el = make_mock_element tag in
+ let eld = match el with Dict d -> d | _ -> Hashtbl.create 0 in
+ parse_attrs eld;
+ skip_ws ();
+ let self_closing =
+ if !pos < len && html.[!pos] = '/' then begin incr pos; true end else false
+ in
+ if !pos < len && html.[!pos] = '>' then incr pos;
+ let is_void = List.mem (String.lowercase_ascii tag) void_tags in
+ if not self_closing && not is_void then begin
+ parse_children eld;
+ if !pos + 1 < len && html.[!pos] = '<' && html.[!pos + 1] = '/' then begin
+ pos := !pos + 2;
+ let _ = parse_name () in
+ skip_ws ();
+ if !pos < len && html.[!pos] = '>' then incr pos
+ end
+ end;
+ ignore (mock_append_child (Dict parent_elem) el)
+ end
+ in
+ pos := 0;
+ parse_children parent_d
+ in
+ let _ = parse_html_into in
+
(* Helper: remove child from parent *)
let mock_remove_child parent child =
match parent, child with
@@ -1578,11 +1717,21 @@ let run_spec_tests env test_files =
in
(* Helper: querySelector - find element matching selector in tree *)
- let mock_matches el sel =
+ let rec mock_matches el sel =
match el with
| Dict d ->
let sel = String.trim sel in
- if String.length sel > 0 && sel.[0] = '#' then
+ (* Compound selector: tag[attr=value] or tag.class or tag#id — split into parts *)
+ if String.length sel > 1 &&
+ ((sel.[0] >= 'a' && sel.[0] <= 'z') || (sel.[0] >= 'A' && sel.[0] <= 'Z')) &&
+ (String.contains sel '[' || String.contains sel '.' || String.contains sel '#') then
+ let i = ref 0 in
+ let n = String.length sel in
+ while !i < n && ((sel.[!i] >= 'a' && sel.[!i] <= 'z') || (sel.[!i] >= 'A' && sel.[!i] <= 'Z') || (sel.[!i] >= '0' && sel.[!i] <= '9') || sel.[!i] = '-') do incr i done;
+ let tag_part = String.sub sel 0 !i in
+ let rest_part = String.sub sel !i (n - !i) in
+ (mock_matches el tag_part) && (mock_matches el rest_part)
+ else if String.length sel > 0 && sel.[0] = '#' then
let id = String.sub sel 1 (String.length sel - 1) in
(match Hashtbl.find_opt d "id" with Some (String i) -> i = id | _ -> false)
else if String.length sel > 0 && sel.[0] = '.' then
@@ -1590,7 +1739,8 @@ let run_spec_tests env test_files =
List.mem cls (get_classes d)
else if String.length sel > 0 && sel.[0] = '[' then
(* [attr] or [attr="value"] *)
- let inner = String.sub sel 1 (String.length sel - 2) in
+ let end_bracket = try String.index sel ']' with Not_found -> String.length sel - 1 in
+ let inner = String.sub sel 1 (end_bracket - 1) in
(match String.split_on_char '=' inner with
| [attr] ->
let attrs = match Hashtbl.find_opt d "attributes" with Some (Dict a) -> a | _ -> Hashtbl.create 0 in
@@ -1621,19 +1771,51 @@ let run_spec_tests env test_files =
| found -> mock_query_selector found (String.concat " " rest))
| [] -> Nil
and mock_query_selector_single el sel =
- match el with
- | Dict d ->
- let kids = match Hashtbl.find_opt d "children" with Some (List l) -> l | _ -> [] in
- let rec search = function
- | [] -> Nil
- | child :: rest ->
- if mock_matches child sel then child
- else match mock_query_selector_single child sel with
- | Nil -> search rest
- | found -> found
- in
- search kids
- | _ -> Nil
+ (* Handle tag:nth-of-type(N): find Nth child of same tag under parent *)
+ let nth_match = try
+ let idx = String.index sel ':' in
+ let tag = String.sub sel 0 idx in
+ let rest = String.sub sel idx (String.length sel - idx) in
+ if String.length rest > String.length ":nth-of-type(" &&
+ String.sub rest 0 (String.length ":nth-of-type(") = ":nth-of-type(" &&
+ rest.[String.length rest - 1] = ')'
+ then
+ let n_str = String.sub rest (String.length ":nth-of-type(")
+ (String.length rest - String.length ":nth-of-type(" - 1) in
+ (try Some (tag, int_of_string (String.trim n_str)) with _ -> None)
+ else None
+ with Not_found -> None in
+ (match nth_match with
+ | Some (tag, n) ->
+ (* Walk tree; collect matching-tag elements in document order; return nth *)
+ let found = ref [] in
+ let rec walk node =
+ match node with
+ | Dict d ->
+ let kids = match Hashtbl.find_opt d "children" with Some (List l) -> l | _ -> [] in
+ List.iter (fun child ->
+ if mock_matches child tag then found := child :: !found;
+ walk child
+ ) kids
+ | _ -> ()
+ in
+ walk el;
+ let matches = List.rev !found in
+ (try List.nth matches (n - 1) with _ -> Nil)
+ | None ->
+ (match el with
+ | Dict d ->
+ let kids = match Hashtbl.find_opt d "children" with Some (List l) -> l | _ -> [] in
+ let rec search = function
+ | [] -> Nil
+ | child :: rest ->
+ if mock_matches child sel then child
+ else match mock_query_selector_single child sel with
+ | Nil -> search rest
+ | found -> found
+ in
+ search kids
+ | _ -> Nil))
in
let rec mock_query_all el sel =
@@ -1742,7 +1924,7 @@ let run_spec_tests env test_files =
| "addEventListener" | "removeEventListener" | "dispatchEvent"
| "appendChild" | "removeChild" | "insertBefore" | "replaceChild"
| "querySelector" | "querySelectorAll" | "closest" | "matches"
- | "contains" | "cloneNode" | "remove" | "focus" | "blur" | "click"
+ | "contains" | "compareDocumentPosition" | "cloneNode" | "remove" | "focus" | "blur" | "click"
| "insertAdjacentHTML" | "prepend" | "showModal" | "show" | "close"
| "getBoundingClientRect" | "getAnimations" | "scrollIntoView"
| "scrollTo" | "scroll" | "reset" -> Bool true
@@ -1841,16 +2023,19 @@ let run_spec_tests env test_files =
| Some (Dict _cl) -> () (* classes live in className *)
| _ -> ())
| "innerHTML" ->
- (* Setting innerHTML clears children and syncs textContent (like a browser) *)
+ (* Setting innerHTML clears existing children, parses the HTML, and
+ creates new mock child elements (approximating browser behavior). *)
let kids = match Hashtbl.find_opt d "children" with Some (List l) -> l | _ -> [] in
List.iter (fun c -> match c with Dict cd ->
Hashtbl.replace cd "parentElement" Nil;
Hashtbl.replace cd "parentNode" Nil | _ -> ()) kids;
Hashtbl.replace d "children" (List []);
Hashtbl.replace d "childNodes" (List []);
- (* Approximate textContent: strip HTML tags from innerHTML *)
+ Hashtbl.replace d "textContent" (String "");
(match stored with
- | String s ->
+ | String s when String.contains s '<' ->
+ parse_html_into d s;
+ (* Strip tags for a best-effort textContent *)
let buf = Buffer.create (String.length s) in
let in_tag = ref false in
String.iter (fun c ->
@@ -1859,6 +2044,7 @@ let run_spec_tests env test_files =
else if not !in_tag then Buffer.add_char buf c
) s;
Hashtbl.replace d "textContent" (String (Buffer.contents buf))
+ | String s -> Hashtbl.replace d "textContent" (String s)
| _ -> Hashtbl.replace d "textContent" (String ""))
| "textContent" ->
(* Setting textContent clears children *)
@@ -1887,6 +2073,8 @@ let run_spec_tests env test_files =
| "setTimeout" -> (match rest with fn :: _ -> ignore (Sx_ref.cek_call fn (List [])); Nil | _ -> Nil)
| "clearTimeout" -> Nil
| _ -> Nil)
+ | Dict d :: String "hasOwnProperty" :: [String k] ->
+ Bool (Hashtbl.mem d k)
| Dict d :: String m :: rest ->
let mt = match Hashtbl.find_opt d "__mock_type" with Some (String t) -> t | _ -> "" in
@@ -2139,6 +2327,33 @@ let run_spec_tests env test_files =
| _ -> Nil
in up (Dict d)
| _ -> Nil)
+ | "compareDocumentPosition" ->
+ (match rest with
+ | [other] ->
+ let self = Dict d in
+ let body = Dict mock_body in
+ let found_self = ref false in
+ let found_other = ref false in
+ let self_first = ref false in
+ let rec walk node =
+ if !found_self && !found_other then ()
+ else begin
+ if mock_el_eq node self then begin
+ if not !found_other then self_first := true;
+ found_self := true
+ end;
+ if mock_el_eq node other then found_other := true;
+ (match node with
+ | Dict dd -> let kids = match Hashtbl.find_opt dd "children" with Some (List l) -> l | _ -> [] in
+ List.iter walk kids
+ | _ -> ())
+ end
+ in
+ walk body;
+ if !found_self && !found_other then
+ Number (if !self_first then 4.0 else 2.0)
+ else Number 0.0
+ | _ -> Number 0.0)
| "matches" ->
(match rest with [String sel] -> Bool (mock_matches (Dict d) sel) | _ -> Bool false)
| "contains" ->
@@ -2213,25 +2428,62 @@ let run_spec_tests env test_files =
Hashtbl.replace r "right" (Number 100.0); Hashtbl.replace r "bottom" (Number 100.0);
Dict r
| "insertAdjacentHTML" ->
- (* Position-aware insertion, coerce value to string *)
+ (* Position-aware insertion. Parse the new HTML into a scratch
+ container, then splice the resulting children into the target
+ position WITHOUT disturbing sibling nodes. *)
(match rest with
- | [String pos; value] ->
+ | [String pos_kind; value] ->
let html = match dom_stringify value with String s -> s | _ -> "" in
- let cur = match Hashtbl.find_opt d "innerHTML" with Some (String s) -> s | _ -> "" in
- let new_html = match pos with
- | "afterbegin" -> html ^ cur (* prepend *)
- | _ -> cur ^ html (* beforeend / default: append *)
+ (* Parse new HTML into scratch container to get new child list.
+ For pure-text content, wrap into the target's innerHTML path. *)
+ let scratch = make_mock_element "div" in
+ let scratch_d = match scratch with Dict sd -> sd | _ -> Hashtbl.create 0 in
+ if String.contains html '<' then parse_html_into scratch_d html;
+ let new_kids = match Hashtbl.find_opt scratch_d "children" with Some (List l) -> l | _ -> [] in
+ let prepend = pos_kind = "beforebegin" || pos_kind = "afterbegin" in
+ let insert_into container_d index =
+ List.iter (fun c -> match c with
+ | Dict cd ->
+ Hashtbl.replace cd "parentElement" (Dict container_d);
+ Hashtbl.replace cd "parentNode" (Dict container_d)
+ | _ -> ()) new_kids;
+ let kids = match Hashtbl.find_opt container_d "children" with Some (List l) -> l | _ -> [] in
+ let before = List.filteri (fun i _ -> i < index) kids in
+ let after = List.filteri (fun i _ -> i >= index) kids in
+ let all = before @ new_kids @ after in
+ Hashtbl.replace container_d "children" (List all);
+ Hashtbl.replace container_d "childNodes" (List all);
+ (* Update container innerHTML based on position kind, not index *)
+ let cur = match Hashtbl.find_opt container_d "innerHTML" with Some (String s) -> s | _ -> "" in
+ let new_html = if prepend then html ^ cur else cur ^ html in
+ Hashtbl.replace container_d "innerHTML" (String new_html);
+ let buf = Buffer.create (String.length new_html) in
+ let in_tag = ref false in
+ String.iter (fun c ->
+ if c = '<' then in_tag := true
+ else if c = '>' then in_tag := false
+ else if not !in_tag then Buffer.add_char buf c
+ ) new_html;
+ Hashtbl.replace container_d "textContent" (String (Buffer.contents buf))
in
- Hashtbl.replace d "innerHTML" (String new_html);
- (* Sync textContent *)
- let buf = Buffer.create (String.length new_html) in
- let in_tag = ref false in
- String.iter (fun c ->
- if c = '<' then in_tag := true
- else if c = '>' then in_tag := false
- else if not !in_tag then Buffer.add_char buf c
- ) new_html;
- Hashtbl.replace d "textContent" (String (Buffer.contents buf));
+ (match pos_kind with
+ | "beforebegin" | "afterend" ->
+ (match Hashtbl.find_opt d "parentElement" with
+ | Some (Dict pd) ->
+ let siblings = match Hashtbl.find_opt pd "children" with Some (List l) -> l | _ -> [] in
+ let rec find_idx i = function
+ | [] -> List.length siblings
+ | x :: _ when mock_el_eq x (Dict d) -> i
+ | _ :: rest -> find_idx (i+1) rest
+ in
+ let self_idx = find_idx 0 siblings in
+ let insert_idx = if pos_kind = "beforebegin" then self_idx else self_idx + 1 in
+ insert_into pd insert_idx
+ | _ -> ())
+ | "afterbegin" -> insert_into d 0
+ | _ (* "beforeend" *) ->
+ let kids_len = match Hashtbl.find_opt d "children" with Some (List l) -> List.length l | _ -> 0 in
+ insert_into d kids_len);
Nil
| _ -> Nil)
| "showModal" | "show" ->
@@ -2341,6 +2593,8 @@ let run_spec_tests env test_files =
Hashtbl.replace ev "_stopped" (Bool false);
Hashtbl.replace ev "_stopImmediate" (Bool false);
Dict ev
+ | [String "Object"] ->
+ Dict (Hashtbl.create 4)
| _ -> Nil);
reg "host-callback" (fun args ->
@@ -2823,7 +3077,39 @@ let () =
let args = Array.to_list Sys.argv |> List.tl in
let foundation_only = List.mem "--foundation" args in
let jit_enabled = List.mem "--jit" args in
- let test_files = List.filter (fun a -> not (String.length a > 0 && a.[0] = '-')) args in
+ (* --only-failing=PATH : read lines of form "FAIL: suite > name: ..." and
+ restrict test runs to those (suite, name) pairs. *)
+ List.iter (fun a ->
+ let prefix = "--only-failing=" in
+ if String.length a > String.length prefix
+ && String.sub a 0 (String.length prefix) = prefix then begin
+ let path = String.sub a (String.length prefix) (String.length a - String.length prefix) in
+ let filter = Hashtbl.create 64 in
+ let ic = open_in path in
+ (try while true do
+ let line = input_line ic in
+ (* Match " FAIL: > : " or "FAIL: > : " *)
+ let line = String.trim line in
+ if String.length line > 6 && String.sub line 0 6 = "FAIL: " then begin
+ let rest = String.sub line 6 (String.length line - 6) in
+ match String.index_opt rest '>' with
+ | Some gt ->
+ let suite = String.trim (String.sub rest 0 gt) in
+ let after = String.sub rest (gt + 1) (String.length rest - gt - 1) in
+ (match String.index_opt after ':' with
+ | Some colon ->
+ let name = String.trim (String.sub after 0 colon) in
+ Hashtbl.replace filter (suite, name) ()
+ | None -> ())
+ | None -> ()
+ end
+ done with End_of_file -> ());
+ close_in ic;
+ Printf.eprintf "[filter] %d tests loaded from %s\n%!" (Hashtbl.length filter) path;
+ suite_filter := Some filter
+ end) args;
+ let test_files = List.filter (fun a ->
+ not (String.length a > 0 && a.[0] = '-')) args in
(* Always run foundation tests *)
run_foundation_tests ();
diff --git a/lib/hyperscript/compiler.sx b/lib/hyperscript/compiler.sx
index 21dbe0ed..fc3ad1e8 100644
--- a/lib/hyperscript/compiler.sx
+++ b/lib/hyperscript/compiler.sx
@@ -77,7 +77,11 @@
((= th (quote ref))
(list (quote set!) (make-symbol (nth target 1)) value))
((= th (quote local))
- (list (quote define) (make-symbol (nth target 1)) value))
+ (list
+ (quote hs-scoped-set!)
+ (quote me)
+ (nth target 1)
+ value))
((= th (quote dom-ref))
(list
(quote hs-dom-set!)
@@ -85,18 +89,18 @@
(nth target 1)
value))
((= th (quote me))
- (list (quote dom-set-inner-html) (quote me) value))
+ (list (quote hs-set-inner-html!) (quote me) value))
((= th (quote it)) (list (quote set!) (quote it) value))
((= th (quote query))
- (list (quote dom-set-inner-html) (hs-to-sx target) value))
+ (list (quote hs-set-inner-html!) (hs-to-sx target) value))
((= th (quote array-index))
(list
- (quote host-set!)
+ (quote hs-array-set!)
(hs-to-sx (nth target 1))
(hs-to-sx (nth target 2))
value))
((or (= th (quote next)) (= th (quote previous)) (= th (quote closest)))
- (list (quote dom-set-inner-html) (hs-to-sx target) value))
+ (list (quote hs-set-inner-html!) (hs-to-sx target) value))
((= th (quote of))
(let
((prop-ast (nth target 1)) (obj-ast (nth target 2)))
@@ -162,10 +166,19 @@
(let
((wrapped-body (if catch-info (let ((var (make-symbol (nth catch-info 0))) (catch-body (hs-to-sx (nth catch-info 1)))) (if finally-info (list (quote do) (list (quote guard) (list var (list true catch-body)) compiled-body) (hs-to-sx finally-info)) (list (quote guard) (list var (list true catch-body)) compiled-body))) (if finally-info (list (quote do) compiled-body (hs-to-sx finally-info)) compiled-body)))
(handler
- (list
- (quote fn)
- (list (quote event))
- wrapped-body)))
+ (let
+ ((uses-the-result? (fn (expr) (cond ((= expr (quote the-result)) true) ((list? expr) (some (fn (x) (uses-the-result? x)) expr)) (true false)))))
+ (list
+ (quote fn)
+ (list (quote event))
+ (if
+ (uses-the-result? wrapped-body)
+ (list
+ (quote let)
+ (list
+ (list (quote the-result) nil))
+ wrapped-body)
+ wrapped-body)))))
(if
every?
(list
@@ -443,9 +456,7 @@
(quote __hs-new)
(list
(quote +)
- (list
- (quote hs-to-number)
- (list (quote nth) var-sym (quote __hs-idx)))
+ (list (quote nth) var-sym (quote __hs-idx))
amount)))
(list
(quote do)
@@ -463,10 +474,7 @@
((t (hs-to-sx expr)))
(list
(quote let)
- (list
- (list
- (quote __hs-new)
- (list (quote +) (list (quote hs-to-number) t) amount)))
+ (list (list (quote __hs-new) (list (quote +) t amount)))
(list
(quote do)
(list (quote set!) t (quote __hs-new))
@@ -564,9 +572,7 @@
(quote __hs-new)
(list
(quote -)
- (list
- (quote hs-to-number)
- (list (quote nth) var-sym (quote __hs-idx)))
+ (list (quote nth) var-sym (quote __hs-idx))
amount)))
(list
(quote do)
@@ -584,10 +590,7 @@
((t (hs-to-sx expr)))
(list
(quote let)
- (list
- (list
- (quote __hs-new)
- (list (quote -) (list (quote hs-to-number) t) amount)))
+ (list (list (quote __hs-new) (list (quote -) t amount)))
(list
(quote do)
(list (quote set!) t (quote __hs-new))
@@ -754,35 +757,53 @@
(hs-to-sx (nth ast 3))))
((= head (quote pick-first))
(list
- (quote hs-pick-first)
- (hs-to-sx (nth ast 1))
- (hs-to-sx (nth ast 2))))
+ (quote set!)
+ (quote it)
+ (list
+ (quote hs-pick-first)
+ (hs-to-sx (nth ast 1))
+ (hs-to-sx (nth ast 2)))))
((= head (quote pick-last))
(list
- (quote hs-pick-last)
- (hs-to-sx (nth ast 1))
- (hs-to-sx (nth ast 2))))
+ (quote set!)
+ (quote it)
+ (list
+ (quote hs-pick-last)
+ (hs-to-sx (nth ast 1))
+ (hs-to-sx (nth ast 2)))))
((= head (quote pick-random))
(list
- (quote hs-pick-random)
- (hs-to-sx (nth ast 1))
- (if (nil? (nth ast 2)) nil (hs-to-sx (nth ast 2)))))
+ (quote set!)
+ (quote it)
+ (list
+ (quote hs-pick-random)
+ (hs-to-sx (nth ast 1))
+ (if (nil? (nth ast 2)) nil (hs-to-sx (nth ast 2))))))
((= head (quote pick-items))
(list
- (quote hs-pick-items)
- (hs-to-sx (nth ast 1))
- (hs-to-sx (nth ast 2))
- (hs-to-sx (nth ast 3))))
+ (quote set!)
+ (quote it)
+ (list
+ (quote hs-pick-items)
+ (hs-to-sx (nth ast 1))
+ (hs-to-sx (nth ast 2))
+ (hs-to-sx (nth ast 3)))))
((= head (quote pick-match))
(list
- (quote regex-match)
- (hs-to-sx (nth ast 1))
- (hs-to-sx (nth ast 2))))
+ (quote set!)
+ (quote it)
+ (list
+ (quote regex-match)
+ (hs-to-sx (nth ast 1))
+ (hs-to-sx (nth ast 2)))))
((= head (quote pick-matches))
(list
- (quote regex-find-all)
- (hs-to-sx (nth ast 1))
- (hs-to-sx (nth ast 2))))
+ (quote set!)
+ (quote it)
+ (list
+ (quote regex-find-all)
+ (hs-to-sx (nth ast 1))
+ (hs-to-sx (nth ast 2)))))
((= head (quote prop-is))
(list
(quote hs-prop-is)
@@ -870,6 +891,11 @@
((= head (quote ref)) (make-symbol (nth ast 1)))
((= head (quote query))
(list (quote hs-query-first) (nth ast 1)))
+ ((= head (quote query-scoped))
+ (list
+ (quote hs-query-all-in)
+ (nth ast 1)
+ (hs-to-sx (nth ast 2))))
((= head (quote attr))
(list
(quote dom-get-attr)
@@ -890,7 +916,8 @@
(quote dom-has-class?)
(hs-to-sx (nth ast 1))
(nth ast 2)))
- ((= head (quote local)) (make-symbol (nth ast 1)))
+ ((= head (quote local))
+ (list (quote hs-scoped-get) (quote me) (nth ast 1)))
((= head (quote array))
(cons (quote list) (map hs-to-sx (rest ast))))
((= head (quote not))
@@ -1163,6 +1190,14 @@
(quote set!)
(hs-to-sx tgt)
(list (quote hs-add-to!) val (hs-to-sx tgt)))))
+ ((= head (quote add-attr))
+ (let
+ ((tgt (nth ast 3)))
+ (list
+ (quote hs-set-attr!)
+ (hs-to-sx tgt)
+ (nth ast 1)
+ (hs-to-sx (nth ast 2)))))
((= head (quote remove-value))
(let
((val (hs-to-sx (nth ast 1))) (tgt (nth ast 2)))
@@ -1296,6 +1331,20 @@
(nth ast 1)
(hs-to-sx (nth ast 2))
(hs-to-sx (nth ast 3))))
+ ((= head (quote toggle-attr-val))
+ (list
+ (quote hs-toggle-attr-val!)
+ (hs-to-sx (nth ast 3))
+ (nth ast 1)
+ (hs-to-sx (nth ast 2))))
+ ((= head (quote toggle-attr-diff))
+ (list
+ (quote hs-toggle-attr-diff!)
+ (hs-to-sx (nth ast 5))
+ (nth ast 1)
+ (hs-to-sx (nth ast 2))
+ (nth ast 3)
+ (hs-to-sx (nth ast 4))))
((= head (quote set!))
(emit-set (nth ast 1) (hs-to-sx (nth ast 2))))
((= head (quote put!))
@@ -1358,14 +1407,49 @@
nil))
((= head (quote hide))
(let
- ((tgt (hs-to-sx (nth ast 1)))
- (strategy (if (> (len ast) 2) (nth ast 2) "display")))
- (list (quote hs-hide!) tgt strategy)))
+ ((tgt (let ((raw-tgt (nth ast 1))) (if (and (list? raw-tgt) (= (first raw-tgt) (quote query))) (list (quote hs-query-all) (nth raw-tgt 1)) (hs-to-sx raw-tgt))))
+ (strategy (if (> (len ast) 2) (nth ast 2) "display"))
+ (when-cond (if (> (len ast) 3) (nth ast 3) nil)))
+ (if
+ (nil? when-cond)
+ (list (quote hs-hide!) tgt strategy)
+ (list
+ (quote hs-hide-when!)
+ tgt
+ strategy
+ (list
+ (quote fn)
+ (list (quote it))
+ (hs-to-sx when-cond))))))
((= head (quote show))
(let
- ((tgt (hs-to-sx (nth ast 1)))
- (strategy (if (> (len ast) 2) (nth ast 2) "display")))
- (list (quote hs-show!) tgt strategy)))
+ ((tgt (let ((raw-tgt (nth ast 1))) (if (and (list? raw-tgt) (= (first raw-tgt) (quote query))) (list (quote hs-query-all) (nth raw-tgt 1)) (hs-to-sx raw-tgt))))
+ (strategy (if (> (len ast) 2) (nth ast 2) "display"))
+ (when-cond (if (> (len ast) 3) (nth ast 3) nil)))
+ (if
+ (nil? when-cond)
+ (list (quote hs-show!) tgt strategy)
+ (list
+ (quote let)
+ (list
+ (list
+ (quote __hs-show-r)
+ (list
+ (quote hs-show-when!)
+ tgt
+ strategy
+ (list
+ (quote fn)
+ (list (quote it))
+ (hs-to-sx when-cond)))))
+ (list
+ (quote begin)
+ (list
+ (quote set!)
+ (quote the-result)
+ (quote __hs-show-r))
+ (list (quote set!) (quote it) (quote __hs-show-r))
+ (quote __hs-show-r))))))
((= head (quote transition)) (emit-transition ast))
((= head (quote transition-from))
(let
@@ -1424,6 +1508,14 @@
(list (quote hs-settle) (quote me)))
((= head (quote go))
(list (quote hs-navigate!) (hs-to-sx (nth ast 1))))
+ ((= head (quote __get-cmd))
+ (let
+ ((val (hs-to-sx (nth ast 1))))
+ (list
+ (quote begin)
+ (list (quote set!) (quote the-result) val)
+ (list (quote set!) (quote it) val)
+ val)))
((= head (quote append!))
(let
((tgt (hs-to-sx (nth ast 2)))
@@ -1648,11 +1740,13 @@
(list (quote hs-reset!) (hs-to-sx (nth ast 1))))
((= head (quote default!))
(let
- ((t (hs-to-sx (nth ast 1))) (v (hs-to-sx (nth ast 2))))
+ ((tgt-ast (nth ast 1))
+ (read (hs-to-sx (nth ast 1)))
+ (v (hs-to-sx (nth ast 2))))
(list
(quote when)
- (list (quote nil?) t)
- (list (quote set!) t v))))
+ (list (quote hs-default?) read)
+ (emit-set tgt-ast v))))
((= head (quote hs-is))
(list
(quote hs-is)
diff --git a/lib/hyperscript/integration.sx b/lib/hyperscript/integration.sx
index db3835f0..147d7350 100644
--- a/lib/hyperscript/integration.sx
+++ b/lib/hyperscript/integration.sx
@@ -16,6 +16,14 @@
(fn
(sx)
(define vars (list))
+ (define
+ reserved
+ (list
+ (quote me)
+ (quote it)
+ (quote event)
+ (quote you)
+ (quote yourself)))
(define
walk
(fn
@@ -30,7 +38,9 @@
(let
((name (nth node 1)))
(when
- (not (some (fn (v) (= v name)) vars))
+ (and
+ (not (some (fn (v) (= v name)) vars))
+ (not (some (fn (v) (= v name)) reserved)))
(set! vars (cons name vars)))))
(for-each walk node))))
(walk sx)
@@ -67,9 +77,10 @@
(fn
(el)
(let
- ((src (dom-get-attr el "_")))
+ ((src (dom-get-attr el "_")) (prev (dom-get-data el "hs-script")))
(when
- (and src (not (dom-get-data el "hs-active")))
+ (and src (not (= src prev)))
+ (dom-set-data el "hs-script" src)
(dom-set-data el "hs-active" true)
(let ((handler (hs-handler src))) (handler el))))))
@@ -77,6 +88,21 @@
;; Called once at page load. Finds all elements with _ attribute,
;; compiles their hyperscript, and activates them.
+(define
+ hs-deactivate!
+ (fn
+ (el)
+ (let
+ ((unlisteners (or (dom-get-data el "hs-unlisteners") (list))))
+ (for-each (fn (u) (when u (u))) unlisteners)
+ (dom-set-data el "hs-unlisteners" (list))
+ (dom-set-data el "hs-active" false)
+ (dom-set-data el "hs-script" nil))))
+
+;; ── Boot subtree: for dynamic content ───────────────────────────
+;; Called after HTMX swaps or dynamic DOM insertion.
+;; Only activates elements within the given root.
+
(define
hs-boot!
(fn
@@ -85,10 +111,6 @@
((elements (dom-query-all (host-get (host-global "document") "body") "[_]")))
(for-each (fn (el) (hs-activate! el)) elements))))
-;; ── Boot subtree: for dynamic content ───────────────────────────
-;; Called after HTMX swaps or dynamic DOM insertion.
-;; Only activates elements within the given root.
-
(define
hs-boot-subtree!
(fn
diff --git a/lib/hyperscript/parser.sx b/lib/hyperscript/parser.sx
index 0bdb84bb..3739084b 100644
--- a/lib/hyperscript/parser.sx
+++ b/lib/hyperscript/parser.sx
@@ -95,6 +95,13 @@
(do (adv!) (list kind (str "." val) (list (quote me)))))
((= typ "id")
(do (adv!) (list kind (str "#" val) (list (quote me)))))
+ ((= typ "attr")
+ (do
+ (adv!)
+ (list
+ (quote attr)
+ val
+ (list kind (str "[" val "]") (list (quote me))))))
(true (list kind "*" (list (quote me))))))))
(define
parse-pos-kw
@@ -146,8 +153,10 @@
(do (adv!) (list (quote me))))
((and (= typ "keyword") (= val "I"))
(do (adv!) (list (quote me))))
- ((and (= typ "keyword") (or (= val "it") (= val "result")))
+ ((and (= typ "keyword") (= val "it"))
(do (adv!) (list (quote it))))
+ ((and (= typ "keyword") (= val "result"))
+ (do (adv!) (quote the-result)))
((and (= typ "keyword") (= val "event"))
(do (adv!) (list (quote event))))
((and (= typ "keyword") (= val "target"))
@@ -174,7 +183,18 @@
(do (adv!) (parse-pos-kw (quote last))))
((= typ "id")
(do (adv!) (list (quote query) (str "#" val))))
- ((= typ "selector") (do (adv!) (list (quote query) val)))
+ ((= typ "selector")
+ (do
+ (adv!)
+ (if
+ (and (= (tp-type) "keyword") (= (tp-val) "in"))
+ (do
+ (adv!)
+ (list
+ (quote query-scoped)
+ val
+ (parse-cmp (parse-arith (parse-poss (parse-atom))))))
+ (list (quote query) val))))
((= typ "attr")
(do (adv!) (list (quote attr) val (list (quote me)))))
((= typ "style")
@@ -426,7 +446,7 @@
(list (quote type-check) left type-name)))))))
(true
(let
- ((right (parse-expr)))
+ ((right (parse-cmp (parse-arith (parse-poss (parse-atom))))))
(if
(match-kw "ignoring")
(do
@@ -530,6 +550,14 @@
(quote and)
(list (quote >=) left lo)
(list (quote <=) left hi))))))
+ ((or (and (or (= (tp-val) "a") (= (tp-val) "an")) (do (adv!) true)))
+ (let
+ ((type-name (tp-val)))
+ (do
+ (adv!)
+ (list
+ (quote not)
+ (list (quote type-check) left type-name)))))
(true
(let
((right (parse-expr)))
@@ -546,6 +574,10 @@
(quote and)
(list (quote >=) left lo)
(list (quote <=) left hi)))))
+ ((or (and (or (= (tp-val) "a") (= (tp-val) "an")) (do (adv!) true)))
+ (let
+ ((type-name (tp-val)))
+ (do (adv!) (list (quote type-check) left type-name))))
(true
(let
((right (parse-expr)))
@@ -576,7 +608,7 @@
(match-kw "case")
(list (quote ends-with-ic?) left rhs))
(list (quote ends-with?) left rhs)))))
- ((and (= typ "keyword") (= val "matches"))
+ ((and (= typ "keyword") (or (= val "matches") (= val "match")))
(do
(adv!)
(let
@@ -618,7 +650,22 @@
(quote as)
left
(str type-name ":" param)))))
- (list (quote as) left type-name))))))
+ (let
+ loop
+ ((result (list (quote as) left type-name)))
+ (if
+ (and (= (tp-type) "op") (= (tp-val) "|"))
+ (do
+ (adv!)
+ (when
+ (or (= (tp-val) "a") (= (tp-val) "an"))
+ (adv!))
+ (let
+ ((next-type (tp-val)))
+ (do
+ (adv!)
+ (loop (list (quote as) result next-type)))))
+ result)))))))
((and (= typ "colon"))
(do
(adv!)
@@ -693,7 +740,7 @@
(list (quote strict-eq) left (parse-expr))))
((and (= typ "keyword") (or (= val "contain") (= val "include") (= val "includes")))
(do (adv!) (list (quote contains?) left (parse-expr))))
- ((and (= typ "keyword") (= val "precedes"))
+ ((and (= typ "keyword") (or (= val "precedes") (= val "precede")))
(do (adv!) (list (quote precedes?) left (parse-atom))))
((and (= typ "keyword") (= val "follows"))
(do (adv!) (list (quote follows?) left (parse-atom))))
@@ -772,7 +819,7 @@
(= (tp-val) "starts")
(= (tp-val) "ends")
(= (tp-val) "contains")
- (= (tp-val) "matches")
+ (or (= (tp-val) "matches") (= (tp-val) "match"))
(= (tp-val) "is")
(= (tp-val) "does")
(= (tp-val) "in")
@@ -892,6 +939,18 @@
(let
((tgt (if (match-kw "to") (parse-expr) (list (quote me)))))
(list (quote set-styles) (reverse pairs) tgt)))))
+ ((and (= (tp-type) "bracket-open") (> (len tokens) (+ p 1)) (= (get (nth tokens (+ p 1)) "type") "attr"))
+ (do
+ (adv!)
+ (let
+ ((attr-name (get (adv!) "value")))
+ (when (and (= (tp-type) "op") (= (tp-val) "=")) (adv!))
+ (let
+ ((attr-val (parse-expr)))
+ (when (= (tp-type) "bracket-close") (adv!))
+ (let
+ ((tgt (parse-tgt-kw "to" (list (quote me)))))
+ (list (quote add-attr) attr-name attr-val tgt))))))
(true
(let
((value (parse-expr)))
@@ -978,20 +1037,58 @@
()
(cond
((match-kw "between")
- (if
- (= (tp-type) "class")
- (let
- ((cls1 (do (let ((v (tp-val))) (adv!) v))))
- (expect-kw! "and")
- (if
- (= (tp-type) "class")
- (let
- ((cls2 (do (let ((v (tp-val))) (adv!) v))))
+ (cond
+ ((= (tp-type) "class")
+ (let
+ ((cls1 (do (let ((v (tp-val))) (adv!) v))))
+ (expect-kw! "and")
+ (if
+ (= (tp-type) "class")
(let
- ((tgt (parse-tgt-kw "on" (list (quote me)))))
- (list (quote toggle-between) cls1 cls2 tgt)))
- nil))
- nil))
+ ((cls2 (do (let ((v (tp-val))) (adv!) v))))
+ (let
+ ((tgt (parse-tgt-kw "on" (list (quote me)))))
+ (list (quote toggle-between) cls1 cls2 tgt)))
+ nil)))
+ ((and (= (tp-type) "bracket-open") (> (len tokens) (+ p 1)) (= (get (nth tokens (+ p 1)) "type") "attr"))
+ (do
+ (adv!)
+ (let
+ ((n1 (get (adv!) "value")))
+ (when
+ (and (= (tp-type) "op") (= (tp-val) "="))
+ (adv!))
+ (let
+ ((v1 (parse-expr)))
+ (when (= (tp-type) "bracket-close") (adv!))
+ (expect-kw! "and")
+ (when (= (tp-type) "bracket-open") (adv!))
+ (let
+ ((n2 (get (adv!) "value")))
+ (when
+ (and (= (tp-type) "op") (= (tp-val) "="))
+ (adv!))
+ (let
+ ((v2 (parse-expr)))
+ (when (= (tp-type) "bracket-close") (adv!))
+ (let
+ ((tgt (parse-tgt-kw "on" (list (quote me)))))
+ (if
+ (= n1 n2)
+ (list
+ (quote toggle-attr-between)
+ n1
+ v1
+ v2
+ tgt)
+ (list
+ (quote toggle-attr-diff)
+ n1
+ v1
+ n2
+ v2
+ tgt)))))))))
+ (true nil)))
((= (tp-type) "class")
(let
((cls (do (let ((v (tp-val))) (adv!) v))))
@@ -1012,38 +1109,67 @@
(match-kw "between")
(let
((val1 (parse-atom)))
- (expect-kw! "and")
- (let
- ((val2 (parse-atom)))
+ (do
+ (when (= (tp-type) "comma") (adv!))
(if
- (match-kw "and")
- (let
- ((val3 (parse-atom)))
- (if
- (match-kw "and")
+ (and (= (tp-type) "keyword") (= (tp-val) "and"))
+ (adv!)
+ nil)
+ (let
+ ((val2 (parse-atom)))
+ (if
+ (or
+ (= (tp-type) "comma")
+ (and
+ (= (tp-type) "keyword")
+ (= (tp-val) "and")))
+ (do
+ (when (= (tp-type) "comma") (adv!))
+ (if
+ (and
+ (= (tp-type) "keyword")
+ (= (tp-val) "and"))
+ (adv!)
+ nil)
(let
- ((val4 (parse-atom)))
- (list
- (quote toggle-style-cycle)
- prop
- tgt
- val1
- val2
- val3
- val4))
- (list
- (quote toggle-style-cycle)
- prop
- tgt
- val1
- val2
- val3)))
- (list
- (quote toggle-style-between)
- prop
- val1
- val2
- tgt))))
+ ((val3 (parse-atom)))
+ (if
+ (or
+ (= (tp-type) "comma")
+ (and
+ (= (tp-type) "keyword")
+ (= (tp-val) "and")))
+ (do
+ (when (= (tp-type) "comma") (adv!))
+ (if
+ (and
+ (= (tp-type) "keyword")
+ (= (tp-val) "and"))
+ (adv!)
+ nil)
+ (let
+ ((val4 (parse-atom)))
+ (list
+ (quote toggle-style-cycle)
+ prop
+ tgt
+ val1
+ val2
+ val3
+ val4)))
+ (list
+ (quote toggle-style-cycle)
+ prop
+ tgt
+ val1
+ val2
+ val3))))
+ (list
+ (quote toggle-style-between)
+ prop
+ val1
+ val2
+ tgt)))))
(list (quote toggle-style) prop tgt)))))
((= (tp-type) "attr")
(let
@@ -1064,6 +1190,18 @@
val2
tgt)))
(list (quote toggle-attr) attr-name tgt)))))
+ ((and (= (tp-type) "bracket-open") (> (len tokens) (+ p 1)) (= (get (nth tokens (+ p 1)) "type") "attr"))
+ (do
+ (adv!)
+ (let
+ ((attr-name (get (adv!) "value")))
+ (when (and (= (tp-type) "op") (= (tp-val) "=")) (adv!))
+ (let
+ ((attr-val (parse-expr)))
+ (when (= (tp-type) "bracket-close") (adv!))
+ (let
+ ((tgt (parse-tgt-kw "on" (list (quote me)))))
+ (list (quote toggle-attr-val) attr-name attr-val tgt))))))
((and (= (tp-type) "keyword") (= (tp-val) "my"))
(do
(adv!)
@@ -1338,19 +1476,23 @@
(fn
()
(let
- ((tgt (cond ((at-end?) (list (quote me))) ((and (= (tp-type) "keyword") (or (= (tp-val) "then") (= (tp-val) "end") (= (tp-val) "with") (= (tp-val) "add") (= (tp-val) "remove") (= (tp-val) "set") (= (tp-val) "put") (= (tp-val) "toggle") (= (tp-val) "hide") (= (tp-val) "show"))) (list (quote me))) (true (parse-expr)))))
+ ((tgt (cond ((at-end?) (list (quote me))) ((and (= (tp-type) "keyword") (or (= (tp-val) "then") (= (tp-val) "end") (= (tp-val) "with") (= (tp-val) "when") (= (tp-val) "add") (= (tp-val) "remove") (= (tp-val) "set") (= (tp-val) "put") (= (tp-val) "toggle") (= (tp-val) "hide") (= (tp-val) "show"))) (list (quote me))) (true (parse-expr)))))
(let
- ((strategy (if (match-kw "with") (if (at-end?) "display" (let ((s (tp-val))) (adv!) s)) "display")))
- (list (quote hide) tgt strategy)))))
+ ((strategy (if (match-kw "with") (if (at-end?) "display" (let ((s (tp-val))) (do (adv!) (cond ((at-end?) s) ((= (tp-type) "colon") (do (adv!) (let ((v (tp-val))) (do (adv!) (str s ":" v))))) ((= (tp-type) "local") (let ((v (tp-val))) (do (adv!) (str s ":" v)))) (true s))))) "display")))
+ (let
+ ((when-cond (if (and (= (tp-type) "keyword") (= (tp-val) "when")) (do (adv!) (parse-expr)) nil)))
+ (list (quote hide) tgt strategy when-cond))))))
(define
parse-show-cmd
(fn
()
(let
- ((tgt (cond ((at-end?) (list (quote me))) ((and (= (tp-type) "keyword") (or (= (tp-val) "then") (= (tp-val) "end") (= (tp-val) "with") (= (tp-val) "add") (= (tp-val) "remove") (= (tp-val) "set") (= (tp-val) "put") (= (tp-val) "toggle") (= (tp-val) "hide") (= (tp-val) "show"))) (list (quote me))) (true (parse-expr)))))
+ ((tgt (cond ((at-end?) (list (quote me))) ((and (= (tp-type) "keyword") (or (= (tp-val) "then") (= (tp-val) "end") (= (tp-val) "with") (= (tp-val) "when") (= (tp-val) "add") (= (tp-val) "remove") (= (tp-val) "set") (= (tp-val) "put") (= (tp-val) "toggle") (= (tp-val) "hide") (= (tp-val) "show"))) (list (quote me))) (true (parse-expr)))))
(let
- ((strategy (if (match-kw "with") (if (at-end?) "display" (let ((s (tp-val))) (adv!) s)) "display")))
- (list (quote show) tgt strategy)))))
+ ((strategy (if (match-kw "with") (if (at-end?) "display" (let ((s (tp-val))) (do (adv!) (cond ((at-end?) s) ((= (tp-type) "colon") (do (adv!) (let ((v (tp-val))) (do (adv!) (str s ":" v))))) ((= (tp-type) "local") (let ((v (tp-val))) (do (adv!) (str s ":" v)))) (true s))))) "display")))
+ (let
+ ((when-cond (if (and (= (tp-type) "keyword") (= (tp-val) "when")) (do (adv!) (parse-expr)) nil)))
+ (list (quote show) tgt strategy when-cond))))))
(define
parse-transition-cmd
(fn
@@ -1493,7 +1635,7 @@
(ca-collect (append acc (list arg)))))))
(ca-collect (list))))
(define parse-call-cmd (fn () (parse-expr)))
- (define parse-get-cmd (fn () (parse-expr)))
+ (define parse-get-cmd (fn () (list (quote __get-cmd) (parse-expr))))
(define
parse-take-cmd
(fn
@@ -1501,12 +1643,34 @@
(cond
((= (tp-type) "class")
(let
- ((cls (do (let ((v (tp-val))) (adv!) v))))
+ ((classes (list)))
(let
- ((from-sel (if (match-kw "from") (parse-expr) nil)))
+ ((collect (fn () (when (= (tp-type) "class") (let ((v (tp-val))) (adv!) (set! classes (append classes (list v))) (collect))))))
+ (collect)
(let
- ((for-tgt (if (match-kw "for") (parse-expr) nil)))
- (list (quote take!) "class" cls from-sel for-tgt)))))
+ ((from-sel (if (match-kw "from") (parse-expr) nil)))
+ (let
+ ((for-tgt (if (match-kw "for") (parse-expr) nil)))
+ (if
+ (= (len classes) 1)
+ (list
+ (quote take!)
+ "class"
+ (first classes)
+ from-sel
+ for-tgt)
+ (cons
+ (quote do)
+ (map
+ (fn
+ (cls)
+ (list
+ (quote take!)
+ "class"
+ cls
+ from-sel
+ for-tgt))
+ classes))))))))
((= (tp-type) "attr")
(let
((attr-name (get (adv!) "value")))
@@ -1540,7 +1704,9 @@
(let
((n (parse-atom)))
(do
- (expect-kw! "of")
+ (if
+ (not (or (match-kw "of") (match-kw "from")))
+ (error (str "Expected 'of' or 'from' at position " p)))
(let
((coll (parse-expr)))
(list (quote pick-first) coll n))))))
@@ -1550,7 +1716,9 @@
(let
((n (parse-atom)))
(do
- (expect-kw! "of")
+ (if
+ (not (or (match-kw "of") (match-kw "from")))
+ (error (str "Expected 'of' or 'from' at position " p)))
(let
((coll (parse-expr)))
(list (quote pick-last) coll n))))))
@@ -1558,14 +1726,17 @@
(do
(adv!)
(if
- (match-kw "of")
+ (or (match-kw "of") (match-kw "from"))
(let
((coll (parse-expr)))
(list (quote pick-random) coll nil))
(let
((n (parse-atom)))
(do
- (expect-kw! "of")
+ (if
+ (not (or (match-kw "of") (match-kw "from")))
+ (error
+ (str "Expected 'of' or 'from' at position " p)))
(let
((coll (parse-expr)))
(list (quote pick-random) coll n)))))))
@@ -1579,7 +1750,10 @@
(let
((end-expr (parse-atom)))
(do
- (expect-kw! "of")
+ (if
+ (not (or (match-kw "of") (match-kw "from")))
+ (error
+ (str "Expected 'of' or 'from' at position " p)))
(let
((coll (parse-expr)))
(list (quote pick-items) coll start-expr end-expr))))))))
@@ -1588,7 +1762,7 @@
(adv!)
(expect-kw! "of")
(let
- ((regex (parse-expr)))
+ ((regex (parse-atom)))
(do
(cond
((match-kw "of") nil)
@@ -1606,7 +1780,7 @@
(adv!)
(expect-kw! "of")
(let
- ((regex (parse-expr)))
+ ((regex (parse-atom)))
(do
(cond
((match-kw "of") nil)
@@ -1619,10 +1793,26 @@
(let
((haystack (parse-expr)))
(list (quote pick-matches) regex haystack))))))
+ ((and (= typ "ident") (= val "item"))
+ (do
+ (adv!)
+ (let
+ ((n (parse-expr)))
+ (do
+ (if
+ (not (or (match-kw "of") (match-kw "from")))
+ (error (str "Expected 'of' or 'from' at position " p)))
+ (let
+ ((coll (parse-expr)))
+ (list
+ (quote pick-items)
+ coll
+ n
+ (list (quote +) n 1)))))))
(true
(error
(str
- "Expected first/last/random/items/match/matches after 'pick' at "
+ "Expected first/last/random/item/items/match/matches after 'pick' at "
p)))))))
(define
parse-go-cmd
@@ -1697,7 +1887,7 @@
(match-kw "of")
(list (make-symbol ".") (parse-expr) val)
(cond
- ((= val "result") (list (quote it)))
+ ((= val "result") (quote the-result))
((= val "first") (parse-pos-kw (quote first)))
((= val "last") (parse-pos-kw (quote last)))
((= val "closest") (parse-trav (quote closest)))
diff --git a/lib/hyperscript/runtime.sx b/lib/hyperscript/runtime.sx
index 35586471..ce30f325 100644
--- a/lib/hyperscript/runtime.sx
+++ b/lib/hyperscript/runtime.sx
@@ -22,7 +22,13 @@
;; Stock hyperscript queues by default; "every" disables queuing.
(define
hs-on
- (fn (target event-name handler) (dom-listen target event-name handler)))
+ (fn
+ (target event-name handler)
+ (let
+ ((unlisten (dom-listen target event-name handler))
+ (prev (or (dom-get-data target "hs-unlisteners") (list))))
+ (dom-set-data target "hs-unlisteners" (append prev (list unlisten)))
+ unlisten)))
;; Run an initializer function immediately.
;; (hs-init thunk) — called at element boot time
@@ -88,7 +94,7 @@
((or (= prop "display") (= prop "opacity"))
(if
(or (= cur "none") (= cur "0"))
- (dom-set-style target prop (if (= prop "opacity") "1" ""))
+ (dom-set-style target prop (if (= prop "opacity") "1" "block"))
(dom-set-style target prop (if (= prop "display") "none" "0"))))
(true
(if
@@ -167,6 +173,45 @@
(fn
(el name val)
(if (nil? val) (dom-remove-attr el name) (dom-set-attr el name val))))
+ (define
+ hs-toggle-attr!
+ (fn
+ (el name)
+ (if
+ (dom-has-attr? el name)
+ (dom-remove-attr el name)
+ (dom-set-attr el name ""))))
+ (define
+ hs-toggle-attr-val!
+ (fn
+ (el name val)
+ (if
+ (= (dom-get-attr el name) val)
+ (dom-remove-attr el name)
+ (dom-set-attr el name val))))
+ (define
+ hs-toggle-attr-between!
+ (fn
+ (el name val1 val2)
+ (if
+ (= (dom-get-attr el name) val1)
+ (dom-set-attr el name val2)
+ (dom-set-attr el name val1))))
+ (define
+ hs-toggle-attr-diff!
+ (fn
+ (el n1 v1 n2 v2)
+ (if
+ (dom-has-attr? el n1)
+ (do (dom-remove-attr el n1) (dom-set-attr el n2 v2))
+ (do
+ (when (dom-has-attr? el n2) (dom-remove-attr el n2))
+ (dom-set-attr el n1 v1)))))
+ (define
+ hs-set-inner-html!
+ (fn
+ (target value)
+ (do (dom-set-inner-html target value) (hs-boot-subtree! target))))
(define
hs-put!
(fn
@@ -407,19 +452,24 @@
hs-query-all
(fn (sel) (host-call (dom-body) "querySelectorAll" sel)))
+(define
+ hs-query-all-in
+ (fn
+ (sel target)
+ (if
+ (nil? target)
+ (hs-query-all sel)
+ (host-call target "querySelectorAll" sel))))
+
(define
hs-list-set
- (fn (lst idx val) (map-indexed (fn (i x) (if (= i idx) val x)) lst)))
+ (fn
+ (lst idx val)
+ (append (take lst idx) (cons val (drop lst (+ idx 1))))))
(define
hs-to-number
- (fn
- (v)
- (cond
- ((number? v) v)
- ((string? v) (or (parse-number v) 0))
- ((nil? v) 0)
- (true (or (parse-number (str v)) 0)))))
+ (fn (v) (if (number? v) v (or (parse-number (str v)) 0))))
(define
hs-query-first
@@ -490,6 +540,10 @@
((= signal "hs-continue") (hs-repeat-while cond-fn thunk))
(true (hs-repeat-while cond-fn thunk)))))))
+
+
+
+
(define
hs-repeat-until
(fn
@@ -502,10 +556,6 @@
(if (cond-fn) nil (hs-repeat-until cond-fn thunk)))
(true (if (cond-fn) nil (hs-repeat-until cond-fn thunk)))))))
-
-
-
-
(define
hs-for-each
(fn
@@ -525,27 +575,38 @@
((= signal "hs-continue") (do-loop (rest remaining)))
(true (do-loop (rest remaining))))))))
(do-loop items))))
-
+;; ── Sandbox/test runtime additions ──────────────────────────────
+;; Property access — dot notation and .length
(begin
(define
hs-append
(fn
(target value)
(cond
+ ((nil? target) value)
((string? target) (str target value))
((list? target) (append target (list value)))
+ ((hs-element? target)
+ (do
+ (dom-insert-adjacent-html target "beforeend" (str value))
+ target))
(true (str target value)))))
(define
hs-append!
- (fn (value target) (dom-insert-adjacent-html target "beforeend" value))))
-;; ── Sandbox/test runtime additions ──────────────────────────────
-;; Property access — dot notation and .length
+ (fn
+ (value target)
+ (cond
+ ((nil? target) nil)
+ ((hs-element? target)
+ (dom-insert-adjacent-html target "beforeend" (str value)))
+ (true nil)))))
+;; DOM query stub — sandbox returns empty list
(define
hs-fetch
(fn
(url format)
(perform (list "io-fetch" url (if format format "text")))))
-;; DOM query stub — sandbox returns empty list
+;; Method dispatch — obj.method(args)
(define
hs-coerce
(fn
@@ -636,7 +697,24 @@
(map (fn (k) (list k (get value k))) (keys value))
value))
(true value))))
-;; Method dispatch — obj.method(args)
+
+;; ── 0.9.90 features ─────────────────────────────────────────────
+;; beep! — debug logging, returns value unchanged
+(define
+ hs-default?
+ (fn
+ (v)
+ (cond
+ ((nil? v) true)
+ ((and (string? v) (= v "")) true)
+ (true false))))
+;; Property-based is — check obj.key truthiness
+(define
+ hs-array-set!
+ (fn
+ (arr i v)
+ (if (list? arr) (do (set-nth! arr i v) v) (host-set! arr i v))))
+;; Array slicing (inclusive both ends)
(define
hs-add
(fn
@@ -646,9 +724,7 @@
((list? b) (cons a b))
((or (string? a) (string? b)) (str a b))
(true (+ a b)))))
-
-;; ── 0.9.90 features ─────────────────────────────────────────────
-;; beep! — debug logging, returns value unchanged
+;; Collection: sorted by
(define
hs-make
(fn
@@ -659,13 +735,13 @@
((= type-name "Set") (list))
((= type-name "Map") (dict))
(true (dict)))))
-;; Property-based is — check obj.key truthiness
+;; Collection: sorted by descending
(define hs-install (fn (behavior-fn) (behavior-fn me)))
-;; Array slicing (inclusive both ends)
+;; Collection: split by
(define
hs-measure
(fn (target) (perform (list (quote io-measure) target))))
-;; Collection: sorted by
+;; Collection: joined by
(define
hs-transition
(fn
@@ -678,7 +754,7 @@
(str prop " " (/ duration 1000) "s")))
(dom-set-style target prop value)
(when duration (hs-settle target))))
-;; Collection: sorted by descending
+
(define
hs-transition-from
(fn
@@ -692,7 +768,7 @@
(str prop " " (/ duration 1000) "s")))
(dom-set-style target prop (str to-val))
(when duration (hs-settle target))))
-;; Collection: split by
+
(define
hs-type-check
(fn
@@ -712,7 +788,7 @@
(= (host-typeof value) "element")
(= (host-typeof value) "text")))
(true (= (host-typeof value) (downcase type-name)))))))
-;; Collection: joined by
+
(define
hs-type-check-strict
(fn
@@ -745,11 +821,26 @@
((nil? suffix) false)
(true (ends-with? (str s) (str suffix))))))
+(define
+ hs-scoped-set!
+ (fn (el name val) (dom-set-data el (str "hs-local-" name) val)))
+
+(define
+ hs-scoped-get
+ (fn (el name) (dom-get-data el (str "hs-local-" name))))
+
(define
hs-precedes?
(fn
(a b)
- (cond ((nil? a) false) ((nil? b) false) (true (< (str a) (str b))))))
+ (cond
+ ((nil? a) false)
+ ((nil? b) false)
+ ((and (dict? a) (dict? b))
+ (let
+ ((pos (host-call a "compareDocumentPosition" b)))
+ (if (number? pos) (not (= 0 (mod (/ pos 4) 2))) false)))
+ (true (< (str a) (str b))))))
(define
hs-follows?
@@ -840,7 +931,18 @@
(= obj (nth r 1))
(= obj nil)))))))
-(define precedes? (fn (a b) (< (str a) (str b))))
+(define
+ precedes?
+ (fn
+ (a b)
+ (cond
+ ((nil? a) false)
+ ((nil? b) false)
+ ((and (dict? a) (dict? b))
+ (let
+ ((pos (host-call a "compareDocumentPosition" b)))
+ (if (number? pos) (not (= 0 (mod (/ pos 4) 2))) false)))
+ (true (< (str a) (str b))))))
(define
hs-empty?
@@ -1124,33 +1226,109 @@
(host-call el "removeAttribute" "open")
(dom-set-prop el "open" false)))))))
-(define
- hs-hide!
- (fn
- (el strategy)
- (let
- ((tag (dom-get-prop el "tagName")))
- (cond
- ((= tag "DIALOG")
- (when (dom-has-attr? el "open") (host-call el "close")))
- ((= tag "DETAILS") (dom-set-prop el "open" false))
- ((= strategy "opacity") (dom-set-style el "opacity" "0"))
- ((= strategy "visibility") (dom-set-style el "visibility" "hidden"))
- (true (dom-set-style el "display" "none"))))))
+(begin
+ (define
+ hs-hide-one!
+ (fn
+ (el strategy)
+ (let
+ ((parts (split strategy ":")) (tag (dom-get-prop el "tagName")))
+ (let
+ ((prop (first parts))
+ (val (if (> (len parts) 1) (nth parts 1) nil)))
+ (cond
+ ((= tag "DIALOG")
+ (when (dom-has-attr? el "open") (host-call el "close")))
+ ((= tag "DETAILS") (dom-set-prop el "open" false))
+ ((= prop "opacity")
+ (dom-set-style el "opacity" (if val val "0")))
+ ((= prop "visibility")
+ (dom-set-style el "visibility" (if val val "hidden")))
+ ((= prop "hidden") (dom-set-attr el "hidden" ""))
+ ((= prop "twDisplay") (dom-add-class el "hidden"))
+ ((= prop "twVisibility") (dom-add-class el "invisible"))
+ ((= prop "twOpacity") (dom-add-class el "opacity-0"))
+ (true (dom-set-style el "display" (if val val "none"))))))))
+ (define
+ hs-hide!
+ (fn
+ (target strategy)
+ (if
+ (list? target)
+ (do (for-each (fn (el) (hs-hide-one! el strategy)) target) target)
+ (do (hs-hide-one! target strategy) target)))))
+
+(begin
+ (define
+ hs-show-one!
+ (fn
+ (el strategy)
+ (let
+ ((parts (split strategy ":")) (tag (dom-get-prop el "tagName")))
+ (let
+ ((prop (first parts))
+ (val (if (> (len parts) 1) (nth parts 1) nil)))
+ (cond
+ ((= tag "DIALOG")
+ (when
+ (not (dom-has-attr? el "open"))
+ (host-call el "showModal")))
+ ((= tag "DETAILS") (dom-set-prop el "open" true))
+ ((= prop "opacity")
+ (dom-set-style el "opacity" (if val val "1")))
+ ((= prop "visibility")
+ (dom-set-style el "visibility" (if val val "visible")))
+ ((= prop "hidden") (dom-remove-attr el "hidden"))
+ ((= prop "twDisplay") (dom-remove-class el "hidden"))
+ ((= prop "twVisibility") (dom-remove-class el "invisible"))
+ ((= prop "twOpacity") (dom-remove-class el "opacity-0"))
+ (true (dom-set-style el "display" (if val val "block"))))))))
+ (define
+ hs-show!
+ (fn
+ (target strategy)
+ (if
+ (list? target)
+ (do (for-each (fn (el) (hs-show-one! el strategy)) target) target)
+ (do (hs-show-one! target strategy) target)))))
(define
- hs-show!
+ hs-show-when!
(fn
- (el strategy)
+ (target strategy pred)
(let
- ((tag (dom-get-prop el "tagName")))
- (cond
- ((= tag "DIALOG")
- (when (not (dom-has-attr? el "open")) (host-call el "showModal")))
- ((= tag "DETAILS") (dom-set-prop el "open" true))
- ((= strategy "opacity") (dom-set-style el "opacity" "1"))
- ((= strategy "visibility") (dom-set-style el "visibility" "visible"))
- (true (dom-set-style el "display" ""))))))
+ ((items (if (list? target) target (list target))))
+ (let
+ ((matched (list)))
+ (do
+ (for-each
+ (fn
+ (el)
+ (if
+ (pred el)
+ (do (hs-show-one! el strategy) (append! matched el))
+ (hs-hide-one! el strategy)))
+ items)
+ matched)))))
+
+(define
+ hs-hide-when!
+ (fn
+ (target strategy pred)
+ (let
+ ((items (if (list? target) target (list target))))
+ (let
+ ((matched (list)))
+ (do
+ (for-each
+ (fn
+ (el)
+ (if
+ (pred el)
+ (do (hs-hide-one! el strategy) (append! matched el))
+ (hs-show-one! el strategy)))
+ items)
+ matched)))))
(define hs-first (fn (lst) (first lst)))
@@ -1390,7 +1568,7 @@
false
(let
((store (host-get el "__hs_vars")))
- (if (nil? store) false (has-key? store name))))))
+ (if (nil? store) false (host-call store "hasOwnProperty" name))))))
(define
hs-dom-get-var-raw
@@ -1409,7 +1587,7 @@
(do
(when
(nil? (host-get el "__hs_vars"))
- (host-set! el "__hs_vars" (dict)))
+ (host-set! el "__hs_vars" (host-new "Object")))
(host-set! (host-get el "__hs_vars") name val)
(when changed (hs-dom-fire-watchers! el name val))))))
diff --git a/lib/hyperscript/tokenizer.sx b/lib/hyperscript/tokenizer.sx
index f846e34d..9440f95a 100644
--- a/lib/hyperscript/tokenizer.sx
+++ b/lib/hyperscript/tokenizer.sx
@@ -436,6 +436,8 @@
(let
((ch (hs-cur)) (start pos))
(cond
+ (and (= ch "-") (< (+ pos 1) src-len) (= (hs-peek 1) "-"))
+ (do (hs-advance! 2) (skip-comment!) (scan!))
(and (= ch "/") (< (+ pos 1) src-len) (= (hs-peek 1) "/"))
(do (hs-advance! 2) (skip-comment!) (scan!))
(and
@@ -613,6 +615,8 @@
(do (hs-emit! "op" "\\" start) (hs-advance! 1) (scan!))
(= ch ":")
(do (hs-emit! "colon" ":" start) (hs-advance! 1) (scan!))
+ (= ch "|")
+ (do (hs-emit! "op" "|" start) (hs-advance! 1) (scan!))
:else (do (hs-advance! 1) (scan!)))))))
(scan!)
(hs-emit! "eof" nil pos)
diff --git a/shared/static/wasm/sx/hs-compiler.sx b/shared/static/wasm/sx/hs-compiler.sx
index 21dbe0ed..05d81a2e 100644
--- a/shared/static/wasm/sx/hs-compiler.sx
+++ b/shared/static/wasm/sx/hs-compiler.sx
@@ -85,18 +85,18 @@
(nth target 1)
value))
((= th (quote me))
- (list (quote dom-set-inner-html) (quote me) value))
+ (list (quote hs-set-inner-html!) (quote me) value))
((= th (quote it)) (list (quote set!) (quote it) value))
((= th (quote query))
- (list (quote dom-set-inner-html) (hs-to-sx target) value))
+ (list (quote hs-set-inner-html!) (hs-to-sx target) value))
((= th (quote array-index))
(list
- (quote host-set!)
+ (quote hs-array-set!)
(hs-to-sx (nth target 1))
(hs-to-sx (nth target 2))
value))
((or (= th (quote next)) (= th (quote previous)) (= th (quote closest)))
- (list (quote dom-set-inner-html) (hs-to-sx target) value))
+ (list (quote hs-set-inner-html!) (hs-to-sx target) value))
((= th (quote of))
(let
((prop-ast (nth target 1)) (obj-ast (nth target 2)))
@@ -162,10 +162,19 @@
(let
((wrapped-body (if catch-info (let ((var (make-symbol (nth catch-info 0))) (catch-body (hs-to-sx (nth catch-info 1)))) (if finally-info (list (quote do) (list (quote guard) (list var (list true catch-body)) compiled-body) (hs-to-sx finally-info)) (list (quote guard) (list var (list true catch-body)) compiled-body))) (if finally-info (list (quote do) compiled-body (hs-to-sx finally-info)) compiled-body)))
(handler
- (list
- (quote fn)
- (list (quote event))
- wrapped-body)))
+ (let
+ ((uses-the-result? (fn (expr) (cond ((= expr (quote the-result)) true) ((list? expr) (some (fn (x) (uses-the-result? x)) expr)) (true false)))))
+ (list
+ (quote fn)
+ (list (quote event))
+ (if
+ (uses-the-result? wrapped-body)
+ (list
+ (quote let)
+ (list
+ (list (quote the-result) nil))
+ wrapped-body)
+ wrapped-body)))))
(if
every?
(list
@@ -443,9 +452,7 @@
(quote __hs-new)
(list
(quote +)
- (list
- (quote hs-to-number)
- (list (quote nth) var-sym (quote __hs-idx)))
+ (list (quote nth) var-sym (quote __hs-idx))
amount)))
(list
(quote do)
@@ -463,10 +470,7 @@
((t (hs-to-sx expr)))
(list
(quote let)
- (list
- (list
- (quote __hs-new)
- (list (quote +) (list (quote hs-to-number) t) amount)))
+ (list (list (quote __hs-new) (list (quote +) t amount)))
(list
(quote do)
(list (quote set!) t (quote __hs-new))
@@ -564,9 +568,7 @@
(quote __hs-new)
(list
(quote -)
- (list
- (quote hs-to-number)
- (list (quote nth) var-sym (quote __hs-idx)))
+ (list (quote nth) var-sym (quote __hs-idx))
amount)))
(list
(quote do)
@@ -584,10 +586,7 @@
((t (hs-to-sx expr)))
(list
(quote let)
- (list
- (list
- (quote __hs-new)
- (list (quote -) (list (quote hs-to-number) t) amount)))
+ (list (list (quote __hs-new) (list (quote -) t amount)))
(list
(quote do)
(list (quote set!) t (quote __hs-new))
@@ -870,6 +869,11 @@
((= head (quote ref)) (make-symbol (nth ast 1)))
((= head (quote query))
(list (quote hs-query-first) (nth ast 1)))
+ ((= head (quote query-scoped))
+ (list
+ (quote hs-query-all-in)
+ (nth ast 1)
+ (hs-to-sx (nth ast 2))))
((= head (quote attr))
(list
(quote dom-get-attr)
@@ -1163,6 +1167,14 @@
(quote set!)
(hs-to-sx tgt)
(list (quote hs-add-to!) val (hs-to-sx tgt)))))
+ ((= head (quote add-attr))
+ (let
+ ((tgt (nth ast 3)))
+ (list
+ (quote hs-set-attr!)
+ (hs-to-sx tgt)
+ (nth ast 1)
+ (hs-to-sx (nth ast 2)))))
((= head (quote remove-value))
(let
((val (hs-to-sx (nth ast 1))) (tgt (nth ast 2)))
@@ -1296,6 +1308,20 @@
(nth ast 1)
(hs-to-sx (nth ast 2))
(hs-to-sx (nth ast 3))))
+ ((= head (quote toggle-attr-val))
+ (list
+ (quote hs-toggle-attr-val!)
+ (hs-to-sx (nth ast 3))
+ (nth ast 1)
+ (hs-to-sx (nth ast 2))))
+ ((= head (quote toggle-attr-diff))
+ (list
+ (quote hs-toggle-attr-diff!)
+ (hs-to-sx (nth ast 5))
+ (nth ast 1)
+ (hs-to-sx (nth ast 2))
+ (nth ast 3)
+ (hs-to-sx (nth ast 4))))
((= head (quote set!))
(emit-set (nth ast 1) (hs-to-sx (nth ast 2))))
((= head (quote put!))
@@ -1359,13 +1385,48 @@
((= head (quote hide))
(let
((tgt (hs-to-sx (nth ast 1)))
- (strategy (if (> (len ast) 2) (nth ast 2) "display")))
- (list (quote hs-hide!) tgt strategy)))
+ (strategy (if (> (len ast) 2) (nth ast 2) "display"))
+ (when-cond (if (> (len ast) 3) (nth ast 3) nil)))
+ (if
+ (nil? when-cond)
+ (list (quote hs-hide!) tgt strategy)
+ (list
+ (quote hs-hide-when!)
+ tgt
+ strategy
+ (list
+ (quote fn)
+ (list (quote it))
+ (hs-to-sx when-cond))))))
((= head (quote show))
(let
((tgt (hs-to-sx (nth ast 1)))
- (strategy (if (> (len ast) 2) (nth ast 2) "display")))
- (list (quote hs-show!) tgt strategy)))
+ (strategy (if (> (len ast) 2) (nth ast 2) "display"))
+ (when-cond (if (> (len ast) 3) (nth ast 3) nil)))
+ (if
+ (nil? when-cond)
+ (list (quote hs-show!) tgt strategy)
+ (list
+ (quote let)
+ (list
+ (list
+ (quote __hs-show-r)
+ (list
+ (quote hs-show-when!)
+ tgt
+ strategy
+ (list
+ (quote fn)
+ (list (quote it))
+ (hs-to-sx when-cond)))))
+ (list
+ (quote begin)
+ (list
+ (quote set!)
+ (quote the-result)
+ (quote __hs-show-r))
+ (list (quote set!) (quote it) (quote __hs-show-r))
+ (quote __hs-show-r))))))
((= head (quote transition)) (emit-transition ast))
((= head (quote transition-from))
(let
@@ -1424,6 +1485,14 @@
(list (quote hs-settle) (quote me)))
((= head (quote go))
(list (quote hs-navigate!) (hs-to-sx (nth ast 1))))
+ ((= head (quote __get-cmd))
+ (let
+ ((val (hs-to-sx (nth ast 1))))
+ (list
+ (quote begin)
+ (list (quote set!) (quote the-result) val)
+ (list (quote set!) (quote it) val)
+ val)))
((= head (quote append!))
(let
((tgt (hs-to-sx (nth ast 2)))
@@ -1648,11 +1717,13 @@
(list (quote hs-reset!) (hs-to-sx (nth ast 1))))
((= head (quote default!))
(let
- ((t (hs-to-sx (nth ast 1))) (v (hs-to-sx (nth ast 2))))
+ ((tgt-ast (nth ast 1))
+ (read (hs-to-sx (nth ast 1)))
+ (v (hs-to-sx (nth ast 2))))
(list
(quote when)
- (list (quote nil?) t)
- (list (quote set!) t v))))
+ (list (quote hs-default?) read)
+ (emit-set tgt-ast v))))
((= head (quote hs-is))
(list
(quote hs-is)
diff --git a/shared/static/wasm/sx/hs-integration.sx b/shared/static/wasm/sx/hs-integration.sx
index 7fd9bf4b..0c3feaa9 100644
--- a/shared/static/wasm/sx/hs-integration.sx
+++ b/shared/static/wasm/sx/hs-integration.sx
@@ -16,6 +16,14 @@
(fn
(sx)
(define vars (list))
+ (define
+ reserved
+ (list
+ (quote me)
+ (quote it)
+ (quote event)
+ (quote you)
+ (quote yourself)))
(define
walk
(fn
@@ -30,7 +38,9 @@
(let
((name (nth node 1)))
(when
- (not (some (fn (v) (= v name)) vars))
+ (and
+ (not (some (fn (v) (= v name)) vars))
+ (not (some (fn (v) (= v name)) reserved)))
(set! vars (cons name vars)))))
(for-each walk node))))
(walk sx)
diff --git a/shared/static/wasm/sx/hs-parser.sx b/shared/static/wasm/sx/hs-parser.sx
index 0bdb84bb..a06076c1 100644
--- a/shared/static/wasm/sx/hs-parser.sx
+++ b/shared/static/wasm/sx/hs-parser.sx
@@ -95,6 +95,13 @@
(do (adv!) (list kind (str "." val) (list (quote me)))))
((= typ "id")
(do (adv!) (list kind (str "#" val) (list (quote me)))))
+ ((= typ "attr")
+ (do
+ (adv!)
+ (list
+ (quote attr)
+ val
+ (list kind (str "[" val "]") (list (quote me))))))
(true (list kind "*" (list (quote me))))))))
(define
parse-pos-kw
@@ -146,8 +153,10 @@
(do (adv!) (list (quote me))))
((and (= typ "keyword") (= val "I"))
(do (adv!) (list (quote me))))
- ((and (= typ "keyword") (or (= val "it") (= val "result")))
+ ((and (= typ "keyword") (= val "it"))
(do (adv!) (list (quote it))))
+ ((and (= typ "keyword") (= val "result"))
+ (do (adv!) (quote the-result)))
((and (= typ "keyword") (= val "event"))
(do (adv!) (list (quote event))))
((and (= typ "keyword") (= val "target"))
@@ -174,7 +183,18 @@
(do (adv!) (parse-pos-kw (quote last))))
((= typ "id")
(do (adv!) (list (quote query) (str "#" val))))
- ((= typ "selector") (do (adv!) (list (quote query) val)))
+ ((= typ "selector")
+ (do
+ (adv!)
+ (if
+ (and (= (tp-type) "keyword") (= (tp-val) "in"))
+ (do
+ (adv!)
+ (list
+ (quote query-scoped)
+ val
+ (parse-cmp (parse-arith (parse-poss (parse-atom))))))
+ (list (quote query) val))))
((= typ "attr")
(do (adv!) (list (quote attr) val (list (quote me)))))
((= typ "style")
@@ -426,7 +446,7 @@
(list (quote type-check) left type-name)))))))
(true
(let
- ((right (parse-expr)))
+ ((right (parse-cmp (parse-arith (parse-poss (parse-atom))))))
(if
(match-kw "ignoring")
(do
@@ -892,6 +912,18 @@
(let
((tgt (if (match-kw "to") (parse-expr) (list (quote me)))))
(list (quote set-styles) (reverse pairs) tgt)))))
+ ((and (= (tp-type) "bracket-open") (> (len tokens) (+ p 1)) (= (get (nth tokens (+ p 1)) "type") "attr"))
+ (do
+ (adv!)
+ (let
+ ((attr-name (get (adv!) "value")))
+ (when (and (= (tp-type) "op") (= (tp-val) "=")) (adv!))
+ (let
+ ((attr-val (parse-expr)))
+ (when (= (tp-type) "bracket-close") (adv!))
+ (let
+ ((tgt (parse-tgt-kw "to" (list (quote me)))))
+ (list (quote add-attr) attr-name attr-val tgt))))))
(true
(let
((value (parse-expr)))
@@ -978,20 +1010,58 @@
()
(cond
((match-kw "between")
- (if
- (= (tp-type) "class")
- (let
- ((cls1 (do (let ((v (tp-val))) (adv!) v))))
- (expect-kw! "and")
- (if
- (= (tp-type) "class")
- (let
- ((cls2 (do (let ((v (tp-val))) (adv!) v))))
+ (cond
+ ((= (tp-type) "class")
+ (let
+ ((cls1 (do (let ((v (tp-val))) (adv!) v))))
+ (expect-kw! "and")
+ (if
+ (= (tp-type) "class")
(let
- ((tgt (parse-tgt-kw "on" (list (quote me)))))
- (list (quote toggle-between) cls1 cls2 tgt)))
- nil))
- nil))
+ ((cls2 (do (let ((v (tp-val))) (adv!) v))))
+ (let
+ ((tgt (parse-tgt-kw "on" (list (quote me)))))
+ (list (quote toggle-between) cls1 cls2 tgt)))
+ nil)))
+ ((and (= (tp-type) "bracket-open") (> (len tokens) (+ p 1)) (= (get (nth tokens (+ p 1)) "type") "attr"))
+ (do
+ (adv!)
+ (let
+ ((n1 (get (adv!) "value")))
+ (when
+ (and (= (tp-type) "op") (= (tp-val) "="))
+ (adv!))
+ (let
+ ((v1 (parse-expr)))
+ (when (= (tp-type) "bracket-close") (adv!))
+ (expect-kw! "and")
+ (when (= (tp-type) "bracket-open") (adv!))
+ (let
+ ((n2 (get (adv!) "value")))
+ (when
+ (and (= (tp-type) "op") (= (tp-val) "="))
+ (adv!))
+ (let
+ ((v2 (parse-expr)))
+ (when (= (tp-type) "bracket-close") (adv!))
+ (let
+ ((tgt (parse-tgt-kw "on" (list (quote me)))))
+ (if
+ (= n1 n2)
+ (list
+ (quote toggle-attr-between)
+ n1
+ v1
+ v2
+ tgt)
+ (list
+ (quote toggle-attr-diff)
+ n1
+ v1
+ n2
+ v2
+ tgt)))))))))
+ (true nil)))
((= (tp-type) "class")
(let
((cls (do (let ((v (tp-val))) (adv!) v))))
@@ -1064,6 +1134,18 @@
val2
tgt)))
(list (quote toggle-attr) attr-name tgt)))))
+ ((and (= (tp-type) "bracket-open") (> (len tokens) (+ p 1)) (= (get (nth tokens (+ p 1)) "type") "attr"))
+ (do
+ (adv!)
+ (let
+ ((attr-name (get (adv!) "value")))
+ (when (and (= (tp-type) "op") (= (tp-val) "=")) (adv!))
+ (let
+ ((attr-val (parse-expr)))
+ (when (= (tp-type) "bracket-close") (adv!))
+ (let
+ ((tgt (parse-tgt-kw "on" (list (quote me)))))
+ (list (quote toggle-attr-val) attr-name attr-val tgt))))))
((and (= (tp-type) "keyword") (= (tp-val) "my"))
(do
(adv!)
@@ -1338,19 +1420,23 @@
(fn
()
(let
- ((tgt (cond ((at-end?) (list (quote me))) ((and (= (tp-type) "keyword") (or (= (tp-val) "then") (= (tp-val) "end") (= (tp-val) "with") (= (tp-val) "add") (= (tp-val) "remove") (= (tp-val) "set") (= (tp-val) "put") (= (tp-val) "toggle") (= (tp-val) "hide") (= (tp-val) "show"))) (list (quote me))) (true (parse-expr)))))
+ ((tgt (cond ((at-end?) (list (quote me))) ((and (= (tp-type) "keyword") (or (= (tp-val) "then") (= (tp-val) "end") (= (tp-val) "with") (= (tp-val) "when") (= (tp-val) "add") (= (tp-val) "remove") (= (tp-val) "set") (= (tp-val) "put") (= (tp-val) "toggle") (= (tp-val) "hide") (= (tp-val) "show"))) (list (quote me))) (true (parse-expr)))))
(let
((strategy (if (match-kw "with") (if (at-end?) "display" (let ((s (tp-val))) (adv!) s)) "display")))
- (list (quote hide) tgt strategy)))))
+ (let
+ ((when-cond (if (and (= (tp-type) "keyword") (= (tp-val) "when")) (do (adv!) (parse-expr)) nil)))
+ (list (quote hide) tgt strategy when-cond))))))
(define
parse-show-cmd
(fn
()
(let
- ((tgt (cond ((at-end?) (list (quote me))) ((and (= (tp-type) "keyword") (or (= (tp-val) "then") (= (tp-val) "end") (= (tp-val) "with") (= (tp-val) "add") (= (tp-val) "remove") (= (tp-val) "set") (= (tp-val) "put") (= (tp-val) "toggle") (= (tp-val) "hide") (= (tp-val) "show"))) (list (quote me))) (true (parse-expr)))))
+ ((tgt (cond ((at-end?) (list (quote me))) ((and (= (tp-type) "keyword") (or (= (tp-val) "then") (= (tp-val) "end") (= (tp-val) "with") (= (tp-val) "when") (= (tp-val) "add") (= (tp-val) "remove") (= (tp-val) "set") (= (tp-val) "put") (= (tp-val) "toggle") (= (tp-val) "hide") (= (tp-val) "show"))) (list (quote me))) (true (parse-expr)))))
(let
((strategy (if (match-kw "with") (if (at-end?) "display" (let ((s (tp-val))) (adv!) s)) "display")))
- (list (quote show) tgt strategy)))))
+ (let
+ ((when-cond (if (and (= (tp-type) "keyword") (= (tp-val) "when")) (do (adv!) (parse-expr)) nil)))
+ (list (quote show) tgt strategy when-cond))))))
(define
parse-transition-cmd
(fn
@@ -1493,7 +1579,7 @@
(ca-collect (append acc (list arg)))))))
(ca-collect (list))))
(define parse-call-cmd (fn () (parse-expr)))
- (define parse-get-cmd (fn () (parse-expr)))
+ (define parse-get-cmd (fn () (list (quote __get-cmd) (parse-expr))))
(define
parse-take-cmd
(fn
@@ -1501,12 +1587,34 @@
(cond
((= (tp-type) "class")
(let
- ((cls (do (let ((v (tp-val))) (adv!) v))))
+ ((classes (list)))
(let
- ((from-sel (if (match-kw "from") (parse-expr) nil)))
+ ((collect (fn () (when (= (tp-type) "class") (let ((v (tp-val))) (adv!) (set! classes (append classes (list v))) (collect))))))
+ (collect)
(let
- ((for-tgt (if (match-kw "for") (parse-expr) nil)))
- (list (quote take!) "class" cls from-sel for-tgt)))))
+ ((from-sel (if (match-kw "from") (parse-expr) nil)))
+ (let
+ ((for-tgt (if (match-kw "for") (parse-expr) nil)))
+ (if
+ (= (len classes) 1)
+ (list
+ (quote take!)
+ "class"
+ (first classes)
+ from-sel
+ for-tgt)
+ (cons
+ (quote do)
+ (map
+ (fn
+ (cls)
+ (list
+ (quote take!)
+ "class"
+ cls
+ from-sel
+ for-tgt))
+ classes))))))))
((= (tp-type) "attr")
(let
((attr-name (get (adv!) "value")))
@@ -1588,7 +1696,7 @@
(adv!)
(expect-kw! "of")
(let
- ((regex (parse-expr)))
+ ((regex (parse-atom)))
(do
(cond
((match-kw "of") nil)
@@ -1606,7 +1714,7 @@
(adv!)
(expect-kw! "of")
(let
- ((regex (parse-expr)))
+ ((regex (parse-atom)))
(do
(cond
((match-kw "of") nil)
@@ -1697,7 +1805,7 @@
(match-kw "of")
(list (make-symbol ".") (parse-expr) val)
(cond
- ((= val "result") (list (quote it)))
+ ((= val "result") (quote the-result))
((= val "first") (parse-pos-kw (quote first)))
((= val "last") (parse-pos-kw (quote last)))
((= val "closest") (parse-trav (quote closest)))
diff --git a/shared/static/wasm/sx/hs-runtime.sx b/shared/static/wasm/sx/hs-runtime.sx
index 8ccdf9a3..c0df704d 100644
--- a/shared/static/wasm/sx/hs-runtime.sx
+++ b/shared/static/wasm/sx/hs-runtime.sx
@@ -22,7 +22,13 @@
;; Stock hyperscript queues by default; "every" disables queuing.
(define
hs-on
- (fn (target event-name handler) (dom-listen target event-name handler)))
+ (fn
+ (target event-name handler)
+ (let
+ ((unlisten (dom-listen target event-name handler))
+ (prev (or (dom-get-data target "hs-unlisteners") (list))))
+ (dom-set-data target "hs-unlisteners" (append prev (list unlisten)))
+ unlisten)))
;; Run an initializer function immediately.
;; (hs-init thunk) — called at element boot time
@@ -156,26 +162,116 @@
(dom-set-attr target name ""))))))))
;; First element matching selector within a scope.
-(define
- hs-put!
- (fn
- (value pos target)
- (cond
- ((= pos "into")
- (if (list? target) target (dom-set-inner-html target value)))
- ((= pos "before")
- (dom-insert-adjacent-html target "beforebegin" value))
- ((= pos "after") (dom-insert-adjacent-html target "afterend" value))
- ((= pos "start")
- (if
- (list? target)
- (append! target value 0)
- (dom-insert-adjacent-html target "afterbegin" value)))
- ((= pos "end")
- (if
- (list? target)
- (append! target value)
- (dom-insert-adjacent-html target "beforeend" value))))))
+(begin
+ (define
+ hs-element?
+ (fn
+ (v)
+ (and v (or (host-get v "nodeType") (host-get v "__mock_type")))))
+ (define
+ hs-set-attr!
+ (fn
+ (el name val)
+ (if (nil? val) (dom-remove-attr el name) (dom-set-attr el name val))))
+ (define
+ hs-toggle-attr!
+ (fn
+ (el name)
+ (if
+ (dom-has-attr? el name)
+ (dom-remove-attr el name)
+ (dom-set-attr el name ""))))
+ (define
+ hs-toggle-attr-val!
+ (fn
+ (el name val)
+ (if
+ (= (dom-get-attr el name) val)
+ (dom-remove-attr el name)
+ (dom-set-attr el name val))))
+ (define
+ hs-toggle-attr-between!
+ (fn
+ (el name val1 val2)
+ (if
+ (= (dom-get-attr el name) val1)
+ (dom-set-attr el name val2)
+ (dom-set-attr el name val1))))
+ (define
+ hs-toggle-attr-diff!
+ (fn
+ (el n1 v1 n2 v2)
+ (if
+ (dom-has-attr? el n1)
+ (do (dom-remove-attr el n1) (dom-set-attr el n2 v2))
+ (do
+ (when (dom-has-attr? el n2) (dom-remove-attr el n2))
+ (dom-set-attr el n1 v1)))))
+ (define
+ hs-set-inner-html!
+ (fn
+ (target value)
+ (do (dom-set-inner-html target value) (hs-boot-subtree! target))))
+ (define
+ hs-put!
+ (fn
+ (value pos target)
+ (cond
+ ((= pos "into")
+ (cond
+ ((list? target) target)
+ ((hs-element? value)
+ (do
+ (dom-set-inner-html target "")
+ (host-call target "appendChild" value)))
+ (true
+ (do
+ (dom-set-inner-html target value)
+ (hs-boot-subtree! target)))))
+ ((= pos "before")
+ (if
+ (hs-element? value)
+ (let
+ ((parent (dom-parent target)))
+ (when parent (host-call parent "insertBefore" value target)))
+ (let
+ ((parent (dom-parent target)))
+ (do
+ (dom-insert-adjacent-html target "beforebegin" value)
+ (when parent (hs-boot-subtree! parent))))))
+ ((= pos "after")
+ (if
+ (hs-element? value)
+ (let
+ ((parent (dom-parent target))
+ (next (host-get target "nextSibling")))
+ (when
+ parent
+ (if
+ next
+ (host-call parent "insertBefore" value next)
+ (host-call parent "appendChild" value))))
+ (let
+ ((parent (dom-parent target)))
+ (do
+ (dom-insert-adjacent-html target "afterend" value)
+ (when parent (hs-boot-subtree! parent))))))
+ ((= pos "start")
+ (cond
+ ((list? target) (append! target value 0))
+ ((hs-element? value) (dom-prepend target value))
+ (true
+ (do
+ (dom-insert-adjacent-html target "afterbegin" value)
+ (hs-boot-subtree! target)))))
+ ((= pos "end")
+ (cond
+ ((list? target) (append! target value))
+ ((hs-element? value) (dom-append target value))
+ (true
+ (do
+ (dom-insert-adjacent-html target "beforeend" value)
+ (hs-boot-subtree! target)))))))))
;; Last element matching selector.
(define
@@ -228,16 +324,22 @@
(define
hs-halt!
(fn
- (mode)
- (when
- event
- (cond
- ((= mode "default") (host-call event "preventDefault"))
- ((= mode "bubbling") (host-call event "stopPropagation"))
- (true
- (do
- (host-call event "preventDefault")
- (host-call event "stopPropagation")))))))
+ (ev mode)
+ (do
+ (when
+ ev
+ (cond
+ ((= mode "default") (host-call ev "preventDefault"))
+ ((= mode "bubbling") (host-call ev "stopPropagation"))
+ ((= mode "the-event")
+ (do
+ (host-call ev "preventDefault")
+ (host-call ev "stopPropagation")))
+ (true
+ (do
+ (host-call ev "preventDefault")
+ (host-call ev "stopPropagation")))))
+ (when (not (= mode "the-event")) (raise (list "hs-return" nil))))))
;; ── Type coercion ───────────────────────────────────────────────
@@ -249,7 +351,51 @@
;; Make a new object of a given type.
;; (hs-make type-name) — creates empty object/collection
-(define hs-reset! (fn (target) (host-call target "reset" (list))))
+(define
+ hs-reset!
+ (fn
+ (target)
+ (cond
+ ((list? target) (for-each (fn (el) (hs-reset! el)) target))
+ ((nil? target) nil)
+ (true
+ (let
+ ((tag (dom-get-prop target "tagName")))
+ (cond
+ ((= tag "FORM") (host-call target "reset" (list)))
+ ((or (= tag "INPUT") (= tag "TEXTAREA"))
+ (let
+ ((input-type (dom-get-prop target "type")))
+ (cond
+ ((or (= input-type "checkbox") (= input-type "radio"))
+ (dom-set-prop
+ target
+ "checked"
+ (dom-get-prop target "defaultChecked")))
+ (true
+ (dom-set-prop
+ target
+ "value"
+ (dom-get-prop target "defaultValue"))))))
+ ((= tag "SELECT")
+ (let
+ ((options (host-call target "querySelectorAll" "option"))
+ (default-val nil))
+ (do
+ (for-each
+ (fn
+ (opt)
+ (when
+ (and
+ (nil? default-val)
+ (dom-get-prop opt "defaultSelected"))
+ (set! default-val (dom-get-prop opt "value"))))
+ options)
+ (when
+ (and (nil? default-val) (> (len options) 0))
+ (set! default-val (dom-get-prop (first options) "value")))
+ (when default-val (dom-set-prop target "value" default-val)))))
+ (true nil)))))))
;; ── Behavior installation ───────────────────────────────────────
@@ -306,6 +452,25 @@
hs-query-all
(fn (sel) (host-call (dom-body) "querySelectorAll" sel)))
+(define
+ hs-query-all-in
+ (fn
+ (sel target)
+ (if
+ (nil? target)
+ (hs-query-all sel)
+ (host-call target "querySelectorAll" sel))))
+
+(define
+ hs-list-set
+ (fn
+ (lst idx val)
+ (append (take lst idx) (cons val (drop lst (+ idx 1))))))
+
+(define
+ hs-to-number
+ (fn (v) (if (number? v) v (or (parse-number (str v)) 0))))
+
(define
hs-query-first
(fn (sel) (host-call (host-global "document") "querySelector" sel)))
@@ -375,6 +540,10 @@
((= signal "hs-continue") (hs-repeat-while cond-fn thunk))
(true (hs-repeat-while cond-fn thunk)))))))
+
+
+
+
(define
hs-repeat-until
(fn
@@ -406,30 +575,38 @@
((= signal "hs-continue") (do-loop (rest remaining)))
(true (do-loop (rest remaining))))))))
(do-loop items))))
-
+;; ── Sandbox/test runtime additions ──────────────────────────────
+;; Property access — dot notation and .length
(begin
(define
hs-append
(fn
(target value)
(cond
+ ((nil? target) value)
((string? target) (str target value))
((list? target) (append target (list value)))
+ ((hs-element? target)
+ (do
+ (dom-insert-adjacent-html target "beforeend" (str value))
+ target))
(true (str target value)))))
(define
hs-append!
- (fn (value target) (dom-insert-adjacent-html target "beforeend" value))))
-
-
-
-
-
+ (fn
+ (value target)
+ (cond
+ ((nil? target) nil)
+ ((hs-element? target)
+ (dom-insert-adjacent-html target "beforeend" (str value)))
+ (true nil)))))
+;; DOM query stub — sandbox returns empty list
(define
hs-fetch
(fn
(url format)
(perform (list "io-fetch" url (if format format "text")))))
-
+;; Method dispatch — obj.method(args)
(define
hs-coerce
(fn
@@ -449,16 +626,18 @@
((= type-name "Array") (if (list? value) value (list value)))
((= type-name "HTML") (str value))
((= type-name "JSON")
- (if
- (string? value)
- value
- (host-call (host-global "JSON") "stringify" value)))
+ (cond
+ ((string? value) (guard (_e (true value)) (json-parse value)))
+ ((dict? value) (json-stringify value))
+ ((list? value) (json-stringify value))
+ (true value)))
((= type-name "Object")
(if
(string? value)
- (host-call (host-global "JSON") "parse" value)
+ (guard (_e (true value)) (json-parse value))
value))
- ((or (= type-name "Fixed") (= type-name "Fixed:"))
+ ((= type-name "JSONString") (json-stringify value))
+ ((or (= type-name "Fixed") (= type-name "Fixed:") (starts-with? type-name "Fixed:"))
(let
((digits (if (> (string-length type-name) 6) (+ (substring type-name 6 (string-length type-name)) 0) 0))
(num (+ value 0)))
@@ -466,7 +645,7 @@
(= digits 0)
(str (floor num))
(let
- ((factor (** 10 digits)))
+ ((factor (pow 10 digits)))
(str (/ (floor (+ (* num factor) 0.5)) factor))))))
((= type-name "Selector") (str value))
((= type-name "Fragment") value)
@@ -475,7 +654,7 @@
(dict? value)
(map (fn (k) (get value k)) (keys value))
value))
- ((= type-name "Keys") (if (dict? value) (keys value) value))
+ ((= type-name "Keys") (if (dict? value) (sort (keys value)) value))
((= type-name "Entries")
(if
(dict? value)
@@ -518,8 +697,24 @@
(map (fn (k) (list k (get value k))) (keys value))
value))
(true value))))
-;; ── Sandbox/test runtime additions ──────────────────────────────
-;; Property access — dot notation and .length
+
+;; ── 0.9.90 features ─────────────────────────────────────────────
+;; beep! — debug logging, returns value unchanged
+(define
+ hs-default?
+ (fn
+ (v)
+ (cond
+ ((nil? v) true)
+ ((and (string? v) (= v "")) true)
+ (true false))))
+;; Property-based is — check obj.key truthiness
+(define
+ hs-array-set!
+ (fn
+ (arr i v)
+ (if (list? arr) (do (set-nth! arr i v) v) (host-set! arr i v))))
+;; Array slicing (inclusive both ends)
(define
hs-add
(fn
@@ -529,7 +724,7 @@
((list? b) (cons a b))
((or (string? a) (string? b)) (str a b))
(true (+ a b)))))
-;; DOM query stub — sandbox returns empty list
+;; Collection: sorted by
(define
hs-make
(fn
@@ -540,15 +735,13 @@
((= type-name "Set") (list))
((= type-name "Map") (dict))
(true (dict)))))
-;; Method dispatch — obj.method(args)
+;; Collection: sorted by descending
(define hs-install (fn (behavior-fn) (behavior-fn me)))
-
-;; ── 0.9.90 features ─────────────────────────────────────────────
-;; beep! — debug logging, returns value unchanged
+;; Collection: split by
(define
hs-measure
(fn (target) (perform (list (quote io-measure) target))))
-;; Property-based is — check obj.key truthiness
+;; Collection: joined by
(define
hs-transition
(fn
@@ -561,7 +754,7 @@
(str prop " " (/ duration 1000) "s")))
(dom-set-style target prop value)
(when duration (hs-settle target))))
-;; Array slicing (inclusive both ends)
+
(define
hs-transition-from
(fn
@@ -575,7 +768,7 @@
(str prop " " (/ duration 1000) "s")))
(dom-set-style target prop (str to-val))
(when duration (hs-settle target))))
-;; Collection: sorted by
+
(define
hs-type-check
(fn
@@ -595,25 +788,68 @@
(= (host-typeof value) "element")
(= (host-typeof value) "text")))
(true (= (host-typeof value) (downcase type-name)))))))
-;; Collection: sorted by descending
+
(define
hs-type-check-strict
(fn
(value type-name)
(if (nil? value) false (hs-type-check value type-name))))
-;; Collection: split by
+
(define
hs-strict-eq
(fn (a b) (and (= (type-of a) (type-of b)) (= a b))))
-;; Collection: joined by
+
(define
hs-eq-ignore-case
(fn (a b) (= (downcase (str a)) (downcase (str b)))))
+(define
+ hs-starts-with?
+ (fn
+ (s prefix)
+ (cond
+ ((nil? s) false)
+ ((nil? prefix) false)
+ (true (starts-with? (str s) (str prefix))))))
+
+(define
+ hs-ends-with?
+ (fn
+ (s suffix)
+ (cond
+ ((nil? s) false)
+ ((nil? suffix) false)
+ (true (ends-with? (str s) (str suffix))))))
+
+(define
+ hs-precedes?
+ (fn
+ (a b)
+ (cond ((nil? a) false) ((nil? b) false) (true (< (str a) (str b))))))
+
+(define
+ hs-follows?
+ (fn
+ (a b)
+ (cond ((nil? a) false) ((nil? b) false) (true (> (str a) (str b))))))
+
(define
hs-starts-with-ic?
(fn (str prefix) (starts-with? (downcase str) (downcase prefix))))
+(define
+ hs-ends-with-ic?
+ (fn (str suffix) (ends-with? (downcase str) (downcase suffix))))
+
+(define
+ hs-matches-ignore-case?
+ (fn
+ (target pattern)
+ (cond
+ ((string? target)
+ (contains? (downcase (str target)) (downcase (str pattern))))
+ (true false))))
+
(define
hs-contains-ignore-case?
(fn
@@ -651,18 +887,35 @@
((nil? collection) false)
((string? collection) (string-contains? collection (str item)))
((list? collection)
- (if
- (list? item)
- (filter (fn (x) (hs-contains? collection x)) item)
- (if
- (= (len collection) 0)
- false
+ (cond
+ ((nil? item) (list))
+ ((list? item)
+ (filter (fn (x) (hs-contains? collection x)) item))
+ (true
(if
- (= (first collection) item)
- true
- (hs-contains? (rest collection) item)))))
+ (= (len collection) 0)
+ false
+ (if
+ (= (first collection) item)
+ true
+ (hs-contains? (rest collection) item))))))
(true false))))
+(define
+ hs-is
+ (fn
+ (obj thunk prop)
+ (cond
+ ((and (dict? obj) (some (fn (k) (= k prop)) (keys obj)))
+ (not (hs-falsy? (get obj prop))))
+ (true
+ (let
+ ((r (cek-try thunk)))
+ (if
+ (and (list? r) (= (first r) (quote ok)))
+ (= obj (nth r 1))
+ (= obj nil)))))))
+
(define precedes? (fn (a b) (< (str a) (str b))))
(define
@@ -676,6 +929,17 @@
((dict? v) (= (len (keys v)) 0))
(true false))))
+(define
+ hs-empty-like
+ (fn
+ (v)
+ (cond
+ ((list? v) (list))
+ ((dict? v) (dict))
+ ((string? v) "")
+ ((nil? v) nil)
+ (true v))))
+
(define
hs-empty-target!
(fn
@@ -694,23 +958,234 @@
(or (= input-type "checkbox") (= input-type "radio"))
(dom-set-prop target "checked" false)
(dom-set-prop target "value" ""))))
- ((= tag "FORM") (dom-set-inner-html target ""))
((= tag "FORM")
(let
((children (host-call target "querySelectorAll" "input, textarea, select")))
(for-each (fn (el) (hs-empty-target! el)) children)))
(true (dom-set-inner-html target ""))))))))
+(define
+ hs-morph-char
+ (fn (s p) (if (or (< p 0) (>= p (string-length s))) nil (nth s p))))
+
+(define
+ hs-morph-index-from
+ (fn
+ (s needle from)
+ (let
+ ((r (index-of (substring s from (string-length s)) needle)))
+ (if (< r 0) -1 (+ from r)))))
+
+(define
+ hs-morph-sws
+ (fn
+ (s p)
+ (let
+ ((c (hs-morph-char s p)))
+ (if (and c (hs-ws? c)) (hs-morph-sws s (+ p 1)) p))))
+
+(define
+ hs-morph-read-until
+ (fn
+ (s p stop)
+ (define
+ loop
+ (fn
+ (q)
+ (let
+ ((c (hs-morph-char s q)))
+ (if (and c (< (index-of stop c) 0)) (loop (+ q 1)) q))))
+ (let ((e (loop p))) (list (substring s p e) e))))
+
+(define
+ hs-morph-parse-attrs
+ (fn
+ (s p acc)
+ (let
+ ((p (hs-morph-sws s p)))
+ (let
+ ((c (hs-morph-char s p)))
+ (cond
+ ((nil? c) (list acc p false))
+ ((= c ">") (list acc (+ p 1) false))
+ ((= c "/")
+ (if
+ (= (hs-morph-char s (+ p 1)) ">")
+ (list acc (+ p 2) true)
+ (list acc (+ p 1) false)))
+ (true
+ (let
+ ((r (hs-morph-read-until s p " \t\n=/>")))
+ (let
+ ((name (first r)) (p2 (nth r 1)))
+ (let
+ ((p3 (hs-morph-sws s p2)))
+ (if
+ (= (hs-morph-char s p3) "=")
+ (let
+ ((p4 (hs-morph-sws s (+ p3 1))))
+ (let
+ ((c2 (hs-morph-char s p4)))
+ (cond
+ ((= c2 "\"")
+ (let
+ ((close (hs-morph-index-from s "\"" (+ p4 1))))
+ (hs-morph-parse-attrs
+ s
+ (+ close 1)
+ (append
+ acc
+ (list
+ (list name (substring s (+ p4 1) close)))))))
+ ((= c2 "'")
+ (let
+ ((close (hs-morph-index-from s "'" (+ p4 1))))
+ (hs-morph-parse-attrs
+ s
+ (+ close 1)
+ (append
+ acc
+ (list
+ (list name (substring s (+ p4 1) close)))))))
+ (true
+ (let
+ ((r2 (hs-morph-read-until s p4 " \t\n/>")))
+ (hs-morph-parse-attrs
+ s
+ (nth r2 1)
+ (append acc (list (list name (first r2))))))))))
+ (hs-morph-parse-attrs
+ s
+ p3
+ (append acc (list (list name ""))))))))))))))
+
+(define
+ hs-morph-parse-element
+ (fn
+ (s p)
+ (let
+ ((p (hs-morph-sws s p)))
+ (if
+ (not (= (hs-morph-char s p) "<"))
+ nil
+ (let
+ ((r (hs-morph-read-until s (+ p 1) " \t\n/>")))
+ (let
+ ((tag (first r)) (p2 (nth r 1)))
+ (let
+ ((ar (hs-morph-parse-attrs s p2 (list))))
+ (let
+ ((attrs (first ar))
+ (p3 (nth ar 1))
+ (self-closing (nth ar 2)))
+ (if
+ self-closing
+ {:children (list) :end p3 :tag tag :type "element" :attrs attrs}
+ (let
+ ((cr (hs-morph-parse-children s p3 (list))))
+ {:children (first cr) :end (nth cr 1) :tag tag :type "element" :attrs attrs}))))))))))
+
+(define
+ hs-morph-parse-children
+ (fn
+ (s p acc)
+ (let
+ ((c (hs-morph-char s p)))
+ (cond
+ ((nil? c) (list acc p))
+ ((= c "<")
+ (if
+ (= (hs-morph-char s (+ p 1)) "/")
+ (let
+ ((close-gt (hs-morph-index-from s ">" (+ p 1))))
+ (list acc (+ close-gt 1)))
+ (let
+ ((child (hs-morph-parse-element s p)))
+ (if
+ (nil? child)
+ (list acc p)
+ (hs-morph-parse-children
+ s
+ (get child :end)
+ (append acc (list child)))))))
+ (true
+ (let
+ ((r (hs-morph-read-until s p "<")))
+ (hs-morph-parse-children
+ s
+ (nth r 1)
+ (append acc (list {:text (first r) :type "text"})))))))))
+
+(define
+ hs-morph-apply-attrs
+ (fn
+ (el attrs keep-id)
+ (for-each
+ (fn
+ (av)
+ (let
+ ((n (first av)) (v (nth av 1)))
+ (cond
+ ((= n "class")
+ (for-each
+ (fn
+ (c)
+ (when (> (string-length c) 0) (dom-add-class el c)))
+ (split v " ")))
+ ((and keep-id (= n "id")) nil)
+ (true (dom-set-attr el n v)))))
+ attrs)))
+
+(define
+ hs-morph-build-children
+ (fn
+ (parent children)
+ (cond
+ ((= (len children) 0) nil)
+ ((and (= (len children) 1) (= (get (first children) :type) "text"))
+ (dom-set-inner-html parent (get (first children) :text)))
+ (true (for-each (fn (c) (hs-morph-build-child parent c)) children)))))
+
+(define
+ hs-morph-build-child
+ (fn
+ (parent node)
+ (cond
+ ((= (get node :type) "element")
+ (let
+ ((el (dom-create-element (get node :tag))))
+ (do
+ (hs-morph-apply-attrs el (get node :attrs) false)
+ (hs-morph-build-children el (get node :children))
+ (dom-append parent el)
+ (hs-activate! el))))
+ (true nil))))
+
+(define
+ hs-morph!
+ (fn
+ (target content)
+ (when
+ target
+ (let
+ ((tree (hs-morph-parse-element content 0)))
+ (when
+ tree
+ (do
+ (hs-morph-apply-attrs target (get tree :attrs) true)
+ (dom-set-inner-html target "")
+ (hs-morph-build-children target (get tree :children))))))))
+
(define
hs-open!
(fn
(el)
(let
((tag (dom-get-prop el "tagName")))
- (if
- (= tag "DIALOG")
- (host-call el "showModal")
- (dom-set-prop el "open" true)))))
+ (cond
+ ((= tag "DIALOG") (host-call el "showModal"))
+ (true
+ (do (dom-set-attr el "open" "") (dom-set-prop el "open" true)))))))
(define
hs-close!
@@ -718,38 +1193,100 @@
(el)
(let
((tag (dom-get-prop el "tagName")))
+ (cond
+ ((= tag "DIALOG") (host-call el "close"))
+ (true
+ (do
+ (host-call el "removeAttribute" "open")
+ (dom-set-prop el "open" false)))))))
+
+(begin
+ (define
+ hs-hide-one!
+ (fn
+ (el strategy)
+ (let
+ ((tag (dom-get-prop el "tagName")))
+ (cond
+ ((= tag "DIALOG")
+ (when (dom-has-attr? el "open") (host-call el "close")))
+ ((= tag "DETAILS") (dom-set-prop el "open" false))
+ ((= strategy "opacity") (dom-set-style el "opacity" "0"))
+ ((= strategy "visibility")
+ (dom-set-style el "visibility" "hidden"))
+ (true (dom-set-style el "display" "none"))))))
+ (define
+ hs-hide!
+ (fn
+ (target strategy)
(if
- (= tag "DIALOG")
- (host-call el "close")
- (dom-set-prop el "open" false)))))
+ (list? target)
+ (do (for-each (fn (el) (hs-hide-one! el strategy)) target) target)
+ (do (hs-hide-one! target strategy) target)))))
+
+(begin
+ (define
+ hs-show-one!
+ (fn
+ (el strategy)
+ (let
+ ((tag (dom-get-prop el "tagName")))
+ (cond
+ ((= tag "DIALOG")
+ (when
+ (not (dom-has-attr? el "open"))
+ (host-call el "showModal")))
+ ((= tag "DETAILS") (dom-set-prop el "open" true))
+ ((= strategy "opacity") (dom-set-style el "opacity" "1"))
+ ((= strategy "visibility")
+ (dom-set-style el "visibility" "visible"))
+ (true (dom-set-style el "display" ""))))))
+ (define
+ hs-show!
+ (fn
+ (target strategy)
+ (if
+ (list? target)
+ (do (for-each (fn (el) (hs-show-one! el strategy)) target) target)
+ (do (hs-show-one! target strategy) target)))))
(define
- hs-hide!
+ hs-show-when!
(fn
- (el strategy)
+ (target strategy pred)
(let
- ((tag (dom-get-prop el "tagName")))
- (cond
- ((= tag "DIALOG")
- (when (dom-has-attr? el "open") (host-call el "close")))
- ((= tag "DETAILS") (dom-set-prop el "open" false))
- ((= strategy "opacity") (dom-set-style el "opacity" "0"))
- ((= strategy "visibility") (dom-set-style el "visibility" "hidden"))
- (true (dom-set-style el "display" "none"))))))
+ ((items (if (list? target) target (list target))))
+ (let
+ ((matched (list)))
+ (do
+ (for-each
+ (fn
+ (el)
+ (if
+ (pred el)
+ (do (hs-show-one! el strategy) (append! matched el))
+ (hs-hide-one! el strategy)))
+ items)
+ matched)))))
(define
- hs-show!
+ hs-hide-when!
(fn
- (el strategy)
+ (target strategy pred)
(let
- ((tag (dom-get-prop el "tagName")))
- (cond
- ((= tag "DIALOG")
- (when (not (dom-has-attr? el "open")) (host-call el "showModal")))
- ((= tag "DETAILS") (dom-set-prop el "open" true))
- ((= strategy "opacity") (dom-set-style el "opacity" "1"))
- ((= strategy "visibility") (dom-set-style el "visibility" "visible"))
- (true (dom-set-style el "display" ""))))))
+ ((items (if (list? target) target (list target))))
+ (let
+ ((matched (list)))
+ (do
+ (for-each
+ (fn
+ (el)
+ (if
+ (pred el)
+ (do (hs-hide-one! el strategy) (append! matched el))
+ (hs-show-one! el strategy)))
+ items)
+ matched)))))
(define hs-first (fn (lst) (first lst)))
@@ -889,6 +1426,33 @@
(e (if (nil? end) (len col) (+ end 1))))
(slice col s e))))
+(define
+ hs-pick-first
+ (fn
+ (col n)
+ (let ((m (if (< n (len col)) n (len col)))) (slice col 0 m))))
+
+(define
+ hs-pick-last
+ (fn
+ (col n)
+ (let
+ ((total (len col)))
+ (let
+ ((start (if (< n total) (- total n) 0)))
+ (slice col start total)))))
+
+(define
+ hs-pick-random
+ (fn
+ (col n)
+ (if
+ (nil? n)
+ (first col)
+ (let ((m (if (< n (len col)) n (len col)))) (slice col 0 m)))))
+
+(define hs-pick-items (fn (col start end) (slice col start end)))
+
(define
hs-sorted-by
(fn
@@ -952,3 +1516,119 @@
(define
hs-sorted-by-desc
(fn (col key-fn) (reverse (hs-sorted-by col key-fn))))
+
+(define
+ hs-dom-has-var?
+ (fn
+ (el name)
+ (if
+ (nil? el)
+ false
+ (let
+ ((store (host-get el "__hs_vars")))
+ (if (nil? store) false (host-call store "hasOwnProperty" name))))))
+
+(define
+ hs-dom-get-var-raw
+ (fn
+ (el name)
+ (let
+ ((store (host-get el "__hs_vars")))
+ (if (nil? store) nil (host-get store name)))))
+
+(define
+ hs-dom-set-var-raw!
+ (fn
+ (el name val)
+ (let
+ ((changed (not (and (hs-dom-has-var? el name) (= (hs-dom-get-var-raw el name) val)))))
+ (do
+ (when
+ (nil? (host-get el "__hs_vars"))
+ (host-set! el "__hs_vars" (host-new "Object")))
+ (host-set! (host-get el "__hs_vars") name val)
+ (when changed (hs-dom-fire-watchers! el name val))))))
+
+(define
+ hs-dom-resolve-start
+ (fn
+ (el)
+ (if
+ (nil? el)
+ nil
+ (let
+ ((scope (dom-get-attr el "dom-scope")))
+ (cond
+ ((or (nil? scope) (= scope "") (= scope "isolated")) el)
+ ((starts-with? scope "closest ")
+ (dom-closest el (slice scope 8 (len scope))))
+ ((starts-with? scope "parent of ")
+ (let
+ ((match (dom-closest el (slice scope 10 (len scope)))))
+ (if match (dom-parent match) nil)))
+ (true el))))))
+
+(define
+ hs-dom-walk
+ (fn
+ (el name)
+ (cond
+ ((nil? el) nil)
+ ((hs-dom-has-var? el name) (hs-dom-get-var-raw el name))
+ ((= (dom-get-attr el "dom-scope") "isolated") nil)
+ (true (hs-dom-walk (dom-parent el) name)))))
+
+(define
+ hs-dom-find-owner
+ (fn
+ (el name)
+ (cond
+ ((nil? el) nil)
+ ((hs-dom-has-var? el name) el)
+ ((= (dom-get-attr el "dom-scope") "isolated") nil)
+ (true (hs-dom-find-owner (dom-parent el) name)))))
+
+(define
+ hs-dom-get
+ (fn (el name) (hs-dom-walk (hs-dom-resolve-start el) name)))
+
+(define
+ hs-dom-set!
+ (fn
+ (el name val)
+ (let
+ ((start (hs-dom-resolve-start el)))
+ (let
+ ((owner (hs-dom-find-owner start name)))
+ (hs-dom-set-var-raw! (if owner owner start) name val)))))
+
+(define _hs-dom-watchers (list))
+
+(define
+ hs-dom-watch!
+ (fn
+ (el name handler)
+ (set! _hs-dom-watchers (cons (list el name handler) _hs-dom-watchers))))
+
+(define
+ hs-dom-fire-watchers!
+ (fn
+ (el name val)
+ (for-each
+ (fn
+ (entry)
+ (when
+ (and
+ (= (nth entry 1) name)
+ (hs-dom-is-ancestor? el (nth entry 0)))
+ ((nth entry 2) val)))
+ _hs-dom-watchers)))
+
+(define
+ hs-dom-is-ancestor?
+ (fn
+ (a b)
+ (cond
+ ((nil? b) false)
+ ((= a b) true)
+ (true (hs-dom-is-ancestor? a (dom-parent b))))))
diff --git a/spec/tests/test-framework.sx b/spec/tests/test-framework.sx
index 3a80ca03..00fc9a5f 100644
--- a/spec/tests/test-framework.sx
+++ b/spec/tests/test-framework.sx
@@ -18,11 +18,18 @@
;; 1. Test framework macros
;; --------------------------------------------------------------------------
-(defmacro deftest (name &rest body)
- `(let ((result (try-call (fn () ,@body))))
- (if (get result "ok")
- (report-pass ,name)
- (report-fail ,name (get result "error")))))
+(defmacro
+ deftest
+ (name &rest body)
+ (quasiquote
+ (when
+ (test-allowed? (unquote name))
+ (let
+ ((result (try-call (fn () (splice-unquote body)))))
+ (if
+ (get result "ok")
+ (report-pass (unquote name))
+ (report-fail (unquote name) (get result "error")))))))
(defmacro defsuite (name &rest items)
`(do (push-suite ,name)
diff --git a/spec/tests/test-hs-diag.sx b/spec/tests/test-hs-diag.sx
index e1ec1316..2ceef447 100644
--- a/spec/tests/test-hs-diag.sx
+++ b/spec/tests/test-hs-diag.sx
@@ -4,7 +4,9 @@
"put into #id compiled"
(let
((sx (hs-to-sx-from-source "on click put \"foo\" into #d1")))
- (assert= (serialize sx) "SHOW")))
+ (assert=
+ (serialize sx)
+ "(hs-on me \"click\" (fn (event) (hs-set-inner-html! (hs-query-first \"#d1\") \"foo\")))")))
(deftest
"put into #id works"
(let
diff --git a/spec/tests/test-hyperscript-behavioral.sx b/spec/tests/test-hyperscript-behavioral.sx
index 05dbe9ec..cc6186ab 100644
--- a/spec/tests/test-hyperscript-behavioral.sx
+++ b/spec/tests/test-hyperscript-behavioral.sx
@@ -1,5 +1,5 @@
;; Hyperscript behavioral tests — auto-generated from upstream _hyperscript test suite
-;; Source: spec/tests/hyperscript-upstream-tests.json (831 tests, v0.9.14 + dev)
+;; Source: spec/tests/hyperscript-upstream-tests.json (1496 tests, v0.9.14 + dev)
;; DO NOT EDIT — regenerate with: python3 tests/playwright/generate-sx-tests.py
;; ── Test helpers ──────────────────────────────────────────────────
@@ -16,7 +16,7 @@
(fn ()
(dom-set-inner-html (dom-body) "")))
-;; Evaluate a hyperscript expression and return its result.
+;; Evaluate a hyperscript expression and return the last-expression value.
;; Compiles the expression, wraps in a thunk, evaluates, returns result.
(define eval-hs
(fn (src)
@@ -32,6 +32,28 @@
(raise _e))))
(handler nil))))))
+;; Evaluate a hyperscript expression with locals. bindings = list of (symbol value).
+;; The locals are injected as fn params so they resolve in the handler body.
+(define eval-hs-locals
+ (fn (src bindings)
+ (let ((sx (hs-to-sx (hs-compile src))))
+ (let ((names (map (fn (b) (first b)) bindings))
+ (vals (map (fn (b) (nth b 1)) bindings)))
+ (let ((param-list (cons (quote me) names)))
+ (let ((wrapper (list (quote fn) param-list
+ (list (quote let)
+ (list (list (quote it) nil) (list (quote event) nil))
+ sx (quote it)))))
+ (let ((handler (eval-expr-cek wrapper)))
+ (guard
+ (_e
+ (true
+ (if
+ (and (list? _e) (= (first _e) "hs-return"))
+ (nth _e 1)
+ (raise _e))))
+ (apply handler (cons nil vals))))))))))
+
;; Evaluate with a specific me value (for "I am between" etc.)
(define eval-hs-with-me
(fn (src me-val)
@@ -49,6 +71,24 @@
;; ── add (19 tests) ──
(defsuite "hs-upstream-add"
+ (deftest "can add a value to a set"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set :s to [] as Set then add 'a' to :s then add 'b' to :s then add 'a' to :s then put :s.size into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "2")
+ ))
+ (deftest "can add a value to an array"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set :arr to [1,2,3] then add 4 to :arr then put :arr as String into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "1,2,3,4")
+ ))
(deftest "can add class ref on a single div"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
@@ -58,15 +98,6 @@
(dom-dispatch _el-div "click" nil)
(assert (dom-has-class? _el-div "foo"))
))
- (deftest "can add class ref w/ double dash on a single div"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add .foo--bar")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-div "foo--bar"))
- ))
(deftest "can add class ref on a single form"
(hs-cleanup!)
(let ((_el-form (dom-create-element "form")))
@@ -76,50 +107,23 @@
(dom-dispatch _el-form "click" nil)
(assert (dom-has-class? _el-form "foo"))
))
- (deftest "can target another div for class ref"
- (hs-cleanup!)
- (let ((_el-bar (dom-create-element "div")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-bar "id" "bar")
- (dom-set-attr _el-div "_" "on click add .foo to #bar")
- (dom-append (dom-body) _el-bar)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-bar "foo"))
- (assert (not (dom-has-class? _el-div "foo")))
- ))
- (deftest "can add to query in me"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
- (dom-set-attr _el-div "_" "on click add .foo to in me")
- (dom-set-attr _el-p1 "id" "p1")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-p1)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-p1 "foo"))
- (assert (not (dom-has-class? _el-div "foo")))
- ))
- (deftest "can add to children"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
- (dom-set-attr _el-div "_" "on click add .foo to my children")
- (dom-set-attr _el-p1 "id" "p1")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-p1)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-p1 "foo"))
- (assert (not (dom-has-class? _el-div "foo")))
- ))
- (deftest "can add non-class attributes"
+ (deftest "can add class ref w/ double dash on a single div"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add [@foo=\"bar\"]")
+ (dom-set-attr _el-div "_" "on click add .foo--bar")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
- (assert= (dom-get-attr _el-div "foo") "bar")
+ (assert (dom-has-class? _el-div "foo--bar"))
+ ))
+ (deftest "can add class refs w/ colons and dashes"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click add .foo:bar-doh")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "foo:bar-doh"))
))
(deftest "can add css properties"
(hs-cleanup!)
@@ -129,18 +133,8 @@
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "color") "red")
- (assert= (dom-get-style _el-div "fontFamily") "monospace")
- ))
- (deftest "can add templated css properties"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add {color: ${\"red\"};}")
- (dom-set-attr _el-div "style" "color: blue")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "color") "red")
+ (assert= (dom-get-style _el-div "color") "")
+ (assert= (dom-get-style _el-div "font-family") "monospace")
))
(deftest "can add multiple class refs"
(hs-cleanup!)
@@ -152,62 +146,116 @@
(assert (dom-has-class? _el-div "foo"))
(assert (dom-has-class? _el-div "bar"))
))
- (deftest "can add class refs w/ colons and dashes"
+ (deftest "can add non-class attributes"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add .foo:bar-doh")
+ (dom-set-attr _el-div "_" "on click add [@foo=\"bar\"]")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-div "foo:bar-doh"))
+ (assert= (dom-get-attr _el-div "foo") "bar")
))
- (deftest "can filter class addition via the when clause"
+ (deftest "can add templated css properties"
(hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add .rey to .bar when it matches .doh")
- (dom-add-class _el-div1 "bar")
- (dom-add-class _el-div2 "bar")
- (dom-add-class _el-div2 "doh")
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click add {color: ${}{\"red\"};}")
+ (dom-set-attr _el-div "style" "color: blue")
(dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (dom-append (dom-body) _el-div2)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
- (assert (not (dom-has-class? _el-div1 "rey")))
- (assert (dom-has-class? _el-div2 "rey"))
- ))
- (deftest "can filter property addition via the when clause"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add @rey to .bar when it matches .doh")
- (dom-add-class _el-div1 "bar")
- (dom-add-class _el-div2 "bar")
- (dom-add-class _el-div2 "doh")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (not (dom-has-attr? _el-div1 "rey")))
- (assert (dom-has-attr? _el-div2 "rey"))
+ (assert= (dom-get-style _el-div "color") "")
))
(deftest "can add to an HTMLCollection"
(hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-bar (dom-create-element "div")) (_el-c1 (dom-create-element "div")) (_el-c2 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add .foo to the children of #bar")
+ (let ((_el-trigger (dom-create-element "div")) (_el-bar (dom-create-element "div")) (_el-c1 (dom-create-element "div")) (_el-c2 (dom-create-element "div")))
+ (dom-set-attr _el-trigger "id" "trigger")
+ (dom-set-attr _el-trigger "_" "on click add .foo to the children of #bar")
(dom-set-attr _el-bar "id" "bar")
(dom-set-attr _el-c1 "id" "c1")
(dom-set-attr _el-c2 "id" "c2")
- (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-trigger)
(dom-append (dom-body) _el-bar)
(dom-append _el-bar _el-c1)
(dom-append _el-bar _el-c2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip byId("c1").classList.contains("foo").should.equal(false)
- ;; SKIP check: skip byId("c2").classList.contains("foo").should.equal(false)
- ;; SKIP check: skip byId("c1").classList.contains("foo").should.equal(true)
- ;; SKIP check: skip byId("c2").classList.contains("foo").should.equal(true)
+ (hs-activate! _el-trigger)
+ (dom-dispatch (dom-query-by-id "trigger") "click" nil)
+ (assert (dom-has-class? (dom-query-by-id "c1") "foo"))
+ (assert (dom-has-class? (dom-query-by-id "c2") "foo"))
+ ))
+ (deftest "can add to children"
+ (hs-cleanup!)
+ (let ((_el-outer (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
+ (dom-set-attr _el-outer "id" "outer")
+ (dom-set-attr _el-outer "_" "on click add .foo to my children")
+ (dom-set-attr _el-p1 "id" "p1")
+ (dom-append (dom-body) _el-outer)
+ (dom-append _el-outer _el-p1)
+ (hs-activate! _el-outer)
+ (dom-dispatch (dom-query-by-id "outer") "click" nil)
+ (assert (dom-has-class? (dom-query-by-id "p1") "foo"))
+ (assert (not (dom-has-class? (dom-query-by-id "outer") "foo")))
+ ))
+ (deftest "can add to query in me"
+ (hs-cleanup!)
+ (let ((_el-outer (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
+ (dom-set-attr _el-outer "id" "outer")
+ (dom-set-attr _el-outer "_" "on click add .foo to in me")
+ (dom-set-attr _el-p1 "id" "p1")
+ (dom-append (dom-body) _el-outer)
+ (dom-append _el-outer _el-p1)
+ (hs-activate! _el-outer)
+ (dom-dispatch (dom-query-by-id "outer") "click" nil)
+ (assert (dom-has-class? (dom-query-by-id "p1") "foo"))
+ (assert (not (dom-has-class? (dom-query-by-id "outer") "foo")))
+ ))
+ (deftest "can filter class addition via the when clause"
+ (hs-cleanup!)
+ (let ((_el-trigger (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-trigger "id" "trigger")
+ (dom-set-attr _el-trigger "_" "on click add .rey to .bar when it matches .doh")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "bar")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "bar")
+ (dom-add-class _el-d3 "doh")
+ (dom-append (dom-body) _el-trigger)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-d3)
+ (hs-activate! _el-trigger)
+ (dom-dispatch (dom-query-by-id "trigger") "click" nil)
+ (assert (not (dom-has-class? (dom-query-by-id "d2") "rey")))
+ (assert (dom-has-class? (dom-query-by-id "d3") "rey"))
+ ))
+ (deftest "can filter property addition via the when clause"
+ (hs-cleanup!)
+ (let ((_el-trigger (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-trigger "id" "trigger")
+ (dom-set-attr _el-trigger "_" "on click add @rey to .bar when it matches .doh")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "bar")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "bar")
+ (dom-add-class _el-d3 "doh")
+ (dom-append (dom-body) _el-trigger)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-d3)
+ (hs-activate! _el-trigger)
+ (dom-dispatch (dom-query-by-id "trigger") "click" nil)
+ (assert (not (dom-has-attr? (dom-query-by-id "d2") "rey")))
+ (assert (dom-has-attr? (dom-query-by-id "d3") "rey"))
+ ))
+ (deftest "can target another div for class ref"
+ (hs-cleanup!)
+ (let ((_el-bar (dom-create-element "div")) (_el-trigger (dom-create-element "div")))
+ (dom-set-attr _el-bar "id" "bar")
+ (dom-set-attr _el-trigger "id" "trigger")
+ (dom-set-attr _el-trigger "_" "on click add .foo to #bar")
+ (dom-append (dom-body) _el-bar)
+ (dom-append (dom-body) _el-trigger)
+ (hs-activate! _el-trigger)
+ (dom-dispatch (dom-query-by-id "trigger") "click" nil)
+ (assert (dom-has-class? (dom-query-by-id "bar") "foo"))
+ (assert (not (dom-has-class? (dom-query-by-id "trigger") "foo")))
))
(deftest "supports async expressions in when clause"
(hs-cleanup!)
@@ -221,6 +269,22 @@
(dom-dispatch (dom-query-by-id "trigger") "click" nil)
(assert (dom-has-class? (dom-query-by-id "d2") "foo"))
))
+ (deftest "when clause result is empty when nothing matches"
+ (hs-cleanup!)
+ (let ((_el-trigger (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-none (dom-create-element "div")))
+ (dom-set-attr _el-trigger "id" "trigger")
+ (dom-set-attr _el-trigger "_" "on click add .foo to .item when it matches .nope then if the result is empty remove @hidden from #none")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "item")
+ (dom-set-attr _el-none "id" "none")
+ (dom-set-attr _el-none "hidden" "")
+ (dom-append (dom-body) _el-trigger)
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-none)
+ (hs-activate! _el-trigger)
+ (dom-dispatch (dom-query-by-id "trigger") "click" nil)
+ (assert (not (dom-has-attr? (dom-query-by-id "none") "hidden")))
+ ))
(deftest "when clause sets result to matched elements"
(hs-cleanup!)
(let ((_el-trigger (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-none (dom-create-element "div")))
@@ -241,3047 +305,10 @@
(dom-dispatch (dom-query-by-id "trigger") "click" nil)
(assert (not (dom-visible? (dom-query-by-id "none"))))
))
- (deftest "when clause result is empty when nothing matches"
- (hs-cleanup!)
- (let ((_el-trigger (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-none (dom-create-element "div")))
- (dom-set-attr _el-trigger "id" "trigger")
- (dom-set-attr _el-trigger "_" "on click add .foo to .item when it matches .nope then if the result is empty remove @hidden from #none")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-add-class _el-d1 "item")
- (dom-set-attr _el-none "id" "none")
- (dom-set-attr _el-none "hidden" "")
- (dom-append (dom-body) _el-trigger)
- (dom-append (dom-body) _el-d1)
- (dom-append (dom-body) _el-none)
- (hs-activate! _el-trigger)
- (dom-dispatch (dom-query-by-id "trigger") "click" nil)
- (assert (not (dom-has-attr? (dom-query-by-id "none") "hidden")))
- ))
- (deftest "can add a value to an array"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set :arr to [1,2,3] then add 4 to :arr then put :arr as String into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "1,2,3,4")
- ))
- (deftest "can add a value to a set"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set :s to [] as Set then add 'a' to :s then add 'b' to :s then add 'a' to :s then put :s.size into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "2")
- ))
-)
-
-;; ── remove (14 tests) ──
-(defsuite "hs-upstream-remove"
- (deftest "can remove class ref on a single div"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-add-class _el-div "foo")
- (dom-set-attr _el-div "_" "on click remove .foo")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (not (dom-has-class? _el-div "foo")))
- ))
- (deftest "can remove class ref on a single form"
- (hs-cleanup!)
- (let ((_el-form (dom-create-element "form")))
- (dom-add-class _el-form "foo")
- (dom-set-attr _el-form "_" "on click remove .foo")
- (dom-append (dom-body) _el-form)
- (hs-activate! _el-form)
- (dom-dispatch _el-form "click" nil)
- (assert (not (dom-has-class? _el-form "foo")))
- ))
- (deftest "can target another div for class ref"
- (hs-cleanup!)
- (let ((_el-bar (dom-create-element "div")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-bar "id" "bar")
- (dom-add-class _el-bar "foo")
- (dom-set-attr _el-div "_" "on click remove .foo from #bar")
- (dom-append (dom-body) _el-bar)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (not (dom-has-class? _el-bar "foo")))
- (assert (not (dom-has-class? _el-div "foo")))
- ))
- (deftest "can remove non-class attributes"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click remove [@foo]")
- (dom-set-attr _el-div "foo" "bar")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (not (dom-has-attr? _el-div "foo")))
- ))
- (deftest "can remove elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click remove me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (nil? (dom-parent _el-div)))
- ))
- (deftest "can remove other elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-that (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click remove #that")
- (dom-set-attr _el-that "id" "that")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-that)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (nil? (dom-parent _el-that)))
- ))
- (deftest "can remove parent element"
- (hs-cleanup!)
- (let ((_el-p1 (dom-create-element "div")) (_el-b1 (dom-create-element "button")))
- (dom-set-attr _el-p1 "id" "p1")
- (dom-set-attr _el-b1 "id" "b1")
- (dom-set-attr _el-b1 "_" "on click remove my.parentElement")
- (dom-append (dom-body) _el-p1)
- (dom-append _el-p1 _el-b1)
- (hs-activate! _el-b1)
- (dom-dispatch _el-p1 "click" nil)
- (assert (nil? (dom-parent _el-p1)))
- ))
- (deftest "can remove multiple class refs"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-add-class _el-div "foo")
- (dom-add-class _el-div "bar")
- (dom-add-class _el-div "doh")
- (dom-set-attr _el-div "_" "on click remove .foo .bar")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (not (dom-has-class? _el-div "foo")))
- (assert (not (dom-has-class? _el-div "bar")))
- (assert (dom-has-class? _el-div "doh"))
- ))
- (deftest "can remove query refs from specific things"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-p3 (dom-create-element "p")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click remove from me")
- (dom-set-inner-html _el-p "foo")
- (dom-set-inner-html _el-p3 "doh")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-d1)
- (dom-append _el-d1 _el-p)
- (dom-append _el-div _el-p3)
- (hs-activate! _el-d1)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip div.innerHTML.includes("foo").should.equal(true)
- ;; SKIP check: skip div.innerHTML.includes("bar").should.equal(true)
- ;; SKIP check: skip div.innerHTML.includes("doh").should.equal(true)
- ;; SKIP check: skip div.innerHTML.includes("foo").should.equal(false)
- ))
- (deftest "can filter class removal via the when clause"
- (hs-cleanup!)
- (let ((_el-trigger (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-trigger "id" "trigger")
- (dom-set-attr _el-trigger "_" "on click remove .highlight from .item when it matches .old")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-add-class _el-d1 "item")
- (dom-add-class _el-d1 "old")
- (dom-add-class _el-d1 "highlight")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-add-class _el-d2 "item")
- (dom-add-class _el-d2 "highlight")
- (dom-append (dom-body) _el-trigger)
- (dom-append (dom-body) _el-d1)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-trigger)
- (dom-dispatch (dom-query-by-id "trigger") "click" nil)
- (assert (not (dom-has-class? (dom-query-by-id "d1") "highlight")))
- (assert (dom-has-class? (dom-query-by-id "d2") "highlight"))
- ))
- (deftest "can remove CSS properties"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click remove {color} from me")
- (dom-set-attr _el-div "style" "color: red; font-weight: bold;")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ))
- (deftest "can remove multiple CSS properties"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click remove {color; font-weight} from me")
- (dom-set-attr _el-div "style" "color: red; font-weight: bold; opacity: 0.5;")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ))
- (deftest "can remove a value from an array"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set :arr to [1,2,3,4] then remove 3 from :arr then put :arr as String into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "1,2,4")
- ))
- (deftest "can remove a value from a set"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set :s to ['a','b','c'] as Set then remove 'b' from :s then put :s.size into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "2")
- ))
-)
-
-;; ── toggle (30 tests) ──
-(defsuite "hs-upstream-toggle"
- (deftest "can toggle class ref on a single div"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle .foo")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (assert (not (dom-has-class? _el-div "foo")))
- ))
- (deftest "can toggle class ref on a single form"
- (hs-cleanup!)
- (let ((_el-form (dom-create-element "form")))
- (dom-set-attr _el-form "_" "on click toggle .foo")
- (dom-append (dom-body) _el-form)
- (hs-activate! _el-form)
- (dom-dispatch _el-form "click" nil)
- (dom-dispatch _el-form "click" nil)
- (assert (not (dom-has-class? _el-form "foo")))
- ))
- (deftest "can target another div for class ref toggle"
- (hs-cleanup!)
- (let ((_el-bar (dom-create-element "div")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-bar "id" "bar")
- (dom-set-attr _el-div "_" "on click toggle .foo on #bar")
- (dom-append (dom-body) _el-bar)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (assert (not (dom-has-class? _el-bar "foo")))
- (assert (not (dom-has-class? _el-div "foo")))
- ))
- (deftest "can toggle non-class attributes"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle [@foo=\"bar\"]")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (assert (not (dom-has-attr? _el-div "foo")))
- ))
- (deftest "can toggle non-class attributes on selects"
- (hs-cleanup!)
- (let ((_el-select (dom-create-element "select")))
- (dom-set-attr _el-select "_" "on click toggle [@foo=\"bar\"]")
- (dom-append (dom-body) _el-select)
- (hs-activate! _el-select)
- (dom-dispatch _el-select "click" nil)
- (dom-dispatch _el-select "click" nil)
- (assert (not (dom-has-attr? _el-select "foo")))
- ))
- (deftest "can toggle for a fixed amount of time"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle .foo for 10ms")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (not (dom-has-class? _el-div "foo")))
- ))
- (deftest "can toggle until an event on another element"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-div "_" "on click toggle .foo until foo from #d1")
- (dom-append (dom-body) _el-d1)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-d1 "foo" nil)
- (assert (not (dom-has-class? _el-div "foo")))
- ))
- (deftest "can toggle between two classes"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-add-class _el-div "foo")
- (dom-set-attr _el-div "_" "on click toggle between .foo and .bar")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-div "foo"))
- (assert (not (dom-has-class? _el-div "bar")))
- ))
- (deftest "can toggle multiple class refs"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-add-class _el-div "bar")
- (dom-set-attr _el-div "_" "on click toggle .foo .bar")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (assert (not (dom-has-class? _el-div "foo")))
- (assert (dom-has-class? _el-div "bar"))
- ))
- (deftest "can toggle display"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle *display")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div.display
- ))
- (deftest "can toggle opacity"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle *opacity")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div.opacity
- ))
- (deftest "can toggle opacity"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle *visibility")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div.visibility
- ))
- (deftest "can toggle display w/ my"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle my *display")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div.display
- ))
- (deftest "can toggle display w/ my"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle my *opacity")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div.opacity
- ))
- (deftest "can toggle display w/ my"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle my *visibility")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div.visibility
- ))
- (deftest "can toggle display on other elt"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle the *display of #d2")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div2.display
- ))
- (deftest "can toggle display on other elt"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle the *opacity of #d2")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div2.opacity
- ))
- (deftest "can toggle display on other elt"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle the *visibility of #d2")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div2.visibility
- ))
- (deftest "can toggle crazy tailwinds class ref on a single form"
- (hs-cleanup!)
- (let ((_el-form (dom-create-element "form")))
- (dom-set-attr _el-form "_" "on click toggle .group-[:nth-of-type(3)_&]:block")
- (dom-append (dom-body) _el-form)
- (hs-activate! _el-form)
- (dom-dispatch _el-form "click" nil)
- (dom-dispatch _el-form "click" nil)
- (assert (not (dom-has-class? _el-form "group-[:nth-of-type(3)_&]:block")))
- ))
- (deftest "can toggle between two attribute values"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle between [@data-state='active'] and [@data-state='inactive']")
- (dom-set-attr _el-div "data-state" "active")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-attr _el-div "data-state") "inactive")
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-attr _el-div "data-state") "active")
- ))
- (deftest "can toggle between different attributes"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle between [@enabled='true'] and [@disabled='true']")
- (dom-set-attr _el-div "enabled" "true")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-attr _el-div "disabled") "true")
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-attr _el-div "enabled") "true")
- ))
- (deftest "can toggle visibility"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle *visibility")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "visibility") "hidden")
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "visibility") "visible")
- ))
- (deftest "can toggle opacity w/ my"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle my *opacity")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "opacity") "0")
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "opacity") "1")
- ))
- (deftest "can toggle visibility w/ my"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle my *visibility")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "visibility") "hidden")
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "visibility") "visible")
- ))
- (deftest "can toggle opacity on other elt"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle the *opacity of #d2")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style (dom-query-by-id "d2") "opacity") "0")
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style (dom-query-by-id "d2") "opacity") "1")
- ))
- (deftest "can toggle visibility on other elt"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle the *visibility of #d2")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style (dom-query-by-id "d2") "visibility") "hidden")
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style (dom-query-by-id "d2") "visibility") "visible")
- ))
- (deftest "can toggle *display between two values"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle *display of me between 'none' and 'flex'")
- (dom-set-attr _el-div "style" "display:none")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "display") "flex")
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "display") "none")
- ))
- (deftest "can toggle *opacity between three values"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle *opacity of me between '0', '0.5' and '1'")
- (dom-set-attr _el-div "style" "opacity:0")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "opacity") "0.5")
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "opacity") "1")
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "opacity") "0")
- ))
- (deftest "can toggle a global variable between two values"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle $mode between 'edit' and 'preview'")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- ))
- (deftest "can toggle a global variable between three values"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click toggle $state between 'a', 'b' and 'c'")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- ))
-)
-
-;; ── set (25 tests) ──
-(defsuite "hs-upstream-set"
- (deftest "can set properties"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click set #d1.innerHTML to \"foo\"")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "foo")
- ))
- (deftest "can set indirect properties"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click set innerHTML of #d1 to \"foo\"")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "foo")
- ))
- (deftest "can set complex indirect properties lhs"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set parentNode.innerHTML of #d1 to \"foo\"")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-d1)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "can set complex indirect properties rhs"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set innerHTML of #d1.parentNode to \"foo\"")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-d1)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "can set chained indirect properties"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set the innerHTML of the parentNode of #d1 to \"foo\"")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-d1)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "can set styles"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set my.style.color to \"red\"")
- (dom-set-inner-html _el-div "lolwat")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "color") "red")
- ))
- (deftest "can set javascript globals"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set window.temp to \"red\"")
- (dom-set-inner-html _el-div "lolwat")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip window["temp"].should.equal("red")
- ))
- (deftest "can set local variables"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click set newVar to \"foo\" then put newVar into #d1.innerHTML")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "foo")
- ))
- (deftest "can set into id ref"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click set #d1.innerHTML to \"foo\"")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "foo")
- ))
- (deftest "can set into class ref"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click set .divs.innerHTML to \"foo\"")
- (dom-add-class _el-div1 "divs")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- (assert= (dom-inner-html _el-div1) "foo")
- ))
- (deftest "can set into attribute ref"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click set @bar to \"foo\"")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-attr _el-div "bar") "foo")
- ))
- (deftest "can set into indirect attribute ref"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click set #div2's @bar to 'foo'")
- (dom-set-attr _el-div2 "id" "div2")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-attr _el-div2 "bar") "foo")
- ))
- (deftest "can set into indirect attribute ref 2"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click set #div2's @bar to 'foo'")
- (dom-set-attr _el-div2 "id" "div2")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-attr _el-div2 "bar") "foo")
- ))
- (deftest "can set into indirect attribute ref 3"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click set @bar of #div2 to 'foo'")
- (dom-set-attr _el-div2 "id" "div2")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-attr _el-div2 "bar") "foo")
- ))
- (deftest "can set into style ref"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click set *color to \"red\"")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip d1.style["color"].should.equal("red")
- ))
- (deftest "can set into indirect style ref"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click set #div2's *color to 'red'")
- (dom-set-attr _el-div2 "id" "div2")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip d2.style["color"].should.equal("red")
- ))
- (deftest "can set into indirect style ref 2"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click set #div2's *color to 'red'")
- (dom-set-attr _el-div2 "id" "div2")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip d2.style["color"].should.equal("red")
- ))
- (deftest "can set into indirect style ref 3"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click set *color of #div2 to 'red'")
- (dom-set-attr _el-div2 "id" "div2")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip d2.style["color"].should.equal("red")
- ))
- (deftest "set waits on promises"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click set #d1.innerHTML to promiseAString()")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "foo")
- ))
- (deftest "can set many properties at once with object literal"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set {bar: 2, baz: 3} on obj")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ;; SKIP check: skip obj.should.deep.equal({ foo: 1, bar: 2, baz: 3 })
- ))
- (deftest "can set props w/ array access syntax"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set my style[\"color\"] to \"red\"")
- (dom-set-inner-html _el-div "lolwat")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "color") "red")
- ))
- (deftest "can set props w/ array access syntax and var"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set foo to \"color\" then set my style[foo] to \"red\"")
- (dom-set-inner-html _el-div "lolwat")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "color") "red")
- ))
- (deftest "can set arrays w/ array access syntax"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set arr to [1, 2, 3] set arr[0] to \"red\" set my *color to arr[0]")
- (dom-set-inner-html _el-div "lolwat")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "color") "red")
- ))
- (deftest "can set arrays w/ array access syntax and var"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set arr to [1, 2, 3] set idx to 0 set arr[idx] to \"red\" set my *color to arr[0]")
- (dom-set-inner-html _el-div "lolwat")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "color") "red")
- ))
- (deftest "handles set url regression properly"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set trackingcode to `foo` then set pdfurl to `https://yyy.xxxxxx.com/path/out/${trackingcode}.pdf` then put pdfurl into me")
- (dom-set-inner-html _el-div "lolwat")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip d1.innerText.should.equal("https://yyy.xxxxxx.com/path/out/f
- ))
-)
-
-;; ── put (38 tests) ──
-(defsuite "hs-upstream-put"
- (deftest "can set properties"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put \"foo\" into #d1.innerHTML")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "foo")
- ))
- (deftest "can put directly into nodes"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put \"foo\" into #d1")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-text-content _el-d1) "foo")
- ))
- (deftest "can put nodes into nodes"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-set-attr _el-d2 "_" "on click put #d1 into #d2")
- (dom-append (dom-body) _el-d1)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-d2)
- (dom-dispatch _el-d2 "click" nil)
- ;; SKIP check: skip d2.firstChild.should.equal(d1)
- ))
- (deftest "can put directly into symbols"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click put \"foo\" into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "me symbol doesn't get stomped on direct write"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click put \"foo\" into me then put \"bar\" into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "bar")
- ))
- (deftest "can set styles"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click put \"red\" into my.style.color")
- (dom-set-inner-html _el-div "lolwat")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "color") "red")
- ))
- (deftest "can set javascript globals"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click put \"red\" into window.temp")
- (dom-set-inner-html _el-div "lolwat")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip window["temp"].should.equal("red")
- ))
- (deftest "can set into class ref w/ flatmapped property"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-div4 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click put \"foo\" into .divs.parentElement.innerHTML")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-add-class _el-div2 "divs")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-add-class _el-div4 "divs")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-d1)
- (dom-append _el-d1 _el-div2)
- (dom-append (dom-body) _el-d2)
- (dom-append _el-d2 _el-div4)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-d1) "foo")
- (assert= (dom-text-content _el-d2) "foo")
- ))
- (deftest "can set into class ref w/ flatmapped property using of"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-div4 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click put \"foo\" into innerHTML of parentElement of .divs")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-add-class _el-div2 "divs")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-add-class _el-div4 "divs")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-d1)
- (dom-append _el-d1 _el-div2)
- (dom-append (dom-body) _el-d2)
- (dom-append _el-d2 _el-div4)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-d1) "foo")
- (assert= (dom-text-content _el-d2) "foo")
- ))
- (deftest "can set local variables"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put \"foo\" into newVar then put newVar into #d1.innerHTML")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "foo")
- ))
- (deftest "can set into id ref"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put \"foo\" into #d1.innerHTML")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "foo")
- ))
- (deftest "can insert before"
- (hs-cleanup!)
- (let ((_el-d2 (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d2 "id" "d2")
- (dom-set-attr _el-d2 "_" "on click put #d1 before #d2")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-inner-html _el-d1 "foo")
- (dom-append (dom-body) _el-d2)
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d2)
- (dom-dispatch _el-d2 "click" nil)
- ;; SKIP check: skip d2.previousSibling.textContent.should.equal("foo")
- ))
- (deftest "can insert after"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-inner-html _el-d1 "foo")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-set-attr _el-d2 "_" "on click put #d1 after #d2")
- (dom-append (dom-body) _el-d1)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-d2)
- (dom-dispatch _el-d2 "click" nil)
- ;; SKIP check: skip d2.nextSibling.textContent.should.equal("foo")
- ))
- (deftest "can insert after beginning"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put \"foo\" at start of #d1")
- (dom-set-inner-html _el-d1 "*")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-text-content _el-d1) "foo*")
- ))
- (deftest "can insert before end"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put \"foo\" at end of #d1")
- (dom-set-inner-html _el-d1 "*")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-text-content _el-d1) "*foo")
- ))
- (deftest "can set into attribute ref"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click put \"foo\" into @bar")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-attr _el-div "bar") "foo")
- ))
- (deftest "can set into indirect attribute ref"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click put \"foo\" into my @bar")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-attr _el-div "bar") "foo")
- ))
- (deftest "can set into indirect attribute ref 2"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click put 'foo' into #div2's @bar")
- (dom-set-attr _el-div2 "id" "div2")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-attr _el-div2 "bar") "foo")
- ))
- (deftest "can set into indirect attribute ref 3"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click put 'foo' into @bar of #div2")
- (dom-set-attr _el-div2 "id" "div2")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-attr _el-div2 "bar") "foo")
- ))
- (deftest "can set into style ref"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click put \"red\" into *color")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip d1.style["color"].should.equal("red")
- ))
- (deftest "can set into indirect style ref"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click put \"red\" into my *color")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip d1.style["color"].should.equal("red")
- ))
- (deftest "can set into indirect style ref 2"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click put 'red' into #div2's *color")
- (dom-set-attr _el-div2 "id" "div2")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip d2.style["color"].should.equal("red")
- ))
- (deftest "can set into indirect style ref 3"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click put 'red' into the *color of #div2")
- (dom-set-attr _el-div2 "id" "div2")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip d2.style["color"].should.equal("red")
- ))
- (deftest "waits on promises"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put promiseAString() into #d1.innerHTML")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "foo")
- ))
- (deftest "can put properties w/ array access syntax"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click put \"red\" into my style[\"color\"]")
- (dom-set-inner-html _el-div "lolwat")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "color") "red")
- ))
- (deftest "can put properties w/ array access syntax and var"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set foo to \"color\" then put \"red\" into my style[foo]")
- (dom-set-inner-html _el-div "lolwat")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "color") "red")
- ))
- (deftest "can put array vals w/ array access syntax"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set arr to [1, 2, 3] put \"red\" into arr[0] put arr[0] into my *color")
- (dom-set-inner-html _el-div "lolwat")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "color") "red")
- ))
- (deftest "can put array vals w/ array access syntax and var"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set arr to [1, 2, 3] set idx to 0 put \"red\" into arr[idx] put arr[0] into my *color")
- (dom-set-inner-html _el-div "lolwat")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "color") "red")
- ))
- (deftest "properly processes hyperscript in new content in a symbol write"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click put \"\" into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch (dom-query "button") "click" nil)
- (assert= (dom-inner-html (dom-query "button")) "42")
- ))
- (deftest "properly processes hyperscript in new content in a element target"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put \"\" into ")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (dom-dispatch (dom-query "button") "click" nil)
- (assert= (dom-inner-html (dom-query "button")) "42")
- ))
- (deftest "properly processes hyperscript in before"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put \"\" before me")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (dom-dispatch (dom-query "button") "click" nil)
- (assert= (dom-inner-html (dom-query "button")) "42")
- ))
- (deftest "properly processes hyperscript at start of"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put \"\" at the start of me")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (dom-dispatch (dom-query "button") "click" nil)
- (assert= (dom-inner-html (dom-query "button")) "42")
- ))
- (deftest "properly processes hyperscript at end of"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put \"\" at the end of me")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (dom-dispatch (dom-query "button") "click" nil)
- (assert= (dom-inner-html (dom-query "button")) "42")
- ))
- (deftest "properly processes hyperscript after"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put \"\" after me")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (dom-dispatch (dom-query "button") "click" nil)
- (assert= (dom-inner-html (dom-query "button")) "42")
- ))
- (deftest "is null tolerant"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-add-class _el-div "divs")
- (dom-set-attr _el-div "_" "on click put \"red\" into #a-bad-id-that-does-not-exist")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ))
- (deftest "put null into attribute removes it"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put null into @foo")
- (dom-set-attr _el-d1 "foo" "bar")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch (dom-query-by-id "d1") "click" nil)
- (assert (not (dom-has-attr? (dom-query-by-id "d1") "foo")))
- ))
- (deftest "can put at start of an array"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set :arr to [2,3] then put 1 at start of :arr then put :arr as String into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "1,2,3")
- ))
- (deftest "can put at end of an array"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set :arr to [1,2] then put 3 at end of :arr then put :arr as String into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "1,2,3")
- ))
-)
-
-;; ── hide (14 tests) ──
-(defsuite "hs-upstream-hide"
- (deftest "can hide element with no target"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click hide")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div.display
- ))
- (deftest "hide element then show element retains original display"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click 1 hide on click 2 show")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div.display
- ))
- (deftest "can hide element with no target followed by command"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click hide add .foo")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div.display
- (assert (dom-has-class? _el-div "foo"))
- ))
- (deftest "can hide element with no target followed by then"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click hide then add .foo")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div.display
- (assert (dom-has-class? _el-div "foo"))
- ))
- (deftest "can hide element with no target with a with"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click hide with display then add .foo")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div.display
- (assert (dom-has-class? _el-div "foo"))
- ))
- (deftest "can hide element, with display:none by default"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click hide me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div.display
- ))
- (deftest "can hide element with display:none explicitly"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click hide me with display")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div.display
- ))
- (deftest "can hide element with opacity:0"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click hide me with opacity")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div.opacity
- ))
- (deftest "can hide element with opacity style literal"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click hide me with *opacity")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div.opacity
- ))
- (deftest "can hide element, with visibility:hidden"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click hide me with visibility")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: div.visibility
- ))
- (deftest "can hide other elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")))
- (dom-add-class _el-div "hideme")
- (dom-set-attr _el-div1 "_" "on click hide .hideme")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (hs-activate! _el-div1)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP computed style: hideme.display
- ))
- (deftest "can hide with custom strategy"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click hide with myHide")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ;; SKIP action: classList.remove__foo__
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-div "foo"))
- ))
- (deftest "can set default to custom strategy"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click hide")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ;; SKIP action: classList.remove__foo__
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-div "foo"))
- ))
- (deftest "can filter hide via the when clause"
- (hs-cleanup!)
- (let ((_el-trigger (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-trigger "id" "trigger")
- (dom-set-attr _el-trigger "_" "on click hide in me when it matches .hideable")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-add-class _el-d1 "hideable")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-append (dom-body) _el-trigger)
- (dom-append _el-trigger _el-d1)
- (dom-append _el-trigger _el-d2)
- (hs-activate! _el-trigger)
- (dom-dispatch (dom-query-by-id "trigger") "click" nil)
- (assert= (dom-get-style (dom-query-by-id "d1") "display") "none")
- (assert= (dom-get-style (dom-query-by-id "d2") "display") "block")
- ))
-)
-
-;; ── if (19 tests) ──
-(defsuite "hs-upstream-if"
- (deftest "basic true branch works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if true put \"foo\" into me.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "basic true branch works with multiple commands"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if true log me then put \"foo\" into me.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "basic true branch works with end"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if true put \"foo\" into me.innerHTML end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "basic true branch works with naked else"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if true put \"foo\" into me.innerHTML else")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "basic true branch works with naked else end"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if true put \"foo\" into me.innerHTML else end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "basic else branch works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if false else put \"foo\" into me.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "basic else branch works with end"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if false else put \"foo\" into me.innerHTML end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "basic else if branch works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if false else if true put \"foo\" into me.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "basic else if branch works with end"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if false else if true put \"foo\" into me.innerHTML end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "otherwise alias works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if false otherwise put \"foo\" into me.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "triple else if branch works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if false else if false else put \"foo\" into me.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "triple else if branch works with end"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if false else if false else put \"foo\" into me.innerHTML end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "basic else branch works with multiple commands"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if false put \"bar\" into me.innerHTML else log me then put \"foo\" into me.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "true branch with a wait works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if true wait 10 ms then put \"foo\" into me.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "false branch with a wait works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if false else wait 10 ms then put \"foo\" into me.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "if properly passes execution along if child is not executed"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if false end put \"foo\" into me.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "if properly supports nested if statements and end block"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if window.tmp then put \"foo\" into me then else if not window.tmp then // do nothing then end catch e then // just here for the parsing... then")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "foo")
- ))
- (deftest "if on new line does not join w/ else"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click if window.tmp then else then if window.tmp then end put \"foo\" into me then end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "")
- ))
- (deftest "passes the sieve test"
- (hs-cleanup!))
-)
-
-;; ── repeat (30 tests) ──
-(defsuite "hs-upstream-repeat"
- (deftest "basic for loop works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click repeat for x in [1, 2, 3] put x at end of me end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "123")
- ))
- (deftest "basic for loop with null works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click repeat for x in null put x at end of me end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "")
- ))
- (deftest "waiting in for loop works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click repeat for x in [1, 2, 3] log me then put x at end of me then wait 1ms then end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "123")
- ))
- (deftest "basic raw for loop works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click for x in [1, 2, 3] put x at end of me end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "123")
- ))
- (deftest "basic raw for loop works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click for x in null put x at end of me end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "")
- ))
- (deftest "waiting in raw for loop works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click for x in [1, 2, 3] put x at end of me then wait 1ms then end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "123")
- ))
- (deftest "repeat forever works"
- (hs-cleanup!)
- (eval-expr-cek (hs-to-sx (hs-compile "def repeatForeverWithReturn() set retVal to 0 repeat forever set retVal to retVal + 1 if retVal == 5 then return retVal end end end")))
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put repeatForeverWithReturn() into my.innerHTML")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "5")
- ))
- (deftest "repeat forever works w/o keyword"
- (hs-cleanup!)
- (eval-expr-cek (hs-to-sx (hs-compile "def repeatForeverWithReturn() set retVal to 0 repeat set retVal to retVal + 1 if retVal == 5 then return retVal end end end")))
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put repeatForeverWithReturn() into my.innerHTML")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "5")
- ))
- (deftest "basic in loop works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click repeat in [1, 2, 3] put it at end of me end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "123")
- ))
- (deftest "index syntax works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click repeat for x in [\"a\", \"ab\", \"abc\"] index i then put x + i at end of me end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "a0ab1abc2")
- ))
- (deftest "indexed by syntax works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click repeat for x in [\"a\", \"ab\", \"abc\"] indexed by i then put x + i at end of me end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "a0ab1abc2")
- ))
- (deftest "while keyword works"
- (hs-cleanup!)
- (eval-expr-cek (hs-to-sx (hs-compile "def repeatWhileTest() set retVal to 0 repeat while retVal < 5 set retVal to retVal + 1 end return retVal end")))
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put repeatWhileTest() into my.innerHTML")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "5")
- ))
- (deftest "until keyword works"
- (hs-cleanup!)
- (eval-expr-cek (hs-to-sx (hs-compile "def repeatUntilTest() set retVal to 0 repeat until retVal == 5 set retVal to retVal + 1 end return retVal end")))
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put repeatUntilTest() into my.innerHTML")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "5")
- ))
- (deftest "until event keyword works"
- (hs-cleanup!)
- (eval-expr-cek (hs-to-sx (hs-compile "def repeatUntilTest() repeat until event click from #untilTest wait 2ms end return 42 end")))
- (let ((_el-untilTest (dom-create-element "div")))
- (dom-set-attr _el-untilTest "id" "untilTest")
- (dom-append (dom-body) _el-untilTest)
- (dom-dispatch _el-untilTest "click" nil)
- ;; SKIP check: skip value.should.equal(42)
- ))
- (deftest "only executes the init expression once"
- (hs-cleanup!)
- (eval-expr-cek (hs-to-sx (hs-compile "def getArray() set window.called to (window.called or 0) + 1 return [1, 2, 3] end")))
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click for x in getArray() put x into my.innerHTML end")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "3")
- ;; SKIP check: skip window.called.should.equal(1)
- ))
- (deftest "can nest loops"
- (hs-cleanup!)
- (eval-expr-cek (hs-to-sx (hs-compile "def sprayInto(elt) for x in [1, 2, 3] for y in [1, 2, 3] put x * y at end of elt end end end")))
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click call sprayInto(me)")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "123246369")
- ))
- (deftest "basic times loop works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click repeat 3 times put \"a\" at end of me end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "aaa")
- ))
- (deftest "times loop with expression works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click repeat 3 + 3 times put \"a\" at end of me end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "aaaaaa")
- ))
- (deftest "loop continue works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click repeat 2 times for x in ['A', 'B', 'C', 'D'] if (x != 'D') then put 'success ' + x + '. ' at end of me then continue then put 'FAIL!!. ' at end of me then end put 'expected D. ' at end of me then end end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "success A. success B. success C. expected D. success A. success B. success C. expected D. ")
- ))
- (deftest "loop break works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click repeat 2 times for x in ['A', 'B', 'C', 'D'] if x is 'C' then break then end put x at end of me then end end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "ABAB")
- ))
- (deftest "basic raw for loop with null works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click for x in null put x at end of me end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "")
- ))
- (deftest "basic property for loop works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set x to {foo:1, bar:2, baz:3} then for prop in x put x[prop] at end of me end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "123")
- ))
- (deftest "bottom-tested repeat until"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set x to 0 then repeat then set x to until x is 3 end put x into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "3")
- ))
- (deftest "bottom-tested repeat while"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set x to 0 then repeat then set x to while x < 3 end put x into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "3")
- ))
- (deftest "bottom-tested loop always runs at least once"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set x to 0 then repeat then set x to until true end put x into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "1")
- ))
- (deftest "break exits a simple repeat loop"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set x to 0 then repeat 10 times set x to x + 1 then if x is 3 break end end put x into me then")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "3")
- ))
- (deftest "continue skips rest of iteration in simple repeat loop"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click repeat for x in [1, 2, 3, 4, 5] if x is 3 continue end put x at end of me then end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "1245")
- ))
- (deftest "break exits a for-in loop"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click repeat for x in [1, 2, 3, 4, 5] if x is 4 break end put x at end of me then end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "123")
- ))
- (deftest "break exits a while loop"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set x to 0 then repeat while x < 100 then set x to x + 1 then if x is 5 break end end put x into me then")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "5")
- ))
- (deftest "for loop over undefined skips without error"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click repeat for x in doesNotExist put x at end of me end put \"done\" into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "done")
- ))
-)
-
-;; ── wait (7 tests) ──
-(defsuite "hs-upstream-wait"
- (deftest "can wait on time"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add .foo then wait 20ms then add .bar")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-div "foo"))
- (assert (dom-has-class? _el-div "bar"))
- ))
- (deftest "can wait on event"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add .foo then wait for foo then add .bar")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "foo" nil)
- (assert (dom-has-class? _el-div "foo"))
- (assert (dom-has-class? _el-div "bar"))
- ))
- (deftest "waiting on an event sets 'it' to the event"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click wait for foo then put its.detail into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "foo" nil)
- (assert= (dom-inner-html _el-div) "hyperscript is hyper cool")
- ))
- (deftest "can destructure properties in a wait"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click wait for foo(bar) then put bar into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "foo" nil)
- (assert= (dom-inner-html _el-div) "bar")
- ))
- (deftest "can wait on event on another element"
- (hs-cleanup!)
- (let ((_el-d2 (dom-create-element "div")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-d2 "id" "d2")
- (dom-set-attr _el-div "_" "on click add .foo then wait for foo from #d2 then add .bar")
- (dom-append (dom-body) _el-d2)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "foo" nil)
- (assert (dom-has-class? _el-div "foo"))
- (assert (dom-has-class? _el-div "bar"))
- ))
- (deftest "can wait on event or timeout 1"
- (hs-cleanup!)
- (let ((_el-d2 (dom-create-element "div")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-d2 "id" "d2")
- (dom-set-attr _el-div "_" "on click add .foo then wait for foo or 0ms then add .bar")
- (dom-append (dom-body) _el-d2)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "foo" nil)
- (assert (dom-has-class? _el-div "foo"))
- (assert (dom-has-class? _el-div "bar"))
- ))
- (deftest "can wait on event or timeout 2"
- (hs-cleanup!)
- (let ((_el-d2 (dom-create-element "div")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-d2 "id" "d2")
- (dom-set-attr _el-div "_" "on click add .foo then wait for foo or 0ms then add .bar")
- (dom-append (dom-body) _el-d2)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "foo" nil)
- (assert (dom-has-class? _el-div "foo"))
- (assert (dom-has-class? _el-div "bar"))
- ))
-)
-
-;; ── send (8 tests) ──
-(defsuite "hs-upstream-send"
- (deftest "can send events"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-bar (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click send foo to #bar")
- (dom-set-attr _el-bar "id" "bar")
- (dom-set-attr _el-bar "_" "on foo add .foo-sent")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-bar)
- (hs-activate! _el-div)
- (hs-activate! _el-bar)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-bar "foo-sent"))
- ))
- (deftest "can reference sender in events"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-bar (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click log 0 send foo to #bar log 3")
- (dom-set-attr _el-bar "id" "bar")
- (dom-set-attr _el-bar "_" "on foo add .foo-sent to sender log 1, me, sender")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-bar)
- (hs-activate! _el-div)
- (hs-activate! _el-bar)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-bar "foo-sent"))
- ))
- (deftest "can send events with args"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-bar (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click send foo(x:42) to #bar")
- (dom-set-attr _el-bar "id" "bar")
- (dom-set-attr _el-bar "_" "on foo put event.detail.x into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-bar)
- (hs-activate! _el-div)
- (hs-activate! _el-bar)
- (dom-dispatch _el-div "click" nil)
- (assert (not (dom-has-class? _el-bar "foo-sent")))
- (assert= (dom-inner-html _el-bar) "42")
- ))
- (deftest "can send events with dots"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-bar (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click send foo.bar to #bar")
- (dom-set-attr _el-bar "id" "bar")
- (dom-set-attr _el-bar "_" "on foo.bar add .foo-sent")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-bar)
- (hs-activate! _el-div)
- (hs-activate! _el-bar)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-bar "foo-sent"))
- ))
- (deftest "can send events with dots with args"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-bar (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click send foo.bar(x:42) to #bar")
- (dom-set-attr _el-bar "id" "bar")
- (dom-set-attr _el-bar "_" "on foo.bar put event.detail.x into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-bar)
- (hs-activate! _el-div)
- (hs-activate! _el-bar)
- (dom-dispatch _el-div "click" nil)
- (assert (not (dom-has-class? _el-bar "foo-sent")))
- (assert= (dom-inner-html _el-bar) "42")
- ))
- (deftest "can send events with colons"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-bar (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click send foo:bar to #bar")
- (dom-set-attr _el-bar "id" "bar")
- (dom-set-attr _el-bar "_" "on foo:bar add .foo-sent")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-bar)
- (hs-activate! _el-div)
- (hs-activate! _el-bar)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-bar "foo-sent"))
- ))
- (deftest "can send events with colons with args"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-bar (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click send foo:bar(x:42) to #bar")
- (dom-set-attr _el-bar "id" "bar")
- (dom-set-attr _el-bar "_" "on foo:bar put event.detail.x into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-bar)
- (hs-activate! _el-div)
- (hs-activate! _el-bar)
- (dom-dispatch _el-div "click" nil)
- (assert (not (dom-has-class? _el-bar "foo-sent")))
- (assert= (dom-inner-html _el-bar) "42")
- ))
- (deftest "can send events to any expression"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-bar (dom-create-element "div")))
- (dom-set-attr _el-div "_" "def bar return #bar on click send foo to bar()")
- (dom-set-attr _el-bar "id" "bar")
- (dom-set-attr _el-bar "_" "on foo add .foo-sent")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-bar)
- (hs-activate! _el-div)
- (hs-activate! _el-bar)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-bar "foo-sent"))
- ))
-)
-
-;; ── take (12 tests) ──
-(defsuite "hs-upstream-take"
- (deftest "can take a class from other elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "div")
- (dom-add-class _el-div "foo")
- (dom-add-class _el-div1 "div")
- (dom-set-attr _el-div1 "_" "on click take .foo from .div")
- (dom-add-class _el-div2 "div")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div1)
- (dom-dispatch _el-div1 "click" nil)
- (assert (not (dom-has-class? _el-div "foo")))
- (assert (dom-has-class? _el-div1 "foo"))
- (assert (not (dom-has-class? _el-div2 "foo")))
- ))
- (deftest "can take a class from other forms"
- (hs-cleanup!)
- (let ((_el-form (dom-create-element "form")) (_el-form1 (dom-create-element "form")) (_el-form2 (dom-create-element "form")))
- (dom-add-class _el-form "div")
- (dom-add-class _el-form "foo")
- (dom-add-class _el-form1 "div")
- (dom-set-attr _el-form1 "_" "on click take .foo from .div")
- (dom-add-class _el-form2 "div")
- (dom-append (dom-body) _el-form)
- (dom-append (dom-body) _el-form1)
- (dom-append (dom-body) _el-form2)
- (hs-activate! _el-form1)
- (dom-dispatch (dom-query-by-id "f2") "click" nil)
- (assert (not (dom-has-class? (dom-query-by-id "f1") "foo")))
- (assert (dom-has-class? (dom-query-by-id "f2") "foo"))
- (assert (not (dom-has-class? (dom-query-by-id "f3") "foo")))
- ))
- (deftest "can take a class for other elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
- (dom-add-class _el-div "div")
- (dom-add-class _el-div "foo")
- (dom-add-class _el-div1 "div")
- (dom-set-attr _el-div1 "_" "on click take .foo from .div for #d3")
- (dom-set-attr _el-d3 "id" "d3")
- (dom-add-class _el-d3 "div")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (dom-append (dom-body) _el-d3)
- (hs-activate! _el-div1)
- (dom-dispatch _el-div1 "click" nil)
- (assert (not (dom-has-class? _el-div "foo")))
- (assert (not (dom-has-class? _el-div1 "foo")))
- (assert (dom-has-class? _el-d3 "foo"))
- ))
- (deftest "a parent can take a class for other elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click take .foo from .div for event.target")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-add-class _el-d1 "div")
- (dom-add-class _el-d1 "foo")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-add-class _el-d2 "div")
- (dom-set-attr _el-d3 "id" "d3")
- (dom-add-class _el-d3 "div")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-d1)
- (dom-append _el-div _el-d2)
- (dom-append _el-div _el-d3)
- (hs-activate! _el-div)
- (dom-dispatch _el-d2 "click" nil)
- (assert (not (dom-has-class? _el-div "foo")))
- (assert (dom-has-class? _el-d2 "foo"))
- (assert (not (dom-has-class? _el-d3 "foo")))
- ))
- (deftest "can take an attribute from other elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "div")
- (dom-set-attr _el-div "data-foo" "bar")
- (dom-add-class _el-div1 "div")
- (dom-set-attr _el-div1 "_" "on click take @data-foo from .div")
- (dom-add-class _el-div2 "div")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div1)
- (dom-dispatch _el-div1 "click" nil)
- (assert= (dom-get-attr _el-div "data-foo") "bar")
- (assert= (dom-get-attr _el-div1 "data-foo") "")
- ;; SKIP check: skip assert.isNull(d2.getAttribute("data-foo")
- ;; SKIP check: skip assert.isNull(d3.getAttribute("data-foo")
- ;; SKIP check: skip assert.isNull(d1.getAttribute("data-foo")
- ))
- (deftest "can take an attribute with specific value from other elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "div")
- (dom-set-attr _el-div "data-foo" "bar")
- (dom-add-class _el-div1 "div")
- (dom-set-attr _el-div1 "_" "on click take @data-foo=baz from .div")
- (dom-add-class _el-div2 "div")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div1)
- (dom-dispatch _el-div1 "click" nil)
- (assert= (dom-get-attr _el-div "data-foo") "bar")
- (assert= (dom-get-attr _el-div1 "data-foo") "baz")
- ;; SKIP check: skip assert.isNull(d2.getAttribute("data-foo")
- ;; SKIP check: skip assert.isNull(d3.getAttribute("data-foo")
- ;; SKIP check: skip assert.isNull(d1.getAttribute("data-foo")
- ))
- (deftest "can take an attribute value from other elements and set specific values instead"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "div")
- (dom-set-attr _el-div "data-foo" "bar")
- (dom-add-class _el-div1 "div")
- (dom-set-attr _el-div1 "_" "on click take @data-foo=baz with \"qux\" from .div")
- (dom-add-class _el-div2 "div")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div1)
- (dom-dispatch _el-div1 "click" nil)
- (assert= (dom-get-attr _el-div "data-foo") "qux")
- (assert= (dom-get-attr _el-div1 "data-foo") "baz")
- (assert= (dom-get-attr _el-div2 "data-foo") "qux")
- ;; SKIP check: skip assert.isNull(d2.getAttribute("data-foo")
- ;; SKIP check: skip assert.isNull(d3.getAttribute("data-foo")
- ))
- (deftest "can take an attribute value from other elements and set value from an expression instead"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "div")
- (dom-set-attr _el-div "data-foo" "bar")
- (dom-add-class _el-div1 "div")
- (dom-set-attr _el-div1 "_" "on click take @data-foo=baz with my @data-foo from .div")
- (dom-set-attr _el-div1 "data-foo" "qux")
- (dom-add-class _el-div2 "div")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div1)
- (dom-dispatch _el-div1 "click" nil)
- (assert= (dom-get-attr _el-div "data-foo") "qux")
- (assert= (dom-get-attr _el-div1 "data-foo") "baz")
- (assert= (dom-get-attr _el-div2 "data-foo") "qux")
- ;; SKIP check: skip assert.isNull(d3.getAttribute("data-foo")
- ))
- (deftest "can take an attribute for other elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
- (dom-add-class _el-div "div")
- (dom-set-attr _el-div "data-foo" "bar")
- (dom-add-class _el-div1 "div")
- (dom-set-attr _el-div1 "_" "on click take @data-foo from .div for #d3")
- (dom-set-attr _el-d3 "id" "d3")
- (dom-add-class _el-d3 "div")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (dom-append (dom-body) _el-d3)
- (hs-activate! _el-div1)
- (dom-dispatch _el-div1 "click" nil)
- (assert= (dom-get-attr _el-div "data-foo") "bar")
- (assert= (dom-get-attr _el-d3 "data-foo") "")
- ;; SKIP check: skip assert.isNull(d2.getAttribute("data-foo")
- ;; SKIP check: skip assert.isNull(d3.getAttribute("data-foo")
- ;; SKIP check: skip assert.isNull(d1.getAttribute("data-foo")
- ))
- (deftest "a parent can take an attribute for other elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click take @data-foo from .div for event.target")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-add-class _el-d1 "div")
- (dom-set-attr _el-d1 "data-foo" "bar")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-add-class _el-d2 "div")
- (dom-set-attr _el-d3 "id" "d3")
- (dom-add-class _el-d3 "div")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-d1)
- (dom-append _el-div _el-d2)
- (dom-append _el-div _el-d3)
- (hs-activate! _el-div)
- (dom-dispatch _el-d2 "click" nil)
- (assert= (dom-get-attr _el-div "data-foo") "bar")
- (assert= (dom-get-attr _el-d2 "data-foo") "")
- ;; SKIP check: skip assert.isNull(d2.getAttribute("data-foo")
- ;; SKIP check: skip assert.isNull(d3.getAttribute("data-foo")
- ;; SKIP check: skip assert.isNull(d1.getAttribute("data-foo")
- ))
- (deftest "can take multiple classes from other elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "div")
- (dom-add-class _el-div "foo")
- (dom-add-class _el-div1 "div")
- (dom-set-attr _el-div1 "_" "on click take .foo .bar")
- (dom-add-class _el-div2 "div")
- (dom-add-class _el-div2 "bar")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div1)
- (dom-dispatch _el-div1 "click" nil)
- (assert (not (dom-has-class? _el-div "foo")))
- (assert (dom-has-class? _el-div1 "foo"))
- (assert (not (dom-has-class? _el-div2 "foo")))
- (assert (not (dom-has-class? _el-div "bar")))
- (assert (dom-has-class? _el-div1 "bar"))
- (assert (not (dom-has-class? _el-div2 "bar")))
- ))
- (deftest "can take multiple classes from specific element"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-add-class _el-div "div1")
- (dom-add-class _el-div "foo")
- (dom-add-class _el-div "bar")
- (dom-add-class _el-div1 "div")
- (dom-set-attr _el-div1 "_" "on click take .foo .bar from .div1")
- (dom-add-class _el-div2 "div")
- (dom-add-class _el-div2 "bar")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div1)
- (dom-dispatch _el-div1 "click" nil)
- (assert (not (dom-has-class? _el-div "foo")))
- (assert (dom-has-class? _el-div1 "foo"))
- (assert (not (dom-has-class? _el-div2 "foo")))
- (assert (not (dom-has-class? _el-div "bar")))
- (assert (dom-has-class? _el-div1 "bar"))
- (assert (dom-has-class? _el-div2 "bar"))
- ))
-)
-
-;; ── transition (23 tests) ──
-(defsuite "hs-upstream-transition"
- (deftest "can transition a single property on current element"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click transition width from 0px to 100px")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "width") "100px")
- ))
- (deftest "can transition with parameterized values"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set startWidth to 0 then set endWidth to 100 transition width from (startWidth)px to (endWidth)px")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "width") "100px")
- ))
- (deftest "can transition a single property on form"
- (hs-cleanup!)
- (let ((_el-form (dom-create-element "form")))
- (dom-set-attr _el-form "_" "on click transition width from 0px to 100px")
- (dom-append (dom-body) _el-form)
- (hs-activate! _el-form)
- (dom-dispatch _el-form "click" nil)
- (assert= (dom-get-style _el-form "width") "100px")
- ))
- (deftest "can transition a single property on current element with the my prefix"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click transition my width from 0px to 100px")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "width") "100px")
- ))
- (deftest "can transition two properties on current element"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click transition width from 0px to 100px height from 0px to 100px")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "width") "100px")
- (assert= (dom-get-style _el-div "height") "100px")
- ))
- (deftest "can transition on another element"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-foo (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click transition element #foo width from 0px to 100px")
- (dom-set-attr _el-foo "id" "foo")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-foo)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-foo "width") "100px")
- ))
- (deftest "can transition on another element no element prefix"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-foo (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click transition #foo width from 0px to 100px")
- (dom-set-attr _el-foo "id" "foo")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-foo)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-foo "width") "100px")
- ))
- (deftest "can transition on another element no element prefix + possessive"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-foo (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click transition #foo's width from 0px to 100px")
- (dom-set-attr _el-foo "id" "foo")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-foo)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-foo "width") "100px")
- ))
- (deftest "can transition on another element no element prefix with it"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-foo (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click get #foo then transition its width from 0px to 100px")
- (dom-set-attr _el-foo "id" "foo")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-foo)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-foo "width") "100px")
- ))
- (deftest "can transition with a custom transition time"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-foo (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click transition element #foo width from 0px to 100px using \"width 2s ease-in\"")
- (dom-set-attr _el-foo "id" "foo")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-foo)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-foo "width") "100px")
- ))
- (deftest "can transition with a custom transition time via the over syntax"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-foo (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click transition element #foo width from 0px to 100px over 2s")
- (dom-set-attr _el-foo "id" "foo")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-foo)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-foo "width") "100px")
- ))
- (deftest "can transition a single property on current element using style ref"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click transition *width from 0px to 100px")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "width") "100px")
- ))
- (deftest "can transition a single property on form using style ref"
- (hs-cleanup!)
- (let ((_el-form (dom-create-element "form")))
- (dom-set-attr _el-form "_" "on click transition *width from 0px to 100px")
- (dom-append (dom-body) _el-form)
- (hs-activate! _el-form)
- (dom-dispatch _el-form "click" nil)
- (assert= (dom-get-style _el-form "width") "100px")
- ))
- (deftest "can transition a single property on current element with the my prefix using style ref"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click transition my *width from 0px to 100px")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "width") "100px")
- ))
- (deftest "can use initial to transition to original value"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click 1 transition my *width to 100px on click 2 transition my *width to initial")
- (dom-set-attr _el-div "style" "width: 10px")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "width") "10px")
- ))
- (deftest "can transition on another element with of syntax"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-foo (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click transition *width of #foo from 0px to 100px")
- (dom-set-attr _el-foo "id" "foo")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-foo)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style (dom-query-by-id "foo") "width") "100px")
- ))
- (deftest "can transition on another element with possessive"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-foo (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click transition #foo's *width from 0px to 100px")
- (dom-set-attr _el-foo "id" "foo")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-foo)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style (dom-query-by-id "foo") "width") "100px")
- ))
- (deftest "can transition on another element with it"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-foo (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click get #foo then transition its *width from 0px to 100px")
- (dom-set-attr _el-foo "id" "foo")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-foo)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style (dom-query-by-id "foo") "width") "100px")
- ))
- (deftest "can transition with a custom transition string"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-foo (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click transition #foo's *width from 0px to 100px using \"width 2s ease-in\"")
- (dom-set-attr _el-foo "id" "foo")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-foo)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style (dom-query-by-id "foo") "width") "100px")
- ))
- (deftest "can transition a single property on form using style ref"
- (hs-cleanup!)
- (let ((_el-form (dom-create-element "form")))
- (dom-set-attr _el-form "_" "on click transition *width from 0px to 100px")
- (dom-append (dom-body) _el-form)
- (hs-activate! _el-form)
- ))
- (deftest "can transition a single property on current element with the my prefix using style ref"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click transition my *width from 0px to 100px")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "can transition on query ref with possessive"
- (hs-cleanup!))
- (deftest "can transition on query ref with of syntax"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-div "_" "on click transition *width of the next from 0px to 100px")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-span "width") "100px")
- ))
-)
-
-;; ── log (4 tests) ──
-(defsuite "hs-upstream-log"
- (deftest "can log single item"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click log me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ))
- (deftest "can log multiple items"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click log me, my")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ))
- (deftest "can log multiple items with debug"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click log me, my with console.debug")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ))
- (deftest "can log multiple items with error"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click log me, my with console.error")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ))
-)
-
-;; ── call (6 tests) ──
-(defsuite "hs-upstream-call"
- (deftest "can call javascript instance functions"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click call document.getElementById(\"d1\") then put it into window.results")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- ;; SKIP check: skip value.should.equal(d1)
- ))
- (deftest "can call global javascript functions"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click call globalFunction(\"foo\")")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip "foo".should.equal(calledWith)
- ))
- (deftest "can call no argument functions"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click call globalFunction()")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip called.should.equal(true)
- ))
- (deftest "can call functions w/ underscores"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click call global_function()")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip called.should.equal(true)
- ))
- (deftest "can call functions w/ dollar signs"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click call $()")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip called.should.equal(true)
- ))
- (deftest "call functions that return promises are waited on"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click call promiseAnInt() then put it into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip div.innerText.should.equal("")
- ;; SKIP check: skip div.innerText.should.equal("42")
- ))
-)
-
-;; ── fetch (23 tests) ──
-(defsuite "hs-upstream-fetch"
- (deftest "can do a simple fetch"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click fetch \"/test\" then put it into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "yay")
- ))
- (deftest "can do a simple fetch w/ a naked URL"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click fetch /test then put it into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "yay")
- ))
- (deftest "can do a simple fetch w/ html"
- (hs-cleanup!))
- (deftest "can do a simple fetch w/ json"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click fetch /test as json then get result as JSON then put it into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "{\"foo\":1}")
- ))
- (deftest "can do a simple fetch w/ json using Object syntax"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click fetch /test as Object then get result as JSON then put it into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "{\"foo\":1}")
- ))
- (deftest "can do a simple fetch w/ json using Object syntax and an 'an' prefix"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click fetch /test as an Object then get result as JSON then put it into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "{\"foo\":1}")
- ))
- (deftest "can do a simple fetch with a response object"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click fetch /test as response then if its.ok put \"yep\" into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "yep")
- ))
- (deftest "can do a simple fetch w/ a custom conversion"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click fetch /test as Number then put it into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "1.2")
- ))
- (deftest "can do a simple post"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click fetch /test {method:\"POST\"} then put it into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "yay")
- ))
- (deftest "can do a simple post alt syntax without curlies"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click fetch /test with method:\"POST\" then put it into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "yay")
- ))
- (deftest "can do a simple post alt syntax w/ curlies"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click fetch /test with {method:\"POST\"} then put it into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "yay")
- ))
- (deftest "can put response conversion after with"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click fetch /test with {method:\"POST\"} as text then put it into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "yay")
- ))
- (deftest "can put response conversion before with"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click fetch /test as text with {method:\"POST\"} then put it into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "yay")
- ))
- (deftest "triggers an event just before fetching"
- (hs-cleanup!))
- (deftest "submits the fetch parameters to the event handler"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click fetch \"/test\" {headers: {\"X-CustomHeader\": \"foo\"}} then put it into my.innerHTML end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip event.detail.headers.should.have.property('X-CustomHeader',
- (assert= (dom-inner-html _el-div) "yay")
- ))
- (deftest "allows the event handler to change the fetch parameters"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click fetch \"/test\" then put it into my.innerHTML end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip arguments[1].should.have.property('headers')
- ;; SKIP check: skip arguments[1].headers.should.have.property('X-CustomHeader',
- (assert= (dom-inner-html _el-div) "yay")
- ))
- (deftest "can catch an error that occurs when using fetch"
- (hs-cleanup!))
- (deftest "can do a simple fetch w/ json using JSON syntax"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click fetch /test as JSON then get result as JSONString then put it into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "{\"foo\":1}")
- ))
- (deftest "throws on non-2xx response by default"
- (hs-cleanup!))
- (deftest "do not throw passes through 404 response"
- (hs-cleanup!))
- (deftest "don't throw passes through 404 response"
- (hs-cleanup!))
- (deftest "as response does not throw on 404"
- (hs-cleanup!))
- (deftest "Response can be converted to JSON via as JSON"
- (hs-cleanup!))
-)
-
-;; ── increment (20 tests) ──
-(defsuite "hs-upstream-increment"
- (deftest "can increment an empty variable"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click increment value then put value into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "1")
- ))
- (deftest "can increment a variable"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set value to 20 then increment value by 2 then put value into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "22")
- ))
- (deftest "can increment refer to result"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click increment value by 2 then put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "2")
- ))
- (deftest "can increment an attribute"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click increment @value then put @value into me")
- (dom-set-attr _el-div "value" "5")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "8")
- ))
- (deftest "can increment an floating point numbers"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set value to 5.2 then increment value by 6.1 then put value into me")
- (dom-set-attr _el-div "value" "5")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "11.3")
- ))
- (deftest "can increment a property"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click increment my.innerHTML")
- (dom-set-inner-html _el-div "3")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "6")
- ))
- (deftest "can increment by zero"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set value to 20 then increment value by 0 then put value into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "20")
- ))
- (deftest "can increment a value multiple times"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click increment my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "5")
- ))
- (deftest "can decrement an empty variable"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click decrement value then put value into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "-1")
- ))
- (deftest "can decrement a variable"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set value to 20 then decrement value by 2 then put value into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "18")
- ))
- (deftest "can decrement an attribute"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click decrement @value then put @value into me")
- (dom-set-attr _el-div "value" "5")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "2")
- ))
- (deftest "can decrement an floating point numbers"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set value to 6.1 then decrement value by 5.1 then put value into me")
- (dom-set-attr _el-div "value" "5")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "1")
- ))
- (deftest "can decrement a property"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click decrement my.innerHTML")
- (dom-set-inner-html _el-div "3")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "0")
- ))
- (deftest "can decrement a value multiple times"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click decrement my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "-5")
- ))
- (deftest "can decrement by zero"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set value to 20 then decrement value by 0 then put value into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "20")
- ))
- (deftest "can increment an array element"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set arr to [10, 20, 30] then increment arr[1] then put arr[1] into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "21")
- ))
- (deftest "can decrement an array element"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set arr to [10, 20, 30] then decrement arr[1] then put arr[1] into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "19")
- ))
- (deftest "can increment a possessive property"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click increment #d1's innerHTML")
- (dom-set-inner-html _el-d1 "5")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch (dom-query-by-id "d1") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "d1")) "6")
- ))
- (deftest "can increment a property of expression"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click increment innerHTML of #d1")
- (dom-set-inner-html _el-d1 "5")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch (dom-query-by-id "d1") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "d1")) "6")
- ))
- (deftest "can increment a style ref"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set my *opacity to 0.5 then increment *opacity by 0.25 then put *opacity into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "0.75")
- ))
)
;; ── append (13 tests) ──
(defsuite "hs-upstream-append"
- (deftest "can append a string to another string"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set value to 'Hello there.' then append ' General Kenobi.' to value then set my.innerHTML to value")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "Hello there. General Kenobi.")
- ))
- (deftest "can append a value into an array"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set value to [1,2,3] then append 4 to value then set my.innerHTML to value as String")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "1,2,3,4")
- ))
- (deftest "can append a value to 'it'"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set result to [1,2,3] then append 4 then put it as String into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "1,2,3,4")
- ))
- (deftest "can append a value to a DOM node"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click append 'This is my inner HTML' to me then append 'With Tags' to me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "This is my inner HTMLWith Tags")
- ))
- (deftest "can append a value to a DOM element"
- (hs-cleanup!)
- (let ((_el-content (dom-create-element "div")))
- (dom-set-attr _el-content "id" "content")
- (dom-set-attr _el-content "_" "on click append 'Content' to #content")
- (dom-append (dom-body) _el-content)
- (hs-activate! _el-content)
- (dom-dispatch _el-content "click" nil)
- (assert= (dom-inner-html _el-content) "Content")
- ))
- (deftest "can append a value to I"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click append 'Content' to I")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "Content")
- ))
- (deftest "can append a value to an object property"
- (hs-cleanup!)
- (let ((_el-id (dom-create-element "div")))
- (dom-set-attr _el-id "id" "id")
- (dom-set-attr _el-id "_" "on click append '_new' to my id")
- (dom-append (dom-body) _el-id)
- (hs-activate! _el-id)
- (dom-dispatch _el-id "click" nil)
- ;; SKIP check: skip div.id.should.equal("id_new")
- ))
- (deftest "multiple appends work"
- (hs-cleanup!)
- (let ((_el-id (dom-create-element "div")))
- (dom-set-attr _el-id "id" "id")
- (dom-set-attr _el-id "_" "on click get 'foo' then append 'bar' then append 'doh' then append it to me")
- (dom-append (dom-body) _el-id)
- (hs-activate! _el-id)
- (dom-dispatch _el-id "click" nil)
- (assert= (dom-inner-html _el-id) "foobardoh")
- ))
- (deftest "append to undefined ignores the undefined"
- (hs-cleanup!)
- (let ((_el-id (dom-create-element "div")))
- (dom-set-attr _el-id "id" "id")
- (dom-set-attr _el-id "_" "on click append 'bar' then append it to me")
- (dom-append (dom-body) _el-id)
- (hs-activate! _el-id)
- (dom-dispatch _el-id "click" nil)
- (assert= (dom-inner-html _el-id) "bar")
- ))
(deftest "append preserves existing content rather than overwriting it"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-btn1 (dom-create-element "button")))
@@ -3292,30 +319,71 @@
(dom-append _el-div _el-btn1)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip clicks.should.equal(1)
- ;; SKIP check: skip div.innerHTML.should.contain("New Content")
- ;; SKIP check: skip btn.parentNode.should.equal(div)
))
- (deftest "new content added by append will be live"
+ (deftest "append to undefined ignores the undefined"
+ (hs-cleanup!)
+ (let ((_el-id (dom-create-element "div")))
+ (dom-set-attr _el-id "id" "id")
+ (dom-set-attr _el-id "_" "on click append 'bar' then append it to me")
+ (dom-append (dom-body) _el-id)
+ (hs-activate! _el-id)
+ (dom-dispatch _el-id "click" nil)
+ (assert= (dom-text-content _el-id) "bar")
+ ))
+ (deftest "can append a string to another string"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click append `` to me")
+ (dom-set-attr _el-div "_" "on click set value to 'Hello there.' then append ' General Kenobi.' to value then set my.innerHTML to value")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip window.temp.should.equal(1)
+ (assert= (dom-text-content _el-div) "Hello there. General Kenobi.")
))
- (deftest "new DOM content added by append will be live"
+ (deftest "can append a value into an array"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click make a then append it to me")
+ (dom-set-attr _el-div "_" "on click set value to [1,2,3] then append 4 to value then set my.innerHTML to value as String")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? (dom-query "span") "topping"))
+ (assert= (dom-text-content _el-div) "1,2,3,4")
+ ))
+ (deftest "can append a value to 'it'"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set result to [1,2,3] then append 4 then put it as String into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "1,2,3,4")
+ ))
+ (deftest "can append a value to I"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click append 'Content' to I")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "Content")
+ ))
+ (deftest "can append a value to a DOM element"
+ (hs-cleanup!)
+ (let ((_el-content (dom-create-element "div")))
+ (dom-set-attr _el-content "id" "content")
+ (dom-set-attr _el-content "_" "on click append 'Content' to #content")
+ (dom-append (dom-body) _el-content)
+ (hs-activate! _el-content)
+ (dom-dispatch _el-content "click" nil)
+ (assert= (dom-text-content _el-content) "Content")
+ ))
+ (deftest "can append a value to a DOM node"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click append 'This is my inner HTML' to me then append 'With Tags' to me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "This is my inner HTMLWith Tags")
))
(deftest "can append a value to a set"
(hs-cleanup!)
@@ -3326,766 +394,71 @@
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "3")
))
-)
-
-;; ── tell (10 tests) ──
-(defsuite "hs-upstream-tell"
- (deftest "establishes a proper beingTold symbol"
+ (deftest "can append a value to an object property"
(hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click add .foo then tell #d2 then add .bar")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-append (dom-body) _el-d1)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert (not (dom-has-class? _el-d1 "bar")))
- (assert (dom-has-class? _el-d1 "foo"))
- (assert (dom-has-class? _el-d2 "bar"))
- (assert (not (dom-has-class? _el-d2 "foo")))
+ (let ((_el-id (dom-create-element "div")))
+ (dom-set-attr _el-id "id" "id")
+ (dom-set-attr _el-id "_" "on click append '_new' to my id")
+ (dom-append (dom-body) _el-id)
+ (hs-activate! _el-id)
+ (dom-dispatch _el-id "click" nil)
))
- (deftest "does not overwrite the me symbol"
+ (deftest "multiple appends work"
(hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click add .foo then tell #d2 then add .bar to me")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-append (dom-body) _el-d1)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert (dom-has-class? _el-d1 "bar"))
- (assert (dom-has-class? _el-d1 "foo"))
- (assert (not (dom-has-class? _el-d2 "bar")))
- (assert (not (dom-has-class? _el-d2 "foo")))
+ (let ((_el-id (dom-create-element "div")))
+ (dom-set-attr _el-id "id" "id")
+ (dom-set-attr _el-id "_" "on click get 'foo' then append 'bar' then append 'doh' then append it to me")
+ (dom-append (dom-body) _el-id)
+ (hs-activate! _el-id)
+ (dom-dispatch _el-id "click" nil)
+ (assert= (dom-text-content _el-id) "foobardoh")
))
- (deftest "works with an array"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-p1 (dom-create-element "p")) (_el-p2 (dom-create-element "p")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click add .foo then tell in me add .bar")
- (dom-set-attr _el-p1 "id" "p1")
- (dom-set-attr _el-p2 "id" "p2")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-append (dom-body) _el-d1)
- (dom-append _el-d1 _el-p1)
- (dom-append _el-d1 _el-p2)
- (dom-append _el-d1 _el-d2)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert (not (dom-has-class? _el-d1 "bar")))
- (assert (dom-has-class? _el-d1 "foo"))
- (assert (not (dom-has-class? (dom-query-by-id "div2") "bar")))
- (assert (not (dom-has-class? (dom-query-by-id "div2") "foo")))
- (assert (dom-has-class? _el-p1 "bar"))
- (assert (not (dom-has-class? _el-p1 "foo")))
- (assert (dom-has-class? _el-p2 "bar"))
- (assert (not (dom-has-class? _el-p2 "foo")))
- ))
- (deftest "restores a proper implicit me symbol"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click tell #d2 then add .bar end add .foo")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-append (dom-body) _el-d1)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert (not (dom-has-class? _el-d1 "bar")))
- (assert (dom-has-class? _el-d1 "foo"))
- (assert (dom-has-class? _el-d2 "bar"))
- (assert (not (dom-has-class? _el-d2 "foo")))
- ))
- (deftest "ignores null"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click tell null then add .bar end add .foo")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-append (dom-body) _el-d1)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert (not (dom-has-class? _el-d1 "bar")))
- (assert (dom-has-class? _el-d1 "foo"))
- (assert (not (dom-has-class? _el-d2 "bar")))
- (assert (not (dom-has-class? _el-d2 "foo")))
- ))
- (deftest "you symbol represents the thing being told"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click tell #d2 then add .bar to you")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-append (dom-body) _el-d1)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert (not (dom-has-class? _el-d1 "bar")))
- (assert (dom-has-class? _el-d2 "bar"))
- ))
- (deftest "your symbol represents the thing being told"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click tell #d2 then put your innerText into me")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-set-inner-html _el-d2 "foo")
- (dom-append (dom-body) _el-d1)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- ;; SKIP check: skip div1.innerText.should.equal("")
- ;; SKIP check: skip div2.innerText.should.equal("foo")
- ;; SKIP check: skip div1.innerText.should.equal("foo")
- ))
- (deftest "attributes refer to the thing being told"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click tell #d2 then put @foo into me")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-set-attr _el-d2 "foo" "bar")
- (dom-append (dom-body) _el-d1)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- ;; SKIP check: skip div1.innerText.should.equal("")
- ;; SKIP check: skip div2.innerText.should.equal("")
- ;; SKIP check: skip div1.innerText.should.equal("bar")
- ))
- (deftest "yourself attribute also works"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click tell #d2 remove yourself")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-append (dom-body) _el-d1)
- (dom-append _el-d1 _el-d2)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "")
- ))
- (deftest "tell terminates with a feature"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click tell #d2 remove yourself on click tell #d3 remove yourself")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-set-attr _el-d3 "id" "d3")
- (dom-append (dom-body) _el-d1)
- (dom-append _el-d1 _el-d2)
- (dom-append _el-d1 _el-d3)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- (assert= (dom-inner-html _el-d1) "")
- ))
-)
-
-;; ── on (63 tests) ──
-(defsuite "hs-upstream-on"
- (deftest "can respond to events with dots in names"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click send example.event to #d1")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on example.event add .called")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-div)
- (hs-activate! _el-d1)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-d1 "called"))
- ))
- (deftest "can respond to events with colons in names"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click send example:event to #d1")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on example:event add .called")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-div)
- (hs-activate! _el-d1)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-d1 "called"))
- ))
- (deftest "can respond to events with minus in names"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click send \"a-b\" to #d1")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on \"a-b\" add .called")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-div)
- (hs-activate! _el-d1)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-d1 "called"))
- ))
- (deftest "can respond to events on other elements"
- (hs-cleanup!)
- (let ((_el-bar (dom-create-element "div")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-bar "id" "bar")
- (dom-set-attr _el-div "_" "on click from #bar add .clicked")
- (dom-append (dom-body) _el-bar)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-bar "click" nil)
- (assert (dom-has-class? _el-div "clicked"))
- ))
- (deftest "listeners on other elements are removed when the registering element is removed"
- (hs-cleanup!))
- (deftest "listeners on self are not removed when the element is removed"
- (hs-cleanup!))
- (deftest "supports \"elsewhere\" modifier"
- (hs-cleanup!))
- (deftest "supports \"from elsewhere\" modifier"
- (hs-cleanup!))
- (deftest "can pick detail fields out by name"
- (hs-cleanup!))
- (deftest "can pick event properties out by name"
- (hs-cleanup!))
- (deftest "can fire an event on load"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on load put \"Loaded\" into my.innerHTML")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- ;; SKIP check: skip div.innerText.should.equal("Loaded")
- ))
- (deftest "can be in a top level script tag"
- (hs-cleanup!))
- (deftest "can have a simple event filter"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click[false] log event then put \"Clicked\" into my.innerHTML")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch _el-d1 "click" nil)
- ;; SKIP check: skip byId("d1").innerText.should.equal("")
- ))
- (deftest "can refer to event properties directly in filter"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click[buttons==0] log event then put \"Clicked\" into my.innerHTML")
- (dom-set-attr _el-div1 "_" "on click[buttons==1] log event then put \"Clicked\" into my.innerHTML")
- (dom-set-attr _el-div2 "_" "on click[buttons==1 and buttons==0] log event then put \"Clicked\" into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div)
- (hs-activate! _el-div1)
- (hs-activate! _el-div2)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip div.innerText.should.equal("Clicked")
- ;; SKIP check: skip div.innerText.should.equal("")
- ))
- (deftest "can refer to event detail properties directly in filter"
+ (deftest "new DOM content added by append will be live"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on example[foo] increment @count then put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ;; SKIP action: div.dispatchEvent_event_
- ;; SKIP action: div.dispatchEvent_event_
- ;; SKIP action: div.dispatchEvent_event_
- ;; SKIP check: skip div.innerText.should.equal("1")
- ;; SKIP check: skip div.innerText.should.equal("2")
- ))
- (deftest "can click after a positive event filter"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on foo(bar)[bar] put \"triggered\" into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "foo" nil)
- (dom-dispatch _el-div "foo" nil)
- ;; SKIP check: skip div.innerText.should.equal("")
- ;; SKIP check: skip div.innerText.should.equal("triggered")
- ))
- (deftest "multiple event handlers at a time are allowed to execute with the every keyword"
- (hs-cleanup!))
- (deftest "can have multiple event handlers"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on foo put increment() into my.innerHTML end on bar put increment() into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "foo" nil)
- (dom-dispatch _el-div "bar" nil)
- (dom-dispatch _el-div "foo" nil)
- ;; SKIP check: skip div.innerText.should.equal("1")
- ;; SKIP check: skip div.innerText.should.equal("2")
- ;; SKIP check: skip div.innerText.should.equal("3")
- ))
- (deftest "can have multiple event handlers, no end"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on foo put increment() into my.innerHTML on bar put increment() into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "foo" nil)
- (dom-dispatch _el-div "bar" nil)
- (dom-dispatch _el-div "foo" nil)
- ;; SKIP check: skip div.innerText.should.equal("1")
- ;; SKIP check: skip div.innerText.should.equal("2")
- ;; SKIP check: skip div.innerText.should.equal("3")
- ))
- (deftest "can queue events"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on foo wait for bar then call increment()")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "foo" nil)
- (dom-dispatch _el-div "foo" nil)
- (dom-dispatch _el-div "foo" nil)
- (dom-dispatch _el-div "bar" nil)
- (dom-dispatch _el-div "bar" nil)
- (dom-dispatch _el-div "bar" nil)
- ;; SKIP check: skip i.should.equal(0)
- ;; SKIP check: skip i.should.equal(1)
- ;; SKIP check: skip i.should.equal(2)
- ))
- (deftest "can queue first event"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on foo queue first wait for bar then call increment()")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "foo" nil)
- (dom-dispatch _el-div "foo" nil)
- (dom-dispatch _el-div "foo" nil)
- (dom-dispatch _el-div "bar" nil)
- (dom-dispatch _el-div "bar" nil)
- (dom-dispatch _el-div "bar" nil)
- ;; SKIP check: skip i.should.equal(0)
- ;; SKIP check: skip i.should.equal(1)
- ;; SKIP check: skip i.should.equal(2)
- ))
- (deftest "can queue last event"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on foo queue last wait for bar then call increment()")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "foo" nil)
- (dom-dispatch _el-div "foo" nil)
- (dom-dispatch _el-div "foo" nil)
- (dom-dispatch _el-div "bar" nil)
- (dom-dispatch _el-div "bar" nil)
- (dom-dispatch _el-div "bar" nil)
- ;; SKIP check: skip i.should.equal(0)
- ;; SKIP check: skip i.should.equal(1)
- ;; SKIP check: skip i.should.equal(2)
- ))
- (deftest "can queue all events"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on foo queue all wait for bar then call increment()")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "foo" nil)
- (dom-dispatch _el-div "foo" nil)
- (dom-dispatch _el-div "foo" nil)
- (dom-dispatch _el-div "bar" nil)
- (dom-dispatch _el-div "bar" nil)
- (dom-dispatch _el-div "bar" nil)
- ;; SKIP check: skip i.should.equal(0)
- ;; SKIP check: skip i.should.equal(1)
- ;; SKIP check: skip i.should.equal(2)
- ;; SKIP check: skip i.should.equal(3)
- ))
- (deftest "queue none does not allow future queued events"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click queue none put increment() into my.innerHTML then wait for a customEvent")
+ (dom-set-attr _el-div "_" "on click make a then append it to me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "customEvent" nil)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip div.innerText.should.equal("1")
- ;; SKIP check: skip div.innerText.should.equal("2")
+ (assert (dom-has-class? (dom-query "span.topping") "topping"))
))
- (deftest "can invoke on multiple events"
+ (deftest "new content added by append will be live"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click or foo call increment()")
+ (dom-set-attr _el-div "_" "on click append `` to me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
- (dom-dispatch _el-div "foo" nil)
- ;; SKIP check: skip i.should.equal(1)
- ;; SKIP check: skip i.should.equal(2)
+ (dom-dispatch (dom-query-by-id "b1") "click" nil)
))
- (deftest "can listen for events in another element (lazy)"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click in #d1 put it into window.tmp")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-d1)
- (dom-append _el-div _el-d2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip div1.should.equal(window.tmp)
- ))
- (deftest "can filter events based on count"
- (hs-cleanup!))
- (deftest "can filter events based on count range"
- (hs-cleanup!))
- (deftest "can filter events based on unbounded count range"
- (hs-cleanup!))
- (deftest "can mix ranges"
- (hs-cleanup!))
- (deftest "can listen for general mutations"
- (hs-cleanup!))
- (deftest "can listen for attribute mutations"
- (hs-cleanup!))
- (deftest "can listen for specific attribute mutations"
- (hs-cleanup!))
- (deftest "can listen for specific attribute mutations and filter out other attribute mutations"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on mutation of @bar put \"Mutated\" into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-set-attr _el-div "foo" "bar")
- (assert= (dom-inner-html _el-div) "")
- ))
- (deftest "can listen for childList mutations"
- (hs-cleanup!))
- (deftest "can listen for childList mutation filter out other mutations"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on mutation of childList put \"Mutated\" into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-set-attr _el-div "foo" "bar")
- (assert= (dom-inner-html _el-div) "")
- ))
- (deftest "can listen for characterData mutation filter out other mutations"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on mutation of characterData put \"Mutated\" into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-set-attr _el-div "foo" "bar")
- (assert= (dom-inner-html _el-div) "")
- ))
- (deftest "can listen for multiple mutations"
- (hs-cleanup!))
- (deftest "can listen for multiple mutations 2"
- (hs-cleanup!))
- (deftest "can listen for attribute mutations on other elements"
- (hs-cleanup!))
- (deftest "each behavior installation has its own event queue"
- (hs-cleanup!))
- (deftest "can catch exceptions thrown in js functions"
- (hs-cleanup!))
- (deftest "can catch exceptions thrown in hyperscript functions"
- (hs-cleanup!))
- (deftest "can catch top-level exceptions"
- (hs-cleanup!)
- (let ((_el-button (dom-create-element "button")))
- (dom-set-attr _el-button "_" "on click throw \"bar\" catch e put e into me")
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-inner-html _el-button) "bar")
- ))
- (deftest "can catch async top-level exceptions"
- (hs-cleanup!)
- (let ((_el-button (dom-create-element "button")))
- (dom-set-attr _el-button "_" "on click wait 1ms then throw \"bar\" catch e put e into me")
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-inner-html _el-button) "bar")
- ))
- (deftest "async exceptions don't kill the event queue"
- (hs-cleanup!)
- (let ((_el-button (dom-create-element "button")))
- (dom-set-attr _el-button "_" "on click increment :x then if :x is 1 then wait 1ms then throw \"bar\" otherwise then put \"success\" into me end catch e then put e into me")
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-inner-html _el-button) "success")
- ))
- (deftest "exceptions in catch block don't kill the event queue"
- (hs-cleanup!)
- (let ((_el-button (dom-create-element "button")))
- (dom-set-attr _el-button "_" "on click increment :x then if :x is 1 then throw \"bar\" otherwise then put \"success\" into me end catch e then put e into me then throw e")
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-inner-html _el-button) "success")
- ))
- (deftest "uncaught exceptions trigger 'exception' event"
- (hs-cleanup!))
- (deftest "caught exceptions do not trigger 'exception' event"
- (hs-cleanup!)
- (let ((_el-button (dom-create-element "button")))
- (dom-set-attr _el-button "_" "on click put \"foo\" into me then throw \"bar\" catch e log e on exception(error) put error into me")
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-inner-html _el-button) "foo")
- ))
- (deftest "rethrown exceptions trigger 'exception' event"
- (hs-cleanup!))
- (deftest "basic finally blocks work"
- (hs-cleanup!))
- (deftest "finally blocks work when exception thrown in catch"
- (hs-cleanup!))
- (deftest "async basic finally blocks work"
- (hs-cleanup!))
- (deftest "async finally blocks work when exception thrown in catch"
- (hs-cleanup!))
- (deftest "async exceptions in finally block don't kill the event queue"
- (hs-cleanup!))
- (deftest "exceptions in finally block don't kill the event queue"
- (hs-cleanup!))
- (deftest "can ignore when target doesn't exist"
- (hs-cleanup!))
- (deftest "can handle an or after a from clause"
- (hs-cleanup!))
- (deftest "handles custom events with null detail"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on myEvent(foo) if foo put foo into me else put \"no-detail\" into me")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- ))
- (deftest "on first click fires only once"
- (hs-cleanup!))
- (deftest "caught exceptions do not trigger 'exception' event"
- (hs-cleanup!)
- (let ((_el-button (dom-create-element "button")))
- (dom-set-attr _el-button "_" "on click put \"foo\" into me then throw \"bar\" catch e log e on exception(error) put error into me")
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-text-content _el-button) "foo")
- ))
- (deftest "rethrown exceptions trigger 'exception' event"
- (hs-cleanup!))
- (deftest "can ignore when target doesn\\'t exist"
- (hs-cleanup!))
-)
-
-;; ── init (3 tests) ──
-(defsuite "hs-upstream-init"
- (deftest "can define an init block inline"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "init then set my.foo to 42 end on click put my.foo into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-div) "42")
- ))
- (deftest "can define an init block in a script"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "init set window.foo to 42 end"))))
- )
- (deftest "can initialize immediately"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "init set window.foo to 10 init immediately set window.bar to window.foo"))))
- )
-)
-
-;; ── def (27 tests) ──
-(defsuite "hs-upstream-def"
- (deftest "can define a basic no arg function"
- (hs-cleanup!)
- (eval-expr-cek (hs-to-sx (hs-compile "def foo() add .called to #d1 end")))
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click call foo()")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-d1 "called"))
- ))
- (deftest "can define a basic one arg function"
- (hs-cleanup!)
- (eval-expr-cek (hs-to-sx (hs-compile "def foo(str) put str into #d1.innerHTML end")))
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click call foo(\"called\")")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-inner-html _el-d1) "called")
- ))
- (deftest "functions can be namespaced"
- (hs-cleanup!))
- (deftest "is called synchronously"
- (hs-cleanup!))
- (deftest "can call asynchronously"
- (hs-cleanup!))
- (deftest "can return a value synchronously"
- (hs-cleanup!)
- (eval-expr-cek (hs-to-sx (hs-compile "def foo() return \"foo\"end")))
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click call foo() then put it into #d1.innerText")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip div.innerText.should.equal("")
- ;; SKIP check: skip div.innerText.should.equal("foo")
- ))
- (deftest "can exit"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() exit end"))))
- )
- (deftest "can return a value asynchronously"
- (hs-cleanup!)
- (eval-expr-cek (hs-to-sx (hs-compile "def foo() wait 1ms return \"foo\"end")))
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click call foo() then put it into #d1.innerText")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- ;; SKIP check: skip div.innerText.should.equal("")
- ;; SKIP check: skip div.innerText.should.equal("foo")
- ))
- (deftest "can interop with javascript"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() return \"foo\"end"))))
- )
- (deftest "can interop with javascript asynchronously"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() wait 1ms return \"foo\"end"))))
- )
- (deftest "can catch exceptions"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() throw \"bar\"catch e set window.bar to e end"))))
- )
- (deftest "can rethrow in catch blocks"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() throw \"bar\"catch e throw e end"))))
- )
- (deftest "can return in catch blocks"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() throw \"bar\"catch e return 42 end"))))
- )
- (deftest "can catch async exceptions"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def doh() wait 10ms throw \"bar\"end def foo() call doh()catch e set window.bar to e end"))))
- )
- (deftest "can catch nested async exceptions"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def doh() wait 10ms throw \"bar\"end def foo() call doh()catch e set window.bar to e end"))))
- )
- (deftest "can rethrow in async catch blocks"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() throw \"bar\"catch e wait 10ms throw e end"))))
- )
- (deftest "can return in async catch blocks"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() throw \"bar\"catch e wait 10ms return 42 end"))))
- )
- (deftest "can install a function on an element and use in children w/ no leak"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "def func() put 42 into #d3")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click call func()")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-set-attr _el-d3 "id" "d3")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-d1)
- (dom-append _el-div _el-d2)
- (dom-append _el-div _el-d3)
- (hs-activate! _el-div)
- (hs-activate! _el-d1)
- ;; SKIP check: skip byId("d3").innerText.should.equal("42")
- ))
- (deftest "can install a function on an element and use in children w/ return value"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "def func() return 42")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click put func() into me")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-set-attr _el-d3 "id" "d3")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-d1)
- (dom-append _el-div _el-d2)
- (dom-append _el-div _el-d3)
- (hs-activate! _el-div)
- (hs-activate! _el-d1)
- ;; SKIP check: skip byId("d1").innerText.should.equal("42")
- ))
- (deftest "can install a function on an element and use me symbol correctly"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
- (dom-set-attr _el-div "_" "def func() put 42 into me")
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click call func()")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-set-attr _el-d3 "id" "d3")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-d1)
- (dom-append _el-div _el-d2)
- (dom-append _el-div _el-d3)
- (hs-activate! _el-div)
- (hs-activate! _el-d1)
- ;; SKIP check: skip div.innerText.should.equal("42")
- ))
- (deftest "finally blocks run normally"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() set window.bar to 10finally set window.bar to 20 end"))))
- )
- (deftest "finally blocks run when an exception occurs"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() set window.bar to 10 throw \"foo\"finally set window.bar to 20 end"))))
- )
- (deftest "finally blocks run when an exception expr occurs"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() set window.bar to 10 call throwsAsyncException()finally set window.bar to 20 end"))))
- )
- (deftest "async finally blocks run normally"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() wait a tick then set window.bar to 10finally set window.bar to 20 end"))))
- )
- (deftest "async finally blocks run when an exception occurs"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() wait a tick then set window.bar to 10 throw \"foo\"finally set window.bar to 20 end"))))
- )
- (deftest "exit stops execution mid-function"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() set x to 1 then exit then set x to 2 then return x end"))))
- )
- (deftest "can return without a value"
- (hs-cleanup!)
- (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() return end"))))
- )
)
;; ── askAnswer (5 tests) ──
(defsuite "hs-upstream-askAnswer"
+ (deftest "confirm returns first choice on OK"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
+ (dom-set-attr _el-button "_" "on click answer \"Save?\" with \"Yes\" or \"No\" then put it into #out")
+ (dom-set-inner-html _el-button "Go")
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-out)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "out")) "Yes")
+ ))
+ (deftest "confirm returns second choice on cancel"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
+ (dom-set-attr _el-button "_" "on click answer \"Save?\" with \"Yes\" or \"No\" then put it into #out")
+ (dom-set-inner-html _el-button "Go")
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-out)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "out")) "No")
+ ))
(deftest "prompts and puts result in it"
(hs-cleanup!)
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
@@ -4122,47 +495,2342 @@
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query-by-id "out")) "done")
))
- (deftest "confirm returns first choice on OK"
+)
+
+;; ── behavior (10 tests) ──
+(defsuite "hs-upstream-behavior"
+ (deftest "can declare variables in init blocks"
(hs-cleanup!)
- (let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
- (dom-set-attr _el-button "_" "on click answer \"Save?\" with \"Yes\" or \"No\" then put it into #out")
- (dom-set-inner-html _el-button "Go")
- (dom-set-attr _el-out "id" "out")
- (dom-append (dom-body) _el-button)
- (dom-append (dom-body) _el-out)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-text-content (dom-query-by-id "out")) "Yes")
+ (let ((_el-script (dom-create-element "script")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-set-inner-html _el-script "behavior Behave
+ init
+ set element's foo to 1
+ set element's bar to {}
+ end
+ on click
+ increment element's foo
+ set element's bar[\"count\"] to element's foo
+ put element's bar[\"count\"] into me
+ end
+ end")
+ (dom-set-attr _el-div "_" "install Behave")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "2")
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "3")
))
- (deftest "confirm returns second choice on cancel"
+ (deftest "can define behaviors"
(hs-cleanup!)
- (let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
- (dom-set-attr _el-button "_" "on click answer \"Save?\" with \"Yes\" or \"No\" then put it into #out")
- (dom-set-inner-html _el-button "Go")
- (dom-set-attr _el-out "id" "out")
- (dom-append (dom-body) _el-button)
- (dom-append (dom-body) _el-out)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-text-content (dom-query-by-id "out")) "No")
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-set-inner-html _el-script "behavior TheBehaviorWeAreDefiningForHyperscriptTestingPurposes init log 'foo' end end")
+ (dom-append (dom-body) _el-script)
+ ))
+ (deftest "can install behaviors"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-set-inner-html _el-script "behavior Behave on click add .foo end end")
+ (dom-set-attr _el-div "_" "install Behave")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "foo"))
+ ))
+ (deftest "can pass arguments to behaviors"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-set-inner-html _el-script "behavior Behave(foo, bar) on click put foo + bar into me end end")
+ (dom-set-attr _el-div "_" "install Behave(foo: 1, bar: 1)")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "2")
+ ))
+ (deftest "can pass element arguments to listen to in behaviors"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-b1 (dom-create-element "button")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-set-inner-html _el-script "behavior Behave(elt) on click from elt put 'foo' into me end end")
+ (dom-set-attr _el-b1 "id" "b1")
+ (dom-set-attr _el-div "_" "install Behave(elt: #b1)")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-b1)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch (dom-query-by-id "b1") "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "can refer to arguments in init blocks"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-d1 (dom-create-element "div")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-set-inner-html _el-script "behavior Behave(elt) init put 'foo' into elt end end")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-div "_" "install Behave(elt: #d1)")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "install resolves namespaced behavior paths"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-set-inner-html _el-script "behavior App.Widgets.Clickable on click add .clicked end end")
+ (dom-set-attr _el-div "_" "install App.Widgets.Clickable")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "clicked"))
+ ))
+ (deftest "install throws when the behavior path does not exist"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "install NoSuchBehavior")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "install throws when the path resolves to a non-function"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "install NotABehavior")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "supports init blocks in behaviors"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-set-inner-html _el-script "behavior Behave init add .foo to me end")
+ (dom-set-attr _el-div "_" "install Behave")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
))
)
-;; ── dialog (10 tests) ──
-(defsuite "hs-upstream-dialog"
- (deftest "show opens a dialog as modal"
+;; ── bind (44 tests) ──
+(defsuite "hs-upstream-bind"
+ (deftest "\"with\" is a synonym for \"and\""
(hs-cleanup!)
- (let ((_el-d (dom-create-element "dialog")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
+ (let ((_el-city-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-city-input "id" "city-input")
+ (dom-set-attr _el-city-input "type" "text")
+ (dom-set-attr _el-city-input "value" "Paris")
+ (dom-set-attr _el-span "_" "bind $city to #city-input.value end when $city changes put it into me")
+ (dom-append (dom-body) _el-city-input)
+ (dom-append (dom-body) _el-span)
+ (hs-activate! _el-span)
+ ))
+ (deftest "attribute bound to another element input value"
+ (hs-cleanup!)
+ (let ((_el-title-input (dom-create-element "input")) (_el-h1 (dom-create-element "h1")))
+ (dom-set-attr _el-title-input "id" "title-input")
+ (dom-set-attr _el-title-input "type" "text")
+ (dom-set-attr _el-title-input "value" "Hello")
+ (dom-set-attr _el-h1 "_" "bind @data-title and #title-input's value")
+ (dom-append (dom-body) _el-title-input)
+ (dom-append (dom-body) _el-h1)
+ (hs-activate! _el-h1)
+ (dom-set-prop (dom-query-by-id "title-input") "value" "World")
+ (dom-dispatch (dom-query-by-id "title-input") "input" nil)
+ ))
+ (deftest "bind element to element: both sides auto-detect"
+ (hs-cleanup!)
+ (let ((_el-range-slider (dom-create-element "input")) (_el-input (dom-create-element "input")))
+ (dom-set-attr _el-range-slider "id" "range-slider")
+ (dom-set-attr _el-range-slider "type" "range")
+ (dom-set-attr _el-range-slider "value" "50")
+ (dom-set-attr _el-input "_" "bind me to #range-slider")
+ (dom-set-attr _el-input "type" "number")
+ (dom-append (dom-body) _el-range-slider)
+ (dom-append (dom-body) _el-input)
+ (hs-activate! _el-input)
+ ))
+ (deftest "bind to contenteditable element auto-detects textContent"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "bind $text to me")
+ (dom-set-attr _el-div "contenteditable" "true")
+ (dom-set-inner-html _el-div "initial")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "bind to custom element with value property auto-detects value"
+ (hs-cleanup!)
+ (let ((_el-test-input (dom-create-element "test-input")))
+ (dom-set-attr _el-test-input "_" "bind $custom to me")
+ (dom-append (dom-body) _el-test-input)
+ (hs-activate! _el-test-input)
+ ))
+ (deftest "bind variable to checkbox by id auto-detects checked"
+ (hs-cleanup!)
+ (let ((_el-agree-cb (dom-create-element "input")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-agree-cb "id" "agree-cb")
+ (dom-set-attr _el-agree-cb "type" "checkbox")
+ (dom-set-attr _el-div "_" "bind $agreed to #agree-cb")
+ (dom-append (dom-body) _el-agree-cb)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "bind variable to element by id auto-detects value"
+ (hs-cleanup!)
+ (let ((_el-name-field (dom-create-element "input")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-name-field "id" "name-field")
+ (dom-set-attr _el-name-field "type" "text")
+ (dom-set-attr _el-name-field "value" "")
+ (dom-set-attr _el-div "_" "bind $name to #name-field")
+ (dom-append (dom-body) _el-name-field)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "bind variable to number input by id auto-detects valueAsNumber"
+ (hs-cleanup!)
+ (let ((_el-qty-input (dom-create-element "input")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-qty-input "id" "qty-input")
+ (dom-set-attr _el-qty-input "type" "number")
+ (dom-set-attr _el-div "_" "bind $qty to #qty-input")
+ (dom-append (dom-body) _el-qty-input)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "boolean bind to aria-* attribute uses \"true\"/\"false\" strings"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "bind $isHidden and @aria-hidden")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "boolean bind to attribute uses presence/absence"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "bind $isEnabled and @data-active")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "class bound to another element checkbox"
+ (hs-cleanup!)
+ (let ((_el-dark-toggle (dom-create-element "input")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-dark-toggle "id" "dark-toggle")
+ (dom-set-attr _el-dark-toggle "type" "checkbox")
+ (dom-set-attr _el-div "_" "bind .dark and #dark-toggle's checked")
+ (dom-set-inner-html _el-div "test")
+ (dom-append (dom-body) _el-dark-toggle)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-set-prop (dom-query-by-id "dark-toggle") "checked" true)
+ (dom-dispatch (dom-query-by-id "dark-toggle") "change" nil)
+ (dom-set-prop (dom-query-by-id "dark-toggle") "checked" false)
+ (dom-dispatch (dom-query-by-id "dark-toggle") "change" nil)
+ ))
+ (deftest "clicking a radio sets the variable to its value"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")) (_el-input2 (dom-create-element "input")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-input "_" "bind $color to me")
+ (dom-set-attr _el-input "type" "radio")
+ (dom-set-attr _el-input "name" "color")
+ (dom-set-attr _el-input "value" "red")
+ (dom-set-attr _el-input1 "_" "bind $color to me")
+ (dom-set-attr _el-input1 "type" "radio")
+ (dom-set-attr _el-input1 "name" "color")
+ (dom-set-attr _el-input1 "value" "blue")
+ (dom-set-attr _el-input2 "_" "bind $color to me")
+ (dom-set-attr _el-input2 "type" "radio")
+ (dom-set-attr _el-input2 "name" "color")
+ (dom-set-attr _el-input2 "value" "green")
+ (dom-set-attr _el-span "_" "when $color changes put it into me")
+ (dom-append (dom-body) _el-input)
+ (dom-append (dom-body) _el-input1)
+ (dom-append (dom-body) _el-input2)
+ (dom-append (dom-body) _el-span)
+ (hs-activate! _el-input)
+ (hs-activate! _el-input1)
+ (hs-activate! _el-input2)
+ (hs-activate! _el-span)
+ (dom-dispatch (dom-query "input[value="blue"]") "click" nil)
+ (dom-dispatch (dom-query "input[value="green"]") "click" nil)
+ ))
+ (deftest "dedup prevents infinite loop in two-way bind"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "bind $color and @data-color")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "external JS property write does not sync (known limitation)"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-input "_" "bind $searchTerm to me")
+ (dom-set-attr _el-input "type" "text")
+ (dom-set-attr _el-input "value" "original")
+ (dom-set-attr _el-span "_" "when $searchTerm changes put it into me")
+ (dom-append (dom-body) _el-input)
+ (dom-append (dom-body) _el-span)
+ (hs-activate! _el-input)
+ (hs-activate! _el-span)
+ ))
+ (deftest "external class change syncs back to variable"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "bind .dark and $darkMode")
+ (dom-set-inner-html _el-div "test")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "form reset listener is removed on cleanup"
+ (hs-cleanup!)
+ (let ((_el-form (dom-create-element "form")) (_el-binput (dom-create-element "input")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-binput "id" "binput")
+ (dom-set-attr _el-binput "_" "bind $val to me")
+ (dom-set-attr _el-binput "type" "text")
+ (dom-set-attr _el-binput "value" "initial")
+ (dom-set-attr _el-button "type" "reset")
+ (dom-set-inner-html _el-button "Reset")
+ (dom-append (dom-body) _el-form)
+ (dom-append _el-form _el-binput)
+ (dom-append _el-form _el-button)
+ (hs-activate! _el-binput)
+ ))
+ (deftest "form.reset() syncs variable back to default value"
+ (hs-cleanup!)
+ (let ((_el-test-form (dom-create-element "form")) (_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-test-form "id" "test-form")
+ (dom-set-attr _el-input "_" "bind $formField to me")
+ (dom-set-attr _el-input "type" "text")
+ (dom-set-attr _el-input "value" "default")
+ (dom-set-attr _el-span "_" "when $formField changes put it into me")
+ (dom-append (dom-body) _el-test-form)
+ (dom-append _el-test-form _el-input)
+ (dom-append (dom-body) _el-span)
+ (hs-activate! _el-input)
+ (hs-activate! _el-span)
+ (dom-set-prop _el-input "value" "user typed this")
+ (dom-dispatch _el-input "input" nil)
+ ))
+ (deftest "init: right side wins - attribute (Y) initializes variable (X)"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "bind $color to @data-color")
+ (dom-set-attr _el-div "data-color" "red")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "init: right side wins - class (Y) drives variable (X)"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "dark")
+ (dom-set-attr _el-div "_" "bind $isDark to .dark")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "init: right side wins - input value (Y) overwrites variable (X)"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")))
+ (dom-set-attr _el-input "_" "bind $name to my value")
+ (dom-set-attr _el-input "type" "text")
+ (dom-set-attr _el-input "value" "Bob")
+ (dom-append (dom-body) _el-input)
+ (hs-activate! _el-input)
+ ))
+ (deftest "init: right side wins - variable (Y) drives class (X)"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "bind .dark to $isDark")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "init: right side wins - variable (Y) initializes attribute (X)"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "bind @data-theme to $theme")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "init: right side wins - variable (Y) overwrites input value (X)"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")))
+ (dom-set-attr _el-input "_" "bind my value to $name")
+ (dom-set-attr _el-input "type" "text")
+ (dom-set-attr _el-input "value" "Bob")
+ (dom-append (dom-body) _el-input)
+ (hs-activate! _el-input)
+ ))
+ (deftest "initial value checks the correct radio on load"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")) (_el-input2 (dom-create-element "input")))
+ (dom-set-attr _el-input "_" "bind $fruit to me")
+ (dom-set-attr _el-input "type" "radio")
+ (dom-set-attr _el-input "name" "fruit")
+ (dom-set-attr _el-input "value" "apple")
+ (dom-set-attr _el-input1 "_" "bind $fruit to me")
+ (dom-set-attr _el-input1 "type" "radio")
+ (dom-set-attr _el-input1 "name" "fruit")
+ (dom-set-attr _el-input1 "value" "banana")
+ (dom-set-attr _el-input2 "_" "bind $fruit to me")
+ (dom-set-attr _el-input2 "type" "radio")
+ (dom-set-attr _el-input2 "name" "fruit")
+ (dom-set-attr _el-input2 "value" "cherry")
+ (dom-append (dom-body) _el-input)
+ (dom-append (dom-body) _el-input1)
+ (dom-append (dom-body) _el-input2)
+ (hs-activate! _el-input)
+ (hs-activate! _el-input1)
+ (hs-activate! _el-input2)
+ ))
+ (deftest "of-expression: bind $var to value of #input"
+ (hs-cleanup!)
+ (let ((_el-of-input (dom-create-element "input")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-of-input "id" "of-input")
+ (dom-set-attr _el-of-input "type" "text")
+ (dom-set-attr _el-of-input "value" "initial")
+ (dom-set-attr _el-div "_" "bind $search to value of #of-input")
+ (dom-append (dom-body) _el-of-input)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "possessive attribute: bind $var and my @data-label"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "bind $label and my @data-label")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "possessive property: bind $var to my value"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")))
+ (dom-set-attr _el-input "_" "bind $myVal to my value")
+ (dom-set-attr _el-input "type" "text")
+ (dom-set-attr _el-input "value" "hello")
+ (dom-append (dom-body) _el-input)
+ (hs-activate! _el-input)
+ (dom-set-prop _el-input "value" "world")
+ (dom-dispatch _el-input "input" nil)
+ ))
+ (deftest "radio change listener is removed on cleanup"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")))
+ (dom-set-attr _el-input "_" "bind $color to me")
+ (dom-set-attr _el-input "type" "radio")
+ (dom-set-attr _el-input "name" "color")
+ (dom-set-attr _el-input "value" "red")
+ (dom-set-attr _el-input "checked" "")
+ (dom-set-attr _el-input1 "_" "bind $color to me")
+ (dom-set-attr _el-input1 "type" "radio")
+ (dom-set-attr _el-input1 "name" "color")
+ (dom-set-attr _el-input1 "value" "blue")
+ (dom-append (dom-body) _el-input)
+ (dom-append (dom-body) _el-input1)
+ (hs-activate! _el-input)
+ (hs-activate! _el-input1)
+ ))
+ (deftest "right side wins on class init"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "bind .highlight to $highlighted")
+ (dom-set-inner-html _el-div "test")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "right side wins on init: input (Y) initializes variable (X)"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")))
+ (dom-set-attr _el-input "_" "bind $name to me")
+ (dom-set-attr _el-input "type" "text")
+ (dom-set-attr _el-input "value" "Bob")
+ (dom-append (dom-body) _el-input)
+ (hs-activate! _el-input)
+ ))
+ (deftest "right side wins on init: variable (Y) initializes input (X)"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")))
+ (dom-set-attr _el-input "_" "bind me to $name")
+ (dom-set-attr _el-input "type" "text")
+ (dom-set-attr _el-input "value" "Bob")
+ (dom-append (dom-body) _el-input)
+ (hs-activate! _el-input)
+ ))
+ (deftest "same value does not re-set input (prevents cursor jump)"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")))
+ (dom-set-attr _el-input "_" "bind $message to me")
+ (dom-set-attr _el-input "type" "text")
+ (dom-set-attr _el-input "value" "hello")
+ (dom-append (dom-body) _el-input)
+ (hs-activate! _el-input)
+ ))
+ (deftest "setting variable programmatically checks the matching radio"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")) (_el-input2 (dom-create-element "input")))
+ (dom-set-attr _el-input "_" "bind $size to me")
+ (dom-set-attr _el-input "type" "radio")
+ (dom-set-attr _el-input "name" "size")
+ (dom-set-attr _el-input "value" "small")
+ (dom-set-attr _el-input1 "_" "bind $size to me")
+ (dom-set-attr _el-input1 "type" "radio")
+ (dom-set-attr _el-input1 "name" "size")
+ (dom-set-attr _el-input1 "value" "medium")
+ (dom-set-attr _el-input2 "_" "bind $size to me")
+ (dom-set-attr _el-input2 "type" "radio")
+ (dom-set-attr _el-input2 "name" "size")
+ (dom-set-attr _el-input2 "value" "large")
+ (dom-append (dom-body) _el-input)
+ (dom-append (dom-body) _el-input1)
+ (dom-append (dom-body) _el-input2)
+ (hs-activate! _el-input)
+ (hs-activate! _el-input1)
+ (hs-activate! _el-input2)
+ ))
+ (deftest "shorthand on checkbox binds to checked"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-input "_" "bind $isDarkMode to me")
+ (dom-set-attr _el-input "type" "checkbox")
+ (dom-set-attr _el-span "_" "when $isDarkMode changes put it into me")
+ (dom-append (dom-body) _el-input)
+ (dom-append (dom-body) _el-span)
+ (hs-activate! _el-input)
+ (hs-activate! _el-span)
+ (dom-set-prop _el-input "checked" true)
+ (dom-dispatch _el-input "change" nil)
+ ))
+ (deftest "shorthand on select binds to value"
+ (hs-cleanup!)
+ (let ((_el-select (dom-create-element "select")) (_el-option (dom-create-element "option")) (_el-option2 (dom-create-element "option")) (_el-option3 (dom-create-element "option")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-select "_" "bind $country to me")
+ (dom-set-attr _el-option "value" "us")
+ (dom-set-inner-html _el-option "United States")
+ (dom-set-attr _el-option2 "value" "uk")
+ (dom-set-inner-html _el-option2 "United Kingdom")
+ (dom-set-attr _el-option3 "value" "fr")
+ (dom-set-inner-html _el-option3 "France")
+ (dom-set-attr _el-span "_" "when $country changes put it into me")
+ (dom-append (dom-body) _el-select)
+ (dom-append _el-select _el-option)
+ (dom-append _el-select _el-option2)
+ (dom-append _el-select _el-option3)
+ (dom-append (dom-body) _el-span)
+ (hs-activate! _el-select)
+ (hs-activate! _el-span)
+ (dom-set-prop _el-select "value" "uk")
+ (dom-dispatch _el-select "change" nil)
+ ))
+ (deftest "shorthand on text input binds to value"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-input "_" "bind $greeting to me end when $greeting changes put it into next ")
+ (dom-set-attr _el-input "type" "text")
+ (dom-set-attr _el-input "value" "hello")
+ (dom-append (dom-body) _el-input)
+ (dom-append (dom-body) _el-span)
+ (hs-activate! _el-input)
+ (dom-set-prop _el-input "value" "goodbye")
+ (dom-dispatch _el-input "input" nil)
+ ))
+ (deftest "shorthand on textarea binds to value"
+ (hs-cleanup!)
+ (let ((_el-textarea (dom-create-element "textarea")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-textarea "_" "bind $bio to me")
+ (dom-set-inner-html _el-textarea "Hello world")
+ (dom-set-attr _el-span "_" "when $bio changes put it into me")
+ (dom-append (dom-body) _el-textarea)
+ (dom-append (dom-body) _el-span)
+ (hs-activate! _el-textarea)
+ (hs-activate! _el-span)
+ (dom-set-prop _el-textarea "value" "New bio")
+ (dom-dispatch _el-textarea "input" nil)
+ ))
+ (deftest "shorthand on type=number preserves number type"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-input "_" "bind $price to me")
+ (dom-set-attr _el-input "type" "number")
+ (dom-set-attr _el-span "_" "when $price changes put it into me")
+ (dom-append (dom-body) _el-input)
+ (dom-append (dom-body) _el-span)
+ (hs-activate! _el-input)
+ (hs-activate! _el-span)
+ ))
+ (deftest "style bind is one-way: variable drives style, not vice versa"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "bind $opacity and *opacity")
+ (dom-set-inner-html _el-div "visible")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "syncs variable and attribute in both directions"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "bind $theme and @data-theme")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "syncs variable and input value in both directions"
+ (hs-cleanup!)
+ (let ((_el-name-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-name-input "id" "name-input")
+ (dom-set-attr _el-name-input "type" "text")
+ (dom-set-attr _el-name-input "value" "Alice")
+ (dom-set-attr _el-span "_" "bind $name and #name-input.value end when $name changes put it into me")
+ (dom-append (dom-body) _el-name-input)
+ (dom-append (dom-body) _el-span)
+ (hs-activate! _el-span)
+ ))
+ (deftest "two inputs synced via bind"
+ (hs-cleanup!)
+ (let ((_el-slider (dom-create-element "input")) (_el-input (dom-create-element "input")))
+ (dom-set-attr _el-slider "id" "slider")
+ (dom-set-attr _el-slider "type" "range")
+ (dom-set-attr _el-slider "value" "50")
+ (dom-set-attr _el-input "_" "bind my value and #slider's value")
+ (dom-set-attr _el-input "type" "number")
+ (dom-append (dom-body) _el-slider)
+ (dom-append (dom-body) _el-input)
+ (hs-activate! _el-input)
+ ))
+ (deftest "unsupported element: bind to plain div errors"
+ (error "SKIP (untranslated): unsupported element: bind to plain div errors"))
+ (deftest "variable drives class: setting variable adds/removes class"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "bind .dark and $darkMode")
+ (dom-set-inner-html _el-div "test")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+)
+
+;; ── breakpoint (2 tests) ──
+(defsuite "hs-upstream-breakpoint"
+ (deftest "parses as a top-level command"
+ (error "SKIP (untranslated): parses as a top-level command"))
+ (deftest "parses inside an event handler"
+ (error "SKIP (untranslated): parses inside an event handler"))
+)
+
+;; ── call (6 tests) ──
+(defsuite "hs-upstream-call"
+ (deftest "call functions that return promises are waited on"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click call promiseAnInt() then put it into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "42")
+ ))
+ (deftest "can call functions w/ dollar signs"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click call $()")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "can call functions w/ underscores"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click call global_function()")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "can call global javascript functions"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click call globalFunction(\"foo\")")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "can call javascript instance functions"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click call document.getElementById(\"d1\") then put it into window.results")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ ))
+ (deftest "can call no argument functions"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click call globalFunction()")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+)
+
+;; ── core/api (1 tests) ──
+(defsuite "hs-upstream-core/api"
+ (deftest "processNodes does not reinitialize a node already processed"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set window.global_int to window.global_int + 1")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ ))
+)
+
+;; ── core/asyncError (2 tests) ──
+(defsuite "hs-upstream-core/asyncError"
+ (deftest "rejected promise stops execution"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
+ (dom-set-attr _el-button "_" "on click call failAsync() then put 'should not reach' into #out then")
+ (dom-set-inner-html _el-button "Go")
+ (dom-set-attr _el-out "id" "out")
+ (dom-set-inner-html _el-out "original")
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-out)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "out")) "original")
+ ))
+ (deftest "rejected promise triggers catch block"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
+ (dom-set-attr _el-button "_" "on click call failAsync() then put 'unreachable' into #out then catch e then put e.message into #out then")
+ (dom-set-inner-html _el-button "Go")
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-out)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "out")) "boom")
+ ))
+)
+
+;; ── core/bootstrap (26 tests) ──
+(defsuite "hs-upstream-core/bootstrap"
+ (deftest "can call functions"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click call globalFunction(\"foo\")")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "can change non-class properties"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click add [@foo=\"bar\"]")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-get-attr _el-div "foo") "bar")
+ ))
+ (deftest "can respond to events on other elements"
+ (hs-cleanup!)
+ (let ((_el-bar (dom-create-element "div")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-bar "id" "bar")
+ (dom-set-attr _el-div "_" "on click from #bar then add .clicked")
+ (dom-append (dom-body) _el-bar)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch (dom-query-by-id "bar") "click" nil)
+ (assert (dom-has-class? (dom-query "div:nth-of-type(2)") "clicked"))
+ ))
+ (deftest "can send events"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-bar (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click send foo to #bar")
+ (dom-set-attr _el-bar "id" "bar")
+ (dom-set-attr _el-bar "_" "on foo add .foo-sent")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-bar)
+ (hs-activate! _el-div)
+ (hs-activate! _el-bar)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? (dom-query-by-id "bar") "foo-sent"))
+ ))
+ (deftest "can send events with args"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-bar (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click send foo(x:42) to #bar")
+ (dom-set-attr _el-bar "id" "bar")
+ (dom-set-attr _el-bar "_" "on foo put event.detail.x into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-bar)
+ (hs-activate! _el-div)
+ (hs-activate! _el-bar)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "bar")) "42")
+ ))
+ (deftest "can set properties"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click put \"foo\" into #d1.innerHTML")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "foo")
+ ))
+ (deftest "can set styles"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click put \"red\" into my.style.color")
+ (dom-set-inner-html _el-div "lolwat")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-get-style _el-div "color") "")
+ ))
+ (deftest "can take a class from other elements"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
+ (dom-add-class _el-div "divs")
+ (dom-add-class _el-div "foo")
+ (dom-add-class _el-div1 "divs")
+ (dom-set-attr _el-div1 "_" "on click take .foo from .divs")
+ (dom-add-class _el-div2 "divs")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-div1)
+ (dom-append (dom-body) _el-div2)
+ (hs-activate! _el-div1)
+ ))
+ (deftest "can target another div"
+ (hs-cleanup!)
+ (let ((_el-bar (dom-create-element "div")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-bar "id" "bar")
+ (dom-set-attr _el-div "_" "on click add .foo to #bar")
+ (dom-append (dom-body) _el-bar)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
+ (assert (dom-has-class? (dom-query-by-id "bar") "foo"))
+ ))
+ (deftest "can wait"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click add .foo then wait 20ms then add .bar")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "foo"))
+ (assert (dom-has-class? _el-div "bar"))
+ ))
+ (deftest "cleanup clears elt._hyperscript"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click add .foo")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "cleanup removes cross-element event listeners"
+ (hs-cleanup!)
+ (let ((_el-source (dom-create-element "div")) (_el-target (dom-create-element "div")))
+ (dom-set-attr _el-source "id" "source")
+ (dom-set-attr _el-target "id" "target")
+ (dom-set-attr _el-target "_" "on click from #source add .foo")
+ (dom-append (dom-body) _el-source)
+ (dom-append (dom-body) _el-target)
+ (hs-activate! _el-target)
+ (dom-dispatch (dom-query-by-id "source") "click" nil)
+ (assert (dom-has-class? (dom-query-by-id "target") "foo"))
+ ))
+ (deftest "cleanup removes data-hyperscript-powered"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click add .foo")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "cleanup removes event listeners on the element"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click add .foo")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "foo"))
+ (assert (not (dom-has-class? _el-div "foo")))
+ ))
+ (deftest "cleanup tracks listeners in elt._hyperscript"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click add .foo")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "fires hyperscript:before:cleanup and hyperscript:after:cleanup"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click add .foo")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "fires hyperscript:before:init and hyperscript:after:init"
+ (error "SKIP (untranslated): fires hyperscript:before:init and hyperscript:after:init"))
+ (deftest "hyperscript can have more than one action"
+ (hs-cleanup!)
+ (let ((_el-bar (dom-create-element "div")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-bar "id" "bar")
+ (dom-set-attr _el-div "_" "on click add .foo to #bar then add .blah")
+ (dom-append (dom-body) _el-bar)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
+ (assert (dom-has-class? (dom-query-by-id "bar") "foo"))
+ (assert (not (dom-has-class? (dom-query-by-id "bar") "blah")))
+ (assert (not (dom-has-class? (dom-query "div:nth-of-type(2)") "foo")))
+ (assert (dom-has-class? (dom-query "div:nth-of-type(2)") "blah"))
+ ))
+ (deftest "hyperscript:before:init can cancel initialization"
+ (error "SKIP (untranslated): hyperscript:before:init can cancel initialization"))
+ (deftest "logAll config logs events to console"
+ (error "SKIP (untranslated): logAll config logs events to console"))
+ (deftest "on a single div"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click add .foo")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "foo"))
+ ))
+ (deftest "reinitializes if script attribute changes"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click add .foo")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "foo"))
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "bar"))
+ ))
+ (deftest "sets data-hyperscript-powered on initialized elements"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click add .foo")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "skips reinitialization if script unchanged"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click add .foo")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "stores state on elt._hyperscript"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click add .foo")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "toggles"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click toggle .foo")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "foo"))
+ (dom-dispatch _el-div "click" nil)
+ (assert (not (dom-has-class? _el-div "foo")))
+ ))
+)
+
+;; ── core/dom-scope (5 tests) ──
+(defsuite "hs-upstream-core/dom-scope"
+ (deftest "closest jumps to matching ancestor"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
+ (dom-add-class _el-div "outer")
+ (dom-set-attr _el-div "_" "init set ^val to 'from-outer'")
+ (dom-set-attr _el-div1 "_" "init set ^val to 'from-inner'")
+ (dom-set-attr _el-div1 "dom-scope" "isolated")
+ (dom-set-attr _el-span "_" "init put ^val into me")
+ (dom-set-attr _el-span "dom-scope" "closest .outer")
+ (dom-set-inner-html _el-span "none")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-div1)
+ (dom-append _el-div1 _el-span)
+ (hs-activate! _el-div)
+ (hs-activate! _el-div1)
+ (hs-activate! _el-span)
+ ))
+ (deftest "closest with no match stops resolution"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-div "_" "init set ^val to 'found'")
+ (dom-set-attr _el-span "_" "init if ^val is not undefined put 'leaked' into me else put 'blocked' into me")
+ (dom-set-attr _el-span "dom-scope" "closest .nonexistent")
+ (dom-set-inner-html _el-span "waiting")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-span)
+ (hs-activate! _el-div)
+ (hs-activate! _el-span)
+ ))
+ (deftest "isolated allows setting ^var on the isolated element itself"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-div "_" "init set ^outer to 'leaked'")
+ (dom-set-attr _el-div1 "_" "init set ^inner to 'contained'")
+ (dom-set-attr _el-div1 "dom-scope" "isolated")
+ (dom-set-attr _el-span "_" "init put ^inner into me")
+ (dom-set-inner-html _el-span "none")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-div1)
+ (dom-append _el-div1 _el-span)
+ (hs-activate! _el-div)
+ (hs-activate! _el-div1)
+ (hs-activate! _el-span)
+ ))
+ (deftest "isolated stops ^var resolution"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-div "_" "init set ^color to 'red'")
+ (dom-set-attr _el-div1 "dom-scope" "isolated")
+ (dom-set-attr _el-span "_" "init if ^color is not undefined put 'leaked' into me else put 'blocked' into me")
+ (dom-set-inner-html _el-span "waiting")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-div1)
+ (dom-append _el-div1 _el-span)
+ (hs-activate! _el-div)
+ (hs-activate! _el-span)
+ ))
+ (deftest "parent of jumps past matching ancestor to its parent"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
+ (dom-add-class _el-div "outer")
+ (dom-set-attr _el-div "_" "init set ^val to 'from-outer'")
+ (dom-add-class _el-div1 "middle")
+ (dom-set-attr _el-div1 "_" "init set ^val to 'from-middle'")
+ (dom-set-attr _el-div1 "dom-scope" "isolated")
+ (dom-set-attr _el-span "_" "init put ^val into me")
+ (dom-set-attr _el-span "dom-scope" "parent of .middle")
+ (dom-set-inner-html _el-span "none")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-div1)
+ (dom-append _el-div1 _el-span)
+ (hs-activate! _el-div)
+ (hs-activate! _el-div1)
+ (hs-activate! _el-span)
+ ))
+)
+
+;; ── core/evalStatically (8 tests) ──
+(defsuite "hs-upstream-core/evalStatically"
+ (deftest "throws on math expressions"
+ (error "SKIP (untranslated): throws on math expressions"))
+ (deftest "throws on symbol references"
+ (error "SKIP (untranslated): throws on symbol references"))
+ (deftest "throws on template strings"
+ (error "SKIP (untranslated): throws on template strings"))
+ (deftest "works on boolean literals"
+ (assert= (eval-hs "true") true)
+ (assert= (eval-hs "false") false)
+ )
+ (deftest "works on null literal"
+ (assert= (eval-hs "null") nil)
+ )
+ (deftest "works on number literals"
+ (assert= (eval-hs "42") 42)
+ (assert= (eval-hs "3.14") 3.14)
+ )
+ (deftest "works on plain string literals"
+ (assert= (eval-hs "\"hello\"") "hello")
+ (assert= (eval-hs "'world'") "world")
+ )
+ (deftest "works on time expressions"
+ (assert= (eval-hs "200ms") 200)
+ (assert= (eval-hs "2s") 2000)
+ )
+)
+
+;; ── core/liveTemplate (16 tests) ──
+(defsuite "hs-upstream-core/liveTemplate"
+ (deftest "applies init script from _ attribute"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "_" "init set ^msg to 'initialized'")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script "${}{^msg}")
+ (dom-append (dom-body) _el-script)
+ (hs-activate! _el-script)
+ ))
+ (deftest "loop index variable is captured alongside loop variable"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script "
+ #for item in $idxItems index i
+ - ${}{item}
+ #end
+
")
+ (dom-append (dom-body) _el-script)
+ ))
+ (deftest "loop variable capture works with remove for live list"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script "
+ #for item in $removeItems
+ - ${}{item.name}
+ #end
+
")
+ (dom-append (dom-body) _el-script)
+ (dom-dispatch (dom-query "[data-live-template] li').nth(1).locator('button") "click" nil)
+ (assert= (dom-text-content (dom-query "[data-live-template] li').first().locator('span")) "A")
+ (assert= (dom-text-content (dom-query "[data-live-template] li').last().locator('span")) "C")
+ ))
+ (deftest "loop variables are captured and available in _= handlers"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script "
+ #for item in $captureItems index i
+ - ${}{item.name}
+ #end
+
")
+ (dom-append (dom-body) _el-script)
+ ))
+ (deftest "multiple live templates are independent"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-script1 (dom-create-element "script")))
+ (dom-set-attr _el-script "_" "init set ^x to 'first'")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script "${}{^x}")
+ (dom-set-attr _el-script1 "_" "init set ^x to 'second'")
+ (dom-set-attr _el-script1 "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script1 "${}{^x}")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-script1)
+ (hs-activate! _el-script)
+ (hs-activate! _el-script1)
+ ))
+ (deftest "processes hyperscript on inner elements"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "_" "init set ^val to 0")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script "\">+
+ ")
+ (dom-append (dom-body) _el-script)
+ (hs-activate! _el-script)
+ (dom-dispatch (dom-query "[data-live-template] button") "click" nil)
+ ))
+ (deftest "reactively updates when dependencies change"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "_" "init set ^count to 0")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script "
+ Count: ${}{^count}")
+ (dom-append (dom-body) _el-script)
+ (hs-activate! _el-script)
+ (dom-dispatch (dom-query "[data-live-template] button") "click" nil)
+ ))
+ (deftest "reacts to global state without init script"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script "Hello, ${}{$ltGlobal}!
")
+ (dom-append (dom-body) _el-script)
+ ))
+ (deftest "renders static content after the template"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script "Hello World")
+ (dom-append (dom-body) _el-script)
+ ))
+ (deftest "renders template expressions"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script "Hello ${}{$ltName}!")
+ (dom-append (dom-body) _el-script)
+ ))
+ (deftest "scope is refreshed after morph so surviving elements get updated indices"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script "
+ #for item in $morphItems index i
+ - ${}{item.name}
+ #end
+
")
+ (dom-append (dom-body) _el-script)
+ (dom-dispatch (dom-query "[data-live-template] li") "click" nil)
+ (assert= (dom-text-content (dom-query "[data-live-template] li")) "2:C")
+ (dom-dispatch (dom-query "[data-live-template] li") "click" nil)
+ (assert= (dom-text-content (dom-query "[data-live-template] li")) "1:C")
+ ))
+ (deftest "script type=\"text/hyperscript-template\" works as a live template source"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "_" "init set ^stMsg to 'from script'")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script "${}{^stMsg}")
+ (dom-append (dom-body) _el-script)
+ (hs-activate! _el-script)
+ ))
+ (deftest "script-based live template preserves ${} in bare attribute position"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "_" "init set ^items to [{text:'A', done:true},{text:'B', done:false}]")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script "")
+ (dom-append (dom-body) _el-script)
+ (hs-activate! _el-script)
+ ))
+ (deftest "supports #for loops"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "_" "init set ^items to ['a', 'b', 'c']")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script "
+ #for item in ^items
+ - ${}{item}
+ #end
+
")
+ (dom-append (dom-body) _el-script)
+ (hs-activate! _el-script)
+ ))
+ (deftest "supports #if conditionals"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "_" "init set ^show to true")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script "#if ^show
+ visible
+ #end")
+ (dom-append (dom-body) _el-script)
+ (hs-activate! _el-script)
+ ))
+ (deftest "wrapper has display:contents"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-script "test")
+ (dom-append (dom-body) _el-script)
+ ))
+)
+
+;; ── core/parser (14 tests) ──
+(defsuite "hs-upstream-core/parser"
+ (deftest "_hyperscript() evaluate API still throws on first error"
+ (error "SKIP (untranslated): _hyperscript() evaluate API still throws on first error"))
+ (deftest "basic parse error messages work"
+ (error "SKIP (untranslated): basic parse error messages work"))
+ (deftest "can have alternate comments in attributes"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click put \"clicked\" into my.innerHTML // put some content into the div...")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "clicked")
+ ))
+ (deftest "can have alternate comments in scripts"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "// this is a comment def foo() // this is another comment return \"foo\" end // end with a comment"))))
+ )
+ (deftest "can have comments in attributes"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click put \"clicked\" into my.innerHTML -- put some content into the div...")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "clicked")
+ ))
+ (deftest "can have comments in attributes (triple dash)"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click put \"clicked\" into my.innerHTML ---put some content into the div...")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "clicked")
+ ))
+ (deftest "can have comments in scripts"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "-- this is a comment def foo() -- this is another comment return \"foo\" end -- end with a comment--- this is a comment ----this is a comment---- def bar() ---this is another comment return \"bar\" end --- end with a comment"))))
+ )
+ (deftest "can support parenthesized commands and features"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "(on click (log me) (trigger foo)) (on foo (put \"clicked\" into my.innerHTML))")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "clicked")
+ ))
+ (deftest "continues initializing elements in the presence of a parse error"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click bad")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-set-attr _el-d2 "_" "on click put \"clicked\" into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-d1)
+ (dom-append _el-div _el-d2)
+ (hs-activate! _el-d1)
+ (hs-activate! _el-d2)
+ (dom-dispatch (dom-query-by-id "d2") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d2")) "clicked")
+ ))
+ (deftest "element-level isolation still works with error recovery"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click blargh end on mouseenter also_bad")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-set-attr _el-d2 "_" "on click put \"clicked\" into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-d1)
+ (dom-append _el-div _el-d2)
+ (hs-activate! _el-d1)
+ (hs-activate! _el-d2)
+ (dom-dispatch (dom-query-by-id "d2") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d2")) "clicked")
+ ))
+ (deftest "fires hyperscript:parse-error event with all errors"
+ (error "SKIP (untranslated): fires hyperscript:parse-error event with all errors"))
+ (deftest "parse error at EOF on trailing newline does not crash"
+ (error "SKIP (untranslated): parse error at EOF on trailing newline does not crash"))
+ (deftest "recovers across feature boundaries and reports all errors"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click blargh end on mouseenter put \"hovered\" into my.innerHTML")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ ))
+ (deftest "recovers across multiple feature errors"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click blargh end on mouseenter also_bad end on focus put \"focused\" into my.innerHTML")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ ))
+)
+
+;; ── core/reactivity (8 tests) ──
+(defsuite "hs-upstream-core/reactivity"
+ (deftest "NaN → NaN does not retrigger handlers (Object.is semantics)"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "when $rxNanVal changes increment $rxNanCount")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "effect switches its dependencies based on control flow"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live if $rxCond put $rxA into me else put $rxB into me end end")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "effects fire in source registration order"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "when $rxTrigger changes call $rxOrder.push('first')")
+ (dom-set-attr _el-div1 "_" "when $rxTrigger changes call $rxOrder.push('second')")
+ (dom-set-attr _el-div2 "_" "when $rxTrigger changes call $rxOrder.push('third')")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-div1)
+ (dom-append (dom-body) _el-div2)
+ (hs-activate! _el-div)
+ (hs-activate! _el-div1)
+ (hs-activate! _el-div2)
+ ))
+ (deftest "effects on disconnected elements stop automatically"
+ (hs-cleanup!)
+ (let ((_el-persist (dom-create-element "div")) (_el-doomed (dom-create-element "div")))
+ (dom-set-attr _el-persist "id" "persist")
+ (dom-set-attr _el-persist "_" "when $rxDcVal changes increment $rxDcCount then put $rxDcVal into me")
+ (dom-set-attr _el-doomed "id" "doomed")
+ (dom-set-attr _el-doomed "_" "when $rxDcVal changes increment $rxDcCount")
+ (dom-append (dom-body) _el-persist)
+ (dom-append (dom-body) _el-doomed)
+ (hs-activate! _el-persist)
+ (hs-activate! _el-doomed)
+ ))
+ (deftest "element-scoped writes only trigger effects on the same element"
+ (hs-cleanup!)
+ (let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")))
+ (dom-set-attr _el-a "id" "a")
+ (dom-set-attr _el-a "_" "init set :count to 0 then on click increment :count then when :count changes put :count into me")
+ (dom-set-attr _el-b "id" "b")
+ (dom-set-attr _el-b "_" "init set :count to 0 then when :count changes put :count into me")
+ (dom-append (dom-body) _el-a)
+ (dom-append (dom-body) _el-b)
+ (hs-activate! _el-a)
+ (hs-activate! _el-b)
+ (dom-dispatch (dom-query-by-id "a") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "a")) "1")
+ (assert= (dom-text-content (dom-query-by-id "b")) "0")
+ ))
+ (deftest "multiple effects on the same global fire once per write"
+ (hs-cleanup!)
+ (let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")))
+ (dom-set-attr _el-a "id" "a")
+ (dom-set-attr _el-a "_" "when $rxVal changes increment $rxCount then put $rxVal into me")
+ (dom-set-attr _el-b "id" "b")
+ (dom-set-attr _el-b "_" "when $rxVal changes put $rxVal into me")
+ (dom-append (dom-body) _el-a)
+ (dom-append (dom-body) _el-b)
+ (hs-activate! _el-a)
+ (hs-activate! _el-b)
+ ))
+ (deftest "reactive loops are detected and stopped after 100 consecutive triggers"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "when $rxLoop changes increment $rxLoop")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "setting same value does not retrigger handler"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "when $rxSameVal changes increment $rxSameCount")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+)
+
+;; ── core/regressions (16 tests) ──
+(defsuite "hs-upstream-core/regressions"
+ (deftest "async exception"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click async transition opacity to 0 log \"hello!\"")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "button query in form"
+ (hs-cleanup!)
+ (let ((_el-form (dom-create-element "form")) (_el-b1 (dom-create-element "button")))
+ (dom-set-attr _el-form "_" "on click get the in me set it @disabled to true")
+ (dom-set-attr _el-b1 "id" "b1")
+ (dom-set-inner-html _el-b1 "Button")
+ (dom-append (dom-body) _el-form)
+ (dom-append _el-form _el-b1)
+ (hs-activate! _el-form)
+ (dom-dispatch _el-form "click" nil)
+ ))
+ (deftest "can create a paragraph tag"
+ (hs-cleanup!)
+ (let ((_el-i1 (dom-create-element "input")) (_el-d2 (dom-create-element "div")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-i1 "id" "i1")
+ (dom-set-attr _el-i1 "value" "foo")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-set-attr _el-div "_" "on click make a then put #i1.value into its textContent put it.outerHTML at end of #d2")
+ (dom-append (dom-body) _el-i1)
+ (dom-append _el-i1 _el-d2)
+ (dom-append _el-i1 _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
+ ))
+ (deftest "can invoke functions w/ numbers in name"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")))
+ (dom-set-attr _el-button "_" "on click put select2() into me")
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content _el-button) "select2")
+ ))
+ (deftest "can pick detail fields out by name"
+ (error "SKIP (skip-list): can pick detail fields out by name"))
+ (deftest "can refer to function in init blocks"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-append (dom-body) _el-d1)
+ ))
+ (deftest "can remove by clicks elsewhere"
+ (hs-cleanup!)
+ (let ((_el-target (dom-create-element "div")) (_el-other (dom-create-element "div")))
+ (dom-set-attr _el-target "id" "target")
+ (dom-set-attr _el-target "_" "on click elsewhere remove me")
+ (dom-set-attr _el-other "id" "other")
+ (dom-append (dom-body) _el-target)
+ (dom-append (dom-body) _el-other)
+ (hs-activate! _el-target)
+ ))
+ (deftest "can remove class by id"
+ (hs-cleanup!)
+ (let ((_el-email-form (dom-create-element "form")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-email-form "id" "email-form")
+ (dom-add-class _el-email-form "hideme")
+ (dom-set-attr _el-div "_" "on click remove .hideme from #email-form")
+ (dom-append (dom-body) _el-email-form)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (not (dom-has-class? (dom-query-by-id "email-form") "hideme")))
+ ))
+ (deftest "can trigger htmx events"
+ (hs-cleanup!)
+ (let ((_el-div1 (dom-create-element "div")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div1 "id" "div1")
+ (dom-set-attr _el-div1 "_" "on htmx:foo put \"foo\" into my.innerHTML")
+ (dom-set-attr _el-div "_" "on click send htmx:foo to #div1")
+ (dom-append (dom-body) _el-div1)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div1)
+ (hs-activate! _el-div)
+ (dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "div1")) "foo")
+ ))
+ (deftest "extra chars cause error when evaling"
+ (error "SKIP (untranslated): extra chars cause error when evaling"))
+ (deftest "listen for event on form"
+ (hs-cleanup!)
+ (let ((_el-form (dom-create-element "form")) (_el-b1 (dom-create-element "button")))
+ (dom-set-attr _el-b1 "id" "b1")
+ (dom-set-attr _el-b1 "_" "on click from closest put \"clicked\" into me")
+ (dom-set-inner-html _el-b1 "Button")
+ (dom-append (dom-body) _el-form)
+ (dom-append _el-form _el-b1)
+ (hs-activate! _el-b1)
+ (dom-dispatch _el-form "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "b1")) "clicked")
+ ))
+ (deftest "me and it is properly set when responding to events"
+ (hs-cleanup!)
+ (let ((_el-name (dom-create-element "div")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-name "id" "name")
+ (dom-set-attr _el-div "_" "on click from #name set window.me to me set window.it to it")
+ (dom-append (dom-body) _el-name)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch (dom-query-by-id "name") "click" nil)
+ ))
+ (deftest "me symbol works in from expressions"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click from closest parent put \"Foo\" into me")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "Foo")
+ ))
+ (deftest "properly interpolates values"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")))
+ (dom-set-attr _el-button "_" "on click set count to 1 then set optName to `options_${count}_value` then put optName into me")
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content _el-button) "options_1_value")
+ ))
+ (deftest "properly interpolates values 2"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")))
+ (dom-set-attr _el-button "_" "on click set trackingcode to `AB123456789KK` then set pdfurl to `https://yyy.xxxxxx.com/path/out/${trackingcode}.pdf` then put pdfurl into me")
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content _el-button) "https://yyy.xxxxxx.com/path/out/AB123456789KK.pdf")
+ ))
+ (deftest "string literals can dot-invoked against"
+ (assert= (eval-hs "'foo'.length") 3)
+ (assert= (eval-hs "`foo`.length") 3)
+ (assert= (eval-hs "\"foo\".length") 3)
+ )
+)
+
+;; ── core/runtime (7 tests) ──
+(defsuite "hs-upstream-core/runtime"
+ (deftest "arrays args are handled properly wrt Promises"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def invokesArrayPromise() return { foo: stringPromise(), bar: stringPromise(), baz: stringPromise() }end def stringPromise() wait 20ms return 'foo' end"))))
+ )
+ (deftest "async hypertrace is reasonable"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click call bar()")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "has proper stack"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() return bar() end def bar() return meta.caller end"))))
+ )
+ (deftest "has proper stack from event handler"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click put bar().meta.feature.type into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "onFeature")
+ ))
+ (deftest "hypertrace from javascript is reasonable"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click call bar()")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "hypertrace is reasonable"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click call bar()")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "scalar args are handled properly wrt Promises"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def invokesScalarPromise() return stringPromise()end def stringPromise() wait 20ms return 'foo' end"))))
+ )
+)
+
+;; ── core/runtimeErrors (18 tests) ──
+(defsuite "hs-upstream-core/runtimeErrors"
+ (deftest "reports basic function invocation null errors properly"
+ (error "SKIP (untranslated): reports basic function invocation null errors properly"))
+ (deftest "reports basic function invocation null errors properly w/ of"
+ (error "SKIP (untranslated): reports basic function invocation null errors properly w/ of"))
+ (deftest "reports basic function invocation null errors properly w/ possessives"
+ (error "SKIP (untranslated): reports basic function invocation null errors properly w/ possessives"))
+ (deftest "reports null errors on add command properly"
+ (error "SKIP (untranslated): reports null errors on add command properly"))
+ (deftest "reports null errors on decrement command properly"
+ (error "SKIP (untranslated): reports null errors on decrement command properly"))
+ (deftest "reports null errors on default command properly"
+ (error "SKIP (untranslated): reports null errors on default command properly"))
+ (deftest "reports null errors on hide command properly"
+ (error "SKIP (untranslated): reports null errors on hide command properly"))
+ (deftest "reports null errors on increment command properly"
+ (error "SKIP (untranslated): reports null errors on increment command properly"))
+ (deftest "reports null errors on measure command properly"
+ (error "SKIP (untranslated): reports null errors on measure command properly"))
+ (deftest "reports null errors on put command properly"
+ (error "SKIP (untranslated): reports null errors on put command properly"))
+ (deftest "reports null errors on remove command properly"
+ (error "SKIP (untranslated): reports null errors on remove command properly"))
+ (deftest "reports null errors on send command properly"
+ (error "SKIP (untranslated): reports null errors on send command properly"))
+ (deftest "reports null errors on sets properly"
+ (error "SKIP (untranslated): reports null errors on sets properly"))
+ (deftest "reports null errors on settle command properly"
+ (error "SKIP (untranslated): reports null errors on settle command properly"))
+ (deftest "reports null errors on show command properly"
+ (error "SKIP (untranslated): reports null errors on show command properly"))
+ (deftest "reports null errors on toggle command properly"
+ (error "SKIP (untranslated): reports null errors on toggle command properly"))
+ (deftest "reports null errors on transition command properly"
+ (error "SKIP (untranslated): reports null errors on transition command properly"))
+ (deftest "reports null errors on trigger command properly"
+ (error "SKIP (untranslated): reports null errors on trigger command properly"))
+)
+
+;; ── core/scoping (20 tests) ──
+(defsuite "hs-upstream-core/scoping"
+ (deftest "basic behavior scoping works"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-set-inner-html _el-script "behavior Behave(foo) on click set @out to foo")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "install Behave(foo:10)")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
+ ))
+ (deftest "behavior scoping is at the element level"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-set-inner-html _el-script "behavior Behave(foo) on click 1 set foo to foo + 10 on click 2 set @out to foo")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "install Behave(foo:10)")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "20")
+ ))
+ (deftest "behavior scoping is isolated from other behaviors"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-set-inner-html _el-script "behavior Behave(foo) on click 1 set foo to foo + 10 on click 3 set @out to foo behavior BehaveTwo(foo) on click 2 set element foo to 1 on click 4 set @out2 to foo")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "install Behave(foo:10) install BehaveTwo(foo:42)")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "20")
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out2") "1")
+ ))
+ (deftest "behavior scoping is isolated from the core element scope"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-set-inner-html _el-script "behavior Behave(foo) on click 1 set foo to foo + 10 on click 3 set @out to foo")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "install Behave(foo:10) on click 2 set element foo to 1 on click 4 set @out2 to foo")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "20")
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out2") "1")
+ ))
+ (deftest "element scoped variables are local only to element"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set element x to 10")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-set-attr _el-d2 "_" "on click set @out to x")
+ (dom-append (dom-body) _el-d1)
+ (dom-append _el-d1 _el-d2)
+ (hs-activate! _el-d1)
+ (hs-activate! _el-d2)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (dom-dispatch (dom-query-by-id "d2") "click" nil)
+ ))
+ (deftest "element scoped variables span features"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click 1 set element x to 10 on click 2 set @out to x")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
+ ))
+ (deftest "element scoped variables span features w/short syntax"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click 1 set :x to 10 on click 2 set @out to :x")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
+ ))
+ (deftest "element scoped variables support pseudo-possessive syntax"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set the element's x to 10 then set @out to the element's x")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
+ ))
+ (deftest "element scoped variables work"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set element x to 10 then set @out to x")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
+ ))
+ (deftest "element scoped variables work w/short syntax"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set :x to 10 then set @out to :x")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
+ ))
+ (deftest "global scoped variables span features"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click 1 set $x to 10 on click 2 set @out to $x")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
+ ))
+ (deftest "global scoped variables work"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set global x to 10 then set @out to x")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
+ ))
+ (deftest "global scoped variables work w/ short syntax"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set $x to 10 then set @out to $x")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
+ ))
+ (deftest "locally scoped variables do not span features"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click 1 set x to 10 on click 2 set @out to x")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ ))
+ (deftest "locally scoped variables don't clash with built-in variables"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click repeat for meta in [1, 2, 3] set @out to meta end")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "[object Object]")
+ ))
+ (deftest "locally scoped variables work"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set x to 10 then set @out to x")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
+ ))
+ (deftest "set favors local variables over global variables"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click 1 set foo to 20 then set @out to foo")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "20")
+ ))
+ (deftest "setting a global scoped variable spans features"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click 1 default global x to 0 on click 2 set global x to 10 on click 3 set @out to x")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
+ ))
+ (deftest "setting an element scoped variable spans features"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click 1 default element x to 0 on click 2 set element x to 10 on click 3 set @out to x")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
+ ))
+ (deftest "variables are hoisted"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click if true set foo to 10 end set @out to foo")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
+ ))
+)
+
+;; ── core/security (1 tests) ──
+(defsuite "hs-upstream-core/security"
+ (deftest "on a single div"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click add .foo")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert (not (dom-has-class? (dom-query-by-id "d1") "foo")))
+ ))
+)
+
+;; ── core/sourceInfo (4 tests) ──
+(defsuite "hs-upstream-core/sourceInfo"
+ (deftest "debug"
+ (error "SKIP (untranslated): debug"))
+ (deftest "get line works for statements"
+ (error "SKIP (untranslated): get line works for statements"))
+ (deftest "get source works for expressions"
+ (error "SKIP (untranslated): get source works for expressions"))
+ (deftest "get source works for statements"
+ (error "SKIP (untranslated): get source works for statements"))
+)
+
+;; ── core/tokenizer (17 tests) ──
+(defsuite "hs-upstream-core/tokenizer"
+ (deftest "handles $ in template properly"
+ (error "SKIP (untranslated): handles $ in template properly"))
+ (deftest "handles all special escapes properly"
+ (error "SKIP (untranslated): handles all special escapes properly"))
+ (deftest "handles basic token types"
+ (error "SKIP (untranslated): handles basic token types"))
+ (deftest "handles class identifiers properly"
+ (error "SKIP (untranslated): handles class identifiers properly"))
+ (deftest "handles comments properly"
+ (error "SKIP (untranslated): handles comments properly"))
+ (deftest "handles hex escapes properly"
+ (error "SKIP (untranslated): handles hex escapes properly"))
+ (deftest "handles id references properly"
+ (error "SKIP (untranslated): handles id references properly"))
+ (deftest "handles identifiers properly"
+ (error "SKIP (untranslated): handles identifiers properly"))
+ (deftest "handles identifiers with numbers properly"
+ (error "SKIP (untranslated): handles identifiers with numbers properly"))
+ (deftest "handles look ahead property"
+ (error "SKIP (untranslated): handles look ahead property"))
+ (deftest "handles numbers properly"
+ (error "SKIP (untranslated): handles numbers properly"))
+ (deftest "handles operators properly"
+ (error "SKIP (untranslated): handles operators properly"))
+ (deftest "handles strings properly"
+ (error "SKIP (untranslated): handles strings properly"))
+ (deftest "handles strings properly 2"
+ (error "SKIP (untranslated): handles strings properly 2"))
+ (deftest "handles template bootstrap properly"
+ (error "SKIP (untranslated): handles template bootstrap properly"))
+ (deftest "handles whitespace properly"
+ (error "SKIP (untranslated): handles whitespace properly"))
+ (deftest "string interpolation isnt surprising"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set x to 42 then put `test${x} test ${x} test$x test $x test $x test ${x} test$x test_$x test_${x} test-$x test.$x` into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "test${x} test 42 test$x test 42 test $x test ${x} test42 test_42 test_42 test-42 test.42")
+ ))
+)
+
+;; ── def (27 tests) ──
+(defsuite "hs-upstream-def"
+ (deftest "async finally blocks run normally"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() wait a tick then set window.bar to 10 finally set window.bar to 20 end"))))
+ )
+ (deftest "async finally blocks run when an exception occurs"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() wait a tick then set window.bar to 10 throw \"foo\" finally set window.bar to 20 end"))))
+ )
+ (deftest "can call asynchronously"
+ (error "SKIP (skip-list): can call asynchronously"))
+ (deftest "can catch async exceptions"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def doh() wait 10ms throw \"bar\" end def foo() call doh() catch e set window.bar to e end"))))
+ )
+ (deftest "can catch exceptions"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() throw \"bar\" catch e set window.bar to e end"))))
+ )
+ (deftest "can catch nested async exceptions"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def doh() wait 10ms throw \"bar\" end def foo() call doh() catch e set window.bar to e end"))))
+ )
+ (deftest "can define a basic no arg function"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click call foo()")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? (dom-query-by-id "d1") "called"))
+ ))
+ (deftest "can define a basic one arg function"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click call foo(\"called\")")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "called")
+ ))
+ (deftest "can exit"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() exit end"))))
+ )
+ (deftest "can install a function on an element and use in children w/ no leak"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "def func() put 42 into #d3")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click call func()")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-d1)
+ (dom-append _el-div _el-d2)
+ (dom-append _el-div _el-d3)
+ (hs-activate! _el-div)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d3")) "42")
+ ))
+ (deftest "can install a function on an element and use in children w/ return value"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "def func() return 42")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click put func() into me")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-d1)
+ (dom-append _el-div _el-d2)
+ (dom-append _el-div _el-d3)
+ (hs-activate! _el-div)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "42")
+ ))
+ (deftest "can install a function on an element and use me symbol correctly"
+ (hs-cleanup!)
+ (let ((_el-outer (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-outer "id" "outer")
+ (dom-set-attr _el-outer "_" "def func() put 42 into me")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click call func()")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-append (dom-body) _el-outer)
+ (dom-append _el-outer _el-d1)
+ (dom-append _el-outer _el-d2)
+ (dom-append _el-outer _el-d3)
+ (hs-activate! _el-outer)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "outer")) "42")
+ ))
+ (deftest "can interop with javascript"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() return \"foo\" end"))))
+ )
+ (deftest "can interop with javascript asynchronously"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() wait 1ms return \"foo\" end"))))
+ )
+ (deftest "can rethrow in async catch blocks"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() throw \"bar\" catch e wait 10ms throw e end"))))
+ )
+ (deftest "can rethrow in catch blocks"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() throw \"bar\" catch e throw e end"))))
+ )
+ (deftest "can return a value asynchronously"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click call foo() then put it into #d1.innerText")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "foo")
+ ))
+ (deftest "can return a value synchronously"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click call foo() then put it into #d1.innerText")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "foo")
+ ))
+ (deftest "can return in async catch blocks"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() throw \"bar\" catch e wait 10ms return 42 end"))))
+ )
+ (deftest "can return in catch blocks"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() throw \"bar\" catch e return 42 end"))))
+ )
+ (deftest "can return without a value"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() return end"))))
+ )
+ (deftest "exit stops execution mid-function"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() set x to 1 then exit then set x to 2 then return x end"))))
+ )
+ (deftest "finally blocks run normally"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() set window.bar to 10 finally set window.bar to 20 end"))))
+ )
+ (deftest "finally blocks run when an exception expr occurs"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() set window.bar to 10 call throwsAsyncException() finally set window.bar to 20 end"))))
+ )
+ (deftest "finally blocks run when an exception occurs"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() set window.bar to 10 throw \"foo\" finally set window.bar to 20 end"))))
+ )
+ (deftest "functions can be namespaced"
+ (error "SKIP (skip-list): functions can be namespaced"))
+ (deftest "is called synchronously"
+ (error "SKIP (skip-list): is called synchronously"))
+)
+
+;; ── default (15 tests) ──
+(defsuite "hs-upstream-default"
+ (deftest "can default array elements"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set arr to [null, null] then default arr[0] to 'yes' then put arr[0] into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "yes")
+ ))
+ (deftest "can default attributes"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click default @foo to \"foo\" then put @foo into me")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "foo")
+ ))
+ (deftest "can default of-expression properties"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click default foo of me to 'bar' then put my foo into me")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "bar")
+ ))
+ (deftest "can default possessive properties"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click default #d1's foo to 'bar' then put #d1's foo into me")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "bar")
+ ))
+ (deftest "can default properties"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click default me.foo to \"foo\" then put me.foo into me")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "foo")
+ ))
+ (deftest "can default style ref when unset"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click default *background-color to 'red'")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-get-style _el-div "background-color") "")
+ ))
+ (deftest "can default variables"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click default x to \"foo\" then put x into me")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "foo")
+ ))
+ (deftest "default array element respects existing value"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set arr to ['existing', null] then default arr[0] to 'new' then put arr[0] into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "existing")
+ ))
+ (deftest "default attributes respect existing values"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click default @foo to \"foo\" then put @foo into me")
+ (dom-set-attr _el-d1 "foo" "bar")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "bar")
+ ))
+ (deftest "default overwrites empty string"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set x to \"\" then default x to \"fallback\" then put x into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "fallback")
+ ))
+ (deftest "default preserves false"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set x to false then default x to true then put x into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "false")
+ ))
+ (deftest "default preserves zero"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set x to 0 then default x to 10 then put x into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "0")
+ ))
+ (deftest "default properties respect existing values"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set me.foo to \"bar\" then default me.foo to \"foo\" then put me.foo into me")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "bar")
+ ))
+ (deftest "default style ref preserves existing value"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click default *color to 'red'")
+ (dom-set-attr _el-div "style" "color: blue")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-get-style _el-div "color") "")
+ ))
+ (deftest "default variables respect existing values"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set x to \"bar\" then default x to \"foo\" then put x into me")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "bar")
+ ))
+)
+
+;; ── dialog (12 tests) ──
+(defsuite "hs-upstream-dialog"
+ (deftest "close closes a details element"
+ (hs-cleanup!)
+ (let ((_el-d (dom-create-element "details")) (_el-summary (dom-create-element "summary")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-d "id" "d")
- (dom-set-inner-html _el-p "Hello")
- (dom-set-attr _el-button "_" "on click show #d")
- (dom-set-inner-html _el-button "Open")
+ (dom-set-attr _el-d "open" "")
+ (dom-set-inner-html _el-summary "More")
+ (dom-set-inner-html _el-p "Content")
+ (dom-set-attr _el-button "_" "on click close #d")
+ (dom-set-inner-html _el-button "Close")
(dom-append (dom-body) _el-d)
+ (dom-append _el-d _el-summary)
(dom-append _el-d _el-p)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
- (assert (dom-has-attr? (dom-query-by-id "d") "open"))
+ (assert (not (dom-has-attr? (dom-query-by-id "d") "open")))
+ ))
+ (deftest "close closes a dialog"
+ (hs-cleanup!)
+ (let ((_el-d (dom-create-element "dialog")) (_el-p (dom-create-element "p")) (_el-close (dom-create-element "button")))
+ (dom-set-attr _el-d "id" "d")
+ (dom-set-inner-html _el-p "Hello")
+ (dom-set-attr _el-close "id" "close")
+ (dom-set-attr _el-close "_" "on click close #d")
+ (dom-set-inner-html _el-close "Close")
+ (dom-append (dom-body) _el-d)
+ (dom-append _el-d _el-p)
+ (dom-append _el-d _el-close)
+ (hs-activate! _el-close)
+ (dom-dispatch (dom-query-by-id "close") "click" nil)
+ (assert (not (dom-has-attr? (dom-query-by-id "d") "open")))
+ ))
+ (deftest "close hides a popover"
+ (hs-cleanup!)
+ (let ((_el-p (dom-create-element "div")) (_el-p1 (dom-create-element "p")) (_el-close (dom-create-element "button")))
+ (dom-set-attr _el-p "id" "p")
+ (dom-set-inner-html _el-p1 "Popover content")
+ (dom-set-attr _el-close "id" "close")
+ (dom-set-attr _el-close "_" "on click close #p")
+ (dom-set-inner-html _el-close "Close")
+ (dom-append (dom-body) _el-p)
+ (dom-append _el-p _el-p1)
+ (dom-append _el-p _el-close)
+ (hs-activate! _el-close)
+ (dom-dispatch (dom-query-by-id "close") "click" nil)
))
(deftest "hide closes a dialog"
(hs-cleanup!)
@@ -4179,16 +2847,32 @@
(dom-dispatch (dom-query-by-id "close") "click" nil)
(assert (not (dom-has-attr? (dom-query-by-id "d") "open")))
))
- (deftest "show on already-open dialog is a no-op"
+ (deftest "open on implicit me"
(hs-cleanup!)
- (let ((_el-d (dom-create-element "dialog")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
+ (let ((_el-d (dom-create-element "dialog")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-d "id" "d")
- (dom-set-inner-html _el-p "Hello")
- (dom-set-attr _el-button "_" "on click show #d")
- (dom-set-inner-html _el-button "Show Again")
+ (dom-set-attr _el-d "_" "on myOpen open")
+ (dom-set-attr _el-button "_" "on click send myOpen to #d")
+ (dom-set-inner-html _el-button "Open")
(dom-append (dom-body) _el-d)
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-d)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (assert (dom-has-attr? (dom-query-by-id "d") "open"))
+ ))
+ (deftest "open opens a details element"
+ (hs-cleanup!)
+ (let ((_el-d (dom-create-element "details")) (_el-summary (dom-create-element "summary")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-d "id" "d")
+ (dom-set-inner-html _el-summary "More")
+ (dom-set-inner-html _el-p "Content")
+ (dom-set-attr _el-button "_" "on click open #d")
+ (dom-set-inner-html _el-button "Open")
+ (dom-append (dom-body) _el-d)
+ (dom-append _el-d _el-summary)
(dom-append _el-d _el-p)
- (dom-append _el-d _el-button)
+ (dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert (dom-has-attr? (dom-query-by-id "d") "open"))
@@ -4207,53 +2891,18 @@
(dom-dispatch _el-button "click" nil)
(assert (dom-has-attr? (dom-query-by-id "d") "open"))
))
- (deftest "close closes a dialog"
+ (deftest "open opens a modal dialog (matches :modal)"
(hs-cleanup!)
- (let ((_el-d (dom-create-element "dialog")) (_el-p (dom-create-element "p")) (_el-close (dom-create-element "button")))
+ (let ((_el-d (dom-create-element "dialog")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-d "id" "d")
(dom-set-inner-html _el-p "Hello")
- (dom-set-attr _el-close "id" "close")
- (dom-set-attr _el-close "_" "on click close #d")
- (dom-set-inner-html _el-close "Close")
- (dom-append (dom-body) _el-d)
- (dom-append _el-d _el-p)
- (dom-append _el-d _el-close)
- (hs-activate! _el-close)
- (dom-dispatch (dom-query-by-id "close") "click" nil)
- (assert (not (dom-has-attr? (dom-query-by-id "d") "open")))
- ))
- (deftest "open opens a details element"
- (hs-cleanup!)
- (let ((_el-d (dom-create-element "details")) (_el-summary (dom-create-element "summary")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-d "id" "d")
- (dom-set-inner-html _el-summary "More")
- (dom-set-inner-html _el-p "Content")
(dom-set-attr _el-button "_" "on click open #d")
(dom-set-inner-html _el-button "Open")
(dom-append (dom-body) _el-d)
- (dom-append _el-d _el-summary)
(dom-append _el-d _el-p)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
- (assert (dom-has-attr? (dom-query-by-id "d") "open"))
- ))
- (deftest "close closes a details element"
- (hs-cleanup!)
- (let ((_el-d (dom-create-element "details")) (_el-summary (dom-create-element "summary")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-d "id" "d")
- (dom-set-attr _el-d "open" "")
- (dom-set-inner-html _el-summary "More")
- (dom-set-inner-html _el-p "Content")
- (dom-set-attr _el-button "_" "on click close #d")
- (dom-set-inner-html _el-button "Close")
- (dom-append (dom-body) _el-d)
- (dom-append _el-d _el-summary)
- (dom-append _el-d _el-p)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (assert (not (dom-has-attr? (dom-query-by-id "d") "open")))
))
(deftest "open shows a popover"
(hs-cleanup!)
@@ -4268,98 +2917,89 @@
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
))
- (deftest "close hides a popover"
+ (deftest "show on already-open dialog is a no-op"
(hs-cleanup!)
- (let ((_el-p (dom-create-element "div")) (_el-p1 (dom-create-element "p")) (_el-close (dom-create-element "button")))
- (dom-set-attr _el-p "id" "p")
- (dom-set-inner-html _el-p1 "Popover content")
- (dom-set-attr _el-close "id" "close")
- (dom-set-attr _el-close "_" "on click close #p")
- (dom-set-inner-html _el-close "Close")
- (dom-append (dom-body) _el-p)
- (dom-append _el-p _el-p1)
- (dom-append _el-p _el-close)
- (hs-activate! _el-close)
- (dom-dispatch (dom-query-by-id "close") "click" nil)
- ))
- (deftest "open on implicit me"
- (hs-cleanup!)
- (let ((_el-d (dom-create-element "dialog")) (_el-button (dom-create-element "button")))
+ (let ((_el-d (dom-create-element "dialog")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-d "id" "d")
- (dom-set-attr _el-d "_" "on myOpen open")
- (dom-set-attr _el-button "_" "on click send myOpen to #d")
- (dom-set-inner-html _el-button "Open")
+ (dom-set-inner-html _el-p "Hello")
+ (dom-set-attr _el-button "_" "on click show #d")
+ (dom-set-inner-html _el-button "Show Again")
(dom-append (dom-body) _el-d)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-d)
+ (dom-append _el-d _el-p)
+ (dom-append _el-d _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert (dom-has-attr? (dom-query-by-id "d") "open"))
))
+ (deftest "show opens a dialog (non-modal)"
+ (hs-cleanup!)
+ (let ((_el-d (dom-create-element "dialog")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-d "id" "d")
+ (dom-set-inner-html _el-p "Hello")
+ (dom-set-attr _el-button "_" "on click show #d")
+ (dom-set-inner-html _el-button "Open")
+ (dom-append (dom-body) _el-d)
+ (dom-append _el-d _el-p)
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (assert (dom-has-attr? (dom-query-by-id "d") "open"))
+ ))
+ (deftest "show opens a non-modal dialog (no ::backdrop)"
+ (hs-cleanup!)
+ (let ((_el-d (dom-create-element "dialog")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-d "id" "d")
+ (dom-set-inner-html _el-p "Hello")
+ (dom-set-attr _el-button "_" "on click show #d")
+ (dom-set-inner-html _el-button "Open")
+ (dom-append (dom-body) _el-d)
+ (dom-append _el-d _el-p)
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
)
;; ── empty (13 tests) ──
(defsuite "hs-upstream-empty"
- (deftest "can empty an element"
+ (deftest "can empty a checkbox"
(hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-p2 (dom-create-element "p")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-inner-html _el-p "hello")
- (dom-set-inner-html _el-p2 "world")
- (dom-set-attr _el-button "_" "on click empty #d1")
- (dom-append (dom-body) _el-d1)
- (dom-append _el-d1 _el-p)
- (dom-append _el-d1 _el-p2)
+ (let ((_el-cb1 (dom-create-element "input")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-cb1 "id" "cb1")
+ (dom-set-attr _el-cb1 "type" "checkbox")
+ (dom-set-attr _el-cb1 "checked" "")
+ (dom-set-attr _el-button "_" "on click empty #cb1")
+ (dom-set-inner-html _el-button "Empty")
+ (dom-append (dom-body) _el-cb1)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
- (assert= (dom-text-content (dom-query-by-id "d1")) "")
+ (assert (not (dom-get-prop (dom-query-by-id "cb1") "checked")))
))
- (deftest "empty with no target empties me"
+ (deftest "can empty a form (clears all inputs)"
(hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click empty")
- (dom-set-inner-html _el-div "content")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "")
- ))
- (deftest "can empty multiple elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-div2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-button (dom-create-element "button")))
- (dom-add-class _el-div "clearme")
- (dom-set-inner-html _el-p "a")
- (dom-add-class _el-div2 "clearme")
- (dom-set-inner-html _el-p3 "b")
- (dom-set-attr _el-button "_" "on click empty .clearme")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-p)
- (dom-append (dom-body) _el-div2)
- (dom-append _el-div2 _el-p3)
+ (let ((_el-f1 (dom-create-element "form")) (_el-t2 (dom-create-element "input")) (_el-ta2 (dom-create-element "textarea")) (_el-cb2 (dom-create-element "input")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-f1 "id" "f1")
+ (dom-set-attr _el-t2 "id" "t2")
+ (dom-set-attr _el-t2 "type" "text")
+ (dom-set-attr _el-t2 "value" "val")
+ (dom-set-attr _el-ta2 "id" "ta2")
+ (dom-set-inner-html _el-ta2 "text")
+ (dom-set-attr _el-cb2 "id" "cb2")
+ (dom-set-attr _el-cb2 "type" "checkbox")
+ (dom-set-attr _el-cb2 "checked" "")
+ (dom-set-attr _el-button "_" "on click empty #f1")
+ (dom-set-inner-html _el-button "Empty")
+ (dom-append (dom-body) _el-f1)
+ (dom-append _el-f1 _el-t2)
+ (dom-append _el-f1 _el-ta2)
+ (dom-append _el-f1 _el-cb2)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
- (assert= (dom-text-content (dom-query ".clearme")) "")
- (assert= (dom-text-content (dom-query ".clearme")) "")
- ))
- (deftest "can empty an array"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set :arr to [1,2,3] then empty :arr then put :arr.length into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "0")
- ))
- (deftest "can empty a set"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set :s to [1,2,3] as Set then empty :s then put :s.size into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "0")
+ (assert= (dom-get-prop (dom-query-by-id "t2") "value") "")
+ (assert= (dom-get-prop (dom-query-by-id "ta2") "value") "")
+ (assert (not (dom-get-prop (dom-query-by-id "cb2") "checked")))
))
(deftest "can empty a map"
(hs-cleanup!)
@@ -4370,6 +3010,33 @@
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "0")
))
+ (deftest "can empty a select"
+ (hs-cleanup!)
+ (let ((_el-sel1 (dom-create-element "select")) (_el-option (dom-create-element "option")) (_el-option2 (dom-create-element "option")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-sel1 "id" "sel1")
+ (dom-set-attr _el-option "value" "a")
+ (dom-set-inner-html _el-option "A")
+ (dom-set-attr _el-option2 "value" "b")
+ (dom-set-attr _el-option2 "selected" "")
+ (dom-set-inner-html _el-option2 "B")
+ (dom-set-attr _el-button "_" "on click empty #sel1")
+ (dom-set-inner-html _el-button "Empty")
+ (dom-append (dom-body) _el-sel1)
+ (dom-append _el-sel1 _el-option)
+ (dom-append _el-sel1 _el-option2)
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "can empty a set"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set :s to [1,2,3] as Set then empty :s then put :s.size into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "0")
+ ))
(deftest "can empty a text input"
(hs-cleanup!)
(let ((_el-t1 (dom-create-element "input")) (_el-button (dom-create-element "button")))
@@ -4397,62 +3064,47 @@
(dom-dispatch _el-button "click" nil)
(assert= (dom-get-prop (dom-query-by-id "ta1") "value") "")
))
- (deftest "can empty a checkbox"
+ (deftest "can empty an array"
(hs-cleanup!)
- (let ((_el-cb1 (dom-create-element "input")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-cb1 "id" "cb1")
- (dom-set-attr _el-cb1 "type" "checkbox")
- (dom-set-attr _el-cb1 "checked" "")
- (dom-set-attr _el-button "_" "on click empty #cb1")
- (dom-set-inner-html _el-button "Empty")
- (dom-append (dom-body) _el-cb1)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (assert (not (dom-get-prop (dom-query-by-id "cb1") "checked")))
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set :arr to [1,2,3] then empty :arr then put :arr.length into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "0")
))
- (deftest "can empty a select"
+ (deftest "can empty an element"
(hs-cleanup!)
- (let ((_el-sel1 (dom-create-element "select")) (_el-option (dom-create-element "option")) (_el-option2 (dom-create-element "option")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-sel1 "id" "sel1")
- (dom-set-attr _el-option "value" "a")
- (dom-set-inner-html _el-option "A")
- (dom-set-attr _el-option2 "value" "b")
- (dom-set-attr _el-option2 "selected" "")
- (dom-set-inner-html _el-option2 "B")
- (dom-set-attr _el-button "_" "on click empty #sel1")
- (dom-set-inner-html _el-button "Empty")
- (dom-append (dom-body) _el-sel1)
- (dom-append _el-sel1 _el-option)
- (dom-append _el-sel1 _el-option2)
+ (let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-p2 (dom-create-element "p")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-inner-html _el-p "hello")
+ (dom-set-inner-html _el-p2 "world")
+ (dom-set-attr _el-button "_" "on click empty #d1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append _el-d1 _el-p)
+ (dom-append _el-d1 _el-p2)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "")
))
- (deftest "can empty a form (clears all inputs)"
+ (deftest "can empty multiple elements"
(hs-cleanup!)
- (let ((_el-f1 (dom-create-element "form")) (_el-t2 (dom-create-element "input")) (_el-ta2 (dom-create-element "textarea")) (_el-cb2 (dom-create-element "input")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-f1 "id" "f1")
- (dom-set-attr _el-t2 "id" "t2")
- (dom-set-attr _el-t2 "type" "text")
- (dom-set-attr _el-t2 "value" "val")
- (dom-set-attr _el-ta2 "id" "ta2")
- (dom-set-inner-html _el-ta2 "text")
- (dom-set-attr _el-cb2 "id" "cb2")
- (dom-set-attr _el-cb2 "type" "checkbox")
- (dom-set-attr _el-cb2 "checked" "")
- (dom-set-attr _el-button "_" "on click empty #f1")
- (dom-set-inner-html _el-button "Empty")
- (dom-append (dom-body) _el-f1)
- (dom-append _el-f1 _el-t2)
- (dom-append _el-f1 _el-ta2)
- (dom-append _el-f1 _el-cb2)
+ (let ((_el-div (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-div2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-button (dom-create-element "button")))
+ (dom-add-class _el-div "clearme")
+ (dom-set-inner-html _el-p "a")
+ (dom-add-class _el-div2 "clearme")
+ (dom-set-inner-html _el-p3 "b")
+ (dom-set-attr _el-button "_" "on click empty .clearme")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-p)
+ (dom-append (dom-body) _el-div2)
+ (dom-append _el-div2 _el-p3)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
- (assert= (dom-get-prop (dom-query-by-id "t2") "value") "")
- (assert= (dom-get-prop (dom-query-by-id "ta2") "value") "")
- (assert (not (dom-get-prop (dom-query-by-id "cb2") "checked")))
+ (assert= (dom-text-content (dom-query ".clearme")) "")
+ (assert= (dom-text-content (dom-query ".clearme")) "")
))
(deftest "clear is an alias for empty"
(hs-cleanup!)
@@ -4481,2439 +3133,352 @@
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query-by-id "d2")) "")
))
-)
-
-;; ── focus (3 tests) ──
-(defsuite "hs-upstream-focus"
- (deftest "can focus an element"
- (hs-cleanup!)
- (let ((_el-i1 (dom-create-element "input")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-i1 "id" "i1")
- (dom-set-attr _el-button "_" "on click focus #i1")
- (dom-append (dom-body) _el-i1)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- ))
- (deftest "focus with no target focuses me"
- (hs-cleanup!)
- (let ((_el-i1 (dom-create-element "input")))
- (dom-set-attr _el-i1 "id" "i1")
- (dom-set-attr _el-i1 "_" "on click focus")
- (dom-append (dom-body) _el-i1)
- (hs-activate! _el-i1)
- (dom-dispatch (dom-query-by-id "i1") "click" nil)
- ))
- (deftest "can blur an element"
- (hs-cleanup!)
- (let ((_el-i1 (dom-create-element "input")))
- (dom-set-attr _el-i1 "id" "i1")
- (dom-set-attr _el-i1 "_" "on focus wait 10ms then blur me")
- (dom-append (dom-body) _el-i1)
- (hs-activate! _el-i1)
- (dom-focus (dom-query-by-id "i1"))
- ))
-)
-
-;; ── go (5 tests) ──
-(defsuite "hs-upstream-go"
- (deftest "can parse go to with string URL"
+ (deftest "empty with no target empties me"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click go to \"#test-hash\"")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "deprecated url keyword still parses"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click go to url /test")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "go to naked URL starting with / parses"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click go to /test/path")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "go to element scrolls"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-target (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-set-attr _el-div "style" "height: 2000px")
- (dom-set-attr _el-target "id" "target")
- (dom-set-inner-html _el-target "Target")
- (dom-set-attr _el-div2 "_" "on click go to #target")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-target)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div2)
- (dom-dispatch (dom-query "div:nth-of-type(3)") "click" nil)
- ))
- (deftest "deprecated scroll form still works"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-target (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-set-attr _el-div "style" "height: 2000px")
- (dom-set-attr _el-target "id" "target")
- (dom-set-inner-html _el-target "Target")
- (dom-set-attr _el-div2 "_" "on click go to the top of #target")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-target)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div2)
- (dom-dispatch (dom-query "div:nth-of-type(3)") "click" nil)
- ))
-)
-
-;; ── halt (7 tests) ──
-(defsuite "hs-upstream-halt"
- (deftest "halts event propagation and default"
- (hs-cleanup!)
- (let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "a")))
- (dom-set-attr _el-outer "id" "outer")
- (dom-set-attr _el-outer "_" "on click add .outer-clicked")
- (dom-set-attr _el-inner "id" "inner")
- (dom-set-attr _el-inner "_" "on click halt")
- (dom-set-attr _el-inner "href" "#shouldnot")
- (dom-set-inner-html _el-inner "click me")
- (dom-append (dom-body) _el-outer)
- (dom-append _el-outer _el-inner)
- (hs-activate! _el-outer)
- (hs-activate! _el-inner)
- (dom-dispatch (dom-query-by-id "inner") "click" nil)
- (assert (not (dom-has-class? (dom-query-by-id "outer") "outer-clicked")))
- ))
- (deftest "halt stops execution after the halt"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click halt then add .should-not-happen")
- (dom-set-inner-html _el-div "test")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (not (dom-has-class? _el-div "should-not-happen")))
- ))
- (deftest "halt the event stops propagation but continues execution"
- (hs-cleanup!)
- (let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "div")))
- (dom-set-attr _el-outer "id" "outer")
- (dom-set-attr _el-outer "_" "on click add .outer-clicked")
- (dom-set-attr _el-inner "id" "inner")
- (dom-set-attr _el-inner "_" "on click halt the event then add .continued")
- (dom-set-inner-html _el-inner "click me")
- (dom-append (dom-body) _el-outer)
- (dom-append _el-outer _el-inner)
- (hs-activate! _el-outer)
- (hs-activate! _el-inner)
- (dom-dispatch (dom-query-by-id "inner") "click" nil)
- (assert (not (dom-has-class? (dom-query-by-id "outer") "outer-clicked")))
- (assert (dom-has-class? (dom-query-by-id "inner") "continued"))
- ))
- (deftest "halt the event's stops propagation but continues execution"
- (hs-cleanup!)
- (let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "div")))
- (dom-set-attr _el-outer "id" "outer")
- (dom-set-attr _el-outer "_" "on click add .outer-clicked")
- (dom-set-attr _el-inner "id" "inner")
- (dom-set-attr _el-inner "_" "on click halt the event's then add .continued")
- (dom-set-inner-html _el-inner "click me")
- (dom-append (dom-body) _el-outer)
- (dom-append _el-outer _el-inner)
- (hs-activate! _el-outer)
- (hs-activate! _el-inner)
- (dom-dispatch (dom-query-by-id "inner") "click" nil)
- (assert (not (dom-has-class? (dom-query-by-id "outer") "outer-clicked")))
- (assert (dom-has-class? (dom-query-by-id "inner") "continued"))
- ))
- (deftest "halt bubbling only stops propagation, not default"
- (hs-cleanup!)
- (let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "div")))
- (dom-set-attr _el-outer "id" "outer")
- (dom-set-attr _el-outer "_" "on click add .outer-clicked")
- (dom-set-attr _el-inner "id" "inner")
- (dom-set-attr _el-inner "_" "on click halt bubbling then add .continued")
- (dom-set-inner-html _el-inner "click me")
- (dom-append (dom-body) _el-outer)
- (dom-append _el-outer _el-inner)
- (hs-activate! _el-outer)
- (hs-activate! _el-inner)
- (dom-dispatch (dom-query-by-id "inner") "click" nil)
- (assert (not (dom-has-class? (dom-query-by-id "outer") "outer-clicked")))
- (assert (not (dom-has-class? (dom-query-by-id "inner") "continued")))
- ))
- (deftest "halt works outside of event context"
- (hs-cleanup!))
- (deftest "halt default only prevents default, not propagation"
- (hs-cleanup!)
- (let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "div")))
- (dom-set-attr _el-outer "id" "outer")
- (dom-set-attr _el-outer "_" "on click add .outer-clicked")
- (dom-set-attr _el-inner "id" "inner")
- (dom-set-attr _el-inner "_" "on click halt default")
- (dom-set-inner-html _el-inner "click me")
- (dom-append (dom-body) _el-outer)
- (dom-append _el-outer _el-inner)
- (hs-activate! _el-outer)
- (hs-activate! _el-inner)
- (dom-dispatch (dom-query-by-id "inner") "click" nil)
- (assert (dom-has-class? (dom-query-by-id "outer") "outer-clicked"))
- ))
-)
-
-;; ── morph (10 tests) ──
-(defsuite "hs-upstream-morph"
- (deftest "basic morph updates content"
- (hs-cleanup!)
- (let ((_el-target (dom-create-element "div")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-target "id" "target")
- (dom-set-inner-html _el-target "old")
- (dom-set-attr _el-button "_" "on click morph #target to \"new
\"")
- (dom-set-inner-html _el-button "go")
- (dom-append (dom-body) _el-target)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-text-content (dom-query-by-id "target")) "new")
- ))
- (deftest "morph preserves element identity"
- (hs-cleanup!)
- (let ((_el-target (dom-create-element "div")) (_el-go (dom-create-element "button")))
- (dom-set-attr _el-target "id" "target")
- (dom-set-inner-html _el-target "old")
- (dom-set-attr _el-go "id" "go")
- (dom-set-attr _el-go "_" "on click morph #target to \"new
\"")
- (dom-set-inner-html _el-go "go")
- (dom-append (dom-body) _el-target)
- (dom-append (dom-body) _el-go)
- (hs-activate! _el-go)
- (dom-dispatch (dom-query-by-id "go") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "target")) "new")
- ))
- (deftest "morph updates attributes"
- (hs-cleanup!)
- (let ((_el-target (dom-create-element "div")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-target "id" "target")
- (dom-add-class _el-target "old")
- (dom-set-inner-html _el-target "content")
- (dom-set-attr _el-button "_" "on click morph #target to \"content
\"")
- (dom-set-inner-html _el-button "go")
- (dom-append (dom-body) _el-target)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (assert (dom-has-class? (dom-query-by-id "target") "new"))
- ))
- (deftest "morph adds new children"
- (hs-cleanup!)
- (let ((_el-target (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-go (dom-create-element "button")))
- (dom-set-attr _el-target "id" "target")
- (dom-set-inner-html _el-span "first")
- (dom-set-attr _el-go "id" "go")
- (dom-set-attr _el-go "_" "on click morph #target to 'firstsecond
'")
- (dom-set-inner-html _el-go "go")
- (dom-append (dom-body) _el-target)
- (dom-append _el-target _el-span)
- (dom-append (dom-body) _el-go)
- (hs-activate! _el-go)
- (dom-dispatch (dom-query-by-id "go") "click" nil)
- ))
- (deftest "morph removes old children"
- (hs-cleanup!)
- (let ((_el-target (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-span2 (dom-create-element "span")) (_el-go (dom-create-element "button")))
- (dom-set-attr _el-target "id" "target")
- (dom-set-inner-html _el-span "first")
- (dom-set-inner-html _el-span2 "second")
- (dom-set-attr _el-go "id" "go")
- (dom-set-attr _el-go "_" "on click morph #target to 'first
'")
- (dom-set-inner-html _el-go "go")
- (dom-append (dom-body) _el-target)
- (dom-append _el-target _el-span)
- (dom-append _el-target _el-span2)
- (dom-append (dom-body) _el-go)
- (hs-activate! _el-go)
- (dom-dispatch (dom-query-by-id "go") "click" nil)
- ))
- (deftest "morph initializes hyperscript on new elements"
- (hs-cleanup!)
- (let ((_el-target (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-go (dom-create-element "button")))
- (dom-set-attr _el-target "id" "target")
- (dom-set-inner-html _el-p "old")
- (dom-set-attr _el-go "id" "go")
- (dom-set-attr _el-go "_" "on click morph #target to ''")
- (dom-set-inner-html _el-go "go")
- (dom-append (dom-body) _el-target)
- (dom-append _el-target _el-p)
- (dom-append (dom-body) _el-go)
- (hs-activate! _el-go)
- (dom-dispatch (dom-query-by-id "go") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "inner")) "new")
- (dom-dispatch (dom-query-by-id "inner") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "inner")) "clicked")
- ))
- (deftest "morph cleans up removed hyperscript elements"
- (hs-cleanup!)
- (let ((_el-target (dom-create-element "div")) (_el-child (dom-create-element "div")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-target "id" "target")
- (dom-set-attr _el-child "id" "child")
- (dom-set-attr _el-child "_" "on click put \"alive\" into me")
- (dom-set-inner-html _el-child "child")
- (dom-set-attr _el-button "_" "on click morph #target to \"\"")
- (dom-set-inner-html _el-button "go")
- (dom-append (dom-body) _el-target)
- (dom-append _el-target _el-child)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-child)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- ))
- (deftest "morph reorders children by id"
- (hs-cleanup!)
- (let ((_el-target (dom-create-element "div")) (_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-target "id" "target")
- (dom-set-attr _el-a "id" "a")
- (dom-set-inner-html _el-a "A")
- (dom-set-attr _el-b "id" "b")
- (dom-set-inner-html _el-b "B")
- (dom-set-attr _el-button "_" "on click morph #target to \"\"")
- (dom-set-inner-html _el-button "go")
- (dom-append (dom-body) _el-target)
- (dom-append _el-target _el-a)
- (dom-append _el-target _el-b)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- ))
- (deftest "morph preserves matched child identity"
- (hs-cleanup!)
- (let ((_el-target (dom-create-element "div")) (_el-child (dom-create-element "div")) (_el-go (dom-create-element "button")))
- (dom-set-attr _el-target "id" "target")
- (dom-set-attr _el-child "id" "child")
- (dom-set-inner-html _el-child "old")
- (dom-set-attr _el-go "id" "go")
- (dom-set-attr _el-go "_" "on click morph #target to \"\"")
- (dom-set-inner-html _el-go "go")
- (dom-append (dom-body) _el-target)
- (dom-append _el-target _el-child)
- (dom-append (dom-body) _el-go)
- (hs-activate! _el-go)
- (dom-dispatch (dom-query-by-id "go") "click" nil)
- ))
- (deftest "morph with variable content"
- (hs-cleanup!)
- (let ((_el-target (dom-create-element "div")) (_el-go (dom-create-element "button")))
- (dom-set-attr _el-target "id" "target")
- (dom-set-inner-html _el-target "original")
- (dom-set-attr _el-go "id" "go")
- (dom-set-attr _el-go "_" "on click set content to \"morphed
\" then morph #target to content")
- (dom-set-inner-html _el-go "go")
- (dom-append (dom-body) _el-target)
- (dom-append (dom-body) _el-go)
- (hs-activate! _el-go)
- (dom-dispatch (dom-query-by-id "go") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "target")) "morphed")
- ))
-)
-
-;; ── reset (8 tests) ──
-(defsuite "hs-upstream-reset"
- (deftest "can reset a form"
- (hs-cleanup!)
- (let ((_el-f1 (dom-create-element "form")) (_el-t1 (dom-create-element "input")) (_el-button (dom-create-element "button")) (_el-rst (dom-create-element "button")))
- (dom-set-attr _el-f1 "id" "f1")
- (dom-set-attr _el-t1 "id" "t1")
- (dom-set-attr _el-t1 "type" "text")
- (dom-set-attr _el-t1 "value" "original")
- (dom-set-attr _el-button "_" "on click set #t1's value to 'changed'")
- (dom-set-attr _el-button "type" "button")
- (dom-set-inner-html _el-button "Change")
- (dom-set-attr _el-rst "id" "rst")
- (dom-set-attr _el-rst "_" "on click reset #f1")
- (dom-set-attr _el-rst "type" "button")
- (dom-set-inner-html _el-rst "Reset")
- (dom-append (dom-body) _el-f1)
- (dom-append _el-f1 _el-t1)
- (dom-append _el-f1 _el-button)
- (dom-append _el-f1 _el-rst)
- (hs-activate! _el-button)
- (hs-activate! _el-rst)
- (dom-set-prop (dom-query-by-id "t1") "value" "changed")
- (dom-dispatch (dom-query-by-id "t1") "input" nil)
- (assert= (dom-get-prop (dom-query-by-id "t1") "value") "changed")
- (dom-dispatch (dom-query-by-id "rst") "click" nil)
- (assert= (dom-get-prop (dom-query-by-id "t1") "value") "original")
- ))
- (deftest "reset with no target resets me (form)"
- (hs-cleanup!)
- (let ((_el-form (dom-create-element "form")) (_el-t2 (dom-create-element "input")))
- (dom-set-attr _el-form "_" "on custom reset")
- (dom-set-attr _el-t2 "id" "t2")
- (dom-set-attr _el-t2 "type" "text")
- (dom-set-attr _el-t2 "value" "default")
- (dom-append (dom-body) _el-form)
- (dom-append _el-form _el-t2)
- (hs-activate! _el-form)
- (dom-set-prop (dom-query-by-id "t2") "value" "modified")
- (dom-dispatch (dom-query-by-id "t2") "input" nil)
- (assert= (dom-get-prop (dom-query-by-id "t2") "value") "modified")
- (dom-dispatch _el-form "custom" nil)
- (assert= (dom-get-prop (dom-query-by-id "t2") "value") "default")
- ))
- (deftest "can reset a text input to defaultValue"
- (hs-cleanup!)
- (let ((_el-t3 (dom-create-element "input")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-t3 "id" "t3")
- (dom-set-attr _el-t3 "type" "text")
- (dom-set-attr _el-t3 "value" "hello")
- (dom-set-attr _el-button "_" "on click reset #t3")
- (dom-set-inner-html _el-button "Reset")
- (dom-append (dom-body) _el-t3)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-set-prop (dom-query-by-id "t3") "value" "goodbye")
- (dom-dispatch (dom-query-by-id "t3") "input" nil)
- (assert= (dom-get-prop (dom-query-by-id "t3") "value") "goodbye")
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-get-prop (dom-query-by-id "t3") "value") "hello")
- ))
- (deftest "can reset a checkbox"
- (hs-cleanup!)
- (let ((_el-cb1 (dom-create-element "input")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-cb1 "id" "cb1")
- (dom-set-attr _el-cb1 "type" "checkbox")
- (dom-set-attr _el-cb1 "checked" "")
- (dom-set-attr _el-button "_" "on click reset #cb1")
- (dom-set-inner-html _el-button "Reset")
- (dom-append (dom-body) _el-cb1)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-set-prop (dom-query-by-id "cb1") "checked" false)
- (dom-dispatch (dom-query-by-id "cb1") "change" nil)
- (assert (not (dom-get-prop (dom-query-by-id "cb1") "checked")))
- (dom-dispatch _el-button "click" nil)
- (assert (dom-get-prop (dom-query-by-id "cb1") "checked"))
- ))
- (deftest "can reset an unchecked checkbox"
- (hs-cleanup!)
- (let ((_el-cb2 (dom-create-element "input")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-cb2 "id" "cb2")
- (dom-set-attr _el-cb2 "type" "checkbox")
- (dom-set-attr _el-button "_" "on click reset #cb2")
- (dom-set-inner-html _el-button "Reset")
- (dom-append (dom-body) _el-cb2)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-set-prop (dom-query-by-id "cb2") "checked" true)
- (dom-dispatch (dom-query-by-id "cb2") "change" nil)
- (assert (dom-get-prop (dom-query-by-id "cb2") "checked"))
- (dom-dispatch _el-button "click" nil)
- (assert (not (dom-get-prop (dom-query-by-id "cb2") "checked")))
- ))
- (deftest "can reset a textarea"
- (hs-cleanup!)
- (let ((_el-ta1 (dom-create-element "textarea")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-ta1 "id" "ta1")
- (dom-set-inner-html _el-ta1 "original text")
- (dom-set-attr _el-button "_" "on click reset #ta1")
- (dom-set-inner-html _el-button "Reset")
- (dom-append (dom-body) _el-ta1)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-set-prop (dom-query-by-id "ta1") "value" "new text")
- (dom-dispatch (dom-query-by-id "ta1") "input" nil)
- (assert= (dom-get-prop (dom-query-by-id "ta1") "value") "new text")
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-get-prop (dom-query-by-id "ta1") "value") "original text")
- ))
- (deftest "can reset a select"
- (hs-cleanup!)
- (let ((_el-sel1 (dom-create-element "select")) (_el-option (dom-create-element "option")) (_el-option2 (dom-create-element "option")) (_el-option3 (dom-create-element "option")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-sel1 "id" "sel1")
- (dom-set-attr _el-option "value" "a")
- (dom-set-inner-html _el-option "A")
- (dom-set-attr _el-option2 "value" "b")
- (dom-set-attr _el-option2 "selected" "")
- (dom-set-inner-html _el-option2 "B")
- (dom-set-attr _el-option3 "value" "c")
- (dom-set-inner-html _el-option3 "C")
- (dom-set-attr _el-button "_" "on click reset #sel1")
- (dom-set-inner-html _el-button "Reset")
- (dom-append (dom-body) _el-sel1)
- (dom-append _el-sel1 _el-option)
- (dom-append _el-sel1 _el-option2)
- (dom-append _el-sel1 _el-option3)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-set-prop (dom-query-by-id "sel1") "value" "c")
- (dom-dispatch (dom-query-by-id "sel1") "change" nil)
- (assert= (dom-get-prop (dom-query-by-id "sel1") "value") "c")
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-get-prop (dom-query-by-id "sel1") "value") "b")
- ))
- (deftest "can reset multiple inputs"
- (hs-cleanup!)
- (let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")) (_el-button (dom-create-element "button")))
- (dom-add-class _el-input "resettable")
- (dom-set-attr _el-input "type" "text")
- (dom-set-attr _el-input "value" "one")
- (dom-add-class _el-input1 "resettable")
- (dom-set-attr _el-input1 "type" "text")
- (dom-set-attr _el-input1 "value" "two")
- (dom-set-attr _el-button "_" "on click reset .resettable")
- (dom-set-inner-html _el-button "Reset")
- (dom-append (dom-body) _el-input)
- (dom-append (dom-body) _el-input1)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-set-prop (dom-query ".resettable") "value" "changed1")
- (dom-dispatch (dom-query ".resettable") "input" nil)
- (dom-set-prop (dom-query ".resettable") "value" "changed2")
- (dom-dispatch (dom-query ".resettable") "input" nil)
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-get-prop (dom-query ".resettable") "value") "one")
- (assert= (dom-get-prop (dom-query ".resettable") "value") "two")
- ))
-)
-
-;; ── scroll (8 tests) ──
-(defsuite "hs-upstream-scroll"
- (deftest "can scroll to an element"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-target (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-set-attr _el-div "style" "height: 2000px")
- (dom-set-attr _el-target "id" "target")
- (dom-set-inner-html _el-target "Target")
- (dom-set-attr _el-div2 "_" "on click scroll to #target")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-target)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div2)
- (dom-dispatch (dom-query "div:nth-of-type(3)") "click" nil)
- ))
- (deftest "can scroll to top of element"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-target (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
- (dom-set-attr _el-div "style" "height: 2000px")
- (dom-set-attr _el-target "id" "target")
- (dom-set-attr _el-target "style" "height: 200px")
- (dom-set-inner-html _el-target "Target")
- (dom-set-attr _el-div2 "_" "on click scroll to the top of #target")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-target)
- (dom-append (dom-body) _el-div2)
- (hs-activate! _el-div2)
- (dom-dispatch (dom-query "div:nth-of-type(3)") "click" nil)
- ))
- (deftest "can scroll down by amount"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")))
- (dom-set-attr _el-div "style" "height: 5000px")
- (dom-set-attr _el-div1 "_" "on click scroll down by 300px")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (hs-activate! _el-div1)
- (dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
- ))
- (deftest "can scroll up by amount"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")))
- (dom-set-attr _el-div "style" "height: 5000px")
- (dom-set-attr _el-div1 "_" "on click scroll up by 100px")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (hs-activate! _el-div1)
- (dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
- ))
- (deftest "can scroll by without direction (defaults to down)"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")))
- (dom-set-attr _el-div "style" "height: 5000px")
- (dom-set-attr _el-div1 "_" "on click scroll by 200px")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (hs-activate! _el-div1)
- (dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
- ))
- (deftest "can scroll container by amount"
- (hs-cleanup!)
- (let ((_el-box (dom-create-element "div")) (_el-div (dom-create-element "div")) (_el-go (dom-create-element "button")))
- (dom-set-attr _el-box "id" "box")
- (dom-set-attr _el-box "style" "height: 100px; overflow: auto")
- (dom-set-attr _el-div "style" "height: 1000px")
- (dom-set-inner-html _el-div "tall")
- (dom-set-attr _el-go "id" "go")
- (dom-set-attr _el-go "_" "on click scroll #box down by 200px")
- (dom-set-inner-html _el-go "go")
- (dom-append (dom-body) _el-box)
- (dom-append _el-box _el-div)
- (dom-append (dom-body) _el-go)
- (hs-activate! _el-go)
- (dom-dispatch (dom-query-by-id "go") "click" nil)
- ))
- (deftest "can scroll to element in container"
- (hs-cleanup!)
- (let ((_el-box (dom-create-element "div")) (_el-div (dom-create-element "div")) (_el-item (dom-create-element "div")) (_el-go (dom-create-element "button")))
- (dom-set-attr _el-box "id" "box")
- (dom-set-attr _el-box "style" "height: 100px; overflow: auto")
- (dom-set-attr _el-div "style" "height: 500px")
- (dom-set-inner-html _el-div "spacer")
- (dom-set-attr _el-item "id" "item")
- (dom-set-inner-html _el-item "target")
- (dom-set-attr _el-go "id" "go")
- (dom-set-attr _el-go "_" "on click scroll to #item in #box")
- (dom-set-inner-html _el-go "go")
- (dom-append (dom-body) _el-box)
- (dom-append _el-box _el-div)
- (dom-append _el-box _el-item)
- (dom-append (dom-body) _el-go)
- (hs-activate! _el-go)
- (dom-dispatch (dom-query-by-id "go") "click" nil)
- ))
- (deftest "can scroll left by amount"
- (hs-cleanup!)
- (let ((_el-box (dom-create-element "div")) (_el-div (dom-create-element "div")) (_el-go (dom-create-element "button")))
- (dom-set-attr _el-box "id" "box")
- (dom-set-attr _el-box "style" "width: 100px; overflow: auto; white-space: nowrap")
- (dom-set-attr _el-div "style" "width: 5000px; height: 50px")
- (dom-set-inner-html _el-div "wide")
- (dom-set-attr _el-go "id" "go")
- (dom-set-attr _el-go "_" "on click scroll #box right by 300px")
- (dom-set-inner-html _el-go "go")
- (dom-append (dom-body) _el-box)
- (dom-append _el-box _el-div)
- (dom-append (dom-body) _el-go)
- (hs-activate! _el-go)
- (dom-dispatch (dom-query-by-id "go") "click" nil)
- ))
-)
-
-;; ── select (4 tests) ──
-(defsuite "hs-upstream-select"
- (deftest "selects text in an input"
- (hs-cleanup!)
- (let ((_el-inp (dom-create-element "input")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-inp "id" "inp")
- (dom-set-attr _el-inp "value" "hello world")
- (dom-set-attr _el-button "_" "on click select #inp")
- (dom-set-inner-html _el-button "Select")
- (dom-append (dom-body) _el-inp)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- ))
- (deftest "selects text in a textarea"
- (hs-cleanup!)
- (let ((_el-ta (dom-create-element "textarea")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-ta "id" "ta")
- (dom-set-inner-html _el-ta "some text")
- (dom-set-attr _el-button "_" "on click select #ta")
- (dom-set-inner-html _el-button "Select")
- (dom-append (dom-body) _el-ta)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- ))
- (deftest "selects implicit me"
- (hs-cleanup!)
- (let ((_el-inp (dom-create-element "input")))
- (dom-set-attr _el-inp "id" "inp")
- (dom-set-attr _el-inp "_" "on click select")
- (dom-set-attr _el-inp "value" "test")
- (dom-append (dom-body) _el-inp)
- (hs-activate! _el-inp)
- (dom-dispatch (dom-query-by-id "inp") "click" nil)
- ))
- (deftest "returns selected text"
- (hs-cleanup!)
- (let ((_el-text (dom-create-element "p")) (_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
- (dom-set-attr _el-text "id" "text")
- (dom-set-inner-html _el-text "Hello World")
- (dom-set-attr _el-button "_" "on click put the selection into #out")
- (dom-set-inner-html _el-button "Get")
- (dom-set-attr _el-out "id" "out")
- (dom-append (dom-body) _el-text)
- (dom-append (dom-body) _el-button)
- (dom-append (dom-body) _el-out)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-text-content (dom-query-by-id "out")) "Hello")
- ))
-)
-
-;; ── swap (4 tests) ──
-(defsuite "hs-upstream-swap"
- (deftest "can swap two variables"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click set x to \"a\" then set y to \"b\" then swap x with y then put x + y into me")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch (dom-query-by-id "d1") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "d1")) "ba")
- ))
- (deftest "can swap two properties"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-a (dom-create-element "span")) (_el-b (dom-create-element "span")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click set #a.textContent to \"hello\" then set #b.textContent to \"world\" then swap #a.textContent with #b.textContent")
- (dom-set-attr _el-a "id" "a")
- (dom-set-inner-html _el-a "x")
- (dom-set-attr _el-b "id" "b")
- (dom-set-inner-html _el-b "y")
- (dom-append (dom-body) _el-d1)
- (dom-append (dom-body) _el-a)
- (dom-append (dom-body) _el-b)
- (hs-activate! _el-d1)
- (dom-dispatch (dom-query-by-id "d1") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "a")) "world")
- (assert= (dom-text-content (dom-query-by-id "b")) "hello")
- ))
- (deftest "can swap array elements"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click set arr to [1,2,3] then swap arr[0] with arr[2] then put arr as String into me")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch (dom-query-by-id "d1") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "d1")) "3,2,1")
- ))
- (deftest "can swap a variable with a property"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-target (dom-create-element "span")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click set x to \"old\" then set #target.dataset.val to \"new\" then swap x with #target.dataset.val then put x into me")
- (dom-set-attr _el-target "id" "target")
- (dom-set-attr _el-target "data-val" "x")
- (dom-append (dom-body) _el-d1)
- (dom-append (dom-body) _el-target)
- (hs-activate! _el-d1)
- (dom-dispatch (dom-query-by-id "d1") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "d1")) "new")
- (assert= (dom-get-attr (dom-query-by-id "target") "data-val") "old")
- ))
-)
-
-;; ── bind (44 tests) ──
-(defsuite "hs-upstream-bind"
- (deftest "syncs variable and input value in both directions"
- (hs-cleanup!)
- (let ((_el-name-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-name-input "id" "name-input")
- (dom-set-attr _el-name-input "type" "text")
- (dom-set-attr _el-name-input "value" "Alice")
- (dom-set-attr _el-span "_" "bind $name and #name-input.value end when $name changes put it into me")
- (dom-append (dom-body) _el-name-input)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-span)
- ))
- (deftest "syncs variable and attribute in both directions"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "bind $theme and @data-theme")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "dedup prevents infinite loop in two-way bind"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "bind $color and @data-color")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "\"with\" is a synonym for \"and\""
- (hs-cleanup!)
- (let ((_el-city-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-city-input "id" "city-input")
- (dom-set-attr _el-city-input "type" "text")
- (dom-set-attr _el-city-input "value" "Paris")
- (dom-set-attr _el-span "_" "bind $city to #city-input.value end when $city changes put it into me")
- (dom-append (dom-body) _el-city-input)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-span)
- ))
- (deftest "shorthand on text input binds to value"
- (hs-cleanup!)
- (let ((_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-input "_" "bind $greeting to me end when $greeting changes put it into next ")
- (dom-set-attr _el-input "type" "text")
- (dom-set-attr _el-input "value" "hello")
- (dom-append (dom-body) _el-input)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-input)
- (dom-set-prop _el-input "value" "goodbye")
- (dom-dispatch _el-input "input" nil)
- ))
- (deftest "shorthand on checkbox binds to checked"
- (hs-cleanup!)
- (let ((_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-input "_" "bind $isDarkMode to me")
- (dom-set-attr _el-input "type" "checkbox")
- (dom-set-attr _el-span "_" "when $isDarkMode changes put it into me")
- (dom-append (dom-body) _el-input)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-input)
- (hs-activate! _el-span)
- (dom-set-prop _el-input "checked" true)
- (dom-dispatch _el-input "change" nil)
- ))
- (deftest "shorthand on textarea binds to value"
- (hs-cleanup!)
- (let ((_el-textarea (dom-create-element "textarea")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-textarea "_" "bind $bio to me")
- (dom-set-inner-html _el-textarea "Hello world")
- (dom-set-attr _el-span "_" "when $bio changes put it into me")
- (dom-append (dom-body) _el-textarea)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-textarea)
- (hs-activate! _el-span)
- (dom-set-prop _el-textarea "value" "New bio")
- (dom-dispatch _el-textarea "input" nil)
- ))
- (deftest "shorthand on select binds to value"
- (hs-cleanup!)
- (let ((_el-select (dom-create-element "select")) (_el-option (dom-create-element "option")) (_el-option2 (dom-create-element "option")) (_el-option3 (dom-create-element "option")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-select "_" "bind $country to me")
- (dom-set-attr _el-option "value" "us")
- (dom-set-inner-html _el-option "United States")
- (dom-set-attr _el-option2 "value" "uk")
- (dom-set-inner-html _el-option2 "United Kingdom")
- (dom-set-attr _el-option3 "value" "fr")
- (dom-set-inner-html _el-option3 "France")
- (dom-set-attr _el-span "_" "when $country changes put it into me")
- (dom-append (dom-body) _el-select)
- (dom-append _el-select _el-option)
- (dom-append _el-select _el-option2)
- (dom-append _el-select _el-option3)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-select)
- (hs-activate! _el-span)
- (dom-set-prop _el-select "value" "uk")
- (dom-dispatch _el-select "change" nil)
- ))
- (deftest "unsupported element: bind to plain div errors"
- (hs-cleanup!))
- (deftest "shorthand on type=number preserves number type"
- (hs-cleanup!)
- (let ((_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-input "_" "bind $price to me")
- (dom-set-attr _el-input "type" "number")
- (dom-set-attr _el-span "_" "when $price changes put it into me")
- (dom-append (dom-body) _el-input)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-input)
- (hs-activate! _el-span)
- ))
- (deftest "boolean bind to attribute uses presence/absence"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "bind $isEnabled and @data-active")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "boolean bind to aria-* attribute uses \"true\"/\"false\" strings"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "bind $isHidden and @aria-hidden")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "style bind is one-way: variable drives style, not vice versa"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "bind $opacity and *opacity")
- (dom-set-inner-html _el-div "visible")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "same value does not re-set input (prevents cursor jump)"
- (hs-cleanup!)
- (let ((_el-input (dom-create-element "input")))
- (dom-set-attr _el-input "_" "bind $message to me")
- (dom-set-attr _el-input "type" "text")
- (dom-set-attr _el-input "value" "hello")
- (dom-append (dom-body) _el-input)
- (hs-activate! _el-input)
- ))
- (deftest "external JS property write does not sync (known limitation)"
- (hs-cleanup!)
- (let ((_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-input "_" "bind $searchTerm to me")
- (dom-set-attr _el-input "type" "text")
- (dom-set-attr _el-input "value" "original")
- (dom-set-attr _el-span "_" "when $searchTerm changes put it into me")
- (dom-append (dom-body) _el-input)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-input)
- (hs-activate! _el-span)
- ))
- (deftest "form.reset() syncs variable back to default value"
- (hs-cleanup!)
- (let ((_el-test-form (dom-create-element "form")) (_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-test-form "id" "test-form")
- (dom-set-attr _el-input "_" "bind $formField to me")
- (dom-set-attr _el-input "type" "text")
- (dom-set-attr _el-input "value" "default")
- (dom-set-attr _el-span "_" "when $formField changes put it into me")
- (dom-append (dom-body) _el-test-form)
- (dom-append _el-test-form _el-input)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-input)
- (hs-activate! _el-span)
- (dom-set-prop _el-input "value" "user typed this")
- (dom-dispatch _el-input "input" nil)
- ))
- (deftest "clicking a radio sets the variable to its value"
- (hs-cleanup!)
- (let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")) (_el-input2 (dom-create-element "input")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-input "_" "bind $color to me")
- (dom-set-attr _el-input "type" "radio")
- (dom-set-attr _el-input "name" "color")
- (dom-set-attr _el-input "value" "red")
- (dom-set-attr _el-input1 "_" "bind $color to me")
- (dom-set-attr _el-input1 "type" "radio")
- (dom-set-attr _el-input1 "name" "color")
- (dom-set-attr _el-input1 "value" "blue")
- (dom-set-attr _el-input2 "_" "bind $color to me")
- (dom-set-attr _el-input2 "type" "radio")
- (dom-set-attr _el-input2 "name" "color")
- (dom-set-attr _el-input2 "value" "green")
- (dom-set-attr _el-span "_" "when $color changes put it into me")
- (dom-append (dom-body) _el-input)
- (dom-append (dom-body) _el-input1)
- (dom-append (dom-body) _el-input2)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-input)
- (hs-activate! _el-input1)
- (hs-activate! _el-input2)
- (hs-activate! _el-span)
- (dom-dispatch (dom-query "input[value="blue"]") "click" nil)
- (dom-dispatch (dom-query "input[value="green"]") "click" nil)
- ))
- (deftest "setting variable programmatically checks the matching radio"
- (hs-cleanup!)
- (let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")) (_el-input2 (dom-create-element "input")))
- (dom-set-attr _el-input "_" "bind $size to me")
- (dom-set-attr _el-input "type" "radio")
- (dom-set-attr _el-input "name" "size")
- (dom-set-attr _el-input "value" "small")
- (dom-set-attr _el-input1 "_" "bind $size to me")
- (dom-set-attr _el-input1 "type" "radio")
- (dom-set-attr _el-input1 "name" "size")
- (dom-set-attr _el-input1 "value" "medium")
- (dom-set-attr _el-input2 "_" "bind $size to me")
- (dom-set-attr _el-input2 "type" "radio")
- (dom-set-attr _el-input2 "name" "size")
- (dom-set-attr _el-input2 "value" "large")
- (dom-append (dom-body) _el-input)
- (dom-append (dom-body) _el-input1)
- (dom-append (dom-body) _el-input2)
- (hs-activate! _el-input)
- (hs-activate! _el-input1)
- (hs-activate! _el-input2)
- ))
- (deftest "initial value checks the correct radio on load"
- (hs-cleanup!)
- (let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")) (_el-input2 (dom-create-element "input")))
- (dom-set-attr _el-input "_" "bind $fruit to me")
- (dom-set-attr _el-input "type" "radio")
- (dom-set-attr _el-input "name" "fruit")
- (dom-set-attr _el-input "value" "apple")
- (dom-set-attr _el-input1 "_" "bind $fruit to me")
- (dom-set-attr _el-input1 "type" "radio")
- (dom-set-attr _el-input1 "name" "fruit")
- (dom-set-attr _el-input1 "value" "banana")
- (dom-set-attr _el-input2 "_" "bind $fruit to me")
- (dom-set-attr _el-input2 "type" "radio")
- (dom-set-attr _el-input2 "name" "fruit")
- (dom-set-attr _el-input2 "value" "cherry")
- (dom-append (dom-body) _el-input)
- (dom-append (dom-body) _el-input1)
- (dom-append (dom-body) _el-input2)
- (hs-activate! _el-input)
- (hs-activate! _el-input1)
- (hs-activate! _el-input2)
- ))
- (deftest "variable drives class: setting variable adds/removes class"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "bind .dark and $darkMode")
- (dom-set-inner-html _el-div "test")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "external class change syncs back to variable"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "bind .dark and $darkMode")
- (dom-set-inner-html _el-div "test")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "right side wins on class init"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "bind .highlight to $highlighted")
- (dom-set-inner-html _el-div "test")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "init: right side wins — input value (Y) overwrites variable (X)"
- (hs-cleanup!)
- (let ((_el-input (dom-create-element "input")))
- (dom-set-attr _el-input "_" "bind $name to my value")
- (dom-set-attr _el-input "type" "text")
- (dom-set-attr _el-input "value" "Bob")
- (dom-append (dom-body) _el-input)
- (hs-activate! _el-input)
- ))
- (deftest "init: right side wins — variable (Y) overwrites input value (X)"
- (hs-cleanup!)
- (let ((_el-input (dom-create-element "input")))
- (dom-set-attr _el-input "_" "bind my value to $name")
- (dom-set-attr _el-input "type" "text")
- (dom-set-attr _el-input "value" "Bob")
- (dom-append (dom-body) _el-input)
- (hs-activate! _el-input)
- ))
- (deftest "init: right side wins — attribute (Y) initializes variable (X)"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "bind $color to @data-color")
- (dom-set-attr _el-div "data-color" "red")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "init: right side wins — variable (Y) initializes attribute (X)"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "bind @data-theme to $theme")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "init: right side wins — variable (Y) drives class (X)"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "bind .dark to $isDark")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "init: right side wins — class (Y) drives variable (X)"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-add-class _el-div "dark")
- (dom-set-attr _el-div "_" "bind $isDark to .dark")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "possessive property: bind $var to my value"
- (hs-cleanup!)
- (let ((_el-input (dom-create-element "input")))
- (dom-set-attr _el-input "_" "bind $myVal to my value")
- (dom-set-attr _el-input "type" "text")
- (dom-set-attr _el-input "value" "hello")
- (dom-append (dom-body) _el-input)
- (hs-activate! _el-input)
- (dom-set-prop _el-input "value" "world")
- (dom-dispatch _el-input "input" nil)
- ))
- (deftest "possessive attribute: bind $var and my @data-label"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "bind $label and my @data-label")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "of-expression: bind $var to value of #input"
- (hs-cleanup!)
- (let ((_el-of-input (dom-create-element "input")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-of-input "id" "of-input")
- (dom-set-attr _el-of-input "type" "text")
- (dom-set-attr _el-of-input "value" "initial")
- (dom-set-attr _el-div "_" "bind $search to value of #of-input")
- (dom-append (dom-body) _el-of-input)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "class bound to another element checkbox"
- (hs-cleanup!)
- (let ((_el-dark-toggle (dom-create-element "input")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-dark-toggle "id" "dark-toggle")
- (dom-set-attr _el-dark-toggle "type" "checkbox")
- (dom-set-attr _el-div "_" "bind .dark and #dark-toggle's checked")
- (dom-set-inner-html _el-div "test")
- (dom-append (dom-body) _el-dark-toggle)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-set-prop (dom-query-by-id "dark-toggle") "checked" true)
- (dom-dispatch (dom-query-by-id "dark-toggle") "change" nil)
- (dom-set-prop (dom-query-by-id "dark-toggle") "checked" false)
- (dom-dispatch (dom-query-by-id "dark-toggle") "change" nil)
- ))
- (deftest "attribute bound to another element input value"
- (hs-cleanup!)
- (let ((_el-title-input (dom-create-element "input")) (_el-h1 (dom-create-element "h1")))
- (dom-set-attr _el-title-input "id" "title-input")
- (dom-set-attr _el-title-input "type" "text")
- (dom-set-attr _el-title-input "value" "Hello")
- (dom-set-attr _el-h1 "_" "bind @data-title and #title-input's value")
- (dom-append (dom-body) _el-title-input)
- (dom-append (dom-body) _el-h1)
- (hs-activate! _el-h1)
- (dom-set-prop (dom-query-by-id "title-input") "value" "World")
- (dom-dispatch (dom-query-by-id "title-input") "input" nil)
- ))
- (deftest "two inputs synced via bind"
- (hs-cleanup!)
- (let ((_el-slider (dom-create-element "input")) (_el-input (dom-create-element "input")))
- (dom-set-attr _el-slider "id" "slider")
- (dom-set-attr _el-slider "type" "range")
- (dom-set-attr _el-slider "value" "50")
- (dom-set-attr _el-input "_" "bind my value and #slider's value")
- (dom-set-attr _el-input "type" "number")
- (dom-append (dom-body) _el-slider)
- (dom-append (dom-body) _el-input)
- (hs-activate! _el-input)
- ))
- (deftest "bind variable to element by id auto-detects value"
- (hs-cleanup!)
- (let ((_el-name-field (dom-create-element "input")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-name-field "id" "name-field")
- (dom-set-attr _el-name-field "type" "text")
- (dom-set-attr _el-name-field "value" "")
- (dom-set-attr _el-div "_" "bind $name to #name-field")
- (dom-append (dom-body) _el-name-field)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "bind variable to checkbox by id auto-detects checked"
- (hs-cleanup!)
- (let ((_el-agree-cb (dom-create-element "input")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-agree-cb "id" "agree-cb")
- (dom-set-attr _el-agree-cb "type" "checkbox")
- (dom-set-attr _el-div "_" "bind $agreed to #agree-cb")
- (dom-append (dom-body) _el-agree-cb)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "bind variable to number input by id auto-detects valueAsNumber"
- (hs-cleanup!)
- (let ((_el-qty-input (dom-create-element "input")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-qty-input "id" "qty-input")
- (dom-set-attr _el-qty-input "type" "number")
- (dom-set-attr _el-div "_" "bind $qty to #qty-input")
- (dom-append (dom-body) _el-qty-input)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "bind element to element: both sides auto-detect"
- (hs-cleanup!)
- (let ((_el-range-slider (dom-create-element "input")) (_el-input (dom-create-element "input")))
- (dom-set-attr _el-range-slider "id" "range-slider")
- (dom-set-attr _el-range-slider "type" "range")
- (dom-set-attr _el-range-slider "value" "50")
- (dom-set-attr _el-input "_" "bind me to #range-slider")
- (dom-set-attr _el-input "type" "number")
- (dom-append (dom-body) _el-range-slider)
- (dom-append (dom-body) _el-input)
- (hs-activate! _el-input)
- ))
- (deftest "right side wins on init: variable (Y) initializes input (X)"
- (hs-cleanup!)
- (let ((_el-input (dom-create-element "input")))
- (dom-set-attr _el-input "_" "bind me to $name")
- (dom-set-attr _el-input "type" "text")
- (dom-set-attr _el-input "value" "Bob")
- (dom-append (dom-body) _el-input)
- (hs-activate! _el-input)
- ))
- (deftest "right side wins on init: input (Y) initializes variable (X)"
- (hs-cleanup!)
- (let ((_el-input (dom-create-element "input")))
- (dom-set-attr _el-input "_" "bind $name to me")
- (dom-set-attr _el-input "type" "text")
- (dom-set-attr _el-input "value" "Bob")
- (dom-append (dom-body) _el-input)
- (hs-activate! _el-input)
- ))
- (deftest "bind to contenteditable element auto-detects textContent"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "bind $text to me")
- (dom-set-attr _el-div "contenteditable" "true")
- (dom-set-inner-html _el-div "initial")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "bind to custom element with value property auto-detects value"
- (hs-cleanup!)
- (let ((_el-test-input (dom-create-element "test-input")))
- (dom-set-attr _el-test-input "_" "bind $custom to me")
- (dom-append (dom-body) _el-test-input)
- (hs-activate! _el-test-input)
- ))
- (deftest "radio change listener is removed on cleanup"
- (hs-cleanup!)
- (let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")))
- (dom-set-attr _el-input "_" "bind $color to me")
- (dom-set-attr _el-input "type" "radio")
- (dom-set-attr _el-input "name" "color")
- (dom-set-attr _el-input "value" "red")
- (dom-set-attr _el-input "checked" "")
- (dom-set-attr _el-input1 "_" "bind $color to me")
- (dom-set-attr _el-input1 "type" "radio")
- (dom-set-attr _el-input1 "name" "color")
- (dom-set-attr _el-input1 "value" "blue")
- (dom-append (dom-body) _el-input)
- (dom-append (dom-body) _el-input1)
- (hs-activate! _el-input)
- (hs-activate! _el-input1)
- ))
- (deftest "form reset listener is removed on cleanup"
- (hs-cleanup!)
- (let ((_el-form (dom-create-element "form")) (_el-binput (dom-create-element "input")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-binput "id" "binput")
- (dom-set-attr _el-binput "_" "bind $val to me")
- (dom-set-attr _el-binput "type" "text")
- (dom-set-attr _el-binput "value" "initial")
- (dom-set-attr _el-button "type" "reset")
- (dom-set-inner-html _el-button "Reset")
- (dom-append (dom-body) _el-form)
- (dom-append _el-form _el-binput)
- (dom-append _el-form _el-button)
- (hs-activate! _el-binput)
- ))
-)
-
-;; ── live (23 tests) ──
-(defsuite "hs-upstream-live"
- (deftest "derives a variable from a computed expression"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live set $total to ($price * $qty) end when $total changes put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "updates DOM text reactively with put"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live put 'hello ' + $greeting into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "sets an attribute reactively"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live set my @data-theme to $theme")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "sets a style reactively"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live set *opacity to $opacity")
- (dom-set-inner-html _el-div "visible")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "puts a computed dollar amount into the DOM"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live put '$' + ($price * $qty) into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "block form re-runs all commands when any dependency changes"
- (hs-cleanup!)
- (let ((_el-w (dom-create-element "span")) (_el-h (dom-create-element "span")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-w "id" "w")
- (dom-set-attr _el-w "_" "when $doubleWidth changes put it into me")
- (dom-set-attr _el-h "id" "h")
- (dom-set-attr _el-h "_" "when $doubleHeight changes put it into me")
- (dom-set-attr _el-div "_" "live then set $doubleWidth to ($width * 2) then set $doubleHeight to ($height * 2) then end")
- (dom-append (dom-body) _el-w)
- (dom-append (dom-body) _el-h)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-w)
- (hs-activate! _el-h)
- (hs-activate! _el-div)
- ))
- (deftest "separate live statements create independent effects"
- (hs-cleanup!)
- (let ((_el-w (dom-create-element "span")) (_el-h (dom-create-element "span")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-w "id" "w")
- (dom-set-attr _el-w "_" "when $doubleWidth changes put it into me")
- (dom-set-attr _el-h "id" "h")
- (dom-set-attr _el-h "_" "when $doubleHeight changes put it into me")
- (dom-set-attr _el-div "_" "live set $doubleWidth to ($width * 2) end live set $doubleHeight to ($height * 2)")
- (dom-append (dom-body) _el-w)
- (dom-append (dom-body) _el-h)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-w)
- (hs-activate! _el-h)
- (hs-activate! _el-div)
- ))
- (deftest "block form cascades inter-dependent commands"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live then set $subtotal to ($price * $qty) then set $total to ($subtotal + $tax) then end when $total changes put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "toggles a class based on a boolean variable"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live then if $isActive add .active to me else remove .active from me end end")
- (dom-set-inner-html _el-div "test")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "toggles display style based on a boolean variable"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live then if $isVisible set *display to 'block' else set *display to 'none' end end")
+ (dom-set-attr _el-div "_" "on click empty")
(dom-set-inner-html _el-div "content")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "")
))
- (deftest "effects stop when element is removed from DOM"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live put $message into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "conditional branch only tracks the active dependency"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live then if $showFirst put $firstName into me else put $lastName into me end end")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "multiple live on same element work independently"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live set my @data-name to $firstName end live set my @data-age to $age")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "live and when on same element do not interfere"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live set my @data-status to $status end when $status changes put 'Status: ' + it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "bind and live on same element do not interfere"
- (hs-cleanup!)
- (let ((_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-input "_" "bind $username to me end live set my @data-mirror to $username")
- (dom-set-attr _el-input "type" "text")
- (dom-set-attr _el-input "value" "alice")
- (dom-set-attr _el-span "_" "when $username changes put it into me")
- (dom-append (dom-body) _el-input)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-input)
- (hs-activate! _el-span)
- (dom-set-prop _el-input "value" "bob")
- (dom-dispatch _el-input "input" nil)
- ))
- (deftest "reactive effects are stopped on cleanup"
+)
+
+;; ── expressions/arrayIndex (14 tests) ──
+(defsuite "hs-upstream-expressions/arrayIndex"
+ (deftest "can create an array literal"
+ (assert= (eval-hs "[1, 2, 3]") (list 1 2 3))
+ )
+ (deftest "can get the range of first values in an array"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "live put $count into me")
+ (dom-set-attr _el-d1 "_" "on click set var to [0,1,2,3,4,5] then put var[..3] as String into #d1")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
- ))
- (deftest "append triggers live block"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live put $items.join(', ') into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "push via pseudo-command triggers live block"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live put $items.join(', ') into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "push via call triggers live block"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live put $items.join(', ') into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "array + still works with live"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live put $items.join(', ') into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "set property still works with live"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "live put $obj.name into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "property change on object in array triggers live re-render"
- (hs-cleanup!)
- (let ((_el-people-tmpl (dom-create-element "template")) (_el-span (dom-create-element "span")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-people-tmpl "id" "people-tmpl")
- (dom-set-inner-html _el-people-tmpl "#for p in people\\n")
- (dom-set-inner-html _el-span "\\${p.name}")
- (dom-set-attr _el-div "_" "live render #people-tmpl with people: $people then put it into my.innerHTML end")
- (dom-append (dom-body) _el-people-tmpl)
- (dom-append _el-people-tmpl _el-span)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "push object then modify its property both trigger live"
- (hs-cleanup!)
- (let ((_el-items-tmpl (dom-create-element "template")) (_el-span (dom-create-element "span")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-items-tmpl "id" "items-tmpl")
- (dom-set-inner-html _el-items-tmpl "#for item in items\\n")
- (dom-set-inner-html _el-span "\\${item.label}")
- (dom-set-attr _el-div "_" "live render #items-tmpl with items: $items then put it into my.innerHTML end")
- (dom-append (dom-body) _el-items-tmpl)
- (dom-append _el-items-tmpl _el-span)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
-)
-
-;; ── reactive-properties (4 tests) ──
-(defsuite "hs-upstream-reactive-properties"
- (deftest "setting a property on a plain object triggers reactivity"
- (hs-cleanup!)
- (let ((_el-output (dom-create-element "output")))
- (dom-set-attr _el-output "_" "when ($obj's x + $obj's y) changes put it into me")
- (dom-append (dom-body) _el-output)
- (hs-activate! _el-output)
- ))
- (deftest "nested property chain triggers on intermediate reassignment"
- (hs-cleanup!)
- (let ((_el-output (dom-create-element "output")))
- (dom-set-attr _el-output "_" "when $data's inner's val changes put it into me")
- (dom-append (dom-body) _el-output)
- (hs-activate! _el-output)
- ))
- (deftest "property change on DOM element triggers reactivity via setProperty"
- (hs-cleanup!)
- (let ((_el-prop-input (dom-create-element "input")) (_el-output (dom-create-element "output")))
- (dom-set-attr _el-prop-input "id" "prop-input")
- (dom-set-attr _el-prop-input "type" "text")
- (dom-set-attr _el-prop-input "value" "start")
- (dom-set-attr _el-output "_" "when #prop-input's value changes put it into me")
- (dom-append (dom-body) _el-prop-input)
- (dom-append (dom-body) _el-output)
- (hs-activate! _el-output)
- ))
- (deftest "live block tracks property reads on plain objects"
- (hs-cleanup!)
- (let ((_el-output (dom-create-element "output")))
- (dom-set-attr _el-output "_" "live put $config's label into me")
- (dom-append (dom-body) _el-output)
- (hs-activate! _el-output)
- ))
-)
-
-;; ── resize (3 tests) ──
-(defsuite "hs-upstream-resize"
- (deftest "fires when element is resized"
- (hs-cleanup!)
- (let ((_el-box (dom-create-element "div")) (_el-out (dom-create-element "div")))
- (dom-set-attr _el-box "id" "box")
- (dom-set-attr _el-box "_" "on resize put detail.width into #out")
- (dom-set-attr _el-box "style" "width:100px; height:100px")
- (dom-set-attr _el-out "id" "out")
- (dom-append (dom-body) _el-box)
- (dom-append (dom-body) _el-out)
- (hs-activate! _el-box)
- ))
- (deftest "provides height in detail"
- (hs-cleanup!)
- (let ((_el-box (dom-create-element "div")) (_el-out (dom-create-element "div")))
- (dom-set-attr _el-box "id" "box")
- (dom-set-attr _el-box "_" "on resize put detail.height into #out")
- (dom-set-attr _el-box "style" "width:100px; height:100px")
- (dom-set-attr _el-out "id" "out")
- (dom-append (dom-body) _el-box)
- (dom-append (dom-body) _el-out)
- (hs-activate! _el-box)
- ))
- (deftest "works with from clause"
- (hs-cleanup!)
- (let ((_el-box (dom-create-element "div")) (_el-out (dom-create-element "div")))
- (dom-set-attr _el-box "id" "box")
- (dom-set-attr _el-box "style" "width:100px; height:100px")
- (dom-set-attr _el-out "id" "out")
- (dom-set-attr _el-out "_" "on resize from #box put detail.width into me")
- (dom-append (dom-body) _el-box)
- (dom-append (dom-body) _el-out)
- (hs-activate! _el-out)
- ))
-)
-
-;; ── when (41 tests) ──
-(defsuite "hs-upstream-when"
- (deftest "provides access to `it` and syncs initial value"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when $global changes put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "detects changes from $global variable"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when $global changes put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "detects changes from :element variable"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "init set :count to 0 end when :count changes put it into me end on click increment :count")
- (dom-set-inner-html _el-div "0")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "1")
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "2")
- ))
- (deftest "triggers multiple elements watching same variable"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "when $shared changes put 'first' into me")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-set-attr _el-d2 "_" "when $shared changes put 'second' into me")
- (dom-append (dom-body) _el-d1)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-d1)
- (hs-activate! _el-d2)
- ))
- (deftest "executes multiple commands"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when $multi changes put 'first' into me then add .executed to me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "does not execute when variable is undefined initially"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when $neverSet changes put 'synced' into me")
- (dom-set-inner-html _el-div "original")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "only triggers when variable actually changes value"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when $dedup changes increment :callCount then put :callCount into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "auto-tracks compound expressions"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when ($a + $b) changes put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "detects attribute changes"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when @data-title changes put it into me")
- (dom-set-attr _el-div "data-title" "original")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "detects form input value changes via user interaction"
- (hs-cleanup!)
- (let ((_el-reactive-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-reactive-input "id" "reactive-input")
- (dom-set-attr _el-reactive-input "type" "text")
- (dom-set-attr _el-reactive-input "value" "start")
- (dom-set-attr _el-span "_" "when #reactive-input.value changes put it into me")
- (dom-append (dom-body) _el-reactive-input)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-span)
- ))
- (deftest "detects property change via hyperscript set"
- (hs-cleanup!)
- (let ((_el-prog-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-prog-input "id" "prog-input")
- (dom-set-attr _el-prog-input "type" "text")
- (dom-set-attr _el-prog-input "value" "initial")
- (dom-set-attr _el-span "_" "when #prog-input.value changes put it into me")
- (dom-append (dom-body) _el-prog-input)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-span)
- ))
- (deftest "disposes effect when element is removed from DOM"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when $dispose changes put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "batches multiple synchronous writes into one effect run"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when ($batchA + $batchB) changes increment :runCount then put :runCount into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "handles chained reactivity across elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-output (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when $source changes set $derived to (it * 2)")
- (dom-set-attr _el-output "id" "output")
- (dom-set-attr _el-output "_" "when $derived changes put it into me")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-output)
- (hs-activate! _el-div)
- (hs-activate! _el-output)
- ))
- (deftest "supports multiple when features on the same element"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when $left changes put it into my @data-left end when $right changes put it into my @data-right")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "works with on handlers that modify the watched variable"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "init set :label to 'initial' end when :label changes put it into me end on click set :label to 'clicked'")
- (dom-set-inner-html _el-div "initial")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "clicked")
- ))
- (deftest "does not cross-trigger on unrelated variable writes"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when $trigger changes then increment :count then put :count into me then set $other to 'side-effect'")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "handles rapid successive changes correctly"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when $rapid changes put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "isolates element-scoped variables between elements"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "init set :value to 'A' end when :value changes put it into me end on click set :value to 'A-clicked'")
- (dom-set-inner-html _el-d1 "A")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-set-attr _el-d2 "_" "init set :value to 'B' end when :value changes put it into me end on click set :value to 'B-clicked'")
- (dom-set-inner-html _el-d2 "B")
- (dom-append (dom-body) _el-d1)
- (dom-append (dom-body) _el-d2)
- (hs-activate! _el-d1)
- (hs-activate! _el-d2)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "d1")) "A-clicked")
- (assert= (dom-text-content (dom-query-by-id "d2")) "B")
- (dom-dispatch (dom-query-by-id "d2") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "d2")) "B-clicked")
- (assert= (dom-text-content (dom-query-by-id "d1")) "A-clicked")
+ (assert= (dom-text-content (dom-query-by-id "d1")) "0,1,2,3")
))
- (deftest "handles NaN without infinite re-firing"
+ (deftest "can get the range of last values in an array"
(hs-cleanup!)
- (let ((_el-nan-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-nan-input "id" "nan-input")
- (dom-set-attr _el-nan-input "type" "text")
- (dom-set-attr _el-nan-input "value" "not a number")
- (dom-set-attr _el-span "_" "when (#nan-input.value * 1) changes put it into me")
- (dom-append (dom-body) _el-nan-input)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-span)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set var to [0,1,2,3,4,5] then put var[3 ..] as String into #d1")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "3,4,5")
))
- (deftest "fires when either expression changes using or"
+ (deftest "can get the range of last values in an array WITHOUT EXTRA SPACES"
(hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when $x or $y changes put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set var to [0,1,2,3,4,5] then put var[3..] as String into #d1")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "3,4,5")
))
- (deftest "supports three or more expressions with or"
+ (deftest "can get the range of middle values in an array"
(hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when $r or $g or $b changes put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set var to [0,1,2,3,4,5] then put var[2 .. 3] as String into #d1")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "2,3")
))
- (deftest "#element.checked is tracked"
+ (deftest "can get the range of middle values in an array WITHOUT EXTRA SPACES"
(hs-cleanup!)
- (let ((_el-cb-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-cb-input "id" "cb-input")
- (dom-set-attr _el-cb-input "type" "checkbox")
- (dom-set-attr _el-span "_" "when #cb-input.checked changes put it into me")
- (dom-append (dom-body) _el-cb-input)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-span)
- (dom-set-prop (dom-query-by-id "cb-input") "checked" true)
- (dom-dispatch (dom-query-by-id "cb-input") "change" nil)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set var to [0,1,2,3,4,5] then put var[2..3] as String into #d1")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "2,3")
))
- (deftest "my @attr is tracked"
+ (deftest "can get the range of middle values in an array using an expression"
(hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when my @data-x changes put it into me")
- (dom-set-attr _el-div "data-x" "one")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set index to 3 then set var to [0,1,2,3,4,5] then put var[(index-1)..(index+1)] as String into #d1")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "2,3,4")
))
- (deftest "value of #element is tracked"
+ (deftest "can index an array value"
(hs-cleanup!)
- (let ((_el-of-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-of-input "id" "of-input")
- (dom-set-attr _el-of-input "type" "text")
- (dom-set-attr _el-of-input "value" "init")
- (dom-set-attr _el-span "_" "when (value of #of-input) changes put it into me")
- (dom-append (dom-body) _el-of-input)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-span)
- (dom-set-prop (dom-query-by-id "of-input") "value" "changed")
- (dom-dispatch (dom-query-by-id "of-input") "input" nil)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set newVar to [10, 20, 30] then put newVar[0] into #d1.innerHTML")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "10")
))
- (deftest "math on tracked symbols works"
+ (deftest "can index an array value at the beginning of the array"
(hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when ($mA * $mB) changes put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set newVar to [10, 20, 30] then put newVar[0] into #d1.innerHTML")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "10")
))
- (deftest "comparison on tracked symbol works"
+ (deftest "can index an array value at the end of the array"
(hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when ($cmpVal > 5) changes put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set newVar to [10, 20, 30] then put newVar[2] into #d1.innerHTML")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "30")
))
- (deftest "string template with tracked symbol works"
+ (deftest "can index an array value in the middle of the array"
(hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when `hello ${$tplName}` changes put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set newVar to [10, 20, 30] then put newVar[1] into #d1.innerHTML")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "20")
))
- (deftest "function call on tracked value works (Math.round)"
+ (deftest "can index an array value with an expression"
(hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when (Math.round($rawNum)) changes put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set newVar to [\"A\", \"B\", \"C\"] then put newVar[1+1] into #d1.innerHTML")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "C")
))
- (deftest "inline style change via JS is NOT detected"
+ (deftest "errors when index exceeds array length"
(hs-cleanup!)
- (let ((_el-style-target (dom-create-element "div")))
- (dom-set-attr _el-style-target "id" "style-target")
- (dom-set-attr _el-style-target "_" "when (*opacity) changes put it into me")
- (dom-set-attr _el-style-target "style" "opacity: 1")
- (dom-set-inner-html _el-style-target "not fired")
- (dom-append (dom-body) _el-style-target)
- (hs-activate! _el-style-target)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set newVar to [10, 20, 30] then put newVar[10] into #d1.innerHTML")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
))
- (deftest "reassigning whole array IS detected"
+ (deftest "errors when indexed value is not an array"
(hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when $arrWhole changes put it.join(',') into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "mutating array element in place is NOT detected"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when $arrMut[0] changes put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "local variable in when expression produces a parse error"
- (hs-cleanup!))
- (deftest "attribute observers are persistent (not recreated on re-run)"
- (hs-cleanup!))
- (deftest "boolean short-circuit does not track unread branch"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "when ($x and $y) changes put it into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "diamond: cascaded derived values produce correct final value"
- (hs-cleanup!)
- (let ((_el-d-b (dom-create-element "span")) (_el-d-c (dom-create-element "span")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-d-b "id" "d-b")
- (dom-set-attr _el-d-b "_" "when $a changes set $b to (it * 2)")
- (dom-set-attr _el-d-c "id" "d-c")
- (dom-set-attr _el-d-c "_" "when $a changes set $c to (it * 3)")
- (dom-set-attr _el-div "_" "live increment :runs then put ($ (runs:)' into me")
- (dom-append (dom-body) _el-d-b)
- (dom-append (dom-body) _el-d-c)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-d-b)
- (hs-activate! _el-d-c)
- (hs-activate! _el-div)
- ))
- (deftest "error in one effect does not break other effects in the same batch"
- (hs-cleanup!)
- (let ((_el-err-a (dom-create-element "span")))
- (dom-set-attr _el-err-a "id" "err-a")
- (dom-set-attr _el-err-a "_" "when $trigger changes put null.boom into me")
- (dom-append (dom-body) _el-err-a)
- (hs-activate! _el-err-a)
- ))
- (deftest "circular guard resets after cascade settles"
- (hs-cleanup!)
- (let ((_el-span (dom-create-element "span")))
- (dom-set-attr _el-span "_" "when $ping changes set $ping to (i as Values into my.customData")
+ (dom-add-class _el-input "include")
+ (dom-set-attr _el-input "name" "firstName")
+ (dom-set-attr _el-input "value" "John")
+ (dom-add-class _el-input3 "include")
+ (dom-set-attr _el-input3 "name" "lastName")
+ (dom-set-attr _el-input3 "value" "Connor")
+ (dom-add-class _el-input5 "include")
+ (dom-set-attr _el-input5 "name" "areaCode")
+ (dom-set-attr _el-input5 "value" "213")
+ (dom-add-class _el-input6 "dont-include")
+ (dom-set-attr _el-input6 "name" "phone")
+ (dom-set-attr _el-input6 "value" "555-1212")
+ (dom-append (dom-body) _el-qsdiv)
+ (dom-append _el-qsdiv _el-input)
+ (dom-append _el-input _el-br)
+ (dom-append _el-br _el-input3)
+ (dom-append _el-input3 _el-br4)
+ (dom-append _el-br4 _el-input5)
+ (dom-append _el-input5 _el-input6)
+ (hs-activate! _el-qsdiv)
+ (dom-dispatch (dom-query-by-id "qsdiv") "click" nil)
))
- (deftest "closest jumps to matching ancestor"
+ (deftest "converts an array into HTML"
+ (assert= (eval-hs "d as HTML") "`this-is-html`")
+ )
+ (deftest "converts an element into HTML"
+ (error "SKIP (untranslated): converts an element into HTML"))
+ (deftest "converts an input element into Values"
+ (error "SKIP (untranslated): converts an input element into Values"))
+ (deftest "converts array as Reversed"
+ (assert= (eval-hs "[1,2,3] as Reversed") (list 3 2 1))
+ )
+ (deftest "converts array as Set"
+ (error "SKIP (untranslated): converts array as Set"))
+ (deftest "converts array as Unique"
+ (assert= (eval-hs "[1,2,2,3,3] as Unique") (list 1 2 3))
+ )
+ (deftest "converts arrays into fragments"
+ (error "SKIP (untranslated): converts arrays into fragments"))
+ (deftest "converts checkboxes into a Value correctly"
+ (error "SKIP (untranslated): converts checkboxes into a Value correctly"))
+ (deftest "converts elements into fragments"
+ (error "SKIP (untranslated): converts elements into fragments"))
+ (deftest "converts multiple selects into a Value correctly"
+ (error "SKIP (untranslated): converts multiple selects into a Value correctly"))
+ (deftest "converts multiple selects with programmatically changed selections"
+ (error "SKIP (untranslated): converts multiple selects with programmatically changed selections"))
+ (deftest "converts nested array as Flat"
+ (assert= (eval-hs "[[1,2],[3,4]] as Flat") (list 1 2 3 4))
+ )
+ (deftest "converts null as null"
+ (error "SKIP (untranslated): converts null as null"))
+ (deftest "converts numbers things 'HTML'"
+ (assert= (eval-hs "value as HTML") "123")
+ )
+ (deftest "converts object as Entries"
+ (assert= (eval-hs "{a:1} as Entries") (list (list "a" 1)))
+ )
+ (deftest "converts object as Keys"
+ (assert= (eval-hs "{a:1, b:2} as Keys") (list "a" "b"))
+ )
+ (deftest "converts object as Map"
+ (error "SKIP (untranslated): converts object as Map"))
+ (deftest "converts radio buttons into a Value correctly"
+ (error "SKIP (untranslated): converts radio buttons into a Value correctly"))
+ (deftest "converts string as Object"
+ (assert= (host-get (eval-hs "'{\"foo\":\"bar\"}' as Object") "foo") "bar")
+ )
+ (deftest "converts strings into fragments"
+ (error "SKIP (untranslated): converts strings into fragments"))
+ (deftest "converts value as Boolean"
+ (assert= (eval-hs "1 as Boolean") true)
+ (assert= (eval-hs "0 as Boolean") false)
+ (assert= (eval-hs "'' as Boolean") false)
+ (assert= (eval-hs "'hello' as Boolean") true)
+ )
+ (deftest "converts value as Date"
+ (error "SKIP (untranslated): converts value as Date"))
+ (deftest "converts value as Fixed"
+ (assert= (eval-hs "'10.4' as Fixed") "10")
+ (assert= (eval-hs "'10.4' as Fixed") "10.49")
+ )
+ (deftest "converts value as Float"
+ (assert= (eval-hs "'10' as Float") 10)
+ (assert= (eval-hs "'10' as Float") 10.4)
+ )
+ (deftest "converts value as Int"
+ (assert= (eval-hs "'10' as Int") 10)
+ (assert= (eval-hs "'10' as Int") 10)
+ )
+ (deftest "converts value as JSONString"
+ (assert= (eval-hs "{foo:'bar'} as JSONString") "{"foo":"bar"}")
+ )
+ (deftest "converts value as Number"
+ (assert= (eval-hs "'10' as Number") 10)
+ (assert= (eval-hs "'10' as Number") 10.4)
+ )
+ (deftest "converts value as Object"
+ (assert= (host-get (eval-hs "x as Object") "foo") "bar")
+ )
+ (deftest "converts value as String"
+ (assert= (eval-hs "10 as String") "10")
+ (assert= (eval-hs "10 as String") "true")
+ )
+ (deftest "parses string as JSON to object"
+ (assert= (host-get (eval-hs "'{\"foo\":\"bar\"}' as JSON") "foo") "bar")
+ )
+ (deftest "pipe operator chains conversions"
+ (assert= (host-get (eval-hs "{foo:'bar'} as JSONString | JSON") "foo") "bar")
+ )
+)
+
+;; ── expressions/assignableElements (8 tests) ──
+(defsuite "hs-upstream-expressions/assignableElements"
+ (deftest "hyperscript in replacement content is initialized"
(hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
- (dom-add-class _el-div "outer")
- (dom-set-attr _el-div "_" "init set ^val to 'from-outer'")
- (dom-set-attr _el-div1 "_" "init set ^val to 'from-inner'")
- (dom-set-attr _el-div1 "dom-scope" "isolated")
- (dom-set-attr _el-span "_" "init put ^val into me")
- (dom-set-attr _el-span "dom-scope" "closest .outer")
- (dom-set-inner-html _el-span "none")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-div1)
- (dom-append _el-div1 _el-span)
- (hs-activate! _el-div)
- (hs-activate! _el-div1)
- (hs-activate! _el-span)
+ (let ((_el-target (dom-create-element "div")) (_el-go (dom-create-element "button")))
+ (dom-set-attr _el-target "id" "target")
+ (dom-set-inner-html _el-target "old")
+ (dom-set-attr _el-go "id" "go")
+ (dom-set-attr _el-go "_" "on click set #target to 'new
'")
+ (dom-set-inner-html _el-go "go")
+ (dom-append (dom-body) _el-target)
+ (dom-append (dom-body) _el-go)
+ (hs-activate! _el-go)
+ (dom-dispatch (dom-query-by-id "go") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "target")) "new")
+ (dom-dispatch (dom-query-by-id "target") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "target")) "clicked")
))
- (deftest "closest with no match stops resolution"
+ (deftest "put into still works as innerHTML"
(hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-div "_" "init set ^val to 'found'")
- (dom-set-attr _el-span "_" "init if ^val is not undefined put 'leaked' into me else put 'blocked' into me")
- (dom-set-attr _el-span "dom-scope" "closest .nonexistent")
- (dom-set-inner-html _el-span "waiting")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-span)
- (hs-activate! _el-div)
- (hs-activate! _el-span)
- ))
- (deftest "parent of jumps past matching ancestor to its parent"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
- (dom-add-class _el-div "outer")
- (dom-set-attr _el-div "_" "init set ^val to 'from-outer'")
- (dom-add-class _el-div1 "middle")
- (dom-set-attr _el-div1 "_" "init set ^val to 'from-middle'")
- (dom-set-attr _el-div1 "dom-scope" "isolated")
- (dom-set-attr _el-span "_" "init put ^val into me")
- (dom-set-attr _el-span "dom-scope" "parent of .middle")
- (dom-set-inner-html _el-span "none")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-div1)
- (dom-append _el-div1 _el-span)
- (hs-activate! _el-div)
- (hs-activate! _el-div1)
- (hs-activate! _el-span)
- ))
- (deftest "isolated allows setting ^var on the isolated element itself"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-div "_" "init set ^outer to 'leaked'")
- (dom-set-attr _el-div1 "_" "init set ^inner to 'contained'")
- (dom-set-attr _el-div1 "dom-scope" "isolated")
- (dom-set-attr _el-span "_" "init put ^inner into me")
- (dom-set-inner-html _el-span "none")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-div1)
- (dom-append _el-div1 _el-span)
- (hs-activate! _el-div)
- (hs-activate! _el-div1)
- (hs-activate! _el-span)
- ))
- (deftest "child reads ^var set by parent"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-div "_" "init set ^count to 42")
- (dom-set-attr _el-span "_" "on click put ^count into me")
- (dom-set-inner-html _el-span "0")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-span)
- (hs-activate! _el-div)
- (hs-activate! _el-span)
- (dom-dispatch _el-span "click" nil)
- (assert= (dom-text-content _el-span) "42")
- ))
- (deftest "child writes ^var and parent sees it"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-div "_" "init set ^count to 0")
- (dom-set-attr _el-button "_" "on click set ^count to 99")
- (dom-set-inner-html _el-button "set")
- (dom-set-attr _el-span "_" "on click put ^count into me")
- (dom-set-inner-html _el-span "0")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-button)
- (dom-append _el-div _el-span)
- (hs-activate! _el-div)
- (hs-activate! _el-button)
- (hs-activate! _el-span)
- (dom-dispatch _el-button "click" nil)
- (dom-dispatch _el-span "click" nil)
- (assert= (dom-text-content _el-span) "99")
- ))
- (deftest "deeply nested child reads ^var from grandparent"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-div "_" "init set ^name to 'alice'")
- (dom-set-attr _el-span "_" "on click put ^name into me")
- (dom-set-inner-html _el-span "empty")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-div1)
- (dom-append _el-div1 _el-div2)
- (dom-append _el-div2 _el-span)
- (hs-activate! _el-div)
- (hs-activate! _el-span)
- (dom-dispatch _el-span "click" nil)
- (assert= (dom-text-content _el-span) "alice")
- ))
- (deftest "closest ancestor wins (shadowing)"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-div "_" "init set ^color to 'red'")
- (dom-set-attr _el-div1 "_" "init set ^color to 'blue'")
- (dom-set-attr _el-span "_" "on click put ^color into me")
- (dom-set-inner-html _el-span "empty")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-div1)
- (dom-append _el-div1 _el-span)
- (hs-activate! _el-div)
- (hs-activate! _el-div1)
- (hs-activate! _el-span)
- (dom-dispatch _el-span "click" nil)
- (assert= (dom-text-content _el-span) "blue")
- ))
- (deftest "sibling subtrees have independent ^vars"
- (hs-cleanup!)
- (let ((_el-a (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-b (dom-create-element "div")) (_el-span3 (dom-create-element "span")))
- (dom-set-attr _el-a "id" "a")
- (dom-set-attr _el-a "_" "init set ^val to 'A'")
- (dom-set-attr _el-span "_" "on click put ^val into me")
- (dom-set-inner-html _el-span "empty")
- (dom-set-attr _el-b "id" "b")
- (dom-set-attr _el-b "_" "init set ^val to 'B'")
- (dom-set-attr _el-span3 "_" "on click put ^val into me")
- (dom-set-inner-html _el-span3 "empty")
- (dom-append (dom-body) _el-a)
- (dom-append _el-a _el-span)
- (dom-append (dom-body) _el-b)
- (dom-append _el-b _el-span3)
- (hs-activate! _el-a)
- (hs-activate! _el-span)
- (hs-activate! _el-b)
- (hs-activate! _el-span3)
- (dom-dispatch (dom-query "#a span") "click" nil)
- (assert= (dom-text-content (dom-query "#a span")) "A")
- (dom-dispatch (dom-query "#b span") "click" nil)
- (assert= (dom-text-content (dom-query "#b span")) "B")
- ))
- (deftest "write to ^var not found anywhere creates on current element"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-button "_" "on click set ^newvar to 'created' then put ^newvar into next ")
+ (let ((_el-target (dom-create-element "div")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-target "id" "target")
+ (dom-set-inner-html _el-target "old")
+ (dom-set-attr _el-button "_" "on click put \"new\" into #target")
(dom-set-inner-html _el-button "go")
- (dom-set-inner-html _el-span "empty")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-button)
- (dom-append _el-div _el-span)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-text-content _el-span) "created")
- ))
- (deftest "child write updates the ancestor, not a local copy"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-div "_" "init set ^shared to 0")
- (dom-set-attr _el-button "_" "on click set ^shared to 10")
- (dom-set-inner-html _el-button "set")
- (dom-set-attr _el-span "_" "on click put ^shared into me")
- (dom-set-inner-html _el-span "0")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-button)
- (dom-append _el-div _el-span)
- (hs-activate! _el-div)
- (hs-activate! _el-button)
- (hs-activate! _el-span)
- (dom-dispatch _el-button "click" nil)
- (dom-dispatch _el-span "click" nil)
- (assert= (dom-text-content _el-span) "10")
- ))
- (deftest "increment works on inherited var"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-div "_" "init set ^count to 0")
- (dom-set-attr _el-button "_" "on click put ^count into me")
- (dom-set-inner-html _el-button "0")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-button)
- (hs-activate! _el-div)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (dom-dispatch _el-button "click" nil)
- (dom-dispatch _el-button "click" nil)
- (dom-dispatch (dom-query "span") "click" nil)
- (assert= (dom-text-content (dom-query "span")) "3")
- ))
- (deftest "dom keyword works as scope modifier"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-div "_" "init set dom count to 42")
- (dom-set-attr _el-span "_" "on click put dom count into me")
- (dom-set-inner-html _el-span "0")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-span)
- (hs-activate! _el-div)
- (hs-activate! _el-span)
- (dom-dispatch _el-span "click" nil)
- (assert= (dom-text-content _el-span) "42")
- ))
- (deftest "set ^var on explicit element"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
- (dom-add-class _el-div "store")
- (dom-set-attr _el-button "_" "on click set ^data on closest .store to 'hello'")
- (dom-set-inner-html _el-button "set")
- (dom-set-attr _el-span "_" "on click put ^data on closest .store into me")
- (dom-set-inner-html _el-span "read")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-button)
- (dom-append _el-div _el-span)
- (hs-activate! _el-button)
- (hs-activate! _el-span)
- (dom-dispatch _el-button "click" nil)
- (dom-dispatch _el-span "click" nil)
- (assert= (dom-text-content _el-span) "hello")
- ))
- (deftest "on clause targets a specific ancestor"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
- (dom-add-class _el-div "outer")
- (dom-set-attr _el-div "_" "init set ^outerVal to 'outer'")
- (dom-add-class _el-div1 "inner")
- (dom-set-attr _el-div1 "_" "init set ^innerVal to 'inner'")
- (dom-set-attr _el-span "_" "on click put ^outerVal on closest .outer into me")
- (dom-set-inner-html _el-span "read")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-div1)
- (dom-append _el-div1 _el-span)
- (hs-activate! _el-div)
- (hs-activate! _el-div1)
- (hs-activate! _el-span)
- (dom-dispatch _el-span "click" nil)
- (assert= (dom-text-content _el-span) "outer")
- ))
- (deftest "on clause with id reference"
- (hs-cleanup!)
- (let ((_el-state-holder (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-state-holder "id" "state-holder")
- (dom-set-attr _el-button "_" "on click set ^count on #state-holder to 99")
- (dom-set-inner-html _el-button "set")
- (dom-set-attr _el-span "_" "on click put ^count on #state-holder into me")
- (dom-set-inner-html _el-span "read")
- (dom-append (dom-body) _el-state-holder)
+ (dom-append (dom-body) _el-target)
(dom-append (dom-body) _el-button)
- (dom-append (dom-body) _el-span)
- (hs-activate! _el-button)
- (hs-activate! _el-span)
- (dom-dispatch _el-button "click" nil)
- (dom-dispatch _el-span "click" nil)
- (assert= (dom-text-content _el-span) "99")
- ))
- (deftest "when reacts to ^var changes"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-div "_" "init set ^count to 0")
- (dom-set-attr _el-button "_" "when ^count changes put it into me")
- (dom-set-inner-html _el-button "0")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-button)
- (hs-activate! _el-div)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
- (assert= (dom-text-content (dom-query "output")) "1")
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-text-content (dom-query "output")) "2")
+ (assert= (dom-text-content (dom-query-by-id "target")) "new")
))
- (deftest "always reacts to ^var changes"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-div "_" "init set ^name to 'alice'")
- (dom-set-attr _el-button "_" "on click set ^name to 'bob'")
- (dom-set-inner-html _el-button "rename")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-button)
- (hs-activate! _el-div)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- ))
- (deftest "multiple children react to same ^var"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-a (dom-create-element "span")) (_el-b (dom-create-element "span")))
- (dom-set-attr _el-div "_" "init set ^color to 'red'")
- (dom-set-attr _el-button "_" "on click set ^color to 'blue'")
- (dom-set-inner-html _el-button "change")
- (dom-set-attr _el-a "id" "a")
- (dom-set-attr _el-a "_" "when ^color changes put it into me")
- (dom-set-attr _el-b "id" "b")
- (dom-set-attr _el-b "_" "when ^color changes put it into me")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-button)
- (dom-append _el-div _el-a)
- (dom-append _el-div _el-b)
- (hs-activate! _el-div)
- (hs-activate! _el-button)
- (hs-activate! _el-a)
- (hs-activate! _el-b)
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-text-content (dom-query-by-id "a")) "blue")
- (assert= (dom-text-content (dom-query-by-id "b")) "blue")
- ))
- (deftest "sibling subtrees react independently with ^var"
- (hs-cleanup!)
- (let ((_el-a (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-b (dom-create-element "div")) (_el-button3 (dom-create-element "button")))
- (dom-set-attr _el-a "id" "a")
- (dom-set-attr _el-a "_" "init set ^val to 0")
- (dom-set-attr _el-button "_" "when ^val changes put it into me")
- (dom-set-inner-html _el-button "0")
- (dom-set-attr _el-b "id" "b")
- (dom-set-attr _el-b "_" "init set ^val to 0")
- (dom-set-attr _el-button3 "_" "when ^val changes put it into me")
- (dom-set-inner-html _el-button3 "0")
- (dom-append (dom-body) _el-a)
- (dom-append _el-a _el-button)
- (dom-append _el-button _el-b)
- (dom-append _el-b _el-button3)
- (hs-activate! _el-a)
- (hs-activate! _el-button)
- (hs-activate! _el-b)
- (hs-activate! _el-button3)
- (dom-dispatch (dom-query "#a button") "click" nil)
- (dom-dispatch (dom-query "#a button") "click" nil)
- (assert= (dom-text-content (dom-query "#a output")) "2")
- (assert= (dom-text-content (dom-query "#b output")) "0")
- (dom-dispatch (dom-query "#b button") "click" nil)
- (assert= (dom-text-content (dom-query "#b output")) "1")
- (assert= (dom-text-content (dom-query "#a output")) "2")
- ))
- (deftest "bind works with ^var"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-input (dom-create-element "input")) (_el-output (dom-create-element "output")))
- (dom-set-attr _el-div "_" "init set ^search to ''")
- (dom-set-attr _el-input "_" "bind ^search and my value")
- (dom-set-attr _el-input "type" "text")
- (dom-set-attr _el-output "_" "when ^search changes put it into me")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-input)
- (dom-append _el-div _el-output)
- (hs-activate! _el-div)
- (hs-activate! _el-input)
- (hs-activate! _el-output)
- (dom-set-prop _el-input "value" "hello")
- (dom-dispatch _el-input "input" nil)
- ))
- (deftest "derived ^var chains reactively"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-span2 (dom-create-element "span")) (_el-output (dom-create-element "output")) (_el-price-btn (dom-create-element "button")) (_el-qty-btn (dom-create-element "button")))
- (dom-set-attr _el-div "_" "init set ^price to 10 then set ^qty to 2 then set ^total to 20")
- (dom-set-attr _el-span "_" "when ^price changes set ^total to (^price * ^qty)")
- (dom-set-attr _el-span2 "_" "when ^qty changes set ^total to (^price * ^qty)")
- (dom-set-attr _el-output "_" "when ^total changes put it into me")
- (dom-set-attr _el-price-btn "id" "price-btn")
- (dom-set-attr _el-price-btn "_" "on click set ^price to 25")
- (dom-set-inner-html _el-price-btn "set price")
- (dom-set-attr _el-qty-btn "id" "qty-btn")
- (dom-set-attr _el-qty-btn "_" "on click set ^qty to 5")
- (dom-set-inner-html _el-qty-btn "set qty")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-span)
- (dom-append _el-div _el-span2)
- (dom-append _el-div _el-output)
- (dom-append _el-div _el-price-btn)
- (dom-append _el-div _el-qty-btn)
- (hs-activate! _el-div)
- (hs-activate! _el-span)
- (hs-activate! _el-span2)
- (hs-activate! _el-output)
- (hs-activate! _el-price-btn)
- (hs-activate! _el-qty-btn)
- (dom-dispatch (dom-query-by-id "price-btn") "click" nil)
- (dom-dispatch (dom-query-by-id "qty-btn") "click" nil)
- ))
- (deftest "effect stops when element is removed"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-output (dom-create-element "output")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-div "_" "init set ^msg to 'hello'")
- (dom-set-attr _el-output "_" "when ^msg changes put it into me")
- (dom-set-attr _el-button "_" "on click set ^msg to 'updated'")
- (dom-set-inner-html _el-button "update")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-output)
- (dom-append _el-div _el-button)
- (hs-activate! _el-div)
- (hs-activate! _el-output)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- ))
- (deftest "dedup prevents re-fire on same ^var value"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-output (dom-create-element "output")) (_el-button (dom-create-element "button")) (_el-diff (dom-create-element "button")))
- (dom-set-attr _el-div "_" "init set ^val to 'same'")
- (dom-set-attr _el-output "_" "when ^val changes increment :runs then put :runs into me")
- (dom-set-attr _el-button "_" "on click set ^val to 'same'")
- (dom-set-inner-html _el-button "same")
- (dom-set-attr _el-diff "id" "diff")
- (dom-set-attr _el-diff "_" "on click set ^val to 'different'")
- (dom-set-inner-html _el-diff "diff")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-output)
- (dom-append _el-div _el-button)
- (dom-append _el-div _el-diff)
- (hs-activate! _el-div)
- (hs-activate! _el-output)
- (hs-activate! _el-button)
- (hs-activate! _el-diff)
- (dom-dispatch (dom-query "button:text("same")") "click" nil)
- (assert= (dom-text-content _el-output) "1")
- (dom-dispatch (dom-query-by-id "diff") "click" nil)
- (assert= (dom-text-content _el-output) "2")
- ))
-)
-
-;; ── evalStatically (8 tests) ──
-(defsuite "hs-upstream-evalStatically"
- (deftest "works on number literals"
- (assert= (eval-hs "42") 42)
- (assert= (eval-hs "3.14") 3.14)
- )
- (deftest "works on boolean literals"
- (assert= (eval-hs "true") true)
- (assert= (eval-hs "false") false)
- )
- (deftest "works on null literal"
- (assert= (eval-hs "null") nil)
- )
- (deftest "works on plain string literals"
- (assert= (eval-hs "\"hello\"") "hello")
- (assert= (eval-hs "'world'") "world")
- )
- (deftest "works on time expressions"
- (assert= (eval-hs "200ms") 200)
- (assert= (eval-hs "2s") 2000)
- )
- (deftest "throws on template strings"
- (hs-cleanup!))
- (deftest "throws on symbol references"
- (hs-cleanup!))
- (deftest "throws on math expressions"
- (hs-cleanup!))
-)
-
-;; ── liveTemplate (10 tests) ──
-(defsuite "hs-upstream-liveTemplate"
- (deftest "renders static content after the template"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-span (dom-create-element "span")))
- (dom-set-inner-html _el-span "Hello World")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-span)
- ))
- (deftest "renders template expressions"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-span (dom-create-element "span")))
- (dom-set-inner-html _el-span "Hello ${\"\\x24\"}{$ltName}!")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-span)
- ))
- (deftest "applies init script from _ attribute"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-template "_" "init set ^msg to 'initialized'")
- (dom-set-inner-html _el-span "${\"\\x24\"}{^msg}")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-span)
- (hs-activate! _el-template)
- ))
- (deftest "reactively updates when dependencies change"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-template "_" "init set ^count to 0")
- (dom-set-attr _el-button "_" "on click increment ^count")
- (dom-set-inner-html _el-button "+")
- (dom-set-inner-html _el-span "Count: ${\"\\x24\"}{^count}")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-button)
- (dom-append _el-template _el-span)
- (hs-activate! _el-template)
- (hs-activate! _el-button)
- (dom-dispatch (dom-query "[data-live-template] button") "click" nil)
- ))
- (deftest "processes hyperscript on inner elements"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-button (dom-create-element "button")) (_el-output (dom-create-element "output")))
- (dom-set-attr _el-template "_" "init set ^val to 0")
- (dom-set-attr _el-button "_" "on click increment ^val then put ^val into the next ")
- (dom-set-inner-html _el-button "+")
- (dom-set-inner-html _el-output "0")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-button)
- (dom-append _el-template _el-output)
- (hs-activate! _el-template)
- (hs-activate! _el-button)
- (dom-dispatch (dom-query "[data-live-template] button") "click" nil)
- ))
- (deftest "supports #for loops"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-ul (dom-create-element "ul")) (_el-li (dom-create-element "li")))
- (dom-set-attr _el-template "_" "init set ^items to ['a', 'b', 'c']")
- (dom-set-inner-html _el-ul "#for item in ^items")
- (dom-set-inner-html _el-li "${\"\\x24\"}{item}")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-ul)
- (dom-append _el-ul _el-li)
- (hs-activate! _el-template)
- ))
- (deftest "supports #if conditionals"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-template "_" "init set ^show to true")
- (dom-set-inner-html _el-template "#if ^show")
- (dom-set-inner-html _el-span "visible")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-span)
- (hs-activate! _el-template)
- ))
- (deftest "reacts to global state without init script"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-p (dom-create-element "p")))
- (dom-set-inner-html _el-p "Hello, ${\"\\x24\"}{$ltGlobal}!")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-p)
- ))
- (deftest "wrapper has display:contents"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-span (dom-create-element "span")))
- (dom-set-inner-html _el-span "test")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-span)
- ))
- (deftest "multiple live templates are independent"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-span (dom-create-element "span")) (_el-template2 (dom-create-element "template")) (_el-span3 (dom-create-element "span")))
- (dom-set-attr _el-template "_" "init set ^x to 'first'")
- (dom-add-class _el-span "a")
- (dom-set-inner-html _el-span "${\"\\x24\"}{^x}")
- (dom-set-attr _el-template2 "_" "init set ^x to 'second'")
- (dom-add-class _el-span3 "b")
- (dom-set-inner-html _el-span3 "${\"\\x24\"}{^x}")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-span)
- (dom-append (dom-body) _el-template2)
- (dom-append _el-template2 _el-span3)
- (hs-activate! _el-template)
- (hs-activate! _el-template2)
- ))
-)
-
-;; ── assignableElements (8 tests) ──
-(defsuite "hs-upstream-assignableElements"
(deftest "set #id replaces element with HTML string"
(hs-cleanup!)
(let ((_el-target (dom-create-element "div")) (_el-button (dom-create-element "button")))
@@ -6986,22 +3551,6 @@
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query ".wrapper")) "replaced")
))
- (deftest "hyperscript in replacement content is initialized"
- (hs-cleanup!)
- (let ((_el-target (dom-create-element "div")) (_el-go (dom-create-element "button")))
- (dom-set-attr _el-target "id" "target")
- (dom-set-inner-html _el-target "old")
- (dom-set-attr _el-go "id" "go")
- (dom-set-attr _el-go "_" "on click set #target to 'new
'")
- (dom-set-inner-html _el-go "go")
- (dom-append (dom-body) _el-target)
- (dom-append (dom-body) _el-go)
- (hs-activate! _el-go)
- (dom-dispatch (dom-query-by-id "go") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "target")) "new")
- (dom-dispatch (dom-query-by-id "target") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "target")) "clicked")
- ))
(deftest "swap #a with #b swaps DOM positions"
(hs-cleanup!)
(let ((_el-container (dom-create-element "div")) (_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")) (_el-button (dom-create-element "button")))
@@ -7019,93 +3568,509 @@
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
))
- (deftest "put into still works as innerHTML"
+)
+
+;; ── expressions/attributeRef (22 tests) ──
+(defsuite "hs-upstream-expressions/attributeRef"
+ (deftest "attributeRef can be put as symbol"
(hs-cleanup!)
- (let ((_el-target (dom-create-element "div")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-target "id" "target")
- (dom-set-inner-html _el-target "old")
- (dom-set-attr _el-button "_" "on click put \"new\" into #target")
- (dom-set-inner-html _el-button "go")
- (dom-append (dom-body) _el-target)
- (dom-append (dom-body) _el-button)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-text-content (dom-query-by-id "target")) "new")
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "_" "on click put \"blue\" into [@data-foo]")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ (hs-activate! _el-arDiv)
+ (dom-dispatch (dom-query-by-id "arDiv") "click" nil)
+ ))
+ (deftest "attributeRef can be put as symbol w/ short syntax"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "_" "on click put \"blue\" into @data-foo")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ (hs-activate! _el-arDiv)
+ (dom-dispatch (dom-query-by-id "arDiv") "click" nil)
+ ))
+ (deftest "attributeRef can be put indirectly"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ ))
+ (deftest "attributeRef can be put indirectly w/ short syntax"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ ))
+ (deftest "attributeRef can be set as prop"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ ))
+ (deftest "attributeRef can be set as prop w/ short syntax"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ ))
+ (deftest "attributeRef can be set as symbol"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "_" "on click set [@data-foo] to \"blue\"")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ (hs-activate! _el-arDiv)
+ (dom-dispatch (dom-query-by-id "arDiv") "click" nil)
+ ))
+ (deftest "attributeRef can be set as symbol w/ short syntax"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "_" "on click set @data-foo to \"blue\"")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ (hs-activate! _el-arDiv)
+ (dom-dispatch (dom-query-by-id "arDiv") "click" nil)
+ ))
+ (deftest "attributeRef can be set indirectly"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ ))
+ (deftest "attributeRef can be set indirectly w/ short syntax"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ ))
+ (deftest "attributeRef can be set through possessive"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "_" "on click set my [@data-foo] to \"blue\"")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ (hs-activate! _el-arDiv)
+ (dom-dispatch (dom-query-by-id "arDiv") "click" nil)
+ ))
+ (deftest "attributeRef can be set through possessive w/ short syntax"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "_" "on click set my @data-foo to \"blue\"")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ (hs-activate! _el-arDiv)
+ (dom-dispatch (dom-query-by-id "arDiv") "click" nil)
+ ))
+ (deftest "attributeRef can have value in quotes used in add commands"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "_" "on click add [@data-foo=\"blue\"]")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ (hs-activate! _el-arDiv)
+ (dom-dispatch (dom-query-by-id "arDiv") "click" nil)
+ ))
+ (deftest "attributeRef can have value in quotes used in add commands w/ short syntax"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "_" "on click add @data-foo=\"blue\"")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ (hs-activate! _el-arDiv)
+ (dom-dispatch (dom-query-by-id "arDiv") "click" nil)
+ ))
+ (deftest "attributeRef can have value in quotes with spaces used in add commands"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "_" "on click add [@data-foo=\"blue green\"]")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ (hs-activate! _el-arDiv)
+ (dom-dispatch (dom-query-by-id "arDiv") "click" nil)
+ ))
+ (deftest "attributeRef can have value in quotes with spaces used in add commands w/ short syntax"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "_" "on click add @data-foo=\"blue green\"")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ (hs-activate! _el-arDiv)
+ (dom-dispatch (dom-query-by-id "arDiv") "click" nil)
+ ))
+ (deftest "attributeRef can have value used in add commands"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "_" "on click add [@data-foo=blue]")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ (hs-activate! _el-arDiv)
+ (dom-dispatch (dom-query-by-id "arDiv") "click" nil)
+ ))
+ (deftest "attributeRef can have value used in add commands w/ short syntax"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "_" "on click add @data-foo=blue")
+ (dom-set-attr _el-arDiv "data-foo" "red")
+ (dom-append (dom-body) _el-arDiv)
+ (hs-activate! _el-arDiv)
+ (dom-dispatch (dom-query-by-id "arDiv") "click" nil)
+ ))
+ (deftest "attributeRef with dashes name works"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "data-foo" "c1")
+ (dom-append (dom-body) _el-arDiv)
+ ))
+ (deftest "attributeRef with dashes name works w/ short syntax"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "data-foo" "c1")
+ (dom-append (dom-body) _el-arDiv)
+ ))
+ (deftest "attributeRef with no value works"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "foo" "c1")
+ (dom-append (dom-body) _el-arDiv)
+ ))
+ (deftest "attributeRef with no value works w/ short syntax"
+ (hs-cleanup!)
+ (let ((_el-arDiv (dom-create-element "div")))
+ (dom-set-attr _el-arDiv "id" "arDiv")
+ (dom-set-attr _el-arDiv "foo" "c1")
+ (dom-append (dom-body) _el-arDiv)
))
)
-;; ── collectionExpressions (22 tests) ──
-(defsuite "hs-upstream-collectionExpressions"
+;; ── expressions/beep! (6 tests) ──
+(defsuite "hs-upstream-expressions/beep!"
+ (deftest "beeps a basic value"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click get beep! 10")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "beeps a formatted string value"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click get beep! \"foo\"")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "beeps a null value"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click get beep! null")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "beeps the result of an ElementCollection"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "foo")
+ (dom-set-attr _el-div "_" "on click get beep! .foo")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "can be cancelled"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on hyperscript:beep halt on click get beep! \"foo\"")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "can capture information from event"
+ (hs-cleanup!)
+ (let ((_el-beepDiv (dom-create-element "div")))
+ (dom-set-attr _el-beepDiv "id" "beepDiv")
+ (dom-set-attr _el-beepDiv "_" "on hyperscript:beep(value) set my @data-value to the value on click get beep! \"foo\"")
+ (dom-append (dom-body) _el-beepDiv)
+ (hs-activate! _el-beepDiv)
+ (dom-dispatch (dom-query-by-id "beepDiv") "click" nil)
+ ))
+)
+
+;; ── expressions/blockLiteral (4 tests) ──
+(defsuite "hs-upstream-expressions/blockLiteral"
+ (deftest "basic block literals work"
+ (error "SKIP (untranslated): basic block literals work"))
+ (deftest "basic identity works"
+ (error "SKIP (untranslated): basic identity works"))
+ (deftest "basic two arg identity works"
+ (error "SKIP (untranslated): basic two arg identity works"))
+ (deftest "can map an array"
+ (error "SKIP (untranslated): can map an array"))
+)
+
+;; ── expressions/boolean (2 tests) ──
+(defsuite "hs-upstream-expressions/boolean"
+ (deftest "false boolean literals work"
+ (assert= (eval-hs "false") false)
+ )
+ (deftest "true boolean literals work"
+ (assert= (eval-hs "true") true)
+ )
+)
+
+;; ── expressions/classRef (9 tests) ──
+(defsuite "hs-upstream-expressions/classRef"
+ (deftest "basic classRef works"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "c1")
+ (dom-append (dom-body) _el-div)
+ ))
+ (deftest "basic classRef works w no match"
+ (error "SKIP (untranslated): basic classRef works w no match"))
+ (deftest "colon class ref works"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "c1:foo")
+ (dom-append (dom-body) _el-div)
+ ))
+ (deftest "dashed class ref works"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "c1-foo")
+ (dom-append (dom-body) _el-div)
+ ))
+ (deftest "leading minus class ref works"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "-c1")
+ (dom-append (dom-body) _el-div)
+ ))
+ (deftest "multiple colon class ref works"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "c1:foo:bar")
+ (dom-append (dom-body) _el-div)
+ ))
+ (deftest "slashes in class references work"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "-c1/22")
+ (dom-append (dom-body) _el-div)
+ ))
+ (deftest "tailwind insanity in class references work"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "group-[:nth-of-type(3)_&]:block")
+ (dom-append (dom-body) _el-div)
+ ))
+ (deftest "template classRef works"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "c1")
+ (dom-append (dom-body) _el-div)
+ ))
+)
+
+;; ── expressions/closest (10 tests) ──
+(defsuite "hs-upstream-expressions/closest"
+ (deftest "attributes can be looked up and referred to in same expression"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-div "foo" "bar")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click put closest @foo into me")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "bar")
+ ))
+ (deftest "attributes can be set via the closest expression"
+ (hs-cleanup!)
+ (let ((_el-outerDiv (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-outerDiv "id" "outerDiv")
+ (dom-set-attr _el-outerDiv "foo" "bar")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click set closest @foo to \"doh\"")
+ (dom-append (dom-body) _el-outerDiv)
+ (dom-append _el-outerDiv _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ ))
+ (deftest "attributes can be set via the closest expression 2"
+ (hs-cleanup!)
+ (let ((_el-outerDiv2 (dom-create-element "div")) (_el-d1b (dom-create-element "div")))
+ (dom-set-attr _el-outerDiv2 "id" "outerDiv2")
+ (dom-set-attr _el-outerDiv2 "foo" "bar")
+ (dom-set-attr _el-d1b "id" "d1b")
+ (dom-set-attr _el-d1b "_" "on click set closest @foo to \"doh\"")
+ (dom-append (dom-body) _el-outerDiv2)
+ (dom-append _el-outerDiv2 _el-d1b)
+ (hs-activate! _el-d1b)
+ (dom-dispatch (dom-query-by-id "d1b") "click" nil)
+ ))
+ (deftest "attributes resolve as attributes"
+ (hs-cleanup!)
+ (let ((_el-d3 (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-set-attr _el-d3 "foo" "bar")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-append (dom-body) _el-d3)
+ (dom-append _el-d3 _el-d1)
+ (dom-append _el-d3 _el-d2)
+ ))
+ (deftest "basic query return values"
+ (hs-cleanup!)
+ (let ((_el-d3 (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-append (dom-body) _el-d3)
+ (dom-append _el-d3 _el-d1)
+ (dom-append _el-d3 _el-d2)
+ ))
+ (deftest "closest does not consume a following where clause"
+ (hs-cleanup!)
+ (let ((_el-table (dom-create-element "table")) (_el-tr (dom-create-element "tr")) (_el-td (dom-create-element "td")) (_el-input (dom-create-element "input")) (_el-input4 (dom-create-element "input")) (_el-master (dom-create-element "input")) (_el-out (dom-create-element "div")))
+ (dom-add-class _el-input "cb")
+ (dom-set-attr _el-input "type" "checkbox")
+ (dom-add-class _el-input4 "cb")
+ (dom-set-attr _el-input4 "type" "checkbox")
+ (dom-set-attr _el-master "id" "master")
+ (dom-set-attr _el-master "_" "set :others to in the closest where it is not me on click put :others.length into #out")
+ (dom-set-attr _el-master "type" "checkbox")
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-table)
+ (dom-append _el-table _el-tr)
+ (dom-append _el-tr _el-td)
+ (dom-append _el-td _el-input)
+ (dom-append _el-input _el-input4)
+ (dom-append _el-input4 _el-master)
+ (dom-append _el-master _el-out)
+ (hs-activate! _el-master)
+ (dom-dispatch (dom-query-by-id "master") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "out")) "2")
+ ))
+ (deftest "closest with to modifier still works after parse change"
+ (hs-cleanup!)
+ (let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "div")))
+ (dom-set-attr _el-outer "id" "outer")
+ (dom-set-attr _el-inner "id" "inner")
+ (dom-append (dom-body) _el-outer)
+ (dom-append _el-outer _el-inner)
+ ))
+ (deftest "parent modifier works"
+ (hs-cleanup!)
+ (let ((_el-d3 (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-append (dom-body) _el-d3)
+ (dom-append _el-d3 _el-d1)
+ (dom-append _el-d3 _el-d2)
+ ))
+ (deftest "parenthesizing allows you to nest to modifiers properly"
+ (hs-cleanup!)
+ (let ((_el-outerDiv (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
+ (dom-set-attr _el-outerDiv "id" "outerDiv")
+ (dom-set-attr _el-outerDiv "foo" "bar")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-div2 "id" "div2")
+ (dom-set-attr _el-div2 "_" "on click set (closest @foo to #d1) to \"doh\"")
+ (dom-append (dom-body) _el-outerDiv)
+ (dom-append _el-outerDiv _el-d1)
+ (dom-append (dom-body) _el-div2)
+ (hs-activate! _el-div2)
+ (dom-dispatch (dom-query-by-id "div2") "click" nil)
+ ))
+ (deftest "returns an array where appropriate"
+ (hs-cleanup!)
+ (let ((_el-d2 (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d3 (dom-create-element "div")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "bar")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "foo")
+ (dom-set-attr _el-d1 "_" "on click add .doh to closest .bar to .foo")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "bar")
+ (dom-add-class _el-div "foo")
+ (dom-append (dom-body) _el-d2)
+ (dom-append _el-d2 _el-d1)
+ (dom-append (dom-body) _el-d3)
+ (dom-append _el-d3 _el-div)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ ))
+)
+
+;; ── expressions/collectionExpressions (28 tests) ──
+(defsuite "hs-upstream-expressions/collectionExpressions"
(deftest "filters an array by condition"
(assert= (map (fn (x) (get x "name")) (eval-hs "set arr to [{name: \"a\", active: true}, {name: \"b\", active: false}, {name: \"c\", active: true}] then return arr where its active")) (list "a" "c"))
)
(deftest "filters with comparison"
(assert= (eval-hs "set arr to [1, 2, 3, 4, 5] then return arr where it > 3") (list 4 5))
)
- (deftest "works with DOM elements"
+ (deftest "full select-all pattern with multiple on features"
(hs-cleanup!)
- (let ((_el-list (dom-create-element "ul")) (_el-li (dom-create-element "li")) (_el-li2 (dom-create-element "li")) (_el-li3 (dom-create-element "li")) (_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
- (dom-set-attr _el-list "id" "list")
- (dom-add-class _el-li "yes")
- (dom-set-inner-html _el-li "A")
- (dom-set-inner-html _el-li2 "B")
- (dom-add-class _el-li3 "yes")
- (dom-set-inner-html _el-li3 "C")
- (dom-set-attr _el-button "_" "on click set items to in #list set matches to items where it matches .yes then put matches mapped to its textContent into #out")
- (dom-set-inner-html _el-button "Go")
- (dom-set-attr _el-out "id" "out")
- (dom-append (dom-body) _el-list)
- (dom-append _el-list _el-li)
- (dom-append _el-list _el-li2)
- (dom-append _el-list _el-li3)
- (dom-append (dom-body) _el-button)
- (dom-append (dom-body) _el-out)
- (hs-activate! _el-button)
- (dom-dispatch _el-button "click" nil)
- (assert= (dom-text-content (dom-query-by-id "out")) "AC")
+ (let ((_el-table (dom-create-element "table")) (_el-tr (dom-create-element "tr")) (_el-td (dom-create-element "td")) (_el-input (dom-create-element "input")) (_el-tr4 (dom-create-element "tr")) (_el-td5 (dom-create-element "td")) (_el-input6 (dom-create-element "input")) (_el-tr7 (dom-create-element "tr")) (_el-td8 (dom-create-element "td")) (_el-input9 (dom-create-element "input")) (_el-tr10 (dom-create-element "tr")) (_el-td11 (dom-create-element "td")) (_el-master (dom-create-element "input")))
+ (dom-add-class _el-input "cb")
+ (dom-set-attr _el-input "type" "checkbox")
+ (dom-set-attr _el-input "checked" "")
+ (dom-add-class _el-input6 "cb")
+ (dom-set-attr _el-input6 "type" "checkbox")
+ (dom-add-class _el-input9 "cb")
+ (dom-set-attr _el-input9 "type" "checkbox")
+ (dom-set-attr _el-input9 "checked" "")
+ (dom-set-attr _el-master "id" "master")
+ (dom-set-attr _el-master "_" "set :checkboxes to in the closest where it is not me then on change set checked of the :checkboxes to my checked then on change from the closest then if no :checkboxes where it is checked then set my indeterminate to false then set my checked to false then else if no :checkboxes where it is not checked then set my indeterminate to false then set my checked to true then else then set my indeterminate to true then end")
+ (dom-set-attr _el-master "type" "checkbox")
+ (dom-append (dom-body) _el-table)
+ (dom-append _el-table _el-tr)
+ (dom-append _el-tr _el-td)
+ (dom-append _el-td _el-input)
+ (dom-append _el-input _el-tr4)
+ (dom-append _el-tr4 _el-td5)
+ (dom-append _el-td5 _el-input6)
+ (dom-append _el-input6 _el-tr7)
+ (dom-append _el-tr7 _el-td8)
+ (dom-append _el-td8 _el-input9)
+ (dom-append _el-input9 _el-tr10)
+ (dom-append _el-tr10 _el-td11)
+ (dom-append _el-td11 _el-master)
+ (hs-activate! _el-master)
+ (dom-dispatch (dom-query-by-id "master") "click" nil)
+ (dom-dispatch (dom-query ".cb") "click" nil)
))
- (deftest "sorts by a property"
- (assert= (map (fn (x) (get x "name")) (eval-hs "set arr to [{name: \"Charlie\"}, {name: \"Alice\"}, {name: \"Bob\"}] then return arr sorted by its name")) (list "Alice" "Bob" "Charlie"))
- )
- (deftest "sorts descending"
- (assert= (eval-hs "set arr to [3, 1, 2] then return arr sorted by it descending") (list 3 2 1))
- )
- (deftest "sorts numbers by a computed key"
- (assert= (map (fn (x) (get x "name")) (eval-hs "set arr to [{name: \"b\", age: 30}, {name: \"a\", age: 20}, {name: \"c\", age: 25}] then return arr sorted by its age")) (list "a" "c" "b"))
- )
+ (deftest "joined by on null returns null"
+ (error "SKIP (untranslated): joined by on null returns null"))
+ (deftest "mapped to on null returns null"
+ (error "SKIP (untranslated): mapped to on null returns null"))
(deftest "maps to a property"
(assert= (eval-hs "set arr to [{name: \"Alice\"}, {name: \"Bob\"}] then return arr mapped to its name") (list "Alice" "Bob"))
)
(deftest "maps with an expression"
(assert= (eval-hs "set arr to [1, 2, 3] then return arr mapped to (it * 2)") (list 2 4 6))
)
- (deftest "where then mapped to"
- (assert= (eval-hs "set arr to [{name: \"Alice\", active: true}, {name: \"Bob\", active: false}, {name: \"Charlie\", active: true}] then return arr where its active mapped to its name") (list "Alice" "Charlie"))
- )
- (deftest "sorted by then mapped to"
- (assert= (eval-hs "set arr to [{name: \"Charlie\", age: 30}, {name: \"Alice\", age: 20}] then return arr sorted by its age mapped to its name") (list "Alice" "Charlie"))
- )
- (deftest "where then sorted by then mapped to"
- (assert= (eval-hs "set arr to [{name: \"Charlie\", active: true, age: 30}, {name: \"Alice\", active: false, age: 20}, {name: \"Bob\", active: true, age: 25}] then return arr where its active sorted by its age mapped to its name") (list "Bob" "Charlie"))
- )
- (deftest "the result inside where refers to previous command result, not current element"
- (assert= (eval-hs "get 3 then set arr to [1, 2, 3, 4, 5] then return arr where it > the result") (list 4 5))
- )
- (deftest "where binds after in without parens"
- (hs-cleanup!)
- (let ((_el-container (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-span2 (dom-create-element "span")) (_el-span3 (dom-create-element "span")))
- (dom-set-attr _el-container "id" "container")
- (dom-add-class _el-span "a")
- (dom-set-inner-html _el-span "A")
- (dom-add-class _el-span2 "b")
- (dom-set-inner-html _el-span2 "B")
- (dom-add-class _el-span3 "a")
- (dom-set-inner-html _el-span3 "C")
- (dom-append (dom-body) _el-container)
- (dom-append _el-container _el-span)
- (dom-append _el-container _el-span2)
- (dom-append _el-container _el-span3)
- ))
(deftest "sorted by binds after in without parens"
(hs-cleanup!)
(let ((_el-list (dom-create-element "ul")) (_el-li (dom-create-element "li")) (_el-li2 (dom-create-element "li")) (_el-li3 (dom-create-element "li")))
@@ -7118,8 +4083,24 @@
(dom-append _el-list _el-li2)
(dom-append _el-list _el-li3)
))
- (deftest "where binds after property access"
- (assert= (eval-hs "obj.items where it > 2") (list 3 4))
+ (deftest "sorted by on null returns null"
+ (error "SKIP (untranslated): sorted by on null returns null"))
+ (deftest "sorted by then mapped to"
+ (assert= (eval-hs "set arr to [{name: \"Charlie\", age: 30}, {name: \"Alice\", age: 20}] then return arr sorted by its age mapped to its name") (list "Alice" "Charlie"))
+ )
+ (deftest "sorts by a property"
+ (assert= (map (fn (x) (get x "name")) (eval-hs "set arr to [{name: \"Charlie\"}, {name: \"Alice\"}, {name: \"Bob\"}] then return arr sorted by its name")) (list "Alice" "Bob" "Charlie"))
+ )
+ (deftest "sorts descending"
+ (assert= (eval-hs "set arr to [3, 1, 2] then return arr sorted by it descending") (list 3 2 1))
+ )
+ (deftest "sorts numbers by a computed key"
+ (assert= (map (fn (x) (get x "name")) (eval-hs "set arr to [{name: \"b\", age: 30}, {name: \"a\", age: 20}, {name: \"c\", age: 25}] then return arr sorted by its age")) (list "a" "c" "b"))
+ )
+ (deftest "split by on null returns null"
+ (error "SKIP (untranslated): split by on null returns null"))
+ (deftest "the result inside where refers to previous command result, not current element"
+ (assert= (eval-hs "get 3 then set arr to [1, 2, 3, 4, 5] then return arr where it > the result") (list 4 5))
)
(deftest "where after in with mapped to"
(hs-cleanup!)
@@ -7163,6 +4144,46 @@
(dom-dispatch (dom-query-by-id "b2") "click" nil)
(assert= (dom-text-content (dom-query-by-id "b2")) "2")
))
+ (deftest "where binds after in without parens"
+ (hs-cleanup!)
+ (let ((_el-container (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-span2 (dom-create-element "span")) (_el-span3 (dom-create-element "span")))
+ (dom-set-attr _el-container "id" "container")
+ (dom-add-class _el-span "a")
+ (dom-set-inner-html _el-span "A")
+ (dom-add-class _el-span2 "b")
+ (dom-set-inner-html _el-span2 "B")
+ (dom-add-class _el-span3 "a")
+ (dom-set-inner-html _el-span3 "C")
+ (dom-append (dom-body) _el-container)
+ (dom-append _el-container _el-span)
+ (dom-append _el-container _el-span2)
+ (dom-append _el-container _el-span3)
+ ))
+ (deftest "where binds after property access"
+ (assert= (eval-hs "obj.items where it > 2") (list 3 4))
+ )
+ (deftest "where in component init followed by on feature"
+ (hs-cleanup!)
+ (let ((_el-box (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-span2 (dom-create-element "span")) (_el-script (dom-create-element "script")) (_el-test-where-comp (dom-create-element "test-where-comp")))
+ (dom-set-attr _el-box "id" "box")
+ (dom-add-class _el-span "a")
+ (dom-set-inner-html _el-span "A")
+ (dom-add-class _el-span2 "b")
+ (dom-set-inner-html _el-span2 "B")
+ (dom-set-attr _el-script "_" "set :items to in #box where it matches .a then on click put :items.length into me")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-where-comp")
+ (dom-set-inner-html _el-script "")
+ (dom-set-inner-html _el-test-where-comp "go")
+ (dom-append (dom-body) _el-box)
+ (dom-append _el-box _el-span)
+ (dom-append _el-box _el-span2)
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-where-comp)
+ (hs-activate! _el-script)
+ (dom-dispatch _el-test-where-comp "click" nil)
+ (assert= (dom-text-content _el-test-where-comp) "1")
+ ))
(deftest "where in init followed by on feature"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-span2 (dom-create-element "span")) (_el-button (dom-create-element "button")))
@@ -7181,47 +4202,16 @@
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content _el-button) "1")
))
- (deftest "where in component init followed by on feature"
- (hs-cleanup!)
- (let ((_el-box (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-span2 (dom-create-element "span")) (_el-template (dom-create-element "template")) (_el-slot (dom-create-element "slot")) (_el-test-where-comp (dom-create-element "test-where-comp")))
- (dom-set-attr _el-box "id" "box")
- (dom-add-class _el-span "a")
- (dom-set-inner-html _el-span "A")
- (dom-add-class _el-span2 "b")
- (dom-set-inner-html _el-span2 "B")
- (dom-set-attr _el-template "_" "set :items to in #box where it matches .a then on click put :items.length into me")
- (dom-set-attr _el-template "component" "test-where-comp")
- (dom-set-inner-html _el-test-where-comp "go")
- (dom-append (dom-body) _el-box)
- (dom-append _el-box _el-span)
- (dom-append _el-box _el-span2)
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-slot)
- (dom-append (dom-body) _el-test-where-comp)
- (hs-activate! _el-template)
- (dom-dispatch _el-test-where-comp "click" nil)
- (assert= (dom-text-content _el-test-where-comp) "1")
- ))
- (deftest "where with is not me in component template"
- (hs-cleanup!)
- (let ((_el-box (dom-create-element "div")) (_el-input (dom-create-element "input")) (_el-input2 (dom-create-element "input")) (_el-template (dom-create-element "template")) (_el-input4 (dom-create-element "input")) (_el-test-where-me (dom-create-element "test-where-me")))
- (dom-set-attr _el-box "id" "box")
- (dom-add-class _el-input "cb")
- (dom-set-attr _el-input "type" "checkbox")
- (dom-add-class _el-input2 "cb")
- (dom-set-attr _el-input2 "type" "checkbox")
- (dom-set-attr _el-template "component" "test-where-me")
- (dom-set-attr _el-input4 "_" "set :checkboxes to in #box where it is not me on change set checked of the :checkboxes to my checked")
- (dom-set-attr _el-input4 "type" "checkbox")
- (dom-append (dom-body) _el-box)
- (dom-append _el-box _el-input)
- (dom-append _el-input _el-input2)
- (dom-append _el-input2 _el-template)
- (dom-append _el-template _el-input4)
- (dom-append _el-input4 _el-test-where-me)
- (hs-activate! _el-input4)
- (dom-dispatch (dom-query "test-where-me input") "click" nil)
- ))
+ (deftest "where on null returns null"
+ (error "SKIP (untranslated): where on null returns null"))
+ (deftest "where on undefined returns undefined"
+ (error "SKIP (untranslated): where on undefined returns undefined"))
+ (deftest "where then mapped to"
+ (assert= (eval-hs "set arr to [{name: \"Alice\", active: true}, {name: \"Bob\", active: false}, {name: \"Charlie\", active: true}] then return arr where its active mapped to its name") (list "Alice" "Charlie"))
+ )
+ (deftest "where then sorted by then mapped to"
+ (assert= (eval-hs "set arr to [{name: \"Charlie\", active: true, age: 30}, {name: \"Alice\", active: false, age: 20}, {name: \"Bob\", active: true, age: 25}] then return arr where its active sorted by its age mapped to its name") (list "Bob" "Charlie"))
+ )
(deftest "where with is not me followed by on feature"
(hs-cleanup!)
(let ((_el-table (dom-create-element "table")) (_el-tr (dom-create-element "tr")) (_el-td (dom-create-element "td")) (_el-input (dom-create-element "input")) (_el-tr4 (dom-create-element "tr")) (_el-td5 (dom-create-element "td")) (_el-input6 (dom-create-element "input")) (_el-tr7 (dom-create-element "tr")) (_el-td8 (dom-create-element "td")) (_el-input9 (dom-create-element "input")) (_el-tr10 (dom-create-element "tr")) (_el-td11 (dom-create-element "td")) (_el-master (dom-create-element "input")))
@@ -7252,1015 +4242,69 @@
(hs-activate! _el-master)
(dom-dispatch (dom-query-by-id "master") "click" nil)
))
- (deftest "full select-all pattern with multiple on features"
+ (deftest "where with is not me in component template"
(hs-cleanup!)
- (let ((_el-table (dom-create-element "table")) (_el-tr (dom-create-element "tr")) (_el-td (dom-create-element "td")) (_el-input (dom-create-element "input")) (_el-tr4 (dom-create-element "tr")) (_el-td5 (dom-create-element "td")) (_el-input6 (dom-create-element "input")) (_el-tr7 (dom-create-element "tr")) (_el-td8 (dom-create-element "td")) (_el-input9 (dom-create-element "input")) (_el-tr10 (dom-create-element "tr")) (_el-td11 (dom-create-element "td")) (_el-master (dom-create-element "input")))
+ (let ((_el-box (dom-create-element "div")) (_el-input (dom-create-element "input")) (_el-input2 (dom-create-element "input")) (_el-script (dom-create-element "script")) (_el-test-where-me (dom-create-element "test-where-me")))
+ (dom-set-attr _el-box "id" "box")
(dom-add-class _el-input "cb")
(dom-set-attr _el-input "type" "checkbox")
- (dom-set-attr _el-input "checked" "")
- (dom-add-class _el-input6 "cb")
- (dom-set-attr _el-input6 "type" "checkbox")
- (dom-add-class _el-input9 "cb")
- (dom-set-attr _el-input9 "type" "checkbox")
- (dom-set-attr _el-input9 "checked" "")
- (dom-set-attr _el-master "id" "master")
- (dom-set-attr _el-master "_" "set :checkboxes to in the closest where it is not me then on change set checked of the :checkboxes to my checked then on change from the closest then if no :checkboxes where it is checked then set my indeterminate to false then set my checked to false then else if no :checkboxes where it is not checked then set my indeterminate to false then set my checked to true then else then set my indeterminate to true then end")
- (dom-set-attr _el-master "type" "checkbox")
- (dom-append (dom-body) _el-table)
- (dom-append _el-table _el-tr)
- (dom-append _el-tr _el-td)
- (dom-append _el-td _el-input)
- (dom-append _el-input _el-tr4)
- (dom-append _el-tr4 _el-td5)
- (dom-append _el-td5 _el-input6)
- (dom-append _el-input6 _el-tr7)
- (dom-append _el-tr7 _el-td8)
- (dom-append _el-td8 _el-input9)
- (dom-append _el-input9 _el-tr10)
- (dom-append _el-tr10 _el-td11)
- (dom-append _el-td11 _el-master)
- (hs-activate! _el-master)
- (dom-dispatch (dom-query-by-id "master") "click" nil)
- (dom-dispatch (dom-query ".cb") "click" nil)
+ (dom-add-class _el-input2 "cb")
+ (dom-set-attr _el-input2 "type" "checkbox")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-where-me")
+ (dom-set-inner-html _el-script " in #box where it is not me on change set checked of the :checkboxes to my checked\">")
+ (dom-append (dom-body) _el-box)
+ (dom-append _el-box _el-input)
+ (dom-append _el-input _el-input2)
+ (dom-append _el-input2 _el-script)
+ (dom-append _el-input2 _el-test-where-me)
+ (dom-dispatch (dom-query "test-where-me input") "click" nil)
))
-)
-
-;; ── splitJoin (7 tests) ──
-(defsuite "hs-upstream-splitJoin"
- (deftest "splits a string by delimiter"
- (assert= (eval-hs "return \"a,b,c\" split by \",\"") (list "a" "b" "c"))
- )
- (deftest "splits by whitespace"
- (assert= (eval-hs "return \"hello world\" split by \" \"") (list "hello" "world"))
- )
- (deftest "joins an array with delimiter"
- (assert= (eval-hs "return [\"a\", \"b\", \"c\"] joined by \", \"") "a, b, c")
- )
- (deftest "joins with empty string"
- (assert= (eval-hs "return [\"x\", \"y\", \"z\"] joined by \"\"") "xyz")
- )
- (deftest "split then where then joined"
- (assert= (eval-hs "return \"a,,b,,c\" split by \",\" where it is not \"\" joined by \"-\"") "a-b-c")
- )
- (deftest "split then sorted then joined"
- (assert= (eval-hs "return \"banana,apple,cherry\" split by \",\" sorted by it joined by \", \"") "apple, banana, cherry")
- )
- (deftest "split then mapped then joined"
- (assert= (eval-hs "return \"hello world\" split by \" \" mapped to its length joined by \",\"") "5,5")
- )
-)
-
-;; ── component (19 tests) ──
-(defsuite "hs-upstream-component"
- (deftest "registers a custom element from a template"
+ (deftest "works with DOM elements"
(hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-span (dom-create-element "span")) (_el-test-hello (dom-create-element "test-hello")))
- (dom-set-attr _el-template "component" "test-hello")
- (dom-set-inner-html _el-span "Hello World")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-span)
- (dom-append (dom-body) _el-test-hello)
- ))
- (deftest "renders template expressions"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-span (dom-create-element "span")) (_el-test-greet (dom-create-element "test-greet")))
- (dom-set-attr _el-template "component" "test-greet")
- (dom-set-inner-html _el-span "Hello ${\"\\x24\"}{$name}!")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-span)
- (dom-append (dom-body) _el-test-greet)
- ))
- (deftest "applies _ hyperscript to component instance"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-span (dom-create-element "span")) (_el-test-init (dom-create-element "test-init")))
- (dom-set-attr _el-template "_" "init set ^msg to 'initialized'")
- (dom-set-attr _el-template "component" "test-init")
- (dom-set-inner-html _el-span "${\"\\x24\"}{^msg}")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-span)
- (dom-append (dom-body) _el-test-init)
- (hs-activate! _el-template)
- ))
- (deftest "processes _ on inner elements"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")) (_el-test-inner (dom-create-element "test-inner")))
- (dom-set-attr _el-template "_" "init set ^count to 0")
- (dom-set-attr _el-template "component" "test-inner")
- (dom-set-attr _el-button "_" "on click increment ^count then put ^count into the next ")
- (dom-set-inner-html _el-button "+")
- (dom-set-inner-html _el-span "0")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-button)
- (dom-append _el-template _el-span)
- (dom-append (dom-body) _el-test-inner)
- (hs-activate! _el-template)
- (hs-activate! _el-button)
- (dom-dispatch (dom-query "test-inner button") "click" nil)
- ))
- (deftest "reactively updates template expressions"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")) (_el-test-reactive (dom-create-element "test-reactive")))
- (dom-set-attr _el-template "_" "init set ^count to 0")
- (dom-set-attr _el-template "component" "test-reactive")
- (dom-set-attr _el-button "_" "on click increment ^count")
- (dom-set-inner-html _el-button "+")
- (dom-set-inner-html _el-span "Count: ${\"\\x24\"}{^count}")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-button)
- (dom-append _el-template _el-span)
- (dom-append (dom-body) _el-test-reactive)
- (hs-activate! _el-template)
- (hs-activate! _el-button)
- (dom-dispatch (dom-query "test-reactive button") "click" nil)
- ))
- (deftest "supports multiple independent instances"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")) (_el-a (dom-create-element "test-multi")) (_el-b (dom-create-element "test-multi")))
- (dom-set-attr _el-template "_" "init set ^count to 0")
- (dom-set-attr _el-template "component" "test-multi")
- (dom-set-attr _el-button "_" "on click increment ^count then put ^count into the next ")
- (dom-set-inner-html _el-button "+")
- (dom-set-inner-html _el-span "0")
- (dom-set-attr _el-a "id" "a")
- (dom-set-attr _el-b "id" "b")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-button)
- (dom-append _el-template _el-span)
- (dom-append (dom-body) _el-a)
- (dom-append (dom-body) _el-b)
- (hs-activate! _el-template)
- (hs-activate! _el-button)
- (dom-dispatch (dom-query "#a button") "click" nil)
- ))
- (deftest "reads attributes via @"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-span (dom-create-element "span")) (_el-test-attrs (dom-create-element "test-attrs")))
- (dom-set-attr _el-template "_" "init set ^val to @data-start as Int")
- (dom-set-attr _el-template "component" "test-attrs")
- (dom-set-inner-html _el-span "${\"\\x24\"}{^val}")
- (dom-set-attr _el-test-attrs "data-start" "42")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-span)
- (dom-append (dom-body) _el-test-attrs)
- (hs-activate! _el-template)
- ))
- (deftest "supports #for loops in template"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-ul (dom-create-element "ul")) (_el-li (dom-create-element "li")) (_el-test-loop (dom-create-element "test-loop")))
- (dom-set-attr _el-template "_" "init set ^items to ['a', 'b', 'c']")
- (dom-set-attr _el-template "component" "test-loop")
- (dom-set-inner-html _el-ul "#for item in ^items")
- (dom-set-inner-html _el-li "${\"\\x24\"}{item}")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-ul)
- (dom-append _el-ul _el-li)
- (dom-append (dom-body) _el-test-loop)
- (hs-activate! _el-template)
- ))
- (deftest "supports #if conditionals in template"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-span (dom-create-element "span")) (_el-test-cond (dom-create-element "test-cond")))
- (dom-set-attr _el-template "_" "init set ^show to true")
- (dom-set-attr _el-template "component" "test-cond")
- (dom-set-inner-html _el-template "#if ^show")
- (dom-set-inner-html _el-span "visible")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-span)
- (dom-append (dom-body) _el-test-cond)
- (hs-activate! _el-template)
- ))
- (deftest "substitutes slot content into template"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-div (dom-create-element "div")) (_el-slot (dom-create-element "slot")) (_el-test-card (dom-create-element "test-card")) (_el-p (dom-create-element "p")))
- (dom-set-attr _el-template "component" "test-card")
- (dom-add-class _el-div "card")
- (dom-set-inner-html _el-p "Hello from slot")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-div)
- (dom-append _el-div _el-slot)
- (dom-append (dom-body) _el-test-card)
- (dom-append _el-test-card _el-p)
- ))
- (deftest "blocks processing of inner hyperscript until render"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-span (dom-create-element "span")) (_el-test-block (dom-create-element "test-block")))
- (dom-set-attr _el-template "_" "init set ^msg to 'ready'")
- (dom-set-attr _el-template "component" "test-block")
- (dom-set-attr _el-span "_" "on click put ^msg into me")
- (dom-set-inner-html _el-span "click me")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-span)
- (dom-append (dom-body) _el-test-block)
- (hs-activate! _el-template)
- (hs-activate! _el-span)
- (dom-dispatch (dom-query "test-block span") "click" nil)
- ))
- (deftest "supports named slots"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-header (dom-create-element "header")) (_el-slot (dom-create-element "slot")) (_el-main (dom-create-element "main")) (_el-slot4 (dom-create-element "slot")) (_el-footer (dom-create-element "footer")) (_el-slot6 (dom-create-element "slot")) (_el-test-named-slot (dom-create-element "test-named-slot")) (_el-h1 (dom-create-element "h1")) (_el-p (dom-create-element "p")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-template "component" "test-named-slot")
- (dom-set-attr _el-slot "name" "title")
- (dom-set-attr _el-slot6 "name" "footer")
- (dom-set-attr _el-h1 "slot" "title")
- (dom-set-inner-html _el-h1 "My Title")
- (dom-set-inner-html _el-p "Default content")
- (dom-set-attr _el-span "slot" "footer")
- (dom-set-inner-html _el-span "Footer text")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-header)
- (dom-append _el-header _el-slot)
- (dom-append _el-template _el-main)
- (dom-append _el-main _el-slot4)
- (dom-append _el-template _el-footer)
- (dom-append _el-footer _el-slot6)
- (dom-append (dom-body) _el-test-named-slot)
- (dom-append _el-test-named-slot _el-h1)
- (dom-append _el-test-named-slot _el-p)
- (dom-append _el-test-named-slot _el-span)
- ))
- (deftest "does not process slotted _ attributes prematurely"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-template (dom-create-element "template")) (_el-div2 (dom-create-element "div")) (_el-slot (dom-create-element "slot")) (_el-test-slot-hs (dom-create-element "test-slot-hs")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-div "_" "init set ^x to 42")
- (dom-set-attr _el-template "component" "test-slot-hs")
- (dom-add-class _el-div2 "wrap")
- (dom-set-attr _el-span "_" "on click put ^x into me")
- (dom-set-inner-html _el-span "before")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-template)
- (dom-append _el-template _el-div2)
- (dom-append _el-div2 _el-slot)
- (dom-append _el-div _el-test-slot-hs)
- (dom-append _el-test-slot-hs _el-span)
- (hs-activate! _el-div)
- (hs-activate! _el-span)
- (dom-dispatch (dom-query "test-slot-hs span") "click" nil)
- ))
- (deftest "slotted content resolves ^var from outer scope, not component scope"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-template (dom-create-element "template")) (_el-div2 (dom-create-element "div")) (_el-slot (dom-create-element "slot")) (_el-test-scope-slot (dom-create-element "test-scope-slot")) (_el-span (dom-create-element "span")))
- (dom-set-attr _el-div "_" "init set ^outer to 'from-outside'")
- (dom-set-attr _el-template "_" "init set ^outer to 'from-component'")
- (dom-set-attr _el-template "component" "test-scope-slot")
- (dom-add-class _el-div2 "inner")
- (dom-set-attr _el-span "_" "init put ^outer into me")
- (dom-set-inner-html _el-span "waiting")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-template)
- (dom-append _el-template _el-div2)
- (dom-append _el-div2 _el-slot)
- (dom-append _el-div _el-test-scope-slot)
- (dom-append _el-test-scope-slot _el-span)
- (hs-activate! _el-div)
- (hs-activate! _el-template)
- (hs-activate! _el-span)
- ))
- (deftest "component isolation prevents ^var leaking inward"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-template (dom-create-element "template")) (_el-span (dom-create-element "span")) (_el-test-isolated (dom-create-element "test-isolated")))
- (dom-set-attr _el-div "_" "init set ^leaked to 'should-not-see'")
- (dom-set-attr _el-template "_" "init set ^internal to 'component-only'")
- (dom-set-attr _el-template "component" "test-isolated")
- (dom-set-attr _el-span "_" "init if ^leaked is not undefined put 'leaked!' into me else put ^internal into me")
- (dom-set-inner-html _el-span "waiting")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-template)
- (dom-append _el-template _el-span)
- (dom-append _el-div _el-test-isolated)
- (hs-activate! _el-div)
- (hs-activate! _el-template)
- (hs-activate! _el-span)
- ))
- (deftest "bind keeps ^var in sync with attribute changes"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-span (dom-create-element "span")) (_el-test-bind-attr (dom-create-element "test-bind-attr")))
- (dom-set-attr _el-template "_" "bind ^count to @data-count")
- (dom-set-attr _el-template "component" "test-bind-attr")
- (dom-set-inner-html _el-span "${\"\\x24\"}{^count}")
- (dom-set-attr _el-test-bind-attr "data-count" "5")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-span)
- (dom-append (dom-body) _el-test-bind-attr)
- (hs-activate! _el-template)
- ))
- (deftest "attrs evaluates attribute as hyperscript in parent scope"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-ul (dom-create-element "ul")) (_el-li (dom-create-element "li")) (_el-test-args (dom-create-element "test-args")))
- (dom-set-attr _el-template "_" "init set ^list to attrs.items")
- (dom-set-attr _el-template "component" "test-args")
- (dom-set-inner-html _el-ul "#for item in ^list")
- (dom-set-inner-html _el-li "${\"\\x24\"}{item}")
- (dom-set-attr _el-test-args "items" "$stuff")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-ul)
- (dom-append _el-ul _el-li)
- (dom-append (dom-body) _el-test-args)
- (hs-activate! _el-template)
- ))
- (deftest "attrs works with bind for reactive pass-through"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-span (dom-create-element "span")) (_el-test-args-bind (dom-create-element "test-args-bind")) (_el-button (dom-create-element "button")))
- (dom-set-attr _el-template "_" "bind ^val to attrs.count")
- (dom-set-attr _el-template "component" "test-args-bind")
- (dom-set-inner-html _el-span "${\"\\x24\"}{^val}")
- (dom-set-attr _el-test-args-bind "count" "$count")
- (dom-set-attr _el-button "_" "on click increment $count")
- (dom-set-inner-html _el-button "+")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-span)
- (dom-append (dom-body) _el-test-args-bind)
+ (let ((_el-list (dom-create-element "ul")) (_el-li (dom-create-element "li")) (_el-li2 (dom-create-element "li")) (_el-li3 (dom-create-element "li")) (_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
+ (dom-set-attr _el-list "id" "list")
+ (dom-add-class _el-li "yes")
+ (dom-set-inner-html _el-li "A")
+ (dom-set-inner-html _el-li2 "B")
+ (dom-add-class _el-li3 "yes")
+ (dom-set-inner-html _el-li3 "C")
+ (dom-set-attr _el-button "_" "on click set items to in #list set matches to items where it matches .yes then put matches mapped to its textContent into #out")
+ (dom-set-inner-html _el-button "Go")
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-list)
+ (dom-append _el-list _el-li)
+ (dom-append _el-list _el-li2)
+ (dom-append _el-list _el-li3)
(dom-append (dom-body) _el-button)
- (hs-activate! _el-template)
+ (dom-append (dom-body) _el-out)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
- ))
- (deftest "attrs bind is bidirectional — inner changes flow outward"
- (hs-cleanup!)
- (let ((_el-template (dom-create-element "template")) (_el-span (dom-create-element "span")) (_el-button (dom-create-element "button")) (_el-test-args-bidir (dom-create-element "test-args-bidir")) (_el-p (dom-create-element "p")))
- (dom-set-attr _el-template "_" "bind ^count to attrs.count")
- (dom-set-attr _el-template "component" "test-args-bidir")
- (dom-set-inner-html _el-span "${\"\\x24\"}{^count}")
- (dom-set-attr _el-button "_" "on click increment ^count")
- (dom-set-inner-html _el-button "+")
- (dom-set-attr _el-test-args-bidir "count" "$count")
- (dom-set-attr _el-p "_" "live put $count into me")
- (dom-append (dom-body) _el-template)
- (dom-append _el-template _el-span)
- (dom-append _el-template _el-button)
- (dom-append (dom-body) _el-test-args-bidir)
- (dom-append (dom-body) _el-p)
- (hs-activate! _el-template)
- (hs-activate! _el-button)
- (hs-activate! _el-p)
- (dom-dispatch (dom-query "test-args-bidir button") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "out")) "AC")
))
)
-;; ── default (9 tests) ──
-(defsuite "hs-upstream-default"
- (deftest "can default possessive properties"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click default #d1's foo to 'bar' then put #d1's foo into me")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch (dom-query-by-id "d1") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "d1")) "bar")
- ))
- (deftest "can default of-expression properties"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click default foo of me to 'bar' then put my foo into me")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch (dom-query-by-id "d1") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "d1")) "bar")
- ))
- (deftest "can default array elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set arr to [null, null] then default arr[0] to 'yes' then put arr[0] into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "yes")
- ))
- (deftest "default array element respects existing value"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set arr to ['existing', null] then default arr[0] to 'new' then put arr[0] into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "existing")
- ))
- (deftest "default preserves zero"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set x to 0 then default x to 10 then put x into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "0")
- ))
- (deftest "default overwrites empty string"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set x to \"\" then default x to \"fallback\" then put x into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "fallback")
- ))
- (deftest "default preserves false"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click set x to false then default x to true then put x into me")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "false")
- ))
- (deftest "can default style ref when unset"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click default *background-color to 'red'")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "background-color") "")
- ))
- (deftest "default style ref preserves existing value"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click default *color to 'red'")
- (dom-set-attr _el-div "style" "color: blue")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-get-style _el-div "color") "")
- ))
-)
-
-;; ── js (1 tests) ──
-(defsuite "hs-upstream-js"
- (deftest "handles rejected promises without hanging"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click js return Promise.reject(\"boom\") end catch e put e into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "boom")
- ))
-)
-
-;; ── measure (2 tests) ──
-(defsuite "hs-upstream-measure"
- (deftest "can measure with possessive syntax"
- (hs-cleanup!)
- (let ((_el-other (dom-create-element "div")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-other "id" "other")
- (dom-set-attr _el-other "style" "all: initial; position: fixed; top: 89px")
- (dom-set-attr _el-div "_" "on click measure #other's top then set window.measurement to {top: top}")
- (dom-append (dom-body) _el-other)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
- ))
- (deftest "can measure with of syntax"
- (hs-cleanup!)
- (let ((_el-other (dom-create-element "div")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-other "id" "other")
- (dom-set-attr _el-other "style" "all: initial; position: fixed; top: 89px")
- (dom-set-attr _el-div "_" "on click measure top of #other then set window.measurement to {top: top}")
- (dom-append (dom-body) _el-other)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
- ))
-)
-
-;; ── pick (7 tests) ──
-(defsuite "hs-upstream-pick"
- (deftest "does not hang on zero-length regex matches"
- (let ((haystack "a1b")) (eval-hs "pick matches of \"d*\" from haystack set window.test to it") (assert (not (nil? it))))
- )
- (deftest "can pick first n items"
- (let ((arr (list 10 20 30 40 50))) (eval-hs "pick first 3 of arr set $test to it") (assert= it (list 10 20 30)))
- )
- (deftest "can pick last n items"
- (let ((arr (list 10 20 30 40 50))) (eval-hs "pick last 2 of arr set $test to it") (assert= it (list 40 50)))
- )
- (deftest "can pick random item"
- (let ((arr (list 10 20 30))) (eval-hs "pick random of arr set $test to it") (assert (not (nil? it))))
- )
- (deftest "can pick random n items"
- (let ((arr (list 10 20 30 40 50))) (eval-hs "pick random 2 of arr set $test to it") (assert= (len it) 2))
- )
- (deftest "can pick items using 'of' syntax"
- (let ((arr (list 10 11 12 13 14 15 16))) (eval-hs "pick items 1 to 3 of arr set $test to it") (assert= it (list 11 12)))
- )
- (deftest "can pick match using 'of' syntax"
- (assert= (eval-hs "pick match of \"d+\" of haystack set window.test to it") (list "32"))
- )
-)
-
-;; ── settle (1 tests) ──
-(defsuite "hs-upstream-settle"
- (deftest "can settle a collection of elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-trigger (dom-create-element "div")))
- (dom-add-class _el-div "item")
- (dom-add-class _el-div1 "item")
- (dom-set-attr _el-trigger "id" "trigger")
- (dom-set-attr _el-trigger "_" "on click settle <.item/> then add .done to <.item/>")
- (dom-append (dom-body) _el-div)
- (dom-append (dom-body) _el-div1)
- (dom-append (dom-body) _el-trigger)
- (hs-activate! _el-trigger)
- (dom-dispatch (dom-query-by-id "trigger") "click" nil)
- (assert (dom-has-class? (dom-query ".item") "done"))
- ))
-)
-
-;; ── show (2 tests) ──
-(defsuite "hs-upstream-show"
- (deftest "the result in a when clause refers to previous command result, not element being tested"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-s1 (dom-create-element "span")) (_el-s2 (dom-create-element "span")))
- (dom-set-attr _el-div "_" "on click get 'found' then show in me when the result is 'found'")
- (dom-set-attr _el-s1 "id" "s1")
- (dom-set-attr _el-s1 "style" "display:none")
- (dom-set-inner-html _el-s1 "A")
- (dom-set-attr _el-s2 "id" "s2")
- (dom-set-attr _el-s2 "style" "display:none")
- (dom-set-inner-html _el-s2 "B")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-s1)
- (dom-append _el-div _el-s2)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-visible? (dom-query-by-id "s1")))
- (assert (dom-visible? (dom-query-by-id "s2")))
- ))
- (deftest "the result after show...when is the matched elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-p2 (dom-create-element "p")) (_el-out (dom-create-element "span")))
- (dom-set-attr _el-div "_" "on click show in me when its textContent is 'yes' then if the result is empty put 'none' into #out else put 'some' into #out")
- (dom-set-attr _el-p "style" "display:none")
- (dom-set-inner-html _el-p "yes")
- (dom-set-attr _el-p2 "style" "display:none")
- (dom-set-inner-html _el-p2 "no")
- (dom-set-attr _el-out "id" "out")
- (dom-set-inner-html _el-out "--")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-p)
- (dom-append _el-div _el-p2)
- (dom-append _el-div _el-out)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content (dom-query-by-id "out")) "some")
- ))
-)
-
-;; ── socket (4 tests) ──
-(defsuite "hs-upstream-socket"
- (deftest "parses socket with absolute ws:// URL"
- (hs-cleanup!))
- (deftest "converts relative URL to wss:// on https pages"
- (hs-cleanup!))
- (deftest "converts relative URL to ws:// on http pages"
- (hs-cleanup!))
- (deftest "namespaced sockets work"
- (hs-cleanup!))
-)
-
-;; ── bootstrap (14 tests) ──
-(defsuite "hs-upstream-bootstrap"
- (deftest "hyperscript can have more than one action"
- (hs-cleanup!)
- (let ((_el-bar (dom-create-element "div")) (_el-div (dom-create-element "div")))
- (dom-set-attr _el-bar "id" "bar")
- (dom-set-attr _el-div "_" "on click add .foo to #bar then add .blah")
- (dom-append (dom-body) _el-bar)
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
- (assert (dom-has-class? (dom-query-by-id "bar") "foo"))
- (assert (not (dom-has-class? (dom-query-by-id "bar") "blah")))
- (assert (not (dom-has-class? (dom-query "div:nth-of-type(2)") "foo")))
- (assert (dom-has-class? (dom-query "div:nth-of-type(2)") "blah"))
- ))
- (deftest "stores state on elt._hyperscript"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add .foo")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "skips reinitialization if script unchanged"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add .foo")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "reinitializes if script attribute changes"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add .foo")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-div "foo"))
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-div "bar"))
- ))
- (deftest "cleanup removes event listeners on the element"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add .foo")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert (dom-has-class? _el-div "foo"))
- (assert (not (dom-has-class? _el-div "foo")))
- ))
- (deftest "cleanup removes cross-element event listeners"
- (hs-cleanup!)
- (let ((_el-source (dom-create-element "div")) (_el-target (dom-create-element "div")))
- (dom-set-attr _el-source "id" "source")
- (dom-set-attr _el-target "id" "target")
- (dom-set-attr _el-target "_" "on click from #source add .foo")
- (dom-append (dom-body) _el-source)
- (dom-append (dom-body) _el-target)
- (hs-activate! _el-target)
- (dom-dispatch (dom-query-by-id "source") "click" nil)
- (assert (dom-has-class? (dom-query-by-id "target") "foo"))
- ))
- (deftest "cleanup tracks listeners in elt._hyperscript"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add .foo")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "cleanup clears elt._hyperscript"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add .foo")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "sets data-hyperscript-powered on initialized elements"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add .foo")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "cleanup removes data-hyperscript-powered"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add .foo")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "fires hyperscript:before:init and hyperscript:after:init"
- (hs-cleanup!))
- (deftest "hyperscript:before:init can cancel initialization"
- (hs-cleanup!))
- (deftest "fires hyperscript:before:cleanup and hyperscript:after:cleanup"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click add .foo")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- ))
- (deftest "logAll config logs events to console"
- (hs-cleanup!))
-)
-
-;; ── parser (7 tests) ──
-(defsuite "hs-upstream-parser"
- (deftest "can have comments in attributes (triple dash)"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")))
- (dom-set-attr _el-div "_" "on click put \"clicked\" into my.innerHTML ---put some content into the div...")
- (dom-append (dom-body) _el-div)
- (hs-activate! _el-div)
- (dom-dispatch _el-div "click" nil)
- (assert= (dom-text-content _el-div) "clicked")
- ))
- (deftest "recovers across feature boundaries and reports all errors"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click blargh end on mouseenter put \"hovered\" into my.innerHTML")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- ))
- (deftest "recovers across multiple feature errors"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click blargh end on mouseenter also_bad end on focus put \"focused\" into my.innerHTML")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- ))
- (deftest "fires hyperscript:parse-error event with all errors"
- (hs-cleanup!))
- (deftest "element-level isolation still works with error recovery"
- (hs-cleanup!)
- (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click blargh end on mouseenter also_bad")
- (dom-set-attr _el-d2 "id" "d2")
- (dom-set-attr _el-d2 "_" "on click put \"clicked\" into my.innerHTML")
- (dom-append (dom-body) _el-div)
- (dom-append _el-div _el-d1)
- (dom-append _el-div _el-d2)
- (hs-activate! _el-d1)
- (hs-activate! _el-d2)
- (dom-dispatch (dom-query-by-id "d2") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "d2")) "clicked")
- ))
- (deftest "_hyperscript() evaluate API still throws on first error"
- (hs-cleanup!))
- (deftest "parse error at EOF on trailing newline does not crash"
- (hs-cleanup!))
-)
-
-;; ── scoping (1 tests) ──
-(defsuite "hs-upstream-scoping"
- (deftest "element scoped variables span features w/short syntax"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click 1 set :x to 10 on click 2 set @out to :x")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch (dom-query-by-id "d1") "click" nil)
- (dom-dispatch (dom-query-by-id "d1") "click" nil)
- (assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
- ))
-)
-
-;; ── asExpression (17 tests) ──
-(defsuite "hs-upstream-asExpression"
- (deftest "converts value as Boolean"
- (assert= (eval-hs "1 as Boolean") true)
- (assert= (eval-hs "0 as Boolean") false)
- (assert= (eval-hs "'' as Boolean") false)
- (assert= (eval-hs "'hello' as Boolean") true)
- )
- (deftest "can use the a modifier if you like"
- (hs-cleanup!))
- (deftest "parses string as JSON to object"
- (assert= (host-get (eval-hs "'{\"foo\":\"bar\"}' as JSON") "foo") "bar")
- )
- (deftest "converts value as JSONString"
- (assert= (eval-hs "{foo:'bar'} as JSONString") "{"foo":"bar"}")
- )
- (deftest "pipe operator chains conversions"
- (assert= (host-get (eval-hs "{foo:'bar'} as JSONString | JSON") "foo") "bar")
- )
- (deftest "can use the an modifier if you'd like"
- (assert= (host-get (eval-hs "'{\"foo\":\"bar\"}' as an Object") "foo") "bar")
- )
- (deftest "collects duplicate text inputs into an array"
- (hs-cleanup!))
- (deftest "converts multiple selects with programmatically changed selections"
- (hs-cleanup!))
- (deftest "converts a form element into Values | JSONString"
- (hs-cleanup!))
- (deftest "converts a form element into Values | FormEncoded"
- (hs-cleanup!))
- (deftest "converts array as Set"
- (hs-cleanup!))
- (deftest "converts object as Map"
- (hs-cleanup!))
- (deftest "converts object as Keys"
- (assert= (eval-hs "{a:1, b:2} as Keys") (list "a" "b"))
- )
- (deftest "converts object as Entries"
- (assert= (eval-hs "{a:1} as Entries") (list (list "a" 1)))
- )
- (deftest "converts array as Reversed"
- (assert= (eval-hs "[1,2,3] as Reversed") (list 3 2 1))
- )
- (deftest "converts array as Unique"
- (assert= (eval-hs "[1,2,2,3,3] as Unique") (list 1 2 3))
- )
- (deftest "converts nested array as Flat"
- (assert= (eval-hs "[[1,2],[3,4]] as Flat") (list 1 2 3 4))
- )
-)
-
-;; ── attributeRef (1 tests) ──
-(defsuite "hs-upstream-attributeRef"
- (deftest "attributeRef can be set through possessive w/ short syntax"
- (hs-cleanup!)
- (let ((_el-arDiv (dom-create-element "div")))
- (dom-set-attr _el-arDiv "id" "arDiv")
- (dom-set-attr _el-arDiv "_" "on click set my @data-foo to \"blue\"")
- (dom-set-attr _el-arDiv "data-foo" "red")
- (dom-append (dom-body) _el-arDiv)
- (hs-activate! _el-arDiv)
- (dom-dispatch (dom-query-by-id "arDiv") "click" nil)
- ))
-)
-
-;; ── closest (3 tests) ──
-(defsuite "hs-upstream-closest"
- (deftest "attributes can be set via the closest expression 2"
- (hs-cleanup!)
- (let ((_el-outerDiv2 (dom-create-element "div")) (_el-d1b (dom-create-element "div")))
- (dom-set-attr _el-outerDiv2 "id" "outerDiv2")
- (dom-set-attr _el-outerDiv2 "foo" "bar")
- (dom-set-attr _el-d1b "id" "d1b")
- (dom-set-attr _el-d1b "_" "on click set closest @foo to \"doh\"")
- (dom-append (dom-body) _el-outerDiv2)
- (dom-append _el-outerDiv2 _el-d1b)
- (hs-activate! _el-d1b)
- (dom-dispatch (dom-query-by-id "d1b") "click" nil)
- ))
- (deftest "closest does not consume a following where clause"
- (hs-cleanup!)
- (let ((_el-table (dom-create-element "table")) (_el-tr (dom-create-element "tr")) (_el-td (dom-create-element "td")) (_el-input (dom-create-element "input")) (_el-input4 (dom-create-element "input")) (_el-master (dom-create-element "input")) (_el-out (dom-create-element "div")))
- (dom-add-class _el-input "cb")
- (dom-set-attr _el-input "type" "checkbox")
- (dom-add-class _el-input4 "cb")
- (dom-set-attr _el-input4 "type" "checkbox")
- (dom-set-attr _el-master "id" "master")
- (dom-set-attr _el-master "_" "set :others to in the closest where it is not me on click put :others.length into #out")
- (dom-set-attr _el-master "type" "checkbox")
- (dom-set-attr _el-out "id" "out")
- (dom-append (dom-body) _el-table)
- (dom-append _el-table _el-tr)
- (dom-append _el-tr _el-td)
- (dom-append _el-td _el-input)
- (dom-append _el-input _el-input4)
- (dom-append _el-input4 _el-master)
- (dom-append _el-master _el-out)
- (hs-activate! _el-master)
- (dom-dispatch (dom-query-by-id "master") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "out")) "2")
- ))
- (deftest "closest with to modifier still works after parse change"
- (hs-cleanup!)
- (let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "div")))
- (dom-set-attr _el-outer "id" "outer")
- (dom-set-attr _el-inner "id" "inner")
- (dom-append (dom-body) _el-outer)
- (dom-append _el-outer _el-inner)
- ))
-)
-
-;; ── comparisonOperator (40 tests) ──
-(defsuite "hs-upstream-comparisonOperator"
- (deftest "is a works with instanceof fallback"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click if I am a Element put \"yes\" into me")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch (dom-query-by-id "d1") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "d1")) "yes")
- ))
- (deftest "is a Node works via instanceof"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click if I am a Node put \"yes\" into me")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch (dom-query-by-id "d1") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "d1")) "yes")
- ))
- (deftest "is not a works with instanceof fallback"
- (hs-cleanup!)
- (let ((_el-d1 (dom-create-element "div")))
- (dom-set-attr _el-d1 "id" "d1")
- (dom-set-attr _el-d1 "_" "on click if \"hello\" is not a Element put \"yes\" into me")
- (dom-append (dom-body) _el-d1)
- (hs-activate! _el-d1)
- (dom-dispatch (dom-query-by-id "d1") "click" nil)
- (assert= (dom-text-content (dom-query-by-id "d1")) "yes")
- ))
- (deftest "is ignoring case works"
- (assert= (eval-hs "'Hello' is 'hello' ignoring case") true)
- (assert= (eval-hs "'Hello' is 'HELLO' ignoring case") true)
- (assert= (eval-hs "'Hello' is 'world' ignoring case") false)
- )
- (deftest "is not ignoring case works"
- (assert= (eval-hs "'Hello' is not 'world' ignoring case") true)
- (assert= (eval-hs "'Hello' is not 'hello' ignoring case") false)
- )
- (deftest "contains ignoring case works"
- (assert= (eval-hs "'Hello World' contains 'hello' ignoring case") true)
- (assert= (eval-hs "'Hello World' contains 'WORLD' ignoring case") true)
- (assert= (eval-hs "'Hello World' contains 'missing' ignoring case") false)
- )
- (deftest "matches ignoring case works"
- (assert= (eval-hs "'Hello' matches 'hello' ignoring case") true)
- (assert= (eval-hs "'Hello' matches 'HELLO' ignoring case") true)
- )
- (deftest "starts with works"
- (assert= (eval-hs "'hello world' starts with 'hello'") true)
- (assert= (eval-hs "'hello world' starts with 'world'") false)
- (assert= (eval-hs "'hello' starts with 'hello'") true)
- (assert= (eval-hs "'' starts with 'x'") false)
- )
- (deftest "ends with works"
- (assert= (eval-hs "'hello world' ends with 'world'") true)
- (assert= (eval-hs "'hello world' ends with 'hello'") false)
- (assert= (eval-hs "'hello' ends with 'hello'") true)
- (assert= (eval-hs "'' ends with 'x'") false)
- )
- (deftest "does not start with works"
- (assert= (eval-hs "'hello world' does not start with 'hello'") false)
- (assert= (eval-hs "'hello world' does not start with 'world'") true)
- )
- (deftest "does not end with works"
- (assert= (eval-hs "'hello world' does not end with 'world'") false)
- (assert= (eval-hs "'hello world' does not end with 'hello'") true)
- )
- (deftest "starts with null is false"
- (assert= (eval-hs "null starts with 'x'") false)
- (assert= (eval-hs "null does not start with 'x'") true)
- )
- (deftest "ends with null is false"
- (assert= (eval-hs "null ends with 'x'") false)
- (assert= (eval-hs "null does not end with 'x'") true)
- )
- (deftest "starts with ignoring case works"
- (assert= (eval-hs "'Hello World' starts with 'hello' ignoring case") true)
- (assert= (eval-hs "'Hello World' starts with 'HELLO' ignoring case") true)
- (assert= (eval-hs "'Hello World' starts with 'world' ignoring case") false)
- )
- (deftest "ends with ignoring case works"
- (assert= (eval-hs "'Hello World' ends with 'world' ignoring case") true)
- (assert= (eval-hs "'Hello World' ends with 'WORLD' ignoring case") true)
- (assert= (eval-hs "'Hello World' ends with 'hello' ignoring case") false)
- )
- (deftest "starts with coerces to string"
- (assert= (eval-hs "123 starts with '12'") true)
- (assert= (eval-hs "123 starts with '23'") false)
- )
- (deftest "ends with coerces to string"
- (assert= (eval-hs "123 ends with '23'") true)
- (assert= (eval-hs "123 ends with '12'") false)
- )
- (deftest "is between works"
- (assert= (eval-hs "5 is between 1 and 10") true)
- (assert= (eval-hs "1 is between 1 and 10") true)
- (assert= (eval-hs "10 is between 1 and 10") true)
- (assert= (eval-hs "0 is between 1 and 10") false)
- (assert= (eval-hs "11 is between 1 and 10") false)
- )
- (deftest "is not between works"
- (assert= (eval-hs "5 is not between 1 and 10") false)
- (assert= (eval-hs "0 is not between 1 and 10") true)
- (assert= (eval-hs "11 is not between 1 and 10") true)
- (assert= (eval-hs "1 is not between 1 and 10") false)
- (assert= (eval-hs "10 is not between 1 and 10") false)
- )
- (deftest "between works with strings"
- (assert= (eval-hs "'b' is between 'a' and 'c'") true)
- (assert= (eval-hs "'d' is between 'a' and 'c'") false)
- )
+;; ── expressions/comparisonOperator (83 tests) ──
+(defsuite "hs-upstream-expressions/comparisonOperator"
(deftest "I am between works"
(assert= (eval-hs-with-me "I am between 1 and 10" 5) true)
(assert= (eval-hs-with-me "I am between 1 and 10" 0) false)
)
+ (deftest "I am in works"
+ (assert= (eval-hs-with-me "I am in [1, 2]" 1) true)
+ (assert= (eval-hs-with-me "I am in [1, 2]" 2) true)
+ (assert= (eval-hs-with-me "I am in [1, 2]" 3) false)
+ (assert= (eval-hs "I am in null") false)
+ )
(deftest "I am not between works"
(assert= (eval-hs-with-me "I am not between 1 and 10" 5) false)
(assert= (eval-hs-with-me "I am not between 1 and 10" 0) true)
)
- (deftest "precedes works"
- (hs-cleanup!)
- (let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")) (_el-c (dom-create-element "div")))
- (dom-set-attr _el-a "id" "a")
- (dom-set-attr _el-b "id" "b")
- (dom-set-attr _el-c "id" "c")
- (dom-append (dom-body) _el-a)
- (dom-append (dom-body) _el-b)
- (dom-append (dom-body) _el-c)
- ))
- (deftest "follows works"
- (hs-cleanup!)
- (let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")) (_el-c (dom-create-element "div")))
- (dom-set-attr _el-a "id" "a")
- (dom-set-attr _el-b "id" "b")
- (dom-set-attr _el-c "id" "c")
- (dom-append (dom-body) _el-a)
- (dom-append (dom-body) _el-b)
- (dom-append (dom-body) _el-c)
- ))
- (deftest "does not precede works"
- (hs-cleanup!)
- (let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")))
- (dom-set-attr _el-a "id" "a")
- (dom-set-attr _el-b "id" "b")
- (dom-append (dom-body) _el-a)
- (dom-append (dom-body) _el-b)
- ))
- (deftest "does not follow works"
- (hs-cleanup!)
- (let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")))
- (dom-set-attr _el-a "id" "a")
- (dom-set-attr _el-b "id" "b")
- (dom-append (dom-body) _el-a)
- (dom-append (dom-body) _el-b)
- ))
- (deftest "precedes with null is false"
- (assert= (eval-hs "null precedes null") false)
- (assert= (eval-hs "null does not precede null") true)
+ (deftest "I am not in works"
+ (assert= (eval-hs-with-me "I am not in [1, 2]" 1) false)
+ (assert= (eval-hs-with-me "I am not in [1, 2]" 2) false)
+ (assert= (eval-hs-with-me "I am not in [1, 2]" 3) true)
+ (assert= (eval-hs "I am not in null") true)
)
(deftest "I precede works"
(hs-cleanup!)
@@ -8274,70 +4318,228 @@
(dom-dispatch (dom-query-by-id "a") "click" nil)
(assert= (dom-text-content (dom-query-by-id "a")) "yes")
))
- (deftest "is really works without equal to"
- (assert= (eval-hs "2 is really 2") true)
- (assert= (eval-hs "2 is really '2'") false)
- )
- (deftest "is not really works without equal to"
- (assert= (eval-hs "2 is not really '2'") true)
- (assert= (eval-hs "2 is not really 2") false)
- )
- (deftest "is equal works without to"
- (assert= (eval-hs "2 is equal 2") true)
- (assert= (eval-hs "2 is equal 1") false)
- )
- (deftest "is not equal works without to"
- (assert= (eval-hs "2 is not equal 2") false)
- (assert= (eval-hs "2 is not equal 1") true)
- )
(deftest "am works as alias for is"
(assert= (eval-hs "2 am 2") true)
(assert= (eval-hs "2 am 1") false)
)
- (deftest "is not undefined still works as equality"
- (assert= (eval-hs "5 is not undefined") true)
- (assert= (eval-hs "null is not undefined") false)
+ (deftest "between works with strings"
+ (assert= (eval-hs "'b' is between 'a' and 'c'") true)
+ (assert= (eval-hs "'d' is between 'a' and 'c'") false)
)
- (deftest "is not null still works as equality"
- (assert= (eval-hs "5 is not null") true)
- (assert= (eval-hs "null is not null") false)
+ (deftest "contains ignoring case works"
+ (assert= (eval-hs "'Hello World' contains 'hello' ignoring case") true)
+ (assert= (eval-hs "'Hello World' contains 'WORLD' ignoring case") true)
+ (assert= (eval-hs "'Hello World' contains 'missing' ignoring case") false)
)
- (deftest "is falls back to boolean property when rhs is undefined"
+ (deftest "contains works with arrays"
+ (let ((that 1)) (assert= (eval-hs "I contain that") true))
+ (let ((that "[1")) (assert= (eval-hs "that contains me") true))
+ )
+ (deftest "contains works with css literals"
(hs-cleanup!)
- (let ((_el-c1 (dom-create-element "input")) (_el-c2 (dom-create-element "input")))
- (dom-set-attr _el-c1 "id" "c1")
- (dom-set-attr _el-c1 "type" "checkbox")
- (dom-set-attr _el-c1 "checked" "checked")
- (dom-set-attr _el-c2 "id" "c2")
- (dom-set-attr _el-c2 "type" "checkbox")
- (dom-append (dom-body) _el-c1)
- (dom-append (dom-body) _el-c2)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "outer")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-append (dom-body) _el-d1)
+ (dom-append _el-d1 _el-d2)
))
- (deftest "is not falls back to boolean property when rhs is undefined"
+ (deftest "contains works with elts"
(hs-cleanup!)
- (let ((_el-c1 (dom-create-element "input")) (_el-c2 (dom-create-element "input")))
- (dom-set-attr _el-c1 "id" "c1")
- (dom-set-attr _el-c1 "type" "checkbox")
- (dom-set-attr _el-c1 "checked" "checked")
- (dom-set-attr _el-c2 "id" "c2")
- (dom-set-attr _el-c2 "type" "checkbox")
- (dom-append (dom-body) _el-c1)
- (dom-append (dom-body) _el-c2)
+ (let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "div")))
+ (dom-set-attr _el-outer "id" "outer")
+ (dom-set-attr _el-inner "id" "inner")
+ (dom-append (dom-body) _el-outer)
+ (dom-append _el-outer _el-inner)
))
- (deftest "is boolean property works with disabled"
+ (deftest "does not contain works"
(hs-cleanup!)
- (let ((_el-b1 (dom-create-element "button")) (_el-b2 (dom-create-element "button")))
- (dom-set-attr _el-b1 "id" "b1")
- (dom-set-attr _el-b1 "disabled" "")
- (dom-set-inner-html _el-b1 "Disabled")
- (dom-set-attr _el-b2 "id" "b2")
- (dom-set-inner-html _el-b2 "Enabled")
- (dom-append (dom-body) _el-b1)
- (dom-append (dom-body) _el-b2)
+ (let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "div")))
+ (dom-set-attr _el-outer "id" "outer")
+ (dom-set-attr _el-inner "id" "inner")
+ (dom-append (dom-body) _el-outer)
+ (dom-append _el-outer _el-inner)
))
- (deftest "is still does equality when rhs variable exists"
- (let ((x 5) (y 5)) (assert= (eval-hs "x is y") true))
- (let ((x 5) (y 6)) (assert= (eval-hs "x is y") false))
+ (deftest "does not end with works"
+ (assert= (eval-hs "'hello world' does not end with 'world'") false)
+ (assert= (eval-hs "'hello world' does not end with 'hello'") true)
+ )
+ (deftest "does not exist works"
+ (assert= (eval-hs "undefined does not exist") true)
+ (assert= (eval-hs "null does not exist") true)
+ (assert= (eval-hs "#doesNotExist does not exist") true)
+ (assert= (eval-hs ".aClassThatDoesNotExist does not exist") true)
+ (assert= (eval-hs "<.aClassThatDoesNotExist/> does not exist") true)
+ (assert= (eval-hs " does not exist") false)
+ )
+ (deftest "does not follow works"
+ (hs-cleanup!)
+ (let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")))
+ (dom-set-attr _el-a "id" "a")
+ (dom-set-attr _el-b "id" "b")
+ (dom-append (dom-body) _el-a)
+ (dom-append (dom-body) _el-b)
+ ))
+ (deftest "does not match works"
+ (hs-cleanup!)
+ (let ((_el-mDiv (dom-create-element "div")))
+ (dom-set-attr _el-mDiv "id" "mDiv")
+ (dom-add-class _el-mDiv "foo")
+ (dom-append (dom-body) _el-mDiv)
+ ))
+ (deftest "does not match works w/ strings"
+ (assert= (eval-hs "'a' does not match '.*'") false)
+ (assert= (eval-hs "'a' does not match 'b'") true)
+ )
+ (deftest "does not precede works"
+ (hs-cleanup!)
+ (let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")))
+ (dom-set-attr _el-a "id" "a")
+ (dom-set-attr _el-b "id" "b")
+ (dom-append (dom-body) _el-a)
+ (dom-append (dom-body) _el-b)
+ ))
+ (deftest "does not start with works"
+ (assert= (eval-hs "'hello world' does not start with 'hello'") false)
+ (assert= (eval-hs "'hello world' does not start with 'world'") true)
+ )
+ (deftest "ends with coerces to string"
+ (assert= (eval-hs "123 ends with '23'") true)
+ (assert= (eval-hs "123 ends with '12'") false)
+ )
+ (deftest "ends with ignoring case works"
+ (assert= (eval-hs "'Hello World' ends with 'world' ignoring case") true)
+ (assert= (eval-hs "'Hello World' ends with 'WORLD' ignoring case") true)
+ (assert= (eval-hs "'Hello World' ends with 'hello' ignoring case") false)
+ )
+ (deftest "ends with null is false"
+ (assert= (eval-hs "null ends with 'x'") false)
+ (assert= (eval-hs "null does not end with 'x'") true)
+ )
+ (deftest "ends with works"
+ (assert= (eval-hs "'hello world' ends with 'world'") true)
+ (assert= (eval-hs "'hello world' ends with 'hello'") false)
+ (assert= (eval-hs "'hello' ends with 'hello'") true)
+ (assert= (eval-hs "'' ends with 'x'") false)
+ )
+ (deftest "english greater than or equal works"
+ (assert= (eval-hs "1 is greater than or equal to 2") false)
+ (assert= (eval-hs "2 is greater than or equal to 1") true)
+ (assert= (eval-hs "2 is greater than or equal to 2") true)
+ )
+ (deftest "english greater than works"
+ (assert= (eval-hs "1 is greater than 2") false)
+ (assert= (eval-hs "2 is greater than 1") true)
+ (assert= (eval-hs "2 is greater than 2") false)
+ )
+ (deftest "english less than or equal works"
+ (assert= (eval-hs "1 is less than or equal to 2") true)
+ (assert= (eval-hs "2 is less than or equal to 1") false)
+ (assert= (eval-hs "2 is less than or equal to 2") true)
+ )
+ (deftest "english less than works"
+ (assert= (eval-hs "1 is less than 2") true)
+ (assert= (eval-hs "2 is less than 1") false)
+ (assert= (eval-hs "2 is less than 2") false)
+ )
+ (deftest "equal works"
+ (assert= (eval-hs "1 == 2") false)
+ (assert= (eval-hs "2 == 1") false)
+ (assert= (eval-hs "2 == 2") true)
+ )
+ (deftest "equals works"
+ (assert= (eval-hs "1 equals 2") false)
+ (assert= (eval-hs "2 equals 1") false)
+ (assert= (eval-hs "2 equals 2") true)
+ )
+ (deftest "exists works"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-d3)
+ ))
+ (deftest "follows works"
+ (hs-cleanup!)
+ (let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")) (_el-c (dom-create-element "div")))
+ (dom-set-attr _el-a "id" "a")
+ (dom-set-attr _el-b "id" "b")
+ (dom-set-attr _el-c "id" "c")
+ (dom-append (dom-body) _el-a)
+ (dom-append (dom-body) _el-b)
+ (dom-append (dom-body) _el-c)
+ ))
+ (deftest "greater than or equal works"
+ (assert= (eval-hs "1 >= 2") false)
+ (assert= (eval-hs "2 >= 1") true)
+ (assert= (eval-hs "2 >= 2") true)
+ )
+ (deftest "greater than works"
+ (assert= (eval-hs "1 > 2") false)
+ (assert= (eval-hs "2 > 1") true)
+ (assert= (eval-hs "2 > 2") false)
+ )
+ (deftest "include works"
+ (let ((foo "foo") (foobar "foobar")) (assert= (eval-hs "foo includes foobar") false))
+ (let ((foo "foo") (foobar "foobar")) (assert= (eval-hs "foobar includes foo") true))
+ (let ((foo "foo") (foobar "foobar")) (assert= (eval-hs "foo does not include foobar") true))
+ (let ((foo "foo") (foobar "foobar")) (assert= (eval-hs "foobar does not include foo") false))
+ )
+ (deftest "includes works with arrays"
+ (let ((that 1)) (assert= (eval-hs "I include that") true))
+ (let ((that "[1")) (assert= (eval-hs "that includes me") true))
+ )
+ (deftest "includes works with css literals"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "outer")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-append (dom-body) _el-d1)
+ (dom-append _el-d1 _el-d2)
+ ))
+ (deftest "is a Node works via instanceof"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click if I am a Node put \"yes\" into me")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "yes")
+ ))
+ (deftest "is a works"
+ (assert= (eval-hs "null is a String") true)
+ (assert= (eval-hs "null is a String!") false)
+ (assert= (eval-hs "'' is a String!") true)
+ )
+ (deftest "is a works with instanceof fallback"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click if I am a Element put \"yes\" into me")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "yes")
+ ))
+ (deftest "is an works"
+ (assert= (eval-hs "null is an String") true)
+ (assert= (eval-hs "null is an String!") false)
+ (assert= (eval-hs "'' is an String!") true)
+ )
+ (deftest "is between works"
+ (assert= (eval-hs "5 is between 1 and 10") true)
+ (assert= (eval-hs "1 is between 1 and 10") true)
+ (assert= (eval-hs "10 is between 1 and 10") true)
+ (assert= (eval-hs "0 is between 1 and 10") false)
+ (assert= (eval-hs "11 is between 1 and 10") false)
)
(deftest "is boolean property works in where clause"
(hs-cleanup!)
@@ -8354,64 +4556,908 @@
(dom-append (dom-body) _el-input1)
(dom-append (dom-body) _el-input2)
))
+ (deftest "is boolean property works with disabled"
+ (hs-cleanup!)
+ (let ((_el-b1 (dom-create-element "button")) (_el-b2 (dom-create-element "button")))
+ (dom-set-attr _el-b1 "id" "b1")
+ (dom-set-attr _el-b1 "disabled" "")
+ (dom-set-inner-html _el-b1 "Disabled")
+ (dom-set-attr _el-b2 "id" "b2")
+ (dom-set-inner-html _el-b2 "Enabled")
+ (dom-append (dom-body) _el-b1)
+ (dom-append (dom-body) _el-b2)
+ ))
+ (deftest "is empty works"
+ (assert= (eval-hs "undefined is empty") true)
+ (assert= (eval-hs "'' is empty") true)
+ (assert= (eval-hs "[] is empty") true)
+ (assert= (eval-hs "'not empty' is empty") false)
+ (assert= (eval-hs "1000 is empty") false)
+ (assert= (eval-hs "[1,2,3] is empty") false)
+ (assert= (eval-hs ".aClassThatDoesNotExist is empty") true)
+ )
+ (deftest "is equal to works"
+ (assert= (eval-hs "1 is equal to 2") false)
+ (assert= (eval-hs "2 is equal to 1") false)
+ (assert= (eval-hs "2 is equal to 2") true)
+ )
+ (deftest "is equal works without to"
+ (assert= (eval-hs "2 is equal 2") true)
+ (assert= (eval-hs "2 is equal 1") false)
+ )
+ (deftest "is falls back to boolean property when rhs is undefined"
+ (hs-cleanup!)
+ (let ((_el-c1 (dom-create-element "input")) (_el-c2 (dom-create-element "input")))
+ (dom-set-attr _el-c1 "id" "c1")
+ (dom-set-attr _el-c1 "type" "checkbox")
+ (dom-set-attr _el-c1 "checked" "checked")
+ (dom-set-attr _el-c2 "id" "c2")
+ (dom-set-attr _el-c2 "type" "checkbox")
+ (dom-append (dom-body) _el-c1)
+ (dom-append (dom-body) _el-c2)
+ ))
+ (deftest "is ignoring case works"
+ (assert= (eval-hs "'Hello' is 'hello' ignoring case") true)
+ (assert= (eval-hs "'Hello' is 'HELLO' ignoring case") true)
+ (assert= (eval-hs "'Hello' is 'world' ignoring case") false)
+ )
+ (deftest "is in works"
+ (assert= (eval-hs "1 is in [1, 2]") true)
+ (assert= (eval-hs "2 is in [1, 2]") true)
+ (assert= (eval-hs "3 is in [1, 2]") false)
+ (assert= (eval-hs "3 is in null") false)
+ )
+ (deftest "is not a works"
+ (assert= (eval-hs "null is not a String") false)
+ (assert= (eval-hs "null is not a String!") true)
+ (assert= (eval-hs "'' is not a String!") false)
+ )
+ (deftest "is not a works with instanceof fallback"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click if \"hello\" is not a Element put \"yes\" into me")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "yes")
+ ))
+ (deftest "is not an works"
+ (assert= (eval-hs "null is not an String") false)
+ (assert= (eval-hs "null is not an String!") true)
+ (assert= (eval-hs "'' is not an String!") false)
+ )
+ (deftest "is not between works"
+ (assert= (eval-hs "5 is not between 1 and 10") false)
+ (assert= (eval-hs "0 is not between 1 and 10") true)
+ (assert= (eval-hs "11 is not between 1 and 10") true)
+ (assert= (eval-hs "1 is not between 1 and 10") false)
+ (assert= (eval-hs "10 is not between 1 and 10") false)
+ )
+ (deftest "is not empty works"
+ (assert= (eval-hs "undefined is not empty") false)
+ (assert= (eval-hs "'' is not empty") false)
+ (assert= (eval-hs "[] is not empty") false)
+ (assert= (eval-hs "'not empty' is not empty") true)
+ (assert= (eval-hs "1000 is not empty") true)
+ (assert= (eval-hs "[1,2,3] is not empty") true)
+ )
+ (deftest "is not equal to works"
+ (assert= (eval-hs "1 is not equal to 2") true)
+ (assert= (eval-hs "2 is not equal to 1") true)
+ (assert= (eval-hs "2 is not equal to 2") false)
+ )
+ (deftest "is not equal works without to"
+ (assert= (eval-hs "2 is not equal 2") false)
+ (assert= (eval-hs "2 is not equal 1") true)
+ )
+ (deftest "is not falls back to boolean property when rhs is undefined"
+ (hs-cleanup!)
+ (let ((_el-c1 (dom-create-element "input")) (_el-c2 (dom-create-element "input")))
+ (dom-set-attr _el-c1 "id" "c1")
+ (dom-set-attr _el-c1 "type" "checkbox")
+ (dom-set-attr _el-c1 "checked" "checked")
+ (dom-set-attr _el-c2 "id" "c2")
+ (dom-set-attr _el-c2 "type" "checkbox")
+ (dom-append (dom-body) _el-c1)
+ (dom-append (dom-body) _el-c2)
+ ))
+ (deftest "is not ignoring case works"
+ (assert= (eval-hs "'Hello' is not 'world' ignoring case") true)
+ (assert= (eval-hs "'Hello' is not 'hello' ignoring case") false)
+ )
+ (deftest "is not in works"
+ (assert= (eval-hs "1 is not in [1, 2]") false)
+ (assert= (eval-hs "2 is not in [1, 2]") false)
+ (assert= (eval-hs "3 is not in [1, 2]") true)
+ (assert= (eval-hs "3 is not in null") true)
+ )
+ (deftest "is not null still works as equality"
+ (assert= (eval-hs "5 is not null") true)
+ (assert= (eval-hs "null is not null") false)
+ )
+ (deftest "is not really equal to works"
+ (assert= (eval-hs "1 is not really equal to 2") true)
+ (assert= (eval-hs "2 is not really equal to 1") true)
+ (assert= (eval-hs "2 is not really equal to '2'") true)
+ (assert= (eval-hs "2 is not really equal to 2") false)
+ )
+ (deftest "is not really works without equal to"
+ (assert= (eval-hs "2 is not really '2'") true)
+ (assert= (eval-hs "2 is not really 2") false)
+ )
+ (deftest "is not undefined still works as equality"
+ (assert= (eval-hs "5 is not undefined") true)
+ (assert= (eval-hs "null is not undefined") false)
+ )
+ (deftest "is not works"
+ (assert= (eval-hs "1 is not 2") true)
+ (assert= (eval-hs "2 is not 1") true)
+ (assert= (eval-hs "2 is not 2") false)
+ )
+ (deftest "is really equal to works"
+ (assert= (eval-hs "1 is really equal to 2") false)
+ (assert= (eval-hs "2 is really equal to 1") false)
+ (assert= (eval-hs "2 is really equal to '2'") false)
+ (assert= (eval-hs "2 is really equal to 2") true)
+ )
+ (deftest "is really works without equal to"
+ (assert= (eval-hs "2 is really 2") true)
+ (assert= (eval-hs "2 is really '2'") false)
+ )
+ (deftest "is still does equality when rhs variable exists"
+ (let ((x 5) (y 5)) (assert= (eval-hs "x is y") true))
+ (let ((x 5) (y 6)) (assert= (eval-hs "x is y") false))
+ )
+ (deftest "is works"
+ (assert= (eval-hs "1 is 2") false)
+ (assert= (eval-hs "2 is 1") false)
+ (assert= (eval-hs "2 is 2") true)
+ )
+ (deftest "less than or equal works"
+ (assert= (eval-hs "1 <= 2") true)
+ (assert= (eval-hs "2 <= 1") false)
+ (assert= (eval-hs "2 <= 2") true)
+ )
+ (deftest "less than works"
+ (assert= (eval-hs "1 < 2") true)
+ (assert= (eval-hs "2 < 1") false)
+ (assert= (eval-hs "2 < 2") false)
+ )
+ (deftest "match works"
+ (hs-cleanup!)
+ (let ((_el-mDiv (dom-create-element "div")))
+ (dom-set-attr _el-mDiv "id" "mDiv")
+ (dom-add-class _el-mDiv "foo")
+ (dom-append (dom-body) _el-mDiv)
+ ))
+ (deftest "match works w/ strings"
+ (assert= (eval-hs "'a' matches '.*'") true)
+ (assert= (eval-hs "'a' matches 'b'") false)
+ )
+ (deftest "matches ignoring case works"
+ (assert= (eval-hs "'Hello' matches 'hello' ignoring case") true)
+ (assert= (eval-hs "'Hello' matches 'HELLO' ignoring case") true)
+ )
+ (deftest "not equal works"
+ (assert= (eval-hs "1 != 2") true)
+ (assert= (eval-hs "2 != 1") true)
+ (assert= (eval-hs "2 != 2") false)
+ )
+ (deftest "precedes with null is false"
+ (assert= (eval-hs "null precedes null") false)
+ (assert= (eval-hs "null does not precede null") true)
+ )
+ (deftest "precedes works"
+ (hs-cleanup!)
+ (let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")) (_el-c (dom-create-element "div")))
+ (dom-set-attr _el-a "id" "a")
+ (dom-set-attr _el-b "id" "b")
+ (dom-set-attr _el-c "id" "c")
+ (dom-append (dom-body) _el-a)
+ (dom-append (dom-body) _el-b)
+ (dom-append (dom-body) _el-c)
+ ))
+ (deftest "really equals works"
+ (assert= (eval-hs "1 really equals 2") false)
+ (assert= (eval-hs "2 really equals 1") false)
+ (assert= (eval-hs "2 really equals 2") true)
+ )
+ (deftest "starts with coerces to string"
+ (assert= (eval-hs "123 starts with '12'") true)
+ (assert= (eval-hs "123 starts with '23'") false)
+ )
+ (deftest "starts with ignoring case works"
+ (assert= (eval-hs "'Hello World' starts with 'hello' ignoring case") true)
+ (assert= (eval-hs "'Hello World' starts with 'HELLO' ignoring case") true)
+ (assert= (eval-hs "'Hello World' starts with 'world' ignoring case") false)
+ )
+ (deftest "starts with null is false"
+ (assert= (eval-hs "null starts with 'x'") false)
+ (assert= (eval-hs "null does not start with 'x'") true)
+ )
+ (deftest "starts with works"
+ (assert= (eval-hs "'hello world' starts with 'hello'") true)
+ (assert= (eval-hs "'hello world' starts with 'world'") false)
+ (assert= (eval-hs "'hello' starts with 'hello'") true)
+ (assert= (eval-hs "'' starts with 'x'") false)
+ )
+ (deftest "triple equal works"
+ (assert= (eval-hs "1 === 2") false)
+ (assert= (eval-hs "2 === 1") false)
+ (assert= (eval-hs "2 === 2") true)
+ )
+ (deftest "triple not equal works"
+ (assert= (eval-hs "1 !== 2") true)
+ (assert= (eval-hs "2 !== 1") true)
+ (assert= (eval-hs "2 !== 2") false)
+ )
)
-;; ── cookies (1 tests) ──
-(defsuite "hs-upstream-cookies"
+;; ── expressions/cookies (5 tests) ──
+(defsuite "hs-upstream-expressions/cookies"
+ (deftest "basic clear cookie values work"
+ (error "SKIP (untranslated): basic clear cookie values work"))
+ (deftest "basic set cookie values work"
+ (error "SKIP (untranslated): basic set cookie values work"))
+ (deftest "iterate cookies values work"
+ (error "SKIP (untranslated): iterate cookies values work"))
(deftest "length is 0 when no cookies are set"
- (hs-cleanup!))
+ (error "SKIP (untranslated): length is 0 when no cookies are set"))
+ (deftest "update cookie values work"
+ (error "SKIP (untranslated): update cookie values work"))
)
-;; ── in (1 tests) ──
-(defsuite "hs-upstream-in"
+;; ── expressions/dom-scope (20 tests) ──
+(defsuite "hs-upstream-expressions/dom-scope"
+ (deftest "always reacts to ^var changes"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-output (dom-create-element "output")))
+ (dom-set-attr _el-div "_" "init set ^name to 'alice'")
+ (dom-set-attr _el-button "_" "on click set ^name to 'bob'")
+ (dom-set-inner-html _el-button "rename")
+ (dom-set-attr _el-output "_" "live put 'Hello ' + ^name into me")
+ (dom-set-inner-html _el-output "loading")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-button)
+ (dom-append _el-div _el-output)
+ (hs-activate! _el-div)
+ (hs-activate! _el-button)
+ (hs-activate! _el-output)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "bind works with ^var"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-input (dom-create-element "input")) (_el-output (dom-create-element "output")))
+ (dom-set-attr _el-div "_" "init set ^search to ''")
+ (dom-set-attr _el-input "_" "bind ^search and my value")
+ (dom-set-attr _el-input "type" "text")
+ (dom-set-attr _el-output "_" "when ^search changes put it into me")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-input)
+ (dom-append _el-div _el-output)
+ (hs-activate! _el-div)
+ (hs-activate! _el-input)
+ (hs-activate! _el-output)
+ (dom-set-prop _el-input "value" "hello")
+ (dom-dispatch _el-input "input" nil)
+ ))
+ (deftest "child reads ^var set by parent"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-div "_" "init set ^count to 42")
+ (dom-set-attr _el-span "_" "on click put ^count into me")
+ (dom-set-inner-html _el-span "0")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-span)
+ (hs-activate! _el-div)
+ (hs-activate! _el-span)
+ (dom-dispatch _el-span "click" nil)
+ (assert= (dom-text-content _el-span) "42")
+ ))
+ (deftest "child write updates the ancestor, not a local copy"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-div "_" "init set ^shared to 0")
+ (dom-set-attr _el-button "_" "on click set ^shared to 10")
+ (dom-set-inner-html _el-button "set")
+ (dom-set-attr _el-span "_" "on click put ^shared into me")
+ (dom-set-inner-html _el-span "0")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-button)
+ (dom-append _el-div _el-span)
+ (hs-activate! _el-div)
+ (hs-activate! _el-button)
+ (hs-activate! _el-span)
+ (dom-dispatch _el-button "click" nil)
+ (dom-dispatch _el-span "click" nil)
+ (assert= (dom-text-content _el-span) "10")
+ ))
+ (deftest "child writes ^var and parent sees it"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-div "_" "init set ^count to 0")
+ (dom-set-attr _el-button "_" "on click set ^count to 99")
+ (dom-set-inner-html _el-button "set")
+ (dom-set-attr _el-span "_" "on click put ^count into me")
+ (dom-set-inner-html _el-span "0")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-button)
+ (dom-append _el-div _el-span)
+ (hs-activate! _el-div)
+ (hs-activate! _el-button)
+ (hs-activate! _el-span)
+ (dom-dispatch _el-button "click" nil)
+ (dom-dispatch _el-span "click" nil)
+ (assert= (dom-text-content _el-span) "99")
+ ))
+ (deftest "closest ancestor wins (shadowing)"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-div "_" "init set ^color to 'red'")
+ (dom-set-attr _el-div1 "_" "init set ^color to 'blue'")
+ (dom-set-attr _el-span "_" "on click put ^color into me")
+ (dom-set-inner-html _el-span "empty")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-div1)
+ (dom-append _el-div1 _el-span)
+ (hs-activate! _el-div)
+ (hs-activate! _el-div1)
+ (hs-activate! _el-span)
+ (dom-dispatch _el-span "click" nil)
+ (assert= (dom-text-content _el-span) "blue")
+ ))
+ (deftest "dedup prevents re-fire on same ^var value"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-output (dom-create-element "output")) (_el-button (dom-create-element "button")) (_el-diff (dom-create-element "button")))
+ (dom-set-attr _el-div "_" "init set ^val to 'same'")
+ (dom-set-attr _el-output "_" "when ^val changes increment :runs then put :runs into me")
+ (dom-set-attr _el-button "_" "on click set ^val to 'same'")
+ (dom-set-inner-html _el-button "same")
+ (dom-set-attr _el-diff "id" "diff")
+ (dom-set-attr _el-diff "_" "on click set ^val to 'different'")
+ (dom-set-inner-html _el-diff "diff")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-output)
+ (dom-append _el-div _el-button)
+ (dom-append _el-div _el-diff)
+ (hs-activate! _el-div)
+ (hs-activate! _el-output)
+ (hs-activate! _el-button)
+ (hs-activate! _el-diff)
+ (dom-dispatch (dom-query "button:text("same")") "click" nil)
+ (assert= (dom-text-content _el-output) "1")
+ (dom-dispatch (dom-query-by-id "diff") "click" nil)
+ (assert= (dom-text-content _el-output) "2")
+ ))
+ (deftest "deeply nested child reads ^var from grandparent"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-div "_" "init set ^name to 'alice'")
+ (dom-set-attr _el-span "_" "on click put ^name into me")
+ (dom-set-inner-html _el-span "empty")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-div1)
+ (dom-append _el-div1 _el-div2)
+ (dom-append _el-div2 _el-span)
+ (hs-activate! _el-div)
+ (hs-activate! _el-span)
+ (dom-dispatch _el-span "click" nil)
+ (assert= (dom-text-content _el-span) "alice")
+ ))
+ (deftest "derived ^var chains reactively"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-span2 (dom-create-element "span")) (_el-output (dom-create-element "output")) (_el-price-btn (dom-create-element "button")) (_el-qty-btn (dom-create-element "button")))
+ (dom-set-attr _el-div "_" "init set ^price to 10 then set ^qty to 2 then set ^total to 20")
+ (dom-set-attr _el-span "_" "when ^price changes set ^total to (^price * ^qty)")
+ (dom-set-attr _el-span2 "_" "when ^qty changes set ^total to (^price * ^qty)")
+ (dom-set-attr _el-output "_" "when ^total changes put it into me")
+ (dom-set-attr _el-price-btn "id" "price-btn")
+ (dom-set-attr _el-price-btn "_" "on click set ^price to 25")
+ (dom-set-inner-html _el-price-btn "set price")
+ (dom-set-attr _el-qty-btn "id" "qty-btn")
+ (dom-set-attr _el-qty-btn "_" "on click set ^qty to 5")
+ (dom-set-inner-html _el-qty-btn "set qty")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-span)
+ (dom-append _el-div _el-span2)
+ (dom-append _el-div _el-output)
+ (dom-append _el-div _el-price-btn)
+ (dom-append _el-div _el-qty-btn)
+ (hs-activate! _el-div)
+ (hs-activate! _el-span)
+ (hs-activate! _el-span2)
+ (hs-activate! _el-output)
+ (hs-activate! _el-price-btn)
+ (hs-activate! _el-qty-btn)
+ (dom-dispatch (dom-query-by-id "price-btn") "click" nil)
+ (dom-dispatch (dom-query-by-id "qty-btn") "click" nil)
+ ))
+ (deftest "dom keyword works as scope modifier"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-div "_" "init set dom count to 42")
+ (dom-set-attr _el-span "_" "on click put dom count into me")
+ (dom-set-inner-html _el-span "0")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-span)
+ (hs-activate! _el-div)
+ (hs-activate! _el-span)
+ (dom-dispatch _el-span "click" nil)
+ (assert= (dom-text-content _el-span) "42")
+ ))
+ (deftest "effect stops when element is removed"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-output (dom-create-element "output")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-div "_" "init set ^msg to 'hello'")
+ (dom-set-attr _el-output "_" "when ^msg changes put it into me")
+ (dom-set-attr _el-button "_" "on click set ^msg to 'updated'")
+ (dom-set-inner-html _el-button "update")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-output)
+ (dom-append _el-div _el-button)
+ (hs-activate! _el-div)
+ (hs-activate! _el-output)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "increment works on inherited var"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-div "_" "init set ^count to 0")
+ (dom-set-attr _el-button "_" "on click increment ^count")
+ (dom-set-inner-html _el-button "+1")
+ (dom-set-attr _el-span "_" "on click put ^count into me")
+ (dom-set-inner-html _el-span "0")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-button)
+ (dom-append _el-div _el-span)
+ (hs-activate! _el-div)
+ (hs-activate! _el-button)
+ (hs-activate! _el-span)
+ (dom-dispatch _el-button "click" nil)
+ (dom-dispatch _el-button "click" nil)
+ (dom-dispatch _el-button "click" nil)
+ (dom-dispatch _el-span "click" nil)
+ (assert= (dom-text-content _el-span) "3")
+ ))
+ (deftest "multiple children react to same ^var"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-a (dom-create-element "span")) (_el-b (dom-create-element "span")))
+ (dom-set-attr _el-div "_" "init set ^color to 'red'")
+ (dom-set-attr _el-button "_" "on click set ^color to 'blue'")
+ (dom-set-inner-html _el-button "change")
+ (dom-set-attr _el-a "id" "a")
+ (dom-set-attr _el-a "_" "when ^color changes put it into me")
+ (dom-set-attr _el-b "id" "b")
+ (dom-set-attr _el-b "_" "when ^color changes put it into me")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-button)
+ (dom-append _el-div _el-a)
+ (dom-append _el-div _el-b)
+ (hs-activate! _el-div)
+ (hs-activate! _el-button)
+ (hs-activate! _el-a)
+ (hs-activate! _el-b)
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "a")) "blue")
+ (assert= (dom-text-content (dom-query-by-id "b")) "blue")
+ ))
+ (deftest "on clause targets a specific ancestor"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
+ (dom-add-class _el-div "outer")
+ (dom-set-attr _el-div "_" "init set ^outerVal to 'outer'")
+ (dom-add-class _el-div1 "inner")
+ (dom-set-attr _el-div1 "_" "init set ^innerVal to 'inner'")
+ (dom-set-attr _el-span "_" "on click put ^outerVal on closest .outer into me")
+ (dom-set-inner-html _el-span "read")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-div1)
+ (dom-append _el-div1 _el-span)
+ (hs-activate! _el-div)
+ (hs-activate! _el-div1)
+ (hs-activate! _el-span)
+ (dom-dispatch _el-span "click" nil)
+ (assert= (dom-text-content _el-span) "outer")
+ ))
+ (deftest "on clause with id reference"
+ (hs-cleanup!)
+ (let ((_el-state-holder (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-state-holder "id" "state-holder")
+ (dom-set-attr _el-button "_" "on click set ^count on #state-holder to 99")
+ (dom-set-inner-html _el-button "set")
+ (dom-set-attr _el-span "_" "on click put ^count on #state-holder into me")
+ (dom-set-inner-html _el-span "read")
+ (dom-append (dom-body) _el-state-holder)
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-span)
+ (hs-activate! _el-button)
+ (hs-activate! _el-span)
+ (dom-dispatch _el-button "click" nil)
+ (dom-dispatch _el-span "click" nil)
+ (assert= (dom-text-content _el-span) "99")
+ ))
+ (deftest "set ^var on explicit element"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
+ (dom-add-class _el-div "store")
+ (dom-set-attr _el-button "_" "on click set ^data on closest .store to 'hello'")
+ (dom-set-inner-html _el-button "set")
+ (dom-set-attr _el-span "_" "on click put ^data on closest .store into me")
+ (dom-set-inner-html _el-span "read")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-button)
+ (dom-append _el-div _el-span)
+ (hs-activate! _el-button)
+ (hs-activate! _el-span)
+ (dom-dispatch _el-button "click" nil)
+ (dom-dispatch _el-span "click" nil)
+ (assert= (dom-text-content _el-span) "hello")
+ ))
+ (deftest "sibling subtrees have independent ^vars"
+ (hs-cleanup!)
+ (let ((_el-a (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-b (dom-create-element "div")) (_el-span3 (dom-create-element "span")))
+ (dom-set-attr _el-a "id" "a")
+ (dom-set-attr _el-a "_" "init set ^val to 'A'")
+ (dom-set-attr _el-span "_" "on click put ^val into me")
+ (dom-set-inner-html _el-span "empty")
+ (dom-set-attr _el-b "id" "b")
+ (dom-set-attr _el-b "_" "init set ^val to 'B'")
+ (dom-set-attr _el-span3 "_" "on click put ^val into me")
+ (dom-set-inner-html _el-span3 "empty")
+ (dom-append (dom-body) _el-a)
+ (dom-append _el-a _el-span)
+ (dom-append (dom-body) _el-b)
+ (dom-append _el-b _el-span3)
+ (hs-activate! _el-a)
+ (hs-activate! _el-span)
+ (hs-activate! _el-b)
+ (hs-activate! _el-span3)
+ (dom-dispatch (dom-query "#a span") "click" nil)
+ (assert= (dom-text-content (dom-query "#a span")) "A")
+ (dom-dispatch (dom-query "#b span") "click" nil)
+ (assert= (dom-text-content (dom-query "#b span")) "B")
+ ))
+ (deftest "sibling subtrees react independently with ^var"
+ (hs-cleanup!)
+ (let ((_el-a (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-output (dom-create-element "output")) (_el-b (dom-create-element "div")) (_el-button4 (dom-create-element "button")) (_el-output5 (dom-create-element "output")))
+ (dom-set-attr _el-a "id" "a")
+ (dom-set-attr _el-a "_" "init set ^val to 0")
+ (dom-set-attr _el-button "_" "on click increment ^val")
+ (dom-set-inner-html _el-button "+1")
+ (dom-set-attr _el-output "_" "when ^val changes put it into me")
+ (dom-set-inner-html _el-output "0")
+ (dom-set-attr _el-b "id" "b")
+ (dom-set-attr _el-b "_" "init set ^val to 0")
+ (dom-set-attr _el-button4 "_" "on click increment ^val")
+ (dom-set-inner-html _el-button4 "+1")
+ (dom-set-attr _el-output5 "_" "when ^val changes put it into me")
+ (dom-set-inner-html _el-output5 "0")
+ (dom-append (dom-body) _el-a)
+ (dom-append _el-a _el-button)
+ (dom-append _el-a _el-output)
+ (dom-append (dom-body) _el-b)
+ (dom-append _el-b _el-button4)
+ (dom-append _el-b _el-output5)
+ (hs-activate! _el-a)
+ (hs-activate! _el-button)
+ (hs-activate! _el-output)
+ (hs-activate! _el-b)
+ (hs-activate! _el-button4)
+ (hs-activate! _el-output5)
+ (dom-dispatch (dom-query "#a button") "click" nil)
+ (dom-dispatch (dom-query "#a button") "click" nil)
+ (assert= (dom-text-content (dom-query "#a output")) "2")
+ (assert= (dom-text-content (dom-query "#b output")) "0")
+ (dom-dispatch (dom-query "#b button") "click" nil)
+ (assert= (dom-text-content (dom-query "#b output")) "1")
+ (assert= (dom-text-content (dom-query "#a output")) "2")
+ ))
+ (deftest "when reacts to ^var changes"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-output (dom-create-element "output")))
+ (dom-set-attr _el-div "_" "init set ^count to 0")
+ (dom-set-attr _el-button "_" "on click increment ^count")
+ (dom-set-inner-html _el-button "+1")
+ (dom-set-attr _el-output "_" "when ^count changes put it into me")
+ (dom-set-inner-html _el-output "0")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-button)
+ (dom-append _el-div _el-output)
+ (hs-activate! _el-div)
+ (hs-activate! _el-button)
+ (hs-activate! _el-output)
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content _el-output) "1")
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content _el-output) "2")
+ ))
+ (deftest "write to ^var not found anywhere creates on current element"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-button "_" "on click set ^newvar to 'created' then put ^newvar into next ")
+ (dom-set-inner-html _el-button "go")
+ (dom-set-inner-html _el-span "empty")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-button)
+ (dom-append _el-div _el-span)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content _el-span) "created")
+ ))
+)
+
+;; ── expressions/functionCalls (12 tests) ──
+(defsuite "hs-upstream-expressions/functionCalls"
+ (deftest "can access a property of a call's result"
+ (assert= (eval-hs "makePoint(3, 4).x") 3)
+ (assert= (eval-hs "makePoint(3, 4).y") 4)
+ )
+ (deftest "can chain calls on the result of a call"
+ (assert= (eval-hs "getObj().greet()") "hi")
+ )
+ (deftest "can invoke function on object"
+ (assert= (eval-hs "obj.getValue()") "foo")
+ )
+ (deftest "can invoke function on object w/ async arg"
+ (error "SKIP (untranslated): can invoke function on object w/ async arg"))
+ (deftest "can invoke function on object w/ async root & arg"
+ (error "SKIP (untranslated): can invoke function on object w/ async root & arg"))
+ (deftest "can invoke global function"
+ (assert= (eval-hs "identity(\"foo\")") "foo")
+ )
+ (deftest "can invoke global function w/ async arg"
+ (error "SKIP (untranslated): can invoke global function w/ async arg"))
+ (deftest "can pass an array literal as an argument"
+ (assert= (eval-hs "sum([1, 2, 3, 4])") 10)
+ )
+ (deftest "can pass an expression as an argument"
+ (assert= (eval-hs "double(3 + 4)") 14)
+ )
+ (deftest "can pass an object literal as an argument"
+ (assert= (eval-hs "getName({name: 'Alice'})") "Alice")
+ )
+ (deftest "can pass multiple arguments"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click put add(1, 2, 3) into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "6")
+ ))
+ (deftest "can pass no arguments"
+ (assert= (eval-hs "getFortyTwo()") 42)
+ )
+)
+
+;; ── expressions/idRef (4 tests) ──
+(defsuite "hs-upstream-expressions/idRef"
+ (deftest "basic id ref works"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-append (dom-body) _el-d1)
+ ))
+ (deftest "basic id ref works w no match"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-append (dom-body) _el-div)
+ ))
+ (deftest "id ref works from a disconnected element"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-append (dom-body) _el-d1)
+ ))
+ (deftest "template id ref works"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-append (dom-body) _el-d1)
+ ))
+)
+
+;; ── expressions/in (10 tests) ──
+(defsuite "hs-upstream-expressions/in"
+ (deftest "basic no query return values"
+ (assert= (eval-hs "1 in [1, 2, 3]") (list 1))
+ (assert= (eval-hs "[1, 3] in [1, 2, 3]") (list 1 3))
+ (assert= (eval-hs "[1, 3, 4] in [1, 2, 3]") (list 1 3))
+ (assert= (eval-hs "[4, 5, 6] in [1, 2, 3]") (list))
+ )
+ (deftest "basic query return values"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-p2 (dom-create-element "p")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append _el-d1 _el-p)
+ (dom-append _el-d1 _el-p2)
+ ))
+ (deftest "class returns values"
+ (hs-cleanup!)
+ (let ((_el-inDiv (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
+ (dom-set-attr _el-inDiv "id" "inDiv")
+ (dom-set-attr _el-inDiv "_" "on click get the first .p1 in me put its id into @result")
+ (dom-set-attr _el-p1 "id" "p1")
+ (dom-add-class _el-p1 "p1")
+ (dom-append (dom-body) _el-inDiv)
+ (dom-append _el-inDiv _el-p1)
+ (hs-activate! _el-inDiv)
+ (dom-dispatch (dom-query-by-id "inDiv") "click" nil)
+ ))
+ (deftest "class template returns values"
+ (hs-cleanup!)
+ (let ((_el-inDiv (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
+ (dom-set-attr _el-inDiv "id" "inDiv")
+ (dom-set-attr _el-inDiv "_" "on click get the first .{\"p1\"} in me put its id into @result")
+ (dom-set-attr _el-p1 "id" "p1")
+ (dom-add-class _el-p1 "p1")
+ (dom-append (dom-body) _el-inDiv)
+ (dom-append _el-inDiv _el-p1)
+ (hs-activate! _el-inDiv)
+ (dom-dispatch (dom-query-by-id "inDiv") "click" nil)
+ ))
+ (deftest "id returns values"
+ (hs-cleanup!)
+ (let ((_el-inDiv (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
+ (dom-set-attr _el-inDiv "id" "inDiv")
+ (dom-set-attr _el-inDiv "_" "on click get #p1 in me put its id into @result")
+ (dom-set-attr _el-p1 "id" "p1")
+ (dom-append (dom-body) _el-inDiv)
+ (dom-append _el-inDiv _el-p1)
+ (hs-activate! _el-inDiv)
+ (dom-dispatch (dom-query-by-id "inDiv") "click" nil)
+ ))
+ (deftest "id template returns values"
+ (hs-cleanup!)
+ (let ((_el-inDiv (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
+ (dom-set-attr _el-inDiv "id" "inDiv")
+ (dom-set-attr _el-inDiv "_" "on click get #{\"p1\"} in me put its id into @result")
+ (dom-set-attr _el-p1 "id" "p1")
+ (dom-append (dom-body) _el-inDiv)
+ (dom-append _el-inDiv _el-p1)
+ (hs-activate! _el-inDiv)
+ (dom-dispatch (dom-query-by-id "inDiv") "click" nil)
+ ))
+ (deftest "in expression binds to unaryOperators"
+ (hs-cleanup!)
+ (let ((_el-d2 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-p2 (dom-create-element "p")))
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-p "foo")
+ (dom-set-inner-html _el-p "bar")
+ (dom-append (dom-body) _el-d2)
+ (dom-append _el-d2 _el-p)
+ (dom-append _el-d2 _el-p2)
+ ))
(deftest "null value in array returns empty"
(assert= (eval-hs "null in [1, 2, 3]") (list))
)
+ (deftest "query returns values"
+ (hs-cleanup!)
+ (let ((_el-inDiv (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
+ (dom-set-attr _el-inDiv "id" "inDiv")
+ (dom-set-attr _el-inDiv "_" "on click get the first in me put its id into @result")
+ (dom-set-attr _el-p1 "id" "p1")
+ (dom-add-class _el-p1 "p1")
+ (dom-append (dom-body) _el-inDiv)
+ (dom-append _el-inDiv _el-p1)
+ (hs-activate! _el-inDiv)
+ (dom-dispatch (dom-query-by-id "inDiv") "click" nil)
+ ))
+ (deftest "query template returns values"
+ (hs-cleanup!)
+ (let ((_el-inDiv (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
+ (dom-set-attr _el-inDiv "id" "inDiv")
+ (dom-set-attr _el-inDiv "_" "on click get the first <${\"p\"}/> in me put its id into @result")
+ (dom-set-attr _el-p1 "id" "p1")
+ (dom-add-class _el-p1 "p1")
+ (dom-append (dom-body) _el-inDiv)
+ (dom-append _el-inDiv _el-p1)
+ (hs-activate! _el-inDiv)
+ (dom-dispatch (dom-query-by-id "inDiv") "click" nil)
+ ))
)
-;; ── logicalOperator (3 tests) ──
-(defsuite "hs-upstream-logicalOperator"
+;; ── expressions/logicalOperator (10 tests) ──
+(defsuite "hs-upstream-expressions/logicalOperator"
(deftest "and short-circuits when lhs promise resolves to false"
- (hs-cleanup!))
- (deftest "or short-circuits when lhs promise resolves to true"
- (hs-cleanup!))
+ (error "SKIP (untranslated): and short-circuits when lhs promise resolves to false"))
+ (deftest "and works"
+ (assert= (eval-hs "true and false") false)
+ )
+ (deftest "and works w/ more than one value"
+ (assert= (eval-hs "true and true and false") false)
+ )
(deftest "or evaluates rhs when lhs promise resolves to false"
- (hs-cleanup!))
+ (error "SKIP (untranslated): or evaluates rhs when lhs promise resolves to false"))
+ (deftest "or short-circuits when lhs promise resolves to true"
+ (error "SKIP (untranslated): or short-circuits when lhs promise resolves to true"))
+ (deftest "or works"
+ (assert= (eval-hs "true or false") true)
+ )
+ (deftest "parenthesized expressions with multiple operators work"
+ (assert= (eval-hs "true and (false or true)") true)
+ )
+ (deftest "should short circuit with and expression"
+ (error "SKIP (untranslated): should short circuit with and expression"))
+ (deftest "should short circuit with or expression"
+ (error "SKIP (untranslated): should short circuit with or expression"))
+ (deftest "unparenthesized expressions with multiple operators cause an error"
+ (error "SKIP (untranslated): unparenthesized expressions with multiple operators cause an error"))
)
-;; ── mathOperator (5 tests) ──
-(defsuite "hs-upstream-mathOperator"
+;; ── expressions/mathOperator (15 tests) ──
+(defsuite "hs-upstream-expressions/mathOperator"
+ (deftest "addition works"
+ (assert= (eval-hs "1 + 1") 2)
+ )
+ (deftest "addition works w/ more than one value"
+ (assert= (eval-hs "1 + 2 + 3") 6)
+ )
(deftest "array + array concats"
(assert= (eval-hs "[1, 2] + [3, 4]") (list 1 2 3 4))
)
- (deftest "array + single value appends"
- (assert= (eval-hs "[1, 2] + 3") (list 1 2 3))
- )
(deftest "array + array does not mutate original"
(assert= (eval-hs "set a to [1, 2] then set b to a + [3] then return a") (list 1 2))
)
+ (deftest "array + single value appends"
+ (assert= (eval-hs "[1, 2] + 3") (list 1 2 3))
+ )
(deftest "array concat chains"
(assert= (eval-hs "[1] + [2] + [3]") (list 1 2 3))
)
+ (deftest "can use mixed expressions"
+ (error "SKIP (untranslated): can use mixed expressions"))
+ (deftest "division works"
+ (assert= (eval-hs "1 / 2") 0.5)
+ )
(deftest "empty array + array works"
(assert= (eval-hs "[] + [1, 2]") (list 1 2))
)
+ (deftest "mod works"
+ (assert= (eval-hs "3 mod 2") 1)
+ )
+ (deftest "multiplication works"
+ (assert= (eval-hs "1 * 2") 2)
+ )
+ (deftest "parenthesized expressions with multiple operators work"
+ (assert= (eval-hs "1 + (2 * 3)") 7)
+ )
+ (deftest "string concat works"
+ (assert= (eval-hs "'a' + 'b'") "ab")
+ )
+ (deftest "subtraction works"
+ (assert= (eval-hs "1 - 1") 0)
+ )
+ (deftest "unparenthesized expressions with multiple operators cause an error"
+ (error "SKIP (untranslated): unparenthesized expressions with multiple operators cause an error"))
)
-;; ── no (5 tests) ──
-(defsuite "hs-upstream-no"
+;; ── expressions/no (9 tests) ──
+(defsuite "hs-upstream-expressions/no"
(deftest "no returns false for non-empty array"
(assert= (eval-hs "no ['thing']") false)
)
- (deftest "no with where filters then checks emptiness"
- (assert= (eval-hs "no [1, 2, 3] where it > 5") true)
+ (deftest "no returns false for non-null"
+ (assert= (eval-hs "no 'thing'") false)
)
- (deftest "no with where returns false when matches exist"
- (assert= (eval-hs "no [1, 2, 3] where it > 1") false)
+ (deftest "no returns true for empty array"
+ (assert= (eval-hs "no []") true)
+ )
+ (deftest "no returns true for empty selector"
+ (assert= (eval-hs "no .aClassThatDoesNotExist") true)
+ )
+ (deftest "no returns true for null"
+ (assert= (eval-hs "no null") true)
)
(deftest "no with where and is not"
(assert= (eval-hs "no [1, 2, 3] where it is not 2") false)
)
+ (deftest "no with where filters then checks emptiness"
+ (assert= (eval-hs "no [1, 2, 3] where it > 5") true)
+ )
(deftest "no with where on DOM elements"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-span2 (dom-create-element "span")) (_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
@@ -8432,17 +5478,455 @@
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query-by-id "out")) "none")
))
-)
-
-;; ── objectLiteral (1 tests) ──
-(defsuite "hs-upstream-objectLiteral"
- (deftest "allows trailing commas"
- ;; TODO: assert= (eval-hs "{foo:true, bar-baz:false,}") against { "foo": true, "bar-baz": false }
+ (deftest "no with where returns false when matches exist"
+ (assert= (eval-hs "no [1, 2, 3] where it > 1") false)
)
)
-;; ── queryRef (1 tests) ──
-(defsuite "hs-upstream-queryRef"
+;; ── expressions/not (9 tests) ──
+(defsuite "hs-upstream-expressions/not"
+ (deftest "not has higher precedence than and"
+ (assert= (eval-hs "not false and true") true)
+ (assert= (eval-hs "not true and true") false)
+ )
+ (deftest "not has higher precedence than or"
+ (assert= (eval-hs "not true or true") true)
+ (assert= (eval-hs "not false or false") true)
+ )
+ (deftest "not inverts equality comparisons"
+ (assert= (eval-hs "not (1 is 2)") true)
+ (assert= (eval-hs "not (1 is 1)") false)
+ )
+ (deftest "not inverts false"
+ (assert= (eval-hs "not false") true)
+ )
+ (deftest "not inverts true"
+ (assert= (eval-hs "not true") false)
+ )
+ (deftest "not null and not undefined"
+ (assert= (eval-hs "not null") true)
+ (assert= (eval-hs "not undefined") true)
+ )
+ (deftest "not with numeric truthy/falsy values"
+ (assert= (eval-hs "not 0") true)
+ (assert= (eval-hs "not 1") false)
+ (assert= (eval-hs "not 42") false)
+ )
+ (deftest "not with string truthy/falsy values"
+ (assert= (eval-hs "not ''") true)
+ (assert= (eval-hs "not 'hello'") false)
+ )
+ (deftest "two nots make a true"
+ (assert= (eval-hs "not not true") true)
+ )
+)
+
+;; ── expressions/null (1 tests) ──
+(defsuite "hs-upstream-expressions/null"
+ (deftest "null literal work"
+ (error "SKIP (untranslated): null literal work"))
+)
+
+;; ── expressions/numbers (1 tests) ──
+(defsuite "hs-upstream-expressions/numbers"
+ (deftest "handles numbers properly"
+ (assert= (eval-hs "-1") -1)
+ (assert= (eval-hs "1") 1)
+ (assert= (eval-hs "1.1") 1.1)
+ (assert= (eval-hs "1e6") 1e6)
+ (assert= (eval-hs "1e-6") 1e-6)
+ (assert= (eval-hs "1.1e6") 1.1e6)
+ (assert= (eval-hs "1.1e-6") 1.1e-6)
+ (assert= (eval-hs "1234567890.1234567890") 1234567890.123456789)
+ )
+)
+
+;; ── expressions/objectLiteral (12 tests) ──
+(defsuite "hs-upstream-expressions/objectLiteral"
+ (deftest "allows trailing commas"
+ ;; TODO: assert= (eval-hs "{foo:true, bar-baz:false,}") against { "foo": true, "bar-baz": false }
+ )
+ (deftest "deeply nested object literals work"
+ ;; TODO: assert= (eval-hs "{a: {b: {c: 'deep'}}}") against { a: { b: { c: 'deep' } } }
+ )
+ (deftest "empty object literals work"
+ ;; TODO: assert= (eval-hs "{}") against {}
+ )
+ (deftest "expressions work in object literal field names"
+ (error "SKIP (untranslated): expressions work in object literal field names"))
+ (deftest "hyphens work in object literal field names"
+ ;; TODO: assert= (eval-hs "{-foo:true, bar-baz:false}") against { "-foo": true, "bar-baz": false }
+ )
+ (deftest "mixed field name styles in one literal"
+ ;; TODO: assert= (eval-hs "{plain: 1, \"quoted\": 2, -dashed: 3}") against { plain: 1, quoted: 2, "-dashed": 3 }
+ )
+ (deftest "multi-field object literal works"
+ ;; TODO: assert= (eval-hs "{foo:true, bar:false}") against { foo: true, bar: false }
+ )
+ (deftest "nested object literals work"
+ ;; TODO: assert= (eval-hs "{outer: {inner: 1}}") against { outer: { inner: 1 } }
+ )
+ (deftest "object literal values can be expressions"
+ ;; TODO: assert= (eval-hs "{sum: 1 + 2, product: 3 * 4}") against { sum: 3, product: 12 }
+ )
+ (deftest "object literals can contain arrays"
+ ;; TODO: assert= (eval-hs "{items: [1, 2, 3], count: 3}") against { items: [1, 2, 3], count: 3 }
+ )
+ (deftest "one field object literal works"
+ ;; TODO: assert= (eval-hs "{foo:true}") against { foo: true }
+ )
+ (deftest "strings work in object literal field names"
+ ;; TODO: assert= (eval-hs "{\"foo\":true, \"bar\":false}") against { foo: true, bar: false }
+ )
+)
+
+;; ── expressions/positionalExpression (7 tests) ──
+(defsuite "hs-upstream-expressions/positionalExpression"
+ (deftest "first works"
+ (assert= (eval-hs "the first of [1, 2, 3]") 1)
+ )
+ (deftest "first works w/ array-like"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-d3)
+ ))
+ (deftest "first works w/ node"
+ (hs-cleanup!)
+ (let ((_el-outerDiv (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-outerDiv "id" "outerDiv")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-outerDiv)
+ (dom-append _el-outerDiv _el-d1)
+ (dom-append _el-outerDiv _el-d2)
+ (dom-append _el-outerDiv _el-d3)
+ ))
+ (deftest "is null safe"
+ (error "SKIP (untranslated): is null safe"))
+ (deftest "last works"
+ (assert= (eval-hs "the last of [1, 2, 3]") 3)
+ )
+ (deftest "last works w/ array-like"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-d3)
+ ))
+ (deftest "last works w/ node"
+ (hs-cleanup!)
+ (let ((_el-outerDiv (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-outerDiv "id" "outerDiv")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-outerDiv)
+ (dom-append _el-outerDiv _el-d1)
+ (dom-append _el-outerDiv _el-d2)
+ (dom-append _el-outerDiv _el-d3)
+ ))
+)
+
+;; ── expressions/possessiveExpression (23 tests) ──
+(defsuite "hs-upstream-expressions/possessiveExpression"
+ (deftest "can access basic attribute"
+ (hs-cleanup!)
+ (let ((_el-pDiv (dom-create-element "div")))
+ (dom-set-attr _el-pDiv "id" "pDiv")
+ (dom-set-attr _el-pDiv "data-foo" "bar")
+ (dom-append (dom-body) _el-pDiv)
+ ))
+ (deftest "can access basic properties"
+ (assert= (eval-hs "foo's foo") "foo")
+ )
+ (deftest "can access basic style"
+ (hs-cleanup!)
+ (let ((_el-pDiv (dom-create-element "div")))
+ (dom-set-attr _el-pDiv "id" "pDiv")
+ (dom-set-attr _el-pDiv "style" "color:red")
+ (dom-append (dom-body) _el-pDiv)
+ ))
+ (deftest "can access its properties"
+ (assert= (eval-hs "its foo") "foo")
+ )
+ (deftest "can access multiple basic attributes"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")))
+ (dom-add-class _el-div "c1")
+ (dom-set-attr _el-div "data-foo" "bar")
+ (dom-add-class _el-div1 "c1")
+ (dom-set-attr _el-div1 "data-foo" "bar")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-div1)
+ ))
+ (deftest "can access multiple basic styles"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")))
+ (dom-add-class _el-div "c1")
+ (dom-set-attr _el-div "style" "color:red")
+ (dom-add-class _el-div1 "c1")
+ (dom-set-attr _el-div1 "style" "color:red")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-div1)
+ ))
+ (deftest "can access my attribute"
+ (hs-cleanup!)
+ (let ((_el-pDiv (dom-create-element "div")))
+ (dom-set-attr _el-pDiv "id" "pDiv")
+ (dom-set-attr _el-pDiv "data-foo" "bar")
+ (dom-append (dom-body) _el-pDiv)
+ ))
+ (deftest "can access my properties"
+ (assert= (eval-hs "my foo") "foo")
+ )
+ (deftest "can access my style"
+ (hs-cleanup!)
+ (let ((_el-pDiv (dom-create-element "div")))
+ (dom-set-attr _el-pDiv "id" "pDiv")
+ (dom-set-attr _el-pDiv "style" "color:red")
+ (dom-append (dom-body) _el-pDiv)
+ ))
+ (deftest "can access properties on classrefs"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "foo")
+ (dom-set-attr _el-div "style" "display: inline")
+ (dom-append (dom-body) _el-div)
+ ))
+ (deftest "can access properties on classrefs 2"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "foo")
+ (dom-set-attr _el-div "style" "display: inline")
+ (dom-append (dom-body) _el-div)
+ ))
+ (deftest "can access properties on idrefs"
+ (hs-cleanup!)
+ (let ((_el-foo (dom-create-element "div")))
+ (dom-set-attr _el-foo "id" "foo")
+ (dom-set-attr _el-foo "style" "display: inline")
+ (dom-append (dom-body) _el-foo)
+ ))
+ (deftest "can access properties on idrefs 2"
+ (hs-cleanup!)
+ (let ((_el-foo (dom-create-element "div")))
+ (dom-set-attr _el-foo "id" "foo")
+ (dom-set-attr _el-foo "style" "display: inline")
+ (dom-append (dom-body) _el-foo)
+ ))
+ (deftest "can access properties on queryrefs"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "foo")
+ (dom-set-attr _el-div "style" "display: inline")
+ (dom-append (dom-body) _el-div)
+ ))
+ (deftest "can access properties on queryrefs 2"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "foo")
+ (dom-set-attr _el-div "style" "display: inline")
+ (dom-append (dom-body) _el-div)
+ ))
+ (deftest "can set basic attributes"
+ (hs-cleanup!)
+ (let ((_el-pDiv (dom-create-element "div")))
+ (dom-set-attr _el-pDiv "id" "pDiv")
+ (dom-set-attr _el-pDiv "data-foo" "bar")
+ (dom-append (dom-body) _el-pDiv)
+ ))
+ (deftest "can set basic styles"
+ (hs-cleanup!)
+ (let ((_el-pDiv (dom-create-element "div")))
+ (dom-set-attr _el-pDiv "id" "pDiv")
+ (dom-set-attr _el-pDiv "style" "color:red")
+ (dom-append (dom-body) _el-pDiv)
+ ))
+ (deftest "can set multiple basic attributes"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d1 "data-foo" "bar")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-set-attr _el-d2 "data-foo" "bar")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-d2)
+ ))
+ (deftest "can set multiple basic styles"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d1 "style" "color:red")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-set-attr _el-d2 "style" "color:red")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-d2)
+ ))
+ (deftest "can set root styles"
+ (hs-cleanup!)
+ (let ((_el-pDiv (dom-create-element "div")))
+ (dom-set-attr _el-pDiv "id" "pDiv")
+ (dom-set-attr _el-pDiv "style" "color:red")
+ (dom-append (dom-body) _el-pDiv)
+ ))
+ (deftest "is null safe"
+ (error "SKIP (untranslated): is null safe"))
+ (deftest "its property is null safe"
+ (error "SKIP (untranslated): its property is null safe"))
+ (deftest "my property is null safe"
+ (error "SKIP (untranslated): my property is null safe"))
+)
+
+;; ── expressions/propertyAccess (12 tests) ──
+(defsuite "hs-upstream-expressions/propertyAccess"
+ (deftest "can access basic properties"
+ (assert= (eval-hs "foo.foo") "foo")
+ )
+ (deftest "chained property access (four levels)"
+ (assert= (eval-hs "a.b.c.d") 42)
+ )
+ (deftest "chained property access (three levels)"
+ (assert= (eval-hs "a.b.c") "deep")
+ )
+ (deftest "is null safe"
+ (error "SKIP (untranslated): is null safe"))
+ (deftest "mixing dot and of forms"
+ (assert= (eval-hs "c of a.b") "mixed")
+ )
+ (deftest "null-safe access through an undefined intermediate"
+ (error "SKIP (untranslated): null-safe access through an undefined intermediate"))
+ (deftest "of form chains through multiple levels"
+ (assert= (eval-hs "c of b of a") "deep")
+ )
+ (deftest "of form works"
+ (assert= (eval-hs "foo of foo") "foo")
+ )
+ (deftest "of form works w/ complex left side"
+ (assert= (eval-hs "bar.doh of foo") "foo")
+ )
+ (deftest "of form works w/ complex right side"
+ (assert= (eval-hs "doh of foo.bar") "foo")
+ )
+ (deftest "property access on function result"
+ (assert= (eval-hs "makeObj().name") "hi")
+ )
+ (deftest "works properly w/ boolean properties"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")))
+ (dom-add-class _el-input "cb")
+ (dom-set-attr _el-input "type" "checkbox")
+ (dom-set-attr _el-input "checked" "checked")
+ (dom-add-class _el-input1 "cb")
+ (dom-set-attr _el-input1 "type" "checkbox")
+ (dom-append (dom-body) _el-input)
+ (dom-append (dom-body) _el-input1)
+ ))
+)
+
+;; ── expressions/queryRef (13 tests) ──
+(defsuite "hs-upstream-expressions/queryRef"
+ (deftest "basic queryRef works"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "c1")
+ (dom-append (dom-body) _el-div)
+ ))
+ (deftest "basic queryRef works w no match"
+ (error "SKIP (untranslated): basic queryRef works w no match"))
+ (deftest "basic queryRef works w properties w/ strings"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
+ (dom-add-class _el-div "c1")
+ (dom-add-class _el-div1 "c2")
+ (dom-set-attr _el-div1 "foo" "bar")
+ (dom-add-class _el-div2 "c3")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-div1)
+ (dom-append (dom-body) _el-div2)
+ ))
+ (deftest "basic queryRef works w/ div selector"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
+ (dom-add-class _el-div "c1")
+ (dom-add-class _el-div1 "c2")
+ (dom-add-class _el-div2 "c3")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-div1)
+ (dom-append (dom-body) _el-div2)
+ ))
+ (deftest "basic queryRef works w/ funny selector"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
+ (dom-set-attr _el-div "title" "t1")
+ (dom-set-attr _el-div1 "title" "t2")
+ (dom-set-attr _el-div2 "title" "t3")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-div1)
+ (dom-append (dom-body) _el-div2)
+ ))
+ (deftest "basic queryRef works w/ multiple matches"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
+ (dom-add-class _el-div "c1")
+ (dom-add-class _el-div1 "c1")
+ (dom-add-class _el-div2 "c1")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-div1)
+ (dom-append (dom-body) _el-div2)
+ ))
+ (deftest "basic queryRef works w/ properties"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
+ (dom-set-attr _el-div "title" "t1")
+ (dom-set-attr _el-div1 "title" "t2")
+ (dom-set-attr _el-div2 "title" "t3")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-div1)
+ (dom-append (dom-body) _el-div2)
+ ))
+ (deftest "can interpolate elements into queries"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")))
+ (dom-add-class _el-div "a")
+ (dom-add-class _el-div1 "b")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-div1)
+ ))
+ (deftest "queryRef w/ $ no curlies works"
+ (hs-cleanup!)
+ (let ((_el-t1 (dom-create-element "div")) (_el-t2 (dom-create-element "div")) (_el-t3 (dom-create-element "div")))
+ (dom-set-attr _el-t1 "id" "t1")
+ (dom-set-attr _el-t2 "id" "t2")
+ (dom-set-attr _el-t3 "id" "t3")
+ (dom-append (dom-body) _el-t1)
+ (dom-append (dom-body) _el-t2)
+ (dom-append (dom-body) _el-t3)
+ ))
(deftest "queryRef w/ $ works"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
@@ -8454,10 +5938,30 @@
(dom-append (dom-body) _el-div1)
(dom-append (dom-body) _el-div2)
))
+ (deftest "queryRefs support colons properly"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")))
+ (dom-add-class _el-input "foo")
+ (dom-set-attr _el-input "type" "checkbox")
+ (dom-set-attr _el-input "checked" "checked")
+ (dom-append (dom-body) _el-input)
+ ))
+ (deftest "queryRefs support dollar properly"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "title" "little flower")
+ (dom-append (dom-body) _el-div)
+ ))
+ (deftest "queryRefs support tildes properly"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "title" "little flower")
+ (dom-append (dom-body) _el-div)
+ ))
)
-;; ── relativePositionalExpression (4 tests) ──
-(defsuite "hs-upstream-relativePositionalExpression"
+;; ── expressions/relativePositionalExpression (23 tests) ──
+(defsuite "hs-upstream-expressions/relativePositionalExpression"
(deftest "can access property of next element with possessive"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
@@ -8486,5 +5990,7322 @@
(dom-append (dom-body) _el-d2)
))
(deftest "can write to next element with put command"
- (hs-cleanup!))
+ (error "SKIP (untranslated): can write to next element with put command"))
+ (deftest "next works properly among siblings"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-d3)
+ ))
+ (deftest "next works properly among siblings with wrapping"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-d3)
+ ))
+ (deftest "next works properly with array-like"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-d2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-add-class _el-p "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-add-class _el-p3 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-p)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-p3)
+ (dom-append (dom-body) _el-d3)
+ ))
+ (deftest "next works properly with array-like and wrap"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-d2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-add-class _el-p "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-add-class _el-p3 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-p)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-p3)
+ (dom-append (dom-body) _el-d3)
+ ))
+ (deftest "next works properly with array-like no match"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-d2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-add-class _el-p "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-add-class _el-p3 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-p)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-p3)
+ (dom-append (dom-body) _el-d3)
+ ))
+ (deftest "next works properly with array-like no match and wrap"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-d2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-add-class _el-p "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-add-class _el-p3 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-p)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-p3)
+ (dom-append (dom-body) _el-d3)
+ ))
+ (deftest "previous works properly among siblings"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-d3)
+ ))
+ (deftest "previous works properly among siblings with wrapping"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-d3)
+ ))
+ (deftest "previous works properly with array-like"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-d2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-add-class _el-p "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-add-class _el-p3 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-p)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-p3)
+ (dom-append (dom-body) _el-d3)
+ ))
+ (deftest "previous works properly with array-like and wrap"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-d2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-add-class _el-p "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-add-class _el-p3 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-p)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-p3)
+ (dom-append (dom-body) _el-d3)
+ ))
+ (deftest "previous works properly with array-like no match"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-d2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-add-class _el-p "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-add-class _el-p3 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-p)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-p3)
+ (dom-append (dom-body) _el-d3)
+ ))
+ (deftest "previous works properly with array-like no match and wrap"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-d2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-d3 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-add-class _el-p "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-add-class _el-p3 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-p)
+ (dom-append (dom-body) _el-d2)
+ (dom-append (dom-body) _el-p3)
+ (dom-append (dom-body) _el-d3)
+ ))
+ (deftest "properly constrains via the within modifier"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")) (_el-d4 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-set-attr _el-d3 "id" "d3")
+ (dom-add-class _el-d3 "c1")
+ (dom-set-attr _el-d4 "id" "d4")
+ (dom-add-class _el-d4 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append _el-d1 _el-d2)
+ (dom-append _el-d1 _el-d3)
+ (dom-append (dom-body) _el-d4)
+ ))
+ (deftest "relative next works properly among siblings w/ class"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d1 "_" "on click add .foo to next .c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-d2)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ ))
+ (deftest "relative next works properly among siblings w/ query"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d1 "_" "on click add .foo to next ")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-d2)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ ))
+ (deftest "relative next works properly among siblings w/ query & class"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d1 "_" "on click add .foo to next ")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-d2)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ ))
+ (deftest "relative previous works properly among siblings w/ class"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-set-attr _el-d2 "_" "on click add .foo to previous .c1")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-d2)
+ (hs-activate! _el-d2)
+ (dom-dispatch (dom-query-by-id "d2") "click" nil)
+ ))
+ (deftest "relative previous works properly among siblings w/ query"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-set-attr _el-d2 "_" "on click add .foo to previous ")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-d2)
+ (hs-activate! _el-d2)
+ (dom-dispatch (dom-query-by-id "d2") "click" nil)
+ ))
+ (deftest "relative previous works properly among siblings w/ query & class"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "c1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-add-class _el-d2 "c1")
+ (dom-set-attr _el-d2 "_" "on click add .foo to previous ")
+ (dom-append (dom-body) _el-d1)
+ (dom-append (dom-body) _el-d2)
+ (hs-activate! _el-d2)
+ (dom-dispatch (dom-query-by-id "d2") "click" nil)
+ ))
+)
+
+;; ── expressions/some (6 tests) ──
+(defsuite "hs-upstream-expressions/some"
+ (deftest "some returns false for empty array"
+ (assert= (eval-hs "some []") false)
+ )
+ (deftest "some returns false for empty selector"
+ (assert= (eval-hs "some .aClassThatDoesNotExist") false)
+ )
+ (deftest "some returns false for null"
+ (assert= (eval-hs "some null") false)
+ )
+ (deftest "some returns true for filled array"
+ (assert= (eval-hs "some ['thing']") true)
+ )
+ (deftest "some returns true for non-null"
+ (assert= (eval-hs "some 'thing'") true)
+ )
+ (deftest "some returns true for nonempty selector"
+ (assert= (eval-hs "some ") true)
+ )
+)
+
+;; ── expressions/splitJoin (7 tests) ──
+(defsuite "hs-upstream-expressions/splitJoin"
+ (deftest "joins an array with delimiter"
+ (assert= (eval-hs "return [\"a\", \"b\", \"c\"] joined by \", \"") "a, b, c")
+ )
+ (deftest "joins with empty string"
+ (assert= (eval-hs "return [\"x\", \"y\", \"z\"] joined by \"\"") "xyz")
+ )
+ (deftest "split then mapped then joined"
+ (assert= (eval-hs "return \"hello world\" split by \" \" mapped to its length joined by \",\"") "5,5")
+ )
+ (deftest "split then sorted then joined"
+ (assert= (eval-hs "return \"banana,apple,cherry\" split by \",\" sorted by it joined by \", \"") "apple, banana, cherry")
+ )
+ (deftest "split then where then joined"
+ (assert= (eval-hs "return \"a,,b,,c\" split by \",\" where it is not \"\" joined by \"-\"") "a-b-c")
+ )
+ (deftest "splits a string by delimiter"
+ (assert= (eval-hs "return \"a,b,c\" split by \",\"") (list "a" "b" "c"))
+ )
+ (deftest "splits by whitespace"
+ (assert= (eval-hs "return \"hello world\" split by \" \"") (list "hello" "world"))
+ )
+)
+
+;; ── expressions/stringPostfix (3 tests) ──
+(defsuite "hs-upstream-expressions/stringPostfix"
+ (deftest "handles basic postfix strings properly"
+ (assert= (eval-hs "1em") "1em")
+ (assert= (eval-hs "1px") "1px")
+ (assert= (eval-hs "-1px") "-1px")
+ (assert= (eval-hs "100%") "100%")
+ )
+ (deftest "handles basic postfix strings with spaces properly"
+ (assert= (eval-hs "1 em") "1em")
+ (assert= (eval-hs "1 px") "1px")
+ (assert= (eval-hs "100 %") "100%")
+ )
+ (deftest "handles expression roots properly"
+ (assert= (eval-hs "(0 + 1) em") "1em")
+ (assert= (eval-hs "(0 + 1) px") "1px")
+ (assert= (eval-hs "(100 + 0) %") "100%")
+ )
+)
+
+;; ── expressions/strings (8 tests) ──
+(defsuite "hs-upstream-expressions/strings"
+ (deftest "handles strings properly"
+ (assert= (eval-hs "\"foo\"") "foo")
+ (assert= (eval-hs "\"foo\"") "fo'o")
+ (assert= (eval-hs "\"foo\"") "foo")
+ )
+ (deftest "should handle back slashes in non-template content"
+ (assert= (eval-hs "`https://${foo}`") "https://bar")
+ )
+ (deftest "should handle strings with tags and quotes"
+ (error "SKIP (untranslated): should handle strings with tags and quotes"))
+ (deftest "string templates preserve white space"
+ (assert= (eval-hs "` ${1 + 2} ${1 + 2} `") " 3 3 ")
+ (assert= (eval-hs "` ${1 + 2} ${1 + 2} `") "3 3 ")
+ (assert= (eval-hs "` ${1 + 2} ${1 + 2} `") "33 ")
+ (assert= (eval-hs "` ${1 + 2} ${1 + 2} `") "3 3")
+ )
+ (deftest "string templates work properly"
+ (assert= (eval-hs "`$1`") "1")
+ )
+ (deftest "string templates work properly w braces"
+ (assert= (eval-hs "`${1 + 2}`") "3")
+ )
+ (deftest "string templates work w/ props"
+ (assert= (eval-hs "`$window.foo`") "foo")
+ )
+ (deftest "string templates work w/ props w/ braces"
+ (assert= (eval-hs "`${window.foo}`") "foo")
+ )
+)
+
+;; ── expressions/styleRef (6 tests) ──
+(defsuite "hs-upstream-expressions/styleRef"
+ (deftest "basic style ref works"
+ (hs-cleanup!)
+ (let ((_el-sDiv (dom-create-element "div")))
+ (dom-set-attr _el-sDiv "id" "sDiv")
+ (dom-set-attr _el-sDiv "style" "color: red; text-align: center; width: 10px")
+ (dom-append (dom-body) _el-sDiv)
+ ))
+ (deftest "calculated of style ref works"
+ (hs-cleanup!)
+ (let ((_el-sDiv (dom-create-element "div")))
+ (dom-set-attr _el-sDiv "id" "sDiv")
+ (dom-set-attr _el-sDiv "style" "color: red; text-align: center; width: 10px")
+ (dom-append (dom-body) _el-sDiv)
+ ))
+ (deftest "calculated possessive style ref works"
+ (hs-cleanup!)
+ (let ((_el-sDiv (dom-create-element "div")))
+ (dom-set-attr _el-sDiv "id" "sDiv")
+ (dom-set-attr _el-sDiv "style" "color: red; text-align: center; width: 10px")
+ (dom-append (dom-body) _el-sDiv)
+ ))
+ (deftest "calculated style ref works"
+ (hs-cleanup!)
+ (let ((_el-sDiv (dom-create-element "div")))
+ (dom-set-attr _el-sDiv "id" "sDiv")
+ (dom-set-attr _el-sDiv "style" "color: red; text-align: center; width: 10px")
+ (dom-append (dom-body) _el-sDiv)
+ ))
+ (deftest "of style ref works"
+ (hs-cleanup!)
+ (let ((_el-sDiv (dom-create-element "div")))
+ (dom-set-attr _el-sDiv "id" "sDiv")
+ (dom-set-attr _el-sDiv "style" "color: red; text-align: center; width: 10px")
+ (dom-append (dom-body) _el-sDiv)
+ ))
+ (deftest "possessive style ref works"
+ (hs-cleanup!)
+ (let ((_el-sDiv (dom-create-element "div")))
+ (dom-set-attr _el-sDiv "id" "sDiv")
+ (dom-set-attr _el-sDiv "style" "color: red; text-align: center; width: 10px")
+ (dom-append (dom-body) _el-sDiv)
+ ))
+)
+
+;; ── expressions/symbol (2 tests) ──
+(defsuite "hs-upstream-expressions/symbol"
+ (deftest "resolves global context properly"
+ (error "SKIP (untranslated): resolves global context properly"))
+ (deftest "resolves local context properly"
+ (assert= (eval-hs "foo") 42)
+ )
+)
+
+;; ── expressions/typecheck (5 tests) ──
+(defsuite "hs-upstream-expressions/typecheck"
+ (deftest "can do basic non-string typecheck failure"
+ (error "SKIP (untranslated): can do basic non-string typecheck failure"))
+ (deftest "can do basic string non-null typecheck"
+ (assert= (eval-hs "'foo' : String!") "foo")
+ )
+ (deftest "can do basic string typecheck"
+ (assert= (eval-hs "'foo' : String") "foo")
+ )
+ (deftest "can do null as string typecheck"
+ (error "SKIP (untranslated): can do null as string typecheck"))
+ (deftest "null causes null safe string check to fail"
+ (error "SKIP (untranslated): null causes null safe string check to fail"))
+)
+
+;; ── ext/component (20 tests) ──
+(defsuite "hs-upstream-ext/component"
+ (deftest "applies _ hyperscript to component instance"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-test-init (dom-create-element "test-init")))
+ (dom-set-attr _el-script "_" "init set ^msg to 'initialized'")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-init")
+ (dom-set-inner-html _el-script "${}{^msg}")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-init)
+ (hs-activate! _el-script)
+ ))
+ (deftest "attrs bind is bidirectional - inner changes flow outward"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-test-args-bidir (dom-create-element "test-args-bidir")) (_el-p (dom-create-element "p")))
+ (dom-set-attr _el-script "_" "bind ^count to attrs.count")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-args-bidir")
+ (dom-set-inner-html _el-script "${}{^count}
+ ")
+ (dom-set-attr _el-test-args-bidir "count" "$count")
+ (dom-set-attr _el-p "_" "live put $count into me")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-args-bidir)
+ (dom-append (dom-body) _el-p)
+ (hs-activate! _el-script)
+ (hs-activate! _el-p)
+ (dom-dispatch (dom-query "test-args-bidir button") "click" nil)
+ ))
+ (deftest "attrs evaluates attribute as hyperscript in parent scope"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-test-args (dom-create-element "test-args")))
+ (dom-set-attr _el-script "_" "init set ^list to attrs.items")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-args")
+ (dom-set-inner-html _el-script "
+ #for item in ^list
+ - ${}{item}
+ #end
+
")
+ (dom-set-attr _el-test-args "items" "$stuff")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-args)
+ (hs-activate! _el-script)
+ ))
+ (deftest "attrs works with bind for reactive pass-through"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-test-args-bind (dom-create-element "test-args-bind")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-script "_" "bind ^val to attrs.count")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-args-bind")
+ (dom-set-inner-html _el-script "${}{^val}")
+ (dom-set-attr _el-test-args-bind "count" "$count")
+ (dom-set-attr _el-button "_" "on click increment $count")
+ (dom-set-inner-html _el-button "+")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-args-bind)
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-script)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "bind keeps ^var in sync with attribute changes"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-test-bind-attr (dom-create-element "test-bind-attr")))
+ (dom-set-attr _el-script "_" "bind ^count to @data-count")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-bind-attr")
+ (dom-set-inner-html _el-script "${}{^count}")
+ (dom-set-attr _el-test-bind-attr "data-count" "5")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-bind-attr)
+ (hs-activate! _el-script)
+ ))
+ (deftest "blocks processing of inner hyperscript until render"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-test-block (dom-create-element "test-block")))
+ (dom-set-attr _el-script "_" "init set ^msg to 'ready'")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-block")
+ (dom-set-inner-html _el-script "click me")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-block)
+ (hs-activate! _el-script)
+ (dom-dispatch (dom-query "test-block span") "click" nil)
+ ))
+ (deftest "component isolation prevents ^var leaking inward"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-script (dom-create-element "script")) (_el-test-isolated (dom-create-element "test-isolated")))
+ (dom-set-attr _el-div "_" "init set ^leaked to 'should-not-see'")
+ (dom-set-attr _el-script "_" "init set ^internal to 'component-only'")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-isolated")
+ (dom-set-inner-html _el-script "waiting")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-script)
+ (dom-append _el-div _el-test-isolated)
+ (hs-activate! _el-div)
+ (hs-activate! _el-script)
+ ))
+ (deftest "does not process slotted _ attributes prematurely"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-script (dom-create-element "script")) (_el-test-slot-hs (dom-create-element "test-slot-hs")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-div "_" "init set ^x to 42")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-slot-hs")
+ (dom-set-inner-html _el-script "
")
+ (dom-set-attr _el-span "_" "on click put ^x into me")
+ (dom-set-inner-html _el-span "before")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-script)
+ (dom-append _el-div _el-test-slot-hs)
+ (dom-append _el-test-slot-hs _el-span)
+ (hs-activate! _el-div)
+ (hs-activate! _el-span)
+ (dom-dispatch (dom-query "test-slot-hs span") "click" nil)
+ ))
+ (deftest "extracts
+ styled")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-styled)
+ ))
+ (deftest "processes _ on inner elements"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-test-inner (dom-create-element "test-inner")))
+ (dom-set-attr _el-script "_" "init set ^count to 0")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-inner")
+ (dom-set-inner-html _el-script "\">+
+ 0")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-inner)
+ (hs-activate! _el-script)
+ (dom-dispatch (dom-query "test-inner button") "click" nil)
+ ))
+ (deftest "reactively updates template expressions"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-test-reactive (dom-create-element "test-reactive")))
+ (dom-set-attr _el-script "_" "init set ^count to 0")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-reactive")
+ (dom-set-inner-html _el-script "
+ Count: ${}{^count}")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-reactive)
+ (hs-activate! _el-script)
+ (dom-dispatch (dom-query "test-reactive button") "click" nil)
+ ))
+ (deftest "reads attributes via @"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-test-attrs (dom-create-element "test-attrs")))
+ (dom-set-attr _el-script "_" "init set ^val to @data-start as Int")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-attrs")
+ (dom-set-inner-html _el-script "${}{^val}")
+ (dom-set-attr _el-test-attrs "data-start" "42")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-attrs)
+ (hs-activate! _el-script)
+ ))
+ (deftest "registers a custom element from a template"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-test-hello (dom-create-element "test-hello")))
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-hello")
+ (dom-set-inner-html _el-script "Hello World")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-hello)
+ ))
+ (deftest "renders template expressions"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-test-greet (dom-create-element "test-greet")))
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-greet")
+ (dom-set-inner-html _el-script "Hello ${}{$name}!")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-greet)
+ ))
+ (deftest "slotted content resolves ^var from outer scope, not component scope"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-script (dom-create-element "script")) (_el-test-scope-slot (dom-create-element "test-scope-slot")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-div "_" "init set ^outer to 'from-outside'")
+ (dom-set-attr _el-script "_" "init set ^outer to 'from-component'")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-scope-slot")
+ (dom-set-inner-html _el-script "
")
+ (dom-set-attr _el-span "_" "init put ^outer into me")
+ (dom-set-inner-html _el-span "waiting")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-script)
+ (dom-append _el-div _el-test-scope-slot)
+ (dom-append _el-test-scope-slot _el-span)
+ (hs-activate! _el-div)
+ (hs-activate! _el-script)
+ (hs-activate! _el-span)
+ ))
+ (deftest "substitutes slot content into template"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-test-card (dom-create-element "test-card")) (_el-p (dom-create-element "p")))
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-card")
+ (dom-set-inner-html _el-script "
")
+ (dom-set-inner-html _el-p "Hello from slot")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-card)
+ (dom-append _el-test-card _el-p)
+ ))
+ (deftest "supports #for loops in template"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-test-loop (dom-create-element "test-loop")))
+ (dom-set-attr _el-script "_" "init set ^items to ['a', 'b', 'c']")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-loop")
+ (dom-set-inner-html _el-script "
+ #for item in ^items
+ - ${}{item}
+ #end
+
")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-loop)
+ (hs-activate! _el-script)
+ ))
+ (deftest "supports #if conditionals in template"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-test-cond (dom-create-element "test-cond")))
+ (dom-set-attr _el-script "_" "init set ^show to true")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-cond")
+ (dom-set-inner-html _el-script "#if ^show
+ visible
+ #end")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-cond)
+ (hs-activate! _el-script)
+ ))
+ (deftest "supports multiple independent instances"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-a (dom-create-element "test-multi")) (_el-b (dom-create-element "test-multi")))
+ (dom-set-attr _el-script "_" "init set ^count to 0")
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-multi")
+ (dom-set-inner-html _el-script "\">+
+ 0")
+ (dom-set-attr _el-a "id" "a")
+ (dom-set-attr _el-b "id" "b")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-a)
+ (dom-append (dom-body) _el-b)
+ (hs-activate! _el-script)
+ (dom-dispatch (dom-query "#a button") "click" nil)
+ ))
+ (deftest "supports named slots"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")) (_el-test-named-slot (dom-create-element "test-named-slot")) (_el-h1 (dom-create-element "h1")) (_el-p (dom-create-element "p")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-script "type" "text/hyperscript-template")
+ (dom-set-attr _el-script "component" "test-named-slot")
+ (dom-set-inner-html _el-script "
+
+ ")
+ (dom-set-attr _el-h1 "slot" "title")
+ (dom-set-inner-html _el-h1 "My Title")
+ (dom-set-inner-html _el-p "Default content")
+ (dom-set-attr _el-span "slot" "footer")
+ (dom-set-inner-html _el-span "Footer text")
+ (dom-append (dom-body) _el-script)
+ (dom-append (dom-body) _el-test-named-slot)
+ (dom-append _el-test-named-slot _el-h1)
+ (dom-append _el-test-named-slot _el-p)
+ (dom-append _el-test-named-slot _el-span)
+ ))
+)
+
+;; ── ext/eventsource (13 tests) ──
+(defsuite "hs-upstream-ext/eventsource"
+ (deftest "as json decodes data"
+ (hs-cleanup!)
+ (let ((_el-name (dom-create-element "div")) (_el-age (dom-create-element "div")))
+ (dom-set-attr _el-name "id" "name")
+ (dom-set-attr _el-age "id" "age")
+ (dom-append (dom-body) _el-name)
+ (dom-append (dom-body) _el-age)
+ ))
+ (deftest "catch-all * matches every event"
+ (hs-cleanup!)
+ (let ((_el-out (dom-create-element "div")))
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-out)
+ ))
+ (deftest "close() stops the connection"
+ (hs-cleanup!)
+ (let ((_el-out (dom-create-element "div")))
+ (dom-set-attr _el-out "id" "out")
+ (dom-set-inner-html _el-out "0")
+ (dom-append (dom-body) _el-out)
+ ))
+ (deftest "dispatches named SSE events on the element"
+ (hs-cleanup!)
+ (let ((_el-out (dom-create-element "div")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-out "id" "out")
+ (dom-set-attr _el-button "_" "on click fetch /stream-named as Stream then on status from me then put event.detail.data into #out then end")
+ (dom-set-inner-html _el-button "go")
+ (dom-append (dom-body) _el-out)
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "dynamic open with new URL"
+ (hs-cleanup!)
+ (let ((_el-out (dom-create-element "div")))
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-out)
+ ))
+ (deftest "fires streamEnd when the stream closes"
+ (hs-cleanup!)
+ (let ((_el-out (dom-create-element "div")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-out "id" "out")
+ (dom-set-attr _el-button "_" "on click fetch /streamEnd as Stream then wait for streamEnd from me then put 'finished' into #out")
+ (dom-set-inner-html _el-button "go")
+ (dom-append (dom-body) _el-out)
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "iterates plain messages with for loop"
+ (hs-cleanup!)
+ (let ((_el-out (dom-create-element "div")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-out "id" "out")
+ (dom-set-attr _el-button "_" "on click fetch /stream-iter as Stream then for message in the result then put message + ' ' at end of #out then end")
+ (dom-set-inner-html _el-button "go")
+ (dom-append (dom-body) _el-out)
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "open and close lifecycle events fire"
+ (hs-cleanup!)
+ (let ((_el-opened (dom-create-element "div")))
+ (dom-set-attr _el-opened "id" "opened")
+ (dom-append (dom-body) _el-opened)
+ ))
+ (deftest "receives named events"
+ (hs-cleanup!)
+ (let ((_el-greet (dom-create-element "div")) (_el-bye (dom-create-element "div")))
+ (dom-set-attr _el-greet "id" "greet")
+ (dom-set-attr _el-bye "id" "bye")
+ (dom-append (dom-body) _el-greet)
+ (dom-append (dom-body) _el-bye)
+ ))
+ (deftest "receives unnamed messages"
+ (hs-cleanup!)
+ (let ((_el-out (dom-create-element "div")))
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-out)
+ ))
+ (deftest "wildcard pattern matches multiple events"
+ (hs-cleanup!)
+ (let ((_el-count (dom-create-element "div")))
+ (dom-set-attr _el-count "id" "count")
+ (dom-set-inner-html _el-count "0")
+ (dom-append (dom-body) _el-count)
+ ))
+ (deftest "with headers sends custom headers"
+ (hs-cleanup!)
+ (let ((_el-out (dom-create-element "div")))
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-out)
+ ))
+ (deftest "with method sends POST"
+ (hs-cleanup!)
+ (let ((_el-out (dom-create-element "div")))
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-out)
+ ))
+)
+
+;; ── ext/hs-include (10 tests) ──
+(defsuite "hs-upstream-ext/hs-include"
+ (deftest "JSON-serializes object values"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
+ (dom-set-attr _el-button "_" "init set :obj to {name: 'Alice', age: 30}")
+ (dom-set-attr _el-button "hs-include" ":obj")
+ (dom-set-attr _el-button "hx-post" "/api")
+ (dom-set-attr _el-button "hx-target" "#out")
+ (dom-set-inner-html _el-button "Send")
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-out)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "direct hs-include takes precedence over inherited"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "init set :parent to 1")
+ (dom-set-attr _el-div "hs-include:inherited" ":parent")
+ (dom-set-attr _el-button "_" "init set :child to 2")
+ (dom-set-attr _el-button "hs-include" ":child")
+ (dom-set-attr _el-button "hx-post" "/api")
+ (dom-set-attr _el-button "hx-target" "#out")
+ (dom-set-inner-html _el-button "Send")
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-button)
+ (dom-append (dom-body) _el-out)
+ (hs-activate! _el-div)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "elements without hs-include are unaffected"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
+ (dom-set-attr _el-button "_" "init set :secret to 42")
+ (dom-set-attr _el-button "hx-post" "/api")
+ (dom-set-attr _el-button "hx-target" "#out")
+ (dom-set-inner-html _el-button "Send")
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-out)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "hs-include:inherited applies to descendant triggers"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "init set :ctx to 'parent-val'")
+ (dom-set-attr _el-div "hs-include:inherited" ":ctx")
+ (dom-set-attr _el-button "hx-post" "/api")
+ (dom-set-attr _el-button "hx-target" "#out")
+ (dom-set-inner-html _el-button "Send")
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-button)
+ (dom-append (dom-body) _el-out)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "includes a named element-scoped variable"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
+ (dom-set-attr _el-button "_" "init set :userId to 42")
+ (dom-set-attr _el-button "hs-include" ":userId")
+ (dom-set-attr _el-button "hx-post" "/api")
+ (dom-set-attr _el-button "hx-target" "#out")
+ (dom-set-inner-html _el-button "Send")
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-out)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "includes multiple named variables"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
+ (dom-set-attr _el-button "_" "init set :a to 1 then set :b to 2")
+ (dom-set-attr _el-button "hs-include" ":a, :b")
+ (dom-set-attr _el-button "hx-post" "/api")
+ (dom-set-attr _el-button "hx-target" "#out")
+ (dom-set-inner-html _el-button "Send")
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-out)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "missing variables are silently skipped"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
+ (dom-set-attr _el-button "hs-include" ":nonexistent")
+ (dom-set-attr _el-button "hx-post" "/api")
+ (dom-set-attr _el-button "hx-target" "#out")
+ (dom-set-inner-html _el-button "Send")
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-out)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "resolves inherited var via ^"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "init set :shared to 99")
+ (dom-set-attr _el-button "hs-include" "^shared")
+ (dom-set-attr _el-button "hx-post" "/api")
+ (dom-set-attr _el-button "hx-target" "#out")
+ (dom-set-inner-html _el-button "Send")
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-button)
+ (dom-append (dom-body) _el-out)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "resolves var from another element via #selector"
+ (hs-cleanup!)
+ (let ((_el-source (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
+ (dom-set-attr _el-source "id" "source")
+ (dom-set-attr _el-source "_" "init set :data to 'hello'")
+ (dom-set-attr _el-button "hs-include" "#source:data")
+ (dom-set-attr _el-button "hx-post" "/api")
+ (dom-set-attr _el-button "hx-target" "#out")
+ (dom-set-inner-html _el-button "Send")
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-source)
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-out)
+ (hs-activate! _el-source)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "wildcard includes all element-scoped vars"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
+ (dom-set-attr _el-button "_" "init set :x to 10 then set :y to 20")
+ (dom-set-attr _el-button "hs-include" "*")
+ (dom-set-attr _el-button "hx-post" "/api")
+ (dom-set-attr _el-button "hx-target" "#out")
+ (dom-set-inner-html _el-button "Send")
+ (dom-set-attr _el-out "id" "out")
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-out)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+)
+
+;; ── ext/tailwind (12 tests) ──
+(defsuite "hs-upstream-ext/tailwind"
+ (deftest "can hide element, with tailwindcss hidden class"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide with twDisplay")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "hidden"))
+ ))
+ (deftest "can hide element, with tailwindcss hidden class default strategy"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "hidden"))
+ ))
+ (deftest "can hide element, with tailwindcss invisible class"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide with twVisibility")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "invisible"))
+ ))
+ (deftest "can hide element, with tailwindcss invisible class default strategy"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "invisible"))
+ ))
+ (deftest "can hide element, with tailwindcss opacity-0 class"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide with twOpacity")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "opacity-0"))
+ ))
+ (deftest "can hide element, with tailwindcss opacity-0 class default strategy"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "opacity-0"))
+ ))
+ (deftest "can show element, with tailwindcss removing hidden class"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "hidden")
+ (dom-set-attr _el-div "_" "on click show with twDisplay")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (not (dom-has-class? _el-div "hidden")))
+ ))
+ (deftest "can show element, with tailwindcss removing hidden class default strategy"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "hidden")
+ (dom-set-attr _el-div "_" "on click show")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (not (dom-has-class? _el-div "hidden")))
+ ))
+ (deftest "can show element, with tailwindcss removing invisible class"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "invisible")
+ (dom-set-attr _el-div "_" "on click show with twVisibility")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (not (dom-has-class? _el-div "invisible")))
+ ))
+ (deftest "can show element, with tailwindcss removing invisible class default strategy"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "invisible")
+ (dom-set-attr _el-div "_" "on click show")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (not (dom-has-class? _el-div "invisible")))
+ ))
+ (deftest "can show element, with tailwindcss removing opacity-0 class"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "opacity-0")
+ (dom-set-attr _el-div "_" "on click show with twOpacity")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (not (dom-has-class? _el-div "opacity-0")))
+ ))
+ (deftest "can show element, with tailwindcss removing opacity-0 class default strategy"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-add-class _el-div "opacity-0")
+ (dom-set-attr _el-div "_" "on click show")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (not (dom-has-class? _el-div "opacity-0")))
+ ))
+)
+
+;; ── fetch (23 tests) ──
+(defsuite "hs-upstream-fetch"
+ (deftest "Response can be converted to JSON via as JSON"
+ (error "SKIP (skip-list): Response can be converted to JSON via as JSON"))
+ (deftest "allows the event handler to change the fetch parameters"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click fetch \"/test\" then put it into my.innerHTML end")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "yay")
+ ))
+ (deftest "as response does not throw on 404"
+ (error "SKIP (skip-list): as response does not throw on 404"))
+ (deftest "can catch an error that occurs when using fetch"
+ (error "SKIP (skip-list): can catch an error that occurs when using fetch"))
+ (deftest "can do a simple fetch"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click fetch \"/test\" then put it into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "yay")
+ ))
+ (deftest "can do a simple fetch w/ a custom conversion"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click fetch /test as Number then put it into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "1.2")
+ ))
+ (deftest "can do a simple fetch w/ a naked URL"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click fetch /test then put it into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "yay")
+ ))
+ (deftest "can do a simple fetch w/ html"
+ (error "SKIP (skip-list): can do a simple fetch w/ html"))
+ (deftest "can do a simple fetch w/ json"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click fetch /test as json then get result as JSONString then put it into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "{\"foo\":1}")
+ ))
+ (deftest "can do a simple fetch w/ json using JSON syntax"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click fetch /test as JSON then get result as JSONString then put it into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "{\"foo\":1}")
+ ))
+ (deftest "can do a simple fetch w/ json using Object syntax"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click fetch /test as Object then get result as JSONString then put it into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "{\"foo\":1}")
+ ))
+ (deftest "can do a simple fetch w/ json using Object syntax and an 'an' prefix"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click fetch /test as an Object then get result as JSONString then put it into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "{\"foo\":1}")
+ ))
+ (deftest "can do a simple fetch with a response object"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click fetch /test as response then if its.ok put \"yep\" into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "yep")
+ ))
+ (deftest "can do a simple post"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click fetch /test {method:\"POST\"} then put it into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "yay")
+ ))
+ (deftest "can do a simple post alt syntax w/ curlies"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click fetch /test with {method:\"POST\"} then put it into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "yay")
+ ))
+ (deftest "can do a simple post alt syntax without curlies"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click fetch /test with method:\"POST\" then put it into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "yay")
+ ))
+ (deftest "can put response conversion after with"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click fetch /test with {method:\"POST\"} as text then put it into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "yay")
+ ))
+ (deftest "can put response conversion before with"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click fetch /test as text with {method:\"POST\"} then put it into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "yay")
+ ))
+ (deftest "do not throw passes through 404 response"
+ (error "SKIP (skip-list): do not throw passes through 404 response"))
+ (deftest "don't throw passes through 404 response"
+ (error "SKIP (skip-list): don't throw passes through 404 response"))
+ (deftest "submits the fetch parameters to the event handler"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click fetch \"/test\" {headers: {\"X-CustomHeader\": \"foo\"}} then put it into my.innerHTML end")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "yay")
+ ))
+ (deftest "throws on non-2xx response by default"
+ (error "SKIP (skip-list): throws on non-2xx response by default"))
+ (deftest "triggers an event just before fetching"
+ (error "SKIP (skip-list): triggers an event just before fetching"))
+)
+
+;; ── focus (3 tests) ──
+(defsuite "hs-upstream-focus"
+ (deftest "can blur an element"
+ (hs-cleanup!)
+ (let ((_el-i1 (dom-create-element "input")))
+ (dom-set-attr _el-i1 "id" "i1")
+ (dom-set-attr _el-i1 "_" "on focus wait 10ms then blur me")
+ (dom-append (dom-body) _el-i1)
+ (hs-activate! _el-i1)
+ (dom-focus (dom-query-by-id "i1"))
+ ))
+ (deftest "can focus an element"
+ (hs-cleanup!)
+ (let ((_el-i1 (dom-create-element "input")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-i1 "id" "i1")
+ (dom-set-attr _el-button "_" "on click focus #i1")
+ (dom-append (dom-body) _el-i1)
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "focus with no target focuses me"
+ (hs-cleanup!)
+ (let ((_el-i1 (dom-create-element "input")))
+ (dom-set-attr _el-i1 "id" "i1")
+ (dom-set-attr _el-i1 "_" "on click focus")
+ (dom-append (dom-body) _el-i1)
+ (hs-activate! _el-i1)
+ (dom-dispatch (dom-query-by-id "i1") "click" nil)
+ ))
+)
+
+;; ── go (5 tests) ──
+(defsuite "hs-upstream-go"
+ (deftest "can parse go to with string URL"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click go to \"#test-hash\"")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "deprecated scroll form still works"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-target (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
+ (dom-set-attr _el-div "style" "height: 2000px")
+ (dom-set-attr _el-target "id" "target")
+ (dom-set-inner-html _el-target "Target")
+ (dom-set-attr _el-div2 "_" "on click go to the top of #target")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-target)
+ (dom-append (dom-body) _el-div2)
+ (hs-activate! _el-div2)
+ (dom-dispatch (dom-query "div:nth-of-type(3)") "click" nil)
+ ))
+ (deftest "deprecated url keyword still parses"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click go to url /test")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "go to element scrolls"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-target (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
+ (dom-set-attr _el-div "style" "height: 2000px")
+ (dom-set-attr _el-target "id" "target")
+ (dom-set-inner-html _el-target "Target")
+ (dom-set-attr _el-div2 "_" "on click go to #target")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-target)
+ (dom-append (dom-body) _el-div2)
+ (hs-activate! _el-div2)
+ (dom-dispatch (dom-query "div:nth-of-type(3)") "click" nil)
+ ))
+ (deftest "go to naked URL starting with / parses"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click go to /test/path")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+)
+
+;; ── halt (7 tests) ──
+(defsuite "hs-upstream-halt"
+ (deftest "halt bubbling only stops propagation, not default"
+ (hs-cleanup!)
+ (let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "div")))
+ (dom-set-attr _el-outer "id" "outer")
+ (dom-set-attr _el-outer "_" "on click add .outer-clicked")
+ (dom-set-attr _el-inner "id" "inner")
+ (dom-set-attr _el-inner "_" "on click halt bubbling then add .continued")
+ (dom-set-inner-html _el-inner "click me")
+ (dom-append (dom-body) _el-outer)
+ (dom-append _el-outer _el-inner)
+ (hs-activate! _el-outer)
+ (hs-activate! _el-inner)
+ (dom-dispatch (dom-query-by-id "inner") "click" nil)
+ (assert (not (dom-has-class? (dom-query-by-id "outer") "outer-clicked")))
+ (assert (not (dom-has-class? (dom-query-by-id "inner") "continued")))
+ ))
+ (deftest "halt default only prevents default, not propagation"
+ (hs-cleanup!)
+ (let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "div")))
+ (dom-set-attr _el-outer "id" "outer")
+ (dom-set-attr _el-outer "_" "on click add .outer-clicked")
+ (dom-set-attr _el-inner "id" "inner")
+ (dom-set-attr _el-inner "_" "on click halt default")
+ (dom-set-inner-html _el-inner "click me")
+ (dom-append (dom-body) _el-outer)
+ (dom-append _el-outer _el-inner)
+ (hs-activate! _el-outer)
+ (hs-activate! _el-inner)
+ (dom-dispatch (dom-query-by-id "inner") "click" nil)
+ (assert (dom-has-class? (dom-query-by-id "outer") "outer-clicked"))
+ ))
+ (deftest "halt stops execution after the halt"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click halt then add .should-not-happen")
+ (dom-set-inner-html _el-div "test")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (not (dom-has-class? _el-div "should-not-happen")))
+ ))
+ (deftest "halt the event stops propagation but continues execution"
+ (hs-cleanup!)
+ (let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "div")))
+ (dom-set-attr _el-outer "id" "outer")
+ (dom-set-attr _el-outer "_" "on click add .outer-clicked")
+ (dom-set-attr _el-inner "id" "inner")
+ (dom-set-attr _el-inner "_" "on click halt the event then add .continued")
+ (dom-set-inner-html _el-inner "click me")
+ (dom-append (dom-body) _el-outer)
+ (dom-append _el-outer _el-inner)
+ (hs-activate! _el-outer)
+ (hs-activate! _el-inner)
+ (dom-dispatch (dom-query-by-id "inner") "click" nil)
+ (assert (not (dom-has-class? (dom-query-by-id "outer") "outer-clicked")))
+ (assert (dom-has-class? (dom-query-by-id "inner") "continued"))
+ ))
+ (deftest "halt the event's stops propagation but continues execution"
+ (hs-cleanup!)
+ (let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "div")))
+ (dom-set-attr _el-outer "id" "outer")
+ (dom-set-attr _el-outer "_" "on click add .outer-clicked")
+ (dom-set-attr _el-inner "id" "inner")
+ (dom-set-attr _el-inner "_" "on click halt the event's then add .continued")
+ (dom-set-inner-html _el-inner "click me")
+ (dom-append (dom-body) _el-outer)
+ (dom-append _el-outer _el-inner)
+ (hs-activate! _el-outer)
+ (hs-activate! _el-inner)
+ (dom-dispatch (dom-query-by-id "inner") "click" nil)
+ (assert (not (dom-has-class? (dom-query-by-id "outer") "outer-clicked")))
+ (assert (dom-has-class? (dom-query-by-id "inner") "continued"))
+ ))
+ (deftest "halt works outside of event context"
+ (error "SKIP (untranslated): halt works outside of event context"))
+ (deftest "halts event propagation and default"
+ (hs-cleanup!)
+ (let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "a")))
+ (dom-set-attr _el-outer "id" "outer")
+ (dom-set-attr _el-outer "_" "on click add .outer-clicked")
+ (dom-set-attr _el-inner "id" "inner")
+ (dom-set-attr _el-inner "_" "on click halt")
+ (dom-set-attr _el-inner "href" "#shouldnot")
+ (dom-set-inner-html _el-inner "click me")
+ (dom-append (dom-body) _el-outer)
+ (dom-append _el-outer _el-inner)
+ (hs-activate! _el-outer)
+ (hs-activate! _el-inner)
+ (dom-dispatch (dom-query-by-id "inner") "click" nil)
+ (assert (not (dom-has-class? (dom-query-by-id "outer") "outer-clicked")))
+ ))
+)
+
+;; ── hide (16 tests) ──
+(defsuite "hs-upstream-hide"
+ (deftest "can configure hidden as the default hide strategy"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-get-attr _el-div "hidden") "")
+ ))
+ (deftest "can filter hide via the when clause"
+ (hs-cleanup!)
+ (let ((_el-trigger (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-trigger "id" "trigger")
+ (dom-set-attr _el-trigger "_" "on click hide in me when it matches .hideable")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-add-class _el-d1 "hideable")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-append (dom-body) _el-trigger)
+ (dom-append _el-trigger _el-d1)
+ (dom-append _el-trigger _el-d2)
+ (hs-activate! _el-trigger)
+ (dom-dispatch (dom-query-by-id "trigger") "click" nil)
+ (assert= (dom-get-style (dom-query-by-id "d1") "display") "none")
+ (assert= (dom-get-style (dom-query-by-id "d2") "display") "block")
+ ))
+ (deftest "can hide element with display:none explicitly"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide me with display")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-get-style _el-div "display") "none")
+ ))
+ (deftest "can hide element with no target"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (not (dom-visible? _el-div)))
+ ))
+ (deftest "can hide element with no target followed by command"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide add .foo")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-get-style _el-div "display") "none")
+ (assert (dom-has-class? _el-div "foo"))
+ ))
+ (deftest "can hide element with no target followed by then"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide then add .foo")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-get-style _el-div "display") "none")
+ (assert (dom-has-class? _el-div "foo"))
+ ))
+ (deftest "can hide element with no target with a with"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide with display then add .foo")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-get-style _el-div "display") "none")
+ (assert (dom-has-class? _el-div "foo"))
+ ))
+ (deftest "can hide element with opacity style literal"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide me with *opacity")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-get-style _el-div "opacity") "0")
+ ))
+ (deftest "can hide element with opacity:0"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide me with opacity")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-get-style _el-div "opacity") "0")
+ ))
+ (deftest "can hide element, with display:none by default"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-get-style _el-div "display") "none")
+ ))
+ (deftest "can hide element, with visibility:hidden"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide me with visibility")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-get-style _el-div "visibility") "hidden")
+ ))
+ (deftest "can hide other elements"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")))
+ (dom-add-class _el-div "hideme")
+ (dom-set-attr _el-div1 "_" "on click hide .hideme")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-div1)
+ (hs-activate! _el-div1)
+ (dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
+ (assert= (dom-get-style (dom-query ".hideme") "display") "none")
+ ))
+ (deftest "can hide via the hidden attribute strategy"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide me with hidden")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-get-attr _el-div "hidden") "")
+ ))
+ (deftest "can hide with custom strategy"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide with myHide")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "foo"))
+ ))
+ (deftest "can set default to custom strategy"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click hide")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? _el-div "foo"))
+ ))
+ (deftest "hide element then show element retains original display"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click 1 hide on click 2 show")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-get-style _el-div "display") "none")
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-get-style _el-div "display") "block")
+ ))
+)
+
+;; ── if (19 tests) ──
+(defsuite "hs-upstream-if"
+ (deftest "basic else branch works"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if false else put \"foo\" into me.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "basic else branch works with end"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if false else put \"foo\" into me.innerHTML end")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "basic else branch works with multiple commands"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if false put \"bar\" into me.innerHTML else log me then put \"foo\" into me.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "basic else if branch works"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if false else if true put \"foo\" into me.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "basic else if branch works with end"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if false else if true put \"foo\" into me.innerHTML end")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "basic true branch works"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if true put \"foo\" into me.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "basic true branch works with end"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if true put \"foo\" into me.innerHTML end")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "basic true branch works with multiple commands"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if true log me then put \"foo\" into me.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "basic true branch works with naked else"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if true put \"foo\" into me.innerHTML else")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "basic true branch works with naked else end"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if true put \"foo\" into me.innerHTML else end")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "false branch with a wait works"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if false else wait 10 ms then put \"foo\" into me.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "if on new line does not join w/ else"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if window.tmp then else then if window.tmp then end put \"foo\" into me then end")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "")
+ ))
+ (deftest "if properly passes execution along if child is not executed"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if false end put \"foo\" into me.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "if properly supports nested if statements and end block"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if window.tmp then put \"foo\" into me then else if not window.tmp then // do nothing then end catch e then // just here for the parsing... then")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "")
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "otherwise alias works"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if false otherwise put \"foo\" into me.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "passes the sieve test"
+ (error "SKIP (untranslated): passes the sieve test"))
+ (deftest "triple else if branch works"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if false else if false else put \"foo\" into me.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "triple else if branch works with end"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if false else if false else put \"foo\" into me.innerHTML end")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+ (deftest "true branch with a wait works"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click if true wait 10 ms then put \"foo\" into me.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "foo")
+ ))
+)
+
+;; ── increment (20 tests) ──
+(defsuite "hs-upstream-increment"
+ (deftest "can decrement a property"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click decrement my.innerHTML")
+ (dom-set-inner-html _el-div "3")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "0")
+ ))
+ (deftest "can decrement a value multiple times"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click decrement my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "-5")
+ ))
+ (deftest "can decrement a variable"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set value to 20 then decrement value by 2 then put value into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "18")
+ ))
+ (deftest "can decrement an array element"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set arr to [10, 20, 30] then decrement arr[1] then put arr[1] into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "19")
+ ))
+ (deftest "can decrement an attribute"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click decrement @value then put @value into me")
+ (dom-set-attr _el-div "value" "5")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "2")
+ ))
+ (deftest "can decrement an empty variable"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click decrement value then put value into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "-1")
+ ))
+ (deftest "can decrement an floating point numbers"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set value to 6.1 then decrement value by 5.1 then put value into me")
+ (dom-set-attr _el-div "value" "5")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "1")
+ ))
+ (deftest "can decrement by zero"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set value to 20 then decrement value by 0 then put value into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "20")
+ ))
+ (deftest "can increment a possessive property"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click increment #d1's innerHTML")
+ (dom-set-inner-html _el-d1 "5")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "6")
+ ))
+ (deftest "can increment a property"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click increment my.innerHTML")
+ (dom-set-inner-html _el-div "3")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "6")
+ ))
+ (deftest "can increment a property of expression"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click increment innerHTML of #d1")
+ (dom-set-inner-html _el-d1 "5")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "6")
+ ))
+ (deftest "can increment a style ref"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set my *opacity to 0.5 then increment *opacity by 0.25 then put *opacity into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "0.75")
+ ))
+ (deftest "can increment a value multiple times"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click increment my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "5")
+ ))
+ (deftest "can increment a variable"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set value to 20 then increment value by 2 then put value into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "22")
+ ))
+ (deftest "can increment an array element"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set arr to [10, 20, 30] then increment arr[1] then put arr[1] into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "21")
+ ))
+ (deftest "can increment an attribute"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click increment @value then put @value into me")
+ (dom-set-attr _el-div "value" "5")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "8")
+ ))
+ (deftest "can increment an empty variable"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click increment value then put value into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "1")
+ ))
+ (deftest "can increment an floating point numbers"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set value to 5.2 then increment value by 6.1 then put value into me")
+ (dom-set-attr _el-div "value" "5")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "11.3")
+ ))
+ (deftest "can increment by zero"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set value to 20 then increment value by 0 then put value into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "20")
+ ))
+ (deftest "can increment refer to result"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click increment value by 2 then put it into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "2")
+ ))
+)
+
+;; ── init (3 tests) ──
+(defsuite "hs-upstream-init"
+ (deftest "can define an init block in a script"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "init set window.foo to 42 end"))))
+ )
+ (deftest "can define an init block inline"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "init then set my.foo to 42 end on click put my.foo into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "42")
+ ))
+ (deftest "can initialize immediately"
+ (hs-cleanup!)
+ (guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "init immediately set window.bar to 42 end"))))
+ )
+)
+
+;; ── integration/htmx (9 tests) ──
+(defsuite "hs-upstream-integration/htmx"
+ (deftest "htmx content added by hyperscript is processed and can issue requests"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")) (_el-result9 (dom-create-element "div")))
+ (dom-set-attr _el-button "_" "on click put 'Click me
' after me")
+ (dom-set-inner-html _el-button "Inject")
+ (dom-set-attr _el-result9 "id" "result9")
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-result9)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (dom-dispatch (dom-query-by-id "injected") "click" nil)
+ ))
+ (deftest "hyperscript can cancel htmx request via halt"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")) (_el-out5 (dom-create-element "div")))
+ (dom-set-attr _el-button "_" "on htmx:before:request halt the event")
+ (dom-set-attr _el-button "hx-get" "/test5")
+ (dom-set-attr _el-button "hx-target" "#out5")
+ (dom-set-inner-html _el-button "Load")
+ (dom-set-attr _el-out5 "id" "out5")
+ (dom-set-inner-html _el-out5 "original")
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-out5)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "out5")) "original")
+ ))
+ (deftest "hyperscript can modify form values before htmx submit"
+ (hs-cleanup!)
+ (let ((_el-form (dom-create-element "form")) (_el-hidden4 (dom-create-element "input")) (_el-button (dom-create-element "button")) (_el-result4 (dom-create-element "div")))
+ (dom-set-attr _el-form "_" "on submit set #hidden4's value to 'injected'")
+ (dom-set-attr _el-form "hx-post" "/test4")
+ (dom-set-attr _el-form "hx-target" "#result4")
+ (dom-set-attr _el-hidden4 "id" "hidden4")
+ (dom-set-attr _el-hidden4 "type" "hidden")
+ (dom-set-attr _el-hidden4 "name" "data")
+ (dom-set-attr _el-hidden4 "value" "")
+ (dom-set-attr _el-button "type" "submit")
+ (dom-set-inner-html _el-button "Submit")
+ (dom-set-attr _el-result4 "id" "result4")
+ (dom-append (dom-body) _el-form)
+ (dom-append _el-form _el-hidden4)
+ (dom-append _el-form _el-button)
+ (dom-append (dom-body) _el-result4)
+ (hs-activate! _el-form)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "hyperscript can react to htmx:after:request"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")) (_el-out8 (dom-create-element "div")) (_el-status8 (dom-create-element "span")))
+ (dom-set-attr _el-button "_" "on htmx:after:request put 'finished' into #status8")
+ (dom-set-attr _el-button "hx-get" "/test8")
+ (dom-set-attr _el-button "hx-target" "#out8")
+ (dom-set-inner-html _el-button "Send")
+ (dom-set-attr _el-out8 "id" "out8")
+ (dom-set-attr _el-status8 "id" "status8")
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-out8)
+ (dom-append (dom-body) _el-status8)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "hyperscript can trigger htmx requests via send"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-out2 (dom-create-element "div")))
+ (dom-set-attr _el-div "hx-get" "/test2")
+ (dom-set-attr _el-div "hx-trigger" "doLoad")
+ (dom-set-attr _el-div "hx-target" "#out2")
+ (dom-set-inner-html _el-div "waiting")
+ (dom-set-attr _el-button "_" "on click send doLoad to the previous ")
+ (dom-set-inner-html _el-button "Go")
+ (dom-set-attr _el-out2 "id" "out2")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-out2)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "hyperscript element-scoped vars persist across htmx swaps"
+ (hs-cleanup!)
+ (let ((_el-outer7 (dom-create-element "div")) (_el-clicker7 (dom-create-element "button")) (_el-swapper7 (dom-create-element "button")) (_el-target7 (dom-create-element "div")) (_el-count7 (dom-create-element "span")))
+ (dom-set-attr _el-outer7 "id" "outer7")
+ (dom-set-attr _el-outer7 "_" "set :counter to 0 then on click from #clicker7 increment :counter then put :counter into #count7")
+ (dom-set-attr _el-clicker7 "id" "clicker7")
+ (dom-set-inner-html _el-clicker7 "Count")
+ (dom-set-attr _el-swapper7 "id" "swapper7")
+ (dom-set-attr _el-swapper7 "hx-get" "/test7")
+ (dom-set-attr _el-swapper7 "hx-target" "#target7")
+ (dom-set-attr _el-swapper7 "hx-swap" "innerHTML")
+ (dom-set-inner-html _el-swapper7 "Swap")
+ (dom-set-attr _el-target7 "id" "target7")
+ (dom-set-inner-html _el-target7 "original")
+ (dom-set-attr _el-count7 "id" "count7")
+ (dom-set-inner-html _el-count7 "0")
+ (dom-append (dom-body) _el-outer7)
+ (dom-append _el-outer7 _el-clicker7)
+ (dom-append _el-outer7 _el-swapper7)
+ (dom-append _el-outer7 _el-target7)
+ (dom-append _el-outer7 _el-count7)
+ (hs-activate! _el-outer7)
+ (dom-dispatch (dom-query-by-id "clicker7") "click" nil)
+ (dom-dispatch (dom-query-by-id "swapper7") "click" nil)
+ (dom-dispatch (dom-query-by-id "clicker7") "click" nil)
+ ))
+ (deftest "hyperscript responds to htmx swap events"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")) (_el-target1 (dom-create-element "div")) (_el-status1 (dom-create-element "span")))
+ (dom-set-attr _el-button "_" "on htmx:after:swap put 'swapped!' into #status1")
+ (dom-set-attr _el-button "hx-get" "/test1")
+ (dom-set-attr _el-button "hx-target" "#target1")
+ (dom-set-inner-html _el-button "Load")
+ (dom-set-attr _el-target1 "id" "target1")
+ (dom-set-attr _el-status1 "id" "status1")
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-target1)
+ (dom-append (dom-body) _el-status1)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "hyperscript responds to htmx:after:settle on target"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")) (_el-target6 (dom-create-element "div")))
+ (dom-set-attr _el-button "hx-get" "/test6")
+ (dom-set-attr _el-button "hx-target" "#target6")
+ (dom-set-inner-html _el-button "Load")
+ (dom-set-attr _el-target6 "id" "target6")
+ (dom-set-attr _el-target6 "_" "on htmx:after:settle add .done to me")
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-target6)
+ (hs-activate! _el-target6)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "hyperscript works on htmx-swapped content"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")) (_el-container3 (dom-create-element "div")))
+ (dom-set-attr _el-button "hx-get" "/test3")
+ (dom-set-attr _el-button "hx-target" "#container3")
+ (dom-set-inner-html _el-button "Load")
+ (dom-set-attr _el-container3 "id" "container3")
+ (dom-append (dom-body) _el-button)
+ (dom-append (dom-body) _el-container3)
+ (dom-dispatch _el-button "click" nil)
+ (dom-dispatch (dom-query-by-id "inner") "click" nil)
+ ))
+)
+
+;; ── js (11 tests) ──
+(defsuite "hs-upstream-js"
+ (deftest "can access values from _hyperscript"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set t to true then js(t) window.testSuccess = t end")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "can deal with empty input list"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click js() window.testSuccess = true end")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "can do both of the above"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click set a to 1 then js(a) return a + 1 end put it into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "2")
+ ))
+ (deftest "can expose functions"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-append (dom-body) _el-script)
+ ))
+ (deftest "can expose globals"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-append (dom-body) _el-script)
+ ))
+ (deftest "can hide functions"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-append (dom-body) _el-script)
+ ))
+ (deftest "can return values to _hyperscript"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click js return 'test success' end put it into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "test success")
+ ))
+ (deftest "can run js"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click js window.testSuccess = true end")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "can run js at the top level"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-append (dom-body) _el-script)
+ ))
+ (deftest "does not expose variables"
+ (hs-cleanup!)
+ (let ((_el-script (dom-create-element "script")))
+ (dom-set-attr _el-script "type" "text/hyperscript")
+ (dom-append (dom-body) _el-script)
+ ))
+ (deftest "handles rejected promises without hanging"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click js return Promise.reject(\"boom\") end catch e put e into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ (assert= (dom-text-content _el-div) "boom")
+ ))
+)
+
+;; ── live (23 tests) ──
+(defsuite "hs-upstream-live"
+ (deftest "append triggers live block"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live put $items.join(', ') into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "array + still works with live"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live put $items.join(', ') into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "bind and live on same element do not interfere"
+ (hs-cleanup!)
+ (let ((_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
+ (dom-set-attr _el-input "_" "bind $username to me end live set my @data-mirror to $username")
+ (dom-set-attr _el-input "type" "text")
+ (dom-set-attr _el-input "value" "alice")
+ (dom-set-attr _el-span "_" "when $username changes put it into me")
+ (dom-append (dom-body) _el-input)
+ (dom-append (dom-body) _el-span)
+ (hs-activate! _el-input)
+ (hs-activate! _el-span)
+ (dom-set-prop _el-input "value" "bob")
+ (dom-dispatch _el-input "input" nil)
+ ))
+ (deftest "block form cascades inter-dependent commands"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live then set $subtotal to ($price * $qty) then set $total to ($subtotal + $tax) then end when $total changes put it into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "block form re-runs all commands when any dependency changes"
+ (hs-cleanup!)
+ (let ((_el-w (dom-create-element "span")) (_el-h (dom-create-element "span")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-w "id" "w")
+ (dom-set-attr _el-w "_" "when $doubleWidth changes put it into me")
+ (dom-set-attr _el-h "id" "h")
+ (dom-set-attr _el-h "_" "when $doubleHeight changes put it into me")
+ (dom-set-attr _el-div "_" "live then set $doubleWidth to ($width * 2) then set $doubleHeight to ($height * 2) then end")
+ (dom-append (dom-body) _el-w)
+ (dom-append (dom-body) _el-h)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-w)
+ (hs-activate! _el-h)
+ (hs-activate! _el-div)
+ ))
+ (deftest "conditional branch only tracks the active dependency"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live then if $showFirst put $firstName into me else put $lastName into me end end")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "derives a variable from a computed expression"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live set $total to ($price * $qty) end when $total changes put it into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "effects stop when element is removed from DOM"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live put $message into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "live and when on same element do not interfere"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live set my @data-status to $status end when $status changes put 'Status: ' + it into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "multiple live on same element work independently"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live set my @data-name to $firstName end live set my @data-age to $age")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "property change on object in array triggers live re-render"
+ (hs-cleanup!)
+ (let ((_el-people-tmpl (dom-create-element "script")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-people-tmpl "id" "people-tmpl")
+ (dom-set-attr _el-people-tmpl "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-people-tmpl "#for p in people
+${p.name}
+#end")
+ (dom-set-attr _el-div "_" "live render #people-tmpl with people: $people then put it into my.innerHTML end")
+ (dom-append (dom-body) _el-people-tmpl)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "push object then modify its property both trigger live"
+ (hs-cleanup!)
+ (let ((_el-items-tmpl (dom-create-element "script")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-items-tmpl "id" "items-tmpl")
+ (dom-set-attr _el-items-tmpl "type" "text/hyperscript-template")
+ (dom-set-inner-html _el-items-tmpl "#for item in items
+${item.label}
+#end")
+ (dom-set-attr _el-div "_" "live render #items-tmpl with items: $items then put it into my.innerHTML end")
+ (dom-append (dom-body) _el-items-tmpl)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "push via call triggers live block"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live put $items.join(', ') into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "push via pseudo-command triggers live block"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live put $items.join(', ') into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "puts a computed dollar amount into the DOM"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live put '$' + ($price * $qty) into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "reactive effects are stopped on cleanup"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "live put $count into me")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ ))
+ (deftest "separate live statements create independent effects"
+ (hs-cleanup!)
+ (let ((_el-w (dom-create-element "span")) (_el-h (dom-create-element "span")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-w "id" "w")
+ (dom-set-attr _el-w "_" "when $doubleWidth changes put it into me")
+ (dom-set-attr _el-h "id" "h")
+ (dom-set-attr _el-h "_" "when $doubleHeight changes put it into me")
+ (dom-set-attr _el-div "_" "live set $doubleWidth to ($width * 2) end live set $doubleHeight to ($height * 2)")
+ (dom-append (dom-body) _el-w)
+ (dom-append (dom-body) _el-h)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-w)
+ (hs-activate! _el-h)
+ (hs-activate! _el-div)
+ ))
+ (deftest "set property still works with live"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live put $obj.name into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "sets a style reactively"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live set *opacity to $opacity")
+ (dom-set-inner-html _el-div "visible")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "sets an attribute reactively"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live set my @data-theme to $theme")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "toggles a class based on a boolean variable"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live then if $isActive add .active to me else remove .active from me end end")
+ (dom-set-inner-html _el-div "test")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "toggles display style based on a boolean variable"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live then if $isVisible set *display to 'block' else set *display to 'none' end end")
+ (dom-set-inner-html _el-div "content")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "updates DOM text reactively with put"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "live put 'hello ' + $greeting into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+)
+
+;; ── log (4 tests) ──
+(defsuite "hs-upstream-log"
+ (deftest "can log multiple items"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click log me, my")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "can log multiple items with debug"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click log me, my with console.debug")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "can log multiple items with error"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click log me, my with console.error")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "can log single item"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click log me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+)
+
+;; ── make (8 tests) ──
+(defsuite "hs-upstream-make"
+ (deftest "can make elements"
+ (error "SKIP (untranslated): can make elements"))
+ (deftest "can make elements with id and classes"
+ (error "SKIP (untranslated): can make elements with id and classes"))
+ (deftest "can make named objects"
+ (error "SKIP (untranslated): can make named objects"))
+ (deftest "can make named objects w/ global scope"
+ (error "SKIP (untranslated): can make named objects w/ global scope"))
+ (deftest "can make named objects with arguments"
+ (error "SKIP (untranslated): can make named objects with arguments"))
+ (deftest "can make objects"
+ (error "SKIP (untranslated): can make objects"))
+ (deftest "can make objects with arguments"
+ (error "SKIP (untranslated): can make objects with arguments"))
+ (deftest "creates a div by default"
+ (error "SKIP (untranslated): creates a div by default"))
+)
+
+;; ── measure (6 tests) ──
+(defsuite "hs-upstream-measure"
+ (deftest "can assign measurements to locals"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click measure my x,y,left,top,right,bottom then set window.measurement to {left:left,top:top,right:right,bottom:bottom}")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "can measure all the supported properties"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click measure x,y,left,top,right,bottom,width,height,bounds,scrollLeft,scrollTop,scrollLeftMax,scrollTopMax,scrollWidth,scrollHeight,scroll")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "can measure another element"
+ (hs-cleanup!)
+ (let ((_el-other (dom-create-element "div")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-other "id" "other")
+ (dom-set-attr _el-other "style" "all: initial; position: fixed; top: 89px")
+ (dom-set-attr _el-div "_" "on click measure #other then set window.measurement to it")
+ (dom-append (dom-body) _el-other)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
+ ))
+ (deftest "can measure me"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click measure me then set window.measurement to it")
+ (dom-set-attr _el-div "style" "all: initial; position: fixed; top: 89px")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "can measure with of syntax"
+ (hs-cleanup!)
+ (let ((_el-other (dom-create-element "div")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-other "id" "other")
+ (dom-set-attr _el-other "style" "all: initial; position: fixed; top: 89px")
+ (dom-set-attr _el-div "_" "on click measure top of #other then set window.measurement to {top: top}")
+ (dom-append (dom-body) _el-other)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
+ ))
+ (deftest "can measure with possessive syntax"
+ (hs-cleanup!)
+ (let ((_el-other (dom-create-element "div")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-other "id" "other")
+ (dom-set-attr _el-other "style" "all: initial; position: fixed; top: 89px")
+ (dom-set-attr _el-div "_" "on click measure #other's top then set window.measurement to {top: top}")
+ (dom-append (dom-body) _el-other)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
+ ))
+)
+
+;; ── morph (10 tests) ──
+(defsuite "hs-upstream-morph"
+ (deftest "basic morph updates content"
+ (hs-cleanup!)
+ (let ((_el-target (dom-create-element "div")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-target "id" "target")
+ (dom-set-inner-html _el-target "old")
+ (dom-set-attr _el-button "_" "on click morph #target to \"new
\"")
+ (dom-set-inner-html _el-button "go")
+ (dom-append (dom-body) _el-target)
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "target")) "new")
+ ))
+ (deftest "morph adds new children"
+ (hs-cleanup!)
+ (let ((_el-target (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-go (dom-create-element "button")))
+ (dom-set-attr _el-target "id" "target")
+ (dom-set-inner-html _el-span "first")
+ (dom-set-attr _el-go "id" "go")
+ (dom-set-attr _el-go "_" "on click morph #target to 'firstsecond
'")
+ (dom-set-inner-html _el-go "go")
+ (dom-append (dom-body) _el-target)
+ (dom-append _el-target _el-span)
+ (dom-append (dom-body) _el-go)
+ (hs-activate! _el-go)
+ (dom-dispatch (dom-query-by-id "go") "click" nil)
+ ))
+ (deftest "morph cleans up removed hyperscript elements"
+ (hs-cleanup!)
+ (let ((_el-target (dom-create-element "div")) (_el-child (dom-create-element "div")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-target "id" "target")
+ (dom-set-attr _el-child "id" "child")
+ (dom-set-attr _el-child "_" "on click put \"alive\" into me")
+ (dom-set-inner-html _el-child "child")
+ (dom-set-attr _el-button "_" "on click morph #target to \"\"")
+ (dom-set-inner-html _el-button "go")
+ (dom-append (dom-body) _el-target)
+ (dom-append _el-target _el-child)
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-child)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "morph initializes hyperscript on new elements"
+ (hs-cleanup!)
+ (let ((_el-target (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-go (dom-create-element "button")))
+ (dom-set-attr _el-target "id" "target")
+ (dom-set-inner-html _el-p "old")
+ (dom-set-attr _el-go "id" "go")
+ (dom-set-attr _el-go "_" "on click morph #target to ''")
+ (dom-set-inner-html _el-go "go")
+ (dom-append (dom-body) _el-target)
+ (dom-append _el-target _el-p)
+ (dom-append (dom-body) _el-go)
+ (hs-activate! _el-go)
+ (dom-dispatch (dom-query-by-id "go") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "inner")) "new")
+ (dom-dispatch (dom-query-by-id "inner") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "inner")) "clicked")
+ ))
+ (deftest "morph preserves element identity"
+ (hs-cleanup!)
+ (let ((_el-target (dom-create-element "div")) (_el-go (dom-create-element "button")))
+ (dom-set-attr _el-target "id" "target")
+ (dom-set-inner-html _el-target "old")
+ (dom-set-attr _el-go "id" "go")
+ (dom-set-attr _el-go "_" "on click morph #target to \"new
\"")
+ (dom-set-inner-html _el-go "go")
+ (dom-append (dom-body) _el-target)
+ (dom-append (dom-body) _el-go)
+ (hs-activate! _el-go)
+ (dom-dispatch (dom-query-by-id "go") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "target")) "new")
+ ))
+ (deftest "morph preserves matched child identity"
+ (hs-cleanup!)
+ (let ((_el-target (dom-create-element "div")) (_el-child (dom-create-element "div")) (_el-go (dom-create-element "button")))
+ (dom-set-attr _el-target "id" "target")
+ (dom-set-attr _el-child "id" "child")
+ (dom-set-inner-html _el-child "old")
+ (dom-set-attr _el-go "id" "go")
+ (dom-set-attr _el-go "_" "on click morph #target to \"\"")
+ (dom-set-inner-html _el-go "go")
+ (dom-append (dom-body) _el-target)
+ (dom-append _el-target _el-child)
+ (dom-append (dom-body) _el-go)
+ (hs-activate! _el-go)
+ (dom-dispatch (dom-query-by-id "go") "click" nil)
+ ))
+ (deftest "morph removes old children"
+ (hs-cleanup!)
+ (let ((_el-target (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-span2 (dom-create-element "span")) (_el-go (dom-create-element "button")))
+ (dom-set-attr _el-target "id" "target")
+ (dom-set-inner-html _el-span "first")
+ (dom-set-inner-html _el-span2 "second")
+ (dom-set-attr _el-go "id" "go")
+ (dom-set-attr _el-go "_" "on click morph #target to 'first
'")
+ (dom-set-inner-html _el-go "go")
+ (dom-append (dom-body) _el-target)
+ (dom-append _el-target _el-span)
+ (dom-append _el-target _el-span2)
+ (dom-append (dom-body) _el-go)
+ (hs-activate! _el-go)
+ (dom-dispatch (dom-query-by-id "go") "click" nil)
+ ))
+ (deftest "morph reorders children by id"
+ (hs-cleanup!)
+ (let ((_el-target (dom-create-element "div")) (_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-target "id" "target")
+ (dom-set-attr _el-a "id" "a")
+ (dom-set-inner-html _el-a "A")
+ (dom-set-attr _el-b "id" "b")
+ (dom-set-inner-html _el-b "B")
+ (dom-set-attr _el-button "_" "on click morph #target to \"\"")
+ (dom-set-inner-html _el-button "go")
+ (dom-append (dom-body) _el-target)
+ (dom-append _el-target _el-a)
+ (dom-append _el-target _el-b)
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ ))
+ (deftest "morph updates attributes"
+ (hs-cleanup!)
+ (let ((_el-target (dom-create-element "div")) (_el-button (dom-create-element "button")))
+ (dom-set-attr _el-target "id" "target")
+ (dom-add-class _el-target "old")
+ (dom-set-inner-html _el-target "content")
+ (dom-set-attr _el-button "_" "on click morph #target to \"content
\"")
+ (dom-set-inner-html _el-button "go")
+ (dom-append (dom-body) _el-target)
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (assert (dom-has-class? (dom-query-by-id "target") "new"))
+ ))
+ (deftest "morph with variable content"
+ (hs-cleanup!)
+ (let ((_el-target (dom-create-element "div")) (_el-go (dom-create-element "button")))
+ (dom-set-attr _el-target "id" "target")
+ (dom-set-inner-html _el-target "original")
+ (dom-set-attr _el-go "id" "go")
+ (dom-set-attr _el-go "_" "on click set content to \"morphed
\" then morph #target to content")
+ (dom-set-inner-html _el-go "go")
+ (dom-append (dom-body) _el-target)
+ (dom-append (dom-body) _el-go)
+ (hs-activate! _el-go)
+ (dom-dispatch (dom-query-by-id "go") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "target")) "morphed")
+ ))
+)
+
+;; ── on (70 tests) ──
+(defsuite "hs-upstream-on"
+ (deftest "async basic finally blocks work"
+ (error "SKIP (skip-list): async basic finally blocks work"))
+ (deftest "async exceptions don't kill the event queue"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")))
+ (dom-set-attr _el-button "_" "on click increment :x then if :x is 1 then wait 1ms then throw \"bar\" otherwise then put \"success\" into me end catch e then put e into me")
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content _el-button) "success")
+ ))
+ (deftest "async exceptions in finally block don't kill the event queue"
+ (error "SKIP (skip-list): async exceptions in finally block don't kill the event queue"))
+ (deftest "async finally blocks work when exception thrown in catch"
+ (error "SKIP (skip-list): async finally blocks work when exception thrown in catch"))
+ (deftest "basic finally blocks work"
+ (error "SKIP (skip-list): basic finally blocks work"))
+ (deftest "can be in a top level script tag"
+ (error "SKIP (skip-list): can be in a top level script tag"))
+ (deftest "can catch async top-level exceptions"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")))
+ (dom-set-attr _el-button "_" "on click wait 1ms then throw \"bar\" catch e put e into me")
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content _el-button) "bar")
+ ))
+ (deftest "can catch exceptions thrown in hyperscript functions"
+ (error "SKIP (skip-list): can catch exceptions thrown in hyperscript functions"))
+ (deftest "can catch exceptions thrown in js functions"
+ (error "SKIP (skip-list): can catch exceptions thrown in js functions"))
+ (deftest "can catch top-level exceptions"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")))
+ (dom-set-attr _el-button "_" "on click throw \"bar\" catch e put e into me")
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content _el-button) "bar")
+ ))
+ (deftest "can click after a positive event filter"
+ (hs-cleanup!)
+ (let ((_el-pf (dom-create-element "div")))
+ (dom-set-attr _el-pf "id" "pf")
+ (dom-set-attr _el-pf "_" "on foo(bar)[bar] put \"triggered\" into my.innerHTML")
+ (dom-append (dom-body) _el-pf)
+ (hs-activate! _el-pf)
+ ))
+ (deftest "can filter events based on count"
+ (error "SKIP (skip-list): can filter events based on count"))
+ (deftest "can filter events based on count range"
+ (error "SKIP (skip-list): can filter events based on count range"))
+ (deftest "can filter events based on unbounded count range"
+ (error "SKIP (skip-list): can filter events based on unbounded count range"))
+ (deftest "can fire an event on load"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on load put \"Loaded\" into my.innerHTML")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ ))
+ (deftest "can handle an or after a from clause"
+ (error "SKIP (skip-list): can handle an or after a from clause"))
+ (deftest "can have a simple event filter"
+ (hs-cleanup!)
+ (let ((_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on click[false] log event then put \"Clicked\" into my.innerHTML")
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-d1)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "d1")) "")
+ ))
+ (deftest "can have multiple event handlers"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on foo put increment() into my.innerHTML end on bar put increment() into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "can have multiple event handlers, no end"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on foo put increment() into my.innerHTML on bar put increment() into my.innerHTML")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "can ignore when target doesn't exist"
+ (error "SKIP (skip-list): can ignore when target doesn't exist"))
+ (deftest "can invoke on multiple events"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click or foo call increment()")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch _el-div "click" nil)
+ ))
+ (deftest "can listen for attribute mutations"
+ (error "SKIP (skip-list): can listen for attribute mutations"))
+ (deftest "can listen for attribute mutations on other elements"
+ (error "SKIP (skip-list): can listen for attribute mutations on other elements"))
+ (deftest "can listen for characterData mutation filter out other mutations"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on mutation of characterData put \"Mutated\" into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "can listen for childList mutation filter out other mutations"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on mutation of childList put \"Mutated\" into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "can listen for childList mutations"
+ (error "SKIP (skip-list): can listen for childList mutations"))
+ (deftest "can listen for events in another element (lazy)"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click in #d1 put it into window.tmp")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d2 "id" "d2")
+ (dom-append (dom-body) _el-div)
+ (dom-append _el-div _el-d1)
+ (dom-append _el-div _el-d2)
+ (hs-activate! _el-div)
+ (dom-dispatch (dom-query-by-id "d1") "click" nil)
+ ))
+ (deftest "can listen for general mutations"
+ (error "SKIP (skip-list): can listen for general mutations"))
+ (deftest "can listen for multiple mutations"
+ (error "SKIP (skip-list): can listen for multiple mutations"))
+ (deftest "can listen for multiple mutations 2"
+ (error "SKIP (skip-list): can listen for multiple mutations 2"))
+ (deftest "can listen for specific attribute mutations"
+ (error "SKIP (skip-list): can listen for specific attribute mutations"))
+ (deftest "can listen for specific attribute mutations and filter out other attribute mutations"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on mutation of @bar put \"Mutated\" into me")
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ ))
+ (deftest "can mix ranges"
+ (error "SKIP (skip-list): can mix ranges"))
+ (deftest "can pick detail fields out by name"
+ (error "SKIP (skip-list): can pick detail fields out by name"))
+ (deftest "can pick event properties out by name"
+ (error "SKIP (skip-list): can pick event properties out by name"))
+ (deftest "can queue all events"
+ (hs-cleanup!)
+ (let ((_el-qa (dom-create-element "div")))
+ (dom-set-attr _el-qa "id" "qa")
+ (dom-set-attr _el-qa "_" "on foo queue all wait for bar then call increment()")
+ (dom-append (dom-body) _el-qa)
+ (hs-activate! _el-qa)
+ ))
+ (deftest "can queue events"
+ (hs-cleanup!)
+ (let ((_el-qe (dom-create-element "div")))
+ (dom-set-attr _el-qe "id" "qe")
+ (dom-set-attr _el-qe "_" "on foo wait for bar then call increment()")
+ (dom-append (dom-body) _el-qe)
+ (hs-activate! _el-qe)
+ ))
+ (deftest "can queue first event"
+ (hs-cleanup!)
+ (let ((_el-qf (dom-create-element "div")))
+ (dom-set-attr _el-qf "id" "qf")
+ (dom-set-attr _el-qf "_" "on foo queue first wait for bar then call increment()")
+ (dom-append (dom-body) _el-qf)
+ (hs-activate! _el-qf)
+ ))
+ (deftest "can queue last event"
+ (hs-cleanup!)
+ (let ((_el-ql (dom-create-element "div")))
+ (dom-set-attr _el-ql "id" "ql")
+ (dom-set-attr _el-ql "_" "on foo queue last wait for bar then call increment()")
+ (dom-append (dom-body) _el-ql)
+ (hs-activate! _el-ql)
+ ))
+ (deftest "can refer to event detail properties directly in filter"
+ (hs-cleanup!)
+ (let ((_el-fd (dom-create-element "div")))
+ (dom-set-attr _el-fd "id" "fd")
+ (dom-set-attr _el-fd "_" "on example[foo] increment @count then put it into me")
+ (dom-append (dom-body) _el-fd)
+ (hs-activate! _el-fd)
+ ))
+ (deftest "can refer to event properties directly in filter"
+ (hs-cleanup!)
+ (let ((_el-t1 (dom-create-element "div")))
+ (dom-set-attr _el-t1 "id" "t1")
+ (dom-set-attr _el-t1 "_" "on click[buttons==0] log event then put \"Clicked\" into my.innerHTML")
+ (dom-append (dom-body) _el-t1)
+ (hs-activate! _el-t1)
+ (dom-dispatch (dom-query-by-id "t1") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "t1")) "Clicked")
+ (dom-dispatch (dom-query-by-id "t2") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "t2")) "")
+ (dom-dispatch (dom-query-by-id "t3") "click" nil)
+ (assert= (dom-text-content (dom-query-by-id "t3")) "")
+ ))
+ (deftest "can respond to events on other elements"
+ (hs-cleanup!)
+ (let ((_el-bar (dom-create-element "div")) (_el-div (dom-create-element "div")))
+ (dom-set-attr _el-bar "id" "bar")
+ (dom-set-attr _el-div "_" "on click from #bar add .clicked")
+ (dom-append (dom-body) _el-bar)
+ (dom-append (dom-body) _el-div)
+ (hs-activate! _el-div)
+ (dom-dispatch (dom-query-by-id "bar") "click" nil)
+ ))
+ (deftest "can respond to events with colons in names"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click send example:event to #d1")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on example:event add .called")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-div)
+ (hs-activate! _el-d1)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? (dom-query-by-id "d1") "called"))
+ ))
+ (deftest "can respond to events with dots in names"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click send example.event to #d1")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on example.event add .called")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-div)
+ (hs-activate! _el-d1)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? (dom-query-by-id "d1") "called"))
+ ))
+ (deftest "can respond to events with minus in names"
+ (hs-cleanup!)
+ (let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
+ (dom-set-attr _el-div "_" "on click send \"a-b\" to #d1")
+ (dom-set-attr _el-d1 "id" "d1")
+ (dom-set-attr _el-d1 "_" "on \"a-b\" add .called")
+ (dom-append (dom-body) _el-div)
+ (dom-append (dom-body) _el-d1)
+ (hs-activate! _el-div)
+ (hs-activate! _el-d1)
+ (dom-dispatch _el-div "click" nil)
+ (assert (dom-has-class? (dom-query-by-id "d1") "called"))
+ ))
+ (deftest "caught exceptions do not trigger 'exception' event"
+ (hs-cleanup!)
+ (let ((_el-button (dom-create-element "button")))
+ (dom-set-attr _el-button "_" "on click put \"foo\" into me then throw \"bar\" catch e log e on exception(error) put error into me")
+ (dom-append (dom-body) _el-button)
+ (hs-activate! _el-button)
+ (dom-dispatch _el-button "click" nil)
+ (assert= (dom-text-content _el-button) "foo")
+ ))
+ (deftest "debounced at