mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-11-14 04:46:07 -08:00
Merge remote-tracking branch 'origin/main' into add-hachidori-backend
# Conflicts: # launcher/main.test.ts # src/cli/help.test.ts # src/core/services/__tests__/stats-server.test.ts # src/core/services/cli-command.test.ts # src/core/services/tokenizer/yomitan-parser-runtime.test.ts # src/main/overlay-runtime.test.ts # src/main/runtime/character-dictionary-auto-sync.test.ts # src/main/runtime/cli-command-context-deps.test.ts # src/main/runtime/cli-command-context-factory.test.ts # src/main/runtime/cli-command-context-main-deps.test.ts # src/main/runtime/composers/cli-startup-composer.test.ts # src/main/runtime/stats-server-runtime.test.ts # src/main/runtime/tray-main-actions.test.ts # src/main/runtime/tray-main-deps.test.ts # src/main/runtime/tray-runtime-handlers.test.ts # src/main/runtime/tray-runtime.test.ts # src/main/runtime/yomitan-anki-server-sync.test.ts # src/preload.ts # src/renderer/handlers/mouse.test.ts # src/renderer/yomitan-popup.test.ts
This commit is contained in:
417 files changed
+19477
-60921
No files matched your search
@@ -0,0 +1,195 @@
|
||||
import type { WindowGeometry } from '../types';
|
||||
import {
|
||||
OVERLAY_WINDOW_CONTENT_READY_FLAG,
|
||||
OVERLAY_WINDOW_DOCUMENT_LOADED_FLAG,
|
||||
} from '../core/services/overlay-window-flags';
|
||||
import {
|
||||
createOverlayModalRuntimeService,
|
||||
type OverlayModalRuntimeOptions,
|
||||
type OverlayWindowResolver,
|
||||
} from './overlay-runtime';
|
||||
|
||||
export const MODAL_URL = 'file:///overlay/index.html?layer=modal';
|
||||
export const MODAL_GEOMETRY: WindowGeometry = { x: 0, y: 0, width: 400, height: 300 };
|
||||
|
||||
export interface FakeOverlayWindowInit {
|
||||
// Prefix for show/hide entries pushed to `log`, e.g. `modal:show`.
|
||||
name?: string;
|
||||
log?: string[];
|
||||
visible?: boolean;
|
||||
focused?: boolean;
|
||||
webContentsFocused?: boolean;
|
||||
ignoreMouseEvents?: boolean;
|
||||
loading?: boolean;
|
||||
url?: string;
|
||||
contentReady?: boolean;
|
||||
documentLoaded?: boolean;
|
||||
}
|
||||
|
||||
// A still-loading window whose document has not been committed yet.
|
||||
export const LOADING_BLANK_WINDOW: FakeOverlayWindowInit = {
|
||||
loading: true,
|
||||
url: '',
|
||||
contentReady: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* BrowserWindow stand-in covering the surface overlay-runtime touches. Defaults to a hidden,
|
||||
* fully loaded, content-ready modal window.
|
||||
*/
|
||||
export class FakeOverlayWindow {
|
||||
destroyed = false;
|
||||
visible = false;
|
||||
focused = false;
|
||||
webContentsFocused = false;
|
||||
ignoreMouseEvents = false;
|
||||
forwardedIgnoreMouseEvents = false;
|
||||
loading = false;
|
||||
url = MODAL_URL;
|
||||
alwaysOnTopCalls: string[] = [];
|
||||
showCount = 0;
|
||||
hideCount = 0;
|
||||
sent: unknown[][] = [];
|
||||
[OVERLAY_WINDOW_CONTENT_READY_FLAG] = true;
|
||||
[OVERLAY_WINDOW_DOCUMENT_LOADED_FLAG] = true;
|
||||
|
||||
private readonly name: string;
|
||||
private readonly log: string[] | undefined;
|
||||
private loadListeners: Array<() => void> = [];
|
||||
private stopLoadingListeners: Array<() => void> = [];
|
||||
private readyToShowListeners: Array<() => void> = [];
|
||||
|
||||
readonly webContents = {
|
||||
isLoading: (): boolean => this.loading,
|
||||
getURL: (): string => this.url,
|
||||
send: (channel: string, ...payload: unknown[]): void => {
|
||||
this.sent.push([channel, ...payload]);
|
||||
},
|
||||
isFocused: (): boolean => this.webContentsFocused,
|
||||
focus: (): void => {
|
||||
this.webContentsFocused = true;
|
||||
},
|
||||
once: (event: 'did-finish-load' | 'did-stop-loading', listener: () => void): void => {
|
||||
(event === 'did-stop-loading' ? this.stopLoadingListeners : this.loadListeners).push(
|
||||
listener,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
constructor(init: FakeOverlayWindowInit = {}) {
|
||||
const { name = 'window', log, contentReady = true, documentLoaded = true, ...fields } = init;
|
||||
this.name = name;
|
||||
this.log = log;
|
||||
Object.assign(this, fields);
|
||||
this[OVERLAY_WINDOW_CONTENT_READY_FLAG] = contentReady;
|
||||
this[OVERLAY_WINDOW_DOCUMENT_LOADED_FLAG] = documentLoaded;
|
||||
}
|
||||
|
||||
isDestroyed(): boolean {
|
||||
return this.destroyed;
|
||||
}
|
||||
|
||||
isVisible(): boolean {
|
||||
return this.visible;
|
||||
}
|
||||
|
||||
isFocused(): boolean {
|
||||
return this.focused;
|
||||
}
|
||||
|
||||
setIgnoreMouseEvents(ignore: boolean, options?: { forward?: boolean }): void {
|
||||
this.ignoreMouseEvents = ignore;
|
||||
this.forwardedIgnoreMouseEvents = options?.forward === true;
|
||||
}
|
||||
|
||||
setAlwaysOnTop(flag: boolean, level?: string, relativeLevel?: number): void {
|
||||
this.alwaysOnTopCalls.push(`top:${flag}:${level ?? ''}:${relativeLevel ?? ''}`);
|
||||
}
|
||||
|
||||
moveTop(): void {}
|
||||
|
||||
show(): void {
|
||||
this.log?.push(`${this.name}:show`);
|
||||
this.visible = true;
|
||||
this.showCount += 1;
|
||||
}
|
||||
|
||||
showInactive(): void {
|
||||
this.log?.push(`${this.name}:show-inactive`);
|
||||
this.visible = true;
|
||||
this.showCount += 1;
|
||||
}
|
||||
|
||||
hide(): void {
|
||||
this.log?.push(`${this.name}:hide`);
|
||||
this.visible = false;
|
||||
this.hideCount += 1;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.destroyed = true;
|
||||
this.visible = false;
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
this.focused = true;
|
||||
}
|
||||
|
||||
once(_event: 'ready-to-show', listener: () => void): void {
|
||||
this.readyToShowListeners.push(listener);
|
||||
}
|
||||
|
||||
// Commits the modal document like Electron does: isLoading() stays true inside
|
||||
// did-finish-load and only settles before did-stop-loading.
|
||||
finishLoad(): void {
|
||||
this.url = MODAL_URL;
|
||||
this[OVERLAY_WINDOW_DOCUMENT_LOADED_FLAG] = true;
|
||||
for (const listener of this.loadListeners.splice(0)) listener();
|
||||
this.loading = false;
|
||||
for (const listener of this.stopLoadingListeners.splice(0)) listener();
|
||||
}
|
||||
|
||||
// Marks renderer content ready and fires the queued ready-to-show listeners.
|
||||
emitReadyToShow(): void {
|
||||
this[OVERLAY_WINDOW_CONTENT_READY_FLAG] = true;
|
||||
for (const listener of this.readyToShowListeners.splice(0)) listener();
|
||||
}
|
||||
}
|
||||
|
||||
export interface OverlayRuntimeHarnessInit {
|
||||
main?: FakeOverlayWindow | null;
|
||||
modal?: FakeOverlayWindow | null;
|
||||
// Called by the runtime's createModalWindow; its result becomes the current modal window.
|
||||
createModal?: () => FakeOverlayWindow | null;
|
||||
// Defaults to linux so behavior does not depend on the host running the tests.
|
||||
platform?: NodeJS.Platform;
|
||||
options?: Omit<OverlayModalRuntimeOptions, 'platform'>;
|
||||
setModalWindowBounds?: OverlayWindowResolver['setModalWindowBounds'];
|
||||
}
|
||||
|
||||
/** Builds the modal runtime over fake windows and records resolver traffic. */
|
||||
export function createHarness(init: OverlayRuntimeHarnessInit = {}) {
|
||||
const harness = {
|
||||
modal: init.modal ?? null,
|
||||
createCalls: 0,
|
||||
boundsCalls: [] as WindowGeometry[],
|
||||
};
|
||||
const runtime = createOverlayModalRuntimeService(
|
||||
{
|
||||
getMainWindow: () => (init.main ?? null) as never,
|
||||
getModalWindow: () => harness.modal as never,
|
||||
createModalWindow: () => {
|
||||
harness.createCalls += 1;
|
||||
harness.modal = init.createModal?.() ?? null;
|
||||
return harness.modal as never;
|
||||
},
|
||||
getModalGeometry: () => MODAL_GEOMETRY,
|
||||
setModalWindowBounds: (geometry) => {
|
||||
harness.boundsCalls.push(geometry);
|
||||
return init.setModalWindowBounds?.(geometry);
|
||||
},
|
||||
},
|
||||
{ ...init.options, platform: init.platform ?? 'linux' },
|
||||
);
|
||||
return Object.assign(harness, { runtime });
|
||||
}
|
||||
Reference in new issue
Block a user