alias FirehoseWeb.MicroprintsLive defmodule FirehoseWeb.MicroprintsLiveTest do use FirehoseWeb.ConnCase, async: true import FirehoseWeb.MicroprintsCase import Phoenix.LiveViewTest describe "scan_source_files/0" do test "returns exactly 2 files" do files = FirehoseWeb.MicroprintsLive.scan_source_files() assert length(files) == 2 end test "returns only .ex files from app/ and blogex/ directories" do files = FirehoseWeb.MicroprintsLive.scan_source_files() # With the 2-file limit, check that returned files are valid .ex paths Enum.each(files, fn file -> assert String.ends_with?(file, ".ex") assert String.starts_with?(file, "lib/") or String.starts_with?(file, "../blogex/") end) end test "does not include files from _build/ directory" do files = FirehoseWeb.MicroprintsLive.scan_source_files() refute Enum.any?(files, &String.starts_with?(&1, "_build/")) end test "does not include files from deps/ directory" do files = FirehoseWeb.MicroprintsLive.scan_source_files() refute Enum.any?(files, &String.starts_with?(&1, "deps/")) end test "does not include files from examples/ directory" do files = FirehoseWeb.MicroprintsLive.scan_source_files() refute Enum.any?(files, &String.contains?(&1, "/examples/")) end test "does not include test files" do files = FirehoseWeb.MicroprintsLive.scan_source_files() refute Enum.any?(files, &String.contains?(&1, "/test/")) end test "paths are relative (no leading slash or absolute path)" do files = FirehoseWeb.MicroprintsLive.scan_source_files() refute Enum.any?(files, &String.starts_with?(&1, "/")) end test "app paths do not contain source dir prefix" do files = FirehoseWeb.MicroprintsLive.scan_source_files() app_files = Enum.filter(files, &String.starts_with?(&1, "lib/")) refute Enum.any?(app_files, &String.starts_with?(&1, "app/")) end test "blogex paths start with ../blogex/" do files = FirehoseWeb.MicroprintsLive.scan_source_files() blogex_files = Enum.filter(files, &String.starts_with?(&1, "../blogex/")) # With the 2-file limit, blogex files may or may not be included # depending on which files are most recently modified assert is_list(blogex_files) end end describe "expanded_path and highlighted_path coupling" do test "expanding file A and highlighting a line in file B should collapse file A", %{ conn: conn } do files = MicroprintsLive.scan_source_files() assert length(files) >= 2, "Need at least 2 files to test coupling" [file_a, file_b | _] = files {:ok, view, _html} = live(conn, ~p"/microprints") # Expand file A expand_file(view, file_a) # Verify file A shows "Collapse" button (it's expanded) html = render(view) assert html =~ ~s(phx-value-path="#{file_a}") assert html =~ "Collapse" # Highlight a line in file B click_microprint_rect(view, file_b, 1) # file A should be collapsed when highlighting a different file html = render(view) refute html =~ "Collapse", "file A should be collapsed after highlighting a different file, but the Collapse button is still visible (expanded_path is uncoupled from highlighted_path)" end test "switching expand from file A to file B shows each file's own source content", %{ conn: conn } do files = MicroprintsLive.scan_source_files() assert length(files) >= 2, "Need at least 2 files to test source switching" [file_a, file_b | _] = files {:ok, view, _html} = live(conn, ~p"/microprints") # Expand file A expand_file(view, file_a) # Capture file A's source content from the rendered HTML html_a = render(view) # Each .ex file starts with "defmodule", grab the first defmodule line as unique content assert html_a =~ "defmodule", "File A source should be visible when expanded" # Expand file B (should auto-collapse A) expand_file(view, file_b) # Capture file B's source content html_b = render(view) # The two source viewers must show different content refute html_a == html_b, "Source content should differ when switching between files" # File A's button should now say "Expand" (collapsed) refute html_b =~ ~r(phx-value-path="#{Regex.escape(file_a)}"[^>]*>\s*Collapse\s*]*>\s*Collapse\s* List.first() {:ok, view, _html} = live(conn, ~p"/microprints?expanded=#{path}") assert has_element?(view, "#line-1"), "restored expansion should render the file's source lines" assert has_element?(view, ~s(button[phx-value-path="#{path}"]), "Collapse") collapse_file(view, path) refute has_element?(view, "#line-1"), "collapsing should stop rendering the source viewer" refute render(view) =~ "Collapse", "no card should still be expanded once the only expanded one is collapsed" assert has_element?(view, ~s(button[phx-value-path="#{path}"]), "Expand"), "the card's control should be Expand again" end end describe "app layout with menu" do test "renders the microprints page", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/microprints") assert html =~ "Microprints" end test "renders with page content and legend", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/microprints") # Verify the page renders expected content (render/1 returns LV inner HTML only) assert html =~ "Visual fingerprints of source code files" assert html =~ "Function" assert html =~ "Module" assert html =~ "Keyword" end end describe "URL state persistence" do test "expanded path is restored from query param on mount", %{conn: conn} do files = MicroprintsLive.scan_source_files() file_a = List.first(files) {:ok, view, _html} = live(conn, ~p"/microprints?expanded=#{file_a}") html = render(view) assert html =~ "Collapse", "Expanded file should show Collapse button when restored from URL param" assert html =~ ~s(phx-value-path="#{file_a}") end test "unknown expanded path in URL is ignored gracefully", %{conn: conn} do {:ok, view, _html} = live(conn, ~p"/microprints?expanded=nonexistent.ex") html = render(view) refute html =~ "Collapse", "Nonexistent expanded path should not show Collapse" end test "expand updates URL with expanded param", %{conn: conn} do files = MicroprintsLive.scan_source_files() file_a = List.first(files) {:ok, view, _html} = live(conn, ~p"/microprints") expand_file(view, file_a) # URL should contain expanded param after clicking Expand html = render(view) assert html =~ "Collapse", "File should remain expanded after push_patch updates URL" end end end