Packages
lustre
5.5.2
5.7.1
5.7.0
5.6.0
5.5.2
5.5.1
5.5.0
5.4.0
5.3.5
5.3.4
5.3.3
5.3.2
5.3.1
5.3.0
5.2.1
5.2.0
5.1.1
5.1.0
5.0.3
5.0.2
5.0.1
5.0.0
4.6.4
4.6.3
4.6.2
4.6.1
4.6.0
4.5.1
4.5.0
4.4.4
4.4.3
4.4.1
4.4.0
4.3.6
4.3.5
4.3.4
4.3.3
4.3.2
4.3.1
4.3.0
4.2.6
4.2.5
4.2.4
4.2.3
4.2.2
4.2.1
4.2.0
4.1.8
4.1.7
4.1.6
4.1.5
4.1.4
4.1.3
4.1.2
4.1.1
4.1.0
4.0.0
4.0.0-rc1
4.0.0-rc.2
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.12
3.0.11
3.0.10
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.8
3.0.0-rc.7
3.0.0-rc.6
3.0.0-rc.5
3.0.0-rc.4
3.0.0-rc.3
3.0.0-rc.2
3.0.0-rc.1
2.0.1
2.0.0
1.3.0
1.2.0
1.1.0
1.0.0
Create HTML templates, single page applications, Web Components, and real-time server components in Gleam!
Current section
Files
Jump to
Current section
Files
src/lustre@dev@simulate.erl
-module(lustre@dev@simulate).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/lustre/dev/simulate.gleam").
-export([simple/3, application/3, start/2, message/2, problem/3, model/1, view/1, history/1, event/4, click/2, input/3, submit/3]).
-export_type([app/3, simulation/2, event/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-opaque app(WZP, WZQ, WZR) :: {app,
fun((WZP) -> {WZQ, lustre@effect:effect(WZR)}),
fun((WZQ, WZR) -> {WZQ, lustre@effect:effect(WZR)}),
fun((WZQ) -> lustre@vdom@vnode:element(WZR))}.
-opaque simulation(WZS, WZT) :: {simulation,
fun((WZS, WZT) -> {WZS, lustre@effect:effect(WZT)}),
fun((WZS) -> lustre@vdom@vnode:element(WZT)),
list(event(WZT)),
WZS,
lustre@vdom@vnode:element(WZT)}.
-type event(WZU) :: {dispatch, WZU} |
{event, lustre@dev@query:'query'(), binary(), gleam@json:json()} |
{problem, binary(), binary()}.
-file("src/lustre/dev/simulate.gleam", 81).
?DOC(
" Construct a simulated simple Lustre application. The simulation can be started\n"
" with the [`start`](#start) function by providing the initial arguments for\n"
" your app's `init` function.\n"
"\n"
" DOM events and messages dispatched by effects can be simulated using the\n"
" [`event`](#event) and [`messgae`](#message) functions.\n"
).
-spec simple(
fun((WZV) -> WZW),
fun((WZW, WZX) -> WZW),
fun((WZW) -> lustre@vdom@vnode:element(WZX))
) -> app(WZV, WZW, WZX).
simple(Init, Update, View) ->
{app,
fun(Args) -> {Init(Args), lustre@effect:none()} end,
fun(Model, Msg) -> {Update(Model, Msg), lustre@effect:none()} end,
View}.
-file("src/lustre/dev/simulate.gleam", 104).
?DOC(
" Construct a simulated Lustre application. The simulation can be started\n"
" with the [`start`](#start) function by providing the initial arguments for\n"
" your app's `init` function.\n"
"\n"
" DOM events and messages dispatched by effects can be simulated using the\n"
" [`event`](#event) and [`messgae`](#message) functions.\n"
"\n"
" > **Note**: simulated apps do not run any effects! You can simulate the result\n"
" > of an effect by using the [`message`](#message) function, but to test side\n"
" > effects you should test your application in a real environment.\n"
).
-spec application(
fun((XAC) -> {XAD, lustre@effect:effect(XAE)}),
fun((XAD, XAE) -> {XAD, lustre@effect:effect(XAE)}),
fun((XAD) -> lustre@vdom@vnode:element(XAE))
) -> app(XAC, XAD, XAE).
application(Init, Update, View) ->
{app, Init, Update, View}.
-file("src/lustre/dev/simulate.gleam", 118).
?DOC(
" Start a simulated Lustre application. Once a simulation is running you can\n"
" use the [`message`](#message) and [`event`](#event) functions to simulate\n"
" events\n"
).
-spec start(app(XAL, XAM, XAN), XAL) -> simulation(XAM, XAN).
start(App, Args) ->
{Model, _} = (erlang:element(2, App))(Args),
Html = (erlang:element(4, App))(Model),
{simulation,
erlang:element(3, App),
erlang:element(4, App),
[],
Model,
Html}.
-file("src/lustre/dev/simulate.gleam", 157).
?DOC(
" Simulate a message sent directly to the runtime. This is often used to mimic\n"
" the result of some effect you would have run in a real environment. For example,\n"
" you might simulate a click event on a login button and then simulate the\n"
" successful response from the server by calling this function with the message\n"
" you would dispatch from the effect:\n"
"\n"
" ```gleam\n"
" import birdie\n"
" import lustre/dev/simulate\n"
" import lustre/dev/query\n"
" import lustre/element\n"
"\n"
" pub fn login_test() {\n"
" let app = simulate.application(init:, update:, view:)\n"
" let login_button = query.element(matching: query.id(\"login\"))\n"
" let user = User(name: \"Lucy\")\n"
"\n"
" simulate.start(app, Nil)\n"
" |> simulate.event(on: login_button, name: \"click\", data: [])\n"
" // Simulate a successful response from the server\n"
" |> simulate.message(ApiReturnedUser(Ok(user)))\n"
" |> simulate.view\n"
" |> element.to_readable_string\n"
" |> birdie.snap(\"Successful login\")\n"
" }\n"
" ```\n"
"\n"
" > **Note**: your app's `view` function will probably be rendering quite a lot\n"
" > of HTML! To make your snapshots more meaningful, you might want to couple\n"
" > this with the [`query`](./query.html) module to only snapshot parts of the\n"
" > page that are relevant to the test.\n"
).
-spec message(simulation(XAT, XAU), XAU) -> simulation(XAT, XAU).
message(Simulation, Msg) ->
{Model, _} = (erlang:element(2, Simulation))(
erlang:element(5, Simulation),
Msg
),
Html = (erlang:element(3, Simulation))(Model),
History = [{dispatch, Msg} | erlang:element(4, Simulation)],
{simulation,
erlang:element(2, Simulation),
erlang:element(3, Simulation),
History,
Model,
Html}.
-file("src/lustre/dev/simulate.gleam", 323).
?DOC(
" Log a problem that occured during the simulation. This function is useful for\n"
" external packages that want to provide functions to simulate certain effects\n"
" that may fail in the real world. For example, a routing package may log a\n"
" problem if a link has an invalid `href` attribute that would cause no message\n"
" to be dispatched.\n"
"\n"
" > **Note**: logging a problem will not stop the simulation from running, just\n"
" > like a real application!\n"
).
-spec problem(simulation(XBZ, XCA), binary(), binary()) -> simulation(XBZ, XCA).
problem(Simulation, Name, Message) ->
History = [{problem, Name, Message} | erlang:element(4, Simulation)],
{simulation,
erlang:element(2, Simulation),
erlang:element(3, Simulation),
History,
erlang:element(5, Simulation),
erlang:element(6, Simulation)}.
-file("src/lustre/dev/simulate.gleam", 338).
?DOC(
" Introspect the current `model` of a running simulation. This can be useful\n"
" to debug why a simulation is not producing the view you expect.\n"
).
-spec model(simulation(XCF, any())) -> XCF.
model(Simulation) ->
erlang:element(5, Simulation).
-file("src/lustre/dev/simulate.gleam", 347).
?DOC(
" Introspect the current `view` of a running simulation. Typically you would\n"
" use this with a snapshot testing library like [`birdie`](https://hexdocs.pm/birdie/index.html)\n"
" and/or with the [`query`](./query.html) api to make assertions about the state\n"
" of the page.\n"
).
-spec view(simulation(any(), XCK)) -> lustre@vdom@vnode:element(XCK).
view(Simulation) ->
erlang:element(6, Simulation).
-file("src/lustre/dev/simulate.gleam", 359).
?DOC(
" Receive the current [`Event`](#Event) log of a running simulation. You can\n"
" use this to produce more detailed snapshots by also rendering the sequence of\n"
" events that produced the given view.\n"
"\n"
" In addition to simulated DOM events and message dispatch, the event log will\n"
" also include entries for when the queried event target could not be found in\n"
" the view and cases where an event was fired but not handled by your application.\n"
).
-spec history(simulation(any(), XCP)) -> list(event(XCP)).
history(Simulation) ->
_pipe = erlang:element(4, Simulation),
lists:reverse(_pipe).
-file("src/lustre/dev/simulate.gleam", 181).
?DOC(
" Simulate a DOM event on the first element that matches the given query. The\n"
" payload represents a simulated event object, and should be used to pass data\n"
" you expect your event handlers to decode.\n"
"\n"
" If no element matches the query, an [`EventTargetNotFound`](#Event) event is\n"
" logged in the simulation history. If an element is found, but the application\n"
" has no handler for the event, the [`EventHandlerNotFound`](#Event) event is\n"
" logged instead.\n"
"\n"
" > **Note**: this is not a perfect simulation of a real DOM event. There is no\n"
" > capture phase of a simulated event and simulated events will not bubble up\n"
" > to parent elements.\n"
).
-spec event(
simulation(XAZ, XBA),
lustre@dev@query:'query'(),
binary(),
list({binary(), gleam@json:json()})
) -> simulation(XAZ, XBA).
event(Simulation, Query, Event, Payload) ->
Result = begin
gleam@result:'try'(
gleam@result:replace_error(
lustre@dev@query:find_path(
erlang:element(6, Simulation),
Query,
0,
root
),
problem(
Simulation,
<<"EventTargetNotFound"/utf8>>,
<<"No element matching "/utf8,
(lustre@dev@query:to_readable_string(Query))/binary>>
)
),
fun(_use0) ->
{_, Path} = _use0,
Events = lustre@vdom@cache:from_node(
erlang:element(6, Simulation)
),
Data = gleam@json:object(Payload),
gleam@result:'try'(
gleam@result:replace_error(
gleam@pair:second(
lustre@vdom@cache:handle(
Events,
lustre@vdom@path:to_string(Path),
Event,
begin
_pipe = Data,
_pipe@1 = gleam@json:to_string(_pipe),
_pipe@2 = gleam@json:parse(
_pipe@1,
{decoder,
fun gleam@dynamic@decode:decode_dynamic/1}
),
gleam@result:unwrap(
_pipe@2,
gleam@function:identity(nil)
)
end
)
),
problem(
Simulation,
<<"EventHandlerNotFound"/utf8>>,
<<<<<<"No "/utf8, Event/binary>>/binary,
" handler for element matching "/utf8>>/binary,
(lustre@dev@query:to_readable_string(Query))/binary>>
)
),
fun(Handler) ->
{Model, _} = (erlang:element(2, Simulation))(
erlang:element(5, Simulation),
erlang:element(4, Handler)
),
Html = (erlang:element(3, Simulation))(Model),
History = [{event, Query, Event, Data} |
erlang:element(4, Simulation)],
{ok,
{simulation,
erlang:element(2, Simulation),
erlang:element(3, Simulation),
History,
Model,
Html}}
end
)
end
)
end,
case Result of
{ok, Simulation@1} ->
Simulation@1;
{error, Problem} ->
Problem
end.
-file("src/lustre/dev/simulate.gleam", 246).
?DOC(
" A convenience function that simulates a click event on the first element\n"
" matching the given query. This event will have no payload and is only\n"
" appropriate for event handlers that use Lustre's `on_click` handler or custom\n"
" handlers that do not decode the event payload.\n"
).
-spec click(simulation(XBG, XBH), lustre@dev@query:'query'()) -> simulation(XBG, XBH).
click(Simulation, Query) ->
event(Simulation, Query, <<"click"/utf8>>, []).
-file("src/lustre/dev/simulate.gleam", 267).
?DOC(
" Simulate an input event on the first element matching the given query. This\n"
" helper has an event payload that looks like this:\n"
"\n"
" ```json\n"
" {\n"
" \"target\": {\n"
" \"value\": value\n"
" }\n"
" }\n"
" ```\n"
"\n"
" and is appropriate for event handlers that use Lustre's `on_input` handler\n"
" or custom handlers that only decode the event target value.\n"
).
-spec input(simulation(XBM, XBN), lustre@dev@query:'query'(), binary()) -> simulation(XBM, XBN).
input(Simulation, Query, Value) ->
event(
Simulation,
Query,
<<"input"/utf8>>,
[{<<"target"/utf8>>,
gleam@json:object(
[{<<"value"/utf8>>, gleam@json:string(Value)}]
)}]
).
-file("src/lustre/dev/simulate.gleam", 294).
?DOC(
" Simulate a submit event on the first element matching the given query. The\n"
" simulated event payload looks like this:\n"
"\n"
" ```json\n"
" {\n"
" \"detail\": {\n"
" \"formData\": [\n"
" ...\n"
" ]\n"
" }\n"
" }\n"
" ```\n"
"\n"
" and is appropriate for event handlers that use Lustre's `on_submit` handler\n"
" or custom handlers that only decode the non-standard `detail.formData`\n"
" property.\n"
).
-spec submit(
simulation(XBS, XBT),
lustre@dev@query:'query'(),
list({binary(), binary()})
) -> simulation(XBS, XBT).
submit(Simulation, Query, Form_data) ->
event(
Simulation,
Query,
<<"submit"/utf8>>,
[{<<"detail"/utf8>>,
gleam@json:object(
[{<<"formData"/utf8>>,
gleam@json:array(
Form_data,
fun(Entry) ->
gleam@json:preprocessed_array(
[gleam@json:string(
erlang:element(1, Entry)
),
gleam@json:string(
erlang:element(2, Entry)
)]
)
end
)}]
)}]
).