2023-05-20 01:40:56 +08:00
|
|
|
defmodule LivebookWeb.AppSessionLiveTest do
|
|
|
|
use LivebookWeb.ConnCase, async: true
|
|
|
|
|
|
|
|
import Phoenix.LiveViewTest
|
|
|
|
import Livebook.TestHelpers
|
|
|
|
|
|
|
|
alias Livebook.{App, Apps, Notebook, Utils}
|
|
|
|
|
|
|
|
test "shows a nonexisting message if the session does not exist", %{conn: conn} do
|
|
|
|
slug = Utils.random_short_id()
|
|
|
|
app_settings = %{Notebook.AppSettings.new() | slug: slug}
|
|
|
|
notebook = %{Notebook.new() | app_settings: app_settings}
|
|
|
|
|
|
|
|
{:ok, app_pid} = Apps.deploy(notebook)
|
|
|
|
|
|
|
|
{:ok, view, _} = live(conn, ~p"/apps/#{slug}/nonexistent")
|
|
|
|
assert render(view) =~ "This app session does not exist"
|
|
|
|
assert render(view) =~ ~p"/apps/#{slug}"
|
|
|
|
|
|
|
|
App.close(app_pid)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "shows a nonexisting message if the session is deactivated", %{conn: conn} do
|
|
|
|
slug = Utils.random_short_id()
|
|
|
|
app_settings = %{Notebook.AppSettings.new() | slug: slug}
|
|
|
|
notebook = %{Notebook.new() | app_settings: app_settings}
|
|
|
|
|
|
|
|
Apps.subscribe()
|
|
|
|
{:ok, app_pid} = Apps.deploy(notebook)
|
|
|
|
|
|
|
|
assert_receive {:app_created, %{pid: ^app_pid}}
|
|
|
|
|
|
|
|
assert_receive {:app_updated,
|
|
|
|
%{pid: ^app_pid, sessions: [%{id: session_id, pid: session_pid}]}}
|
|
|
|
|
|
|
|
Livebook.Session.app_deactivate(session_pid)
|
2023-05-26 19:12:41 +08:00
|
|
|
|
|
|
|
assert_receive {:app_updated,
|
|
|
|
%{pid: ^app_pid, sessions: [%{app_status: %{lifecycle: :deactivated}}]}}
|
2023-05-20 01:40:56 +08:00
|
|
|
|
|
|
|
{:ok, view, _} = live(conn, ~p"/apps/#{slug}/#{session_id}")
|
|
|
|
assert render(view) =~ "This app session does not exist"
|
|
|
|
assert render(view) =~ ~p"/apps/#{slug}"
|
|
|
|
|
|
|
|
App.close(app_pid)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "redirects to homepage if the session gets deactivated", %{conn: conn} do
|
|
|
|
slug = Utils.random_short_id()
|
|
|
|
app_settings = %{Notebook.AppSettings.new() | slug: slug}
|
|
|
|
notebook = %{Notebook.new() | app_settings: app_settings}
|
|
|
|
|
|
|
|
Apps.subscribe()
|
|
|
|
{:ok, app_pid} = Apps.deploy(notebook)
|
|
|
|
|
|
|
|
assert_receive {:app_created, %{pid: ^app_pid}}
|
|
|
|
|
|
|
|
assert_receive {:app_updated,
|
|
|
|
%{pid: ^app_pid, sessions: [%{id: session_id, pid: session_pid}]}}
|
|
|
|
|
|
|
|
{:ok, view, _} = live(conn, ~p"/apps/#{slug}/#{session_id}")
|
|
|
|
|
|
|
|
Livebook.Session.app_deactivate(session_pid)
|
|
|
|
|
|
|
|
flash = assert_redirect(view, ~p"/")
|
|
|
|
assert flash["info"] == "Session has been closed"
|
|
|
|
|
|
|
|
App.close(app_pid)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "renders only rich output when output type is rich", %{conn: conn} do
|
|
|
|
slug = Livebook.Utils.random_short_id()
|
|
|
|
app_settings = %{Livebook.Notebook.AppSettings.new() | slug: slug, output_type: :rich}
|
|
|
|
|
|
|
|
notebook = %{
|
|
|
|
Livebook.Notebook.new()
|
|
|
|
| app_settings: app_settings,
|
|
|
|
sections: [
|
|
|
|
%{
|
|
|
|
Livebook.Notebook.Section.new()
|
|
|
|
| cells: [
|
|
|
|
%{
|
|
|
|
Livebook.Notebook.Cell.new(:code)
|
2023-08-24 05:25:04 +08:00
|
|
|
| source:
|
|
|
|
source_for_output(%{
|
|
|
|
type: :terminal_text,
|
|
|
|
text: "Printed output",
|
|
|
|
chunk: false
|
|
|
|
})
|
2023-05-20 01:40:56 +08:00
|
|
|
},
|
|
|
|
%{
|
|
|
|
Livebook.Notebook.Cell.new(:code)
|
2023-08-24 05:25:04 +08:00
|
|
|
| source:
|
|
|
|
source_for_output(%{type: :plain_text, text: "Custom text", chunk: false})
|
2023-05-20 01:40:56 +08:00
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
Livebook.Apps.subscribe()
|
|
|
|
{:ok, app_pid} = Apps.deploy(notebook)
|
|
|
|
|
|
|
|
assert_receive {:app_created, %{pid: ^app_pid} = app}
|
2023-05-26 19:12:41 +08:00
|
|
|
|
|
|
|
assert_receive {:app_updated,
|
|
|
|
%{pid: ^app_pid, sessions: [%{app_status: %{execution: :executed}}]}}
|
2023-05-20 01:40:56 +08:00
|
|
|
|
|
|
|
{:ok, view, _} = conn |> live(~p"/apps/#{slug}") |> follow_redirect(conn)
|
|
|
|
|
|
|
|
refute render(view) =~ "Printed output"
|
|
|
|
assert render(view) =~ "Custom text"
|
|
|
|
|
|
|
|
Livebook.App.close(app.pid)
|
|
|
|
end
|
2023-05-26 19:12:41 +08:00
|
|
|
|
2023-07-13 18:26:10 +08:00
|
|
|
test "shows an error message when session errors", %{conn: conn, test: test} do
|
2023-05-26 19:12:41 +08:00
|
|
|
slug = Livebook.Utils.random_short_id()
|
|
|
|
app_settings = %{Livebook.Notebook.AppSettings.new() | slug: slug}
|
|
|
|
|
|
|
|
notebook = %{
|
|
|
|
Livebook.Notebook.new()
|
|
|
|
| app_settings: app_settings,
|
|
|
|
sections: [
|
|
|
|
%{
|
|
|
|
Livebook.Notebook.Section.new()
|
|
|
|
| cells: [
|
|
|
|
%{
|
|
|
|
Livebook.Notebook.Cell.new(:code)
|
2023-08-24 05:25:04 +08:00
|
|
|
| source:
|
|
|
|
source_for_output(%{
|
|
|
|
type: :terminal_text,
|
|
|
|
text: "Printed output",
|
|
|
|
chunk: false
|
|
|
|
})
|
2023-05-26 19:12:41 +08:00
|
|
|
},
|
|
|
|
%{
|
|
|
|
Livebook.Notebook.Cell.new(:code)
|
2023-07-13 18:26:10 +08:00
|
|
|
| id: "error-cell",
|
|
|
|
source: """
|
|
|
|
# Fail on the first run
|
|
|
|
unless :persistent_term.get(#{inspect(test)}, false) do
|
|
|
|
:persistent_term.put(#{inspect(test)}, true)
|
|
|
|
raise "oops"
|
|
|
|
end
|
|
|
|
"""
|
2023-05-26 19:12:41 +08:00
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
Livebook.Apps.subscribe()
|
|
|
|
{:ok, app_pid} = Apps.deploy(notebook)
|
|
|
|
|
|
|
|
assert_receive {:app_created, %{pid: ^app_pid} = app}
|
|
|
|
|
|
|
|
assert_receive {:app_updated,
|
2023-07-12 04:29:38 +08:00
|
|
|
%{
|
|
|
|
pid: ^app_pid,
|
|
|
|
sessions: [%{id: session_id, app_status: %{execution: :error}}]
|
|
|
|
}}
|
2023-05-26 19:12:41 +08:00
|
|
|
|
|
|
|
{:ok, view, _} = conn |> live(~p"/apps/#{slug}") |> follow_redirect(conn)
|
|
|
|
|
2023-07-12 04:29:38 +08:00
|
|
|
assert render(view) =~ "Printed output"
|
2023-05-26 19:12:41 +08:00
|
|
|
assert render(view) =~ "Something went wrong"
|
2023-07-12 04:29:38 +08:00
|
|
|
assert render(view) =~ ~p"/sessions/#{session_id}" <> "#cell-error-cell"
|
2023-05-26 19:12:41 +08:00
|
|
|
|
2023-07-13 18:26:10 +08:00
|
|
|
view
|
|
|
|
|> element("button", "Retry")
|
|
|
|
|> render_click()
|
|
|
|
|
|
|
|
assert_receive {:app_updated,
|
|
|
|
%{pid: ^app_pid, sessions: [%{app_status: %{execution: :executed}}]}}
|
|
|
|
|
2023-05-26 19:12:41 +08:00
|
|
|
Livebook.App.close(app.pid)
|
|
|
|
end
|
2023-07-28 00:28:14 +08:00
|
|
|
|
|
|
|
test "shows the reprocessing button when there are changed inputs and no errors",
|
|
|
|
%{conn: conn, test: test} do
|
|
|
|
slug = Livebook.Utils.random_short_id()
|
|
|
|
app_settings = %{Livebook.Notebook.AppSettings.new() | slug: slug}
|
|
|
|
|
|
|
|
Process.register(self(), test)
|
|
|
|
|
|
|
|
input = %{
|
2023-08-24 05:25:04 +08:00
|
|
|
type: :input,
|
|
|
|
ref: "ref1",
|
2023-07-28 00:28:14 +08:00
|
|
|
id: "input1",
|
2023-08-24 05:25:04 +08:00
|
|
|
destination: test,
|
|
|
|
attrs: %{type: :number, default: 1, label: "Name"}
|
2023-07-28 00:28:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
notebook = %{
|
|
|
|
Livebook.Notebook.new()
|
|
|
|
| app_settings: app_settings,
|
|
|
|
sections: [
|
|
|
|
%{
|
|
|
|
Livebook.Notebook.Section.new()
|
|
|
|
| cells: [
|
|
|
|
%{
|
|
|
|
Livebook.Notebook.Cell.new(:code)
|
2023-08-24 05:25:04 +08:00
|
|
|
| source: source_for_output(input)
|
2023-07-28 00:28:14 +08:00
|
|
|
},
|
|
|
|
%{
|
|
|
|
Livebook.Notebook.Cell.new(:code)
|
|
|
|
| source: source_for_input_read(input.id)
|
|
|
|
},
|
|
|
|
%{
|
|
|
|
Livebook.Notebook.Cell.new(:code)
|
|
|
|
| id: "error-cell",
|
|
|
|
source: """
|
|
|
|
# Fail on the first run
|
|
|
|
unless :persistent_term.get(#{inspect(test)}, false) do
|
|
|
|
:persistent_term.put(#{inspect(test)}, true)
|
|
|
|
raise "oops"
|
|
|
|
end
|
|
|
|
"""
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
Livebook.Apps.subscribe()
|
|
|
|
{:ok, app_pid} = Apps.deploy(notebook)
|
|
|
|
|
|
|
|
assert_receive {:app_created, %{pid: ^app_pid} = app}
|
|
|
|
|
|
|
|
assert_receive {:app_updated,
|
|
|
|
%{
|
|
|
|
pid: ^app_pid,
|
|
|
|
sessions: [%{pid: session_pid, app_status: %{execution: :error}}]
|
|
|
|
}}
|
|
|
|
|
|
|
|
Livebook.Session.set_input_value(session_pid, input.id, 10)
|
|
|
|
|
|
|
|
{:ok, view, _} = conn |> live(~p"/apps/#{slug}") |> follow_redirect(conn)
|
|
|
|
|
|
|
|
# The button should not appear on error
|
|
|
|
refute render(view) =~
|
|
|
|
"Some inputs have changed.\nClick this button to process with latest values."
|
|
|
|
|
|
|
|
view
|
|
|
|
|> element("button", "Retry")
|
|
|
|
|> render_click()
|
|
|
|
|
|
|
|
assert_receive {:app_updated,
|
|
|
|
%{pid: ^app_pid, sessions: [%{app_status: %{execution: :executed}}]}}
|
|
|
|
|
|
|
|
Livebook.Session.set_input_value(session_pid, input.id, 20)
|
|
|
|
Livebook.SessionHelpers.wait_for_session_update(session_pid)
|
|
|
|
|
|
|
|
assert render(view) =~
|
|
|
|
"Some inputs have changed.\nClick this button to process with latest values."
|
|
|
|
|
|
|
|
Livebook.App.close(app.pid)
|
|
|
|
end
|
2023-05-20 01:40:56 +08:00
|
|
|
end
|