@voltkit/volt-test 0.1.2
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.
- package/dist/artifacts.d.ts +18 -0
- package/dist/artifacts.js +109 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.js +154 -0
- package/dist/config.js.map +1 -0
- package/dist/drivers/file-dialog.d.ts +22 -0
- package/dist/drivers/file-dialog.js +119 -0
- package/dist/drivers/file-dialog.js.map +1 -0
- package/dist/drivers/index.d.ts +6 -0
- package/dist/drivers/index.js +4 -0
- package/dist/drivers/index.js.map +1 -0
- package/dist/drivers/menu.d.ts +17 -0
- package/dist/drivers/menu.js +34 -0
- package/dist/drivers/menu.js.map +1 -0
- package/dist/drivers/tray.d.ts +13 -0
- package/dist/drivers/tray.js +27 -0
- package/dist/drivers/tray.js.map +1 -0
- package/dist/fs.d.ts +9 -0
- package/dist/fs.js +49 -0
- package/dist/fs.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/launcher.d.ts +42 -0
- package/dist/launcher.js +172 -0
- package/dist/launcher.js.map +1 -0
- package/dist/path.d.ts +1 -0
- package/dist/path.js +11 -0
- package/dist/path.js.map +1 -0
- package/dist/process.d.ts +10 -0
- package/dist/process.js +71 -0
- package/dist/process.js.map +1 -0
- package/dist/runner.d.ts +11 -0
- package/dist/runner.js +230 -0
- package/dist/runner.js.map +1 -0
- package/dist/suites/hello-world.d.ts +15 -0
- package/dist/suites/hello-world.js +139 -0
- package/dist/suites/hello-world.js.map +1 -0
- package/dist/suites/index.d.ts +2 -0
- package/dist/suites/index.js +3 -0
- package/dist/suites/index.js.map +1 -0
- package/dist/suites/ipc-demo.d.ts +26 -0
- package/dist/suites/ipc-demo.js +287 -0
- package/dist/suites/ipc-demo.js.map +1 -0
- package/dist/types.d.ts +48 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/window.d.ts +13 -0
- package/dist/window.js +65 -0
- package/dist/window.js.map +1 -0
- package/package.json +36 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { sanitizePathSegment } from './path.js';
|
|
2
|
+
import type { VoltTestLogger } from './types.js';
|
|
3
|
+
export interface ScreenshotCommand {
|
|
4
|
+
command: string;
|
|
5
|
+
args: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function createRunArtifactRoot(repoRoot: string, artifactDir?: string): string;
|
|
8
|
+
export declare function createSuiteAttemptArtifactDir(runArtifactRoot: string, suiteName: string, attemptNumber: number): string;
|
|
9
|
+
export declare function writeJsonArtifact(filePath: string, payload: unknown): void;
|
|
10
|
+
export declare function writeTextArtifact(filePath: string, contents: string): void;
|
|
11
|
+
export declare function captureDesktopScreenshot(screenshotPath: string, logger: VoltTestLogger, platform?: NodeJS.Platform): Promise<boolean>;
|
|
12
|
+
export declare function buildScreenshotCommand(platform: string, screenshotPath: string): ScreenshotCommand | null;
|
|
13
|
+
declare function timestampForPath(date: Date): string;
|
|
14
|
+
export declare const __testOnly: {
|
|
15
|
+
sanitizePathSegment: typeof sanitizePathSegment;
|
|
16
|
+
timestampForPath: typeof timestampForPath;
|
|
17
|
+
};
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import { sanitizePathSegment } from './path.js';
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
export function createRunArtifactRoot(repoRoot, artifactDir) {
|
|
8
|
+
const root = artifactDir
|
|
9
|
+
? resolve(repoRoot, artifactDir)
|
|
10
|
+
: resolve(repoRoot, 'artifacts', 'volt-test', timestampForPath(new Date()));
|
|
11
|
+
mkdirSync(root, { recursive: true });
|
|
12
|
+
return root;
|
|
13
|
+
}
|
|
14
|
+
export function createSuiteAttemptArtifactDir(runArtifactRoot, suiteName, attemptNumber) {
|
|
15
|
+
const suiteSegment = sanitizePathSegment(suiteName);
|
|
16
|
+
const attemptSegment = `attempt-${attemptNumber}`;
|
|
17
|
+
const suiteDir = join(runArtifactRoot, suiteSegment, attemptSegment);
|
|
18
|
+
mkdirSync(suiteDir, { recursive: true });
|
|
19
|
+
return suiteDir;
|
|
20
|
+
}
|
|
21
|
+
export function writeJsonArtifact(filePath, payload) {
|
|
22
|
+
const serialized = `${JSON.stringify(payload, null, 2)}\n`;
|
|
23
|
+
writeTextArtifact(filePath, serialized);
|
|
24
|
+
}
|
|
25
|
+
export function writeTextArtifact(filePath, contents) {
|
|
26
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
27
|
+
writeFileSync(filePath, contents, 'utf8');
|
|
28
|
+
}
|
|
29
|
+
export async function captureDesktopScreenshot(screenshotPath, logger, platform = process.platform) {
|
|
30
|
+
const screenshotCommand = buildScreenshotCommand(platform, screenshotPath);
|
|
31
|
+
if (!screenshotCommand) {
|
|
32
|
+
logger.warn(`[volt:test] screenshot capture is not supported on ${platform}.`);
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
await execFileAsync(screenshotCommand.command, screenshotCommand.args, {
|
|
37
|
+
timeout: 15_000,
|
|
38
|
+
windowsHide: true,
|
|
39
|
+
});
|
|
40
|
+
logger.log(`[volt:test] screenshot captured: ${screenshotPath}`);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
logger.warn(`[volt:test] failed to capture screenshot at ${screenshotPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function buildScreenshotCommand(platform, screenshotPath) {
|
|
49
|
+
if (platform === 'darwin') {
|
|
50
|
+
return {
|
|
51
|
+
command: 'screencapture',
|
|
52
|
+
args: ['-x', screenshotPath],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (platform === 'win32') {
|
|
56
|
+
// Embed the path directly in the script instead of relying on $args,
|
|
57
|
+
// because PowerShell -Command mode parses trailing arguments as
|
|
58
|
+
// expressions rather than passing them as $args.
|
|
59
|
+
const escapedPath = screenshotPath.replace(/'/g, "''");
|
|
60
|
+
const captureScript = [
|
|
61
|
+
'Add-Type -AssemblyName System.Windows.Forms',
|
|
62
|
+
'Add-Type -AssemblyName System.Drawing',
|
|
63
|
+
'$bounds = [System.Windows.Forms.SystemInformation]::VirtualScreen',
|
|
64
|
+
'$bitmap = New-Object System.Drawing.Bitmap $bounds.Width, $bounds.Height',
|
|
65
|
+
'$graphics = [System.Drawing.Graphics]::FromImage($bitmap)',
|
|
66
|
+
'$graphics.CopyFromScreen($bounds.Location, [System.Drawing.Point]::Empty, $bounds.Size)',
|
|
67
|
+
`$bitmap.Save('${escapedPath}', [System.Drawing.Imaging.ImageFormat]::Png)`,
|
|
68
|
+
'$graphics.Dispose()',
|
|
69
|
+
'$bitmap.Dispose()',
|
|
70
|
+
].join('; ');
|
|
71
|
+
return {
|
|
72
|
+
command: 'powershell',
|
|
73
|
+
args: [
|
|
74
|
+
'-NoProfile',
|
|
75
|
+
'-NonInteractive',
|
|
76
|
+
'-ExecutionPolicy',
|
|
77
|
+
'Bypass',
|
|
78
|
+
'-Command',
|
|
79
|
+
captureScript,
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (platform === 'linux') {
|
|
84
|
+
const script = [
|
|
85
|
+
'if command -v import >/dev/null 2>&1; then',
|
|
86
|
+
' import -window root "$1"',
|
|
87
|
+
'elif command -v gnome-screenshot >/dev/null 2>&1; then',
|
|
88
|
+
' gnome-screenshot -f "$1"',
|
|
89
|
+
'else',
|
|
90
|
+
' exit 127',
|
|
91
|
+
'fi',
|
|
92
|
+
].join('; ');
|
|
93
|
+
return {
|
|
94
|
+
command: 'sh',
|
|
95
|
+
args: ['-lc', script, 'volt-test-screenshot', screenshotPath],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
function timestampForPath(date) {
|
|
101
|
+
return date.toISOString()
|
|
102
|
+
.replace(/[:]/g, '-')
|
|
103
|
+
.replace(/\..+$/, 'Z');
|
|
104
|
+
}
|
|
105
|
+
export const __testOnly = {
|
|
106
|
+
sanitizePathSegment,
|
|
107
|
+
timestampForPath,
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=artifacts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifacts.js","sourceRoot":"","sources":["../src/artifacts.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { LoadedVoltTestConfig, LoadVoltTestConfigOptions, VoltTestConfig } from './types.js';
|
|
2
|
+
export declare function defineTestConfig(config: VoltTestConfig): VoltTestConfig;
|
|
3
|
+
export declare function validateTestConfig(config: VoltTestConfig, sourceLabel?: string): VoltTestConfig;
|
|
4
|
+
export declare function loadVoltTestConfig(projectRoot: string, options?: LoadVoltTestConfigOptions): Promise<LoadedVoltTestConfig>;
|
|
5
|
+
declare function resolveConfigCandidates(projectRoot: string, options: LoadVoltTestConfigOptions): string[];
|
|
6
|
+
declare function normalizeConfigModule(moduleValue: unknown, sourceLabel: string): VoltTestConfig;
|
|
7
|
+
declare function validateTimeoutMs(timeoutMs: number, sourceLabel: string): void;
|
|
8
|
+
declare function validateRetryCount(retries: number, sourceLabel: string): void;
|
|
9
|
+
export declare const __testOnly: {
|
|
10
|
+
DEFAULT_TEST_CONFIG_FILES: readonly ["volt.test.config.ts", "volt.test.config.mjs", "volt.test.config.js"];
|
|
11
|
+
normalizeConfigModule: typeof normalizeConfigModule;
|
|
12
|
+
resolveConfigCandidates: typeof resolveConfigCandidates;
|
|
13
|
+
validateRetryCount: typeof validateRetryCount;
|
|
14
|
+
validateTimeoutMs: typeof validateTimeoutMs;
|
|
15
|
+
};
|
|
16
|
+
export {};
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
const DEFAULT_TEST_CONFIG_FILES = [
|
|
5
|
+
'volt.test.config.ts',
|
|
6
|
+
'volt.test.config.mjs',
|
|
7
|
+
'volt.test.config.js',
|
|
8
|
+
];
|
|
9
|
+
export function defineTestConfig(config) {
|
|
10
|
+
return config;
|
|
11
|
+
}
|
|
12
|
+
export function validateTestConfig(config, sourceLabel = 'config') {
|
|
13
|
+
if (!config || typeof config !== 'object') {
|
|
14
|
+
throw new Error(`[volt:test] Invalid ${sourceLabel}: expected object.`);
|
|
15
|
+
}
|
|
16
|
+
if (!Array.isArray(config.suites) || config.suites.length === 0) {
|
|
17
|
+
throw new Error(`[volt:test] Invalid ${sourceLabel}: expected at least one test suite.`);
|
|
18
|
+
}
|
|
19
|
+
const names = new Set();
|
|
20
|
+
for (const suite of config.suites) {
|
|
21
|
+
validateSuite(suite, sourceLabel);
|
|
22
|
+
if (names.has(suite.name)) {
|
|
23
|
+
throw new Error(`[volt:test] Duplicate suite name "${suite.name}" in ${sourceLabel}.`);
|
|
24
|
+
}
|
|
25
|
+
names.add(suite.name);
|
|
26
|
+
}
|
|
27
|
+
if (config.timeoutMs !== undefined) {
|
|
28
|
+
validateTimeoutMs(config.timeoutMs, `${sourceLabel}.timeoutMs`);
|
|
29
|
+
}
|
|
30
|
+
if (config.retries !== undefined) {
|
|
31
|
+
validateRetryCount(config.retries, `${sourceLabel}.retries`);
|
|
32
|
+
}
|
|
33
|
+
if (config.artifactDir !== undefined) {
|
|
34
|
+
if (typeof config.artifactDir !== 'string' || config.artifactDir.trim().length === 0) {
|
|
35
|
+
throw new Error(`[volt:test] Invalid ${sourceLabel}.artifactDir: expected non-empty string.`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return config;
|
|
39
|
+
}
|
|
40
|
+
export async function loadVoltTestConfig(projectRoot, options = {}) {
|
|
41
|
+
const candidates = resolveConfigCandidates(projectRoot, options);
|
|
42
|
+
for (const candidate of candidates) {
|
|
43
|
+
if (!existsSync(candidate)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const config = await loadConfigModule(candidate);
|
|
47
|
+
return {
|
|
48
|
+
configPath: candidate,
|
|
49
|
+
config: validateTestConfig(config, candidate),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (options.strict ?? true) {
|
|
53
|
+
const formattedCandidates = candidates
|
|
54
|
+
.map((candidate) => candidate.replace(`${projectRoot}\\`, '').replace(`${projectRoot}/`, ''))
|
|
55
|
+
.join(', ');
|
|
56
|
+
throw new Error(`[volt:test] No test config found. Expected one of: ${formattedCandidates}.`);
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
configPath: '',
|
|
60
|
+
config: {
|
|
61
|
+
suites: [],
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function resolveConfigCandidates(projectRoot, options) {
|
|
66
|
+
if (options.configPath) {
|
|
67
|
+
return [resolve(projectRoot, options.configPath)];
|
|
68
|
+
}
|
|
69
|
+
return DEFAULT_TEST_CONFIG_FILES.map((filename) => resolve(projectRoot, filename));
|
|
70
|
+
}
|
|
71
|
+
async function loadConfigModule(configPath) {
|
|
72
|
+
if (configPath.endsWith('.ts')) {
|
|
73
|
+
const viaJiti = await loadWithJiti(configPath);
|
|
74
|
+
if (viaJiti) {
|
|
75
|
+
return viaJiti;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return loadWithDynamicImport(configPath);
|
|
79
|
+
}
|
|
80
|
+
async function loadWithJiti(configPath) {
|
|
81
|
+
const jitiModuleName = 'jiti';
|
|
82
|
+
let jitiModule;
|
|
83
|
+
try {
|
|
84
|
+
jitiModule = await import(jitiModuleName);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (isMissingOptionalJitiDependency(error)) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
throw new Error(`[volt:test] Failed to initialize jiti for ${configPath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
91
|
+
}
|
|
92
|
+
const createJiti = jitiModule.createJiti;
|
|
93
|
+
if (!createJiti) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const jiti = createJiti(configPath);
|
|
97
|
+
const loaded = await jiti.import(configPath);
|
|
98
|
+
return normalizeConfigModule(loaded, configPath);
|
|
99
|
+
}
|
|
100
|
+
function isMissingOptionalJitiDependency(error) {
|
|
101
|
+
if (!(error instanceof Error)) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
const message = error.message.toLowerCase();
|
|
105
|
+
return message.includes("cannot find module 'jiti'")
|
|
106
|
+
|| message.includes('cannot find module "jiti"')
|
|
107
|
+
|| message.includes("cannot find package 'jiti'");
|
|
108
|
+
}
|
|
109
|
+
async function loadWithDynamicImport(configPath) {
|
|
110
|
+
const fileUrl = pathToFileURL(configPath).href;
|
|
111
|
+
const loaded = await import(fileUrl).catch((error) => {
|
|
112
|
+
throw new Error(`[volt:test] Failed to load config at ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
113
|
+
});
|
|
114
|
+
return normalizeConfigModule(loaded, configPath);
|
|
115
|
+
}
|
|
116
|
+
function normalizeConfigModule(moduleValue, sourceLabel) {
|
|
117
|
+
const candidate = moduleValue?.default ?? moduleValue;
|
|
118
|
+
if (!candidate || typeof candidate !== 'object') {
|
|
119
|
+
throw new Error(`[volt:test] Invalid config export in ${sourceLabel}.`);
|
|
120
|
+
}
|
|
121
|
+
return candidate;
|
|
122
|
+
}
|
|
123
|
+
function validateSuite(suite, sourceLabel) {
|
|
124
|
+
if (!suite || typeof suite !== 'object') {
|
|
125
|
+
throw new Error(`[volt:test] Invalid suite in ${sourceLabel}: expected object.`);
|
|
126
|
+
}
|
|
127
|
+
if (typeof suite.name !== 'string' || suite.name.trim().length === 0) {
|
|
128
|
+
throw new Error(`[volt:test] Invalid suite in ${sourceLabel}: name must be a non-empty string.`);
|
|
129
|
+
}
|
|
130
|
+
if (typeof suite.run !== 'function') {
|
|
131
|
+
throw new Error(`[volt:test] Invalid suite "${suite.name}" in ${sourceLabel}: missing run function.`);
|
|
132
|
+
}
|
|
133
|
+
if (suite.timeoutMs !== undefined) {
|
|
134
|
+
validateTimeoutMs(suite.timeoutMs, `suite "${suite.name}".timeoutMs`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function validateTimeoutMs(timeoutMs, sourceLabel) {
|
|
138
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
139
|
+
throw new Error(`[volt:test] Invalid ${sourceLabel}: expected positive milliseconds.`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function validateRetryCount(retries, sourceLabel) {
|
|
143
|
+
if (!Number.isInteger(retries) || retries < 0) {
|
|
144
|
+
throw new Error(`[volt:test] Invalid ${sourceLabel}: expected non-negative integer.`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
export const __testOnly = {
|
|
148
|
+
DEFAULT_TEST_CONFIG_FILES,
|
|
149
|
+
normalizeConfigModule,
|
|
150
|
+
resolveConfigCandidates,
|
|
151
|
+
validateRetryCount,
|
|
152
|
+
validateTimeoutMs,
|
|
153
|
+
};
|
|
154
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type FileDialogAutomationPlatform = 'win32' | 'darwin' | 'linux';
|
|
2
|
+
export interface OpenDialogAutomationResult {
|
|
3
|
+
canceled: boolean;
|
|
4
|
+
filePaths: string[];
|
|
5
|
+
}
|
|
6
|
+
export interface SaveDialogAutomationResult {
|
|
7
|
+
canceled: boolean;
|
|
8
|
+
filePath: string;
|
|
9
|
+
}
|
|
10
|
+
export interface FileDialogAutomationDriverOptions {
|
|
11
|
+
platform?: FileDialogAutomationPlatform;
|
|
12
|
+
}
|
|
13
|
+
export declare class FileDialogAutomationDriver {
|
|
14
|
+
private readonly platform;
|
|
15
|
+
constructor(options?: FileDialogAutomationDriverOptions);
|
|
16
|
+
getPlatform(): FileDialogAutomationPlatform;
|
|
17
|
+
parseOpenDialogResult(payload: unknown): OpenDialogAutomationResult;
|
|
18
|
+
parseSaveDialogResult(payload: unknown): SaveDialogAutomationResult;
|
|
19
|
+
normalizePath(pathValue: string): string;
|
|
20
|
+
assertOpenSelection(result: OpenDialogAutomationResult, expectedAbsolutePaths: readonly string[]): void;
|
|
21
|
+
assertSaveSelection(result: SaveDialogAutomationResult, expectedAbsolutePath: string): void;
|
|
22
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { isAbsolute, posix, win32 } from 'node:path';
|
|
2
|
+
export class FileDialogAutomationDriver {
|
|
3
|
+
platform;
|
|
4
|
+
constructor(options = {}) {
|
|
5
|
+
this.platform = resolveAutomationPlatform(options.platform ?? process.platform);
|
|
6
|
+
}
|
|
7
|
+
getPlatform() {
|
|
8
|
+
return this.platform;
|
|
9
|
+
}
|
|
10
|
+
parseOpenDialogResult(payload) {
|
|
11
|
+
const value = asRecord(payload);
|
|
12
|
+
if (!value) {
|
|
13
|
+
throw new Error('[volt:test] invalid open dialog payload: expected object.');
|
|
14
|
+
}
|
|
15
|
+
const canceled = value.canceled;
|
|
16
|
+
if (typeof canceled !== 'boolean') {
|
|
17
|
+
throw new Error('[volt:test] invalid open dialog payload: missing canceled boolean.');
|
|
18
|
+
}
|
|
19
|
+
const rawFilePaths = value.filePaths;
|
|
20
|
+
if (!Array.isArray(rawFilePaths) || rawFilePaths.some((entry) => typeof entry !== 'string')) {
|
|
21
|
+
throw new Error('[volt:test] invalid open dialog payload: filePaths must be a string array.');
|
|
22
|
+
}
|
|
23
|
+
const filePaths = rawFilePaths.map((entry) => this.normalizePath(entry));
|
|
24
|
+
if (canceled && filePaths.length > 0) {
|
|
25
|
+
throw new Error('[volt:test] invalid open dialog payload: canceled dialog must not return file paths.');
|
|
26
|
+
}
|
|
27
|
+
if (!canceled && filePaths.some((entry) => entry.length === 0)) {
|
|
28
|
+
throw new Error('[volt:test] invalid open dialog payload: selected file paths must be non-empty.');
|
|
29
|
+
}
|
|
30
|
+
return { canceled, filePaths };
|
|
31
|
+
}
|
|
32
|
+
parseSaveDialogResult(payload) {
|
|
33
|
+
const value = asRecord(payload);
|
|
34
|
+
if (!value) {
|
|
35
|
+
throw new Error('[volt:test] invalid save dialog payload: expected object.');
|
|
36
|
+
}
|
|
37
|
+
const canceled = value.canceled;
|
|
38
|
+
if (typeof canceled !== 'boolean') {
|
|
39
|
+
throw new Error('[volt:test] invalid save dialog payload: missing canceled boolean.');
|
|
40
|
+
}
|
|
41
|
+
const rawFilePath = value.filePath;
|
|
42
|
+
if (typeof rawFilePath !== 'string') {
|
|
43
|
+
throw new Error('[volt:test] invalid save dialog payload: missing filePath string.');
|
|
44
|
+
}
|
|
45
|
+
const filePath = this.normalizePath(rawFilePath);
|
|
46
|
+
if (!canceled && filePath.length === 0) {
|
|
47
|
+
throw new Error('[volt:test] invalid save dialog payload: filePath must be non-empty when canceled=false.');
|
|
48
|
+
}
|
|
49
|
+
if (canceled && filePath.length > 0) {
|
|
50
|
+
throw new Error('[volt:test] invalid save dialog payload: canceled dialog must not return filePath.');
|
|
51
|
+
}
|
|
52
|
+
return { canceled, filePath };
|
|
53
|
+
}
|
|
54
|
+
normalizePath(pathValue) {
|
|
55
|
+
const trimmed = pathValue.trim();
|
|
56
|
+
if (trimmed.length === 0) {
|
|
57
|
+
return '';
|
|
58
|
+
}
|
|
59
|
+
if (this.platform === 'win32') {
|
|
60
|
+
const withWinSeparators = trimmed.replace(/\//g, '\\');
|
|
61
|
+
const normalized = win32.normalize(withWinSeparators);
|
|
62
|
+
if (isWindowsDrivePath(normalized)) {
|
|
63
|
+
return `${normalized.slice(0, 1).toUpperCase()}${normalized.slice(1)}`;
|
|
64
|
+
}
|
|
65
|
+
return normalized;
|
|
66
|
+
}
|
|
67
|
+
const withPosixSeparators = trimmed.replace(/\\/g, '/');
|
|
68
|
+
return posix.normalize(withPosixSeparators);
|
|
69
|
+
}
|
|
70
|
+
assertOpenSelection(result, expectedAbsolutePaths) {
|
|
71
|
+
if (result.canceled) {
|
|
72
|
+
throw new Error('[volt:test] expected file selection but dialog was canceled.');
|
|
73
|
+
}
|
|
74
|
+
const normalizedExpected = expectedAbsolutePaths.map((entry) => this.normalizePath(entry));
|
|
75
|
+
const normalizedActual = result.filePaths.map((entry) => this.normalizePath(entry));
|
|
76
|
+
if (normalizedExpected.length !== normalizedActual.length) {
|
|
77
|
+
throw new Error(`[volt:test] expected ${normalizedExpected.length} selected files, got ${normalizedActual.length}.`);
|
|
78
|
+
}
|
|
79
|
+
for (let index = 0; index < normalizedExpected.length; index += 1) {
|
|
80
|
+
const expectedPath = normalizedExpected[index];
|
|
81
|
+
const actualPath = normalizedActual[index];
|
|
82
|
+
if (actualPath !== expectedPath) {
|
|
83
|
+
throw new Error(`[volt:test] selected file mismatch at index ${index}: expected "${expectedPath}", got "${actualPath}".`);
|
|
84
|
+
}
|
|
85
|
+
if (!isAbsolute(actualPath)) {
|
|
86
|
+
throw new Error(`[volt:test] expected absolute file path but got "${actualPath}".`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
assertSaveSelection(result, expectedAbsolutePath) {
|
|
91
|
+
if (result.canceled) {
|
|
92
|
+
throw new Error('[volt:test] expected save path but dialog was canceled.');
|
|
93
|
+
}
|
|
94
|
+
const normalizedExpected = this.normalizePath(expectedAbsolutePath);
|
|
95
|
+
const normalizedActual = this.normalizePath(result.filePath);
|
|
96
|
+
if (normalizedActual !== normalizedExpected) {
|
|
97
|
+
throw new Error(`[volt:test] save file mismatch: expected "${normalizedExpected}", got "${normalizedActual}".`);
|
|
98
|
+
}
|
|
99
|
+
if (!isAbsolute(normalizedActual)) {
|
|
100
|
+
throw new Error(`[volt:test] expected absolute save path but got "${normalizedActual}".`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function resolveAutomationPlatform(platformValue) {
|
|
105
|
+
if (platformValue === 'win32' || platformValue === 'darwin' || platformValue === 'linux') {
|
|
106
|
+
return platformValue;
|
|
107
|
+
}
|
|
108
|
+
throw new Error(`[volt:test] unsupported file-dialog automation platform: ${platformValue}`);
|
|
109
|
+
}
|
|
110
|
+
function asRecord(value) {
|
|
111
|
+
if (!value || typeof value !== 'object') {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
function isWindowsDrivePath(value) {
|
|
117
|
+
return /^[a-zA-Z]:\\/.test(value);
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=file-dialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-dialog.js","sourceRoot":"","sources":["../../src/drivers/file-dialog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { AutomationEvent, MenuSetupState, MenuAutomationDriverOptions } from './menu.js';
|
|
2
|
+
export { MenuAutomationDriver } from './menu.js';
|
|
3
|
+
export type { TraySetupState, TrayAutomationDriverOptions } from './tray.js';
|
|
4
|
+
export { TrayAutomationDriver } from './tray.js';
|
|
5
|
+
export type { FileDialogAutomationPlatform, FileDialogAutomationDriverOptions, OpenDialogAutomationResult, SaveDialogAutomationResult, } from './file-dialog.js';
|
|
6
|
+
export { FileDialogAutomationDriver } from './file-dialog.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/drivers/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAOjD,OAAO,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface AutomationEvent {
|
|
2
|
+
event: string;
|
|
3
|
+
payload: unknown;
|
|
4
|
+
}
|
|
5
|
+
export interface MenuSetupState {
|
|
6
|
+
shortcut: string;
|
|
7
|
+
shortcutRegistered: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface MenuAutomationDriverOptions {
|
|
10
|
+
clickEventName?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class MenuAutomationDriver {
|
|
13
|
+
private readonly clickEventName;
|
|
14
|
+
constructor(options?: MenuAutomationDriverOptions);
|
|
15
|
+
parseSetupPayload(payload: unknown): MenuSetupState;
|
|
16
|
+
countClickEvents(events: readonly AutomationEvent[]): number;
|
|
17
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export class MenuAutomationDriver {
|
|
2
|
+
clickEventName;
|
|
3
|
+
constructor(options = {}) {
|
|
4
|
+
this.clickEventName = options.clickEventName ?? 'demo:menu-click';
|
|
5
|
+
}
|
|
6
|
+
parseSetupPayload(payload) {
|
|
7
|
+
const value = asRecord(payload);
|
|
8
|
+
if (!value) {
|
|
9
|
+
throw new Error('[volt:test] invalid menu setup payload: expected object.');
|
|
10
|
+
}
|
|
11
|
+
const shortcut = value.shortcut;
|
|
12
|
+
if (typeof shortcut !== 'string' || shortcut.trim().length === 0) {
|
|
13
|
+
throw new Error('[volt:test] invalid menu setup payload: missing shortcut string.');
|
|
14
|
+
}
|
|
15
|
+
const shortcutRegistered = value.shortcutRegistered;
|
|
16
|
+
if (typeof shortcutRegistered !== 'boolean') {
|
|
17
|
+
throw new Error('[volt:test] invalid menu setup payload: missing shortcutRegistered boolean.');
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
shortcut,
|
|
21
|
+
shortcutRegistered,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
countClickEvents(events) {
|
|
25
|
+
return events.filter((entry) => entry.event === this.clickEventName).length;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function asRecord(value) {
|
|
29
|
+
if (!value || typeof value !== 'object') {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=menu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.js","sourceRoot":"","sources":["../../src/drivers/menu.ts"],"names":[],"mappings":"AAcA,MAAM,OAAO,oBAAoB;IACd,cAAc,CAAS;IAExC,YAAmB,UAAuC,EAAE;QAC1D,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,iBAAiB,CAAC;IACpE,CAAC;IAEM,iBAAiB,CAAC,OAAgB;QACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;QAC9E,CAAC;QAED,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAChC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjE,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;QACtF,CAAC;QAED,MAAM,kBAAkB,GAAG,KAAK,CAAC,kBAAkB,CAAC;QACpD,IAAI,OAAO,kBAAkB,KAAK,SAAS,EAAE,CAAC;YAC5C,MAAM,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAC;QACjG,CAAC;QAED,OAAO;YACL,QAAQ;YACR,kBAAkB;SACnB,CAAC;IACJ,CAAC;IAEM,gBAAgB,CAAC,MAAkC;QACxD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC;IAC9E,CAAC;CACF;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAgC,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AutomationEvent } from './menu.js';
|
|
2
|
+
export interface TraySetupState {
|
|
3
|
+
trayReady: boolean;
|
|
4
|
+
}
|
|
5
|
+
export interface TrayAutomationDriverOptions {
|
|
6
|
+
clickEventName?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class TrayAutomationDriver {
|
|
9
|
+
private readonly clickEventName;
|
|
10
|
+
constructor(options?: TrayAutomationDriverOptions);
|
|
11
|
+
parseSetupPayload(payload: unknown): TraySetupState;
|
|
12
|
+
countClickEvents(events: readonly AutomationEvent[]): number;
|
|
13
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export class TrayAutomationDriver {
|
|
2
|
+
clickEventName;
|
|
3
|
+
constructor(options = {}) {
|
|
4
|
+
this.clickEventName = options.clickEventName ?? 'demo:tray-click';
|
|
5
|
+
}
|
|
6
|
+
parseSetupPayload(payload) {
|
|
7
|
+
const value = asRecord(payload);
|
|
8
|
+
if (!value) {
|
|
9
|
+
throw new Error('[volt:test] invalid tray setup payload: expected object.');
|
|
10
|
+
}
|
|
11
|
+
const trayReady = value.trayReady;
|
|
12
|
+
if (typeof trayReady !== 'boolean') {
|
|
13
|
+
throw new Error('[volt:test] invalid tray setup payload: missing trayReady boolean.');
|
|
14
|
+
}
|
|
15
|
+
return { trayReady };
|
|
16
|
+
}
|
|
17
|
+
countClickEvents(events) {
|
|
18
|
+
return events.filter((entry) => entry.event === this.clickEventName).length;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function asRecord(value) {
|
|
22
|
+
if (!value || typeof value !== 'object') {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=tray.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tray.js","sourceRoot":"","sources":["../../src/drivers/tray.ts"],"names":[],"mappings":"AAUA,MAAM,OAAO,oBAAoB;IACd,cAAc,CAAS;IAExC,YAAmB,UAAuC,EAAE;QAC1D,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,iBAAiB,CAAC;IACpE,CAAC;IAEM,iBAAiB,CAAC,OAAgB;QACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;QAC9E,CAAC;QAED,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;QAClC,IAAI,OAAO,SAAS,KAAK,SAAS,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;QACxF,CAAC;QAED,OAAO,EAAE,SAAS,EAAE,CAAC;IACvB,CAAC;IAEM,gBAAgB,CAAC,MAAkC;QACxD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC;IAC9E,CAAC;CACF;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAgC,CAAC;AAC1C,CAAC"}
|
package/dist/fs.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { VoltTestLogger } from './types.js';
|
|
2
|
+
export interface CopiedProjectPaths {
|
|
3
|
+
tempRoot: string;
|
|
4
|
+
tempProjectDir: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function copyProjectToTemp(projectDir: string, repoRoot: string): CopiedProjectPaths;
|
|
7
|
+
export declare function cleanupDirectoryBestEffort(directoryPath: string, logger: VoltTestLogger): Promise<void>;
|
|
8
|
+
export declare function readTextFile(filePath: string): string;
|
|
9
|
+
export declare function writeTextFile(filePath: string, contents: string): void;
|