"use strict";
// readme.md says what the code does: its table of interfaces is the one the registry gives, a program it shows is
// the text of a file that a test runs, and the other code blocks compile.
const { test } = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const { run, ROOT } = require("./helpers/examples.js");
const { table, update, shown, BEGIN, END, DRIVEN, README } = require("../scripts/capability-table.js");
const alex2node = require("..");
const readme = fs.readFileSync(README, "utf8");
const AsyncFunction = (async () => {}).constructor;
// The js code blocks, each with the file its marker line names
const blocks = [...readme.matchAll(/(?:<!-- file: (\S+) -->\n)?```js\n([\s\S]*?)\n```/g)]
.map(([, file, code]) => ({ file, code }));
const loads = (code) => /require\("alex2node"\)|from "alex2node"/.test(code);
test("the table of interfaces and the files in the readme are the ones of the code: npm run docs", () => {
assert.equal(update(readme), readme);
});
test("the table has a row for every described interface, and a line for the others", () => {
const text = table();
for (const { namespace, tier } of alex2node.registry.list()) {
const row = text.split("\n").filter((line) => line.startsWith(`| [${namespace}](`));
assert.equal(row.length, tier === 3 ? 0 : 1, namespace);
assert.ok(text.includes(`[${namespace}](`), namespace);
}
assert.ok(readme.indexOf(BEGIN) < readme.indexOf(END));
});
test("an interface is said to be driven through Alexa only when a run is recorded for it", () => {
// The runs of 2026-09-28, with alex2node 1.5.2. A name is added here with the record of its run.
assert.deepEqual(Object.keys(DRIVEN).sort(), [
"Alexa", "Alexa.BrightnessController", "Alexa.ColorController", "Alexa.ColorTemperatureController",
"Alexa.ContactSensor", "Alexa.LockController", "Alexa.ModeController", "Alexa.MotionSensor",
"Alexa.PercentageController", "Alexa.PowerController", "Alexa.PowerLevelController", "Alexa.RangeController",
"Alexa.SceneController", "Alexa.TemperatureSensor", "Alexa.ThermostatController", "Alexa.ToggleController",
]);
const rows = table().split("\n").filter((line) => line.startsWith("| [Alexa"));
for (const line of rows) {
const cells = line.split(" | ");
const namespace = cells[0].slice(3, cells[0].indexOf("]"));
assert.equal(cells[cells.length - 1].replace(/ \|$/, ""), DRIVEN[namespace] ?? "-", namespace);
}
});
test("a program in the readme is a file, and a test runs the file", () => {
assert.deepEqual(shown(readme), ["examples/lamp.js", "examples/testing/lamp.test.js", "examples/legacy/ExamplePowerController.js"]);
// More than the line that loads the library: a program, which has to be one of the files
const programs = blocks.filter(({ code }) => loads(code) && code.split("\n").length > 1);
assert.deepEqual(programs.map(({ file }) => file), shown(readme));
const examples = fs.readFileSync(path.join(__dirname, "examples.test.js"), "utf8");
assert.ok(examples.includes('start("lamp.js"'));
assert.ok("ExamplePowerController.js" in require("./fixtures/legacy-examples/discovery-1.5.2.json"));
// examples/testing/lamp.test.js: the next test
});
test("examples/testing/lamp.test.js passes, without a broker", async () => {
// LOOPBACK_PORT 1, where nothing listens: a connection would fail
const example = await run(path.join("testing", "lamp.test.js"));
assert.equal(await example.exited, 0, example.output());
assert.match(example.output(), /pass 4\n/);
assert.match(example.output(), /fail 0\n/);
});
test("the other code blocks of the readme compile, and load what the library exports", () => {
const fragments = blocks.filter(({ file }) => !file);
assert.ok(fragments.length >= 8);
for (const { code } of fragments) {
for (const [, names] of code.matchAll(/(?:const|import) \{([^}]+)\} (?:= require\("alex2node"\)|from "alex2node")/g)) {
for (const name of names.split(",").map((part) => part.trim())) assert.ok(name in alex2node, `${name} is not an export`);
}
const body = code.split("\n").filter((line) => !/^import .* from "alex2node";$/.test(line)).join("\n");
assert.doesNotThrow(() => new AsyncFunction(body), `this block does not compile:\n${code}`);
}
});
test("what the readme calls on AlexaErrors and on the registry is there", () => {
for (const [, helper] of readme.matchAll(/AlexaErrors\.(\w+)\(/g)) assert.equal(typeof alex2node.AlexaErrors[helper], "function", helper);
for (const [, namespace] of readme.matchAll(/registry\.get\("([^"]+)"\)/g)) assert.ok(alex2node.registry.has(namespace), namespace);
assert.equal(Object.keys(require("../dist/cjs/registry/catalog.js").ERROR_TYPES).length, 73);
});