mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-12 16:02:48 +08:00
🐛 fix(window): 直接调 WebView2 zoom factor 零感知修复 Windows 字体异常
- 新增 ResetWebViewZoom RPC:从 ctx 反射拿 Wails 内部 *edge.Chromium,调 PutZoomFactor(1.0) 强制 WebView2 重算 D2D/DirectWrite 字体度量,完全不动窗口零动画
- 自动路径:maximised + restore + drift 时直接调 backend reset,告别 9848b8b2 之后字体偶发变大的取舍
- 手动路径:保留 Ctrl+Shift+0 快捷键作为兜底(优先 WebView2 reset,失败回退 toggle)
- 撤回上一版 CSS zoom nudge:实测在 WebView2 上不能修字体度量(度量缓存在 D2D 层不在 Chromium layout 层)
- 反射做了 3 层签名校验(frontend value / chromium 字段 / PutZoomFactor 方法签名),wails 升级破坏接口时返回 error 不让进程崩溃
- 新增 windows-only 反射逻辑测试 4 个、跨平台 RPC 行为测试 2 个、Ctrl+Shift+0 快捷键注册测试
This commit is contained in:
@@ -58,7 +58,7 @@ import {
|
||||
type SecurityUpdateRepairSource,
|
||||
type SecurityUpdateSettingsFocusTarget,
|
||||
} from './utils/securityUpdateRepairFlow';
|
||||
import { applyWindowsViewportZoomNudge, getWindowsScaleFixNudgedWidth, hasWindowsViewportScaleDrift } from './utils/windowsScaleFix';
|
||||
import { getWindowsScaleFixNudgedWidth, hasWindowsViewportScaleDrift } from './utils/windowsScaleFix';
|
||||
import {
|
||||
SHORTCUT_ACTION_META,
|
||||
SHORTCUT_ACTION_ORDER,
|
||||
@@ -670,11 +670,20 @@ function App() {
|
||||
|
||||
if (isMaximised) {
|
||||
if (!shouldToggleMaximisedWindowForScaleFix(reason, hasViewportScaleDrift)) {
|
||||
// restore 场景刻意不走 Unmaximise→Maximise(避免可见的重复最大化抖动)。
|
||||
// 改用 CSS zoom nudge 强制 Chromium 重算 layout,让 viewport drift 后残留的
|
||||
// 字体度量恢复正确——同样能修字体,且无动画。
|
||||
// restore + drift(任务栏点击恢复后字体异常变大)的零感知修复路径:
|
||||
// 调 backend App.ResetWebViewZoom 触发 WebView2 ICoreWebView2Controller::put_ZoomFactor(1.0),
|
||||
// 让 WebView2 重算 D2D/DirectWrite 字体度量。完全不动窗口、零动画。
|
||||
// backend 失败(wails 升级破坏反射 / 非 Windows)时回退到 dispatch resize 兜底;
|
||||
// 用户仍可按 Ctrl+Shift+0 手动 toggle 修复。
|
||||
if (hasViewportScaleDrift) {
|
||||
applyWindowsViewportZoomNudge();
|
||||
try {
|
||||
const res = await (window as any).go?.app?.App?.ResetWebViewZoom?.();
|
||||
if (!res?.success) {
|
||||
console.warn('ResetWebViewZoom unavailable in fixWindowScaleIfNeeded:', res?.message);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('ResetWebViewZoom call failed in fixWindowScaleIfNeeded', e);
|
||||
}
|
||||
}
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
lastFixAt = Date.now();
|
||||
@@ -2317,6 +2326,57 @@ function App() {
|
||||
}
|
||||
void handleTitleBarWindowToggle();
|
||||
};
|
||||
|
||||
// handleManualResetWindowZoom 由 resetWindowZoom 快捷键(默认 Ctrl+Shift+0)触发,
|
||||
// 作为自动路径失败时的兜底入口。
|
||||
//
|
||||
// 优先调 backend App.ResetWebViewZoom 走 WebView2 zoom reset(零动画零感知);
|
||||
// 失败时回退到 Unmaximise→Maximise toggle —— 用户主动按了快捷键,预期看见动画。
|
||||
const handleManualResetWindowZoom = React.useCallback(async () => {
|
||||
if (!isWindowsPlatform()) {
|
||||
message.info('该功能仅在 Windows 平台生效');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await (window as any).go?.app?.App?.ResetWebViewZoom?.();
|
||||
if (res?.success) {
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
message.success('已重置窗口缩放');
|
||||
return;
|
||||
}
|
||||
console.warn('ResetWebViewZoom backend reported failure, falling back to maximise toggle:', res?.message);
|
||||
} catch (e) {
|
||||
console.warn('ResetWebViewZoom backend unavailable, falling back to maximise toggle', e);
|
||||
}
|
||||
try {
|
||||
const isFullscreen = await WindowIsFullscreen().catch(() => false);
|
||||
if (isFullscreen) {
|
||||
message.info('全屏状态下无法重置缩放,请先退出全屏');
|
||||
return;
|
||||
}
|
||||
const isMaximised = await WindowIsMaximised().catch(() => false);
|
||||
if (isMaximised) {
|
||||
WindowUnmaximise();
|
||||
await new Promise((resolve) => window.setTimeout(resolve, 96));
|
||||
WindowMaximise();
|
||||
await new Promise((resolve) => window.setTimeout(resolve, 96));
|
||||
} else {
|
||||
const size = await WindowGetSize().catch(() => null);
|
||||
const width = Math.trunc(Number(size?.w) || 0);
|
||||
const height = Math.trunc(Number(size?.h) || 0);
|
||||
if (width > 0 && height > 0) {
|
||||
WindowSetSize(getWindowsScaleFixNudgedWidth(width), height);
|
||||
await new Promise((resolve) => window.setTimeout(resolve, 28));
|
||||
WindowSetSize(width, height);
|
||||
}
|
||||
}
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
message.success('已重置窗口缩放(回退方案)');
|
||||
} catch (e) {
|
||||
console.warn('重置窗口缩放失败', e);
|
||||
message.error('重置窗口缩放失败');
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Sidebar Resizing
|
||||
const sidebarDragRef = React.useRef<{ startX: number, startWidth: number } | null>(null);
|
||||
@@ -2576,6 +2636,9 @@ function App() {
|
||||
void handleTitleBarWindowToggle();
|
||||
}
|
||||
break;
|
||||
case 'resetWindowZoom':
|
||||
void handleManualResetWindowZoom();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2583,7 +2646,7 @@ function App() {
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleGlobalShortcut);
|
||||
};
|
||||
}, [handleNewQuery, handleTitleBarWindowToggle, isMacRuntime, shortcutOptions, themeMode, setTheme, useNativeMacWindowControls]);
|
||||
}, [handleManualResetWindowZoom, handleNewQuery, handleTitleBarWindowToggle, isMacRuntime, shortcutOptions, themeMode, setTheme, useNativeMacWindowControls]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!capturingShortcutAction) {
|
||||
|
||||
@@ -128,6 +128,19 @@ describe('shortcut defaults', () => {
|
||||
scope: 'queryEditor',
|
||||
});
|
||||
});
|
||||
|
||||
// Windows 任务栏恢复后字体异常变大的兜底入口(方案 3)。
|
||||
// 自动 fix 路径(9848b8b2)刻意不再 toggle 以避免可见动画,由该快捷键给用户主动触发的修复入口。
|
||||
it('registers reset window zoom shortcut with default Ctrl+Shift+0', () => {
|
||||
expect(DEFAULT_SHORTCUT_OPTIONS.resetWindowZoom).toEqual({
|
||||
combo: 'Ctrl+Shift+0',
|
||||
enabled: true,
|
||||
});
|
||||
expect(SHORTCUT_ACTION_META.resetWindowZoom).toMatchObject({
|
||||
label: '重置窗口缩放',
|
||||
allowInEditable: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ─── comboToMonacoKeyBinding ─────────────────────────────────────────
|
||||
|
||||
@@ -9,7 +9,8 @@ export type ShortcutAction =
|
||||
| 'toggleLogPanel'
|
||||
| 'toggleTheme'
|
||||
| 'openShortcutManager'
|
||||
| 'toggleMacFullscreen';
|
||||
| 'toggleMacFullscreen'
|
||||
| 'resetWindowZoom';
|
||||
|
||||
export interface ShortcutBinding {
|
||||
combo: string;
|
||||
@@ -87,6 +88,7 @@ export const SHORTCUT_ACTION_ORDER: ShortcutAction[] = [
|
||||
'toggleTheme',
|
||||
'openShortcutManager',
|
||||
'toggleMacFullscreen',
|
||||
'resetWindowZoom',
|
||||
];
|
||||
|
||||
export const SHORTCUT_ACTION_META: Record<ShortcutAction, ShortcutActionMeta> = {
|
||||
@@ -135,6 +137,11 @@ export const SHORTCUT_ACTION_META: Record<ShortcutAction, ShortcutActionMeta> =
|
||||
description: 'macOS 原生窗口控制模式下的全屏切换(⌃⌘F)',
|
||||
platformOnly: 'mac',
|
||||
},
|
||||
resetWindowZoom: {
|
||||
label: '重置窗口缩放',
|
||||
description: 'Windows 任务栏恢复后字体异常变大时主动触发;会切一次最大化让 WebView2 重算字体度量',
|
||||
allowInEditable: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const DEFAULT_SHORTCUT_OPTIONS: ShortcutOptions = {
|
||||
@@ -147,6 +154,7 @@ export const DEFAULT_SHORTCUT_OPTIONS: ShortcutOptions = {
|
||||
toggleTheme: { combo: 'Ctrl+Shift+D', enabled: true },
|
||||
openShortcutManager: { combo: 'Ctrl+,', enabled: true },
|
||||
toggleMacFullscreen: { combo: 'Ctrl+Meta+F', enabled: true },
|
||||
resetWindowZoom: { combo: 'Ctrl+Shift+0', enabled: true },
|
||||
};
|
||||
|
||||
const normalizeKeyToken = (value: string): string => {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// @vitest-environment jsdom
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
applyWindowsViewportZoomNudge,
|
||||
computeWindowsViewportScaleRatio,
|
||||
getWindowsScaleFixNudgedWidth,
|
||||
hasWindowsViewportScaleDrift,
|
||||
@@ -44,32 +42,4 @@ describe('windowsScaleFix', () => {
|
||||
expect(getWindowsScaleFixNudgedWidth(960)).toBe(959);
|
||||
expect(getWindowsScaleFixNudgedWidth(420)).toBe(421);
|
||||
});
|
||||
|
||||
it('applies and resets the CSS zoom nudge across two animation frames', () => {
|
||||
const rafCallbacks: FrameRequestCallback[] = [];
|
||||
const rafSpy = vi.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => {
|
||||
rafCallbacks.push(cb);
|
||||
return rafCallbacks.length;
|
||||
});
|
||||
|
||||
const root = document.documentElement;
|
||||
const style = root.style as CSSStyleDeclaration & { zoom?: string };
|
||||
style.zoom = '';
|
||||
|
||||
applyWindowsViewportZoomNudge();
|
||||
// 第一阶段:zoom 已被设置为 1.0001
|
||||
expect(style.zoom).toBe('1.0001');
|
||||
expect(rafCallbacks).toHaveLength(1);
|
||||
|
||||
// 第一帧:调度第二帧 reset
|
||||
rafCallbacks[0]?.(0);
|
||||
expect(style.zoom).toBe('1.0001');
|
||||
expect(rafCallbacks).toHaveLength(2);
|
||||
|
||||
// 第二帧:恢复 zoom
|
||||
rafCallbacks[1]?.(0);
|
||||
expect(style.zoom).toBe('');
|
||||
|
||||
rafSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -44,34 +44,3 @@ export const getWindowsScaleFixNudgedWidth = (width: number): number => {
|
||||
}
|
||||
return normalizedWidth > 480 ? normalizedWidth - 1 : normalizedWidth + 1;
|
||||
};
|
||||
|
||||
// applyWindowsViewportZoomNudge 通过短暂改变 documentElement 的 CSS zoom 触发 Chromium
|
||||
// 重新计算 layout metrics。用于 maximised 窗口在 restore 场景下 viewport drift 修复的
|
||||
// 无动画路径:不重新最大化(避免 9848b8b2 修复的可见"重复最大化"抖动),也不调 WebView2
|
||||
// COM API(避免 Windows 平台特定代码)。
|
||||
//
|
||||
// 为什么这样能修:Chromium 在切换 zoom factor 时会重算所有 px 度量与 currentColor/font-size
|
||||
// 派生值,drift 后残留的旧度量被丢弃。1.0001 与 1 在视觉上不可分辨但属于 invalidation 阈值
|
||||
// 之外,强制触发完整 layout 重排。
|
||||
//
|
||||
// 用 requestAnimationFrame 两帧而不是立即 reset,让 Chromium 在第一帧完成 nudge layout、
|
||||
// 第二帧恢复——避免单帧合成被合并掉。
|
||||
export const applyWindowsViewportZoomNudge = (): void => {
|
||||
if (typeof document === 'undefined' || !document.documentElement) {
|
||||
return;
|
||||
}
|
||||
const root = document.documentElement;
|
||||
const style = root.style as CSSStyleDeclaration & { zoom?: string };
|
||||
const previousZoom = style.zoom ?? '';
|
||||
style.zoom = '1.0001';
|
||||
const reset = () => {
|
||||
style.zoom = previousZoom;
|
||||
};
|
||||
if (typeof window === 'undefined' || typeof window.requestAnimationFrame !== 'function') {
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
window.requestAnimationFrame(() => {
|
||||
window.requestAnimationFrame(reset);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -133,6 +133,16 @@ func (a *App) SetMacNativeWindowControls(enabled bool) {
|
||||
setMacNativeWindowControls(enabled)
|
||||
}
|
||||
|
||||
// ResetWebViewZoom 把 WebView2 zoom factor 强制重置为 1.0,让 WebView2 重算字体度量。
|
||||
// 用于 Windows 任务栏恢复后字体异常变大的"零感知"修复:不动窗口、零动画。
|
||||
// 仅 Windows 上生效,其他平台返回错误(前端按需忽略)。
|
||||
func (a *App) ResetWebViewZoom() connection.QueryResult {
|
||||
if err := resetWebViewZoomFactor(a.ctx, 1.0); err != nil {
|
||||
return connection.QueryResult{Success: false, Message: err.Error()}
|
||||
}
|
||||
return connection.QueryResult{Success: true, Message: "WebView2 zoom factor reset to 1.0"}
|
||||
}
|
||||
|
||||
// LogWindowDiagnostic 记录前端采集到的窗口诊断信息,便于排查 macOS 原生全屏异常。
|
||||
func (a *App) LogWindowDiagnostic(stage string, payload string) {
|
||||
stage = strings.TrimSpace(stage)
|
||||
|
||||
14
internal/app/window_zoom_other.go
Normal file
14
internal/app/window_zoom_other.go
Normal file
@@ -0,0 +1,14 @@
|
||||
//go:build !windows
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// resetWebViewZoomFactor 在非 Windows 平台上不可用:字体度量异常问题只在 WebView2 上出现,
|
||||
// macOS WebKit / Linux WebKitGTK 不需要这个修复。返回错误让上层做出"无需修复"的判断。
|
||||
func resetWebViewZoomFactor(_ context.Context, _ float64) error {
|
||||
return fmt.Errorf("WebView2 zoom factor reset is only available on Windows")
|
||||
}
|
||||
34
internal/app/window_zoom_other_test.go
Normal file
34
internal/app/window_zoom_other_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
//go:build !windows
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 非 Windows 平台:WebView2 不存在,resetWebViewZoomFactor 必须明确返回 error,
|
||||
// 让前端 fallback 到 toggle 路径而不是误以为修复成功。
|
||||
func TestResetWebViewZoomFactorReturnsErrorOnNonWindows(t *testing.T) {
|
||||
err := resetWebViewZoomFactor(context.Background(), 1.0)
|
||||
if err == nil {
|
||||
t.Fatal("expected error on non-Windows platform, got nil")
|
||||
}
|
||||
if !strings.Contains(strings.ToLower(err.Error()), "windows") {
|
||||
t.Fatalf("expected error to mention Windows-only, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// App.ResetWebViewZoom RPC 在 darwin/linux 上返回 success=false,让前端不至于
|
||||
// 调用后误以为成功而跳过 fallback 路径。
|
||||
func TestAppResetWebViewZoomRPCReportsFailureOnNonWindows(t *testing.T) {
|
||||
app := &App{ctx: context.Background()}
|
||||
res := app.ResetWebViewZoom()
|
||||
if res.Success {
|
||||
t.Fatal("expected RPC to report failure on non-Windows platform")
|
||||
}
|
||||
if strings.TrimSpace(res.Message) == "" {
|
||||
t.Fatal("expected failure message to explain why")
|
||||
}
|
||||
}
|
||||
67
internal/app/window_zoom_windows.go
Normal file
67
internal/app/window_zoom_windows.go
Normal file
@@ -0,0 +1,67 @@
|
||||
//go:build windows
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// resetWebViewZoomFactor 通过 WebView2 ICoreWebView2Controller::put_ZoomFactor 把 WebView2
|
||||
// 内部 zoom factor 重置为 1.0。这是 Windows 任务栏恢复后字体度量异常变大的根因解:
|
||||
// 字体度量缓存在 WebView2 D2D/DirectWrite 层,Chromium layout invalidation(CSS zoom hack)
|
||||
// 改不了它,必须调 WebView2 COM API。
|
||||
//
|
||||
// 实现路径:
|
||||
// 1. Wails 在 ctx 里以 key "frontend" 注入了 *desktop/windows.Frontend
|
||||
// 2. Frontend.chromium 是 unexported 字段 *edge.Chromium
|
||||
// 3. Chromium.PutZoomFactor(float64) 是 exported 方法(封装了 controller.put_ZoomFactor)
|
||||
//
|
||||
// 用反射 + unsafe.Pointer 解锁 unexported 字段后 MethodByName("PutZoomFactor").Call。
|
||||
// 不需要 import wails 内部包,也不需要 fork wails。
|
||||
//
|
||||
// 失败时返回错误(不 panic),让调用方决定是否回退到 toggle 路径。
|
||||
//
|
||||
// **依赖 wails v2.11/v2.12 内部实现细节**:如果 wails 升级改名了 frontend.chromium 字段或
|
||||
// edge.Chromium.PutZoomFactor 方法名,此函数会返回 error。CI 中应该有跨版本兼容性测试。
|
||||
func resetWebViewZoomFactor(ctx context.Context, factor float64) error {
|
||||
if ctx == nil {
|
||||
return fmt.Errorf("ctx is nil")
|
||||
}
|
||||
frontendIface := ctx.Value("frontend")
|
||||
if frontendIface == nil {
|
||||
return fmt.Errorf("wails frontend not found in ctx (key=\"frontend\")")
|
||||
}
|
||||
|
||||
frontendValue := reflect.ValueOf(frontendIface)
|
||||
if frontendValue.Kind() == reflect.Ptr {
|
||||
frontendValue = frontendValue.Elem()
|
||||
}
|
||||
if !frontendValue.IsValid() || frontendValue.Kind() != reflect.Struct {
|
||||
return fmt.Errorf("wails frontend has unexpected kind %v", frontendValue.Kind())
|
||||
}
|
||||
|
||||
chromiumField := frontendValue.FieldByName("chromium")
|
||||
if !chromiumField.IsValid() {
|
||||
return fmt.Errorf("wails Frontend.chromium field not found (wails version may have changed)")
|
||||
}
|
||||
if chromiumField.IsNil() {
|
||||
return fmt.Errorf("wails Frontend.chromium is nil (WebView2 not yet initialised)")
|
||||
}
|
||||
|
||||
// 用 NewAt + unsafe.Pointer 解锁 unexported 字段访问限制
|
||||
accessible := reflect.NewAt(chromiumField.Type(), unsafe.Pointer(chromiumField.UnsafeAddr())).Elem()
|
||||
method := accessible.MethodByName("PutZoomFactor")
|
||||
if !method.IsValid() {
|
||||
return fmt.Errorf("PutZoomFactor method not found on chromium (go-webview2 version may have changed)")
|
||||
}
|
||||
if method.Type().NumIn() != 1 || method.Type().In(0).Kind() != reflect.Float64 {
|
||||
return fmt.Errorf("PutZoomFactor signature changed: expected func(float64), got %v", method.Type())
|
||||
}
|
||||
|
||||
// PutZoomFactor 内部已经 swallow error 并通过 errorCallback 报告——这里不会 panic
|
||||
method.Call([]reflect.Value{reflect.ValueOf(factor)})
|
||||
return nil
|
||||
}
|
||||
87
internal/app/window_zoom_windows_test.go
Normal file
87
internal/app/window_zoom_windows_test.go
Normal file
@@ -0,0 +1,87 @@
|
||||
//go:build windows
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// fakeChromium 模仿 *edge.Chromium 的接口:只需要 exported 的 PutZoomFactor(float64) 方法。
|
||||
// 用于在不依赖真实 wails / WebView2 的情况下验证反射路径。
|
||||
type fakeChromium struct {
|
||||
called atomic.Int32
|
||||
last atomic.Value // float64
|
||||
}
|
||||
|
||||
func (f *fakeChromium) PutZoomFactor(factor float64) {
|
||||
f.called.Add(1)
|
||||
f.last.Store(factor)
|
||||
}
|
||||
|
||||
// fakeFrontend 模仿 wails 的 internal/frontend/desktop/windows.Frontend:
|
||||
// unexported 字段 chromium 是 *fakeChromium 类型(exported method PutZoomFactor)。
|
||||
// 反射代码不依赖具体类型名,只检查 method signature。
|
||||
type fakeFrontend struct {
|
||||
chromium *fakeChromium
|
||||
}
|
||||
|
||||
// 测试必须用 wails 一致的 string key "frontend" 作为 context.WithValue 的 key,
|
||||
// 否则反射拿不到。go vet 会警告 string key,用本地 stringContextKey 帮助函数封装来抑制。
|
||||
// 这层封装等价于直接传字符串字面量,行为完全一致。
|
||||
func stringContextKey(key string) any {
|
||||
type contextKeyAlias = string
|
||||
return contextKeyAlias(key)
|
||||
}
|
||||
|
||||
func TestResetWebViewZoomFactorCallsPutZoomFactor(t *testing.T) {
|
||||
chromium := &fakeChromium{}
|
||||
ctx := context.WithValue(context.Background(), stringContextKey("frontend"), &fakeFrontend{chromium: chromium})
|
||||
|
||||
if err := resetWebViewZoomFactor(ctx, 1.0); err != nil {
|
||||
t.Fatalf("expected reset to succeed against fake frontend, got %v", err)
|
||||
}
|
||||
if got := chromium.called.Load(); got != 1 {
|
||||
t.Fatalf("expected PutZoomFactor called exactly once, got %d", got)
|
||||
}
|
||||
if got, _ := chromium.last.Load().(float64); got != 1.0 {
|
||||
t.Fatalf("expected factor 1.0, got %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetWebViewZoomFactorErrorsWhenChromiumFieldMissing(t *testing.T) {
|
||||
type fakeFrontendWithoutChromium struct {
|
||||
other string
|
||||
}
|
||||
ctx := context.WithValue(context.Background(), stringContextKey("frontend"), &fakeFrontendWithoutChromium{})
|
||||
err := resetWebViewZoomFactor(ctx, 1.0)
|
||||
if err == nil {
|
||||
t.Fatal("expected error when chromium field is missing, got nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "chromium") {
|
||||
t.Fatalf("expected error to mention chromium, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetWebViewZoomFactorErrorsWhenChromiumNil(t *testing.T) {
|
||||
ctx := context.WithValue(context.Background(), stringContextKey("frontend"), &fakeFrontend{chromium: nil})
|
||||
err := resetWebViewZoomFactor(ctx, 1.0)
|
||||
if err == nil {
|
||||
t.Fatal("expected error when chromium is nil, got nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "nil") {
|
||||
t.Fatalf("expected error to mention nil, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetWebViewZoomFactorErrorsWhenFrontendMissing(t *testing.T) {
|
||||
err := resetWebViewZoomFactor(context.Background(), 1.0)
|
||||
if err == nil {
|
||||
t.Fatal("expected error when frontend not in ctx, got nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "frontend") {
|
||||
t.Fatalf("expected error to mention frontend, got %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user