hokusai-zero 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Dockerfile +26 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +91 -0
- data/LICENSE +21 -0
- data/README.md +28 -0
- data/ast/genheader +3 -0
- data/ast/include/hashmap.c +1151 -0
- data/ast/include/hashmap.c.license +20 -0
- data/ast/include/hashmap.h +54 -0
- data/ast/src/core/ast.c +448 -0
- data/ast/src/core/ast.h +259 -0
- data/ast/src/core/common.h +24 -0
- data/ast/src/core/component.c +85 -0
- data/ast/src/core/component.h +35 -0
- data/ast/src/core/hml.c +665 -0
- data/ast/src/core/hml.h +11 -0
- data/ast/src/core/input.c +458 -0
- data/ast/src/core/input.h +118 -0
- data/ast/src/core/style.c +101 -0
- data/ast/src/core/style.h +41 -0
- data/ast/src/core/text.c +784 -0
- data/ast/src/core/text.h +93 -0
- data/ast/src/core/util.c +140 -0
- data/ast/src/core/util.h +48 -0
- data/ast/src/hokusai.c +6 -0
- data/ast/src/hokusai.h +6 -0
- data/ast/test/fixtures/test.ui +13 -0
- data/ast/test/greatest.h +1266 -0
- data/ast/test/hokusai.c +14 -0
- data/ast/test/parser.c +234 -0
- data/ast/test/text.c +116 -0
- data/ext/extconf.rb +27 -0
- data/grammar/Cargo.lock +80 -0
- data/grammar/Cargo.toml +26 -0
- data/grammar/binding.gyp +20 -0
- data/grammar/bindings/node/binding.cc +28 -0
- data/grammar/bindings/node/index.js +19 -0
- data/grammar/bindings/rust/build.rs +40 -0
- data/grammar/bindings/rust/lib.rs +52 -0
- data/grammar/corpus/1_document.txt +131 -0
- data/grammar/corpus/2_selectors.txt +58 -0
- data/grammar/corpus/3_spaces.txt +69 -0
- data/grammar/corpus/4_errors.txt +10 -0
- data/grammar/corpus/5_macros.txt +175 -0
- data/grammar/corpus/6_styles.txt +81 -0
- data/grammar/grammar.js +275 -0
- data/grammar/package-lock.json +34 -0
- data/grammar/package.json +33 -0
- data/grammar/src/grammar.json +1269 -0
- data/grammar/src/node-types.json +474 -0
- data/grammar/src/parser.c +5772 -0
- data/grammar/src/scanner.c +258 -0
- data/grammar/src/tree_sitter/parser.h +230 -0
- data/grammar/src/tree_sitter/scanner.h +12 -0
- data/grammar/test.nml +10 -0
- data/hokusai.gemspec +19 -0
- data/ui/examples/assets/DigitalDisplay.ttf +0 -0
- data/ui/examples/assets/OpenSans-Regular.ttf +0 -0
- data/ui/examples/assets/addy.png +0 -0
- data/ui/examples/assets/baby_sean.png +0 -0
- data/ui/examples/assets/football-troll.png +0 -0
- data/ui/examples/assets/gear.png +0 -0
- data/ui/examples/assets/icecold.ttf +0 -0
- data/ui/examples/assets/science-troll.png +0 -0
- data/ui/examples/buddy.rb +31 -0
- data/ui/examples/clock.rb +58 -0
- data/ui/examples/counter.rb +123 -0
- data/ui/examples/dynamic.rb +147 -0
- data/ui/examples/foobar.rb +236 -0
- data/ui/examples/stock.rb +115 -0
- data/ui/examples/stock_decider/option.rb +74 -0
- data/ui/examples/tic_tac_toe.rb +246 -0
- data/ui/lib/lib_hokusai.rb +425 -0
- data/ui/spec/hokusai/ast_spec.rb +88 -0
- data/ui/spec/hokusai/automation/keys_transcoder_spec.rb +50 -0
- data/ui/spec/hokusai/automation/selector_spec.rb +68 -0
- data/ui/spec/hokusai/block_spec.rb +126 -0
- data/ui/spec/hokusai/directives_spec.rb +327 -0
- data/ui/spec/hokusai/e2e/client_spec.rb +58 -0
- data/ui/spec/hokusai/e2e/meta_spec.rb +42 -0
- data/ui/spec/hokusai/publisher_spec.rb +38 -0
- data/ui/spec/hokusai/slots_spec.rb +150 -0
- data/ui/spec/hokusai/util/piece_table_spec.rb +90 -0
- data/ui/spec/hokusai_spec.rb +0 -0
- data/ui/spec/spec_helper.rb +30 -0
- data/ui/src/hokusai/ast.rb +446 -0
- data/ui/src/hokusai/automation/client.rb +167 -0
- data/ui/src/hokusai/automation/constants.rb +98 -0
- data/ui/src/hokusai/automation/converters/selector_converter.rb +61 -0
- data/ui/src/hokusai/automation/driver.rb +54 -0
- data/ui/src/hokusai/automation/driver_command_queue.rb +50 -0
- data/ui/src/hokusai/automation/driver_commands/base.rb +79 -0
- data/ui/src/hokusai/automation/driver_commands/get_attribute.rb +41 -0
- data/ui/src/hokusai/automation/driver_commands/invoke.rb +33 -0
- data/ui/src/hokusai/automation/driver_commands/locate.rb +48 -0
- data/ui/src/hokusai/automation/driver_commands/trigger_keyboard.rb +94 -0
- data/ui/src/hokusai/automation/driver_commands/trigger_mouse.rb +213 -0
- data/ui/src/hokusai/automation/keys_transcoder.rb +128 -0
- data/ui/src/hokusai/automation/selector.rb +39 -0
- data/ui/src/hokusai/automation/server.rb +114 -0
- data/ui/src/hokusai/automation.rb +3 -0
- data/ui/src/hokusai/backends/raylib/config.rb +47 -0
- data/ui/src/hokusai/backends/raylib/font.rb +113 -0
- data/ui/src/hokusai/backends/raylib/keys.rb +124 -0
- data/ui/src/hokusai/backends/raylib.rb +449 -0
- data/ui/src/hokusai/backends/sdl2/Monaco.ttf +0 -0
- data/ui/src/hokusai/backends/sdl2/color.rb +12 -0
- data/ui/src/hokusai/backends/sdl2/config.rb +31 -0
- data/ui/src/hokusai/backends/sdl2/font.rb +127 -0
- data/ui/src/hokusai/backends/sdl2/keys.rb +119 -0
- data/ui/src/hokusai/backends/sdl2.rb +529 -0
- data/ui/src/hokusai/block.rb +237 -0
- data/ui/src/hokusai/blocks/button.rb +100 -0
- data/ui/src/hokusai/blocks/checkbox.rb +51 -0
- data/ui/src/hokusai/blocks/circle.rb +28 -0
- data/ui/src/hokusai/blocks/clipped.rb +23 -0
- data/ui/src/hokusai/blocks/cursor.rb +49 -0
- data/ui/src/hokusai/blocks/dynamic.rb +37 -0
- data/ui/src/hokusai/blocks/empty.rb +10 -0
- data/ui/src/hokusai/blocks/hblock.rb +35 -0
- data/ui/src/hokusai/blocks/image.rb +18 -0
- data/ui/src/hokusai/blocks/input.rb +200 -0
- data/ui/src/hokusai/blocks/label.rb +39 -0
- data/ui/src/hokusai/blocks/panel.rb +126 -0
- data/ui/src/hokusai/blocks/rect.rb +24 -0
- data/ui/src/hokusai/blocks/scissor_begin.rb +18 -0
- data/ui/src/hokusai/blocks/scissor_end.rb +12 -0
- data/ui/src/hokusai/blocks/scrollbar.rb +103 -0
- data/ui/src/hokusai/blocks/selectable.rb +77 -0
- data/ui/src/hokusai/blocks/svg.rb +20 -0
- data/ui/src/hokusai/blocks/text.rb +214 -0
- data/ui/src/hokusai/blocks/titlebar/osx.rb +145 -0
- data/ui/src/hokusai/blocks/toggle.rb +55 -0
- data/ui/src/hokusai/blocks/vblock.rb +35 -0
- data/ui/src/hokusai/commands/base.rb +22 -0
- data/ui/src/hokusai/commands/circle.rb +47 -0
- data/ui/src/hokusai/commands/image.rb +45 -0
- data/ui/src/hokusai/commands/rect.rb +158 -0
- data/ui/src/hokusai/commands/scissor.rb +22 -0
- data/ui/src/hokusai/commands/text.rb +92 -0
- data/ui/src/hokusai/commands.rb +87 -0
- data/ui/src/hokusai/diff.rb +124 -0
- data/ui/src/hokusai/error.rb +3 -0
- data/ui/src/hokusai/event.rb +54 -0
- data/ui/src/hokusai/events/keyboard.rb +84 -0
- data/ui/src/hokusai/events/mouse.rb +172 -0
- data/ui/src/hokusai/font.rb +280 -0
- data/ui/src/hokusai/meta.rb +152 -0
- data/ui/src/hokusai/mounting/loop_entry.rb +230 -0
- data/ui/src/hokusai/mounting/mount_entry.rb +74 -0
- data/ui/src/hokusai/mounting/update_entry.rb +101 -0
- data/ui/src/hokusai/node.rb +98 -0
- data/ui/src/hokusai/node_mounter.rb +102 -0
- data/ui/src/hokusai/painter.rb +214 -0
- data/ui/src/hokusai/publisher.rb +32 -0
- data/ui/src/hokusai/style.rb +72 -0
- data/ui/src/hokusai/types.rb +266 -0
- data/ui/src/hokusai/util/clamping_iterator.rb +202 -0
- data/ui/src/hokusai/util/piece_table.rb +111 -0
- data/ui/src/hokusai/util/selection.rb +145 -0
- data/ui/src/hokusai.rb +120 -0
- data/ui/vendor/.gitkeep +0 -0
- data/vendor/.gitkeep +0 -0
- data/xmake.lua +192 -0
- metadata +222 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
require_relative "../spec_helper"
|
2
|
+
|
3
|
+
describe Hokusai::Ast do
|
4
|
+
let(:template) do
|
5
|
+
<<~EOF
|
6
|
+
[template]
|
7
|
+
first#id.class1.class2 { prop1="one" prop2="two" prop3="three" @event1="first" @event2="second" }
|
8
|
+
EOF
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:parent) { Hokusai::Ast.parse(template, "root") }
|
12
|
+
let(:ast) { parent.children.first }
|
13
|
+
|
14
|
+
it ".parse returns a new ast from a template" do
|
15
|
+
expect(parent).to be_instance_of(Hokusai::Ast), "#{ast.class} not #{Hokusai::Ast}"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "#type returns the ast type" do
|
19
|
+
expect(ast.type).to eq("first")
|
20
|
+
end
|
21
|
+
|
22
|
+
it "#id returns the ast id" do
|
23
|
+
expect(ast.id).to eq("id")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "#each_event iterates events" do
|
27
|
+
count = 0
|
28
|
+
ast.each_event do |_|
|
29
|
+
count += 1
|
30
|
+
end
|
31
|
+
|
32
|
+
expect(count).to eq(2)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "#each_prop iterates props" do
|
36
|
+
count = 0
|
37
|
+
ast.each_prop do |_|
|
38
|
+
count += 1
|
39
|
+
end
|
40
|
+
|
41
|
+
expect(count).to eq(3)
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#prop" do
|
45
|
+
let(:template) do
|
46
|
+
<<~EOF
|
47
|
+
[template]
|
48
|
+
first { :computed="true" }
|
49
|
+
second { notcomputed="true" }
|
50
|
+
EOF
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns nil if a prop is not found" do
|
54
|
+
expect(ast.prop("nope")).to be(nil)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns a Hokusai::Ast::Prop" do
|
58
|
+
prop1 = parent.children[0].prop("computed")
|
59
|
+
prop2 = parent.children[1].prop("notcomputed")
|
60
|
+
|
61
|
+
expect(prop1).to be_a(Hokusai::Ast::Prop)
|
62
|
+
expect(prop1.value.method).to eq("true")
|
63
|
+
expect(prop1.computed?).to be(true)
|
64
|
+
|
65
|
+
expect(prop2.computed?).to be(false)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "#event" do
|
70
|
+
let(:template) do
|
71
|
+
<<~EOF
|
72
|
+
[template]
|
73
|
+
first { @click="handle_click", @hover="handle_hover" }
|
74
|
+
EOF
|
75
|
+
end
|
76
|
+
|
77
|
+
it "returns nil if an event isn't found" do
|
78
|
+
expect(ast.event("none")).to be(nil)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "returns a Hokusai::Ast::Event" do
|
82
|
+
event = ast.event("hover")
|
83
|
+
expect(event).to be_a(Hokusai::Ast::Event)
|
84
|
+
expect(event.name).to eq("hover")
|
85
|
+
expect(event.value.method).to eq("handle_hover")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
describe Hokusai::Automation::KeysTranscoder do
|
2
|
+
def with_encoded(*keys)
|
3
|
+
encoded = Hokusai::Automation::KeysTranscoder.encode(keys.to_a)
|
4
|
+
yield encoded
|
5
|
+
end
|
6
|
+
|
7
|
+
def decode(encoded)
|
8
|
+
Hokusai::Automation::KeysTranscoder.decode(encoded)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "decodes a simple string" do
|
12
|
+
with_encoded("h", "e", "l", "l", "o") do |encoded|
|
13
|
+
expect(decode(encoded)).to eq(%w[h e l l o])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it "decodes a string with modifiers" do
|
18
|
+
with_encoded([:shift, 'h'], 'e','l','l','o') do |encoded|
|
19
|
+
decode(encoded).should eq([[:lshift, 'h'], 'e','l','l','o'])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "decodes multiple modifiers" do
|
24
|
+
with_encoded([:shift, 'h'], [:ctrl, 'e','l','l','o']) do |encoded|
|
25
|
+
decode(encoded).should eq([[:lshift, 'h'], [:lctrl, 'e','l','l','o']])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "encodes a simple string" do
|
30
|
+
with_encoded('h','e','l','l','o') do |encoded|
|
31
|
+
encoded.should eq("hello")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it "encodeds a modifier key" do
|
36
|
+
with_encoded(:tab) do |encoded|
|
37
|
+
encoded.should eq("\ue004")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "encodes mmultiple modifiers" do
|
42
|
+
with_encoded([:shift, 'h'], [:ctrl, 'e','l','l','o']) do |encoded|
|
43
|
+
encoded.should eq("\ue008h\ue000\ue009ello\ue000")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "raises when a modifier cannot be found" do
|
48
|
+
expect { with_encoded('h','e','l','l','o', :foobar) {} }.to raise_error(Hokusai::Automation::Error)
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
describe Hokusai::Automation::Selector do
|
2
|
+
let(:selector_child_klass) do
|
3
|
+
Class.new(Hokusai::Block) do
|
4
|
+
template <<~EOF
|
5
|
+
[template]
|
6
|
+
virtual
|
7
|
+
EOF
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:app) do
|
12
|
+
child = selector_child_klass
|
13
|
+
|
14
|
+
Class.new(Hokusai::Block) do
|
15
|
+
template <<~EOF
|
16
|
+
[template]
|
17
|
+
child#id.class1.class2
|
18
|
+
child2.class1
|
19
|
+
EOF
|
20
|
+
|
21
|
+
uses(child: child, child2: child)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:fixture) { app.mount }
|
26
|
+
let(:child1) { fixture.children[0] }
|
27
|
+
let(:child2) { fixture.children[1] }
|
28
|
+
|
29
|
+
let(:with_selector) do
|
30
|
+
->(node: nil, id: nil, classes: nil, &block) do
|
31
|
+
block.call Hokusai::Automation::Selector.new(node, id, classes)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it "#match returns true if a selector matches a block" do
|
36
|
+
with_selector.call(id: "id") do |selector|
|
37
|
+
expect(selector.matches(child1)).to be(true)
|
38
|
+
end
|
39
|
+
|
40
|
+
with_selector.call(node: "child") do |selector|
|
41
|
+
expect(selector.matches(child1)).to be(true)
|
42
|
+
end
|
43
|
+
|
44
|
+
with_selector.call(classes: %w[class1 class2]) do |selector|
|
45
|
+
expect(selector.matches(child1)).to be(true)
|
46
|
+
end
|
47
|
+
|
48
|
+
with_selector.call(node: "child", id: "id", classes: %w[class1 class2]) do |selector|
|
49
|
+
expect(selector.matches(child1)).to be(true)
|
50
|
+
end
|
51
|
+
|
52
|
+
with_selector.call(node: "child2", id: nil, classes: %w[class1]) do |selector|
|
53
|
+
expect(selector.matches(child2)).to be(true)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it "#match returns false if a selector doesn't match a block" do
|
58
|
+
with_selector.call(id: "foobar") do |selector|
|
59
|
+
expect(selector.matches(child1)).to be(false)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it "#match returns false if any part of the selector doesn't match a block" do
|
64
|
+
with_selector.call(id: "id", node: "child", classes: %w[class1 class3]) do |selector|
|
65
|
+
expect(selector.matches(child1)).to be(false)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require_relative "../spec_helper"
|
2
|
+
|
3
|
+
describe Hokusai::Block do
|
4
|
+
let(:child_klass) do
|
5
|
+
Class.new(Hokusai::Block) do
|
6
|
+
template <<~EOF
|
7
|
+
[template]
|
8
|
+
virtual
|
9
|
+
EOF
|
10
|
+
computed! :echo
|
11
|
+
|
12
|
+
def self.foobar
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def hello
|
17
|
+
@state += 1
|
18
|
+
|
19
|
+
emit("get", @state)
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(**args)
|
23
|
+
@state = 0
|
24
|
+
|
25
|
+
super(**args)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:parent) do
|
31
|
+
child_block = child_klass
|
32
|
+
klass = Class.new(Hokusai::Block) do
|
33
|
+
template <<~EOF
|
34
|
+
[template]
|
35
|
+
child { :echo="total" @get="get_it" }
|
36
|
+
EOF
|
37
|
+
|
38
|
+
attr_reader :state, :total, :mounted
|
39
|
+
|
40
|
+
uses(child: child_block)
|
41
|
+
|
42
|
+
def get_it(count)
|
43
|
+
@state += count
|
44
|
+
@total = "total: #{state}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_mounted
|
48
|
+
@mounted = true
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(**args)
|
52
|
+
@state = 0
|
53
|
+
@total = "total: #{state}"
|
54
|
+
@mounted = false
|
55
|
+
|
56
|
+
super(**args)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
klass.mount
|
61
|
+
end
|
62
|
+
|
63
|
+
let(:child) do
|
64
|
+
parent.children.first
|
65
|
+
end
|
66
|
+
|
67
|
+
describe ".mount" do
|
68
|
+
it "mounts blocks together" do
|
69
|
+
expect(parent.children.size).to eq(1)
|
70
|
+
expect(child).to respond_to(:hello)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "blocks have a node and portal node" do
|
74
|
+
expect(parent.node.type).to eq("root")
|
75
|
+
expect(child.node.portal.type).to eq("child")
|
76
|
+
end
|
77
|
+
|
78
|
+
it "mounts props into children" do
|
79
|
+
expect(child.echo).to eq("total: 0")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "#emit" do
|
84
|
+
it "emits events to the parent" do
|
85
|
+
child.emit("get", 1)
|
86
|
+
|
87
|
+
expect(parent.state).to eq(1)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "raises when an emit is missing a param" do
|
91
|
+
expect { child.emit("get") }.to raise_error(ArgumentError)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "does not raise when an emit is not subscribe to" do
|
95
|
+
expect { child.emit("foobarrr", 1,23) }.not_to raise_error
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#update" do
|
100
|
+
it "updates child props with new state" do
|
101
|
+
child.emit("get", 1)
|
102
|
+
expect(child.echo).to eq("total: 0")
|
103
|
+
|
104
|
+
parent.update
|
105
|
+
expect(child.echo).to eq("total: 1")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe ".using" do
|
110
|
+
it "returns a Hokusai::Block by a key" do
|
111
|
+
expect(parent.class.use("child")).to respond_to(:foobar)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "raise if the key isn't found" do
|
115
|
+
expect { parent.class.use("whaaa") }.to raise_error(Hokusai::Error) do |err|
|
116
|
+
expect(err.message).to match(/Type whaaa is not used/)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "#on_mounted" do
|
122
|
+
it "triggers when the block is mounted" do
|
123
|
+
expect(parent.mounted).to be(true)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,327 @@
|
|
1
|
+
require_relative "../spec_helper"
|
2
|
+
|
3
|
+
describe "Block directives" do
|
4
|
+
let(:virtual) do
|
5
|
+
klass = Class.new(Hokusai::Block) do
|
6
|
+
template <<~EOF
|
7
|
+
[template]
|
8
|
+
virtual
|
9
|
+
EOF
|
10
|
+
end
|
11
|
+
|
12
|
+
klass
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:slotted) do
|
16
|
+
klass = Class.new(Hokusai::Block) do
|
17
|
+
template <<~EOF
|
18
|
+
[template]
|
19
|
+
slot
|
20
|
+
EOF
|
21
|
+
end
|
22
|
+
|
23
|
+
klass
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "if directive" do
|
27
|
+
let(:condition1) do
|
28
|
+
virtual_klass = virtual
|
29
|
+
klass = Class.new(Hokusai::Block) do
|
30
|
+
template <<~EOF
|
31
|
+
[template]
|
32
|
+
type { content="1" }
|
33
|
+
slot
|
34
|
+
EOF
|
35
|
+
|
36
|
+
uses(type: virtual_klass)
|
37
|
+
end
|
38
|
+
|
39
|
+
klass
|
40
|
+
end
|
41
|
+
|
42
|
+
let(:condition2) do
|
43
|
+
virtual_klass = virtual
|
44
|
+
klass = Class.new(Hokusai::Block) do
|
45
|
+
template <<~EOF
|
46
|
+
[template]
|
47
|
+
type { content="2" }
|
48
|
+
slot
|
49
|
+
EOF
|
50
|
+
|
51
|
+
uses(type: virtual_klass)
|
52
|
+
end
|
53
|
+
|
54
|
+
klass
|
55
|
+
end
|
56
|
+
|
57
|
+
let(:branch) do
|
58
|
+
virtual_klass = virtual
|
59
|
+
cond1 = condition1
|
60
|
+
cond2 = condition2
|
61
|
+
slot = slotted
|
62
|
+
klass = Class.new(Hokusai::Block) do
|
63
|
+
template <<~EOF
|
64
|
+
[template]
|
65
|
+
slotted
|
66
|
+
[if="state"]
|
67
|
+
cond1
|
68
|
+
control1
|
69
|
+
[else]
|
70
|
+
cond2
|
71
|
+
control2
|
72
|
+
control
|
73
|
+
EOF
|
74
|
+
|
75
|
+
uses(
|
76
|
+
slotted: slot,
|
77
|
+
cond1: cond1,
|
78
|
+
cond2: cond2,
|
79
|
+
control: virtual_klass,
|
80
|
+
control1: virtual_klass,
|
81
|
+
control2: virtual_klass
|
82
|
+
)
|
83
|
+
|
84
|
+
attr_accessor :state
|
85
|
+
|
86
|
+
def initialize(**props)
|
87
|
+
@state = true
|
88
|
+
|
89
|
+
super
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
let (:content) do
|
95
|
+
->(root) do
|
96
|
+
root
|
97
|
+
.children[0]
|
98
|
+
.children[0]
|
99
|
+
.children[0]
|
100
|
+
.node
|
101
|
+
.meta
|
102
|
+
.get_prop(:content)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
let (:portal_type) do
|
107
|
+
->(root) do
|
108
|
+
root
|
109
|
+
.children[0]
|
110
|
+
.children[0]
|
111
|
+
.children[1]
|
112
|
+
.node
|
113
|
+
.type
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
it "simple condition" do
|
118
|
+
root = branch.mount
|
119
|
+
expect(content.call(root)).to eq("1")
|
120
|
+
|
121
|
+
root.state = false
|
122
|
+
root.update
|
123
|
+
expect(content.call(root)).to eq("2")
|
124
|
+
end
|
125
|
+
|
126
|
+
it "switches back and forth" do
|
127
|
+
root = branch.mount
|
128
|
+
|
129
|
+
expect(content.call(root)).to eq("1")
|
130
|
+
expect(root.children[0].children[-1].node.type).to eq("control")
|
131
|
+
|
132
|
+
root.state = false
|
133
|
+
root.update
|
134
|
+
|
135
|
+
expect(content.call(root)).to eq("2")
|
136
|
+
expect(root.children[0].children[-1].node.type).to eq("control")
|
137
|
+
|
138
|
+
root.state = true
|
139
|
+
root.update
|
140
|
+
|
141
|
+
expect(content.call(root)).to eq("1")
|
142
|
+
expect(root.children[0].children[-1].node.type).to eq("control")
|
143
|
+
end
|
144
|
+
|
145
|
+
it "mounts portaled children into the slot" do
|
146
|
+
root = branch.mount
|
147
|
+
|
148
|
+
expect(portal_type.call(root)).to eq("control1")
|
149
|
+
|
150
|
+
root.state = false
|
151
|
+
root.update
|
152
|
+
|
153
|
+
expect(portal_type.call(root)).to eq("control2")
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "loop directives" do
|
158
|
+
let(:cell) do
|
159
|
+
virtual_klass = virtual
|
160
|
+
klass = Class.new(Hokusai::Block) do
|
161
|
+
template <<~EOF
|
162
|
+
[template]
|
163
|
+
empty { :content="value" }
|
164
|
+
EOF
|
165
|
+
|
166
|
+
computed! :value
|
167
|
+
|
168
|
+
uses(empty: virtual_klass)
|
169
|
+
end
|
170
|
+
|
171
|
+
klass
|
172
|
+
end
|
173
|
+
|
174
|
+
let(:column) do
|
175
|
+
cell_klass = cell
|
176
|
+
slotted_klass = slotted
|
177
|
+
|
178
|
+
klass = Class.new(Hokusai::Block) do
|
179
|
+
template <<~EOF
|
180
|
+
[template]
|
181
|
+
slotted
|
182
|
+
[for="value in columns"]
|
183
|
+
cell { :value="value" }
|
184
|
+
EOF
|
185
|
+
computed! :row_index
|
186
|
+
|
187
|
+
uses(cell: cell_klass, slotted: slotted_klass)
|
188
|
+
|
189
|
+
def columns
|
190
|
+
%w[A B C D].map { |letter| "#{letter}#{row_index}" }
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
klass
|
195
|
+
end
|
196
|
+
|
197
|
+
let(:csv) do
|
198
|
+
column_klass = column
|
199
|
+
slotted_klass = slotted
|
200
|
+
|
201
|
+
|
202
|
+
klass = Class.new(Hokusai::Block) do
|
203
|
+
template <<~EOF
|
204
|
+
[template]
|
205
|
+
slotted
|
206
|
+
[for="row in rows"]
|
207
|
+
column { :row_index="row" }
|
208
|
+
EOF
|
209
|
+
|
210
|
+
uses(slotted: slotted_klass, column: column_klass)
|
211
|
+
|
212
|
+
def rows
|
213
|
+
%w[1 2 3 4]
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
klass
|
218
|
+
end
|
219
|
+
|
220
|
+
let(:list_item) do
|
221
|
+
virtual_klass = virtual
|
222
|
+
|
223
|
+
klass = Class.new(Hokusai::Block) do
|
224
|
+
template <<~EOF
|
225
|
+
[template]
|
226
|
+
empty
|
227
|
+
EOF
|
228
|
+
|
229
|
+
attr_accessor :state
|
230
|
+
|
231
|
+
uses(empty: virtual_klass)
|
232
|
+
|
233
|
+
computed! :item
|
234
|
+
|
235
|
+
def initialize(**args)
|
236
|
+
@state = "hello"
|
237
|
+
|
238
|
+
super
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
klass
|
243
|
+
end
|
244
|
+
|
245
|
+
let(:list) do
|
246
|
+
list_item_klass = list_item
|
247
|
+
slotted_klass = slotted
|
248
|
+
|
249
|
+
klass = Class.new(Hokusai::Block) do
|
250
|
+
template <<~EOF
|
251
|
+
[template]
|
252
|
+
slotted
|
253
|
+
[for="item in list"]
|
254
|
+
list_item { :item="item" :key="key(item, index)" }
|
255
|
+
EOF
|
256
|
+
|
257
|
+
attr_accessor :limit
|
258
|
+
|
259
|
+
uses(slotted: slotted_klass, list_item: list_item_klass)
|
260
|
+
|
261
|
+
def list
|
262
|
+
(0...limit).to_a
|
263
|
+
end
|
264
|
+
|
265
|
+
def key(item, index)
|
266
|
+
"#{item}-#{index}"
|
267
|
+
end
|
268
|
+
|
269
|
+
def initialize(**args)
|
270
|
+
@limit = 5
|
271
|
+
|
272
|
+
super
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
klass
|
277
|
+
end
|
278
|
+
|
279
|
+
it "mounts nested loops" do
|
280
|
+
root = csv.mount
|
281
|
+
|
282
|
+
rows = %w[1 2 3 4]
|
283
|
+
cols = %w[A B C D]
|
284
|
+
|
285
|
+
rows.each_with_index do |row, ridx|
|
286
|
+
actual_rows = root.children[0].children
|
287
|
+
expect(actual_rows.size).to eq(4)
|
288
|
+
|
289
|
+
actual_row = actual_rows[ridx]
|
290
|
+
expect(actual_row.row_index).to eq(row)
|
291
|
+
|
292
|
+
actual_columns = actual_row.children[0].children
|
293
|
+
expect(actual_columns.size).to eq(4)
|
294
|
+
|
295
|
+
cols.each_with_index do |col, cidx|
|
296
|
+
expect(actual_columns[cidx].value).to eq("#{col}#{row}")
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
it "diffs so that unchanged blocks are not remounted" do
|
302
|
+
root = list.mount
|
303
|
+
children = root.children[0].children
|
304
|
+
expect(children.size).to eq(5)
|
305
|
+
|
306
|
+
children[0].state = "1"
|
307
|
+
children[1].state = "2"
|
308
|
+
children[2].state = "3"
|
309
|
+
children[3].state = "4"
|
310
|
+
children[4].state = "5"
|
311
|
+
|
312
|
+
root.limit = 2
|
313
|
+
root.update
|
314
|
+
expect(root.children[0].children.size).to eq(2)
|
315
|
+
|
316
|
+
|
317
|
+
root.limit = 3
|
318
|
+
root.update
|
319
|
+
expect(root.children[0].children.size).to eq(3)
|
320
|
+
|
321
|
+
updated_children = root.children[0].children
|
322
|
+
expect(updated_children[0].state).to eq("1")
|
323
|
+
expect(updated_children[1].state).to eq("2")
|
324
|
+
expect(updated_children[2].state).to eq("hello")
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
describe Hokusai::Automation::Client do
|
2
|
+
let(:counter) do
|
3
|
+
klass = Class.new(Hokusai::Block) do
|
4
|
+
template <<~EOF
|
5
|
+
[template]
|
6
|
+
vblock
|
7
|
+
label#count { :content="count" size="90" }
|
8
|
+
hblock.counter
|
9
|
+
label#inc { @click="increment" content="Increment" }
|
10
|
+
label#dec { @click="decrement" content="Decrement" }
|
11
|
+
EOF
|
12
|
+
|
13
|
+
uses(
|
14
|
+
vblock: Hokusai::Blocks::Vblock,
|
15
|
+
hblock: Hokusai::Blocks::Hblock,
|
16
|
+
label: Hokusai::Blocks::Label
|
17
|
+
)
|
18
|
+
|
19
|
+
attr_accessor :count
|
20
|
+
|
21
|
+
def increment(_event)
|
22
|
+
self.count += 1
|
23
|
+
end
|
24
|
+
|
25
|
+
def decrement(_event)
|
26
|
+
self.count -= 1
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(**args)
|
30
|
+
@count = 0
|
31
|
+
|
32
|
+
super(**args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
klass
|
37
|
+
end
|
38
|
+
|
39
|
+
it "client works" do
|
40
|
+
with_app(counter) do |client, app|
|
41
|
+
output = client.block("#count")
|
42
|
+
|
43
|
+
expect(output.prop("content")).to eq(0)
|
44
|
+
|
45
|
+
client.block(".counter #inc") do |increment|
|
46
|
+
3.times { increment.click }
|
47
|
+
end
|
48
|
+
|
49
|
+
expect(output.prop("content")).to eq(3)
|
50
|
+
|
51
|
+
client.block("#dec") do |decrement|
|
52
|
+
2.times { decrement.click }
|
53
|
+
end
|
54
|
+
|
55
|
+
expect(output.prop("content")).to eq(1)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|