diff --git a/cypress/e2e/smoke.cy.js b/cypress/e2e/smoke.cy.js
index 7416723d32..3de6db722a 100644
--- a/cypress/e2e/smoke.cy.js
+++ b/cypress/e2e/smoke.cy.js
@@ -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();
diff --git a/integration/scenarios/plan-a-trip-from-homepage.js b/integration/scenarios/plan-a-trip-from-homepage.js
index b4a241e98f..92bb0c83e7 100644
--- a/integration/scenarios/plan-a-trip-from-homepage.js
+++ b/integration/scenarios/plan-a-trip-from-homepage.js
@@ -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);
};
diff --git a/integration/scenarios/plan-a-trip.js b/integration/scenarios/plan-a-trip.js
index 4473a476b1..fde2eb62be 100644
--- a/integration/scenarios/plan-a-trip.js
+++ b/integration/scenarios/plan-a-trip.js
@@ -3,29 +3,35 @@ 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("fieldset#trip-planner-locations-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("fieldset#trip-planner-locations-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();
+ // The B location pin.
+ await page.waitForSelector("#mbta-metro-pin-1");
- await expect(
- page.getByRole("heading", { name: "Trip Planner" }),
- ).toBeVisible();
+ await page.getByText("Get trip suggestions").click();
await expect
.poll(async () =>
- page.locator("div.m-trip-plan-results__itinerary").count(),
+ page.locator("section#trip-planner-results").count(),
)
.toBeGreaterThan(0);
};
diff --git a/lib/dotcom_web/controllers/trip_plan_controller.ex b/lib/dotcom_web/controllers/trip_plan_controller.ex
deleted file mode 100644
index cc60b210af..0000000000
--- a/lib/dotcom_web/controllers/trip_plan_controller.ex
+++ /dev/null
@@ -1,309 +0,0 @@
-defmodule DotcomWeb.TripPlanController do
- @moduledoc """
- Controller for trip plans.
- """
-
- use DotcomWeb, :controller
-
- require Logger
-
- alias Dotcom.TripPlan.{
- Itinerary,
- ItineraryRowList,
- Leg,
- NamedPosition,
- PersonalDetail,
- Query,
- RelatedLink,
- TransitDetail
- }
-
- alias Dotcom.TripPlan.Map, as: TripPlanMap
- alias Routes.Route
-
- @location_service Application.compile_env!(:dotcom, :location_service)
-
- @type route_map :: %{optional(Route.id_t()) => Route.t()}
- @type route_mapper :: (Route.id_t() -> Route.t() | nil)
-
- plug(:assign_initial_map)
- plug(:breadcrumbs)
- plug(:modes)
- plug(:wheelchair)
- plug(:meta_description)
- plug(:assign_params)
-
- def index(conn, %{"plan" => %{"to" => _to, "from" => _fr} = plan}) do
- conn
- |> assign(:expanded, conn.query_params["expanded"])
- |> render_plan(plan)
- end
-
- def index(conn, _params) do
- render(conn, :index)
- end
-
- def from(conn, %{"plan" => _plan} = params) do
- redirect(conn, to: trip_plan_path(conn, :index, Map.delete(params, "address")))
- end
-
- def from(conn, %{
- "address" => address
- }) do
- if String.match?(address, ~r/^(\-?\d+(\.\d+)?),(\-?\d+(\.\d+)?),.*$/) do
- [latitude, longitude, name] = String.split(address, ",", parts: 3)
- # Avoid extra geocode call, just use these coordinates
- destination = %NamedPosition{
- latitude: String.to_float(latitude),
- longitude: String.to_float(longitude),
- name: name,
- stop: nil
- }
-
- do_from(conn, destination)
- else
- updated_address = check_address(address)
-
- case @location_service.geocode(updated_address) do
- {:ok, [geocoded_from | _]} ->
- do_from(conn, NamedPosition.new(geocoded_from))
-
- _ ->
- # redirect to the initial index page
- redirect(conn, to: trip_plan_path(conn, :index))
- end
- end
- end
-
- defp do_from(conn, destination) do
- # build a default query with a pre-filled 'from' field:
- query = %Query{
- from: destination,
- to: {:error, :unknown},
- time: {:error, :unknown}
- }
-
- now = Util.now()
-
- # build map information for a single leg with the 'from' field:
- map_data =
- TripPlanMap.itinerary_map([
- %Leg{
- from: destination,
- to: nil,
- mode: %PersonalDetail{},
- start: now,
- stop: now
- }
- ])
-
- %{markers: [marker]} = map_data
- from_marker = %{marker | id: "B"}
- map_info_for_from_destination = %{map_data | markers: [from_marker]}
-
- conn
- |> assign(:query, query)
- |> assign(:map_data, map_info_for_from_destination)
- |> render(:index)
- end
-
- def to(conn, %{"plan" => _plan} = params) do
- redirect(conn, to: trip_plan_path(conn, :index, Map.delete(params, "address")))
- end
-
- def to(conn, %{
- "address" => address
- }) do
- if String.match?(address, ~r/^(\-?\d+(\.\d+)?),(\-?\d+(\.\d+)?),.*$/) do
- [latitude, longitude, name] = String.split(address, ",", parts: 3)
- # Avoid extra geocode call, just use these coordinates
- destination = %NamedPosition{
- latitude: String.to_float(latitude),
- longitude: String.to_float(longitude),
- name: name,
- stop: nil
- }
-
- do_to(conn, destination)
- else
- updated_address = check_address(address)
-
- case @location_service.geocode(updated_address) do
- {:ok, [geocoded_to | _]} ->
- do_to(conn, NamedPosition.new(geocoded_to))
-
- _ ->
- # redirect to the initial index page
- redirect(conn, to: trip_plan_path(conn, :index))
- end
- end
- end
-
- defp do_to(conn, destination) do
- # build a default query with a pre-filled 'to' field:
- query = %Query{
- to: destination,
- time: {:error, :unknown},
- from: {:error, :unknown}
- }
-
- now = Util.now()
-
- # build map information for a single leg with the 'to' field:
- map_data =
- TripPlanMap.itinerary_map([
- %Leg{
- from: nil,
- to: destination,
- mode: %PersonalDetail{},
- start: now,
- stop: now
- }
- ])
-
- %{markers: [marker]} = map_data
- to_marker = %{marker | id: "B"}
- map_info_for_to_destination = %{map_data | markers: [to_marker]}
-
- conn
- |> assign(:query, query)
- |> assign(:map_data, map_info_for_to_destination)
- |> render(:index)
- end
-
- defp assign_params(conn, _) do
- conn
- |> assign(:chosen_date_time, conn.params["plan"]["date_time"])
- |> assign(:chosen_time, conn.params["plan"]["time"])
- end
-
- @spec check_address(String.t()) :: String.t()
- defp check_address(address) do
- # address can be a String containing "lat,lon" so we check for that case
-
- [lat, lon] =
- case String.split(address, ",", parts: 2) do
- [lat, lon] -> [lat, lon]
- _ -> ["error", "error"]
- end
-
- if Float.parse(lat) == :error || Float.parse(lon) == :error do
- address
- else
- {parsed_lat, _} = Float.parse(lat)
- {parsed_lon, _} = Float.parse(lon)
-
- case @location_service.reverse_geocode(parsed_lat, parsed_lon) do
- {:ok, [first | _]} ->
- first.formatted
-
- _ ->
- "#{lat}, #{lon}"
- end
- end
- end
-
- defp get_route(link) do
- if is_bitstring(link.text) do
- link.text
- else
- link.text |> List.to_string()
- end
- end
-
- defp filter_duplicate_links(related_links) do
- Enum.map(related_links, fn x -> Enum.uniq_by(x, fn y -> get_route(y) end) end)
- end
-
- @spec render_plan(Plug.Conn.t(), map) :: Plug.Conn.t()
- defp render_plan(conn, plan_params) do
- query =
- Query.from_query(
- plan_params,
- now: conn.assigns.date_time,
- end_of_rating: Map.get(conn.assigns, :end_of_rating, Schedules.Repo.end_of_rating())
- )
-
- itineraries =
- query
- |> Query.get_itineraries()
-
- itinerary_row_lists = itinerary_row_lists(itineraries, plan_params)
-
- conn
- |> render(
- query: query,
- itineraries: itineraries,
- plan_error: MapSet.to_list(query.errors),
- routes: Enum.map(itineraries, &routes_for_itinerary(&1)),
- itinerary_maps: Enum.map(itineraries, &TripPlanMap.itinerary_map(&1)),
- related_links:
- filter_duplicate_links(Enum.map(itineraries, &RelatedLink.links_for_itinerary(&1))),
- itinerary_row_lists: itinerary_row_lists
- )
- end
-
- @spec itinerary_row_lists([Itinerary.t()], map) :: [ItineraryRowList.t()]
- defp itinerary_row_lists(itineraries, plan) do
- Enum.map(itineraries, &ItineraryRowList.from_itinerary(&1, to_and_from(plan)))
- end
-
- @spec assign_initial_map(Plug.Conn.t(), any()) :: Plug.Conn.t()
- def assign_initial_map(conn, _opts) do
- conn
- |> assign(:map_data, TripPlanMap.initial_map_data())
- end
-
- @spec modes(Plug.Conn.t(), Keyword.t()) :: Plug.Conn.t()
- def modes(%Plug.Conn{params: %{"plan" => %{"modes" => modes}}} = conn, _) do
- assign(
- conn,
- :modes,
- Map.new(modes, fn {mode, active?} -> {String.to_existing_atom(mode), active? === "true"} end)
- )
- end
-
- def modes(%Plug.Conn{} = conn, _) do
- assign(
- conn,
- :modes,
- %{subway: true, bus: true, commuter_rail: true, ferry: true}
- )
- end
-
- @spec breadcrumbs(Plug.Conn.t(), Keyword.t()) :: Plug.Conn.t()
- defp breadcrumbs(conn, _) do
- assign(conn, :breadcrumbs, [Breadcrumb.build("Trip Planner")])
- end
-
- @spec wheelchair(Plug.Conn.t(), Keyword.t()) :: Plug.Conn.t()
- def wheelchair(%Plug.Conn{params: %{"plan" => plan_params}} = conn, _) do
- assign(conn, :wheelchair, get_in(plan_params, ["wheelchair"]) === "true")
- end
-
- # Initialize to checked state for trip plan accessibility
- def wheelchair(%Plug.Conn{} = conn, _) do
- assign(conn, :wheelchair, true)
- end
-
- @spec routes_for_itinerary(Itinerary.t()) :: [Route.t()]
- defp routes_for_itinerary(itinerary) do
- itinerary.legs
- |> Enum.filter(&match?(%TransitDetail{}, &1.mode))
- |> Enum.map(& &1.mode.route)
- end
-
- @spec to_and_from(map) :: [to: String.t() | nil, from: String.t() | nil]
- def to_and_from(plan) do
- [to: Map.get(plan, "to"), from: Map.get(plan, "from")]
- end
-
- defp meta_description(conn, _) do
- conn
- |> assign(
- :meta_description,
- "Plan a trip on public transit in the Greater Boston region with directions " <>
- "and suggestions based on real-time data."
- )
- end
-end
diff --git a/lib/dotcom_web/controllers/vote_controller.ex b/lib/dotcom_web/controllers/vote_controller.ex
index c16b493ba0..0283175d76 100644
--- a/lib/dotcom_web/controllers/vote_controller.ex
+++ b/lib/dotcom_web/controllers/vote_controller.ex
@@ -44,7 +44,7 @@ defmodule DotcomWeb.VoteController do
conn
|> assign(:polling_location, polling_location)
|> assign(:polling_location_name, polling_location_name)
- |> assign(:trip_plan_path, trip_plan_path(DotcomWeb.Endpoint, :index, params))
+ |> assign(:trip_plan_path, "/trip-planner#{URI.encode_query(params)}")
_ ->
conn |> assign(:polling_error, true)
diff --git a/lib/dotcom_web/live/admin.ex b/lib/dotcom_web/live/admin.ex
index 8ae633fe2a..4202009b75 100644
--- a/lib/dotcom_web/live/admin.ex
+++ b/lib/dotcom_web/live/admin.ex
@@ -11,11 +11,6 @@ defmodule DotcomWeb.Live.Admin do
url: Helpers.live_path(socket, DotcomWeb.Live.Admin.TripPlanFeedback),
title: "Trip Planner Feedback",
description: "Find and download the latest comments and votes."
- },
- %{
- url: Helpers.live_path(socket, DotcomWeb.Live.TripPlanner),
- title: "Trip Planner Preview",
- description: "WIP on the trip planner rewrite."
}
]
)}
diff --git a/lib/dotcom_web/live/trip_planner.ex b/lib/dotcom_web/live/trip_planner.ex
index 1b7c2bc651..7029b32a96 100644
--- a/lib/dotcom_web/live/trip_planner.ex
+++ b/lib/dotcom_web/live/trip_planner.ex
@@ -62,7 +62,7 @@ defmodule DotcomWeb.Live.TripPlanner do
"""
def render(assigns) do
~H"""
-
Trip Planner Preview
+ Trip Planner
<.input_form class="mb-4" changeset={@input_form.changeset} />
diff --git a/lib/dotcom_web/router.ex b/lib/dotcom_web/router.ex
index e993fcf159..f53865d67a 100644
--- a/lib/dotcom_web/router.ex
+++ b/lib/dotcom_web/router.ex
@@ -227,11 +227,6 @@ defmodule DotcomWeb.Router do
get("/style-guide/:section/:subpage", StyleGuideController, :show)
get("/transit-near-me", TransitNearMeController, :index)
resources("/alerts", AlertController, only: [:index, :show])
- get("/trip-planner", TripPlanController, :index)
- get("/trip-planner/from/", Redirector, to: "/trip-planner")
- get("/trip-planner/from/:address", TripPlanController, :from)
- get("/trip-planner/to/", Redirector, to: "/trip-planner")
- get("/trip-planner/to/:address", TripPlanController, :to)
delete("/trip-planner/feedback", TripPlan.Feedback, :delete)
post("/trip-planner/feedback", TripPlan.Feedback, :put)
get("/customer-support", CustomerSupportController, :index)
@@ -263,11 +258,11 @@ defmodule DotcomWeb.Router do
end
end
- scope "/preview", DotcomWeb do
+ scope "/", DotcomWeb do
import Phoenix.LiveView.Router
- pipe_through([:browser, :browser_live, :basic_auth_readonly])
+ pipe_through([:browser, :browser_live])
- live_session :rider, layout: {DotcomWeb.LayoutView, :preview} do
+ live_session :rider, layout: {DotcomWeb.LayoutView, :live} do
live("/trip-planner", Live.TripPlanner)
end
end
diff --git a/lib/dotcom_web/templates/fare/_nearby_locations.html.eex b/lib/dotcom_web/templates/fare/_nearby_locations.html.eex
index e09fc7fe47..750250fcaa 100644
--- a/lib/dotcom_web/templates/fare/_nearby_locations.html.eex
+++ b/lib/dotcom_web/templates/fare/_nearby_locations.html.eex
@@ -36,7 +36,7 @@
<% end %>
+
<%= if assigns[:search_header?] do %>
<%= render "_searchbar.html", assigns %>
<% end %>
diff --git a/lib/dotcom_web/templates/vote/show.html.heex b/lib/dotcom_web/templates/vote/show.html.heex
index 44db0cfd62..337089fca1 100644
--- a/lib/dotcom_web/templates/vote/show.html.heex
+++ b/lib/dotcom_web/templates/vote/show.html.heex
@@ -56,7 +56,7 @@
Secretary of State's official site
to find your polling place, and then use the
-
+
MBTA Trip Planner
to plan your trip.
diff --git a/test/dotcom_web/controllers/trip_plan_controller_test.exs b/test/dotcom_web/controllers/trip_plan_controller_test.exs
deleted file mode 100644
index 0e2cb30203..0000000000
--- a/test/dotcom_web/controllers/trip_plan_controller_test.exs
+++ /dev/null
@@ -1,720 +0,0 @@
-defmodule DotcomWeb.TripPlanControllerTest do
- use DotcomWeb.ConnCase, async: true
-
- alias Dotcom.TripPlan.{Itinerary, PersonalDetail, Query, TransitDetail}
- alias Fares.Fare
-
- import Test.Support.Factories.LocationService.LocationService
-
- doctest DotcomWeb.TripPlanController
-
- import Mox
-
- @system_time "2017-01-01T12:20:00-05:00"
- @morning %{
- "year" => "2017",
- "month" => "1",
- "day" => "2",
- "hour" => "9",
- "minute" => "30",
- "am_pm" => "AM"
- }
- @afternoon %{
- "year" => "2017",
- "month" => "1",
- "day" => "2",
- "hour" => "5",
- "minute" => "30",
- "am_pm" => "PM"
- }
- @after_hours %{
- "year" => "2017",
- "month" => "1",
- "day" => "2",
- "hour" => "3",
- "minute" => "00",
- "am_pm" => "AM"
- }
- @modes %{"subway" => "true", "commuter_rail" => "true", "bus" => "false", "ferry" => "false"}
-
- @good_params %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "from address",
- "to" => "to address",
- "date_time" => @afternoon,
- "time" => "depart",
- "modes" => @modes,
- "wheelchair" => "true"
- }
- }
-
- @bad_params %{
- "date_time" => @system_time,
- "plan" => %{"from" => "no results", "to" => "too many results", "date_time" => @afternoon}
- }
-
- setup :verify_on_exit!
-
- setup do
- stub(MBTA.Api.Mock, :get_json, fn "/schedules/", [route: "Red", date: "1970-01-01"] ->
- {:error,
- [
- %JsonApi.Error{
- code: "no_service",
- source: %{
- "parameter" => "date"
- },
- detail: "The current rating does not describe service on that date.",
- meta: %{
- "end_date" => "2024-06-15",
- "start_date" => "2024-05-10",
- "version" => "Spring 2024, 2024-05-17T21:10:15+00:00, version D"
- }
- }
- ]}
- end)
-
- stub(OpenTripPlannerClient.Mock, :plan, fn _from, _to, _opts ->
- {:ok, %OpenTripPlannerClient.Plan{itineraries: []}}
- end)
-
- stub(LocationService.Mock, :geocode, fn name ->
- {:ok, build_list(2, :address, %{formatted: name})}
- end)
-
- stub(Stops.Repo.Mock, :get_parent, fn _ ->
- %Stops.Stop{}
- end)
-
- cache = Application.get_env(:dotcom, :cache)
- cache.flush()
-
- conn = default_conn()
-
- end_of_rating =
- @system_time
- |> Timex.parse!("{ISO:Extended}")
- |> Timex.shift(months: 3)
- |> DateTime.to_date()
-
- {:ok, conn: assign(conn, :end_of_rating, end_of_rating)}
- end
-
- describe "index without params" do
- test "renders index.html", %{conn: conn} do
- conn = get(conn, trip_plan_path(conn, :index))
- assert html_response(conn, 200) =~ "Trip Planner"
- end
-
- test "assigns initial map data", %{conn: conn} do
- conn = get(conn, trip_plan_path(conn, :index))
- assert conn.assigns.map_data
- end
-
- test "sets a custom meta description", %{conn: conn} do
- conn = get(conn, trip_plan_path(conn, :index))
- assert conn.assigns.meta_description
- end
- end
-
- describe "index with params" do
- test "renders the query plan", %{conn: conn} do
- conn = get(conn, trip_plan_path(conn, :index, @good_params))
- response = html_response(conn, 200)
- assert response =~ "Trip Planner"
- assert %Query{} = conn.assigns.query
- assert conn.assigns.itineraries
- assert conn.assigns.routes
- assert conn.assigns.itinerary_maps
- assert conn.assigns.related_links
- end
-
- test "uses current location to render a query plan", %{conn: conn} do
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "Your current location",
- "from_latitude" => "42.3428",
- "from_longitude" => "-71.0857",
- "to" => "to address",
- "to_latitude" => "",
- "to_longitude" => "",
- "date_time" => @morning,
- "modes" => @modes
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
-
- assert html_response(conn, 200) =~ "Trip Planner"
- assert %Query{} = conn.assigns.query
- end
-
- test "sets hidden inputs for lat/lng", %{conn: conn} do
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "from address",
- "from_latitude" => "1",
- "from_longitude" => "2",
- "to" => "to address",
- "to_latitude" => "3",
- "to_longitude" => "4",
- "date_time" => @morning,
- "modes" => @modes
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
-
- resp = html_response(conn, 200)
- assert from_latitude = Floki.find(resp, "#from_latitude")
- assert from_longitude = Floki.find(resp, "#from_longitude")
- assert to_latitude = Floki.find(resp, "#to_latitude")
- assert to_longitude = Floki.find(resp, "#to_longitude")
- assert List.first(Floki.attribute(from_latitude, "value")) == "1.0"
- assert List.first(Floki.attribute(from_longitude, "value")) == "2.0"
- assert List.first(Floki.attribute(to_latitude, "value")) == "3.0"
- assert List.first(Floki.attribute(to_longitude, "value")) == "4.0"
- end
-
- test "assigns.mode is a map of parsed mode state", %{conn: conn} do
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "Your current location",
- "from_latitude" => "42.3428",
- "from_longitude" => "-71.0857",
- "to" => "to address",
- "to_latitude" => "",
- "to_longitude" => "",
- "date_time" => @morning,
- "modes" => @modes
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
-
- assert html_response(conn, 200) =~ "Trip Planner"
- assert conn.assigns.modes == %{subway: true, commuter_rail: true, bus: false, ferry: false}
- assert %Query{} = conn.assigns.query
- end
-
- test "assigns.wheelchair uses value provided in params", %{conn: conn} do
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "Your current location",
- "from_latitude" => "42.3428",
- "from_longitude" => "-71.0857",
- "to" => "to address",
- "to_latitude" => "",
- "to_longitude" => "",
- "date_time" => @morning,
- "modes" => @modes,
- "wheelchair" => "true"
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
-
- assert html_response(conn, 200) =~ "Trip Planner"
- assert conn.assigns.wheelchair == true
- end
-
- test "can use the old date time format", %{conn: conn} do
- old_dt_format = Map.delete(@afternoon, "am_pm")
-
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "from_address",
- "from_latitude" => "",
- "from_longitude" => "",
- "to" => "to address",
- "to_latitude" => "",
- "to_longitude" => "",
- "date_time" => old_dt_format,
- "mode" => @modes
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
- assert html_response(conn, 200)
- end
-
- test "each map url has a path color", %{conn: conn} do
- conn = get(conn, trip_plan_path(conn, :index, @good_params))
-
- for {map_data, static_map} <- conn.assigns.itinerary_maps do
- assert static_map =~ "color"
-
- for path <- map_data.polylines do
- assert path.color
- end
- end
- end
-
- test "renders a geocoding error", %{conn: conn} do
- conn = get(conn, trip_plan_path(conn, :index, @bad_params))
- response = html_response(conn, 200)
- assert response =~ "Trip Planner"
- # shows error styling on location input with "required" label
- assert response =~ "(Required)"
- assert %Query{} = conn.assigns.query
- end
-
- test "assigns maps for each itinerary", %{conn: conn} do
- conn = get(conn, trip_plan_path(conn, :index, @good_params))
- assert conn.assigns.itinerary_maps
- end
-
- test "gets routes from each itinerary", %{conn: conn} do
- conn = get(conn, trip_plan_path(conn, :index, @good_params))
- assert conn.assigns.routes
-
- for routes_for_itinerary <- conn.assigns.routes do
- assert length(routes_for_itinerary) > 0
- end
- end
-
- test "assigns an ItineraryRowList for each itinerary", %{conn: conn} do
- conn = get(conn, trip_plan_path(conn, :index, @good_params))
- assert conn.assigns.itinerary_row_lists
- end
-
- test "adds fare data to each transit leg of each itinerary", %{conn: conn} do
- conn = get(conn, trip_plan_path(conn, :index, @good_params))
-
- assert Enum.all?(conn.assigns.itineraries, fn itinerary ->
- Enum.all?(itinerary.legs, fn leg ->
- match?(%PersonalDetail{}, leg.mode) ||
- match?(
- %TransitDetail{
- fares: %{
- highest_one_way_fare: %Fares.Fare{},
- lowest_one_way_fare: %Fares.Fare{},
- reduced_one_way_fare: %Fares.Fare{}
- }
- },
- leg.mode
- )
- end)
- end)
- end
-
- test "returns all nil fares when there is not enough information", %{conn: conn} do
- conn = get(conn, trip_plan_path(conn, :index, @good_params))
-
- for itinerary <- conn.assigns.itineraries do
- for leg <- itinerary.legs do
- if Dotcom.TripPlan.Leg.transit?(leg) do
- assert leg.mode.fares == %{
- highest_one_way_fare: nil,
- lowest_one_way_fare: nil,
- reduced_one_way_fare: nil
- }
- end
- end
- end
- end
-
- test "adds monthly pass data to each itinerary", %{conn: conn} do
- conn = get(conn, trip_plan_path(conn, :index, @good_params))
-
- assert Enum.all?(conn.assigns.itineraries, fn itinerary ->
- %Itinerary{passes: %{base_month_pass: %Fare{}, recommended_month_pass: %Fare{}}} =
- itinerary
- end)
- end
-
- test "renders an error if longitude and latitude from both addresses are the same", %{
- conn: conn
- } do
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from_latitude" => "90",
- "to_latitude" => "90",
- "from_longitude" => "50",
- "to_longitude" => "50",
- "date_time" => @afternoon,
- "from" => "from St",
- "to" => "from Street"
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
- assert conn.assigns.plan_error == [:same_address]
- assert html_response(conn, 200)
- assert html_response(conn, 200) =~ "two different locations"
- end
-
- test "doesn't render an error if longitudes and latitudes are unique", %{conn: conn} do
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from_latitude" => "90",
- "to_latitude" => "90.5",
- "from_longitude" => "50.5",
- "to_longitude" => "50",
- "date_time" => @afternoon,
- "from" => "from St",
- "to" => "from Street"
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
- assert conn.assigns.plan_error == []
- assert html_response(conn, 200)
- end
-
- test "renders an error if to and from address are the same", %{conn: conn} do
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "from",
- "to" => "from",
- "date_time" => @afternoon
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
- assert conn.assigns.plan_error == [:same_address]
- assert html_response(conn, 200)
- assert html_response(conn, 200) =~ "two different locations"
- end
-
- test "doesn't render an error if to and from address are unique", %{conn: conn} do
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "from",
- "to" => "to",
- "date_time" => @afternoon
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
- assert conn.assigns.plan_error == []
- assert html_response(conn, 200)
- end
-
- test "handles empty lat/lng", %{conn: conn} do
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "from",
- "to" => "from",
- "to_latitude" => "",
- "to_longitude" => "",
- "from_latitude" => "",
- "from_longitude" => "",
- "date_time" => @afternoon
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
- assert conn.assigns.plan_error == [:same_address]
- assert html_response(conn, 200)
- assert html_response(conn, 200) =~ "two different locations"
- end
-
- test "bad date input: fictional day", %{conn: conn} do
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "from address",
- "to" => "to address",
- "date_time" => %{@morning | "month" => "6", "day" => "31"}
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
- response = html_response(conn, 200)
- assert response =~ "Date is not valid"
- end
-
- test "bad date input: partial input", %{conn: conn} do
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "from address",
- "to" => "to address",
- "date_time" => %{@morning | "month" => ""}
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
- response = html_response(conn, 200)
- assert response =~ "Date is not valid"
- end
-
- test "bad date input: corrupt day", %{conn: conn} do
- date_input = %{
- "year" => "A",
- "month" => "B",
- "day" => "C",
- "hour" => "D",
- "minute" => "E",
- "am_pm" => "PM"
- }
-
- params = %{
- "date_time" => @system_time,
- "plan" => %{"from" => "from address", "to" => "to address", "date_time" => date_input}
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
- response = html_response(conn, 200)
- assert response =~ "Date is not valid"
- end
-
- test "bad date input: too far in future", %{conn: conn} do
- end_date = Timex.shift(Schedules.Repo.end_of_rating(), days: 1)
-
- end_date_as_params = %{
- "month" => Integer.to_string(end_date.month),
- "day" => Integer.to_string(end_date.day),
- "year" => Integer.to_string(end_date.year),
- "hour" => "12",
- "minute" => "15",
- "am_pm" => "PM"
- }
-
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "from address",
- "to" => "to address",
- "date_time" => end_date_as_params,
- "time" => "depart"
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
- response = html_response(conn, 200)
- assert Map.get(conn.assigns, :plan_error) == [:too_future]
- assert response =~ "Date is too far in the future"
-
- expected =
- [:too_future]
- |> DotcomWeb.TripPlanView.plan_error_description()
- |> IO.iodata_to_binary()
-
- assert response =~ expected
- end
-
- test "bad date input: date in past", %{conn: conn} do
- past_date =
- @system_time
- |> Timex.parse!("{ISO:Extended}")
- |> Timex.shift(days: -10)
-
- past_date_as_params = %{
- "month" => Integer.to_string(past_date.month),
- "day" => Integer.to_string(past_date.day),
- "year" => Integer.to_string(past_date.year),
- "hour" => "12",
- "minute" => "15",
- "am_pm" => "PM"
- }
-
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "from address",
- "to" => "to address",
- "date_time" => past_date_as_params,
- "time" => "depart"
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
- response = html_response(conn, 200)
- assert Map.get(conn.assigns, :plan_error) == [:past]
- assert response =~ "Date is in the past"
- end
-
- test "handles missing date and time params, using today's values if they are missing",
- %{conn: conn} do
- wrong_datetime_params = %{
- "year" => "2017",
- "day" => "2",
- "hour" => "9",
- "am_pm" => "AM"
- }
-
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "from address",
- "to" => "to address",
- "date_time" => wrong_datetime_params
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
- assert html_response(conn, 200)
- end
-
- test "handles non-existing date and time params, using today's values",
- %{conn: conn} do
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "from address",
- "to" => "to address",
- "date_time" => %{}
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
- assert html_response(conn, 200)
- end
-
- test "does not need to default date and time params as they are present",
- %{conn: conn} do
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "from address",
- "to" => "to address",
- "date_time" => @morning
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
- assert html_response(conn, 200)
- end
-
- test "good date input: date within service date of end of rating", %{conn: conn} do
- # after midnight but before end of service on last day of rating
- # should still be inside of the rating
-
- date = Timex.shift(conn.assigns.end_of_rating, days: 1)
-
- date_params = %{
- "month" => Integer.to_string(date.month),
- "day" => Integer.to_string(date.day),
- "year" => Integer.to_string(date.year),
- "hour" => "12",
- "minute" => "15",
- "am_pm" => "AM"
- }
-
- params = %{
- "date_time" => @system_time,
- "plan" => %{"from" => "from address", "to" => "to address", "date_time" => date_params}
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
-
- response = html_response(conn, 200)
- assert Map.get(conn.assigns, :plan_error) == []
- refute response =~ "Date is too far in the future"
- refute response =~ "Date is not valid"
- end
-
- test "hour and minute are processed correctly when provided as single digits", %{conn: conn} do
- params = %{
- "date_time" => @system_time,
- "plan" => %{
- "from" => "from address",
- "to" => "to address",
- "date_time" => %{@after_hours | "hour" => "1", "minute" => "1"},
- "time" => "depart"
- }
- }
-
- conn = get(conn, trip_plan_path(conn, :index, params))
- response = html_response(conn, 200)
- assert Map.get(conn.assigns, :plan_error) == []
- refute response =~ "Date is not valid"
- end
- end
-
- describe "/from/ address path" do
- test "gets a valid address in the 'from' field", %{conn: conn} do
- conn = get(conn, trip_plan_path(conn, :from, "Boston Common"))
-
- assert conn.assigns.query.from.name == "Boston Common"
- end
-
- test "uses expected values when addres is formatted latitutde,longitude,stopName", %{
- conn: conn
- } do
- conn = get(conn, trip_plan_path(conn, :from, "42.395428,-71.142483,Cobbs Corner, Canton"))
-
- assert html_response(conn, 200)
- assert conn.assigns.query.from.name == "Cobbs Corner, Canton"
- assert conn.assigns.query.from.latitude == 42.395428
- assert conn.assigns.query.from.longitude == -71.142483
- end
-
- test "is unable to get address so it redirects to index", %{conn: conn} do
- expect(LocationService.Mock, :geocode, fn _ ->
- {:error, :something}
- end)
-
- conn = get(conn, trip_plan_path(conn, :from, "Atlantis"))
- assert html_response(conn, 302) =~ "/trip-planner"
- end
-
- test "when 'plan' is part of the parameters, it redirects to the usual trip planner", %{
- conn: conn
- } do
- plan_params = %{"plan" => %{"from" => "from address", "to" => "to address"}}
-
- conn =
- get(
- conn,
- trip_plan_path(conn, :from, "Address", plan_params)
- )
-
- assert redirected_to(conn) == trip_plan_path(conn, :index, plan_params)
- end
- end
-
- describe "/to/ address path" do
- test "gets a valid address in the 'to' field", %{conn: conn} do
- conn = get(conn, trip_plan_path(conn, :to, "Boston Common"))
- assert conn.assigns.query.to.name == "Boston Common"
- end
-
- test "uses expected values when address is formatted latitutde,longitude,stopName", %{
- conn: conn
- } do
- conn = get(conn, trip_plan_path(conn, :to, "42.395428,-71.142483,Cobbs Corner, Canton"))
-
- assert html_response(conn, 200)
- assert conn.assigns.query.to.name == "Cobbs Corner, Canton"
- assert conn.assigns.query.to.latitude == 42.395428
- assert conn.assigns.query.to.longitude == -71.142483
- end
-
- test "is unable to get address so it redirects to index", %{conn: conn} do
- expect(LocationService.Mock, :geocode, fn _ ->
- {:error, :something}
- end)
-
- conn = get(conn, trip_plan_path(conn, :to, "Atlantis"))
- assert html_response(conn, 302) =~ "/trip-planner"
- end
-
- test "when 'plan' is part of the parameters, it redirects to the usual trip planner", %{
- conn: conn
- } do
- plan_params = %{"plan" => %{"from" => "from address", "to" => "to address"}}
-
- conn =
- get(
- conn,
- trip_plan_path(conn, :to, "Address", plan_params)
- )
-
- assert redirected_to(conn) == trip_plan_path(conn, :index, plan_params)
- end
- end
-end
diff --git a/test/dotcom_web/live/trip_planner_test.exs b/test/dotcom_web/live/trip_planner_test.exs
index dedbc30e6f..f0617ef1d2 100644
--- a/test/dotcom_web/live/trip_planner_test.exs
+++ b/test/dotcom_web/live/trip_planner_test.exs
@@ -62,23 +62,11 @@ defmodule DotcomWeb.Live.TripPlannerTest do
end)
end
- test "Preview version behind basic auth", %{conn: conn} do
- conn = get(conn, ~p"/preview/trip-planner")
-
- {_header_name, header_value} = List.keyfind(conn.resp_headers, "www-authenticate", 0)
- assert conn.status == 401
- assert header_value =~ "Basic"
- end
-
describe "Trip Planner" do
setup %{conn: conn} do
- [username: username, password: password] =
- Application.get_env(:dotcom, DotcomWeb.Router)[:basic_auth_readonly]
-
{:ok, view, html} =
conn
- |> put_req_header("authorization", "Basic " <> Base.encode64("#{username}:#{password}"))
- |> live(~p"/preview/trip-planner")
+ |> live(~p"/trip-planner")
%{html: html, view: view}
end
@@ -154,7 +142,7 @@ defmodule DotcomWeb.Live.TripPlannerTest do
stub_otp_results([])
- {:ok, view, _html} = live(conn, ~p"/preview/trip-planner?#{params}")
+ {:ok, view, _html} = live(conn, ~p"/trip-planner?#{params}")
assert render_async(view) =~ "No trips found"
end
@@ -190,7 +178,7 @@ defmodule DotcomWeb.Live.TripPlannerTest do
test "starts out with no 'View All Options' button", %{conn: conn, params: params} do
stub_populated_otp_results()
- {:ok, view, _html} = live(conn, ~p"/preview/trip-planner?#{params}")
+ {:ok, view, _html} = live(conn, ~p"/trip-planner?#{params}")
refute render_async(view) =~ "View All Options"
end
@@ -198,7 +186,7 @@ defmodule DotcomWeb.Live.TripPlannerTest do
test "clicking 'Details' button opens details view", %{conn: conn, params: params} do
stub_populated_otp_results()
- {:ok, view, _html} = live(conn, ~p"/preview/trip-planner?#{params}")
+ {:ok, view, _html} = live(conn, ~p"/trip-planner?#{params}")
render_async(view)
view |> element("button[phx-value-index=\"0\"]", "Details") |> render_click()
@@ -212,7 +200,7 @@ defmodule DotcomWeb.Live.TripPlannerTest do
} do
stub_populated_otp_results()
- {:ok, view, _html} = live(conn, ~p"/preview/trip-planner?#{params}")
+ {:ok, view, _html} = live(conn, ~p"/trip-planner?#{params}")
render_async(view)
@@ -244,7 +232,7 @@ defmodule DotcomWeb.Live.TripPlannerTest do
}
end)
- {:ok, view, _html} = live(conn, ~p"/preview/trip-planner?#{params}")
+ {:ok, view, _html} = live(conn, ~p"/trip-planner?#{params}")
render_async(view)
@@ -278,7 +266,7 @@ defmodule DotcomWeb.Live.TripPlannerTest do
}
end)
- {:ok, view, _html} = live(conn, ~p"/preview/trip-planner?#{params}")
+ {:ok, view, _html} = live(conn, ~p"/trip-planner?#{params}")
render_async(view)
@@ -309,7 +297,7 @@ defmodule DotcomWeb.Live.TripPlannerTest do
}
end)
- {:ok, view, _html} = live(conn, ~p"/preview/trip-planner?#{params}")
+ {:ok, view, _html} = live(conn, ~p"/trip-planner?#{params}")
render_async(view)
@@ -328,7 +316,7 @@ defmodule DotcomWeb.Live.TripPlannerTest do
} do
stub_otp_results([])
- {:ok, view, _html} = live(conn, ~p"/preview/trip-planner?#{params}")
+ {:ok, view, _html} = live(conn, ~p"/trip-planner?#{params}")
assert render_async(view) =~ "No trips found."
end
@@ -340,7 +328,7 @@ defmodule DotcomWeb.Live.TripPlannerTest do
{:error, [%OpenTripPlannerClient.Error{message: error_message}]}
end)
- {:ok, view, _html} = live(conn, ~p"/preview/trip-planner?#{params}")
+ {:ok, view, _html} = live(conn, ~p"/trip-planner?#{params}")
assert render_async(view) =~ error_message
end
@@ -353,7 +341,7 @@ defmodule DotcomWeb.Live.TripPlannerTest do
{:error, [%OpenTripPlannerClient.Error{message: Faker.Lorem.sentence()}]}
end)
- {:ok, view, _html} = live(conn, ~p"/preview/trip-planner?#{params}")
+ {:ok, view, _html} = live(conn, ~p"/trip-planner?#{params}")
refute render_async(view) =~ "No trips found."
end
diff --git a/test/dotcom_web/router_test.exs b/test/dotcom_web/router_test.exs
index 9fdb9bd782..2caf6891d4 100644
--- a/test/dotcom_web/router_test.exs
+++ b/test/dotcom_web/router_test.exs
@@ -114,11 +114,6 @@ defmodule Phoenix.Router.RoutingTest do
assert redirected_to(conn, 301) == "/betterbus-440s"
end
- test "trip planner with 'to' but without an address", %{conn: conn} do
- conn = get(conn, "/trip-planner/to/")
- assert redirected_to(conn, 301) == "/trip-planner"
- end
-
test "redirect to canonical host securely", %{conn: conn} do
System.put_env("HOST", @canonical_host)