diff options
| -rw-r--r-- | .circleci/config.yml | 1 | ||||
| -rw-r--r-- | QA.md | 33 | ||||
| -rw-r--r-- | e2e/clipboard.test.js | 123 | ||||
| -rw-r--r-- | e2e/completion.test.js | 136 | ||||
| -rw-r--r-- | e2e/completion_buffers.test.js | 214 | ||||
| -rw-r--r-- | e2e/completion_open.test.js | 132 | ||||
| -rw-r--r-- | e2e/completion_set.test.js | 75 | ||||
| -rw-r--r-- | e2e/lib/Console.js | 41 | ||||
| -rw-r--r-- | e2e/lib/clipboard.js | 63 | ||||
| -rw-r--r-- | package-lock.json | 2 | 
10 files changed, 788 insertions, 32 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index 2d83bc3..3a2bae4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -70,6 +70,7 @@ jobs:      executor:        name: default      steps: +      - run: sudo apt-get update && sudo apt-get -y install xsel        - install_firefox        - checkout        - setup_npm @@ -10,17 +10,13 @@ The behaviors of the console are tested in [Console section](#consoles).  #### Misc -- [ ] <kbd>y</kbd>: yank current URL and show a message -- [ ] <kbd>p</kbd>: open clipboard's URL in current tab -- [ ] <kbd>P</kbd>: open clipboard's URL in new tab -- [ ] <kbd>p</kbd>: search clipboard's keywords in current tab -- [ ] <kbd>P</kbd>: search clipboard's keywords in new tab  - [ ] Toggle enabled/disabled of plugin bu <kbd>Shift</kbd>+<kbd>Esc</kbd>  - [ ] Hide error and info console by <kbd>Esc</kbd>  - [ ] Vim-Vixen icons changes on <kbd>Shift</kbd>+<kbd>Esc</kbd>  - [ ] Add-on is enabled and disabled by clicking the indicator on the tool bar.  - [ ] The indicator changed on selected tab changed (changes add-on enabled)  - [ ] Notify to users on add-on updated at first time. +- [ ] Reopen tab on *only current window* by <kbd>u</kbd>  ### Following links @@ -31,6 +27,7 @@ The behaviors of the console are tested in [Console section](#consoles).  - [ ] Select link and open it in new tab in `<iframe>`/`<frame`> on following by <kbd>F</kbd>  - [ ] Select link and open it in `<area>` tags, for <kbd>f</kbd> and <kbd>F</kbd>  - [ ] Open new tab in background by `"background": true` +- [ ] Opened tabs is in child on Tree Style Tab  ### Consoles @@ -38,40 +35,14 @@ The behaviors of the console are tested in [Console section](#consoles).  - [ ] `<EMPTY>`: do nothing -### Completions - -#### History and search engines - -- [ ] `open<SP>`: show all engines and some history items -- [ ] `open g`: complete search engines starts with `g` and matched with keywords `g` -- [ ] `open foo bar`: complete history items matched with keywords `foo` and `bar` -- [ ] `set `: show prperties starts with keywords -- [ ] The completions shows histories, search engines, and bookmarks. -- [ ] also `tabopen` and `winopen` -- shortening commands such as `o` are not test in this release -- [ ] Complete commands matched with input keywords in the prefix. - -#### Buffer command - -- [ ] `buffer<SP>`: show all opened tabs in completion -- [ ] `buffer x`: show tabs which has title and URL matches with `x` -- [ ] shows tab index and marks - -#### Buffer command - -- [ ] `bdelete`, `bdeletes`: show tabs excluding pinned tabs -- [ ] `bdelete!`, `bdeletes!`: show tabs including pinned tabs -  #### Misc  - [ ] Select next item by <kbd>Tab</kbd> and previous item by <kbd>Shift</kbd>+<kbd>Tab</kbd> -- [ ] Reopen tab on *only current window* by <kbd>u</kbd>  ### Properties  - [ ] Configure custom hint character by `:set hintchars=012345678`  - [ ] Configure custom hint character by settings `"hintchars": "012345678"` in add-on preferences -- [ ] Opened tabs is in child on Tree Style Tab  - [ ] Smooth scroll by `:set smoothscroll`  - [ ] Non-smooth scroll by `:set nosmoothscroll` diff --git a/e2e/clipboard.test.js b/e2e/clipboard.test.js new file mode 100644 index 0000000..82e45fc --- /dev/null +++ b/e2e/clipboard.test.js @@ -0,0 +1,123 @@ +const express = require('express'); +const lanthan = require('lanthan'); +const path = require('path'); +const assert = require('assert'); +const eventually = require('./eventually'); +const clipboard = require('./lib/clipboard'); +const settings = require('./settings'); + +const Key = lanthan.Key; + +const newApp = () => { +  let app = express(); +  app.get('/', (req, res) => { +    res.status(200).send(`<html lang="en"></html">`); +  }); +  return app; +}; + +describe("navigate test", () => { + +  const port = 12321; +  let http; +  let firefox; +  let session; +  let browser; + +  before(async() => { +    http = newApp().listen(port); + +    firefox = await lanthan.firefox({ +      spy: path.join(__dirname, '..'), +    }); +    session = firefox.session; +    browser = firefox.browser; + +    await browser.storage.local.set({ +      settings, +    }); +  }); + +  after(async() => { +    if (firefox) { +      await firefox.close(); +    } +    http.close(); +  }); + +  beforeEach(async() => { +    let tabs = await browser.tabs.query({}); +    for (let tab of tabs.slice(1)) { +      await browser.tabs.remove(tab.id); +    } +  }) + +  it('should copy current URL by y', async () => { +    await session.navigateTo(`http://127.0.0.1:${port}/#should_copy_url`); +    let body = await session.findElementByCSS('body'); + +    await body.sendKeys('y'); +    await eventually(async() => { +      let data = await clipboard.read(); +      assert.equal(data, `http://127.0.0.1:${port}/#should_copy_url`); +    }); +  }); + +  it('should open an URL from clipboard by p', async () => { +    await session.navigateTo(`http://127.0.0.1:${port}/`); +    let body = await session.findElementByCSS('body'); + +    await clipboard.write(`http://127.0.0.1:${port}/#open_from_clipboard`); +    await body.sendKeys('p'); + +    await eventually(async() => { +      let tabs = await browser.tabs.query({ active: true }); +      assert.equal(tabs[0].url, `http://127.0.0.1:${port}/#open_from_clipboard`); +    }); +  }); + +  it('should open an URL from clipboard to new tab by P', async () => { +    await session.navigateTo(`http://127.0.0.1:${port}/`); +    let body = await session.findElementByCSS('body'); + +    await clipboard.write(`http://127.0.0.1:${port}/#open_to_new_tab`); +    await body.sendKeys(Key.Shift, 'p'); + +    await eventually(async() => { +      let tabs = await browser.tabs.query({}); +      assert.deepEqual(tabs.map(t => t.url), [ +        `http://127.0.0.1:${port}/`, +        `http://127.0.0.1:${port}/#open_to_new_tab`, +      ]); +    }); +  }); + +  it('should open search result with keywords in clipboard by p', async () => { +    await session.navigateTo(`http://127.0.0.1:${port}/`); +    let body = await session.findElementByCSS('body'); + +    await clipboard.write(`an apple`); +    await body.sendKeys('p'); + +    await eventually(async() => { +      let tabs = await browser.tabs.query({}); +      assert.equal(tabs[0].url, `http://127.0.0.1:${port}/google?q=an%20apple`); +    }); +  }); + +  it('should open search result with keywords in clipboard to new tabby P', async () => { +    await session.navigateTo(`http://127.0.0.1:${port}/`); +    let body = await session.findElementByCSS('body'); + +    await clipboard.write(`an apple`); +    await body.sendKeys(Key.Shift, 'p'); + +    await eventually(async() => { +      let tabs = await browser.tabs.query({}); +      assert.deepEqual(tabs.map(t => t.url), [ +        `http://127.0.0.1:${port}/`, +        `http://127.0.0.1:${port}/google?q=an%20apple`, +      ]); +    }); +  }); +}); diff --git a/e2e/completion.test.js b/e2e/completion.test.js new file mode 100644 index 0000000..5d910c6 --- /dev/null +++ b/e2e/completion.test.js @@ -0,0 +1,136 @@ +const express = require('express'); +const lanthan = require('lanthan'); +const path = require('path'); +const assert = require('assert'); +const eventually = require('./eventually'); +const settings = require('./settings'); +const Console = require('./lib/Console'); + +const Key = lanthan.Key; + +const newApp = () => { +  let app = express(); +  app.get('/', (req, res) => { +    res.send(`<!DOCTYPEhtml> +<html lang="en"> +  <body>ok</body> +</html">`); +  }); +  return app; +}; + +describe("general completion test", () => { +  const port = 12321; +  let http; +  let firefox; +  let session; +  let browser; +  let body; + +  before(async() => { +    firefox = await lanthan.firefox({ +      spy: path.join(__dirname, '..'), +      builderf: (builder) => { +        builder.addFile('build/settings.js'); +      }, +    }); +    session = firefox.session; +    browser = firefox.browser; +    http = newApp().listen(port); + +    await browser.storage.local.set({ +      settings, +    }); +  }); + +  after(async() => { +    http.close(); +    if (firefox) { +      await firefox.close(); +    } +  }); + +  beforeEach(async() => { +    await session.navigateTo(`http://127.0.0.1:${port}`); +    body = await session.findElementByCSS('body'); +  }); + +  it('should all commands on empty line', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); + +    await eventually(async() => { +      let items = await c.getCompletions(); +      assert.equal(items.length, 10); +      assert.deepEqual(items[0], { type: 'title', text: 'Console Command' }); +      assert(items[1].text.startsWith('set')) +      assert(items[2].text.startsWith('open')) +      assert(items[3].text.startsWith('tabopen')) +    }); +  }); + +  it('should only commands filtered by prefix', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('b'); + +    await eventually(async() => { +      let items = await c.getCompletions(); +      assert.equal(items.length, 4); +      assert.deepEqual(items[0], { type: 'title', text: 'Console Command' }); +      assert(items[1].text.startsWith('buffer')) +      assert(items[2].text.startsWith('bdelete')) +      assert(items[3].text.startsWith('bdeletes')) +    }); +  }); + +  it('selects completion items by <Tab>/<S-Tab> keys', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('b'); + +    await eventually(async() => { +      let items = await c.getCompletions(); +      assert.equal(items.length, 4); +    }); + +    await c.sendKeys(Key.Tab); +    await eventually(async() => { +      let items = await c.getCompletions(); +      assert(items[1].highlight) + +      let v = await c.currentValue(); +      assert.equal(v, 'buffer'); +    }); + +    await c.sendKeys(Key.Tab, Key.Tab); +    await eventually(async() => { +      let items = await c.getCompletions(); +      assert(items[3].highlight) + +      let v = await c.currentValue(); +      assert.equal(v, 'bdeletes'); +    }); + +    await c.sendKeys(Key.Tab); +    await eventually(async() => { +      let v = await c.currentValue(); +      assert.equal(v, 'b'); +    }); + +    await c.sendKeys(Key.Shift, Key.Tab); +    await eventually(async() => { +      let items = await c.getCompletions(); +      assert(items[3].highlight) + +      let v = await c.currentValue(); +      assert.equal(v, 'bdeletes'); +    }); +  }); +}); diff --git a/e2e/completion_buffers.test.js b/e2e/completion_buffers.test.js new file mode 100644 index 0000000..de26747 --- /dev/null +++ b/e2e/completion_buffers.test.js @@ -0,0 +1,214 @@ +const express = require('express'); +const lanthan = require('lanthan'); +const path = require('path'); +const assert = require('assert'); +const eventually = require('./eventually'); +const settings = require('./settings'); +const Console = require('./lib/Console'); + +const Key = lanthan.Key; + +const newApp = () => { + +  let app = express(); +  app.get('/*', (req, res) => { +    res.send(`<!DOCTYPEhtml> +<html lang="en"> +  <head> +    <title>title_${req.path.slice(1)}</title> +  </head> +  <body><h1>home</h1></body> +</html">`); +  }); +  return app; +}; + +describe("completion on buffer/bdelete/bdeletes", () => { +  const port = 12321; +  let http; +  let firefox; +  let session; +  let browser; +  let body; + +  before(async() => { +    firefox = await lanthan.firefox({ +      spy: path.join(__dirname, '..'), +      builderf: (builder) => { +        builder.addFile('build/settings.js'); +      }, +    }); +    session = firefox.session; +    browser = firefox.browser; +    http = newApp().listen(port); + +    await browser.storage.local.set({ +      settings, +    }); +  }); + +  after(async() => { +    http.close(); +    if (firefox) { +      await firefox.close(); +    } +  }); + +  beforeEach(async() => { +    let tabs = await browser.tabs.query({}); +    for (let tab of tabs.slice(1)) { +      await browser.tabs.remove(tab.id); +    } + +    await browser.tabs.update(tabs[0].id, { url: `http://127.0.0.1:${port}/site1`, pinned: true }); +    await browser.tabs.create({ url: `http://127.0.0.1:${port}/site2`, pinned: true }) +    for (let i = 3; i <= 5; ++i) { +      await browser.tabs.create({ url: `http://127.0.0.1:${port}/site${i}` }) +    } + +    await eventually(async() => { +      let handles = await session.getWindowHandles(); +      assert.equal(handles.length, 5); +      await session.switchToWindow(handles[2]); +      await session.findElementByCSS('iframe'); +    }); +    body = await session.findElementByCSS('body'); + +    await new Promise((resolve) => setTimeout(resolve, 100)); +  }); + +  it('should all tabs by "buffer" command with empty params', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('buffer '); + +    await eventually(async() => { +      let items = await c.getCompletions(); +      assert.equal(items.length, 6); +      assert.deepEqual(items[0], { type: 'title', text: 'Buffers' }); +      assert(items[1].text.startsWith('1:')); +      assert(items[2].text.startsWith('2:')); +      assert(items[3].text.startsWith('3:')); +      assert(items[4].text.startsWith('4:')); +      assert(items[5].text.startsWith('5:')); + +      assert(items[3].text.includes('%')); +      assert(items[5].text.includes('#')); +    }); +  }) + +  it('should filter items with URLs by keywords on "buffer" command', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('buffer title_site2'); + +    await eventually(async() => { +      let items = await c.getCompletions(); +      assert.deepEqual(items[0], { type: 'title', text: 'Buffers' }); +      assert(items[1].text.startsWith('2:')); +      assert(items[1].text.includes('title_site2')); +      assert(items[1].text.includes(`http://127.0.0.1:${port}/site2`)); +    }); +  }) + +  it('should filter items with titles by keywords on "buffer" command', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('buffer /site2'); + +    await eventually(async() => { +      let items = await c.getCompletions(); +      assert.deepEqual(items[0], { type: 'title', text: 'Buffers' }); +      assert(items[1].text.startsWith('2:')); +    }); +  }) + +  it('should show one item by number on "buffer" command', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('buffer 2'); + +    await eventually(async() => { +      let items = await c.getCompletions(); +      assert.equal(items.length, 2); +      assert.deepEqual(items[0], { type: 'title', text: 'Buffers' }); +      assert(items[1].text.startsWith('2:')); +    }); +  }) + +  it('should show unpinned tabs "bdelete" command', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('bdelete site'); + +    await eventually(async() => { +      let items = await c.getCompletions(); +      assert.equal(items.length, 4); +      assert(items[1].text.includes('site3')); +      assert(items[2].text.includes('site4')); +      assert(items[3].text.includes('site5')); +    }); +  }) + +  it('should show unpinned tabs "bdeletes" command', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('bdelete site'); + +    await eventually(async() => { +      let items = await c.getCompletions(); +      assert.equal(items.length, 4); +      assert(items[1].text.includes('site3')); +      assert(items[2].text.includes('site4')); +      assert(items[3].text.includes('site5')); +    }); +  }) + +  it('should show both pinned and unpinned tabs "bdelete!" command', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('bdelete! site'); + +    await eventually(async() => { +      let items = await c.getCompletions(); +      assert.equal(items.length, 6); +      assert(items[1].text.includes('site1')); +      assert(items[2].text.includes('site2')); +      assert(items[3].text.includes('site3')); +      assert(items[4].text.includes('site4')); +      assert(items[5].text.includes('site5')); +    }); +  }) + +  it('should show both pinned and unpinned tabs "bdeletes!" command', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('bdeletes! site'); + +    await eventually(async() => { +      let items = await c.getCompletions(); +      assert.equal(items.length, 6); +      assert(items[1].text.includes('site1')); +      assert(items[2].text.includes('site2')); +      assert(items[3].text.includes('site3')); +      assert(items[4].text.includes('site4')); +      assert(items[5].text.includes('site5')); +    }); +  }) +}); diff --git a/e2e/completion_open.test.js b/e2e/completion_open.test.js new file mode 100644 index 0000000..59d6b83 --- /dev/null +++ b/e2e/completion_open.test.js @@ -0,0 +1,132 @@ +const express = require('express'); +const lanthan = require('lanthan'); +const path = require('path'); +const assert = require('assert'); +const eventually = require('./eventually'); +const settings = require('./settings'); +const Console = require('./lib/Console'); + +const Key = lanthan.Key; + +const newApp = () => { + +  let app = express(); +  app.get('/', (req, res) => { +    res.send(`<!DOCTYPEhtml> +<html lang="en"> +  <body>ok</body> +</html">`); +  }); +  return app; +}; + +describe("completion on open/tabopen/winopen commands", () => { +  const port = 12321; +  let http; +  let firefox; +  let session; +  let browser; +  let body; + +  before(async() => { +    firefox = await lanthan.firefox({ +      spy: path.join(__dirname, '..'), +      builderf: (builder) => { +        builder.addFile('build/settings.js'); +      }, +    }); +    session = firefox.session; +    browser = firefox.browser; +    http = newApp().listen(port); + +    await browser.storage.local.set({ +      settings, +    }); +     +    // Add item into hitories +    await session.navigateTo(`https://i-beam.org`); +  }); + +  after(async() => { +    http.close(); +    if (firefox) { +      await firefox.close(); +    } +  }); + +  beforeEach(async() => { +    await session.navigateTo(`http://127.0.0.1:${port}`); +    body = await session.findElementByCSS('body'); +  }); + +  it('should show completions from search engines, bookmarks, and histories by "open" command', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('open '); + +    await eventually(async() => { +      let completions = await c.getCompletions(); +      assert(completions.find(x => x.type === 'title' && x.text === 'Search Engines')); +      assert(completions.find(x => x.type === 'title' && x.text === 'Bookmarks')); +      assert(completions.find(x => x.type === 'title' && x.text === 'History')); +    }); +  }); + +  it('should filter items with URLs by keywords on "open" command', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('open https://'); + +    await eventually(async() => { +      let completions = await c.getCompletions(); +      let items = completions.filter(x => x.type === 'item').map(x => x.text); +      assert(items.every(x => x.includes('https://'))); +    }); +  }) + +  it('should filter items with titles by keywords on "open" command', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('open getting'); + +    await eventually(async() => { +      let completions = await c.getCompletions(); +      let items = completions.filter(x => x.type === 'item').map(x => x.text); +      assert(items.every(x => x.toLowerCase().includes('getting'))); +    }); +  }) + +  it('should filter items with titles by keywords on "tabopen" command', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('tabopen https://'); + +    await eventually(async() => { +      let completions = await c.getCompletions(); +      let items = completions.filter(x => x.type === 'item').map(x => x.text); +      assert(items.every(x => x.includes('https://'))); +    }); +  }) + +  it('should filter items with titles by keywords on "winopen" command', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('winopen https://'); + +    await eventually(async() => { +      let completions = await c.getCompletions(); +      let items = completions.filter(x => x.type === 'item').map(x => x.text); +      assert(items.every(x => x.includes('https://'))); +    }); +  }) +}); diff --git a/e2e/completion_set.test.js b/e2e/completion_set.test.js new file mode 100644 index 0000000..cf5ff5b --- /dev/null +++ b/e2e/completion_set.test.js @@ -0,0 +1,75 @@ +const express = require('express'); +const lanthan = require('lanthan'); +const path = require('path'); +const assert = require('assert'); +const eventually = require('./eventually'); +const settings = require('./settings'); +const Console = require('./lib/Console'); + +const Key = lanthan.Key; + +describe("completion on set commands", () => { +  const port = 12321; +  let firefox; +  let session; +  let browser; +  let body; + +  before(async() => { +    firefox = await lanthan.firefox({ +      spy: path.join(__dirname, '..'), +      builderf: (builder) => { +        builder.addFile('build/settings.js'); +      }, +    }); +    session = firefox.session; +    browser = firefox.browser; + +    await browser.storage.local.set({ +      settings, +    }); +  }); + +  after(async() => { +    if (firefox) { +      await firefox.close(); +    } +  }); + +  beforeEach(async() => { +    await session.navigateTo(`about:blank`); +    body = await session.findElementByCSS('body'); +  }); + +  it('should show all property names by "set" command with empty params', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('set '); + +    await eventually(async() => { +      let items = await c.getCompletions(); +      assert.equal(items.length, 5); +      assert.deepEqual(items[0], { type: 'title', text: 'Properties' }); +      assert(items[1].text.startsWith('hintchars')) +      assert(items[2].text.startsWith('smoothscroll')) +      assert(items[3].text.startsWith('nosmoothscroll')) +      assert(items[4].text.startsWith('complete')) +    }); +  }); + +  it('should show filtered property names by "set" command', async() => { +    await body.sendKeys(':'); + +    await session.switchToFrame(0); +    let c = new Console(session); +    await c.sendKeys('set no'); + +    await eventually(async() => { +      let items = await c.getCompletions(); +      assert.equal(items.length, 2); +      assert(items[1].text.includes('nosmoothscroll')) +    }); +  }); +}); diff --git a/e2e/lib/Console.js b/e2e/lib/Console.js new file mode 100644 index 0000000..3a39b64 --- /dev/null +++ b/e2e/lib/Console.js @@ -0,0 +1,41 @@ +class Console { +  constructor(session) { +    this.session = session; +  } + +  async sendKeys(...keys) { +    let input = await this.session.findElementByCSS('input'); +    input.sendKeys(...keys); +  } + +  async currentValue() { +    return await this.session.executeScript(() => { +      let input = document.querySelector('input'); +      return input.value; +    }); +  } + +  async getCompletions() { +    return await this.session.executeScript(() => { +      let items = document.querySelectorAll('.vimvixen-console-completion > li'); +      if (items.length === 0) { +        throw new Error('completion items not found'); +      } + +      let objs = []; +      for (let li of items) { +        if (li.classList.contains('vimvixen-console-completion-title')) { +          objs.push({ type: 'title', text: li.textContent.trim() }); +        } else if ('vimvixen-console-completion-item') { +          let highlight = li.classList.contains('vimvixen-completion-selected'); +          objs.push({ type: 'item', text: li.textContent.trim(), highlight }); +        } else { +          throw new Error(`unexpected class: ${li.className}`); +        } +      } +      return objs; +    }); +  } +} + +module.exports = Console; diff --git a/e2e/lib/clipboard.js b/e2e/lib/clipboard.js new file mode 100644 index 0000000..4061dbd --- /dev/null +++ b/e2e/lib/clipboard.js @@ -0,0 +1,63 @@ +'use strict'; + +const { spawn } = require('child_process'); + +const readLinux = () => { +  let stdout = '', stderr = ''; +  return new Promise((resolve, reject) => { +    let xsel = spawn('xsel', ['--clipboard', '--output']); +    xsel.stdout.on('data', (data) => { +      stdout += data; +    }); +    xsel.stderr.on('data', (data) => { +      stderr += data; +    }); +    xsel.on('close', (code) => { +      if (code !== 0) { +        throw new Error(`xsel returns ${code}: ${stderr}`) +      } +      resolve(stdout); +    }); +  }); +}; + +const writeLinux = (data) => { +  let stdout = '', stderr = ''; +  return new Promise((resolve, reject) => { +    let xsel = spawn('xsel', ['--clipboard', '--input']); +    xsel.stderr.on('data', (data) => { +      stderr += data; +    }); +    xsel.on('close', (code) => { +      if (code !== 0) { +        throw new Error(`xsel returns ${code}: ${stderr}`) +      } +      resolve(); +    }); +    xsel.stdin.write(data); +    xsel.stdin.end(); +  }); +}; + +const unsupported = (os) => { +  return () => { +    throw new Error(`Unsupported os: ${os}`); +  }; +}; + +const detect = () => { +  switch (process.platform) { +    case 'linux': +      return { +        read: readLinux, +        write: writeLinux, +      }; +    default: +      return { +        read: unsupported(process.platform), +        write: unsupported(process.platform), +      }; +  } +} + +module.exports = detect(); diff --git a/package-lock.json b/package-lock.json index df179de..53eb21d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6854,7 +6854,7 @@        }      },      "lanthan": { -      "version": "git+https://github.com/ueokande/lanthan.git#2a2aa2ebfc1ce8528fae8b01992ae56967e18f8a", +      "version": "git+https://github.com/ueokande/lanthan.git#a6795eb90006b11e65d436becda339258c00643c",        "from": "git+https://github.com/ueokande/lanthan.git#master",        "dev": true,        "requires": {  | 
