131 lines
3.9 KiB
JavaScript
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"],
|
|
);
|
|
});
|