diff --git a/conf.ts b/conf.ts index 38763e1..ea86d7b 100644 --- a/conf.ts +++ b/conf.ts @@ -79,3 +79,13 @@ export function getConfig(): Config { }; return conf; } + +/** @internal for test */ +function resetConfig(newConf?: Config): void { + conf = newConf; +} + +/** @internal */ +export const _internal = { + resetConfig, +}; diff --git a/conf_test.ts b/conf_test.ts new file mode 100644 index 0000000..88b7da2 --- /dev/null +++ b/conf_test.ts @@ -0,0 +1,129 @@ +import { + assert, + assertEquals, + assertObjectMatch, + assertThrows, +} from "jsr:@std/assert@0.225.1"; +import { stub } from "jsr:@std/testing@0.224/mock"; +import { basename, isAbsolute } from "jsr:@std/path@0.224.0"; +import { _internal, getConfig } from "./conf.ts"; + +const ENV_VARS: Readonly> = { + DENOPS_TEST_DENOPS_PATH: "denops.vim", + DENOPS_TEST_VIM_EXECUTABLE: undefined, + DENOPS_TEST_NVIM_EXECUTABLE: undefined, + DENOPS_TEST_VERBOSE: undefined, + DENOPS_TEST_CONNECT_TIMEOUT: undefined, +}; + +function stubEnvVars(envVars: Readonly>) { + return stub(Deno.env, "get", (name) => envVars[name]); +} + +function stubConfModule(): Disposable { + const savedConf = getConfig(); + _internal.resetConfig(undefined); + return { + [Symbol.dispose]() { + _internal.resetConfig(savedConf); + }, + }; +} + +Deno.test("getConfig() throws if DENOPS_TEST_DENOPS_PATH env var is not set", () => { + using _module = stubConfModule(); + using _env = stubEnvVars({ ...ENV_VARS, DENOPS_TEST_DENOPS_PATH: undefined }); + assertThrows( + () => { + getConfig(); + }, + Error, + "'DENOPS_TEST_DENOPS_PATH' is required", + ); +}); + +Deno.test("getConfig() returns `{ denopsPath: ... }` with resolved DENOPS_TEST_DENOPS_PATH env var", () => { + using _module = stubConfModule(); + using _env = stubEnvVars({ ...ENV_VARS, DENOPS_TEST_DENOPS_PATH: "foo" }); + const actual = getConfig(); + assert(isAbsolute(actual.denopsPath), "`denopsPath` should be absolute path"); + assertEquals(basename(actual.denopsPath), "foo"); +}); + +Deno.test("getConfig() returns `{ vimExecutable: 'vim' }` if DENOPS_TEST_VIM_EXECUTABLE env var is not set", () => { + using _module = stubConfModule(); + using _env = stubEnvVars({ + ...ENV_VARS, + DENOPS_TEST_VIM_EXECUTABLE: undefined, + }); + const actual = getConfig(); + assertObjectMatch(actual, { vimExecutable: "vim" }); +}); + +Deno.test("getConfig() returns `{ vimExecutable: ... }` with DENOPS_TEST_VIM_EXECUTABLE env var", () => { + using _module = stubConfModule(); + using _env = stubEnvVars({ ...ENV_VARS, DENOPS_TEST_VIM_EXECUTABLE: "foo" }); + const actual = getConfig(); + assertObjectMatch(actual, { vimExecutable: "foo" }); +}); + +Deno.test("getConfig() returns `{ nvimExecutable: 'nvim' }` if DENOPS_TEST_NVIM_EXECUTABLE env var is not set", () => { + using _module = stubConfModule(); + using _env = stubEnvVars({ + ...ENV_VARS, + DENOPS_TEST_NVIM_EXECUTABLE: undefined, + }); + const actual = getConfig(); + assertObjectMatch(actual, { nvimExecutable: "nvim" }); +}); + +Deno.test("getConfig() returns `{ nvimExecutable: ... }` with DENOPS_TEST_NVIM_EXECUTABLE env var", () => { + using _module = stubConfModule(); + using _env = stubEnvVars({ ...ENV_VARS, DENOPS_TEST_NVIM_EXECUTABLE: "foo" }); + const actual = getConfig(); + assertObjectMatch(actual, { nvimExecutable: "foo" }); +}); + +Deno.test("getConfig() returns `{ verbose: false }` if DENOPS_TEST_VERBOSE env var is not set", () => { + using _module = stubConfModule(); + using _env = stubEnvVars({ ...ENV_VARS, DENOPS_TEST_VERBOSE: undefined }); + const actual = getConfig(); + assertObjectMatch(actual, { verbose: false }); +}); + +for ( + const [input, expected] of [ + ["false", false], + ["0", false], + ["invalid", false], + ["true", true], + ["1", true], + ] as const +) { + Deno.test(`getConfig() returns \`{ verbose: ${expected} }\` if DENOPS_TEST_VERBOSE env var is '${input}'`, () => { + using _module = stubConfModule(); + using _env = stubEnvVars({ ...ENV_VARS, DENOPS_TEST_VERBOSE: input }); + const actual = getConfig(); + assertObjectMatch(actual, { verbose: expected }); + }); +} + +for ( + const [input, expected] of [ + ["123", 123], + ["123.456", 123], + ["0", undefined], + ["-123", undefined], + ["string", undefined], + ] as const +) { + Deno.test(`getConfig() returns \`{ connectTimeout: ${expected} }\` if DENOPS_TEST_CONNECT_TIMEOUT env var is '${input}'`, () => { + using _module = stubConfModule(); + using _env = stubEnvVars({ + ...ENV_VARS, + DENOPS_TEST_CONNECT_TIMEOUT: input, + }); + const actual = getConfig(); + assertObjectMatch(actual, { connectTimeout: expected }); + }); +} diff --git a/with_test.ts b/with_test.ts index 88f8b5d..638873c 100644 --- a/with_test.ts +++ b/with_test.ts @@ -1,5 +1,10 @@ -import { assert, assertFalse, assertRejects } from "jsr:@std/assert@0.225.1"; -import { assertSpyCalls, spy } from "jsr:@std/testing@0.224.0/mock"; +import { + assert, + assertEquals, + assertFalse, + assertRejects, +} from "jsr:@std/assert@0.225.1"; +import { assertSpyCalls, spy, stub } from "jsr:@std/testing@0.224.0/mock"; import type { Denops } from "jsr:@denops/core@6.0.6"; import { withDenops } from "./with.ts"; @@ -20,6 +25,78 @@ Deno.test("test(mode:nvim) start nvim to test denops features", async () => { }); for (const mode of ["vim", "nvim"] as const) { + Deno.test(`test(mode:${mode}) outputs ${mode} messages if 'verbose' option is true`, async () => { + using s = stub(console, "error"); + await withDenops("vim", async (denops: Denops) => { + await denops.cmd("echomsg 'foobar'"); + }, { verbose: true }); + // NOTE: Maybe some other values are included, so find target with `Array.some`. + // NOTE: Maybe "\r" or "\n" is prepend or postpend, so use `String.trim`. + assert(s.calls.some((c) => c.args[0].trim() === "foobar")); + }); + + Deno.test(`test(mode:${mode}) should be able to call Denops#redraw()`, async () => { + await withDenops("vim", async (denops: Denops) => { + await denops.redraw(); + await denops.redraw(true); + // FIXME: assert redraw is correctly called. + }); + }); + + Deno.test(`test(mode:${mode}) should be able to call Denops#call()`, async () => { + await withDenops("vim", async (denops: Denops) => { + await denops.call("execute", [`let g:with_test__${mode}__call = 'foo'`]); + assertEquals(await denops.eval(`g:with_test__${mode}__call`), "foo"); + }); + }); + + Deno.test(`test(mode:${mode}) should be able to call Denops#batch()`, async () => { + await withDenops("vim", async (denops: Denops) => { + await denops.batch( + ["execute", [`let g:with_test__${mode}__batch_1 = 'foo'`]], + ["execute", [`let g:with_test__${mode}__batch_2 = 'bar'`]], + ); + assertEquals(await denops.eval(`g:with_test__${mode}__batch_1`), "foo"); + assertEquals(await denops.eval(`g:with_test__${mode}__batch_2`), "bar"); + }); + }); + + Deno.test(`test(mode:${mode}) should be able to call Denops#cmd()`, async () => { + await withDenops("vim", async (denops: Denops) => { + await denops.cmd(`let g:with_test__${mode}__cmd = 'foo'`); + assertEquals(await denops.eval(`g:with_test__${mode}__cmd`), "foo"); + }); + }); + + Deno.test(`test(mode:${mode}) should be able to call Denops#eval()`, async () => { + await withDenops("vim", async (denops: Denops) => { + await denops.eval(`execute('let g:with_test__${mode}__eval = "foo"')`); + assertEquals(await denops.eval(`g:with_test__${mode}__eval`), "foo"); + }); + }); + + Deno.test(`test(mode:${mode}) should be able to call Denops#dispatch()`, async () => { + const api = spy(() => Promise.resolve()); + await withDenops("vim", async (denops: Denops) => { + denops.dispatcher = { + foo: api, + }; + await denops.dispatch(denops.name, "foo", [123, "bar"]); + assertSpyCalls(api, 1); + }); + }); + + Deno.test(`test(mode:${mode}) calls plugin dispatcher from ${mode}`, async () => { + const api = spy(() => Promise.resolve()); + await withDenops("vim", async (denops: Denops) => { + denops.dispatcher = { + foo: api, + }; + await denops.call("denops#notify", denops.name, "foo", [123, "bar"]); + assertSpyCalls(api, 1); + }); + }); + Deno.test(`test(mode:${mode}) rejects if process aborted`, async () => { const fn = spy(() => {}); await assertRejects(