| 
 | 1 | +/* eslint-env jest */  | 
 | 2 | +import * as cp from 'child_process';  | 
 | 3 | +import * as path from 'path';  | 
 | 4 | + | 
 | 5 | +import { NeovimClient, attach, findNvim } from 'neovim';  | 
 | 6 | + | 
 | 7 | +/**  | 
 | 8 | + * Runs a program and returns its output.  | 
 | 9 | + */  | 
 | 10 | +async function run(cmd: string, args: string[]) {  | 
 | 11 | +  return new Promise<{ proc: ReturnType<typeof cp.spawn>, stdout: string, stderr: string}>((resolve, reject) => {  | 
 | 12 | +    const proc = cp.spawn(cmd, args, { shell: false });  | 
 | 13 | +    const rv = {  | 
 | 14 | +      proc: proc,  | 
 | 15 | +      stdout: '',  | 
 | 16 | +      stderr: '',  | 
 | 17 | +    }  | 
 | 18 | + | 
 | 19 | +    proc.stdout.on('data', (data) => {  | 
 | 20 | +      rv.stdout += data.toString();  | 
 | 21 | +    });  | 
 | 22 | + | 
 | 23 | +    proc.stderr.on('data', (data) => {  | 
 | 24 | +      rv.stderr += data.toString();  | 
 | 25 | +    });  | 
 | 26 | + | 
 | 27 | +    proc.on('exit', (code_) => {  | 
 | 28 | +      resolve(rv);  | 
 | 29 | +    });  | 
 | 30 | + | 
 | 31 | +    proc.on('error', (e) => {  | 
 | 32 | +      reject(e);  | 
 | 33 | +    });  | 
 | 34 | +  });  | 
 | 35 | +}  | 
 | 36 | + | 
 | 37 | +describe('Node host2', () => {  | 
 | 38 | +  const thisDir = path.resolve(__dirname);  | 
 | 39 | +  const pluginDir = path.resolve(thisDir, '../../example-plugin2/');  | 
 | 40 | +  const pluginMain = path.resolve(pluginDir, 'index.js').replace(/\\/g, '/');  | 
 | 41 | + | 
 | 42 | +  const testdir = process.cwd();  | 
 | 43 | +  let nvimProc: ReturnType<typeof cp.spawn>;  | 
 | 44 | +  let nvim: NeovimClient;  | 
 | 45 | + | 
 | 46 | +  beforeAll(async () => {  | 
 | 47 | +    const minVersion = '0.9.5'  | 
 | 48 | +    const nvimInfo = findNvim({ minVersion: minVersion });  | 
 | 49 | +    const nvimPath = nvimInfo.matches[0]?.path;  | 
 | 50 | +    if (!nvimPath) {  | 
 | 51 | +      throw new Error(`nvim ${minVersion} not found`)  | 
 | 52 | +    }  | 
 | 53 | + | 
 | 54 | +    nvimProc = cp.spawn(nvimPath, ['--clean', '-n', '--headless', '--embed'], {});  | 
 | 55 | +    nvim = attach({ proc: nvimProc });  | 
 | 56 | +  });  | 
 | 57 | + | 
 | 58 | +  afterAll(() => {  | 
 | 59 | +    process.chdir(testdir);  | 
 | 60 | +    nvim.quit();  | 
 | 61 | +    if (nvimProc && nvimProc.connected) {  | 
 | 62 | +      nvimProc.disconnect();  | 
 | 63 | +    }  | 
 | 64 | +  });  | 
 | 65 | + | 
 | 66 | +  beforeEach(() => {});  | 
 | 67 | + | 
 | 68 | +  afterEach(() => {});  | 
 | 69 | + | 
 | 70 | + | 
 | 71 | +  /**  | 
 | 72 | +   * From the Nvim process, starts a new "node …/plugin/index.js" RPC job (that  | 
 | 73 | +   * is, a node "plugin host", aka an Nvim node client).  | 
 | 74 | +   */  | 
 | 75 | +  async function newPluginChan() {  | 
 | 76 | +    const nodePath = process.argv0.replace(/\\/g, '/');  | 
 | 77 | +    const luacode = `  | 
 | 78 | +      -- "node …/plugin/index.js"  | 
 | 79 | +      local argv = { [[${nodePath}]], [[${pluginMain}]] }  | 
 | 80 | +      local chan = vim.fn.jobstart(argv, { rpc = true, stderr_buffered = true })  | 
 | 81 | +      return chan  | 
 | 82 | +    `  | 
 | 83 | +    return await nvim.lua(luacode);  | 
 | 84 | +  }  | 
 | 85 | + | 
 | 86 | +  it('`node plugin.js --version` prints node-client version', async () => {  | 
 | 87 | +    //process.chdir(thisDir);  | 
 | 88 | +    const proc = await run(process.argv0, [pluginMain, '--version']);  | 
 | 89 | +    // "5.1.1-dev.0\n"  | 
 | 90 | +    expect(proc.stdout).toMatch(/\d+\.\d+\.\d+/);  | 
 | 91 | + | 
 | 92 | +    proc.proc.kill('SIGKILL');  | 
 | 93 | +  });  | 
 | 94 | + | 
 | 95 | +  it('responds to "poll" with "ok"', async () => {  | 
 | 96 | +    // See also the old provider#Poll() function.  | 
 | 97 | + | 
 | 98 | +    // From Nvim, start an "node …/plugin/index.js" RPC job.  | 
 | 99 | +    // Then use that channel to call methods on the remote plugin.  | 
 | 100 | +    const chan = await newPluginChan();  | 
 | 101 | +    const rv = await nvim.lua(`return vim.rpcrequest(..., 'poll')`, [ chan ]);  | 
 | 102 | + | 
 | 103 | +    expect(rv).toEqual('ok');  | 
 | 104 | +  });  | 
 | 105 | + | 
 | 106 | +  //it('responds to "nvim_xx" methods', async () => {  | 
 | 107 | +  //  // This is just a happy accident of the fact that Nvim plugin host === client.  | 
 | 108 | +  //  const chan = await newPluginChan();  | 
 | 109 | +  //  const rv = await nvim.lua(`return vim.rpcrequest(..., 'nvim_eval', '1 + 3')`, [ chan ]);  | 
 | 110 | +  //  expect(rv).toEqual(3);  | 
 | 111 | +  //});  | 
 | 112 | + | 
 | 113 | +  it('responds to custom, plugin-defined methods', async () => {  | 
 | 114 | +    const chan = await newPluginChan();  | 
 | 115 | +    // The "testMethod1" function is defined in …/example-plugin2/index.js.  | 
 | 116 | +    const rv = await nvim.lua(`return vim.rpcrequest(..., 'testMethod1', {})`, [ chan ]);  | 
 | 117 | + | 
 | 118 | +    expect(rv).toEqual('called hostTest');  | 
 | 119 | +  });  | 
 | 120 | + | 
 | 121 | +  // TODO  | 
 | 122 | +  //it('Lua plugin can define autocmds/functions that call the remote plugin', async () => {  | 
 | 123 | +  //  // JSHostTestCmd  | 
 | 124 | +  //  // BufEnter  | 
 | 125 | +  //});  | 
 | 126 | +});  | 
 | 127 | + | 
0 commit comments