From b171f745db767beb651fc2735a63fd83d79b226c Mon Sep 17 00:00:00 2001 From: Zakaria Date: Fri, 23 Oct 2026 11:12:51 -0400 Subject: [PATCH] test: bring stale tests and lint in line with the current UI - CodeDeploy: query the redesigned form's labels. - CloudFormation: expect the parameters and capabilities fields the form now submits. - ALB listeners: advance the five-second minimum create state before deleting, since row actions are disabled while it is pending. - Lightsail: cast the fetch mock as a whole so it type-checks. - TargetGroupPanel: depend on the stable refetch function to satisfy react-hooks/exhaustive-deps. --- .../adapter-aws/AwsLightsailAdapter.test.ts | 4 +-- .../CloudFormationCreatePanel.test.tsx | 2 +- .../src/components/CodeDeployPanel.test.tsx | 26 +++++++++---------- .../LoadBalancerListenerPanel.test.tsx | 15 ++++++++--- .../src/components/TargetGroupPanel.tsx | 5 ++-- 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/packages/api/src/adapter-aws/AwsLightsailAdapter.test.ts b/packages/api/src/adapter-aws/AwsLightsailAdapter.test.ts index 5e7727a..86e7ce2 100644 --- a/packages/api/src/adapter-aws/AwsLightsailAdapter.test.ts +++ b/packages/api/src/adapter-aws/AwsLightsailAdapter.test.ts @@ -60,9 +60,9 @@ describe('AwsLightsailAdapter', () => { test('merges diagnostic host-port mappings into UI metadata', async () => { process.env.FLOCI_ENDPOINT = 'http://core.test:4566' const fetchMock = globalThis.fetch - globalThis.fetch = async () => new Response(JSON.stringify({mappings: [ + globalThis.fetch = (async () => new Response(JSON.stringify({mappings: [ {instancePort: 80, host: 'localhost', hostPort: 18080, protocol: 'tcp'}, - ]})) + ]}))) as unknown as typeof fetch try { const resource = await new AwsLightsailAdapter(stubLightsail().client).get('web') expect(resource?.metadata.hostPortMappings).toEqual([ diff --git a/packages/frontend/src/components/CloudFormationCreatePanel.test.tsx b/packages/frontend/src/components/CloudFormationCreatePanel.test.tsx index 924fc97..88204cd 100644 --- a/packages/frontend/src/components/CloudFormationCreatePanel.test.tsx +++ b/packages/frontend/src/components/CloudFormationCreatePanel.test.tsx @@ -17,6 +17,6 @@ describe('CloudFormationCreatePanel', () => { fireEvent.change(screen.getByPlaceholderText('orders-stack'), {target: {value: 'orders-stack'}}) fireEvent.change(screen.getByLabelText('Template S3 URL'), {target: {value: 'http://templates.localhost:4566/orders.yaml'}}) fireEvent.click(screen.getByRole('button', {name: 'Create stack'})) - expect(onSubmit).toHaveBeenCalledWith({stackName: 'orders-stack', templateBody: '', templateUrl: 'http://templates.localhost:4566/orders.yaml'}) + expect(onSubmit).toHaveBeenCalledWith({stackName: 'orders-stack', templateBody: '', templateUrl: 'http://templates.localhost:4566/orders.yaml', parameters: '{}', capabilities: ''}) }) }) diff --git a/packages/frontend/src/components/CodeDeployPanel.test.tsx b/packages/frontend/src/components/CodeDeployPanel.test.tsx index 09f8576..c31aa9a 100644 --- a/packages/frontend/src/components/CodeDeployPanel.test.tsx +++ b/packages/frontend/src/components/CodeDeployPanel.test.tsx @@ -9,12 +9,12 @@ describe('CodeDeployCreatePanel', () => { const onSubmit = vi.fn() render() - await user.type(screen.getByLabelText('Application name'), 'orders') - await user.type(screen.getByLabelText('Deployment group name'), 'production') - await user.type(screen.getByLabelText('CodeDeploy service role ARN'), 'role-arn') + await user.type(screen.getByLabelText(/^Application name/), 'orders') + await user.type(screen.getByLabelText(/^Deployment group name/), 'production') + await user.type(screen.getByLabelText(/^Service role ARN/), 'role-arn') await user.click(screen.getByRole('button', {name: 'Create deployment group'})) - expect(screen.queryByLabelText('ECS cluster name')).not.toBeInTheDocument() + expect(screen.queryByLabelText(/^Cluster name/)).not.toBeInTheDocument() expect(onSubmit).toHaveBeenCalledWith({applicationName: 'orders', deploymentGroupName: 'production', computePlatform: 'Lambda', serviceRoleArn: 'role-arn', deploymentConfig: 'AllAtOnce', rollbackOnFailure: true}) }) @@ -24,16 +24,16 @@ describe('CodeDeployCreatePanel', () => { render() await user.selectOptions(screen.getByLabelText('Compute platform'), 'ECS') - await user.type(screen.getByLabelText('Application name'), 'orders') - await user.type(screen.getByLabelText('Deployment group name'), 'production') - await user.type(screen.getByLabelText('CodeDeploy service role ARN'), 'role-arn') - await user.type(screen.getByLabelText('ECS cluster name'), 'orders-cluster') - await user.type(screen.getByLabelText('ECS service name'), 'orders-service') - await user.type(screen.getByLabelText('Blue target group name'), 'orders-blue') - await user.type(screen.getByLabelText('Green target group name'), 'orders-green') - await user.type(screen.getByLabelText('Production listener ARN'), 'listener-arn') + await user.type(screen.getByLabelText(/^Application name/), 'orders') + await user.type(screen.getByLabelText(/^Deployment group name/), 'production') + await user.type(screen.getByLabelText(/^Service role ARN/), 'role-arn') + await user.type(screen.getByLabelText(/^Cluster name/), 'orders-cluster') + await user.type(screen.getByLabelText(/^Service name/), 'orders-service') + await user.type(screen.getByLabelText(/Current production target group/), 'orders-blue') + await user.type(screen.getByLabelText(/Replacement target group/), 'orders-green') + await user.type(screen.getByLabelText(/^Production listener ARN/), 'listener-arn') await user.selectOptions(screen.getByLabelText('Traffic configuration'), 'Linear10PercentEvery3Minutes') - await user.click(screen.getByLabelText('Roll back automatically on deployment failure')) + await user.click(screen.getByLabelText(/Automatic rollback/)) await user.click(screen.getByRole('button', {name: 'Create deployment group'})) expect(onSubmit).toHaveBeenCalledWith({applicationName: 'orders', deploymentGroupName: 'production', computePlatform: 'ECS', serviceRoleArn: 'role-arn', clusterName: 'orders-cluster', serviceName: 'orders-service', blueTargetGroupName: 'orders-blue', greenTargetGroupName: 'orders-green', productionListenerArn: 'listener-arn', deploymentConfig: 'Linear10PercentEvery3Minutes', rollbackOnFailure: false}) diff --git a/packages/frontend/src/components/LoadBalancerListenerPanel.test.tsx b/packages/frontend/src/components/LoadBalancerListenerPanel.test.tsx index 2fe2fa9..c7dfe5c 100644 --- a/packages/frontend/src/components/LoadBalancerListenerPanel.test.tsx +++ b/packages/frontend/src/components/LoadBalancerListenerPanel.test.tsx @@ -1,7 +1,7 @@ -import {render, screen} from '@testing-library/react' +import {render, screen, waitFor} from '@testing-library/react' import userEvent from '@testing-library/user-event' import {QueryClient, QueryClientProvider} from '@tanstack/react-query' -import {expect, test, vi} from 'vitest' +import {afterEach, expect, test, vi} from 'vitest' import {LoadBalancerListenerPanel} from './LoadBalancerListenerPanel' const api = vi.hoisted(() => ({ @@ -14,8 +14,13 @@ const api = vi.hoisted(() => ({ vi.mock('@/api/cloudProxyClient', () => api) vi.mock('@/lib/accountStore', () => ({useAccountId: () => '000000000000'})) +afterEach(() => { + vi.useRealTimers() +}) + test('creates and deletes selected ALB HTTP listeners through generic cloud proxy items', async () => { - const user = userEvent.setup() + vi.useFakeTimers({shouldAdvanceTime: true}) + const user = userEvent.setup({advanceTimers: vi.advanceTimersByTime}) api.listCloudResources.mockResolvedValue([{id: 'target-group-arn', name: 'web-targets'}]) api.listFlatItems.mockResolvedValue({items: [{id: 'listener-arn', body: {protocol: 'HTTP', port: 80, targetGroupArn: 'target-group-arn'}}], nextCursor: null}) api.putFlatItem.mockResolvedValue({}) @@ -32,6 +37,10 @@ test('creates and deletes selected ALB HTTP listeners through generic cloud prox await user.click(screen.getByRole('button', {name: /create listener/i})) expect(api.putFlatItem).toHaveBeenCalledWith('aws', 'loadbalancing', 'load-balancer-arn', {port: 8080, targetGroupArn: 'target-group-arn'}) + // Creation holds its pending state for a minimum of five seconds, which disables the row actions. + await vi.advanceTimersByTimeAsync(5_000) + await waitFor(() => expect(screen.getByRole('button', {name: /delete listener 80/i})).toBeEnabled()) + await user.click(screen.getByRole('button', {name: /delete listener 80/i})) expect(api.deleteFlatItem).toHaveBeenCalledWith('aws', 'loadbalancing', 'load-balancer-arn', 'listener-arn') }) diff --git a/packages/frontend/src/components/TargetGroupPanel.tsx b/packages/frontend/src/components/TargetGroupPanel.tsx index 17c3c25..13f1ad8 100644 --- a/packages/frontend/src/components/TargetGroupPanel.tsx +++ b/packages/frontend/src/components/TargetGroupPanel.tsx @@ -31,6 +31,7 @@ export function TargetGroupPanel({resource, runtimeReachable}: Props) { queryFn: ({signal}) => listFlatItems('aws', 'targetgroups', resource?.id ?? '', undefined, signal), enabled: Boolean(resource) && runtimeReachable, }) + const refetchHealth = healthQuery.refetch useEffect(() => { setInstanceId('') @@ -50,10 +51,10 @@ export function TargetGroupPanel({resource, runtimeReachable}: Props) { return } const interval = window.setInterval(() => { - void healthQuery.refetch() + void refetchHealth() }, 1_000) return () => window.clearInterval(interval) - }, [pendingTargets.length, healthQuery.refetch]) + }, [pendingTargets.length, refetchHealth]) const refresh = () => { void Promise.all([dependenciesQuery.refetch(), healthQuery.refetch()])