Skip to content

Commit 10da27d

Browse files
authored
fix: workspace home icon alignment in title bar when already fullscreen (usebruno#7967)
1 parent 55774a8 commit 10da27d

3 files changed

Lines changed: 140 additions & 0 deletions

File tree

packages/bruno-app/src/components/AppTitleBar/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ const AppTitleBar = () => {
5252
const { ipcRenderer } = window;
5353
if (!ipcRenderer) return;
5454

55+
ipcRenderer.invoke('renderer:window-is-fullscreen')
56+
.then((fullscreen) => {
57+
setIsFullScreen(fullscreen);
58+
})
59+
.catch((error) => {
60+
console.error('Error getting initial fullscreen state:', error);
61+
});
62+
5563
const removeEnterFullScreenListener = ipcRenderer.on('main:enter-full-screen', () => {
5664
setIsFullScreen(true);
5765
});
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import '@testing-library/jest-dom';
2+
import React from 'react';
3+
import { render, waitFor, act } from '@testing-library/react';
4+
import { ThemeProvider } from 'styled-components';
5+
import { Provider } from 'react-redux';
6+
import { configureStore } from '@reduxjs/toolkit';
7+
8+
jest.mock('ui/MenuDropdown', () => ({ children }) => <div>{children}</div>);
9+
jest.mock('ui/ActionIcon', () => ({ children, onClick, label }) => (
10+
<button onClick={onClick} aria-label={label}>{children}</button>
11+
));
12+
jest.mock('components/ResponsePane/ResponseLayoutToggle', () => () => null);
13+
14+
import AppTitleBar from './index';
15+
16+
const theme = {
17+
text: '#333',
18+
sidebar: {
19+
bg: '#fff',
20+
color: '#333',
21+
muted: '#888',
22+
collection: { item: { hoverBg: '#eee' } }
23+
},
24+
dropdown: { color: '#333', mutedText: '#888', hoverBg: '#eee' }
25+
};
26+
27+
const mockStore = configureStore({
28+
reducer: {
29+
workspaces: (state = { workspaces: [], activeWorkspaceUid: null }) => state,
30+
app: (state = { preferences: {}, sidebarCollapsed: false }) => state,
31+
logs: (state = { isConsoleOpen: false }) => state
32+
}
33+
});
34+
35+
const renderWithProviders = () => render(
36+
<Provider store={mockStore}>
37+
<ThemeProvider theme={theme}>
38+
<AppTitleBar />
39+
</ThemeProvider>
40+
</Provider>
41+
);
42+
43+
const getTitleBar = (container) => container.querySelector('.app-titlebar');
44+
45+
const mockInvokeWithFullscreen = (isFullScreen) => jest.fn((channel) => {
46+
if (channel === 'renderer:window-is-fullscreen') return Promise.resolve(isFullScreen);
47+
return Promise.resolve(false);
48+
});
49+
50+
describe('AppTitleBar — fullscreen state sync', () => {
51+
let ipcListeners;
52+
53+
beforeEach(() => {
54+
ipcListeners = {};
55+
window.ipcRenderer = {
56+
invoke: jest.fn().mockResolvedValue(false),
57+
send: jest.fn(),
58+
on: jest.fn((channel, cb) => {
59+
ipcListeners[channel] = cb;
60+
return jest.fn();
61+
})
62+
};
63+
});
64+
65+
afterEach(() => {
66+
delete window.ipcRenderer;
67+
});
68+
69+
describe('initial state on mount', () => {
70+
it('should query the main process for current fullscreen state', async () => {
71+
renderWithProviders();
72+
await waitFor(() => {
73+
expect(window.ipcRenderer.invoke).toHaveBeenCalledWith('renderer:window-is-fullscreen');
74+
});
75+
});
76+
77+
it('should apply fullscreen class when window is already fullscreen at mount', async () => {
78+
window.ipcRenderer.invoke = mockInvokeWithFullscreen(true);
79+
80+
const { container } = renderWithProviders();
81+
82+
await waitFor(() => {
83+
expect(getTitleBar(container)).toHaveClass('fullscreen');
84+
});
85+
});
86+
87+
it('should not apply fullscreen class when window is windowed at mount', async () => {
88+
const { container } = renderWithProviders();
89+
90+
await waitFor(() => {
91+
expect(window.ipcRenderer.invoke).toHaveBeenCalledWith('renderer:window-is-fullscreen');
92+
});
93+
expect(getTitleBar(container)).not.toHaveClass('fullscreen');
94+
});
95+
});
96+
97+
describe('fullscreen transitions after mount', () => {
98+
it('should add fullscreen class on main:enter-full-screen event', async () => {
99+
const { container } = renderWithProviders();
100+
101+
await waitFor(() => {
102+
expect(window.ipcRenderer.invoke).toHaveBeenCalledWith('renderer:window-is-fullscreen');
103+
});
104+
105+
act(() => {
106+
ipcListeners['main:enter-full-screen']();
107+
});
108+
109+
expect(getTitleBar(container)).toHaveClass('fullscreen');
110+
});
111+
112+
it('should remove fullscreen class on main:leave-full-screen event', async () => {
113+
window.ipcRenderer.invoke = mockInvokeWithFullscreen(true);
114+
115+
const { container } = renderWithProviders();
116+
117+
await waitFor(() => {
118+
expect(getTitleBar(container)).toHaveClass('fullscreen');
119+
});
120+
121+
act(() => {
122+
ipcListeners['main:leave-full-screen']();
123+
});
124+
125+
expect(getTitleBar(container)).not.toHaveClass('fullscreen');
126+
});
127+
});
128+
});

packages/bruno-electron/src/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,10 @@ app.on('ready', async () => {
275275
return mainWindow.isMaximized();
276276
});
277277

278+
ipcMain.handle('renderer:window-is-fullscreen', () => {
279+
return mainWindow.isFullScreen();
280+
});
281+
278282
ipcMain.handle('renderer:open-preferences', () => {
279283
ipcMain.emit('main:open-preferences');
280284
});

0 commit comments

Comments
 (0)