mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-26 00:55:16 -07:00
upgrade Electron 39→42 and fix Hyprland overlay z-order/placement (#79)
This commit is contained in:
@@ -3,40 +3,13 @@ import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
import { withProcessExitIntercept } from '../test-support/exit-intercept.js';
|
||||
import {
|
||||
applyInvocationsToArgs,
|
||||
applyRootOptionsToArgs,
|
||||
createDefaultArgs,
|
||||
} from './args-normalizer.js';
|
||||
|
||||
class ExitSignal extends Error {
|
||||
code: number;
|
||||
|
||||
constructor(code: number) {
|
||||
super(`exit:${code}`);
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
function withProcessExitIntercept(callback: () => void): ExitSignal {
|
||||
const originalExit = process.exit;
|
||||
try {
|
||||
process.exit = ((code?: number) => {
|
||||
throw new ExitSignal(code ?? 0);
|
||||
}) as typeof process.exit;
|
||||
callback();
|
||||
} catch (error) {
|
||||
if (error instanceof ExitSignal) {
|
||||
return error;
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
process.exit = originalExit;
|
||||
}
|
||||
|
||||
throw new Error('expected process.exit');
|
||||
}
|
||||
|
||||
function withTempDir<T>(fn: (dir: string) => T): T {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-launcher-args-'));
|
||||
try {
|
||||
@@ -106,6 +79,7 @@ test('applyRootOptionsToArgs rejects unsupported targets', () => {
|
||||
|
||||
assert.equal(error.code, 1);
|
||||
assert.match(error.message, /exit:1/);
|
||||
assert.match(error.stderr, /Not a file, directory, or supported URL/);
|
||||
});
|
||||
|
||||
test('applyInvocationsToArgs maps config and jellyfin invocation state', () => {
|
||||
@@ -231,6 +205,7 @@ test('applyInvocationsToArgs fails when config invocation has no action', () =>
|
||||
});
|
||||
|
||||
assert.equal(error.code, 1);
|
||||
assert.match(error.stderr, /Unknown config action: \(none\)/);
|
||||
});
|
||||
|
||||
test('applyInvocationsToArgs maps texthooker browser-open request', () => {
|
||||
|
||||
+2
-28
@@ -7,6 +7,7 @@ import net from 'node:net';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import type { Args } from './types';
|
||||
import { getAppControlSocketPath } from '../src/shared/app-control';
|
||||
import { withProcessExitIntercept } from './test-support/exit-intercept.js';
|
||||
import {
|
||||
buildConfiguredMpvDefaultArgs,
|
||||
buildMpvBackendArgs,
|
||||
@@ -28,34 +29,6 @@ import {
|
||||
} from './mpv';
|
||||
import * as mpvModule from './mpv';
|
||||
|
||||
class ExitSignal extends Error {
|
||||
code: number;
|
||||
|
||||
constructor(code: number) {
|
||||
super(`exit:${code}`);
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
function withProcessExitIntercept(callback: () => void): ExitSignal {
|
||||
const originalExit = process.exit;
|
||||
try {
|
||||
process.exit = ((code?: number) => {
|
||||
throw new ExitSignal(code ?? 0);
|
||||
}) as typeof process.exit;
|
||||
callback();
|
||||
} catch (error) {
|
||||
if (error instanceof ExitSignal) {
|
||||
return error;
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
process.exit = originalExit;
|
||||
}
|
||||
|
||||
throw new Error('expected process.exit');
|
||||
}
|
||||
|
||||
function createTempSocketPath(): { dir: string; socketPath: string } {
|
||||
const baseDir = path.join(process.cwd(), '.tmp', 'launcher-mpv-tests');
|
||||
fs.mkdirSync(baseDir, { recursive: true });
|
||||
@@ -393,6 +366,7 @@ test('launchTexthookerOnly exits non-zero when app binary cannot be spawned', ()
|
||||
});
|
||||
|
||||
assert.equal(error.code, 1);
|
||||
assert.match(error.stderr, /Failed to launch texthooker mode/);
|
||||
});
|
||||
|
||||
test('launchTexthookerOnly forwards browser-open request to app command', () => {
|
||||
|
||||
@@ -1,34 +1,7 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { parseArgs } from './config';
|
||||
|
||||
class ExitSignal extends Error {
|
||||
code: number;
|
||||
|
||||
constructor(code: number) {
|
||||
super(`exit:${code}`);
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
function withProcessExitIntercept(callback: () => void): ExitSignal {
|
||||
const originalExit = process.exit;
|
||||
try {
|
||||
process.exit = ((code?: number) => {
|
||||
throw new ExitSignal(code ?? 0);
|
||||
}) as typeof process.exit;
|
||||
callback();
|
||||
} catch (error) {
|
||||
if (error instanceof ExitSignal) {
|
||||
return error;
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
process.exit = originalExit;
|
||||
}
|
||||
|
||||
throw new Error('expected parseArgs to exit');
|
||||
}
|
||||
import { withProcessExitIntercept } from './test-support/exit-intercept.js';
|
||||
|
||||
test('parseArgs captures passthrough args for app subcommand', () => {
|
||||
const parsed = parseArgs(['app', '--anilist', '--log-level', 'debug'], 'subminer', {});
|
||||
@@ -131,7 +104,9 @@ test('parseArgs rejects removed config open and launch actions', () => {
|
||||
});
|
||||
|
||||
assert.equal(openExit.code, 1);
|
||||
assert.match(openExit.stderr, /Unknown config action: open/);
|
||||
assert.equal(exit.code, 1);
|
||||
assert.match(exit.stderr, /Unknown config action: launch/);
|
||||
});
|
||||
|
||||
test('parseArgs requires an explicit action for the config subcommand', () => {
|
||||
@@ -140,6 +115,7 @@ test('parseArgs requires an explicit action for the config subcommand', () => {
|
||||
});
|
||||
|
||||
assert.equal(exit.code, 1);
|
||||
assert.match(exit.stderr, /Unknown config action: \(none\)/);
|
||||
});
|
||||
|
||||
test('parseArgs maps mpv idle action', () => {
|
||||
@@ -180,6 +156,7 @@ test('parseArgs rejects conflicting dictionary candidate and selection modes', (
|
||||
});
|
||||
|
||||
assert.equal(exit.code, 1);
|
||||
assert.match(exit.stderr, /Dictionary --candidates and --select cannot be combined/);
|
||||
});
|
||||
|
||||
test('parseArgs maps stats command and log-level override', () => {
|
||||
@@ -243,6 +220,7 @@ test('parseArgs rejects cleanup-only stats flags without cleanup action', () =>
|
||||
|
||||
assert.equal(error.code, 1);
|
||||
assert.match(error.message, /exit:1/);
|
||||
assert.match(error.stderr, /Stats --vocab and --lifetime flags require the cleanup action/);
|
||||
});
|
||||
|
||||
test('parseArgs maps stats rebuild action to cleanup lifetime mode', () => {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
export class ExitSignal extends Error {
|
||||
code: number;
|
||||
stderr: string;
|
||||
|
||||
constructor(code: number, stderr: string) {
|
||||
super(`exit:${code}`);
|
||||
this.code = code;
|
||||
this.stderr = stderr;
|
||||
}
|
||||
}
|
||||
|
||||
function stderrChunkToString(chunk: string | Uint8Array, encoding?: BufferEncoding): string {
|
||||
if (typeof chunk === 'string') return chunk;
|
||||
return Buffer.from(chunk).toString(encoding);
|
||||
}
|
||||
|
||||
export function withProcessExitIntercept(callback: () => void): ExitSignal {
|
||||
const originalExit = process.exit;
|
||||
const originalStderrWrite = process.stderr.write;
|
||||
let stderr = '';
|
||||
|
||||
try {
|
||||
process.stderr.write = ((chunk: string | Uint8Array, ...args: unknown[]) => {
|
||||
const encoding = typeof args[0] === 'string' ? (args[0] as BufferEncoding) : undefined;
|
||||
stderr += stderrChunkToString(chunk, encoding);
|
||||
const writeCallback = args.find((arg): arg is (error?: Error | null) => void => {
|
||||
return typeof arg === 'function';
|
||||
});
|
||||
writeCallback?.();
|
||||
return true;
|
||||
}) as typeof process.stderr.write;
|
||||
process.exit = ((code?: number) => {
|
||||
throw new ExitSignal(code ?? 0, stderr);
|
||||
}) as typeof process.exit;
|
||||
callback();
|
||||
} catch (error) {
|
||||
if (error instanceof ExitSignal) {
|
||||
return error;
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
process.stderr.write = originalStderrWrite;
|
||||
process.exit = originalExit;
|
||||
}
|
||||
|
||||
throw new Error('expected process.exit');
|
||||
}
|
||||
Reference in New Issue
Block a user