mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-04-10 04:19:25 -07:00
Overlay 2.0 (#12)
This commit is contained in:
@@ -2,6 +2,12 @@ import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
import { createRendererRecoveryController } from './error-recovery.js';
|
||||
import {
|
||||
YOMITAN_POPUP_IFRAME_SELECTOR,
|
||||
hasYomitanPopupIframe,
|
||||
isYomitanPopupIframe,
|
||||
} from './yomitan-popup.js';
|
||||
import { scrollActiveRuntimeOptionIntoView } from './modals/runtime-options.js';
|
||||
import { resolvePlatformInfo } from './utils/platform.js';
|
||||
|
||||
test('handleError logs context and recovers overlay state', () => {
|
||||
@@ -26,7 +32,6 @@ test('handleError logs context and recovers overlay state', () => {
|
||||
secondarySubtitlePreview: 'secondary',
|
||||
isOverlayInteractive: true,
|
||||
isOverSubtitle: true,
|
||||
invisiblePositionEditMode: false,
|
||||
overlayLayer: 'visible',
|
||||
}),
|
||||
logError: (payload) => {
|
||||
@@ -72,8 +77,7 @@ test('handleError normalizes non-Error values', () => {
|
||||
secondarySubtitlePreview: '',
|
||||
isOverlayInteractive: false,
|
||||
isOverSubtitle: false,
|
||||
invisiblePositionEditMode: false,
|
||||
overlayLayer: 'invisible',
|
||||
overlayLayer: 'visible',
|
||||
}),
|
||||
logError: (payload) => {
|
||||
payloads.push(payload);
|
||||
@@ -107,7 +111,6 @@ test('nested recovery errors are ignored while current recovery is active', () =
|
||||
secondarySubtitlePreview: '',
|
||||
isOverlayInteractive: true,
|
||||
isOverSubtitle: false,
|
||||
invisiblePositionEditMode: true,
|
||||
overlayLayer: 'visible',
|
||||
}),
|
||||
logError: (payload) => {
|
||||
@@ -130,7 +133,7 @@ test('resolvePlatformInfo prefers query layer over preload layer', () => {
|
||||
configurable: true,
|
||||
value: {
|
||||
electronAPI: {
|
||||
getOverlayLayer: () => 'invisible',
|
||||
getOverlayLayer: () => 'modal',
|
||||
},
|
||||
location: { search: '?layer=visible' },
|
||||
},
|
||||
@@ -146,7 +149,6 @@ test('resolvePlatformInfo prefers query layer over preload layer', () => {
|
||||
try {
|
||||
const info = resolvePlatformInfo();
|
||||
assert.equal(info.overlayLayer, 'visible');
|
||||
assert.equal(info.isInvisibleLayer, false);
|
||||
} finally {
|
||||
Object.defineProperty(globalThis, 'window', { configurable: true, value: previousWindow });
|
||||
Object.defineProperty(globalThis, 'navigator', {
|
||||
@@ -156,7 +158,7 @@ test('resolvePlatformInfo prefers query layer over preload layer', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('resolvePlatformInfo supports secondary layer and disables mouse-ignore toggles', () => {
|
||||
test('resolvePlatformInfo ignores legacy secondary layer and falls back to visible', () => {
|
||||
const previousWindow = (globalThis as { window?: unknown }).window;
|
||||
const previousNavigator = (globalThis as { navigator?: unknown }).navigator;
|
||||
|
||||
@@ -179,9 +181,8 @@ test('resolvePlatformInfo supports secondary layer and disables mouse-ignore tog
|
||||
|
||||
try {
|
||||
const info = resolvePlatformInfo();
|
||||
assert.equal(info.overlayLayer, 'secondary');
|
||||
assert.equal(info.isSecondaryLayer, true);
|
||||
assert.equal(info.shouldToggleMouseIgnore, false);
|
||||
assert.equal(info.overlayLayer, 'visible');
|
||||
assert.equal(info.shouldToggleMouseIgnore, true);
|
||||
} finally {
|
||||
Object.defineProperty(globalThis, 'window', { configurable: true, value: previousWindow });
|
||||
Object.defineProperty(globalThis, 'navigator', {
|
||||
@@ -225,3 +226,88 @@ test('resolvePlatformInfo supports modal layer and disables mouse-ignore toggles
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('isYomitanPopupIframe matches modern popup class and legacy id prefix', () => {
|
||||
const createElement = (options: {
|
||||
tagName: string;
|
||||
id?: string;
|
||||
classNames?: string[];
|
||||
}): Element =>
|
||||
({
|
||||
tagName: options.tagName,
|
||||
id: options.id ?? '',
|
||||
classList: {
|
||||
contains: (className: string) => (options.classNames ?? []).includes(className),
|
||||
},
|
||||
}) as unknown as Element;
|
||||
|
||||
assert.equal(
|
||||
isYomitanPopupIframe(
|
||||
createElement({
|
||||
tagName: 'IFRAME',
|
||||
classNames: ['yomitan-popup'],
|
||||
}),
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
isYomitanPopupIframe(
|
||||
createElement({
|
||||
tagName: 'IFRAME',
|
||||
id: 'yomitan-popup-123',
|
||||
}),
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
isYomitanPopupIframe(
|
||||
createElement({
|
||||
tagName: 'IFRAME',
|
||||
id: 'something-else',
|
||||
}),
|
||||
),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test('hasYomitanPopupIframe queries for modern + legacy selector', () => {
|
||||
let selector = '';
|
||||
const root = {
|
||||
querySelector: (value: string) => {
|
||||
selector = value;
|
||||
return {};
|
||||
},
|
||||
} as unknown as ParentNode;
|
||||
|
||||
assert.equal(hasYomitanPopupIframe(root), true);
|
||||
assert.equal(selector, YOMITAN_POPUP_IFRAME_SELECTOR);
|
||||
});
|
||||
|
||||
test('scrollActiveRuntimeOptionIntoView scrolls active runtime option with nearest block', () => {
|
||||
const calls: Array<{ block?: ScrollLogicalPosition }> = [];
|
||||
const activeItem = {
|
||||
scrollIntoView: (options?: ScrollIntoViewOptions) => {
|
||||
calls.push({ block: options?.block });
|
||||
},
|
||||
};
|
||||
|
||||
const list = {
|
||||
querySelector: (selector: string) => {
|
||||
assert.equal(selector, '.runtime-options-item.active');
|
||||
return activeItem as unknown as Element;
|
||||
},
|
||||
};
|
||||
|
||||
scrollActiveRuntimeOptionIntoView(list);
|
||||
assert.deepEqual(calls, [{ block: 'nearest' }]);
|
||||
});
|
||||
|
||||
test('scrollActiveRuntimeOptionIntoView no-ops without active option', () => {
|
||||
const list = {
|
||||
querySelector: () => null,
|
||||
};
|
||||
|
||||
assert.doesNotThrow(() => {
|
||||
scrollActiveRuntimeOptionIntoView(list);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user