Skip to content

Commit

Permalink
Allow referencing HTML elements and components in tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
gdotdesign committed Nov 24, 2024
1 parent 02cbd3a commit f8aeeed
Show file tree
Hide file tree
Showing 13 changed files with 188 additions and 30 deletions.
22 changes: 22 additions & 0 deletions core/tests/tests/Test.mint
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,25 @@ suite "Test (Function)" {
test() == ""
}
}

suite "Test with HTML reference" {
test "it works" {
<button as button/>
|> Test.Html.start
|> (context : Test.Context(Dom.Element)) { button != Maybe.Nothing }
}
}

component TestReference {
fun render {
<div>"TestReference"</div>
}
}

suite "Test with Component reference" {
test "it works" {
<TestReference as button/>
|> Test.Html.start
|> (context : Test.Context(Dom.Element)) { button != Maybe.Nothing }
}
}
45 changes: 45 additions & 0 deletions spec/compilers/test_with_reference
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
type Maybe(a) {
Nothing
Just(a)
}

suite "Test" {
test "X" {
<button as button/>

button == Maybe.Nothing
}
}
--------------------------------------------------------------------------------
import {
createElement as D,
testRunner as B,
createRef as C,
compare as F,
variant as A,
setRef as E
} from "./runtime.js";

export const
G = A(0, `Maybe.Nothing`),
H = A(1, `Maybe.Just`);

export default () => {
new B([{
tests: [{
proc: () => {
const a = C(new G());
return (() => {
D(`button`, {
ref: E(a, H)
});
return F(a.current, new G())
})()
},
location: {"start":[7,2],"end":[11,3],"filename":"compilers/test_with_reference"},
name: `X`
}],
location: {"start":[6,0],"end":[12,1],"filename":"compilers/test_with_reference"},
name: `Test`
}], {}, ``, ``)
};
61 changes: 61 additions & 0 deletions spec/compilers/test_with_reference_component
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
type Maybe(a) {
Nothing
Just(a)
}

component Button {
fun render : Html {
<div/>
}
}

suite "Test" {
test "X" {
<Button as button/>

button == Maybe.Nothing
}
}
--------------------------------------------------------------------------------
import {
createElement as C,
testRunner as D,
createRef as E,
compare as G,
useMemo as B,
variant as A,
setRef as F
} from "./runtime.js";

export const
H = A(0, `Maybe.Nothing`),
I = A(1, `Maybe.Just`),
J = ({
_
}) => {
const a = B(() => {
return {}
}, []);
(_ ? _(a) : null);
return C(`div`, {})
};

export default () => {
new D([{
tests: [{
proc: () => {
const b = E(new H());
return (() => {
C(J, {
_: F(b, I)
});
return G(b.current, new H())
})()
},
location: {"start":[13,2],"end":[17,3],"filename":"compilers/test_with_reference_component"},
name: `X`
}],
location: {"start":[12,0],"end":[18,1],"filename":"compilers/test_with_reference_component"},
name: `Test`
}], {}, ``, ``)
};
2 changes: 1 addition & 1 deletion spec/errors/html_element_reference_outside_of_component
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ component Main {
--------------------------------------------------------------------------------
░ ERROR (HTML_ELEMENT_REFERENCE_OUTSIDE_OF_COMPONENT) ░░░░░░░░░░░░░░░░░░░░░░░░░░

Referencing elements outside of components is not allowed:
Referencing elements outside of components or tests is not allowed:

┌ errors/html_element_reference_outside_of_component:3:13
├────────────────────────────────────────────────────────
Expand Down
5 changes: 3 additions & 2 deletions src/ast/test.cr
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
module Mint
class Ast
class Test < Node
getter expression, name
getter expression, name, refs

def initialize(@from : Parser::Location,
def initialize(@refs : Array(Tuple(Variable, Node)),
@from : Parser::Location,
@to : Parser::Location,
@name : StringLiteral,
@file : Parser::File,
Expand Down
5 changes: 4 additions & 1 deletion src/compiler/js.cr
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ module Mint

# Renders multiple const assignments (as one).
def consts(items : Array(Tuple(Ast::Node, Id, Compiled))) : Compiled
if items.size == 1
case items.size
when 0
[] of Item
when 1
_, id, value =
items[0]

Expand Down
30 changes: 14 additions & 16 deletions src/compilers/component.cr
Original file line number Diff line number Diff line change
Expand Up @@ -84,22 +84,20 @@ module Mint
[item[1]] of Item
end

unless items.empty?
variable =
Variable.new

properties << ["_"] of Item
[
js.const(variable, js.call(Builtin::UseMemo, [
js.arrow_function { js.return(js.object_destructuring(items)) },
js.array([] of Compiled),
])),
js.tenary(
["_"] of Item,
js.call(["_"] of Item, [[variable] of Item]),
js.null),
]
end
variable =
Variable.new

properties << ["_"] of Item
[
js.const(variable, js.call(Builtin::UseMemo, [
js.arrow_function { js.return(js.object_destructuring(items)) },
js.array([] of Compiled),
])),
js.tenary(
["_"] of Item,
js.call(["_"] of Item, [[variable] of Item]),
js.null),
]
end || [] of Compiled

arguments =
Expand Down
7 changes: 6 additions & 1 deletion src/compilers/test.cr
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ module Mint
name =
compile node.name

refs =
js.consts(node.refs.to_h.keys.map do |ref|
{node, ref, js.call(Builtin::CreateRef, [js.new(nothing, [] of Compiled)])}
end)

expression =
case operation = node.expression
when Ast::Operation
Expand All @@ -31,7 +36,7 @@ module Mint
end || compile(node.expression)

js.object({
"proc" => js.arrow_function { js.return(expression) },
"proc" => js.arrow_function { js.statements([refs, js.return(expression)]) },
"location" => location,
"name" => name,
})
Expand Down
6 changes: 4 additions & 2 deletions src/compilers/variable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ module Mint

case {entity, parent}
when {Ast::Component, Ast::Component},
{Ast::HtmlElement, Ast::Component}
{Ast::Component, Ast::Test},
{Ast::HtmlElement, Ast::Component},
{Ast::HtmlElement, Ast::Test}
case parent
when Ast::Component
when Ast::Component, Ast::Test
ref =
parent
.refs
Expand Down
17 changes: 16 additions & 1 deletion src/parsers/test.cr
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module Mint
class Parser
def test : Ast::Test?
parse do |start_position|
parse do |start_position, start_nodes_position|
next unless keyword! "test"
whitespace

Expand All @@ -28,10 +28,25 @@ module Mint

next unless expression

refs = [] of Tuple(Ast::Variable, Ast::Node)

ast.nodes[start_nodes_position...].each do |node|
case node
when Ast::HtmlComponent,
Ast::HtmlElement
node.in_component = true

if ref = node.ref
refs << {ref, node}
end
end
end

Ast::Test.new(
expression: expression,
from: start_position,
to: position,
refs: refs,
file: file,
name: name)
end
Expand Down
8 changes: 6 additions & 2 deletions src/scope.cr
Original file line number Diff line number Diff line change
Expand Up @@ -351,15 +351,15 @@ module Mint
build(node.children, node)
build(node.styles, node)

if (root = scopes[parent][1].node).is_a?(Ast::Component) &&
if (root = find_parent_by_class(parent, [Ast::Component, Ast::Test])) &&
(ref = node.ref)
add(root, ref.value, node)
end
when Ast::HtmlComponent
build(node.attributes, node)
build(node.children, node)

if (root = scopes[parent][1].node).is_a?(Ast::Component) &&
if (root = find_parent_by_class(parent, [Ast::Component, Ast::Test])) &&
(ref = node.ref)
component =
@ast.components.find(&.name.value.==(node.component.value))
Expand All @@ -374,5 +374,9 @@ module Mint
raise "SCOPE!!!: #{node.class.name}"
end
end

def find_parent_by_class(node, classes)
scopes[node].find(&.node.class.in?(classes)).try(&.node)
end
end
end
4 changes: 2 additions & 2 deletions src/type_checkers/html_element.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ module Mint

node.ref.try do |ref|
error! :html_element_reference_outside_of_component do
snippet "Referencing elements outside of components is not " \
"allowed:", ref
snippet "Referencing elements outside of components or tests " \
"is not allowed:", ref
end unless node.in_component?
end

Expand Down
6 changes: 4 additions & 2 deletions src/type_checkers/variable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ module Mint
else
raise "Cannot happen!"
end
when item[0].is_a?(Ast::HtmlElement) && item[1].is_a?(Ast::Component)
when item[0].is_a?(Ast::HtmlElement) &&
(item[1].is_a?(Ast::Component) || item[1].is_a?(Ast::Test))
Type.new("Maybe", [Type.new("Dom.Element")] of Checkable)
when item[0].is_a?(Ast::Component) && item[1].is_a?(Ast::Component)
when item[0].is_a?(Ast::Component) &&
(item[1].is_a?(Ast::Component) || item[1].is_a?(Ast::Test))
components_touched.add(item[0].as(Ast::Component))
Type.new("Maybe", [component_records[item[0]]] of Checkable)
else
Expand Down

0 comments on commit f8aeeed

Please sign in to comment.