diff --git a/site/src/components/IconField/IconField.stories.tsx b/site/src/components/IconField/IconField.stories.tsx index 7def38e2fe3..0773c78ae83 100644 --- a/site/src/components/IconField/IconField.stories.tsx +++ b/site/src/components/IconField/IconField.stories.tsx @@ -50,9 +50,12 @@ export const OpenPicker: Story = { }); await userEvent.click(button); await expect(button).toHaveAttribute("aria-expanded", "true"); - const popover = await screen.findByRole("dialog"); - await waitFor(() => - expect(popover.querySelector("em-emoji-picker")).toBeInTheDocument(), - ); + const dialog = await screen.findByRole("dialog"); + await waitFor(() => { + expect( + within(dialog).queryByRole("status", { name: "Loading" }), + ).not.toBeInTheDocument(); + }); + await expect(dialog).toBeVisible(); }, }; diff --git a/site/src/modules/dashboard/DashboardLayout.stories.tsx b/site/src/modules/dashboard/DashboardLayout.stories.tsx index 94f6017251c..c23c84aab63 100644 --- a/site/src/modules/dashboard/DashboardLayout.stories.tsx +++ b/site/src/modules/dashboard/DashboardLayout.stories.tsx @@ -25,7 +25,7 @@ import { MockUserMember, MockUserOwner, } from "#/testHelpers/entities"; -import { pixelWithTablet } from "#/testHelpers/pixel"; +import { pixelWithDesktop, pixelWithTablet } from "#/testHelpers/pixel"; import { withAuthProvider, withDashboardProvider, @@ -118,6 +118,7 @@ export const ForMember: Story = { export const CustomOrganizationRoleCanOpenModels: Story = { parameters: { + pixel: { matrix: pixelWithDesktop }, user: MockUserMember, permissions: MockNoPermissions, reactRouter: modelSettingsRouter, @@ -141,6 +142,7 @@ export const CustomOrganizationRoleCanOpenModels: Story = { export const ACLReadableMemberCanOpenModels: Story = { parameters: { + pixel: { matrix: pixelWithDesktop }, user: MockUserMember, permissions: MockNoPermissions, reactRouter: modelSettingsRouter, diff --git a/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx b/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx index a958675c709..ba5a6821d35 100644 --- a/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx +++ b/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx @@ -284,6 +284,7 @@ export const ForMember: Story = { export const ForMemberWithModelAccess: Story = { parameters: { + pixel: { matrix: pixelWithDesktop }, reactRouter: reactRouterParameters({ location: { path: "/" }, routing: [ diff --git a/site/src/modules/management/DeploymentSidebarView.stories.tsx b/site/src/modules/management/DeploymentSidebarView.stories.tsx index 02320b84450..69e44ef29b8 100644 --- a/site/src/modules/management/DeploymentSidebarView.stories.tsx +++ b/site/src/modules/management/DeploymentSidebarView.stories.tsx @@ -71,10 +71,9 @@ export const PremiumTabVisible: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - await expect(canvas.getByRole("link", { name: "Premium" })).toHaveAttribute( - "href", - "/deployment/premium", - ); + await expect( + canvas.getByRole("link", { name: "Trial Upgrade" }), + ).toHaveAttribute("href", "/deployment/premium"); }, }; @@ -87,7 +86,7 @@ export const PremiumTabHidden: Story = { const canvas = within(canvasElement); await expect( - canvas.queryByRole("link", { name: "Premium" }), + canvas.queryByRole("link", { name: "Trial Upgrade" }), ).not.toBeInTheDocument(); // A neighbouring item must survive the change. await expect( diff --git a/site/src/modules/terminal/WorkspaceTerminal.tsx b/site/src/modules/terminal/WorkspaceTerminal.tsx index a1f96888b09..a5b140cfa6e 100644 --- a/site/src/modules/terminal/WorkspaceTerminal.tsx +++ b/site/src/modules/terminal/WorkspaceTerminal.tsx @@ -331,8 +331,9 @@ export const WorkspaceTerminal = ({ window.removeEventListener("resize", refit); resizeObserver.disconnect(); fitAddonRef.current = undefined; - nextTerminal.dispose(); setTerminal(undefined); + // xterm queues its initial viewport synchronization in a timer. + window.setTimeout(() => nextTerminal.dispose(), 0); }; }, [ isVisible, diff --git a/site/src/pages/AISettingsPage/ModelsPage/OrganizationModelsLayout.stories.tsx b/site/src/pages/AISettingsPage/ModelsPage/OrganizationModelsLayout.stories.tsx index dd410688ff5..28fcd7c9177 100644 --- a/site/src/pages/AISettingsPage/ModelsPage/OrganizationModelsLayout.stories.tsx +++ b/site/src/pages/AISettingsPage/ModelsPage/OrganizationModelsLayout.stories.tsx @@ -84,6 +84,16 @@ const meta: Meta = { key: organizationsPermissions([MockOrganization2.id]).queryKey, data: { [MockOrganization2.id]: MockOrganizationPermissions }, }, + { + key: organizationsPermissions([ + MockDefaultOrganization.id, + MockOrganization2.id, + ]).queryKey, + data: { + [MockDefaultOrganization.id]: MockOrganizationPermissions, + [MockOrganization2.id]: MockOrganizationPermissions, + }, + }, ], }, }; @@ -206,6 +216,15 @@ export const InvalidRequestedOrganizationDeniesAdd: Story = { [MockDefaultOrganization.id]: MockOrganizationPermissions, }, }, + { + key: organizationsPermissions([ + MockDefaultOrganization.id, + MockOrganization2.id, + ]).queryKey, + data: { + [MockDefaultOrganization.id]: MockOrganizationPermissions, + }, + }, ], }, play: async ({ canvasElement }) => { @@ -281,6 +300,16 @@ export const DuplicateDisplayNamesAreDisambiguated: Story = { [MockDefaultOrganization.id]: MockOrganizationPermissions, }, }, + { + key: organizationsPermissions([ + MockDefaultOrganization.id, + duplicateNameOrganization.id, + ]).queryKey, + data: { + [MockDefaultOrganization.id]: MockOrganizationPermissions, + [duplicateNameOrganization.id]: MockOrganizationPermissions, + }, + }, ], }, play: async ({ canvasElement }) => { @@ -389,6 +418,7 @@ export const NoReadableOrganizationIsNotFound: Story = { isAxiosError: true, response: { status: 403 }, }); + spyOn(API, "checkAuthorization").mockResolvedValue({}); }, parameters: { queries: [] }, play: async ({ canvasElement }) => { diff --git a/site/src/pages/AgentsPage/AgentChatPage.stories.tsx b/site/src/pages/AgentsPage/AgentChatPage.stories.tsx index c6836456f7f..4b098b29f12 100644 --- a/site/src/pages/AgentsPage/AgentChatPage.stories.tsx +++ b/site/src/pages/AgentsPage/AgentChatPage.stories.tsx @@ -1607,16 +1607,11 @@ const capacityPollingChat: TypesGen.Chat = { export const QueuedForCapacityAfterPolling: Story = { parameters: { - queries: [ - { key: chatEntityKey(CHAT_ID), data: capacityPollingChat }, - { - key: chatMessagesKey(CHAT_ID), - data: { - pages: [{ messages: [], queued_messages: [], has_more: false }], - pageParams: [undefined], - }, - }, - ], + queries: buildQueries( + capacityPollingChat, + { messages: [], queued_messages: [], has_more: false }, + { diffUrl: undefined }, + ), }, beforeEach: () => { spyOn(API.experimental, "getChat").mockResolvedValue({ @@ -3322,7 +3317,10 @@ export const SlashCompactCommandSubmits: Story = { await userEvent.keyboard("/compact"); // First Enter accepts the highlighted menu entry; second Enter // submits the composer. - expect(await within(document.body).findByText("Commands")).toBeVisible(); + const body = within(document.body); + await waitFor(() => { + expect(body.getByRole("option", { name: /\/compact/i })).toBeVisible(); + }); await userEvent.keyboard("{Enter}"); await userEvent.keyboard("{Enter}"); @@ -3388,7 +3386,9 @@ export const SlashCompactYieldsToPersonalSkill: Story = { // visibility rather than asserting it once. await waitFor(() => { expect( - within(document.body).getByText("Personal compact skill"), + within(document.body).getByRole("option", { + name: /personal compact skill/i, + }), ).toBeVisible(); }); await userEvent.keyboard("{Enter}"); diff --git a/site/src/pages/AgentsPage/AgentChatPageView.stories.tsx b/site/src/pages/AgentsPage/AgentChatPageView.stories.tsx index fb1c428123e..4f7e0db540d 100644 --- a/site/src/pages/AgentsPage/AgentChatPageView.stories.tsx +++ b/site/src/pages/AgentsPage/AgentChatPageView.stories.tsx @@ -365,7 +365,7 @@ export const QueuedForCapacityCommunityAdmin: Story = { const trialLink = canvas.getByRole("link", { name: /start an unlimited trial/i, }); - expect(trialLink).toHaveAttribute("href", "https://coder.com/trial"); + expect(trialLink).toHaveAttribute("href", "/deployment/premium"); const learnMoreLink = canvas.getByRole("link", { name: /learn more/i }); expect(learnMoreLink).toHaveAttribute( "href", @@ -1588,7 +1588,7 @@ export const FailedHistoryPageOffersKeyboardRetry: Story = { export const TerminalFocusOnTabSwitch: Story = { parameters: { pixel: { exclude: true }, - webSocket: { "/api/v2/workspaceagents/": [{ event: "message", data: "" }] }, + webSocket: [], }, decorators: [withWebSocket], render: () => ( @@ -1614,12 +1614,12 @@ export const TerminalFocusOnTabSwitch: Story = { return el; }); - // The xterm focus target is a textarea inside the terminal container. + const terminal = within(terminalContainer); await waitFor( () => { - const textarea = terminalContainer.querySelector("textarea"); - expect(textarea).not.toBeNull(); - expect(document.activeElement).toBe(textarea); + expect( + terminal.getByRole("textbox", { name: "Terminal input" }), + ).toHaveFocus(); }, { timeout: 3000 }, ); @@ -1629,12 +1629,12 @@ export const TerminalFocusOnTabSwitch: Story = { await userEvent.click(gitTab); await userEvent.click(terminalTab); - // Focus should return to the terminal textarea. + // Focus should return to the terminal input. await waitFor( () => { - const textarea = terminalContainer.querySelector("textarea"); - expect(textarea).not.toBeNull(); - expect(document.activeElement).toBe(textarea); + expect( + terminal.getByRole("textbox", { name: "Terminal input" }), + ).toHaveFocus(); }, { timeout: 3000 }, ); diff --git a/site/src/pages/AgentsPage/components/AgentCreateForm.stories.tsx b/site/src/pages/AgentsPage/components/AgentCreateForm.stories.tsx index e65e0300f8b..ab078bd8e4f 100644 --- a/site/src/pages/AgentsPage/components/AgentCreateForm.stories.tsx +++ b/site/src/pages/AgentsPage/components/AgentCreateForm.stories.tsx @@ -14,6 +14,7 @@ import { import { API } from "#/api/api"; import { aiProvidersListKey } from "#/api/queries/aiProviders"; import { + mcpServerConfigsKey, organizationChatModelsKey, userChatPersonalModelOverrides, userChatProviderConfigsKey, @@ -2435,8 +2436,12 @@ export const MCPServersErrorShowsAlertAndDisablesSend: Story = { }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const matches = await canvas.findAllByText(/failed to load mcp servers/i); - expect(matches.length).toBeGreaterThan(0); + const alert = await canvas.findByRole("alert"); + expect( + within(alert).getByRole("heading", { + name: /failed to load mcp servers/i, + }), + ).toBeVisible(); expect(canvas.getByRole("button", { name: "Send" })).toBeDisabled(); }, }; @@ -2463,10 +2468,13 @@ export const MCPServersRefetchErrorKeepsSendEnabled: Story = { if (!capturedQueryClient) { throw new Error("query client was not captured by the story decorator"); } - await capturedQueryClient.refetchQueries(); - expect( - canvas.queryByText(/failed to refresh mcp servers/i), - ).not.toBeInTheDocument(); - expect(send).toBeEnabled(); + await capturedQueryClient.refetchQueries({ + queryKey: mcpServerConfigsKey(MockDefaultOrganization.id), + exact: true, + }); + await waitFor(() => { + expect(canvas.queryByRole("alert")).not.toBeInTheDocument(); + expect(send).toBeEnabled(); + }); }, }; diff --git a/site/src/pages/AgentsPage/components/AgentCreateForm.tsx b/site/src/pages/AgentsPage/components/AgentCreateForm.tsx index a4c42ee3188..b7e8340079b 100644 --- a/site/src/pages/AgentsPage/components/AgentCreateForm.tsx +++ b/site/src/pages/AgentsPage/components/AgentCreateForm.tsx @@ -614,9 +614,6 @@ export const AgentCreateForm: FC = ({ {personalModelOverridesQuery.error != null && ( )} - {mcpServersQuery.error != null && ( - - )} {showOrganizations && orgSelectionSettled && permittedOrgs.length > 1 && ( diff --git a/site/src/pages/DeploymentSettingsPage/PremiumPage/PremiumPageView.stories.tsx b/site/src/pages/DeploymentSettingsPage/PremiumPage/PremiumPageView.stories.tsx index 231df078326..830fb18bead 100644 --- a/site/src/pages/DeploymentSettingsPage/PremiumPage/PremiumPageView.stories.tsx +++ b/site/src/pages/DeploymentSettingsPage/PremiumPage/PremiumPageView.stories.tsx @@ -26,7 +26,7 @@ export const NoLicense: Story = { // The hero tracks the panel, so the trial pitch only appears here. await expect( canvas.getByRole("heading", { - name: "Start a 30-day trial of Coder Premium", + name: "Start an unlimited 30-day Coder trial", level: 2, }), ).toBeInTheDocument();