diff --git a/site/src/components/DurationField/DurationField.tsx b/site/src/components/DurationField/DurationField.tsx index bd2a1fd22b..728039931d 100644 --- a/site/src/components/DurationField/DurationField.tsx +++ b/site/src/components/DurationField/DurationField.tsx @@ -86,12 +86,10 @@ export const DurationField: FC = ({ } const handleTextChange = (raw: string) => { - // Preserve exactly what the user typed rather than silently stripping - // non-digits (which surprisingly turned "ABC123DEF" into "123"). The - // pattern constraint on the Input surfaces invalid entries instead. - setText(raw); + const digits = raw.replace(/\D/g, ""); + setText(digits); - const ms = toMs(raw, unit); + const ms = toMs(digits, unit); if (ms !== valueMs) { onChange(ms); } diff --git a/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx b/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx index 80c0986f30..dd88e6cac5 100644 --- a/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx +++ b/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx @@ -7,7 +7,7 @@ import { MockUserMember, MockUserOwner, } from "#/testHelpers/entities"; -import { pixelWithTablet } from "#/testHelpers/pixel"; +import { pixelWithDesktop, pixelWithTablet } from "#/testHelpers/pixel"; import { withDashboardProvider } from "#/testHelpers/storybook"; import { NavbarView } from "./NavbarView"; @@ -49,6 +49,7 @@ export default meta; type Story = StoryObj; export const ForAdmin: Story = { + parameters: { pixel: { matrix: pixelWithDesktop } }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); await userEvent.click( @@ -58,6 +59,7 @@ export const ForAdmin: Story = { }; export const ForAuditor: Story = { + parameters: { pixel: { matrix: pixelWithDesktop } }, args: { user: MockUserMember, adminPermissions: { @@ -73,6 +75,7 @@ export const ForAuditor: Story = { }; export const ForOrgAdmin: Story = { + parameters: { pixel: { matrix: pixelWithDesktop } }, args: { user: MockUserMember, adminPermissions: { @@ -89,6 +92,7 @@ export const ForOrgAdmin: Story = { }; export const ForSingleOrgOSSAdmin: Story = { + parameters: { pixel: { matrix: pixelWithDesktop } }, args: { adminPermissions: { canViewDeployment: true, diff --git a/site/src/pages/AIBridgePage/SessionThreadsPage/SessionTimeline/NetworkCallsTable.stories.tsx b/site/src/pages/AIBridgePage/SessionThreadsPage/SessionTimeline/NetworkCallsTable.stories.tsx index de612d7036..c2ef4b23f8 100644 --- a/site/src/pages/AIBridgePage/SessionThreadsPage/SessionTimeline/NetworkCallsTable.stories.tsx +++ b/site/src/pages/AIBridgePage/SessionThreadsPage/SessionTimeline/NetworkCallsTable.stories.tsx @@ -34,7 +34,9 @@ export const BlockedBadge: Story = { }, play: async ({ canvas }) => { await expect( - canvas.getByText("Blocked network calls: 9"), + canvas.getByText((_content, element) => { + return element?.textContent === "Blocked network calls: 9"; + }), ).toBeInTheDocument(); await expect(canvas.getAllByText("Blocked")).toHaveLength(2); }, diff --git a/site/src/pages/AgentsPage/AgentChatPageView.stories.tsx b/site/src/pages/AgentsPage/AgentChatPageView.stories.tsx index 3381ad9012..537e6580a2 100644 --- a/site/src/pages/AgentsPage/AgentChatPageView.stories.tsx +++ b/site/src/pages/AgentsPage/AgentChatPageView.stories.tsx @@ -1579,12 +1579,12 @@ const sidebarTabStorageKey = `${lastActiveSidebarTabStorageKeyPrefix}${AGENT_ID} /** * When localStorage contains a persisted tab ID for this chat, the sidebar - * should restore it on mount. Seed localStorage with "terminal" and verify - * that the Terminal tab is selected instead of the default Git tab. + * should restore it on mount. Seed localStorage with "git" and verify that + * the Git tab is selected instead of the default Summary tab. */ export const RestoresPersistedSidebarTab: Story = { beforeEach: () => { - localStorage.setItem(sidebarTabStorageKey, "terminal"); + localStorage.setItem(sidebarTabStorageKey, "git"); return () => { localStorage.removeItem(sidebarTabStorageKey); }; @@ -1601,12 +1601,12 @@ export const RestoresPersistedSidebarTab: Story = { const canvas = within(canvasElement); await waitFor(() => { - const terminalTab = canvas.getByRole("tab", { name: "Terminal" }); - expect(terminalTab).toHaveAttribute("aria-selected", "true"); + const gitTab = canvas.getByRole("tab", { name: "Git" }); + expect(gitTab).toHaveAttribute("aria-selected", "true"); }); - const gitTab = canvas.getByRole("tab", { name: "Git" }); - expect(gitTab).toHaveAttribute("aria-selected", "false"); + const summaryTab = canvas.getByRole("tab", { name: "Summary" }); + expect(summaryTab).toHaveAttribute("aria-selected", "false"); }, }; @@ -1637,14 +1637,14 @@ export const PersistsSidebarTabClick: Story = { expect(summaryTab).toHaveAttribute("aria-selected", "true"); }); - const terminalTab = canvas.getByRole("tab", { name: "Terminal" }); - await userEvent.click(terminalTab); + const gitTab = canvas.getByRole("tab", { name: "Git" }); + await userEvent.click(gitTab); await waitFor(() => { - expect(terminalTab).toHaveAttribute("aria-selected", "true"); + expect(gitTab).toHaveAttribute("aria-selected", "true"); }); - expect(localStorage.getItem(sidebarTabStorageKey)).toBe("terminal"); + expect(localStorage.getItem(sidebarTabStorageKey)).toBe("git"); }, }; @@ -1711,11 +1711,11 @@ export const DoesNotPersistForArchivedChat: Story = { expect(summaryTab).toHaveAttribute("aria-selected", "true"); }); - const terminalTab = canvas.getByRole("tab", { name: "Terminal" }); - await userEvent.click(terminalTab); + const gitTab = canvas.getByRole("tab", { name: "Git" }); + await userEvent.click(gitTab); await waitFor(() => { - expect(terminalTab).toHaveAttribute("aria-selected", "true"); + expect(gitTab).toHaveAttribute("aria-selected", "true"); }); expect(localStorage.getItem(sidebarTabStorageKey)).toBeNull(); diff --git a/site/src/pages/AgentsPage/components/AgentPageHeader.stories.tsx b/site/src/pages/AgentsPage/components/AgentPageHeader.stories.tsx index bb83b0d5a0..dd1dbbff5c 100644 --- a/site/src/pages/AgentsPage/components/AgentPageHeader.stories.tsx +++ b/site/src/pages/AgentsPage/components/AgentPageHeader.stories.tsx @@ -2,6 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; import { type FC, useMemo, useState } from "react"; import { Outlet } from "react-router"; import { expect, userEvent, waitFor, within } from "storybook/test"; +import { pixelWithPhone } from "#/testHelpers/pixel"; import { withDashboardProvider } from "#/testHelpers/storybook"; import { AgentPageHeader } from "./AgentPageHeader"; import { ChimeButton } from "./ChimeButton"; @@ -184,7 +185,9 @@ export const MobileActionsExcludeAnalytics: Story = { }, render: () => , parameters: { + layout: "fullscreen", viewport: { defaultViewport: "mobile1" }, + pixel: { matrix: pixelWithPhone }, reactRouter: { location: { path: "/agents", diff --git a/site/src/pages/DeploymentSettingsPage/NotificationsPage/NotificationEvents.stories.tsx b/site/src/pages/DeploymentSettingsPage/NotificationsPage/NotificationEvents.stories.tsx index 202fccf483..131e29f122 100644 --- a/site/src/pages/DeploymentSettingsPage/NotificationsPage/NotificationEvents.stories.tsx +++ b/site/src/pages/DeploymentSettingsPage/NotificationsPage/NotificationEvents.stories.tsx @@ -67,7 +67,7 @@ export const ChangeMethod: Story = { throw new Error("Could not find notification template row"); } await user.click( - within(row).getByRole("combobox", { name: "Notification method" }), + within(row).getByRole("combobox", { name: /Notification method/ }), ); await user.click( await within(document.body).findByRole("option", { name: "Webhook" }), @@ -88,7 +88,7 @@ export const ChangeMethodError: Story = { throw new Error("Could not find notification template row"); } await user.click( - within(row).getByRole("combobox", { name: "Notification method" }), + within(row).getByRole("combobox", { name: /Notification method/ }), ); await user.click( await within(document.body).findByRole("option", { name: "Webhook" }), diff --git a/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/CreateOAuth2AppPageView.stories.tsx b/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/CreateOAuth2AppPageView.stories.tsx index 8197b2d1ea..72744a8ae4 100644 --- a/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/CreateOAuth2AppPageView.stories.tsx +++ b/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/CreateOAuth2AppPageView.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { expect, spyOn, userEvent, within } from "storybook/test"; +import { expect, spyOn, userEvent, waitFor, within } from "storybook/test"; import { reactRouterParameters } from "storybook-addon-remix-react-router"; import { API } from "#/api/api"; import { @@ -41,9 +41,13 @@ export const Default: Story = { name: /add an oauth2 application/i, }), ).toBeVisible(); - await expect( - canvas.getByRole("button", { name: /create application/i }), - ).toBeDisabled(); + // The submit button renders enabled for a frame until the form's + // validate-on-mount pass reports the empty required fields. + await waitFor(() => + expect( + canvas.getByRole("button", { name: /create application/i }), + ).toBeDisabled(), + ); }, }; diff --git a/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/DynamicClientRegistrationSetting.stories.tsx b/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/DynamicClientRegistrationSetting.stories.tsx index 8a42803df4..50c9772b3c 100644 --- a/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/DynamicClientRegistrationSetting.stories.tsx +++ b/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/DynamicClientRegistrationSetting.stories.tsx @@ -254,7 +254,9 @@ export const KeepsFocusWhileUpdating: Story = { // The same element becomes the opposite action once the request lands, and // focus rides along rather than resetting to the top of the document. - await expect(canvas.getByText("Enable")).toBeVisible(); + await waitFor(() => { + expect(canvas.getByRole("button", { name: "Enable" })).toBeVisible(); + }); await expect(button).toHaveFocus(); }, }; diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx index 7c2550c6e3..1bbcd7889c 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx @@ -46,6 +46,7 @@ export const UpdateAutomaticUpdatesPolicy: Story = { await waitFor(() => expect(args.onSubmit).toHaveBeenCalledWith( expect.objectContaining({ automatic_updates: "always" }), + expect.anything(), ), ); }, diff --git a/site/src/testHelpers/pixel.ts b/site/src/testHelpers/pixel.ts index cc3d1611c1..61194a6081 100644 --- a/site/src/testHelpers/pixel.ts +++ b/site/src/testHelpers/pixel.ts @@ -5,3 +5,11 @@ export const pixelWithTablet = { viewports: ["tablet", "desktop"], }; + +export const pixelWithDesktop = { + viewports: ["desktop"], +}; + +export const pixelWithPhone = { + viewports: ["phone"], +};