Spec gap D4-5: add TemplateEditor LiveComponent tests for save/validate/delete events
This commit is contained in:
@@ -180,7 +180,7 @@ All reconciled to follow code. Specs must be self-consistent and match code.
|
|||||||
| ~~D4-2~~ | ~~editor_settings.allium~~ | ~~AI endpoints, airplane toggle, rebuild~~ | ~~Protected categories~~ (resolved D1-17), ~~MCP agents~~ (6 `mcp_rows` + 3 `toggle_mcp_agent` tests), ~~style/theme~~ (19 `build_style` + 4 select/change/apply/display tests), ~~search filter~~ (9 `build_settings` visibility tests), ~~categories CRUD~~ (7 `category_rows` + 2 update + 3 add + 2 save + 4 reset tests) | **Resolved:** 3 new test files (mcp_config_test.exs, style_editor_test.exs, settings_search_test.exs) + expanded managed_categories_test.exs cover all untested areas. Total 56 tests added across MCP agents, style/theme, search filter, and categories CRUD. |
|
| ~~D4-2~~ | ~~editor_settings.allium~~ | ~~AI endpoints, airplane toggle, rebuild~~ | ~~Protected categories~~ (resolved D1-17), ~~MCP agents~~ (6 `mcp_rows` + 3 `toggle_mcp_agent` tests), ~~style/theme~~ (19 `build_style` + 4 select/change/apply/display tests), ~~search filter~~ (9 `build_settings` visibility tests), ~~categories CRUD~~ (7 `category_rows` + 2 update + 3 add + 2 save + 4 reset tests) | **Resolved:** 3 new test files (mcp_config_test.exs, style_editor_test.exs, settings_search_test.exs) + expanded managed_categories_test.exs cover all untested areas. Total 56 tests added across MCP agents, style/theme, search filter, and categories CRUD. |
|
||||||
| D4-3 | ~~editor_chat.allium~~ | ~~Chat creation, pinned tab~~ | ~~API key screen, message rendering, input area, model selector, inline surfaces~~ | **Resolved:** 3 tests added — WelcomeScreen assertions (robot icon, title, 5 tips), CSP external image rewriting in markdown, chart inline surface rendering with series labels/values |
|
| D4-3 | ~~editor_chat.allium~~ | ~~Chat creation, pinned tab~~ | ~~API key screen, message rendering, input area, model selector, inline surfaces~~ | **Resolved:** 3 tests added — WelcomeScreen assertions (robot icon, title, 5 tips), CSP external image rewriting in markdown, chart inline surface rendering with series labels/values |
|
||||||
| D4-4 | editor_script.allium | Editor layout, create defaults | ~~Save, syntax check, run, delete~~ | **Resolved:** 4 tests added — save persists title change + version bump to DB, run executes script without crash, check syntax validates without side effects, delete removes DB record + file |
|
| D4-4 | editor_script.allium | Editor layout, create defaults | ~~Save, syntax check, run, delete~~ | **Resolved:** 4 tests added — save persists title change + version bump to DB, run executes script without crash, check syntax validates without side effects, delete removes DB record + file |
|
||||||
| D4-5 | editor_template.allium | Editor layout, create defaults | Save with validation, validate, delete with references |
|
| D4-5 | editor_template.allium | Editor layout, create defaults | ~~Save with validation, validate, delete with references~~ | **Resolved:** 6 tests added in template_editor_live_test.exs covering save with valid Liquid (version bumps), save with invalid Liquid (rejected, version unchanged), validate (no side effects), delete (removes DB row + .liquid file), delete with references (clears post template_slug) |
|
||||||
| D4-6 | editor_tags.allium | Sync/discover, merge | Cloud sizing, color picker, delete confirmation, create form |
|
| D4-6 | editor_tags.allium | Sync/discover, merge | Cloud sizing, color picker, delete confirmation, create form |
|
||||||
| D4-7 | editor_misc.allium | Menu add/save, metadata diff, validation | Menu protection, import analysis, translation fix, duplicate dismiss, git diff |
|
| D4-7 | editor_misc.allium | Menu add/save, metadata diff, validation | Menu protection, import analysis, translation fix, duplicate dismiss, git diff |
|
||||||
|
|
||||||
|
|||||||
237
test/bds/desktop/template_editor_live_test.exs
Normal file
237
test/bds/desktop/template_editor_live_test.exs
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
defmodule BDS.Desktop.TemplateEditorLiveTest do
|
||||||
|
use ExUnit.Case, async: false
|
||||||
|
|
||||||
|
import Phoenix.ConnTest
|
||||||
|
import Phoenix.LiveViewTest
|
||||||
|
|
||||||
|
alias BDS.Posts
|
||||||
|
alias BDS.Posts.Post
|
||||||
|
alias BDS.Projects
|
||||||
|
alias BDS.Repo
|
||||||
|
alias BDS.Templates
|
||||||
|
|
||||||
|
@endpoint BDS.Desktop.Endpoint
|
||||||
|
|
||||||
|
setup do
|
||||||
|
:ok = Ecto.Adapters.SQL.Sandbox.checkout(BDS.Repo)
|
||||||
|
Ecto.Adapters.SQL.Sandbox.mode(BDS.Repo, {:shared, self()})
|
||||||
|
|
||||||
|
Enum.each(BDS.Tasks.list_running_tasks(), fn task ->
|
||||||
|
BDS.Tasks.cancel_task(task.id)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if :ets.whereis(:bds_ai_in_flight) != :undefined do
|
||||||
|
Enum.each(:ets.tab2list(:bds_ai_in_flight), fn {_conversation_id, pid} ->
|
||||||
|
Process.exit(pid, :kill)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
for {_, pid, _, _} <- DynamicSupervisor.which_children(BDS.TCP.TaskSupervisor) do
|
||||||
|
DynamicSupervisor.terminate_child(BDS.TCP.TaskSupervisor, pid)
|
||||||
|
end
|
||||||
|
|
||||||
|
for {_, pid, _, _} <- DynamicSupervisor.which_children(BDS.Tasks.TaskSupervisor) do
|
||||||
|
DynamicSupervisor.terminate_child(BDS.Tasks.TaskSupervisor, pid)
|
||||||
|
end
|
||||||
|
|
||||||
|
Process.sleep(100)
|
||||||
|
|
||||||
|
temp_dir =
|
||||||
|
Path.join(
|
||||||
|
System.tmp_dir!(),
|
||||||
|
"bds-template-editor-live-#{System.unique_integer([:positive])}"
|
||||||
|
)
|
||||||
|
|
||||||
|
File.mkdir_p!(temp_dir)
|
||||||
|
|
||||||
|
on_exit(fn -> File.rm_rf(temp_dir) end)
|
||||||
|
|
||||||
|
{:ok, project} = Projects.create_project(%{name: "Template Editor", data_path: temp_dir})
|
||||||
|
{:ok, _project} = Projects.set_active_project(project.id)
|
||||||
|
|
||||||
|
%{project: project, temp_dir: temp_dir}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp open_template(view, template_id) do
|
||||||
|
view
|
||||||
|
|> element("[data-testid='sidebar-open-item'][data-item-id='#{template_id}']")
|
||||||
|
|> render_click()
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "TemplateEditor save" do
|
||||||
|
test "save with valid Liquid content persists changes and bumps version",
|
||||||
|
%{project: project} do
|
||||||
|
assert {:ok, template} =
|
||||||
|
Templates.create_template(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "My Template",
|
||||||
|
kind: :post,
|
||||||
|
content: "{{ content }}"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert template.version == 1
|
||||||
|
|
||||||
|
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
_html = render_click(view, "select_view", %{"view" => "templates"})
|
||||||
|
html = open_template(view, template.id)
|
||||||
|
|
||||||
|
assert html =~ "templates-view-shell"
|
||||||
|
|
||||||
|
view
|
||||||
|
|> element("[data-testid='template-editor'] .templates-save-button")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
reloaded = Templates.get_template(template.id)
|
||||||
|
assert reloaded.version == 2
|
||||||
|
end
|
||||||
|
|
||||||
|
test "save with invalid Liquid content is rejected (version unchanged)",
|
||||||
|
%{project: project} do
|
||||||
|
assert {:ok, template} =
|
||||||
|
Templates.create_template(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Bad Template",
|
||||||
|
kind: :post,
|
||||||
|
content: "{% invalid_tag_xyz %}"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert template.version == 1
|
||||||
|
|
||||||
|
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
_html = render_click(view, "select_view", %{"view" => "templates"})
|
||||||
|
html = open_template(view, template.id)
|
||||||
|
|
||||||
|
assert html =~ "templates-view-shell"
|
||||||
|
|
||||||
|
view
|
||||||
|
|> element("[data-testid='template-editor'] .templates-save-button")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
reloaded = Templates.get_template(template.id)
|
||||||
|
assert reloaded.version == 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "TemplateEditor validate" do
|
||||||
|
test "validate runs without side effects (version unchanged)", %{project: project} do
|
||||||
|
assert {:ok, template} =
|
||||||
|
Templates.create_template(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Validate Template",
|
||||||
|
kind: :post,
|
||||||
|
content: "{{ content }}"
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
_html = render_click(view, "select_view", %{"view" => "templates"})
|
||||||
|
html = open_template(view, template.id)
|
||||||
|
|
||||||
|
assert html =~ "templates-view-shell"
|
||||||
|
|
||||||
|
view
|
||||||
|
|> element("[data-testid='template-editor'] .templates-validate-button")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
reloaded = Templates.get_template(template.id)
|
||||||
|
assert reloaded.version == 1
|
||||||
|
assert reloaded.content == "{{ content }}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "TemplateEditor delete" do
|
||||||
|
test "delete removes template from DB", %{project: project} do
|
||||||
|
assert {:ok, template} =
|
||||||
|
Templates.create_template(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Delete Template",
|
||||||
|
kind: :post,
|
||||||
|
content: "{{ content }}"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, _published} = Templates.publish_template(template.id)
|
||||||
|
|
||||||
|
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
_html = render_click(view, "select_view", %{"view" => "templates"})
|
||||||
|
html = open_template(view, template.id)
|
||||||
|
|
||||||
|
assert html =~ "templates-view-shell"
|
||||||
|
|
||||||
|
view
|
||||||
|
|> element("[data-testid='template-editor'] .danger")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
refute Repo.get(BDS.Templates.Template, template.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "delete with references clears them (force delete), removes template",
|
||||||
|
%{project: project} do
|
||||||
|
assert {:ok, template} =
|
||||||
|
Templates.create_template(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Referenced Template",
|
||||||
|
kind: :post,
|
||||||
|
content: "{{ content }}"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, published} = Templates.publish_template(template.id)
|
||||||
|
|
||||||
|
assert {:ok, post} =
|
||||||
|
Posts.create_post(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "Ref Post",
|
||||||
|
content: "Body",
|
||||||
|
template_slug: published.slug
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, _published_post} = Posts.publish_post(post.id)
|
||||||
|
|
||||||
|
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
_html = render_click(view, "select_view", %{"view" => "templates"})
|
||||||
|
html = open_template(view, template.id)
|
||||||
|
|
||||||
|
assert html =~ "templates-view-shell"
|
||||||
|
|
||||||
|
view
|
||||||
|
|> element("[data-testid='template-editor'] .danger")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
refute Repo.get(BDS.Templates.Template, template.id)
|
||||||
|
|
||||||
|
reloaded_post = Repo.get(Post, post.id)
|
||||||
|
assert reloaded_post.template_slug == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
test "delete removes the .liquid file from disk", %{project: project, temp_dir: temp_dir} do
|
||||||
|
assert {:ok, template} =
|
||||||
|
Templates.create_template(%{
|
||||||
|
project_id: project.id,
|
||||||
|
title: "File Template",
|
||||||
|
kind: :post,
|
||||||
|
content: "{{ content }}"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert {:ok, published} = Templates.publish_template(template.id)
|
||||||
|
|
||||||
|
file_path = Path.join(temp_dir, published.file_path)
|
||||||
|
assert File.exists?(file_path)
|
||||||
|
|
||||||
|
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
_html = render_click(view, "select_view", %{"view" => "templates"})
|
||||||
|
html = open_template(view, template.id)
|
||||||
|
|
||||||
|
assert html =~ "templates-view-shell"
|
||||||
|
|
||||||
|
view
|
||||||
|
|> element("[data-testid='template-editor'] .danger")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
refute File.exists?(file_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user