From a153ec726dd852c65c6f71c5d95fc442ca022d44 Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Tue, 21 Nov 2023 12:41:47 +0100 Subject: [PATCH 01/19] AbstractPlutoDingetjes. Display.with_js_link to request calculations and data from Julia dynamically --- frontend/components/CellOutput.js | 2 ++ frontend/components/Editor.js | 13 ++++++++++ src/runner/PlutoRunner.jl | 42 +++++++++++++++++++++++++++++++ src/webserver/Dynamic.jl | 18 +++++++++++++ 4 files changed, 75 insertions(+) diff --git a/frontend/components/CellOutput.js b/frontend/components/CellOutput.js index 007f096582..6f403be426 100644 --- a/frontend/components/CellOutput.js +++ b/frontend/components/CellOutput.js @@ -395,6 +395,8 @@ const execute_scripttags = async ({ root_node, script_nodes, previous_results_ma // @ts-ignore getPublishedObject: (id) => cell.getPublishedObject(id), + _internal_getJSLinkResponse: (cell_id, link_id) => (input) => pluto_actions.request_js_link_response(cell_id, link_id, input), + getBoundElementValueLikePluto: get_input_value, setBoundElementValueLikePluto: set_input_value, getBoundElementEventNameLikePluto: eventof, diff --git a/frontend/components/Editor.js b/frontend/components/Editor.js index 4aa9bef518..0f5a5a7db5 100644 --- a/frontend/components/Editor.js +++ b/frontend/components/Editor.js @@ -651,6 +651,19 @@ export class Editor extends Component { false ) }, + request_js_link_response: (cell_id, link_id, input) => { + return this.client + .send( + "request_js_link_response", + { + cell_id, + link_id, + input, + }, + { notebook_id: this.state.notebook.notebook_id } + ) + .then((r) => r.message) + }, /** This actions avoids pushing selected cells all the way down, which is too heavy to handle! */ get_selected_cells: (cell_id, /** @type {boolean} */ allow_other_selected_cells) => allow_other_selected_cells ? this.state.selected_cells : [cell_id], diff --git a/src/runner/PlutoRunner.jl b/src/runner/PlutoRunner.jl index 57a93e6657..aa4712ec36 100644 --- a/src/runner/PlutoRunner.jl +++ b/src/runner/PlutoRunner.jl @@ -541,6 +541,9 @@ function run_expression( # reset registered bonds cell_registered_bond_names[cell_id] = Set{Symbol}() + + # reset JS links + cell_js_links[cell_id] = Dict{String,Any}() # If the cell contains macro calls, we want those macro calls to preserve their identity, # so we macroexpand this earlier (during expression explorer stuff), and then we find it here. @@ -990,6 +993,7 @@ const default_iocontext = IOContext(devnull, :is_pluto => true, :pluto_supported_integration_features => supported_integration_features, :pluto_published_to_js => (io, x) -> core_published_to_js(io, x), + :pluto_with_js_link => (io, callback) -> core_with_js_link(io, callback), ) const default_stdout_iocontext = IOContext(devnull, @@ -1703,6 +1707,9 @@ const integrations = Integration[ if isdefined(AbstractPlutoDingetjes.Display, :published_to_js) supported!(AbstractPlutoDingetjes.Display.published_to_js) end + if isdefined(AbstractPlutoDingetjes.Display, :with_js_link) + supported!(AbstractPlutoDingetjes.Display.with_js_link) + end end end, @@ -2486,6 +2493,41 @@ function Base.show(io::IO, m::MIME"text/html", e::DivElement) Base.show(io, m, embed_display(e)) end + +### +# JS LINK +### + +const cell_js_links = Dict{UUID,Dict{String,Any}}() + +function core_with_js_link(io, callback) + + _notebook_id = get(io, :pluto_notebook_id, notebook_id[])::UUID + _cell_id = get(io, :pluto_cell_id, currently_running_cell_id[])::UUID + + # TODO is this okay? prob not + # link_id = objectid2str(callback) + link_id = String(rand('a':'z', 16)) + + links = get!(() -> Dict{String,Any}(), cell_js_links, _cell_id) + links[link_id] = callback + + write(io, "/* See the documentation for AbstractPlutoDingetjes.Display.with_js_link */ _internal_getJSLinkResponse(\"$(_cell_id)\", \"$(link_id)\")") +end + +function evaluate_js_link(cell_id::UUID, link_id::String, input::Any) + links = get(() -> Dict{String,Any}(), cell_js_links, cell_id) + callback = get(links, link_id, nothing) + if callback === nothing + @error "🚨 AbstractPlutoDingetjes: JS link not found." link_id + else + result = callback(input) + assertpackable(result) + + result + end +end + ### # LOGGING ### diff --git a/src/webserver/Dynamic.jl b/src/webserver/Dynamic.jl index 8a138813e9..75b0f770a1 100644 --- a/src/webserver/Dynamic.jl +++ b/src/webserver/Dynamic.jl @@ -525,6 +525,24 @@ responses[:reshow_cell] = function response_reshow_cell(🙋::ClientRequest) send_notebook_changes!(🙋 |> without_initiator) end +responses[:request_js_link_response] = function response_request_js_link_response(🙋::ClientRequest) + require_notebook(🙋) + @assert will_run_code(🙋.notebook) + + result = WorkspaceManager.eval_fetch_in_workspace( + (🙋.session, 🙋.notebook), + quote + PlutoRunner.evaluate_js_link( + $(UUID(🙋.body["cell_id"])), + $(🙋.body["link_id"]), + $(🙋.body["input"]), + ) + end + ) + + putclientupdates!(🙋.session, 🙋.initiator, UpdateMessage(:🐤, result, nothing, nothing, 🙋.initiator)) +end + responses[:nbpkg_available_versions] = function response_nbpkg_available_versions(🙋::ClientRequest) # require_notebook(🙋) all_versions = PkgCompat.package_versions(🙋.body["package_name"]) From 9f67f6ceedb0152d7dc00f84bbdc38a6ac38f181 Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Tue, 21 Nov 2023 13:47:28 +0100 Subject: [PATCH 02/19] Capture logs and stdout --- src/runner/PlutoRunner.jl | 15 +++++++++++++-- src/webserver/Dynamic.jl | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/runner/PlutoRunner.jl b/src/runner/PlutoRunner.jl index aa4712ec36..e24d1a4381 100644 --- a/src/runner/PlutoRunner.jl +++ b/src/runner/PlutoRunner.jl @@ -2515,13 +2515,24 @@ function core_with_js_link(io, callback) write(io, "/* See the documentation for AbstractPlutoDingetjes.Display.with_js_link */ _internal_getJSLinkResponse(\"$(_cell_id)\", \"$(link_id)\")") end -function evaluate_js_link(cell_id::UUID, link_id::String, input::Any) +function evaluate_js_link(notebook_id::UUID, cell_id::UUID, link_id::String, input::Any) links = get(() -> Dict{String,Any}(), cell_js_links, cell_id) callback = get(links, link_id, nothing) if callback === nothing @error "🚨 AbstractPlutoDingetjes: JS link not found." link_id else - result = callback(input) + logger = get!(() -> PlutoCellLogger(notebook_id, cell_id), pluto_cell_loggers, cell_id) + + result = with_logger_and_io_to_logs(logger; capture_stdout=true, stdio_loglevel=stdout_log_level) do + try + result = callback(input) + assertpackable(result) + result + catch ex + @error "🚨 AbstractPlutoDingetjes.Display.with_js_link: Exception while evaluating Julia callback." input exception=(ex, catch_backtrace()) + end + end + assertpackable(result) result diff --git a/src/webserver/Dynamic.jl b/src/webserver/Dynamic.jl index 75b0f770a1..e207693591 100644 --- a/src/webserver/Dynamic.jl +++ b/src/webserver/Dynamic.jl @@ -533,6 +533,7 @@ responses[:request_js_link_response] = function response_request_js_link_respons (🙋.session, 🙋.notebook), quote PlutoRunner.evaluate_js_link( + $(🙋.notebook.notebook_id), $(UUID(🙋.body["cell_id"])), $(🙋.body["link_id"]), $(🙋.body["input"]), From b4a498f71b53767d0fc2f8400c4315563fec4469 Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Tue, 21 Nov 2023 13:49:04 +0100 Subject: [PATCH 03/19] Create with_js_link.jl --- test/frontend/fixtures/with_js_link.jl | 136 +++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 test/frontend/fixtures/with_js_link.jl diff --git a/test/frontend/fixtures/with_js_link.jl b/test/frontend/fixtures/with_js_link.jl new file mode 100644 index 0000000000..0242f74267 --- /dev/null +++ b/test/frontend/fixtures/with_js_link.jl @@ -0,0 +1,136 @@ +### A Pluto.jl notebook ### +# v0.19.32 + +using Markdown +using InteractiveUtils + +# ╔═╡ b0f2a778-885f-11ee-3d28-939ca4069ee8 +begin + import Pkg + Pkg.activate(temp=true) + Pkg.add([ + Pkg.PackageSpec(name="AbstractPlutoDingetjes", rev="Display.with_js_link") + Pkg.PackageSpec(name="HypertextLiteral") + ]) + + using AbstractPlutoDingetjes + using HypertextLiteral +end + +# ╔═╡ 3d836ff3-995e-4353-807e-bf2cd78920e2 +some_global = rand(200) + +# ╔═╡ 12e64b86-3866-4e21-9af5-0e546452b4e1 +function function_evaluator(f::Function, default="") + @htl(""" +
+

Input:
+  

+ +

Output:
+ + + +

+ """) +end + +# ╔═╡ 4b80dda0-74b6-4a0e-a50e-61c5380111a4 +function_evaluator(123) do input + sqrt(parse(Float64, input)) +end + +# ╔═╡ 33a2293c-6202-47ca-80d1-4a9e261cae7f +function_evaluator(4) do input + @info "You should see this log $(input)" + println("You should see this print $(input)") + + rand(parse(Int, input)) +end + +# ╔═╡ 480aea45-da00-4e89-b43a-38e4d1827ec2 +function_evaluator(4) do input + @warn("You should see the following error:") + + error("You should see this error $(input)") +end + +# ╔═╡ b310dd30-dddd-4b75-81d2-aaf35c9dd1d3 +function_evaluator(4) do input + @warn("You should see the assertpackable fail after this log") + + :(@heyyy cant msgpack me) +end + +# ╔═╡ 58999fba-6631-4482-a811-12bf2412d65e +function_evaluator(4) do input + some_global[parse(Int, input)] +end + +# ╔═╡ 9e5c0f8d-6ac1-4aee-a00d-938f17eec146 +md""" +You should be able to use `with_js_link` multiple times within one cell, and they should work independently of eachother: +""" + +# ╔═╡ 306d03da-cd50-4b0c-a5dd-7ec1a278cde1 +@htl(""" +
+ $(function_evaluator(uppercase, "Παναγιώτης")) + $(function_evaluator(lowercase, "Παναγιώτης")) +
+""") + +# ╔═╡ 2cf033a7-bcd7-434d-9faf-ea761897fb64 +md""" +You should be able to set up a `with_js_link` in one cell, and use it in another. This example is a bit trivial though... +""" + +# ╔═╡ 40031867-ee3c-4aa9-884f-b76b5a9c4dec +fe = function_evaluator(length, "Alberto") + +# ╔═╡ 7f6ada79-8e3b-40b7-b477-ce05ae79a668 +fe + +# ╔═╡ f344c4cb-8226-4145-ab92-a37542f697dd +md""" +You should see a warning message when `with_js_link` is not used inside an HTML renderer that supports it: +""" + +# ╔═╡ 8bbd32f8-56f7-4f29-aea8-6906416f6cfd +let + html_repr = repr(MIME"text/html"(), fe) + HTML(html_repr) +end + +# ╔═╡ Cell order: +# ╠═b0f2a778-885f-11ee-3d28-939ca4069ee8 +# ╠═4b80dda0-74b6-4a0e-a50e-61c5380111a4 +# ╠═33a2293c-6202-47ca-80d1-4a9e261cae7f +# ╠═480aea45-da00-4e89-b43a-38e4d1827ec2 +# ╠═b310dd30-dddd-4b75-81d2-aaf35c9dd1d3 +# ╠═3d836ff3-995e-4353-807e-bf2cd78920e2 +# ╠═58999fba-6631-4482-a811-12bf2412d65e +# ╠═12e64b86-3866-4e21-9af5-0e546452b4e1 +# ╟─9e5c0f8d-6ac1-4aee-a00d-938f17eec146 +# ╠═306d03da-cd50-4b0c-a5dd-7ec1a278cde1 +# ╟─2cf033a7-bcd7-434d-9faf-ea761897fb64 +# ╠═40031867-ee3c-4aa9-884f-b76b5a9c4dec +# ╠═7f6ada79-8e3b-40b7-b477-ce05ae79a668 +# ╟─f344c4cb-8226-4145-ab92-a37542f697dd +# ╠═8bbd32f8-56f7-4f29-aea8-6906416f6cfd From d3b8b99a2d206ab0230edb1009cbe6b83cc8b4fd Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Tue, 21 Nov 2023 14:44:27 +0100 Subject: [PATCH 04/19] Update with_js_link.jl --- test/frontend/fixtures/with_js_link.jl | 119 ++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 1 deletion(-) diff --git a/test/frontend/fixtures/with_js_link.jl b/test/frontend/fixtures/with_js_link.jl index 0242f74267..2515c17fda 100644 --- a/test/frontend/fixtures/with_js_link.jl +++ b/test/frontend/fixtures/with_js_link.jl @@ -17,6 +17,89 @@ begin using HypertextLiteral end +# ╔═╡ 37aacc7f-61fd-4c4b-b24d-42361d508e8d +@htl(""" + +""") + +# ╔═╡ b3186d7b-8fd7-4575-bccf-8e89ce611010 +md""" +# Benchmark + +We call the `next` function from JavaScript in a loop until `max` is reached, to calculate the time of each round trip. +""" + +# ╔═╡ 82c7a083-c84d-4924-bad2-776d3cdad797 +next(x) = x + 1; + +# ╔═╡ e8abaff9-f629-47c6-8009-066bcdf67693 +max = 250; + +# ╔═╡ bf9861e0-be91-4041-aa61-8ac2ef6cb719 +@htl(""" +
+

+

Current value:

+

Past values:

+

Time per round trip:

+ +
+""") + +# ╔═╡ ebf79ee4-2590-4b5a-a957-213ed03a5921 +md""" +# Concurrency +""" + +# ╔═╡ f9ff5f6b-7525-4928-a19c-2d0bf56fd952 + + +# ╔═╡ 2417de50-e6e4-4c99-8836-ee8ff04d586a +1 + 1 + +# ╔═╡ 663e5a70-4d07-4d6a-8725-dc9a2b26b65d +md""" +# Tests +""" + # ╔═╡ 3d836ff3-995e-4353-807e-bf2cd78920e2 some_global = rand(200) @@ -53,7 +136,29 @@ end # ╔═╡ 4b80dda0-74b6-4a0e-a50e-61c5380111a4 function_evaluator(123) do input - sqrt(parse(Float64, input)) + num = parse(Float64, input) + sqrt(num) +end + +# ╔═╡ a399cb12-39d4-43c0-a0a7-05cb683dffbd +function_evaluator(123) do input + @info "start" + sleep(5) + @warn "end" + uppercase(input) +end + +# ╔═╡ 2bff3975-5918-40fe-9761-eb7b47f16df2 +function_evaluator(123) do input + @info "start" + sleep(5) + @warn "end" + uppercase(input) +end + +# ╔═╡ abb24301-357c-40f0-832e-86f26404d3d9 +function_evaluator("THIS IN LOWERCASE") do input + "you should see $(lowercase(input))" end # ╔═╡ 33a2293c-6202-47ca-80d1-4a9e261cae7f @@ -121,6 +226,18 @@ end # ╔═╡ Cell order: # ╠═b0f2a778-885f-11ee-3d28-939ca4069ee8 # ╠═4b80dda0-74b6-4a0e-a50e-61c5380111a4 +# ╠═37aacc7f-61fd-4c4b-b24d-42361d508e8d +# ╟─b3186d7b-8fd7-4575-bccf-8e89ce611010 +# ╠═82c7a083-c84d-4924-bad2-776d3cdad797 +# ╠═e8abaff9-f629-47c6-8009-066bcdf67693 +# ╟─bf9861e0-be91-4041-aa61-8ac2ef6cb719 +# ╟─ebf79ee4-2590-4b5a-a957-213ed03a5921 +# ╠═f9ff5f6b-7525-4928-a19c-2d0bf56fd952 +# ╠═a399cb12-39d4-43c0-a0a7-05cb683dffbd +# ╠═2bff3975-5918-40fe-9761-eb7b47f16df2 +# ╠═2417de50-e6e4-4c99-8836-ee8ff04d586a +# ╟─663e5a70-4d07-4d6a-8725-dc9a2b26b65d +# ╠═abb24301-357c-40f0-832e-86f26404d3d9 # ╠═33a2293c-6202-47ca-80d1-4a9e261cae7f # ╠═480aea45-da00-4e89-b43a-38e4d1827ec2 # ╠═b310dd30-dddd-4b75-81d2-aaf35c9dd1d3 From d31e48115f4661faa8442e12efa2503917a83248 Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Tue, 21 Nov 2023 15:10:32 +0100 Subject: [PATCH 05/19] run callback async on server --- src/runner/PlutoRunner.jl | 2 +- src/webserver/Dynamic.jl | 28 ++++++++++++++------------ test/frontend/fixtures/with_js_link.jl | 4 ++-- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/runner/PlutoRunner.jl b/src/runner/PlutoRunner.jl index e24d1a4381..6a0fac0712 100644 --- a/src/runner/PlutoRunner.jl +++ b/src/runner/PlutoRunner.jl @@ -2523,7 +2523,7 @@ function evaluate_js_link(notebook_id::UUID, cell_id::UUID, link_id::String, inp else logger = get!(() -> PlutoCellLogger(notebook_id, cell_id), pluto_cell_loggers, cell_id) - result = with_logger_and_io_to_logs(logger; capture_stdout=true, stdio_loglevel=stdout_log_level) do + result = with_logger_and_io_to_logs(logger; capture_stdout=false, stdio_loglevel=stdout_log_level) do try result = callback(input) assertpackable(result) diff --git a/src/webserver/Dynamic.jl b/src/webserver/Dynamic.jl index e207693591..cf688c2b5c 100644 --- a/src/webserver/Dynamic.jl +++ b/src/webserver/Dynamic.jl @@ -528,20 +528,22 @@ end responses[:request_js_link_response] = function response_request_js_link_response(🙋::ClientRequest) require_notebook(🙋) @assert will_run_code(🙋.notebook) - - result = WorkspaceManager.eval_fetch_in_workspace( - (🙋.session, 🙋.notebook), - quote - PlutoRunner.evaluate_js_link( - $(🙋.notebook.notebook_id), - $(UUID(🙋.body["cell_id"])), - $(🙋.body["link_id"]), - $(🙋.body["input"]), - ) - end - ) - putclientupdates!(🙋.session, 🙋.initiator, UpdateMessage(:🐤, result, nothing, nothing, 🙋.initiator)) + @asynclog begin + result = WorkspaceManager.eval_fetch_in_workspace( + (🙋.session, 🙋.notebook), + quote + PlutoRunner.evaluate_js_link( + $(🙋.notebook.notebook_id), + $(UUID(🙋.body["cell_id"])), + $(🙋.body["link_id"]), + $(🙋.body["input"]), + ) + end + ) + + putclientupdates!(🙋.session, 🙋.initiator, UpdateMessage(:🐤, result, nothing, nothing, 🙋.initiator)) + end end responses[:nbpkg_available_versions] = function response_nbpkg_available_versions(🙋::ClientRequest) diff --git a/test/frontend/fixtures/with_js_link.jl b/test/frontend/fixtures/with_js_link.jl index 2515c17fda..7e04dda136 100644 --- a/test/frontend/fixtures/with_js_link.jl +++ b/test/frontend/fixtures/with_js_link.jl @@ -163,8 +163,8 @@ end # ╔═╡ 33a2293c-6202-47ca-80d1-4a9e261cae7f function_evaluator(4) do input - @info "You should see this log $(input)" - println("You should see this print $(input)") + @info "you should see this log $(input)" + println("(not currently supported) you should see this print $(input)") rand(parse(Int, input)) end From 4458029cc3b3248a446346772507a2881d620423 Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Wed, 6 Dec 2023 15:25:48 +0100 Subject: [PATCH 06/19] cleanup hook --- src/runner/PlutoRunner.jl | 42 +++++++++--- test/frontend/fixtures/with_js_link.jl | 92 ++++++++++++++++++++++++-- 2 files changed, 118 insertions(+), 16 deletions(-) diff --git a/src/runner/PlutoRunner.jl b/src/runner/PlutoRunner.jl index 6a0fac0712..71a1cc1aef 100644 --- a/src/runner/PlutoRunner.jl +++ b/src/runner/PlutoRunner.jl @@ -543,7 +543,17 @@ function run_expression( cell_registered_bond_names[cell_id] = Set{Symbol}() # reset JS links - cell_js_links[cell_id] = Dict{String,Any}() + begin + # cancel old links + old_links = get!(() -> Dict{String,JSLink}(), cell_js_links, cell_id) + for (name, link) in old_links + c = link.on_cancellation + c === nothing || c() + end + + # clear + cell_js_links[cell_id] = Dict{String,JSLink}() + end # If the cell contains macro calls, we want those macro calls to preserve their identity, # so we macroexpand this earlier (during expression explorer stuff), and then we find it here. @@ -810,7 +820,7 @@ function delete_toplevel_methods(f::Function, cell_id::UUID)::Bool # we define `Base.isodd(n::Integer) = rand(Bool)`, which overrides the existing method `Base.isodd(n::Integer)` # calling `Base.delete_method` on this method won't bring back the old method, because our new method still exists in the method table, and it has a world age which is newer than the original. (our method has a deleted_world value set, which disables it) # - # To solve this, we iterate again, and _re-enable any methods that were hidden in this way_, by adding them again to the method table with an even newer`primary_world`. + # To solve this, we iterate again, and _re-enable any methods that were hidden in this way_, by adding them again to the method table with an even newer `primary_world`. if !isempty(deleted_sigs) to_insert = Method[] Base.visit(methods_table) do method @@ -993,7 +1003,7 @@ const default_iocontext = IOContext(devnull, :is_pluto => true, :pluto_supported_integration_features => supported_integration_features, :pluto_published_to_js => (io, x) -> core_published_to_js(io, x), - :pluto_with_js_link => (io, callback) -> core_with_js_link(io, callback), + :pluto_with_js_link => (io, callback, on_cancellation) -> core_with_js_link(io, callback, on_cancellation), ) const default_stdout_iocontext = IOContext(devnull, @@ -2498,9 +2508,15 @@ end # JS LINK ### -const cell_js_links = Dict{UUID,Dict{String,Any}}() +struct JSLink + callback::Function + on_cancellation::Union{Nothing,Function} + cancelled_ref::Ref{Bool} +end + +const cell_js_links = Dict{UUID,Dict{String,JSLink}}() -function core_with_js_link(io, callback) +function core_with_js_link(io, callback, on_cancellation) _notebook_id = get(io, :pluto_notebook_id, notebook_id[])::UUID _cell_id = get(io, :pluto_cell_id, currently_running_cell_id[])::UUID @@ -2509,23 +2525,27 @@ function core_with_js_link(io, callback) # link_id = objectid2str(callback) link_id = String(rand('a':'z', 16)) - links = get!(() -> Dict{String,Any}(), cell_js_links, _cell_id) - links[link_id] = callback + links = get!(() -> Dict{String,JSLink}(), cell_js_links, _cell_id) + links[link_id] = JSLink(callback, on_cancellation, Ref(false)) write(io, "/* See the documentation for AbstractPlutoDingetjes.Display.with_js_link */ _internal_getJSLinkResponse(\"$(_cell_id)\", \"$(link_id)\")") end function evaluate_js_link(notebook_id::UUID, cell_id::UUID, link_id::String, input::Any) - links = get(() -> Dict{String,Any}(), cell_js_links, cell_id) - callback = get(links, link_id, nothing) - if callback === nothing + links = get(() -> Dict{String,JSLink}(), cell_js_links, cell_id) + link = get(links, link_id, nothing) + if link === nothing + # TODO log to notebook @error "🚨 AbstractPlutoDingetjes: JS link not found." link_id + elseif link.cancelled_ref[] + # TODO log to notebook + @error "🚨 AbstractPlutoDingetjes: JS link has already been invalidated." link_id else logger = get!(() -> PlutoCellLogger(notebook_id, cell_id), pluto_cell_loggers, cell_id) result = with_logger_and_io_to_logs(logger; capture_stdout=false, stdio_loglevel=stdout_log_level) do try - result = callback(input) + result = link.callback(input) assertpackable(result) result catch ex diff --git a/test/frontend/fixtures/with_js_link.jl b/test/frontend/fixtures/with_js_link.jl index 7e04dda136..bbfcd575f6 100644 --- a/test/frontend/fixtures/with_js_link.jl +++ b/test/frontend/fixtures/with_js_link.jl @@ -29,6 +29,45 @@ console.log(result) """) +# ╔═╡ 30d7c350-f792-47e9-873a-01adf909bc84 +md""" +If you change `String` to `AbstractString` here then you get some back logs: +""" + +# ╔═╡ 75752f77-1e3f-4997-869b-8bee2c12a2cb +function cool(x::String) + uppercase(x) +end + +# ╔═╡ 3098e16a-4730-4564-a484-02a6b0278930 +# function cool() +# end + +# ╔═╡ 37fc039e-7a4d-4d2d-80f3-d409a9ee096d +# ╠═╡ disabled = true +#=╠═╡ +let + function f(x) + cool(x) + end + @htl(""" + + """) +end + ╠═╡ =# + +# ╔═╡ 977c59f7-9f3a-40ae-981d-2a8a48e08349 + + # ╔═╡ b3186d7b-8fd7-4575-bccf-8e89ce611010 md""" # Benchmark @@ -89,7 +128,7 @@ md""" # Concurrency """ -# ╔═╡ f9ff5f6b-7525-4928-a19c-2d0bf56fd952 +# ╔═╡ c79cfb05-bf14-479f-8a31-d1e4fcce79cb # ╔═╡ 2417de50-e6e4-4c99-8836-ee8ff04d586a @@ -100,6 +139,14 @@ md""" # Tests """ +# ╔═╡ 1d32fd55-9ca0-45c8-97f5-23cb29eaa8b3 +md""" +Test a closure +""" + +# ╔═╡ 5f3c590e-07f2-4dea-b6d1-e9d90f501fda +some_other_global = rand(100) + # ╔═╡ 3d836ff3-995e-4353-807e-bf2cd78920e2 some_global = rand(200) @@ -140,22 +187,47 @@ function_evaluator(123) do input sqrt(num) end +# ╔═╡ f9ff5f6b-7525-4928-a19c-2d0bf56fd952 +function_evaluator(123) do input + sqrt(-1) +end + # ╔═╡ a399cb12-39d4-43c0-a0a7-05cb683dffbd function_evaluator(123) do input - @info "start" + # @info "start" sleep(5) - @warn "end" + # @warn "end" uppercase(input) + end # ╔═╡ 2bff3975-5918-40fe-9761-eb7b47f16df2 function_evaluator(123) do input - @info "start" + # @info "start" sleep(5) - @warn "end" + # @warn "end" uppercase(input) end +# ╔═╡ 2b5cc4b1-ca57-4cb6-a42a-dcb331ed2c26 +let + thing = function_evaluator("1") do str + some_other_global[1:parse(Int,str)] + end + if false + fff() = 123 + end + thing +end + +# ╔═╡ 85e9daf1-d8e3-4fc0-8acd-10d863e724d0 +let + x = rand(100) + function_evaluator("1") do str + x[parse(Int,str)] + end +end + # ╔═╡ abb24301-357c-40f0-832e-86f26404d3d9 function_evaluator("THIS IN LOWERCASE") do input "you should see $(lowercase(input))" @@ -227,16 +299,26 @@ end # ╠═b0f2a778-885f-11ee-3d28-939ca4069ee8 # ╠═4b80dda0-74b6-4a0e-a50e-61c5380111a4 # ╠═37aacc7f-61fd-4c4b-b24d-42361d508e8d +# ╟─30d7c350-f792-47e9-873a-01adf909bc84 +# ╠═75752f77-1e3f-4997-869b-8bee2c12a2cb +# ╠═3098e16a-4730-4564-a484-02a6b0278930 +# ╠═37fc039e-7a4d-4d2d-80f3-d409a9ee096d +# ╠═977c59f7-9f3a-40ae-981d-2a8a48e08349 # ╟─b3186d7b-8fd7-4575-bccf-8e89ce611010 # ╠═82c7a083-c84d-4924-bad2-776d3cdad797 # ╠═e8abaff9-f629-47c6-8009-066bcdf67693 # ╟─bf9861e0-be91-4041-aa61-8ac2ef6cb719 # ╟─ebf79ee4-2590-4b5a-a957-213ed03a5921 +# ╠═c79cfb05-bf14-479f-8a31-d1e4fcce79cb # ╠═f9ff5f6b-7525-4928-a19c-2d0bf56fd952 # ╠═a399cb12-39d4-43c0-a0a7-05cb683dffbd # ╠═2bff3975-5918-40fe-9761-eb7b47f16df2 # ╠═2417de50-e6e4-4c99-8836-ee8ff04d586a # ╟─663e5a70-4d07-4d6a-8725-dc9a2b26b65d +# ╟─1d32fd55-9ca0-45c8-97f5-23cb29eaa8b3 +# ╠═5f3c590e-07f2-4dea-b6d1-e9d90f501fda +# ╠═2b5cc4b1-ca57-4cb6-a42a-dcb331ed2c26 +# ╠═85e9daf1-d8e3-4fc0-8acd-10d863e724d0 # ╠═abb24301-357c-40f0-832e-86f26404d3d9 # ╠═33a2293c-6202-47ca-80d1-4a9e261cae7f # ╠═480aea45-da00-4e89-b43a-38e4d1827ec2 From 0c79025ff4991b2514f0c2a7756bd367289a6794 Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Mon, 15 Jan 2024 14:06:54 +0100 Subject: [PATCH 07/19] some tests --- test/frontend/__tests__/with_js_link.js | 112 ++++++++++++++++++++++++ test/frontend/fixtures/with_js_link.jl | 38 ++++---- test/frontend/helpers/common.js | 13 ++- test/frontend/helpers/pluto.js | 16 ++++ 4 files changed, 154 insertions(+), 25 deletions(-) create mode 100644 test/frontend/__tests__/with_js_link.js diff --git a/test/frontend/__tests__/with_js_link.js b/test/frontend/__tests__/with_js_link.js new file mode 100644 index 0000000000..a7f33f91ba --- /dev/null +++ b/test/frontend/__tests__/with_js_link.js @@ -0,0 +1,112 @@ +import puppeteer from "puppeteer" +import { saveScreenshot, createPage, waitForContentToBecome, getTextContent } from "../helpers/common" +import { importNotebook, getPlutoUrl, shutdownCurrentNotebook, setupPlutoBrowser, getLogs } from "../helpers/pluto" + +describe("with_js_link", () => { + /** + * Launch a shared browser instance for all tests. + * I don't use jest-puppeteer because it takes away a lot of control and works buggy for me, + * so I need to manually create the shared browser. + * @type {puppeteer.Browser} + */ + let browser = null + /** @type {puppeteer.Page} */ + let page = null + beforeAll(async () => { + browser = await setupPlutoBrowser() + }) + beforeEach(async () => { + page = await createPage(browser) + await page.goto(getPlutoUrl(), { waitUntil: "networkidle0" }) + }) + afterEach(async () => { + await saveScreenshot(page) + await shutdownCurrentNotebook(page) + await page.close() + page = null + }) + afterAll(async () => { + await browser.close() + browser = null + }) + + it("Should correctly show published_to_js in cell output, and in logs", async () => { + await importNotebook(page, "with_js_link.jl", { timeout: 120 * 1000 }) + + const submit_ev_input = (id, value) => + page.evaluate( + (id, value) => { + document.querySelector(`.function_evaluator#${id} input`).value = value + + document.querySelector(`.function_evaluator#${id} input[type="submit"]`).click() + }, + id, + value + ) + + const ev_output_sel = (id) => `.function_evaluator#${id} textarea` + + const expect_ev_output = async (id, expected) => { + expect(await waitForContentToBecome(page, ev_output_sel(id), expected)).toBe(expected) + } + + ////// BASIC + await expect_ev_output("sqrt", "30") + await submit_ev_input("sqrt", "25") + await expect_ev_output("sqrt", "5") + + // TODO test concurrency + + // TODO closure + + // TODO test refresh + + ////// LOGS AND ERRORS + const logs1 = await getLogs(page, "33a2293c-6202-47ca-80d1-4a9e261cae7f") + expect(logs1).toEqual([{ class: "Info", description: "you should see this log 4", kwargs: {} }]) + await submit_ev_input("logs1", "90") + + // TODO + // const logs2 = await getLogs(page, "33a2293c-6202-47ca-80d1-4a9e261cae7f") + // expect(logs2).toEqual([ + // { class: "Info", description: "you should see this log 4", kwargs: {} }, + // { class: "Info", description: "you should see this log 1", kwargs: {} }, + // ]) + + // TODO RERUN cELL + + const logs3 = await getLogs(page, "480aea45-da00-4e89-b43a-38e4d1827ec2") + expect(logs3.length).toEqual(2) + expect(logs3[0]).toEqual({ class: "Warn", description: "You should see the following error:", kwargs: {} }) + expect(logs3[1].class).toEqual("Error") + expect(logs3[1].description).toContain("with_js_link") + expect(logs3[1].kwargs.input).toEqual('"coOL"') + expect(logs3[1].kwargs.exception).toContain("You should see this error COOL") + + ////// GLOBALS + await expect_ev_output("globals", "54") + + ////// MULTIPLE IN ONE CELL + await expect_ev_output("uppercase", "ΠΑΝΑΓΙΏΤΗΣ") + await expect_ev_output("lowercase", "παναγιώτης") + + await submit_ev_input("uppercase", "wOw") + + await expect_ev_output("uppercase", "WOW") + await expect_ev_output("lowercase", "παναγιώτης") + + await submit_ev_input("lowercase", "drOEF") + + await expect_ev_output("uppercase", "WOW") + await expect_ev_output("lowercase", "droef") + + ////// REPEATED + await expect_ev_output(`length[cellid="40031867-ee3c-4aa9-884f-b76b5a9c4dec"]`, "7") + await expect_ev_output(`length[cellid="7f6ada79-8e3b-40b7-b477-ce05ae79a668"]`, "7") + + await submit_ev_input(`length[cellid="40031867-ee3c-4aa9-884f-b76b5a9c4dec"]`, "yay") + + await expect_ev_output(`length[cellid="40031867-ee3c-4aa9-884f-b76b5a9c4dec"]`, "3") + await expect_ev_output(`length[cellid="7f6ada79-8e3b-40b7-b477-ce05ae79a668"]`, "7") + }) +}) diff --git a/test/frontend/fixtures/with_js_link.jl b/test/frontend/fixtures/with_js_link.jl index bbfcd575f6..e40705bef8 100644 --- a/test/frontend/fixtures/with_js_link.jl +++ b/test/frontend/fixtures/with_js_link.jl @@ -1,5 +1,5 @@ ### A Pluto.jl notebook ### -# v0.19.32 +# v0.19.36 using Markdown using InteractiveUtils @@ -10,6 +10,7 @@ begin Pkg.activate(temp=true) Pkg.add([ Pkg.PackageSpec(name="AbstractPlutoDingetjes", rev="Display.with_js_link") + # Pkg.PackageSpec(path="/Users/fons/Documents/AbstractPlutoDingetjes.jl/") Pkg.PackageSpec(name="HypertextLiteral") ]) @@ -128,9 +129,6 @@ md""" # Concurrency """ -# ╔═╡ c79cfb05-bf14-479f-8a31-d1e4fcce79cb - - # ╔═╡ 2417de50-e6e4-4c99-8836-ee8ff04d586a 1 + 1 @@ -148,12 +146,15 @@ Test a closure some_other_global = rand(100) # ╔═╡ 3d836ff3-995e-4353-807e-bf2cd78920e2 -some_global = rand(200) +some_global = 51:81 + +# ╔═╡ 2461d75e-81dc-4e00-99e3-bbc44000579f +AbstractPlutoDingetjes.Display.with_js_link(x -> x) # ╔═╡ 12e64b86-3866-4e21-9af5-0e546452b4e1 -function function_evaluator(f::Function, default="") +function function_evaluator(f::Function, default=""; id=string(f)) @htl(""" -
+

Input:
 

@@ -163,6 +164,7 @@ function function_evaluator(f::Function, default="") let sqrt_with_julia = $(AbstractPlutoDingetjes.Display.with_js_link(f)) let wrapper = currentScript.closest("div") + wrapper.setAttribute("cellid", currentScript.closest("pluto-cell").id) let input = wrapper.querySelector("input") let submit = wrapper.querySelector("input[type='submit']") @@ -182,18 +184,13 @@ function function_evaluator(f::Function, default="") end # ╔═╡ 4b80dda0-74b6-4a0e-a50e-61c5380111a4 -function_evaluator(123) do input +function_evaluator(900; id="sqrt") do input num = parse(Float64, input) sqrt(num) end -# ╔═╡ f9ff5f6b-7525-4928-a19c-2d0bf56fd952 -function_evaluator(123) do input - sqrt(-1) -end - # ╔═╡ a399cb12-39d4-43c0-a0a7-05cb683dffbd -function_evaluator(123) do input +function_evaluator(123; id="c1") do input # @info "start" sleep(5) # @warn "end" @@ -202,7 +199,7 @@ function_evaluator(123) do input end # ╔═╡ 2bff3975-5918-40fe-9761-eb7b47f16df2 -function_evaluator(123) do input +function_evaluator(123; id="c2") do input # @info "start" sleep(5) # @warn "end" @@ -234,7 +231,7 @@ function_evaluator("THIS IN LOWERCASE") do input end # ╔═╡ 33a2293c-6202-47ca-80d1-4a9e261cae7f -function_evaluator(4) do input +function_evaluator(4; id="logs1") do input @info "you should see this log $(input)" println("(not currently supported) you should see this print $(input)") @@ -242,10 +239,10 @@ function_evaluator(4) do input end # ╔═╡ 480aea45-da00-4e89-b43a-38e4d1827ec2 -function_evaluator(4) do input +function_evaluator("coOL") do input @warn("You should see the following error:") - error("You should see this error $(input)") + error("You should see this error $(uppercase(input))") end # ╔═╡ b310dd30-dddd-4b75-81d2-aaf35c9dd1d3 @@ -256,7 +253,7 @@ function_evaluator(4) do input end # ╔═╡ 58999fba-6631-4482-a811-12bf2412d65e -function_evaluator(4) do input +function_evaluator(4; id="globals") do input some_global[parse(Int, input)] end @@ -309,8 +306,6 @@ end # ╠═e8abaff9-f629-47c6-8009-066bcdf67693 # ╟─bf9861e0-be91-4041-aa61-8ac2ef6cb719 # ╟─ebf79ee4-2590-4b5a-a957-213ed03a5921 -# ╠═c79cfb05-bf14-479f-8a31-d1e4fcce79cb -# ╠═f9ff5f6b-7525-4928-a19c-2d0bf56fd952 # ╠═a399cb12-39d4-43c0-a0a7-05cb683dffbd # ╠═2bff3975-5918-40fe-9761-eb7b47f16df2 # ╠═2417de50-e6e4-4c99-8836-ee8ff04d586a @@ -325,6 +320,7 @@ end # ╠═b310dd30-dddd-4b75-81d2-aaf35c9dd1d3 # ╠═3d836ff3-995e-4353-807e-bf2cd78920e2 # ╠═58999fba-6631-4482-a811-12bf2412d65e +# ╠═2461d75e-81dc-4e00-99e3-bbc44000579f # ╠═12e64b86-3866-4e21-9af5-0e546452b4e1 # ╟─9e5c0f8d-6ac1-4aee-a00d-938f17eec146 # ╠═306d03da-cd50-4b0c-a5dd-7ec1a278cde1 diff --git a/test/frontend/helpers/common.js b/test/frontend/helpers/common.js index c0578974b4..2a48d516a6 100644 --- a/test/frontend/helpers/common.js +++ b/test/frontend/helpers/common.js @@ -66,7 +66,7 @@ const with_connections_debug = (page, action) => { export const getTextContent = (page, selector) => { // https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent#differences_from_innertext return page.evaluate( - (selector) => document.querySelector(selector).innerText, + (selector) => document.querySelector(selector).textContent, selector ); }; @@ -127,18 +127,23 @@ export const waitForContentToChange = async ( return getTextContent(page, selector); }; -export const waitForContentToBecome = async (page, selector, targetContent) => { +export const waitForContentToBecome = async (/** @type {puppeteer.Page} */ page, /** @type {string} */ selector, /** @type {string} */ targetContent) => { await page.waitForSelector(selector, { visible: true }); - await page.waitForFunction( + try{ + await page.waitForFunction( (selector, targetContent) => { const element = document.querySelector(selector); // https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent#differences_from_innertext - return element !== null && element.innerText === targetContent; + return element !== null && element.textContent === targetContent; }, { polling: 100 }, selector, targetContent ); + } catch(e) { + console.log("Failed! Current content:", await getTextContent(page, selector)) + throw(e) + } return getTextContent(page, selector); }; diff --git a/test/frontend/helpers/pluto.js b/test/frontend/helpers/pluto.js index 6a92bd9d32..fc971b3144 100644 --- a/test/frontend/helpers/pluto.js +++ b/test/frontend/helpers/pluto.js @@ -192,6 +192,22 @@ export const waitForNoUpdateOngoing = async (page, options = {}) => { ) } +export const getLogs = async (page, cellid) => { + return await page.evaluate((cellid) => { + const logs = document.querySelector(`pluto-cell[id="${cellid}"] pluto-logs`) + return Array.from(logs.children).map((el) => ({ + class: el.className.trim(), + description: el.querySelector("pluto-log-dot > pre").textContent, + kwargs: Object.fromEntries( + Array.from(el.querySelectorAll("pluto-log-dot-kwarg")).map((x) => [ + x.querySelector("pluto-key").textContent, + x.querySelector("pluto-value").textContent, + ]) + ), + })) + }, cellid) +} + /** * @param {Page} page */ From 258dd0012ea0bea6a43d26dc8db1c72664a93d83 Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Mon, 15 Jan 2024 19:35:39 +0100 Subject: [PATCH 08/19] some more --- test/frontend/__tests__/with_js_link.js | 90 ++++++++++++++----------- test/frontend/helpers/pluto.js | 8 ++- 2 files changed, 55 insertions(+), 43 deletions(-) diff --git a/test/frontend/__tests__/with_js_link.js b/test/frontend/__tests__/with_js_link.js index a7f33f91ba..9de3515b96 100644 --- a/test/frontend/__tests__/with_js_link.js +++ b/test/frontend/__tests__/with_js_link.js @@ -1,6 +1,6 @@ import puppeteer from "puppeteer" import { saveScreenshot, createPage, waitForContentToBecome, getTextContent } from "../helpers/common" -import { importNotebook, getPlutoUrl, shutdownCurrentNotebook, setupPlutoBrowser, getLogs } from "../helpers/pluto" +import { importNotebook, getPlutoUrl, shutdownCurrentNotebook, setupPlutoBrowser, getLogs, getLogSelector } from "../helpers/pluto" describe("with_js_link", () => { /** @@ -14,67 +14,77 @@ describe("with_js_link", () => { let page = null beforeAll(async () => { browser = await setupPlutoBrowser() - }) - beforeEach(async () => { page = await createPage(browser) await page.goto(getPlutoUrl(), { waitUntil: "networkidle0" }) + + await importNotebook(page, "with_js_link.jl", { timeout: 120 * 1000 }) }) + beforeEach(async () => {}) afterEach(async () => { await saveScreenshot(page) + }) + afterAll(async () => { await shutdownCurrentNotebook(page) await page.close() page = null - }) - afterAll(async () => { await browser.close() browser = null }) - it("Should correctly show published_to_js in cell output, and in logs", async () => { - await importNotebook(page, "with_js_link.jl", { timeout: 120 * 1000 }) + const submit_ev_input = (id, value) => + page.evaluate( + (id, value) => { + document.querySelector(`.function_evaluator#${id} input`).value = value - const submit_ev_input = (id, value) => - page.evaluate( - (id, value) => { - document.querySelector(`.function_evaluator#${id} input`).value = value + document.querySelector(`.function_evaluator#${id} input[type="submit"]`).click() + }, + id, + value + ) - document.querySelector(`.function_evaluator#${id} input[type="submit"]`).click() - }, - id, - value - ) + const ev_output_sel = (id) => `.function_evaluator#${id} textarea` - const ev_output_sel = (id) => `.function_evaluator#${id} textarea` - - const expect_ev_output = async (id, expected) => { - expect(await waitForContentToBecome(page, ev_output_sel(id), expected)).toBe(expected) - } + const expect_ev_output = async (id, expected) => { + expect(await waitForContentToBecome(page, ev_output_sel(id), expected)).toBe(expected) + } + it("basic", async () => { ////// BASIC await expect_ev_output("sqrt", "30") await submit_ev_input("sqrt", "25") await expect_ev_output("sqrt", "5") + }) - // TODO test concurrency + // TODO test concurrency - // TODO closure + // TODO closure - // TODO test refresh + // TODO test refresh - ////// LOGS AND ERRORS - const logs1 = await getLogs(page, "33a2293c-6202-47ca-80d1-4a9e261cae7f") + // TODO RERUN cELL + + it("LOGS AND ERRORS", async () => { + ////// + let log_id = "33a2293c-6202-47ca-80d1-4a9e261cae7f" + const logs1 = await getLogs(page, log_id) expect(logs1).toEqual([{ class: "Info", description: "you should see this log 4", kwargs: {} }]) await submit_ev_input("logs1", "90") // TODO - // const logs2 = await getLogs(page, "33a2293c-6202-47ca-80d1-4a9e261cae7f") - // expect(logs2).toEqual([ - // { class: "Info", description: "you should see this log 4", kwargs: {} }, - // { class: "Info", description: "you should see this log 1", kwargs: {} }, - // ]) - - // TODO RERUN cELL - + await page.waitForFunction( + (sel) => { + return document.querySelector(sel).textContent.includes("90") + }, + { polling: 100 }, + getLogSelector(log_id) + ) + const logs2 = await getLogs(page, log_id) + expect(logs2).toEqual([ + { class: "Info", description: "you should see this log 4", kwargs: {} }, + { class: "Info", description: "you should see this log 90", kwargs: {} }, + ]) + }) + it("LOGS AND ERRORS 2", async () => { const logs3 = await getLogs(page, "480aea45-da00-4e89-b43a-38e4d1827ec2") expect(logs3.length).toEqual(2) expect(logs3[0]).toEqual({ class: "Warn", description: "You should see the following error:", kwargs: {} }) @@ -82,11 +92,11 @@ describe("with_js_link", () => { expect(logs3[1].description).toContain("with_js_link") expect(logs3[1].kwargs.input).toEqual('"coOL"') expect(logs3[1].kwargs.exception).toContain("You should see this error COOL") - - ////// GLOBALS + }) + it("globals", async () => { await expect_ev_output("globals", "54") - - ////// MULTIPLE IN ONE CELL + }) + it("multiple in one cell", async () => { await expect_ev_output("uppercase", "ΠΑΝΑΓΙΏΤΗΣ") await expect_ev_output("lowercase", "παναγιώτης") @@ -99,8 +109,8 @@ describe("with_js_link", () => { await expect_ev_output("uppercase", "WOW") await expect_ev_output("lowercase", "droef") - - ////// REPEATED + }) + it("repeated", async () => { await expect_ev_output(`length[cellid="40031867-ee3c-4aa9-884f-b76b5a9c4dec"]`, "7") await expect_ev_output(`length[cellid="7f6ada79-8e3b-40b7-b477-ce05ae79a668"]`, "7") diff --git a/test/frontend/helpers/pluto.js b/test/frontend/helpers/pluto.js index fc971b3144..87e4540ca9 100644 --- a/test/frontend/helpers/pluto.js +++ b/test/frontend/helpers/pluto.js @@ -192,9 +192,11 @@ export const waitForNoUpdateOngoing = async (page, options = {}) => { ) } +export const getLogSelector = (cellId) => `pluto-cell[id="${cellId}"] pluto-logs` + export const getLogs = async (page, cellid) => { - return await page.evaluate((cellid) => { - const logs = document.querySelector(`pluto-cell[id="${cellid}"] pluto-logs`) + return await page.evaluate((sel) => { + const logs = document.querySelector(sel) return Array.from(logs.children).map((el) => ({ class: el.className.trim(), description: el.querySelector("pluto-log-dot > pre").textContent, @@ -205,7 +207,7 @@ export const getLogs = async (page, cellid) => { ]) ), })) - }, cellid) + }, getLogSelector(cellid)) } /** From 8370523b31116b32033c01435afc34e900f119db Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Tue, 16 Jan 2024 14:41:17 +0100 Subject: [PATCH 09/19] more testzzts --- test/frontend/__tests__/with_js_link.js | 30 +++++++++++++++++++++---- test/frontend/fixtures/with_js_link.jl | 8 +++---- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/test/frontend/__tests__/with_js_link.js b/test/frontend/__tests__/with_js_link.js index 9de3515b96..8ff4931398 100644 --- a/test/frontend/__tests__/with_js_link.js +++ b/test/frontend/__tests__/with_js_link.js @@ -55,14 +55,12 @@ describe("with_js_link", () => { await expect_ev_output("sqrt", "5") }) - // TODO test concurrency - - // TODO closure - // TODO test refresh // TODO RERUN cELL + // TODO invalidation + it("LOGS AND ERRORS", async () => { ////// let log_id = "33a2293c-6202-47ca-80d1-4a9e261cae7f" @@ -93,6 +91,16 @@ describe("with_js_link", () => { expect(logs3[1].kwargs.input).toEqual('"coOL"') expect(logs3[1].kwargs.exception).toContain("You should see this error COOL") }) + it("LOGS AND ERRORS 3: assertpackable", async () => { + const logs = await getLogs(page, "b310dd30-dddd-4b75-81d2-aaf35c9dd1d3") + expect(logs.length).toEqual(2) + expect(logs[0]).toEqual({ class: "Warn", description: "You should see the assertpackable fail after this log", kwargs: {} }) + expect(logs[1].class).toEqual("Error") + expect(logs[1].description).toContain("with_js_link") + expect(logs[1].kwargs.input).toEqual('"4"') + expect(logs[1].kwargs.exception).toContain("Only simple objects can be shared with JS") + }) + it("globals", async () => { await expect_ev_output("globals", "54") }) @@ -119,4 +127,18 @@ describe("with_js_link", () => { await expect_ev_output(`length[cellid="40031867-ee3c-4aa9-884f-b76b5a9c4dec"]`, "3") await expect_ev_output(`length[cellid="7f6ada79-8e3b-40b7-b477-ce05ae79a668"]`, "7") }) + + it("concurrency", async () => { + await expect_ev_output("c1", "C1") + await expect_ev_output("c2", "C2") + + await submit_ev_input("c1", "cc1") + await submit_ev_input("c2", "cc2") + + await page.waitForTimeout(4000) + + // they should run in parallel: after 4 seconds both should be finished + expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c1"))).toBe("CC1") + expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c2"))).toBe("CC2") + }) }) diff --git a/test/frontend/fixtures/with_js_link.jl b/test/frontend/fixtures/with_js_link.jl index e40705bef8..de7413edfe 100644 --- a/test/frontend/fixtures/with_js_link.jl +++ b/test/frontend/fixtures/with_js_link.jl @@ -190,18 +190,18 @@ function_evaluator(900; id="sqrt") do input end # ╔═╡ a399cb12-39d4-43c0-a0a7-05cb683dffbd -function_evaluator(123; id="c1") do input +function_evaluator("c1"; id="c1") do input # @info "start" - sleep(5) + sleep(3) # @warn "end" uppercase(input) end # ╔═╡ 2bff3975-5918-40fe-9761-eb7b47f16df2 -function_evaluator(123; id="c2") do input +function_evaluator("c2"; id="c2") do input # @info "start" - sleep(5) + sleep(3) # @warn "end" uppercase(input) end From 4ef1c2572ecc62230788a2aa3119af506c85419f Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Wed, 17 Jan 2024 10:28:41 +0100 Subject: [PATCH 10/19] Update with_js_link.jl --- test/frontend/fixtures/with_js_link.jl | 46 ++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/test/frontend/fixtures/with_js_link.jl b/test/frontend/fixtures/with_js_link.jl index de7413edfe..00d87b90fe 100644 --- a/test/frontend/fixtures/with_js_link.jl +++ b/test/frontend/fixtures/with_js_link.jl @@ -18,6 +18,9 @@ begin using HypertextLiteral end +# ╔═╡ 5e42ea32-a1ce-49db-b55f-5e252c8c3f57 +using Dates + # ╔═╡ 37aacc7f-61fd-4c4b-b24d-42361d508e8d @htl(""" """ @@ -53,7 +53,7 @@ describe("JavaScript API", () => { ) await runAllChanged(page) await waitForPlutoToCalmDown(page, { polling: 100 }) - const initialLastCellContent = await waitForContentToBecome(page, `pluto-cell:last-child pluto-output`, expected) + const initialLastCellContent = await waitForContentToBecome(page, `pluto-cell:last-child pluto-output find-me`, expected) expect(initialLastCellContent).toBe(expected) }) @@ -69,7 +69,7 @@ describe("JavaScript API", () => { ) await runAllChanged(page) await waitForPlutoToCalmDown(page, { polling: 100 }) - let initialLastCellContent = await waitForContentToBecome(page, `pluto-cell:last-child pluto-output`, expected) + let initialLastCellContent = await waitForContentToBecome(page, `pluto-cell:last-child pluto-output span`, expected) expect(initialLastCellContent).toBe(expected) await paste( @@ -84,7 +84,7 @@ describe("JavaScript API", () => { ) await runAllChanged(page) await waitForPlutoToCalmDown(page, { polling: 100 }) - initialLastCellContent = await waitForContentToBecome(page, `pluto-cell:last-child pluto-output`, expected) + initialLastCellContent = await waitForContentToBecome(page, `pluto-cell:last-child pluto-output span`, expected) expect(initialLastCellContent).toBe(expected) }) diff --git a/test/frontend/__tests__/slide_controls.js b/test/frontend/__tests__/slide_controls.js index 15a50e07ea..b4949ad828 100644 --- a/test/frontend/__tests__/slide_controls.js +++ b/test/frontend/__tests__/slide_controls.js @@ -45,7 +45,7 @@ describe("slideControls", () => { await importNotebook(page, "slides.jl", { permissionToRunCode: false }) const plutoCellIds = await getCellIds(page) const content = await waitForContent(page, `pluto-cell[id="${plutoCellIds[1]}"] pluto-output`) - expect(content).toBe("Slide 2") + expect(content).toBe("Slide 2\n") const slide_1_title = await page.$(`pluto-cell[id="${plutoCellIds[0]}"] pluto-output h1`) const slide_2_title = await page.$(`pluto-cell[id="${plutoCellIds[1]}"] pluto-output h1`) diff --git a/test/frontend/__tests__/wind_directions.js b/test/frontend/__tests__/wind_directions.js index b7a06bb688..b848e2b39d 100644 --- a/test/frontend/__tests__/wind_directions.js +++ b/test/frontend/__tests__/wind_directions.js @@ -95,14 +95,14 @@ describe("wind_directions", () => { ).toBe(expected) } - await expect_chosen_directions('chosen_directions_copy\n"North"') + await expect_chosen_directions('chosen_directions_copyString1"North"') expect(await page.evaluate((sel) => document.querySelector(sel).checked, checkbox_selector(0))).toBe(true) await page.click(checkbox_selector(2)) await waitForPlutoToCalmDown(page) - await expect_chosen_directions('chosen_directions_copy\n"North"\n"South"') + await expect_chosen_directions('chosen_directions_copyString1"North"2"South"') expect(await page.evaluate((sel) => document.querySelector(sel).checked, checkbox_selector(0))).toBe(true) expect(await page.evaluate((sel) => document.querySelector(sel).checked, checkbox_selector(1))).toBe(false) diff --git a/test/frontend/__tests__/with_js_link.js b/test/frontend/__tests__/with_js_link.js index 8fff99f64c..c4f2c96216 100644 --- a/test/frontend/__tests__/with_js_link.js +++ b/test/frontend/__tests__/with_js_link.js @@ -1,6 +1,16 @@ import puppeteer from "puppeteer" import { saveScreenshot, createPage, waitForContentToBecome, getTextContent } from "../helpers/common" -import { importNotebook, getPlutoUrl, shutdownCurrentNotebook, setupPlutoBrowser, getLogs, getLogSelector } from "../helpers/pluto" +import { + importNotebook, + getPlutoUrl, + shutdownCurrentNotebook, + setupPlutoBrowser, + getLogs, + getLogSelector, + writeSingleLineInPlutoInput, + runAllChanged, + waitForPlutoToCalmDown, +} from "../helpers/pluto" describe("with_js_link", () => { /** @@ -137,16 +147,48 @@ describe("with_js_link", () => { await page.waitForTimeout(4000) + // NOT // they dont run in parallel so right now only cc1 should be finished - expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c1"))).toBe("CC1") - expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c2"))).toBe("C2") + // expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c1"))).toBe("CC1") + // expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c2"))).toBe("C2") - await expect_ev_output("c1", "CC1") - await expect_ev_output("c2", "CC2") + // await expect_ev_output("c1", "CC1") + // await expect_ev_output("c2", "CC2") - // NOT // they should run in parallel: after 4 seconds both should be finished - // expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c1"))).toBe("CC1") - // expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c2"))).toBe("CC2") + expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c1"))).toBe("CC1") + expect(await page.evaluate((s) => document.querySelector(s).textContent, ev_output_sel("c2"))).toBe("CC2") + }) + + const expect_jslog = async (expected) => { + expect(await waitForContentToBecome(page, "#checkme", expected)).toBe(expected) + } + it("js errors", async () => { + await waitForPlutoToCalmDown(page) + await page.waitForTimeout(100) + await expect_jslog("hello!") + await page.click("#jslogbtn") + await page.waitForTimeout(500) + await page.click("#jslogbtn") + await page.waitForTimeout(100) + + // We clicked twice, but sometimes it only registers one click for some reason. I don't care, so let's check for either. + let prefix = await Promise.race([ + waitForContentToBecome(page, "#checkme", "hello!clickyay KRATJE"), + waitForContentToBecome(page, "#checkme", "hello!clickclickyay KRATJEyay KRATJE"), + ]) + + const yolotriggerid = "8782cc14-eb1a-48a8-a114-2f71f77be275" + await page.click(`pluto-cell[id="${yolotriggerid}"] pluto-output input[type="button"]`) + await expect_jslog(`${prefix}hello!`) + await page.click("#jslogbtn") + await expect_jslog(`${prefix}hello!clicknee exception in Julia callback:ErrorException("bad")`) + + await page.click("#jslogbtn") + await page.waitForTimeout(500) + + await page.click(`pluto-cell[id="${yolotriggerid}"] .runcell`) + + await expect_jslog(`${prefix}hello!clicknee exception in Julia callback:ErrorException("bad")clickhello!nee link not found`) }) }) diff --git a/test/frontend/fixtures/slides.jl b/test/frontend/fixtures/slides.jl index d903c7dce1..fe0f3262e7 100644 --- a/test/frontend/fixtures/slides.jl +++ b/test/frontend/fixtures/slides.jl @@ -1,5 +1,5 @@ ### A Pluto.jl notebook ### -# v0.11.14 +# v0.19.40 using Markdown using InteractiveUtils diff --git a/test/frontend/fixtures/wind_directions.jl b/test/frontend/fixtures/wind_directions.jl index 7f3d48f700..a3f805598c 100644 --- a/test/frontend/fixtures/wind_directions.jl +++ b/test/frontend/fixtures/wind_directions.jl @@ -1,5 +1,5 @@ ### A Pluto.jl notebook ### -# v0.19.31 +# v0.19.40 using Markdown using InteractiveUtils @@ -679,6 +679,7 @@ version = "1.2.0" [[ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +version = "1.1.1" [[Artifacts]] uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" @@ -698,6 +699,11 @@ git-tree-sha1 = "532c4185d3c9037c0237546d817858b23cf9e071" uuid = "a80b9123-70ca-4bc0-993e-6e3bcb318db6" version = "0.8.12" +[[CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +version = "1.0.5+1" + [[Crayons]] git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" @@ -708,8 +714,12 @@ deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" [[Downloads]] -deps = ["ArgTools", "LibCURL", "NetworkOptions"] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +version = "1.6.0" + +[[FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" [[FixedPointNumbers]] deps = ["Statistics"] @@ -748,24 +758,32 @@ version = "0.21.4" [[LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" +version = "0.6.4" [[LibCURL_jll]] deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" +version = "8.4.0+0" [[LibGit2]] -deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" +[[LibGit2_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] +uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" +version = "1.6.4+0" + [[LibSSH2_jll]] deps = ["Artifacts", "Libdl", "MbedTLS_jll"] uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.11.0+1" [[Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" [[LinearAlgebra]] -deps = ["Libdl"] +deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" [[Logging]] @@ -789,15 +807,23 @@ version = "0.1.1" [[MbedTLS_jll]] deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.2+1" [[Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" [[MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +version = "2023.1.10" [[NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.2.0" + +[[OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" +version = "0.3.23+2" [[Parsers]] deps = ["Dates", "PrecompileTools", "UUIDs"] @@ -806,8 +832,9 @@ uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" version = "2.7.2" [[Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "1.10.0" [[PlutoUI]] deps = ["AbstractPlutoDingetjes", "Base64", "ColorTypes", "Dates", "FixedPointNumbers", "Hyperscript", "HypertextLiteral", "IOCapture", "InteractiveUtils", "JSON", "Logging", "MIMEs", "Markdown", "Random", "Reexport", "URIs", "UUIDs"] @@ -836,7 +863,7 @@ deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[Random]] -deps = ["Serialization"] +deps = ["SHA"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [[Reexport]] @@ -846,6 +873,7 @@ version = "1.2.2" [[SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" [[Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" @@ -854,20 +882,29 @@ uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" uuid = "6462fe0b-24de-5631-8697-dd941f90decc" [[SparseArrays]] -deps = ["LinearAlgebra", "Random"] +deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +version = "1.10.0" [[Statistics]] deps = ["LinearAlgebra", "SparseArrays"] uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +version = "1.10.0" + +[[SuiteSparse_jll]] +deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] +uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" +version = "7.2.1+1" [[TOML]] deps = ["Dates"] uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +version = "1.0.3" [[Tar]] deps = ["ArgTools", "SHA"] uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +version = "1.10.0" [[Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] @@ -893,14 +930,22 @@ uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" [[Zlib_jll]] deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" +version = "1.2.13+1" + +[[libblastrampoline_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" +version = "5.8.0+1" [[nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" +version = "1.52.0+1" [[p7zip_jll]] deps = ["Artifacts", "Libdl"] uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "17.4.0+2" """ # ╔═╡ Cell order: diff --git a/test/frontend/fixtures/with_js_link.jl b/test/frontend/fixtures/with_js_link.jl index 47f01665c3..49fc09b4b6 100644 --- a/test/frontend/fixtures/with_js_link.jl +++ b/test/frontend/fixtures/with_js_link.jl @@ -22,9 +22,11 @@ begin Pkg.PackageSpec(name="AbstractPlutoDingetjes", rev="Display.with_js_link") # Pkg.PackageSpec(path="/Users/fons/Documents/AbstractPlutoDingetjes.jl/") Pkg.PackageSpec(name="HypertextLiteral") + Pkg.PackageSpec(name="PlutoUI") ]) using AbstractPlutoDingetjes + using PlutoUI using HypertextLiteral end @@ -60,23 +62,25 @@ end # ╔═╡ 37fc039e-7a4d-4d2d-80f3-d409a9ee096d # ╠═╡ disabled = true #=╠═╡ -let - function f(x) - cool(x) - end - @htl(""" - - """) -end +# +# """) +# end ╠═╡ =# # ╔═╡ 977c59f7-9f3a-40ae-981d-2a8a48e08349 @@ -145,23 +149,6 @@ md""" # ╔═╡ 60444c4c-5705-4b92-8eac-2c102f14f395 -# ╔═╡ 5fe4fc2e-a502-4b05-bc97-6b6c3525d7d3 -md""" -# Not a background task - -JS link calculations are not background tasks – they run in sequence will all other computations in the notebook. If a Julia calculation is requested with a JS link, this task will be added to the notebook's execution queue. This means: -- JS link calculations **do not run in parallel** to other computations in the notebook. -- If something else is running in the notebook, like running cells, then the JS link requests will wait for other computations to finish before executing. -- Vice versa, if your JS link requests take a long time to execute, then all other activity in the notebook (like `@bind`) is blocked. - -!!! warning "Don't make too many requests!" - If you make too many requests from JS, then the notebook becomes almost unusable. As a developer using this API, you need to take care to keep your users' notebooks responsive. - - The JS link request returns a `Promise` that resolves to the response. Consider keeping track of whether you are currently requesting something from Julia, and avoid making more requests in the meantime. - - It can also help to use **throttling** or **debouncing** to reduce the number of requests that you make. -""" - # ╔═╡ 07c832c1-fd8f-44de-bdfa-389048c1e4e9 md""" ## With a function in the closure @@ -302,7 +289,7 @@ end # ╔═╡ 480aea45-da00-4e89-b43a-38e4d1827ec2 function_evaluator("coOL") do input - @warn("You should see the following error:", [1,2,"asdf"]) + @warn("You should see the following error:") error("You should see this error $(uppercase(input))") end @@ -354,6 +341,48 @@ let HTML(html_repr) end +# ╔═╡ 8782cc14-eb1a-48a8-a114-2f71f77be275 +@bind yolotrigger CounterButton() + +# ╔═╡ e5df2451-f4b9-4511-b25f-1a5e463f3eb2 +name = yolotrigger > 0 ? "krat" : "kratje" + +# ╔═╡ 3c5c1325-ad3e-4c54-8d29-c17939bb8529 +function useme(x) + length(x) > 5 ? uppercase(x) : error("bad") +end + +# ╔═╡ 6c5f79b9-598d-41ad-800d-0a9ff63d6f6c +@htl(""" + + +""") + # ╔═╡ Cell order: # ╠═b0f2a778-885f-11ee-3d28-939ca4069ee8 # ╠═4b80dda0-74b6-4a0e-a50e-61c5380111a4 @@ -372,7 +401,6 @@ end # ╠═5e42ea32-a1ce-49db-b55f-5e252c8c3f57 # ╠═60444c4c-5705-4b92-8eac-2c102f14f395 # ╠═2bff3975-5918-40fe-9761-eb7b47f16df2 -# ╟─5fe4fc2e-a502-4b05-bc97-6b6c3525d7d3 # ╟─07c832c1-fd8f-44de-bdfa-389048c1e4e9 # ╠═10d80b00-f7ab-4bd7-9ea7-cca98c089e9c # ╠═53e60352-3a56-4b5c-9568-1ac58b758497 @@ -398,3 +426,7 @@ end # ╠═7f6ada79-8e3b-40b7-b477-ce05ae79a668 # ╟─f344c4cb-8226-4145-ab92-a37542f697dd # ╠═8bbd32f8-56f7-4f29-aea8-6906416f6cfd +# ╠═8782cc14-eb1a-48a8-a114-2f71f77be275 +# ╠═e5df2451-f4b9-4511-b25f-1a5e463f3eb2 +# ╠═3c5c1325-ad3e-4c54-8d29-c17939bb8529 +# ╠═6c5f79b9-598d-41ad-800d-0a9ff63d6f6c diff --git a/test/frontend/helpers/common.js b/test/frontend/helpers/common.js index 2a48d516a6..b911e08654 100644 --- a/test/frontend/helpers/common.js +++ b/test/frontend/helpers/common.js @@ -66,7 +66,7 @@ const with_connections_debug = (page, action) => { export const getTextContent = (page, selector) => { // https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent#differences_from_innertext return page.evaluate( - (selector) => document.querySelector(selector).textContent, + (selector) => document.querySelector(selector)?.textContent, selector ); }; @@ -141,7 +141,7 @@ export const waitForContentToBecome = async (/** @type {puppeteer.Page} */ page, targetContent ); } catch(e) { - console.log("Failed! Current content:", await getTextContent(page, selector)) + console.error("Failed! Current content:", JSON.stringify(await getTextContent(page, selector)), JSON.stringify(targetContent)) throw(e) } return getTextContent(page, selector); @@ -216,7 +216,7 @@ export const createPage = async (browser) => { return page }; -let testname = () => expect.getState()?.currentTestName?.replace(/ /g, "_") ?? "unnkown"; +let testname = () => expect.getState()?.currentTestName?.replace(/[ \:]/g, "_") ?? "unnkown"; export const lastElement = (arr) => arr[arr.length - 1]; From 2573c746bc6fcebc4e8274afd055aa6b1c03c114 Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Wed, 28 Feb 2024 17:05:33 +0100 Subject: [PATCH 18/19] update logger --- src/runner/PlutoRunner/src/PlutoRunner.jl | 56 ++++++++++------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/src/runner/PlutoRunner/src/PlutoRunner.jl b/src/runner/PlutoRunner/src/PlutoRunner.jl index 315e4c78f4..7dd41dff27 100644 --- a/src/runner/PlutoRunner/src/PlutoRunner.jl +++ b/src/runner/PlutoRunner/src/PlutoRunner.jl @@ -283,14 +283,9 @@ function try_macroexpand(mod::Module, notebook_id::UUID, cell_id::UUID, expr; ca Expr(:block, expr) end - logger = get!(() -> PlutoCellLogger(notebook_id, cell_id), pluto_cell_loggers, cell_id) - if logger.workspace_count < moduleworkspace_count[] - logger = pluto_cell_loggers[cell_id] = PlutoCellLogger(notebook_id, cell_id) - end - - capture_logger = CaptureLogger(nothing, logger, Dict[]) + capture_logger = CaptureLogger(nothing, get_cell_logger(notebook_id, cell_id), Dict[]) - expanded_expr, elapsed_ns = with_logger_and_io_to_logs(capture_logger; capture_stdout, stdio_loglevel=stdout_log_level) do + expanded_expr, elapsed_ns = with_logger_and_io_to_logs(capture_logger; capture_stdout) do elapsed_ns = time_ns() expanded_expr = macroexpand(mod, expr_not_toplevel)::Expr elapsed_ns = time_ns() - elapsed_ns @@ -531,10 +526,7 @@ function run_expression( old_currently_running_cell_id = currently_running_cell_id[] currently_running_cell_id[] = cell_id - logger = get!(() -> PlutoCellLogger(notebook_id, cell_id), pluto_cell_loggers, cell_id) - if logger.workspace_count < moduleworkspace_count[] - logger = pluto_cell_loggers[cell_id] = PlutoCellLogger(notebook_id, cell_id) - end + logger = get_cell_logger(notebook_id, cell_id) # reset published objects cell_published_objects[cell_id] = Dict{String,Any}() @@ -596,7 +588,7 @@ function run_expression( throw("Expression still contains macro calls!!") end - result, runtime = with_logger_and_io_to_logs(logger; capture_stdout, stdio_loglevel=stdout_log_level) do # about 200ns + 3ms overhead + result, runtime = with_logger_and_io_to_logs(logger; capture_stdout) do # about 200ns + 3ms overhead if function_wrapped_info === nothing toplevel_expr = Expr(:toplevel, expr) wrapped = timed_expr(toplevel_expr) @@ -934,8 +926,7 @@ function formatted_result_of( errored = ans isa CapturedException output_formatted = if (!ends_with_semicolon || errored) - logger = get!(() -> PlutoCellLogger(notebook_id, cell_id), pluto_cell_loggers, cell_id) - with_logger_and_io_to_logs(logger; capture_stdout, stdio_loglevel=stdout_log_level) do + with_logger_and_io_to_logs(get_cell_logger(notebook_id, cell_id); capture_stdout) do format_output(ans; context=IOContext( default_iocontext, :extra_items=>extra_items, @@ -2566,8 +2557,6 @@ function core_with_js_link(io, callback, on_cancellation) _cell_id = get(io, :pluto_cell_id, currently_running_cell_id[])::UUID - # TODO is this okay? prob not - # link_id = objectid2str(callback) link_id = String(rand('a':'z', 16)) links = get!(() -> Dict{String,JSLink}(), cell_js_links, _cell_id) @@ -2594,20 +2583,17 @@ end function evaluate_js_link(notebook_id::UUID, cell_id::UUID, link_id::String, input::Any) links = get(() -> Dict{String,JSLink}(), cell_js_links, cell_id) link = get(links, link_id, nothing) - if link === nothing - # TODO log to notebook - @error "🚨 AbstractPlutoDingetjes: JS link not found." link_id - - (false, "link not found") - elseif link.cancelled_ref[] - # TODO log to notebook - @error "🚨 AbstractPlutoDingetjes: JS link has already been invalidated." link_id - - (false, "link has been invalidated") - else - logger = get!(() -> PlutoCellLogger(notebook_id, cell_id), pluto_cell_loggers, cell_id) - - result = with_logger_and_io_to_logs(logger; capture_stdout=false, stdio_loglevel=stdout_log_level) do + + with_logger_and_io_to_logs(get_cell_logger(notebook_id, cell_id); capture_stdout=false) do + if link === nothing + @warn "🚨 AbstractPlutoDingetjes: JS link not found." link_id + + (false, "link not found") + elseif link.cancelled_ref[] + @warn "🚨 AbstractPlutoDingetjes: JS link has already been invalidated." link_id + + (false, "link has been invalidated") + else try result = link.callback(input) assertpackable(result) @@ -2662,6 +2648,14 @@ end const pluto_cell_loggers = Dict{UUID,PlutoCellLogger}() # One logger per cell const pluto_log_channels = Dict{UUID,Channel{Any}}() # One channel per notebook +function get_cell_logger(notebook_id, cell_id) + logger = get!(() -> PlutoCellLogger(notebook_id, cell_id), pluto_cell_loggers, cell_id) + if logger.workspace_count < moduleworkspace_count[] + logger = pluto_cell_loggers[cell_id] = PlutoCellLogger(notebook_id, cell_id) + end + logger +end + function Logging.shouldlog(logger::PlutoCellLogger, level, _module, _...) # Accept logs # - Only if the logger is the latest for this cell using the increasing workspace_count tied to each logger @@ -2824,7 +2818,7 @@ function with_io_to_logs(f::Function; enabled::Bool=true, loglevel::Logging.LogL result end -function with_logger_and_io_to_logs(f, logger; capture_stdout=true, stdio_loglevel=Logging.LogLevel(1)) +function with_logger_and_io_to_logs(f, logger; capture_stdout=true, stdio_loglevel=stdout_log_level) Logging.with_logger(logger) do with_io_to_logs(f; enabled=capture_stdout, loglevel=stdio_loglevel) end From a1c6bcde7fac0fe3c6a33f140fc133a0f65542f3 Mon Sep 17 00:00:00 2001 From: Fons van der Plas Date: Wed, 28 Feb 2024 17:34:33 +0100 Subject: [PATCH 19/19] Update common.js --- test/frontend/helpers/common.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/frontend/helpers/common.js b/test/frontend/helpers/common.js index b911e08654..c2739c6b28 100644 --- a/test/frontend/helpers/common.js +++ b/test/frontend/helpers/common.js @@ -141,7 +141,7 @@ export const waitForContentToBecome = async (/** @type {puppeteer.Page} */ page, targetContent ); } catch(e) { - console.error("Failed! Current content:", JSON.stringify(await getTextContent(page, selector)), JSON.stringify(targetContent)) + console.error("Failed! Current content: ", JSON.stringify(await getTextContent(page, selector)), "Expected content: ", JSON.stringify(targetContent)) throw(e) } return getTextContent(page, selector);