Files
WRNexusJS/editors/vscode/test/component-metadata.test.js
T
2026-07-24 12:46:44 +05:30

131 lines
3.9 KiB
JavaScript

"use strict";
const assert = require("node:assert");
const { test } = require("node:test");
const {
attributeValueType,
parseComponentMetadata,
parseComponentTags,
validateComponentTags,
} = require("../src/component-metadata");
const source = `component Button {
props {
// @required
label = ""
variant = "primary"
disabled = false
count = 0
items = []
}
view {
<button class="{variant === 'primary' ? 'brand' : variant === 'danger' ? 'danger' : ''}">{label}</button>
}
}`;
test("extracts component prop types, required props, defaults, and options", () => {
const component = parseComponentMetadata(source);
assert.equal(component.name, "Button");
assert.deepEqual(
component.props.map(({ name, type, required, options }) => ({ name, type, required, options })),
[
{ name: "label", type: "string", required: true, options: [] },
{ name: "variant", type: "string", required: false, options: ["danger", "primary"] },
{ name: "disabled", type: "boolean", required: false, options: [] },
{ name: "count", type: "number", required: false, options: [] },
{ name: "items", type: "array", required: false, options: [] },
],
);
});
test("parses multiline component tags and their attribute locations", () => {
const page = `<Button
label="Save"
disabled="{false}"
/>`;
const [tag] = parseComponentTags(page);
assert.equal(tag.name, "Button");
assert.deepEqual(
tag.attributes.map(({ name, value }) => ({ name, value })),
[
{ name: "label", value: "Save" },
{ name: "disabled", value: "{false}" },
],
);
});
test("reports missing, unknown, mismatched, and invalid-option component props", () => {
const catalog = new Map([["Button", parseComponentMetadata(source)]]);
const diagnostics = validateComponentTags(
`<Button variant="quiet" count="many" extra="value" />`,
catalog,
);
assert.deepEqual(
diagnostics.map(({ code, severity }) => ({ code, severity })),
[
{ code: "wrn-missing-component-prop", severity: "error" },
{ code: "wrn-component-prop-option", severity: "warning" },
{ code: "wrn-component-prop-type", severity: "error" },
{ code: "wrn-unknown-component-prop", severity: "warning" },
],
);
});
test("recognizes literal attribute value types", () => {
assert.equal(attributeValueType("{false}"), "boolean");
assert.equal(attributeValueType("{42}"), "number");
assert.equal(attributeValueType("{[1, 2]}"), "array");
assert.equal(attributeValueType("plain"), "string");
});
test("extracts explicit prop types and resolves typed prop/state expressions", () => {
const input = `component DatePicker {
props {
required: boolean = false
value: string
}
state count: number = 0
view { <DateInput required="{required}" value="{value}" count="{count}" /> }
}`;
const target = parseComponentMetadata(`component DateInput {
props {
required: boolean = false
value: string
count: number = 0
}
view { <input/> }
}`);
const own = parseComponentMetadata(input);
assert.deepEqual(
own.props.map(({ name, type, required }) => ({ name, type, required })),
[
{ name: "required", type: "boolean", required: false },
{ name: "value", type: "string", required: true },
],
);
assert.deepEqual(validateComponentTags(input, new Map([["DateInput", target]])), []);
});
test("extracts declared events and validates component event bindings", () => {
const target = parseComponentMetadata(`component PinInput {
props {
value = ""
@event complete = function
@event clear = function
}
view { <div></div> }
}`);
assert.deepEqual(target.events, ["complete", "clear"]);
assert.deepEqual(
validateComponentTags(
`<PinInput @complete="save(event)" @missing="fail(event)" />`,
new Map([["PinInput", target]]),
).map(({ code }) => code),
["wrn-unknown-component-event"],
);
});