Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { fireEvent, render, screen } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { InlineSolutionPanel } from './ClusterDetailsErrorOverview';
import { ClusterPriorityError } from './clusterHealth';

vi.mock('@iconify/react', () => ({
Icon: ({ icon }: { icon: string }) => <span data-icon={icon} />,
}));

vi.mock('./capiUtils', async importOriginal => ({
...(await importOriginal<typeof import('./capiUtils')>()),
copyToClipboard: vi.fn().mockResolvedValue(undefined),
}));

const priorityError: ClusterPriorityError = {
errorDef: {
id: 'test-error',
title: 'Test error',
description: 'A test error',
severity: 'warning',
matcher: {},
solution: {
steps: ['do a thing'],
quickFixCommands: [
{ description: 'Fix A', command: 'kubectl apply -f a.yaml' },
{ description: 'Fix B', command: 'kubectl apply -f b.yaml' },
],
},
},
} as ClusterPriorityError;

function copyIconFor(command: string): string {
const row = screen.getByText(command).closest('div')?.parentElement;
return row!.querySelector('[data-icon]')!.getAttribute('data-icon')!;
}

describe('InlineSolutionPanel copy feedback', () => {
beforeEach(() => {
vi.useFakeTimers();
});

afterEach(() => {
vi.useRealTimers();
});

it("does not clear a newer copy's checkmark when an older copy's timeout fires", async () => {
render(<InlineSolutionPanel priorityError={priorityError} open />);

const buttons = screen.getAllByRole('button');
fireEvent.click(buttons[0]); // copy "Fix A"
await vi.advanceTimersByTimeAsync(1000);
fireEvent.click(buttons[1]); // copy "Fix B", within A's 1800ms window

// A's original timeout fires now (1800ms after A's click); B's hasn't yet.
await vi.advanceTimersByTimeAsync(800);

expect(copyIconFor('kubectl apply -f a.yaml')).toBe('mdi:content-copy');
expect(copyIconFor('kubectl apply -f b.yaml')).toBe('mdi:check');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function renderClusterHealthStatus(healthy: boolean, priorityError: ClusterPrior
* @param props.open - Whether the panel is currently expanded or collapsed.
* @returns A Collapse-wrapped React component with solution instructions.
*/
function InlineSolutionPanel({
export function InlineSolutionPanel({
priorityError,
open,
}: {
Expand All @@ -85,7 +85,9 @@ function InlineSolutionPanel({
try {
await copyToClipboard(text);
setCopiedCommand(text);
setTimeout(() => setCopiedCommand(null), 1800);
// only clear if this timeout's command is still the one showing —
// otherwise it clobbers a more recent copy before its own 1800ms is up
setTimeout(() => setCopiedCommand(current => (current === text ? null : current)), 1800);
} catch {
setCopiedCommand(null);
}
Expand Down