gestament 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +122 -0
  2. package/dist/element.d.ts +15 -0
  3. package/dist/element.d.ts.map +1 -0
  4. package/dist/errors.d.ts +28 -0
  5. package/dist/errors.d.ts.map +1 -0
  6. package/dist/generated/packageMetadata.d.ts +18 -0
  7. package/dist/generated/packageMetadata.d.ts.map +1 -0
  8. package/dist/gestament-config.cjs +87 -0
  9. package/dist/gestament-config.cjs.map +1 -0
  10. package/dist/gestament-config.d.ts +18 -0
  11. package/dist/gestament-config.d.ts.map +1 -0
  12. package/dist/gestament-config.mjs +86 -0
  13. package/dist/gestament-config.mjs.map +1 -0
  14. package/dist/gestament-tray-host.cjs +12 -0
  15. package/dist/gestament-tray-host.cjs.map +1 -0
  16. package/dist/gestament-tray-host.d.ts +13 -0
  17. package/dist/gestament-tray-host.d.ts.map +1 -0
  18. package/dist/gestament-tray-host.mjs +11 -0
  19. package/dist/gestament-tray-host.mjs.map +1 -0
  20. package/dist/gestament-xvfb-worker.cjs +138 -0
  21. package/dist/gestament-xvfb-worker.cjs.map +1 -0
  22. package/dist/gestament-xvfb-worker.d.ts +13 -0
  23. package/dist/gestament-xvfb-worker.d.ts.map +1 -0
  24. package/dist/gestament-xvfb-worker.mjs +137 -0
  25. package/dist/gestament-xvfb-worker.mjs.map +1 -0
  26. package/dist/gestament-xvfb.cjs +132 -0
  27. package/dist/gestament-xvfb.cjs.map +1 -0
  28. package/dist/gestament-xvfb.d.ts +13 -0
  29. package/dist/gestament-xvfb.d.ts.map +1 -0
  30. package/dist/gestament-xvfb.mjs +131 -0
  31. package/dist/gestament-xvfb.mjs.map +1 -0
  32. package/dist/index.cjs +1077 -0
  33. package/dist/index.cjs.map +1 -0
  34. package/dist/index.d.ts +13 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.mjs +1077 -0
  37. package/dist/index.mjs.map +1 -0
  38. package/dist/launchGtkApp.d.ts +37 -0
  39. package/dist/launchGtkApp.d.ts.map +1 -0
  40. package/dist/native-BRnrsqMn.cjs +249 -0
  41. package/dist/native-BRnrsqMn.cjs.map +1 -0
  42. package/dist/native-DAhTiLnf.js +249 -0
  43. package/dist/native-DAhTiLnf.js.map +1 -0
  44. package/dist/native.d.ts +170 -0
  45. package/dist/native.d.ts.map +1 -0
  46. package/dist/testing.cjs +1180 -0
  47. package/dist/testing.cjs.map +1 -0
  48. package/dist/testing.d.ts +329 -0
  49. package/dist/testing.d.ts.map +1 -0
  50. package/dist/testing.mjs +1158 -0
  51. package/dist/testing.mjs.map +1 -0
  52. package/dist/tray.d.ts +17 -0
  53. package/dist/tray.d.ts.map +1 -0
  54. package/dist/types.d.ts +920 -0
  55. package/dist/types.d.ts.map +1 -0
  56. package/images/gestament-120.png +0 -0
  57. package/include/gestament/gtk.h +112 -0
  58. package/package.json +92 -0
  59. package/prebuilds/linux-arm/gtk3/node.napi.armv7.glibc.node +0 -0
  60. package/prebuilds/linux-arm/gtk4/node.napi.armv7.glibc.node +0 -0
  61. package/prebuilds/linux-arm64/gtk3/node.napi.glibc.node +0 -0
  62. package/prebuilds/linux-arm64/gtk4/node.napi.glibc.node +0 -0
  63. package/prebuilds/linux-ia32/gtk3/node.napi.glibc.node +0 -0
  64. package/prebuilds/linux-ia32/gtk4/node.napi.glibc.node +0 -0
  65. package/prebuilds/linux-riscv64/gtk3/node.napi.glibc.node +0 -0
  66. package/prebuilds/linux-riscv64/gtk4/node.napi.glibc.node +0 -0
  67. package/prebuilds/linux-x64/gtk3/node.napi.glibc.node +0 -0
  68. package/prebuilds/linux-x64/gtk4/node.napi.glibc.node +0 -0
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "node:child_process";
3
+ import { realpathSync } from "node:fs";
4
+ import { resolve, dirname } from "node:path";
5
+ const trayHostReadyLine = "gestament-tray-host-ready";
6
+ const trayHostReadyTimeoutMs = 3e4;
7
+ const parseArguments = (args) => {
8
+ let withTrayHost = false;
9
+ let index = 0;
10
+ while (index < args.length) {
11
+ const argument = args[index];
12
+ if (argument === "--") {
13
+ return {
14
+ command: args.slice(index + 1),
15
+ withTrayHost
16
+ };
17
+ }
18
+ if (argument === "--with-tray-host") {
19
+ withTrayHost = true;
20
+ index += 1;
21
+ continue;
22
+ }
23
+ throw new Error(`Unknown gestament-xvfb worker option: ${argument}`);
24
+ }
25
+ throw new Error("Missing command separator: --");
26
+ };
27
+ const waitForTrayHostReady = (host) => new Promise((resolve2, reject) => {
28
+ if (host.stdout === null) {
29
+ reject(new Error("gestament tray host did not expose stdout."));
30
+ return;
31
+ }
32
+ let output = "";
33
+ let settled = false;
34
+ const timeout = setTimeout(() => {
35
+ if (!settled) {
36
+ settled = true;
37
+ reject(new Error("Timed out waiting for gestament tray host."));
38
+ }
39
+ }, trayHostReadyTimeoutMs);
40
+ host.stdout.on("data", (chunk) => {
41
+ const text = chunk.toString("utf8");
42
+ output += text;
43
+ if (!settled && output.includes(trayHostReadyLine)) {
44
+ settled = true;
45
+ clearTimeout(timeout);
46
+ resolve2();
47
+ return;
48
+ }
49
+ if (settled) {
50
+ process.stdout.write(text);
51
+ }
52
+ });
53
+ host.on("exit", (code, signal) => {
54
+ if (!settled) {
55
+ settled = true;
56
+ clearTimeout(timeout);
57
+ reject(
58
+ new Error(
59
+ `gestament tray host exited before ready: code=${String(
60
+ code
61
+ )}, signal=${String(signal)}`
62
+ )
63
+ );
64
+ }
65
+ });
66
+ host.on("error", (error) => {
67
+ if (!settled) {
68
+ settled = true;
69
+ clearTimeout(timeout);
70
+ reject(error);
71
+ }
72
+ });
73
+ });
74
+ const startTrayHost = async () => {
75
+ const executablePath = process.argv[1];
76
+ if (executablePath === void 0) {
77
+ throw new Error("Missing executable path.");
78
+ }
79
+ const hostPath = resolve(
80
+ dirname(realpathSync(executablePath)),
81
+ "gestament-tray-host.cjs"
82
+ );
83
+ const host = spawn(process.execPath, [hostPath], {
84
+ env: process.env,
85
+ stdio: ["ignore", "pipe", "inherit"]
86
+ });
87
+ try {
88
+ await waitForTrayHostReady(host);
89
+ return host;
90
+ } catch (error) {
91
+ if (host.exitCode === null && host.signalCode === null) {
92
+ host.kill("SIGTERM");
93
+ }
94
+ throw error;
95
+ }
96
+ };
97
+ const runCommand = (command) => new Promise((resolve2, reject) => {
98
+ if (command.length === 0) {
99
+ reject(new Error("Missing command to run under Xvfb."));
100
+ return;
101
+ }
102
+ const child = spawn(command[0], command.slice(1), {
103
+ env: process.env,
104
+ stdio: "inherit"
105
+ });
106
+ child.on("error", reject);
107
+ child.on("exit", (code, signal) => {
108
+ resolve2({ code, signal });
109
+ });
110
+ });
111
+ const run = async () => {
112
+ const parsed = parseArguments(process.argv.slice(2));
113
+ const trayHost = parsed.withTrayHost ? await startTrayHost() : void 0;
114
+ try {
115
+ const result = await runCommand(parsed.command);
116
+ if (result.code !== null) {
117
+ process.exitCode = result.code;
118
+ return;
119
+ }
120
+ process.stderr.write(
121
+ `gestament-xvfb command exited by signal: ${result.signal}
122
+ `
123
+ );
124
+ process.exitCode = 1;
125
+ } finally {
126
+ if (trayHost !== void 0 && trayHost.exitCode === null) {
127
+ trayHost.kill("SIGTERM");
128
+ }
129
+ }
130
+ };
131
+ run().catch((error) => {
132
+ const message = error instanceof Error ? error.message : String(error);
133
+ process.stderr.write(`gestament-xvfb worker: ${message}
134
+ `);
135
+ process.exitCode = 2;
136
+ });
137
+ //# sourceMappingURL=gestament-xvfb-worker.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gestament-xvfb-worker.mjs","sources":["../src/gestament-xvfb-worker.ts"],"sourcesContent":["#!/usr/bin/env node\n// gestament - TypeScript based test driver for GTK.\n// Copyright (c) Kouji Matsui. (@kekyo@mi.kekyo.net)\n// Under MIT.\n// https://github.com/kekyo/gestament\n\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport { realpathSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\n\n/////////////////////////////////////////////////////////////////////////////////////////\n\ninterface WorkerArguments {\n readonly command: readonly string[];\n readonly withTrayHost: boolean;\n}\n\nconst trayHostReadyLine = 'gestament-tray-host-ready';\nconst trayHostReadyTimeoutMs = 30_000;\n\nconst parseArguments = (args: readonly string[]): WorkerArguments => {\n let withTrayHost = false;\n let index = 0;\n\n while (index < args.length) {\n const argument = args[index];\n if (argument === '--') {\n return {\n command: args.slice(index + 1),\n withTrayHost,\n };\n }\n\n if (argument === '--with-tray-host') {\n withTrayHost = true;\n index += 1;\n continue;\n }\n\n throw new Error(`Unknown gestament-xvfb worker option: ${argument}`);\n }\n\n throw new Error('Missing command separator: --');\n};\n\nconst waitForTrayHostReady = (host: ChildProcess): Promise<void> =>\n new Promise<void>((resolve, reject) => {\n if (host.stdout === null) {\n reject(new Error('gestament tray host did not expose stdout.'));\n return;\n }\n\n let output = '';\n let settled = false;\n\n const timeout = setTimeout(() => {\n if (!settled) {\n settled = true;\n reject(new Error('Timed out waiting for gestament tray host.'));\n }\n }, trayHostReadyTimeoutMs);\n\n host.stdout.on('data', (chunk: Buffer) => {\n const text = chunk.toString('utf8');\n output += text;\n if (!settled && output.includes(trayHostReadyLine)) {\n settled = true;\n clearTimeout(timeout);\n resolve();\n return;\n }\n if (settled) {\n process.stdout.write(text);\n }\n });\n\n host.on('exit', (code, signal) => {\n if (!settled) {\n settled = true;\n clearTimeout(timeout);\n reject(\n new Error(\n `gestament tray host exited before ready: code=${String(\n code\n )}, signal=${String(signal)}`\n )\n );\n }\n });\n\n host.on('error', (error) => {\n if (!settled) {\n settled = true;\n clearTimeout(timeout);\n reject(error);\n }\n });\n });\n\nconst startTrayHost = async (): Promise<ChildProcess> => {\n const executablePath = process.argv[1];\n if (executablePath === undefined) {\n throw new Error('Missing executable path.');\n }\n\n const hostPath = resolve(\n dirname(realpathSync(executablePath)),\n 'gestament-tray-host.cjs'\n );\n const host = spawn(process.execPath, [hostPath], {\n env: process.env,\n stdio: ['ignore', 'pipe', 'inherit'],\n });\n try {\n await waitForTrayHostReady(host);\n return host;\n } catch (error) {\n if (host.exitCode === null && host.signalCode === null) {\n host.kill('SIGTERM');\n }\n throw error;\n }\n};\n\nconst runCommand = (\n command: readonly string[]\n): Promise<{\n readonly code: number | null;\n readonly signal: NodeJS.Signals | null;\n}> =>\n new Promise((resolve, reject) => {\n if (command.length === 0) {\n reject(new Error('Missing command to run under Xvfb.'));\n return;\n }\n\n const child = spawn(command[0] as string, command.slice(1), {\n env: process.env,\n stdio: 'inherit',\n });\n\n child.on('error', reject);\n child.on('exit', (code, signal) => {\n resolve({ code, signal });\n });\n });\n\nconst run = async (): Promise<void> => {\n const parsed = parseArguments(process.argv.slice(2));\n const trayHost = parsed.withTrayHost ? await startTrayHost() : undefined;\n\n try {\n const result = await runCommand(parsed.command);\n if (result.code !== null) {\n process.exitCode = result.code;\n return;\n }\n\n process.stderr.write(\n `gestament-xvfb command exited by signal: ${result.signal}\\n`\n );\n process.exitCode = 1;\n } finally {\n if (trayHost !== undefined && trayHost.exitCode === null) {\n trayHost.kill('SIGTERM');\n }\n }\n};\n\n/////////////////////////////////////////////////////////////////////////////////////////\n\nrun().catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`gestament-xvfb worker: ${message}\\n`);\n process.exitCode = 2;\n});\n"],"names":["resolve"],"mappings":"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"}
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ const node_child_process = require("node:child_process");
4
+ const node_fs = require("node:fs");
5
+ const node_path = require("node:path");
6
+ const defaultScreen = "1280x720x24";
7
+ const screenPattern = /^[1-9][0-9]*x[1-9][0-9]*x[1-9][0-9]*$/;
8
+ const printUsage = () => {
9
+ process.stdout.write(
10
+ [
11
+ "Usage: gestament-xvfb [--screen=WIDTHxHEIGHTxDEPTH] -- <command> [args...]",
12
+ " gestament-xvfb [--with-tray-host] [--screen=WIDTHxHEIGHTxDEPTH] -- <command> [args...]",
13
+ "",
14
+ "Runs a command under xvfb-run and dbus-run-session for GTK visual tests.",
15
+ ""
16
+ ].join("\n")
17
+ );
18
+ };
19
+ const parseArguments = (args) => {
20
+ let screen = defaultScreen;
21
+ let withTrayHost = false;
22
+ let index = 0;
23
+ while (index < args.length) {
24
+ const argument = args[index];
25
+ if (!argument || argument === "--help" || argument === "-h") {
26
+ printUsage();
27
+ process.exit(0);
28
+ }
29
+ if (argument === "--") {
30
+ return {
31
+ command: args.slice(index + 1),
32
+ screen,
33
+ withTrayHost
34
+ };
35
+ }
36
+ if (argument.startsWith("--screen=")) {
37
+ screen = argument.slice("--screen=".length);
38
+ index += 1;
39
+ continue;
40
+ }
41
+ if (argument === "--screen") {
42
+ const value = args[index + 1];
43
+ if (value === void 0) {
44
+ throw new Error("--screen requires WIDTHxHEIGHTxDEPTH.");
45
+ }
46
+ screen = value;
47
+ index += 2;
48
+ continue;
49
+ }
50
+ if (argument === "--with-tray-host") {
51
+ withTrayHost = true;
52
+ index += 1;
53
+ continue;
54
+ }
55
+ throw new Error(`Unknown gestament-xvfb option: ${argument}`);
56
+ }
57
+ throw new Error("Missing command separator: --");
58
+ };
59
+ const run = () => {
60
+ const parsed = parseArguments(process.argv.slice(2));
61
+ if (!screenPattern.test(parsed.screen)) {
62
+ throw new Error(
63
+ `Invalid Xvfb screen value: ${parsed.screen}. Expected WIDTHxHEIGHTxDEPTH.`
64
+ );
65
+ }
66
+ if (parsed.command.length === 0) {
67
+ throw new Error("Missing command to run under Xvfb.");
68
+ }
69
+ const env = {
70
+ ...process.env,
71
+ GDK_BACKEND: "x11",
72
+ GSETTINGS_BACKEND: "memory",
73
+ GTK_THEME: process.env.GTK_THEME ?? "Adwaita"
74
+ };
75
+ delete env.NO_AT_BRIDGE;
76
+ delete env.AT_SPI_BUS_ADDRESS;
77
+ const executablePath = process.argv[1];
78
+ if (executablePath === void 0) {
79
+ throw new Error("Missing executable path.");
80
+ }
81
+ const workerPath = node_path.resolve(
82
+ node_path.dirname(node_fs.realpathSync(executablePath)),
83
+ "gestament-xvfb-worker.cjs"
84
+ );
85
+ const workerArgs = parsed.withTrayHost ? ["--with-tray-host"] : [];
86
+ const child = node_child_process.spawn(
87
+ "xvfb-run",
88
+ [
89
+ "-a",
90
+ "-s",
91
+ `-screen 0 ${parsed.screen}`,
92
+ "--",
93
+ "dbus-run-session",
94
+ "--",
95
+ process.execPath,
96
+ workerPath,
97
+ ...workerArgs,
98
+ "--",
99
+ ...parsed.command
100
+ ],
101
+ {
102
+ env,
103
+ stdio: "inherit"
104
+ }
105
+ );
106
+ child.on("error", (error) => {
107
+ process.stderr.write(`gestament-xvfb failed to start: ${error.message}
108
+ `);
109
+ process.exitCode = 1;
110
+ });
111
+ child.on("exit", (code, signal) => {
112
+ if (code !== null) {
113
+ process.exitCode = code;
114
+ return;
115
+ }
116
+ process.stderr.write(
117
+ `gestament-xvfb command exited by signal: ${signal}
118
+ `
119
+ );
120
+ process.exitCode = 1;
121
+ });
122
+ };
123
+ try {
124
+ run();
125
+ } catch (error) {
126
+ const message = error instanceof Error ? error.message : String(error);
127
+ process.stderr.write(`gestament-xvfb: ${message}
128
+ `);
129
+ process.stderr.write('Run "gestament-xvfb --help" for usage.\n');
130
+ process.exitCode = 2;
131
+ }
132
+ //# sourceMappingURL=gestament-xvfb.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gestament-xvfb.cjs","sources":["../src/gestament-xvfb.ts"],"sourcesContent":["#!/usr/bin/env node\n// gestament - TypeScript based test driver for GTK.\n// Copyright (c) Kouji Matsui. (@kekyo@mi.kekyo.net)\n// Under MIT.\n// https://github.com/kekyo/gestament\n\nimport { spawn } from 'node:child_process';\nimport { realpathSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\n\n/////////////////////////////////////////////////////////////////////////////////////////\n\ninterface ParsedArguments {\n readonly screen: string;\n readonly command: readonly string[];\n readonly withTrayHost: boolean;\n}\n\nconst defaultScreen = '1280x720x24';\nconst screenPattern = /^[1-9][0-9]*x[1-9][0-9]*x[1-9][0-9]*$/;\n\nconst printUsage = (): void => {\n process.stdout.write(\n [\n 'Usage: gestament-xvfb [--screen=WIDTHxHEIGHTxDEPTH] -- <command> [args...]',\n ' gestament-xvfb [--with-tray-host] [--screen=WIDTHxHEIGHTxDEPTH] -- <command> [args...]',\n '',\n 'Runs a command under xvfb-run and dbus-run-session for GTK visual tests.',\n '',\n ].join('\\n')\n );\n};\n\nconst parseArguments = (args: readonly string[]): ParsedArguments => {\n let screen = defaultScreen;\n let withTrayHost = false;\n let index = 0;\n\n while (index < args.length) {\n const argument = args[index];\n if (!argument || argument === '--help' || argument === '-h') {\n printUsage();\n process.exit(0);\n }\n\n if (argument === '--') {\n return {\n command: args.slice(index + 1),\n screen,\n withTrayHost,\n };\n }\n\n if (argument.startsWith('--screen=')) {\n screen = argument.slice('--screen='.length);\n index += 1;\n continue;\n }\n\n if (argument === '--screen') {\n const value = args[index + 1];\n if (value === undefined) {\n throw new Error('--screen requires WIDTHxHEIGHTxDEPTH.');\n }\n screen = value;\n index += 2;\n continue;\n }\n\n if (argument === '--with-tray-host') {\n withTrayHost = true;\n index += 1;\n continue;\n }\n\n throw new Error(`Unknown gestament-xvfb option: ${argument}`);\n }\n\n throw new Error('Missing command separator: --');\n};\n\nconst run = (): void => {\n const parsed = parseArguments(process.argv.slice(2));\n if (!screenPattern.test(parsed.screen)) {\n throw new Error(\n `Invalid Xvfb screen value: ${parsed.screen}. Expected WIDTHxHEIGHTxDEPTH.`\n );\n }\n if (parsed.command.length === 0) {\n throw new Error('Missing command to run under Xvfb.');\n }\n\n const env: NodeJS.ProcessEnv = {\n ...process.env,\n GDK_BACKEND: 'x11',\n GSETTINGS_BACKEND: 'memory',\n GTK_THEME: process.env.GTK_THEME ?? 'Adwaita',\n };\n delete env.NO_AT_BRIDGE;\n delete env.AT_SPI_BUS_ADDRESS;\n\n const executablePath = process.argv[1];\n if (executablePath === undefined) {\n throw new Error('Missing executable path.');\n }\n\n const workerPath = resolve(\n dirname(realpathSync(executablePath)),\n 'gestament-xvfb-worker.cjs'\n );\n const workerArgs = parsed.withTrayHost ? ['--with-tray-host'] : [];\n\n const child = spawn(\n 'xvfb-run',\n [\n '-a',\n '-s',\n `-screen 0 ${parsed.screen}`,\n '--',\n 'dbus-run-session',\n '--',\n process.execPath,\n workerPath,\n ...workerArgs,\n '--',\n ...parsed.command,\n ],\n {\n env,\n stdio: 'inherit',\n }\n );\n\n child.on('error', (error) => {\n process.stderr.write(`gestament-xvfb failed to start: ${error.message}\\n`);\n process.exitCode = 1;\n });\n\n child.on('exit', (code, signal) => {\n if (code !== null) {\n process.exitCode = code;\n return;\n }\n\n process.stderr.write(\n `gestament-xvfb command exited by signal: ${signal}\\n`\n );\n process.exitCode = 1;\n });\n};\n\n/////////////////////////////////////////////////////////////////////////////////////////\n\ntry {\n run();\n} catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`gestament-xvfb: ${message}\\n`);\n process.stderr.write('Run \"gestament-xvfb --help\" for usage.\\n');\n process.exitCode = 2;\n}\n"],"names":["resolve","dirname","realpathSync","spawn"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ /*!
3
+ * name: gestament
4
+ * version: 0.1.0
5
+ * description: TypeScript based test driver for GTK
6
+ * author: Kouji Matsui (@kekyo@mi.kekyo.net)
7
+ * license: MIT
8
+ * repository.url: https://github.com/kekyo/gestament.git
9
+ * git.commit.hash: 2e2298272ff7a9fff6945b8d87de6223ac4d7847
10
+ */
11
+
12
+ export {};
13
+ //# sourceMappingURL=gestament-xvfb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gestament-xvfb.d.ts","sourceRoot":"","sources":["../src/gestament-xvfb.ts"],"names":[],"mappings":";;;;;;;;;"}
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "node:child_process";
3
+ import { realpathSync } from "node:fs";
4
+ import { resolve, dirname } from "node:path";
5
+ const defaultScreen = "1280x720x24";
6
+ const screenPattern = /^[1-9][0-9]*x[1-9][0-9]*x[1-9][0-9]*$/;
7
+ const printUsage = () => {
8
+ process.stdout.write(
9
+ [
10
+ "Usage: gestament-xvfb [--screen=WIDTHxHEIGHTxDEPTH] -- <command> [args...]",
11
+ " gestament-xvfb [--with-tray-host] [--screen=WIDTHxHEIGHTxDEPTH] -- <command> [args...]",
12
+ "",
13
+ "Runs a command under xvfb-run and dbus-run-session for GTK visual tests.",
14
+ ""
15
+ ].join("\n")
16
+ );
17
+ };
18
+ const parseArguments = (args) => {
19
+ let screen = defaultScreen;
20
+ let withTrayHost = false;
21
+ let index = 0;
22
+ while (index < args.length) {
23
+ const argument = args[index];
24
+ if (!argument || argument === "--help" || argument === "-h") {
25
+ printUsage();
26
+ process.exit(0);
27
+ }
28
+ if (argument === "--") {
29
+ return {
30
+ command: args.slice(index + 1),
31
+ screen,
32
+ withTrayHost
33
+ };
34
+ }
35
+ if (argument.startsWith("--screen=")) {
36
+ screen = argument.slice("--screen=".length);
37
+ index += 1;
38
+ continue;
39
+ }
40
+ if (argument === "--screen") {
41
+ const value = args[index + 1];
42
+ if (value === void 0) {
43
+ throw new Error("--screen requires WIDTHxHEIGHTxDEPTH.");
44
+ }
45
+ screen = value;
46
+ index += 2;
47
+ continue;
48
+ }
49
+ if (argument === "--with-tray-host") {
50
+ withTrayHost = true;
51
+ index += 1;
52
+ continue;
53
+ }
54
+ throw new Error(`Unknown gestament-xvfb option: ${argument}`);
55
+ }
56
+ throw new Error("Missing command separator: --");
57
+ };
58
+ const run = () => {
59
+ const parsed = parseArguments(process.argv.slice(2));
60
+ if (!screenPattern.test(parsed.screen)) {
61
+ throw new Error(
62
+ `Invalid Xvfb screen value: ${parsed.screen}. Expected WIDTHxHEIGHTxDEPTH.`
63
+ );
64
+ }
65
+ if (parsed.command.length === 0) {
66
+ throw new Error("Missing command to run under Xvfb.");
67
+ }
68
+ const env = {
69
+ ...process.env,
70
+ GDK_BACKEND: "x11",
71
+ GSETTINGS_BACKEND: "memory",
72
+ GTK_THEME: process.env.GTK_THEME ?? "Adwaita"
73
+ };
74
+ delete env.NO_AT_BRIDGE;
75
+ delete env.AT_SPI_BUS_ADDRESS;
76
+ const executablePath = process.argv[1];
77
+ if (executablePath === void 0) {
78
+ throw new Error("Missing executable path.");
79
+ }
80
+ const workerPath = resolve(
81
+ dirname(realpathSync(executablePath)),
82
+ "gestament-xvfb-worker.cjs"
83
+ );
84
+ const workerArgs = parsed.withTrayHost ? ["--with-tray-host"] : [];
85
+ const child = spawn(
86
+ "xvfb-run",
87
+ [
88
+ "-a",
89
+ "-s",
90
+ `-screen 0 ${parsed.screen}`,
91
+ "--",
92
+ "dbus-run-session",
93
+ "--",
94
+ process.execPath,
95
+ workerPath,
96
+ ...workerArgs,
97
+ "--",
98
+ ...parsed.command
99
+ ],
100
+ {
101
+ env,
102
+ stdio: "inherit"
103
+ }
104
+ );
105
+ child.on("error", (error) => {
106
+ process.stderr.write(`gestament-xvfb failed to start: ${error.message}
107
+ `);
108
+ process.exitCode = 1;
109
+ });
110
+ child.on("exit", (code, signal) => {
111
+ if (code !== null) {
112
+ process.exitCode = code;
113
+ return;
114
+ }
115
+ process.stderr.write(
116
+ `gestament-xvfb command exited by signal: ${signal}
117
+ `
118
+ );
119
+ process.exitCode = 1;
120
+ });
121
+ };
122
+ try {
123
+ run();
124
+ } catch (error) {
125
+ const message = error instanceof Error ? error.message : String(error);
126
+ process.stderr.write(`gestament-xvfb: ${message}
127
+ `);
128
+ process.stderr.write('Run "gestament-xvfb --help" for usage.\n');
129
+ process.exitCode = 2;
130
+ }
131
+ //# sourceMappingURL=gestament-xvfb.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gestament-xvfb.mjs","sources":["../src/gestament-xvfb.ts"],"sourcesContent":["#!/usr/bin/env node\n// gestament - TypeScript based test driver for GTK.\n// Copyright (c) Kouji Matsui. (@kekyo@mi.kekyo.net)\n// Under MIT.\n// https://github.com/kekyo/gestament\n\nimport { spawn } from 'node:child_process';\nimport { realpathSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\n\n/////////////////////////////////////////////////////////////////////////////////////////\n\ninterface ParsedArguments {\n readonly screen: string;\n readonly command: readonly string[];\n readonly withTrayHost: boolean;\n}\n\nconst defaultScreen = '1280x720x24';\nconst screenPattern = /^[1-9][0-9]*x[1-9][0-9]*x[1-9][0-9]*$/;\n\nconst printUsage = (): void => {\n process.stdout.write(\n [\n 'Usage: gestament-xvfb [--screen=WIDTHxHEIGHTxDEPTH] -- <command> [args...]',\n ' gestament-xvfb [--with-tray-host] [--screen=WIDTHxHEIGHTxDEPTH] -- <command> [args...]',\n '',\n 'Runs a command under xvfb-run and dbus-run-session for GTK visual tests.',\n '',\n ].join('\\n')\n );\n};\n\nconst parseArguments = (args: readonly string[]): ParsedArguments => {\n let screen = defaultScreen;\n let withTrayHost = false;\n let index = 0;\n\n while (index < args.length) {\n const argument = args[index];\n if (!argument || argument === '--help' || argument === '-h') {\n printUsage();\n process.exit(0);\n }\n\n if (argument === '--') {\n return {\n command: args.slice(index + 1),\n screen,\n withTrayHost,\n };\n }\n\n if (argument.startsWith('--screen=')) {\n screen = argument.slice('--screen='.length);\n index += 1;\n continue;\n }\n\n if (argument === '--screen') {\n const value = args[index + 1];\n if (value === undefined) {\n throw new Error('--screen requires WIDTHxHEIGHTxDEPTH.');\n }\n screen = value;\n index += 2;\n continue;\n }\n\n if (argument === '--with-tray-host') {\n withTrayHost = true;\n index += 1;\n continue;\n }\n\n throw new Error(`Unknown gestament-xvfb option: ${argument}`);\n }\n\n throw new Error('Missing command separator: --');\n};\n\nconst run = (): void => {\n const parsed = parseArguments(process.argv.slice(2));\n if (!screenPattern.test(parsed.screen)) {\n throw new Error(\n `Invalid Xvfb screen value: ${parsed.screen}. Expected WIDTHxHEIGHTxDEPTH.`\n );\n }\n if (parsed.command.length === 0) {\n throw new Error('Missing command to run under Xvfb.');\n }\n\n const env: NodeJS.ProcessEnv = {\n ...process.env,\n GDK_BACKEND: 'x11',\n GSETTINGS_BACKEND: 'memory',\n GTK_THEME: process.env.GTK_THEME ?? 'Adwaita',\n };\n delete env.NO_AT_BRIDGE;\n delete env.AT_SPI_BUS_ADDRESS;\n\n const executablePath = process.argv[1];\n if (executablePath === undefined) {\n throw new Error('Missing executable path.');\n }\n\n const workerPath = resolve(\n dirname(realpathSync(executablePath)),\n 'gestament-xvfb-worker.cjs'\n );\n const workerArgs = parsed.withTrayHost ? ['--with-tray-host'] : [];\n\n const child = spawn(\n 'xvfb-run',\n [\n '-a',\n '-s',\n `-screen 0 ${parsed.screen}`,\n '--',\n 'dbus-run-session',\n '--',\n process.execPath,\n workerPath,\n ...workerArgs,\n '--',\n ...parsed.command,\n ],\n {\n env,\n stdio: 'inherit',\n }\n );\n\n child.on('error', (error) => {\n process.stderr.write(`gestament-xvfb failed to start: ${error.message}\\n`);\n process.exitCode = 1;\n });\n\n child.on('exit', (code, signal) => {\n if (code !== null) {\n process.exitCode = code;\n return;\n }\n\n process.stderr.write(\n `gestament-xvfb command exited by signal: ${signal}\\n`\n );\n process.exitCode = 1;\n });\n};\n\n/////////////////////////////////////////////////////////////////////////////////////////\n\ntry {\n run();\n} catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`gestament-xvfb: ${message}\\n`);\n process.stderr.write('Run \"gestament-xvfb --help\" for usage.\\n');\n process.exitCode = 2;\n}\n"],"names":[],"mappings":"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"}