243 lines
7.7 KiB
JavaScript
243 lines
7.7 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { execFile } from "node:child_process";
|
|
import {
|
|
chmod,
|
|
mkdir,
|
|
mkdtemp,
|
|
readdir,
|
|
readFile,
|
|
rename,
|
|
rm,
|
|
symlink,
|
|
writeFile,
|
|
} from "node:fs/promises";
|
|
import { existsSync } from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { promisify } from "node:util";
|
|
import { fileURLToPath } from "node:url";
|
|
import {
|
|
copyWasmBuildInputs,
|
|
generateTypeMapModule,
|
|
hashPackageInputs,
|
|
parseTypeMapYaml,
|
|
} from "../dist/vite/index.js";
|
|
|
|
const run = promisify(execFile);
|
|
const repositoryRoot = path.resolve(
|
|
path.dirname(fileURLToPath(import.meta.url)),
|
|
"..",
|
|
);
|
|
|
|
const repeatedNames = `
|
|
protocol_version: "2.0"
|
|
type_maps:
|
|
"1.0":
|
|
CommunicationTypes:
|
|
Message: 32
|
|
LegacyMessage: 33
|
|
DataTypes:
|
|
Payload: 32
|
|
LegacyPayload: 33
|
|
"2.0":
|
|
CommunicationTypes:
|
|
Message: 35
|
|
CurrentMessage: 36
|
|
DataTypes:
|
|
Payload: 35
|
|
CurrentPayload: 36
|
|
`;
|
|
|
|
const selected = parseTypeMapYaml(repeatedNames, "repeated.yaml");
|
|
assert.ok(selected.communicationTypes.includes("Message"));
|
|
assert.ok(selected.communicationTypes.includes("CurrentMessage"));
|
|
assert.ok(!selected.communicationTypes.includes("LegacyMessage"));
|
|
assert.ok(selected.dataTypes.includes("Payload"));
|
|
assert.ok(selected.dataTypes.includes("CurrentPayload"));
|
|
assert.ok(!selected.dataTypes.includes("LegacyPayload"));
|
|
assert.ok(selected.communicationTypes.includes("Ping"));
|
|
assert.ok(selected.dataTypes.includes("Version"));
|
|
|
|
const generated = generateTypeMapModule(selected);
|
|
assert.match(generated.dts, /"Message"/);
|
|
assert.match(generated.dts, /"CurrentMessage"/);
|
|
assert.doesNotMatch(generated.dts, /LegacyMessage/);
|
|
assert.doesNotMatch(generated.dts, /LegacyPayload/);
|
|
|
|
assert.throws(
|
|
() =>
|
|
parseTypeMapYaml(
|
|
`protocol_version: "1.0"\ntype_maps:\n "1.0":\n CommunicationTypes:\n Ping: 32\n`,
|
|
"reserved.yaml",
|
|
),
|
|
/uses a reserved type name/,
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
parseTypeMapYaml(
|
|
`protocol_version: "2.0"\ntype_maps:\n "1.0": {}\n`,
|
|
"missing-version.yaml",
|
|
),
|
|
/is not defined in type_maps/,
|
|
);
|
|
assert.throws(
|
|
() => parseTypeMapYaml(`protocol_version: "1.0"\ntype_maps: {}`, "empty.yaml"),
|
|
/is not defined in type_maps/,
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
parseTypeMapYaml(
|
|
`protocol_version: "latest"\ntype_maps:\n "latest": {}\n`,
|
|
"invalid-version.yaml",
|
|
),
|
|
/protocol_version must be a string/,
|
|
);
|
|
|
|
const temporaryBuildRoot = await mkdtemp(path.join(os.tmpdir(), "mtp-vite-inputs-"));
|
|
try {
|
|
await copyWasmBuildInputs(temporaryBuildRoot);
|
|
for (const requiredInput of [
|
|
"Cargo.lock",
|
|
"wasm/Cargo.toml",
|
|
"wasm/src/lib.rs",
|
|
"common/Cargo.toml",
|
|
"codec/Cargo.toml",
|
|
"crypto/Cargo.toml",
|
|
"type-map/Cargo.toml",
|
|
"type-map/build.rs",
|
|
"type-map/reserved.json",
|
|
]) {
|
|
assert.equal(
|
|
existsSync(path.join(temporaryBuildRoot, requiredInput)),
|
|
true,
|
|
`temporary WASM build input is missing: ${requiredInput}`,
|
|
);
|
|
}
|
|
assert.equal(
|
|
await readFile(path.join(temporaryBuildRoot, "type-map/reserved.json"), "utf8"),
|
|
await readFile(path.join(repositoryRoot, "type-map/reserved.json"), "utf8"),
|
|
);
|
|
assert.match(
|
|
await readFile(path.join(temporaryBuildRoot, "type-map/build.rs"), "utf8"),
|
|
/include_str!\("reserved\.json"\)/,
|
|
);
|
|
} finally {
|
|
await rm(temporaryBuildRoot, { recursive: true, force: true });
|
|
}
|
|
|
|
const hashTestRoot = await mkdtemp(path.join(os.tmpdir(), "mtp-vite-hash-"));
|
|
try {
|
|
await mkdir(path.join(hashTestRoot, "type-map"), { recursive: true });
|
|
const manifestPath = path.join(hashTestRoot, "type-map/reserved.json");
|
|
await writeFile(manifestPath, "{\"version\":1}");
|
|
const firstHash = await hashPackageInputs(hashTestRoot);
|
|
await writeFile(manifestPath, "{\"version\":2}");
|
|
const secondHash = await hashPackageInputs(hashTestRoot);
|
|
assert.notEqual(firstHash, secondHash);
|
|
} finally {
|
|
await rm(hashTestRoot, { recursive: true, force: true });
|
|
}
|
|
|
|
const viteCandidates = [
|
|
path.join(repositoryRoot, "example/web-client/node_modules/.bin/vite"),
|
|
path.join(repositoryRoot, "node_modules/.bin/vite"),
|
|
];
|
|
const viteBin = viteCandidates.find((candidate) => existsSync(candidate));
|
|
if (!viteBin) {
|
|
console.warn("Skipping packed Vite smoke test: Vite is not installed");
|
|
} else {
|
|
const temporaryPackageRoot = await mkdtemp(path.join(os.tmpdir(), "mtp-vite-package-"));
|
|
try {
|
|
await run(
|
|
"npm",
|
|
["pack", "--pack-destination", temporaryPackageRoot],
|
|
{
|
|
cwd: repositoryRoot,
|
|
env: {
|
|
...process.env,
|
|
npm_config_cache: path.join(temporaryPackageRoot, "npm-cache"),
|
|
},
|
|
},
|
|
);
|
|
const packageName = (await readdir(temporaryPackageRoot)).find((entry) =>
|
|
entry.endsWith(".tgz"),
|
|
);
|
|
assert.ok(packageName, "npm pack did not report a tarball");
|
|
|
|
const extractedRoot = path.join(temporaryPackageRoot, "extracted");
|
|
const appRoot = path.join(temporaryPackageRoot, "app");
|
|
await mkdir(extractedRoot, { recursive: true });
|
|
await run("tar", ["-xzf", path.join(temporaryPackageRoot, packageName), "-C", extractedRoot]);
|
|
await mkdir(path.join(appRoot, "node_modules"), { recursive: true });
|
|
await rename(path.join(extractedRoot, "package"), path.join(appRoot, "node_modules/mtp"));
|
|
await symlink(
|
|
path.join(repositoryRoot, "node_modules/yaml"),
|
|
path.join(appRoot, "node_modules/yaml"),
|
|
"dir",
|
|
);
|
|
await symlink(
|
|
path.join(path.dirname(path.dirname(viteBin)), "vite"),
|
|
path.join(appRoot, "node_modules/vite"),
|
|
"dir",
|
|
);
|
|
|
|
await writeFile(
|
|
path.join(appRoot, "package.json"),
|
|
JSON.stringify({ type: "module", private: true }),
|
|
);
|
|
await writeFile(
|
|
path.join(appRoot, "index.html"),
|
|
'<script type="module" src="/main.js"></script>',
|
|
);
|
|
await writeFile(
|
|
path.join(appRoot, "main.js"),
|
|
'import { communicationTypes } from "mtp/type-map";\n' +
|
|
'if (!communicationTypes.includes("CurrentMessage") || communicationTypes.includes("LegacyMessage")) throw new Error("wrong browser type-map selection");\n',
|
|
);
|
|
await writeFile(
|
|
path.join(appRoot, "type-maps.yaml"),
|
|
repeatedNames,
|
|
);
|
|
await writeFile(
|
|
path.join(appRoot, "vite.config.mjs"),
|
|
'import { defineConfig } from "vite";\n' +
|
|
'import { mtp } from "mtp/vite";\n' +
|
|
'export default defineConfig({ plugins: [mtp({ typeMaps: "./type-maps.yaml", release: false })] });\n',
|
|
);
|
|
|
|
const fakeBin = path.join(temporaryPackageRoot, "bin");
|
|
const fakeWasmPack = path.join(fakeBin, "wasm-pack");
|
|
await mkdir(fakeBin, { recursive: true });
|
|
await writeFile(
|
|
fakeWasmPack,
|
|
`#!/usr/bin/env node
|
|
const fs = require("node:fs");
|
|
const path = require("node:path");
|
|
const args = process.argv.slice(2);
|
|
const outIndex = args.indexOf("--out-dir");
|
|
if (outIndex < 0) throw new Error("fake wasm-pack did not receive --out-dir");
|
|
if (!fs.existsSync(path.join(process.cwd(), "type-map", "reserved.json"))) {
|
|
throw new Error("temporary wasm build is missing type-map/reserved.json");
|
|
}
|
|
const outDir = args[outIndex + 1];
|
|
fs.mkdirSync(outDir, { recursive: true });
|
|
fs.writeFileSync(path.join(outDir, "mtp_wasm.js"), "export default function init() {}\\n");
|
|
fs.writeFileSync(path.join(outDir, "mtp_wasm_bg.wasm"), Buffer.from([0, 97, 115, 109, 1, 0, 0, 0]));
|
|
`,
|
|
);
|
|
await chmod(fakeWasmPack, 0o755);
|
|
|
|
await run(viteBin, ["build", "--config", "vite.config.mjs"], {
|
|
cwd: appRoot,
|
|
env: {
|
|
...process.env,
|
|
PATH: `${fakeBin}${path.delimiter}${process.env.PATH ?? ""}`,
|
|
},
|
|
});
|
|
assert.equal(existsSync(path.join(appRoot, "dist/index.html")), true);
|
|
} finally {
|
|
await rm(temporaryPackageRoot, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
console.log("Vite type-map tests passed");
|