@prismatic-io/spectral
Version:
Utility library for building Prismatic connectors and code-native integrations
168 lines (167 loc) • 8.63 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Verifies what spectral serializes for the inline-action example-perform fields
* on publish — the converted server component `prism component publish` sends.
*
* Scope is conversion output only: which fields land on the wire, with what values.
* It does not verify runtime dispatch (SAFE vs. NOT_ALLOWED choosing
* which perform runs) — the safety flags are inert metadata, converted from the
* author-facing camelCase values to the platform's GraphQL enum names, and the
* runner decides at execution time.
*/
const ejs_1 = require("ejs");
const path_1 = __importDefault(require("path"));
const vitest_1 = require("vitest");
const helpers_1 = require("../generators/componentManifest/helpers");
const index_1 = require("../index");
const display = (label) => ({ label, description: label });
// Three input shapes exercised on every permutation.
const connectionInput = (0, index_1.input)({ label: "Connection", type: "connection", required: true });
const structuredInput = (0, index_1.structuredObjectInput)({
label: "Name",
inputs: {
first: (0, index_1.input)({ type: "string", label: "First", required: true }),
last: (0, index_1.input)({ type: "string", label: "Last", required: true }),
},
});
const collectionInput = (0, index_1.input)({ label: "Tags", type: "string", collection: "valuelist" });
const sharedInputs = {
connection: connectionInput,
name: structuredInput,
tags: collectionInput,
};
const noop = () => __awaiter(void 0, void 0, void 0, function* () { return ({ data: null }); });
const echo = (_ctx, params) => __awaiter(void 0, void 0, void 0, function* () { return ({ data: params }); });
// Action permutations.
const testComponent = (0, index_1.component)({
key: "inline-action-testbed",
public: false,
display: Object.assign(Object.assign({}, display("Inline Action Testbed")), { iconPath: "icon.png" }),
actions: {
// A — no inline-calling fields.
plainAction: (0, index_1.action)({
display: display("Plain Action"),
inputs: sharedInputs,
perform: noop,
}),
// B — example perform.
examplePerformAction: (0, index_1.action)({
display: display("Example Perform Action"),
inputs: sharedInputs,
perform: noop,
examplePerform: echo,
}),
// C — real perform marked runnable.
runnablePerformAction: (0, index_1.action)({
display: display("Runnable Perform Action"),
inputs: sharedInputs,
perform: noop,
performSafety: index_1.PerformSafety.SAFE,
}),
// D — real perform marked not runnable.
notAllowedPerformAction: (0, index_1.action)({
display: display("Not Allowed Perform Action"),
inputs: sharedInputs,
perform: noop,
performSafety: index_1.PerformSafety.NOT_ALLOWED,
}),
},
});
// Converted actions are keyed by action key.
const actionsByKey = testComponent.actions;
(0, vitest_1.describe)("inline action calling: publish-wire conversion output", () => {
(0, vitest_1.it)("A — plain action: no inline-calling fields leak onto the wire", () => {
const a = actionsByKey.plainAction;
(0, vitest_1.expect)("examplePerform" in a).toBe(false);
(0, vitest_1.expect)(a.examplePerformSafety).toBeUndefined();
(0, vitest_1.expect)(a.performSafety).toBeUndefined();
});
(0, vitest_1.it)("B — example perform is wrapped (invokable) and implies a SAFE safety flag", () => __awaiter(void 0, void 0, void 0, function* () {
var _a;
const a = actionsByKey.examplePerformAction;
(0, vitest_1.expect)(typeof a.examplePerform).toBe("function");
(0, vitest_1.expect)(a.examplePerformSafety).toBe("SAFE");
// The example flag must not imply anything about the real perform.
(0, vitest_1.expect)(a.performSafety).toBeUndefined();
// Wrapped fn is invokable and passes params through.
const result = yield ((_a = a.examplePerform) === null || _a === void 0 ? void 0 : _a.call(a, {}, { tags: ["x"] }));
(0, vitest_1.expect)(result.data.tags).toStrictEqual(["x"]);
}));
(0, vitest_1.it)("C — real-perform SAFE flag carried; no separate example perform", () => {
const a = actionsByKey.runnablePerformAction;
(0, vitest_1.expect)(a.performSafety).toBe("SAFE");
(0, vitest_1.expect)("examplePerform" in a).toBe(false);
(0, vitest_1.expect)(a.examplePerformSafety).toBeUndefined();
});
(0, vitest_1.it)("D — real-perform notAllowed converts to NOT_ALLOWED; no example perform", () => {
const a = actionsByKey.notAllowedPerformAction;
(0, vitest_1.expect)(a.performSafety).toBe("NOT_ALLOWED");
(0, vitest_1.expect)("examplePerform" in a).toBe(false);
(0, vitest_1.expect)(a.examplePerformSafety).toBeUndefined();
});
(0, vitest_1.it)("the three input shapes survive conversion on every permutation", () => {
for (const key of [
"plainAction",
"examplePerformAction",
"runnablePerformAction",
"notAllowedPerformAction",
]) {
const inputs = actionsByKey[key].inputs;
const byKey = Object.fromEntries(inputs.map((i) => [i.key, i]));
(0, vitest_1.expect)(byKey.connection.type).toBe("connection");
(0, vitest_1.expect)(byKey.name.type).toBe("structuredObject");
(0, vitest_1.expect)(byKey.name.inputs).toHaveLength(2); // nested children survived
(0, vitest_1.expect)(byKey.tags.collection).toBe("valuelist");
}
});
});
(0, vitest_1.describe)("inline action calling: component-manifest emit", () => {
const templatePath = path_1.default.join(__dirname, "../generators/componentManifest/templates/actions/action.ts.ejs");
const baseAction = {
typeInterface: "DoThing",
import: "doThing",
key: "doThing",
label: "Do Thing",
description: "Does a thing.",
inputs: [],
componentKey: "testbed",
};
const render = (extra) => (0, ejs_1.renderFile)(templatePath, { action: Object.assign(Object.assign({}, baseAction), extra), helpers: helpers_1.helpers, imports: {} });
(0, vitest_1.it)("emits both *Safety scalars when present", () => __awaiter(void 0, void 0, void 0, function* () {
const out = yield render({
examplePerformSafety: "safe",
performSafety: "notAllowed",
});
(0, vitest_1.expect)(out).toContain('examplePerformSafety: "safe"');
(0, vitest_1.expect)(out).toContain('performSafety: "notAllowed"');
}));
(0, vitest_1.it)("emits a notAllowed performSafety scalar", () => __awaiter(void 0, void 0, void 0, function* () {
const out = yield render({ performSafety: "notAllowed" });
(0, vitest_1.expect)(out).toContain('performSafety: "notAllowed"');
}));
(0, vitest_1.it)("omits the *Safety scalars when absent (permutation A)", () => __awaiter(void 0, void 0, void 0, function* () {
const out = yield render({});
(0, vitest_1.expect)(out).not.toContain("examplePerformSafety");
(0, vitest_1.expect)(out).not.toContain("performSafety");
}));
(0, vitest_1.it)("never emits an examplePerform stub (invoked via the server action, not the manifest)", () => __awaiter(void 0, void 0, void 0, function* () {
const out = yield render({
examplePerformSafety: "safe",
performSafety: "notAllowed",
});
(0, vitest_1.expect)(out).not.toContain("examplePerform:");
}));
});