diff --git a/test/mv/membership/members_pdf_test.exs b/test/mv/membership/members_pdf_test.exs index 2b83e3b..3ae18cf 100644 --- a/test/mv/membership/members_pdf_test.exs +++ b/test/mv/membership/members_pdf_test.exs @@ -274,17 +274,38 @@ defmodule Mv.Membership.MembersPDFTest do assert {:ok, _pdf_binary} = result - # Wait a bit for cleanup (async cleanup might take a moment) - Process.sleep(100) - - # Count temp directories after - after_count = + count_export_dirs = fn -> temp_base |> File.ls!() |> Enum.count(fn name -> String.starts_with?(name, "mv_pdf_export_") end) + end + + # Poll the observable cleanup condition (temp-dir count returns to the baseline) + # with a bounded deadline instead of a fixed sleep, so the test waits no longer + # than the cleanup actually needs and still fails if cleanup never runs. + after_count = poll_until_cleaned(count_export_dirs, before_count, 100) # Should have same or fewer temp dirs (cleanup should have run) assert after_count <= before_count + 1 end end + + # Bounded poll: returns the export-dir count once it drops back to the baseline + # (cleanup done), or the last observed count when the attempt budget is exhausted + # (so the caller's assertion reports the real state on a genuine cleanup stall). + defp poll_until_cleaned(count_fun, baseline, attempts_left) do + current = count_fun.() + + cond do + current <= baseline -> + current + + attempts_left <= 0 -> + current + + true -> + Process.sleep(10) + poll_until_cleaned(count_fun, baseline, attempts_left - 1) + end + end end diff --git a/test/mv_web/live/import_live_test.exs b/test/mv_web/live/import_live_test.exs index bd5cdec..8837fc6 100644 --- a/test/mv_web/live/import_live_test.exs +++ b/test/mv_web/live/import_live_test.exs @@ -37,7 +37,27 @@ defmodule MvWeb.ImportLiveTest do confirm_import(view) end - defp wait_for_import_completion, do: Process.sleep(1000) + # Waits for the asynchronous chunk-import to finish by polling the rendered + # results panel, instead of a fixed sleep. In the test sandbox the chunks run + # in-process and signal completion via self-messages; each render/2 forces the + # LiveView to drain its mailbox before replying, so the panel appears once all + # chunk messages have been processed. Bounded so a genuine stall still fails. + defp wait_for_import_completion(view) do + wait_for_import_completion(view, 200) + end + + defp wait_for_import_completion(_view, 0) do + flunk("import did not complete: results panel never rendered") + end + + defp wait_for_import_completion(view, attempts_left) do + if has_element?(view, "[data-testid='import-results-panel']") do + :ok + else + Process.sleep(10) + wait_for_import_completion(view, attempts_left - 1) + end + end # ---------- Business logic: Authorization ---------- describe "Authorization" do @@ -67,7 +87,7 @@ defmodule MvWeb.ImportLiveTest do {:ok, view, _html} = live(conn, ~p"/admin/import") run_full_import(view, csv_content) - wait_for_import_completion() + wait_for_import_completion(view) assert has_element?(view, "[data-testid='import-results-panel']") assert has_element?(view, "[data-testid='import-summary']") @@ -131,7 +151,7 @@ defmodule MvWeb.ImportLiveTest do } do {:ok, view, _html} = live(conn, ~p"/admin/import") run_full_import(view, csv_content, "invalid_import.csv") - wait_for_import_completion() + wait_for_import_completion(view) assert has_element?(view, "[data-testid='import-results-panel']") assert has_element?(view, "[data-testid='import-error-list']") @@ -150,7 +170,7 @@ defmodule MvWeb.ImportLiveTest do for i <- 1..100, do: "Row#{i};Last#{i};;Country#{i};City#{i};Street#{i};12345\n" run_full_import(view, header <> Enum.join(invalid_rows), "large_invalid.csv") - wait_for_import_completion() + wait_for_import_completion(view) assert has_element?(view, "[data-testid='import-results-panel']") assert has_element?(view, "[data-testid='import-error-list']") @@ -182,7 +202,7 @@ defmodule MvWeb.ImportLiveTest do |> File.read!() run_full_import(view, csv_content, "bom_import.csv") - wait_for_import_completion() + wait_for_import_completion(view) assert has_element?(view, "[data-testid='import-results-panel']") html = render(view) @@ -200,7 +220,7 @@ defmodule MvWeb.ImportLiveTest do |> File.read!() run_full_import(view, csv_content, "empty_lines.csv") - wait_for_import_completion() + wait_for_import_completion(view) assert has_element?(view, "[data-testid='import-error-list']") html = render(view) @@ -214,7 +234,7 @@ defmodule MvWeb.ImportLiveTest do } do {:ok, view, _html} = live(conn, ~p"/admin/import") run_full_import(view, csv_content, "unknown_custom.csv") - wait_for_import_completion() + wait_for_import_completion(view) assert has_element?(view, "[data-testid='import-results-panel']") assert has_element?(view, "[data-testid='import-warnings']") @@ -279,7 +299,7 @@ defmodule MvWeb.ImportLiveTest do {:ok, view, _html} = live(conn, ~p"/admin/import") run_full_import(view, csv_content) - wait_for_import_completion() + wait_for_import_completion(view) assert has_element?(view, "[data-testid='import-progress-container']") html = render(view) assert html =~ "aria-live" @@ -342,7 +362,7 @@ defmodule MvWeb.ImportLiveTest do } do {:ok, view, _html} = live(conn, ~p"/admin/import") run_full_import(view, csv_content) - wait_for_import_completion() + wait_for_import_completion(view) assert has_element?(view, "[data-testid='import-results-panel']") diff --git a/test/mv_web/member_live/index_field_visibility_test.exs b/test/mv_web/member_live/index_field_visibility_test.exs index 79d078b..007939d 100644 --- a/test/mv_web/member_live/index_field_visibility_test.exs +++ b/test/mv_web/member_live/index_field_visibility_test.exs @@ -157,9 +157,6 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do |> element("button[phx-click='select_item'][phx-value-item='email']") |> render_click() - # Wait for update - :timer.sleep(100) - # Email should no longer be visible html = render(view) refute html =~ "alice@example.com" @@ -186,9 +183,6 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do |> element("button[phx-click='select_item'][phx-value-item='#{custom_field_string}']") |> render_click() - # Wait for update - :timer.sleep(100) - # Custom field should no longer be visible html = render(view) refute html =~ "M001" @@ -213,9 +207,6 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do |> element("button[phx-click='select_all']") |> render_click() - # Wait for update - :timer.sleep(100) - # All fields should be visible html = render(view) assert html =~ "alice@example.com" @@ -237,9 +228,6 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do |> element("button[phx-click='select_none']") |> render_click() - # Wait for update - :timer.sleep(100) - # Only first_name should be visible (it's always shown) html = render(view) # Email and street should be hidden @@ -262,9 +250,6 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do |> element("button[phx-click='select_item'][phx-value-item='email']") |> render_click() - # Wait for URL update - :timer.sleep(100) - # Check that URL contains fields parameter # Note: In LiveView tests, we check the rendered HTML for the updated state # The actual URL update happens via push_patch @@ -329,8 +314,6 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do |> element("button[phx-click='select_item'][phx-value-item='email']") |> render_click() - :timer.sleep(100) - html = render(view) refute html =~ "alice@example.com" end @@ -387,8 +370,6 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do view |> element("button[phx-click='select_item'][phx-value-item='email']") |> render_click() - - :timer.sleep(50) end # Should still work correctly @@ -458,9 +439,6 @@ defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do |> element("button[phx-click='select_item'][phx-value-item='email']") |> render_keydown(%{key: "Enter"}) - # Wait for update - :timer.sleep(100) - # Email should no longer be visible html = render(view) refute html =~ "alice@example.com"