@ripplo/testing 0.3.2 → 0.3.4
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 +36 -1
- package/dist/actions.d.ts +12 -22
- package/dist/actions.js +10 -8
- package/dist/assert.d.ts +10 -20
- package/dist/assert.js +8 -7
- package/dist/{chunk-76BU4M6E.js → chunk-55HIO5LW.js} +39 -2
- package/dist/chunk-P67G7RA7.js +58 -0
- package/dist/control.d.ts +11 -1
- package/dist/control.js +9 -20
- package/dist/index.js +25 -1
- package/package.json +3 -3
- package/dist/chunk-2VUWFRR5.js +0 -20
package/README.md
CHANGED
|
@@ -229,9 +229,13 @@ import { extract, variable } from "@ripplo/testing/control";
|
|
|
229
229
|
|
|
230
230
|
const token = variable("token");
|
|
231
231
|
extract(testId("token-value"), token).as("capture token");
|
|
232
|
-
// token
|
|
232
|
+
// pass the token anywhere a string value is accepted:
|
|
233
|
+
fill(role("textbox", "Paste here"), token).as("paste token");
|
|
234
|
+
assert.value(role("textbox", "Paste here"), token).as("assert token");
|
|
233
235
|
```
|
|
234
236
|
|
|
237
|
+
Never reference a runtime variable with a literal `"{{vars.name}}"` string — always pass the `Variable` token so TypeScript verifies the reference.
|
|
238
|
+
|
|
235
239
|
### Step Labels
|
|
236
240
|
|
|
237
241
|
Every step **must** have a `.as("description")` label:
|
|
@@ -260,6 +264,37 @@ test("delete-project")
|
|
|
260
264
|
|
|
261
265
|
**Always destructure and use precondition data.** Never hardcode values that come from preconditions — if a precondition implementation changes, the test should not break.
|
|
262
266
|
|
|
267
|
+
**Never write `"{{namespace.key}}"` as a string literal.** Pass the destructured proxy value directly. Internally the proxy stringifies to `{{namespace.key}}`, but writing the literal bypasses TypeScript so typos (`{{tabel.name}}`) silently compile and silently fail at runtime. The `no-literal-template-strings` lint rule enforces this.
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
// ❌ WRONG — literal template string, no type-checking
|
|
271
|
+
.steps(({ table }) => [
|
|
272
|
+
assert.value(role("textbox", "Table name"), "{{table.name}}").as("name visible"),
|
|
273
|
+
])
|
|
274
|
+
|
|
275
|
+
// ✅ CORRECT — proxy value, type-checked against requires()
|
|
276
|
+
.steps(({ table }) => [
|
|
277
|
+
assert.value(role("textbox", "Table name"), table.name).as("name visible"),
|
|
278
|
+
])
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Runtime variables (captured via `clipboard`, `extract`, etc.) follow the same rule. Create a token with `variable("name")` and pass it anywhere a value is accepted:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
import { variable } from "@ripplo/testing/control";
|
|
285
|
+
|
|
286
|
+
.steps(() => {
|
|
287
|
+
const copied = variable("copied");
|
|
288
|
+
return [
|
|
289
|
+
click(role("button", "Copy")).as("copy"),
|
|
290
|
+
clipboard({ action: "read", target: copied, value: undefined }).as("read clipboard"),
|
|
291
|
+
assert.value(role("button", "Copy"), copied).as("match clipboard"),
|
|
292
|
+
];
|
|
293
|
+
})
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Never write `"{{vars.copied}}"` as a string literal — use the token.
|
|
297
|
+
|
|
263
298
|
## Wiring it together
|
|
264
299
|
|
|
265
300
|
### `.ripplo/ripplo.ts` — the definitions funnel
|
package/dist/actions.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { Variable, StaticStringRef, VariableRef } from './control.js';
|
|
1
2
|
import { U as UnlabeledStep } from './step-De52hTLd.js';
|
|
2
3
|
import { CheckLocator, InputLocator, AnyLocator, SelectLocator } from './locators.js';
|
|
3
4
|
import '@ripplo/spec';
|
|
4
5
|
|
|
6
|
+
type StringOrVariable = string | Variable<string>;
|
|
5
7
|
declare function navigate(url: string): UnlabeledStep<{
|
|
6
8
|
type: "goto";
|
|
7
9
|
url: {
|
|
@@ -24,7 +26,7 @@ declare function click(locator: AnyLocator, options?: StepOptions): UnlabeledSte
|
|
|
24
26
|
type: "click";
|
|
25
27
|
uiOnly: boolean | undefined;
|
|
26
28
|
}>;
|
|
27
|
-
declare function fill(locator: InputLocator, value:
|
|
29
|
+
declare function fill(locator: InputLocator, value: StringOrVariable): UnlabeledStep<{
|
|
28
30
|
locator: {
|
|
29
31
|
by: "testId";
|
|
30
32
|
value: string;
|
|
@@ -34,12 +36,9 @@ declare function fill(locator: InputLocator, value: string): UnlabeledStep<{
|
|
|
34
36
|
name?: string | undefined;
|
|
35
37
|
};
|
|
36
38
|
type: "fill";
|
|
37
|
-
value:
|
|
38
|
-
type: "static";
|
|
39
|
-
value: string;
|
|
40
|
-
};
|
|
39
|
+
value: StaticStringRef | VariableRef;
|
|
41
40
|
}>;
|
|
42
|
-
declare function select(locator: SelectLocator, value:
|
|
41
|
+
declare function select(locator: SelectLocator, value: StringOrVariable): UnlabeledStep<{
|
|
43
42
|
locator: {
|
|
44
43
|
by: "testId";
|
|
45
44
|
value: string;
|
|
@@ -49,10 +48,7 @@ declare function select(locator: SelectLocator, value: string): UnlabeledStep<{
|
|
|
49
48
|
name?: string | undefined;
|
|
50
49
|
};
|
|
51
50
|
type: "select";
|
|
52
|
-
value:
|
|
53
|
-
type: "static";
|
|
54
|
-
value: string;
|
|
55
|
-
};
|
|
51
|
+
value: StaticStringRef | VariableRef;
|
|
56
52
|
}>;
|
|
57
53
|
declare function check(locator: CheckLocator): UnlabeledStep<{
|
|
58
54
|
locator: {
|
|
@@ -137,7 +133,7 @@ declare function clear(locator: InputLocator): UnlabeledStep<{
|
|
|
137
133
|
};
|
|
138
134
|
type: "clear";
|
|
139
135
|
}>;
|
|
140
|
-
declare function typeText(locator: InputLocator, value:
|
|
136
|
+
declare function typeText(locator: InputLocator, value: StringOrVariable): UnlabeledStep<{
|
|
141
137
|
locator: {
|
|
142
138
|
by: "testId";
|
|
143
139
|
value: string;
|
|
@@ -147,10 +143,7 @@ declare function typeText(locator: InputLocator, value: string): UnlabeledStep<{
|
|
|
147
143
|
name?: string | undefined;
|
|
148
144
|
};
|
|
149
145
|
type: "type";
|
|
150
|
-
value:
|
|
151
|
-
type: "static";
|
|
152
|
-
value: string;
|
|
153
|
-
};
|
|
146
|
+
value: StaticStringRef | VariableRef;
|
|
154
147
|
}>;
|
|
155
148
|
declare function rightClick(locator: AnyLocator): UnlabeledStep<{
|
|
156
149
|
locator: {
|
|
@@ -206,16 +199,13 @@ declare function handleDialog({ action, promptText, uiOnly }: HandleDialogOption
|
|
|
206
199
|
}>;
|
|
207
200
|
interface ClipboardOptions {
|
|
208
201
|
readonly action: "read" | "write";
|
|
209
|
-
readonly
|
|
210
|
-
readonly
|
|
202
|
+
readonly target: Variable<string> | undefined;
|
|
203
|
+
readonly value: StringOrVariable | undefined;
|
|
211
204
|
}
|
|
212
|
-
declare function clipboard({ action,
|
|
205
|
+
declare function clipboard({ action, target, value }: ClipboardOptions): UnlabeledStep<{
|
|
213
206
|
action: "read" | "write";
|
|
214
207
|
type: "clipboard";
|
|
215
|
-
value:
|
|
216
|
-
type: "static";
|
|
217
|
-
value: string;
|
|
218
|
-
} | undefined;
|
|
208
|
+
value: StaticStringRef | VariableRef | undefined;
|
|
219
209
|
variable: string | undefined;
|
|
220
210
|
}>;
|
|
221
211
|
interface SetPermissionOptions {
|
package/dist/actions.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
readVariable,
|
|
3
|
+
toSpecLocator,
|
|
4
|
+
toStringValueRef
|
|
5
|
+
} from "./chunk-P67G7RA7.js";
|
|
4
6
|
import {
|
|
5
7
|
createStep
|
|
6
8
|
} from "./chunk-MGATMMCZ.js";
|
|
@@ -21,14 +23,14 @@ function fill(locator, value) {
|
|
|
21
23
|
return createStep({
|
|
22
24
|
locator: toSpecLocator(locator),
|
|
23
25
|
type: "fill",
|
|
24
|
-
value:
|
|
26
|
+
value: toStringValueRef(value)
|
|
25
27
|
});
|
|
26
28
|
}
|
|
27
29
|
function select(locator, value) {
|
|
28
30
|
return createStep({
|
|
29
31
|
locator: toSpecLocator(locator),
|
|
30
32
|
type: "select",
|
|
31
|
-
value:
|
|
33
|
+
value: toStringValueRef(value)
|
|
32
34
|
});
|
|
33
35
|
}
|
|
34
36
|
function check(locator) {
|
|
@@ -64,7 +66,7 @@ function typeText(locator, value) {
|
|
|
64
66
|
return createStep({
|
|
65
67
|
locator: toSpecLocator(locator),
|
|
66
68
|
type: "type",
|
|
67
|
-
value:
|
|
69
|
+
value: toStringValueRef(value)
|
|
68
70
|
});
|
|
69
71
|
}
|
|
70
72
|
function rightClick(locator) {
|
|
@@ -83,12 +85,12 @@ function drag(source, target) {
|
|
|
83
85
|
function handleDialog({ action, promptText, uiOnly }) {
|
|
84
86
|
return createStep({ action, promptText, type: "handleDialog", uiOnly });
|
|
85
87
|
}
|
|
86
|
-
function clipboard({ action,
|
|
88
|
+
function clipboard({ action, target, value }) {
|
|
87
89
|
return createStep({
|
|
88
90
|
action,
|
|
89
91
|
type: "clipboard",
|
|
90
|
-
value: value == null ? void 0 :
|
|
91
|
-
variable
|
|
92
|
+
value: value == null ? void 0 : toStringValueRef(value),
|
|
93
|
+
variable: target == null ? void 0 : readVariable(target)
|
|
92
94
|
});
|
|
93
95
|
}
|
|
94
96
|
function setPermission({ permission, state }) {
|
package/dist/assert.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { O as ObserverHandle, a as ObserverInput, b as ObserverBudgetTier } from './types-Degkxs1f.js';
|
|
2
|
+
import { Variable, StaticStringRef, VariableRef } from './control.js';
|
|
2
3
|
import { U as UnlabeledStep } from './step-De52hTLd.js';
|
|
3
4
|
import { CheckLocator, AnyLocator } from './locators.js';
|
|
4
5
|
import 'zod';
|
|
5
6
|
import '@ripplo/spec';
|
|
6
7
|
|
|
8
|
+
type StringOrVariable = string | Variable<string>;
|
|
7
9
|
declare const assert: {
|
|
8
10
|
not: {
|
|
9
11
|
checked(locator: CheckLocator): UnlabeledStep<{
|
|
@@ -51,12 +53,9 @@ declare const assert: {
|
|
|
51
53
|
type: "assertNotVisible";
|
|
52
54
|
}>;
|
|
53
55
|
};
|
|
54
|
-
attribute(locator: AnyLocator, attribute: string, expected:
|
|
56
|
+
attribute(locator: AnyLocator, attribute: string, expected: StringOrVariable): UnlabeledStep<{
|
|
55
57
|
attribute: string;
|
|
56
|
-
expected:
|
|
57
|
-
type: "static";
|
|
58
|
-
value: string;
|
|
59
|
-
};
|
|
58
|
+
expected: StaticStringRef | VariableRef;
|
|
60
59
|
locator: {
|
|
61
60
|
by: "testId";
|
|
62
61
|
value: string;
|
|
@@ -137,11 +136,8 @@ declare const assert: {
|
|
|
137
136
|
};
|
|
138
137
|
type: "assertFocused";
|
|
139
138
|
}>;
|
|
140
|
-
text(locator: AnyLocator, expected:
|
|
141
|
-
expected:
|
|
142
|
-
type: "static";
|
|
143
|
-
value: string;
|
|
144
|
-
};
|
|
139
|
+
text(locator: AnyLocator, expected: StringOrVariable): UnlabeledStep<{
|
|
140
|
+
expected: StaticStringRef | VariableRef;
|
|
145
141
|
locator: {
|
|
146
142
|
by: "testId";
|
|
147
143
|
value: string;
|
|
@@ -153,19 +149,13 @@ declare const assert: {
|
|
|
153
149
|
operator: "equals";
|
|
154
150
|
type: "assertText";
|
|
155
151
|
}>;
|
|
156
|
-
url(expected:
|
|
157
|
-
expected:
|
|
158
|
-
type: "static";
|
|
159
|
-
value: string;
|
|
160
|
-
};
|
|
152
|
+
url(expected: StringOrVariable): UnlabeledStep<{
|
|
153
|
+
expected: StaticStringRef | VariableRef;
|
|
161
154
|
operator: "contains";
|
|
162
155
|
type: "assertUrl";
|
|
163
156
|
}>;
|
|
164
|
-
value(locator: AnyLocator, expected:
|
|
165
|
-
expected:
|
|
166
|
-
type: "static";
|
|
167
|
-
value: string;
|
|
168
|
-
};
|
|
157
|
+
value(locator: AnyLocator, expected: StringOrVariable): UnlabeledStep<{
|
|
158
|
+
expected: StaticStringRef | VariableRef;
|
|
169
159
|
locator: {
|
|
170
160
|
by: "testId";
|
|
171
161
|
value: string;
|
package/dist/assert.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
toSpecLocator
|
|
3
|
-
|
|
2
|
+
toSpecLocator,
|
|
3
|
+
toStringValueRef
|
|
4
|
+
} from "./chunk-P67G7RA7.js";
|
|
4
5
|
import {
|
|
5
6
|
readObserverBudget,
|
|
6
7
|
readObserverName
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-55HIO5LW.js";
|
|
8
9
|
import {
|
|
9
10
|
createStep
|
|
10
11
|
} from "./chunk-MGATMMCZ.js";
|
|
@@ -29,7 +30,7 @@ var assert = {
|
|
|
29
30
|
attribute(locator, attribute, expected) {
|
|
30
31
|
return createStep({
|
|
31
32
|
attribute,
|
|
32
|
-
expected:
|
|
33
|
+
expected: toStringValueRef(expected),
|
|
33
34
|
locator: toSpecLocator(locator),
|
|
34
35
|
operator: "equals",
|
|
35
36
|
type: "assertAttribute"
|
|
@@ -65,7 +66,7 @@ var assert = {
|
|
|
65
66
|
},
|
|
66
67
|
text(locator, expected) {
|
|
67
68
|
return createStep({
|
|
68
|
-
expected:
|
|
69
|
+
expected: toStringValueRef(expected),
|
|
69
70
|
locator: toSpecLocator(locator),
|
|
70
71
|
operator: "equals",
|
|
71
72
|
type: "assertText"
|
|
@@ -73,14 +74,14 @@ var assert = {
|
|
|
73
74
|
},
|
|
74
75
|
url(expected) {
|
|
75
76
|
return createStep({
|
|
76
|
-
expected:
|
|
77
|
+
expected: toStringValueRef(expected),
|
|
77
78
|
operator: "contains",
|
|
78
79
|
type: "assertUrl"
|
|
79
80
|
});
|
|
80
81
|
},
|
|
81
82
|
value(locator, expected) {
|
|
82
83
|
return createStep({
|
|
83
|
-
expected:
|
|
84
|
+
expected: toStringValueRef(expected),
|
|
84
85
|
locator: toSpecLocator(locator),
|
|
85
86
|
operator: "equals",
|
|
86
87
|
type: "assertValue"
|
|
@@ -6,7 +6,23 @@ var DEFAULT_WATCH_PATHS = [
|
|
|
6
6
|
"apps/**",
|
|
7
7
|
"pages/**",
|
|
8
8
|
"routes/**",
|
|
9
|
-
"components/**"
|
|
9
|
+
"components/**",
|
|
10
|
+
"server/**",
|
|
11
|
+
"api/**",
|
|
12
|
+
"backend/**",
|
|
13
|
+
"features/**",
|
|
14
|
+
"modules/**",
|
|
15
|
+
"views/**",
|
|
16
|
+
"ui/**",
|
|
17
|
+
"hooks/**",
|
|
18
|
+
"contexts/**",
|
|
19
|
+
"providers/**",
|
|
20
|
+
"controllers/**",
|
|
21
|
+
"handlers/**",
|
|
22
|
+
"resolvers/**",
|
|
23
|
+
"services/**",
|
|
24
|
+
"middleware/**",
|
|
25
|
+
"lib/**"
|
|
10
26
|
];
|
|
11
27
|
var DEFAULT_IGNORE_PATHS = [
|
|
12
28
|
"**/*.gen.*",
|
|
@@ -18,7 +34,28 @@ var DEFAULT_IGNORE_PATHS = [
|
|
|
18
34
|
"**/dist/**",
|
|
19
35
|
"**/build/**",
|
|
20
36
|
".ripplo/**",
|
|
21
|
-
"**/*.md"
|
|
37
|
+
"**/*.md",
|
|
38
|
+
"**/.next/**",
|
|
39
|
+
"**/.turbo/**",
|
|
40
|
+
"**/.vercel/**",
|
|
41
|
+
"**/.svelte-kit/**",
|
|
42
|
+
"**/.nuxt/**",
|
|
43
|
+
"**/.astro/**",
|
|
44
|
+
"**/coverage/**",
|
|
45
|
+
"**/storybook-static/**",
|
|
46
|
+
"**/*.stories.*",
|
|
47
|
+
"**/*.story.*",
|
|
48
|
+
"**/__tests__/**",
|
|
49
|
+
"**/__mocks__/**",
|
|
50
|
+
"**/__fixtures__/**",
|
|
51
|
+
"**/*.config.*",
|
|
52
|
+
"**/*.setup.*",
|
|
53
|
+
"**/public/**",
|
|
54
|
+
"**/static/**",
|
|
55
|
+
"**/assets/**",
|
|
56
|
+
"**/migrations/**",
|
|
57
|
+
"**/prisma/migrations/**",
|
|
58
|
+
"**/scripts/**"
|
|
22
59
|
];
|
|
23
60
|
var dslConfigSchema = z.object({
|
|
24
61
|
appUrl: z.string(),
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createStep
|
|
3
|
+
} from "./chunk-MGATMMCZ.js";
|
|
4
|
+
import {
|
|
5
|
+
readLocator
|
|
6
|
+
} from "./chunk-DCJBLS2U.js";
|
|
7
|
+
|
|
8
|
+
// src/steps/to-spec-locator.ts
|
|
9
|
+
function toSpecLocator(loc) {
|
|
10
|
+
const spec = readLocator(loc);
|
|
11
|
+
switch (spec.by) {
|
|
12
|
+
case "role": {
|
|
13
|
+
return { by: "role", name: spec.name, role: spec.role };
|
|
14
|
+
}
|
|
15
|
+
case "testId": {
|
|
16
|
+
return { by: "testId", value: spec.value };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/steps/control.ts
|
|
22
|
+
function variable(name) {
|
|
23
|
+
return { name };
|
|
24
|
+
}
|
|
25
|
+
function readVariable(v) {
|
|
26
|
+
return v.name;
|
|
27
|
+
}
|
|
28
|
+
function isVariable(v) {
|
|
29
|
+
if (typeof v !== "object" || v == null) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (!("name" in v)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return typeof v.name === "string";
|
|
36
|
+
}
|
|
37
|
+
function toStringValueRef(value) {
|
|
38
|
+
if (typeof value === "string") {
|
|
39
|
+
return { type: "static", value };
|
|
40
|
+
}
|
|
41
|
+
return { name: readVariable(value), type: "variable" };
|
|
42
|
+
}
|
|
43
|
+
function extract(locator, target) {
|
|
44
|
+
return createStep({
|
|
45
|
+
locator: toSpecLocator(locator),
|
|
46
|
+
type: "extractText",
|
|
47
|
+
variable: readVariable(target)
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
toSpecLocator,
|
|
53
|
+
variable,
|
|
54
|
+
readVariable,
|
|
55
|
+
isVariable,
|
|
56
|
+
toStringValueRef,
|
|
57
|
+
extract
|
|
58
|
+
};
|
package/dist/control.d.ts
CHANGED
|
@@ -8,6 +8,16 @@ interface Variable<_TName extends string> {
|
|
|
8
8
|
}
|
|
9
9
|
declare function variable<TName extends string>(name: TName): Variable<TName>;
|
|
10
10
|
declare function readVariable(v: Variable<string>): string;
|
|
11
|
+
declare function isVariable(v: unknown): v is Variable<string>;
|
|
12
|
+
interface StaticStringRef {
|
|
13
|
+
readonly type: "static";
|
|
14
|
+
readonly value: string;
|
|
15
|
+
}
|
|
16
|
+
interface VariableRef {
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly type: "variable";
|
|
19
|
+
}
|
|
20
|
+
declare function toStringValueRef(value: string | Variable<string>): StaticStringRef | VariableRef;
|
|
11
21
|
declare function extract(locator: AnyLocator, target: Variable<string>): UnlabeledStep<{
|
|
12
22
|
locator: {
|
|
13
23
|
by: "testId";
|
|
@@ -21,4 +31,4 @@ declare function extract(locator: AnyLocator, target: Variable<string>): Unlabel
|
|
|
21
31
|
variable: string;
|
|
22
32
|
}>;
|
|
23
33
|
|
|
24
|
-
export { type Variable, extract, readVariable, variable };
|
|
34
|
+
export { type StaticStringRef, type Variable, type VariableRef, extract, isVariable, readVariable, toStringValueRef, variable };
|
package/dist/control.js
CHANGED
|
@@ -1,27 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
extract,
|
|
3
|
+
isVariable,
|
|
4
|
+
readVariable,
|
|
5
|
+
toStringValueRef,
|
|
6
|
+
variable
|
|
7
|
+
} from "./chunk-P67G7RA7.js";
|
|
8
|
+
import "./chunk-MGATMMCZ.js";
|
|
7
9
|
import "./chunk-DCJBLS2U.js";
|
|
8
|
-
|
|
9
|
-
// src/steps/control.ts
|
|
10
|
-
function variable(name) {
|
|
11
|
-
return { name };
|
|
12
|
-
}
|
|
13
|
-
function readVariable(v) {
|
|
14
|
-
return v.name;
|
|
15
|
-
}
|
|
16
|
-
function extract(locator, target) {
|
|
17
|
-
return createStep({
|
|
18
|
-
locator: toSpecLocator(locator),
|
|
19
|
-
type: "extractText",
|
|
20
|
-
variable: readVariable(target)
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
10
|
export {
|
|
24
11
|
extract,
|
|
12
|
+
isVariable,
|
|
25
13
|
readVariable,
|
|
14
|
+
toStringValueRef,
|
|
26
15
|
variable
|
|
27
16
|
};
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
readPreconditionName,
|
|
13
13
|
readTestValue,
|
|
14
14
|
userDslConfigSchema
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-55HIO5LW.js";
|
|
16
16
|
import {
|
|
17
17
|
compile
|
|
18
18
|
} from "./chunk-KNF4K4JH.js";
|
|
@@ -716,9 +716,33 @@ function observerParamsReferenceVariables(nodes, test2, report) {
|
|
|
716
716
|
});
|
|
717
717
|
});
|
|
718
718
|
}
|
|
719
|
+
function noLiteralTemplateStrings(nodes, test2, report) {
|
|
720
|
+
const declaredKeys = new Set(Object.keys(test2.spec.variables ?? {}));
|
|
721
|
+
const pattern = /\{\{([^{}]+?)\}\}/g;
|
|
722
|
+
nodes.forEach((node) => {
|
|
723
|
+
const serialized = JSON.stringify(node);
|
|
724
|
+
const allMatches = [...serialized.matchAll(pattern)];
|
|
725
|
+
const undeclared = [
|
|
726
|
+
...new Set(
|
|
727
|
+
allMatches.map((m) => m[1]).filter((key) => key != null && !declaredKeys.has(key))
|
|
728
|
+
)
|
|
729
|
+
];
|
|
730
|
+
if (undeclared.length === 0) {
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
const keyList = undeclared.map((k) => `{{${k}}}`).join(", ");
|
|
734
|
+
const namespaces = [...new Set(undeclared.map((k) => k.split(".")[0] ?? k))].join(", ");
|
|
735
|
+
report({
|
|
736
|
+
message: `"${node.label ?? node.id}" contains literal template string(s) ${keyList} \u2014 destructure the proxy in .steps(({ ${namespaces} }) => \u2026) and pass the proxy value (e.g. \`${undeclared[0] ?? ""}\`) directly. Writing the template as a string bypasses type-checking and silently accepts typos.`,
|
|
737
|
+
rule: "no-literal-template-strings",
|
|
738
|
+
step: node.label ?? node.id
|
|
739
|
+
});
|
|
740
|
+
});
|
|
741
|
+
}
|
|
719
742
|
var RULES = [
|
|
720
743
|
exactTextMatch,
|
|
721
744
|
noHardcodedData,
|
|
745
|
+
noLiteralTemplateStrings,
|
|
722
746
|
preferPreconditionData,
|
|
723
747
|
missingLabel,
|
|
724
748
|
noDuplicateLabels,
|
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.3.
|
|
4
|
+
"version": "0.3.4",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
@@ -71,8 +71,8 @@
|
|
|
71
71
|
"tsup": "^8.5.1",
|
|
72
72
|
"typescript": "catalog:",
|
|
73
73
|
"vitest": "^4.1.4",
|
|
74
|
-
"@ripplo/
|
|
75
|
-
"@ripplo/
|
|
74
|
+
"@ripplo/spec": "^0.0.0",
|
|
75
|
+
"@ripplo/eslint-config": "0.0.0"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
78
|
"dotenv": "^16.0.0 || ^17.0.0",
|
package/dist/chunk-2VUWFRR5.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
readLocator
|
|
3
|
-
} from "./chunk-DCJBLS2U.js";
|
|
4
|
-
|
|
5
|
-
// src/steps/to-spec-locator.ts
|
|
6
|
-
function toSpecLocator(loc) {
|
|
7
|
-
const spec = readLocator(loc);
|
|
8
|
-
switch (spec.by) {
|
|
9
|
-
case "role": {
|
|
10
|
-
return { by: "role", name: spec.name, role: spec.role };
|
|
11
|
-
}
|
|
12
|
-
case "testId": {
|
|
13
|
-
return { by: "testId", value: spec.value };
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export {
|
|
19
|
-
toSpecLocator
|
|
20
|
-
};
|