// @vitest-environment jsdom // // The title bar: borderless actions that go accent when on, and the fullscreen // shift (macOS hides the traffic lights, so the project name moves to the edge). import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' import { act, cleanup, render, waitFor } from '@testing-library/react' import React from 'react' vi.mock('../src/renderer/src/terminals', () => { let n = 0 return { Terminal: () => null, lid: () => ++n } }) import { App } from '../src/renderer/src/App' import { ProjectProvider } from '../src/renderer/src/project' /** Fires the fullscreen callbacks the App subscribed to. */ let fullscreenCbs: ((on: boolean) => void)[] = [] function stubBridge(): void { const noop = (): void => {} const off = (): (() => void) => noop ;(window as unknown as { helder: unknown }).helder = { platform: 'darwin', clipboard: { writeText: noop, readText: () => '' }, project: { current: async () => ({ root: '/repo', name: 'repo' }), open: async () => ({ root: '/repo', name: 'repo' }), openPath: async () => ({ root: '/repo', name: 'repo' }), recent: async () => [], }, fs: { tree: async () => ({ name: 'repo', type: 'dir', path: '', children: [] }), readDir: async () => [], files: async () => ({}), read: async () => '', imageDataUrl: async () => '', write: async () => {}, delete: async () => {}, create: async () => {}, mkdir: async () => {}, }, shell: { reveal: noop }, notes: { read: async () => '', write: async () => {} }, git: { load: async () => ({ branch: 'main', changes: [] }), stage: async () => {}, unstage: async () => {}, commit: async () => {}, push: async () => ({ ok: true, message: '' }), discard: async () => {}, }, pty: { available: async () => false, create: async () => 1, write: noop, resize: noop, kill: noop, onData: off, onExit: off }, config: { get: async () => (await import('../src/renderer/src/types')).DEFAULT_CONFIG, theme: async () => '' }, recent: { get: async () => [], set: async () => {} }, search: { content: async () => [], files: async () => [] }, dialog: { unsavedClose: async () => 'cancel' }, log: { write: noop, path: async () => null, open: async () => {}, reveal: async () => {} }, onFullscreen: (cb: (on: boolean) => void) => { fullscreenCbs.push(cb) return () => { fullscreenCbs = fullscreenCbs.filter((f) => f !== cb) } }, onProjectChanged: off, onConfigChanged: off, onRefresh: off, } } beforeAll(() => { globalThis.ResizeObserver = class { observe(): void {} unobserve(): void {} disconnect(): void {} } as never if (!Element.prototype.scrollIntoView) Element.prototype.scrollIntoView = (): void => {} }) beforeEach(() => { fullscreenCbs = []; stubBridge() }) afterEach(() => { cleanup() localStorage.clear() delete (window as unknown as { helder?: unknown }).helder }) async function boot(): Promise { const c = render().container await waitFor(() => { if (!c.querySelector('.git-foot')) throw new Error('not ready') }) return c } function setFullscreen(on: boolean): void { act(() => { fullscreenCbs.forEach((cb) => cb(on)) }) } describe('title bar', () => { it('shifts left in fullscreen and back out again', async () => { const c = await boot() const bar = c.querySelector('.titlebar') as HTMLElement expect(bar.className).not.toContain('fullscreen') await waitFor(() => expect(fullscreenCbs.length).toBe(1)) setFullscreen(true) expect(bar.className).toContain('fullscreen') setFullscreen(false) expect(bar.className).not.toContain('fullscreen') }) it('shows the state with the accent, not an On/Off badge', async () => { const c = await boot() const hidden = [...c.querySelectorAll('.titlebar .tb-btn')] .find((b) => b.textContent?.includes('Hidden')) as HTMLButtonElement expect(c.querySelector('.titlebar .tb-state')).toBeNull() expect(hidden.className).not.toContain('on') act(() => { hidden.click() }) await waitFor(() => expect(hidden.className).toContain('on')) expect(hidden.textContent).not.toContain('On') }) })