Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trip Planner Release #2341

Open
wants to merge 35 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ba95425
first pass at swapping in new trip planner
anthonyshull Jan 6, 2025
7c95771
restore and remove
anthonyshull Jan 6, 2025
27a6b12
fix vote paths
anthonyshull Jan 6, 2025
2dbebe2
tests
anthonyshull Jan 6, 2025
59415c7
remove the smoke test
anthonyshull Jan 6, 2025
4f8a0cc
add banner
anthonyshull Jan 7, 2025
fbdaf9c
remove preview from admin page
anthonyshull Jan 7, 2025
43c9479
tests
anthonyshull Jan 17, 2025
141fea1
tests and factories
anthonyshull Jan 17, 2025
76f572f
tests
anthonyshull Jan 20, 2025
b46d04a
groupable itineraries tests work
anthonyshull Jan 21, 2025
da95311
format
anthonyshull Jan 21, 2025
ad5ba7b
stray bracket
anthonyshull Jan 21, 2025
0aa36aa
rearrange
anthonyshull Jan 21, 2025
0fcb49d
start of reloading
anthonyshull Jan 20, 2025
ae3d857
works loading old params and reloading
anthonyshull Jan 20, 2025
ba25ea8
remove inspect
anthonyshull Jan 20, 2025
c686507
handle the from/to logic in controller
anthonyshull Jan 20, 2025
2b202de
simplify mount
anthonyshull Jan 20, 2025
509f4a6
remove inspect
anthonyshull Jan 20, 2025
2154eeb
dont replace history
anthonyshull Jan 21, 2025
0e9b658
handle swapping directions
anthonyshull Jan 21, 2025
013a60e
remove tests
anthonyshull Jan 21, 2025
dd676cf
typo
anthonyshull Jan 21, 2025
e01c88f
clean up controller
anthonyshull Jan 21, 2025
0fffa5c
add name
anthonyshull Jan 22, 2025
750181c
format
anthonyshull Jan 22, 2025
c52d45f
merge conflict
anthonyshull Jan 23, 2025
e922e80
follow redirects in test setup
anthonyshull Jan 23, 2025
e102c37
format
anthonyshull Jan 23, 2025
f8ec545
data-test moved to new code structure
anthonyshull Jan 23, 2025
8fa22fc
mount tests
anthonyshull Jan 23, 2025
e7e6156
controller tests
anthonyshull Jan 23, 2025
1378aa4
update scenario for changes in trip planner
anthonyshull Jan 23, 2025
6048c03
fix(autocomplete): adjust clear button width only in detached mode (#…
thecristen Jan 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions assets/css/_autocomplete-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,6 @@

.aa-InputWrapperSuffix {
order: 3;
width: calc(var(--aa-spacing) + var(--aa-icon-size) - 1px);
}

// hide default search magnifying glass icon
Expand All @@ -252,6 +251,12 @@
content: "B"
}

.aa-DetachedOverlay .aa-InputWrapper {
padding-left: calc(var(--aa-spacing));
.aa-DetachedOverlay {
.aa-InputWrapper {
padding-left: calc(var(--aa-spacing));
}

.aa-InputWrapperSuffix {
width: calc(var(--aa-spacing) + var(--aa-icon-size) - 1px);
}
}
24 changes: 0 additions & 24 deletions cypress/e2e/smoke.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,30 +152,6 @@ describe("passes smoke test", () => {
}
});

it("trip planner", () => {
cy.visit("/trip-planner");

// reverses the inputs
cy.get("#from").type("A");
cy.get("#to").type("B");
cy.get("#trip-plan-reverse-control").click();
cy.get("#from").should("have.value", "B");
cy.get("#to").should("have.value", "A");

// opens the date picker
cy.contains("#trip-plan-datepicker").should("not.exist");
cy.get('label[for="arrive"]').click();
cy.get("#trip-plan-datepicker");

// shortcut /from/ - marker A prepopulated
cy.visit("/trip-planner/from/North+Station");
cy.get('img.leaflet-marker-icon[src="/icon-svg/icon-map-pin-a.svg"]');

// shortcut /to/ - marker B prepopulated
cy.visit("/trip-planner/to/North+Station");
cy.get('img.leaflet-marker-icon[src="/icon-svg/icon-map-pin-b.svg"]');
});

it("alerts page", () => {
cy.visit("/alerts");
cy.contains(".m-alerts__mode-buttons a", "Bus").click();
Expand Down
2 changes: 1 addition & 1 deletion integration/scenarios/plan-a-trip-from-homepage.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ exports.scenario = async ({ page, baseURL }) => {

await expect
.poll(async () =>
page.locator("div.m-trip-plan-results__itinerary").count(),
page.locator("section#trip-planner-results").count(),
)
.toBeGreaterThan(0);
};
24 changes: 14 additions & 10 deletions integration/scenarios/plan-a-trip.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,33 @@ const { expect } = require("@playwright/test");
exports.scenario = async ({ page, baseURL }) => {
await page.goto(`${baseURL}/trip-planner`);

await page.locator("input#from").pressSequentially("North Station");
await expect(
page.getByRole("heading", { name: "Trip Planner" }),
).toBeVisible();

await page.locator("#trip-planner-input-form--from input[type='search']").pressSequentially("North Station");
await page.waitForSelector(
"div#from-autocomplete-results span.c-search-bar__-dropdown-menu",
"ul.aa-List",
);
await page.keyboard.press("ArrowDown");
await page.keyboard.press("Enter");

await page.locator("input#to").pressSequentially("South Station");
// The A location pin.
await page.waitForSelector("#mbta-metro-pin-0");

await page.locator("#trip-planner-input-form--to input[type='search']").pressSequentially("South Station");
await page.waitForSelector(
"div#to-autocomplete-results span.c-search-bar__-dropdown-menu",
"ul.aa-List",
);
await page.keyboard.press("ArrowDown");
await page.keyboard.press("Enter");

await page.locator("button#trip-plan__submit").click();

await expect(
page.getByRole("heading", { name: "Trip Planner" }),
).toBeVisible();
// The B location pin.
await page.waitForSelector("#mbta-metro-pin-1");

await expect
.poll(async () =>
page.locator("div.m-trip-plan-results__itinerary").count(),
page.locator("section#trip-planner-results").count(),
)
.toBeGreaterThan(0);
};
116 changes: 83 additions & 33 deletions lib/dotcom/trip_plan/anti_corruption_layer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,72 @@ defmodule Dotcom.TripPlan.AntiCorruptionLayer do
We ignore datetime_type and datetime and allow those to be set to 'now' and the current time respectively.
"""

@location_service Application.compile_env!(:dotcom, :location_service)
@default_modes Dotcom.TripPlan.InputForm.initial_modes()
@default_params %{
"datetime_type" => "now",
"modes" => @default_modes,
"wheelchair" => "false"
}

@doc """
Given a query for the old trip planner /to or /from actions, replicate the old
behavior by searching for a location and using the first result. Convert this
to the new trip planner form values.
Given the params from the old trip planner, convert them to the new trip planner form values.

If no plan is given, then we default to empty form values.
"""
def convert_old_action(action) do
with [key] when key in [:from, :to] <- Map.keys(action),
query when is_binary(query) <- Map.get(action, key),
{:ok, [%LocationService.Address{} = geocoded | _]} <- @location_service.geocode(query) do
%{
"plan" => %{
"#{key}_latitude" => geocoded.latitude,
"#{key}_longitude" => geocoded.longitude,
"#{key}" => geocoded.formatted
}
}
def convert_old_params(%{"plan" => params}) do
Map.merge(@default_params, copy_params(params))
end

def convert_old_params(_), do: convert_old_params(%{"plan" => %{}})

# Decode a string into form values.
# Add in defaults if they were omitted.
def decode(string) do
with {:ok, binary} <- Base.url_decode64(string),
{:ok, params} <- :msgpack.unpack(binary) do
params
|> decode_datetime()
|> add_defaults()
else
_ ->
%{"plan" => %{}}
_ -> @default_params
end
end

@doc """
Given the params from the old trip planner, convert them to the new trip planner form values.
def default_params, do: @default_params

If no plan is given, then we default to empty form values.
"""
def convert_old_params(%{"plan" => params}) do
# Encode form values into a single string.
# Strip out defaults so we don't waste space encoding them.
def encode(params) do
params
|> strip_defaults()
|> encode_datetime()
|> :msgpack.pack()
|> Base.url_encode64()
end

# Make sure that the params have all of the defaults set.
defp add_defaults(params) do
Map.merge(@default_params, params)
end

# All other modes but commuter rail are just the mode in uppercase.
defp convert_mode("commuter_rail"), do: "RAIL"
defp convert_mode(mode), do: String.upcase(mode)

# When modes are given, we set all non-given modes to false.
defp convert_modes(modes) when is_map(modes) do
default_modes = for {k, _} <- @default_modes, into: %{}, do: {k, "false"}

Enum.reduce(modes, default_modes, fn {key, value}, acc ->
Map.put(acc, convert_mode(key), value)
end)
end

# When no modes are given, we use the initial modes--all modes are true.
defp convert_modes(_), do: @default_modes

# Copy the old params into the new param structure.
defp copy_params(params) do
%{
"from" => %{
"latitude" => Map.get(params, "from_latitude"),
Expand All @@ -56,20 +92,34 @@ defmodule Dotcom.TripPlan.AntiCorruptionLayer do
}
end

def convert_old_params(_), do: convert_old_params(%{"plan" => %{}})
defp decode_datetime(%{"datetime" => datetime} = params) do
case DateTime.from_iso8601(datetime) do
{:ok, datetime, _} -> Map.put(params, "datetime", datetime)
_ -> params
end
end

defp convert_modes(modes) when is_map(modes) do
default_modes =
for {k, _} <- Dotcom.TripPlan.InputForm.initial_modes(), into: %{}, do: {k, "false"}
defp decode_datetime(params), do: params

modes
|> Enum.reduce(default_modes, fn {key, value}, acc ->
Map.put(acc, convert_mode(key), value)
end)
# Encode the datetime into an ISO8601 string.
defp encode_datetime(params) do
case params |> Map.get("datetime") do
%DateTime{} = datetime -> Map.put(params, "datetime", DateTime.to_iso8601(datetime))
_ -> params
end
end

defp convert_modes(_), do: Dotcom.TripPlan.InputForm.initial_modes()
# If the params have a key set and it's just the default value, then remove it.
defp strip_default(params, key) do
if Map.has_key?(params, key) && Map.get(params, key) == Map.get(@default_params, key) do
Map.delete(params, key)
else
params
end
end

defp convert_mode("commuter_rail"), do: "RAIL"
defp convert_mode(mode), do: String.upcase(mode)
# Strip default params so we don't waste space encoding them.
defp strip_defaults(params) do
Enum.reduce(@default_params, params, fn {key, _}, acc -> strip_default(acc, key) end)
end
end
2 changes: 1 addition & 1 deletion lib/dotcom/trip_plan/input_form.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ defmodule Dotcom.TripPlan.InputForm do
embeds_one(:modes, __MODULE__.Modes)
field(:datetime_type, :string)
field(:datetime, :naive_datetime)
field(:wheelchair, :boolean, default: true)
field(:wheelchair, :boolean)
end

def initial_modes do
Expand Down
34 changes: 23 additions & 11 deletions lib/dotcom/trip_plan/parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ defmodule Dotcom.TripPlan.Parser do
MBTA system.
"""

require Logger

alias Dotcom.TripPlan.{FarePasses, Itinerary, Leg, NamedPosition, PersonalDetail, TransitDetail}
alias OpenTripPlannerClient.Schema

Expand Down Expand Up @@ -164,20 +166,30 @@ defmodule Dotcom.TripPlan.Parser do
defp route_color("Logan Express", "DV", _), do: "704c9f"
defp route_color(_, _, color), do: color

defp build_stop(stop, attributes \\ %{}) do
case stop.gtfs_id do
"mbta-ma-us:" <> gtfs_id ->
@stops_repo.get(gtfs_id)
|> struct(attributes)

_ ->
stop
|> Map.from_struct()
|> Map.merge(attributes)
|> then(&struct(Stops.Stop, &1))
defp build_stop(stop, attributes \\ %{})

defp build_stop(%Schema.Stop{gtfs_id: "mbta-ma-us:" <> gtfs_id} = schema_stop, attributes) do
stop = @stops_repo.get(gtfs_id)

if stop do
stop
|> Map.merge(attributes)
else
Logger.notice("dotcom.trip_plan.parser unknown_stop=mbta-ma-us:#{gtfs_id}")

schema_stop
|> Map.put(:gtfs_id, gtfs_id)
|> build_stop(attributes)
end
end

defp build_stop(stop, attributes) do
stop
|> Map.from_struct()
|> Map.merge(attributes)
|> then(&struct(Stops.Stop, &1))
end

defp id_from_gtfs(gtfs_id) do
case String.split(gtfs_id, ":") do
[_, id] -> id
Expand Down
5 changes: 4 additions & 1 deletion lib/dotcom_web/components/trip_planner/results.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ defmodule DotcomWeb.Components.TripPlanner.Results do
<div
:if={Enum.count(@results.itinerary_groups) > 0 && @results.itinerary_group_selection}
class="h-min w-full mb-3.5"
data-test={"results:itinerary_group:selected:#{@results.itinerary_group_selection}"}
>
<button type="button" phx-click="reset_itinerary_group" class="btn-link">
<span class="flex flex-row items-center">
Expand Down Expand Up @@ -55,6 +56,7 @@ defmodule DotcomWeb.Components.TripPlanner.Results do
class="border border-solid border-gray-lighter p-4"
phx-click="select_itinerary_group"
phx-value-index={index}
data-test={"results:itinerary_group:#{index}"}
>
<div
:if={group.summary.tag}
Expand Down Expand Up @@ -107,7 +109,7 @@ defmodule DotcomWeb.Components.TripPlanner.Results do
}

~H"""
<div>
<div data-test={"itinerary_detail:selected:#{@itinerary_selection}"}>
<.itinerary_summary summary={@summary} />
<div :if={Enum.count(@all_times) > 1}>
<hr class="border-gray-lighter" />
Expand All @@ -121,6 +123,7 @@ defmodule DotcomWeb.Components.TripPlanner.Results do
variant="secondary"
phx-click="select_itinerary"
phx-value-index={index}
data-test={"itinerary_detail:#{index}"}
>
{formatted_time(time)}
</.button>
Expand Down
4 changes: 3 additions & 1 deletion lib/dotcom_web/components/trip_planner/results_summary.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ defmodule DotcomWeb.Components.TripPlanner.ResultsSummary do

defp results_feedback(assigns) do
~H"""
<.feedback kind={:error}>{@results.error}</.feedback>
<.feedback kind={:error}>
<span data-test="results-summary:error">{@results.error}</span>
</.feedback>
"""
end

Expand Down
2 changes: 2 additions & 0 deletions lib/dotcom_web/components/trip_planner/transit_leg.ex
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ defmodule DotcomWeb.Components.TripPlanner.TransitLeg do
"""
end

defp stop_url(_, %Stop{} = stop) when is_nil(stop.id), do: nil

defp stop_url(%Route{external_agency_name: nil}, %Stop{} = stop) do
~p"/stops/#{stop}"
end
Expand Down
Loading
Loading