@ripplo/testing 0.0.7 → 0.0.8
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/{chunk-GTHRSFXF.js → chunk-AQ52MYXE.js} +2 -2
- package/dist/compiler.js +1 -1
- package/dist/index.js +1 -1
- package/dist/lockfile.d.ts +5 -10
- package/dist/lockfile.js +327 -182
- package/package.json +2 -2
|
@@ -83,7 +83,7 @@ function compileSteps(steps, accessedKeys, requiresKeys) {
|
|
|
83
83
|
variables[key] = { default: `test-${key}`, type: "string" };
|
|
84
84
|
});
|
|
85
85
|
const variableNamespaces = { ...requiresKeys };
|
|
86
|
-
return { entryNode: "step-0", nodes, variableNamespaces, variables
|
|
86
|
+
return { entryNode: "step-0", nodes, variableNamespaces, variables };
|
|
87
87
|
}
|
|
88
88
|
function compileNode(step, id, next) {
|
|
89
89
|
const { label, node: raw } = readStep(step);
|
|
@@ -116,7 +116,7 @@ function compileGraph(preconditionDefs, testDefs) {
|
|
|
116
116
|
workflow: def.id
|
|
117
117
|
});
|
|
118
118
|
});
|
|
119
|
-
return { edges, preconditions, states
|
|
119
|
+
return { edges, preconditions, states };
|
|
120
120
|
}
|
|
121
121
|
function resolveDependencyChain(requires, preconditionDefs) {
|
|
122
122
|
const defsByName = new Map(preconditionDefs.map((d) => [d.name, d]));
|
package/dist/compiler.js
CHANGED
package/dist/index.js
CHANGED
package/dist/lockfile.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
+
import { Codec } from '@ripplo/spec';
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
import { CompileResult } from './compiler.js';
|
|
3
|
-
import '@ripplo/spec';
|
|
4
4
|
import './builder-DTWMrbuv.js';
|
|
5
5
|
import './step-DLfkKI3V.js';
|
|
6
6
|
|
|
7
|
-
declare const LOCKFILE_VERSION = 1;
|
|
8
7
|
declare const LOCKFILE_RELATIVE_PATH = ".ripplo/ripplo.lock";
|
|
9
|
-
declare const
|
|
8
|
+
declare const lockfileBodySchema: z.ZodObject<{
|
|
10
9
|
graph: z.ZodObject<{
|
|
11
10
|
edges: z.ZodArray<z.ZodObject<{
|
|
12
11
|
from: z.ZodString;
|
|
@@ -23,11 +22,8 @@ declare const lockfileSchema: z.ZodObject<{
|
|
|
23
22
|
preconditions: z.ZodArray<z.ZodString>;
|
|
24
23
|
route: z.ZodString;
|
|
25
24
|
}, z.core.$strip>>;
|
|
26
|
-
version: z.ZodLiteral<3>;
|
|
27
25
|
}, z.core.$strip>;
|
|
28
|
-
lockfileVersion: z.ZodNumber;
|
|
29
26
|
tests: z.ZodArray<z.ZodObject<{
|
|
30
|
-
additionalChecks: z.ZodArray<z.ZodString>;
|
|
31
27
|
description: z.ZodString;
|
|
32
28
|
expectedOutcome: z.ZodString;
|
|
33
29
|
implemented: z.ZodBoolean;
|
|
@@ -696,12 +692,11 @@ declare const lockfileSchema: z.ZodObject<{
|
|
|
696
692
|
key: z.ZodString;
|
|
697
693
|
type: z.ZodLiteral<"env">;
|
|
698
694
|
}, z.core.$strip>], "type">>>;
|
|
699
|
-
version: z.ZodLiteral<2>;
|
|
700
695
|
}, z.core.$strip>;
|
|
701
|
-
warnings: z.ZodArray<z.ZodString>;
|
|
702
696
|
}, z.core.$strip>>;
|
|
703
697
|
}, z.core.$strip>;
|
|
704
|
-
type Lockfile = z.infer<typeof
|
|
698
|
+
type Lockfile = z.infer<typeof lockfileBodySchema>;
|
|
699
|
+
declare const lockfileCodec: Codec<Lockfile>;
|
|
705
700
|
declare function compileResultToLockfile(result: CompileResult): Lockfile;
|
|
706
701
|
declare function serializeLockfile(lockfile: Lockfile): string;
|
|
707
702
|
interface ReadLockfileParams {
|
|
@@ -720,4 +715,4 @@ interface CompareLockfileParams {
|
|
|
720
715
|
}
|
|
721
716
|
declare function compareCompileToLockfile({ compiled, existing, }: CompareLockfileParams): LockfileComparison;
|
|
722
717
|
|
|
723
|
-
export { type CompareLockfileParams, LOCKFILE_RELATIVE_PATH,
|
|
718
|
+
export { type CompareLockfileParams, LOCKFILE_RELATIVE_PATH, type Lockfile, type LockfileComparison, type ReadLockfileParams, type WriteLockfileParams, compareCompileToLockfile, compileResultToLockfile, lockfileCodec, readLockfile, serializeLockfile, writeLockfile };
|
package/dist/lockfile.js
CHANGED
|
@@ -2,39 +2,163 @@
|
|
|
2
2
|
import fs from "fs/promises";
|
|
3
3
|
import path from "path";
|
|
4
4
|
|
|
5
|
-
// ../spec/src/
|
|
5
|
+
// ../spec/src/codec.ts
|
|
6
6
|
import { z } from "zod";
|
|
7
|
-
var
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
var envelopeSchema = z.object({
|
|
8
|
+
__codec: z.string().min(1),
|
|
9
|
+
data: z.unknown(),
|
|
10
|
+
version: z.number().int().positive()
|
|
11
|
+
});
|
|
12
|
+
var CodecVersionError = class extends Error {
|
|
13
|
+
codec;
|
|
14
|
+
currentVersion;
|
|
15
|
+
gotVersion;
|
|
16
|
+
constructor(params) {
|
|
17
|
+
super(
|
|
18
|
+
`Unsupported ${params.codec} version ${String(params.gotVersion)} (current ${String(params.currentVersion)}). Upgrade Ripplo or rebuild with a compatible CLI.`
|
|
19
|
+
);
|
|
20
|
+
this.name = "CodecVersionError";
|
|
21
|
+
this.codec = params.codec;
|
|
22
|
+
this.currentVersion = params.currentVersion;
|
|
23
|
+
this.gotVersion = params.gotVersion;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var CodecMismatchError = class extends Error {
|
|
27
|
+
constructor(params) {
|
|
28
|
+
super(`Codec mismatch: expected "${params.expected}", got "${params.got}"`);
|
|
29
|
+
this.name = "CodecMismatchError";
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
function defineCodec(name) {
|
|
33
|
+
return makeInitial({ legacy: void 0, migrators: [], name, schemas: [] });
|
|
34
|
+
}
|
|
35
|
+
function decodeJson(codec, raw) {
|
|
36
|
+
const parsed = JSON.parse(raw);
|
|
37
|
+
return codec.decode(parsed);
|
|
38
|
+
}
|
|
39
|
+
function makeInitial(state) {
|
|
40
|
+
return {
|
|
41
|
+
initial: (schema) => makeBuilder(schema, { ...state, schemas: [schema] }),
|
|
42
|
+
legacy: (adapter) => makeInitial({ ...state, legacy: adapter })
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function makeBuilder(latestSchema, state) {
|
|
46
|
+
return {
|
|
47
|
+
build: () => buildCodec(latestSchema, state),
|
|
48
|
+
legacy: (adapter) => makeBuilder(latestSchema, { ...state, legacy: adapter }),
|
|
49
|
+
upgrade: (schema, up) => {
|
|
50
|
+
const erased = up;
|
|
51
|
+
return makeBuilder(schema, {
|
|
52
|
+
...state,
|
|
53
|
+
migrators: [...state.migrators, erased],
|
|
54
|
+
schemas: [...state.schemas, schema]
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function buildCodec(latestSchema, state) {
|
|
60
|
+
const currentVersion = state.schemas.length;
|
|
61
|
+
return {
|
|
62
|
+
currentVersion,
|
|
63
|
+
name: state.name,
|
|
64
|
+
decode: (raw) => decodeWith(latestSchema, state, raw),
|
|
65
|
+
encode: (value) => ({
|
|
66
|
+
__codec: state.name,
|
|
67
|
+
data: value,
|
|
68
|
+
version: currentVersion
|
|
69
|
+
})
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function decodeWith(latestSchema, state, raw) {
|
|
73
|
+
const { data, version } = unwrapEnvelope(state, raw);
|
|
74
|
+
const migrated = migrateStep(state, data, version);
|
|
75
|
+
return latestSchema.parse(migrated);
|
|
76
|
+
}
|
|
77
|
+
function unwrapEnvelope(state, raw) {
|
|
78
|
+
const envelopeResult = envelopeSchema.safeParse(raw);
|
|
79
|
+
if (envelopeResult.success) {
|
|
80
|
+
if (envelopeResult.data.__codec !== state.name) {
|
|
81
|
+
throw new CodecMismatchError({
|
|
82
|
+
expected: state.name,
|
|
83
|
+
got: envelopeResult.data.__codec
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
data: envelopeResult.data.data,
|
|
88
|
+
version: envelopeResult.data.version
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (state.legacy != null && state.legacy.detect(raw)) {
|
|
92
|
+
return { data: raw, version: state.legacy.assumedVersion };
|
|
93
|
+
}
|
|
94
|
+
throw new Error(
|
|
95
|
+
`Cannot decode "${state.name}": value is not a codec envelope and no legacy detector matched.`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
function migrateStep(state, data, version) {
|
|
99
|
+
const currentVersion = state.schemas.length;
|
|
100
|
+
if (version > currentVersion) {
|
|
101
|
+
throw new CodecVersionError({
|
|
102
|
+
codec: state.name,
|
|
103
|
+
currentVersion,
|
|
104
|
+
gotVersion: version
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
if (version === currentVersion) {
|
|
108
|
+
return data;
|
|
109
|
+
}
|
|
110
|
+
const sourceSchema = state.schemas[version - 1];
|
|
111
|
+
if (sourceSchema == null) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`Codec "${state.name}" missing schema for v${String(version)}; cannot migrate.`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
const validated = sourceSchema.parse(data);
|
|
117
|
+
const migrator = state.migrators[version - 1];
|
|
118
|
+
if (migrator == null) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Codec "${state.name}" missing migrator v${String(version)} \u2192 v${String(version + 1)}.`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
return migrateStep(state, migrator(validated), version + 1);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ../spec/src/codecs.ts
|
|
127
|
+
import { z as z11 } from "zod";
|
|
128
|
+
|
|
129
|
+
// ../spec/src/graph.ts
|
|
130
|
+
import { z as z5 } from "zod";
|
|
131
|
+
|
|
132
|
+
// ../spec/src/edge.ts
|
|
133
|
+
import { z as z2 } from "zod";
|
|
134
|
+
var edgeSchema = z2.object({
|
|
135
|
+
from: z2.string().min(1).describe("Key of the source state in the states record"),
|
|
136
|
+
requiresKeys: z2.record(z2.string(), z2.string()).optional().describe(
|
|
10
137
|
"Maps workflow variable namespace to precondition name. Used by the runtime to namespace batch precondition data into dot-namespaced variables (e.g. { project: 'data:project' } maps projectId \u2192 project.projectId)."
|
|
11
138
|
),
|
|
12
|
-
to:
|
|
13
|
-
workflow:
|
|
139
|
+
to: z2.string().min(1).describe("Key of the target state in the states record"),
|
|
140
|
+
workflow: z2.string().min(1).describe(
|
|
14
141
|
"Filename (without .json) of the workflow in .ripplo/workflows/ that executes this edge"
|
|
15
142
|
)
|
|
16
143
|
}).describe("A directed edge between two states, executed by a workflow");
|
|
17
144
|
|
|
18
|
-
// ../spec/src/graph.ts
|
|
19
|
-
import { z as z4 } from "zod";
|
|
20
|
-
|
|
21
145
|
// ../spec/src/precondition.ts
|
|
22
|
-
import { z as
|
|
23
|
-
var preconditionSchema =
|
|
24
|
-
depends:
|
|
146
|
+
import { z as z3 } from "zod";
|
|
147
|
+
var preconditionSchema = z3.object({
|
|
148
|
+
depends: z3.array(z3.string().min(1)).optional().describe(
|
|
25
149
|
"Names of other preconditions that must be satisfied first. Resolved via topological sort; cycles are rejected at validation time."
|
|
26
150
|
),
|
|
27
|
-
description:
|
|
28
|
-
returns:
|
|
151
|
+
description: z3.string().min(1).describe("Human-readable description of what this precondition ensures"),
|
|
152
|
+
returns: z3.array(z3.string().min(1)).optional().describe(
|
|
29
153
|
"Keys that the execute response's data field will contain. e.g. ['projectId', 'workflowId']. These are used for route param interpolation ({{projectId}}) and workflow variables. Declared here so generated types are strongly typed per precondition."
|
|
30
154
|
)
|
|
31
155
|
}).describe("A named precondition declared at the graph level. States reference these by name.");
|
|
32
156
|
|
|
33
157
|
// ../spec/src/state.ts
|
|
34
|
-
import { z as
|
|
35
|
-
var stateNodeSchema =
|
|
36
|
-
preconditions:
|
|
37
|
-
route:
|
|
158
|
+
import { z as z4 } from "zod";
|
|
159
|
+
var stateNodeSchema = z4.object({
|
|
160
|
+
preconditions: z4.array(z4.string().min(1)).describe("Ordered list of precondition names to satisfy before entering this state"),
|
|
161
|
+
route: z4.string().min(1).describe(
|
|
38
162
|
"URL pattern with {{placeholders}} for dynamic segments, e.g. '/projects/{{projectId}}/settings'"
|
|
39
163
|
)
|
|
40
164
|
}).describe(
|
|
@@ -42,36 +166,42 @@ var stateNodeSchema = z3.object({
|
|
|
42
166
|
);
|
|
43
167
|
|
|
44
168
|
// ../spec/src/graph.ts
|
|
45
|
-
var
|
|
46
|
-
|
|
47
|
-
|
|
169
|
+
var MAX_STATES = 1e3;
|
|
170
|
+
var MAX_EDGES = 5e3;
|
|
171
|
+
var MAX_PRECONDITIONS = 500;
|
|
172
|
+
var stateGraphSchema = z5.object({
|
|
173
|
+
edges: z5.array(edgeSchema).max(MAX_EDGES).describe("Directed edges between states, each executed by a workflow"),
|
|
174
|
+
preconditions: z5.record(z5.string().max(200), preconditionSchema).refine(
|
|
175
|
+
(obj) => Object.keys(obj).length <= MAX_PRECONDITIONS,
|
|
176
|
+
`Graph has more than ${String(MAX_PRECONDITIONS)} preconditions`
|
|
177
|
+
).describe(
|
|
48
178
|
"Named preconditions keyed by name (e.g. 'auth:admin', 'data:three-projects'). States reference these by name."
|
|
49
179
|
),
|
|
50
|
-
states:
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
"
|
|
54
|
-
);
|
|
180
|
+
states: z5.record(z5.string().max(200), stateNodeSchema).refine(
|
|
181
|
+
(obj) => Object.keys(obj).length <= MAX_STATES,
|
|
182
|
+
`Graph has more than ${String(MAX_STATES)} states`
|
|
183
|
+
).describe("States keyed by stable ID (kebab-case)")
|
|
184
|
+
}).describe("Ripplo State Graph \u2014 models application states, edges, and executable preconditions");
|
|
55
185
|
|
|
56
186
|
// ../spec/src/schema.ts
|
|
57
|
-
import { z as
|
|
187
|
+
import { z as z10 } from "zod";
|
|
58
188
|
|
|
59
189
|
// ../spec/src/locators.ts
|
|
60
|
-
import { z as
|
|
61
|
-
var testIdLocator =
|
|
62
|
-
by:
|
|
63
|
-
value:
|
|
190
|
+
import { z as z6 } from "zod";
|
|
191
|
+
var testIdLocator = z6.object({
|
|
192
|
+
by: z6.literal("testId"),
|
|
193
|
+
value: z6.string().min(1)
|
|
64
194
|
});
|
|
65
|
-
var roleLocator =
|
|
66
|
-
by:
|
|
67
|
-
name:
|
|
68
|
-
role:
|
|
195
|
+
var roleLocator = z6.object({
|
|
196
|
+
by: z6.literal("role"),
|
|
197
|
+
name: z6.string().optional(),
|
|
198
|
+
role: z6.string().min(1)
|
|
69
199
|
});
|
|
70
|
-
var locatorSchema =
|
|
200
|
+
var locatorSchema = z6.discriminatedUnion("by", [testIdLocator, roleLocator]);
|
|
71
201
|
|
|
72
202
|
// ../spec/src/operators.ts
|
|
73
|
-
import { z as
|
|
74
|
-
var comparisonOperator =
|
|
203
|
+
import { z as z7 } from "zod";
|
|
204
|
+
var comparisonOperator = z7.enum([
|
|
75
205
|
"equals",
|
|
76
206
|
"notEquals",
|
|
77
207
|
"contains",
|
|
@@ -79,7 +209,7 @@ var comparisonOperator = z6.enum([
|
|
|
79
209
|
"endsWith",
|
|
80
210
|
"matches"
|
|
81
211
|
]);
|
|
82
|
-
var numericOperator =
|
|
212
|
+
var numericOperator = z7.enum([
|
|
83
213
|
"equals",
|
|
84
214
|
"notEquals",
|
|
85
215
|
"greaterThan",
|
|
@@ -89,240 +219,241 @@ var numericOperator = z6.enum([
|
|
|
89
219
|
]);
|
|
90
220
|
|
|
91
221
|
// ../spec/src/value-ref.ts
|
|
92
|
-
import { z as
|
|
93
|
-
var staticValueSchema =
|
|
94
|
-
type:
|
|
95
|
-
value:
|
|
222
|
+
import { z as z8 } from "zod";
|
|
223
|
+
var staticValueSchema = z8.object({
|
|
224
|
+
type: z8.literal("static"),
|
|
225
|
+
value: z8.union([z8.string(), z8.number(), z8.boolean()])
|
|
96
226
|
});
|
|
97
|
-
var variableRefSchema =
|
|
98
|
-
name:
|
|
99
|
-
type:
|
|
227
|
+
var variableRefSchema = z8.object({
|
|
228
|
+
name: z8.string().min(1),
|
|
229
|
+
type: z8.literal("variable")
|
|
100
230
|
});
|
|
101
|
-
var valueRefSchema =
|
|
102
|
-
var stringValueRefSchema =
|
|
103
|
-
|
|
231
|
+
var valueRefSchema = z8.discriminatedUnion("type", [staticValueSchema, variableRefSchema]);
|
|
232
|
+
var stringValueRefSchema = z8.discriminatedUnion("type", [
|
|
233
|
+
z8.object({ type: z8.literal("static"), value: z8.string() }),
|
|
104
234
|
variableRefSchema
|
|
105
235
|
]);
|
|
106
|
-
var numericValueRefSchema =
|
|
107
|
-
|
|
236
|
+
var numericValueRefSchema = z8.discriminatedUnion("type", [
|
|
237
|
+
z8.object({ type: z8.literal("static"), value: z8.number().int().nonnegative() }),
|
|
108
238
|
variableRefSchema
|
|
109
239
|
]);
|
|
110
240
|
|
|
111
241
|
// ../spec/src/variables.ts
|
|
112
|
-
import { z as
|
|
113
|
-
var variableDefSchema =
|
|
114
|
-
|
|
115
|
-
default:
|
|
116
|
-
type:
|
|
242
|
+
import { z as z9 } from "zod";
|
|
243
|
+
var variableDefSchema = z9.discriminatedUnion("type", [
|
|
244
|
+
z9.object({
|
|
245
|
+
default: z9.string().optional(),
|
|
246
|
+
type: z9.literal("string")
|
|
117
247
|
}),
|
|
118
|
-
|
|
119
|
-
default:
|
|
120
|
-
type:
|
|
248
|
+
z9.object({
|
|
249
|
+
default: z9.number().optional(),
|
|
250
|
+
type: z9.literal("number")
|
|
121
251
|
}),
|
|
122
|
-
|
|
123
|
-
default:
|
|
124
|
-
type:
|
|
252
|
+
z9.object({
|
|
253
|
+
default: z9.boolean().optional(),
|
|
254
|
+
type: z9.literal("boolean")
|
|
125
255
|
}),
|
|
126
|
-
|
|
127
|
-
key:
|
|
128
|
-
type:
|
|
256
|
+
z9.object({
|
|
257
|
+
key: z9.string().min(1),
|
|
258
|
+
type: z9.literal("env")
|
|
129
259
|
})
|
|
130
260
|
]);
|
|
131
261
|
|
|
132
262
|
// ../spec/src/schema.ts
|
|
133
263
|
var nodeBase = {
|
|
134
|
-
comment:
|
|
135
|
-
id:
|
|
136
|
-
label:
|
|
137
|
-
next:
|
|
138
|
-
timeout:
|
|
264
|
+
comment: z10.string().max(2e3).optional(),
|
|
265
|
+
id: z10.string().min(1).max(200),
|
|
266
|
+
label: z10.string().max(500).optional(),
|
|
267
|
+
next: z10.string().max(200).optional(),
|
|
268
|
+
timeout: z10.number().int().positive().optional()
|
|
139
269
|
};
|
|
140
|
-
var
|
|
270
|
+
var MAX_NODES_PER_WORKFLOW = 500;
|
|
271
|
+
var gotoNode = z10.object({
|
|
141
272
|
...nodeBase,
|
|
142
|
-
type:
|
|
273
|
+
type: z10.literal("goto"),
|
|
143
274
|
url: stringValueRefSchema
|
|
144
275
|
});
|
|
145
|
-
var clickNode =
|
|
146
|
-
var fillNode =
|
|
276
|
+
var clickNode = z10.object({ ...nodeBase, locator: locatorSchema, type: z10.literal("click") });
|
|
277
|
+
var fillNode = z10.object({
|
|
147
278
|
...nodeBase,
|
|
148
279
|
locator: locatorSchema,
|
|
149
|
-
type:
|
|
280
|
+
type: z10.literal("fill"),
|
|
150
281
|
value: stringValueRefSchema
|
|
151
282
|
});
|
|
152
|
-
var selectNode =
|
|
283
|
+
var selectNode = z10.object({
|
|
153
284
|
...nodeBase,
|
|
154
285
|
locator: locatorSchema,
|
|
155
|
-
type:
|
|
286
|
+
type: z10.literal("select"),
|
|
156
287
|
value: stringValueRefSchema
|
|
157
288
|
});
|
|
158
|
-
var hoverNode =
|
|
159
|
-
var pressNode =
|
|
289
|
+
var hoverNode = z10.object({ ...nodeBase, locator: locatorSchema, type: z10.literal("hover") });
|
|
290
|
+
var pressNode = z10.object({
|
|
160
291
|
...nodeBase,
|
|
161
|
-
key:
|
|
292
|
+
key: z10.string().min(1),
|
|
162
293
|
locator: locatorSchema.optional(),
|
|
163
|
-
type:
|
|
294
|
+
type: z10.literal("press")
|
|
164
295
|
});
|
|
165
|
-
var checkNode =
|
|
166
|
-
var uncheckNode =
|
|
167
|
-
var setViewportNode =
|
|
296
|
+
var checkNode = z10.object({ ...nodeBase, locator: locatorSchema, type: z10.literal("check") });
|
|
297
|
+
var uncheckNode = z10.object({ ...nodeBase, locator: locatorSchema, type: z10.literal("uncheck") });
|
|
298
|
+
var setViewportNode = z10.object({
|
|
168
299
|
...nodeBase,
|
|
169
|
-
height:
|
|
170
|
-
type:
|
|
171
|
-
width:
|
|
300
|
+
height: z10.number().int().positive(),
|
|
301
|
+
type: z10.literal("setViewport"),
|
|
302
|
+
width: z10.number().int().positive()
|
|
172
303
|
});
|
|
173
|
-
var failNode =
|
|
174
|
-
var setVariableNode =
|
|
304
|
+
var failNode = z10.object({ ...nodeBase, message: z10.string().min(1), type: z10.literal("fail") });
|
|
305
|
+
var setVariableNode = z10.object({
|
|
175
306
|
...nodeBase,
|
|
176
|
-
type:
|
|
307
|
+
type: z10.literal("setVariable"),
|
|
177
308
|
value: valueRefSchema,
|
|
178
|
-
variable:
|
|
309
|
+
variable: z10.string().min(1)
|
|
179
310
|
});
|
|
180
|
-
var extractTextNode =
|
|
311
|
+
var extractTextNode = z10.object({
|
|
181
312
|
...nodeBase,
|
|
182
313
|
locator: locatorSchema,
|
|
183
|
-
type:
|
|
184
|
-
variable:
|
|
314
|
+
type: z10.literal("extractText"),
|
|
315
|
+
variable: z10.string().min(1)
|
|
185
316
|
});
|
|
186
|
-
var uploadNode =
|
|
317
|
+
var uploadNode = z10.object({
|
|
187
318
|
...nodeBase,
|
|
188
|
-
files:
|
|
319
|
+
files: z10.array(z10.string()).min(1),
|
|
189
320
|
locator: locatorSchema,
|
|
190
|
-
type:
|
|
321
|
+
type: z10.literal("upload")
|
|
191
322
|
});
|
|
192
|
-
var dblclickNode =
|
|
323
|
+
var dblclickNode = z10.object({
|
|
193
324
|
...nodeBase,
|
|
194
325
|
locator: locatorSchema,
|
|
195
|
-
type:
|
|
326
|
+
type: z10.literal("dblclick")
|
|
196
327
|
});
|
|
197
|
-
var dragNode =
|
|
328
|
+
var dragNode = z10.object({
|
|
198
329
|
...nodeBase,
|
|
199
330
|
source: locatorSchema,
|
|
200
331
|
target: locatorSchema,
|
|
201
|
-
type:
|
|
332
|
+
type: z10.literal("drag")
|
|
202
333
|
});
|
|
203
|
-
var scrollIntoViewNode =
|
|
334
|
+
var scrollIntoViewNode = z10.object({
|
|
204
335
|
...nodeBase,
|
|
205
336
|
locator: locatorSchema,
|
|
206
|
-
type:
|
|
337
|
+
type: z10.literal("scrollIntoView")
|
|
207
338
|
});
|
|
208
|
-
var typeNode =
|
|
339
|
+
var typeNode = z10.object({
|
|
209
340
|
...nodeBase,
|
|
210
341
|
locator: locatorSchema,
|
|
211
|
-
type:
|
|
342
|
+
type: z10.literal("type"),
|
|
212
343
|
value: stringValueRefSchema
|
|
213
344
|
});
|
|
214
|
-
var focusNode =
|
|
345
|
+
var focusNode = z10.object({
|
|
215
346
|
...nodeBase,
|
|
216
347
|
locator: locatorSchema,
|
|
217
|
-
type:
|
|
348
|
+
type: z10.literal("focus")
|
|
218
349
|
});
|
|
219
|
-
var clearNode =
|
|
220
|
-
var rightClickNode =
|
|
350
|
+
var clearNode = z10.object({ ...nodeBase, locator: locatorSchema, type: z10.literal("clear") });
|
|
351
|
+
var rightClickNode = z10.object({
|
|
221
352
|
...nodeBase,
|
|
222
353
|
locator: locatorSchema,
|
|
223
|
-
type:
|
|
354
|
+
type: z10.literal("rightClick")
|
|
224
355
|
});
|
|
225
|
-
var handleDialogNode =
|
|
356
|
+
var handleDialogNode = z10.object({
|
|
226
357
|
...nodeBase,
|
|
227
|
-
action:
|
|
228
|
-
promptText:
|
|
229
|
-
type:
|
|
358
|
+
action: z10.enum(["accept", "dismiss"]),
|
|
359
|
+
promptText: z10.string().optional(),
|
|
360
|
+
type: z10.literal("handleDialog")
|
|
230
361
|
});
|
|
231
|
-
var clipboardNode =
|
|
362
|
+
var clipboardNode = z10.object({
|
|
232
363
|
...nodeBase,
|
|
233
|
-
action:
|
|
234
|
-
type:
|
|
364
|
+
action: z10.enum(["read", "write"]),
|
|
365
|
+
type: z10.literal("clipboard"),
|
|
235
366
|
value: stringValueRefSchema.optional(),
|
|
236
|
-
variable:
|
|
367
|
+
variable: z10.string().min(1).optional()
|
|
237
368
|
});
|
|
238
|
-
var setPermissionNode =
|
|
369
|
+
var setPermissionNode = z10.object({
|
|
239
370
|
...nodeBase,
|
|
240
|
-
permission:
|
|
241
|
-
state:
|
|
242
|
-
type:
|
|
371
|
+
permission: z10.string().min(1),
|
|
372
|
+
state: z10.enum(["granted", "prompt"]),
|
|
373
|
+
type: z10.literal("setPermission")
|
|
243
374
|
});
|
|
244
|
-
var assertVisibleNode =
|
|
375
|
+
var assertVisibleNode = z10.object({
|
|
245
376
|
...nodeBase,
|
|
246
377
|
locator: locatorSchema,
|
|
247
|
-
type:
|
|
378
|
+
type: z10.literal("assertVisible")
|
|
248
379
|
});
|
|
249
|
-
var assertNotVisibleNode =
|
|
380
|
+
var assertNotVisibleNode = z10.object({
|
|
250
381
|
...nodeBase,
|
|
251
382
|
locator: locatorSchema,
|
|
252
|
-
type:
|
|
383
|
+
type: z10.literal("assertNotVisible")
|
|
253
384
|
});
|
|
254
|
-
var assertTextNode =
|
|
385
|
+
var assertTextNode = z10.object({
|
|
255
386
|
...nodeBase,
|
|
256
387
|
expected: stringValueRefSchema,
|
|
257
388
|
locator: locatorSchema,
|
|
258
389
|
operator: comparisonOperator,
|
|
259
|
-
type:
|
|
390
|
+
type: z10.literal("assertText")
|
|
260
391
|
});
|
|
261
|
-
var assertUrlNode =
|
|
392
|
+
var assertUrlNode = z10.object({
|
|
262
393
|
...nodeBase,
|
|
263
394
|
expected: stringValueRefSchema,
|
|
264
395
|
operator: comparisonOperator,
|
|
265
|
-
type:
|
|
396
|
+
type: z10.literal("assertUrl")
|
|
266
397
|
});
|
|
267
|
-
var assertCountNode =
|
|
398
|
+
var assertCountNode = z10.object({
|
|
268
399
|
...nodeBase,
|
|
269
400
|
expected: numericValueRefSchema,
|
|
270
401
|
locator: locatorSchema,
|
|
271
402
|
operator: numericOperator,
|
|
272
|
-
type:
|
|
403
|
+
type: z10.literal("assertCount")
|
|
273
404
|
});
|
|
274
|
-
var assertValueNode =
|
|
405
|
+
var assertValueNode = z10.object({
|
|
275
406
|
...nodeBase,
|
|
276
407
|
expected: stringValueRefSchema,
|
|
277
408
|
locator: locatorSchema,
|
|
278
409
|
operator: comparisonOperator,
|
|
279
|
-
type:
|
|
410
|
+
type: z10.literal("assertValue")
|
|
280
411
|
});
|
|
281
|
-
var assertAttributeNode =
|
|
412
|
+
var assertAttributeNode = z10.object({
|
|
282
413
|
...nodeBase,
|
|
283
|
-
attribute:
|
|
414
|
+
attribute: z10.string().min(1),
|
|
284
415
|
expected: stringValueRefSchema,
|
|
285
416
|
locator: locatorSchema,
|
|
286
417
|
operator: comparisonOperator,
|
|
287
|
-
type:
|
|
418
|
+
type: z10.literal("assertAttribute")
|
|
288
419
|
});
|
|
289
|
-
var assertEnabledNode =
|
|
420
|
+
var assertEnabledNode = z10.object({
|
|
290
421
|
...nodeBase,
|
|
291
422
|
locator: locatorSchema,
|
|
292
|
-
type:
|
|
423
|
+
type: z10.literal("assertEnabled")
|
|
293
424
|
});
|
|
294
|
-
var assertDisabledNode =
|
|
425
|
+
var assertDisabledNode = z10.object({
|
|
295
426
|
...nodeBase,
|
|
296
427
|
locator: locatorSchema,
|
|
297
|
-
type:
|
|
428
|
+
type: z10.literal("assertDisabled")
|
|
298
429
|
});
|
|
299
|
-
var assertTitleNode =
|
|
430
|
+
var assertTitleNode = z10.object({
|
|
300
431
|
...nodeBase,
|
|
301
432
|
expected: stringValueRefSchema,
|
|
302
433
|
operator: comparisonOperator,
|
|
303
|
-
type:
|
|
434
|
+
type: z10.literal("assertTitle")
|
|
304
435
|
});
|
|
305
|
-
var assertCheckedNode =
|
|
436
|
+
var assertCheckedNode = z10.object({
|
|
306
437
|
...nodeBase,
|
|
307
438
|
locator: locatorSchema,
|
|
308
|
-
type:
|
|
439
|
+
type: z10.literal("assertChecked")
|
|
309
440
|
});
|
|
310
|
-
var assertNotCheckedNode =
|
|
441
|
+
var assertNotCheckedNode = z10.object({
|
|
311
442
|
...nodeBase,
|
|
312
443
|
locator: locatorSchema,
|
|
313
|
-
type:
|
|
444
|
+
type: z10.literal("assertNotChecked")
|
|
314
445
|
});
|
|
315
|
-
var assertFocusedNode =
|
|
446
|
+
var assertFocusedNode = z10.object({
|
|
316
447
|
...nodeBase,
|
|
317
448
|
locator: locatorSchema,
|
|
318
|
-
type:
|
|
449
|
+
type: z10.literal("assertFocused")
|
|
319
450
|
});
|
|
320
|
-
var assertNotFocusedNode =
|
|
451
|
+
var assertNotFocusedNode = z10.object({
|
|
321
452
|
...nodeBase,
|
|
322
453
|
locator: locatorSchema,
|
|
323
|
-
type:
|
|
454
|
+
type: z10.literal("assertNotFocused")
|
|
324
455
|
});
|
|
325
|
-
var specNodeSchema =
|
|
456
|
+
var specNodeSchema = z10.discriminatedUnion("type", [
|
|
326
457
|
gotoNode,
|
|
327
458
|
clickNode,
|
|
328
459
|
fillNode,
|
|
@@ -361,51 +492,67 @@ var specNodeSchema = z9.discriminatedUnion("type", [
|
|
|
361
492
|
assertFocusedNode,
|
|
362
493
|
assertNotFocusedNode
|
|
363
494
|
]);
|
|
364
|
-
var workflowSpecSchema =
|
|
365
|
-
entryNode:
|
|
366
|
-
nodes:
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
495
|
+
var workflowSpecSchema = z10.object({
|
|
496
|
+
entryNode: z10.string().min(1).max(200),
|
|
497
|
+
nodes: z10.record(z10.string().max(200), specNodeSchema).refine(
|
|
498
|
+
(nodes) => Object.keys(nodes).length <= MAX_NODES_PER_WORKFLOW,
|
|
499
|
+
`Workflow has more than ${String(MAX_NODES_PER_WORKFLOW)} nodes`
|
|
500
|
+
),
|
|
501
|
+
variableNamespaces: z10.record(z10.string().max(200), z10.string().max(500)).optional(),
|
|
502
|
+
variables: z10.record(z10.string().max(200), variableDefSchema).optional()
|
|
370
503
|
});
|
|
371
504
|
|
|
505
|
+
// ../spec/src/codecs.ts
|
|
506
|
+
var preconditionMapSchema = z11.record(z11.string().max(200), preconditionSchema);
|
|
507
|
+
var looksLikeWorkflowSpec = {
|
|
508
|
+
assumedVersion: 1,
|
|
509
|
+
detect: (raw) => typeof raw === "object" && raw !== null && "entryNode" in raw && "nodes" in raw
|
|
510
|
+
};
|
|
511
|
+
var looksLikeStateGraph = {
|
|
512
|
+
assumedVersion: 1,
|
|
513
|
+
detect: (raw) => typeof raw === "object" && raw !== null && "edges" in raw && "states" in raw && "preconditions" in raw
|
|
514
|
+
};
|
|
515
|
+
var looksLikePreconditionMap = {
|
|
516
|
+
assumedVersion: 1,
|
|
517
|
+
detect: (raw) => typeof raw === "object" && raw !== null && !Array.isArray(raw) && !("__codec" in raw)
|
|
518
|
+
};
|
|
519
|
+
var workflowSpecCodec = defineCodec("workflow-spec").legacy(looksLikeWorkflowSpec).initial(workflowSpecSchema).build();
|
|
520
|
+
var stateGraphCodec = defineCodec("state-graph").legacy(looksLikeStateGraph).initial(stateGraphSchema).build();
|
|
521
|
+
var preconditionMapCodec = defineCodec("precondition-map").legacy(looksLikePreconditionMap).initial(preconditionMapSchema).build();
|
|
522
|
+
|
|
372
523
|
// src/lockfile.ts
|
|
373
|
-
import { z as
|
|
374
|
-
var LOCKFILE_VERSION = 1;
|
|
524
|
+
import { z as z12 } from "zod";
|
|
375
525
|
var LOCKFILE_RELATIVE_PATH = ".ripplo/ripplo.lock";
|
|
376
|
-
var
|
|
377
|
-
|
|
378
|
-
description:
|
|
379
|
-
expectedOutcome:
|
|
380
|
-
implemented:
|
|
381
|
-
name:
|
|
382
|
-
slug:
|
|
383
|
-
spec: workflowSpecSchema
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
var lockfileSchema = z10.object({
|
|
526
|
+
var MAX_TESTS = 5e3;
|
|
527
|
+
var compiledTestSchema = z12.object({
|
|
528
|
+
description: z12.string().max(2e3),
|
|
529
|
+
expectedOutcome: z12.string().max(2e3),
|
|
530
|
+
implemented: z12.boolean(),
|
|
531
|
+
name: z12.string().max(200),
|
|
532
|
+
slug: z12.string().max(200),
|
|
533
|
+
spec: workflowSpecSchema
|
|
534
|
+
});
|
|
535
|
+
var lockfileBodySchema = z12.object({
|
|
387
536
|
graph: stateGraphSchema,
|
|
388
|
-
|
|
389
|
-
tests: z10.array(compiledTestSchema)
|
|
537
|
+
tests: z12.array(compiledTestSchema).max(MAX_TESTS)
|
|
390
538
|
});
|
|
539
|
+
var lockfileCodec = defineCodec("ripplo-lockfile").initial(lockfileBodySchema).build();
|
|
391
540
|
function compileResultToLockfile(result) {
|
|
392
541
|
return {
|
|
393
542
|
graph: result.graph,
|
|
394
|
-
lockfileVersion: LOCKFILE_VERSION,
|
|
395
543
|
tests: result.tests.map((test) => ({
|
|
396
|
-
additionalChecks: [...test.additionalChecks],
|
|
397
544
|
description: test.description,
|
|
398
545
|
expectedOutcome: test.expectedOutcome,
|
|
399
546
|
implemented: test.implemented,
|
|
400
547
|
name: test.name,
|
|
401
548
|
slug: test.slug,
|
|
402
|
-
spec: test.spec
|
|
403
|
-
warnings: [...test.warnings]
|
|
549
|
+
spec: test.spec
|
|
404
550
|
}))
|
|
405
551
|
};
|
|
406
552
|
}
|
|
407
553
|
function serializeLockfile(lockfile) {
|
|
408
|
-
|
|
554
|
+
const envelope = lockfileCodec.encode(lockfile);
|
|
555
|
+
return `${JSON.stringify(envelope, sortedReplacer(envelope), 2)}
|
|
409
556
|
`;
|
|
410
557
|
}
|
|
411
558
|
async function readLockfile({ cwd }) {
|
|
@@ -414,8 +561,7 @@ async function readLockfile({ cwd }) {
|
|
|
414
561
|
if (raw == null) {
|
|
415
562
|
return null;
|
|
416
563
|
}
|
|
417
|
-
|
|
418
|
-
return lockfileSchema.parse(parsed);
|
|
564
|
+
return decodeJson(lockfileCodec, raw);
|
|
419
565
|
}
|
|
420
566
|
async function writeLockfile({ cwd, result }) {
|
|
421
567
|
const lockfile = compileResultToLockfile(result);
|
|
@@ -460,10 +606,9 @@ function isPlainObject(value) {
|
|
|
460
606
|
}
|
|
461
607
|
export {
|
|
462
608
|
LOCKFILE_RELATIVE_PATH,
|
|
463
|
-
LOCKFILE_VERSION,
|
|
464
609
|
compareCompileToLockfile,
|
|
465
610
|
compileResultToLockfile,
|
|
466
|
-
|
|
611
|
+
lockfileCodec,
|
|
467
612
|
readLockfile,
|
|
468
613
|
serializeLockfile,
|
|
469
614
|
writeLockfile
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ripplo/testing",
|
|
3
3
|
"description": "TypeScript DSL for defining and running Ripplo e2e workflow tests",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.8",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"standardwebhooks": "^1.0.0",
|
|
63
|
-
"zod": "
|
|
63
|
+
"zod": "^4.3.6"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@types/express": "^5.0.2",
|