Skip to content
Merged
34 changes: 34 additions & 0 deletions packages/mcp/src/__tests__/knowledge/component-themes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,46 @@ import {
COMPONENT_THEMES,
getComponentTheme,
getTokenNames,
resolveComponentName,
resolveComponentTheme,
searchComponents,
validateTokens,
} from "../../knowledge/component-themes.js";

describe("Component Themes Knowledge Base", () => {
describe("resolveComponentName", () => {
it("keeps component keys as they are", () => {
expect(resolveComponentName("navdrawer")).toBe("navdrawer");
expect(resolveComponentName("nav-drawer-item")).toBe("nav-drawer-item");
});

it.each([
"igc-nav-drawer",
"igx-nav-drawer",
"nav-drawer",
"Nav-Drawer",
"IGC-NAV-DRAWER",
"IgrNavDrawer",
"IgbNavDrawer",
])("resolves %s to navdrawer", (name) => {
expect(resolveComponentName(name)).toBe("navdrawer");
});

it("resolves selectors whose name differs from the key", () => {
expect(resolveComponentName("igx-linear-bar")).toBe("progress-linear");
});

it("does not resolve React names that aren't listed in metadata", () => {
expect(resolveComponentName("IgrAccordionHeader")).toBe(
"igraccordionheader",
);
});

it("leaves unknown names normalized but unresolved", () => {
expect(resolveComponentName("Unknown-Thing")).toBe("unknown-thing");
});
});

describe("COMPONENT_THEMES", () => {
it("should contain expected components", () => {
expect(COMPONENT_THEMES).toHaveProperty("avatar");
Expand Down
24 changes: 18 additions & 6 deletions packages/mcp/src/__tests__/tools/handlers/component-tokens.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ import { describe, expect, it } from "vitest";
import { handleGetComponentDesignTokens } from "../../../tools/handlers/component-tokens.js";

describe("handleGetComponentDesignTokens", () => {
it.each([
"nav-drawer",
"igc-nav-drawer",
"IgrNavDrawer",
"IgbNavDrawer",
])("resolves %s to the navdrawer component", async (component) => {
const result = await handleGetComponentDesignTokens({ component });
const text = result.content[0].text;

expect(text).toContain(
"Implement a theme for the `navdrawer` component using the following guidance.",
);
});

it("uses instruction-oriented opening format", async () => {
const result = await handleGetComponentDesignTokens({
component: "date-range-picker",
Expand Down Expand Up @@ -176,17 +190,15 @@ describe("handleGetComponentDesignTokens", () => {
);
});

it("suggests progress-linear for linear progress phrasing", async () => {
it("resolves linear progress phrasing to progress-linear", async () => {
const result = await handleGetComponentDesignTokens({
component: "linear progress",
});
const text = result.content[0].text;

expect(text).toContain('Component "linear progress" not found.');
expect(text).toContain("**Similar components:**");

const suggestions = text.split("**Similar components:**")[1] ?? "";
expect(suggestions.trimStart().startsWith("- progress-linear")).toBe(true);
expect(text).toContain(
"Implement a theme for the `progress-linear` component using the following guidance.",
);
});

it("suggests progress components for common typo phrasing", async () => {
Expand Down
41 changes: 31 additions & 10 deletions packages/mcp/src/__tests__/tools/handlers/handlers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,33 @@ describe("handleCreateCustomPalette", () => {
});

describe("handleCreateComponentTheme", () => {
it("tells agents how to add a scoped dark variant after a light call", async () => {
const result = await handleCreateComponentTheme({
platform: "webcomponents",
component: "avatar",
tokens: { background: "#ff5722" },
output: "css",
});

const text = result.content[0].text;
expect(text).toContain('variant: "dark"');
expect(text).toContain('".your-dark-scope igc-avatar"');
});

it("tells agents to scope a dark call and add the light variant", async () => {
const result = await handleCreateComponentTheme({
platform: "angular",
component: "avatar",
variant: "dark",
tokens: { background: "#ff5722" },
output: "sass",
});

const text = result.content[0].text;
expect(text).toContain('".your-dark-scope igx-avatar"');
expect(text).toContain('variant: "light"');
});

it("returns error when platform is not provided", async () => {
const result = await handleCreateComponentTheme({
component: "avatar",
Expand Down Expand Up @@ -496,23 +523,17 @@ describe("handleCreateComponentTheme", () => {
expect(text).toContain("not supported");
});

it("suggests progress-linear for linear progress phrasing", async () => {
it("resolves linear progress phrasing to progress-linear", async () => {
const result = await handleCreateComponentTheme({
platform: "angular",
component: "linear progress",
tokens: {
background: "#ff5722",
"fill-color-info": "#ff5722",
},
});

expect(result.isError).toBe(true);

const text = result.content[0].text;
expect(text).toContain('Component "linear progress" not found.');
expect(text).toContain("**Similar components:**");

const suggestions = text.split("**Similar components:**")[1] ?? "";
expect(suggestions.trimStart().startsWith("- progress-linear")).toBe(true);
expect(result.isError).toBeUndefined();
expect(result.content[0].text).toContain("progress-linear");
});

it("suggests progress components for common typo phrasing", async () => {
Expand Down
25 changes: 25 additions & 0 deletions packages/mcp/src/__tests__/utils/sass.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,34 @@ import {
generatePaletteCode,
generateUseStatement,
quoteFontFamily,
scopeSelector,
toVariableName,
} from "../../utils/sass.js";

describe("scopeSelector", () => {
it("prefixes a single selector", () => {
expect(scopeSelector(".dark", "igc-avatar")).toBe(".dark igc-avatar");
});

it("prefixes each selector in a list", () => {
expect(scopeSelector(".dark", "igx-avatar, .my-avatar")).toBe(
".dark igx-avatar, .dark .my-avatar",
);
});

it("keeps commas inside parentheses", () => {
expect(scopeSelector(".dark", ":is(.a, .b) igc-avatar")).toBe(
".dark :is(.a, .b) igc-avatar",
);
});

it("keeps commas inside attribute selectors", () => {
expect(scopeSelector(".dark", 'igc-avatar[data-label="a,b"]')).toBe(
'.dark igc-avatar[data-label="a,b"]',
);
});
});

describe("toVariableName", () => {
it("converts spaces to hyphens", () => {
expect(toVariableName("My Theme")).toBe("my-theme");
Expand Down
Loading
Loading