flowspec 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.
- package/LICENSE +21 -0
- package/README.md +187 -0
- package/dist/config.d.ts +54 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +102 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +181 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +41 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +173 -0
- package/dist/init.js.map +1 -0
- package/dist/parser.d.ts +16 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +56 -0
- package/dist/parser.js.map +1 -0
- package/dist/reporter.d.ts +22 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +80 -0
- package/dist/reporter.js.map +1 -0
- package/dist/runner.d.ts +24 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +418 -0
- package/dist/runner.js.map +1 -0
- package/dist/types.d.ts +512 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +78 -0
- package/dist/types.js.map +1 -0
- package/package.json +47 -0
package/dist/init.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Default content for flowspec.config.yaml
|
|
5
|
+
*/
|
|
6
|
+
export const DEFAULT_CONFIG_CONTENT = `baseUrl: http://localhost:3000
|
|
7
|
+
timeout: 10000
|
|
8
|
+
specsDir: specs/
|
|
9
|
+
`;
|
|
10
|
+
/**
|
|
11
|
+
* Default content for example flow file
|
|
12
|
+
*/
|
|
13
|
+
export const DEFAULT_EXAMPLE_FLOW_CONTENT = `name: example-flow
|
|
14
|
+
description: Example flow - customize this for your app
|
|
15
|
+
steps:
|
|
16
|
+
- visit: /
|
|
17
|
+
expect:
|
|
18
|
+
- visible: Welcome
|
|
19
|
+
`;
|
|
20
|
+
/**
|
|
21
|
+
* Default content for Claude settings to protect specs
|
|
22
|
+
*/
|
|
23
|
+
export const DEFAULT_CLAUDE_SETTINGS_CONTENT = `{
|
|
24
|
+
"hooks": {
|
|
25
|
+
"PreToolUse": [{
|
|
26
|
+
"matcher": { "tool": ["Edit", "Write"], "path": "specs/**/*.flow.yaml" },
|
|
27
|
+
"command": "echo '❌ Flow specs are immutable. Fix the implementation, not the spec.' && exit 1"
|
|
28
|
+
}]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
/**
|
|
33
|
+
* Safely create a directory if it doesn't exist
|
|
34
|
+
*/
|
|
35
|
+
function ensureDirectory(dirPath) {
|
|
36
|
+
if (!existsSync(dirPath)) {
|
|
37
|
+
mkdirSync(dirPath, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Create a file if it doesn't exist
|
|
42
|
+
* @returns Result indicating whether file was created or skipped
|
|
43
|
+
*/
|
|
44
|
+
function createFileIfNotExists(filePath, content) {
|
|
45
|
+
const result = {
|
|
46
|
+
path: filePath,
|
|
47
|
+
created: false,
|
|
48
|
+
skipped: false,
|
|
49
|
+
};
|
|
50
|
+
try {
|
|
51
|
+
if (existsSync(filePath)) {
|
|
52
|
+
result.skipped = true;
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
// Ensure parent directory exists
|
|
56
|
+
const parentDir = dirname(filePath);
|
|
57
|
+
ensureDirectory(parentDir);
|
|
58
|
+
writeFileSync(filePath, content, "utf-8");
|
|
59
|
+
result.created = true;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
result.error =
|
|
63
|
+
error instanceof Error ? error.message : "Unknown error occurred";
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Update package.json with test:e2e script if it exists
|
|
69
|
+
* @returns Result indicating whether package.json was updated
|
|
70
|
+
*/
|
|
71
|
+
function updatePackageJson(projectDir) {
|
|
72
|
+
const packageJsonPath = join(projectDir, "package.json");
|
|
73
|
+
const result = {
|
|
74
|
+
path: packageJsonPath,
|
|
75
|
+
created: false,
|
|
76
|
+
skipped: false,
|
|
77
|
+
};
|
|
78
|
+
try {
|
|
79
|
+
if (!existsSync(packageJsonPath)) {
|
|
80
|
+
result.skipped = true;
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
const content = readFileSync(packageJsonPath, "utf-8");
|
|
84
|
+
const pkg = JSON.parse(content);
|
|
85
|
+
// Ensure scripts object exists
|
|
86
|
+
if (!pkg.scripts || typeof pkg.scripts !== "object") {
|
|
87
|
+
pkg.scripts = {};
|
|
88
|
+
}
|
|
89
|
+
const scripts = pkg.scripts;
|
|
90
|
+
// Check if test:e2e already exists
|
|
91
|
+
if (scripts["test:e2e"]) {
|
|
92
|
+
result.skipped = true;
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
// Add the test:e2e script
|
|
96
|
+
scripts["test:e2e"] = "flowspec run";
|
|
97
|
+
// Write back with proper formatting
|
|
98
|
+
writeFileSync(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf-8");
|
|
99
|
+
result.created = true;
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
result.error =
|
|
103
|
+
error instanceof Error ? error.message : "Unknown error occurred";
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Initialize FlowSpec in a project directory
|
|
109
|
+
* Creates configuration files and example flow
|
|
110
|
+
*
|
|
111
|
+
* @param projectDir - Directory to initialize (defaults to cwd)
|
|
112
|
+
* @returns Result with details about each file created
|
|
113
|
+
*/
|
|
114
|
+
export function initProject(projectDir = process.cwd()) {
|
|
115
|
+
const files = [];
|
|
116
|
+
// 1. Create flowspec.config.yaml
|
|
117
|
+
const configResult = createFileIfNotExists(join(projectDir, "flowspec.config.yaml"), DEFAULT_CONFIG_CONTENT);
|
|
118
|
+
files.push(configResult);
|
|
119
|
+
// 2. Create specs/example.flow.yaml
|
|
120
|
+
const exampleFlowResult = createFileIfNotExists(join(projectDir, "specs", "example.flow.yaml"), DEFAULT_EXAMPLE_FLOW_CONTENT);
|
|
121
|
+
files.push(exampleFlowResult);
|
|
122
|
+
// 3. Create .claude/settings.local.json
|
|
123
|
+
const claudeSettingsResult = createFileIfNotExists(join(projectDir, ".claude", "settings.local.json"), DEFAULT_CLAUDE_SETTINGS_CONTENT);
|
|
124
|
+
files.push(claudeSettingsResult);
|
|
125
|
+
// 4. Update package.json if it exists
|
|
126
|
+
const packageJsonResult = updatePackageJson(projectDir);
|
|
127
|
+
files.push(packageJsonResult);
|
|
128
|
+
// Determine overall success
|
|
129
|
+
const success = files.every((f) => !f.error);
|
|
130
|
+
return { files, success };
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Format the init result for console output
|
|
134
|
+
*/
|
|
135
|
+
export function formatInitResult(result) {
|
|
136
|
+
const lines = [];
|
|
137
|
+
for (const file of result.files) {
|
|
138
|
+
const relativePath = file.path.includes("/")
|
|
139
|
+
? file.path.split("/").slice(-2).join("/")
|
|
140
|
+
: file.path;
|
|
141
|
+
// Get just the filename or last path component
|
|
142
|
+
const displayPath = file.path.endsWith("package.json")
|
|
143
|
+
? "Added test:e2e script to package.json"
|
|
144
|
+
: relativePath;
|
|
145
|
+
if (file.error) {
|
|
146
|
+
lines.push(`✗ Failed to create ${displayPath}: ${file.error}`);
|
|
147
|
+
}
|
|
148
|
+
else if (file.skipped) {
|
|
149
|
+
if (file.path.endsWith("package.json")) {
|
|
150
|
+
// Don't show anything for skipped package.json (either doesn't exist or already has script)
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
lines.push(`· Skipped ${displayPath} (already exists)`);
|
|
154
|
+
}
|
|
155
|
+
else if (file.created) {
|
|
156
|
+
if (file.path.endsWith("package.json")) {
|
|
157
|
+
lines.push(`✓ ${displayPath}`);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
lines.push(`✓ Created ${displayPath}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (result.success) {
|
|
165
|
+
lines.push("");
|
|
166
|
+
lines.push("FlowSpec initialized! Next steps:");
|
|
167
|
+
lines.push(" 1. Edit specs/example.flow.yaml for your app");
|
|
168
|
+
lines.push(" 2. Start your app on http://localhost:3000");
|
|
169
|
+
lines.push(" 3. Run: flowspec run");
|
|
170
|
+
}
|
|
171
|
+
return lines.join("\n");
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=init.js.map
|
package/dist/init.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
|
package/dist/parser.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type FlowSpec } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Parse a YAML string into a FlowSpec object
|
|
4
|
+
* @param yamlContent - The YAML string to parse
|
|
5
|
+
* @returns The parsed FlowSpec
|
|
6
|
+
* @throws Error if YAML is invalid or doesn't match FlowSpec schema
|
|
7
|
+
*/
|
|
8
|
+
export declare function parseFlowSpec(yamlContent: string): FlowSpec;
|
|
9
|
+
/**
|
|
10
|
+
* Read and parse a YAML flow file
|
|
11
|
+
* @param filePath - Path to the YAML file
|
|
12
|
+
* @returns The parsed FlowSpec
|
|
13
|
+
* @throws Error if file not found, YAML is invalid, or doesn't match FlowSpec schema
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseFlowFile(filePath: string): FlowSpec;
|
|
16
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,QAAQ,EAAkB,MAAM,SAAS,CAAC;AAaxD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,QAAQ,CAqB3D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAaxD"}
|
package/dist/parser.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import yaml from "js-yaml";
|
|
3
|
+
import { FlowSpecSchema } from "./types";
|
|
4
|
+
/**
|
|
5
|
+
* Format a Zod validation error into a human-readable message
|
|
6
|
+
*/
|
|
7
|
+
function formatZodError(error) {
|
|
8
|
+
const issues = error.issues.map((issue) => {
|
|
9
|
+
const path = issue.path.length > 0 ? issue.path.join(".") : "root";
|
|
10
|
+
return `${path}: ${issue.message}`;
|
|
11
|
+
});
|
|
12
|
+
return `Schema validation failed: ${issues.join("; ")}`;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parse a YAML string into a FlowSpec object
|
|
16
|
+
* @param yamlContent - The YAML string to parse
|
|
17
|
+
* @returns The parsed FlowSpec
|
|
18
|
+
* @throws Error if YAML is invalid or doesn't match FlowSpec schema
|
|
19
|
+
*/
|
|
20
|
+
export function parseFlowSpec(yamlContent) {
|
|
21
|
+
let parsed;
|
|
22
|
+
try {
|
|
23
|
+
parsed = yaml.load(yamlContent);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (error instanceof yaml.YAMLException) {
|
|
27
|
+
throw new Error(`Invalid YAML syntax at line ${error.mark?.line ?? "unknown"}, column ${error.mark?.column ?? "unknown"}: ${error.reason}`);
|
|
28
|
+
}
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
const result = FlowSpecSchema.safeParse(parsed);
|
|
32
|
+
if (!result.success) {
|
|
33
|
+
throw new Error(formatZodError(result.error));
|
|
34
|
+
}
|
|
35
|
+
return result.data;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Read and parse a YAML flow file
|
|
39
|
+
* @param filePath - Path to the YAML file
|
|
40
|
+
* @returns The parsed FlowSpec
|
|
41
|
+
* @throws Error if file not found, YAML is invalid, or doesn't match FlowSpec schema
|
|
42
|
+
*/
|
|
43
|
+
export function parseFlowFile(filePath) {
|
|
44
|
+
let content;
|
|
45
|
+
try {
|
|
46
|
+
content = readFileSync(filePath, "utf-8");
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
50
|
+
throw new Error(`File not found: ${filePath}`);
|
|
51
|
+
}
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
return parseFlowSpec(content);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,IAAI,MAAM,SAAS,CAAC;AAE3B,OAAO,EAAiB,cAAc,EAAE,MAAM,SAAS,CAAC;AAExD;;GAEG;AACH,SAAS,cAAc,CAAC,KAAe;IACrC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACxC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QACnE,OAAO,GAAG,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;IACH,OAAO,6BAA6B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,WAAmB;IAC/C,IAAI,MAAe,CAAC;IAEpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,IAAI,CAAC,aAAa,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CACb,+BAA+B,KAAK,CAAC,IAAI,EAAE,IAAI,IAAI,SAAS,YAAY,KAAK,CAAC,IAAI,EAAE,MAAM,IAAI,SAAS,KAAK,KAAK,CAAC,MAAM,EAAE,CAC3H,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAEhD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAChD,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,IAAI,OAAe,CAAC;IAEpB,IAAI,CAAC;QACH,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACzE,MAAM,IAAI,KAAK,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IAED,OAAO,aAAa,CAAC,OAAO,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { FlowError, FlowResult } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Format a FlowError into a human-readable string
|
|
4
|
+
* @param error - The FlowError to format
|
|
5
|
+
* @returns Formatted error message
|
|
6
|
+
*/
|
|
7
|
+
export declare function formatError(error: FlowError): string;
|
|
8
|
+
/**
|
|
9
|
+
* Format a single FlowResult for terminal output
|
|
10
|
+
* Shows checkmark and duration for success, X with error details for failure
|
|
11
|
+
* @param result - The FlowResult to format
|
|
12
|
+
* @returns Formatted result string with ANSI colors
|
|
13
|
+
*/
|
|
14
|
+
export declare function formatResult(result: FlowResult): string;
|
|
15
|
+
/**
|
|
16
|
+
* Format multiple FlowResults into a summary line
|
|
17
|
+
* Shows total, passed, and failed counts
|
|
18
|
+
* @param results - Array of FlowResults to summarize
|
|
19
|
+
* @returns Formatted summary string
|
|
20
|
+
*/
|
|
21
|
+
export declare function formatSummary(results: FlowResult[]): string;
|
|
22
|
+
//# sourceMappingURL=reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAc,MAAM,SAAS,CAAC;AAqCjE;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAUpD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAoBvD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAQ3D"}
|
package/dist/reporter.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// ANSI color codes
|
|
2
|
+
const GREEN = "\x1b[32m";
|
|
3
|
+
const RED = "\x1b[31m";
|
|
4
|
+
const RESET = "\x1b[0m";
|
|
5
|
+
/**
|
|
6
|
+
* Format duration for display
|
|
7
|
+
* Uses seconds for durations >= 1000ms, otherwise milliseconds
|
|
8
|
+
*/
|
|
9
|
+
function formatDuration(ms) {
|
|
10
|
+
if (ms >= 1000) {
|
|
11
|
+
return `${ms / 1000}s`;
|
|
12
|
+
}
|
|
13
|
+
return `${ms}ms`;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Format a StepAction into a human-readable string
|
|
17
|
+
*/
|
|
18
|
+
function formatAction(action) {
|
|
19
|
+
if ("visit" in action) {
|
|
20
|
+
return `visit "${action.visit}"`;
|
|
21
|
+
}
|
|
22
|
+
if ("click" in action) {
|
|
23
|
+
return `click "${action.click}"`;
|
|
24
|
+
}
|
|
25
|
+
if ("fill" in action) {
|
|
26
|
+
return "fill";
|
|
27
|
+
}
|
|
28
|
+
if ("select" in action) {
|
|
29
|
+
return "select";
|
|
30
|
+
}
|
|
31
|
+
return "unknown action";
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Format a FlowError into a human-readable string
|
|
35
|
+
* @param error - The FlowError to format
|
|
36
|
+
* @returns Formatted error message
|
|
37
|
+
*/
|
|
38
|
+
export function formatError(error) {
|
|
39
|
+
const parts = [];
|
|
40
|
+
if (error.step !== undefined && error.action) {
|
|
41
|
+
parts.push(`Step ${error.step}: ${formatAction(error.action)}`);
|
|
42
|
+
}
|
|
43
|
+
parts.push(`Error: ${error.message}`);
|
|
44
|
+
return parts.join("\n ");
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Format a single FlowResult for terminal output
|
|
48
|
+
* Shows checkmark and duration for success, X with error details for failure
|
|
49
|
+
* @param result - The FlowResult to format
|
|
50
|
+
* @returns Formatted result string with ANSI colors
|
|
51
|
+
*/
|
|
52
|
+
export function formatResult(result) {
|
|
53
|
+
const duration = formatDuration(result.duration);
|
|
54
|
+
if (result.success) {
|
|
55
|
+
return `${GREEN}✓ ${result.flowName} (${duration})${RESET}`;
|
|
56
|
+
}
|
|
57
|
+
const lines = [];
|
|
58
|
+
lines.push(`${RED}✗ ${result.flowName} (${duration})${RESET}`);
|
|
59
|
+
if (result.error) {
|
|
60
|
+
if (result.error.step !== undefined && result.error.action) {
|
|
61
|
+
lines.push(` Step ${result.error.step}: ${formatAction(result.error.action)}`);
|
|
62
|
+
}
|
|
63
|
+
lines.push(` Error: ${result.error.message}`);
|
|
64
|
+
}
|
|
65
|
+
return lines.join("\n");
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Format multiple FlowResults into a summary line
|
|
69
|
+
* Shows total, passed, and failed counts
|
|
70
|
+
* @param results - Array of FlowResults to summarize
|
|
71
|
+
* @returns Formatted summary string
|
|
72
|
+
*/
|
|
73
|
+
export function formatSummary(results) {
|
|
74
|
+
const total = results.length;
|
|
75
|
+
const passed = results.filter((r) => r.success).length;
|
|
76
|
+
const failed = total - passed;
|
|
77
|
+
const flowWord = total === 1 ? "flow" : "flows";
|
|
78
|
+
return `${total} ${flowWord}: ${passed} passed, ${failed} failed`;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"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"}
|
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { FlowResult, FlowSpec } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Options for flow execution
|
|
4
|
+
*/
|
|
5
|
+
export interface RunnerOptions {
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
timeout?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Default timeout for assertion retries (in milliseconds)
|
|
11
|
+
*/
|
|
12
|
+
export declare const DEFAULT_TIMEOUT = 5000;
|
|
13
|
+
/**
|
|
14
|
+
* Interval between retry attempts (in milliseconds)
|
|
15
|
+
*/
|
|
16
|
+
export declare const POLL_INTERVAL = 250;
|
|
17
|
+
/**
|
|
18
|
+
* Execute a flow specification and return the result
|
|
19
|
+
* @param flow - The FlowSpec to execute
|
|
20
|
+
* @param options - Optional runner configuration
|
|
21
|
+
* @returns Promise resolving to the FlowResult
|
|
22
|
+
*/
|
|
23
|
+
export declare function runFlow(flow: FlowSpec, options?: RunnerOptions): Promise<FlowResult>;
|
|
24
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,UAAU,EACV,QAAQ,EAGT,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC;;GAEG;AACH,eAAO,MAAM,aAAa,MAAM,CAAC;AA4bjC;;;;;GAKG;AACH,wBAAsB,OAAO,CAC3B,IAAI,EAAE,QAAQ,EACd,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,UAAU,CAAC,CAuDrB"}
|