@@ -6,8 +6,10 @@ vi.mock('electron', () => import('@/test/electron-mock'))
66import { Menu , WebContentsView } from 'electron'
77import {
88 attachAgentContextMenu ,
9+ BASE_ZOOM_FACTOR ,
910 buildAgentContextMenuTemplate ,
1011 steppedZoomFactor ,
12+ zoomPercentOf ,
1113} from '@/main/browser-agent/context-menu'
1214
1315const EDIT_FLAGS : ContextMenuParams [ 'editFlags' ] = {
@@ -30,7 +32,8 @@ function params(overrides: Partial<Params> = {}): Params {
3032}
3133
3234function page ( overrides : Partial < Page > = { } ) : Page {
33- return { canGoBack : true , canGoForward : true , zoomFactor : 1 , ...overrides }
35+ // A fresh tab sits at the panel's baseline, which the menu reports as 100%.
36+ return { canGoBack : true , canGoForward : true , zoomFactor : BASE_ZOOM_FACTOR , ...overrides }
3437}
3538
3639function handlers ( ) : Handlers {
@@ -130,7 +133,10 @@ describe('buildAgentContextMenuTemplate', () => {
130133 } )
131134
132135 it ( 'reports the current zoom and disables the ends of the ladder' , ( ) => {
133- const stepped = buildAgentContextMenuTemplate ( params ( ) , page ( { zoomFactor : 1.21 } ) , handlers ( ) )
136+ // Two rungs up from the baseline, reported against the baseline rather than
137+ // against Chromium's native scale (where this factor would read 110%).
138+ const twoUp = steppedZoomFactor ( steppedZoomFactor ( BASE_ZOOM_FACTOR , 1 ) , 1 )
139+ const stepped = buildAgentContextMenuTemplate ( params ( ) , page ( { zoomFactor : twoUp } ) , handlers ( ) )
134140 expect ( item ( stepped , 'Actual Size (121%)' ) ?. enabled ) . toBe ( true )
135141
136142 const atMax = buildAgentContextMenuTemplate ( params ( ) , page ( { zoomFactor : 3 } ) , handlers ( ) )
@@ -147,17 +153,16 @@ describe('buildAgentContextMenuTemplate', () => {
147153 ) . toBe ( false )
148154 } )
149155
150- it ( 'resets to exactly 100% , undoing accumulated drift' , ( ) => {
156+ it ( 'resets to exactly the baseline , undoing accumulated drift' , ( ) => {
151157 const handled = handlers ( )
152- const template = buildAgentContextMenuTemplate (
153- params ( ) ,
154- page ( { zoomFactor : 1.3310000000000004 } ) ,
155- handled
156- )
158+ // Three rungs of float multiplication up, so the factor no longer sits on a
159+ // clean value — reset has to restore the baseline exactly, not step back.
160+ const drifted = [ 1 , 1 , 1 ] . reduce ( ( factor ) => steppedZoomFactor ( factor , 1 ) , BASE_ZOOM_FACTOR )
161+ const template = buildAgentContextMenuTemplate ( params ( ) , page ( { zoomFactor : drifted } ) , handled )
157162
158163 item ( template , 'Actual Size (133%)' ) ?. click ?.( { } as never , undefined as never , { } as never )
159164
160- expect ( handled . setZoomFactor ) . toHaveBeenCalledWith ( 1 )
165+ expect ( handled . setZoomFactor ) . toHaveBeenCalledWith ( BASE_ZOOM_FACTOR )
161166 } )
162167
163168 it ( 'never leaves a separator with nothing above it' , ( ) => {
@@ -215,8 +220,41 @@ describe('steppedZoomFactor', () => {
215220 expect ( steppedZoomFactor ( 0.5 , - 1 ) ) . toBe ( 0.5 )
216221 } )
217222
218- it ( 'treats a nonsense factor as 100%' , ( ) => {
219- expect ( steppedZoomFactor ( Number . NaN , 1 ) ) . toBe ( 1.1 )
220- expect ( steppedZoomFactor ( 0 , 1 ) ) . toBe ( 1.1 )
223+ it ( 'treats a nonsense factor as the baseline' , ( ) => {
224+ // One rung up from the baseline is Chromium's native 1.0.
225+ expect ( steppedZoomFactor ( Number . NaN , 1 ) ) . toBe ( 1 )
226+ expect ( steppedZoomFactor ( 0 , 1 ) ) . toBe ( 1 )
227+ } )
228+ } )
229+
230+ describe ( 'BASE_ZOOM_FACTOR' , ( ) => {
231+ it ( 'renders a rung below native but reads as 100%' , ( ) => {
232+ expect ( BASE_ZOOM_FACTOR ) . toBeCloseTo ( 0.909 , 3 )
233+ expect ( zoomPercentOf ( BASE_ZOOM_FACTOR ) ) . toBe ( 100 )
234+ } )
235+
236+ it ( 'keeps the ladder landing exactly on Chromium native one step up' , ( ) => {
237+ expect ( steppedZoomFactor ( BASE_ZOOM_FACTOR , 1 ) ) . toBe ( 1 )
238+ expect ( zoomPercentOf ( 1 ) ) . toBe ( 110 )
239+ } )
240+
241+ it ( 'stays inside the ladder, so the page menu can still step both ways' , ( ) => {
242+ expect ( steppedZoomFactor ( BASE_ZOOM_FACTOR , 1 ) ) . not . toBe ( BASE_ZOOM_FACTOR )
243+ expect ( steppedZoomFactor ( BASE_ZOOM_FACTOR , - 1 ) ) . not . toBe ( BASE_ZOOM_FACTOR )
244+ } )
245+ } )
246+
247+ describe ( 'zoomPercentOf' , ( ) => {
248+ it ( 'reports every rung relative to the panel baseline, not to native' , ( ) => {
249+ expect ( zoomPercentOf ( steppedZoomFactor ( BASE_ZOOM_FACTOR , - 1 ) ) ) . toBe ( 91 )
250+ expect ( zoomPercentOf ( BASE_ZOOM_FACTOR ) ) . toBe ( 100 )
251+ expect ( zoomPercentOf ( steppedZoomFactor ( BASE_ZOOM_FACTOR , 1 ) ) ) . toBe ( 110 )
252+ } )
253+
254+ it ( 'still reads 100% after a round trip up and back down' , ( ) => {
255+ // The ladder is float arithmetic, so the reset item's `!== 100` guard has to
256+ // survive a step that does not return bit-identically to the baseline.
257+ const roundTripped = steppedZoomFactor ( steppedZoomFactor ( BASE_ZOOM_FACTOR , 1 ) , - 1 )
258+ expect ( zoomPercentOf ( roundTripped ) ) . toBe ( 100 )
221259 } )
222260} )
0 commit comments