Current section
Files
Jump to
Current section
Files
src/plushie@testing@script@runner.erl
-module(plushie@testing@script@runner).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/plushie/testing/script/runner.gleam").
-export([run/2]).
-export_type([failure/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Script executor for `.plushie` test scripts.\n"
"\n"
" Runs parsed scripts step by step, collecting per-instruction\n"
" results. Returns Ok(Nil) on success or Error(failures) with\n"
" a list of (instruction, reason) tuples.\n"
).
-type failure() :: {failure, plushie@testing@script:instruction(), binary()}.
-file("src/plushie/testing/script/runner.gleam", 233).
-spec split_scoped_id(binary()) -> {binary(), list(binary())}.
split_scoped_id(Id) ->
Clean_id = case gleam_stdlib:string_starts_with(Id, <<"#"/utf8>>) of
true ->
gleam@string:drop_start(Id, 1);
false ->
Id
end,
case gleam@string:split(Clean_id, <<"/"/utf8>>) of
[] ->
{Clean_id, []};
[Single] ->
{Single, []};
Segments ->
Local@1 = case gleam@list:last(Segments) of
{ok, Local} -> Local;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"plushie/testing/script/runner"/utf8>>,
function => <<"split_scoped_id"/utf8>>,
line => 243,
value => _assert_fail,
start => 6515,
'end' => 6557,
pattern_start => 6526,
pattern_end => 6535})
end,
Scope = gleam@list:take(Segments, erlang:length(Segments) - 1),
{Local@1, Scope}
end.
-file("src/plushie/testing/script/runner.gleam", 251).
?DOC(" Recursively check if any node in the tree contains the given text.\n").
-spec tree_contains_text(plushie@node:node_(), binary()) -> boolean().
tree_contains_text(Tree, Text) ->
Props = erlang:element(4, Tree),
Values = [gleam_stdlib:map_get(Props, <<"content"/utf8>>),
gleam_stdlib:map_get(Props, <<"label"/utf8>>),
gleam_stdlib:map_get(Props, <<"value"/utf8>>),
gleam_stdlib:map_get(Props, <<"placeholder"/utf8>>)],
Found = gleam@list:any(Values, fun(Result) -> case Result of
{ok, {string_val, S}} ->
S =:= Text;
_ ->
false
end end),
case Found of
true ->
true;
false ->
gleam@list:any(
erlang:element(5, Tree),
fun(Child) -> tree_contains_text(Child, Text) end
)
end.
-file("src/plushie/testing/script/runner.gleam", 59).
-spec execute(
plushie@testing@session:test_session(QDP, plushie@event:event()),
plushie@testing@script:instruction()
) -> {ok, plushie@testing@session:test_session(QDP, plushie@event:event())} |
{error, binary()}.
execute(Session, Instruction) ->
case Instruction of
{click, Selector} ->
{Local, Scope} = split_scoped_id(Selector),
{ok,
plushie@testing@session:send_event(
Session,
{widget_click, Local, Scope}
)};
{type_text, Selector@1, Text} ->
{Local@1, Scope@1} = split_scoped_id(Selector@1),
{ok,
plushie@testing@session:send_event(
Session,
{widget_input, Local@1, Scope@1, Text}
)};
{type_key, Key} ->
{ok,
plushie@testing@session:send_event(
Session,
{key_press,
Key,
Key,
plushie@event:modifiers_none(),
none,
standard,
{some, Key},
false,
false}
)};
{press, Key@1} ->
{ok,
plushie@testing@session:send_event(
Session,
{key_press,
Key@1,
Key@1,
plushie@event:modifiers_none(),
none,
standard,
none,
false,
false}
)};
{release, Key@2} ->
{ok,
plushie@testing@session:send_event(
Session,
{key_release,
Key@2,
Key@2,
plushie@event:modifiers_none(),
none,
standard,
none,
false}
)};
{toggle, Selector@2} ->
{Local@2, Scope@2} = split_scoped_id(Selector@2),
{ok,
plushie@testing@session:send_event(
Session,
{widget_toggle, Local@2, Scope@2, true}
)};
{select, Selector@3, Value} ->
{Local@3, Scope@3} = split_scoped_id(Selector@3),
{ok,
plushie@testing@session:send_event(
Session,
{widget_select, Local@3, Scope@3, Value}
)};
{slide, Selector@4, Value@1} ->
{Local@4, Scope@4} = split_scoped_id(Selector@4),
{ok,
plushie@testing@session:send_event(
Session,
{widget_slide, Local@4, Scope@4, Value@1}
)};
{move, _} ->
{ok, Session};
{move_to, X, Y} ->
{ok,
plushie@testing@session:send_event(
Session,
{mouse_moved, erlang:float(X), erlang:float(Y), false}
)};
{expect, Text@1} ->
Tree = plushie@testing@session:current_tree(Session),
case tree_contains_text(Tree, Text@1) of
true ->
{ok, Session};
false ->
{error,
<<<<"expected to find text \""/utf8, Text@1/binary>>/binary,
"\" in tree"/utf8>>}
end;
{assert_text, Selector@5, Expected} ->
Tree@1 = plushie@testing@session:current_tree(Session),
case plushie@testing@element:find(Tree@1, Selector@5) of
{some, El} ->
case plushie@testing@element:text(El) of
{some, Actual} ->
case Actual =:= Expected of
true ->
{ok, Session};
false ->
{error,
<<<<<<<<<<<<"expected text \""/utf8,
Expected/binary>>/binary,
"\" for \""/utf8>>/binary,
Selector@5/binary>>/binary,
"\", got \""/utf8>>/binary,
Actual/binary>>/binary,
"\""/utf8>>}
end;
none ->
{error,
<<<<"element \""/utf8, Selector@5/binary>>/binary,
"\" has no text content"/utf8>>}
end;
none ->
{error,
<<<<"element \""/utf8, Selector@5/binary>>/binary,
"\" not found"/utf8>>}
end;
{assert_model, Expression} ->
Model_str = gleam@string:inspect(
plushie@testing@session:model(Session)
),
case gleam_stdlib:contains_string(Model_str, Expression) of
true ->
{ok, Session};
false ->
{error,
<<<<<<"assert_model failed: \""/utf8,
Expression/binary>>/binary,
"\" not found in model: "/utf8>>/binary,
Model_str/binary>>}
end;
{assert_tree_hash, _} ->
{ok, Session};
{assert_screenshot, _} ->
{ok, Session};
{wait, _} ->
{ok, Session}
end.
-file("src/plushie/testing/script/runner.gleam", 29).
?DOC(
" Run a parsed script against a test session.\n"
" Returns Ok(Nil) on success or Error(failures).\n"
).
-spec run(
plushie@testing@script:script(),
plushie@testing@session:test_session(any(), plushie@event:event())
) -> {ok, nil} | {error, list(failure())}.
run(Script_val, Session) ->
Result = gleam@list:index_fold(
erlang:element(3, Script_val),
{Session, []},
fun(Acc, Instruction, Idx) ->
{Sess, Failures} = Acc,
case execute(Sess, Instruction) of
{ok, New_sess} ->
{New_sess, Failures};
{error, Reason} ->
Failure = {failure,
Instruction,
<<<<<<"line "/utf8,
(erlang:integer_to_binary(Idx + 1))/binary>>/binary,
": "/utf8>>/binary,
Reason/binary>>},
{Sess, [Failure | Failures]}
end
end
),
case erlang:element(2, Result) of
[] ->
{ok, nil};
Failures@1 ->
{error, lists:reverse(Failures@1)}
end.