eslint-plugin-executable-stories-playwright 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/README.md +55 -0
- package/dist/index.cjs +345 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +317 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# eslint-plugin-executable-stories-playwright
|
|
2
|
+
|
|
3
|
+
ESLint rules for [executable-stories-playwright](https://github.com/jagreehal/executable-stories). Use with Playwright to catch common mistakes when writing story tests.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add -D eslint-plugin-executable-stories-playwright
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires ESLint 9+ (flat config).
|
|
12
|
+
|
|
13
|
+
## Usage (flat config)
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import playwrightExecutableStories from 'eslint-plugin-executable-stories-playwright';
|
|
17
|
+
|
|
18
|
+
export default [
|
|
19
|
+
{
|
|
20
|
+
plugins: {
|
|
21
|
+
'executable-stories-playwright': playwrightExecutableStories,
|
|
22
|
+
},
|
|
23
|
+
rules: {
|
|
24
|
+
...playwrightExecutableStories.configs.recommended[0].rules,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or spread the recommended config:
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
import playwrightExecutableStories from 'eslint-plugin-executable-stories-playwright';
|
|
34
|
+
|
|
35
|
+
export default [...playwrightExecutableStories.configs.recommended];
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Rules
|
|
39
|
+
|
|
40
|
+
| Rule | Description | Config |
|
|
41
|
+
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------- | ----------- |
|
|
42
|
+
| `require-story-context-for-steps` | Ensure `given/when/then/and/but` (and aliases) are called inside `story(...)` or `doc.story(..., callback)` | recommended |
|
|
43
|
+
| `require-test-context-for-doc-story` | Ensure `doc.story(title)` is called inside a `test()` or `it()` callback (framework-native tests) | recommended |
|
|
44
|
+
|
|
45
|
+
These rules apply only when the file uses legacy patterns (e.g. top-level steps or `doc.story()`). If you use test() + `story.init(testInfo)` + `story.given`/`story.when`/`story.then`, the rules do not run; the current API has no top-level steps and no `doc.story()`, so no changes are required.
|
|
46
|
+
|
|
47
|
+
## Configs
|
|
48
|
+
|
|
49
|
+
| Config | Description |
|
|
50
|
+
| ------------- | ----------------------- |
|
|
51
|
+
| `recommended` | Enables the rules above |
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
configs: () => configs,
|
|
24
|
+
default: () => src_default,
|
|
25
|
+
rules: () => rules
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(src_exports);
|
|
28
|
+
|
|
29
|
+
// src/rules/require-story-context-for-steps.ts
|
|
30
|
+
var V1_PACKAGE = "executable-stories-playwright";
|
|
31
|
+
var STEP_NAMES = /* @__PURE__ */ new Set([
|
|
32
|
+
"given",
|
|
33
|
+
"when",
|
|
34
|
+
"then",
|
|
35
|
+
"and",
|
|
36
|
+
"but",
|
|
37
|
+
"arrange",
|
|
38
|
+
"act",
|
|
39
|
+
"assert",
|
|
40
|
+
"setup",
|
|
41
|
+
"context",
|
|
42
|
+
"execute",
|
|
43
|
+
"action",
|
|
44
|
+
"verify"
|
|
45
|
+
]);
|
|
46
|
+
var STORY_MODIFIERS = /* @__PURE__ */ new Set(["skip", "only"]);
|
|
47
|
+
function isFunction(node) {
|
|
48
|
+
return node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
49
|
+
}
|
|
50
|
+
function isFunctionNode(node) {
|
|
51
|
+
return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
52
|
+
}
|
|
53
|
+
function isStoryCall(node) {
|
|
54
|
+
const { callee } = node;
|
|
55
|
+
if (callee.type === "Identifier") return callee.name === "story";
|
|
56
|
+
if (callee.type !== "MemberExpression") return false;
|
|
57
|
+
if (callee.object.type !== "Identifier" || callee.object.name !== "story")
|
|
58
|
+
return false;
|
|
59
|
+
return callee.property.type === "Identifier" && STORY_MODIFIERS.has(callee.property.name);
|
|
60
|
+
}
|
|
61
|
+
function isDocStoryCall(node) {
|
|
62
|
+
const { callee } = node;
|
|
63
|
+
if (callee.type !== "MemberExpression") return false;
|
|
64
|
+
return callee.object.type === "Identifier" && callee.object.name === "doc" && callee.property.type === "Identifier" && callee.property.name === "story";
|
|
65
|
+
}
|
|
66
|
+
function isStepCall(node) {
|
|
67
|
+
const { callee } = node;
|
|
68
|
+
if (callee.type === "Identifier") return STEP_NAMES.has(callee.name);
|
|
69
|
+
if (callee.type !== "MemberExpression") return false;
|
|
70
|
+
if (callee.object.type !== "Identifier") return false;
|
|
71
|
+
if (callee.object.name !== "steps" && callee.object.name !== "step")
|
|
72
|
+
return false;
|
|
73
|
+
return callee.property.type === "Identifier" && STEP_NAMES.has(callee.property.name);
|
|
74
|
+
}
|
|
75
|
+
function insideStoryCallback(node, context) {
|
|
76
|
+
const ancestors = context.getSourceCode().getAncestors(node);
|
|
77
|
+
const functionAncestors = new Set(ancestors.filter(isFunction));
|
|
78
|
+
for (const ancestor of ancestors) {
|
|
79
|
+
if (ancestor.type !== "CallExpression") continue;
|
|
80
|
+
if (!isStoryCall(ancestor) && !isDocStoryCall(ancestor)) continue;
|
|
81
|
+
for (const arg of ancestor.arguments) {
|
|
82
|
+
if (arg && typeof arg === "object" && functionAncestors.has(arg)) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
var rule = {
|
|
90
|
+
meta: {
|
|
91
|
+
type: "problem",
|
|
92
|
+
docs: {
|
|
93
|
+
description: "Require step functions (given/when/then/and/but and aliases) to be called inside story() or doc.story(..., callback).",
|
|
94
|
+
recommended: true
|
|
95
|
+
},
|
|
96
|
+
schema: [],
|
|
97
|
+
messages: {
|
|
98
|
+
requireStory: "Step functions must be called inside story(...) (or doc.story(..., callback))."
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
create(context) {
|
|
102
|
+
let hasV1Import = false;
|
|
103
|
+
const namedFunctions = /* @__PURE__ */ new Map();
|
|
104
|
+
const storyCallbackNames = /* @__PURE__ */ new Set();
|
|
105
|
+
const pendingStepCalls = [];
|
|
106
|
+
function getContainingFunctionName(node) {
|
|
107
|
+
const ancestors = context.getSourceCode().getAncestors(node);
|
|
108
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
109
|
+
const ancestor = ancestors[i];
|
|
110
|
+
if (ancestor.type === "FunctionDeclaration" && ancestor.id) {
|
|
111
|
+
return ancestor.id.name;
|
|
112
|
+
}
|
|
113
|
+
if (ancestor.type === "VariableDeclarator") {
|
|
114
|
+
const declarator = ancestor;
|
|
115
|
+
if (declarator.id.type === "Identifier" && declarator.init && isFunctionNode(declarator.init)) {
|
|
116
|
+
return declarator.id.name;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (ancestor.type === "Property") {
|
|
120
|
+
const prop = ancestor;
|
|
121
|
+
if (prop.key.type === "Identifier" && prop.value && isFunctionNode(prop.value)) {
|
|
122
|
+
return prop.key.name;
|
|
123
|
+
}
|
|
124
|
+
if (prop.key.type === "Identifier" && prop.method) {
|
|
125
|
+
return prop.key.name;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
ImportDeclaration(node) {
|
|
133
|
+
if (node.source.value === V1_PACKAGE) {
|
|
134
|
+
hasV1Import = true;
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
FunctionDeclaration(node) {
|
|
138
|
+
if (node.id) {
|
|
139
|
+
namedFunctions.set(node.id.name, node);
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
VariableDeclarator(node) {
|
|
143
|
+
if (node.id.type === "Identifier" && node.init && isFunctionNode(node.init)) {
|
|
144
|
+
namedFunctions.set(node.id.name, node.init);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
CallExpression(node) {
|
|
148
|
+
if (!hasV1Import) return;
|
|
149
|
+
if (isStoryCall(node) || isDocStoryCall(node)) {
|
|
150
|
+
for (const arg of node.arguments) {
|
|
151
|
+
if (arg.type === "Identifier") {
|
|
152
|
+
storyCallbackNames.add(arg.name);
|
|
153
|
+
}
|
|
154
|
+
if (arg.type === "MemberExpression" && arg.property.type === "Identifier") {
|
|
155
|
+
storyCallbackNames.add(arg.property.name);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (!isStepCall(node)) return;
|
|
161
|
+
if (insideStoryCallback(node, context)) return;
|
|
162
|
+
const containingFunctionName = getContainingFunctionName(node);
|
|
163
|
+
pendingStepCalls.push({ node, containingFunctionName });
|
|
164
|
+
},
|
|
165
|
+
"Program:exit"() {
|
|
166
|
+
for (const { node, containingFunctionName } of pendingStepCalls) {
|
|
167
|
+
if (containingFunctionName && storyCallbackNames.has(containingFunctionName)) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
context.report({ node, messageId: "requireStory" });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
var require_story_context_for_steps_default = rule;
|
|
177
|
+
|
|
178
|
+
// src/rules/require-test-context-for-doc-story.ts
|
|
179
|
+
var V1_PACKAGE2 = "executable-stories-playwright";
|
|
180
|
+
var TEST_MODIFIERS = /* @__PURE__ */ new Set(["only", "skip", "fixme", "fail", "slow"]);
|
|
181
|
+
function isFunction2(node) {
|
|
182
|
+
return node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
183
|
+
}
|
|
184
|
+
function isFunctionNode2(node) {
|
|
185
|
+
return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
186
|
+
}
|
|
187
|
+
function isDocStoryCall2(node) {
|
|
188
|
+
const { callee } = node;
|
|
189
|
+
if (callee.type !== "MemberExpression") return false;
|
|
190
|
+
return callee.object.type === "Identifier" && callee.object.name === "doc" && callee.property.type === "Identifier" && callee.property.name === "story";
|
|
191
|
+
}
|
|
192
|
+
function isTestCallExpression(node) {
|
|
193
|
+
const { callee } = node;
|
|
194
|
+
if (callee.type === "Identifier") {
|
|
195
|
+
return callee.name === "test" || callee.name === "it";
|
|
196
|
+
}
|
|
197
|
+
if (callee.type !== "MemberExpression") return false;
|
|
198
|
+
if (callee.object.type !== "Identifier") return false;
|
|
199
|
+
if (callee.object.name !== "test" && callee.object.name !== "it")
|
|
200
|
+
return false;
|
|
201
|
+
if (callee.property.type !== "Identifier") return false;
|
|
202
|
+
return TEST_MODIFIERS.has(callee.property.name);
|
|
203
|
+
}
|
|
204
|
+
function insideTestCallback(node, context) {
|
|
205
|
+
const ancestors = context.getSourceCode().getAncestors(node);
|
|
206
|
+
const functionAncestors = new Set(ancestors.filter(isFunction2));
|
|
207
|
+
for (const ancestor of ancestors) {
|
|
208
|
+
if (ancestor.type !== "CallExpression") continue;
|
|
209
|
+
if (!isTestCallExpression(ancestor)) continue;
|
|
210
|
+
for (const arg of ancestor.arguments) {
|
|
211
|
+
if (arg && typeof arg === "object" && functionAncestors.has(arg)) {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
var rule2 = {
|
|
219
|
+
meta: {
|
|
220
|
+
type: "problem",
|
|
221
|
+
docs: {
|
|
222
|
+
description: "Require doc.story(title) to be called inside a test() or it() callback (framework-native pattern).",
|
|
223
|
+
recommended: true
|
|
224
|
+
},
|
|
225
|
+
schema: [],
|
|
226
|
+
messages: {
|
|
227
|
+
requireTest: "doc.story(title) must be called inside a test() callback (e.g. test('...', () => { doc.story('Title'); ... })).",
|
|
228
|
+
requireTitle: "doc.story(title) requires a title argument."
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
create(context) {
|
|
232
|
+
let hasV1Import = false;
|
|
233
|
+
const namedFunctions = /* @__PURE__ */ new Map();
|
|
234
|
+
const testCallbackNames = /* @__PURE__ */ new Set();
|
|
235
|
+
const pendingDocStoryCalls = [];
|
|
236
|
+
function getContainingFunctionName(node) {
|
|
237
|
+
const ancestors = context.getSourceCode().getAncestors(node);
|
|
238
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
239
|
+
const ancestor = ancestors[i];
|
|
240
|
+
if (ancestor.type === "FunctionDeclaration" && ancestor.id) {
|
|
241
|
+
return ancestor.id.name;
|
|
242
|
+
}
|
|
243
|
+
if (ancestor.type === "VariableDeclarator") {
|
|
244
|
+
const declarator = ancestor;
|
|
245
|
+
if (declarator.id.type === "Identifier" && declarator.init && isFunctionNode2(declarator.init)) {
|
|
246
|
+
return declarator.id.name;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (ancestor.type === "Property") {
|
|
250
|
+
const prop = ancestor;
|
|
251
|
+
if (prop.key.type === "Identifier" && prop.value && isFunctionNode2(prop.value)) {
|
|
252
|
+
return prop.key.name;
|
|
253
|
+
}
|
|
254
|
+
if (prop.key.type === "Identifier" && prop.method) {
|
|
255
|
+
return prop.key.name;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
ImportDeclaration(node) {
|
|
263
|
+
if (node.source.value === V1_PACKAGE2) {
|
|
264
|
+
hasV1Import = true;
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
FunctionDeclaration(node) {
|
|
268
|
+
if (node.id) {
|
|
269
|
+
namedFunctions.set(node.id.name, node);
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
VariableDeclarator(node) {
|
|
273
|
+
if (node.id.type === "Identifier" && node.init && isFunctionNode2(node.init)) {
|
|
274
|
+
namedFunctions.set(node.id.name, node.init);
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
CallExpression(node) {
|
|
278
|
+
if (!hasV1Import) return;
|
|
279
|
+
if (isTestCallExpression(node)) {
|
|
280
|
+
for (const arg of node.arguments) {
|
|
281
|
+
if (arg.type === "Identifier") {
|
|
282
|
+
testCallbackNames.add(arg.name);
|
|
283
|
+
}
|
|
284
|
+
if (arg.type === "MemberExpression" && arg.property.type === "Identifier") {
|
|
285
|
+
testCallbackNames.add(arg.property.name);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if (!isDocStoryCall2(node)) return;
|
|
291
|
+
if (node.arguments.length === 0) {
|
|
292
|
+
context.report({ node, messageId: "requireTitle" });
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (node.arguments.length >= 2) return;
|
|
296
|
+
if (insideTestCallback(node, context)) return;
|
|
297
|
+
const containingFunctionName = getContainingFunctionName(node);
|
|
298
|
+
pendingDocStoryCalls.push({ node, containingFunctionName });
|
|
299
|
+
},
|
|
300
|
+
"Program:exit"() {
|
|
301
|
+
for (const { node, containingFunctionName } of pendingDocStoryCalls) {
|
|
302
|
+
if (containingFunctionName && testCallbackNames.has(containingFunctionName)) {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
context.report({ node, messageId: "requireTest" });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
var require_test_context_for_doc_story_default = rule2;
|
|
312
|
+
|
|
313
|
+
// src/index.ts
|
|
314
|
+
var rules = {
|
|
315
|
+
"require-story-context-for-steps": require_story_context_for_steps_default,
|
|
316
|
+
"require-test-context-for-doc-story": require_test_context_for_doc_story_default
|
|
317
|
+
};
|
|
318
|
+
var configs = {
|
|
319
|
+
recommended: [
|
|
320
|
+
{
|
|
321
|
+
plugins: {
|
|
322
|
+
"executable-stories-playwright": { rules }
|
|
323
|
+
},
|
|
324
|
+
rules: {
|
|
325
|
+
"executable-stories-playwright/require-story-context-for-steps": "error",
|
|
326
|
+
"executable-stories-playwright/require-test-context-for-doc-story": "error"
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
]
|
|
330
|
+
};
|
|
331
|
+
var plugin = {
|
|
332
|
+
meta: {
|
|
333
|
+
name: "eslint-plugin-executable-stories-playwright",
|
|
334
|
+
version: "0.1.0"
|
|
335
|
+
},
|
|
336
|
+
rules,
|
|
337
|
+
configs
|
|
338
|
+
};
|
|
339
|
+
var src_default = plugin;
|
|
340
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
341
|
+
0 && (module.exports = {
|
|
342
|
+
configs,
|
|
343
|
+
rules
|
|
344
|
+
});
|
|
345
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/rules/require-story-context-for-steps.ts","../src/rules/require-test-context-for-doc-story.ts"],"sourcesContent":["import type { ESLint, Linter } from 'eslint';\nimport requireStoryContextForSteps from './rules/require-story-context-for-steps.js';\nimport requireTestContextForDocStory from './rules/require-test-context-for-doc-story.js';\n\nconst rules = {\n 'require-story-context-for-steps': requireStoryContextForSteps,\n 'require-test-context-for-doc-story': requireTestContextForDocStory,\n};\n\nconst configs: Record<string, Linter.Config[]> = {\n recommended: [\n {\n plugins: {\n 'executable-stories-playwright': { rules },\n },\n rules: {\n 'executable-stories-playwright/require-story-context-for-steps':\n 'error',\n 'executable-stories-playwright/require-test-context-for-doc-story':\n 'error',\n },\n },\n ],\n};\n\nconst plugin: ESLint.Plugin = {\n meta: {\n name: 'eslint-plugin-executable-stories-playwright',\n version: '0.1.0',\n },\n rules,\n configs,\n};\n\nexport default plugin;\nexport { rules, configs };\n","import type { Rule } from 'eslint';\nimport type {\n ArrowFunctionExpression,\n CallExpression,\n FunctionDeclaration,\n FunctionExpression,\n Node,\n Property,\n VariableDeclarator,\n} from 'estree';\n\n/** Current API uses story.given() etc. inside test(); no top-level given/when/then. */\nconst V1_PACKAGE = 'executable-stories-playwright';\n\nconst STEP_NAMES = new Set([\n 'given',\n 'when',\n 'then',\n 'and',\n 'but',\n 'arrange',\n 'act',\n 'assert',\n 'setup',\n 'context',\n 'execute',\n 'action',\n 'verify',\n]);\n\nconst STORY_MODIFIERS = new Set(['skip', 'only']);\n\nfunction isFunction(node: Node): boolean {\n return (\n node.type === 'FunctionExpression' ||\n node.type === 'ArrowFunctionExpression'\n );\n}\n\nfunction isFunctionNode(\n node: Node,\n): node is FunctionDeclaration | ArrowFunctionExpression | FunctionExpression {\n return (\n node.type === 'FunctionDeclaration' ||\n node.type === 'FunctionExpression' ||\n node.type === 'ArrowFunctionExpression'\n );\n}\n\nfunction isStoryCall(node: CallExpression): boolean {\n const { callee } = node;\n if (callee.type === 'Identifier') return callee.name === 'story';\n if (callee.type !== 'MemberExpression') return false;\n if (callee.object.type !== 'Identifier' || callee.object.name !== 'story')\n return false;\n return (\n callee.property.type === 'Identifier' &&\n STORY_MODIFIERS.has(callee.property.name)\n );\n}\n\nfunction isDocStoryCall(node: CallExpression): boolean {\n const { callee } = node;\n if (callee.type !== 'MemberExpression') return false;\n return (\n callee.object.type === 'Identifier' &&\n callee.object.name === 'doc' &&\n callee.property.type === 'Identifier' &&\n callee.property.name === 'story'\n );\n}\n\nfunction isStepCall(node: CallExpression): boolean {\n const { callee } = node;\n if (callee.type === 'Identifier') return STEP_NAMES.has(callee.name);\n if (callee.type !== 'MemberExpression') return false;\n if (callee.object.type !== 'Identifier') return false;\n if (callee.object.name !== 'steps' && callee.object.name !== 'step')\n return false;\n return (\n callee.property.type === 'Identifier' &&\n STEP_NAMES.has(callee.property.name)\n );\n}\n\nfunction insideStoryCallback(\n node: CallExpression,\n context: Rule.RuleContext,\n): boolean {\n const ancestors = context.getSourceCode().getAncestors(node);\n const functionAncestors = new Set(ancestors.filter(isFunction));\n\n for (const ancestor of ancestors) {\n if (ancestor.type !== 'CallExpression') continue;\n if (!isStoryCall(ancestor) && !isDocStoryCall(ancestor)) continue;\n for (const arg of ancestor.arguments) {\n if (arg && typeof arg === 'object' && functionAncestors.has(arg)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Require step functions (given/when/then/and/but and aliases) to be called inside story() or doc.story(..., callback).',\n recommended: true,\n },\n schema: [],\n messages: {\n requireStory:\n 'Step functions must be called inside story(...) (or doc.story(..., callback)).',\n },\n },\n create(context) {\n let hasV1Import = false;\n const namedFunctions = new Map<string, Node>();\n const storyCallbackNames = new Set<string>();\n const pendingStepCalls: Array<{\n node: CallExpression;\n containingFunctionName: string | null;\n }> = [];\n\n function getContainingFunctionName(node: CallExpression): string | null {\n const ancestors = context.getSourceCode().getAncestors(node);\n for (let i = ancestors.length - 1; i >= 0; i--) {\n const ancestor = ancestors[i];\n if (ancestor.type === 'FunctionDeclaration' && ancestor.id) {\n return ancestor.id.name;\n }\n if (ancestor.type === 'VariableDeclarator') {\n const declarator = ancestor as VariableDeclarator;\n if (\n declarator.id.type === 'Identifier' &&\n declarator.init &&\n isFunctionNode(declarator.init)\n ) {\n return declarator.id.name;\n }\n }\n // Check for object method definitions\n if (ancestor.type === 'Property') {\n const prop = ancestor as Property;\n if (\n prop.key.type === 'Identifier' &&\n prop.value &&\n isFunctionNode(prop.value)\n ) {\n return prop.key.name;\n }\n // Shorthand method syntax: { define() {} }\n if (prop.key.type === 'Identifier' && prop.method) {\n return prop.key.name;\n }\n }\n }\n return null;\n }\n\n return {\n ImportDeclaration(node) {\n if (node.source.value === V1_PACKAGE) {\n hasV1Import = true;\n }\n },\n FunctionDeclaration(node: FunctionDeclaration) {\n if (node.id) {\n namedFunctions.set(node.id.name, node);\n }\n },\n VariableDeclarator(node: VariableDeclarator) {\n if (\n node.id.type === 'Identifier' &&\n node.init &&\n isFunctionNode(node.init)\n ) {\n namedFunctions.set(node.id.name, node.init);\n }\n },\n CallExpression(node: CallExpression) {\n if (!hasV1Import) return;\n\n if (isStoryCall(node) || isDocStoryCall(node)) {\n for (const arg of node.arguments) {\n if (arg.type === 'Identifier') {\n storyCallbackNames.add(arg.name);\n }\n // Handle member expression callbacks like handlers.define\n if (\n arg.type === 'MemberExpression' &&\n arg.property.type === 'Identifier'\n ) {\n storyCallbackNames.add(arg.property.name);\n }\n }\n return;\n }\n\n if (!isStepCall(node)) return;\n if (insideStoryCallback(node, context)) return;\n\n const containingFunctionName = getContainingFunctionName(node);\n pendingStepCalls.push({ node, containingFunctionName });\n },\n 'Program:exit'() {\n for (const { node, containingFunctionName } of pendingStepCalls) {\n if (\n containingFunctionName &&\n storyCallbackNames.has(containingFunctionName)\n ) {\n continue;\n }\n context.report({ node, messageId: 'requireStory' });\n }\n },\n };\n },\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\nimport type {\n ArrowFunctionExpression,\n CallExpression,\n FunctionDeclaration,\n FunctionExpression,\n Node,\n Property,\n VariableDeclarator,\n} from 'estree';\n\n/** Legacy doc.story() is not used; current API uses story.init(testInfo) inside test(). */\nconst V1_PACKAGE = 'executable-stories-playwright';\n\nconst TEST_MODIFIERS = new Set(['only', 'skip', 'fixme', 'fail', 'slow']);\n\nfunction isFunction(node: Node): boolean {\n return (\n node.type === 'FunctionExpression' ||\n node.type === 'ArrowFunctionExpression'\n );\n}\n\nfunction isFunctionNode(\n node: Node,\n): node is FunctionDeclaration | ArrowFunctionExpression | FunctionExpression {\n return (\n node.type === 'FunctionDeclaration' ||\n node.type === 'FunctionExpression' ||\n node.type === 'ArrowFunctionExpression'\n );\n}\n\nfunction isDocStoryCall(node: CallExpression): boolean {\n const { callee } = node;\n if (callee.type !== 'MemberExpression') return false;\n return (\n callee.object.type === 'Identifier' &&\n callee.object.name === 'doc' &&\n callee.property.type === 'Identifier' &&\n callee.property.name === 'story'\n );\n}\n\nfunction isTestCallExpression(node: CallExpression): boolean {\n const { callee } = node;\n if (callee.type === 'Identifier') {\n return callee.name === 'test' || callee.name === 'it';\n }\n if (callee.type !== 'MemberExpression') return false;\n if (callee.object.type !== 'Identifier') return false;\n if (callee.object.name !== 'test' && callee.object.name !== 'it')\n return false;\n if (callee.property.type !== 'Identifier') return false;\n return TEST_MODIFIERS.has(callee.property.name);\n}\n\nfunction insideTestCallback(\n node: CallExpression,\n context: Rule.RuleContext,\n): boolean {\n const ancestors = context.getSourceCode().getAncestors(node);\n const functionAncestors = new Set(ancestors.filter(isFunction));\n\n for (const ancestor of ancestors) {\n if (ancestor.type !== 'CallExpression') continue;\n if (!isTestCallExpression(ancestor)) continue;\n for (const arg of ancestor.arguments) {\n if (arg && typeof arg === 'object' && functionAncestors.has(arg)) {\n return true;\n }\n }\n }\n return false;\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Require doc.story(title) to be called inside a test() or it() callback (framework-native pattern).',\n recommended: true,\n },\n schema: [],\n messages: {\n requireTest:\n \"doc.story(title) must be called inside a test() callback (e.g. test('...', () => { doc.story('Title'); ... })).\",\n requireTitle: 'doc.story(title) requires a title argument.',\n },\n },\n create(context) {\n let hasV1Import = false;\n const namedFunctions = new Map<string, Node>();\n const testCallbackNames = new Set<string>();\n const pendingDocStoryCalls: Array<{\n node: CallExpression;\n containingFunctionName: string | null;\n }> = [];\n\n function getContainingFunctionName(node: CallExpression): string | null {\n const ancestors = context.getSourceCode().getAncestors(node);\n for (let i = ancestors.length - 1; i >= 0; i--) {\n const ancestor = ancestors[i];\n if (ancestor.type === 'FunctionDeclaration' && ancestor.id) {\n return ancestor.id.name;\n }\n if (ancestor.type === 'VariableDeclarator') {\n const declarator = ancestor as VariableDeclarator;\n if (\n declarator.id.type === 'Identifier' &&\n declarator.init &&\n isFunctionNode(declarator.init)\n ) {\n return declarator.id.name;\n }\n }\n // Check for object method definitions\n if (ancestor.type === 'Property') {\n const prop = ancestor as Property;\n if (\n prop.key.type === 'Identifier' &&\n prop.value &&\n isFunctionNode(prop.value)\n ) {\n return prop.key.name;\n }\n // Shorthand method syntax: { run() {} }\n if (prop.key.type === 'Identifier' && prop.method) {\n return prop.key.name;\n }\n }\n }\n return null;\n }\n\n return {\n ImportDeclaration(node) {\n if (node.source.value === V1_PACKAGE) {\n hasV1Import = true;\n }\n },\n FunctionDeclaration(node: FunctionDeclaration) {\n if (node.id) {\n namedFunctions.set(node.id.name, node);\n }\n },\n VariableDeclarator(node: VariableDeclarator) {\n if (\n node.id.type === 'Identifier' &&\n node.init &&\n isFunctionNode(node.init)\n ) {\n namedFunctions.set(node.id.name, node.init);\n }\n },\n CallExpression(node: CallExpression) {\n if (!hasV1Import) return;\n\n if (isTestCallExpression(node)) {\n for (const arg of node.arguments) {\n if (arg.type === 'Identifier') {\n testCallbackNames.add(arg.name);\n }\n // Handle member expression callbacks like handlers.run\n if (\n arg.type === 'MemberExpression' &&\n arg.property.type === 'Identifier'\n ) {\n testCallbackNames.add(arg.property.name);\n }\n }\n return;\n }\n\n if (!isDocStoryCall(node)) return;\n\n if (node.arguments.length === 0) {\n context.report({ node, messageId: 'requireTitle' });\n return;\n }\n if (node.arguments.length >= 2) return;\n\n if (insideTestCallback(node, context)) return;\n\n const containingFunctionName = getContainingFunctionName(node);\n pendingDocStoryCalls.push({ node, containingFunctionName });\n },\n 'Program:exit'() {\n for (const { node, containingFunctionName } of pendingDocStoryCalls) {\n if (\n containingFunctionName &&\n testCallbackNames.has(containingFunctionName)\n ) {\n continue;\n }\n context.report({ node, messageId: 'requireTest' });\n }\n },\n };\n },\n};\n\nexport default rule;\n"],"mappings":"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","names":["V1_PACKAGE","isFunction","isFunctionNode","isDocStoryCall","rule"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as eslint from 'eslint';
|
|
2
|
+
import { Linter, ESLint } from 'eslint';
|
|
3
|
+
|
|
4
|
+
declare const rules: {
|
|
5
|
+
'require-story-context-for-steps': eslint.Rule.RuleModule;
|
|
6
|
+
'require-test-context-for-doc-story': eslint.Rule.RuleModule;
|
|
7
|
+
};
|
|
8
|
+
declare const configs: Record<string, Linter.Config[]>;
|
|
9
|
+
declare const plugin: ESLint.Plugin;
|
|
10
|
+
|
|
11
|
+
export { configs, plugin as default, rules };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as eslint from 'eslint';
|
|
2
|
+
import { Linter, ESLint } from 'eslint';
|
|
3
|
+
|
|
4
|
+
declare const rules: {
|
|
5
|
+
'require-story-context-for-steps': eslint.Rule.RuleModule;
|
|
6
|
+
'require-test-context-for-doc-story': eslint.Rule.RuleModule;
|
|
7
|
+
};
|
|
8
|
+
declare const configs: Record<string, Linter.Config[]>;
|
|
9
|
+
declare const plugin: ESLint.Plugin;
|
|
10
|
+
|
|
11
|
+
export { configs, plugin as default, rules };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
// src/rules/require-story-context-for-steps.ts
|
|
2
|
+
var V1_PACKAGE = "executable-stories-playwright";
|
|
3
|
+
var STEP_NAMES = /* @__PURE__ */ new Set([
|
|
4
|
+
"given",
|
|
5
|
+
"when",
|
|
6
|
+
"then",
|
|
7
|
+
"and",
|
|
8
|
+
"but",
|
|
9
|
+
"arrange",
|
|
10
|
+
"act",
|
|
11
|
+
"assert",
|
|
12
|
+
"setup",
|
|
13
|
+
"context",
|
|
14
|
+
"execute",
|
|
15
|
+
"action",
|
|
16
|
+
"verify"
|
|
17
|
+
]);
|
|
18
|
+
var STORY_MODIFIERS = /* @__PURE__ */ new Set(["skip", "only"]);
|
|
19
|
+
function isFunction(node) {
|
|
20
|
+
return node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
21
|
+
}
|
|
22
|
+
function isFunctionNode(node) {
|
|
23
|
+
return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
24
|
+
}
|
|
25
|
+
function isStoryCall(node) {
|
|
26
|
+
const { callee } = node;
|
|
27
|
+
if (callee.type === "Identifier") return callee.name === "story";
|
|
28
|
+
if (callee.type !== "MemberExpression") return false;
|
|
29
|
+
if (callee.object.type !== "Identifier" || callee.object.name !== "story")
|
|
30
|
+
return false;
|
|
31
|
+
return callee.property.type === "Identifier" && STORY_MODIFIERS.has(callee.property.name);
|
|
32
|
+
}
|
|
33
|
+
function isDocStoryCall(node) {
|
|
34
|
+
const { callee } = node;
|
|
35
|
+
if (callee.type !== "MemberExpression") return false;
|
|
36
|
+
return callee.object.type === "Identifier" && callee.object.name === "doc" && callee.property.type === "Identifier" && callee.property.name === "story";
|
|
37
|
+
}
|
|
38
|
+
function isStepCall(node) {
|
|
39
|
+
const { callee } = node;
|
|
40
|
+
if (callee.type === "Identifier") return STEP_NAMES.has(callee.name);
|
|
41
|
+
if (callee.type !== "MemberExpression") return false;
|
|
42
|
+
if (callee.object.type !== "Identifier") return false;
|
|
43
|
+
if (callee.object.name !== "steps" && callee.object.name !== "step")
|
|
44
|
+
return false;
|
|
45
|
+
return callee.property.type === "Identifier" && STEP_NAMES.has(callee.property.name);
|
|
46
|
+
}
|
|
47
|
+
function insideStoryCallback(node, context) {
|
|
48
|
+
const ancestors = context.getSourceCode().getAncestors(node);
|
|
49
|
+
const functionAncestors = new Set(ancestors.filter(isFunction));
|
|
50
|
+
for (const ancestor of ancestors) {
|
|
51
|
+
if (ancestor.type !== "CallExpression") continue;
|
|
52
|
+
if (!isStoryCall(ancestor) && !isDocStoryCall(ancestor)) continue;
|
|
53
|
+
for (const arg of ancestor.arguments) {
|
|
54
|
+
if (arg && typeof arg === "object" && functionAncestors.has(arg)) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
var rule = {
|
|
62
|
+
meta: {
|
|
63
|
+
type: "problem",
|
|
64
|
+
docs: {
|
|
65
|
+
description: "Require step functions (given/when/then/and/but and aliases) to be called inside story() or doc.story(..., callback).",
|
|
66
|
+
recommended: true
|
|
67
|
+
},
|
|
68
|
+
schema: [],
|
|
69
|
+
messages: {
|
|
70
|
+
requireStory: "Step functions must be called inside story(...) (or doc.story(..., callback))."
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
create(context) {
|
|
74
|
+
let hasV1Import = false;
|
|
75
|
+
const namedFunctions = /* @__PURE__ */ new Map();
|
|
76
|
+
const storyCallbackNames = /* @__PURE__ */ new Set();
|
|
77
|
+
const pendingStepCalls = [];
|
|
78
|
+
function getContainingFunctionName(node) {
|
|
79
|
+
const ancestors = context.getSourceCode().getAncestors(node);
|
|
80
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
81
|
+
const ancestor = ancestors[i];
|
|
82
|
+
if (ancestor.type === "FunctionDeclaration" && ancestor.id) {
|
|
83
|
+
return ancestor.id.name;
|
|
84
|
+
}
|
|
85
|
+
if (ancestor.type === "VariableDeclarator") {
|
|
86
|
+
const declarator = ancestor;
|
|
87
|
+
if (declarator.id.type === "Identifier" && declarator.init && isFunctionNode(declarator.init)) {
|
|
88
|
+
return declarator.id.name;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (ancestor.type === "Property") {
|
|
92
|
+
const prop = ancestor;
|
|
93
|
+
if (prop.key.type === "Identifier" && prop.value && isFunctionNode(prop.value)) {
|
|
94
|
+
return prop.key.name;
|
|
95
|
+
}
|
|
96
|
+
if (prop.key.type === "Identifier" && prop.method) {
|
|
97
|
+
return prop.key.name;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
ImportDeclaration(node) {
|
|
105
|
+
if (node.source.value === V1_PACKAGE) {
|
|
106
|
+
hasV1Import = true;
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
FunctionDeclaration(node) {
|
|
110
|
+
if (node.id) {
|
|
111
|
+
namedFunctions.set(node.id.name, node);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
VariableDeclarator(node) {
|
|
115
|
+
if (node.id.type === "Identifier" && node.init && isFunctionNode(node.init)) {
|
|
116
|
+
namedFunctions.set(node.id.name, node.init);
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
CallExpression(node) {
|
|
120
|
+
if (!hasV1Import) return;
|
|
121
|
+
if (isStoryCall(node) || isDocStoryCall(node)) {
|
|
122
|
+
for (const arg of node.arguments) {
|
|
123
|
+
if (arg.type === "Identifier") {
|
|
124
|
+
storyCallbackNames.add(arg.name);
|
|
125
|
+
}
|
|
126
|
+
if (arg.type === "MemberExpression" && arg.property.type === "Identifier") {
|
|
127
|
+
storyCallbackNames.add(arg.property.name);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (!isStepCall(node)) return;
|
|
133
|
+
if (insideStoryCallback(node, context)) return;
|
|
134
|
+
const containingFunctionName = getContainingFunctionName(node);
|
|
135
|
+
pendingStepCalls.push({ node, containingFunctionName });
|
|
136
|
+
},
|
|
137
|
+
"Program:exit"() {
|
|
138
|
+
for (const { node, containingFunctionName } of pendingStepCalls) {
|
|
139
|
+
if (containingFunctionName && storyCallbackNames.has(containingFunctionName)) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
context.report({ node, messageId: "requireStory" });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
var require_story_context_for_steps_default = rule;
|
|
149
|
+
|
|
150
|
+
// src/rules/require-test-context-for-doc-story.ts
|
|
151
|
+
var V1_PACKAGE2 = "executable-stories-playwright";
|
|
152
|
+
var TEST_MODIFIERS = /* @__PURE__ */ new Set(["only", "skip", "fixme", "fail", "slow"]);
|
|
153
|
+
function isFunction2(node) {
|
|
154
|
+
return node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
155
|
+
}
|
|
156
|
+
function isFunctionNode2(node) {
|
|
157
|
+
return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
158
|
+
}
|
|
159
|
+
function isDocStoryCall2(node) {
|
|
160
|
+
const { callee } = node;
|
|
161
|
+
if (callee.type !== "MemberExpression") return false;
|
|
162
|
+
return callee.object.type === "Identifier" && callee.object.name === "doc" && callee.property.type === "Identifier" && callee.property.name === "story";
|
|
163
|
+
}
|
|
164
|
+
function isTestCallExpression(node) {
|
|
165
|
+
const { callee } = node;
|
|
166
|
+
if (callee.type === "Identifier") {
|
|
167
|
+
return callee.name === "test" || callee.name === "it";
|
|
168
|
+
}
|
|
169
|
+
if (callee.type !== "MemberExpression") return false;
|
|
170
|
+
if (callee.object.type !== "Identifier") return false;
|
|
171
|
+
if (callee.object.name !== "test" && callee.object.name !== "it")
|
|
172
|
+
return false;
|
|
173
|
+
if (callee.property.type !== "Identifier") return false;
|
|
174
|
+
return TEST_MODIFIERS.has(callee.property.name);
|
|
175
|
+
}
|
|
176
|
+
function insideTestCallback(node, context) {
|
|
177
|
+
const ancestors = context.getSourceCode().getAncestors(node);
|
|
178
|
+
const functionAncestors = new Set(ancestors.filter(isFunction2));
|
|
179
|
+
for (const ancestor of ancestors) {
|
|
180
|
+
if (ancestor.type !== "CallExpression") continue;
|
|
181
|
+
if (!isTestCallExpression(ancestor)) continue;
|
|
182
|
+
for (const arg of ancestor.arguments) {
|
|
183
|
+
if (arg && typeof arg === "object" && functionAncestors.has(arg)) {
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
var rule2 = {
|
|
191
|
+
meta: {
|
|
192
|
+
type: "problem",
|
|
193
|
+
docs: {
|
|
194
|
+
description: "Require doc.story(title) to be called inside a test() or it() callback (framework-native pattern).",
|
|
195
|
+
recommended: true
|
|
196
|
+
},
|
|
197
|
+
schema: [],
|
|
198
|
+
messages: {
|
|
199
|
+
requireTest: "doc.story(title) must be called inside a test() callback (e.g. test('...', () => { doc.story('Title'); ... })).",
|
|
200
|
+
requireTitle: "doc.story(title) requires a title argument."
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
create(context) {
|
|
204
|
+
let hasV1Import = false;
|
|
205
|
+
const namedFunctions = /* @__PURE__ */ new Map();
|
|
206
|
+
const testCallbackNames = /* @__PURE__ */ new Set();
|
|
207
|
+
const pendingDocStoryCalls = [];
|
|
208
|
+
function getContainingFunctionName(node) {
|
|
209
|
+
const ancestors = context.getSourceCode().getAncestors(node);
|
|
210
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
211
|
+
const ancestor = ancestors[i];
|
|
212
|
+
if (ancestor.type === "FunctionDeclaration" && ancestor.id) {
|
|
213
|
+
return ancestor.id.name;
|
|
214
|
+
}
|
|
215
|
+
if (ancestor.type === "VariableDeclarator") {
|
|
216
|
+
const declarator = ancestor;
|
|
217
|
+
if (declarator.id.type === "Identifier" && declarator.init && isFunctionNode2(declarator.init)) {
|
|
218
|
+
return declarator.id.name;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (ancestor.type === "Property") {
|
|
222
|
+
const prop = ancestor;
|
|
223
|
+
if (prop.key.type === "Identifier" && prop.value && isFunctionNode2(prop.value)) {
|
|
224
|
+
return prop.key.name;
|
|
225
|
+
}
|
|
226
|
+
if (prop.key.type === "Identifier" && prop.method) {
|
|
227
|
+
return prop.key.name;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
ImportDeclaration(node) {
|
|
235
|
+
if (node.source.value === V1_PACKAGE2) {
|
|
236
|
+
hasV1Import = true;
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
FunctionDeclaration(node) {
|
|
240
|
+
if (node.id) {
|
|
241
|
+
namedFunctions.set(node.id.name, node);
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
VariableDeclarator(node) {
|
|
245
|
+
if (node.id.type === "Identifier" && node.init && isFunctionNode2(node.init)) {
|
|
246
|
+
namedFunctions.set(node.id.name, node.init);
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
CallExpression(node) {
|
|
250
|
+
if (!hasV1Import) return;
|
|
251
|
+
if (isTestCallExpression(node)) {
|
|
252
|
+
for (const arg of node.arguments) {
|
|
253
|
+
if (arg.type === "Identifier") {
|
|
254
|
+
testCallbackNames.add(arg.name);
|
|
255
|
+
}
|
|
256
|
+
if (arg.type === "MemberExpression" && arg.property.type === "Identifier") {
|
|
257
|
+
testCallbackNames.add(arg.property.name);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
if (!isDocStoryCall2(node)) return;
|
|
263
|
+
if (node.arguments.length === 0) {
|
|
264
|
+
context.report({ node, messageId: "requireTitle" });
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (node.arguments.length >= 2) return;
|
|
268
|
+
if (insideTestCallback(node, context)) return;
|
|
269
|
+
const containingFunctionName = getContainingFunctionName(node);
|
|
270
|
+
pendingDocStoryCalls.push({ node, containingFunctionName });
|
|
271
|
+
},
|
|
272
|
+
"Program:exit"() {
|
|
273
|
+
for (const { node, containingFunctionName } of pendingDocStoryCalls) {
|
|
274
|
+
if (containingFunctionName && testCallbackNames.has(containingFunctionName)) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
context.report({ node, messageId: "requireTest" });
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
var require_test_context_for_doc_story_default = rule2;
|
|
284
|
+
|
|
285
|
+
// src/index.ts
|
|
286
|
+
var rules = {
|
|
287
|
+
"require-story-context-for-steps": require_story_context_for_steps_default,
|
|
288
|
+
"require-test-context-for-doc-story": require_test_context_for_doc_story_default
|
|
289
|
+
};
|
|
290
|
+
var configs = {
|
|
291
|
+
recommended: [
|
|
292
|
+
{
|
|
293
|
+
plugins: {
|
|
294
|
+
"executable-stories-playwright": { rules }
|
|
295
|
+
},
|
|
296
|
+
rules: {
|
|
297
|
+
"executable-stories-playwright/require-story-context-for-steps": "error",
|
|
298
|
+
"executable-stories-playwright/require-test-context-for-doc-story": "error"
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
]
|
|
302
|
+
};
|
|
303
|
+
var plugin = {
|
|
304
|
+
meta: {
|
|
305
|
+
name: "eslint-plugin-executable-stories-playwright",
|
|
306
|
+
version: "0.1.0"
|
|
307
|
+
},
|
|
308
|
+
rules,
|
|
309
|
+
configs
|
|
310
|
+
};
|
|
311
|
+
var src_default = plugin;
|
|
312
|
+
export {
|
|
313
|
+
configs,
|
|
314
|
+
src_default as default,
|
|
315
|
+
rules
|
|
316
|
+
};
|
|
317
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/rules/require-story-context-for-steps.ts","../src/rules/require-test-context-for-doc-story.ts","../src/index.ts"],"sourcesContent":["import type { Rule } from 'eslint';\nimport type {\n ArrowFunctionExpression,\n CallExpression,\n FunctionDeclaration,\n FunctionExpression,\n Node,\n Property,\n VariableDeclarator,\n} from 'estree';\n\n/** Current API uses story.given() etc. inside test(); no top-level given/when/then. */\nconst V1_PACKAGE = 'executable-stories-playwright';\n\nconst STEP_NAMES = new Set([\n 'given',\n 'when',\n 'then',\n 'and',\n 'but',\n 'arrange',\n 'act',\n 'assert',\n 'setup',\n 'context',\n 'execute',\n 'action',\n 'verify',\n]);\n\nconst STORY_MODIFIERS = new Set(['skip', 'only']);\n\nfunction isFunction(node: Node): boolean {\n return (\n node.type === 'FunctionExpression' ||\n node.type === 'ArrowFunctionExpression'\n );\n}\n\nfunction isFunctionNode(\n node: Node,\n): node is FunctionDeclaration | ArrowFunctionExpression | FunctionExpression {\n return (\n node.type === 'FunctionDeclaration' ||\n node.type === 'FunctionExpression' ||\n node.type === 'ArrowFunctionExpression'\n );\n}\n\nfunction isStoryCall(node: CallExpression): boolean {\n const { callee } = node;\n if (callee.type === 'Identifier') return callee.name === 'story';\n if (callee.type !== 'MemberExpression') return false;\n if (callee.object.type !== 'Identifier' || callee.object.name !== 'story')\n return false;\n return (\n callee.property.type === 'Identifier' &&\n STORY_MODIFIERS.has(callee.property.name)\n );\n}\n\nfunction isDocStoryCall(node: CallExpression): boolean {\n const { callee } = node;\n if (callee.type !== 'MemberExpression') return false;\n return (\n callee.object.type === 'Identifier' &&\n callee.object.name === 'doc' &&\n callee.property.type === 'Identifier' &&\n callee.property.name === 'story'\n );\n}\n\nfunction isStepCall(node: CallExpression): boolean {\n const { callee } = node;\n if (callee.type === 'Identifier') return STEP_NAMES.has(callee.name);\n if (callee.type !== 'MemberExpression') return false;\n if (callee.object.type !== 'Identifier') return false;\n if (callee.object.name !== 'steps' && callee.object.name !== 'step')\n return false;\n return (\n callee.property.type === 'Identifier' &&\n STEP_NAMES.has(callee.property.name)\n );\n}\n\nfunction insideStoryCallback(\n node: CallExpression,\n context: Rule.RuleContext,\n): boolean {\n const ancestors = context.getSourceCode().getAncestors(node);\n const functionAncestors = new Set(ancestors.filter(isFunction));\n\n for (const ancestor of ancestors) {\n if (ancestor.type !== 'CallExpression') continue;\n if (!isStoryCall(ancestor) && !isDocStoryCall(ancestor)) continue;\n for (const arg of ancestor.arguments) {\n if (arg && typeof arg === 'object' && functionAncestors.has(arg)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Require step functions (given/when/then/and/but and aliases) to be called inside story() or doc.story(..., callback).',\n recommended: true,\n },\n schema: [],\n messages: {\n requireStory:\n 'Step functions must be called inside story(...) (or doc.story(..., callback)).',\n },\n },\n create(context) {\n let hasV1Import = false;\n const namedFunctions = new Map<string, Node>();\n const storyCallbackNames = new Set<string>();\n const pendingStepCalls: Array<{\n node: CallExpression;\n containingFunctionName: string | null;\n }> = [];\n\n function getContainingFunctionName(node: CallExpression): string | null {\n const ancestors = context.getSourceCode().getAncestors(node);\n for (let i = ancestors.length - 1; i >= 0; i--) {\n const ancestor = ancestors[i];\n if (ancestor.type === 'FunctionDeclaration' && ancestor.id) {\n return ancestor.id.name;\n }\n if (ancestor.type === 'VariableDeclarator') {\n const declarator = ancestor as VariableDeclarator;\n if (\n declarator.id.type === 'Identifier' &&\n declarator.init &&\n isFunctionNode(declarator.init)\n ) {\n return declarator.id.name;\n }\n }\n // Check for object method definitions\n if (ancestor.type === 'Property') {\n const prop = ancestor as Property;\n if (\n prop.key.type === 'Identifier' &&\n prop.value &&\n isFunctionNode(prop.value)\n ) {\n return prop.key.name;\n }\n // Shorthand method syntax: { define() {} }\n if (prop.key.type === 'Identifier' && prop.method) {\n return prop.key.name;\n }\n }\n }\n return null;\n }\n\n return {\n ImportDeclaration(node) {\n if (node.source.value === V1_PACKAGE) {\n hasV1Import = true;\n }\n },\n FunctionDeclaration(node: FunctionDeclaration) {\n if (node.id) {\n namedFunctions.set(node.id.name, node);\n }\n },\n VariableDeclarator(node: VariableDeclarator) {\n if (\n node.id.type === 'Identifier' &&\n node.init &&\n isFunctionNode(node.init)\n ) {\n namedFunctions.set(node.id.name, node.init);\n }\n },\n CallExpression(node: CallExpression) {\n if (!hasV1Import) return;\n\n if (isStoryCall(node) || isDocStoryCall(node)) {\n for (const arg of node.arguments) {\n if (arg.type === 'Identifier') {\n storyCallbackNames.add(arg.name);\n }\n // Handle member expression callbacks like handlers.define\n if (\n arg.type === 'MemberExpression' &&\n arg.property.type === 'Identifier'\n ) {\n storyCallbackNames.add(arg.property.name);\n }\n }\n return;\n }\n\n if (!isStepCall(node)) return;\n if (insideStoryCallback(node, context)) return;\n\n const containingFunctionName = getContainingFunctionName(node);\n pendingStepCalls.push({ node, containingFunctionName });\n },\n 'Program:exit'() {\n for (const { node, containingFunctionName } of pendingStepCalls) {\n if (\n containingFunctionName &&\n storyCallbackNames.has(containingFunctionName)\n ) {\n continue;\n }\n context.report({ node, messageId: 'requireStory' });\n }\n },\n };\n },\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\nimport type {\n ArrowFunctionExpression,\n CallExpression,\n FunctionDeclaration,\n FunctionExpression,\n Node,\n Property,\n VariableDeclarator,\n} from 'estree';\n\n/** Legacy doc.story() is not used; current API uses story.init(testInfo) inside test(). */\nconst V1_PACKAGE = 'executable-stories-playwright';\n\nconst TEST_MODIFIERS = new Set(['only', 'skip', 'fixme', 'fail', 'slow']);\n\nfunction isFunction(node: Node): boolean {\n return (\n node.type === 'FunctionExpression' ||\n node.type === 'ArrowFunctionExpression'\n );\n}\n\nfunction isFunctionNode(\n node: Node,\n): node is FunctionDeclaration | ArrowFunctionExpression | FunctionExpression {\n return (\n node.type === 'FunctionDeclaration' ||\n node.type === 'FunctionExpression' ||\n node.type === 'ArrowFunctionExpression'\n );\n}\n\nfunction isDocStoryCall(node: CallExpression): boolean {\n const { callee } = node;\n if (callee.type !== 'MemberExpression') return false;\n return (\n callee.object.type === 'Identifier' &&\n callee.object.name === 'doc' &&\n callee.property.type === 'Identifier' &&\n callee.property.name === 'story'\n );\n}\n\nfunction isTestCallExpression(node: CallExpression): boolean {\n const { callee } = node;\n if (callee.type === 'Identifier') {\n return callee.name === 'test' || callee.name === 'it';\n }\n if (callee.type !== 'MemberExpression') return false;\n if (callee.object.type !== 'Identifier') return false;\n if (callee.object.name !== 'test' && callee.object.name !== 'it')\n return false;\n if (callee.property.type !== 'Identifier') return false;\n return TEST_MODIFIERS.has(callee.property.name);\n}\n\nfunction insideTestCallback(\n node: CallExpression,\n context: Rule.RuleContext,\n): boolean {\n const ancestors = context.getSourceCode().getAncestors(node);\n const functionAncestors = new Set(ancestors.filter(isFunction));\n\n for (const ancestor of ancestors) {\n if (ancestor.type !== 'CallExpression') continue;\n if (!isTestCallExpression(ancestor)) continue;\n for (const arg of ancestor.arguments) {\n if (arg && typeof arg === 'object' && functionAncestors.has(arg)) {\n return true;\n }\n }\n }\n return false;\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Require doc.story(title) to be called inside a test() or it() callback (framework-native pattern).',\n recommended: true,\n },\n schema: [],\n messages: {\n requireTest:\n \"doc.story(title) must be called inside a test() callback (e.g. test('...', () => { doc.story('Title'); ... })).\",\n requireTitle: 'doc.story(title) requires a title argument.',\n },\n },\n create(context) {\n let hasV1Import = false;\n const namedFunctions = new Map<string, Node>();\n const testCallbackNames = new Set<string>();\n const pendingDocStoryCalls: Array<{\n node: CallExpression;\n containingFunctionName: string | null;\n }> = [];\n\n function getContainingFunctionName(node: CallExpression): string | null {\n const ancestors = context.getSourceCode().getAncestors(node);\n for (let i = ancestors.length - 1; i >= 0; i--) {\n const ancestor = ancestors[i];\n if (ancestor.type === 'FunctionDeclaration' && ancestor.id) {\n return ancestor.id.name;\n }\n if (ancestor.type === 'VariableDeclarator') {\n const declarator = ancestor as VariableDeclarator;\n if (\n declarator.id.type === 'Identifier' &&\n declarator.init &&\n isFunctionNode(declarator.init)\n ) {\n return declarator.id.name;\n }\n }\n // Check for object method definitions\n if (ancestor.type === 'Property') {\n const prop = ancestor as Property;\n if (\n prop.key.type === 'Identifier' &&\n prop.value &&\n isFunctionNode(prop.value)\n ) {\n return prop.key.name;\n }\n // Shorthand method syntax: { run() {} }\n if (prop.key.type === 'Identifier' && prop.method) {\n return prop.key.name;\n }\n }\n }\n return null;\n }\n\n return {\n ImportDeclaration(node) {\n if (node.source.value === V1_PACKAGE) {\n hasV1Import = true;\n }\n },\n FunctionDeclaration(node: FunctionDeclaration) {\n if (node.id) {\n namedFunctions.set(node.id.name, node);\n }\n },\n VariableDeclarator(node: VariableDeclarator) {\n if (\n node.id.type === 'Identifier' &&\n node.init &&\n isFunctionNode(node.init)\n ) {\n namedFunctions.set(node.id.name, node.init);\n }\n },\n CallExpression(node: CallExpression) {\n if (!hasV1Import) return;\n\n if (isTestCallExpression(node)) {\n for (const arg of node.arguments) {\n if (arg.type === 'Identifier') {\n testCallbackNames.add(arg.name);\n }\n // Handle member expression callbacks like handlers.run\n if (\n arg.type === 'MemberExpression' &&\n arg.property.type === 'Identifier'\n ) {\n testCallbackNames.add(arg.property.name);\n }\n }\n return;\n }\n\n if (!isDocStoryCall(node)) return;\n\n if (node.arguments.length === 0) {\n context.report({ node, messageId: 'requireTitle' });\n return;\n }\n if (node.arguments.length >= 2) return;\n\n if (insideTestCallback(node, context)) return;\n\n const containingFunctionName = getContainingFunctionName(node);\n pendingDocStoryCalls.push({ node, containingFunctionName });\n },\n 'Program:exit'() {\n for (const { node, containingFunctionName } of pendingDocStoryCalls) {\n if (\n containingFunctionName &&\n testCallbackNames.has(containingFunctionName)\n ) {\n continue;\n }\n context.report({ node, messageId: 'requireTest' });\n }\n },\n };\n },\n};\n\nexport default rule;\n","import type { ESLint, Linter } from 'eslint';\nimport requireStoryContextForSteps from './rules/require-story-context-for-steps.js';\nimport requireTestContextForDocStory from './rules/require-test-context-for-doc-story.js';\n\nconst rules = {\n 'require-story-context-for-steps': requireStoryContextForSteps,\n 'require-test-context-for-doc-story': requireTestContextForDocStory,\n};\n\nconst configs: Record<string, Linter.Config[]> = {\n recommended: [\n {\n plugins: {\n 'executable-stories-playwright': { rules },\n },\n rules: {\n 'executable-stories-playwright/require-story-context-for-steps':\n 'error',\n 'executable-stories-playwright/require-test-context-for-doc-story':\n 'error',\n },\n },\n ],\n};\n\nconst plugin: ESLint.Plugin = {\n meta: {\n name: 'eslint-plugin-executable-stories-playwright',\n version: '0.1.0',\n },\n rules,\n configs,\n};\n\nexport default plugin;\nexport { rules, configs };\n"],"mappings":"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","names":["V1_PACKAGE","isFunction","isFunctionNode","isDocStoryCall","rule"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "eslint-plugin-executable-stories-playwright",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "ESLint rules for executable-stories-playwright: step context, doc.story in test/it",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"sideEffects": false,
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.cjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"type-check": "tsc --noEmit",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest watch",
|
|
26
|
+
"lint": "eslint .",
|
|
27
|
+
"clean": "rm -rf dist",
|
|
28
|
+
"quality": "pnpm type-check && pnpm test && pnpm lint"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"eslint",
|
|
32
|
+
"eslint-plugin",
|
|
33
|
+
"playwright",
|
|
34
|
+
"executable-stories",
|
|
35
|
+
"bdd"
|
|
36
|
+
],
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"eslint": ">=9"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/eslint": "^9.6.1",
|
|
43
|
+
"@types/estree": "^1.0.8",
|
|
44
|
+
"@types/node": "^25.2.1",
|
|
45
|
+
"eslint": "^9.39.2",
|
|
46
|
+
"eslint-config-executable-stories": "workspace:*",
|
|
47
|
+
"tsup": "^8.5.1",
|
|
48
|
+
"typescript": "^5.9.3",
|
|
49
|
+
"vitest": "^4.0.18"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=22"
|
|
53
|
+
}
|
|
54
|
+
}
|