feat: added a gallery quick action and fleshed out builtin macros
This commit is contained in:
216
test/bds/image_import_pipeline_test.exs
Normal file
216
test/bds/image_import_pipeline_test.exs
Normal file
@@ -0,0 +1,216 @@
|
||||
defmodule BDS.ImageImportPipelineTest do
|
||||
use ExUnit.Case, async: false
|
||||
|
||||
import Phoenix.ConnTest
|
||||
import Phoenix.LiveViewTest
|
||||
|
||||
alias BDS.Desktop.{FilePicker, Overlay}
|
||||
alias BDS.{Metadata, AI, Repo}
|
||||
|
||||
@endpoint BDS.Desktop.Endpoint
|
||||
|
||||
setup do
|
||||
:ok = Ecto.Adapters.SQL.Sandbox.checkout(Repo)
|
||||
Ecto.Adapters.SQL.Sandbox.mode(Repo, {:shared, self()})
|
||||
|
||||
temp_dir =
|
||||
Path.join(System.tmp_dir!(), "bds-image-import-#{System.unique_integer([:positive])}")
|
||||
|
||||
File.mkdir_p!(temp_dir)
|
||||
on_exit(fn -> File.rm_rf(temp_dir) end)
|
||||
|
||||
{:ok, project} = BDS.Projects.create_project(%{name: "Image Import Test", data_path: temp_dir})
|
||||
%{project: project, temp_dir: temp_dir}
|
||||
end
|
||||
|
||||
# ── FilePicker multi-select parsing ────────────────────────────────────────
|
||||
|
||||
describe "FilePicker.choose_files/2" do
|
||||
test "single selection returns a single-item list" do
|
||||
# Simulate what osascript returns for regular choose file
|
||||
result = FilePicker.parse_choose_files_result("/Users/test/image.png", false)
|
||||
assert result == {:ok, "/Users/test/image.png"}
|
||||
end
|
||||
|
||||
test "multi selection parses newline-separated paths" do
|
||||
result =
|
||||
FilePicker.parse_choose_files_result(
|
||||
"/Users/test/photo1.jpg\n/Users/test/photo2.png\n/Users/test/photo3.heic",
|
||||
true
|
||||
)
|
||||
|
||||
assert result ==
|
||||
{:ok, ["/Users/test/photo1.jpg", "/Users/test/photo2.png", "/Users/test/photo3.heic"]}
|
||||
end
|
||||
|
||||
test "multi selection filters out empty lines" do
|
||||
result =
|
||||
FilePicker.parse_choose_files_result(
|
||||
"/Users/test/photo1.jpg\n\n/Users/test/photo2.png\n \n/Users/test/photo3.heic\n",
|
||||
true
|
||||
)
|
||||
|
||||
assert result ==
|
||||
{:ok, ["/Users/test/photo1.jpg", "/Users/test/photo2.png", "/Users/test/photo3.heic"]}
|
||||
end
|
||||
|
||||
test "multi selection with single file returns list with one element" do
|
||||
result = FilePicker.parse_choose_files_result("/Users/test/photo1.jpg", true)
|
||||
assert result == {:ok, ["/Users/test/photo1.jpg"]}
|
||||
end
|
||||
end
|
||||
|
||||
# ── Metadata image_import_concurrency ───────────────────────────────────────
|
||||
|
||||
describe "Metadata image_import_concurrency" do
|
||||
test "defaults to 4 for new projects", %{project: project} do
|
||||
{:ok, metadata} = Metadata.get_project_metadata(project.id)
|
||||
assert metadata.image_import_concurrency == 4
|
||||
end
|
||||
|
||||
test "clamps to minimum 1", %{project: project} do
|
||||
{:ok, _metadata} =
|
||||
Metadata.update_project_metadata(project.id, %{image_import_concurrency: 0})
|
||||
|
||||
{:ok, metadata} = Metadata.get_project_metadata(project.id)
|
||||
assert metadata.image_import_concurrency == 1
|
||||
end
|
||||
|
||||
test "clamps to maximum 8", %{project: project} do
|
||||
{:ok, _metadata} =
|
||||
Metadata.update_project_metadata(project.id, %{image_import_concurrency: 100})
|
||||
|
||||
{:ok, metadata} = Metadata.get_project_metadata(project.id)
|
||||
assert metadata.image_import_concurrency == 8
|
||||
end
|
||||
|
||||
test "persists and reads correctly", %{project: project} do
|
||||
{:ok, _metadata} =
|
||||
Metadata.update_project_metadata(project.id, %{image_import_concurrency: 3})
|
||||
|
||||
{:ok, metadata} = Metadata.get_project_metadata(project.id)
|
||||
assert metadata.image_import_concurrency == 3
|
||||
end
|
||||
|
||||
test "handles string input", %{project: project} do
|
||||
{:ok, _metadata} =
|
||||
Metadata.update_project_metadata(project.id, %{image_import_concurrency: "5"})
|
||||
|
||||
{:ok, metadata} = Metadata.get_project_metadata(project.id)
|
||||
assert metadata.image_import_concurrency == 5
|
||||
end
|
||||
|
||||
test "handles nil as default", %{project: project} do
|
||||
{:ok, _metadata} =
|
||||
Metadata.update_project_metadata(project.id, %{image_import_concurrency: nil})
|
||||
|
||||
{:ok, metadata} = Metadata.get_project_metadata(project.id)
|
||||
assert metadata.image_import_concurrency == 4
|
||||
end
|
||||
|
||||
test "reflects in form as string", %{project: project} do
|
||||
{:ok, metadata} = Metadata.get_project_metadata(project.id)
|
||||
|
||||
form =
|
||||
BDS.Desktop.ShellLive.SettingsEditor.ProjectSettings.project_form(metadata)
|
||||
|
||||
assert form["image_import_concurrency"] == "4"
|
||||
end
|
||||
end
|
||||
|
||||
# ── Overlay struct post_id ────────────────────────────────────────────────
|
||||
|
||||
describe "Overlay insert_media" do
|
||||
test "open(:post, :insert_media, context) includes post_id" do
|
||||
context = %{
|
||||
current_tab: %{
|
||||
type: :post,
|
||||
id: "post-uuid-123",
|
||||
title: "Test Post",
|
||||
subtitle: "draft"
|
||||
},
|
||||
media: [],
|
||||
insert_media_title: "Insert Media"
|
||||
}
|
||||
|
||||
overlay = Overlay.open(:post, :insert_media, context)
|
||||
assert overlay.post_id == "post-uuid-123"
|
||||
end
|
||||
|
||||
test "post_id is nil when opened from non-post context" do
|
||||
context = %{
|
||||
current_tab: %{
|
||||
type: :media,
|
||||
id: "media-uuid-456",
|
||||
title: "Test Media",
|
||||
subtitle: "image/png"
|
||||
},
|
||||
media: [],
|
||||
insert_media_title: "Insert Media"
|
||||
}
|
||||
|
||||
overlay = Overlay.open(:media, :insert_media, context)
|
||||
assert overlay == nil
|
||||
end
|
||||
|
||||
test "set_search_query preserves post_id" do
|
||||
overlay = %{
|
||||
kind: :insert_media,
|
||||
title: "Insert Media",
|
||||
search_query: "",
|
||||
results: [],
|
||||
all_media: [],
|
||||
post_id: "post-uuid-789"
|
||||
}
|
||||
|
||||
result = Overlay.set_search_query(overlay, "search term")
|
||||
assert result.post_id == "post-uuid-789"
|
||||
end
|
||||
end
|
||||
|
||||
# ── Airplane mode gating via shell_live event ─────────────────────────────
|
||||
|
||||
describe "overlay_add_images airplane mode gating" do
|
||||
setup do
|
||||
prev_auto = System.get_env("BDS_DESKTOP_AUTOMATION")
|
||||
System.put_env("BDS_DESKTOP_AUTOMATION", "1")
|
||||
|
||||
on_exit(fn ->
|
||||
if prev_auto,
|
||||
do: System.put_env("BDS_DESKTOP_AUTOMATION", prev_auto),
|
||||
else: System.delete_env("BDS_DESKTOP_AUTOMATION")
|
||||
end)
|
||||
end
|
||||
|
||||
test "shows toast in airplane mode when Add Gallery Images is clicked", %{project: project} do
|
||||
{:ok, _project} = BDS.Projects.set_active_project(project.id)
|
||||
|
||||
assert :ok = AI.set_airplane_mode(true)
|
||||
|
||||
{:ok, post} =
|
||||
BDS.Posts.create_post(%{
|
||||
project_id: project.id,
|
||||
title: "Gallery Post",
|
||||
content: "Content"
|
||||
})
|
||||
|
||||
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||
|
||||
render_click(view, "pin_sidebar_item", %{
|
||||
"route" => "post",
|
||||
"id" => post.id,
|
||||
"title" => post.title,
|
||||
"subtitle" => "draft"
|
||||
})
|
||||
|
||||
html =
|
||||
view
|
||||
|> element("[phx-click='add_gallery_images']")
|
||||
|> render_click()
|
||||
|
||||
assert html =~ "Automatic AI actions stay gated by airplane mode"
|
||||
|
||||
assert :ok = AI.set_airplane_mode(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user