From 10da27dde8a03afc7cd2c7d95ae4ba8f5798a9ca Mon Sep 17 00:00:00 2001 From: gopu-bruno Date: Mon, 18 May 2026 12:54:07 +0530 Subject: [PATCH] fix: workspace home icon alignment in title bar when already fullscreen (#7967) --- .../src/components/AppTitleBar/index.js | 8 ++ .../src/components/AppTitleBar/index.spec.js | 128 ++++++++++++++++++ packages/bruno-electron/src/index.js | 4 + 3 files changed, 140 insertions(+) create mode 100644 packages/bruno-app/src/components/AppTitleBar/index.spec.js diff --git a/packages/bruno-app/src/components/AppTitleBar/index.js b/packages/bruno-app/src/components/AppTitleBar/index.js index 1010d0a88..3551556dd 100644 --- a/packages/bruno-app/src/components/AppTitleBar/index.js +++ b/packages/bruno-app/src/components/AppTitleBar/index.js @@ -52,6 +52,14 @@ const AppTitleBar = () => { const { ipcRenderer } = window; if (!ipcRenderer) return; + ipcRenderer.invoke('renderer:window-is-fullscreen') + .then((fullscreen) => { + setIsFullScreen(fullscreen); + }) + .catch((error) => { + console.error('Error getting initial fullscreen state:', error); + }); + const removeEnterFullScreenListener = ipcRenderer.on('main:enter-full-screen', () => { setIsFullScreen(true); }); diff --git a/packages/bruno-app/src/components/AppTitleBar/index.spec.js b/packages/bruno-app/src/components/AppTitleBar/index.spec.js new file mode 100644 index 000000000..ec57c3f21 --- /dev/null +++ b/packages/bruno-app/src/components/AppTitleBar/index.spec.js @@ -0,0 +1,128 @@ +import '@testing-library/jest-dom'; +import React from 'react'; +import { render, waitFor, act } from '@testing-library/react'; +import { ThemeProvider } from 'styled-components'; +import { Provider } from 'react-redux'; +import { configureStore } from '@reduxjs/toolkit'; + +jest.mock('ui/MenuDropdown', () => ({ children }) =>
{children}
); +jest.mock('ui/ActionIcon', () => ({ children, onClick, label }) => ( + +)); +jest.mock('components/ResponsePane/ResponseLayoutToggle', () => () => null); + +import AppTitleBar from './index'; + +const theme = { + text: '#333', + sidebar: { + bg: '#fff', + color: '#333', + muted: '#888', + collection: { item: { hoverBg: '#eee' } } + }, + dropdown: { color: '#333', mutedText: '#888', hoverBg: '#eee' } +}; + +const mockStore = configureStore({ + reducer: { + workspaces: (state = { workspaces: [], activeWorkspaceUid: null }) => state, + app: (state = { preferences: {}, sidebarCollapsed: false }) => state, + logs: (state = { isConsoleOpen: false }) => state + } +}); + +const renderWithProviders = () => render( + + + + + +); + +const getTitleBar = (container) => container.querySelector('.app-titlebar'); + +const mockInvokeWithFullscreen = (isFullScreen) => jest.fn((channel) => { + if (channel === 'renderer:window-is-fullscreen') return Promise.resolve(isFullScreen); + return Promise.resolve(false); +}); + +describe('AppTitleBar — fullscreen state sync', () => { + let ipcListeners; + + beforeEach(() => { + ipcListeners = {}; + window.ipcRenderer = { + invoke: jest.fn().mockResolvedValue(false), + send: jest.fn(), + on: jest.fn((channel, cb) => { + ipcListeners[channel] = cb; + return jest.fn(); + }) + }; + }); + + afterEach(() => { + delete window.ipcRenderer; + }); + + describe('initial state on mount', () => { + it('should query the main process for current fullscreen state', async () => { + renderWithProviders(); + await waitFor(() => { + expect(window.ipcRenderer.invoke).toHaveBeenCalledWith('renderer:window-is-fullscreen'); + }); + }); + + it('should apply fullscreen class when window is already fullscreen at mount', async () => { + window.ipcRenderer.invoke = mockInvokeWithFullscreen(true); + + const { container } = renderWithProviders(); + + await waitFor(() => { + expect(getTitleBar(container)).toHaveClass('fullscreen'); + }); + }); + + it('should not apply fullscreen class when window is windowed at mount', async () => { + const { container } = renderWithProviders(); + + await waitFor(() => { + expect(window.ipcRenderer.invoke).toHaveBeenCalledWith('renderer:window-is-fullscreen'); + }); + expect(getTitleBar(container)).not.toHaveClass('fullscreen'); + }); + }); + + describe('fullscreen transitions after mount', () => { + it('should add fullscreen class on main:enter-full-screen event', async () => { + const { container } = renderWithProviders(); + + await waitFor(() => { + expect(window.ipcRenderer.invoke).toHaveBeenCalledWith('renderer:window-is-fullscreen'); + }); + + act(() => { + ipcListeners['main:enter-full-screen'](); + }); + + expect(getTitleBar(container)).toHaveClass('fullscreen'); + }); + + it('should remove fullscreen class on main:leave-full-screen event', async () => { + window.ipcRenderer.invoke = mockInvokeWithFullscreen(true); + + const { container } = renderWithProviders(); + + await waitFor(() => { + expect(getTitleBar(container)).toHaveClass('fullscreen'); + }); + + act(() => { + ipcListeners['main:leave-full-screen'](); + }); + + expect(getTitleBar(container)).not.toHaveClass('fullscreen'); + }); + }); +}); diff --git a/packages/bruno-electron/src/index.js b/packages/bruno-electron/src/index.js index db1c7d509..bacd42dd0 100644 --- a/packages/bruno-electron/src/index.js +++ b/packages/bruno-electron/src/index.js @@ -275,6 +275,10 @@ app.on('ready', async () => { return mainWindow.isMaximized(); }); + ipcMain.handle('renderer:window-is-fullscreen', () => { + return mainWindow.isFullScreen(); + }); + ipcMain.handle('renderer:open-preferences', () => { ipcMain.emit('main:open-preferences'); });