genat-mcp 2.3.1 → 2.3.2

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.
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Strip accidental Gherkin prefix and rewrite scenario() paths for pytest-bdd step modules.
3
+ */
4
+ import { rewriteScenarioFeaturePaths } from './fix-pytest-bdd-scenario-path.js';
5
+ import { resolvePytestBddPaths } from './pytest-bdd-paths.js';
6
+ import { stripLeadingGherkinFromPythonSteps } from './strip-gherkin-from-python-steps.js';
7
+
8
+ /**
9
+ * @param {string} root - project root
10
+ * @param {string} stepDefCode
11
+ * @param {{ featureRel?: string, stepRel?: string }} [paths] - optional overrides (e.g. from suggestedPaths) for where feature and step files are written
12
+ * @returns {string}
13
+ */
14
+ export function finalizePytestBddStepDef(root, stepDefCode, paths) {
15
+ if (!stepDefCode || typeof stepDefCode !== 'string') return stepDefCode;
16
+ const pb = resolvePytestBddPaths(root);
17
+ const featureRel = paths?.featureRel ?? pb.featureRel;
18
+ const stepRel = paths?.stepRel ?? pb.stepRel;
19
+ let s = stripLeadingGherkinFromPythonSteps(stepDefCode);
20
+ s = rewriteScenarioFeaturePaths(s, root, featureRel, stepRel);
21
+ return s;
22
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Rewrite pytest-bdd scenario() / @scenario() first argument so the .feature path is correct
3
+ * relative to the step file directory (matches where write-files places the feature).
4
+ */
5
+ import { dirname, join, relative, resolve } from 'path';
6
+
7
+ /**
8
+ * @param {string} code
9
+ * @param {string} root - project root (absolute or resolved)
10
+ * @param {string} featureRel - path to .feature relative to project root (posix-style segments)
11
+ * @param {string} stepRel - path to step .py relative to project root
12
+ * @returns {string}
13
+ */
14
+ export function rewriteScenarioFeaturePaths(code, root, featureRel, stepRel) {
15
+ if (!code || typeof code !== 'string') return code;
16
+ const rootAbs = resolve(root);
17
+ const featureAbs = join(rootAbs, featureRel);
18
+ const stepDir = dirname(join(rootAbs, stepRel));
19
+ let posixRel = relative(stepDir, featureAbs);
20
+ if (!posixRel.startsWith('.')) {
21
+ posixRel = posixRel ? `./${posixRel}` : '.';
22
+ }
23
+ posixRel = posixRel.replace(/\\/g, '/');
24
+
25
+ return code.replace(/(@scenario|scenario)\s*\(\s*(['"])([^'"]*\.feature)\2/gi, (_m, decorator, quote) => {
26
+ return `${decorator}(${quote}${posixRel}${quote}`;
27
+ });
28
+ }
package/index.js CHANGED
@@ -11,6 +11,8 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
11
11
  import { z } from 'zod';
12
12
  import { detectFramework } from './detect-framework.js';
13
13
  import { detectLogin } from './detect-login.js';
14
+ import { finalizePytestBddStepDef } from './finalize-pytest-bdd-steps.js';
15
+ import { resolvePytestBddPaths } from './pytest-bdd-paths.js';
14
16
  import { writeGeneratedFiles } from './write-files.js';
15
17
  import { loadOclcAccessibilityContext } from './oclc-context.js';
16
18
 
@@ -56,7 +58,7 @@ function insecureHttpsFetch(url, { method = 'GET', headers = {}, body, signal })
56
58
  const server = new McpServer(
57
59
  {
58
60
  name: 'GenAT',
59
- version: '2.3.1',
61
+ version: '2.3.2',
60
62
  },
61
63
  {
62
64
  capabilities: {
@@ -269,6 +271,18 @@ server.registerTool(
269
271
  };
270
272
  }
271
273
 
274
+ const bddResolved = (data?.bddFramework ?? framework.bddFramework ?? 'none').toString().toLowerCase();
275
+ if (framework.scriptType === 'python' && bddResolved === 'pytest-bdd' && resolvedPath) {
276
+ const pb = resolvePytestBddPaths(resolvedPath);
277
+ const sp = data.suggestedPaths || {};
278
+ const pathOverrides = {
279
+ featureRel: sp.feature || pb.featureRel,
280
+ stepRel: sp.stepDef || pb.stepRel,
281
+ };
282
+ if (data.stepDefCode) data.stepDefCode = finalizePytestBddStepDef(resolvedPath, data.stepDefCode, pathOverrides);
283
+ if (data.testCode) data.testCode = finalizePytestBddStepDef(resolvedPath, data.testCode, pathOverrides);
284
+ }
285
+
272
286
  if (writeToProject && resolvedPath && data) {
273
287
  if (data.bddFramework == null) data.bddFramework = framework.bddFramework;
274
288
  try {
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "genat-mcp",
3
- "version": "2.3.1",
3
+ "version": "2.3.2",
4
4
  "mcpName": "io.github.asokans@oclc.org/genat",
5
5
  "description": "MCP server GenAT: generate accessibility tests via n8n workflow (url + project folder)",
6
6
  "type": "module",
7
7
  "main": "index.js",
8
8
  "bin": { "genat-mcp": "index.js" },
9
9
  "engines": { "node": ">=20" },
10
- "files": ["index.js", "detect-framework.js", "detect-login.js", "write-files.js", "strip-gherkin-from-python-steps.js", "oclc-context.js", "oclc-accessibility-confluence-context.md", "run-genat.mjs", "rules/genat-mcp.mdc"],
10
+ "files": ["index.js", "detect-framework.js", "detect-login.js", "write-files.js", "strip-gherkin-from-python-steps.js", "finalize-pytest-bdd-steps.js", "fix-pytest-bdd-scenario-path.js", "pytest-bdd-paths.js", "oclc-context.js", "oclc-accessibility-confluence-context.md", "run-genat.mjs", "rules/genat-mcp.mdc"],
11
11
  "keywords": ["mcp", "accessibility", "playwright", "n8n", "model-context-protocol", "a11y", "testing"],
12
12
  "repository": {
13
13
  "type": "git",
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Resolve pytest-bdd feature / step / spec paths from project layout (same rules as write-files).
3
+ */
4
+ import { existsSync } from 'fs';
5
+ import { join } from 'path';
6
+
7
+ const DEFAULT_DIR = 'tests/accessibility';
8
+
9
+ /**
10
+ * @param {string} root
11
+ * @returns {{ featureRel: string, stepRel: string, specRel: string }}
12
+ */
13
+ export function resolvePytestBddPaths(root) {
14
+ let featureRel;
15
+ if (existsSync(join(root, 'features'))) featureRel = join('features', 'accessibility.feature');
16
+ else if (existsSync(join(root, 'tests', 'features'))) featureRel = join('tests', 'features', 'accessibility.feature');
17
+ else featureRel = join('features', 'accessibility.feature');
18
+
19
+ let stepRel;
20
+ if (existsSync(join(root, 'step_defs'))) stepRel = join('step_defs', 'accessibility_steps.py');
21
+ else if (existsSync(join(root, 'steps'))) stepRel = join('steps', 'accessibility_steps.py');
22
+ else if (existsSync(join(root, 'tests', 'step_defs'))) stepRel = join('tests', 'step_defs', 'accessibility_steps.py');
23
+ else if (existsSync(join(root, 'tests', 'steps'))) stepRel = join('tests', 'steps', 'accessibility_steps.py');
24
+ else stepRel = join('step_defs', 'accessibility_steps.py');
25
+
26
+ const specRel = join(DEFAULT_DIR, 'accessibility_test.py');
27
+ return { featureRel, stepRel, specRel };
28
+ }
package/write-files.js CHANGED
@@ -2,34 +2,13 @@
2
2
  * Write generated accessibility test files into the project folder.
3
3
  * Uses suggested paths from the workflow response when present.
4
4
  */
5
- import { existsSync } from 'fs';
6
5
  import { writeFile, mkdir } from 'fs/promises';
7
6
  import { dirname, join, resolve } from 'path';
8
- import { stripLeadingGherkinFromPythonSteps } from './strip-gherkin-from-python-steps.js';
7
+ import { finalizePytestBddStepDef } from './finalize-pytest-bdd-steps.js';
8
+ import { resolvePytestBddPaths } from './pytest-bdd-paths.js';
9
9
 
10
10
  const DEFAULT_DIR = 'tests/accessibility';
11
11
 
12
- /**
13
- * @param {string} root
14
- * @returns {{ featureRel: string, stepRel: string, specRel: string }}
15
- */
16
- function resolvePytestBddPaths(root) {
17
- let featureRel;
18
- if (existsSync(join(root, 'features'))) featureRel = join('features', 'accessibility.feature');
19
- else if (existsSync(join(root, 'tests', 'features'))) featureRel = join('tests', 'features', 'accessibility.feature');
20
- else featureRel = join('features', 'accessibility.feature');
21
-
22
- let stepRel;
23
- if (existsSync(join(root, 'step_defs'))) stepRel = join('step_defs', 'accessibility_steps.py');
24
- else if (existsSync(join(root, 'steps'))) stepRel = join('steps', 'accessibility_steps.py');
25
- else if (existsSync(join(root, 'tests', 'step_defs'))) stepRel = join('tests', 'step_defs', 'accessibility_steps.py');
26
- else if (existsSync(join(root, 'tests', 'steps'))) stepRel = join('tests', 'steps', 'accessibility_steps.py');
27
- else stepRel = join('step_defs', 'accessibility_steps.py');
28
-
29
- const specRel = join(DEFAULT_DIR, 'accessibility_test.py');
30
- return { featureRel, stepRel, specRel };
31
- }
32
-
33
12
  /**
34
13
  * @param {string} parentProjectFolder - project root path
35
14
  * @param {{ testCode?: string, scriptType?: string, bddFramework?: string, featureFile?: string, stepDefCode?: string, pageObjectCode?: string, suggestedPaths?: Record<string, string> }} data - workflow response
@@ -55,10 +34,14 @@ export async function writeGeneratedFiles(parentProjectFolder, data) {
55
34
 
56
35
  if (usePytestBddLayout) {
57
36
  const pb = resolvePytestBddPaths(root);
37
+ const pathOverrides = {
38
+ featureRel: suggested.feature || pb.featureRel,
39
+ stepRel: suggested.stepDef || pb.stepRel,
40
+ };
58
41
  const stepDefStripped = data.stepDefCode
59
- ? stripLeadingGherkinFromPythonSteps(data.stepDefCode)
42
+ ? finalizePytestBddStepDef(root, data.stepDefCode, pathOverrides)
60
43
  : '';
61
- const testStripped = data.testCode ? stripLeadingGherkinFromPythonSteps(data.testCode) : '';
44
+ const testStripped = data.testCode ? finalizePytestBddStepDef(root, data.testCode, pathOverrides) : '';
62
45
  const dup =
63
46
  testStripped &&
64
47
  stepDefStripped &&