Skip to content

Commit 95a8dad

Browse files
committed
👍 Show stack-trace on plugin error
1 parent 74715fa commit 95a8dad

File tree

3 files changed

+53
-19
lines changed

3 files changed

+53
-19
lines changed

denops/@denops-private/service.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,9 @@ class Plugin {
275275
try {
276276
return await this.#denops.dispatcher[fn](...args);
277277
} catch (err) {
278-
const errMsg = err instanceof Error ? err.message : String(err);
278+
const errMsg = err instanceof Error
279+
? err.stack ?? err.message // Prefer 'stack' if available
280+
: String(err);
279281
throw new Error(
280282
`Failed to call '${fn}' API in '${this.name}': ${errMsg}`,
281283
);

tests/denops/runtime/functions/denops/request_async_test.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
assertArrayIncludes,
33
assertEquals,
44
assertObjectMatch,
5+
assertStringIncludes,
56
} from "jsr:@std/assert@^1.0.1";
67
import { INVALID_PLUGIN_NAMES } from "/denops-testdata/invalid_plugin_names.ts";
78
import { resolveTestDataPath } from "/denops-testdata/resolve.ts";
@@ -131,21 +132,34 @@ testHost({
131132

132133
await t.step("calls failure callback", async () => {
133134
await wait(() => host.call("eval", "len(g:__test_denops_events)"));
135+
const result = await host.call(
136+
"eval",
137+
"g:__test_denops_events",
138+
// deno-lint-ignore no-explicit-any
139+
) as any[];
134140
assertObjectMatch(
135-
await host.call("eval", "g:__test_denops_events") as unknown[],
141+
result,
136142
{
137143
0: [
138144
"TestDenopsRequestAsyncFailure:Called",
139145
[
140146
{
141-
message:
142-
"Failed to call 'fail' API in 'dummy': Dummy failure",
143147
name: "Error",
144148
},
145149
],
146150
],
147151
},
148152
);
153+
const message = result[0][1][0].message as string;
154+
assertStringIncludes(
155+
message,
156+
"Failed to call 'fail' API in 'dummy': Error: Dummy failure",
157+
);
158+
assertStringIncludes(
159+
message,
160+
"dummy_dispatcher_plugin.ts:19:13",
161+
"Error message should include the where the original error occurred",
162+
);
149163
});
150164
});
151165

@@ -165,21 +179,29 @@ testHost({
165179

166180
await t.step("calls failure callback", async () => {
167181
await wait(() => host.call("eval", "len(g:__test_denops_events)"));
182+
const result = await host.call(
183+
"eval",
184+
"g:__test_denops_events",
185+
// deno-lint-ignore no-explicit-any
186+
) as any[];
168187
assertObjectMatch(
169-
await host.call("eval", "g:__test_denops_events") as unknown[],
188+
result,
170189
{
171190
0: [
172191
"TestDenopsRequestAsyncFailure:Called",
173192
[
174193
{
175-
message:
176-
"Failed to call 'not_exist_method' API in 'dummy': this[#denops].dispatcher[fn] is not a function",
177194
name: "Error",
178195
},
179196
],
180197
],
181198
},
182199
);
200+
const message = result[0][1][0].message as string;
201+
assertStringIncludes(
202+
message,
203+
"Failed to call 'not_exist_method' API in 'dummy': TypeError: this[#denops].dispatcher[fn] is not a function",
204+
);
183205
});
184206
});
185207
});

tests/denops/runtime/functions/denops/request_test.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { assertEquals, assertRejects } from "jsr:@std/assert@^1.0.1";
1+
import {
2+
assertEquals,
3+
assertInstanceOf,
4+
assertRejects,
5+
assertStringIncludes,
6+
} from "jsr:@std/assert@^1.0.1";
27
import { INVALID_PLUGIN_NAMES } from "/denops-testdata/invalid_plugin_names.ts";
38
import { resolveTestDataPath } from "/denops-testdata/resolve.ts";
49
import { testHost } from "/denops-testutil/host.ts";
@@ -68,16 +73,21 @@ testHost({
6873

6974
await t.step("if the dispatcher method throws an error", async (t) => {
7075
await t.step("throws an error", async () => {
71-
await assertRejects(
72-
() =>
73-
host.call(
74-
"denops#request",
75-
"dummy",
76-
"fail",
77-
["foo"],
78-
),
79-
Error,
80-
"Failed to call 'fail' API in 'dummy': Dummy failure",
76+
const result = await host.call(
77+
"denops#request",
78+
"dummy",
79+
"fail",
80+
["foo"],
81+
).catch((e) => e);
82+
assertInstanceOf(result, Error);
83+
assertStringIncludes(
84+
result.message,
85+
"Failed to call 'fail' API in 'dummy': Error: Dummy failure",
86+
);
87+
assertStringIncludes(
88+
result.message,
89+
"dummy_dispatcher_plugin.ts:19:13",
90+
"Error message should include the where the original error occurred",
8191
);
8292
});
8393
});
@@ -93,7 +103,7 @@ testHost({
93103
["foo"],
94104
),
95105
Error,
96-
"Failed to call 'not_exist_method' API in 'dummy': this[#denops].dispatcher[fn] is not a function",
106+
"Failed to call 'not_exist_method' API in 'dummy': TypeError: this[#denops].dispatcher[fn] is not a function",
97107
);
98108
});
99109
});

0 commit comments

Comments
 (0)