nx_ttt_bits_up_e2e.nx source
↩ module page · 106 lines · 4867 B
1// nx_ttt_bits_up_e2e.nx -- REAL bits-up end-to-end test driving the
2// actual nx_tictactoe game through the 5 bits-up test driver primitives.
3// Equivalent to nishi-pages/scripts/_bits_up_check.mjs but 100% NishiLang.
4//
5// What this proves: the .mjs Playwright harness can be retired in favor
6// of bits-up substrate. No JS, no Playwright, no browser engine -- the
7// game's actual state machine + AI + render are driven through the same
8// click-dispatch lifecycle the browser uses, with HTML emitted via fd 99
9// captured into a buffer, queried by the DOM primitives, asserted by
10// the test harness DSL.
11
12import "nx_test_harness.nx"
13import "nx_game_harness.nx"
14import "nx_dom_query.nx"
15import "nx_dom_click.nx"
16import "nx_tictactoe.nx"
17import "nx_tictactoe_render.nx"
18const K_MAGIC_65536: i64 = 65536
19
20func _ttt_e2e_strlen(s: *u8) -> i64 {
21 var n: i64 = 0
22 while s[n] != 0 { n = n + 1 }
23 return n
24}
25
26func main() -> i64 {
27 nx_test_init()
28
29 // ---- Cycle 1: fresh game ----
30 let state: *i64 = nx_ttt_init()
31 let buf: *u8 = sys_mmap(K_MAGIC_65536)
32
33 let cap1: *NxGameCapture = nx_capture_start()
34 nx_ttt_render(state)
35 let n1: i64 = nx_capture_end(cap1, buf, K_MAGIC_65536 as i64)
36
37 nx_test_expect_eq("real game cycle 1: bytes captured > 0" as *u8,
38 n1 > 0, 1 as i64)
39 nx_test_expect_class_count("real game: 9 .cell elements" as *u8,
40 buf, n1, "cell" as *u8, 9 as i64)
41 // 9 cell buttons + 1 reset button = 10 buttons in controls.
42 nx_test_expect_tag_count("real game: 10 <button> tags (9 cells + reset)" as *u8,
43 buf, n1, "button" as *u8, 10 as i64)
44 nx_test_expect_contains("real game: status shows player turn" as *u8,
45 buf, n1, "Your move" as *u8)
46 nx_test_expect_not_contains("real game: no X yet" as *u8,
47 buf, n1, ">X<" as *u8)
48 nx_test_expect_not_contains("real game: no O yet" as *u8,
49 buf, n1, ">O<" as *u8)
50
51 // ---- Click cell 4 (center) -- bits-up click intent extraction ----
52 let intent: *NxClickIntent = nx_dom_click_intent(buf, n1,
53 "data-cell-idx" as *u8,
54 "4" as *u8)
55 nx_test_expect_click("click intent for cell 4 (action=1, arg0=4)" as *u8,
56 intent, 1 as i64, 4 as i64)
57 // Dispatch through the REAL game's action function.
58 nx_ttt_action(state, intent.action, intent.arg0, intent.arg1,
59 intent.arg2, intent.arg3)
60
61 // ---- Cycle 2: after click ----
62 let cap2: *NxGameCapture = nx_capture_start()
63 nx_ttt_render(state)
64 let n2: i64 = nx_capture_end(cap2, buf, K_MAGIC_65536 as i64)
65
66 nx_test_expect_contains("after click: X visible somewhere" as *u8,
67 buf, n2, ">X<" as *u8)
68 nx_test_expect_contains("after click: O visible (AI played)" as *u8,
69 buf, n2, ">O<" as *u8)
70
71 // Verify the X landed at cell 4 specifically by querying the DOM.
72 let cell4: *NxDomQueryResult = nx_dom_find_attr_eq(buf, n2,
73 "data-cell-idx" as *u8,
74 "4" as *u8)
75 nx_test_expect_eq("cell-4 element still found in DOM" as *u8,
76 cell4.found, 1 as i64)
77
78 // State-hash determinism: the same state produces the same hash.
79 let h1: i64 = nx_ttt_state_hash(state)
80 let h2: i64 = nx_ttt_state_hash(state)
81 nx_test_expect_eq("state hash is deterministic for same state" as *u8,
82 h1, h2)
83
84 // ---- Click the reset button (data-action=2) ----
85 let reset_intent: *NxClickIntent = nx_dom_click_intent(buf, n2,
86 "data-action" as *u8,
87 "2" as *u8)
88 nx_test_expect_click("reset button found (action=2, arg0=0)" as *u8,
89 reset_intent, 2 as i64, 0 as i64)
90 nx_ttt_action(state, reset_intent.action, reset_intent.arg0,
91 reset_intent.arg1, reset_intent.arg2, reset_intent.arg3)
92
93 // ---- Cycle 3: after reset, should be clean board ----
94 let cap3: *NxGameCapture = nx_capture_start()
95 nx_ttt_render(state)
96 let n3: i64 = nx_capture_end(cap3, buf, K_MAGIC_65536 as i64)
97
98 nx_test_expect_class_count("after reset: 9 .cell elements" as *u8,
99 buf, n3, "cell" as *u8, 9 as i64)
100 nx_test_expect_not_contains("after reset: no X" as *u8,
101 buf, n3, ">X<" as *u8)
102 nx_test_expect_not_contains("after reset: no O" as *u8,
103 buf, n3, ">O<" as *u8)
104
105 return nx_test_summary()
106}