mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-10-07 20:16:31 +08:00
Format Elixir code in exported LiveMarkdown (#40)
* Format Elixir code in exported LiveMarkdown * Make it possible to disable code formatting for Elixir cells * Add tests
This commit is contained in:
parent
1996dfada9
commit
33409e7564
16 changed files with 281 additions and 7 deletions
|
@ -37,14 +37,21 @@ defmodule LiveBook.LiveMarkdown.Export do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp render_cell(%{type: :elixir} = cell) do
|
defp render_cell(%{type: :elixir} = cell) do
|
||||||
|
code = get_elixir_cell_code(cell)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
```elixir
|
```elixir
|
||||||
#{cell.source}
|
#{code}
|
||||||
```\
|
```\
|
||||||
"""
|
"""
|
||||||
|> prepend_metadata(cell.metadata)
|
|> prepend_metadata(cell.metadata)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp get_elixir_cell_code(%{source: source, metadata: %{"disable_formatting" => true}}),
|
||||||
|
do: source
|
||||||
|
|
||||||
|
defp get_elixir_cell_code(%{source: source}), do: format_code(source)
|
||||||
|
|
||||||
defp render_metadata(metadata) do
|
defp render_metadata(metadata) do
|
||||||
metadata_json = Jason.encode!(metadata)
|
metadata_json = Jason.encode!(metadata)
|
||||||
"<!-- livebook:#{metadata_json} -->"
|
"<!-- livebook:#{metadata_json} -->"
|
||||||
|
@ -89,4 +96,12 @@ defmodule LiveBook.LiveMarkdown.Export do
|
||||||
[ast_node]
|
[ast_node]
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp format_code(code) do
|
||||||
|
try do
|
||||||
|
Code.format_string!(code)
|
||||||
|
rescue
|
||||||
|
_ -> code
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,11 +15,13 @@ defmodule LiveBook.Notebook do
|
||||||
|
|
||||||
alias LiveBook.Notebook.{Section, Cell}
|
alias LiveBook.Notebook.{Section, Cell}
|
||||||
|
|
||||||
|
@type metadata :: %{String.t() => term()}
|
||||||
|
|
||||||
@type t :: %__MODULE__{
|
@type t :: %__MODULE__{
|
||||||
name: String.t(),
|
name: String.t(),
|
||||||
version: String.t(),
|
version: String.t(),
|
||||||
sections: list(Section.t()),
|
sections: list(Section.t()),
|
||||||
metadata: %{String.t() => term()}
|
metadata: metadata()
|
||||||
}
|
}
|
||||||
|
|
||||||
@version "1.0"
|
@version "1.0"
|
||||||
|
|
|
@ -14,12 +14,22 @@ defmodule LiveBook.Notebook.Cell do
|
||||||
@type id :: Utils.id()
|
@type id :: Utils.id()
|
||||||
@type type :: :markdown | :elixir
|
@type type :: :markdown | :elixir
|
||||||
|
|
||||||
|
@typedoc """
|
||||||
|
Arbitrary cell information persisted as part of the notebook.
|
||||||
|
|
||||||
|
## Recognised entries
|
||||||
|
|
||||||
|
* `disable_formatting` - whether this particular cell should no be automatically formatted.
|
||||||
|
Relevant for Elixir cells only.
|
||||||
|
"""
|
||||||
|
@type metadata :: %{String.t() => term()}
|
||||||
|
|
||||||
@type t :: %__MODULE__{
|
@type t :: %__MODULE__{
|
||||||
id: id(),
|
id: id(),
|
||||||
type: type(),
|
type: type(),
|
||||||
source: String.t(),
|
source: String.t(),
|
||||||
outputs: list(),
|
outputs: list(),
|
||||||
metadata: %{String.t() => term()}
|
metadata: metadata()
|
||||||
}
|
}
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
|
|
@ -12,12 +12,13 @@ defmodule LiveBook.Notebook.Section do
|
||||||
alias LiveBook.Utils
|
alias LiveBook.Utils
|
||||||
|
|
||||||
@type id :: Utils.id()
|
@type id :: Utils.id()
|
||||||
|
@type metadata :: %{String.t() => term()}
|
||||||
|
|
||||||
@type t :: %__MODULE__{
|
@type t :: %__MODULE__{
|
||||||
id: id(),
|
id: id(),
|
||||||
name: String.t(),
|
name: String.t(),
|
||||||
cells: list(Cell.t()),
|
cells: list(Cell.t()),
|
||||||
metadata: %{String.t() => term()}
|
metadata: metadata()
|
||||||
}
|
}
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
|
|
@ -190,6 +190,14 @@ defmodule LiveBook.Session do
|
||||||
GenServer.cast(name(session_id), {:report_cell_revision, self(), cell_id, revision})
|
GenServer.cast(name(session_id), {:report_cell_revision, self(), cell_id, revision})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Asynchronously sends a cell metadata update to the server.
|
||||||
|
"""
|
||||||
|
@spec set_cell_metadata(id(), Cell.id(), Cell.metadata()) :: :ok
|
||||||
|
def set_cell_metadata(session_id, cell_id, metadata) do
|
||||||
|
GenServer.cast(name(session_id), {:set_cell_metadata, self(), cell_id, metadata})
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Asynchronously connects to the given runtime.
|
Asynchronously connects to the given runtime.
|
||||||
|
|
||||||
|
@ -365,6 +373,11 @@ defmodule LiveBook.Session do
|
||||||
{:noreply, handle_operation(state, operation)}
|
{:noreply, handle_operation(state, operation)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_cast({:set_cell_metadata, client_pid, cell_id, metadata}, state) do
|
||||||
|
operation = {:set_cell_metadata, client_pid, cell_id, metadata}
|
||||||
|
{:noreply, handle_operation(state, operation)}
|
||||||
|
end
|
||||||
|
|
||||||
def handle_cast({:connect_runtime, client_pid, runtime}, state) do
|
def handle_cast({:connect_runtime, client_pid, runtime}, state) do
|
||||||
if state.data.runtime do
|
if state.data.runtime do
|
||||||
Runtime.disconnect(state.data.runtime)
|
Runtime.disconnect(state.data.runtime)
|
||||||
|
|
|
@ -86,6 +86,7 @@ defmodule LiveBook.Session.Data do
|
||||||
| {:client_leave, pid()}
|
| {:client_leave, pid()}
|
||||||
| {:apply_cell_delta, pid(), Cell.id(), Delta.t(), cell_revision()}
|
| {:apply_cell_delta, pid(), Cell.id(), Delta.t(), cell_revision()}
|
||||||
| {:report_cell_revision, pid(), Cell.id(), cell_revision()}
|
| {:report_cell_revision, pid(), Cell.id(), cell_revision()}
|
||||||
|
| {:set_cell_metadata, pid(), Cell.id(), Cell.metadata()}
|
||||||
| {:set_runtime, pid(), Runtime.t() | nil}
|
| {:set_runtime, pid(), Runtime.t() | nil}
|
||||||
| {:set_path, pid(), String.t() | nil}
|
| {:set_path, pid(), String.t() | nil}
|
||||||
| {:mark_as_not_dirty, pid()}
|
| {:mark_as_not_dirty, pid()}
|
||||||
|
@ -361,6 +362,18 @@ defmodule LiveBook.Session.Data do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def apply_operation(data, {:set_cell_metadata, _client_pid, cell_id, metadata}) do
|
||||||
|
with {:ok, cell, _} <- Notebook.fetch_cell_and_section(data.notebook, cell_id) do
|
||||||
|
data
|
||||||
|
|> with_actions()
|
||||||
|
|> set_cell_metadata(cell, metadata)
|
||||||
|
|> set_dirty()
|
||||||
|
|> wrap_ok()
|
||||||
|
else
|
||||||
|
_ -> :error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def apply_operation(data, {:set_runtime, _client_pid, runtime}) do
|
def apply_operation(data, {:set_runtime, _client_pid, runtime}) do
|
||||||
data
|
data
|
||||||
|> with_actions()
|
|> with_actions()
|
||||||
|
@ -654,6 +667,11 @@ defmodule LiveBook.Session.Data do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp set_cell_metadata({data, _} = data_actions, cell, metadata) do
|
||||||
|
data_actions
|
||||||
|
|> set!(notebook: Notebook.update_cell(data.notebook, cell.id, &%{&1 | metadata: metadata}))
|
||||||
|
end
|
||||||
|
|
||||||
defp purge_deltas(cell_info) do
|
defp purge_deltas(cell_info) do
|
||||||
# Given client at revision X and upstream revision Y,
|
# Given client at revision X and upstream revision Y,
|
||||||
# we need Y - X last deltas that the client is not aware of,
|
# we need Y - X last deltas that the client is not aware of,
|
||||||
|
|
|
@ -66,6 +66,9 @@ defmodule LiveBookWeb.CellComponent do
|
||||||
<button phx-click="delete_focused_cell" class="text-gray-500 hover:text-current">
|
<button phx-click="delete_focused_cell" class="text-gray-500 hover:text-current">
|
||||||
<%= Icons.svg(:trash, class: "h-6") %>
|
<%= Icons.svg(:trash, class: "h-6") %>
|
||||||
</button>
|
</button>
|
||||||
|
<%= live_patch to: Routes.session_path(@socket, :cell_settings, @session_id, @cell.id), class: "text-gray-500 hover:text-current" do %>
|
||||||
|
<%= Icons.svg(:adjustments, class: "h-6") %>
|
||||||
|
<% end %>
|
||||||
<button class="text-gray-500 hover:text-current"
|
<button class="text-gray-500 hover:text-current"
|
||||||
phx-click="move_focused_cell"
|
phx-click="move_focused_cell"
|
||||||
phx-value-offset="-1">
|
phx-value-offset="-1">
|
||||||
|
|
|
@ -169,6 +169,16 @@ defmodule LiveBookWeb.Icons do
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def svg(:adjustments, attrs) do
|
||||||
|
assigns = %{attrs: heroicon_svg_attrs(attrs)}
|
||||||
|
|
||||||
|
~L"""
|
||||||
|
<%= tag(:svg, @attrs) %>
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" />
|
||||||
|
</svg>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
# https://heroicons.com
|
# https://heroicons.com
|
||||||
defp heroicon_svg_attrs(attrs) do
|
defp heroicon_svg_attrs(attrs) do
|
||||||
heroicon_svg_attrs = [
|
heroicon_svg_attrs = [
|
||||||
|
|
|
@ -33,6 +33,7 @@ defmodule LiveBookWeb.SectionComponent do
|
||||||
<%= for {cell, index} <- Enum.with_index(@section.cells) do %>
|
<%= for {cell, index} <- Enum.with_index(@section.cells) do %>
|
||||||
<%= live_component @socket, LiveBookWeb.CellComponent,
|
<%= live_component @socket, LiveBookWeb.CellComponent,
|
||||||
id: cell.id,
|
id: cell.id,
|
||||||
|
session_id: @session_id,
|
||||||
cell: cell,
|
cell: cell,
|
||||||
cell_info: @cell_infos[cell.id],
|
cell_info: @cell_infos[cell.id],
|
||||||
focused: @selected and cell.id == @focused_cell_id,
|
focused: @selected and cell.id == @focused_cell_id,
|
||||||
|
|
|
@ -77,6 +77,14 @@ defmodule LiveBookWeb.SessionLive do
|
||||||
return_to: Routes.session_path(@socket, :page, @session_id) %>
|
return_to: Routes.session_path(@socket, :page, @session_id) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<%= if @live_action == :cell_settings do %>
|
||||||
|
<%= live_modal @socket, LiveBookWeb.SessionLive.CellSettingsComponent,
|
||||||
|
id: :cell_settings_modal,
|
||||||
|
session_id: @session_id,
|
||||||
|
cell: @cell,
|
||||||
|
return_to: Routes.session_path(@socket, :page, @session_id) %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<div class="flex flex-grow h-full"
|
<div class="flex flex-grow h-full"
|
||||||
id="session"
|
id="session"
|
||||||
phx-hook="Session"
|
phx-hook="Session"
|
||||||
|
@ -148,6 +156,7 @@ defmodule LiveBookWeb.SessionLive do
|
||||||
<%= for section <- @data.notebook.sections do %>
|
<%= for section <- @data.notebook.sections do %>
|
||||||
<%= live_component @socket, LiveBookWeb.SectionComponent,
|
<%= live_component @socket, LiveBookWeb.SectionComponent,
|
||||||
id: section.id,
|
id: section.id,
|
||||||
|
session_id: @session_id,
|
||||||
section: section,
|
section: section,
|
||||||
selected: section.id == @selected_section_id,
|
selected: section.id == @selected_section_id,
|
||||||
cell_infos: @data.cell_infos,
|
cell_infos: @data.cell_infos,
|
||||||
|
@ -168,6 +177,11 @@ defmodule LiveBookWeb.SessionLive do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
def handle_params(%{"cell_id" => cell_id}, _url, socket) do
|
||||||
|
{:ok, cell, _} = Notebook.fetch_cell_and_section(socket.assigns.data.notebook, cell_id)
|
||||||
|
{:noreply, assign(socket, cell: cell)}
|
||||||
|
end
|
||||||
|
|
||||||
def handle_params(_params, _url, socket) do
|
def handle_params(_params, _url, socket) do
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
defmodule LiveBookWeb.SessionLive.CellSettingsComponent do
|
||||||
|
use LiveBookWeb, :live_component
|
||||||
|
|
||||||
|
alias LiveBook.Session
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def update(assigns, socket) do
|
||||||
|
metadata = assigns.cell.metadata
|
||||||
|
|
||||||
|
assigns =
|
||||||
|
Map.merge(assigns, %{disable_formatting: Map.get(metadata, "disable_formatting", false)})
|
||||||
|
|
||||||
|
{:ok, assign(socket, assigns)}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def render(assigns) do
|
||||||
|
~L"""
|
||||||
|
<div class="p-6 pb-4 max-w-4xl flex flex-col space-y-3">
|
||||||
|
<h3 class="text-lg font-medium text-gray-900">
|
||||||
|
Cell settings
|
||||||
|
</h3>
|
||||||
|
<form phx-submit="save" phx-target="<%= @myself %>">
|
||||||
|
<div class="w-full flex-col space-y-3">
|
||||||
|
<label class="flex space-x-3 items-center cursor-pointer">
|
||||||
|
<%= tag :input, class: "checkbox-base", type: "checkbox", name: "disable_formatting", checked: @disable_formatting %>
|
||||||
|
<span>Disable code formatting (when saving to file)</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="mt-6 flex justify-end space-x-2">
|
||||||
|
<%= live_patch "Cancel", to: @return_to, class: "button-base button-sm" %>
|
||||||
|
<button class="button-base button-primary button-sm" type="submit">
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_event("save", params, socket) do
|
||||||
|
metadata = update_metadata(socket.assigns.cell.metadata, params)
|
||||||
|
Session.set_cell_metadata(socket.assigns.session_id, socket.assigns.cell.id, metadata)
|
||||||
|
{:noreply, push_patch(socket, to: socket.assigns.return_to)}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_metadata(metadata, form_data) do
|
||||||
|
if Map.has_key?(form_data, "disable_formatting") do
|
||||||
|
Map.put(metadata, "disable_formatting", true)
|
||||||
|
else
|
||||||
|
Map.delete(metadata, "disable_formatting")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -78,8 +78,7 @@ defmodule LiveBookWeb.SessionLive.PersistenceComponent do
|
||||||
path = normalize_path(socket.assigns.path)
|
path = normalize_path(socket.assigns.path)
|
||||||
Session.set_path(socket.assigns.session_id, path)
|
Session.set_path(socket.assigns.session_id, path)
|
||||||
|
|
||||||
{:noreply,
|
{:noreply, push_patch(socket, to: socket.assigns.return_to)}
|
||||||
push_patch(socket, to: Routes.session_path(socket, :page, socket.assigns.session_id))}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp default_path() do
|
defp default_path() do
|
||||||
|
|
|
@ -22,5 +22,6 @@ defmodule LiveBookWeb.Router do
|
||||||
live "/sessions/:id/file", SessionLive, :file
|
live "/sessions/:id/file", SessionLive, :file
|
||||||
live "/sessions/:id/runtime", SessionLive, :runtime
|
live "/sessions/:id/runtime", SessionLive, :runtime
|
||||||
live "/sessions/:id/shortcuts", SessionLive, :shortcuts
|
live "/sessions/:id/shortcuts", SessionLive, :shortcuts
|
||||||
|
live "/sessions/:id/cell-settings/:cell_id", SessionLive, :cell_settings
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -295,4 +295,83 @@ defmodule LiveBook.LiveMarkdown.ExportTest do
|
||||||
|
|
||||||
assert expected_document == document
|
assert expected_document == document
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "formats code in Elixir cells" do
|
||||||
|
notebook = %{
|
||||||
|
Notebook.new()
|
||||||
|
| name: "My Notebook",
|
||||||
|
metadata: %{},
|
||||||
|
sections: [
|
||||||
|
%{
|
||||||
|
Notebook.Section.new()
|
||||||
|
| name: "Section 1",
|
||||||
|
metadata: %{},
|
||||||
|
cells: [
|
||||||
|
%{
|
||||||
|
Notebook.Cell.new(:elixir)
|
||||||
|
| metadata: %{},
|
||||||
|
source: """
|
||||||
|
[1,2,3] # Comment
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
expected_document = """
|
||||||
|
# My Notebook
|
||||||
|
|
||||||
|
## Section 1
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
# Comment
|
||||||
|
[1, 2, 3]
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
|
document = Export.notebook_to_markdown(notebook)
|
||||||
|
|
||||||
|
assert expected_document == document
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not format code in Elixir cells which explicitly state so in metadata" do
|
||||||
|
notebook = %{
|
||||||
|
Notebook.new()
|
||||||
|
| name: "My Notebook",
|
||||||
|
metadata: %{},
|
||||||
|
sections: [
|
||||||
|
%{
|
||||||
|
Notebook.Section.new()
|
||||||
|
| name: "Section 1",
|
||||||
|
metadata: %{},
|
||||||
|
cells: [
|
||||||
|
%{
|
||||||
|
Notebook.Cell.new(:elixir)
|
||||||
|
| metadata: %{"disable_formatting" => true},
|
||||||
|
source: """
|
||||||
|
[1,2,3] # Comment\
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
expected_document = """
|
||||||
|
# My Notebook
|
||||||
|
|
||||||
|
## Section 1
|
||||||
|
|
||||||
|
<!-- livebook:{"disable_formatting":true} -->
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
[1,2,3] # Comment
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
|
document = Export.notebook_to_markdown(notebook)
|
||||||
|
|
||||||
|
assert expected_document == document
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1053,6 +1053,33 @@ defmodule LiveBook.Session.DataTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "apply_operation/2 given :set_cell_metadata" do
|
||||||
|
test "returns an error given invalid cell id" do
|
||||||
|
data = Data.new()
|
||||||
|
|
||||||
|
operation = {:set_cell_metadata, self(), "nonexistent", %{}}
|
||||||
|
assert :error = Data.apply_operation(data, operation)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates cell metadata with the given map" do
|
||||||
|
data =
|
||||||
|
data_after_operations!([
|
||||||
|
{:insert_section, self(), 0, "s1"},
|
||||||
|
{:insert_cell, self(), "s1", 0, :elixir, "c1"}
|
||||||
|
])
|
||||||
|
|
||||||
|
metadata = %{"disable_formatting" => true}
|
||||||
|
operation = {:set_cell_metadata, self(), "c1", metadata}
|
||||||
|
|
||||||
|
assert {:ok,
|
||||||
|
%{
|
||||||
|
notebook: %{
|
||||||
|
sections: [%{cells: [%{metadata: ^metadata}]}]
|
||||||
|
}
|
||||||
|
}, _} = Data.apply_operation(data, operation)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "apply_operation/2 given :set_runtime" do
|
describe "apply_operation/2 given :set_runtime" do
|
||||||
test "updates data with the given runtime" do
|
test "updates data with the given runtime" do
|
||||||
data = Data.new()
|
data = Data.new()
|
||||||
|
|
|
@ -112,7 +112,7 @@ defmodule LiveBook.SessionTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "apply_cell_delta/5" do
|
describe "apply_cell_delta/4" do
|
||||||
test "sends a cell delta operation to subscribers", %{session_id: session_id} do
|
test "sends a cell delta operation to subscribers", %{session_id: session_id} do
|
||||||
Phoenix.PubSub.subscribe(LiveBook.PubSub, "sessions:#{session_id}")
|
Phoenix.PubSub.subscribe(LiveBook.PubSub, "sessions:#{session_id}")
|
||||||
pid = self()
|
pid = self()
|
||||||
|
@ -127,6 +127,32 @@ defmodule LiveBook.SessionTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "report_cell_revision/3" do
|
||||||
|
test "sends a revision report operation to subscribers", %{session_id: session_id} do
|
||||||
|
Phoenix.PubSub.subscribe(LiveBook.PubSub, "sessions:#{session_id}")
|
||||||
|
pid = self()
|
||||||
|
|
||||||
|
{_section_id, cell_id} = insert_section_and_cell(session_id)
|
||||||
|
revision = 1
|
||||||
|
|
||||||
|
Session.report_cell_revision(session_id, cell_id, revision)
|
||||||
|
assert_receive {:operation, {:report_cell_revision, ^pid, ^cell_id, ^revision}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "set_cell_metadata/3" do
|
||||||
|
test "sends a metadata update operation to subscribers", %{session_id: session_id} do
|
||||||
|
Phoenix.PubSub.subscribe(LiveBook.PubSub, "sessions:#{session_id}")
|
||||||
|
pid = self()
|
||||||
|
|
||||||
|
{_section_id, cell_id} = insert_section_and_cell(session_id)
|
||||||
|
metadata = %{"disable_formatting" => true}
|
||||||
|
|
||||||
|
Session.set_cell_metadata(session_id, cell_id, metadata)
|
||||||
|
assert_receive {:operation, {:set_cell_metadata, ^pid, ^cell_id, ^metadata}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "connect_runtime/2" do
|
describe "connect_runtime/2" do
|
||||||
test "sends a runtime update operation to subscribers", %{session_id: session_id} do
|
test "sends a runtime update operation to subscribers", %{session_id: session_id} do
|
||||||
Phoenix.PubSub.subscribe(LiveBook.PubSub, "sessions:#{session_id}")
|
Phoenix.PubSub.subscribe(LiveBook.PubSub, "sessions:#{session_id}")
|
||||||
|
|
Loading…
Add table
Reference in a new issue