diff --git a/lib/dotcom_web/components/trip_planner/itinerary_group.ex b/lib/dotcom_web/components/trip_planner/itinerary_group.ex
index e1dd1c2d01..afe02d802d 100644
--- a/lib/dotcom_web/components/trip_planner/itinerary_group.ex
+++ b/lib/dotcom_web/components/trip_planner/itinerary_group.ex
@@ -15,13 +15,14 @@ defmodule DotcomWeb.Components.TripPlanner.ItineraryGroup do
def itinerary_group(assigns) do
~H"""
+ <% [first | rest] = @group %>
Group with <%= Enum.count(@group) %> options
- <.accordion :for={{variation, index} <- Enum.with_index(@group)} open={index === 0}>
+ <.accordion>
<:heading>
- <%= format_datetime(variation.departure) %> — <%= format_datetime(variation.arrival) %>
+ <%= format_datetime_full(first.departure) %> — <%= format_datetime_full(first.arrival) %>
<:content>
-
+
<.leg
start_time={leg.start}
end_time={leg.stop}
@@ -32,13 +33,21 @@ defmodule DotcomWeb.Components.TripPlanner.ItineraryGroup do
realtime_state={leg.realtime_state}
/>
+ <%= if Enum.count(rest) > 0, do: "Similar trips depart at:" %>
+
+ <%= format_datetime_short(alternative.departure) %>
+
"""
end
- defp format_datetime(datetime) do
+ defp format_datetime_full(datetime) do
Timex.format!(datetime, "%-I:%M %p", :strftime)
end
+
+ defp format_datetime_short(datetime) do
+ Timex.format!(datetime, "%-I:%M", :strftime)
+ end
end
diff --git a/test/dotcom/trip_plan/itinerary_group_test.exs b/test/dotcom/trip_plan/itinerary_group_test.exs
new file mode 100644
index 0000000000..34e362f3c9
--- /dev/null
+++ b/test/dotcom/trip_plan/itinerary_group_test.exs
@@ -0,0 +1,104 @@
+defmodule Dotcom.TripPlan.ItineraryTest do
+ @moduledoc false
+
+ use ExUnit.Case, async: true
+
+ import Mox
+
+ alias Dotcom.TripPlan.ItineraryGroups
+ alias Test.Support.Factories.{Stops.Stop, TripPlanner.TripPlanner}
+
+ setup do
+ stub(Stops.Repo.Mock, :get, fn _ ->
+ Stop.build(:stop)
+ end)
+
+ stops = TripPlanner.build_list(3, :stop_named_position)
+
+ {:ok, stops: stops}
+ end
+
+ describe "from_itineraries/1" do
+ test "groups itineraries with the same mode, from, and to", %{stops: [a, b, c]} do
+ # SETUP
+ bus_a_b_leg = TripPlanner.build(:bus_leg, from: a, to: b)
+ subway_b_c_leg = TripPlanner.build(:subway_leg, from: b, to: c)
+
+ itineraries =
+ TripPlanner.build_list(:rand.uniform(5), :itinerary, legs: [bus_a_b_leg, subway_b_c_leg])
+
+ # EXERCISE
+ grouped_itineraries = ItineraryGroups.from_itineraries(itineraries)
+
+ # VERIFY
+ assert Kernel.length(grouped_itineraries) == 1
+ end
+
+ test "does not group itineraries with different modes", %{stops: [a, b, c]} do
+ # SETUP
+ bus_a_b_leg = TripPlanner.build(:bus_leg, from: a, to: b)
+ bus_b_c_leg = TripPlanner.build(:bus_leg, from: b, to: c)
+ subway_b_c_leg = TripPlanner.build(:subway_leg, from: b, to: c)
+
+ first_itinerary = TripPlanner.build(:itinerary, legs: [bus_a_b_leg, bus_b_c_leg])
+ second_interary = TripPlanner.build(:itinerary, legs: [bus_a_b_leg, subway_b_c_leg])
+
+ # EXERCISE
+ grouped_itineraries = ItineraryGroups.from_itineraries([first_itinerary, second_interary])
+
+ # VERIFY
+ assert Kernel.length(grouped_itineraries) == 2
+ end
+
+ test "does not group itineraries with different froms", %{stops: [a, b, c]} do
+ # SETUP
+ bus_a_b_leg = TripPlanner.build(:bus_leg, from: a, to: b)
+ bus_b_c_leg = TripPlanner.build(:bus_leg, from: b, to: c)
+ bus_c_a_leg = TripPlanner.build(:bus_leg, from: c, to: a)
+
+ first_itinerary = TripPlanner.build(:itinerary, legs: [bus_a_b_leg, bus_b_c_leg])
+ second_interary = TripPlanner.build(:itinerary, legs: [bus_a_b_leg, bus_c_a_leg])
+
+ # EXERCISE
+ grouped_itineraries = ItineraryGroups.from_itineraries([first_itinerary, second_interary])
+
+ # VERIFY
+ assert Kernel.length(grouped_itineraries) == 2
+ end
+
+ test "does not group itineraries with different tos", %{stops: [a, b, c]} do
+ # SETUP
+ bus_a_b_leg = TripPlanner.build(:bus_leg, from: a, to: b)
+ bus_b_c_leg = TripPlanner.build(:bus_leg, from: b, to: c)
+ bus_b_a_leg = TripPlanner.build(:bus_leg, from: b, to: a)
+
+ first_itinerary = TripPlanner.build(:itinerary, legs: [bus_a_b_leg, bus_b_c_leg])
+ second_interary = TripPlanner.build(:itinerary, legs: [bus_a_b_leg, bus_b_a_leg])
+
+ # EXERCISE
+ grouped_itineraries = ItineraryGroups.from_itineraries([first_itinerary, second_interary])
+
+ # VERIFY
+ assert Kernel.length(grouped_itineraries) == 2
+ end
+ end
+
+ test "ignores short walking distances of < 0.2 miles", %{stops: [a, b, c]} do
+ # SETUP
+ bus_a_b_leg = TripPlanner.build(:bus_leg, from: a, to: b)
+ walk_b_c_leg = TripPlanner.build(:walking_leg, from: b, to: c) |> Map.put(:distance, 0.199)
+ walk_c_b_leg = TripPlanner.build(:walking_leg, from: c, to: b) |> Map.put(:distance, 0.199)
+ bus_b_a_leg = TripPlanner.build(:bus_leg, from: b, to: a)
+
+ first_itinerary =
+ TripPlanner.build(:itinerary, legs: [bus_a_b_leg, walk_b_c_leg, walk_c_b_leg, bus_b_a_leg])
+
+ second_itinerary = TripPlanner.build(:itinerary, legs: [bus_a_b_leg, bus_b_a_leg])
+
+ # EXERCISE
+ grouped_itineraries = ItineraryGroups.from_itineraries([first_itinerary, second_itinerary])
+
+ # VERIFY
+ assert Kernel.length(grouped_itineraries) == 1
+ end
+end