@gherkle/runner 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.
Files changed (59) hide show
  1. package/README.md +482 -0
  2. package/bin/gherkle-runner.js +111 -0
  3. package/dist/__tests__/agent-executor-selection.test.d.ts +15 -0
  4. package/dist/__tests__/agent-executor-selection.test.d.ts.map +1 -0
  5. package/dist/__tests__/agent-executor-selection.test.js +192 -0
  6. package/dist/__tests__/agent-executor-selection.test.js.map +1 -0
  7. package/dist/__tests__/envelope-mapper.test.d.ts +17 -0
  8. package/dist/__tests__/envelope-mapper.test.d.ts.map +1 -0
  9. package/dist/__tests__/envelope-mapper.test.js +217 -0
  10. package/dist/__tests__/envelope-mapper.test.js.map +1 -0
  11. package/dist/__tests__/gherkin-parser.test.d.ts +2 -0
  12. package/dist/__tests__/gherkin-parser.test.d.ts.map +1 -0
  13. package/dist/__tests__/gherkin-parser.test.js +375 -0
  14. package/dist/__tests__/gherkin-parser.test.js.map +1 -0
  15. package/dist/__tests__/integration-fork.test.d.ts +16 -0
  16. package/dist/__tests__/integration-fork.test.d.ts.map +1 -0
  17. package/dist/__tests__/integration-fork.test.js +141 -0
  18. package/dist/__tests__/integration-fork.test.js.map +1 -0
  19. package/dist/__tests__/tmpdir-materialization.test.d.ts +18 -0
  20. package/dist/__tests__/tmpdir-materialization.test.d.ts.map +1 -0
  21. package/dist/__tests__/tmpdir-materialization.test.js +177 -0
  22. package/dist/__tests__/tmpdir-materialization.test.js.map +1 -0
  23. package/dist/agent.d.ts +71 -0
  24. package/dist/agent.d.ts.map +1 -0
  25. package/dist/agent.js +312 -0
  26. package/dist/agent.js.map +1 -0
  27. package/dist/config.d.ts +31 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +165 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/frameworks/cucumber-subprocess.d.ts +129 -0
  32. package/dist/frameworks/cucumber-subprocess.d.ts.map +1 -0
  33. package/dist/frameworks/cucumber-subprocess.js +469 -0
  34. package/dist/frameworks/cucumber-subprocess.js.map +1 -0
  35. package/dist/frameworks/playwright.d.ts +22 -0
  36. package/dist/frameworks/playwright.d.ts.map +1 -0
  37. package/dist/frameworks/playwright.js +234 -0
  38. package/dist/frameworks/playwright.js.map +1 -0
  39. package/dist/gherkin-parser.d.ts +22 -0
  40. package/dist/gherkin-parser.d.ts.map +1 -0
  41. package/dist/gherkin-parser.js +148 -0
  42. package/dist/gherkin-parser.js.map +1 -0
  43. package/dist/index.d.ts +10 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +31 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/step-registry.d.ts +30 -0
  48. package/dist/step-registry.d.ts.map +1 -0
  49. package/dist/step-registry.js +160 -0
  50. package/dist/step-registry.js.map +1 -0
  51. package/dist/types.d.ts +117 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +6 -0
  54. package/dist/types.js.map +1 -0
  55. package/dist/workers/cucumber-worker.d.ts +29 -0
  56. package/dist/workers/cucumber-worker.d.ts.map +1 -0
  57. package/dist/workers/cucumber-worker.js +191 -0
  58. package/dist/workers/cucumber-worker.js.map +1 -0
  59. package/package.json +74 -0
package/dist/config.js ADDED
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ /**
3
+ * Configuration loading and validation for @gherkle/runner
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.loadConfig = loadConfig;
40
+ exports.generateSampleConfig = generateSampleConfig;
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ const DEFAULT_CONFIG = {
44
+ apiUrl: 'https://app.gherkle.com',
45
+ name: 'gherkle-runner',
46
+ labels: [],
47
+ framework: 'playwright',
48
+ featuresPath: './features',
49
+ stepDefinitionsPath: './steps',
50
+ supportPath: './support',
51
+ browsers: ['chromium'],
52
+ headless: true,
53
+ testTimeout: 30000,
54
+ stepTimeout: 10000,
55
+ screenshots: 'on-failure',
56
+ video: 'never',
57
+ env: {},
58
+ };
59
+ const CONFIG_FILE_NAMES = [
60
+ 'gherkle-runner.config.js',
61
+ 'gherkle-runner.config.mjs',
62
+ 'gherkle-runner.config.ts',
63
+ '.gherklerc.js',
64
+ '.gherklerc.json',
65
+ ];
66
+ /**
67
+ * Load configuration from file and environment
68
+ */
69
+ function loadConfig(cliOptions = {}) {
70
+ let fileConfig = {};
71
+ // Try to find and load config file
72
+ for (const filename of CONFIG_FILE_NAMES) {
73
+ const configPath = path.resolve(process.cwd(), filename);
74
+ if (fs.existsSync(configPath)) {
75
+ try {
76
+ if (filename.endsWith('.json')) {
77
+ fileConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
78
+ }
79
+ else {
80
+ // For JS/TS files, we'd need to use require or dynamic import
81
+ // For now, just log that we found it
82
+ console.log(`Found config file: ${configPath}`);
83
+ try {
84
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
85
+ const loaded = require(configPath);
86
+ fileConfig = loaded.default || loaded;
87
+ }
88
+ catch {
89
+ console.warn(`Could not load ${configPath}, using defaults`);
90
+ }
91
+ }
92
+ break;
93
+ }
94
+ catch (err) {
95
+ console.warn(`Error loading ${configPath}:`, err);
96
+ }
97
+ }
98
+ }
99
+ // Environment variables override
100
+ const envConfig = {};
101
+ if (process.env.GHERKLE_TOKEN) {
102
+ envConfig.token = process.env.GHERKLE_TOKEN;
103
+ }
104
+ if (process.env.GHERKLE_API_URL) {
105
+ envConfig.apiUrl = process.env.GHERKLE_API_URL;
106
+ }
107
+ if (process.env.GHERKLE_RUNNER_NAME) {
108
+ envConfig.name = process.env.GHERKLE_RUNNER_NAME;
109
+ }
110
+ // Merge configs: defaults < file < env < cli (omit undefined cli so defaults are kept)
111
+ const definedCli = Object.fromEntries(Object.entries(cliOptions).filter(([, v]) => v !== undefined));
112
+ const config = {
113
+ ...DEFAULT_CONFIG,
114
+ ...fileConfig,
115
+ ...envConfig,
116
+ ...definedCli,
117
+ };
118
+ // Validate required fields
119
+ if (!config.token) {
120
+ throw new Error('Token is required. Provide via --token, GHERKLE_TOKEN env var, or config file.');
121
+ }
122
+ return config;
123
+ }
124
+ /**
125
+ * Generate a sample config file
126
+ */
127
+ function generateSampleConfig() {
128
+ return `// gherkle-runner.config.js
129
+ module.exports = {
130
+ // Connection (required)
131
+ token: process.env.GHERKLE_TOKEN,
132
+
133
+ // Runner identification
134
+ name: 'my-runner',
135
+ labels: ['local', 'dev'],
136
+
137
+ // Test framework
138
+ framework: 'playwright', // 'playwright' | 'cypress' | 'cucumber'
139
+
140
+ // Paths
141
+ featuresPath: './features',
142
+ stepDefinitionsPath: './steps',
143
+ supportPath: './support',
144
+
145
+ // Browser configuration (E2E only)
146
+ browsers: ['chromium'],
147
+ headless: true,
148
+
149
+ // Timeouts
150
+ testTimeout: 30000,
151
+ stepTimeout: 10000,
152
+
153
+ // Artifacts
154
+ screenshots: 'on-failure', // 'always' | 'on-failure' | 'never'
155
+ video: 'never',
156
+
157
+ // Environment
158
+ baseUrl: process.env.BASE_URL || 'http://localhost:3000',
159
+ env: {
160
+ API_URL: process.env.API_URL,
161
+ },
162
+ };
163
+ `;
164
+ }
165
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * CucumberSubprocessExecutor — runs AI-generated step defs via a forked
3
+ * @cucumber/cucumber child process and maps its envelope stream into the
4
+ * runner's existing scenario/step/run events.
5
+ *
6
+ * Why a subprocess (not in-process)?
7
+ * 1. Cancel: SIGTERM the child cleanly; cannot kill an in-process loop.
8
+ * 2. Isolation: user-supplied AI-generated code can crash or leak module
9
+ * state. A long-lived runner cannot afford either.
10
+ * 3. Module cache: Cucumber.js registers hooks at module load. Two
11
+ * consecutive runs in the same process would double-register.
12
+ *
13
+ * Tmpdir layout written by prepareTmpdir():
14
+ *
15
+ * <tmpdir>/
16
+ * features/<feature_id>.feature
17
+ * steps/<suggested_filename>
18
+ * support/world.<js|ts>
19
+ * cucumber.cjs
20
+ * .use-ts (only when language === 'typescript')
21
+ *
22
+ * The path layout matters: the AI prompt emits
23
+ * import { ICustomWorld } from '../support/world'
24
+ * relative to a step-def file. With step-defs at steps/* and the World at
25
+ * support/world.*, that import resolves cleanly with no source rewriting.
26
+ *
27
+ * Envelope → runner event mapping:
28
+ *
29
+ * envelope.testCase → record testCaseId → pickleId
30
+ * envelope.pickle → record pickleId → { name, uri }
31
+ * envelope.testCaseStarted → onScenarioStart(pickleId, name)
32
+ * envelope.testStepFinished → onStepComplete(pickleId, StepResult)
33
+ * envelope.testCaseFinished → onScenarioComplete(pickleId, status, durationMs, error?)
34
+ *
35
+ * Cancel:
36
+ * - SIGTERM, then SIGKILL after 5s if the child is still alive.
37
+ * - Cleanup (rm -rf tmpdir) runs on child 'exit', so it fires for
38
+ * completion, cancellation, AND crash.
39
+ *
40
+ * Lifecycle state machine (the cross-process flow that bit us in review):
41
+ *
42
+ * parent (agent.ts) child (cucumber-worker)
43
+ * ─────────────────── ─────────────────────────
44
+ * prepareTmpdir()
45
+ * fork(worker, [tmpdir]) ───────► boot, require @cucumber/cucumber
46
+ * load tmpdir/cucumber.cjs
47
+ * runCucumber({onMessage})
48
+ * │
49
+ * envelope ◄─────────┤
50
+ * envelope ◄─────────┤
51
+ * envelope ◄─────────┤
52
+ * ... │
53
+ * envelope ◄─────────┘ runCucumber resolves
54
+ * {kind:'done', success} ◄── process.send({...}, () => exit)
55
+ * │
56
+ * child.on('exit', code, signal) ◄──────────── child exits
57
+ * │
58
+ * ├─ killTimer cleared
59
+ * ├─ cleanupTmpdir() (best-effort)
60
+ * ├─ if __fatal stashed → reject(fatal)
61
+ * ├─ if signal SIGTERM/SIGKILL → resolve(partial) ← cancel path
62
+ * └─ else → resolve(all scenarios)
63
+ *
64
+ * cancel() called any time before exit:
65
+ * SIGTERM → killTimer schedules SIGKILL(+5s) → exit fires above
66
+ */
67
+ import { FeatureContent, GeneratedStepDef, ScenarioResult, StepResult, TestStatus } from '../types';
68
+ export interface CucumberExecutorCallbacks {
69
+ onScenarioStart: (scenarioId: string, scenarioName: string, featureName: string) => void;
70
+ onStepComplete: (scenarioId: string, step: StepResult) => void;
71
+ onScenarioComplete: (scenarioId: string, status: TestStatus, durationMs: number, error?: string) => void;
72
+ }
73
+ export declare class CucumberSubprocessExecutor {
74
+ private child;
75
+ private tmpdir;
76
+ private killTimer;
77
+ private readonly workerPathOverride?;
78
+ constructor(opts?: {
79
+ workerPath?: string;
80
+ });
81
+ executeAll(features: FeatureContent[], stepDefinitions: GeneratedStepDef[], env: Record<string, string>, callbacks: CucumberExecutorCallbacks): Promise<ScenarioResult[]>;
82
+ /**
83
+ * Cancel an in-flight run. Idempotent; safe to call after the child has
84
+ * already exited.
85
+ */
86
+ cancel(): void;
87
+ prepareTmpdir(features: FeatureContent[], stepDefinitions: GeneratedStepDef[]): Promise<string>;
88
+ private cleanupTmpdir;
89
+ /**
90
+ * A minimal World that exposes `this.page`, `this.context`, and `this.env`
91
+ * to step definitions. AI-generated code expects these via the
92
+ * `ICustomWorld` import. The TS variant exports `ICustomWorld` as a type
93
+ * alias so the prompt's `import { ICustomWorld } from '../support/world'`
94
+ * compiles; the JS variant just exports the constructor.
95
+ *
96
+ * Browser is launched per scenario (cheap with chromium) so a crashing
97
+ * step does not poison subsequent scenarios.
98
+ */
99
+ private worldShim;
100
+ private cucumberConfig;
101
+ }
102
+ /**
103
+ * Translates the Cucumber.js envelope stream into runner-shaped events.
104
+ *
105
+ * Cucumber emits envelopes in topological order: `pickle` and `testCase`
106
+ * envelopes arrive BEFORE their corresponding `testCaseStarted`, so the
107
+ * id→pickle and testCase→pickle lookup maps are always populated by the
108
+ * time we need them. Out-of-order arrivals (which shouldn't happen but
109
+ * are guarded against) silently skip rather than crash.
110
+ *
111
+ * Lookup chain for any in-flight step/scenario event:
112
+ * testStepFinished.testCaseStartedId
113
+ * → startedToTestCase → testCase.id
114
+ * → testCaseToPickle → pickle.id (the scenario identity we emit)
115
+ * → pickles → { name, uri (feature path inside tmpdir) }
116
+ * → featureIdFromUri → DB feature_id
117
+ * → featureIdToOriginalName → DB path (what server-side map keys on)
118
+ *
119
+ * Status rollup at testCaseFinished:
120
+ * any step failed → 'failed'
121
+ * else if every step skipped → 'skipped'
122
+ * else → 'passed'
123
+ */
124
+ export interface EnvelopeMapper {
125
+ handle(envelope: unknown): void;
126
+ getScenarios(): ScenarioResult[];
127
+ }
128
+ export declare function createEnvelopeMapper(featureIdToOriginalName: Map<string, string>, callbacks: CucumberExecutorCallbacks): EnvelopeMapper;
129
+ //# sourceMappingURL=cucumber-subprocess.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cucumber-subprocess.d.ts","sourceRoot":"","sources":["../../src/frameworks/cucumber-subprocess.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AAMH,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,UAAU,EACV,UAAU,EACX,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,yBAAyB;IACxC,eAAe,EAAE,CACf,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,KAChB,IAAI,CAAC;IACV,cAAc,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IAC/D,kBAAkB,EAAE,CAClB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,MAAM,KACX,IAAI,CAAC;CACX;AAID,qBAAa,0BAA0B;IACrC,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,SAAS,CAA+B;IAIhD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAS;gBAEjC,IAAI,GAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAO;IAIxC,UAAU,CACd,QAAQ,EAAE,cAAc,EAAE,EAC1B,eAAe,EAAE,gBAAgB,EAAE,EACnC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,SAAS,EAAE,yBAAyB,GACnC,OAAO,CAAC,cAAc,EAAE,CAAC;IA4E5B;;;OAGG;IACH,MAAM,IAAI,IAAI;IAuBR,aAAa,CACjB,QAAQ,EAAE,cAAc,EAAE,EAC1B,eAAe,EAAE,gBAAgB,EAAE,GAClC,OAAO,CAAC,MAAM,CAAC;YAuDJ,aAAa;IAY3B;;;;;;;;;OASG;IACH,OAAO,CAAC,SAAS;IA+EjB,OAAO,CAAC,cAAc;CAYvB;AAID;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,YAAY,IAAI,cAAc,EAAE,CAAC;CAClC;AAED,wBAAgB,oBAAoB,CAClC,uBAAuB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5C,SAAS,EAAE,yBAAyB,GACnC,cAAc,CAqFhB"}