slapify 0.0.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.
- package/README.md +452 -0
- package/bin/slapify.js +2 -0
- package/dist/ai/interpreter.d.ts +54 -0
- package/dist/ai/interpreter.d.ts.map +1 -0
- package/dist/ai/interpreter.js +293 -0
- package/dist/ai/interpreter.js.map +1 -0
- package/dist/browser/agent.d.ts +137 -0
- package/dist/browser/agent.d.ts.map +1 -0
- package/dist/browser/agent.js +362 -0
- package/dist/browser/agent.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1181 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/loader.d.ts +35 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +278 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/index.d.ts +155 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +260 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/flow.d.ts +27 -0
- package/dist/parser/flow.d.ts.map +1 -0
- package/dist/parser/flow.js +117 -0
- package/dist/parser/flow.js.map +1 -0
- package/dist/report/generator.d.ts +61 -0
- package/dist/report/generator.d.ts.map +1 -0
- package/dist/report/generator.js +549 -0
- package/dist/report/generator.js.map +1 -0
- package/dist/runner/index.d.ts +44 -0
- package/dist/runner/index.d.ts.map +1 -0
- package/dist/runner/index.js +403 -0
- package/dist/runner/index.js.map +1 -0
- package/dist/types.d.ts +105 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +61 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// Main exports for programmatic usage
|
|
2
|
+
export { loadConfig, loadCredentials, initConfig, getConfigDir, findSystemBrowsers, checkBrowserPath, } from "./config/loader.js";
|
|
3
|
+
export { parseFlowFile, parseFlowContent, findFlowFiles, validateFlowFile, getFlowSummary, } from "./parser/flow.js";
|
|
4
|
+
export { BrowserAgent } from "./browser/agent.js";
|
|
5
|
+
export { AIInterpreter } from "./ai/interpreter.js";
|
|
6
|
+
export { TestRunner } from "./runner/index.js";
|
|
7
|
+
export { ReportGenerator } from "./report/generator.js";
|
|
8
|
+
// Convenience class for programmatic usage
|
|
9
|
+
import { loadConfig, loadCredentials } from "./config/loader.js";
|
|
10
|
+
import { parseFlowFile, parseFlowContent, findFlowFiles, } from "./parser/flow.js";
|
|
11
|
+
import { TestRunner } from "./runner/index.js";
|
|
12
|
+
import { ReportGenerator } from "./report/generator.js";
|
|
13
|
+
/**
|
|
14
|
+
* Main Slapify class for programmatic usage
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { Slapify } from 'testpilot';
|
|
19
|
+
*
|
|
20
|
+
* // Using config file
|
|
21
|
+
* const pilot = new Slapify({ configDir: '.testpilot' });
|
|
22
|
+
*
|
|
23
|
+
* // Or with inline config
|
|
24
|
+
* const pilot = new Slapify({
|
|
25
|
+
* config: {
|
|
26
|
+
* llm: { provider: 'anthropic', model: 'claude-sonnet-4-20250514', api_key: process.env.ANTHROPIC_API_KEY },
|
|
27
|
+
* browser: { headless: true }
|
|
28
|
+
* }
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* // Run inline steps
|
|
32
|
+
* const result = await pilot.run([
|
|
33
|
+
* 'Go to https://example.com',
|
|
34
|
+
* 'Click on "More information" link',
|
|
35
|
+
* 'Verify URL contains "iana.org"'
|
|
36
|
+
* ]);
|
|
37
|
+
*
|
|
38
|
+
* console.log(result.status); // 'passed' or 'failed'
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export class Slapify {
|
|
42
|
+
config;
|
|
43
|
+
credentials;
|
|
44
|
+
reporter;
|
|
45
|
+
constructor(options = {}) {
|
|
46
|
+
// Load config from directory or use provided
|
|
47
|
+
if (options.configDir) {
|
|
48
|
+
this.config = { ...loadConfig(options.configDir), ...options.config };
|
|
49
|
+
this.credentials =
|
|
50
|
+
options.credentials || loadCredentials(options.configDir);
|
|
51
|
+
}
|
|
52
|
+
else if (options.config?.llm) {
|
|
53
|
+
this.config = {
|
|
54
|
+
llm: options.config.llm,
|
|
55
|
+
browser: options.config.browser || { headless: true, timeout: 30000 },
|
|
56
|
+
report: options.config.report || {
|
|
57
|
+
format: "html",
|
|
58
|
+
screenshots: true,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
this.credentials = options.credentials || { profiles: {} };
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// Try to load from cwd
|
|
65
|
+
this.config = loadConfig();
|
|
66
|
+
this.credentials = loadCredentials();
|
|
67
|
+
}
|
|
68
|
+
this.reporter = new ReportGenerator(this.config.report);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create a new runner instance (each test needs its own runner for parallel execution)
|
|
72
|
+
*/
|
|
73
|
+
createRunner() {
|
|
74
|
+
return new TestRunner(this.config, this.credentials);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Run a flow file
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* const result = await pilot.runFile('tests/login.flow');
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
async runFile(filePath, options = {}) {
|
|
85
|
+
const flow = parseFlowFile(filePath);
|
|
86
|
+
return this.runFlow(flow, options);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Run steps from an array of strings
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* const result = await pilot.run([
|
|
94
|
+
* 'Go to https://google.com',
|
|
95
|
+
* 'Fill search box with "testpilot"',
|
|
96
|
+
* 'Press Enter',
|
|
97
|
+
* 'Verify results contain "testpilot"'
|
|
98
|
+
* ], 'google-search');
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
async run(steps, name = "inline", options = {}) {
|
|
102
|
+
const content = steps.join("\n");
|
|
103
|
+
const flow = parseFlowContent(content, name);
|
|
104
|
+
return this.runFlow(flow, options);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Run a flow from string content
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* const flowContent = `
|
|
112
|
+
* Go to https://example.com
|
|
113
|
+
* Click on "More information"
|
|
114
|
+
* Verify page loads
|
|
115
|
+
* `;
|
|
116
|
+
* const result = await pilot.runContent(flowContent, 'example-test');
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
async runContent(content, name = "inline", options = {}) {
|
|
120
|
+
const flow = parseFlowContent(content, name);
|
|
121
|
+
return this.runFlow(flow, options);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Run a parsed flow
|
|
125
|
+
*/
|
|
126
|
+
async runFlow(flow, options = {}) {
|
|
127
|
+
const runner = this.createRunner();
|
|
128
|
+
if (options.onTestStart) {
|
|
129
|
+
options.onTestStart(flow.name, flow.steps.length);
|
|
130
|
+
}
|
|
131
|
+
const result = await runner.runFlow(flow, (stepResult) => {
|
|
132
|
+
if (options.onStep) {
|
|
133
|
+
options.onStep(stepResult, flow.name);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
if (options.onTestComplete) {
|
|
137
|
+
options.onTestComplete(result);
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Run multiple flow files
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* // Sequential
|
|
147
|
+
* const results = await pilot.runMultiple(['tests/login.flow', 'tests/checkout.flow']);
|
|
148
|
+
*
|
|
149
|
+
* // Parallel with 4 workers
|
|
150
|
+
* const results = await pilot.runMultiple(['tests/*.flow'], { parallel: true, workers: 4 });
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
async runMultiple(patterns, options = {}) {
|
|
154
|
+
// Expand patterns to file paths
|
|
155
|
+
const files = [];
|
|
156
|
+
for (const pattern of patterns) {
|
|
157
|
+
if (pattern.includes("*")) {
|
|
158
|
+
// It's a glob pattern - find matching files
|
|
159
|
+
const found = await findFlowFiles(pattern.replace(/\/\*.*$/, ""));
|
|
160
|
+
files.push(...found);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
const fs = await import("fs");
|
|
164
|
+
if (fs.statSync(pattern).isDirectory()) {
|
|
165
|
+
const found = await findFlowFiles(pattern);
|
|
166
|
+
files.push(...found);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
files.push(pattern);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (files.length === 0) {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
const results = [];
|
|
177
|
+
if (options.parallel && files.length > 1) {
|
|
178
|
+
// Parallel execution
|
|
179
|
+
const workers = options.workers || 4;
|
|
180
|
+
const pending = [...files];
|
|
181
|
+
const running = new Map();
|
|
182
|
+
while (pending.length > 0 || running.size > 0) {
|
|
183
|
+
// Start new tasks up to worker limit
|
|
184
|
+
while (pending.length > 0 && running.size < workers) {
|
|
185
|
+
const file = pending.shift();
|
|
186
|
+
const promise = this.runFile(file, options)
|
|
187
|
+
.then((result) => {
|
|
188
|
+
results.push(result);
|
|
189
|
+
running.delete(file);
|
|
190
|
+
})
|
|
191
|
+
.catch(() => {
|
|
192
|
+
running.delete(file);
|
|
193
|
+
});
|
|
194
|
+
running.set(file, promise);
|
|
195
|
+
}
|
|
196
|
+
// Wait for at least one to complete
|
|
197
|
+
if (running.size > 0) {
|
|
198
|
+
await Promise.race(running.values());
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
// Sequential execution
|
|
204
|
+
for (const file of files) {
|
|
205
|
+
const result = await this.runFile(file, options);
|
|
206
|
+
results.push(result);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return results;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Run all flow files in a directory
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```typescript
|
|
216
|
+
* const results = await pilot.runAll('tests/', { parallel: true });
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
async runAll(directory = "tests", options = {}) {
|
|
220
|
+
return this.runMultiple([directory], options);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Generate a report from results
|
|
224
|
+
*/
|
|
225
|
+
generateReport(result) {
|
|
226
|
+
return this.reporter.generate(result);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Generate a suite report from multiple results
|
|
230
|
+
*/
|
|
231
|
+
generateSuiteReport(results) {
|
|
232
|
+
return this.reporter.generateSuiteReport(results);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Save a report to file/folder
|
|
236
|
+
*/
|
|
237
|
+
saveReport(result, filename) {
|
|
238
|
+
return this.reporter.saveAsFolder(result);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Save a suite report to folder
|
|
242
|
+
*/
|
|
243
|
+
saveSuiteReport(results) {
|
|
244
|
+
return this.reporter.saveSuiteAsFolder(results);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Print summary to console
|
|
248
|
+
*/
|
|
249
|
+
printSummary(result) {
|
|
250
|
+
this.reporter.printSummary(result);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get the current configuration
|
|
254
|
+
*/
|
|
255
|
+
getConfig() {
|
|
256
|
+
return this.config;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
export default Slapify;
|
|
260
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { FlowFile } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Parse a .flow file into a FlowFile object
|
|
4
|
+
*/
|
|
5
|
+
export declare function parseFlowFile(filePath: string): FlowFile;
|
|
6
|
+
/**
|
|
7
|
+
* Parse flow content from a string (for inline tests)
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseFlowContent(content: string, name?: string): FlowFile;
|
|
10
|
+
/**
|
|
11
|
+
* Find all .flow files in a directory
|
|
12
|
+
*/
|
|
13
|
+
export declare function findFlowFiles(dir: string): Promise<string[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Validate a flow file for common issues
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateFlowFile(flow: FlowFile): string[];
|
|
18
|
+
/**
|
|
19
|
+
* Get a summary of a flow file
|
|
20
|
+
*/
|
|
21
|
+
export declare function getFlowSummary(flow: FlowFile): {
|
|
22
|
+
totalSteps: number;
|
|
23
|
+
requiredSteps: number;
|
|
24
|
+
optionalSteps: number;
|
|
25
|
+
conditionalSteps: number;
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=flow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../../src/parser/flow.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAY,MAAM,aAAa,CAAC;AA0CjD;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAuBxD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,MAAiB,GACtB,QAAQ,CAiBV;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAGlE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,EAAE,CAmBzD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAOA"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { glob } from "glob";
|
|
4
|
+
/**
|
|
5
|
+
* Parse a single line into a FlowStep
|
|
6
|
+
*/
|
|
7
|
+
function parseLine(line, lineNumber) {
|
|
8
|
+
const trimmed = line.trim();
|
|
9
|
+
// Skip empty lines and comments
|
|
10
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
// Check for [Optional] prefix
|
|
14
|
+
const optionalMatch = trimmed.match(/^\[Optional\]\s*(.+)$/i);
|
|
15
|
+
const isOptional = !!optionalMatch;
|
|
16
|
+
const stepText = optionalMatch ? optionalMatch[1] : trimmed;
|
|
17
|
+
// Check for conditional (If ... appears, ...)
|
|
18
|
+
const conditionalMatch = stepText.match(/^If\s+(.+?)\s*,\s*(.+)$/i);
|
|
19
|
+
const isConditional = !!conditionalMatch;
|
|
20
|
+
return {
|
|
21
|
+
line: lineNumber,
|
|
22
|
+
text: stepText,
|
|
23
|
+
optional: isOptional,
|
|
24
|
+
conditional: isConditional,
|
|
25
|
+
condition: conditionalMatch ? conditionalMatch[1] : undefined,
|
|
26
|
+
action: conditionalMatch ? conditionalMatch[2] : undefined,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Extract comments from a flow file
|
|
31
|
+
*/
|
|
32
|
+
function extractComments(content) {
|
|
33
|
+
return content
|
|
34
|
+
.split("\n")
|
|
35
|
+
.filter((line) => line.trim().startsWith("#"))
|
|
36
|
+
.map((line) => line.trim().substring(1).trim());
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Parse a .flow file into a FlowFile object
|
|
40
|
+
*/
|
|
41
|
+
export function parseFlowFile(filePath) {
|
|
42
|
+
if (!fs.existsSync(filePath)) {
|
|
43
|
+
throw new Error(`Flow file not found: ${filePath}`);
|
|
44
|
+
}
|
|
45
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
46
|
+
const lines = content.split("\n");
|
|
47
|
+
const steps = [];
|
|
48
|
+
for (let i = 0; i < lines.length; i++) {
|
|
49
|
+
const step = parseLine(lines[i], i + 1);
|
|
50
|
+
if (step) {
|
|
51
|
+
steps.push(step);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
path: filePath,
|
|
56
|
+
name: path.basename(filePath, ".flow"),
|
|
57
|
+
steps,
|
|
58
|
+
comments: extractComments(content),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Parse flow content from a string (for inline tests)
|
|
63
|
+
*/
|
|
64
|
+
export function parseFlowContent(content, name = "inline") {
|
|
65
|
+
const lines = content.split("\n");
|
|
66
|
+
const steps = [];
|
|
67
|
+
for (let i = 0; i < lines.length; i++) {
|
|
68
|
+
const step = parseLine(lines[i], i + 1);
|
|
69
|
+
if (step) {
|
|
70
|
+
steps.push(step);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
path: "",
|
|
75
|
+
name,
|
|
76
|
+
steps,
|
|
77
|
+
comments: extractComments(content),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Find all .flow files in a directory
|
|
82
|
+
*/
|
|
83
|
+
export async function findFlowFiles(dir) {
|
|
84
|
+
const pattern = path.join(dir, "**/*.flow");
|
|
85
|
+
return glob(pattern, { nodir: true });
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Validate a flow file for common issues
|
|
89
|
+
*/
|
|
90
|
+
export function validateFlowFile(flow) {
|
|
91
|
+
const warnings = [];
|
|
92
|
+
if (flow.steps.length === 0) {
|
|
93
|
+
warnings.push("Flow file has no steps");
|
|
94
|
+
}
|
|
95
|
+
// Check for steps that might be incomplete
|
|
96
|
+
for (const step of flow.steps) {
|
|
97
|
+
if (step.text.length < 3) {
|
|
98
|
+
warnings.push(`Line ${step.line}: Step seems too short: "${step.text}"`);
|
|
99
|
+
}
|
|
100
|
+
if (step.conditional && !step.action) {
|
|
101
|
+
warnings.push(`Line ${step.line}: Conditional step missing action`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return warnings;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get a summary of a flow file
|
|
108
|
+
*/
|
|
109
|
+
export function getFlowSummary(flow) {
|
|
110
|
+
return {
|
|
111
|
+
totalSteps: flow.steps.length,
|
|
112
|
+
requiredSteps: flow.steps.filter((s) => !s.optional).length,
|
|
113
|
+
optionalSteps: flow.steps.filter((s) => s.optional).length,
|
|
114
|
+
conditionalSteps: flow.steps.filter((s) => s.conditional).length,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow.js","sourceRoot":"","sources":["../../src/parser/flow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { TestResult, ReportConfig } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Generate test reports in various formats
|
|
4
|
+
*/
|
|
5
|
+
export declare class ReportGenerator {
|
|
6
|
+
private config;
|
|
7
|
+
constructor(config?: ReportConfig);
|
|
8
|
+
/**
|
|
9
|
+
* Generate a report from test results
|
|
10
|
+
*/
|
|
11
|
+
generate(result: TestResult): string;
|
|
12
|
+
/**
|
|
13
|
+
* Generate a suite report from multiple test results
|
|
14
|
+
*/
|
|
15
|
+
generateSuiteReport(results: TestResult[]): string;
|
|
16
|
+
/**
|
|
17
|
+
* Save report to file
|
|
18
|
+
*/
|
|
19
|
+
save(result: TestResult, filename?: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Save report as a folder with embedded images
|
|
22
|
+
*/
|
|
23
|
+
saveAsFolder(result: TestResult): string;
|
|
24
|
+
/**
|
|
25
|
+
* Save suite report as a folder
|
|
26
|
+
*/
|
|
27
|
+
saveSuiteAsFolder(results: TestResult[]): string;
|
|
28
|
+
/**
|
|
29
|
+
* Generate Markdown report
|
|
30
|
+
*/
|
|
31
|
+
private generateMarkdown;
|
|
32
|
+
/**
|
|
33
|
+
* Generate HTML report with Tailwind CSS
|
|
34
|
+
*/
|
|
35
|
+
private generateHTML;
|
|
36
|
+
/**
|
|
37
|
+
* Format a single step for HTML with Tailwind
|
|
38
|
+
*/
|
|
39
|
+
private formatStepHTML;
|
|
40
|
+
/**
|
|
41
|
+
* Generate suite report HTML with Tailwind
|
|
42
|
+
*/
|
|
43
|
+
private generateSuiteHTML;
|
|
44
|
+
/**
|
|
45
|
+
* Format a test item for suite report with Tailwind
|
|
46
|
+
*/
|
|
47
|
+
private formatTestItemHTML;
|
|
48
|
+
/**
|
|
49
|
+
* Format duration in human-readable form
|
|
50
|
+
*/
|
|
51
|
+
private formatDuration;
|
|
52
|
+
/**
|
|
53
|
+
* Get icon for action type
|
|
54
|
+
*/
|
|
55
|
+
private getActionIcon;
|
|
56
|
+
/**
|
|
57
|
+
* Print summary to console
|
|
58
|
+
*/
|
|
59
|
+
printSummary(result: TestResult): void;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/report/generator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAc,YAAY,EAAE,MAAM,aAAa,CAAC;AAEnE;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAe;gBAEjB,MAAM,GAAE,YAAiB;IASrC;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM;IAapC;;OAEG;IACH,mBAAmB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM;IAIlD;;OAEG;IACH,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM;IA0BnD;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM;IAgDxC;;OAEG;IACH,iBAAiB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM;IA6ChD;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAmExB;;OAEG;IACH,OAAO,CAAC,YAAY;IAyHpB;;OAEG;IACH,OAAO,CAAC,cAAc;IAiGtB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAsFzB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAkE1B;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,CAAC,aAAa;IAmBrB;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;CAgBvC"}
|