Consolidate vitest CI & coverage (#33808)
* Consolidate modules vitest coverage * Use vite-common as base for modules vitest config * Make knip happier * Fix coverage paths * Place modules unit tests alongside src * Switch to defineProject for better type safety * Consolidate vitest CI & coverage Kills off vite-common * Update comment * Update lockfile * Fix shared-components vitest config * Soften eslint config for tests in modules * Run eslint on modules/playwright dir too * Make tsc happy
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import WidgetLifecycleModule, { type WidgetLifecycleApiAdapter } from "./WidgetLifecycleModule";
|
||||
import type {
|
||||
CapabilitiesApprover,
|
||||
IdentityApprover,
|
||||
PreloadApprover,
|
||||
WidgetDescriptor,
|
||||
Api,
|
||||
} from "@element-hq/element-web-module-api";
|
||||
|
||||
const createApi = (config: unknown = {}) => {
|
||||
const handlers: {
|
||||
preload?: PreloadApprover;
|
||||
identity?: IdentityApprover;
|
||||
capabilities?: CapabilitiesApprover;
|
||||
} = {};
|
||||
|
||||
const widgetLifecycle: WidgetLifecycleApiAdapter = {
|
||||
registerPreloadApprover: (approver) => {
|
||||
handlers.preload = approver;
|
||||
},
|
||||
registerIdentityApprover: (approver) => {
|
||||
handlers.identity = approver;
|
||||
},
|
||||
registerCapabilitiesApprover: (approver) => {
|
||||
handlers.capabilities = approver;
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
api: {
|
||||
config: {
|
||||
get: () => config,
|
||||
},
|
||||
widgetLifecycle,
|
||||
} as Api,
|
||||
handlers,
|
||||
};
|
||||
};
|
||||
|
||||
const widget: WidgetDescriptor = {
|
||||
id: "widget-id",
|
||||
templateUrl: "https://example.com/",
|
||||
creatorUserId: "@user-id",
|
||||
type: "com.example.custom",
|
||||
origin: "example.com",
|
||||
};
|
||||
|
||||
describe("WidgetLifecycleModule", () => {
|
||||
it("approves preload when configured", async () => {
|
||||
const { api, handlers } = createApi({
|
||||
widget_permissions: {
|
||||
"https://example.com/": { preload_approved: true },
|
||||
},
|
||||
});
|
||||
|
||||
const module = new WidgetLifecycleModule(api);
|
||||
await module.load();
|
||||
|
||||
expect(await handlers.preload?.(widget)).toBe(true);
|
||||
});
|
||||
|
||||
it("approves identity when configured", async () => {
|
||||
const { api, handlers } = createApi({
|
||||
widget_permissions: {
|
||||
"https://example.com/": { identity_approved: true },
|
||||
},
|
||||
});
|
||||
|
||||
const module = new WidgetLifecycleModule(api);
|
||||
await module.load();
|
||||
|
||||
expect(await handlers.identity?.(widget)).toBe(true);
|
||||
});
|
||||
|
||||
it("approves configured capabilities", async () => {
|
||||
const { api, handlers } = createApi({
|
||||
widget_permissions: {
|
||||
"https://example.com/": {
|
||||
capabilities_approved: ["org.matrix.msc2931.navigate"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const module = new WidgetLifecycleModule(api);
|
||||
await module.load();
|
||||
|
||||
const approved = await handlers.capabilities?.(
|
||||
widget,
|
||||
new Set(["org.matrix.msc2931.navigate", "org.matrix.msc2762.timeline:*"]),
|
||||
);
|
||||
|
||||
expect(approved).toEqual(new Set(["org.matrix.msc2931.navigate"]));
|
||||
});
|
||||
|
||||
it("returns no approvals when not configured", async () => {
|
||||
const { api, handlers } = createApi({});
|
||||
|
||||
const module = new WidgetLifecycleModule(api);
|
||||
await module.load();
|
||||
|
||||
expect(await handlers.preload?.(widget)).toBe(false);
|
||||
expect(await handlers.identity?.(widget)).toBe(false);
|
||||
expect(await handlers.capabilities?.(widget, new Set(["org.matrix.msc2931.navigate"]))).toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns no approvals when config is missing", async () => {
|
||||
const { api, handlers } = createApi(undefined);
|
||||
|
||||
const module = new WidgetLifecycleModule(api);
|
||||
await module.load();
|
||||
|
||||
expect(await handlers.preload?.(widget)).toBe(false);
|
||||
expect(await handlers.identity?.(widget)).toBe(false);
|
||||
expect(await handlers.capabilities?.(widget, new Set(["org.matrix.msc2931.navigate"]))).toBeUndefined();
|
||||
});
|
||||
|
||||
it("fails closed on invalid config", async () => {
|
||||
const { api, handlers } = createApi({
|
||||
widget_permissions: {
|
||||
"https://example.com/": {
|
||||
preload_approved: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const module = new WidgetLifecycleModule(api);
|
||||
await module.load();
|
||||
|
||||
expect(await handlers.preload?.(widget)).toBe(false);
|
||||
expect(await handlers.identity?.(widget)).toBe(false);
|
||||
expect(await handlers.capabilities?.(widget, new Set(["org.matrix.msc2931.navigate"]))).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { parseWidgetLifecycleConfig } from "./config";
|
||||
|
||||
describe("parseWidgetLifecycleConfig", () => {
|
||||
it("accepts missing configuration", () => {
|
||||
expect(parseWidgetLifecycleConfig(undefined)).toEqual({});
|
||||
});
|
||||
|
||||
it("accepts empty configuration", () => {
|
||||
expect(parseWidgetLifecycleConfig({})).toEqual({});
|
||||
});
|
||||
|
||||
it("accepts valid configuration", () => {
|
||||
expect(
|
||||
parseWidgetLifecycleConfig({
|
||||
widget_permissions: {
|
||||
"https://localhost": {
|
||||
preload_approved: true,
|
||||
identity_approved: false,
|
||||
capabilities_approved: [],
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
"https://localhost": {
|
||||
preload_approved: true,
|
||||
identity_approved: false,
|
||||
capabilities_approved: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("accepts additional properties", () => {
|
||||
expect(
|
||||
parseWidgetLifecycleConfig({
|
||||
widget_permissions: {
|
||||
"https://localhost": {
|
||||
preload_approved: true,
|
||||
identity_approved: false,
|
||||
capabilities_approved: ["capability"],
|
||||
additional: "tmp",
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
"https://localhost": {
|
||||
preload_approved: true,
|
||||
identity_approved: false,
|
||||
capabilities_approved: ["capability"],
|
||||
additional: "tmp",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ preload_approved: null },
|
||||
{ preload_approved: 123 },
|
||||
{ identity_approved: null },
|
||||
{ identity_approved: 123 },
|
||||
{ capabilities_approved: null },
|
||||
{ capabilities_approved: 123 },
|
||||
{ capabilities_approved: [undefined] },
|
||||
{ capabilities_approved: [null] },
|
||||
{ capabilities_approved: [123] },
|
||||
{ capabilities_approved: [""] },
|
||||
])("rejects invalid widget configuration %j", (patch) => {
|
||||
expect(() =>
|
||||
parseWidgetLifecycleConfig({
|
||||
widget_permissions: {
|
||||
"https://localhost": {
|
||||
preload_approved: true,
|
||||
identity_approved: false,
|
||||
capabilities_approved: ["capability"],
|
||||
...patch,
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
Copyright 2023 Nordeck IT + Consulting GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { constructWidgetPermissions, sortLongestMatchLast } from "./constructWidgetPermissions";
|
||||
|
||||
describe("constructWidgetPermissions", () => {
|
||||
it("finds exact match", () => {
|
||||
expect(constructWidgetPermissions({ "https://a.com/": { preload_approved: true } }, "https://a.com/")).toEqual({
|
||||
preload_approved: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("finds prefix match", () => {
|
||||
expect(
|
||||
constructWidgetPermissions({ "https://a.com/*": { preload_approved: true } }, "https://a.com/some"),
|
||||
).toEqual({ preload_approved: true });
|
||||
});
|
||||
|
||||
it("merges multiple permissions", () => {
|
||||
expect(
|
||||
constructWidgetPermissions(
|
||||
{
|
||||
"https://b.com/path": {
|
||||
preload_approved: false,
|
||||
capabilities_approved: ["org.matrix.msc2762.timeline:*"],
|
||||
},
|
||||
"https://b.com/*": {
|
||||
preload_approved: true,
|
||||
identity_approved: true,
|
||||
capabilities_approved: ["org.matrix.msc2931.navigate"],
|
||||
},
|
||||
},
|
||||
"https://b.com/path",
|
||||
),
|
||||
).toEqual({
|
||||
preload_approved: false,
|
||||
identity_approved: true,
|
||||
capabilities_approved: ["org.matrix.msc2762.timeline:*"],
|
||||
});
|
||||
});
|
||||
|
||||
it("skips unknown url", () => {
|
||||
expect(constructWidgetPermissions({ "https://a.com/": { preload_approved: true } }, "https://a.com/x")).toEqual(
|
||||
{},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("sortLongestMatchLast", () => {
|
||||
it("sorts longest match last", () => {
|
||||
expect(
|
||||
[
|
||||
"org.matrix.msc2762.receive.state_event:*",
|
||||
"org.matrix.msc2762.receive.*",
|
||||
"org.matrix.msc2762.receive.state_event:m.custom*",
|
||||
"org.matrix.msc2762.receive.state_event:m.custom#state_key",
|
||||
"org.matrix.msc2762.receive.state_event:m.custom#*",
|
||||
"*",
|
||||
].sort(sortLongestMatchLast),
|
||||
).toEqual([
|
||||
"*",
|
||||
"org.matrix.msc2762.receive.*",
|
||||
"org.matrix.msc2762.receive.state_event:*",
|
||||
"org.matrix.msc2762.receive.state_event:m.custom*",
|
||||
"org.matrix.msc2762.receive.state_event:m.custom#*",
|
||||
"org.matrix.msc2762.receive.state_event:m.custom#state_key",
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
Copyright 2023 Nordeck IT + Consulting GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { matchPattern } from "./matchPattern";
|
||||
|
||||
describe("matchPattern", () => {
|
||||
it.each([
|
||||
"*",
|
||||
"org.matrix.msc2762.receive.*",
|
||||
"org.matrix.msc2762.receive.state_event:*",
|
||||
"org.matrix.msc2762.receive.state_event:m.custom*",
|
||||
"org.matrix.msc2762.receive.state_event:m.custom#*",
|
||||
"org.matrix.msc2762.receive.state_event:m.custom#state_key",
|
||||
"org.matrix.msc2762.receive.state_event:m.custom#state_key*",
|
||||
])("matches %s", (pattern) => {
|
||||
expect(matchPattern("org.matrix.msc2762.receive.state_event:m.custom#state_key", pattern)).toBe(true);
|
||||
});
|
||||
|
||||
it.each([
|
||||
"org.matrix.msc2762.receive.state_event:",
|
||||
"org.matrix.msc2762.receive.state_event:m.custom",
|
||||
"org.matrix.msc2762.receive.state_event:m.custom#other_key",
|
||||
"org.matrix.msc2762.receive.*:m.custom#state_key",
|
||||
"org.matrix.msc2762.receive.room_event:m.custom",
|
||||
])("does not match %s", (pattern) => {
|
||||
expect(matchPattern("org.matrix.msc2762.receive.state_event:m.custom#state_key", pattern)).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user