open-design/apps/web/tests/components/BoardComposerPopover.pod-chip-hover.test.tsx
chaoxiaoche fce444bcab
Consolidate chat comments preview on main (#2906)
* feat(web): queue chat sends

* feat(web): render code comment directives

* feat(web): add preview comments and manual edits

* fix(web): polish shared chrome controls

* fix(web): align queued send loading state

* feat(web): open primary project artifacts

* fix(web): keep queued sends and tests aligned

* fix(web): restore docked comment tools layout

* fix(web): align preview comment toolbar

* fix(web): place local cli beside handoff

* fix(web): move agent menu beside handoff

* fix(web): make project instructions a direct header action

* fix(web): compact handoff and toolbar labels

* fix(web): clarify handoff menu and annotation label

* fix(web): restore compact cursor handoff trigger

* fix(web): align agent menu trigger with handoff

* fix(web): add draw toolbar close action

* fix(web): move inspect editing into edit mode

* fix(web): avoid reserving comment sidebar in annotation mode

* fix(web): float preview comments panel

* fix(web): keep edit canvas full width

* fix(web): polish preview annotation tools

* fix(web): highlight active preview comments

* fix(web): open comments panel after annotation save

* fix(web): polish comment handoff controls

* fix(web): remove palette preview tool

* fix(web): simplify draw annotation toolbar

* fix(web): restore queued tasks into composer

* fix(web): restore queued send strip styling

* fix(web): hide internal comment target ids

* fix(web): align manual edit panel header

* test(web): cover visual interaction contracts

* fix(web): address PR feedback regressions

* fix(web): preserve artifact chrome state

* fix(daemon): restore project raw file routes

---------

Co-authored-by: chaoxiaoche <chaoxiaoche@chaoxiaochedeMacBook-Pro.local>
Co-authored-by: mrcfps <mrc@powerformer.com>
2026-05-26 10:31:19 +00:00

127 lines
3.8 KiB
TypeScript

// @vitest-environment jsdom
import { cleanup, fireEvent, render, screen, within } from '@testing-library/react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { BoardComposerPopover } from '../../src/components/BoardComposerPopover';
import type { PreviewCommentSnapshot } from '../../src/comments';
import type { PreviewCommentMember } from '../../src/types';
afterEach(() => {
cleanup();
});
function member(elementId: string, label = elementId): PreviewCommentMember {
return {
elementId,
selector: `#${elementId}`,
label,
text: '',
position: { x: 0, y: 0, width: 10, height: 10 },
htmlHint: '',
};
}
function podTarget(members: PreviewCommentMember[]): PreviewCommentSnapshot {
return {
filePath: 'index.html',
elementId: 'pod-1',
selector: '',
label: 'Pod',
text: '',
position: { x: 0, y: 0, width: 100, height: 60 },
htmlHint: '',
selectionKind: 'pod',
memberCount: members.length,
podMembers: members,
};
}
function renderPopover(overrides: {
target: PreviewCommentSnapshot;
onHoverMember?: (elementId: string | null) => void;
}) {
return render(
<BoardComposerPopover
target={overrides.target}
existing={null}
draft=""
notes={[]}
onDraft={() => {}}
onAddDraft={() => {}}
onRemoveQueuedNote={() => {}}
onClose={() => {}}
onSaveComment={() => {}}
onSendBatch={() => {}}
onRemoveMember={() => {}}
onHoverMember={overrides.onHoverMember}
sending={false}
t={((key: string) => String(key)) as never}
/>,
);
}
function chipFor(label: string): HTMLElement {
const chip = screen.getByText(label).closest('.board-pod-chip');
if (!chip) throw new Error(`chip for ${label} not rendered`);
return chip as HTMLElement;
}
describe('BoardComposerPopover captured-chip hover', () => {
it('reports the elementId when the pointer enters a chip and clears on leave', () => {
const onHoverMember = vi.fn();
renderPopover({
target: podTarget([member('alpha', 'Alpha'), member('beta', 'Beta')]),
onHoverMember,
});
fireEvent.pointerEnter(chipFor('Alpha'));
expect(onHoverMember).toHaveBeenLastCalledWith('alpha');
fireEvent.pointerLeave(chipFor('Alpha'));
expect(onHoverMember).toHaveBeenLastCalledWith(null);
});
it('reports the elementId from keyboard focus on the chip remove button', () => {
const onHoverMember = vi.fn();
renderPopover({
target: podTarget([member('alpha', 'Alpha'), member('beta', 'Beta')]),
onHoverMember,
});
const betaRemove = within(chipFor('Beta')).getByRole('button');
fireEvent.focus(betaRemove);
expect(onHoverMember).toHaveBeenLastCalledWith('beta');
fireEvent.blur(betaRemove);
expect(onHoverMember).toHaveBeenLastCalledWith(null);
});
it('ignores pointer events from touch and pen so a tap on a chip does not flicker the highlight', () => {
const onHoverMember = vi.fn();
renderPopover({
target: podTarget([member('alpha', 'Alpha')]),
onHoverMember,
});
fireEvent.pointerEnter(chipFor('Alpha'), { pointerType: 'touch' });
fireEvent.pointerLeave(chipFor('Alpha'), { pointerType: 'touch' });
fireEvent.pointerEnter(chipFor('Alpha'), { pointerType: 'pen' });
fireEvent.pointerLeave(chipFor('Alpha'), { pointerType: 'pen' });
expect(onHoverMember).not.toHaveBeenCalled();
fireEvent.pointerEnter(chipFor('Alpha'), { pointerType: 'mouse' });
expect(onHoverMember).toHaveBeenLastCalledWith('alpha');
});
it('does not throw when onHoverMember is omitted', () => {
expect(() =>
renderPopover({
target: podTarget([member('alpha', 'Alpha')]),
}),
).not.toThrow();
expect(() => fireEvent.pointerEnter(chipFor('Alpha'))).not.toThrow();
expect(() => fireEvent.pointerLeave(chipFor('Alpha'))).not.toThrow();
});
});