diff --git a/packages/renderer/src/lib/help/HelpActions.spec.ts b/packages/renderer/src/lib/help/HelpActions.spec.ts index 143cf97b7..d6513e44e 100644 --- a/packages/renderer/src/lib/help/HelpActions.spec.ts +++ b/packages/renderer/src/lib/help/HelpActions.spec.ts @@ -26,28 +26,77 @@ import { Items } from './HelpItems'; let toggleMenuCallback: () => void; +const receiveEventMock = vi.fn(); + suite('HelpActions component', () => { beforeEach(() => { vi.resetAllMocks(); (window as any).events = { - receive: vi.fn(), + receive: receiveEventMock, }; (window as any).ResizeObserver = vi.fn().mockReturnValue({ observe: vi.fn(), unobserve: vi.fn() }); - vi.mocked(window.events.receive).mockImplementation((channel: string, callback: () => void) => { + }); + + test('by default is not visible', () => { + const ha = render(HelpActions); + const items = ha.queryAllByTitle(Items[0].title); + expect(items).toHaveLength(0); + }); + + test('simulate clicking outside the menu closes it', async () => { + vi.mocked(receiveEventMock).mockImplementation((channel: string, callback: () => void) => { toggleMenuCallback = callback; return { dispose: () => {}, }; }); + const ha = render(HelpActions); + + toggleMenuCallback(); + + await vi.waitFor(() => { + const helpMenu = ha.getByTestId('help-menu'); + expect(helpMenu).toBeVisible(); + }); + + // Click "outside" the menu (body) + const event = new MouseEvent('click', { bubbles: true }); + document.body.dispatchEvent(event); + + await vi.waitFor(() => { + const helpMenu = ha.queryByTestId('help-menu'); + expect(helpMenu).toBeNull(); + }); }); - test('by default is not visible', () => { - const ha = render(HelpActions); - const items = ha.queryAllByTitle(Items[0].title); - expect(items).toHaveLength(0); + test('create a span that has data-task-button=Help attribute, spy on and make sure that it is only called once each click', async () => { + render(HelpActions); + + // Create data-task-button=Help to simulate the status bar icon / button + const span = document.createElement('span'); + span.setAttribute('data-task-button', 'Help'); + document.body.appendChild(span); + + // Click + const event = new MouseEvent('click', { bubbles: true, cancelable: true }); + span.dispatchEvent(event); + + // Expect receiveEventMock to have been called + // why we do this is because we are mocking receiveEvent already, so we're not "toggling" it + // this test ensures that the event is only called once / we are toggling correctly. + expect(receiveEventMock).toHaveBeenCalledTimes(1); + + // Remove the span after (unsure if needed, but dont want to break other tests) + span.remove(); }); test.each(Items)('contains item with $title', async ({ title, tooltip }) => { + vi.mocked(receiveEventMock).mockImplementation((channel: string, callback: () => void) => { + toggleMenuCallback = callback; + return { + dispose: () => {}, + }; + }); const ha = render(HelpActions); toggleMenuCallback(); await vi.waitFor(async () => { diff --git a/packages/renderer/src/lib/help/HelpActions.svelte b/packages/renderer/src/lib/help/HelpActions.svelte index c8ef265d8..08f184022 100644 --- a/packages/renderer/src/lib/help/HelpActions.svelte +++ b/packages/renderer/src/lib/help/HelpActions.svelte @@ -19,7 +19,11 @@ function handleEscape({ key }: KeyboardEvent): void { } function onWindowClick(e: any): void { - showMenu = outsideWindow.contains(e.target); + const target = e.target as HTMLElement; + // Listen to anything **but** the button that has "data-task-button" attribute with a value of "Help" + if (target && target.getAttribute('data-task-button') !== 'Help') { + showMenu = outsideWindow.contains(e.target); + } } async function onClick(action?: ItemAction): Promise { diff --git a/packages/renderer/src/lib/help/HelpMenu.svelte b/packages/renderer/src/lib/help/HelpMenu.svelte index 6a3f60d36..c2b496fc9 100644 --- a/packages/renderer/src/lib/help/HelpMenu.svelte +++ b/packages/renderer/src/lib/help/HelpMenu.svelte @@ -24,7 +24,8 @@ onDestroy(() => window.removeEventListener('resize', updateMenuLocation)); bind:offsetHeight={dropDownHeight} bind:offsetWidth={dropDownWidth} bind:this={dropDownElement} - class="absolute"> + class="absolute" + data-testid="help-menu">
diff --git a/packages/renderer/src/lib/statusbar/StatusBarItem.svelte b/packages/renderer/src/lib/statusbar/StatusBarItem.svelte index 83ec633cc..21a860eb3 100644 --- a/packages/renderer/src/lib/statusbar/StatusBarItem.svelte +++ b/packages/renderer/src/lib/statusbar/StatusBarItem.svelte @@ -42,7 +42,7 @@ async function executeCommand(entry: StatusBarEntry) { )} relative inline-block" title={tooltipText(entry)}> {#if iconClass(entry)} - + {/if} {#if entry.text} {entry.text}