Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions packages/mcp/scripts/__tests__/extract-accessibility.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import { describe, it, expect } from "vitest";
import { cleanUpAccessibilityContent, run } from "../extract-accessibility.js";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const outputDir = path.join(__dirname, "../../dist/generated/accessibility/");

describe("cleanUpAccessibilityContent", () => {
describe("UsageGuidelines extraction", () => {
it("should extract numbered guidelines from quoted strings", () => {
const content = `<UsageGuidelines guidelines={[
"Use labels for all interactive elements",
"Ensure color contrast meets WCAG AA"
]} />`;

const result = cleanUpAccessibilityContent(content);

expect(result).toBe(
"1. Use labels for all interactive elements\n2. Ensure color contrast meets WCAG AA"
);
});

it("should extract guidelines wrapped in React fragments", () => {
const content = `<UsageGuidelines guidelines={[
<>First guideline text</>,
<>Second guideline text</>
]} />`;

const result = cleanUpAccessibilityContent(content);

expect(result).toBe("1. First guideline text\n2. Second guideline text");
});

it("should convert <code> tags to backticks within guidelines", () => {
const content = `<UsageGuidelines guidelines={[
<>Use the <code>aria-label</code> prop for accessibility</>
]} />`;

const result = cleanUpAccessibilityContent(content);

expect(result).toContain("`aria-label`");
expect(result).not.toContain("<code>");
expect(result).not.toContain("</code>");
});

it("should handle mixed quoted strings and React fragments", () => {
const content = `<UsageGuidelines guidelines={[
"Simple string guideline",
<>Fragment with <code>code</code> inside</>
]} />`;

const result = cleanUpAccessibilityContent(content);

expect(result).toContain("1. Simple string guideline");
expect(result).toContain("2. Fragment with `code` inside");
});

it("should number guidelines sequentially", () => {
const content = `<UsageGuidelines guidelines={[
"First",
"Second",
"Third"
]} />`;

const result = cleanUpAccessibilityContent(content);

expect(result).toBe("1. First\n2. Second\n3. Third");
});

it("should skip whitespace-only React fragment guidelines", () => {
const content = `<UsageGuidelines guidelines={[
<>Valid guideline</>,
<> </>,
<>Another valid guideline</>
]} />`;

const result = cleanUpAccessibilityContent(content);

expect(result).toBe("1. Valid guideline\n2. Another valid guideline");
});
});

describe("fallback cleanup", () => {
it("should remove UsageGuidelines JSX components", () => {
const content = `Some text before\n<UsageGuidelines someProp="value" />\nSome text after`;

const result = cleanUpAccessibilityContent(content);

expect(result).not.toContain("UsageGuidelines");
expect(result).toContain("Some text before");
expect(result).toContain("Some text after");
});

it("should replace <code> tags with backticks in fallback mode", () => {
const content = `Use the <code>role</code> attribute for elements.`;

const result = cleanUpAccessibilityContent(content);

expect(result).toBe("Use the `role` attribute for elements.");
});

it("should remove React fragment tags in fallback mode", () => {
const content = `<>Some content inside fragments</>`;

const result = cleanUpAccessibilityContent(content);

expect(result).toBe("Some content inside fragments");
});

it("should remove lines with only brackets or punctuation", () => {
const content = `Valid content\n [\n ]\nMore valid content`;

const result = cleanUpAccessibilityContent(content);

expect(result).not.toMatch(/^\s*[\[\]]\s*$/m);
expect(result).toContain("Valid content");
expect(result).toContain("More valid content");
});

it("should reduce multiple empty lines to double newlines", () => {
const content = `First paragraph\n\n\n\nSecond paragraph`;

const result = cleanUpAccessibilityContent(content);

expect(result).not.toContain("\n\n\n");
expect(result).toContain("First paragraph\n\nSecond paragraph");
});

it("should return fallback message for empty content", () => {
const result = cleanUpAccessibilityContent("");

expect(result).toBe("No accessibility guidelines found in expected format.");
});

it("should return fallback message for whitespace-only content", () => {
const result = cleanUpAccessibilityContent(" \n \n ");

expect(result).toBe("No accessibility guidelines found in expected format.");
});

it("should remove guidelines= attribute syntax in fallback mode", () => {
const content = `Some preamble\nguidelines={something}\nAfter text`;

const result = cleanUpAccessibilityContent(content);

expect(result).not.toContain("guidelines=");
expect(result).toContain("Some preamble");
expect(result).toContain("After text");
});
});
});

describe("run - integration", () => {
it("should generate accessibility markdown files from MDX files", () => {
run();

const outputFiles = fs.readdirSync(outputDir).filter(f => f.endsWith(".md"));
expect(outputFiles.length).toBeGreaterThan(0);
});
});
113 changes: 113 additions & 0 deletions packages/mcp/scripts/__tests__/extract-code-samples.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import { describe, it, expect } from "vitest";
import parser from "@babel/parser";
import { generateCodeForOneLiner, run } from "../extract-code-samples.js";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const outputDir = path.join(__dirname, "../../dist/generated/");

function parseCode(code) {
return parser.parse(code, {
sourceType: "module",
plugins: ["typescript", "jsx"]
});
}

describe("generateCodeForOneLiner", () => {
it("should return generated code for a regular variable declaration", () => {
const code = `const myComponent = () => <div>Hello</div>;`;
const ast = parseCode(code);

const result = generateCodeForOneLiner(ast, "myComponent");

expect(result).not.toBeNull();
expect(result).toContain("const");
expect(result).toContain("myComponent");
});

it("should return empty string for a createComponentTemplate call", () => {
const code = `const myTemplate = createComponentTemplate(SomeComponent);`;
const ast = parseCode(code);

const result = generateCodeForOneLiner(ast, "myTemplate");

expect(result).toBe("");
});

it("should return null when the variable name is not found", () => {
const code = `const other = 42;`;
const ast = parseCode(code);

const result = generateCodeForOneLiner(ast, "nonExistent");

expect(result).toBeNull();
});

it("should handle multiple variable declarations and find the correct one", () => {
const code = `
const first = createComponentTemplate(A);
const second = () => <span>Test</span>;
const third = 42;
`;
const ast = parseCode(code);

expect(generateCodeForOneLiner(ast, "first")).toBe("");
expect(generateCodeForOneLiner(ast, "second")).toContain("second");
expect(generateCodeForOneLiner(ast, "third")).toContain("third");
});

it("should prepend 'const ' to the generated code", () => {
const code = `const myVar = "hello";`;
const ast = parseCode(code);

const result = generateCodeForOneLiner(ast, "myVar");

expect(result).toMatch(/^const /);
});
});

describe("generateCodeForOneLiner - JSX handling", () => {
it("should generate code for a variable with JSX value", () => {
const code = `const myElement = <div className="wrapper"><span>Hello</span></div>;`;
const ast = parseCode(code);

const result = generateCodeForOneLiner(ast, "myElement");

expect(result).toContain("myElement");
expect(result).toContain("div");
expect(result).toContain("wrapper");
});

it("should generate code for an arrow function returning JSX", () => {
const code = `const MyComponent = () => <Button onClick={handler}>Click</Button>;`;
const ast = parseCode(code);

const result = generateCodeForOneLiner(ast, "MyComponent");

expect(result).not.toBeNull();
expect(result).toContain("MyComponent");
expect(result).toContain("Button");
});

it("should handle a variable with a nested function call (not createComponentTemplate)", () => {
const code = `const myVar = someOtherFunction(Arg1, Arg2);`;
const ast = parseCode(code);

const result = generateCodeForOneLiner(ast, "myVar");

expect(result).not.toBeNull();
expect(result).toContain("const");
expect(result).toContain("someOtherFunction");
});
});

describe("run - integration", () => {
it("should generate markdown files from story files", () => {
run();

const outputFiles = fs.readdirSync(outputDir).filter(f => f.endsWith(".md"));
expect(outputFiles.length).toBeGreaterThan(0);
});
});
20 changes: 11 additions & 9 deletions packages/mcp/scripts/extract-accessibility.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@ const __dirname = path.dirname(__filename);
const componentsDir = path.join(__dirname, "../../docs/src/pages/components/");
const outputDir = path.join(__dirname, "../dist/generated/accessibility/");

if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}

function getMdxFiles() {
export function getMdxFiles() {
const mdxFiles = [];

function traverseDirectory(dir) {
Expand All @@ -36,7 +32,7 @@ function getMdxFiles() {
return mdxFiles;
}

function extractAccessibilityFromMdx(file) {
export function extractAccessibilityFromMdx(file) {
const inputFile = path.join(__dirname, file);
const inputFileComponentName = path.basename(inputFile).split(".")[0];
const outputFile = path.resolve(__dirname, outputDir, inputFileComponentName + ".md");
Expand Down Expand Up @@ -91,7 +87,7 @@ function extractAccessibilityFromMdx(file) {
console.log(`Accessibility extracted for ${inputFileComponentName} at ${outputFile}`);
}

function cleanUpAccessibilityContent(content) {
export function cleanUpAccessibilityContent(content) {
// Extract guidelines from UsageGuidelines component
const guidelinesMatch = content.match(/guidelines=\{(\[[\s\S]*?\])\}/);

Expand Down Expand Up @@ -143,7 +139,11 @@ function cleanUpAccessibilityContent(content) {
return cleaned || "No accessibility guidelines found in expected format.";
}

function run() {
export function run() {
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}

console.log("🔍 Extracting accessibility content from MDX files...");

const mdxFiles = getMdxFiles();
Expand All @@ -162,4 +162,6 @@ function run() {
console.log("✅ Accessibility extraction completed!");
}

run();
if (process.argv[1] === __filename) {
run();
}
Loading
Loading