feat(overview): add period-scoped payment filter and field-picker descriptor
This commit is contained in:
parent
95ef3177ac
commit
7d1a71b1fd
12 changed files with 654 additions and 0 deletions
|
|
@ -113,6 +113,74 @@ defmodule MvWeb.MemberLive.Index.DateFilterPropertyTest do
|
|||
end
|
||||
end
|
||||
|
||||
# §2.4 — single-source exit-date state (quick ↔ detail) ----------------
|
||||
#
|
||||
# The active/former quick filter is a shortcut onto the very same
|
||||
# `exit_date` state the detailed control writes: there is no separate
|
||||
# quick-filter field. This property drives interleaved sequences of quick
|
||||
# and detailed changes and asserts that `quick_state/1` is always a pure
|
||||
# function of the single `exit_date` source — i.e. the two views can never
|
||||
# diverge.
|
||||
|
||||
defp quick_choice_gen, do: member_of([:active, :former, :all])
|
||||
|
||||
defp command_gen do
|
||||
one_of([
|
||||
gen(all(s <- quick_choice_gen()), do: {:quick, s}),
|
||||
gen all(
|
||||
mode <- exit_date_mode_gen(),
|
||||
from <- optional_date_gen(),
|
||||
to <- optional_date_gen()
|
||||
) do
|
||||
{:detail, %{mode: mode, from: from, to: to}}
|
||||
end
|
||||
])
|
||||
end
|
||||
|
||||
defp apply_command(filters, {:quick, s}), do: DateFilter.set_quick_state(filters, s)
|
||||
defp apply_command(filters, {:detail, exit_date}), do: Map.put(filters, :exit_date, exit_date)
|
||||
|
||||
defp expected_quick_for(:active_only), do: :active
|
||||
defp expected_quick_for(:inactive_only), do: :former
|
||||
defp expected_quick_for(:all), do: :all
|
||||
defp expected_quick_for(:custom), do: :custom
|
||||
|
||||
property "quick filter is a single-source shortcut onto exit_date, never divergent" do
|
||||
check all(commands <- list_of(command_gen(), max_length: 12)) do
|
||||
final =
|
||||
Enum.reduce(commands, DateFilter.default(), fn command, filters ->
|
||||
next = apply_command(filters, command)
|
||||
|
||||
# Invariant at every step: quick_state is derived solely from the
|
||||
# exit_date mode — one consistent value, no second source.
|
||||
assert DateFilter.quick_state(next) == expected_quick_for(next.exit_date.mode)
|
||||
|
||||
# A quick command resolves to exactly the chosen state and clears bounds.
|
||||
case command do
|
||||
{:quick, s} ->
|
||||
assert DateFilter.quick_state(next) == s
|
||||
assert next.exit_date.from == nil
|
||||
assert next.exit_date.to == nil
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
end
|
||||
|
||||
next
|
||||
end)
|
||||
|
||||
# join_date is untouched by any exit_date command (no cross-contamination).
|
||||
assert final.join_date == DateFilter.default().join_date
|
||||
end
|
||||
end
|
||||
|
||||
property "set_quick_state ∘ quick_state round-trips for the three quick states" do
|
||||
check all(s <- quick_choice_gen(), exit_date <- exit_date_state_gen()) do
|
||||
filters = Map.put(DateFilter.default(), :exit_date, exit_date)
|
||||
assert filters |> DateFilter.set_quick_state(s) |> DateFilter.quick_state() == s
|
||||
end
|
||||
end
|
||||
|
||||
property "encoding then decoding built-in date filter state is identity" do
|
||||
check all(
|
||||
join_date <- join_date_state_gen(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue