



























@@ -81,17 +81,40 @@ function expectRemovedPluginWarnings(
8181expect(result.ok).toBe(true);
8282if (result.ok) {
8383const message = `plugin removed: ${removedLabel} (stale config entry ignored; remove it from plugins config)`;
84-expect(result.warnings).toEqual(
85-expect.arrayContaining([
86-{ path: `plugins.entries.${removedId}`, message },
87-{ path: "plugins.allow", message },
88-{ path: "plugins.deny", message },
89-{ path: "plugins.slots.memory", message },
90-]),
91-);
84+expectPathMessage(result.warnings, `plugins.entries.${removedId}`, message);
85+expectPathMessage(result.warnings, "plugins.allow", message);
86+expectPathMessage(result.warnings, "plugins.deny", message);
87+expectPathMessage(result.warnings, "plugins.slots.memory", message);
9288}
9389}
949091+function expectPathMessage(
92+entries: readonly { path: string; message: string }[] | undefined,
93+pathValue: string,
94+message: string,
95+) {
96+expect(entries?.some((entry) => entry.path === pathValue && entry.message === message)).toBe(
97+true,
98+);
99+}
100+101+function expectPathMessageIncludes(
102+entries: readonly { path: string; message: string }[] | undefined,
103+pathValue: string,
104+fragment: string,
105+) {
106+expect(
107+entries?.some((entry) => entry.path === pathValue && entry.message.includes(fragment)),
108+).toBe(true);
109+}
110+111+function expectNoPath(
112+entries: readonly { path: string; message: string }[] | undefined,
113+pathValue: string,
114+) {
115+expect(entries?.some((entry) => entry.path === pathValue)).toBe(false);
116+}
117+95118describe("config plugin validation", () => {
96119let fixtureRoot = "";
97120let suiteHome = "";
@@ -234,12 +257,8 @@ describe("config plugin validation", () => {
234257});
235258expect(res.ok).toBe(false);
236259if (!res.ok) {
237-expect(res.issues).toEqual(
238-expect.arrayContaining([
239-{ path: "plugins.deny", message: "plugin not found: missing-deny" },
240-{ path: "plugins.slots.memory", message: "plugin not found: missing-slot" },
241-]),
242-);
260+expectPathMessage(res.issues, "plugins.deny", "plugin not found: missing-deny");
261+expectPathMessage(res.issues, "plugins.slots.memory", "plugin not found: missing-slot");
243262expect(res.warnings).toContainEqual({
244263path: "plugins.allow",
245264message:
@@ -276,12 +295,8 @@ describe("config plugin validation", () => {
276295expect(res.ok).toBe(true);
277296const message =
278297"plugin not installed: brave — install the official external plugin with: openclaw plugins install @openclaw/brave-plugin";
279-expect(res.warnings ?? []).toEqual(
280-expect.arrayContaining([
281-{ path: "plugins.entries.brave", message },
282-{ path: "plugins.allow", message },
283-]),
284-);
298+expectPathMessage(res.warnings, "plugins.entries.brave", message);
299+expectPathMessage(res.warnings, "plugins.allow", message);
285300expect(
286301(res.warnings ?? []).some(
287302(warning) =>
@@ -310,17 +325,15 @@ describe("config plugin validation", () => {
310325if (!res.ok) {
311326return;
312327}
313-expect(res.warnings).toEqual(
314-expect.arrayContaining([
315-expect.objectContaining({
316-path: "plugins.entries.blocked-plugin",
317-message: expect.stringContaining("plugin present but blocked: blocked-plugin"),
318-}),
319-expect.objectContaining({
320-path: "plugins.allow",
321-message: expect.stringContaining("plugin present but blocked: blocked-plugin"),
322-}),
323-]),
328+expectPathMessageIncludes(
329+res.warnings,
330+"plugins.entries.blocked-plugin",
331+"plugin present but blocked: blocked-plugin",
332+);
333+expectPathMessageIncludes(
334+res.warnings,
335+"plugins.allow",
336+"plugin present but blocked: blocked-plugin",
324337);
325338expect(
326339res.warnings.some(
@@ -367,17 +380,15 @@ describe("config plugin validation", () => {
367380if (!res.ok) {
368381return;
369382}
370-expect(res.warnings).toEqual(
371-expect.arrayContaining([
372-expect.objectContaining({
373-path: "plugins.entries.blocked-plugin",
374-message: expect.stringContaining("plugin present but blocked: blocked-plugin"),
375-}),
376-expect.objectContaining({
377-path: "plugins.allow",
378-message: expect.stringContaining("plugin present but blocked: blocked-plugin"),
379-}),
380-]),
383+expectPathMessageIncludes(
384+res.warnings,
385+"plugins.entries.blocked-plugin",
386+"plugin present but blocked: blocked-plugin",
387+);
388+expectPathMessageIncludes(
389+res.warnings,
390+"plugins.allow",
391+"plugin present but blocked: blocked-plugin",
381392);
382393expect(
383394res.warnings.some((warning) => warning.message.includes("plugin not found: blocked-plugin")),
@@ -421,28 +432,20 @@ describe("config plugin validation", () => {
421432if (!res.ok) {
422433return;
423434}
424-expect(res.warnings).toEqual(
425-expect.arrayContaining([
426-expect.objectContaining({
427-path: "plugins.entries.actual-id",
428-message: expect.stringContaining("plugin present but blocked: actual-id"),
429-}),
430-expect.objectContaining({
431-path: "plugins.allow",
432-message: expect.stringContaining("plugin present but blocked: actual-id"),
433-}),
434-expect.objectContaining({
435-path: "plugins.entries.alias-dir",
436-message:
437-"plugin not found: alias-dir (stale config entry ignored; remove it from plugins config)",
438-}),
439-expect.objectContaining({
440-path: "plugins.allow",
441-message:
442-"plugin not found: alias-dir (stale config entry ignored; remove it from plugins config)",
443-}),
444-]),
435+expectPathMessageIncludes(
436+res.warnings,
437+"plugins.entries.actual-id",
438+"plugin present but blocked: actual-id",
445439);
440+expectPathMessageIncludes(
441+res.warnings,
442+"plugins.allow",
443+"plugin present but blocked: actual-id",
444+);
445+const aliasMessage =
446+"plugin not found: alias-dir (stale config entry ignored; remove it from plugins config)";
447+expectPathMessage(res.warnings, "plugins.entries.alias-dir", aliasMessage);
448+expectPathMessage(res.warnings, "plugins.allow", aliasMessage);
446449expect(
447450res.warnings.some((warning) =>
448451warning.message.includes("plugin present but blocked: alias-dir"),
@@ -502,7 +505,7 @@ describe("config plugin validation", () => {
502505path: "channels.telegarm",
503506message: "unknown channel id: telegarm",
504507});
505-expect(res.warnings).not.toContainEqual(expect.objectContaining({ path: "channels.telegarm" }));
508+expectNoPath(res.warnings, "channels.telegarm");
506509});
507510508511it("warns when plugins.allow contains a channel id without a plugin manifest (#76872)", () => {
@@ -718,11 +721,10 @@ describe("config plugin validation", () => {
718721719722expect(res.ok).toBe(false);
720723if (!res.ok) {
721-expect(res.issues).toContainEqual(
722-expect.objectContaining({
723-path: "plugins.entries.codex.config.codexPlugins.plugins.github.marketplaceName",
724-message: expect.stringContaining("invalid config"),
725-}),
724+expectPathMessageIncludes(
725+res.issues,
726+"plugins.entries.codex.config.codexPlugins.plugins.github.marketplaceName",
727+"invalid config",
726728);
727729expect(
728730res.issues.some(
@@ -775,11 +777,9 @@ describe("config plugin validation", () => {
775777const issue = res.issues.find(
776778(entry) => entry.path === "plugins.entries.enum-plugin.config.fileFormat",
777779);
778-expect(issue).toMatchObject({
779-message: expect.stringContaining('allowed: "markdown", "html"'),
780-allowedValues: ["markdown", "html"],
781-allowedValuesHiddenCount: 0,
782-});
780+expect(issue?.message).toContain('allowed: "markdown", "html"');
781+expect(issue?.allowedValues).toEqual(["markdown", "html"]);
782+expect(issue?.allowedValuesHiddenCount).toBe(0);
783783}
784784});
785785此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。