mirror of
https://github.com/Pandipipas/scoreko-electron-dev.git
synced 2026-06-05 21:22:07 +00:00
281 lines
8.5 KiB
TypeScript
281 lines
8.5 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import { createApplicationController, ApplicationWindow } from "../main/app/application-controller";
|
|
import { AppRuntimeConfig } from "../main/config/runtime-config";
|
|
import { NodecgProcessManager } from "../main/nodecg/process-manager";
|
|
|
|
class MockWindow implements ApplicationWindow {
|
|
private destroyed = false;
|
|
private minimized = false;
|
|
|
|
constructor(
|
|
private readonly name: string,
|
|
private readonly events: string[],
|
|
) {}
|
|
|
|
close(): void {
|
|
this.events.push(`${this.name}:close`);
|
|
this.destroyed = true;
|
|
}
|
|
|
|
focus(): void {
|
|
this.events.push(`${this.name}:focus`);
|
|
}
|
|
|
|
isDestroyed(): boolean {
|
|
return this.destroyed;
|
|
}
|
|
|
|
isMinimized(): boolean {
|
|
return this.minimized;
|
|
}
|
|
|
|
async loadURL(url: string): Promise<void> {
|
|
this.events.push(`${this.name}:load:${url}`);
|
|
}
|
|
|
|
restore(): void {
|
|
this.events.push(`${this.name}:restore`);
|
|
this.minimized = false;
|
|
}
|
|
|
|
show(): void {
|
|
this.events.push(`${this.name}:show`);
|
|
}
|
|
}
|
|
|
|
function getBaseConfig(): AppRuntimeConfig {
|
|
return {
|
|
title: "Scoreko",
|
|
userModelId: "com.scoreko.desktop",
|
|
userDataDirectoryName: "scoreko",
|
|
nodecgPort: "9090",
|
|
bundleName: "scoreko-dev",
|
|
mainDashboardRoute: "dashboard/scoreko-dev/main.html?standalone=true",
|
|
loadingDashboardRoute: "dashboard/loading/main.html?standalone=true",
|
|
loadDelayMs: 0,
|
|
startupTimeoutMs: 100,
|
|
nodecgKillTimeoutMs: 10,
|
|
updatesEnabled: true,
|
|
updateAssetPattern: "Scoreko-setup-.*\\.exe$",
|
|
updateCheckDelayMs: 5000,
|
|
};
|
|
}
|
|
|
|
function createMockManager(events: string[]): NodecgProcessManager {
|
|
return {
|
|
startNodecgProcess: async () => {
|
|
events.push("start-nodecg");
|
|
},
|
|
waitForNodecgReady: async () => {
|
|
events.push("wait-nodecg");
|
|
},
|
|
stopNodecgProcessGracefully: async () => {
|
|
events.push("stop-nodecg");
|
|
},
|
|
getState: () => "running",
|
|
};
|
|
}
|
|
|
|
test("ApplicationController preserves startup ordering and schedules updates after main window is shown", async () => {
|
|
const events: string[] = [];
|
|
const paths = {
|
|
rootPath: "/app",
|
|
sourceNodecgRuntimePath: "/app/lib/nodecg",
|
|
userDataPath: "/user-data/scoreko",
|
|
nodecgBaseUrl: "http://127.0.0.1:9090",
|
|
mainDashboardUrl: "http://localhost:9090/bundles/scoreko-dev/dashboard/main.html?standalone=true",
|
|
loadingDashboardUrl: "http://localhost:9090/bundles/scoreko-dev/dashboard/loading/main.html?standalone=true",
|
|
};
|
|
|
|
const controller = createApplicationController({
|
|
appConfig: getBaseConfig(),
|
|
appVersion: "0.1.0",
|
|
isPackaged: false,
|
|
isWindows: true,
|
|
paths,
|
|
deps: {
|
|
createLoadingWindow: () => {
|
|
events.push("create-loading");
|
|
return new MockWindow("loading", events);
|
|
},
|
|
createMainWindow: () => {
|
|
events.push("create-main");
|
|
return new MockWindow("main", events);
|
|
},
|
|
createNodecgProcessManager: () => {
|
|
events.push("create-manager");
|
|
return createMockManager(events);
|
|
},
|
|
getAllWindows: () => [],
|
|
log: () => undefined,
|
|
prepareRuntime: () => {
|
|
events.push("prepare-runtime");
|
|
return { runtimePath: "/user-data/scoreko/nodecg", installed: false };
|
|
},
|
|
scheduleUpdateCheck: () => events.push("schedule-update"),
|
|
setAppUserModelId: () => events.push("set-app-user-model-id"),
|
|
exit: (code) => events.push(`exit:${code}`),
|
|
now: () => 0,
|
|
sleep: async (ms) => {
|
|
events.push(`sleep:${ms}`);
|
|
},
|
|
},
|
|
});
|
|
|
|
await controller.launch();
|
|
|
|
assert.equal(controller.getState(), "ready");
|
|
assert.deepEqual(events, [
|
|
"set-app-user-model-id",
|
|
"prepare-runtime",
|
|
"create-manager",
|
|
"create-main",
|
|
"create-loading",
|
|
"start-nodecg",
|
|
"wait-nodecg",
|
|
`loading:load:${paths.loadingDashboardUrl}`,
|
|
"loading:show",
|
|
`main:load:${paths.mainDashboardUrl}`,
|
|
"main:show",
|
|
"loading:close",
|
|
"schedule-update",
|
|
]);
|
|
});
|
|
|
|
test("ApplicationController directly launches packaged app after runtime install without relaunching", async () => {
|
|
const events: string[] = [];
|
|
const controller = createApplicationController({
|
|
appConfig: getBaseConfig(),
|
|
appVersion: "0.1.0",
|
|
isPackaged: true,
|
|
isWindows: false,
|
|
paths: {
|
|
rootPath: "/app",
|
|
sourceNodecgRuntimePath: "/app/lib/nodecg",
|
|
userDataPath: "/user-data/scoreko",
|
|
nodecgBaseUrl: "http://127.0.0.1:9090",
|
|
mainDashboardUrl: "http://localhost:9090/main",
|
|
loadingDashboardUrl: "http://localhost:9090/loading",
|
|
},
|
|
deps: {
|
|
createLoadingWindow: () => {
|
|
events.push("create-loading");
|
|
return new MockWindow("loading", events);
|
|
},
|
|
createMainWindow: () => {
|
|
events.push("create-main");
|
|
return new MockWindow("main", events);
|
|
},
|
|
createNodecgProcessManager: () => {
|
|
events.push("create-manager");
|
|
return createMockManager(events);
|
|
},
|
|
getAllWindows: () => [],
|
|
log: (...args) => events.push(String(args[0])),
|
|
prepareRuntime: () => ({ runtimePath: "/user-data/scoreko/nodecg", installed: true }),
|
|
scheduleUpdateCheck: () => events.push("schedule-update"),
|
|
setAppUserModelId: () => events.push("set-app-user-model-id"),
|
|
exit: (code) => events.push(`exit:${code}`),
|
|
now: () => 0,
|
|
sleep: async (ms) => {
|
|
events.push(`sleep:${ms}`);
|
|
},
|
|
},
|
|
});
|
|
|
|
await controller.launch();
|
|
|
|
assert.equal(controller.getState(), "ready");
|
|
assert.deepEqual(events, [
|
|
"create-manager",
|
|
"create-main",
|
|
"create-loading",
|
|
"start-nodecg",
|
|
"wait-nodecg",
|
|
"loading:load:http://localhost:9090/loading",
|
|
"loading:show",
|
|
"main:load:http://localhost:9090/main",
|
|
"main:show",
|
|
"loading:close",
|
|
"schedule-update",
|
|
]);
|
|
});
|
|
|
|
test("ApplicationController activation before readiness routes through launch", async () => {
|
|
const events: string[] = [];
|
|
const controller = createApplicationController({
|
|
appConfig: getBaseConfig(),
|
|
appVersion: "0.1.0",
|
|
isPackaged: false,
|
|
isWindows: false,
|
|
paths: {
|
|
rootPath: "/app",
|
|
sourceNodecgRuntimePath: "/app/lib/nodecg",
|
|
userDataPath: "/user-data/scoreko",
|
|
nodecgBaseUrl: "http://127.0.0.1:9090",
|
|
mainDashboardUrl: "http://localhost:9090/main",
|
|
loadingDashboardUrl: "http://localhost:9090/loading",
|
|
},
|
|
deps: {
|
|
createLoadingWindow: () => new MockWindow("loading", events),
|
|
createMainWindow: () => new MockWindow("main", events),
|
|
createNodecgProcessManager: () => createMockManager(events),
|
|
getAllWindows: () => [],
|
|
log: () => undefined,
|
|
prepareRuntime: () => {
|
|
events.push("prepare-runtime");
|
|
return { runtimePath: "/user-data/scoreko/nodecg", installed: false };
|
|
},
|
|
scheduleUpdateCheck: () => events.push("schedule-update"),
|
|
setAppUserModelId: () => events.push("set-app-user-model-id"),
|
|
exit: (code) => events.push(`exit:${code}`),
|
|
now: () => 0,
|
|
},
|
|
});
|
|
|
|
await controller.activate();
|
|
|
|
assert.equal(controller.getState(), "ready");
|
|
assert.ok(events.includes("prepare-runtime"));
|
|
assert.ok(events.includes("start-nodecg"));
|
|
assert.ok(events.includes("wait-nodecg"));
|
|
});
|
|
|
|
test("ApplicationController shutdown is idempotent", async () => {
|
|
const events: string[] = [];
|
|
const controller = createApplicationController({
|
|
appConfig: getBaseConfig(),
|
|
appVersion: "0.1.0",
|
|
isPackaged: false,
|
|
isWindows: false,
|
|
paths: {
|
|
rootPath: "/app",
|
|
sourceNodecgRuntimePath: "/app/lib/nodecg",
|
|
userDataPath: "/user-data/scoreko",
|
|
nodecgBaseUrl: "http://127.0.0.1:9090",
|
|
mainDashboardUrl: "http://localhost:9090/main",
|
|
loadingDashboardUrl: "http://localhost:9090/loading",
|
|
},
|
|
deps: {
|
|
createLoadingWindow: () => new MockWindow("loading", events),
|
|
createMainWindow: () => new MockWindow("main", events),
|
|
createNodecgProcessManager: () => createMockManager(events),
|
|
getAllWindows: () => [],
|
|
log: () => undefined,
|
|
prepareRuntime: () => ({ runtimePath: "/user-data/scoreko/nodecg", installed: false }),
|
|
scheduleUpdateCheck: () => events.push("schedule-update"),
|
|
setAppUserModelId: () => events.push("set-app-user-model-id"),
|
|
exit: (code) => events.push(`exit:${code}`),
|
|
now: () => 0,
|
|
},
|
|
});
|
|
|
|
await controller.launch();
|
|
await Promise.all([controller.stopNodecgGracefully(), controller.stopNodecgGracefully()]);
|
|
|
|
assert.equal(controller.getState(), "stopped");
|
|
assert.equal(events.filter((event) => event === "stop-nodecg").length, 1);
|
|
});
|