mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 22:25:24 +00:00
feat: move snippets, keybinding actions and macros out of core into plugins
This commit is contained in:
1 parent
d9bba10612
commit
e0c30a4e6f
262 files changed
+9091
-10744
No files matched your search
@@ -686,3 +686,76 @@ describe("contributes.http.legacyRedirects", () => {
|
||||
expect(errors).toMatch(/extra/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("contributes.http.adminImpersonation", () => {
|
||||
it("accepts a boolean and refuses anything else", () => {
|
||||
expect(
|
||||
validateManifest(
|
||||
base({ contributes: { http: { adminImpersonation: true } } }),
|
||||
),
|
||||
).toEqual([]);
|
||||
expect(
|
||||
validateManifest(
|
||||
base({ contributes: { http: { adminImpersonation: "yes" } } }),
|
||||
).join(),
|
||||
).toMatch(/adminImpersonation" must be a boolean/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("contributes.keybindingActions", () => {
|
||||
const withActions = (keybindingActions: unknown) =>
|
||||
validateManifest(base({ contributes: { keybindingActions } }));
|
||||
|
||||
it("accepts actions with typed parameters", () => {
|
||||
expect(
|
||||
withActions([
|
||||
{ id: "sample.run" },
|
||||
{
|
||||
id: "sample.send",
|
||||
params: {
|
||||
itemId: {
|
||||
type: "string",
|
||||
required: true,
|
||||
pattern: "^[0-9]+$",
|
||||
maxLength: 20,
|
||||
syncEntity: "items",
|
||||
},
|
||||
enter: { type: "boolean" },
|
||||
},
|
||||
},
|
||||
]),
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("refuses the shell's own actions, duplicates and bad ids", () => {
|
||||
const errors = withActions([
|
||||
{ id: "nextTab" },
|
||||
{ id: "sample.run" },
|
||||
{ id: "sample.run" },
|
||||
{ id: "bad id" },
|
||||
]).join();
|
||||
expect(errors).toMatch(/one of the shell's own actions/);
|
||||
expect(errors).toMatch(/duplicates "sample.run"/);
|
||||
expect(errors).toMatch(/letters, digits, dots and dashes/);
|
||||
});
|
||||
|
||||
it("refuses bad parameters", () => {
|
||||
const errors = withActions([
|
||||
{
|
||||
id: "sample.run",
|
||||
params: {
|
||||
type: { type: "string" },
|
||||
a: { type: "number" },
|
||||
b: { type: "string", pattern: "(" },
|
||||
c: { type: "boolean", syncEntity: "items" },
|
||||
d: { type: "string", extra: 1 },
|
||||
},
|
||||
},
|
||||
]).join();
|
||||
expect(errors).toMatch(/params.type is not a valid parameter name/);
|
||||
expect(errors).toMatch(/a.type must be "string" or "boolean"/);
|
||||
expect(errors).toMatch(/b.pattern must be a regular expression/);
|
||||
expect(errors).toMatch(/c.syncEntity needs a string parameter/);
|
||||
expect(errors).toMatch(/d/);
|
||||
});
|
||||
});
|
||||
@@ -9,7 +9,11 @@ import {
|
||||
resetSyncRegistry,
|
||||
unregisterByOwner,
|
||||
} from "../../plugins/sync-registry.js";
|
||||
import { registerCoreSyncEntities } from "../../sync/entities.js";
|
||||
import {
|
||||
mapKeybindingReferences,
|
||||
registerCoreSyncEntities,
|
||||
} from "../../sync/entities.js";
|
||||
import { setKeybindingActionSource } from "../../database/routes/keybinding-validation.js";
|
||||
|
||||
beforeEach(() => {
|
||||
resetSyncRegistry();
|
||||
@@ -197,14 +201,51 @@ describe("core sync entities", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("translates every snippet a host points at", () => {
|
||||
const fields = (getEntity("hosts")?.references ?? [])
|
||||
.filter((reference) => reference.entityType === "commandSnippet")
|
||||
.map((reference) => reference.field);
|
||||
expect(fields).toEqual([
|
||||
"quickActions[].snippetId",
|
||||
"terminalConfig.startupSnippetId",
|
||||
it("leaves plugin data out of the host row, which travels in pluginSettings", () => {
|
||||
const hosts = getEntity("hosts");
|
||||
expect(
|
||||
(hosts?.references ?? []).map((reference) => reference.entityType),
|
||||
).not.toContain("commandSnippet");
|
||||
expect(hosts?.readOnlyFields).toContain("quickActions");
|
||||
});
|
||||
|
||||
it("translates a keybinding parameter that names a sync entity", async () => {
|
||||
setKeybindingActionSource(() => [
|
||||
{
|
||||
pluginId: "fixture",
|
||||
id: "runThing",
|
||||
params: {
|
||||
thingId: { type: "string", syncEntity: "things" },
|
||||
label: { type: "string" },
|
||||
},
|
||||
},
|
||||
]);
|
||||
try {
|
||||
const row = {
|
||||
customKeybindings: JSON.stringify([
|
||||
{ id: "a", action: { type: "runThing", thingId: "4", label: "4" } },
|
||||
{ id: "b", action: { type: "nextTab" } },
|
||||
]),
|
||||
};
|
||||
const out = await mapKeybindingReferences(row, async (entity, value) =>
|
||||
entity === "things" && value === "4" ? "sync-4" : null,
|
||||
);
|
||||
const bindings = JSON.parse(out.customKeybindings as string);
|
||||
expect(bindings[0].action).toEqual({
|
||||
type: "runThing",
|
||||
thingId: "sync-4",
|
||||
label: "4",
|
||||
});
|
||||
expect(bindings[1].action).toEqual({ type: "nextTab" });
|
||||
expect(
|
||||
await mapKeybindingReferences(
|
||||
{ customKeybindings: null },
|
||||
async () => "x",
|
||||
),
|
||||
).toEqual({ customKeybindings: null });
|
||||
} finally {
|
||||
setKeybindingActionSource(() => []);
|
||||
}
|
||||
});
|
||||
|
||||
it("encrypts host and credential secrets", () => {
|
||||
|
||||
@@ -326,6 +326,10 @@ function upgradeChecks(current: () => Booted, bootIndex: number) {
|
||||
containerRuntime: "podman",
|
||||
});
|
||||
expect(await host("ai")).toMatchObject({ enableAiAssistant: true });
|
||||
expect(await host("snippets")).toMatchObject({
|
||||
startupSnippetId: 1,
|
||||
quickActions: [{ name: "d4-uptime", snippetId: 1 }],
|
||||
});
|
||||
expect(await host("wake-on-lan")).toMatchObject({
|
||||
macAddress: "AA:BB:CC:DD:EE:04",
|
||||
broadcastAddress: "10.4.0.255",
|
||||
@@ -473,6 +477,11 @@ function upgradeChecks(current: () => Booted, bootIndex: number) {
|
||||
terminalDefaults: { fontSize: 18, cursorBlink: false },
|
||||
customThemes: [expect.objectContaining({ id: "d4-theme" })],
|
||||
commandAutocomplete: true,
|
||||
macros: [expect.objectContaining({ id: "d4-macro", name: "d4 macro" })],
|
||||
});
|
||||
expect(await settings(booted, "snippets", "user")).toMatchObject({
|
||||
confirmExecution: true,
|
||||
foldersCollapsed: false,
|
||||
});
|
||||
expect(await settings(booted, "step-ca", "admin")).toMatchObject({
|
||||
caUrl: "https://ca.d4.example",
|
||||
|
||||
Reference in new issue
Block a user