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.
- package/finalize-pytest-bdd-steps.js +22 -0
- package/fix-pytest-bdd-scenario-path.js +28 -0
- package/index.js +15 -1
- package/package.json +2 -2
- package/pytest-bdd-paths.js +28 -0
- package/write-files.js +8 -25
|
@@ -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.
|
|
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.
|
|
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 {
|
|
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
|
-
?
|
|
42
|
+
? finalizePytestBddStepDef(root, data.stepDefCode, pathOverrides)
|
|
60
43
|
: '';
|
|
61
|
-
const testStripped = data.testCode ?
|
|
44
|
+
const testStripped = data.testCode ? finalizePytestBddStepDef(root, data.testCode, pathOverrides) : '';
|
|
62
45
|
const dup =
|
|
63
46
|
testStripped &&
|
|
64
47
|
stepDefStripped &&
|