From 75efa2442a85deb0c7525644d1afe117db4ba63d Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Thu, 1 Aug 2024 14:59:28 -0400 Subject: [PATCH 01/38] feat: Add `Activate` button gated behind detours-list test group (#2713) --- .../detours/detourFinishedPanel.tsx | 13 ++- .../src/components/detours/diversionPage.tsx | 3 + .../detours/detourFinishedPanel.stories.tsx | 7 +- .../detours/diversionPageActivate.test.tsx | 84 +++++++++++++++++++ .../components/detours/diversionPage.tsx | 3 + 5 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 assets/tests/components/detours/diversionPageActivate.test.tsx diff --git a/assets/src/components/detours/detourFinishedPanel.tsx b/assets/src/components/detours/detourFinishedPanel.tsx index ed57803e1..efe159823 100644 --- a/assets/src/components/detours/detourFinishedPanel.tsx +++ b/assets/src/components/detours/detourFinishedPanel.tsx @@ -7,10 +7,12 @@ export const DetourFinishedPanel = ({ onNavigateBack, detourText, onChangeDetourText, + onActivateDetour, }: { onNavigateBack: () => void detourText: string onChangeDetourText: (value: string) => void + onActivateDetour?: () => void }) => ( @@ -39,7 +41,7 @@ export const DetourFinishedPanel = ({ /> - + + {onActivateDetour && ( + + )} diff --git a/assets/src/components/detours/diversionPage.tsx b/assets/src/components/detours/diversionPage.tsx index 5cb165ee5..9dfd3d38d 100644 --- a/assets/src/components/detours/diversionPage.tsx +++ b/assets/src/components/detours/diversionPage.tsx @@ -233,6 +233,9 @@ export const DiversionPage = ({ onNavigateBack={editDetour} detourText={textArea} onChangeDetourText={setTextArea} + onActivateDetour={ + inTestGroup(TestGroups.DetoursList) ? () => {} : undefined + } /> ) : null} diff --git a/assets/stories/skate-components/detours/detourFinishedPanel.stories.tsx b/assets/stories/skate-components/detours/detourFinishedPanel.stories.tsx index c14216a6a..521c42fa5 100644 --- a/assets/stories/skate-components/detours/detourFinishedPanel.stories.tsx +++ b/assets/stories/skate-components/detours/detourFinishedPanel.stories.tsx @@ -50,4 +50,9 @@ export default meta type Story = StoryObj -export const Story: Story = {} +export const WithActivateButton: Story = {} +export const WithoutActivateButton: Story = { + args: { + onActivateDetour: undefined, + }, +} diff --git a/assets/tests/components/detours/diversionPageActivate.test.tsx b/assets/tests/components/detours/diversionPageActivate.test.tsx new file mode 100644 index 000000000..5547bc1e5 --- /dev/null +++ b/assets/tests/components/detours/diversionPageActivate.test.tsx @@ -0,0 +1,84 @@ +import React from "react" +import { + DiversionPage as DiversionPageDefault, + DiversionPageProps, +} from "../../../src/components/detours/diversionPage" +import { originalRouteFactory } from "../../factories/originalRouteFactory" +import { beforeEach, describe, expect, jest, test } from "@jest/globals" +import "@testing-library/jest-dom/jest-globals" +import getTestGroups from "../../../src/userTestGroups" +import { TestGroups } from "../../../src/userInTestGroup" +import { act, fireEvent, render } from "@testing-library/react" +import userEvent from "@testing-library/user-event" +import { + activateDetourButton, + originalRouteShape, + reviewDetourButton, +} from "../../testHelpers/selectors/components/detours/diversionPage" +import { + fetchDetourDirections, + fetchFinishedDetour, + fetchNearestIntersection, + fetchRoutePatterns, + fetchUnfinishedDetour, +} from "../../../src/api" +import { neverPromise } from "../../testHelpers/mockHelpers" + +beforeEach(() => { + jest.spyOn(global, "scrollTo").mockImplementationOnce(jest.fn()) +}) + +const DiversionPage = (props: Partial) => ( + +) + +jest.mock("../../../src/api") +jest.mock("../../../src/userTestGroups") + +beforeEach(() => { + jest.mocked(fetchDetourDirections).mockReturnValue(neverPromise()) + jest.mocked(fetchUnfinishedDetour).mockReturnValue(neverPromise()) + jest.mocked(fetchFinishedDetour).mockReturnValue(neverPromise()) + jest.mocked(fetchNearestIntersection).mockReturnValue(neverPromise()) + jest.mocked(fetchRoutePatterns).mockReturnValue(neverPromise()) + + jest + .mocked(getTestGroups) + .mockReturnValue([TestGroups.DetoursPilot, TestGroups.DetoursList]) +}) + +const diversionPageOnReviewScreen = async ( + props?: Partial +) => { + const { container } = render() + + act(() => { + fireEvent.click(originalRouteShape.get(container)) + }) + act(() => { + fireEvent.click(originalRouteShape.get(container)) + }) + await userEvent.click(reviewDetourButton.get()) + + return { container } +} + +describe("DiversionPage activate workflow", () => { + test("does not have an activate button on the review details screen if not in the detours-list test group", async () => { + jest.mocked(getTestGroups).mockReturnValue([TestGroups.DetoursPilot]) + + await diversionPageOnReviewScreen() + + expect(activateDetourButton.query()).not.toBeInTheDocument() + }) + + test("has an activate button on the review details screen", async () => { + await diversionPageOnReviewScreen() + + expect(activateDetourButton.get()).toBeVisible() + }) +}) diff --git a/assets/tests/testHelpers/selectors/components/detours/diversionPage.tsx b/assets/tests/testHelpers/selectors/components/detours/diversionPage.tsx index ce89b6f42..312f54589 100644 --- a/assets/tests/testHelpers/selectors/components/detours/diversionPage.tsx +++ b/assets/tests/testHelpers/selectors/components/detours/diversionPage.tsx @@ -2,6 +2,9 @@ import { expect } from "@jest/globals" import { byRole } from "testing-library-selector" export const reviewDetourButton = byRole("button", { name: "Review Detour" }) +export const activateDetourButton = byRole("button", { + name: "Activate Detour", +}) export const originalRouteShape = { interactive: { From 952af0d5bb59e2b0ee6a950fde68519979c6ef57 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Fri, 2 Aug 2024 07:48:17 -0400 Subject: [PATCH 02/38] feat: Enter the "Activated" state when "Activate Detour" button is clicked (#2714) --- assets/src/components/detours/diversionPage.tsx | 6 +++++- assets/src/models/createDetourMachine.ts | 7 ++++++- .../detours/diversionPageActivate.test.tsx | 12 +++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/assets/src/components/detours/diversionPage.tsx b/assets/src/components/detours/diversionPage.tsx index 9dfd3d38d..69d01feb3 100644 --- a/assets/src/components/detours/diversionPage.tsx +++ b/assets/src/components/detours/diversionPage.tsx @@ -234,7 +234,11 @@ export const DiversionPage = ({ detourText={textArea} onChangeDetourText={setTextArea} onActivateDetour={ - inTestGroup(TestGroups.DetoursList) ? () => {} : undefined + inTestGroup(TestGroups.DetoursList) + ? () => { + send({ type: "detour.share.activate" }) + } + : undefined } /> ) : null} diff --git a/assets/src/models/createDetourMachine.ts b/assets/src/models/createDetourMachine.ts index e8261244c..37f0f7071 100644 --- a/assets/src/models/createDetourMachine.ts +++ b/assets/src/models/createDetourMachine.ts @@ -59,7 +59,8 @@ export const createDetourMachine = setup({ | { type: "detour.edit.place-waypoint-on-route"; location: ShapePoint } | { type: "detour.edit.place-waypoint"; location: ShapePoint } | { type: "detour.edit.undo" } - | { type: "detour.share.copy-detour"; detourText: string }, + | { type: "detour.share.copy-detour"; detourText: string } + | { type: "detour.share.activate" }, }, actors: { "fetch-route-patterns": fromPromise< @@ -411,8 +412,12 @@ export const createDetourMachine = setup({ "detour.edit.resume": { target: "Editing.Finished Drawing", }, + "detour.share.activate": { + target: "Active", + }, }, }, + Active: {}, }, }, }, diff --git a/assets/tests/components/detours/diversionPageActivate.test.tsx b/assets/tests/components/detours/diversionPageActivate.test.tsx index 5547bc1e5..ee674adfb 100644 --- a/assets/tests/components/detours/diversionPageActivate.test.tsx +++ b/assets/tests/components/detours/diversionPageActivate.test.tsx @@ -8,7 +8,7 @@ import { beforeEach, describe, expect, jest, test } from "@jest/globals" import "@testing-library/jest-dom/jest-globals" import getTestGroups from "../../../src/userTestGroups" import { TestGroups } from "../../../src/userInTestGroup" -import { act, fireEvent, render } from "@testing-library/react" +import { act, fireEvent, render, screen } from "@testing-library/react" import userEvent from "@testing-library/user-event" import { activateDetourButton, @@ -81,4 +81,14 @@ describe("DiversionPage activate workflow", () => { expect(activateDetourButton.get()).toBeVisible() }) + + test("clicking the activate button shows the 'Active Detour' screen", async () => { + await diversionPageOnReviewScreen() + + await userEvent.click(activateDetourButton.get()) + + expect( + screen.queryByRole("heading", { name: "Share Detour Details" }) + ).not.toBeInTheDocument() + }) }) From 16aa7431b6ecc5bedeaa552b9b6b38dd8f99fd13 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Fri, 2 Aug 2024 08:11:14 -0400 Subject: [PATCH 03/38] fix(warnings): Fix warnings associated with Elixir 1.17 upgrade (#2723) --- mix.exs | 2 +- mix.lock | 2 +- test/realtime/ghost_test.exs | 6 +++--- test/realtime/server_test.exs | 4 ++-- test/realtime/shape_test.exs | 1 - test/realtime/vehicle_or_ghost_test.exs | 6 +++--- test/realtime/vehicle_test.exs | 2 +- test/realtime/vehicles_test.exs | 4 ++-- test/skate_web/channels/vehicle_channel_test.exs | 4 ++-- test/skate_web/controllers/redirect_controller_test.exs | 2 +- 10 files changed, 16 insertions(+), 17 deletions(-) diff --git a/mix.exs b/mix.exs index 4c09b5a72..310b00fdf 100644 --- a/mix.exs +++ b/mix.exs @@ -87,7 +87,7 @@ defmodule Skate.MixProject do {:server_sent_event_stage, "~> 1.1.0"}, {:sobelow, "~> 0.13", only: [:dev, :test], runtime: false}, {:ssl_verify_fun, "~> 1.1"}, - {:stream_data, "~> 0.6.0", only: :test}, + {:stream_data, "~> 1.1.1", only: :test}, {:timex, "~> 3.7.5"}, {:ueberauth_cognito, "~> 0.4.0"}, {:ueberauth_oidcc, "~> 0.4.0"}, diff --git a/mix.lock b/mix.lock index 4f3eb9584..0d61295f5 100644 --- a/mix.lock +++ b/mix.lock @@ -75,7 +75,7 @@ "server_sent_event_stage": {:hex, :server_sent_event_stage, "1.1.0", "cd5f93e1110455be569533b3c68503fce9f33bbd6e8895f5d5bc139b47d79cab", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:ex_doc, "~> 0.21", [hex: :ex_doc, repo: "hexpm", optional: true]}, {:gen_stage, "~> 1.0", [hex: :gen_stage, repo: "hexpm", optional: false]}, {:mint, "~> 1.4", [hex: :mint, repo: "hexpm", optional: false]}], "hexpm", "5a5fdcd34cac962a24bcbffef843c5bb1127f0cf8f84795dcdf3c8949051ed6f"}, "sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, - "stream_data": {:hex, :stream_data, "0.6.0", "e87a9a79d7ec23d10ff83eb025141ef4915eeb09d4491f79e52f2562b73e5f47", [:mix], [], "hexpm", "b92b5031b650ca480ced047578f1d57ea6dd563f5b57464ad274718c9c29501c"}, + "stream_data": {:hex, :stream_data, "1.1.1", "fd515ca95619cca83ba08b20f5e814aaf1e5ebff114659dc9731f966c9226246", [:mix], [], "hexpm", "45d0cd46bd06738463fd53f22b70042dbb58c384bb99ef4e7576e7bb7d3b8c8c"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, "telemetry_registry": {:hex, :telemetry_registry, "0.3.1", "14a3319a7d9027bdbff7ebcacf1a438f5f5c903057b93aee484cca26f05bdcba", [:mix, :rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6d0ca77b691cf854ed074b459a93b87f4c7f5512f8f7743c635ca83da81f939e"}, "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, diff --git a/test/realtime/ghost_test.exs b/test/realtime/ghost_test.exs index 26fab7581..9d339a18e 100644 --- a/test/realtime/ghost_test.exs +++ b/test/realtime/ghost_test.exs @@ -171,7 +171,7 @@ defmodule Realtime.GhostTest do layover_departure_time: 1_546_318_806, scheduled_timepoint_status: %{ timepoint_id: "t1", - fraction_until_timepoint: 0.0 + fraction_until_timepoint: +0.0 }, route_status: :pulling_out, block_waivers: [ @@ -333,7 +333,7 @@ defmodule Realtime.GhostTest do layover_departure_time: 1_546_318_900, scheduled_timepoint_status: %{ timepoint_id: "t1", - fraction_until_timepoint: 0.0 + fraction_until_timepoint: +0.0 }, scheduled_logon: 1_546_318_850, route_status: :pulling_out, @@ -381,7 +381,7 @@ defmodule Realtime.GhostTest do layover_departure_time: nil, scheduled_timepoint_status: %{ timepoint_id: "t1", - fraction_until_timepoint: 0.0 + fraction_until_timepoint: +0.0 }, scheduled_logon: 1_546_318_850, route_status: :on_route, diff --git a/test/realtime/server_test.exs b/test/realtime/server_test.exs index 20fee6a92..4b858e330 100644 --- a/test/realtime/server_test.exs +++ b/test/realtime/server_test.exs @@ -631,7 +631,7 @@ defmodule Realtime.ServerTest do } operator_search_params = %{ - text: String.slice(@vehicle.operator_last_name, 0..-3), + text: String.slice(@vehicle.operator_last_name, 0..-3//1), property: :all } @@ -660,7 +660,7 @@ defmodule Realtime.ServerTest do test "searches all vehicles by operator name", %{ets: ets} do search_params = %{ - text: String.slice(@vehicle.operator_first_name, 0..-3), + text: String.slice(@vehicle.operator_first_name, 0..-3//1), property: :operator } diff --git a/test/realtime/shape_test.exs b/test/realtime/shape_test.exs index 18169f8f8..e3ce14097 100644 --- a/test/realtime/shape_test.exs +++ b/test/realtime/shape_test.exs @@ -1,6 +1,5 @@ defmodule Realtime.ShapeTest do alias Skate.OpenRouteServiceAPI.DirectionsResponse - alias Util.Location alias Realtime.Shape alias Skate.Detours.RouteSegments use ExUnit.Case diff --git a/test/realtime/vehicle_or_ghost_test.exs b/test/realtime/vehicle_or_ghost_test.exs index 790f7dc48..be89f5887 100644 --- a/test/realtime/vehicle_or_ghost_test.exs +++ b/test/realtime/vehicle_or_ghost_test.exs @@ -82,7 +82,7 @@ defmodule Realtime.VehicleOrGhostTest do [vehicle] assert VehicleOrGhost.find_by(vehicles, %{ - text: String.slice(vehicle.operator_last_name, 0..-3), + text: String.slice(vehicle.operator_last_name, 0..-3//1), property: :all }) == [vehicle] @@ -91,7 +91,7 @@ defmodule Realtime.VehicleOrGhostTest do [vehicle] assert VehicleOrGhost.find_by(vehicles, %{ - text: String.slice(vehicle.operator_first_name, 0..-3), + text: String.slice(vehicle.operator_first_name, 0..-3//1), property: :all }) == [vehicle] @@ -153,7 +153,7 @@ defmodule Realtime.VehicleOrGhostTest do test "matches on operator name" do assert VehicleOrGhost.find_by(@vehicles, %{ - text: String.slice(@vehicle.operator_last_name, 0..-3), + text: String.slice(@vehicle.operator_last_name, 0..-3//1), property: :operator }) == [@vehicle] diff --git a/test/realtime/vehicle_test.exs b/test/realtime/vehicle_test.exs index b3f29fea4..1738ef697 100644 --- a/test/realtime/vehicle_test.exs +++ b/test/realtime/vehicle_test.exs @@ -185,7 +185,7 @@ defmodule Realtime.VehicleTest do via_variant: "_", timepoint_status: %{ timepoint_id: "tp1", - fraction_until_timepoint: 0.0 + fraction_until_timepoint: +0.0 } }, route_status: :on_route, diff --git a/test/realtime/vehicles_test.exs b/test/realtime/vehicles_test.exs index 2d5acad07..f1ad64117 100644 --- a/test/realtime/vehicles_test.exs +++ b/test/realtime/vehicles_test.exs @@ -289,7 +289,7 @@ defmodule Realtime.VehiclesTest do layover_departure_time: nil, scheduled_timepoint_status: %{ timepoint_id: "timepoint", - fraction_until_timepoint: 0.0 + fraction_until_timepoint: +0.0 }, route_status: :on_route, block_waivers: [ @@ -392,7 +392,7 @@ defmodule Realtime.VehiclesTest do layover_departure_time: 1_576_818_001, scheduled_timepoint_status: %{ timepoint_id: "timepoint", - fraction_until_timepoint: 0.0 + fraction_until_timepoint: +0.0 }, route_status: :pulling_out, block_waivers: [ diff --git a/test/skate_web/channels/vehicle_channel_test.exs b/test/skate_web/channels/vehicle_channel_test.exs index 730296eb4..0c3080233 100644 --- a/test/skate_web/channels/vehicle_channel_test.exs +++ b/test/skate_web/channels/vehicle_channel_test.exs @@ -14,8 +14,8 @@ defmodule SkateWeb.VehicleChannelTest do label: "0507", timestamp: 123, timestamp_by_source: %{"swiftly" => 123}, - latitude: 0.0, - longitude: 0.0, + latitude: 41.0, + longitude: -70.0, direction_id: "234", route_id: "1", trip_id: "456", diff --git a/test/skate_web/controllers/redirect_controller_test.exs b/test/skate_web/controllers/redirect_controller_test.exs index 62a377787..f1651eb95 100644 --- a/test/skate_web/controllers/redirect_controller_test.exs +++ b/test/skate_web/controllers/redirect_controller_test.exs @@ -13,7 +13,7 @@ defmodule SkateWeb.RedirectTest do end test "an exception is raised when `external` isn't defined" do - assert_raise Plug.Conn.WrapperError, ~R[Missing required external: option in redirect], fn -> + assert_raise Plug.Conn.WrapperError, ~r/Missing required external: option in redirect/, fn -> call(Router, :get, "/exceptional") end end From 615e7c82f425bebcbea7d43d77769ae8f36647c5 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Fri, 2 Aug 2024 10:33:08 -0400 Subject: [PATCH 04/38] feat: Add stubbed "Active Detour" detour panel (#2715) --- .../components/detours/activeDetourPanel.tsx | 12 ++++++++ .../src/components/detours/diversionPage.tsx | 3 ++ .../detours/activeDetourPanel.stories.tsx | 28 +++++++++++++++++++ .../detours/diversionPageActivate.test.tsx | 1 + 4 files changed, 44 insertions(+) create mode 100644 assets/src/components/detours/activeDetourPanel.tsx create mode 100644 assets/stories/skate-components/detours/activeDetourPanel.stories.tsx diff --git a/assets/src/components/detours/activeDetourPanel.tsx b/assets/src/components/detours/activeDetourPanel.tsx new file mode 100644 index 000000000..664a2e738 --- /dev/null +++ b/assets/src/components/detours/activeDetourPanel.tsx @@ -0,0 +1,12 @@ +import React from "react" +import { Panel } from "./diversionPage" + +export const ActiveDetourPanel = () => ( + + +

Active Detour

+
+ + +
+) diff --git a/assets/src/components/detours/diversionPage.tsx b/assets/src/components/detours/diversionPage.tsx index 69d01feb3..8c873781d 100644 --- a/assets/src/components/detours/diversionPage.tsx +++ b/assets/src/components/detours/diversionPage.tsx @@ -20,6 +20,7 @@ import { Route, RoutePattern } from "../../schedule" import RoutesContext from "../../contexts/routesContext" import { Snapshot } from "xstate" import inTestGroup, { TestGroups } from "../../userInTestGroup" +import { ActiveDetourPanel } from "./activeDetourPanel" const displayFieldsFromRouteAndPattern = ( route: Route, @@ -241,6 +242,8 @@ export const DiversionPage = ({ : undefined } /> + ) : snapshot.matches({ "Detour Drawing": "Active" }) ? ( + ) : null}
diff --git a/assets/stories/skate-components/detours/activeDetourPanel.stories.tsx b/assets/stories/skate-components/detours/activeDetourPanel.stories.tsx new file mode 100644 index 000000000..6f71e2796 --- /dev/null +++ b/assets/stories/skate-components/detours/activeDetourPanel.stories.tsx @@ -0,0 +1,28 @@ +import type { Meta, StoryObj } from "@storybook/react" + +import React from "react" +import { ActiveDetourPanel } from "../../../src/components/detours/activeDetourPanel" + +const meta = { + component: ActiveDetourPanel, + parameters: { + layout: "fullscreen", + stretch: true, + }, + args: {}, + // The bootstrap CSS reset is supposed to set box-sizing: border-box by + // default, we should be able to remove this after that is added + decorators: [ + (StoryFn) => ( +
+ +
+ ), + ], +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Story: Story = {} diff --git a/assets/tests/components/detours/diversionPageActivate.test.tsx b/assets/tests/components/detours/diversionPageActivate.test.tsx index ee674adfb..50a406b2f 100644 --- a/assets/tests/components/detours/diversionPageActivate.test.tsx +++ b/assets/tests/components/detours/diversionPageActivate.test.tsx @@ -90,5 +90,6 @@ describe("DiversionPage activate workflow", () => { expect( screen.queryByRole("heading", { name: "Share Detour Details" }) ).not.toBeInTheDocument() + expect(screen.getByRole("heading", { name: "Active Detour" })).toBeVisible() }) }) From db3dd809b0e814637bf770aa233363822daa6b91 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Mon, 5 Aug 2024 09:54:54 -0400 Subject: [PATCH 05/38] tests: Add test that "Add detour" dropdown is behind a test group (#2717) --- assets/tests/components/ladderPage.test.tsx | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/assets/tests/components/ladderPage.test.tsx b/assets/tests/components/ladderPage.test.tsx index 6130d3548..ed1bb2168 100644 --- a/assets/tests/components/ladderPage.test.tsx +++ b/assets/tests/components/ladderPage.test.tsx @@ -564,6 +564,34 @@ describe("LadderPage", () => { expect(screen.getByRole("heading", { name: "Create Detour" })).toBeVisible() }) + + test("detour dropdown is not visible to users outside of the test group", async () => { + jest.mocked(getTestGroups).mockReturnValue([]) + + const mockState = stateFactory.build({ + routeTabs: [ + routeTabFactory.build({ + selectedRouteIds: ["1"], + isCurrentTab: true, + }), + ], + }) + const { container } = render( + + + + + + + + ) + + expect(container.querySelector(".c-route-ladder__header")).toBeVisible() + + expect( + screen.queryByRole("button", { name: "1 Route Options" }) + ).not.toBeInTheDocument() + }) }) const routes: Route[] = [ From 0f120e84d706d6bbd7c5a19996cd45b714d37b8b Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Mon, 5 Aug 2024 10:05:27 -0400 Subject: [PATCH 06/38] feat: Suppress "Add detour" dropdown on mobile (#2716) --- assets/src/components/routeLadder.tsx | 2 +- assets/tests/components/__snapshots__/routeLadder.test.tsx.snap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/src/components/routeLadder.tsx b/assets/src/components/routeLadder.tsx index 764e909f5..fd801932c 100644 --- a/assets/src/components/routeLadder.tsx +++ b/assets/src/components/routeLadder.tsx @@ -67,7 +67,7 @@ export const Header = ({ {showDropdown && (
diff --git a/assets/src/components/detours/pastDetourPanel.tsx b/assets/src/components/detours/pastDetourPanel.tsx new file mode 100644 index 000000000..1d5e4b779 --- /dev/null +++ b/assets/src/components/detours/pastDetourPanel.tsx @@ -0,0 +1,12 @@ +import React from "react" +import { Panel } from "./diversionPage" + +export const PastDetourPanel = () => ( + + +

Past Detour

+
+ + +
+) diff --git a/assets/src/models/createDetourMachine.ts b/assets/src/models/createDetourMachine.ts index 37f0f7071..f0530830c 100644 --- a/assets/src/models/createDetourMachine.ts +++ b/assets/src/models/createDetourMachine.ts @@ -60,7 +60,8 @@ export const createDetourMachine = setup({ | { type: "detour.edit.place-waypoint"; location: ShapePoint } | { type: "detour.edit.undo" } | { type: "detour.share.copy-detour"; detourText: string } - | { type: "detour.share.activate" }, + | { type: "detour.share.activate" } + | { type: "detour.active.deactivate" }, }, actors: { "fetch-route-patterns": fromPromise< @@ -417,7 +418,14 @@ export const createDetourMachine = setup({ }, }, }, - Active: {}, + Active: { + on: { + "detour.active.deactivate": { + target: "Past", + }, + }, + }, + Past: {}, }, }, }, diff --git a/assets/tests/components/detours/diversionPageActivate.test.tsx b/assets/tests/components/detours/diversionPageActivate.test.tsx index 50a406b2f..6341520dc 100644 --- a/assets/tests/components/detours/diversionPageActivate.test.tsx +++ b/assets/tests/components/detours/diversionPageActivate.test.tsx @@ -92,4 +92,28 @@ describe("DiversionPage activate workflow", () => { ).not.toBeInTheDocument() expect(screen.getByRole("heading", { name: "Active Detour" })).toBeVisible() }) + + test("'Active Detour' screen has a 'Deactivate Detour' button", async () => { + await diversionPageOnReviewScreen() + + await userEvent.click(activateDetourButton.get()) + + expect( + screen.getByRole("button", { name: "Deactivate Detour" }) + ).toBeVisible() + }) + + test("clicking the 'Deactivate Detour' button shows the 'Past Detour' screen", async () => { + await diversionPageOnReviewScreen() + + await userEvent.click(activateDetourButton.get()) + + await userEvent.click( + screen.getByRole("button", { name: "Deactivate Detour" }) + ) + expect( + screen.queryByRole("heading", { name: "Active Detour" }) + ).not.toBeInTheDocument() + expect(screen.getByRole("heading", { name: "Past Detour" })).toBeVisible() + }) }) From 44c33ec58b0c31e2ccfc411c236fcbf1cf5542e2 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Tue, 6 Aug 2024 16:22:53 -0400 Subject: [PATCH 09/38] cleanup: Remove checks for KeycloakSso test group in frontend code (#2727) --- assets/src/components/nav/navMenu.tsx | 32 ++++---- assets/src/components/nav/topNav.tsx | 73 +++++++++---------- assets/src/userInTestGroup.ts | 1 - assets/tests/components/app.test.tsx | 3 - .../tests/components/appStateWrapper.test.tsx | 3 - assets/tests/components/nav/navMenu.test.tsx | 30 -------- assets/tests/components/nav/topNav.test.tsx | 30 -------- 7 files changed, 46 insertions(+), 126 deletions(-) diff --git a/assets/src/components/nav/navMenu.tsx b/assets/src/components/nav/navMenu.tsx index 5e5226f87..c0f91f461 100644 --- a/assets/src/components/nav/navMenu.tsx +++ b/assets/src/components/nav/navMenu.tsx @@ -7,7 +7,6 @@ import * as BsIcon from "../../helpers/bsIcons" import { joinClasses } from "../../helpers/dom" import { reload } from "../../models/browser" import { LoggedInAs } from "../loggedInAs" -import inTestGroup, { TestGroups } from "../../userInTestGroup" import getEmailAddress from "../../userEmailAddress" interface Props { @@ -16,7 +15,6 @@ interface Props { } const NavMenu: React.FC = ({ mobileMenuIsOpen, toggleMobileMenu }) => { - const keycloakEnabled = inTestGroup(TestGroups.KeycloakSso) const email = getEmailAddress() return ( @@ -49,7 +47,7 @@ const NavMenu: React.FC = ({ mobileMenuIsOpen, toggleMobileMenu }) => {
- {keycloakEnabled && email && ( + {email && ( <>
@@ -96,22 +94,18 @@ const NavMenu: React.FC = ({ mobileMenuIsOpen, toggleMobileMenu }) => { Settings - {keycloakEnabled && ( - <> - -
-
- - - Logout - - - - )} + +
+
+ + + Logout + +
diff --git a/assets/src/components/nav/topNav.tsx b/assets/src/components/nav/topNav.tsx index 989e8654c..b145a6c92 100644 --- a/assets/src/components/nav/topNav.tsx +++ b/assets/src/components/nav/topNav.tsx @@ -8,13 +8,11 @@ import { LoggedInAs } from "../loggedInAs" import getEmailAddress from "../../userEmailAddress" import { CircleButton } from "../circleButton" import { UserAvatar } from "../userAvatar" -import inTestGroup, { TestGroups } from "../../userInTestGroup" const TopNav = (): JSX.Element => { const email = getEmailAddress() const [showUserPopover, setShowUserPopover] = useState(false) const userButtonRef = useRef(null) - const showLoggedInUser = inTestGroup(TestGroups.KeycloakSso) return (
@@ -31,46 +29,41 @@ const TopNav = (): JSX.Element => { - {showLoggedInUser && ( -
  • -
    - { - setShowUserPopover(!showUserPopover) - }} - title="User Info" - > - - -
    - +
    + { + setShowUserPopover(!showUserPopover) + }} + title="User Info" > - - - + +
    + + + + + + + + + - - - - - - Log out - - - - - -
  • - )} + Log out + + + + + +
    ) diff --git a/assets/src/userInTestGroup.ts b/assets/src/userInTestGroup.ts index abd393401..eeab87498 100644 --- a/assets/src/userInTestGroup.ts +++ b/assets/src/userInTestGroup.ts @@ -5,7 +5,6 @@ export enum TestGroups { DemoMode = "demo-mode", DetoursList = "detours-list", DetoursPilot = "detours-pilot", - KeycloakSso = "keycloak-sso", MinimalLadderPage = "minimal-ladder-page", LateView = "late-view", } diff --git a/assets/tests/components/app.test.tsx b/assets/tests/components/app.test.tsx index acb8a487e..00b1c3964 100644 --- a/assets/tests/components/app.test.tsx +++ b/assets/tests/components/app.test.tsx @@ -26,8 +26,6 @@ import { OpenView, PagePath } from "../../src/state/pagePanelState" import { viewFactory } from "../factories/pagePanelStateFactory" import userEvent from "@testing-library/user-event" import { mockUsePanelState } from "../testHelpers/usePanelStateMocks" -import getTestGroups from "../../src/userTestGroups" -import { TestGroups } from "../../src/userInTestGroup" jest.mock("../../src/hooks/useDataStatus", () => ({ __esModule: true, @@ -52,7 +50,6 @@ beforeEach(() => { describe("App", () => { test("renders", () => { - jest.mocked(getTestGroups).mockReturnValue([TestGroups.KeycloakSso]) const result = render() expect(result.asFragment()).toMatchSnapshot() }) diff --git a/assets/tests/components/appStateWrapper.test.tsx b/assets/tests/components/appStateWrapper.test.tsx index 45fede0ee..8138e03a8 100644 --- a/assets/tests/components/appStateWrapper.test.tsx +++ b/assets/tests/components/appStateWrapper.test.tsx @@ -2,8 +2,6 @@ import { test, expect, jest } from "@jest/globals" import React from "react" import { render } from "@testing-library/react" import AppStateWrapper from "../../src/components/appStateWrapper" -import getTestGroups from "../../src/userTestGroups" -import { TestGroups } from "../../src/userInTestGroup" jest.mock("userTestGroups", () => ({ __esModule: true, @@ -11,7 +9,6 @@ jest.mock("userTestGroups", () => ({ })) test("renders", () => { - jest.mocked(getTestGroups).mockReturnValue([TestGroups.KeycloakSso]) const result = render() expect(result.asFragment()).toMatchSnapshot() }) diff --git a/assets/tests/components/nav/navMenu.test.tsx b/assets/tests/components/nav/navMenu.test.tsx index c0cd5be67..16c401425 100644 --- a/assets/tests/components/nav/navMenu.test.tsx +++ b/assets/tests/components/nav/navMenu.test.tsx @@ -7,8 +7,6 @@ import * as browser from "../../../src/models/browser" import { openDrift } from "../../../src/helpers/drift" import NavMenu from "../../../src/components/nav/navMenu" import { BrowserRouter } from "react-router-dom" -import getTestGroups from "../../../src/userTestGroups" -import { TestGroups } from "../../../src/userInTestGroup" import getEmailAddress from "../../../src/userEmailAddress" jest.mock("../../../src/helpers/drift", () => ({ @@ -81,7 +79,6 @@ describe("NavMenu", () => { }) test("shows who is logged in", async () => { - jest.mocked(getTestGroups).mockReturnValue([TestGroups.KeycloakSso]) jest.mocked(getEmailAddress).mockReturnValue("test@example.localhost") render( @@ -93,20 +90,7 @@ describe("NavMenu", () => { expect(await screen.findByText("Logged in as")).toBeVisible() }) - test("does not show who is logged in if the user isn't in the Keycloak test group", () => { - jest.mocked(getTestGroups).mockReturnValue([]) - - render( - - - - ) - - expect(screen.queryByText("Logged in as")).not.toBeInTheDocument() - }) - test("shows logout button", async () => { - jest.mocked(getTestGroups).mockReturnValue([TestGroups.KeycloakSso]) jest.mocked(getEmailAddress).mockReturnValue("test@example.localhost") render( @@ -118,20 +102,6 @@ describe("NavMenu", () => { expect(screen.getByRole("link", { name: "Logout" })).toBeVisible() }) - test("doesn't show logout button if the user isn't in the Keycloak test group", async () => { - jest.mocked(getTestGroups).mockReturnValue([]) - - render( - - - - ) - - expect( - screen.queryByRole("link", { name: "Logout" }) - ).not.toBeInTheDocument() - }) - test("refresh button reloads the page", async () => { const reloadSpy = jest .spyOn(browser, "reload") diff --git a/assets/tests/components/nav/topNav.test.tsx b/assets/tests/components/nav/topNav.test.tsx index d30d701a6..1ee031229 100644 --- a/assets/tests/components/nav/topNav.test.tsx +++ b/assets/tests/components/nav/topNav.test.tsx @@ -8,13 +8,6 @@ import { initialState } from "../../../src/state" import { BrowserRouter } from "react-router-dom" import "@testing-library/jest-dom/jest-globals" import * as browser from "../../../src/models/browser" -import getTestGroups from "../../../src/userTestGroups" -import { TestGroups } from "../../../src/userInTestGroup" - -jest.mock("userTestGroups", () => ({ - __esModule: true, - default: jest.fn(() => []), -})) describe("TopNav", () => { test("refresh button reloads the page", async () => { @@ -38,26 +31,7 @@ describe("TopNav", () => { }) describe("User info", () => { - test("does not have a 'User Info' button if the user isn't in the right test group", () => { - jest.mocked(getTestGroups).mockReturnValue([]) - - const dispatch = jest.fn() - render( - - - - - - ) - - expect( - screen.queryByRole("button", { name: "User Info" }) - ).not.toBeInTheDocument() - }) - test("has a 'User Info' button", () => { - jest.mocked(getTestGroups).mockReturnValue([TestGroups.KeycloakSso]) - const dispatch = jest.fn() render( @@ -73,8 +47,6 @@ describe("TopNav", () => { }) test("brings up an element with 'logged in as' text and a logout link when clicked", async () => { - jest.mocked(getTestGroups).mockReturnValue([TestGroups.KeycloakSso]) - const dispatch = jest.fn() const user = userEvent.setup() render( @@ -99,8 +71,6 @@ describe("TopNav", () => { }) test("clicking the 'User Info' button again makes the 'Logged in as' popover disappear", async () => { - jest.mocked(getTestGroups).mockReturnValue([TestGroups.KeycloakSso]) - const dispatch = jest.fn() const user = userEvent.setup() render( From 794d6efc4d2ea7ba06e1fc02e16995aa33c51243 Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Wed, 7 Aug 2024 11:18:08 -0400 Subject: [PATCH 10/38] cleanup: remove __MODULE__ in logger calls (#2733) * cleanup: remove `__MODULE__` in logger calls cleanup: remove leading colons and spaces from logs * cleanup:fix:test: remove module from logs --- lib/concentrate/merge.ex | 6 +++--- .../producer/http/state_machine.ex | 20 +++++++++---------- lib/geonames.ex | 6 ++---- lib/notifications/bridge.ex | 2 +- lib/realtime/alerts_fetcher.ex | 10 +++------- lib/schedule.ex | 2 +- lib/schedule/fetcher.ex | 16 +++++++-------- lib/schedule/garage.ex | 2 +- .../health/checkers/routes_checker.ex | 2 +- .../health/checkers/timepoints_checker.ex | 4 +--- .../checkers/trip_stop_times_checker.ex | 2 +- lib/skate/migrate.ex | 10 +++++----- lib/skate/oban/clean_up_notifications.ex | 6 ++---- lib/skate/repo.ex | 4 ++-- lib/skate/warm_up.ex | 4 ++-- lib/skate_web/channels/user_socket.ex | 4 ++-- .../channels/vehicles_search_channel.ex | 4 ++-- lib/skate_web/controllers/auth_controller.ex | 8 ++------ lib/train_vehicles/stream.ex | 2 +- .../health/checkers/routes_checker_test.exs | 2 +- .../checkers/timepoints_checker_test.exs | 2 +- .../checkers/trip_stop_times_checker_test.exs | 2 +- test/skate/migrate_test.exs | 8 ++++---- .../oban/clean_up_notifications_test.exs | 4 ++-- test/skate/warm_up_test.exs | 12 +++++------ 25 files changed, 65 insertions(+), 79 deletions(-) diff --git a/lib/concentrate/merge.ex b/lib/concentrate/merge.ex index 757cf366c..5507fe500 100644 --- a/lib/concentrate/merge.ex +++ b/lib/concentrate/merge.ex @@ -93,14 +93,14 @@ defmodule Concentrate.Merge do _ = Logger.debug(fn -> - "#{__MODULE__} merge time=#{time / 1_000}" + "merge time=#{time / 1_000}" end) {time, grouped} = :timer.tc(&group/1, [merged]) _ = Logger.debug(fn -> - "#{__MODULE__} group time=#{time / 1_000}" + "group time=#{time / 1_000}" end) state = %{state | timer: nil, demand: ask_demand(state.demand)} @@ -110,7 +110,7 @@ defmodule Concentrate.Merge do def handle_info(msg, state) do _ = Logger.warning(fn -> - "unknown message to #{__MODULE__} #{inspect(self())}: #{inspect(msg)}" + "unknown message to #{inspect(self())}: #{inspect(msg)}" end) {:noreply, [], state} diff --git a/lib/concentrate/producer/http/state_machine.ex b/lib/concentrate/producer/http/state_machine.ex index 033135189..ffd031cfd 100644 --- a/lib/concentrate/producer/http/state_machine.ex +++ b/lib/concentrate/producer/http/state_machine.ex @@ -98,7 +98,7 @@ defmodule Concentrate.Producer.HTTP.StateMachine do _ = Logger.debug(fn -> - "#{__MODULE__} scheduling fetch url=#{inspect(machine.url)} after=#{time}" + "scheduling fetch url=#{inspect(machine.url)} after=#{time}" end) time @@ -162,7 +162,7 @@ defmodule Concentrate.Producer.HTTP.StateMachine do # not modified _ = Logger.info(fn -> - "#{__MODULE__}: not modified url=#{inspect(machine.url)}" + "not modified url=#{inspect(machine.url)}" end) {machine, messages} = check_last_success(machine) @@ -180,7 +180,7 @@ defmodule Concentrate.Producer.HTTP.StateMachine do _ = Logger.log(log_level, fn -> - "#{__MODULE__}: url=#{inspect(machine.url)} error=#{inspect(reason)}" + "url=#{inspect(machine.url)} error=#{inspect(reason)}" end) {machine, messages} = check_last_success(machine) @@ -207,7 +207,7 @@ defmodule Concentrate.Producer.HTTP.StateMachine do _ = Logger.log(log_level, fn -> - "#{__MODULE__}: url=#{inspect(machine.url)} error=#{inspect(reason)}" + "url=#{inspect(machine.url)} error=#{inspect(reason)}" end) {machine, [], []} @@ -216,7 +216,7 @@ defmodule Concentrate.Producer.HTTP.StateMachine do defp handle_message(machine, unknown) do _ = Logger.error(fn -> - "#{__MODULE__}: got unexpected message url=#{inspect(machine.url)} message=#{inspect(unknown)}" + "got unexpected message url=#{inspect(machine.url)} message=#{inspect(unknown)}" end) {machine, [], []} @@ -275,7 +275,7 @@ defmodule Concentrate.Producer.HTTP.StateMachine do ^previous_hash -> _ = Logger.info(fn -> - "#{__MODULE__}: same content url=#{inspect(machine.url)}" + "same content url=#{inspect(machine.url)}" end) {[], machine} @@ -290,7 +290,7 @@ defmodule Concentrate.Producer.HTTP.StateMachine do _ = Logger.info(fn -> - "#{__MODULE__} updated: url=#{inspect(url(machine))} records=#{length(parsed)} time=#{time / 1000}" + "updated: url=#{inspect(url(machine))} records=#{length(parsed)} time=#{time / 1000}" end) machine = @@ -315,7 +315,7 @@ defmodule Concentrate.Producer.HTTP.StateMachine do defp log_parse_error(error, machine, trace) do _ = Logger.error(fn -> - "#{__MODULE__}: parse error url=#{inspect(machine.url)} error=#{inspect(error)}\n#{Exception.format_stacktrace(trace)}" + "parse error url=#{inspect(machine.url)} error=#{inspect(error)}\n#{Exception.format_stacktrace(trace)}" end) [] @@ -328,7 +328,7 @@ defmodule Concentrate.Producer.HTTP.StateMachine do _ = Logger.warning(fn -> delay = div(time_since_last_success, 1000) - "#{__MODULE__}: feed has not been updated url=#{inspect(machine.url)} delay=#{delay}" + "feed has not been updated url=#{inspect(machine.url)} delay=#{delay}" end) activate_fallback(%{machine | last_success: now()}) @@ -340,7 +340,7 @@ defmodule Concentrate.Producer.HTTP.StateMachine do defp activate_fallback(%{fallback: {:not_active, url}} = machine) do _ = Logger.error(fn -> - "#{__MODULE__} activating fallback url=#{inspect(machine.url)} fallback_url=#{inspect(url)}" + "activating fallback url=#{inspect(machine.url)} fallback_url=#{inspect(url)}" end) fallback_machine = diff --git a/lib/geonames.ex b/lib/geonames.ex index 5a92f9e3f..957983e58 100644 --- a/lib/geonames.ex +++ b/lib/geonames.ex @@ -36,15 +36,13 @@ defmodule Geonames do case result do {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> - Logger.info( - "#{__MODULE__} got_intersection_response url=#{sanitized_url} time=#{time_in_ms}" - ) + Logger.info("got_intersection_response url=#{sanitized_url} time=#{time_in_ms}") Jason.decode!(body, strings: :copy) response -> Logger.warning( - "#{__MODULE__} unexpected_response url=#{sanitized_url} response=#{inspect(response)} time=#{time_in_ms} retry=#{retry?}" + "unexpected_response url=#{sanitized_url} response=#{inspect(response)} time=#{time_in_ms} retry=#{retry?}" ) if retry? do diff --git a/lib/notifications/bridge.ex b/lib/notifications/bridge.ex index 19f79851c..eee668428 100644 --- a/lib/notifications/bridge.ex +++ b/lib/notifications/bridge.ex @@ -65,7 +65,7 @@ defmodule Notifications.Bridge do end def handle_info(msg, state) do - Logger.warning("#{__MODULE__} unknown message: #{inspect(msg)}") + Logger.warning("unknown message: #{inspect(msg)}") {:noreply, state} end diff --git a/lib/realtime/alerts_fetcher.ex b/lib/realtime/alerts_fetcher.ex index 6f87a17f0..3e16edbd8 100644 --- a/lib/realtime/alerts_fetcher.ex +++ b/lib/realtime/alerts_fetcher.ex @@ -69,18 +69,14 @@ defmodule Realtime.AlertsFetcher do {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> case parse_and_update_alerts(body, update_fn) do :ok -> - Logger.info("#{__MODULE__}: updated_alerts url=#{inspect(url)}") + Logger.info("updated_alerts url=#{inspect(url)}") {:error, error} -> - Logger.warning( - "#{__MODULE__}: unable_to_parse_alerts url=#{inspect(url)} error=#{inspect(error)}" - ) + Logger.warning("unable_to_parse_alerts url=#{inspect(url)} error=#{inspect(error)}") end response -> - Logger.warning( - "#{__MODULE__}: unexpected_response url=#{inspect(url)} response=#{inspect(response)}" - ) + Logger.warning("unexpected_response url=#{inspect(url)} response=#{inspect(response)}") end {:noreply, state} diff --git a/lib/schedule.ex b/lib/schedule.ex index 8763e9d85..21debe19c 100644 --- a/lib/schedule.ex +++ b/lib/schedule.ex @@ -268,7 +268,7 @@ defmodule Schedule do %{count: count, memory: memory} = :persistent_term.info() Logger.info( - "#{__MODULE__} wrote state to persistent term time_in_ms=#{System.convert_time_unit(time, :microsecond, :millisecond)} count=#{count} memory=#{memory}" + "wrote state to persistent term time_in_ms=#{System.convert_time_unit(time, :microsecond, :millisecond)} count=#{count} memory=#{memory}" ) :ok diff --git a/lib/schedule/fetcher.ex b/lib/schedule/fetcher.ex index 897938d33..14fcc985d 100644 --- a/lib/schedule/fetcher.ex +++ b/lib/schedule/fetcher.ex @@ -62,7 +62,7 @@ defmodule Schedule.Fetcher do def do_poll(state, notify_health_server?) do start_time = Time.utc_now() - Logger.info("#{__MODULE__}: Polling for new schedule data") + Logger.info("Polling for new schedule data") case fetch_gtfs( state[:files_source], @@ -76,11 +76,11 @@ defmodule Schedule.Fetcher do :ok = state[:updater_function].(schedule_state) Logger.info( - "#{__MODULE__}: Sent updated schedule data to receiving process, time_in_ms=#{Time.diff(Time.utc_now(), update_start_time, :millisecond)}" + "Sent updated schedule data to receiving process, time_in_ms=#{Time.diff(Time.utc_now(), update_start_time, :millisecond)}" ) Logger.info( - "#{__MODULE__}: Successfully loaded schedule data, time_in_ms=#{Time.diff(Time.utc_now(), start_time, :millisecond)}" + "Successfully loaded schedule data, time_in_ms=#{Time.diff(Time.utc_now(), start_time, :millisecond)}" ) if notify_health_server? && state[:health_server] do @@ -108,7 +108,7 @@ defmodule Schedule.Fetcher do Process.send_after(self(), :check_gtfs, state[:poll_interval_ms]) Logger.error(fn -> - "#{__MODULE__}: Error loading schedule data, time_in_ms=#{Time.diff(Time.utc_now(), start_time, :millisecond)} error=#{inspect(error)}" + "Error loading schedule data, time_in_ms=#{Time.diff(Time.utc_now(), start_time, :millisecond)} error=#{inspect(error)}" end) {:noreply, state} @@ -130,7 +130,7 @@ defmodule Schedule.Fetcher do defp fetch_gtfs(:remote, latest_gtfs_timestamp, latest_hastus_timestamp) do if CacheFile.should_use_file?() do - Logger.info("#{__MODULE__}: Loading schedule data from cached file") + Logger.info("Loading schedule data from cached file") case CacheFile.load_gtfs() do {:ok, data} -> @@ -159,11 +159,11 @@ defmodule Schedule.Fetcher do | :no_update | {:error, any()} defp gtfs_from_url(latest_gtfs_timestamp, latest_hastus_timestamp) do - Logger.info("#{__MODULE__}: Querying schedule data remote files") + Logger.info("Querying schedule data remote files") case fetch_remote_files(latest_gtfs_timestamp, latest_hastus_timestamp) do {:files, files, gtfs_timestamp, hastus_timestamp} -> - Logger.info("#{__MODULE__}: Updated schedule data found, parsing") + Logger.info("Updated schedule data found, parsing") try do data = Data.parse_files(files) @@ -269,7 +269,7 @@ defmodule Schedule.Fetcher do response -> Logger.warning(fn -> - "#{__MODULE__}: Unexpected response from #{url} : #{inspect(response)}" + "Unexpected response from #{url} : #{inspect(response)}" end) {:error, response} diff --git a/lib/schedule/garage.ex b/lib/schedule/garage.ex index 40ab21461..01b58bbc8 100644 --- a/lib/schedule/garage.ex +++ b/lib/schedule/garage.ex @@ -32,7 +32,7 @@ defmodule Schedule.Garage do garage = @block_code_mapping[block_code] if is_nil(garage) do - Logger.warning("#{__MODULE__}: Unrecognized block code: #{inspect(block_code)}") + Logger.warning("Unrecognized block code: #{inspect(block_code)}") end garage diff --git a/lib/schedule/health/checkers/routes_checker.ex b/lib/schedule/health/checkers/routes_checker.ex index 76c6250c4..060068b6c 100644 --- a/lib/schedule/health/checkers/routes_checker.ex +++ b/lib/schedule/health/checkers/routes_checker.ex @@ -16,7 +16,7 @@ defmodule Schedule.Health.Checkers.RoutesChecker do pass? = length >= min_length if !pass? do - Logger.warning("#{__MODULE__} failed. min_length=#{min_length} length=#{length}") + Logger.warning("failed. min_length=#{min_length} length=#{length}") end pass? diff --git a/lib/schedule/health/checkers/timepoints_checker.ex b/lib/schedule/health/checkers/timepoints_checker.ex index ade3b6687..7c1034444 100644 --- a/lib/schedule/health/checkers/timepoints_checker.ex +++ b/lib/schedule/health/checkers/timepoints_checker.ex @@ -23,9 +23,7 @@ defmodule Schedule.Health.Checkers.TimepointsChecker do pass? = length >= min_length if !pass? do - Logger.warning( - "#{__MODULE__} failed on route_id=#{route_id}. min_length=#{min_length} length=#{length}" - ) + Logger.warning("failed on route_id=#{route_id}. min_length=#{min_length} length=#{length}") end pass? diff --git a/lib/schedule/health/checkers/trip_stop_times_checker.ex b/lib/schedule/health/checkers/trip_stop_times_checker.ex index 0af3459e0..b743dcbe9 100644 --- a/lib/schedule/health/checkers/trip_stop_times_checker.ex +++ b/lib/schedule/health/checkers/trip_stop_times_checker.ex @@ -26,7 +26,7 @@ defmodule Schedule.Health.Checkers.TripStopTimesChecker do if !pass? do Logger.warning( - "#{__MODULE__} failed on trip_id=#{id} of route_id=#{route_id}. min_length=#{min_length} length=#{length}" + "failed on trip_id=#{id} of route_id=#{route_id}. min_length=#{min_length} length=#{length}" ) end diff --git a/lib/skate/migrate.ex b/lib/skate/migrate.ex index d6029c500..d3017f34e 100644 --- a/lib/skate/migrate.ex +++ b/lib/skate/migrate.ex @@ -14,16 +14,16 @@ defmodule Skate.Migrate do @impl GenServer def init(opts) do - Logger.info("#{__MODULE__} synchronous migrations starting") + Logger.info("synchronous migrations starting") Keyword.get(opts, :sync_migrate_fn, &default_migrate_fn/1).("migrations") - Logger.info("#{__MODULE__} synchronous migrations finished") + Logger.info("synchronous migrations finished") {:ok, opts, {:continue, :async_migrations}} end @impl GenServer def handle_continue(:async_migrations, opts) do - Logger.info("#{__MODULE__} async migrations starting") + Logger.info("async migrations starting") try do Keyword.get( @@ -32,10 +32,10 @@ defmodule Skate.Migrate do &default_migrate_fn/1 ).("async_migrations") - Logger.info("#{__MODULE__} async migrations finished") + Logger.info("async migrations finished") rescue e -> - Logger.warning("#{__MODULE__} async migrations failed. error=#{inspect(e)}") + Logger.warning("async migrations failed. error=#{inspect(e)}") :ok end diff --git a/lib/skate/oban/clean_up_notifications.ex b/lib/skate/oban/clean_up_notifications.ex index c3276b7b5..6bb14ff34 100644 --- a/lib/skate/oban/clean_up_notifications.ex +++ b/lib/skate/oban/clean_up_notifications.ex @@ -23,7 +23,7 @@ defmodule Skate.Oban.CleanUpNotifications do oldest_date = DateTime.add(DateTime.utc_now(), -cutoff_days * @seconds_per_day) - Logger.notice("#{__MODULE__} starting cleanup") + Logger.notice("starting cleanup") {time, {count, nil}} = :timer.tc(fn -> @@ -41,9 +41,7 @@ defmodule Skate.Oban.CleanUpNotifications do ) end) - Logger.notice( - "#{__MODULE__} finished cleanup deleted=#{count} time_in_ms=#{time / :timer.seconds(1)}" - ) + Logger.notice("finished cleanup deleted=#{count} time_in_ms=#{time / :timer.seconds(1)}") {:ok, count} end diff --git a/lib/skate/repo.ex b/lib/skate/repo.ex index 5f5250c17..b9aecee51 100644 --- a/lib/skate/repo.ex +++ b/lib/skate/repo.ex @@ -19,11 +19,11 @@ defmodule Skate.Repo do ) if is_nil(token) do - Logger.info("#{__MODULE__} add_prod_credentials token_is_nil") + Logger.info("add_prod_credentials token_is_nil") else hash_string = Base.encode16(:crypto.hash(:sha3_256, token)) - Logger.info("#{__MODULE__} add_prod_credentials token_hash=#{hash_string}") + Logger.info("add_prod_credentials token_hash=#{hash_string}") end Keyword.merge(config, diff --git a/lib/skate/warm_up.ex b/lib/skate/warm_up.ex index 1e9ad9880..3e8fe8a85 100644 --- a/lib/skate/warm_up.ex +++ b/lib/skate/warm_up.ex @@ -48,7 +48,7 @@ defmodule Skate.WarmUp do {:error, error} -> Logger.warning( - "#{__MODULE__} warmup query failed. attempt=#{attempt} query_number=#{index} reason=#{inspect(error)}" + "warmup query failed. attempt=#{attempt} query_number=#{index} reason=#{inspect(error)}" ) {:error, error} @@ -106,6 +106,6 @@ defmodule Skate.WarmUp do total_count: total_count, attempt: attempt }) do - "#{__MODULE__} Repo warm-up attempt complete. status=#{status} count_query_success=#{count_success} total_query_count=#{total_count} attempt=#{attempt}" + "Repo warm-up attempt complete. status=#{status} count_query_success=#{count_success} total_query_count=#{total_count} attempt=#{attempt}" end end diff --git a/lib/skate_web/channels/user_socket.ex b/lib/skate_web/channels/user_socket.ex index 430ac3c8f..b1a03456d 100644 --- a/lib/skate_web/channels/user_socket.ex +++ b/lib/skate_web/channels/user_socket.ex @@ -33,13 +33,13 @@ defmodule SkateWeb.UserSocket do case Guardian.Phoenix.Socket.authenticate(socket, SkateWeb.AuthManager, token) do {:ok, authed_socket} -> - Logger.info("#{__MODULE__} socket_authenticated user_id=#{unverified_user_id}") + Logger.info("socket_authenticated user_id=#{unverified_user_id}") {:ok, authed_socket} {:error, _reason} -> if !is_nil(unverified_user_id) do - Logger.info("#{__MODULE__} socket_auth_rejected user_id=#{unverified_user_id}") + Logger.info("socket_auth_rejected user_id=#{unverified_user_id}") end :error diff --git a/lib/skate_web/channels/vehicles_search_channel.ex b/lib/skate_web/channels/vehicles_search_channel.ex index a5ddc57ff..3cc6880af 100644 --- a/lib/skate_web/channels/vehicles_search_channel.ex +++ b/lib/skate_web/channels/vehicles_search_channel.ex @@ -39,7 +39,7 @@ defmodule SkateWeb.VehiclesSearchChannel do } Logger.info(fn -> - "#{__MODULE__} limited_search User=#{username} searched for property=#{subscribe_args.property}, text=#{subscribe_args.text}" + "limited_search User=#{username} searched for property=#{subscribe_args.property}, text=#{subscribe_args.text}" end) {lookup_key, result} = @@ -77,7 +77,7 @@ defmodule SkateWeb.VehiclesSearchChannel do ]) Logger.info(fn -> - "#{__MODULE__} limited_search User=#{username} updated limit for property=#{property}limit=#{limit}" + "limited_search User=#{username} updated limit for property=#{property}limit=#{limit}" end) {:reply, {:ok, %{data: result}}, Phoenix.Socket.assign(socket, lookup_key: lookup_key)} diff --git a/lib/skate_web/controllers/auth_controller.ex b/lib/skate_web/controllers/auth_controller.ex index 29d191c7c..bf3675a07 100644 --- a/lib/skate_web/controllers/auth_controller.ex +++ b/lib/skate_web/controllers/auth_controller.ex @@ -82,9 +82,7 @@ defmodule SkateWeb.AuthController do } = conn, _params ) do - Logger.error( - "#{__MODULE__} keycloak callback csrf ueberauth_failure struct=#{Kernel.inspect(auth_struct)}" - ) + Logger.error("keycloak callback csrf ueberauth_failure struct=#{Kernel.inspect(auth_struct)}") if get_session(conn, :keycloak_csrf_retry) == 1 do conn @@ -102,9 +100,7 @@ defmodule SkateWeb.AuthController do %{assigns: %{ueberauth_failure: %{provider: :keycloak} = auth_struct}} = conn, _params ) do - Logger.error( - "#{__MODULE__} keycloak callback ueberauth_failure struct=#{Kernel.inspect(auth_struct)}" - ) + Logger.error("keycloak callback ueberauth_failure struct=#{Kernel.inspect(auth_struct)}") send_resp(conn, :unauthorized, "unauthenticated") end diff --git a/lib/train_vehicles/stream.ex b/lib/train_vehicles/stream.ex index fa7319f79..b240a1ffe 100644 --- a/lib/train_vehicles/stream.ex +++ b/lib/train_vehicles/stream.ex @@ -83,6 +83,6 @@ defmodule TrainVehicles.Stream do end defp log_errors({:error, error}) do - Logger.error("module=#{__MODULE__} error=#{inspect(error)}") + Logger.error("error=#{inspect(error)}") end end diff --git a/test/schedule/health/checkers/routes_checker_test.exs b/test/schedule/health/checkers/routes_checker_test.exs index 360504b73..25607b8fa 100644 --- a/test/schedule/health/checkers/routes_checker_test.exs +++ b/test/schedule/health/checkers/routes_checker_test.exs @@ -20,7 +20,7 @@ defmodule Schedule.Health.Checkers.RoutesCheckerTest do test "verify logging when health checker fails" do assert capture_log(fn -> RoutesChecker.healthy?(%{min_length: 4}) - end) =~ "RoutesChecker failed. min_length=4 length=3" + end) =~ "failed. min_length=4 length=3" end end end diff --git a/test/schedule/health/checkers/timepoints_checker_test.exs b/test/schedule/health/checkers/timepoints_checker_test.exs index 946fa3712..b0640e4b0 100644 --- a/test/schedule/health/checkers/timepoints_checker_test.exs +++ b/test/schedule/health/checkers/timepoints_checker_test.exs @@ -30,7 +30,7 @@ defmodule Schedule.Health.Checkers.TimepointsCheckerTest do %{route_id: "1", min_length: 3}, %{route_id: "2", min_length: 4} ]) - end) =~ "TimepointsChecker failed on route_id=2. min_length=4 length=3" + end) =~ "failed on route_id=2. min_length=4 length=3" end end end diff --git a/test/schedule/health/checkers/trip_stop_times_checker_test.exs b/test/schedule/health/checkers/trip_stop_times_checker_test.exs index 2e4109290..ea8071634 100644 --- a/test/schedule/health/checkers/trip_stop_times_checker_test.exs +++ b/test/schedule/health/checkers/trip_stop_times_checker_test.exs @@ -55,7 +55,7 @@ defmodule Schedule.Health.Checkers.TripStopTimesCheckerTest do %{route_id: "2", min_length: 4} ]) end) =~ - "TripStopTimesChecker failed on trip_id=39984755 of route_id=2. min_length=4 length=3" + "failed on trip_id=39984755 of route_id=2. min_length=4 length=3" end end end diff --git a/test/skate/migrate_test.exs b/test/skate/migrate_test.exs index a6c508aa3..1922b5e29 100644 --- a/test/skate/migrate_test.exs +++ b/test/skate/migrate_test.exs @@ -84,8 +84,8 @@ defmodule Skate.MigrateTest do end) assert {:stop, :normal, _opts} = result - assert log =~ "Elixir.Skate.Migrate async migrations starting" - assert log =~ "Elixir.Skate.Migrate async migrations finished" + assert log =~ "async migrations starting" + assert log =~ "async migrations finished" end test "when async migrations raises an exception, logs & returns normal stop" do @@ -100,10 +100,10 @@ defmodule Skate.MigrateTest do assert {:stop, :normal, _opts} = result - assert log =~ "Elixir.Skate.Migrate async migrations starting" + assert log =~ "async migrations starting" assert log =~ - "Elixir.Skate.Migrate async migrations failed. error=%RuntimeError{message: \"migration error\"}" + "async migrations failed. error=%RuntimeError{message: \"migration error\"}" end end diff --git a/test/skate/oban/clean_up_notifications_test.exs b/test/skate/oban/clean_up_notifications_test.exs index 500dff8ca..c189bcbce 100644 --- a/test/skate/oban/clean_up_notifications_test.exs +++ b/test/skate/oban/clean_up_notifications_test.exs @@ -25,10 +25,10 @@ defmodule Skate.Oban.CleanUpNotificationsTest do perform_job(CleanUpNotifications, %{"limit" => total, "cutoff_days" => -1}) end) - assert log =~ "#{Skate.Oban.CleanUpNotifications} starting cleanup" + assert log =~ "starting cleanup" assert log =~ - ~r/#{Skate.Oban.CleanUpNotifications} finished cleanup deleted=#{total} time_in_ms=\d+/ + ~r/finished cleanup deleted=#{total} time_in_ms=\d+/ assert 0 == Skate.Repo.aggregate(Notification, :count, :id) end diff --git a/test/skate/warm_up_test.exs b/test/skate/warm_up_test.exs index 5bce58414..73cf39386 100644 --- a/test/skate/warm_up_test.exs +++ b/test/skate/warm_up_test.exs @@ -24,7 +24,7 @@ defmodule Skate.WarmUpTest do assert :ignore = result assert log =~ - "Elixir.Skate.WarmUp Repo warm-up attempt complete. status=success count_query_success=9 total_query_count=10" + "Repo warm-up attempt complete. status=success count_query_success=9 total_query_count=10" end test "returns :ok if fails on first try and succeeds on second" do @@ -49,10 +49,10 @@ defmodule Skate.WarmUpTest do assert :ignore = result assert log =~ - "[warning] Elixir.Skate.WarmUp Repo warm-up attempt complete. status=failure count_query_success=0 total_query_count=10 attempt=1" + "[warning] Repo warm-up attempt complete. status=failure count_query_success=0 total_query_count=10 attempt=1" assert log =~ - "[info] Elixir.Skate.WarmUp Repo warm-up attempt complete. status=success count_query_success=10 total_query_count=10 attempt=2" + "[info] Repo warm-up attempt complete. status=success count_query_success=10 total_query_count=10 attempt=2" end test "retries on failure the configured number of times before returning stop" do @@ -71,14 +71,14 @@ defmodule Skate.WarmUpTest do {result, log} = with_log(fn -> Skate.WarmUp.init("initial_state") end) assert {:stop, - "Elixir.Skate.WarmUp Repo warm-up attempt complete. status=failure count_query_success=4 total_query_count=10 attempt=2"} = + "Repo warm-up attempt complete. status=failure count_query_success=4 total_query_count=10 attempt=2"} = result assert log =~ - "[warning] Elixir.Skate.WarmUp Repo warm-up attempt complete. status=failure count_query_success=4 total_query_count=10 attempt=1" + "[warning] Repo warm-up attempt complete. status=failure count_query_success=4 total_query_count=10 attempt=1" assert log =~ - "[error] Elixir.Skate.WarmUp Repo warm-up attempt complete. status=failure count_query_success=4 total_query_count=10 attempt=2" + "[error] Repo warm-up attempt complete. status=failure count_query_success=4 total_query_count=10 attempt=2" end end end From 28221b50c5430bad0144ac61b66ce1bd66483e5e Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Wed, 7 Aug 2024 11:24:52 -0400 Subject: [PATCH 11/38] cleanup: move $metadata to front of $level (#2734) * cleanup: move `$metadata` to front of `$level` * cleanup:fix:test: remove level from logs --- config/config.exs | 2 +- config/prod.exs | 2 +- test/skate/warm_up_test.exs | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/config/config.exs b/config/config.exs index b27479095..2040b26fd 100644 --- a/config/config.exs +++ b/config/config.exs @@ -139,7 +139,7 @@ config :skate, Oban, # Configures Elixir's Logger config :logger, :console, - format: "$time $metadata[$level] $message\n", + format: "$time [$level] $metadata$message\n", metadata: [:mfa, :request_id] # "code" is the secret value returned by AWS to /auth/cognito/callback diff --git a/config/prod.exs b/config/prod.exs index ed4d37f96..56d2ff6c9 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -48,7 +48,7 @@ config :skate, Skate.Repo, config :logger, level: :info config :logger, :console, - format: "$time $metadata[$level] node=$node $message\n", + format: "$time [$level] node=$node $metadata$message\n", metadata: [:mfa, :request_id] # Configure Ueberauth to use Cognito / Keycloak diff --git a/test/skate/warm_up_test.exs b/test/skate/warm_up_test.exs index 73cf39386..10196f317 100644 --- a/test/skate/warm_up_test.exs +++ b/test/skate/warm_up_test.exs @@ -49,10 +49,10 @@ defmodule Skate.WarmUpTest do assert :ignore = result assert log =~ - "[warning] Repo warm-up attempt complete. status=failure count_query_success=0 total_query_count=10 attempt=1" + "Repo warm-up attempt complete. status=failure count_query_success=0 total_query_count=10 attempt=1" assert log =~ - "[info] Repo warm-up attempt complete. status=success count_query_success=10 total_query_count=10 attempt=2" + "Repo warm-up attempt complete. status=success count_query_success=10 total_query_count=10 attempt=2" end test "retries on failure the configured number of times before returning stop" do @@ -75,10 +75,10 @@ defmodule Skate.WarmUpTest do result assert log =~ - "[warning] Repo warm-up attempt complete. status=failure count_query_success=4 total_query_count=10 attempt=1" + "Repo warm-up attempt complete. status=failure count_query_success=4 total_query_count=10 attempt=1" assert log =~ - "[error] Repo warm-up attempt complete. status=failure count_query_success=4 total_query_count=10 attempt=2" + "Repo warm-up attempt complete. status=failure count_query_success=4 total_query_count=10 attempt=2" end end end From 8cd5e92812669b7d9091b3e9e96811b0c39c2378 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Wed, 7 Aug 2024 14:32:14 -0400 Subject: [PATCH 12/38] cleanup: Remove `keycloak-sso` test group and Cognito (#2725) --- lib/skate_web/auth_manager.ex | 33 ++------ lib/skate_web/auth_manager/error_handler.ex | 19 +---- lib/skate_web/controllers/auth_controller.ex | 32 -------- .../auth_manager/error_handler_test.exs | 36 --------- test/skate_web/auth_manager_test.exs | 44 +---------- .../controllers/auth_controller_test.exs | 75 ------------------- .../intersection_controller_test.exs | 4 +- .../location_search_controller_test.exs | 12 +-- .../controllers/page_controller_test.exs | 4 +- .../controllers/route_controller_test.exs | 8 +- .../route_pattern_controller_test.exs | 4 +- .../controllers/schedule_controller_test.exs | 8 +- .../controllers/shape_controller_test.exs | 4 +- .../controllers/shuttle_controller_test.exs | 4 +- .../controllers/stop_controller_test.exs | 8 +- .../controllers/swing_controller_test.exs | 4 +- 16 files changed, 40 insertions(+), 259 deletions(-) diff --git a/lib/skate_web/auth_manager.ex b/lib/skate_web/auth_manager.ex index 1a420235e..1f0dc66ab 100644 --- a/lib/skate_web/auth_manager.ex +++ b/lib/skate_web/auth_manager.ex @@ -9,26 +9,12 @@ defmodule SkateWeb.AuthManager do @skate_admin_group "skate-admin" @skate_dispatcher_group "skate-dispatcher" - @v2_resource_prefix "v2:" @v3_resource_prefix "v3:" - def v2_resource_prefix, do: @v2_resource_prefix - def v3_resource_prefix, do: @v3_resource_prefix def subject_for_token(%{id: user_id}, _claims) do - keycloak_enabled? = - "keycloak-sso" in Enum.map(Skate.Settings.TestGroup.get_override_enabled(), & &1.name) - - if keycloak_enabled? do - {:ok, "#{@v3_resource_prefix}#{user_id}"} - else - {:ok, "#{@v2_resource_prefix}#{user_id}"} - end - end - - def resource_from_claims(%{"sub" => @v2_resource_prefix <> user_id}) do - {:ok, %{id: String.to_integer(user_id)}} + {:ok, "#{@v3_resource_prefix}#{user_id}"} end def resource_from_claims(%{"sub" => @v3_resource_prefix <> user_id}) do @@ -37,19 +23,12 @@ defmodule SkateWeb.AuthManager do def resource_from_claims(_), do: {:error, :invalid_claims} - def verify_claims(%{"sub" => subject} = claims, _options) do - auth_provider = - if "keycloak-sso" in Enum.map(Skate.Settings.TestGroup.get_override_enabled(), & &1.name) do - :keycloak - else - :cognito - end + def verify_claims(%{"sub" => @v3_resource_prefix <> _user_id} = claims, _options) do + {:ok, claims} + end - case {auth_provider, subject} do - {:keycloak, @v3_resource_prefix <> _user_id} -> {:ok, claims} - {:cognito, @v2_resource_prefix <> _user_id} -> {:ok, claims} - _ -> {:error, :invalid_claims} - end + def verify_claims(_claims, _options) do + {:error, :invalid_claims} end def(username_from_socket!(socket)) do diff --git a/lib/skate_web/auth_manager/error_handler.ex b/lib/skate_web/auth_manager/error_handler.ex index 117ef3795..529cb3d43 100644 --- a/lib/skate_web/auth_manager/error_handler.ex +++ b/lib/skate_web/auth_manager/error_handler.ex @@ -7,23 +7,6 @@ defmodule SkateWeb.AuthManager.ErrorHandler do @impl Guardian.Plug.ErrorHandler def auth_error(conn, {_type, _reason}, _opts) do - keycloak_enabled? = - "keycloak-sso" in Enum.map(Skate.Settings.TestGroup.get_override_enabled(), & &1.name) - - if keycloak_enabled? do - Phoenix.Controller.redirect(conn, to: ~p"/auth/keycloak") - else - auth_retries = get_session(conn, :auth_retries) || 3 - - if auth_retries > 0 do - conn - |> put_session(:auth_retries, auth_retries - 1) - |> Phoenix.Controller.redirect(to: ~p"/auth/cognito") - else - conn - |> delete_session(:auth_retries) - |> send_resp(:unauthorized, "unauthorized") - end - end + Phoenix.Controller.redirect(conn, to: ~p"/auth/keycloak") end end diff --git a/lib/skate_web/controllers/auth_controller.ex b/lib/skate_web/controllers/auth_controller.ex index bf3675a07..b09429daf 100644 --- a/lib/skate_web/controllers/auth_controller.ex +++ b/lib/skate_web/controllers/auth_controller.ex @@ -38,38 +38,6 @@ defmodule SkateWeb.AuthController do end end - def callback(%{assigns: %{ueberauth_auth: %{provider: :cognito} = auth}} = conn, _params) do - username = auth.uid - email = auth.info.email - credentials = auth.credentials - expiration = credentials.expires_at - - current_time = System.system_time(:second) - - %{id: user_id} = User.upsert(username, email) - - conn - |> delete_session(:auth_retries) - |> Guardian.Plug.sign_in( - AuthManager, - %{id: user_id}, - %{groups: credentials.other[:groups]}, - ttl: {expiration - current_time, :seconds} - ) - |> redirect(to: ~p"/") - end - - def callback(%{assigns: %{ueberauth_failure: %{provider: :cognito}}} = conn, _params) do - # Users are sometimes seeing unexpected Ueberauth failures of unknown provenance. - # Instead of sending a 403 unauthenticated response immediately, we are signing them out and - # sending them to the home page to start the auth path over again. -- MSS 2019-07-03 - # We are maintaining the retry logic but limiting it to only a set number of retries using - # logic in SkateWeb.AuthManager.ErrorHandler. -- LEM 2023-04-27 - conn - |> Guardian.Plug.sign_out(AuthManager, []) - |> redirect(to: ~p"/") - end - def callback( %{ assigns: %{ diff --git a/test/skate_web/auth_manager/error_handler_test.exs b/test/skate_web/auth_manager/error_handler_test.exs index 03cbfb68f..1d2c63143 100644 --- a/test/skate_web/auth_manager/error_handler_test.exs +++ b/test/skate_web/auth_manager/error_handler_test.exs @@ -3,10 +3,6 @@ defmodule SkateWeb.AuthManager.ErrorHandlerTest do describe "auth_error/3" do test "redirects to Keycloak login", %{conn: conn} do - {:ok, test_group} = Skate.Settings.TestGroup.create("keycloak-sso") - - Skate.Settings.TestGroup.update(%{test_group | override: :enabled}) - conn = conn |> init_test_session(%{username: "test@mbta.com"}) @@ -14,37 +10,5 @@ defmodule SkateWeb.AuthManager.ErrorHandlerTest do assert response(conn, :found) =~ ~p"/auth/keycloak" end - - test "redirects to Cognito login with two remaining retries", %{conn: conn} do - conn = - conn - |> init_test_session(%{username: "test_without_refresh_token@mbta.com"}) - |> SkateWeb.AuthManager.ErrorHandler.auth_error({:some_type, :reason}, []) - - assert html_response(conn, 302) =~ "\"/auth/cognito\"" - assert get_session(conn, :auth_retries) == 2 - end - - test "redirects to Cognito login, decrementing retry count if present", %{conn: conn} do - conn = - conn - |> init_test_session(%{username: "test_without_refresh_token@mbta.com"}) - |> put_session(:auth_retries, 2) - |> SkateWeb.AuthManager.ErrorHandler.auth_error({:some_type, :reason}, []) - - assert html_response(conn, 302) =~ "\"/auth/cognito\"" - assert get_session(conn, :auth_retries) == 1 - end - - test "sends unauthorized response if out of retries", %{conn: conn} do - conn = - conn - |> init_test_session(%{username: "test_without_refresh_token@mbta.com"}) - |> put_session(:auth_retries, 0) - |> SkateWeb.AuthManager.ErrorHandler.auth_error({:some_type, :reason}, []) - - assert response(conn, 401) =~ "unauthorized" - assert is_nil(get_session(conn, :auth_retries)) - end end end diff --git a/test/skate_web/auth_manager_test.exs b/test/skate_web/auth_manager_test.exs index b8f58cb68..c2177fb0f 100644 --- a/test/skate_web/auth_manager_test.exs +++ b/test/skate_web/auth_manager_test.exs @@ -9,31 +9,13 @@ defmodule SkateWeb.AuthManagerTest do @user_id 1 describe "subject_for_token/2" do - test "returns v2 formatted user id when given user struct" do - assert {:ok, "v2:#{@user_id}"} == - AuthManager.subject_for_token(%{id: @user_id}, %{}) - end - - test "returns v3 formatted user id when given user struct with Keycloak enabled" do - {:ok, test_group} = Skate.Settings.TestGroup.create("keycloak-sso") - - Skate.Settings.TestGroup.update(%{test_group | override: :enabled}) - + test "returns v3 formatted user id when given user struct" do assert {:ok, "v3:#{@user_id}"} == AuthManager.subject_for_token(%{id: @user_id}, %{}) end end describe "resource_from_claims/2" do - test "returns struct when given v2 formatted user id" do - %{id: user_id} = User.upsert(@username, "email@test.com") - - assert {:ok, %{id: ^user_id}} = - AuthManager.resource_from_claims(%{ - "sub" => "v2:#{user_id}" - }) - end - test "returns struct when given v3 formatted user id" do %{id: user_id} = User.upsert(@username, "email@test.com") @@ -45,33 +27,13 @@ defmodule SkateWeb.AuthManagerTest do end describe "verify_claims/2" do - test "passes with a v2-formatted resource with Keycloak disabled" do - claims = %{"sub" => "v2:#{@user_id}"} - - assert {:ok, ^claims} = AuthManager.verify_claims(claims, %{}) - end - - test "fails with a non-v2-formatted resource with Keycloak disabled" do - claims = %{"sub" => "v3:#{@user_id}"} - - assert {:error, :invalid_claims} = AuthManager.verify_claims(claims, %{}) - end - - test "passes with a v3-formatted resource with Keycloak enabled" do - {:ok, test_group} = Skate.Settings.TestGroup.create("keycloak-sso") - - Skate.Settings.TestGroup.update(%{test_group | override: :enabled}) - + test "passes with a v3-formatted resource" do claims = %{"sub" => "v3:#{@user_id}"} assert {:ok, ^claims} = AuthManager.verify_claims(claims, %{}) end - test "fails with a non-v3-formatted resource with Keycloak enabled" do - {:ok, test_group} = Skate.Settings.TestGroup.create("keycloak-sso") - - Skate.Settings.TestGroup.update(%{test_group | override: :enabled}) - + test "fails with a non-v3-formatted resource" do claims = %{"sub" => "v2:#{@user_id}"} assert {:error, :invalid_claims} = AuthManager.verify_claims(claims, %{}) diff --git a/test/skate_web/controllers/auth_controller_test.exs b/test/skate_web/controllers/auth_controller_test.exs index 3976707d4..49c3f2e6e 100644 --- a/test/skate_web/controllers/auth_controller_test.exs +++ b/test/skate_web/controllers/auth_controller_test.exs @@ -204,79 +204,4 @@ defmodule SkateWeb.AuthControllerTest do assert %{} == Plug.Conn.get_session(conn) end end - - describe "GET /auth/cognito/callback" do - test "redirects to the index page for an ueberauth auth", %{conn: conn} do - mock_auth = %Ueberauth.Auth{ - provider: :cognito, - uid: "test_username", - credentials: %Ueberauth.Auth.Credentials{ - expires_at: System.system_time(:second) + 1_000, - refresh_token: "test_refresh_token", - other: %{groups: ["test1"]} - }, - info: %{email: "test@mbta.com"} - } - - conn = - conn - |> assign(:ueberauth_auth, mock_auth) - |> get("/auth/cognito/callback") - - assert redirected_to(conn) == "/" - assert Guardian.Plug.current_claims(conn)["groups"] == ["test1"] - end - - test "creates user record if it doesn't already exist", %{conn: conn} do - mock_auth = %Ueberauth.Auth{ - provider: :cognito, - uid: "test_username", - credentials: %Ueberauth.Auth.Credentials{ - expires_at: System.system_time(:second) + 1_000, - refresh_token: "test_refresh_token", - other: %{groups: ["test1"]} - }, - info: %{email: "test@mbta.com"} - } - - conn - |> assign(:ueberauth_auth, mock_auth) - |> get("/auth/cognito/callback") - - assert %{username: "test_username", email: "test@mbta.com"} = - User.get_by_email("test@mbta.com") - end - - test "resets auth retries count on a successful auth", %{conn: conn} do - mock_auth = %Ueberauth.Auth{ - provider: :cognito, - uid: "test_username", - credentials: %Ueberauth.Auth.Credentials{ - expires_at: System.system_time(:second) + 1_000, - refresh_token: "test_refresh_token", - other: %{groups: ["test1"]} - }, - info: %{email: "test@mbta.com"} - } - - conn = - conn - |> init_test_session(%{}) - |> put_session(:auth_retries, 2) - |> assign(:ueberauth_auth, mock_auth) - |> get("/auth/cognito/callback") - - assert is_nil(get_session(conn, :auth_retries)) - end - - test "redirects home for an ueberauth failure", %{conn: conn} do - conn = - conn - |> init_test_session(%{username: "test_username"}) - |> assign(:ueberauth_failure, %{provider: :cognito}) - |> get("/auth/cognito/callback") - - assert redirected_to(conn) == "/" - end - end end diff --git a/test/skate_web/controllers/intersection_controller_test.exs b/test/skate_web/controllers/intersection_controller_test.exs index b5e3ee697..861f2319f 100644 --- a/test/skate_web/controllers/intersection_controller_test.exs +++ b/test/skate_web/controllers/intersection_controller_test.exs @@ -3,13 +3,13 @@ defmodule SkateWeb.IntersectionControllerTest do import Test.Support.Helpers describe "GET /api/intersection" do - test "when logged out, redirects you to cognito auth", %{conn: conn} do + test "when logged out, redirects you to keycloak auth", %{conn: conn} do conn = conn |> api_headers() |> get("/api/intersection/?latitude=40&longitude=-70") - assert redirected_to(conn) == ~p"/auth/cognito" + assert redirected_to(conn) == ~p"/auth/keycloak" end @tag :authenticated diff --git a/test/skate_web/controllers/location_search_controller_test.exs b/test/skate_web/controllers/location_search_controller_test.exs index 82329463e..a925d8bb9 100644 --- a/test/skate_web/controllers/location_search_controller_test.exs +++ b/test/skate_web/controllers/location_search_controller_test.exs @@ -6,13 +6,13 @@ defmodule SkateWeb.LocationSearchControllerTest do alias Skate.LocationSearch.Suggestion describe "GET /api/location_search/place" do - test "when logged out, redirects you to cognito auth", %{conn: conn} do + test "when logged out, redirects you to keycloak auth", %{conn: conn} do conn = conn |> api_headers() |> get("/api/location_search/place/place_id") - assert redirected_to(conn) == ~p"/auth/cognito" + assert redirected_to(conn) == ~p"/auth/keycloak" end @tag :authenticated @@ -42,13 +42,13 @@ defmodule SkateWeb.LocationSearchControllerTest do end describe "GET /api/location_search/search" do - test "when logged out, redirects you to cognito auth", %{conn: conn} do + test "when logged out, redirects you to keycloak auth", %{conn: conn} do conn = conn |> api_headers() |> get("/api/location_search/search?query=test") - assert redirected_to(conn) == ~p"/auth/cognito" + assert redirected_to(conn) == ~p"/auth/keycloak" end @tag :authenticated @@ -79,13 +79,13 @@ defmodule SkateWeb.LocationSearchControllerTest do end describe "GET /api/location_search/suggest" do - test "when logged out, redirects you to cognito auth", %{conn: conn} do + test "when logged out, redirects you to keycloak auth", %{conn: conn} do conn = conn |> api_headers() |> get("/api/location_search/suggest?query=test") - assert redirected_to(conn) == ~p"/auth/cognito" + assert redirected_to(conn) == ~p"/auth/keycloak" end @tag :authenticated diff --git a/test/skate_web/controllers/page_controller_test.exs b/test/skate_web/controllers/page_controller_test.exs index 2b3d6adec..7b42b46ba 100644 --- a/test/skate_web/controllers/page_controller_test.exs +++ b/test/skate_web/controllers/page_controller_test.exs @@ -4,10 +4,10 @@ defmodule SkateWeb.PageControllerTest do import Test.Support.Helpers describe "GET /" do - test "when logged out, redirects you to cognito auth", %{conn: conn} do + test "when logged out, redirects you to keycloak auth", %{conn: conn} do conn = get(conn, "/") - assert redirected_to(conn) == ~p"/auth/cognito" + assert redirected_to(conn) == ~p"/auth/keycloak" end @tag :authenticated diff --git a/test/skate_web/controllers/route_controller_test.exs b/test/skate_web/controllers/route_controller_test.exs index 0ba4de889..faa102fbf 100644 --- a/test/skate_web/controllers/route_controller_test.exs +++ b/test/skate_web/controllers/route_controller_test.exs @@ -24,13 +24,13 @@ defmodule SkateWeb.RouteControllerTest do reassign_env(:skate_web, :routes_fn, fn -> @routes end) end - test "when logged out, redirects you to cognito auth", %{conn: conn} do + test "when logged out, redirects you to keycloak auth", %{conn: conn} do conn = conn |> api_headers() |> get("/api/routes") - assert redirected_to(conn) == ~p"/auth/cognito" + assert redirected_to(conn) == ~p"/auth/keycloak" end @tag :authenticated @@ -53,13 +53,13 @@ defmodule SkateWeb.RouteControllerTest do reassign_env(:skate_web, :timepoints_on_route_fn, fn _route_id -> [] end) end - test "when logged out, redirects you to cognito auth", %{conn: conn} do + test "when logged out, redirects you to keycloak auth", %{conn: conn} do conn = conn |> api_headers() |> get("/api/routes/1") - assert redirected_to(conn) == ~p"/auth/cognito" + assert redirected_to(conn) == ~p"/auth/keycloak" end @tag :authenticated diff --git a/test/skate_web/controllers/route_pattern_controller_test.exs b/test/skate_web/controllers/route_pattern_controller_test.exs index 33dd5cc4c..70c098834 100644 --- a/test/skate_web/controllers/route_pattern_controller_test.exs +++ b/test/skate_web/controllers/route_pattern_controller_test.exs @@ -117,13 +117,13 @@ defmodule SkateWeb.RoutePatternControllerTest do end) end - test "when logged out, redirects you to cognito auth", %{conn: conn} do + test "when logged out, redirects you to keycloak auth", %{conn: conn} do conn = conn |> api_headers() |> get("/api/route_patterns/route/r1") - assert redirected_to(conn) == ~p"/auth/cognito" + assert redirected_to(conn) == ~p"/auth/keycloak" end @tag :authenticated diff --git a/test/skate_web/controllers/schedule_controller_test.exs b/test/skate_web/controllers/schedule_controller_test.exs index bc7d385dc..ed26d022c 100644 --- a/test/skate_web/controllers/schedule_controller_test.exs +++ b/test/skate_web/controllers/schedule_controller_test.exs @@ -17,7 +17,7 @@ defmodule SkateWeb.ScheduleControllerTest do ) describe "GET /api/schedule/run" do - test "when logged out, redirects you to cognito auth", %{conn: conn} do + test "when logged out, redirects you to keycloak auth", %{conn: conn} do reassign_env(:skate, :schedule_run_fn, fn _run_id, _trip_id -> @run end) conn = @@ -25,7 +25,7 @@ defmodule SkateWeb.ScheduleControllerTest do |> api_headers() |> get("/api/schedule/run?trip_id=trip&run_id=run") - assert redirected_to(conn) == ~p"/auth/cognito" + assert redirected_to(conn) == ~p"/auth/keycloak" end @tag :authenticated @@ -80,7 +80,7 @@ defmodule SkateWeb.ScheduleControllerTest do end describe "GET /api/schedule/block" do - test "when logged out, redirects you to cognito auth", %{conn: conn} do + test "when logged out, redirects you to keycloak auth", %{conn: conn} do reassign_env(:skate, :schedule_block_fn, fn _trip_id -> @block end) conn = @@ -88,7 +88,7 @@ defmodule SkateWeb.ScheduleControllerTest do |> api_headers() |> get("/api/schedule/block?trip_id=trip") - assert redirected_to(conn) == ~p"/auth/cognito" + assert redirected_to(conn) == ~p"/auth/keycloak" end @tag :authenticated diff --git a/test/skate_web/controllers/shape_controller_test.exs b/test/skate_web/controllers/shape_controller_test.exs index af089906e..f32c4b881 100644 --- a/test/skate_web/controllers/shape_controller_test.exs +++ b/test/skate_web/controllers/shape_controller_test.exs @@ -113,13 +113,13 @@ defmodule SkateWeb.ShapeControllerTest do reassign_env(:skate_web, :shapes_fn, fn _route_id -> [@shape] end) end - test "when logged out, redirects you to cognito auth", %{conn: conn} do + test "when logged out, redirects you to keycloak auth", %{conn: conn} do conn = conn |> api_headers() |> get("/api/shapes/route/1") - assert redirected_to(conn) == ~p"/auth/cognito" + assert redirected_to(conn) == ~p"/auth/keycloak" end @tag :authenticated diff --git a/test/skate_web/controllers/shuttle_controller_test.exs b/test/skate_web/controllers/shuttle_controller_test.exs index 5c2ee0f0a..897dda872 100644 --- a/test/skate_web/controllers/shuttle_controller_test.exs +++ b/test/skate_web/controllers/shuttle_controller_test.exs @@ -5,13 +5,13 @@ defmodule SkateWeb.ShuttleControllerTest do alias Schedule.Gtfs.{Route, RoutePattern, Shape} describe "GET /api/shuttles" do - test "when logged out, redirects you to cognito auth", %{conn: conn} do + test "when logged out, redirects you to keycloak auth", %{conn: conn} do conn = conn |> api_headers() |> get("/api/shuttles") - assert redirected_to(conn) == ~p"/auth/cognito" + assert redirected_to(conn) == ~p"/auth/keycloak" end @tag :authenticated diff --git a/test/skate_web/controllers/stop_controller_test.exs b/test/skate_web/controllers/stop_controller_test.exs index 8c6b42658..15623d5ce 100644 --- a/test/skate_web/controllers/stop_controller_test.exs +++ b/test/skate_web/controllers/stop_controller_test.exs @@ -21,10 +21,10 @@ defmodule SkateWeb.StopControllerTest do reassign_env(:skate_web, :stations_fn, fn -> @stations end) end - test "when logged out, redirects you to cognito auth", %{conn: conn} do + test "when logged out, redirects you to keycloak auth", %{conn: conn} do conn = get(conn, ~p"/api/stops/stations") - assert redirected_to(conn) == ~p"/auth/cognito" + assert redirected_to(conn) == ~p"/auth/keycloak" end @tag :authenticated @@ -87,10 +87,10 @@ defmodule SkateWeb.StopControllerTest do reassign_env(:skate_web, :version_fn, fn -> "latest_version" end) end - test "when logged out, redirects you to cognito auth", %{conn: conn} do + test "when logged out, redirects you to keycloak auth", %{conn: conn} do conn = get(conn, ~p"/api/stops") - assert redirected_to(conn) == ~p"/auth/cognito" + assert redirected_to(conn) == ~p"/auth/keycloak" end @tag :authenticated diff --git a/test/skate_web/controllers/swing_controller_test.exs b/test/skate_web/controllers/swing_controller_test.exs index c0191f853..6aa5d6f28 100644 --- a/test/skate_web/controllers/swing_controller_test.exs +++ b/test/skate_web/controllers/swing_controller_test.exs @@ -34,13 +34,13 @@ defmodule SkateWeb.SwingsControllerTest do end) end - test "when logged out, redirects you to cognito auth", %{conn: conn} do + test "when logged out, redirects you to keycloak auth", %{conn: conn} do conn = conn |> api_headers() |> get("/api/swings") - assert redirected_to(conn) == ~p"/auth/cognito" + assert redirected_to(conn) == ~p"/auth/keycloak" end @tag :authenticated From 8b2b3175308f1b8fd88cd6f85325f400e8f396bd Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Wed, 7 Aug 2024 14:40:55 -0400 Subject: [PATCH 13/38] fix: Remove configurable auth `:provider` in router, forcing keycloak (#2728) --- lib/skate_web/router.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/skate_web/router.ex b/lib/skate_web/router.ex index 05d86774e..b4b5e5b4b 100644 --- a/lib/skate_web/router.ex +++ b/lib/skate_web/router.ex @@ -60,8 +60,8 @@ defmodule SkateWeb.Router do scope "/auth", SkateWeb do pipe_through([:redirect_prod_http, :accepts_html, :browser]) - get("/:provider", AuthController, :request) - get("/:provider/callback", AuthController, :callback) + get("/keycloak", AuthController, :request) + get("/keycloak/callback", AuthController, :callback) end scope "/auth", SkateWeb do From 03652e9cba76a91a8a224d0ffff440d984e4cb97 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Wed, 7 Aug 2024 16:13:57 -0400 Subject: [PATCH 14/38] cleanup: Remove Cognito-related environment variables (#2732) --- .envrc.template | 8 -------- ARCHITECTURE.md | 2 +- config/config.exs | 6 ++---- config/dev.exs | 1 - config/prod.exs | 9 +-------- config/runtime.exs | 3 --- config/test.exs | 1 - mix.exs | 1 - mix.lock | 1 - 9 files changed, 4 insertions(+), 28 deletions(-) diff --git a/.envrc.template b/.envrc.template index 3b08199b2..9fa0cc1fa 100644 --- a/.envrc.template +++ b/.envrc.template @@ -55,14 +55,6 @@ export SECRET_KEY_BASE=$(openssl rand -base64 48) ## Used by Erlang (only required in production) # export RELEASE_COOKIE= -## AWS Cognito Authentication/authorization details (only required in production) -# export COGNITO_DOMAIN -# export COGNITO_CLIENT_ID -# export COGNITO_CLIENT_SECRET -# export COGNITO_USER_POOL_ID -# export COGNITO_AWS_REGION -# export GUARDIAN_SECRET_KEY - ## CDN details (only required in production) # export STATIC_SCHEME # export STATIC_HOST diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 98051909a..0ff19cd37 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -35,4 +35,4 @@ Skate is read-only, so it only needs to ingest data and present it. Skate uses t ## Authentication -Skate's authentication uses AWS Cognito as a middleman to manage interaction with Active Directory (using federated services). The actual login page the user interacts with is hosted by Active Directory, you use your same username and password as your email. We could someday add levels of authorization using groups in Cognito, but don't at this time. +Skate's authentication uses Keycloak as a middleman to manage interaction with Active Directory (using federated services). The actual login page the user interacts with is hosted by Active Directory, you use your same username and password as your email. We could someday add levels of authorization using groups in Keycloak and/or Active Directory, but don't at this time. diff --git a/config/config.exs b/config/config.exs index 2040b26fd..a043708bd 100644 --- a/config/config.exs +++ b/config/config.exs @@ -142,9 +142,8 @@ config :logger, :console, format: "$time [$level] $metadata$message\n", metadata: [:mfa, :request_id] -# "code" is the secret value returned by AWS to /auth/cognito/callback log_filter_params = - ~w(password code token guardian_default_claims guardian_default_resource guardian_default_token) + ~w(password token guardian_default_claims guardian_default_resource guardian_default_token) config :logster, :filter_parameters, log_filter_params @@ -153,10 +152,9 @@ config :phoenix, :filter_parameters, log_filter_params # Use Jason for JSON parsing in Phoenix config :phoenix, :json_library, Jason -# Fake Cognito authentication +# Fake Keycloak authentication config :ueberauth, Ueberauth, providers: [ - cognito: nil, keycloak: nil ] diff --git a/config/dev.exs b/config/dev.exs index badc670df..2201aa3ac 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -47,7 +47,6 @@ config :ex_aws, config :ueberauth, Ueberauth, providers: [ - cognito: {Skate.Ueberauth.Strategy.Fake, [groups: ["skate-dispatcher", "skate-admin"]]}, keycloak: {Skate.Ueberauth.Strategy.Fake, [groups: ["skate-readonly", "skate-dispatcher", "skate-admin"]]} diff --git a/config/prod.exs b/config/prod.exs index 56d2ff6c9..98ea3b9bf 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -51,20 +51,13 @@ config :logger, :console, format: "$time [$level] node=$node $metadata$message\n", metadata: [:mfa, :request_id] -# Configure Ueberauth to use Cognito / Keycloak +# Configure Ueberauth to use Keycloak config :ueberauth, Ueberauth, providers: [ - cognito: {Ueberauth.Strategy.Cognito, []}, keycloak: {Ueberauth.Strategy.Oidcc, userinfo: true, uid_field: "email", scopes: ~w(openid email)} ] -config :ueberauth, Ueberauth.Strategy.Cognito, - auth_domain: {System, :get_env, ["COGNITO_DOMAIN"]}, - client_id: {System, :get_env, ["COGNITO_CLIENT_ID"]}, - user_pool_id: {System, :get_env, ["COGNITO_USER_POOL_ID"]}, - aws_region: {System, :get_env, ["COGNITO_AWS_REGION"]} - config :ex_aws, json_codec: Jason config :ehmon, :report_mf, {:ehmon, :info_report} diff --git a/config/runtime.exs b/config/runtime.exs index ccde92897..1813d9551 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -20,9 +20,6 @@ config :skate, Skate.OpenRouteServiceAPI, api_key: System.get_env("OPEN_ROUTE_SERVICE_API_KEY"), client: Skate.OpenRouteServiceAPI.Client -config :ueberauth, Ueberauth.Strategy.Cognito, - client_secret: System.get_env("COGNITO_CLIENT_SECRET") - config :skate, SkateWeb.AuthManager, secret_key: System.get_env("GUARDIAN_SECRET_KEY") pool_size = diff --git a/config/test.exs b/config/test.exs index 286a43698..83289d967 100644 --- a/config/test.exs +++ b/config/test.exs @@ -24,7 +24,6 @@ config :skate, Oban, testing: :inline config :ueberauth, Ueberauth, providers: [ - cognito: {Skate.Ueberauth.Strategy.Fake, [groups: ["skate-dispatcher", "skate-nav-beta"]]}, keycloak: {Skate.Ueberauth.Strategy.Fake, [groups: ["skate-dispatcher", "skate-nav-beta"]]} ] diff --git a/mix.exs b/mix.exs index 310b00fdf..2be20f4f0 100644 --- a/mix.exs +++ b/mix.exs @@ -89,7 +89,6 @@ defmodule Skate.MixProject do {:ssl_verify_fun, "~> 1.1"}, {:stream_data, "~> 1.1.1", only: :test}, {:timex, "~> 3.7.5"}, - {:ueberauth_cognito, "~> 0.4.0"}, {:ueberauth_oidcc, "~> 0.4.0"}, {:ueberauth, "~> 0.10.5"} ] diff --git a/mix.lock b/mix.lock index 0d61295f5..dd00937e8 100644 --- a/mix.lock +++ b/mix.lock @@ -81,7 +81,6 @@ "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, "tzdata": {:hex, :tzdata, "1.1.1", "20c8043476dfda8504952d00adac41c6eda23912278add38edc140ae0c5bcc46", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a69cec8352eafcd2e198dea28a34113b60fdc6cb57eb5ad65c10292a6ba89787"}, "ueberauth": {:hex, :ueberauth, "0.10.8", "ba78fbcbb27d811a6cd06ad851793aaf7d27c3b30c9e95349c2c362b344cd8f0", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f2d3172e52821375bccb8460e5fa5cb91cfd60b19b636b6e57e9759b6f8c10c1"}, - "ueberauth_cognito": {:hex, :ueberauth_cognito, "0.4.0", "62daa3f675298c2b03002d2e1b7e5a30cbc513400e5732a264864a26847e71ac", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:jose, "~> 1.0", [hex: :jose, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.7", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "62378f4f34c8569cd95cc4e7463c56e9981c8afc83fdc516922065f0e1302a35"}, "ueberauth_oidcc": {:hex, :ueberauth_oidcc, "0.4.0", "3fbfbc38735b4dba54ed8bf3e9b80f5054f73cc1ec9af6ae88b7886d25934768", [:mix], [{:oidcc, "~> 3.2.0", [hex: :oidcc, repo: "hexpm", optional: false]}, {:plug, "~> 1.11", [hex: :plug, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.10", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "cdd8517d773cfe499c0b692f795f213b2eb33119afbec34aefd8be0a85c62b21"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "vector": {:hex, :vector, "1.1.0", "0789b5e00e9c551d8d5880acab9a8f44ed46690d083af397018bf0c7f30c1092", [:mix], [], "hexpm", "48b0a800ec88e55b12c689b09100e4c9ba41ea1befb459221c085a4e70040696"}, From b060e88f883f286383a8731b920d3af9f858f6b3 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Thu, 8 Aug 2024 09:41:03 -0400 Subject: [PATCH 15/38] storybook: New story for PastDetourPanel (#2731) --- .../detours/pastDetourPanel.stories.tsx | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 assets/stories/skate-components/detours/pastDetourPanel.stories.tsx diff --git a/assets/stories/skate-components/detours/pastDetourPanel.stories.tsx b/assets/stories/skate-components/detours/pastDetourPanel.stories.tsx new file mode 100644 index 000000000..47fa27586 --- /dev/null +++ b/assets/stories/skate-components/detours/pastDetourPanel.stories.tsx @@ -0,0 +1,28 @@ +import type { Meta, StoryObj } from "@storybook/react" + +import React from "react" +import { PastDetourPanel } from "../../../src/components/detours/pastDetourPanel" + +const meta = { + component: PastDetourPanel, + parameters: { + layout: "fullscreen", + stretch: true, + }, + args: {}, + // The bootstrap CSS reset is supposed to set box-sizing: border-box by + // default, we should be able to remove this after that is added + decorators: [ + (StoryFn) => ( +
    + +
    + ), + ], +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Story: Story = {} From 5a65a126ac7b77f42972a5e5e40c064b0f1ce6d4 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Thu, 8 Aug 2024 14:25:53 -0400 Subject: [PATCH 16/38] feat: Add mfa to dev logs (#2737) --- config/dev.exs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/config/dev.exs b/config/dev.exs index 2201aa3ac..4af18e3f2 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -76,7 +76,9 @@ config :skate, SkateWeb.Endpoint, config :skate, Skate.Repo, database: "skate_dev" # Do not include metadata nor timestamps in development logs -config :logger, :console, format: "[$level] $message\n" +config :logger, :console, + format: "[$level] $metadata$message\n", + metadata: [:mfa] # Set a higher stacktrace during development. Avoid configuring such # in production as building large stacktraces may be expensive. From 8ff3d786a068c0ee343e633e386d92cbc4184db8 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Wed, 14 Aug 2024 09:29:33 -0400 Subject: [PATCH 17/38] fix: Allow Hastus import to work when a block has as-directeds and regular trips (#2736) * fix: Allow Hastus import to work when a block has as-directeds and regular trips * fix: add :id to as directeds vehicles have issues without this * fix: parse :id on frontend * fix: reorder logic for WAD * wip! convert `isTripData` => `isAsDirectedData` * fix: add :id to as directed FE tests --- .../propertiesPanel/minischedule.tsx | 39 +++--- assets/src/minischedule.d.ts | 1 + assets/src/models/minischeduleData.ts | 9 +- assets/tests/api.test.ts | 2 + .../propertiesPanel/minischedule.test.tsx | 1 + lib/schedule/as_directed.ex | 4 + lib/schedule/hastus/activity.ex | 99 +++++++++----- test/schedule/block_test.exs | 33 ++++- test/schedule/data_test.exs | 124 ++++++++++++++++++ test/support/factory.ex | 1 + 10 files changed, 260 insertions(+), 53 deletions(-) diff --git a/assets/src/components/propertiesPanel/minischedule.tsx b/assets/src/components/propertiesPanel/minischedule.tsx index 86548d019..c9803d396 100644 --- a/assets/src/components/propertiesPanel/minischedule.tsx +++ b/assets/src/components/propertiesPanel/minischedule.tsx @@ -544,29 +544,27 @@ const TripSchedule = ({ activeStatus={layoverActiveStatus} /> ) : null} - {isTrip(trip) ? ( - isDeadhead(trip) ? ( - - ) : ( - - ) - ) : ( + {isAsDirected(trip) ? ( + ) : isDeadhead(trip) ? ( + + ) : ( + )} ) @@ -839,7 +837,10 @@ const isBreak = (activity: Piece | Break): activity is Break => Object.prototype.hasOwnProperty.call(activity, "breakType") const isTrip = (trip: Trip | AsDirected): trip is Trip => - Object.prototype.hasOwnProperty.call(trip, "id") + !Object.prototype.hasOwnProperty.call(trip, "kind") + +const isAsDirected = (trip: Trip | AsDirected): trip is AsDirected => + Object.prototype.hasOwnProperty.call(trip, "kind") const isDeadhead = (trip: Trip | AsDirected): boolean => isTrip(trip) && trip.routeId == null diff --git a/assets/src/minischedule.d.ts b/assets/src/minischedule.d.ts index 4bec92489..af1ff5d94 100644 --- a/assets/src/minischedule.d.ts +++ b/assets/src/minischedule.d.ts @@ -21,6 +21,7 @@ export interface Break { } export interface AsDirected { + id: TripId | null kind: "wad" | "rad" startTime: Time endTime: Time diff --git a/assets/src/models/minischeduleData.ts b/assets/src/models/minischeduleData.ts index 95415d252..ff86c082b 100644 --- a/assets/src/models/minischeduleData.ts +++ b/assets/src/models/minischeduleData.ts @@ -23,6 +23,7 @@ interface BlockData { } interface AsDirectedData { + id: TripId | null kind: "wad" | "rad" start_time: Time end_time: Time @@ -93,7 +94,7 @@ const pieceFromData = (pieceData: PieceData): Piece => ({ startTime: pieceData.start_time, startPlace: pieceData.start_place, trips: pieceData.trips.map((data) => - isTripData(data) ? tripFromData(data) : asDirectedFromData(data) + isAsDirectedData(data) ? asDirectedFromData(data) : tripFromData(data) ), endTime: pieceData.end_time, endPlace: pieceData.end_place, @@ -122,10 +123,12 @@ const tripFromData = (tripData: TripData): Trip => ({ }) const asDirectedFromData = (asDirectedData: AsDirectedData): AsDirected => ({ + id: asDirectedData.id, kind: asDirectedData.kind, startTime: asDirectedData.start_time, endTime: asDirectedData.end_time, }) -const isTripData = (data: TripData | AsDirectedData): data is TripData => - Object.prototype.hasOwnProperty.call(data, "id") +const isAsDirectedData = ( + data: TripData | AsDirectedData +): data is AsDirectedData => Object.prototype.hasOwnProperty.call(data, "kind") diff --git a/assets/tests/api.test.ts b/assets/tests/api.test.ts index 8acb5723c..369540c2e 100644 --- a/assets/tests/api.test.ts +++ b/assets/tests/api.test.ts @@ -885,6 +885,7 @@ describe("fetchScheduleBlock", () => { end_place: "end place", }, { + id: "as-directed", kind: "rad", start_time: 567, end_time: 1000, @@ -923,6 +924,7 @@ describe("fetchScheduleBlock", () => { endPlace: "end place", }, { + id: "as-directed", kind: "rad", startTime: 567, endTime: 1000, diff --git a/assets/tests/components/propertiesPanel/minischedule.test.tsx b/assets/tests/components/propertiesPanel/minischedule.test.tsx index 58b883cbf..c241f77e0 100644 --- a/assets/tests/components/propertiesPanel/minischedule.test.tsx +++ b/assets/tests/components/propertiesPanel/minischedule.test.tsx @@ -116,6 +116,7 @@ const asDirectedPiece: Piece = { startPlace: "place", trips: [ { + id: "asDirected1", kind: "rad", startTime: 16200, endTime: 44400, diff --git a/lib/schedule/as_directed.ex b/lib/schedule/as_directed.ex index 876fa0247..46f3b8c82 100644 --- a/lib/schedule/as_directed.ex +++ b/lib/schedule/as_directed.ex @@ -1,9 +1,11 @@ defmodule Schedule.AsDirected do @moduledoc false + alias Schedule.Trip alias Schedule.Hastus.Place @type t :: %__MODULE__{ + id: Trip.id() | nil, kind: :wad | :rad, start_time: Util.Time.time_of_day(), end_time: Util.Time.time_of_day(), @@ -12,6 +14,7 @@ defmodule Schedule.AsDirected do } @enforce_keys [ + :id, :kind, :start_time, :end_time, @@ -22,6 +25,7 @@ defmodule Schedule.AsDirected do @derive Jason.Encoder defstruct [ + :id, :kind, :start_time, :end_time, diff --git a/lib/schedule/hastus/activity.ex b/lib/schedule/hastus/activity.ex index e83df8af8..247c836f6 100644 --- a/lib/schedule/hastus/activity.ex +++ b/lib/schedule/hastus/activity.ex @@ -151,20 +151,13 @@ defmodule Schedule.Hastus.Activity do end) end - as_directeds_and_schedule_trips = - if operator_is_as_directed?(activity) do - [as_directed_from_trips(trips_in_piece)] - else - Enum.map(trips_in_piece, &Map.fetch!(schedule_trips_by_id, &1.trip_id)) - end - %Piece{ schedule_id: activity.schedule_id, run_id: activity.run_id, block_id: block_id_from_trips(trips_in_piece), start_time: activity.start_time, start_place: activity.start_place, - trips: as_directeds_and_schedule_trips, + trips: as_directeds_and_schedule_trips_from_trips(trips_in_piece, schedule_trips_by_id), end_time: activity.end_time, end_place: activity.end_place, start_mid_route?: @@ -175,29 +168,79 @@ defmodule Schedule.Hastus.Activity do defp operator_activity_to_piece(activity, _, _, _), do: activity - @spec as_directed_from_trips([Hastus.Trip.t()]) :: AsDirected.t() - defp as_directed_from_trips(trips_in_piece) do - [ - %Hastus.Trip{route_id: nil} = _pullout, - as_directed_trip, - %Hastus.Trip{route_id: nil} = _pull_back - ] = trips_in_piece - - kind = - case as_directed_trip.route_id do - "rad" -> :rad - "wad" -> :wad - end + defp as_directeds_and_schedule_trips_from_trips(trips, schedule_trips_by_id) do + trips + |> Enum.map(&convert_as_directed/1) + |> strip_surrounding_pulls_from_as_directeds() + |> lookup_schedule_trips(schedule_trips_by_id) + end + + defp convert_as_directed(%Hastus.Trip{route_id: "wad"} = trip) do + as_directed(trip, :wad) + end + + defp convert_as_directed(%Hastus.Trip{route_id: "rad"} = trip) do + as_directed(trip, :rad) + end + + defp convert_as_directed(trip) do + trip + end + defp as_directed(trip, kind) do %AsDirected{ + id: trip.trip_id, kind: kind, - start_time: as_directed_trip.start_time, - end_time: as_directed_trip.end_time, - start_place: as_directed_trip.start_place, - end_place: as_directed_trip.end_place + start_time: trip.start_time, + end_time: trip.end_time, + start_place: trip.start_place, + end_place: trip.end_place } end + defp strip_surrounding_pulls_from_as_directeds([]) do + [] + end + + defp strip_surrounding_pulls_from_as_directeds([ + %Hastus.Trip{route_id: nil} = _pullout, + %Schedule.AsDirected{} = as_directed_trip, + %Hastus.Trip{route_id: nil} = _pull_back + | rest + ]) do + [ + as_directed_trip | strip_surrounding_pulls_from_as_directeds(rest) + ] + end + + defp strip_surrounding_pulls_from_as_directeds([trip | rest]) do + [ + trip + | strip_surrounding_pulls_from_as_directeds(rest) + ] + end + + defp lookup_schedule_trips( + [], + _schedule_trips_by_id + ) do + [] + end + + defp lookup_schedule_trips( + [%Schedule.AsDirected{} = as_directed_trip | rest], + schedule_trips_by_id + ) do + [as_directed_trip | lookup_schedule_trips(rest, schedule_trips_by_id)] + end + + defp lookup_schedule_trips([%Hastus.Trip{} = trip | rest], schedule_trips_by_id) do + [ + Map.get(schedule_trips_by_id, trip.trip_id, trip) + | lookup_schedule_trips(rest, schedule_trips_by_id) + ] + end + @spec as_directed_activities_to_pieces([__MODULE__.t() | Piece.t()]) :: [ __MODULE__.t() | Piece.t() ] @@ -230,6 +273,7 @@ defmodule Schedule.Hastus.Activity do start_place: activity.start_place, trips: [ %AsDirected{ + id: nil, kind: case activity.activity_type do "wad" -> :wad @@ -327,11 +371,6 @@ defmodule Schedule.Hastus.Activity do trip.start_time <= activity.end_time end - @spec operator_is_as_directed?(__MODULE__.t()) :: boolean() - defp operator_is_as_directed?(%__MODULE__{activity_type: "Operator"} = activity) do - activity.partial_block_id =~ ~r/^[r|w]ad/i - end - @spec start_mid_route?( __MODULE__.t(), [Hastus.Trip.t()], diff --git a/test/schedule/block_test.exs b/test/schedule/block_test.exs index 52563c6ab..9be7e89f8 100644 --- a/test/schedule/block_test.exs +++ b/test/schedule/block_test.exs @@ -4,7 +4,7 @@ defmodule Schedule.BlockTest do import Skate.Factory alias Schedule.Block - alias Schedule.{Block, Trip} + alias Schedule.{Block, Trip, AsDirected} alias Schedule.Gtfs.StopTime @pullout %Trip{ @@ -80,6 +80,31 @@ defmodule Schedule.BlockTest do pieces: [@piece] ) + @as_directed %AsDirected{ + id: "ad1", + kind: :wad, + start_time: 7, + end_time: 8, + start_place: "garage", + end_place: "route" + } + + @piece_with_as_directed build(:piece, + block_id: @pullout.block_id, + start_time: 1, + end_time: 19, + trips: [@pullout, @trip1, @as_directed, @deadhead, @trip2, @pullback] + ) + + @block_with_as_directed build( + :block, + id: "b", + schedule_id: "schedule", + start_time: 1, + end_time: 19, + pieces: [@piece_with_as_directed] + ) + describe "blocks_from_trips/ and get/3 " do test "can create blocks and then get them" do by_id = Block.blocks_from_pieces([@piece]) @@ -133,6 +158,12 @@ defmodule Schedule.BlockTest do test "returns :err if the given trip isn't found" do assert Block.next_revenue_trip(@block, "t3") == :err end + + test "works if there are as-directed trips in the block" do + assert Block.next_revenue_trip(@block_with_as_directed, @trip1.id) == {:trip, @as_directed} + assert Block.next_revenue_trip(@block_with_as_directed, @as_directed.id) == {:trip, @trip2} + assert Block.next_revenue_trip(@block_with_as_directed, @trip2.id) == :last + end end describe "active?" do diff --git a/test/schedule/data_test.exs b/test/schedule/data_test.exs index 42624cc99..4648e4591 100644 --- a/test/schedule/data_test.exs +++ b/test/schedule/data_test.exs @@ -1762,6 +1762,130 @@ defmodule Schedule.DataTest do } = Data.run_from_hastus(run_key, activities, trips, %{}, %{}, %{}) end + test "allows as directed trips and regular trips in the same piece" do + run_key = {"abc20011", "123-9073"} + + activities = [ + %Activity{ + schedule_id: "abc44011", + run_id: "123-1113", + start_time: 68_220, + end_time: 68_220, + start_place: "cabot", + end_place: "cabot", + activity_type: "Sign-on" + }, + %Activity{ + schedule_id: "abc44011", + run_id: "123-1113", + start_time: 68_220, + end_time: 84_600, + start_place: "cabot", + end_place: "cabot", + activity_type: "Operator", + partial_block_id: "wad-294" + } + ] + + trips = [ + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 68_220, + end_time: 68_820, + start_place: "cabot", + end_place: "ctypt", + route_id: nil, + trip_id: "64466045" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 68_820, + end_time: 70_860, + start_place: "ctypt", + end_place: "copst", + route_id: "09", + trip_id: "64464840" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 71_280, + end_time: 73_020, + start_place: "copst", + end_place: "ctypt", + route_id: "09", + trip_id: "64463084" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 73_020, + end_time: 73_560, + start_place: "ctypt", + end_place: "cabot", + route_id: nil, + trip_id: "64465819" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 73_800, + end_time: 84_600, + start_place: "cabot", + end_place: "cabot", + route_id: "wad", + trip_id: "64463012" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 84_600, + end_time: 84_600, + start_place: "cabot", + end_place: "cabot", + route_id: nil, + trip_id: "64466123" + } + ] + + schedule_trips_by_id = %{ + "64466045" => build(:trip, id: "64466045"), + "64464840" => build(:trip, id: "64464840"), + "64463084" => build(:trip, id: "64463084"), + "64465819" => build(:trip, id: "64465819"), + "64463012" => build(:trip, id: "64463012"), + "64466123" => build(:trip, id: "64466123") + } + + assert %Run{ + activities: [ + %Piece{ + block_id: "Cwad-294", + start_time: 68_220, + trips: [ + %Schedule.Trip{id: "64466045"}, + %Schedule.Trip{id: "64464840"}, + %Schedule.Trip{id: "64463084"}, + %Schedule.AsDirected{ + kind: :wad, + start_time: 73_800, + end_time: 84_600 + } + ], + end_time: 84_600 + } + ] + } = Data.run_from_hastus(run_key, activities, trips, %{}, schedule_trips_by_id, %{}) + end + test "makes breaks" do run_key = {"schedule", "run"} diff --git a/test/support/factory.ex b/test/support/factory.ex index 36bf21887..e6cca86ec 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -157,6 +157,7 @@ defmodule Skate.Factory do def as_directed_factory do %Schedule.AsDirected{ + id: sequence("Schedule.AsDirected.id:"), kind: :wad, start_time: 1, end_time: 2, From 5460c8ef00572be92f99edc2497da18733fc4bc3 Mon Sep 17 00:00:00 2001 From: Hannah Purcell <69368883+hannahpurcell@users.noreply.github.com> Date: Fri, 16 Aug 2024 09:54:52 -0400 Subject: [PATCH 18/38] feat: Basic react-bootstrap table for detour list (#2724) * Basic react-bootstrap table for detour list * Rename dummydetourpage * Suppress some columns columns for smallest viewport * Placeholder table snapshot for later hi-fi expansion --- assets/css/_route_pill.scss | 2 +- assets/css/app.scss | 3 +- assets/css/bootstrap.scss | 2 +- assets/css/detours/_detour_table.scss | 17 + assets/css/utilities/_hideable.scss | 6 + assets/src/components/app.tsx | 4 +- assets/src/components/detourListPage.tsx | 84 ++++ assets/src/components/detoursTable.tsx | 47 +++ assets/src/components/dummyDetourPage.tsx | 78 ---- assets/src/util/dateTime.ts | 11 + .../detourListPage.test.tsx.snap | 364 ++++++++++++++++++ .../tests/components/detourListPage.test.tsx | 11 + 12 files changed, 546 insertions(+), 83 deletions(-) create mode 100644 assets/css/detours/_detour_table.scss create mode 100644 assets/src/components/detourListPage.tsx create mode 100644 assets/src/components/detoursTable.tsx delete mode 100644 assets/src/components/dummyDetourPage.tsx create mode 100644 assets/tests/components/__snapshots__/detourListPage.test.tsx.snap create mode 100644 assets/tests/components/detourListPage.test.tsx diff --git a/assets/css/_route_pill.scss b/assets/css/_route_pill.scss index d0750b745..d9c54f2f0 100644 --- a/assets/css/_route_pill.scss +++ b/assets/css/_route_pill.scss @@ -1,5 +1,5 @@ .c-route-pill { - width: 2.875rem; + min-width: 2.875rem; height: 1.5rem; border-radius: 0.8125rem; diff --git a/assets/css/app.scss b/assets/css/app.scss index c6f6e042d..f2a0d272f 100644 --- a/assets/css/app.scss +++ b/assets/css/app.scss @@ -41,6 +41,7 @@ $vpp-location-padding: 1rem; @import "data_status_banner"; @import "detours/detour_map"; @import "detours/detour_modal"; +@import "detours/detour_table"; @import "directions_button"; @import "disconnected_modal"; @import "diversion_page"; @@ -48,12 +49,12 @@ $vpp-location-padding: 1rem; @import "dropdown"; @import "filter_accordion"; @import "garage_filter"; +@import "grouped_autocomplete"; @import "icon_alert_circle"; @import "incoming_box"; @import "input_modal"; @import "ladder_page"; @import "logged_in_as"; -@import "grouped_autocomplete"; @import "ladder"; @import "late_view"; @import "loading_modal"; diff --git a/assets/css/bootstrap.scss b/assets/css/bootstrap.scss index 569b05ea0..c5f83a523 100644 --- a/assets/css/bootstrap.scss +++ b/assets/css/bootstrap.scss @@ -51,7 +51,7 @@ @import "../node_modules/bootstrap/scss/popover"; // @import "../node_modules/bootstrap/scss/progress"; @import "../node_modules/bootstrap/scss/spinners"; -// @import "../node_modules/bootstrap/scss/tables"; +@import "../node_modules/bootstrap/scss/tables"; // @import "../node_modules/bootstrap/scss/toasts"; @import "../node_modules/bootstrap/scss/tooltip"; // @import "../node_modules/bootstrap/scss/transitions"; diff --git a/assets/css/detours/_detour_table.scss b/assets/css/detours/_detour_table.scss new file mode 100644 index 000000000..bad5ecd95 --- /dev/null +++ b/assets/css/detours/_detour_table.scss @@ -0,0 +1,17 @@ +.c-detours-table { + thead { + text-align: left; + font-weight: 600; + } +} + +.c-detours-table__route-info-cell { + display: flex; + align-items: center; +} + +.c-detours-table__route-info-text { + display: flex; + flex-direction: column; + margin-left: 10px; +} diff --git a/assets/css/utilities/_hideable.scss b/assets/css/utilities/_hideable.scss index 67f8eafa3..4afe9af83 100644 --- a/assets/css/utilities/_hideable.scss +++ b/assets/css/utilities/_hideable.scss @@ -9,3 +9,9 @@ transition-delay: $transition-slide-duration; } } + +.u-hide-for-mobile { + @media screen and (max-width: map-get($breakpoints, "max-mobile-landscape-tablet-portrait-width")) { + display: none; + } +} diff --git a/assets/src/components/app.tsx b/assets/src/components/app.tsx index 3b0b2edb8..888919f36 100644 --- a/assets/src/components/app.tsx +++ b/assets/src/components/app.tsx @@ -28,7 +28,7 @@ import { usePanelStateFromStateDispatchContext } from "../hooks/usePanelState" import PropertiesPanel from "./propertiesPanel" import { isGhost, isVehicle } from "../models/vehicle" import { TabMode } from "./propertiesPanel/tabPanels" -import { DummyDetourPage } from "./dummyDetourPage" +import { DetourListPage } from "./detourListPage" import inTestGroup, { TestGroups } from "../userInTestGroup" import { MinimalLadderPage } from "./minimalLadderPage" import { MinimalLadder } from "./minimalLadder" @@ -107,7 +107,7 @@ export const AppRoutes = () => { /> } /> {inTestGroup(TestGroups.DetoursList) && ( - } /> + } /> )} { + const fakeData = [ + { + route: "45", + direction: "Outbound", + name: "Franklin Park via Ruggles Station", + intersection: "John F. Kennedy St & Memorial Drive", + activeSince: 1722372950, + }, + { + route: "83", + direction: "Inbound", + name: "Central Square", + intersection: "Pearl Street & Clearwell Ave", + activeSince: 1722361948, + }, + { + route: "83", + direction: "Outbound", + name: "Rindge Ave", + intersection: "Pearl Street & Clearwell Ave", + activeSince: 1721361948, + }, + { + route: "45", + direction: "Outbound", + name: "Franklin Park via Ruggles Station", + intersection: "John F. Kennedy St & Memorial Drive", + activeSince: 1722372950, + }, + { + route: "83", + direction: "Inbound", + name: "Central Square", + intersection: "Pearl Street & Clearwell Ave", + activeSince: 1722361948, + }, + { + route: "83", + direction: "Outbound", + name: "Rindge Ave", + intersection: "Pearl Street & Clearwell Ave", + activeSince: 1721361948, + }, + { + route: "45", + direction: "Outbound", + name: "Franklin Park via Ruggles Station", + intersection: "John F. Kennedy St & Memorial Drive", + activeSince: 1722372950, + }, + { + route: "83", + direction: "Inbound", + name: "Central Square", + intersection: "Pearl Street & Clearwell Ave", + activeSince: 1722361948, + }, + { + route: "83", + direction: "Outbound", + name: "Rindge Ave", + intersection: "Pearl Street & Clearwell Ave", + activeSince: 1721361948, + }, + ] + + return ( +
    + +
    + ) +} diff --git a/assets/src/components/detoursTable.tsx b/assets/src/components/detoursTable.tsx new file mode 100644 index 000000000..909f18e07 --- /dev/null +++ b/assets/src/components/detoursTable.tsx @@ -0,0 +1,47 @@ +import React from "react" +import { Table } from "react-bootstrap" +import { RoutePill } from "./routePill" +import { useCurrentTimeSeconds } from "../hooks/useCurrentTime" +import { timeAgoLabel } from "../util/dateTime" +import { Detour } from "./detourListPage" + +interface DetoursTableProps { + data: Detour[] +} + +export const DetoursTable = ({ data }: DetoursTableProps) => { + const epochNowInSeconds = useCurrentTimeSeconds() + + return ( + + + + + + + + + + {data.map((detour, index) => ( + + + + + + ))} + +
    RouteStarting IntersectionOn detour since
    +
    + +
    + {detour.direction} + {detour.name} +
    +
    +
    + {detour.intersection} + + {timeAgoLabel(epochNowInSeconds, detour.activeSince)} +
    + ) +} diff --git a/assets/src/components/dummyDetourPage.tsx b/assets/src/components/dummyDetourPage.tsx deleted file mode 100644 index 180b60dab..000000000 --- a/assets/src/components/dummyDetourPage.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React, { useEffect, useMemo, useState } from "react" -import { fetchRoutePatterns } from "../api" -import { RoutePattern } from "../schedule" -import { DiversionPage } from "./detours/diversionPage" -import { useRoute } from "../contexts/routesContext" -import { createDetourMachine } from "../models/createDetourMachine" -import { reload } from "../models/browser" -import { isOk, Err } from "../util/result" -import { isValidSnapshot } from "../util/isValidSnapshot" - -export const DummyDetourPage = () => { - const [routePattern, setRoutePattern] = useState(null) - - const routeNumber = "66" - - useEffect(() => { - fetchRoutePatterns(routeNumber).then((routePatterns) => { - setRoutePattern(routePatterns[0]) - }) - }, []) - const route = useRoute(routePattern?.routeId) - - // The state machine won't "reinitialize", if it's inputs change, so only - // compute the snapshot from `localStorage` once. - const snapshot = useMemo(() => { - const snapshot_string = localStorage.getItem("snapshot") - - if (!snapshot_string) { - return Err<"No Snapshot Present">("No Snapshot Present") - } - - let persisted_snapshot - try { - persisted_snapshot = JSON.parse(snapshot_string) - } catch (error) { - return Err<"Parsing JSON Failed">("Parsing JSON Failed") - } - - return isValidSnapshot(createDetourMachine, persisted_snapshot) - }, []) - - return ( - <> - {isOk(snapshot) ? ( - { - // If the close button is clicked, - // clear snapshot from storage and reinitialize state machine - localStorage.setItem("snapshot", "") - reload() - }} - /> - ) : ( - <> - { - /* temp: since this is an internal page, show snapshot error as text - */ - snapshot.err - } - {route && routePattern && routePattern.shape && ( - {}} - /> - )} - - )} - - ) -} diff --git a/assets/src/util/dateTime.ts b/assets/src/util/dateTime.ts index 32525ccbe..305331ede 100644 --- a/assets/src/util/dateTime.ts +++ b/assets/src/util/dateTime.ts @@ -93,3 +93,14 @@ export const secondsAgoLabel = ( epochNowInSeconds: number, epochTime: number ): string => `${epochNowInSeconds - epochTime}s ago` + +export const timeAgoLabel = ( + epochNowInSeconds: number, + epochTime: number +): string => { + const duration = epochNowInSeconds - epochTime + const diffHours = Math.floor(duration / 3_600) + const diffMinutes = Math.floor((duration % 3_600) / 60) + + return diffHours >= 1 ? `${diffHours} hours ago` : `${diffMinutes} min ago` +} diff --git a/assets/tests/components/__snapshots__/detourListPage.test.tsx.snap b/assets/tests/components/__snapshots__/detourListPage.test.tsx.snap new file mode 100644 index 000000000..f4e48205c --- /dev/null +++ b/assets/tests/components/__snapshots__/detourListPage.test.tsx.snap @@ -0,0 +1,364 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DetourListPage renders detour list page with dummy data 1`] = ` +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Route + + Starting Intersection + + On detour since +
    +
    +
    + 45 +
    +
    + + Outbound + + + Franklin Park via Ruggles Station + +
    +
    +
    + John F. Kennedy St & Memorial Drive + + 400 hours ago +
    +
    +
    + 83 +
    +
    + + Inbound + + + Central Square + +
    +
    +
    + Pearl Street & Clearwell Ave + + 403 hours ago +
    +
    +
    + 83 +
    +
    + + Outbound + + + Rindge Ave + +
    +
    +
    + Pearl Street & Clearwell Ave + + 681 hours ago +
    +
    +
    + 45 +
    +
    + + Outbound + + + Franklin Park via Ruggles Station + +
    +
    +
    + John F. Kennedy St & Memorial Drive + + 400 hours ago +
    +
    +
    + 83 +
    +
    + + Inbound + + + Central Square + +
    +
    +
    + Pearl Street & Clearwell Ave + + 403 hours ago +
    +
    +
    + 83 +
    +
    + + Outbound + + + Rindge Ave + +
    +
    +
    + Pearl Street & Clearwell Ave + + 681 hours ago +
    +
    +
    + 45 +
    +
    + + Outbound + + + Franklin Park via Ruggles Station + +
    +
    +
    + John F. Kennedy St & Memorial Drive + + 400 hours ago +
    +
    +
    + 83 +
    +
    + + Inbound + + + Central Square + +
    +
    +
    + Pearl Street & Clearwell Ave + + 403 hours ago +
    +
    +
    + 83 +
    +
    + + Outbound + + + Rindge Ave + +
    +
    +
    + Pearl Street & Clearwell Ave + + 681 hours ago +
    +
    +`; diff --git a/assets/tests/components/detourListPage.test.tsx b/assets/tests/components/detourListPage.test.tsx new file mode 100644 index 000000000..811e72afc --- /dev/null +++ b/assets/tests/components/detourListPage.test.tsx @@ -0,0 +1,11 @@ +import { describe, test, expect } from "@jest/globals" +import React from "react" +import renderer from "react-test-renderer" +import { DetourListPage } from "../../src/components/detourListPage" + +describe("DetourListPage", () => { + test("renders detour list page with dummy data", () => { + const tree = renderer.create().toJSON() + expect(tree).toMatchSnapshot() + }) +}) From f1abd91fe32099034c68915bab5491e1a44327e4 Mon Sep 17 00:00:00 2001 From: Hannah Purcell <69368883+hannahpurcell@users.noreply.github.com> Date: Mon, 19 Aug 2024 10:49:36 -0400 Subject: [PATCH 19/38] fix: Set time on detourListPage test (#2742) --- .../components/__snapshots__/detourListPage.test.tsx.snap | 6 +++--- assets/tests/components/detourListPage.test.tsx | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/assets/tests/components/__snapshots__/detourListPage.test.tsx.snap b/assets/tests/components/__snapshots__/detourListPage.test.tsx.snap index f4e48205c..8c755515a 100644 --- a/assets/tests/components/__snapshots__/detourListPage.test.tsx.snap +++ b/assets/tests/components/__snapshots__/detourListPage.test.tsx.snap @@ -133,7 +133,7 @@ exports[`DetourListPage renders detour list page with dummy data 1`] = ` - 681 hours ago + 680 hours ago @@ -244,7 +244,7 @@ exports[`DetourListPage renders detour list page with dummy data 1`] = ` - 681 hours ago + 680 hours ago @@ -355,7 +355,7 @@ exports[`DetourListPage renders detour list page with dummy data 1`] = ` - 681 hours ago + 680 hours ago diff --git a/assets/tests/components/detourListPage.test.tsx b/assets/tests/components/detourListPage.test.tsx index 811e72afc..646756ff3 100644 --- a/assets/tests/components/detourListPage.test.tsx +++ b/assets/tests/components/detourListPage.test.tsx @@ -1,8 +1,10 @@ -import { describe, test, expect } from "@jest/globals" +import { describe, test, expect, jest } from "@jest/globals" import React from "react" import renderer from "react-test-renderer" import { DetourListPage } from "../../src/components/detourListPage" +jest.useFakeTimers().setSystemTime(new Date("2024-08-16T13:00:00")) + describe("DetourListPage", () => { test("renders detour list page with dummy data", () => { const tree = renderer.create().toJSON() From b1da9e104832e944473dd9812b1eae2a0f1d9c48 Mon Sep 17 00:00:00 2001 From: Hannah Purcell <69368883+hannahpurcell@users.noreply.github.com> Date: Mon, 19 Aug 2024 11:29:30 -0400 Subject: [PATCH 20/38] fix: Include Sentry LoggerBackend (#2741) --- config/config.exs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/config.exs b/config/config.exs index a043708bd..342bca35e 100644 --- a/config/config.exs +++ b/config/config.exs @@ -137,6 +137,10 @@ config :skate, Oban, } ] +# Include 2 logger backends +config :logger, + backends: [:console, Sentry.LoggerBackend] + # Configures Elixir's Logger config :logger, :console, format: "$time [$level] $metadata$message\n", From 538eb29c594ec0f78ca01e1abbe4827f49ec7303 Mon Sep 17 00:00:00 2001 From: Hannah Purcell <69368883+hannahpurcell@users.noreply.github.com> Date: Mon, 19 Aug 2024 11:35:59 -0400 Subject: [PATCH 21/38] =?UTF-8?q?Revert=20"fix:=20Allow=20Hastus=20import?= =?UTF-8?q?=20to=20work=20when=20a=20block=20has=20as-directeds=20and=20re?= =?UTF-8?q?=E2=80=A6"=20(#2740)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 8ff3d786a068c0ee343e633e386d92cbc4184db8. --- .../propertiesPanel/minischedule.tsx | 39 +++--- assets/src/minischedule.d.ts | 1 - assets/src/models/minischeduleData.ts | 9 +- assets/tests/api.test.ts | 2 - .../propertiesPanel/minischedule.test.tsx | 1 - lib/schedule/as_directed.ex | 4 - lib/schedule/hastus/activity.ex | 99 +++++--------- test/schedule/block_test.exs | 33 +---- test/schedule/data_test.exs | 124 ------------------ test/support/factory.ex | 1 - 10 files changed, 53 insertions(+), 260 deletions(-) diff --git a/assets/src/components/propertiesPanel/minischedule.tsx b/assets/src/components/propertiesPanel/minischedule.tsx index c9803d396..86548d019 100644 --- a/assets/src/components/propertiesPanel/minischedule.tsx +++ b/assets/src/components/propertiesPanel/minischedule.tsx @@ -544,27 +544,29 @@ const TripSchedule = ({ activeStatus={layoverActiveStatus} /> ) : null} - {isAsDirected(trip) ? ( + {isTrip(trip) ? ( + isDeadhead(trip) ? ( + + ) : ( + + ) + ) : ( - ) : isDeadhead(trip) ? ( - - ) : ( - )} ) @@ -837,10 +839,7 @@ const isBreak = (activity: Piece | Break): activity is Break => Object.prototype.hasOwnProperty.call(activity, "breakType") const isTrip = (trip: Trip | AsDirected): trip is Trip => - !Object.prototype.hasOwnProperty.call(trip, "kind") - -const isAsDirected = (trip: Trip | AsDirected): trip is AsDirected => - Object.prototype.hasOwnProperty.call(trip, "kind") + Object.prototype.hasOwnProperty.call(trip, "id") const isDeadhead = (trip: Trip | AsDirected): boolean => isTrip(trip) && trip.routeId == null diff --git a/assets/src/minischedule.d.ts b/assets/src/minischedule.d.ts index af1ff5d94..4bec92489 100644 --- a/assets/src/minischedule.d.ts +++ b/assets/src/minischedule.d.ts @@ -21,7 +21,6 @@ export interface Break { } export interface AsDirected { - id: TripId | null kind: "wad" | "rad" startTime: Time endTime: Time diff --git a/assets/src/models/minischeduleData.ts b/assets/src/models/minischeduleData.ts index ff86c082b..95415d252 100644 --- a/assets/src/models/minischeduleData.ts +++ b/assets/src/models/minischeduleData.ts @@ -23,7 +23,6 @@ interface BlockData { } interface AsDirectedData { - id: TripId | null kind: "wad" | "rad" start_time: Time end_time: Time @@ -94,7 +93,7 @@ const pieceFromData = (pieceData: PieceData): Piece => ({ startTime: pieceData.start_time, startPlace: pieceData.start_place, trips: pieceData.trips.map((data) => - isAsDirectedData(data) ? asDirectedFromData(data) : tripFromData(data) + isTripData(data) ? tripFromData(data) : asDirectedFromData(data) ), endTime: pieceData.end_time, endPlace: pieceData.end_place, @@ -123,12 +122,10 @@ const tripFromData = (tripData: TripData): Trip => ({ }) const asDirectedFromData = (asDirectedData: AsDirectedData): AsDirected => ({ - id: asDirectedData.id, kind: asDirectedData.kind, startTime: asDirectedData.start_time, endTime: asDirectedData.end_time, }) -const isAsDirectedData = ( - data: TripData | AsDirectedData -): data is AsDirectedData => Object.prototype.hasOwnProperty.call(data, "kind") +const isTripData = (data: TripData | AsDirectedData): data is TripData => + Object.prototype.hasOwnProperty.call(data, "id") diff --git a/assets/tests/api.test.ts b/assets/tests/api.test.ts index 369540c2e..8acb5723c 100644 --- a/assets/tests/api.test.ts +++ b/assets/tests/api.test.ts @@ -885,7 +885,6 @@ describe("fetchScheduleBlock", () => { end_place: "end place", }, { - id: "as-directed", kind: "rad", start_time: 567, end_time: 1000, @@ -924,7 +923,6 @@ describe("fetchScheduleBlock", () => { endPlace: "end place", }, { - id: "as-directed", kind: "rad", startTime: 567, endTime: 1000, diff --git a/assets/tests/components/propertiesPanel/minischedule.test.tsx b/assets/tests/components/propertiesPanel/minischedule.test.tsx index c241f77e0..58b883cbf 100644 --- a/assets/tests/components/propertiesPanel/minischedule.test.tsx +++ b/assets/tests/components/propertiesPanel/minischedule.test.tsx @@ -116,7 +116,6 @@ const asDirectedPiece: Piece = { startPlace: "place", trips: [ { - id: "asDirected1", kind: "rad", startTime: 16200, endTime: 44400, diff --git a/lib/schedule/as_directed.ex b/lib/schedule/as_directed.ex index 46f3b8c82..876fa0247 100644 --- a/lib/schedule/as_directed.ex +++ b/lib/schedule/as_directed.ex @@ -1,11 +1,9 @@ defmodule Schedule.AsDirected do @moduledoc false - alias Schedule.Trip alias Schedule.Hastus.Place @type t :: %__MODULE__{ - id: Trip.id() | nil, kind: :wad | :rad, start_time: Util.Time.time_of_day(), end_time: Util.Time.time_of_day(), @@ -14,7 +12,6 @@ defmodule Schedule.AsDirected do } @enforce_keys [ - :id, :kind, :start_time, :end_time, @@ -25,7 +22,6 @@ defmodule Schedule.AsDirected do @derive Jason.Encoder defstruct [ - :id, :kind, :start_time, :end_time, diff --git a/lib/schedule/hastus/activity.ex b/lib/schedule/hastus/activity.ex index 247c836f6..e83df8af8 100644 --- a/lib/schedule/hastus/activity.ex +++ b/lib/schedule/hastus/activity.ex @@ -151,13 +151,20 @@ defmodule Schedule.Hastus.Activity do end) end + as_directeds_and_schedule_trips = + if operator_is_as_directed?(activity) do + [as_directed_from_trips(trips_in_piece)] + else + Enum.map(trips_in_piece, &Map.fetch!(schedule_trips_by_id, &1.trip_id)) + end + %Piece{ schedule_id: activity.schedule_id, run_id: activity.run_id, block_id: block_id_from_trips(trips_in_piece), start_time: activity.start_time, start_place: activity.start_place, - trips: as_directeds_and_schedule_trips_from_trips(trips_in_piece, schedule_trips_by_id), + trips: as_directeds_and_schedule_trips, end_time: activity.end_time, end_place: activity.end_place, start_mid_route?: @@ -168,79 +175,29 @@ defmodule Schedule.Hastus.Activity do defp operator_activity_to_piece(activity, _, _, _), do: activity - defp as_directeds_and_schedule_trips_from_trips(trips, schedule_trips_by_id) do - trips - |> Enum.map(&convert_as_directed/1) - |> strip_surrounding_pulls_from_as_directeds() - |> lookup_schedule_trips(schedule_trips_by_id) - end - - defp convert_as_directed(%Hastus.Trip{route_id: "wad"} = trip) do - as_directed(trip, :wad) - end - - defp convert_as_directed(%Hastus.Trip{route_id: "rad"} = trip) do - as_directed(trip, :rad) - end - - defp convert_as_directed(trip) do - trip - end + @spec as_directed_from_trips([Hastus.Trip.t()]) :: AsDirected.t() + defp as_directed_from_trips(trips_in_piece) do + [ + %Hastus.Trip{route_id: nil} = _pullout, + as_directed_trip, + %Hastus.Trip{route_id: nil} = _pull_back + ] = trips_in_piece + + kind = + case as_directed_trip.route_id do + "rad" -> :rad + "wad" -> :wad + end - defp as_directed(trip, kind) do %AsDirected{ - id: trip.trip_id, kind: kind, - start_time: trip.start_time, - end_time: trip.end_time, - start_place: trip.start_place, - end_place: trip.end_place + start_time: as_directed_trip.start_time, + end_time: as_directed_trip.end_time, + start_place: as_directed_trip.start_place, + end_place: as_directed_trip.end_place } end - defp strip_surrounding_pulls_from_as_directeds([]) do - [] - end - - defp strip_surrounding_pulls_from_as_directeds([ - %Hastus.Trip{route_id: nil} = _pullout, - %Schedule.AsDirected{} = as_directed_trip, - %Hastus.Trip{route_id: nil} = _pull_back - | rest - ]) do - [ - as_directed_trip | strip_surrounding_pulls_from_as_directeds(rest) - ] - end - - defp strip_surrounding_pulls_from_as_directeds([trip | rest]) do - [ - trip - | strip_surrounding_pulls_from_as_directeds(rest) - ] - end - - defp lookup_schedule_trips( - [], - _schedule_trips_by_id - ) do - [] - end - - defp lookup_schedule_trips( - [%Schedule.AsDirected{} = as_directed_trip | rest], - schedule_trips_by_id - ) do - [as_directed_trip | lookup_schedule_trips(rest, schedule_trips_by_id)] - end - - defp lookup_schedule_trips([%Hastus.Trip{} = trip | rest], schedule_trips_by_id) do - [ - Map.get(schedule_trips_by_id, trip.trip_id, trip) - | lookup_schedule_trips(rest, schedule_trips_by_id) - ] - end - @spec as_directed_activities_to_pieces([__MODULE__.t() | Piece.t()]) :: [ __MODULE__.t() | Piece.t() ] @@ -273,7 +230,6 @@ defmodule Schedule.Hastus.Activity do start_place: activity.start_place, trips: [ %AsDirected{ - id: nil, kind: case activity.activity_type do "wad" -> :wad @@ -371,6 +327,11 @@ defmodule Schedule.Hastus.Activity do trip.start_time <= activity.end_time end + @spec operator_is_as_directed?(__MODULE__.t()) :: boolean() + defp operator_is_as_directed?(%__MODULE__{activity_type: "Operator"} = activity) do + activity.partial_block_id =~ ~r/^[r|w]ad/i + end + @spec start_mid_route?( __MODULE__.t(), [Hastus.Trip.t()], diff --git a/test/schedule/block_test.exs b/test/schedule/block_test.exs index 9be7e89f8..52563c6ab 100644 --- a/test/schedule/block_test.exs +++ b/test/schedule/block_test.exs @@ -4,7 +4,7 @@ defmodule Schedule.BlockTest do import Skate.Factory alias Schedule.Block - alias Schedule.{Block, Trip, AsDirected} + alias Schedule.{Block, Trip} alias Schedule.Gtfs.StopTime @pullout %Trip{ @@ -80,31 +80,6 @@ defmodule Schedule.BlockTest do pieces: [@piece] ) - @as_directed %AsDirected{ - id: "ad1", - kind: :wad, - start_time: 7, - end_time: 8, - start_place: "garage", - end_place: "route" - } - - @piece_with_as_directed build(:piece, - block_id: @pullout.block_id, - start_time: 1, - end_time: 19, - trips: [@pullout, @trip1, @as_directed, @deadhead, @trip2, @pullback] - ) - - @block_with_as_directed build( - :block, - id: "b", - schedule_id: "schedule", - start_time: 1, - end_time: 19, - pieces: [@piece_with_as_directed] - ) - describe "blocks_from_trips/ and get/3 " do test "can create blocks and then get them" do by_id = Block.blocks_from_pieces([@piece]) @@ -158,12 +133,6 @@ defmodule Schedule.BlockTest do test "returns :err if the given trip isn't found" do assert Block.next_revenue_trip(@block, "t3") == :err end - - test "works if there are as-directed trips in the block" do - assert Block.next_revenue_trip(@block_with_as_directed, @trip1.id) == {:trip, @as_directed} - assert Block.next_revenue_trip(@block_with_as_directed, @as_directed.id) == {:trip, @trip2} - assert Block.next_revenue_trip(@block_with_as_directed, @trip2.id) == :last - end end describe "active?" do diff --git a/test/schedule/data_test.exs b/test/schedule/data_test.exs index 4648e4591..42624cc99 100644 --- a/test/schedule/data_test.exs +++ b/test/schedule/data_test.exs @@ -1762,130 +1762,6 @@ defmodule Schedule.DataTest do } = Data.run_from_hastus(run_key, activities, trips, %{}, %{}, %{}) end - test "allows as directed trips and regular trips in the same piece" do - run_key = {"abc20011", "123-9073"} - - activities = [ - %Activity{ - schedule_id: "abc44011", - run_id: "123-1113", - start_time: 68_220, - end_time: 68_220, - start_place: "cabot", - end_place: "cabot", - activity_type: "Sign-on" - }, - %Activity{ - schedule_id: "abc44011", - run_id: "123-1113", - start_time: 68_220, - end_time: 84_600, - start_place: "cabot", - end_place: "cabot", - activity_type: "Operator", - partial_block_id: "wad-294" - } - ] - - trips = [ - %Schedule.Hastus.Trip{ - schedule_id: "abc44011", - run_id: "123-1113", - block_id: "Cwad-294", - start_time: 68_220, - end_time: 68_820, - start_place: "cabot", - end_place: "ctypt", - route_id: nil, - trip_id: "64466045" - }, - %Schedule.Hastus.Trip{ - schedule_id: "abc44011", - run_id: "123-1113", - block_id: "Cwad-294", - start_time: 68_820, - end_time: 70_860, - start_place: "ctypt", - end_place: "copst", - route_id: "09", - trip_id: "64464840" - }, - %Schedule.Hastus.Trip{ - schedule_id: "abc44011", - run_id: "123-1113", - block_id: "Cwad-294", - start_time: 71_280, - end_time: 73_020, - start_place: "copst", - end_place: "ctypt", - route_id: "09", - trip_id: "64463084" - }, - %Schedule.Hastus.Trip{ - schedule_id: "abc44011", - run_id: "123-1113", - block_id: "Cwad-294", - start_time: 73_020, - end_time: 73_560, - start_place: "ctypt", - end_place: "cabot", - route_id: nil, - trip_id: "64465819" - }, - %Schedule.Hastus.Trip{ - schedule_id: "abc44011", - run_id: "123-1113", - block_id: "Cwad-294", - start_time: 73_800, - end_time: 84_600, - start_place: "cabot", - end_place: "cabot", - route_id: "wad", - trip_id: "64463012" - }, - %Schedule.Hastus.Trip{ - schedule_id: "abc44011", - run_id: "123-1113", - block_id: "Cwad-294", - start_time: 84_600, - end_time: 84_600, - start_place: "cabot", - end_place: "cabot", - route_id: nil, - trip_id: "64466123" - } - ] - - schedule_trips_by_id = %{ - "64466045" => build(:trip, id: "64466045"), - "64464840" => build(:trip, id: "64464840"), - "64463084" => build(:trip, id: "64463084"), - "64465819" => build(:trip, id: "64465819"), - "64463012" => build(:trip, id: "64463012"), - "64466123" => build(:trip, id: "64466123") - } - - assert %Run{ - activities: [ - %Piece{ - block_id: "Cwad-294", - start_time: 68_220, - trips: [ - %Schedule.Trip{id: "64466045"}, - %Schedule.Trip{id: "64464840"}, - %Schedule.Trip{id: "64463084"}, - %Schedule.AsDirected{ - kind: :wad, - start_time: 73_800, - end_time: 84_600 - } - ], - end_time: 84_600 - } - ] - } = Data.run_from_hastus(run_key, activities, trips, %{}, schedule_trips_by_id, %{}) - end - test "makes breaks" do run_key = {"schedule", "run"} diff --git a/test/support/factory.ex b/test/support/factory.ex index e6cca86ec..36bf21887 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -157,7 +157,6 @@ defmodule Skate.Factory do def as_directed_factory do %Schedule.AsDirected{ - id: sequence("Schedule.AsDirected.id:"), kind: :wad, start_time: 1, end_time: 2, From 8257c6ad8da671e956e17de0d1f85f54623276b2 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Mon, 19 Aug 2024 15:19:51 -0400 Subject: [PATCH 22/38] fix: Make as-directed parsing not-crash and default to regular trips (#2744) --- lib/schedule/hastus/activity.ex | 38 ++--- test/schedule/data_test.exs | 244 ++++++++++++++++++++++++++++++++ 2 files changed, 264 insertions(+), 18 deletions(-) diff --git a/lib/schedule/hastus/activity.ex b/lib/schedule/hastus/activity.ex index e83df8af8..38c6581c6 100644 --- a/lib/schedule/hastus/activity.ex +++ b/lib/schedule/hastus/activity.ex @@ -152,10 +152,11 @@ defmodule Schedule.Hastus.Activity do end as_directeds_and_schedule_trips = - if operator_is_as_directed?(activity) do - [as_directed_from_trips(trips_in_piece)] + with true <- operator_is_as_directed?(activity), + {:ok, as_directed} <- as_directed_from_trips(trips_in_piece) do + [as_directed] else - Enum.map(trips_in_piece, &Map.fetch!(schedule_trips_by_id, &1.trip_id)) + _ -> Enum.map(trips_in_piece, &Map.fetch!(schedule_trips_by_id, &1.trip_id)) end %Piece{ @@ -175,29 +176,30 @@ defmodule Schedule.Hastus.Activity do defp operator_activity_to_piece(activity, _, _, _), do: activity - @spec as_directed_from_trips([Hastus.Trip.t()]) :: AsDirected.t() - defp as_directed_from_trips(trips_in_piece) do - [ - %Hastus.Trip{route_id: nil} = _pullout, - as_directed_trip, - %Hastus.Trip{route_id: nil} = _pull_back - ] = trips_in_piece - + @spec as_directed_from_trips([Hastus.Trip.t()]) :: {:ok, AsDirected.t()} | :error + defp as_directed_from_trips([ + %Hastus.Trip{route_id: nil} = _pullout, + as_directed_trip, + %Hastus.Trip{route_id: nil} = _pull_back + ]) do kind = case as_directed_trip.route_id do "rad" -> :rad "wad" -> :wad end - %AsDirected{ - kind: kind, - start_time: as_directed_trip.start_time, - end_time: as_directed_trip.end_time, - start_place: as_directed_trip.start_place, - end_place: as_directed_trip.end_place - } + {:ok, + %AsDirected{ + kind: kind, + start_time: as_directed_trip.start_time, + end_time: as_directed_trip.end_time, + start_place: as_directed_trip.start_place, + end_place: as_directed_trip.end_place + }} end + defp as_directed_from_trips(_trips_in_piece), do: :error + @spec as_directed_activities_to_pieces([__MODULE__.t() | Piece.t()]) :: [ __MODULE__.t() | Piece.t() ] diff --git a/test/schedule/data_test.exs b/test/schedule/data_test.exs index 42624cc99..e628c17d7 100644 --- a/test/schedule/data_test.exs +++ b/test/schedule/data_test.exs @@ -1762,6 +1762,250 @@ defmodule Schedule.DataTest do } = Data.run_from_hastus(run_key, activities, trips, %{}, %{}, %{}) end + test "allows as directed trips and regular trips in the same piece named like a regular block" do + run_key = {"abc20011", "123-9073"} + + activities = [ + %Activity{ + schedule_id: "abc44011", + run_id: "123-1113", + start_time: 68_220, + end_time: 68_220, + start_place: "cabot", + end_place: "cabot", + activity_type: "Sign-on" + }, + %Activity{ + schedule_id: "abc44011", + run_id: "123-1113", + start_time: 68_220, + end_time: 84_600, + start_place: "cabot", + end_place: "cabot", + activity_type: "Operator", + partial_block_id: "block_id" + } + ] + + trips = [ + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "block_id", + start_time: 68_220, + end_time: 68_820, + start_place: "cabot", + end_place: "ctypt", + route_id: nil, + trip_id: "64466045" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "block_id", + start_time: 68_820, + end_time: 70_860, + start_place: "ctypt", + end_place: "copst", + route_id: "09", + trip_id: "64464840" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "block_id", + start_time: 71_280, + end_time: 73_020, + start_place: "copst", + end_place: "ctypt", + route_id: "09", + trip_id: "64463084" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "block_id", + start_time: 73_020, + end_time: 73_560, + start_place: "ctypt", + end_place: "cabot", + route_id: nil, + trip_id: "64465819" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "block_id", + start_time: 73_800, + end_time: 84_600, + start_place: "cabot", + end_place: "cabot", + route_id: "wad", + trip_id: "64463012" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "block_id", + start_time: 84_600, + end_time: 84_600, + start_place: "cabot", + end_place: "cabot", + route_id: nil, + trip_id: "64466123" + } + ] + + schedule_trips_by_id = %{ + "64466045" => build(:trip, id: "64466045"), + "64464840" => build(:trip, id: "64464840"), + "64463084" => build(:trip, id: "64463084"), + "64465819" => build(:trip, id: "64465819"), + "64463012" => build(:trip, id: "64463012"), + "64466123" => build(:trip, id: "64466123") + } + + assert %Run{ + activities: [ + %Piece{ + block_id: "block_id", + start_time: 68_220, + trips: [ + %Schedule.Trip{id: "64466045"}, + %Schedule.Trip{id: "64464840"}, + %Schedule.Trip{id: "64463084"}, + %Schedule.Trip{id: "64465819"}, + %Schedule.Trip{id: "64463012"}, + %Schedule.Trip{id: "64466123"} + ], + end_time: 84_600 + } + ] + } = Data.run_from_hastus(run_key, activities, trips, %{}, schedule_trips_by_id, %{}) + end + + test "allows as directed trips and regular trips in the same piece named like an as-directed block" do + run_key = {"abc20011", "123-9073"} + + activities = [ + %Activity{ + schedule_id: "abc44011", + run_id: "123-1113", + start_time: 68_220, + end_time: 68_220, + start_place: "cabot", + end_place: "cabot", + activity_type: "Sign-on" + }, + %Activity{ + schedule_id: "abc44011", + run_id: "123-1113", + start_time: 68_220, + end_time: 84_600, + start_place: "cabot", + end_place: "cabot", + activity_type: "Operator", + partial_block_id: "wad-294" + } + ] + + trips = [ + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 68_220, + end_time: 68_820, + start_place: "cabot", + end_place: "ctypt", + route_id: nil, + trip_id: "64466045" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 68_820, + end_time: 70_860, + start_place: "ctypt", + end_place: "copst", + route_id: "09", + trip_id: "64464840" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 71_280, + end_time: 73_020, + start_place: "copst", + end_place: "ctypt", + route_id: "09", + trip_id: "64463084" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 73_020, + end_time: 73_560, + start_place: "ctypt", + end_place: "cabot", + route_id: nil, + trip_id: "64465819" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 73_800, + end_time: 84_600, + start_place: "cabot", + end_place: "cabot", + route_id: "wad", + trip_id: "64463012" + }, + %Schedule.Hastus.Trip{ + schedule_id: "abc44011", + run_id: "123-1113", + block_id: "Cwad-294", + start_time: 84_600, + end_time: 84_600, + start_place: "cabot", + end_place: "cabot", + route_id: nil, + trip_id: "64466123" + } + ] + + schedule_trips_by_id = %{ + "64466045" => build(:trip, id: "64466045"), + "64464840" => build(:trip, id: "64464840"), + "64463084" => build(:trip, id: "64463084"), + "64465819" => build(:trip, id: "64465819"), + "64463012" => build(:trip, id: "64463012"), + "64466123" => build(:trip, id: "64466123") + } + + assert %Run{ + activities: [ + %Piece{ + block_id: "Cwad-294", + start_time: 68_220, + trips: [ + %Schedule.Trip{id: "64466045"}, + %Schedule.Trip{id: "64464840"}, + %Schedule.Trip{id: "64463084"}, + %Schedule.Trip{id: "64465819"}, + %Schedule.Trip{id: "64463012"}, + %Schedule.Trip{id: "64466123"} + ], + end_time: 84_600 + } + ] + } = Data.run_from_hastus(run_key, activities, trips, %{}, schedule_trips_by_id, %{}) + end + test "makes breaks" do run_key = {"schedule", "run"} From f9d88c5aede7555d51a7cf65db88ad7404369902 Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Tue, 20 Aug 2024 09:33:29 -0400 Subject: [PATCH 23/38] feat: add detour button to list page (#2739) * feat: add detour button to list page * feat: add detour machine input to original route type * feat: show modal when add button is clicked --- assets/src/components/detourListPage.tsx | 21 +++++- assets/src/models/createDetourMachine.ts | 6 +- assets/src/models/detour.ts | 9 +-- .../detours/detoursPage.addDetour.test.tsx | 68 +++++++++++++++++++ 4 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 assets/tests/components/detours/detoursPage.addDetour.test.tsx diff --git a/assets/src/components/detourListPage.tsx b/assets/src/components/detourListPage.tsx index e62d6e17c..6f1bcf83a 100644 --- a/assets/src/components/detourListPage.tsx +++ b/assets/src/components/detourListPage.tsx @@ -1,5 +1,9 @@ -import React from "react" +import React, { useState } from "react" import { DetoursTable } from "./detoursTable" +import userInTestGroup, { TestGroups } from "../userInTestGroup" +import { Button } from "react-bootstrap" +import { PlusSquare } from "../helpers/bsIcons" +import { DetourModal } from "./detours/detourModal" export interface Detour { route: string @@ -76,9 +80,24 @@ export const DetourListPage = () => { }, ] + const [showDetourModal, setShowDetourModal] = useState(false) + return (
    + {userInTestGroup(TestGroups.DetoursPilot) && ( + + )} + {showDetourModal && ( + setShowDetourModal(false)} + show + originalRoute={{}} + /> + )}
    ) } diff --git a/assets/src/models/createDetourMachine.ts b/assets/src/models/createDetourMachine.ts index f0530830c..da49e9353 100644 --- a/assets/src/models/createDetourMachine.ts +++ b/assets/src/models/createDetourMachine.ts @@ -36,12 +36,12 @@ export const createDetourMachine = setup({ | { // Caller has target route route: Route - routePattern: undefined + routePattern?: undefined } | { // Caller has no prior selection - route: undefined - routePattern: undefined + route?: undefined + routePattern?: undefined }, events: {} as diff --git a/assets/src/models/detour.ts b/assets/src/models/detour.ts index cddd0d81e..79e84e207 100644 --- a/assets/src/models/detour.ts +++ b/assets/src/models/detour.ts @@ -1,5 +1,6 @@ import { LatLngLiteral } from "leaflet" -import { Route, RoutePattern, ShapePoint, Stop } from "../schedule" +import { ShapePoint, Stop } from "../schedule" +import { CreateDetourMachineInput } from "./createDetourMachine" export interface DetourShape { coordinates: ShapePoint[] @@ -10,13 +11,13 @@ export type DetourDirection = { instruction: string } -export interface OriginalRoute { - route: Route - routePattern?: RoutePattern +export interface MapLocation { center?: LatLngLiteral zoom?: number } +export type OriginalRoute = CreateDetourMachineInput & MapLocation + export interface UnfinishedRouteSegments { beforeStartPoint: ShapePoint[] afterStartPoint: ShapePoint[] diff --git a/assets/tests/components/detours/detoursPage.addDetour.test.tsx b/assets/tests/components/detours/detoursPage.addDetour.test.tsx new file mode 100644 index 000000000..db6568528 --- /dev/null +++ b/assets/tests/components/detours/detoursPage.addDetour.test.tsx @@ -0,0 +1,68 @@ +import { describe, expect, jest, test } from "@jest/globals" + +import React from "react" + +import { screen } from "@testing-library/dom" +import "@testing-library/jest-dom/jest-globals" +import { render } from "@testing-library/react" +import userEvent from "@testing-library/user-event" + +import { DetourListPage } from "../../../src/components/detourListPage" +import { TestGroups } from "../../../src/userInTestGroup" +import getTestGroups from "../../../src/userTestGroups" + +jest.mock("../../../src/userTestGroups") + +const renderDetourListPage = () => { + return render() +} + +describe("Detours Page: Create Detour", () => { + test("Shows the add detour button when in test group", () => { + jest.mocked(getTestGroups).mockReturnValue([TestGroups.DetoursPilot]) + + renderDetourListPage() + + expect(screen.getByRole("button", { name: "Add detour" })).toBeVisible() + }) + + test("Does not show add detour button when not in test group", () => { + jest.mocked(getTestGroups).mockReturnValue([]) + + renderDetourListPage() + + expect( + screen.queryByRole("button", { name: "Add detour" }) + ).not.toBeInTheDocument() + }) + + test("Opens Detour Modal when clicked", async () => { + jest.mocked(getTestGroups).mockReturnValue([TestGroups.DetoursPilot]) + + renderDetourListPage() + + await userEvent.click(screen.getByRole("button", { name: "Add detour" })) + + expect( + await screen.findByRole("heading", { name: "Create Detour" }) + ).toBeVisible() + }) + + test("Returns to page when modal is closed", async () => { + jest.mocked(getTestGroups).mockReturnValue([TestGroups.DetoursPilot]) + + renderDetourListPage() + + await userEvent.click(screen.getByRole("button", { name: "Add detour" })) + + const createDetourHeading = await screen.findByRole("heading", { + name: "Create Detour", + }) + expect(createDetourHeading).toBeVisible() + + await userEvent.click(screen.getByRole("button", { name: "Close" })) + await userEvent.click(screen.getByRole("button", { name: "Yes, I'm sure" })) + + expect(createDetourHeading).not.toBeInTheDocument() + }) +}) From 7ae5b0a5eacc77516459647ce3981f46669a5cc3 Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Thu, 22 Aug 2024 16:25:35 -0400 Subject: [PATCH 24/38] chore: update aws cert bundle (#2746) ```curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem | shasum -a 256``` --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 7ab35654d..400ee6963 100644 --- a/Dockerfile +++ b/Dockerfile @@ -39,7 +39,7 @@ WORKDIR /root RUN curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem \ -o aws-cert-bundle.pem -RUN echo "51b107da46717aed974d97464b63f7357b220fe8737969db1492d1cae74b3947 aws-cert-bundle.pem" | sha256sum -c - +RUN echo "d72191eaa5d48fe2b6e044a0ae0b0e9f35e325b34b1ecab6ffe11d490d5cdb8f aws-cert-bundle.pem" | sha256sum -c - # Add frontend assets compiled in node container, required by phx.digest COPY --from=assets-builder /root/priv/static ./priv/static From 788ddb2bc0a535e5bf01bef331943969880f8818 Mon Sep 17 00:00:00 2001 From: Hannah Purcell <69368883+hannahpurcell@users.noreply.github.com> Date: Mon, 26 Aug 2024 09:49:19 -0400 Subject: [PATCH 25/38] feat: Save detour creation snapshot to db (#2729) * Posts to db but author is null * Save snapshot to db * Include transitional state for saving-in-progress --------- Co-authored-by: Kayla Firestack --- assets/src/api.ts | 15 ++ assets/src/hooks/useDetour.ts | 26 +++- assets/src/models/createDetourMachine.ts | 45 +++++- assets/tests/api.test.ts | 39 +++++ .../detours/diversionPage.saveDetour.test.tsx | 140 ++++++++++++++++++ .../components/detours/diversionPage.test.tsx | 2 + .../detours/diversionPageActivate.test.tsx | 2 + assets/tests/hooks/useDetour.test.ts | 5 +- lib/skate/detours/detours.ex | 34 +++++ .../controllers/detours_controller.ex | 16 ++ lib/skate_web/router.ex | 1 + .../controllers/detours_controller_test.exs | 33 +++++ 12 files changed, 350 insertions(+), 8 deletions(-) create mode 100644 assets/tests/components/detours/diversionPage.saveDetour.test.tsx diff --git a/assets/src/api.ts b/assets/src/api.ts index fce8e2c60..f9ea1e787 100644 --- a/assets/src/api.ts +++ b/assets/src/api.ts @@ -28,6 +28,8 @@ import { enums, Infer, is, + never, + number, object, Struct, StructError, @@ -62,6 +64,7 @@ import { UnfinishedDetourData, unfinishedDetourFromData, } from "./models/unfinishedDetour" +import { Snapshot } from "xstate" export interface RouteData { id: string @@ -441,6 +444,18 @@ export const putRouteTabs = (routeTabs: RouteTab[]): Promise => body: JSON.stringify({ route_tabs: routeTabs }), }) +export const putDetourUpdate = ( + snapshot: Snapshot +): Promise> => + apiCallResult(`/api/detours/update_snapshot`, number(), never(), { + method: "PUT", + headers: { + "Content-Type": "application/json", + "x-csrf-token": getCsrfToken(), + }, + body: JSON.stringify({ snapshot: snapshot }), + }) + const getCsrfToken = (): string => appData()?.csrfToken || "" export const nullableParser = diff --git a/assets/src/hooks/useDetour.ts b/assets/src/hooks/useDetour.ts index d66373d18..b4f1cc62e 100644 --- a/assets/src/hooks/useDetour.ts +++ b/assets/src/hooks/useDetour.ts @@ -1,6 +1,6 @@ import { useCallback } from "react" import { ShapePoint } from "../schedule" -import { fetchUnfinishedDetour } from "../api" +import { fetchUnfinishedDetour, putDetourUpdate } from "../api" import { useApiCall } from "./useApiCall" import { isErr, isOk } from "../util/result" import { useNearestIntersection } from "./useNearestIntersection" @@ -27,9 +27,29 @@ export const useDetour = (input: UseDetourInput) => { // Record snapshots when changed useEffect(() => { - const snapshotSubscription = actorRef.subscribe(() => { - const serializedSnapshot = JSON.stringify(actorRef.getPersistedSnapshot()) + const snapshotSubscription = actorRef.subscribe((snap) => { + const persistedSnapshot = actorRef.getPersistedSnapshot() + const serializedSnapshot = JSON.stringify(persistedSnapshot) localStorage.setItem("snapshot", serializedSnapshot) + + // check for no-save tag before + if (snap.hasTag("no-save")) { + return + } + + actorRef.getSnapshot().can({ type: "detour.save.begin-save" }) && + actorRef.send({ type: "detour.save.begin-save" }) + + putDetourUpdate(persistedSnapshot).then((uuid) => { + if ( + isOk(uuid) && + actorRef + .getSnapshot() + .can({ type: "detour.save.set-uuid", uuid: uuid.ok }) + ) { + actorRef.send({ type: "detour.save.set-uuid", uuid: uuid.ok }) + } + }) }) return () => { diff --git a/assets/src/models/createDetourMachine.ts b/assets/src/models/createDetourMachine.ts index da49e9353..f19b1a5bb 100644 --- a/assets/src/models/createDetourMachine.ts +++ b/assets/src/models/createDetourMachine.ts @@ -13,6 +13,7 @@ import { DetourShape, FinishedDetour } from "./detour" export const createDetourMachine = setup({ types: { context: {} as { + uuid: number | undefined route?: Route routePattern?: RoutePattern @@ -61,7 +62,19 @@ export const createDetourMachine = setup({ | { type: "detour.edit.undo" } | { type: "detour.share.copy-detour"; detourText: string } | { type: "detour.share.activate" } - | { type: "detour.active.deactivate" }, + | { type: "detour.active.deactivate" } + | { type: "detour.save.begin-save" } + | { type: "detour.save.set-uuid"; uuid: number }, + + // We're making an assumption that we'll never want to save detour edits to the database when in particular stages + // of detour drafting: + // -- when starting a detour, before any user input + // -- when the route id / route pattern is getting selected + // -- right after the route pattern is finalized, before any waypoints are added + // That leads to the following interface: if the user begins drafting a detour, adds waypoints, and then changes the route, + // the database will reflect the old route and old waypoints up until the point where a new waypoint is added. + // If that UX assumption isn't the right one, we can iterate in the future! + tags: "no-save", }, actors: { "fetch-route-patterns": fromPromise< @@ -161,18 +174,20 @@ export const createDetourMachine = setup({ context: ({ input }) => ({ ...input, waypoints: [], + uuid: undefined, startPoint: undefined, endPoint: undefined, finishedDetour: undefined, detourShape: undefined, }), - + type: "parallel", initial: "Detour Drawing", states: { "Detour Drawing": { initial: "Begin", states: { Begin: { + tags: "no-save", always: [ { guard: ({ context }) => @@ -185,6 +200,7 @@ export const createDetourMachine = setup({ }, "Pick Route Pattern": { initial: "Pick Route ID", + tags: "no-save", on: { "detour.route-pattern.select-route": { target: ".Pick Route ID", @@ -295,6 +311,7 @@ export const createDetourMachine = setup({ }, states: { "Pick Start Point": { + tags: "no-save", on: { "detour.edit.place-waypoint-on-route": { target: "Place Waypoint", @@ -428,6 +445,30 @@ export const createDetourMachine = setup({ Past: {}, }, }, + SaveState: { + initial: "Unsaved", + states: { + Unsaved: { + on: { + "detour.save.begin-save": { + target: "Saving", + }, + }, + }, + Saving: { + tags: "no-save", + on: { + "detour.save.set-uuid": { + target: "Saved", + actions: assign({ + uuid: ({ event }) => event.uuid, + }), + }, + }, + }, + Saved: {}, + }, + }, }, }) diff --git a/assets/tests/api.test.ts b/assets/tests/api.test.ts index 8acb5723c..430e6bb16 100644 --- a/assets/tests/api.test.ts +++ b/assets/tests/api.test.ts @@ -29,6 +29,7 @@ import { fetchAllStops, fetchFinishedDetour, apiCallWithError, + putDetourUpdate, } from "../src/api" import routeFactory from "./factories/route" import routeTabFactory from "./factories/routeTab" @@ -50,6 +51,7 @@ import stopDataFactory from "./factories/stopData" import { shapePointFactory } from "./factories/shapePointFactory" import { ok, fetchError } from "../src/util/fetchResult" import { directionsFactory } from "./factories/detourShapeFactory" +import { createActor, createMachine } from "xstate" jest.mock("@sentry/react", () => ({ __esModule: true, @@ -67,6 +69,7 @@ const mockFetch = (status: number, json: any): void => { Promise.resolve({ json: () => json, status, + ok: status == 200 ? true : false, } as Response) ) } @@ -1122,3 +1125,39 @@ describe("putRouteTabs", () => { expect(args!.body).toEqual(JSON.stringify({ route_tabs: routeTabs })) }) }) + +describe("putDetourUpdate", () => { + const testToggleMachine = createMachine({ + id: "testToggle", + initial: "inactive", + states: { + inactive: { + on: { + TOGGLE: "active", + }, + }, + active: { + on: { + TOGGLE: "inactive", + }, + }, + }, + }) + const actor = createActor(testToggleMachine).start() + const persistedSnapshot = actor.getPersistedSnapshot() + + test("uses PUT and CSRF token", () => { + mockFetch(200, { data: 1 }) + putDetourUpdate(persistedSnapshot) + expect(window.fetch).toHaveBeenCalledTimes(1) + expect(jest.mocked(window.fetch)).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ + method: "PUT", + headers: expect.objectContaining({ + "x-csrf-token": expect.any(String), + }), + }) + ) + }) +}) diff --git a/assets/tests/components/detours/diversionPage.saveDetour.test.tsx b/assets/tests/components/detours/diversionPage.saveDetour.test.tsx new file mode 100644 index 000000000..cac80d642 --- /dev/null +++ b/assets/tests/components/detours/diversionPage.saveDetour.test.tsx @@ -0,0 +1,140 @@ +import { jest, describe, test, expect, beforeEach } from "@jest/globals" +import { act, fireEvent, render, screen, waitFor } from "@testing-library/react" +import React from "react" +import "@testing-library/jest-dom/jest-globals" +import { fetchRoutePatterns, putDetourUpdate } from "../../../src/api" +import { + DiversionPage as DiversionPageDefault, + DiversionPageProps, +} from "../../../src/components/detours/diversionPage" +import userEvent from "@testing-library/user-event" +import { originalRouteShape } from "../../testHelpers/selectors/components/detours/diversionPage" +import { Ok } from "../../../src/util/result" +import { neverPromise } from "../../testHelpers/mockHelpers" + +import { originalRouteFactory } from "../../factories/originalRouteFactory" +import getTestGroups from "../../../src/userTestGroups" +import { routePatternFactory } from "../../factories/routePattern" +import { RoutesProvider } from "../../../src/contexts/routesContext" +import routeFactory from "../../factories/route" + +const DiversionPage = (props: Partial) => { + return ( + + ) +} + +jest.mock("../../../src/api") +jest.mock("../../../src/userTestGroups") + +beforeEach(() => { + jest.mocked(fetchRoutePatterns).mockReturnValue(neverPromise()) + jest.mocked(getTestGroups).mockReturnValue([]) + jest.mocked(putDetourUpdate).mockReturnValue(neverPromise()) +}) + +describe("DiversionPage autosave flow", () => { + test("calls putDetourUpdate when the first waypoint is added", async () => { + const { container } = render() + + act(() => { + fireEvent.click(originalRouteShape.get(container)) + }) + + act(() => { + fireEvent.click(container.querySelector(".c-vehicle-map")!) + }) + + await waitFor(() => { + expect(putDetourUpdate).toHaveBeenCalledTimes(1) + }) + }) + + test("calls putDetourUpdate multiple times when a waypoint is added and then the detour is finished", async () => { + jest.mocked(putDetourUpdate).mockResolvedValue(Ok(12)) + + const { container } = render() + + act(() => { + fireEvent.click(originalRouteShape.get(container)) + }) + + act(() => { + fireEvent.click(container.querySelector(".c-vehicle-map")!) + }) + + act(() => { + fireEvent.click(originalRouteShape.get(container)) + }) + + await waitFor(() => { + expect(putDetourUpdate).toHaveBeenCalledTimes(3) + }) + }) + + // We made an assumption that we'll never want to save detour edits in response to changing route/variant + test("when finish button is clicked, does not call API to update the database", async () => { + const route = routeFactory.build({ id: "66" }) + const routePatterns = routePatternFactory.buildList(2, { + routeId: route.id, + }) + const [rp1] = routePatterns + + render( + + + + ) + + await userEvent.click( + screen.getByRole("button", { name: "Change route or direction" }) + ) + + await userEvent.click( + screen.getByRole("button", { name: "Start drawing detour" }) + ) + + await waitFor(() => { + expect(putDetourUpdate).toHaveBeenCalledTimes(0) + }) + }) + + // We made an assumption that we'll never want to save detour edits in response to changing route/variant + test("when route is changed, does not call API to update the database", async () => { + const routes = routeFactory.buildList(2) + const [route1, route2] = routes + + render( + + + + ) + + await userEvent.click( + screen.getByRole("button", { name: "Change route or direction" }) + ) + + await userEvent.selectOptions( + screen.getByRole("combobox", { name: "Choose route" }), + route2.name + ) + + await waitFor(() => { + expect(putDetourUpdate).toHaveBeenCalledTimes(0) + }) + }) +}) diff --git a/assets/tests/components/detours/diversionPage.test.tsx b/assets/tests/components/detours/diversionPage.test.tsx index d731eb1f0..05738784c 100644 --- a/assets/tests/components/detours/diversionPage.test.tsx +++ b/assets/tests/components/detours/diversionPage.test.tsx @@ -16,6 +16,7 @@ import { fetchNearestIntersection, fetchRoutePatterns, fetchUnfinishedDetour, + putDetourUpdate, } from "../../../src/api" import { DiversionPage as DiversionPageDefault, @@ -81,6 +82,7 @@ beforeEach(() => { jest.mocked(fetchNearestIntersection).mockReturnValue(neverPromise()) jest.mocked(fetchRoutePatterns).mockReturnValue(neverPromise()) jest.mocked(getTestGroups).mockReturnValue([]) + jest.mocked(putDetourUpdate).mockReturnValue(neverPromise()) }) describe("DiversionPage", () => { diff --git a/assets/tests/components/detours/diversionPageActivate.test.tsx b/assets/tests/components/detours/diversionPageActivate.test.tsx index 6341520dc..e22bffa28 100644 --- a/assets/tests/components/detours/diversionPageActivate.test.tsx +++ b/assets/tests/components/detours/diversionPageActivate.test.tsx @@ -21,6 +21,7 @@ import { fetchNearestIntersection, fetchRoutePatterns, fetchUnfinishedDetour, + putDetourUpdate, } from "../../../src/api" import { neverPromise } from "../../testHelpers/mockHelpers" @@ -45,6 +46,7 @@ beforeEach(() => { jest.mocked(fetchFinishedDetour).mockReturnValue(neverPromise()) jest.mocked(fetchNearestIntersection).mockReturnValue(neverPromise()) jest.mocked(fetchRoutePatterns).mockReturnValue(neverPromise()) + jest.mocked(putDetourUpdate).mockReturnValue(neverPromise()) jest .mocked(getTestGroups) diff --git a/assets/tests/hooks/useDetour.test.ts b/assets/tests/hooks/useDetour.test.ts index cfe08311d..a02b71301 100644 --- a/assets/tests/hooks/useDetour.test.ts +++ b/assets/tests/hooks/useDetour.test.ts @@ -4,6 +4,7 @@ import { fetchFinishedDetour, fetchNearestIntersection, fetchUnfinishedDetour, + putDetourUpdate, } from "../../src/api" import { renderHook, waitFor } from "@testing-library/react" import { useDetour } from "../../src/hooks/useDetour" @@ -24,14 +25,12 @@ jest.mock("../../src/api") beforeEach(() => { jest.mocked(fetchDetourDirections).mockReturnValue(new Promise(() => {})) - jest .mocked(fetchFinishedDetour) .mockResolvedValue(finishedDetourFactory.build()) - jest.mocked(fetchUnfinishedDetour).mockReturnValue(neverPromise()) - jest.mocked(fetchNearestIntersection).mockReturnValue(new Promise(() => {})) + jest.mocked(putDetourUpdate).mockReturnValue(neverPromise()) }) const renderDetourHook = () => diff --git a/lib/skate/detours/detours.ex b/lib/skate/detours/detours.ex index 9371521fa..74ef5f9f9 100644 --- a/lib/skate/detours/detours.ex +++ b/lib/skate/detours/detours.ex @@ -7,6 +7,7 @@ defmodule Skate.Detours.Detours do alias Skate.Repo alias Skate.Detours.Db.Detour + alias Skate.Settings.User @doc """ Returns the list of detours. @@ -55,6 +56,39 @@ defmodule Skate.Detours.Detours do |> Repo.insert() end + @doc """ + Creates a detour given a user id & detour id. + """ + def create_detour_for_user(user_id, attrs \\ %{}) do + user = User.get_by_id!(user_id) + + %Detour{ + author: user + } + |> Detour.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Update or create a detour given a user id & detour id. + """ + def update_or_create_detour_for_user(user_id, uuid, attrs \\ %{}) do + user = User.get_by_id!(user_id) + + case uuid do + nil -> + create_detour_for_user(user_id, attrs) + + _ -> + Repo.insert( + Detour.changeset(%Detour{author: user, id: uuid}, attrs), + returning: true, + conflict_target: [:id], + on_conflict: {:replace, [:state, :updated_at]} + ) + end + end + @doc """ Updates a detour. diff --git a/lib/skate_web/controllers/detours_controller.ex b/lib/skate_web/controllers/detours_controller.ex index ac5b6a1d7..b28758cb1 100644 --- a/lib/skate_web/controllers/detours_controller.ex +++ b/lib/skate_web/controllers/detours_controller.ex @@ -4,10 +4,26 @@ defmodule SkateWeb.DetoursController do alias Skate.OpenRouteServiceAPI use SkateWeb, :controller + alias Skate.Detours.Detours alias Skate.Detours.MissedStops alias Skate.Detours.RouteSegments + alias SkateWeb.AuthManager alias Util.Location + @spec update_snapshot(Plug.Conn.t(), map()) :: Plug.Conn.t() + def update_snapshot(conn, %{"snapshot" => %{"context" => context} = snapshot}) do + uuid = Map.get(context, "uuid") + + %{id: user_id} = AuthManager.Plug.current_resource(conn) + + {:ok, %Skate.Detours.Db.Detour{id: returned_uuid}} = + Detours.update_or_create_detour_for_user(user_id, uuid, %{ + state: snapshot + }) + + json(conn, %{data: returned_uuid}) + end + @spec unfinished_detour(Plug.Conn.t(), map()) :: Plug.Conn.t() def unfinished_detour(conn, %{ "route_pattern_id" => route_pattern_id, diff --git a/lib/skate_web/router.ex b/lib/skate_web/router.ex index b4b5e5b4b..71255cb63 100644 --- a/lib/skate_web/router.ex +++ b/lib/skate_web/router.ex @@ -160,6 +160,7 @@ defmodule SkateWeb.Router do get "/location_search/search", LocationSearchController, :search get "/location_search/suggest", LocationSearchController, :suggest post "/detours/directions/", DetourRouteController, :directions + put "/detours/update_snapshot", DetoursController, :update_snapshot post "/detours/unfinished_detour", DetoursController, :unfinished_detour post "/detours/finished_detour", DetoursController, :finished_detour end diff --git a/test/skate_web/controllers/detours_controller_test.exs b/test/skate_web/controllers/detours_controller_test.exs index 88623cb67..b3cfc5eb6 100644 --- a/test/skate_web/controllers/detours_controller_test.exs +++ b/test/skate_web/controllers/detours_controller_test.exs @@ -8,6 +8,7 @@ defmodule SkateWeb.DetoursControllerTest do import Mox import Skate.Factory + alias Skate.Detours.Detours alias Skate.Detours.MissedStops setup do @@ -25,6 +26,38 @@ defmodule SkateWeb.DetoursControllerTest do setup :verify_on_exit! + describe "update_snapshot/2" do + @tag :authenticated + test "adds new detour to database", %{conn: conn} do + conn = + put(conn, "/api/detours/update_snapshot", %{ + "snapshot" => %{"context" => %{}} + }) + + assert %{"data" => number} = json_response(conn, 200) + + assert %Skate.Detours.Db.Detour{ + id: ^number, + state: %{"context" => %{}} + } = Detours.get_detour!(number) + end + + @tag :authenticated + test "updates detour in database if detour uuid provided", %{conn: conn} do + conn = + put(conn, "/api/detours/update_snapshot", %{ + "snapshot" => %{"context" => %{"uuid" => 8}} + }) + + assert %{"data" => 8} = json_response(conn, 200) + + assert %Skate.Detours.Db.Detour{ + id: 8, + state: %{"context" => %{"uuid" => 8}} + } = Detours.get_detour!(8) + end + end + describe "unfinished_detour/2" do @tag :authenticated test "returns unfinished route segments", %{conn: conn} do From 84d2d3cfd4011ae5bc37110cac59428201336b55 Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Mon, 26 Aug 2024 13:07:59 -0400 Subject: [PATCH 26/38] doc: document api calls and deprecate old api calls (#2747) * doc: document api calls * doc: document intentions of the checked api call function * doc: deprecate old api calls --- assets/src/api.ts | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/assets/src/api.ts b/assets/src/api.ts index f9ea1e787..55a780053 100644 --- a/assets/src/api.ts +++ b/assets/src/api.ts @@ -92,6 +92,15 @@ const checkResponseStatus = (response: Response) => { const parseJson = (response: Response) => response.json() as unknown +/** + * @depcreated use {@linkcode apiCallResult} + * + * A small wrapper around fetch which checks for valid responses and parses + * JSON from the result body. It processes the resulting object with + * {@linkcode parser} + * + * If there is _any_ error, returns {@linkcode defaultResult}. + */ export const apiCall = ({ url, parser, @@ -109,6 +118,17 @@ export const apiCall = ({ .then(({ data: data }: { data: any }) => parser(data)) .catch(() => defaultResult) +/** + * @depcreated use {@linkcode apiCallResult} + * + * A slightly larger (than {@linkcode apiCall}) wrapper around + * {@linkcode fetch} which checks for valid responses and then parses JSON from + * the body, and asserts it's validity with `superstruct`. + * + * It then transforms the input with {@linkcode parser} + * + * If there are any errors, returns {@linkcode defaultResult} + */ export const checkedApiCall = ({ url, dataStruct, @@ -137,6 +157,14 @@ export const checkedApiCall = ({ return defaultResult }) +/** + * @depcreated use {@linkcode apiCallResult} + * + * A wrapper around {@linkcode fetch} which returns a {@linkcode FetchResult}. + * + * This does mainly the same thing as {@linkcode checkedApiCall} but returns + * errors and successes separately using {@linkcode FetchResult}. + */ export const apiCallWithError = ({ url, dataStruct, @@ -163,6 +191,30 @@ export const apiCallWithError = ({ return fetchError() }) +/** + * A wrapper around {@linkcode fetch} which returns a {@linkcode Result}. + * + * This function returns a {@linkcode Result} so that it's easy to differentiate + * between different error states. + * + * For example, previous implementations, + * e.g. {@linkcode checkedApiCall} and {@linkcode apiCall}, provided a + * `defaultResult` parameter which was returned if there was _any_ issue; + * If a successful {@linkcode fetch} to an endpoint _also_ returned the same + * value as `defaultResult`, say `null`, then there isn't a way to tell if the + * `null` was because of an error or because of a successful request. + * This _also_ happened if there was an unrelated error when fetching, so there + * was not an easy way to tell the difference between an errored endpoint or an + * errored fetch call. + * + * Diverging from {@linkcode apiCall}, this does not handle errors such as + * network issues and deals only with json response bodies. That is left up to + * the caller to add a `.catch` handler to the returned {@linkcode Promise}, + * because there may not be a generic way that those kind of errors should be + * handled. (This API could be opinionated or extended to return something like + * `Promise, FetchError>>`, but instead that is left up to + * callers to implement instead of assuming any requirements. + */ export const apiCallResult = async ( url: Parameters[0], OkStruct: Struct, @@ -171,8 +223,13 @@ export const apiCallResult = async ( ): Promise> => fetch(url, requestInit) .then(async (response) => { + // If the fetch does not error and returns something from the endpoint, + // parse as json. const json: unknown = await response.json() + // Then check if the response is `ok` and try to return `Ok(OkStruct)` + // Otherwise, return `Err(ErrStruct)` and attempt to return the data + // according to JSONAPI specifications if (response.ok && is(json, object({ data: any() }))) { return Ok(create(json.data, OkStruct)) } else { From 445884d91d766641b165a0a02af345f13696929a Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Tue, 27 Aug 2024 16:19:17 -0400 Subject: [PATCH 27/38] refactor: Explicitly export vehicleDataFactory (#2749) --- assets/tests/factories/vehicle_data.ts | 164 +++++++++--------- .../hooks/useAutocompleteResults.test.ts | 2 +- .../tests/hooks/usePullbackVehicles.test.ts | 2 +- assets/tests/hooks/useSearchResults.test.ts | 2 +- assets/tests/hooks/useShuttleVehicles.test.ts | 2 +- assets/tests/hooks/useVehicleForId.test.ts | 2 +- .../hooks/useVehicleForNotification.test.tsx | 2 +- assets/tests/hooks/useVehicles.test.ts | 2 +- .../hooks/useVehiclesForBlockIds.test.ts | 2 +- .../tests/hooks/useVehiclesForRunIds.test.ts | 2 +- assets/tests/models/vehicleData.test.ts | 2 +- 11 files changed, 93 insertions(+), 91 deletions(-) diff --git a/assets/tests/factories/vehicle_data.ts b/assets/tests/factories/vehicle_data.ts index 4e1e06092..a9f1c701d 100644 --- a/assets/tests/factories/vehicle_data.ts +++ b/assets/tests/factories/vehicle_data.ts @@ -1,90 +1,92 @@ import { Factory } from "fishery" import { VehicleData } from "../../src/models/vehicleData" -export default Factory.define(({ sequence }) => ({ - id: `v${sequence}`, - label: `v${sequence}-label`, - run_id: `run-${sequence}`, - timestamp: 123, - latitude: 0, - longitude: 0, - direction_id: 0, - route_id: "39", - route_pattern_id: "39-_-0", - trip_id: `t${sequence}`, - headsign: "Forest Hills", - via_variant: "X", - operator_id: `op${sequence}`, - operator_first_name: "WILL", - operator_last_name: "SMITH", - operator_logon_time: Math.floor( - new Date("2018-08-15T13:38:21.000Z").getTime() / 1000 - ), - bearing: 33, - block_id: `block-${sequence}`, - previous_vehicle_id: `v${sequence + 1}`, - schedule_adherence_secs: 0, - incoming_trip_direction_id: null, - is_shuttle: false, - is_overload: false, - is_off_course: false, - is_revenue: true, - layover_departure_time: null, - pull_back_place_name: "Garage", - overload_offset: null, - sources: [], - data_discrepancies: [ - { - attribute: "trip_id", - sources: [ - { - id: "swiftly", - value: "swiftly-trip-id", - }, - { - id: "busloc", - value: "busloc-trip-id", - }, - ], - }, - { - attribute: "route_id", - sources: [ - { - id: "swiftly", - value: null, - }, - { - id: "busloc", - value: "busloc-route-id", - }, - ], - }, - ], - stop_status: { - stop_id: "s1", - stop_name: "Stop Name", - }, - timepoint_status: { - timepoint_id: "tp1", - fraction_until_timepoint: 0.5, - }, - scheduled_location: { +export const vehicleDataFactory = Factory.define( + ({ sequence }) => ({ + id: `v${sequence}`, + label: `v${sequence}-label`, + run_id: `run-${sequence}`, + timestamp: 123, + latitude: 0, + longitude: 0, + direction_id: 0, route_id: "39", route_pattern_id: "39-_-0", - direction_id: 0, - trip_id: "scheduled trip", - run_id: "scheduled run", - time_since_trip_start_time: 0, - headsign: "scheduled headsign", - via_variant: "scheduled via variant", + trip_id: `t${sequence}`, + headsign: "Forest Hills", + via_variant: "X", + operator_id: `op${sequence}`, + operator_first_name: "WILL", + operator_last_name: "SMITH", + operator_logon_time: Math.floor( + new Date("2018-08-15T13:38:21.000Z").getTime() / 1000 + ), + bearing: 33, + block_id: `block-${sequence}`, + previous_vehicle_id: `v${sequence + 1}`, + schedule_adherence_secs: 0, + incoming_trip_direction_id: null, + is_shuttle: false, + is_overload: false, + is_off_course: false, + is_revenue: true, + layover_departure_time: null, + pull_back_place_name: "Garage", + overload_offset: null, + sources: [], + data_discrepancies: [ + { + attribute: "trip_id", + sources: [ + { + id: "swiftly", + value: "swiftly-trip-id", + }, + { + id: "busloc", + value: "busloc-trip-id", + }, + ], + }, + { + attribute: "route_id", + sources: [ + { + id: "swiftly", + value: null, + }, + { + id: "busloc", + value: "busloc-route-id", + }, + ], + }, + ], + stop_status: { + stop_id: "s1", + stop_name: "Stop Name", + }, timepoint_status: { timepoint_id: "tp1", fraction_until_timepoint: 0.5, }, - }, - route_status: "on_route", - end_of_trip_type: "another_trip", - block_waivers: [], - crowding: null, -})) + scheduled_location: { + route_id: "39", + route_pattern_id: "39-_-0", + direction_id: 0, + trip_id: "scheduled trip", + run_id: "scheduled run", + time_since_trip_start_time: 0, + headsign: "scheduled headsign", + via_variant: "scheduled via variant", + timepoint_status: { + timepoint_id: "tp1", + fraction_until_timepoint: 0.5, + }, + }, + route_status: "on_route", + end_of_trip_type: "another_trip", + block_waivers: [], + crowding: null, + }) +) diff --git a/assets/tests/hooks/useAutocompleteResults.test.ts b/assets/tests/hooks/useAutocompleteResults.test.ts index e263e53bb..6738c6db5 100644 --- a/assets/tests/hooks/useAutocompleteResults.test.ts +++ b/assets/tests/hooks/useAutocompleteResults.test.ts @@ -2,7 +2,7 @@ import { describe, test, expect } from "@jest/globals" import { useAutocompleteResults } from "../../src/hooks/useAutocompleteResults" import { renderHook } from "@testing-library/react" import { makeMockSocket, makeMockChannel } from "../testHelpers/socketHelpers" -import vehicleDataFactory from "../factories/vehicle_data" +import { vehicleDataFactory } from "../factories/vehicle_data" import { searchFiltersFactory } from "../factories/searchProperties" describe("useAutocompleteResults", () => { diff --git a/assets/tests/hooks/usePullbackVehicles.test.ts b/assets/tests/hooks/usePullbackVehicles.test.ts index 006d1b4b8..8debed1cf 100644 --- a/assets/tests/hooks/usePullbackVehicles.test.ts +++ b/assets/tests/hooks/usePullbackVehicles.test.ts @@ -3,7 +3,7 @@ import { renderHook } from "@testing-library/react" import usePullbackVehicles from "../../src/hooks/usePullbackVehicles" import { Vehicle } from "../../src/realtime.d" import { makeMockChannel, makeMockSocket } from "../testHelpers/socketHelpers" -import vehicleDataFactory from "../factories/vehicle_data" +import { vehicleDataFactory } from "../factories/vehicle_data" import { vehicleFromData } from "../../src/models/vehicleData" const pullBackVehicle = vehicleDataFactory.build({ diff --git a/assets/tests/hooks/useSearchResults.test.ts b/assets/tests/hooks/useSearchResults.test.ts index 8ba7912d3..c86c80c25 100644 --- a/assets/tests/hooks/useSearchResults.test.ts +++ b/assets/tests/hooks/useSearchResults.test.ts @@ -5,7 +5,7 @@ import { VehiclePropertyQuery } from "../../src/models/searchQuery" import { VehicleData, vehicleFromData } from "../../src/models/vehicleData" import { makeMockChannel, makeMockSocket } from "../testHelpers/socketHelpers" -import vehicleDataFactory from "../factories/vehicle_data" +import { vehicleDataFactory } from "../factories/vehicle_data" describe("useSearchResults", () => { test("when query given and loading, returns loading", () => { diff --git a/assets/tests/hooks/useShuttleVehicles.test.ts b/assets/tests/hooks/useShuttleVehicles.test.ts index ae677e5c5..2751c1737 100644 --- a/assets/tests/hooks/useShuttleVehicles.test.ts +++ b/assets/tests/hooks/useShuttleVehicles.test.ts @@ -3,7 +3,7 @@ import { renderHook } from "@testing-library/react" import useShuttleVehicles from "../../src/hooks/useShuttleVehicles" import { Vehicle } from "../../src/realtime.d" import { makeMockChannel, makeMockSocket } from "../testHelpers/socketHelpers" -import vehicleDataFactory from "../factories/vehicle_data" +import { vehicleDataFactory } from "../factories/vehicle_data" import { vehicleFromData } from "../../src/models/vehicleData" const shuttle = vehicleDataFactory.build({ is_shuttle: true }) diff --git a/assets/tests/hooks/useVehicleForId.test.ts b/assets/tests/hooks/useVehicleForId.test.ts index 5274a99a1..afcb04b38 100644 --- a/assets/tests/hooks/useVehicleForId.test.ts +++ b/assets/tests/hooks/useVehicleForId.test.ts @@ -3,7 +3,7 @@ import { makeMockOneShotChannel, makeMockSocket, } from "../testHelpers/socketHelpers" -import vehicleDataFactory from "../factories/vehicle_data" +import { vehicleDataFactory } from "../factories/vehicle_data" import { renderHook } from "@testing-library/react" import useVehicleForId from "../../src/hooks/useVehicleForId" diff --git a/assets/tests/hooks/useVehicleForNotification.test.tsx b/assets/tests/hooks/useVehicleForNotification.test.tsx index 279031fc5..daeaa715a 100644 --- a/assets/tests/hooks/useVehicleForNotification.test.tsx +++ b/assets/tests/hooks/useVehicleForNotification.test.tsx @@ -16,7 +16,7 @@ import { makeMockOneShotChannel, makeMockSocket, } from "../testHelpers/socketHelpers" -import vehicleDataFactory from "../factories/vehicle_data" +import { vehicleDataFactory } from "../factories/vehicle_data" import ghostDataFactory from "../factories/ghost_data" import { tagManagerEvent } from "../../src/helpers/googleTagManager" import { fullStoryEvent } from "../../src/helpers/fullStory" diff --git a/assets/tests/hooks/useVehicles.test.ts b/assets/tests/hooks/useVehicles.test.ts index 99f962ead..ea679a448 100644 --- a/assets/tests/hooks/useVehicles.test.ts +++ b/assets/tests/hooks/useVehicles.test.ts @@ -7,7 +7,7 @@ import { Ghost, Vehicle } from "../../src/realtime.d" import { RouteId } from "../../src/schedule.d" import { makeMockChannel, makeMockSocket } from "../testHelpers/socketHelpers" import ghostFactory from "../factories/ghost" -import vehicleDataFactory from "../factories/vehicle_data" +import { vehicleDataFactory } from "../factories/vehicle_data" import ghostDataFactory from "../factories/ghost_data" import * as Sentry from "@sentry/react" diff --git a/assets/tests/hooks/useVehiclesForBlockIds.test.ts b/assets/tests/hooks/useVehiclesForBlockIds.test.ts index 488f09ba3..0bbae3d7b 100644 --- a/assets/tests/hooks/useVehiclesForBlockIds.test.ts +++ b/assets/tests/hooks/useVehiclesForBlockIds.test.ts @@ -3,7 +3,7 @@ import { renderHook } from "@testing-library/react" import useVehicleForBlockIds from "../../src/hooks/useVehiclesForBlockIds" import { vehicleFromData } from "../../src/models/vehicleData" import { makeMockChannel, makeMockSocket } from "../testHelpers/socketHelpers" -import vehicleDataFactory from "../factories/vehicle_data" +import { vehicleDataFactory } from "../factories/vehicle_data" describe("useVehiclesForBlockIds", () => { test("returns data", () => { diff --git a/assets/tests/hooks/useVehiclesForRunIds.test.ts b/assets/tests/hooks/useVehiclesForRunIds.test.ts index f8db4fcee..6d2cc2aac 100644 --- a/assets/tests/hooks/useVehiclesForRunIds.test.ts +++ b/assets/tests/hooks/useVehiclesForRunIds.test.ts @@ -3,7 +3,7 @@ import { renderHook } from "@testing-library/react" import useVehiclesForRunIds from "../../src/hooks/useVehiclesForRunIds" import { VehicleData, vehicleFromData } from "../../src/models/vehicleData" import { makeMockChannel, makeMockSocket } from "../testHelpers/socketHelpers" -import vehicleDataFactory from "../factories/vehicle_data" +import { vehicleDataFactory } from "../factories/vehicle_data" describe("useVehiclesForRunIds", () => { test("returns data", () => { diff --git a/assets/tests/models/vehicleData.test.ts b/assets/tests/models/vehicleData.test.ts index b7802ef88..f38ebd7a0 100644 --- a/assets/tests/models/vehicleData.test.ts +++ b/assets/tests/models/vehicleData.test.ts @@ -2,7 +2,7 @@ import { describe, test, expect } from "@jest/globals" import { ghostFromData, vehicleFromData } from "../../src/models/vehicleData" import { dateFromEpochSeconds } from "../../src/util/dateTime" import ghostDataFactory from "../factories/ghost_data" -import vehicleDataFactory from "../factories/vehicle_data" +import { vehicleDataFactory } from "../factories/vehicle_data" describe("vehicleFromData", () => { test("returns vehicle data in expected format", () => { From 49a640a157420b094d0f166d5bf0d12625293a75 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Tue, 27 Aug 2024 20:22:25 -0400 Subject: [PATCH 28/38] refactor: Move `fetchNearestIntersection` to the state machine (#2745) --- assets/src/hooks/useDetour.ts | 7 +-- assets/src/models/createDetourMachine.ts | 58 ++++++++++++++----- .../components/detours/diversionPage.test.tsx | 8 ++- 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/assets/src/hooks/useDetour.ts b/assets/src/hooks/useDetour.ts index b4f1cc62e..28b48b651 100644 --- a/assets/src/hooks/useDetour.ts +++ b/assets/src/hooks/useDetour.ts @@ -3,7 +3,6 @@ import { ShapePoint } from "../schedule" import { fetchUnfinishedDetour, putDetourUpdate } from "../api" import { useApiCall } from "./useApiCall" import { isErr, isOk } from "../util/result" -import { useNearestIntersection } from "./useNearestIntersection" import { useMachine } from "@xstate/react" import { CreateDetourMachineInput, @@ -64,6 +63,7 @@ export const useDetour = (input: UseDetourInput) => { waypoints, finishedDetour, detourShape, + nearestIntersection, } = snapshot.context const { result: unfinishedDetour } = useApiCall({ @@ -76,11 +76,6 @@ export const useDetour = (input: UseDetourInput) => { }, [startPoint, routePattern]), }) - const { result: nearestIntersection } = useNearestIntersection({ - latitude: startPoint?.lat, - longitude: startPoint?.lon, - }) - const coordinates = detourShape && isOk(detourShape) ? detourShape.ok.coordinates : [] diff --git a/assets/src/models/createDetourMachine.ts b/assets/src/models/createDetourMachine.ts index f19b1a5bb..b97779c00 100644 --- a/assets/src/models/createDetourMachine.ts +++ b/assets/src/models/createDetourMachine.ts @@ -6,6 +6,7 @@ import { FetchDetourDirectionsError, fetchDetourDirections, fetchFinishedDetour, + fetchNearestIntersection, fetchRoutePatterns, } from "../api" import { DetourShape, FinishedDetour } from "./detour" @@ -23,6 +24,8 @@ export const createDetourMachine = setup({ startPoint: ShapePoint | undefined endPoint: ShapePoint | undefined + nearestIntersection: string | null + detourShape: Result | undefined finishedDetour: FinishedDetour | undefined | null @@ -91,6 +94,18 @@ export const createDetourMachine = setup({ } }), + "fetch-nearest-intersection": fromPromise< + Awaited>, + { + startPoint?: ShapePoint + } + >(async ({ input: { startPoint } }) => { + if (!startPoint) { + throw "Missing nearest intersection inputs" + } + return fetchNearestIntersection(startPoint.lat, startPoint.lon) + }), + "fetch-detour-directions": fromPromise< Awaited>, { @@ -177,6 +192,7 @@ export const createDetourMachine = setup({ uuid: undefined, startPoint: undefined, endPoint: undefined, + nearestIntersection: null, finishedDetour: undefined, detourShape: undefined, }), @@ -325,22 +341,38 @@ export const createDetourMachine = setup({ }, }, "Place Waypoint": { - invoke: { - src: "fetch-detour-directions", - input: ({ context: { startPoint, waypoints } }) => ({ - points: (startPoint ? [startPoint] : []).concat( - waypoints || [] - ), - }), - - onDone: { - actions: assign({ - detourShape: ({ event }) => event.output, + invoke: [ + { + src: "fetch-nearest-intersection", + input: ({ context: { startPoint } }) => ({ + startPoint, }), + + onDone: { + actions: assign({ + nearestIntersection: ({ event }) => event.output, + }), + }, + + onError: {}, }, + { + src: "fetch-detour-directions", + input: ({ context: { startPoint, waypoints } }) => ({ + points: (startPoint ? [startPoint] : []).concat( + waypoints || [] + ), + }), - onError: {}, - }, + onDone: { + actions: assign({ + detourShape: ({ event }) => event.output, + }), + }, + + onError: {}, + }, + ], on: { "detour.edit.place-waypoint": { target: "Place Waypoint", diff --git a/assets/tests/components/detours/diversionPage.test.tsx b/assets/tests/components/detours/diversionPage.test.tsx index 05738784c..c7558c1c4 100644 --- a/assets/tests/components/detours/diversionPage.test.tsx +++ b/assets/tests/components/detours/diversionPage.test.tsx @@ -1259,8 +1259,8 @@ describe("DiversionPage", () => { }) ) - const intersection = "Avenue 1 & Street 2" - jest.mocked(fetchNearestIntersection).mockResolvedValue(intersection) + const intersectionPromise = Promise.resolve("Avenue 1 & Street 2") + jest.mocked(fetchNearestIntersection).mockReturnValue(intersectionPromise) userEvent.setup() // Configure the clipboard API @@ -1290,6 +1290,10 @@ describe("DiversionPage", () => { fireEvent.click(originalRouteShape.get(container)) }) + await act(async () => { + await intersectionPromise + }) + act(() => { fireEvent.click(originalRouteShape.get(container)) }) From f61b67d5d4dd96fd07c7baa0da05ee2938620dd0 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Wed, 28 Aug 2024 08:07:46 -0400 Subject: [PATCH 29/38] refactor: Explicitly export vehicleFactory (#2751) --- assets/tests/components/app.test.tsx | 2 +- assets/tests/components/groupedAutocomplete.test.tsx | 2 +- assets/tests/components/incomingBox.test.tsx | 2 +- assets/tests/components/ladder.test.tsx | 2 +- assets/tests/components/ladderPage.test.tsx | 2 +- assets/tests/components/lateView.test.tsx | 2 +- assets/tests/components/map.test.tsx | 2 +- assets/tests/components/mapMarkers.test.tsx | 2 +- assets/tests/components/mapPage.test.tsx | 3 ++- assets/tests/components/mapPage/mapDisplay.test.tsx | 3 ++- .../tests/components/mapPage/searchResultsByCategory.test.tsx | 2 +- .../tests/components/mapPage/vehiclePropertiesCard.test.tsx | 2 +- assets/tests/components/propertiesList.test.tsx | 2 +- assets/tests/components/propertiesPanel.test.tsx | 2 +- assets/tests/components/propertiesPanel/header.test.tsx | 2 +- assets/tests/components/propertiesPanel/miniMap.test.tsx | 2 +- assets/tests/components/propertiesPanel/minischedule.test.tsx | 2 +- .../propertiesPanel/staleDataPropertiesPanel.test.tsx | 2 +- assets/tests/components/propertiesPanel/tab_panels.test.tsx | 2 +- .../propertiesPanel/vehiclePropertiesPanel.test.tsx | 2 +- assets/tests/components/rightPanel.test.tsx | 2 +- assets/tests/components/routeLadder.test.tsx | 2 +- assets/tests/components/routeVariantName.test.tsx | 2 +- assets/tests/components/searchForm.test.tsx | 2 +- assets/tests/components/searchResults.test.tsx | 2 +- assets/tests/components/shuttlePicker.test.tsx | 2 +- assets/tests/components/swingsView.test.tsx | 2 +- assets/tests/components/vehicleRouteSummary.test.tsx | 3 ++- assets/tests/contexts/notificationsContext.test.tsx | 2 +- assets/tests/factories/pagePanelStateFactory.ts | 2 +- assets/tests/factories/vehicle.ts | 4 +--- assets/tests/helpers/vehicleLabel.test.ts | 2 +- assets/tests/hooks/useMostRecentVehicleById.test.ts | 2 +- assets/tests/hooks/usePanelState.test.tsx | 2 +- assets/tests/hooks/useSearchResultsByCategory.test.ts | 2 +- assets/tests/models/vehicle.test.ts | 2 +- assets/tests/models/vehicleStatus.test.ts | 2 +- assets/tests/models/vehiclesByPosition.test.ts | 2 +- assets/tests/state.test.ts | 2 +- assets/tests/state/pagePanelState.test.ts | 2 +- assets/tests/util/operatorFormatting.test.ts | 2 +- 41 files changed, 44 insertions(+), 43 deletions(-) diff --git a/assets/tests/components/app.test.tsx b/assets/tests/components/app.test.tsx index 00b1c3964..42f33a9d2 100644 --- a/assets/tests/components/app.test.tsx +++ b/assets/tests/components/app.test.tsx @@ -18,7 +18,7 @@ import { ConnectionStatus } from "../../src/hooks/useSocket" import { initialState } from "../../src/state" import routeTabFactory from "../factories/routeTab" import useVehicles from "../../src/hooks/useVehicles" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import { MemoryRouter } from "react-router-dom" import { vehiclePropertiesPanelHeader } from "../testHelpers/selectors/components/vehiclePropertiesPanel" import stateFactory from "../factories/applicationState" diff --git a/assets/tests/components/groupedAutocomplete.test.tsx b/assets/tests/components/groupedAutocomplete.test.tsx index 84b1d733c..c95a0cf5d 100644 --- a/assets/tests/components/groupedAutocomplete.test.tsx +++ b/assets/tests/components/groupedAutocomplete.test.tsx @@ -18,7 +18,7 @@ import { SearchProperties, searchPropertyDisplayConfig, } from "../../src/models/searchQuery" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import { formatOperatorNameFromVehicle } from "../../src/util/operatorFormatting" import { listbox, diff --git a/assets/tests/components/incomingBox.test.tsx b/assets/tests/components/incomingBox.test.tsx index 6ad935b98..f36c85615 100644 --- a/assets/tests/components/incomingBox.test.tsx +++ b/assets/tests/components/incomingBox.test.tsx @@ -7,7 +7,7 @@ import { LadderDirection } from "../../src/models/ladderDirection" import { Ghost, VehicleInScheduledService } from "../../src/realtime" import { initialState } from "../../src/state" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import ghostFactory from "../factories/ghost" import { render } from "@testing-library/react" import userEvent from "@testing-library/user-event" diff --git a/assets/tests/components/ladder.test.tsx b/assets/tests/components/ladder.test.tsx index 78772ec5d..717ef53ff 100644 --- a/assets/tests/components/ladder.test.tsx +++ b/assets/tests/components/ladder.test.tsx @@ -13,7 +13,7 @@ import { import { Timepoint } from "../../src/schedule.d" import { initialState } from "../../src/state" import * as dateTime from "../../src/util/dateTime" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import ghostFactory from "../factories/ghost" import { render } from "@testing-library/react" import userEvent from "@testing-library/user-event" diff --git a/assets/tests/components/ladderPage.test.tsx b/assets/tests/components/ladderPage.test.tsx index ed1bb2168..a0ff9c00c 100644 --- a/assets/tests/components/ladderPage.test.tsx +++ b/assets/tests/components/ladderPage.test.tsx @@ -25,7 +25,7 @@ import { import { tagManagerEvent } from "../../src/helpers/googleTagManager" import routeFactory from "../factories/route" import routeTabFactory, { routeTabPresetFactory } from "../factories/routeTab" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import userEvent from "@testing-library/user-event" import { VehiclesByRouteIdProvider } from "../../src/contexts/vehiclesByRouteIdContext" import stateFactory from "../factories/applicationState" diff --git a/assets/tests/components/lateView.test.tsx b/assets/tests/components/lateView.test.tsx index 8b61f5285..026155601 100644 --- a/assets/tests/components/lateView.test.tsx +++ b/assets/tests/components/lateView.test.tsx @@ -10,7 +10,7 @@ import { StateDispatchProvider } from "../../src/contexts/stateDispatchContext" import { VehiclesByRouteIdProvider } from "../../src/contexts/vehiclesByRouteIdContext" import { State } from "../../src/state" import blockWaiverFactory from "../factories/blockWaiver" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import ghostFactory from "../factories/ghost" import { tagManagerEvent } from "../../src/helpers/googleTagManager" import userEvent from "@testing-library/user-event" diff --git a/assets/tests/components/map.test.tsx b/assets/tests/components/map.test.tsx index e55ce8439..ec9be887d 100644 --- a/assets/tests/components/map.test.tsx +++ b/assets/tests/components/map.test.tsx @@ -21,7 +21,7 @@ import Map, { } from "../../src/components/map" import { autoCenter } from "../../src/components/map/follower" import { TrainVehicle, VehicleInScheduledService } from "../../src/realtime" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import stopFactory from "../factories/stop" import userEvent from "@testing-library/user-event" diff --git a/assets/tests/components/mapMarkers.test.tsx b/assets/tests/components/mapMarkers.test.tsx index d4f9340ac..e69b65ba8 100644 --- a/assets/tests/components/mapMarkers.test.tsx +++ b/assets/tests/components/mapMarkers.test.tsx @@ -10,7 +10,7 @@ import { VehicleMarker, } from "../../src/components/mapMarkers" import stopFactory from "../factories/stop" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import trainVehicleFactory from "../factories/trainVehicle" import { render, screen } from "@testing-library/react" import React from "react" diff --git a/assets/tests/components/mapPage.test.tsx b/assets/tests/components/mapPage.test.tsx index b9330a8f0..7c8145af0 100644 --- a/assets/tests/components/mapPage.test.tsx +++ b/assets/tests/components/mapPage.test.tsx @@ -42,7 +42,8 @@ import { searchQueryVehicleFactory, } from "../factories/searchQuery" import stopFactory from "../factories/stop" -import vehicleFactory, { +import { + vehicleFactory, randomLocationVehicle, shuttleFactory, } from "../factories/vehicle" diff --git a/assets/tests/components/mapPage/mapDisplay.test.tsx b/assets/tests/components/mapPage/mapDisplay.test.tsx index df58b6e6a..9e910ad62 100644 --- a/assets/tests/components/mapPage/mapDisplay.test.tsx +++ b/assets/tests/components/mapPage/mapDisplay.test.tsx @@ -28,7 +28,8 @@ import routeFactory from "../../factories/route" import { routePatternFactory } from "../../factories/routePattern" import { runIdFactory } from "../../factories/run" import stopFactory from "../../factories/stop" -import vehicleFactory, { +import { + vehicleFactory, randomLocationVehicle, shuttleFactory, } from "../../factories/vehicle" diff --git a/assets/tests/components/mapPage/searchResultsByCategory.test.tsx b/assets/tests/components/mapPage/searchResultsByCategory.test.tsx index 7c5ec84c3..75cf558cf 100644 --- a/assets/tests/components/mapPage/searchResultsByCategory.test.tsx +++ b/assets/tests/components/mapPage/searchResultsByCategory.test.tsx @@ -10,7 +10,7 @@ import React from "react" import { render, screen, within } from "@testing-library/react" import "@testing-library/jest-dom/jest-globals" import SearchResultsByCategory from "../../../src/components/mapPage/searchResultsByCategory" -import vehicleFactory from "../../factories/vehicle" +import { vehicleFactory } from "../../factories/vehicle" import { StateDispatchProvider } from "../../../src/contexts/stateDispatchContext" import stateFactory from "../../factories/applicationState" import { searchPageStateFactory } from "../../factories/searchPageState" diff --git a/assets/tests/components/mapPage/vehiclePropertiesCard.test.tsx b/assets/tests/components/mapPage/vehiclePropertiesCard.test.tsx index 2edbe2960..74dbecd19 100644 --- a/assets/tests/components/mapPage/vehiclePropertiesCard.test.tsx +++ b/assets/tests/components/mapPage/vehiclePropertiesCard.test.tsx @@ -3,7 +3,7 @@ import React from "react" import { render, screen, waitFor } from "@testing-library/react" import "@testing-library/jest-dom/jest-globals" import * as dateTime from "../../../src/util/dateTime" -import vehicleFactory, { shuttleFactory } from "../../factories/vehicle" +import { vehicleFactory, shuttleFactory } from "../../factories/vehicle" import routeFactory from "../../factories/route" import VehiclePropertiesCard from "../../../src/components/mapPage/vehiclePropertiesCard" import { useNearestIntersectionFetchResult } from "../../../src/hooks/useNearestIntersection" diff --git a/assets/tests/components/propertiesList.test.tsx b/assets/tests/components/propertiesList.test.tsx index 01885e611..67b8267f5 100644 --- a/assets/tests/components/propertiesList.test.tsx +++ b/assets/tests/components/propertiesList.test.tsx @@ -9,7 +9,7 @@ import PropertiesList, { } from "../../src/components/propertiesList" import { Ghost, VehicleInScheduledService } from "../../src/realtime" import * as dateTime from "../../src/util/dateTime" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import ghostFactory from "../factories/ghost" import { render } from "@testing-library/react" diff --git a/assets/tests/components/propertiesPanel.test.tsx b/assets/tests/components/propertiesPanel.test.tsx index bf80db00f..784891a17 100644 --- a/assets/tests/components/propertiesPanel.test.tsx +++ b/assets/tests/components/propertiesPanel.test.tsx @@ -17,7 +17,7 @@ import { RoutesProvider } from "../../src/contexts/routesContext" import { Ghost, Vehicle, VehicleInScheduledService } from "../../src/realtime" import { Route } from "../../src/schedule" import * as dateTime from "../../src/util/dateTime" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import ghostFactory from "../factories/ghost" import routeFactory from "../factories/route" import useVehicleForId from "../../src/hooks/useVehicleForId" diff --git a/assets/tests/components/propertiesPanel/header.test.tsx b/assets/tests/components/propertiesPanel/header.test.tsx index 81043b111..11420ce2f 100644 --- a/assets/tests/components/propertiesPanel/header.test.tsx +++ b/assets/tests/components/propertiesPanel/header.test.tsx @@ -18,7 +18,7 @@ import { } from "../../../src/realtime" import { Route } from "../../../src/schedule" import { initialState } from "../../../src/state" -import vehicleFactory from "../../factories/vehicle" +import { vehicleFactory } from "../../factories/vehicle" import ghostFactory from "../../factories/ghost" import routeFactory from "../../factories/route" import routeTabFactory from "../../factories/routeTab" diff --git a/assets/tests/components/propertiesPanel/miniMap.test.tsx b/assets/tests/components/propertiesPanel/miniMap.test.tsx index 8659d33c3..e53b363f6 100644 --- a/assets/tests/components/propertiesPanel/miniMap.test.tsx +++ b/assets/tests/components/propertiesPanel/miniMap.test.tsx @@ -8,7 +8,7 @@ import { } from "@jest/globals" import React from "react" import { VehicleInScheduledService } from "../../../src/realtime" -import vehicleFactory from "../../factories/vehicle" +import { vehicleFactory } from "../../factories/vehicle" import { render, screen } from "@testing-library/react" import "@testing-library/jest-dom/jest-globals" import MiniMap from "../../../src/components/propertiesPanel/miniMap" diff --git a/assets/tests/components/propertiesPanel/minischedule.test.tsx b/assets/tests/components/propertiesPanel/minischedule.test.tsx index 58b883cbf..232d6071a 100644 --- a/assets/tests/components/propertiesPanel/minischedule.test.tsx +++ b/assets/tests/components/propertiesPanel/minischedule.test.tsx @@ -21,7 +21,7 @@ import { initialState } from "../../../src/state" import pieceFactory from "../../factories/piece" import { RunFactory } from "../../factories/run" import { DeadheadTripFactory, TripFactory } from "../../factories/trip" -import vehicleFactory from "../../factories/vehicle" +import { vehicleFactory } from "../../factories/vehicle" import { mockUseStateOnce } from "../../testHelpers/mockHelpers" jest.mock("../../../src/hooks/useMinischedule", () => ({ diff --git a/assets/tests/components/propertiesPanel/staleDataPropertiesPanel.test.tsx b/assets/tests/components/propertiesPanel/staleDataPropertiesPanel.test.tsx index a8c72001f..5bd533210 100644 --- a/assets/tests/components/propertiesPanel/staleDataPropertiesPanel.test.tsx +++ b/assets/tests/components/propertiesPanel/staleDataPropertiesPanel.test.tsx @@ -2,7 +2,7 @@ import { describe, test, expect, jest } from "@jest/globals" import React from "react" import { render, screen } from "@testing-library/react" import StaleDataPropertiesPanel from "../../../src/components/propertiesPanel/staleDataPropertiesPanel" -import vehicleFactory from "../../factories/vehicle" +import { vehicleFactory } from "../../factories/vehicle" import blockWaiverFactory from "../../factories/blockWaiver" import { useMinischeduleBlock, diff --git a/assets/tests/components/propertiesPanel/tab_panels.test.tsx b/assets/tests/components/propertiesPanel/tab_panels.test.tsx index fea743d2a..6ca7fbd45 100644 --- a/assets/tests/components/propertiesPanel/tab_panels.test.tsx +++ b/assets/tests/components/propertiesPanel/tab_panels.test.tsx @@ -3,7 +3,7 @@ import React from "react" import { render } from "@testing-library/react" import TabPanels from "../../../src/components/propertiesPanel/tabPanels" import { VehicleInScheduledService } from "../../../src/realtime" -import vehicleFactory from "../../factories/vehicle" +import { vehicleFactory } from "../../factories/vehicle" const vehicle: VehicleInScheduledService = vehicleFactory.build({ id: "vehicleId", diff --git a/assets/tests/components/propertiesPanel/vehiclePropertiesPanel.test.tsx b/assets/tests/components/propertiesPanel/vehiclePropertiesPanel.test.tsx index 19b8b3287..5a8665604 100644 --- a/assets/tests/components/propertiesPanel/vehiclePropertiesPanel.test.tsx +++ b/assets/tests/components/propertiesPanel/vehiclePropertiesPanel.test.tsx @@ -17,7 +17,7 @@ import { } from "../../../src/realtime" import { Route } from "../../../src/schedule" import * as dateTime from "../../../src/util/dateTime" -import vehicleFactory, { invalidVehicleFactory } from "../../factories/vehicle" +import { vehicleFactory, invalidVehicleFactory } from "../../factories/vehicle" import { render, screen } from "@testing-library/react" import "@testing-library/jest-dom/jest-globals" import { TabMode } from "../../../src/components/propertiesPanel/tabPanels" diff --git a/assets/tests/components/rightPanel.test.tsx b/assets/tests/components/rightPanel.test.tsx index 0874cc21c..882959d0d 100644 --- a/assets/tests/components/rightPanel.test.tsx +++ b/assets/tests/components/rightPanel.test.tsx @@ -8,7 +8,7 @@ import RightPanel from "../../src/components/rightPanel" import * as dateTime from "../../src/util/dateTime" import ghostFactory from "../factories/ghost" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import { RunFactory } from "../factories/run" import { OpenView } from "../../src/state/pagePanelState" diff --git a/assets/tests/components/routeLadder.test.tsx b/assets/tests/components/routeLadder.test.tsx index 4af04b6ee..5714bb337 100644 --- a/assets/tests/components/routeLadder.test.tsx +++ b/assets/tests/components/routeLadder.test.tsx @@ -18,7 +18,7 @@ import { } from "../../src/realtime.d" import { Route } from "../../src/schedule.d" import { initialState } from "../../src/state" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import ghostFactory from "../factories/ghost" import routeFactory from "../factories/route" import userEvent from "@testing-library/user-event" diff --git a/assets/tests/components/routeVariantName.test.tsx b/assets/tests/components/routeVariantName.test.tsx index 92d304ab6..0117163f8 100644 --- a/assets/tests/components/routeVariantName.test.tsx +++ b/assets/tests/components/routeVariantName.test.tsx @@ -6,7 +6,7 @@ import routeFactory from "../factories/route" import { RouteVariantName } from "../../src/components/routeVariantName" import { RoutesProvider } from "../../src/contexts/routesContext" import { Route } from "../../src/schedule" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" describe("RouteVariantName", () => { test("renders for a vehicle with variant and headsign", () => { diff --git a/assets/tests/components/searchForm.test.tsx b/assets/tests/components/searchForm.test.tsx index b13f8594c..54dd88f54 100644 --- a/assets/tests/components/searchForm.test.tsx +++ b/assets/tests/components/searchForm.test.tsx @@ -36,7 +36,7 @@ import { option as autocompleteOption, } from "../testHelpers/selectors/components/groupedAutocomplete" import { useAutocompleteResults } from "../../src/hooks/useAutocompleteResults" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import { SearchPropertyQuery } from "../../src/models/searchQuery" import { formatOperatorName } from "../../src/util/operatorFormatting" import locationSearchSuggestionFactory from "../factories/locationSearchSuggestion" diff --git a/assets/tests/components/searchResults.test.tsx b/assets/tests/components/searchResults.test.tsx index 8224fe221..10823037b 100644 --- a/assets/tests/components/searchResults.test.tsx +++ b/assets/tests/components/searchResults.test.tsx @@ -15,7 +15,7 @@ import * as dateTime from "../../src/util/dateTime" import "@testing-library/jest-dom/jest-globals" import ghostFactory from "../factories/ghost" -import vehicleFactory, { shuttleFactory } from "../factories/vehicle" +import { vehicleFactory, shuttleFactory } from "../factories/vehicle" import { searchPageStateFactory } from "../factories/searchPageState" jest.mock("userTestGroups", () => ({ diff --git a/assets/tests/components/shuttlePicker.test.tsx b/assets/tests/components/shuttlePicker.test.tsx index adbd8eae2..70c1fe57c 100644 --- a/assets/tests/components/shuttlePicker.test.tsx +++ b/assets/tests/components/shuttlePicker.test.tsx @@ -18,7 +18,7 @@ import { selectShuttleRoute, selectShuttleRun, } from "../../src/state" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import { render } from "@testing-library/react" import userEvent from "@testing-library/user-event" diff --git a/assets/tests/components/swingsView.test.tsx b/assets/tests/components/swingsView.test.tsx index 0d2f234c3..c4b585d54 100644 --- a/assets/tests/components/swingsView.test.tsx +++ b/assets/tests/components/swingsView.test.tsx @@ -4,7 +4,7 @@ import { render } from "@testing-library/react" import "@testing-library/jest-dom/jest-globals" import renderer from "react-test-renderer" import ghostFactory from "../factories/ghost" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import routeFactory from "../factories/route" import swingFactory from "../factories/swing" import SwingsView from "../../src/components/swingsView" diff --git a/assets/tests/components/vehicleRouteSummary.test.tsx b/assets/tests/components/vehicleRouteSummary.test.tsx index a5d03294c..b3b5604c1 100644 --- a/assets/tests/components/vehicleRouteSummary.test.tsx +++ b/assets/tests/components/vehicleRouteSummary.test.tsx @@ -7,7 +7,8 @@ import { VehicleRouteSummary } from "../../src/components/vehicleRouteSummary" import { RoutesProvider } from "../../src/contexts/routesContext" import ghostFactory from "../factories/ghost" import routeFactory from "../factories/route" -import vehicleFactory, { +import { + vehicleFactory, invalidVehicleFactory, shuttleFactory, } from "../factories/vehicle" diff --git a/assets/tests/contexts/notificationsContext.test.tsx b/assets/tests/contexts/notificationsContext.test.tsx index 4a4f46142..b4a2458ff 100644 --- a/assets/tests/contexts/notificationsContext.test.tsx +++ b/assets/tests/contexts/notificationsContext.test.tsx @@ -10,7 +10,7 @@ import { StateDispatchProvider } from "../../src/contexts/stateDispatchContext" import useCurrentTime from "../../src/hooks/useCurrentTime" import { Notification, NotificationState } from "../../src/realtime.d" import { initialState } from "../../src/state" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import { tagManagerEvent } from "../../src/helpers/googleTagManager" import { useNotifications } from "../../src/hooks/useNotifications" import notificationFactory from "../factories/notification" diff --git a/assets/tests/factories/pagePanelStateFactory.ts b/assets/tests/factories/pagePanelStateFactory.ts index 8f66db59a..433ebc599 100644 --- a/assets/tests/factories/pagePanelStateFactory.ts +++ b/assets/tests/factories/pagePanelStateFactory.ts @@ -5,7 +5,7 @@ import { initialPageViewState, } from "../../src/state/pagePanelState" import { DeepPartial, Factory } from "fishery" -import vehicleFactory from "./vehicle" +import { vehicleFactory } from "./vehicle" class ViewFactory extends Factory { currentState(currentState: DeepPartial) { diff --git a/assets/tests/factories/vehicle.ts b/assets/tests/factories/vehicle.ts index 7cfd617b3..b23d3f9d6 100644 --- a/assets/tests/factories/vehicle.ts +++ b/assets/tests/factories/vehicle.ts @@ -93,9 +93,7 @@ export const centerLocationVehicle = randomLocationVehicle.params({ longitude: defaultCenter.lng, }) -const vehicleFactory = centerLocationVehicle - -export default vehicleFactory +export const vehicleFactory = centerLocationVehicle export const shuttleFactory = vehicleFactory.params({ isShuttle: true, diff --git a/assets/tests/helpers/vehicleLabel.test.ts b/assets/tests/helpers/vehicleLabel.test.ts index daeb232c6..f00550cf1 100644 --- a/assets/tests/helpers/vehicleLabel.test.ts +++ b/assets/tests/helpers/vehicleLabel.test.ts @@ -5,7 +5,7 @@ import vehicleLabel, { } from "../../src/helpers/vehicleLabel" import { VehicleInScheduledService } from "../../src/realtime" import { UserSettings, VehicleLabelSetting } from "../../src/userSettings" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import ghostFactory from "../factories/ghost" const vehicle: VehicleInScheduledService = vehicleFactory.build({ diff --git a/assets/tests/hooks/useMostRecentVehicleById.test.ts b/assets/tests/hooks/useMostRecentVehicleById.test.ts index 046069a6b..76e8511e7 100644 --- a/assets/tests/hooks/useMostRecentVehicleById.test.ts +++ b/assets/tests/hooks/useMostRecentVehicleById.test.ts @@ -3,7 +3,7 @@ import { makeMockSocket } from "../testHelpers/socketHelpers" import { renderHook } from "@testing-library/react" import useVehicleForId from "../../src/hooks/useVehicleForId" import useMostRecentVehicleById from "../../src/hooks/useMostRecentVehicleById" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import { Ghost, Vehicle, VehicleId } from "../../src/realtime" jest.mock("../../src/hooks/useVehicleForId", () => ({ diff --git a/assets/tests/hooks/usePanelState.test.tsx b/assets/tests/hooks/usePanelState.test.tsx index 1b0b74bef..4ea51d1f5 100644 --- a/assets/tests/hooks/usePanelState.test.tsx +++ b/assets/tests/hooks/usePanelState.test.tsx @@ -3,7 +3,7 @@ import { usePanelStateForViewState, usePanelStateFromStateDispatchContext, } from "../../src/hooks/usePanelState" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import { OpenView, PagePath, diff --git a/assets/tests/hooks/useSearchResultsByCategory.test.ts b/assets/tests/hooks/useSearchResultsByCategory.test.ts index 1c55e389e..ce235bfd8 100644 --- a/assets/tests/hooks/useSearchResultsByCategory.test.ts +++ b/assets/tests/hooks/useSearchResultsByCategory.test.ts @@ -2,7 +2,7 @@ import { jest, describe, test, expect, afterEach } from "@jest/globals" import { renderHook } from "@testing-library/react" import { useSearchResults } from "../../src/hooks/useSearchResults" import { makeMockSocket } from "../testHelpers/socketHelpers" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import useSearchResultsByCategory, { VehicleResultType, } from "../../src/hooks/useSearchResultsByCategory" diff --git a/assets/tests/models/vehicle.test.ts b/assets/tests/models/vehicle.test.ts index b99f756e0..57097df05 100644 --- a/assets/tests/models/vehicle.test.ts +++ b/assets/tests/models/vehicle.test.ts @@ -9,7 +9,7 @@ import { } from "../../src/models/vehicle" import { Ghost, VehicleInScheduledService } from "../../src/realtime" import * as dateTime from "../../src/util/dateTime" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import ghostFactory from "../factories/ghost" import routeFactory from "../factories/route" diff --git a/assets/tests/models/vehicleStatus.test.ts b/assets/tests/models/vehicleStatus.test.ts index 950ad9966..dafa73c42 100644 --- a/assets/tests/models/vehicleStatus.test.ts +++ b/assets/tests/models/vehicleStatus.test.ts @@ -10,7 +10,7 @@ import { defaultUserSettings, VehicleAdherenceColorsSetting, } from "../../src/userSettings" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" describe("onTimeStatus", () => { test("returns on-time", () => { diff --git a/assets/tests/models/vehiclesByPosition.test.ts b/assets/tests/models/vehiclesByPosition.test.ts index 790558e09..55cb1c17a 100644 --- a/assets/tests/models/vehiclesByPosition.test.ts +++ b/assets/tests/models/vehiclesByPosition.test.ts @@ -6,7 +6,7 @@ import { Ghost, VehicleInScheduledService, } from "../../src/realtime.d" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import ghostFactory from "../factories/ghost" describe("groupByPosition", () => { diff --git a/assets/tests/state.test.ts b/assets/tests/state.test.ts index 86f2f4f07..d8e7d540e 100644 --- a/assets/tests/state.test.ts +++ b/assets/tests/state.test.ts @@ -7,7 +7,7 @@ import { } from "../src/userSettings" import { RouteTab } from "../src/models/routeTab" -import vehicleFactory from "./factories/vehicle" +import { vehicleFactory } from "./factories/vehicle" import routeTabFactory from "./factories/routeTab" import stateFactory from "./factories/applicationState" import { diff --git a/assets/tests/state/pagePanelState.test.ts b/assets/tests/state/pagePanelState.test.ts index 72a1ebefb..08c1ec959 100644 --- a/assets/tests/state/pagePanelState.test.ts +++ b/assets/tests/state/pagePanelState.test.ts @@ -1,5 +1,5 @@ import { expect, describe, test } from "@jest/globals" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" import { OpenView, PagePath, diff --git a/assets/tests/util/operatorFormatting.test.ts b/assets/tests/util/operatorFormatting.test.ts index 50f2d11cc..0e40d4982 100644 --- a/assets/tests/util/operatorFormatting.test.ts +++ b/assets/tests/util/operatorFormatting.test.ts @@ -4,7 +4,7 @@ import { formatOperatorName, formatOperatorNameFromVehicle, } from "../../src/util/operatorFormatting" -import vehicleFactory from "../factories/vehicle" +import { vehicleFactory } from "../factories/vehicle" describe("formatOperatorName", () => { test("when given non-null data, should return formatted operator name", () => { From 5eb0fa259bfbacc829f6c2eb1e144a467b12f2d2 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Wed, 28 Aug 2024 08:08:03 -0400 Subject: [PATCH 30/38] refactor: Explicitly export swingFactory (#2753) --- assets/tests/components/swingsView.test.tsx | 2 +- assets/tests/factories/swing.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/tests/components/swingsView.test.tsx b/assets/tests/components/swingsView.test.tsx index c4b585d54..d67adee84 100644 --- a/assets/tests/components/swingsView.test.tsx +++ b/assets/tests/components/swingsView.test.tsx @@ -6,7 +6,7 @@ import renderer from "react-test-renderer" import ghostFactory from "../factories/ghost" import { vehicleFactory } from "../factories/vehicle" import routeFactory from "../factories/route" -import swingFactory from "../factories/swing" +import { swingFactory } from "../factories/swing" import SwingsView from "../../src/components/swingsView" import { RoutesProvider } from "../../src/contexts/routesContext" import { StateDispatchProvider } from "../../src/contexts/stateDispatchContext" diff --git a/assets/tests/factories/swing.ts b/assets/tests/factories/swing.ts index 21f723ced..48b63a29d 100644 --- a/assets/tests/factories/swing.ts +++ b/assets/tests/factories/swing.ts @@ -1,7 +1,7 @@ import { Factory } from "fishery" import { Swing } from "../../src/schedule" -export default Factory.define(() => ({ +export const swingFactory = Factory.define(() => ({ blockId: "A12-34", fromRouteId: "1", fromRunId: "123-456", From 5fad3728f0905b789f00dc9c543a36b16affd5c7 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Wed, 28 Aug 2024 08:08:15 -0400 Subject: [PATCH 31/38] refactor: Explicitly export stopDataFactory (#2754) --- assets/tests/api.test.ts | 2 +- assets/tests/factories/shape_data.ts | 2 +- assets/tests/factories/stopData.ts | 4 +--- assets/tests/models/shapeData.test.ts | 2 +- assets/tests/models/stopData.test.ts | 2 +- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/assets/tests/api.test.ts b/assets/tests/api.test.ts index 430e6bb16..67a298a16 100644 --- a/assets/tests/api.test.ts +++ b/assets/tests/api.test.ts @@ -47,7 +47,7 @@ import locationSearchResultDataFactory from "./factories/locationSearchResultDat import locationSearchResultFactory from "./factories/locationSearchResult" import locationSearchSuggestionDataFactory from "./factories/locationSearchSuggestionData" import locationSearchSuggestionFactory from "./factories/locationSearchSuggestion" -import stopDataFactory from "./factories/stopData" +import { stopDataFactory } from "./factories/stopData" import { shapePointFactory } from "./factories/shapePointFactory" import { ok, fetchError } from "../src/util/fetchResult" import { directionsFactory } from "./factories/detourShapeFactory" diff --git a/assets/tests/factories/shape_data.ts b/assets/tests/factories/shape_data.ts index b14b3939f..04c91c159 100644 --- a/assets/tests/factories/shape_data.ts +++ b/assets/tests/factories/shape_data.ts @@ -1,6 +1,6 @@ import { Factory } from "fishery" import { ShapeData } from "../../src/models/shapeData" -import stopDataFactory from "./stopData" +import { stopDataFactory } from "./stopData" export default Factory.define(({ sequence }) => ({ id: `shape${sequence}`, diff --git a/assets/tests/factories/stopData.ts b/assets/tests/factories/stopData.ts index cf2b02386..59a779a78 100644 --- a/assets/tests/factories/stopData.ts +++ b/assets/tests/factories/stopData.ts @@ -1,7 +1,7 @@ import { Factory } from "fishery" import { StopData } from "../../src/models/stopData" -const stopDataFactory = Factory.define(({ sequence }) => ({ +export const stopDataFactory = Factory.define(({ sequence }) => ({ id: `stop${sequence}`, name: `Some Stop - ${sequence}`, location_type: "stop", @@ -9,5 +9,3 @@ const stopDataFactory = Factory.define(({ sequence }) => ({ lon: 0, routes: [], })) - -export default stopDataFactory diff --git a/assets/tests/models/shapeData.test.ts b/assets/tests/models/shapeData.test.ts index 63a6f472a..a2ded3864 100644 --- a/assets/tests/models/shapeData.test.ts +++ b/assets/tests/models/shapeData.test.ts @@ -6,7 +6,7 @@ import { } from "../../src/models/shapeData" import shapeDataFactory from "../factories/shape_data" import shapeFactory from "../factories/shape" -import stopDataFactory from "../factories/stopData" +import { stopDataFactory } from "../factories/stopData" import { stopsFromData } from "../../src/models/stopData" describe("shapeFromData", () => { diff --git a/assets/tests/models/stopData.test.ts b/assets/tests/models/stopData.test.ts index ae24d9a6a..860cc1313 100644 --- a/assets/tests/models/stopData.test.ts +++ b/assets/tests/models/stopData.test.ts @@ -5,7 +5,7 @@ import { StopData, stopsFromData, } from "../../src/models/stopData" -import stopDataFactory from "../factories/stopData" +import { stopDataFactory } from "../factories/stopData" describe("stopsFromData", () => { test("transforms list of stopData to stops", () => { From bd7b7cd3510ec61a5b6d83adab54a494d4e13032 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Wed, 28 Aug 2024 08:08:35 -0400 Subject: [PATCH 32/38] refactor: Explicitly export shapeDataFactory (#2756) --- assets/tests/factories/shape_data.ts | 2 +- assets/tests/models/shapeData.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/tests/factories/shape_data.ts b/assets/tests/factories/shape_data.ts index 04c91c159..63ce2e867 100644 --- a/assets/tests/factories/shape_data.ts +++ b/assets/tests/factories/shape_data.ts @@ -2,7 +2,7 @@ import { Factory } from "fishery" import { ShapeData } from "../../src/models/shapeData" import { stopDataFactory } from "./stopData" -export default Factory.define(({ sequence }) => ({ +export const shapeDataFactory = Factory.define(({ sequence }) => ({ id: `shape${sequence}`, points: [{ shape_id: `shape${sequence}`, sequence: 1, lat: 0, lon: 0 }], stops: [stopDataFactory.build({ id: `stop${sequence}` })], diff --git a/assets/tests/models/shapeData.test.ts b/assets/tests/models/shapeData.test.ts index a2ded3864..1f61a9fde 100644 --- a/assets/tests/models/shapeData.test.ts +++ b/assets/tests/models/shapeData.test.ts @@ -4,7 +4,7 @@ import { shapeFromData, shapesFromData, } from "../../src/models/shapeData" -import shapeDataFactory from "../factories/shape_data" +import { shapeDataFactory } from "../factories/shape_data" import shapeFactory from "../factories/shape" import { stopDataFactory } from "../factories/stopData" import { stopsFromData } from "../../src/models/stopData" From 64606820671663fb1ad31399807e5c1a6a82c33d Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Wed, 28 Aug 2024 11:16:25 -0400 Subject: [PATCH 33/38] cleanup: move swiftly API config into runtime.exs (#2760) --- config/config.exs | 2 -- config/runtime.exs | 5 +++++ lib/skate/application.ex | 2 -- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/config/config.exs b/config/config.exs index 342bca35e..2466af273 100644 --- a/config/config.exs +++ b/config/config.exs @@ -19,8 +19,6 @@ config :skate, gtfs_url: {:system, "GTFS_URL"}, hastus_url: {:system, "SKATE_HASTUS_URL"}, busloc_url: {:system, "BUSLOC_URL"}, - swiftly_authorization_key: {:system, "SWIFTLY_AUTHORIZATION_KEY"}, - swiftly_realtime_vehicles_url: {:system, "SWIFTLY_REALTIME_VEHICLES_URL"}, trip_updates_url: {:system, "TRIP_UPDATES_URL"}, bridge_requester: Bridge.Request, bridge_url: {:system, "BRIDGE_URL"}, diff --git a/config/runtime.exs b/config/runtime.exs index 1813d9551..1d7b8d9e2 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -11,6 +11,11 @@ config :skate, aws_place_index: System.get_env("AWS_PLACE_INDEX"), environment_name: System.get_env("ENVIRONMENT_NAME", "missing-env") +# Swiftly API +config :skate, + swiftly_authorization_key: System.get_env("SWIFTLY_AUTHORIZATION_KEY"), + swiftly_realtime_vehicles_url: System.get_env("SWIFTLY_REALTIME_VEHICLES_URL") + if System.get_env("SECRET_KEY_BASE") do config :skate, SkateWeb.Endpoint, secret_key_base: System.get_env("SECRET_KEY_BASE") end diff --git a/lib/skate/application.ex b/lib/skate/application.ex index 051105af9..480c7fdc4 100644 --- a/lib/skate/application.ex +++ b/lib/skate/application.ex @@ -74,8 +74,6 @@ defmodule Skate.Application do :gtfs_url, :hastus_url, :busloc_url, - :swiftly_authorization_key, - :swiftly_realtime_vehicles_url, :trip_updates_url, :geonames_url_base, :geonames_token, From 1d1d630d9c7a17354a0cf1216ad0dd5b1b80c726 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Wed, 28 Aug 2024 11:22:39 -0400 Subject: [PATCH 34/38] refactor: Explicitly export stopFactory (#2755) --- .../skate-components/detours/diversionPanel.stories.tsx | 2 +- .../stories/skate-components/routePropertiesCard.stories.tsx | 2 +- assets/tests/api.test.ts | 2 +- assets/tests/components/detours/detourMap.test.tsx | 2 +- assets/tests/components/detours/diversionPage.test.tsx | 2 +- assets/tests/components/map.test.tsx | 2 +- assets/tests/components/mapMarkers.test.tsx | 2 +- assets/tests/components/mapPage.test.tsx | 2 +- assets/tests/components/mapPage/mapDisplay.test.tsx | 2 +- assets/tests/components/mapPage/routePropertiesCard.test.tsx | 2 +- assets/tests/components/stopCard.test.tsx | 2 +- assets/tests/factories/detourFactory.ts | 2 +- assets/tests/factories/shape.ts | 2 +- assets/tests/factories/stop.ts | 4 +--- assets/tests/hooks/useAllStops.test.ts | 2 +- assets/tests/hooks/useShapes.test.ts | 2 +- assets/tests/models/subwayRoute.test.ts | 2 +- assets/tests/testHelpers/mockHelpers.ts | 2 +- 18 files changed, 18 insertions(+), 20 deletions(-) diff --git a/assets/stories/skate-components/detours/diversionPanel.stories.tsx b/assets/stories/skate-components/detours/diversionPanel.stories.tsx index 6462cacf3..3a144423a 100644 --- a/assets/stories/skate-components/detours/diversionPanel.stories.tsx +++ b/assets/stories/skate-components/detours/diversionPanel.stories.tsx @@ -1,7 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react" import { DiversionPanel } from "../../../src/components/detours/diversionPanel" -import stopFactory from "../../../tests/factories/stop" +import { stopFactory } from "../../../tests/factories/stop" const meta = { component: DiversionPanel, diff --git a/assets/stories/skate-components/routePropertiesCard.stories.tsx b/assets/stories/skate-components/routePropertiesCard.stories.tsx index b7de527f3..989e97762 100644 --- a/assets/stories/skate-components/routePropertiesCard.stories.tsx +++ b/assets/stories/skate-components/routePropertiesCard.stories.tsx @@ -5,7 +5,7 @@ import RoutePropertiesCard from "../../src/components/mapPage/routePropertiesCar import { routePatternFactory } from "../../tests/factories/routePattern" import routeFactory from "../../tests/factories/route" import { RoutesProvider } from "../../src/contexts/routesContext" -import stopFactory from "../../tests/factories/stop" +import { stopFactory } from "../../tests/factories/stop" import shapeFactory from "../../tests/factories/shape" const outboundStops = [ diff --git a/assets/tests/api.test.ts b/assets/tests/api.test.ts index 67a298a16..bca194715 100644 --- a/assets/tests/api.test.ts +++ b/assets/tests/api.test.ts @@ -33,7 +33,7 @@ import { } from "../src/api" import routeFactory from "./factories/route" import routeTabFactory from "./factories/routeTab" -import stopFactory from "./factories/stop" +import { stopFactory } from "./factories/stop" import * as browser from "../src/models/browser" import { string, unknown } from "superstruct" import { diff --git a/assets/tests/components/detours/detourMap.test.tsx b/assets/tests/components/detours/detourMap.test.tsx index 9910f2cfd..e2e0e03b0 100644 --- a/assets/tests/components/detours/detourMap.test.tsx +++ b/assets/tests/components/detours/detourMap.test.tsx @@ -10,7 +10,7 @@ import { RealDispatchWrapper } from "../../testHelpers/wrappers" import { layersControlButton } from "../../testHelpers/selectors/components/map" import { mockTileUrls } from "../../testHelpers/mockHelpers" import { tilesetUrlForType } from "../../../src/tilesetUrls" -import stopFactory from "../../factories/stop" +import { stopFactory } from "../../factories/stop" import { missedStopIcon, stopIcon, diff --git a/assets/tests/components/detours/diversionPage.test.tsx b/assets/tests/components/detours/diversionPage.test.tsx index c7558c1c4..f56eed56c 100644 --- a/assets/tests/components/detours/diversionPage.test.tsx +++ b/assets/tests/components/detours/diversionPage.test.tsx @@ -22,7 +22,7 @@ import { DiversionPage as DiversionPageDefault, DiversionPageProps, } from "../../../src/components/detours/diversionPage" -import stopFactory from "../../factories/stop" +import { stopFactory } from "../../factories/stop" import userEvent from "@testing-library/user-event" import { originalRouteShape, diff --git a/assets/tests/components/map.test.tsx b/assets/tests/components/map.test.tsx index ec9be887d..acdb3a103 100644 --- a/assets/tests/components/map.test.tsx +++ b/assets/tests/components/map.test.tsx @@ -22,7 +22,7 @@ import Map, { import { autoCenter } from "../../src/components/map/follower" import { TrainVehicle, VehicleInScheduledService } from "../../src/realtime" import { vehicleFactory } from "../factories/vehicle" -import stopFactory from "../factories/stop" +import { stopFactory } from "../factories/stop" import userEvent from "@testing-library/user-event" import { runIdToLabel } from "../../src/helpers/vehicleLabel" diff --git a/assets/tests/components/mapMarkers.test.tsx b/assets/tests/components/mapMarkers.test.tsx index e69b65ba8..e8f96b733 100644 --- a/assets/tests/components/mapMarkers.test.tsx +++ b/assets/tests/components/mapMarkers.test.tsx @@ -9,7 +9,7 @@ import { TrainVehicleMarker, VehicleMarker, } from "../../src/components/mapMarkers" -import stopFactory from "../factories/stop" +import { stopFactory } from "../factories/stop" import { vehicleFactory } from "../factories/vehicle" import trainVehicleFactory from "../factories/trainVehicle" import { render, screen } from "@testing-library/react" diff --git a/assets/tests/components/mapPage.test.tsx b/assets/tests/components/mapPage.test.tsx index 7c8145af0..8a0a01111 100644 --- a/assets/tests/components/mapPage.test.tsx +++ b/assets/tests/components/mapPage.test.tsx @@ -41,7 +41,7 @@ import { searchQueryRunFactory, searchQueryVehicleFactory, } from "../factories/searchQuery" -import stopFactory from "../factories/stop" +import { stopFactory } from "../factories/stop" import { vehicleFactory, randomLocationVehicle, diff --git a/assets/tests/components/mapPage/mapDisplay.test.tsx b/assets/tests/components/mapPage/mapDisplay.test.tsx index 9e910ad62..1b1a4695b 100644 --- a/assets/tests/components/mapPage/mapDisplay.test.tsx +++ b/assets/tests/components/mapPage/mapDisplay.test.tsx @@ -27,7 +27,7 @@ import ghostFactory from "../../factories/ghost" import routeFactory from "../../factories/route" import { routePatternFactory } from "../../factories/routePattern" import { runIdFactory } from "../../factories/run" -import stopFactory from "../../factories/stop" +import { stopFactory } from "../../factories/stop" import { vehicleFactory, randomLocationVehicle, diff --git a/assets/tests/components/mapPage/routePropertiesCard.test.tsx b/assets/tests/components/mapPage/routePropertiesCard.test.tsx index a2a4ccde8..3ae43632f 100644 --- a/assets/tests/components/mapPage/routePropertiesCard.test.tsx +++ b/assets/tests/components/mapPage/routePropertiesCard.test.tsx @@ -9,7 +9,7 @@ import RoutePropertiesCard, { import { routePatternFactory } from "../../factories/routePattern" import routeFactory from "../../factories/route" import shapeFactory from "../../factories/shape" -import stopFactory from "../../factories/stop" +import { stopFactory } from "../../factories/stop" import { RoutesProvider } from "../../../src/contexts/routesContext" import userEvent from "@testing-library/user-event" diff --git a/assets/tests/components/stopCard.test.tsx b/assets/tests/components/stopCard.test.tsx index 17bfe2979..d603a30c0 100644 --- a/assets/tests/components/stopCard.test.tsx +++ b/assets/tests/components/stopCard.test.tsx @@ -4,7 +4,7 @@ import { render, screen } from "@testing-library/react" import "@testing-library/jest-dom/jest-globals" import { within } from "@testing-library/dom" import StopCard from "../../src/components/stopCard" -import stopFactory from "../factories/stop" +import { stopFactory } from "../factories/stop" jest.mock("react-leaflet", () => ({ __esModule: true, diff --git a/assets/tests/factories/detourFactory.ts b/assets/tests/factories/detourFactory.ts index 7258ab884..afaa67445 100644 --- a/assets/tests/factories/detourFactory.ts +++ b/assets/tests/factories/detourFactory.ts @@ -6,7 +6,7 @@ import { UnfinishedRouteSegments, } from "../../src/models/detour" import { shapePointFactory } from "./shapePointFactory" -import stopFactory from "./stop" +import { stopFactory } from "./stop" import { detourShapeFactory } from "./detourShapeFactory" export const unfinishedRouteSegmentsFactory = diff --git a/assets/tests/factories/shape.ts b/assets/tests/factories/shape.ts index 411adee3c..f0023c297 100644 --- a/assets/tests/factories/shape.ts +++ b/assets/tests/factories/shape.ts @@ -1,6 +1,6 @@ import { Factory } from "fishery" import { Shape } from "../../src/schedule" -import stopFactory from "./stop" +import { stopFactory } from "./stop" const shapeFactory = Factory.define(({ sequence }) => ({ id: `shape${sequence}`, diff --git a/assets/tests/factories/stop.ts b/assets/tests/factories/stop.ts index 75beae010..fff6817b7 100644 --- a/assets/tests/factories/stop.ts +++ b/assets/tests/factories/stop.ts @@ -3,7 +3,7 @@ import { LocationType } from "../../src/models/stopData" import { Stop } from "../../src/schedule" import { localGeoCoordinateFactory } from "./geoCoordinate" -const stopFactory = Factory.define(({ sequence }) => { +export const stopFactory = Factory.define(({ sequence }) => { const coord = localGeoCoordinateFactory.build() return { id: `stop${sequence}`, @@ -15,5 +15,3 @@ const stopFactory = Factory.define(({ sequence }) => { routes: undefined, } }) - -export default stopFactory diff --git a/assets/tests/hooks/useAllStops.test.ts b/assets/tests/hooks/useAllStops.test.ts index 1cdb20d13..45621d409 100644 --- a/assets/tests/hooks/useAllStops.test.ts +++ b/assets/tests/hooks/useAllStops.test.ts @@ -5,7 +5,7 @@ import { LocationType } from "../../src/models/stopData" import { Stop } from "../../src/schedule" import { instantPromise } from "../testHelpers/mockHelpers" import { useAllStops } from "../../src/hooks/useAllStops" -import stopFactory from "../factories/stop" +import { stopFactory } from "../factories/stop" jest.mock("../../src/api", () => ({ __esModule: true, diff --git a/assets/tests/hooks/useShapes.test.ts b/assets/tests/hooks/useShapes.test.ts index f71fbbe2f..767de2824 100644 --- a/assets/tests/hooks/useShapes.test.ts +++ b/assets/tests/hooks/useShapes.test.ts @@ -5,7 +5,7 @@ import { useRouteShapes, useTripShape } from "../../src/hooks/useShapes" import { Shape, Stop } from "../../src/schedule.d" import { instantPromise, mockUseStateOnce } from "../testHelpers/mockHelpers" import shapeFactory from "../factories/shape" -import stopFactory from "../factories/stop" +import { stopFactory } from "../factories/stop" jest.mock("../../src/api", () => ({ __esModule: true, diff --git a/assets/tests/models/subwayRoute.test.ts b/assets/tests/models/subwayRoute.test.ts index 03dee7280..4233b3f11 100644 --- a/assets/tests/models/subwayRoute.test.ts +++ b/assets/tests/models/subwayRoute.test.ts @@ -5,7 +5,7 @@ import { subwayRoutes, } from "../../src/models/subwayRoute" import shapeFactory from "../factories/shape" -import stopFactory from "../factories/stop" +import { stopFactory } from "../factories/stop" const subwayLineIds = ["Blue", "Green", "Orange", "Red", "Mattapan"] diff --git a/assets/tests/testHelpers/mockHelpers.ts b/assets/tests/testHelpers/mockHelpers.ts index 66f42920a..2d936703b 100644 --- a/assets/tests/testHelpers/mockHelpers.ts +++ b/assets/tests/testHelpers/mockHelpers.ts @@ -5,7 +5,7 @@ import useScreenSize from "../../src/hooks/useScreenSize" import { DeviceType } from "../../src/skate" import { VehicleInScheduledService, Ghost } from "../../src/realtime" import { routePatternFactory } from "../factories/routePattern" -import stopFactory from "../factories/stop" +import { stopFactory } from "../factories/stop" import shape from "../factories/shape" import { TileType, tilesetUrlForType } from "../../src/tilesetUrls" From be568b912c1526b678e35dd64dabb9827194b252 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Wed, 28 Aug 2024 11:24:02 -0400 Subject: [PATCH 35/38] refactor: Explicitly export trainVehicleFactory (#2752) --- assets/tests/components/mapMarkers.test.tsx | 2 +- assets/tests/factories/trainVehicle.ts | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/assets/tests/components/mapMarkers.test.tsx b/assets/tests/components/mapMarkers.test.tsx index e8f96b733..135f389af 100644 --- a/assets/tests/components/mapMarkers.test.tsx +++ b/assets/tests/components/mapMarkers.test.tsx @@ -11,7 +11,7 @@ import { } from "../../src/components/mapMarkers" import { stopFactory } from "../factories/stop" import { vehicleFactory } from "../factories/vehicle" -import trainVehicleFactory from "../factories/trainVehicle" +import { trainVehicleFactory } from "../factories/trainVehicle" import { render, screen } from "@testing-library/react" import React from "react" import { MapContainer } from "react-leaflet" diff --git a/assets/tests/factories/trainVehicle.ts b/assets/tests/factories/trainVehicle.ts index c72508f24..c921ee014 100644 --- a/assets/tests/factories/trainVehicle.ts +++ b/assets/tests/factories/trainVehicle.ts @@ -2,9 +2,11 @@ import { Factory } from "fishery" import { TrainVehicle } from "../../src/realtime" import { defaultCenter } from "../../src/components/map" -export default Factory.define(({ sequence }) => ({ - id: `t${sequence}`, - latitude: defaultCenter.lat, - longitude: defaultCenter.lng, - bearing: 0, -})) +export const trainVehicleFactory = Factory.define( + ({ sequence }) => ({ + id: `t${sequence}`, + latitude: defaultCenter.lat, + longitude: defaultCenter.lng, + bearing: 0, + }) +) From d709d55d7d12f7fe7f51c2c0596fe042bf4ec77a Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Wed, 28 Aug 2024 11:54:14 -0400 Subject: [PATCH 36/38] cleanup: move MBTA API config into runtime.exs (#2759) --- config/config.exs | 2 -- config/runtime.exs | 5 +++++ lib/skate/application.ex | 2 -- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/config/config.exs b/config/config.exs index 2466af273..38e3bb854 100644 --- a/config/config.exs +++ b/config/config.exs @@ -11,8 +11,6 @@ config :skate, ecto_repos: [Skate.Repo] config :skate, # Default. Can be configured via environment variable, which is loaded in application.ex - api_url: {:system, "API_URL"}, - api_key: {:system, "API_KEY"}, restrict_environment_access?: false, google_tag_manager_id: {:system, "GOOGLE_TAG_MANAGER_ID"}, tileset_url: {:system, "TILESET_URL"}, diff --git a/config/runtime.exs b/config/runtime.exs index 1d7b8d9e2..01d4ab9f4 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -11,6 +11,11 @@ config :skate, aws_place_index: System.get_env("AWS_PLACE_INDEX"), environment_name: System.get_env("ENVIRONMENT_NAME", "missing-env") +# MBTA API +config :skate, + api_key: System.get_env("API_KEY"), + api_url: System.get_env("API_URL") + # Swiftly API config :skate, swiftly_authorization_key: System.get_env("SWIFTLY_AUTHORIZATION_KEY"), diff --git a/lib/skate/application.ex b/lib/skate/application.ex index 480c7fdc4..2cea3821e 100644 --- a/lib/skate/application.ex +++ b/lib/skate/application.ex @@ -67,8 +67,6 @@ defmodule Skate.Application do @spec load_runtime_config() :: :ok def load_runtime_config() do application_keys = [ - :api_url, - :api_key, :google_tag_manager_id, :tileset_url, :gtfs_url, From e9a30355a20effea8e6e8ec8515fbaac921e32fd Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Wed, 28 Aug 2024 12:03:14 -0400 Subject: [PATCH 37/38] refactor: Explicitly export shapeFactory (#2761) --- .../stories/skate-components/routePropertiesCard.stories.tsx | 2 +- assets/tests/components/map.test.tsx | 2 +- assets/tests/components/mapPage/mapDisplay.test.tsx | 2 +- assets/tests/components/mapPage/routePropertiesCard.test.tsx | 2 +- assets/tests/components/shuttleMapPage.test.tsx | 2 +- assets/tests/factories/routePattern.ts | 4 ++-- assets/tests/factories/shape.ts | 4 +--- assets/tests/hooks/useShapes.test.ts | 2 +- assets/tests/models/shapeData.test.ts | 2 +- assets/tests/models/subwayRoute.test.ts | 2 +- assets/tests/testHelpers/mockHelpers.ts | 4 ++-- 11 files changed, 13 insertions(+), 15 deletions(-) diff --git a/assets/stories/skate-components/routePropertiesCard.stories.tsx b/assets/stories/skate-components/routePropertiesCard.stories.tsx index 989e97762..2c165c90e 100644 --- a/assets/stories/skate-components/routePropertiesCard.stories.tsx +++ b/assets/stories/skate-components/routePropertiesCard.stories.tsx @@ -6,7 +6,7 @@ import { routePatternFactory } from "../../tests/factories/routePattern" import routeFactory from "../../tests/factories/route" import { RoutesProvider } from "../../src/contexts/routesContext" import { stopFactory } from "../../tests/factories/stop" -import shapeFactory from "../../tests/factories/shape" +import { shapeFactory } from "../../tests/factories/shape" const outboundStops = [ stopFactory.build({ name: "Watertown Square" }), diff --git a/assets/tests/components/map.test.tsx b/assets/tests/components/map.test.tsx index acdb3a103..a86cd41eb 100644 --- a/assets/tests/components/map.test.tsx +++ b/assets/tests/components/map.test.tsx @@ -33,7 +33,7 @@ import { setHtmlDefaultWidthHeight } from "../testHelpers/leafletMapWidth" import { mockScreenSize, mockTileUrls } from "../testHelpers/mockHelpers" import { streetViewModeSwitch } from "../testHelpers/selectors/components/mapPage/map" import { streetViewUrl } from "../../src/util/streetViewUrl" -import shapeFactory from "../factories/shape" +import { shapeFactory } from "../factories/shape" import { fullStoryEvent } from "../../src/helpers/fullStory" import { recenterControl } from "../testHelpers/selectors/components/map/controls/recenterControl" diff --git a/assets/tests/components/mapPage/mapDisplay.test.tsx b/assets/tests/components/mapPage/mapDisplay.test.tsx index 1b1a4695b..28a1978c8 100644 --- a/assets/tests/components/mapPage/mapDisplay.test.tsx +++ b/assets/tests/components/mapPage/mapDisplay.test.tsx @@ -40,7 +40,7 @@ import { mockUsePatternsByIdForVehicles, } from "../../testHelpers/mockHelpers" -import shapeFactory from "../../factories/shape" +import { shapeFactory } from "../../factories/shape" import { zoomInButton } from "../../testHelpers/selectors/components/map" import { stopIcon } from "../../testHelpers/selectors/components/map/markers/stopIcon" import { routePropertiesCard } from "../../testHelpers/selectors/components/mapPage/routePropertiesCard" diff --git a/assets/tests/components/mapPage/routePropertiesCard.test.tsx b/assets/tests/components/mapPage/routePropertiesCard.test.tsx index 3ae43632f..6ee9e2895 100644 --- a/assets/tests/components/mapPage/routePropertiesCard.test.tsx +++ b/assets/tests/components/mapPage/routePropertiesCard.test.tsx @@ -8,7 +8,7 @@ import RoutePropertiesCard, { } from "../../../src/components/mapPage/routePropertiesCard" import { routePatternFactory } from "../../factories/routePattern" import routeFactory from "../../factories/route" -import shapeFactory from "../../factories/shape" +import { shapeFactory } from "../../factories/shape" import { stopFactory } from "../../factories/stop" import { RoutesProvider } from "../../../src/contexts/routesContext" diff --git a/assets/tests/components/shuttleMapPage.test.tsx b/assets/tests/components/shuttleMapPage.test.tsx index d757fdbef..66ae3bf3c 100644 --- a/assets/tests/components/shuttleMapPage.test.tsx +++ b/assets/tests/components/shuttleMapPage.test.tsx @@ -23,7 +23,7 @@ import { initialState } from "../../src/state" import * as dateTime from "../../src/util/dateTime" import { shuttleFactory } from "../factories/vehicle" import userEvent from "@testing-library/user-event" -import shapeFactory from "../factories/shape" +import { shapeFactory } from "../factories/shape" import { layersControlButton, zoomInButton, diff --git a/assets/tests/factories/routePattern.ts b/assets/tests/factories/routePattern.ts index ee918763d..7f335d813 100644 --- a/assets/tests/factories/routePattern.ts +++ b/assets/tests/factories/routePattern.ts @@ -1,6 +1,6 @@ import { Factory } from "fishery" import { RoutePattern } from "../../src/schedule" -import shape from "./shape" +import { shapeFactory } from "./shape" export const routePatternFactory = Factory.define( ({ sequence }) => ({ @@ -9,7 +9,7 @@ export const routePatternFactory = Factory.define( routeId: "66", directionId: 0, sortOrder: sequence, - shape: shape.build(), + shape: shapeFactory.build(), headsign: `Headsign ${sequence}`, }) ) diff --git a/assets/tests/factories/shape.ts b/assets/tests/factories/shape.ts index f0023c297..50d494bdb 100644 --- a/assets/tests/factories/shape.ts +++ b/assets/tests/factories/shape.ts @@ -2,10 +2,8 @@ import { Factory } from "fishery" import { Shape } from "../../src/schedule" import { stopFactory } from "./stop" -const shapeFactory = Factory.define(({ sequence }) => ({ +export const shapeFactory = Factory.define(({ sequence }) => ({ id: `shape${sequence}`, points: [{ lat: 0, lon: 0 }], stops: [stopFactory.build({ id: `stop${sequence}` })], })) - -export default shapeFactory diff --git a/assets/tests/hooks/useShapes.test.ts b/assets/tests/hooks/useShapes.test.ts index 767de2824..be6de7cb2 100644 --- a/assets/tests/hooks/useShapes.test.ts +++ b/assets/tests/hooks/useShapes.test.ts @@ -4,7 +4,7 @@ import * as Api from "../../src/api" import { useRouteShapes, useTripShape } from "../../src/hooks/useShapes" import { Shape, Stop } from "../../src/schedule.d" import { instantPromise, mockUseStateOnce } from "../testHelpers/mockHelpers" -import shapeFactory from "../factories/shape" +import { shapeFactory } from "../factories/shape" import { stopFactory } from "../factories/stop" jest.mock("../../src/api", () => ({ diff --git a/assets/tests/models/shapeData.test.ts b/assets/tests/models/shapeData.test.ts index 1f61a9fde..dc3f93dd9 100644 --- a/assets/tests/models/shapeData.test.ts +++ b/assets/tests/models/shapeData.test.ts @@ -5,7 +5,7 @@ import { shapesFromData, } from "../../src/models/shapeData" import { shapeDataFactory } from "../factories/shape_data" -import shapeFactory from "../factories/shape" +import { shapeFactory } from "../factories/shape" import { stopDataFactory } from "../factories/stopData" import { stopsFromData } from "../../src/models/stopData" diff --git a/assets/tests/models/subwayRoute.test.ts b/assets/tests/models/subwayRoute.test.ts index 4233b3f11..1f0852a0a 100644 --- a/assets/tests/models/subwayRoute.test.ts +++ b/assets/tests/models/subwayRoute.test.ts @@ -4,7 +4,7 @@ import { isASubwayRoute, subwayRoutes, } from "../../src/models/subwayRoute" -import shapeFactory from "../factories/shape" +import { shapeFactory } from "../factories/shape" import { stopFactory } from "../factories/stop" const subwayLineIds = ["Blue", "Green", "Orange", "Red", "Mattapan"] diff --git a/assets/tests/testHelpers/mockHelpers.ts b/assets/tests/testHelpers/mockHelpers.ts index 2d936703b..0888ca6d3 100644 --- a/assets/tests/testHelpers/mockHelpers.ts +++ b/assets/tests/testHelpers/mockHelpers.ts @@ -7,7 +7,7 @@ import { VehicleInScheduledService, Ghost } from "../../src/realtime" import { routePatternFactory } from "../factories/routePattern" import { stopFactory } from "../factories/stop" -import shape from "../factories/shape" +import { shapeFactory } from "../factories/shape" import { TileType, tilesetUrlForType } from "../../src/tilesetUrls" /** @@ -66,7 +66,7 @@ export const mockUsePatternsByIdForVehicles = ( id: routePatternId!, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion routeId: routeId!, - shape: shape.build({ + shape: shapeFactory.build({ stops: stopFactory.buildList(params?.stopCount || 2), }), }) From a2fc46a93acf4283a41898b7ee4f2984c876765d Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Wed, 28 Aug 2024 12:03:30 -0400 Subject: [PATCH 38/38] cleanup: move Bridge API config into runtime.exs (#2758) --- config/config.exs | 3 --- config/runtime.exs | 6 ++++++ lib/skate/application.ex | 3 --- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/config/config.exs b/config/config.exs index 38e3bb854..7fa6e690f 100644 --- a/config/config.exs +++ b/config/config.exs @@ -19,9 +19,6 @@ config :skate, busloc_url: {:system, "BUSLOC_URL"}, trip_updates_url: {:system, "TRIP_UPDATES_URL"}, bridge_requester: Bridge.Request, - bridge_url: {:system, "BRIDGE_URL"}, - bridge_api_username: {:system, "BRIDGE_API_USERNAME"}, - bridge_api_password: {:system, "BRIDGE_API_PASSWORD"}, start_data_processes: true, record_appcues: false, record_fullstory: false, diff --git a/config/runtime.exs b/config/runtime.exs index 01d4ab9f4..a345591b5 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -21,6 +21,12 @@ config :skate, swiftly_authorization_key: System.get_env("SWIFTLY_AUTHORIZATION_KEY"), swiftly_realtime_vehicles_url: System.get_env("SWIFTLY_REALTIME_VEHICLES_URL") +# Chelsea Bridge API +config :skate, + bridge_api_password: System.get_env("BRIDGE_API_PASSWORD"), + bridge_api_username: System.get_env("BRIDGE_API_USERNAME"), + bridge_url: System.get_env("BRIDGE_URL") + if System.get_env("SECRET_KEY_BASE") do config :skate, SkateWeb.Endpoint, secret_key_base: System.get_env("SECRET_KEY_BASE") end diff --git a/lib/skate/application.ex b/lib/skate/application.ex index 2cea3821e..bdbfa54f2 100644 --- a/lib/skate/application.ex +++ b/lib/skate/application.ex @@ -75,9 +75,6 @@ defmodule Skate.Application do :trip_updates_url, :geonames_url_base, :geonames_token, - :bridge_url, - :bridge_api_username, - :bridge_api_password, :sentry_frontend_dsn, :sentry_environment, :sentry_org_slug,