diff options
178 files changed, 3396 insertions, 1877 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index 342842d..8700c29 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,7 @@ executors: docker: - image: circleci/node:10-stretch-browsers environment: - - FIREFOX_VERSION: "60.0esr" + - FIREFOX_VERSION: "68.0esr" - GECKODRIVER_VERSION: "0.24.0" working_directory: ~ @@ -63,13 +63,6 @@ jobs: - checkout - setup_npm - run: npm run lint - - run: - # NOTE: Karma loads ts-node automatically and treats karma.conf.js as a TypeScript. - # Karma does not starts by karma.conf.js transpile failure, and this hack removes - # ts-node module from the local before test. - # See: https://github.com/karma-runner/karma/issues/3329 - name: Remove node-ts from node_modules - command: mv node_modules/ts-node node_modules/ts-node.orig - run: npm test - run: npm run package @@ -1,90 +1,38 @@ { "env": { "es6": true, - "node" : true, + "node": true, "browser" : true, "webextensions": true }, + + "extends": [ + "plugin:@typescript-eslint/recommended", + "plugin:react/recommended", + "prettier", + "prettier/@typescript-eslint", + "prettier/react", + "prettier/standard" + ], "plugins": [ + "@typescript-eslint", + "prettier", "react", - "@typescript-eslint" + "standard" ], - "parser": "@typescript-eslint/parser", "parserOptions": { + "sourceType": "module", "ecmaFeatures": { "jsx": true - }, - "sourceType": "module", - "project": "./tsconfig.json" + } }, - "extends": [ "eslint:all", "plugin:react/recommended" ], "rules": { - "array-bracket-newline": ["error", { "multiline": true }], - "array-element-newline": "off", - "arrow-body-style": "off", - "arrow-parens": ["error", "as-needed", { "requireForBlockBody": true }], - "brace-style": ["error", "1tbs", { "allowSingleLine": true }], - "capitalized-comments": "off", - "class-methods-use-this": "off", - "comma-dangle": "off", - "consistent-return": "off", - "default-case": "off", - "dot-location": ["error", "property"], - "function-paren-newline": "off", - "function-call-argument-newline": ["error", "consistent"], - "id-length": "off", - "indent": ["error", 2], - "init-declarations": "off", - "jsx-quotes": ["error", "prefer-single"], - "max-classes-per-file": "off", - "max-lines": "off", - "max-params": ["error", 10], - "max-statements": ["error", 15], - "multiline-comment-style": "off", - "multiline-ternary": "off", - "newline-after-var": "off", - "newline-before-return": "off", - "newline-per-chained-call": "off", - "no-alert": "off", - "no-bitwise": "off", - "no-console": ["error", { "allow": ["warn", "error"] }], - "no-continue": "off", - "no-empty-function": "off", - "no-extra-parens": "off", - "no-magic-numbers": "off", - "no-mixed-operators": "off", - "no-plusplus": "off", - "no-ternary": "off", - "no-undefined": "off", - "no-undef-init": "off", - "no-unused-vars": "off", - "no-use-before-define": "off", - "no-useless-constructor": "off", - "no-warning-comments": "off", - "object-curly-newline": ["error", { "consistent": true }], - "object-curly-spacing": ["error", "always", { "arraysInObjects": false, "objectsInObjects": false }], - "object-property-newline": ["error", { "allowMultiplePropertiesPerLine": true }], - "object-shorthand": "off", - "one-var": "off", - "padded-blocks": "off", - "prefer-const": "off", - "prefer-destructuring": ["error", { "AssignmentExpression": {"array": false}}], - "prefer-template": "off", - "quote-props": "off", - "quotes": ["error", "single", { "allowTemplateLiterals": true }], - "require-jsdoc": "off", - "require-unicode-regexp": "off", - "semi": "off", - "sort-imports": "off", - "sort-keys": "off", - "sort-vars": "off", - "space-before-function-paren": ["error", "never"], - - "react/jsx-indent": ["error", 2], - "react/prop-types": "off", - "react/react-in-jsx-scope": "off", - - "@typescript-eslint/no-unused-vars": ["error", { args: "none" }], - "@typescript-eslint/semi": ["error"] + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-namespace": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], + "@typescript-eslint/no-use-before-define": "off" } } @@ -16,7 +16,7 @@ For usage and more detailed information, check out our [documentations][document ## Compatibility -- Firefox 60 ESR +- Firefox 68 ESR ## Copyright diff --git a/docs/index.md b/docs/index.md index 7917645..b7e8624 100644 --- a/docs/index.md +++ b/docs/index.md @@ -13,7 +13,7 @@ since it uses the WebExtensions API. ### Install Vim Vixen -Vim Vixen is supported on Firefox 60 ESR. Please latest version of stable ESR +Vim Vixen is supported on Firefox 68 ESR. Please latest version of stable ESR from the [download page](https://www.mozilla.org/en-US/firefox/). You can install Vim Vixen from [Firefox add-ons (addons.mozilla.org)][AMO], and manage installed addon-ons on Firefox preferences `about:preferences`. diff --git a/docs/keymaps.md b/docs/keymaps.md index 9ae0c98..504a093 100644 --- a/docs/keymaps.md +++ b/docs/keymaps.md @@ -4,8 +4,11 @@ title: Keymaps # Keymaps -Keymaps are configurable in the add-on's preferences by navigating to `about:addons` and selecting "Extensions". -The default mappings are as follows: +The following descriptions are the default keymaps. +You can configure keymaps in the add-on's preferences by navigating to `about:addons` and selecting "Extensions". + +In the following descriptions, <kbd>Ctrl</kbd>+<kbd>x</kbd> means "press <kbd>x</kbd> with <kbd>Ctrl</kbd>", and <kbd>g</kbd><kbd>x</kbd> means "press <kbd>g</kbd>, then press <kbd>x</kbd>". +Some commands may be preceded by a decimal number, such as <kbd>3</kbd><kbd>d</kbd> deletes three tabs. ## Scrolling @@ -13,10 +16,10 @@ The default mappings are as follows: - <kbd>j</kbd>: scroll down - <kbd>h</kbd>: scroll left - <kbd>l</kbd>: scroll right -- <kbd>Ctrl</kbd>+<kbd>U</kbd>: scroll up half a page -- <kbd>Ctrl</kbd>+<kbd>D</kbd>: scroll down half a page -- <kbd>Ctrl</kbd>+<kbd>B</kbd>: scroll up a page -- <kbd>Ctrl</kbd>+<kbd>F</kbd>: scroll down a page +- <kbd>Ctrl</kbd>+<kbd>u</kbd>: scroll up half a page +- <kbd>Ctrl</kbd>+<kbd>d</kbd>: scroll down half a page +- <kbd>Ctrl</kbd>+<kbd>b</kbd>: scroll up a page +- <kbd>Ctrl</kbd>+<kbd>f</kbd>: scroll down a page - <kbd>g</kbd><kbd>g</kbd>: scroll to the top of a page - <kbd>G</kbd>: scroll to the bottom of a page - <kbd>0</kbd>: scroll to the leftmost part of a page diff --git a/e2e/blacklist.test.ts b/e2e/blacklist.test.ts index dec9d99..79cdb47 100644 --- a/e2e/blacklist.test.ts +++ b/e2e/blacklist.test.ts @@ -9,7 +9,7 @@ import SettingRepository from "./lib/SettingRepository"; import Settings from "../src/shared/settings/Settings"; describe("blacklist test", () => { - let server = new TestServer().receiveContent('/*', + const server = new TestServer().receiveContent('/*', `<!DOCTYPE html><html lang="en"><body style="width:10000px; height:10000px"></body></html>`, ); let lanthan: Lanthan; @@ -25,7 +25,7 @@ describe("blacklist test", () => { browser = lanthan.getWebExtBrowser(); await server.start(); - let url = server.url('/a').replace('http://', ''); + const url = server.url('/a').replace('http://', ''); await new SettingRepository(browser).saveJSON(Settings.fromJSON({ keymaps: { j: { type: "scroll.vertically", count: 1 }, @@ -42,18 +42,18 @@ describe("blacklist test", () => { }); it('should disable add-on if the URL is in the blacklist', async () => { - let page = await Page.navigateTo(webdriver, server.url('/a')); + const page = await Page.navigateTo(webdriver, server.url('/a')); await page.sendKeys('j'); - let scrollY = await page.getScrollY(); + const scrollY = await page.getScrollY(); assert.strictEqual(scrollY, 0); }); it('should enabled add-on if the URL is not in the blacklist', async () => { - let page = await Page.navigateTo(webdriver, server.url('/ab')); + const page = await Page.navigateTo(webdriver, server.url('/ab')); await page.sendKeys('j'); - let scrollY = await page.getScrollY(); + const scrollY = await page.getScrollY(); assert.strictEqual(scrollY, 64); }); }); diff --git a/e2e/clipboard.test.ts b/e2e/clipboard.test.ts index 3f2b289..0a09c77 100644 --- a/e2e/clipboard.test.ts +++ b/e2e/clipboard.test.ts @@ -11,7 +11,7 @@ import SettingRepository from "./lib/SettingRepository"; import Settings from "../src/shared/settings/Settings"; describe("clipboard test", () => { - let server = new TestServer(12321).receiveContent('/happy', 'ok'); + const server = new TestServer(12321).receiveContent('/happy', 'ok'); let lanthan: Lanthan; let webdriver: WebDriver; let browser: any; @@ -44,18 +44,18 @@ describe("clipboard test", () => { }); beforeEach(async() => { - let tabs = await browser.tabs.query({}); - for (let tab of tabs.slice(1)) { + const tabs = await browser.tabs.query({}); + for (const tab of tabs.slice(1)) { await browser.tabs.remove(tab.id); } }); it('should copy current URL by y', async () => { - let page = await Page.navigateTo(webdriver, server.url('/#should_copy_url')); + const page = await Page.navigateTo(webdriver, server.url('/#should_copy_url')); await page.sendKeys('y'); await eventually(async() => { - let data = await clipboard.read(); + const data = await clipboard.read(); assert.strictEqual(data, server.url('/#should_copy_url')); }); }); @@ -63,11 +63,11 @@ describe("clipboard test", () => { it('should open an URL from clipboard by p', async () => { await clipboard.write(server.url('/#open_from_clipboard')); - let page = await Page.navigateTo(webdriver, server.url()); + const page = await Page.navigateTo(webdriver, server.url()); await page.sendKeys('p'); await eventually(async() => { - let tabs = await browser.tabs.query({ active: true }); + const tabs = await browser.tabs.query({ active: true }); assert.strictEqual(tabs[0].url, server.url('/#open_from_clipboard')); }); }); @@ -75,11 +75,11 @@ describe("clipboard test", () => { it('should open an URL from clipboard to new tab by P', async () => { await clipboard.write(server.url('/#open_to_new_tab')); - let page = await Page.navigateTo(webdriver, server.url()); + const page = await Page.navigateTo(webdriver, server.url()); await page.sendKeys(Key.SHIFT, 'p'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.deepStrictEqual(tabs.map((t: any) => t.url), [ server.url(), server.url('/#open_to_new_tab'), @@ -90,11 +90,11 @@ describe("clipboard test", () => { it('should open search result with keywords in clipboard by p', async () => { await clipboard.write(`an apple`); - let page = await Page.navigateTo(webdriver, server.url()); + const page = await Page.navigateTo(webdriver, server.url()); await page.sendKeys(Key.SHIFT, 'p'); await eventually(async() => { - let tabs = await browser.tabs.query({ active: true }); + const tabs = await browser.tabs.query({ active: true }); assert.strictEqual(tabs[0].url, server.url('/google?q=an%20apple')); }); }); @@ -102,11 +102,11 @@ describe("clipboard test", () => { it('should open search result with keywords in clipboard to new tabby P', async () => { await clipboard.write(`an apple`); - let page = await Page.navigateTo(webdriver, server.url()); + const page = await Page.navigateTo(webdriver, server.url()); await page.sendKeys(Key.SHIFT, 'p'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.deepStrictEqual(tabs.map((t: any) => t.url), [ server.url(), server.url('/google?q=an%20apple'), diff --git a/e2e/command_addbookmark.test.ts b/e2e/command_addbookmark.test.ts index 5344292..a54c103 100644 --- a/e2e/command_addbookmark.test.ts +++ b/e2e/command_addbookmark.test.ts @@ -8,7 +8,7 @@ import { WebDriver } from 'selenium-webdriver'; import Page from './lib/Page'; describe('addbookmark command test', () => { - let server = new TestServer().receiveContent('/happy', ` + const server = new TestServer().receiveContent('/happy', ` <!DOCTYPE html> <html lang="en"><head><title>how to be happy</title></head></html>`, ); @@ -38,12 +38,12 @@ describe('addbookmark command test', () => { }); it('should add a bookmark from the current page', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('addbookmark how to be happy'); await eventually(async() => { - var bookmarks = await browser.bookmarks.search({ title: 'how to be happy' }); + const bookmarks = await browser.bookmarks.search({ title: 'how to be happy' }); assert.strictEqual(bookmarks.length, 1); assert.strictEqual(bookmarks[0].url, server.url('/happy')); }); diff --git a/e2e/command_bdelete.test.ts b/e2e/command_bdelete.test.ts index 239074e..c1f27ae 100644 --- a/e2e/command_bdelete.test.ts +++ b/e2e/command_bdelete.test.ts @@ -8,7 +8,7 @@ import { WebDriver } from 'selenium-webdriver'; import Page from './lib/Page'; describe('bdelete/bdeletes command test', () => { - let server = new TestServer().receiveContent('/*', 'ok'); + const server = new TestServer().receiveContent('/*', 'ok'); let lanthan: Lanthan; let webdriver: WebDriver; let browser: any; @@ -31,8 +31,8 @@ describe('bdelete/bdeletes command test', () => { }); beforeEach(async() => { - let tabs = await browser.tabs.query({}); - for (let tab of tabs.slice(1)) { + const tabs = await browser.tabs.query({}); + for (const tab of tabs.slice(1)) { await browser.tabs.remove(tab.id); } await browser.tabs.update(tabs[0].id, { url: server.url('/site1'), pinned: true }); @@ -42,19 +42,19 @@ describe('bdelete/bdeletes command test', () => { await browser.tabs.create({ url: server.url('/site5'), }); await eventually(async() => { - let handles = await webdriver.getAllWindowHandles(); + const handles = await webdriver.getAllWindowHandles(); assert.strictEqual(handles.length, 5); await webdriver.switchTo().window(handles[2]); }); }); it('should delete an unpinned tab by bdelete command', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('bdelete site5'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.deepStrictEqual(tabs.map((t: any) => t.url), [ server.url('/site1'), server.url('/site2'), @@ -65,45 +65,45 @@ describe('bdelete/bdeletes command test', () => { }); it('should not delete an pinned tab by bdelete command by bdelete command', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('bdelete site1'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.strictEqual(tabs.length, 5); }); }); it('should show an error when no tabs are matched by bdelete command', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('bdelete xyz'); await eventually(async() => { - let text = await console.getErrorMessage(); + const text = await console.getErrorMessage(); assert.strictEqual(text, 'No matching buffer for xyz'); }); }); it('should show an error when more than one tabs are matched by bdelete command', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('bdelete site'); await eventually(async() => { - let text = await console.getErrorMessage(); + const text = await console.getErrorMessage(); assert.strictEqual(text, 'More than one match for site'); }); }); it('should delete an unpinned tab by bdelete! command', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('bdelete! site5'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.deepStrictEqual(tabs.map((t: any) => t.url), [ server.url('/site1'), server.url('/site2'), @@ -114,12 +114,12 @@ describe('bdelete/bdeletes command test', () => { }); it('should delete an pinned tab by bdelete! command', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('bdelete! site1'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.deepStrictEqual(tabs.map((t: any) => t.url), [ server.url('/site2'), server.url('/site3'), @@ -130,12 +130,12 @@ describe('bdelete/bdeletes command test', () => { }); it('should delete unpinned tabs by bdeletes command', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('bdeletes site'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.deepStrictEqual(tabs.map((t: any) => t.url), [ server.url('/site1'), server.url('/site2'), @@ -145,12 +145,12 @@ describe('bdelete/bdeletes command test', () => { }); it('should delete both pinned and unpinned tabs by bdeletes! command', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('bdeletes! site'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.strictEqual(tabs.length, 1); }); }); diff --git a/e2e/command_buffer.test.ts b/e2e/command_buffer.test.ts index 472502b..3fa67ba 100644 --- a/e2e/command_buffer.test.ts +++ b/e2e/command_buffer.test.ts @@ -9,7 +9,7 @@ import { WebDriver } from 'selenium-webdriver'; import Page from './lib/Page'; describe('buffer command test', () => { - let server = new TestServer().handle('/*', (req: Request, res: Response) => { + const server = new TestServer().handle('/*', (req: Request, res: Response) => { res.send(` <!DOCTYPE html> <html lang="en"> @@ -40,8 +40,8 @@ describe('buffer command test', () => { }); beforeEach(async() => { - let tabs = await browser.tabs.query({}); - for (let tab of tabs.slice(1)) { + const tabs = await browser.tabs.query({}); + for (const tab of tabs.slice(1)) { await browser.tabs.remove(tab.id); } await browser.tabs.update(tabs[0].id, { url: server.url('/site1') }); @@ -50,41 +50,41 @@ describe('buffer command test', () => { } await eventually(async() => { - let handles = await webdriver.getAllWindowHandles(); + const handles = await webdriver.getAllWindowHandles(); assert.strictEqual(handles.length, 5); await webdriver.switchTo().window(handles[2]); }); }); it('should do nothing by buffer command with no parameters', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('buffer'); await eventually(async() => { - let tabs = await browser.tabs.query({ active: true }); + const tabs = await browser.tabs.query({ active: true }); assert.strictEqual(tabs[0].index, 2); }); }); it('should select a tab by buffer command with a number', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('buffer 1'); await eventually(async() => { - let tabs = await browser.tabs.query({ active: true }); + const tabs = await browser.tabs.query({ active: true }); assert.strictEqual(tabs[0].index, 0); }); }); it('should should an out of range error by buffer commands', async() => { - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); let console = await page.showConsole(); await console.execCommand('buffer 0'); await eventually(async() => { - let text = await console.getErrorMessage(); + const text = await console.getErrorMessage(); assert.strictEqual(text, 'tab 0 does not exist'); }); @@ -94,68 +94,68 @@ describe('buffer command test', () => { await console.execCommand('buffer 9'); await eventually(async() => { - let text = await console.getErrorMessage(); + const text = await console.getErrorMessage(); assert.strictEqual(text, 'tab 9 does not exist'); }); }); it('should select a tab by buffer command with a title', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('buffer my_site1'); await eventually(async() => { - let tabs = await browser.tabs.query({ active: true }); + const tabs = await browser.tabs.query({ active: true }); assert.strictEqual(tabs[0].index, 0); }); }); it('should select a tab by buffer command with an URL', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('buffer /site1'); await eventually(async() => { - let tabs = await browser.tabs.query({ active: true }); + const tabs = await browser.tabs.query({ active: true }); assert.strictEqual(tabs[0].index, 0); }); }); it('should select tabs rotately', async() => { - let handles = await webdriver.getAllWindowHandles(); + const handles = await webdriver.getAllWindowHandles(); await webdriver.switchTo().window(handles[4]); - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('buffer site'); await eventually(async() => { - let tabs = await browser.tabs.query({ active: true }); + const tabs = await browser.tabs.query({ active: true }); assert.strictEqual(tabs[0].index, 0); }); }); it('should do nothing by ":buffer %"', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('buffer %'); await eventually(async() => { - let tabs = await browser.tabs.query({ active: true }); + const tabs = await browser.tabs.query({ active: true }); assert.strictEqual(tabs[0].index, 2); }); }); it('should selects last selected tab by ":buffer #"', async() => { - let handles = await webdriver.getAllWindowHandles(); + const handles = await webdriver.getAllWindowHandles(); await webdriver.switchTo().window(handles[1]); - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('buffer #'); await eventually(async() => { - let tabs = await browser.tabs.query({ active: true }); + const tabs = await browser.tabs.query({ active: true }); assert.strictEqual(tabs[0].index, 2); }); }); diff --git a/e2e/command_help.test.ts b/e2e/command_help.test.ts index 20035fd..9f8a459 100644 --- a/e2e/command_help.test.ts +++ b/e2e/command_help.test.ts @@ -8,7 +8,7 @@ import { WebDriver } from 'selenium-webdriver'; import Page from './lib/Page'; describe("help command test", () => { - let server = new TestServer(); + const server = new TestServer(); let lanthan: Lanthan; let webdriver: WebDriver; let browser: any; @@ -37,11 +37,11 @@ describe("help command test", () => { }); it('should open help page by help command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('help'); await eventually(async() => { - let tabs = await browser.tabs.query({ active: true }); + const tabs = await browser.tabs.query({ active: true }); assert.strictEqual(tabs[0].url, 'https://ueokande.github.io/vim-vixen/') }); }); diff --git a/e2e/command_open.test.ts b/e2e/command_open.test.ts index ba9c51e..f4d2aa1 100644 --- a/e2e/command_open.test.ts +++ b/e2e/command_open.test.ts @@ -10,7 +10,7 @@ import SettingRepository from "./lib/SettingRepository"; import Settings from "../src/shared/settings/Settings"; describe("open command test", () => { - let server = new TestServer() + const server = new TestServer() .receiveContent('/google', 'google') .receiveContent('/yahoo', 'yahoo'); let lanthan: Lanthan; @@ -51,67 +51,67 @@ describe("open command test", () => { }); it('should open default search for keywords by open command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('open an apple'); await eventually(async() => { - let tabs = await browser.tabs.query({ active: true }); - let url = new URL(tabs[0].url); + const tabs = await browser.tabs.query({ active: true }); + const url = new URL(tabs[0].url); assert.strictEqual(url.href, server.url('/google?q=an%20apple')) }); }); it('should open certain search page for keywords by open command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('open yahoo an apple'); await eventually(async() => { - let tabs = await browser.tabs.query({ active: true }); - let url = new URL(tabs[0].url); + const tabs = await browser.tabs.query({ active: true }); + const url = new URL(tabs[0].url); assert.strictEqual(url.href, server.url('/yahoo?q=an%20apple')) }); }); it('should open default engine with empty keywords by open command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('open'); await eventually(async() => { - let tabs = await browser.tabs.query({ active: true }); - let url = new URL(tabs[0].url); + const tabs = await browser.tabs.query({ active: true }); + const url = new URL(tabs[0].url); assert.strictEqual(url.href, server.url('/google?q=')) }); }); it('should open certain search page for empty keywords by open command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('open yahoo'); await eventually(async() => { - let tabs = await browser.tabs.query({ active: true }); - let url = new URL(tabs[0].url); + const tabs = await browser.tabs.query({ active: true }); + const url = new URL(tabs[0].url); assert.strictEqual(url.href, server.url('/yahoo?q=')) }); }); it('should open a site with domain by open command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('open example.com'); await eventually(async() => { - let tabs = await browser.tabs.query({ active: true }); - let url = new URL(tabs[0].url); + const tabs = await browser.tabs.query({ active: true }); + const url = new URL(tabs[0].url); assert.strictEqual(url.href, 'http://example.com/') }); }); it('should open a site with URL by open command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('open https://example.com/'); await eventually(async() => { - let tabs = await browser.tabs.query({ active: true }); - let url = new URL(tabs[0].url); + const tabs = await browser.tabs.query({ active: true }); + const url = new URL(tabs[0].url); assert.strictEqual(url.href, 'https://example.com/') }); }); diff --git a/e2e/command_quit.test.ts b/e2e/command_quit.test.ts index 239d880..037ad09 100644 --- a/e2e/command_quit.test.ts +++ b/e2e/command_quit.test.ts @@ -8,7 +8,7 @@ import { WebDriver } from 'selenium-webdriver'; import Page from './lib/Page'; describe('quit/quitall command test', () => { - let server = new TestServer().receiveContent('/*', 'ok'); + const server = new TestServer().receiveContent('/*', 'ok'); let lanthan: Lanthan; let webdriver: WebDriver; let browser: any; @@ -31,8 +31,8 @@ describe('quit/quitall command test', () => { }); beforeEach(async() => { - let tabs = await browser.tabs.query({}); - for (let tab of tabs.slice(1)) { + const tabs = await browser.tabs.query({}); + for (const tab of tabs.slice(1)) { await browser.tabs.remove(tab.id); } await browser.tabs.update(tabs[0].id, { url: server.url('/site1') }); @@ -41,52 +41,52 @@ describe('quit/quitall command test', () => { } await eventually(async() => { - let handles = await webdriver.getAllWindowHandles(); + const handles = await webdriver.getAllWindowHandles(); assert.strictEqual(handles.length, 5); await webdriver.switchTo().window(handles[2]); }); }); it('should current tab by q command', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('q'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.strictEqual(tabs.length, 4) }); }); it('should current tab by quit command', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('quit'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.strictEqual(tabs.length, 4) }); }); it('should current tab by qa command', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('qa'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.strictEqual(tabs.length, 1) }); }); it('should current tab by quitall command', async() => { - let page = await Page.currentContext(webdriver); - let console = await page.showConsole(); + const page = await Page.currentContext(webdriver); + const console = await page.showConsole(); await console.execCommand('quitall'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.strictEqual(tabs.length, 1) }); }); diff --git a/e2e/command_tabopen.test.ts b/e2e/command_tabopen.test.ts index b5533e6..e96c29e 100644 --- a/e2e/command_tabopen.test.ts +++ b/e2e/command_tabopen.test.ts @@ -10,7 +10,7 @@ import SettingRepository from "./lib/SettingRepository"; import Settings from "../src/shared/settings/Settings"; describe("tabopen command test", () => { - let server = new TestServer() + const server = new TestServer() .receiveContent('/google', 'google') .receiveContent('/yahoo', 'yahoo'); let lanthan: Lanthan; @@ -46,8 +46,8 @@ describe("tabopen command test", () => { }); beforeEach(async() => { - let tabs = await browser.tabs.query({}); - for (let tab of tabs.slice(1)) { + const tabs = await browser.tabs.query({}); + for (const tab of tabs.slice(1)) { await browser.tabs.remove(tab.id); } @@ -55,73 +55,73 @@ describe("tabopen command test", () => { }); it('should open default search for keywords by tabopen command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('tabopen an apple'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.strictEqual(tabs.length, 2); - let url = new URL(tabs[1].url); + const url = new URL(tabs[1].url); assert.strictEqual(url.href, server.url('/google?q=an%20apple') ) }); }); it('should open certain search page for keywords by tabopen command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('tabopen yahoo an apple'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.strictEqual(tabs.length, 2); - let url = new URL(tabs[1].url); + const url = new URL(tabs[1].url); assert.strictEqual(url.href, server.url('/yahoo?q=an%20apple')) }); }); it('should open default engine with empty keywords by tabopen command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('tabopen'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.strictEqual(tabs.length, 2); - let url = new URL(tabs[1].url); + const url = new URL(tabs[1].url); assert.strictEqual(url.href, server.url('/google?q=')) }); }); it('should open certain search page for empty keywords by tabopen command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('tabopen yahoo'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.strictEqual(tabs.length, 2); - let url = new URL(tabs[1].url); + const url = new URL(tabs[1].url); assert.strictEqual(url.href, server.url('/yahoo?q=')) }); }); it('should open a site with domain by tabopen command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('tabopen example.com'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.strictEqual(tabs.length, 2); - let url = new URL(tabs[1].url); + const url = new URL(tabs[1].url); assert.strictEqual(url.href, 'http://example.com/') }); }); it('should open a site with URL by tabopen command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('tabopen https://example.com/'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.strictEqual(tabs.length, 2); - let url = new URL(tabs[1].url); + const url = new URL(tabs[1].url); assert.strictEqual(url.href, 'https://example.com/') }); }); diff --git a/e2e/command_winopen.test.ts b/e2e/command_winopen.test.ts index fb1348d..c9ff8d2 100644 --- a/e2e/command_winopen.test.ts +++ b/e2e/command_winopen.test.ts @@ -10,7 +10,7 @@ import SettingRepository from "./lib/SettingRepository"; import Settings from "../src/shared/settings/Settings"; describe("winopen command test", () => { - let server = new TestServer() + const server = new TestServer() .receiveContent('/google', 'google') .receiveContent('/yahoo', 'yahoo'); let lanthan: Lanthan; @@ -46,8 +46,8 @@ describe("winopen command test", () => { }); beforeEach(async() => { - let wins = await browser.windows.getAll(); - for (let win of wins.slice(1)) { + const wins = await browser.windows.getAll(); + for (const win of wins.slice(1)) { await browser.windows.remove(win.id); } @@ -55,85 +55,85 @@ describe("winopen command test", () => { }); it('should open default search for keywords by winopen command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('winopen an apple'); await eventually(async() => { - let wins = await browser.windows.getAll(); + const wins = await browser.windows.getAll(); assert.strictEqual(wins.length, 2); - let tabs = await browser.tabs.query({ windowId: wins[1].id }); - let url = new URL(tabs[0].url); + const tabs = await browser.tabs.query({ windowId: wins[1].id }); + const url = new URL(tabs[0].url); assert.strictEqual(url.href, server.url('/google?q=an%20apple')) }); }); it('should open certain search page for keywords by winopen command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('winopen yahoo an apple'); await eventually(async() => { - let wins = await browser.windows.getAll(); + const wins = await browser.windows.getAll(); assert.strictEqual(wins.length, 2); - let tabs = await browser.tabs.query({ windowId: wins[1].id }); - let url = new URL(tabs[0].url); + const tabs = await browser.tabs.query({ windowId: wins[1].id }); + const url = new URL(tabs[0].url); assert.strictEqual(url.href, server.url('/yahoo?q=an%20apple')) }); }); it('should open default engine with empty keywords by winopen command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('winopen'); await eventually(async() => { - let wins = await browser.windows.getAll(); + const wins = await browser.windows.getAll(); assert.strictEqual(wins.length, 2); - let tabs = await browser.tabs.query({ windowId: wins[1].id }); - let url = new URL(tabs[0].url); + const tabs = await browser.tabs.query({ windowId: wins[1].id }); + const url = new URL(tabs[0].url); assert.strictEqual(url.href, server.url('/google?q=')) }); }); it('should open certain search page for empty keywords by winopen command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('winopen yahoo'); await eventually(async() => { - let wins = await browser.windows.getAll(); + const wins = await browser.windows.getAll(); assert.strictEqual(wins.length, 2); - let tabs = await browser.tabs.query({ windowId: wins[1].id }); - let url = new URL(tabs[0].url); + const tabs = await browser.tabs.query({ windowId: wins[1].id }); + const url = new URL(tabs[0].url); assert.strictEqual(url.href, server.url('/yahoo?q=')) }); }); it('should open a site with domain by winopen command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('winopen example.com'); await eventually(async() => { - let wins = await browser.windows.getAll(); + const wins = await browser.windows.getAll(); assert.strictEqual(wins.length, 2); - let tabs = await browser.tabs.query({ windowId: wins[1].id }); - let url = new URL(tabs[0].url); + const tabs = await browser.tabs.query({ windowId: wins[1].id }); + const url = new URL(tabs[0].url); assert.strictEqual(url.href, 'http://example.com/') }); }); it('should open a site with URL by winopen command ', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('winopen https://example.com/'); await eventually(async() => { - let wins = await browser.windows.getAll(); + const wins = await browser.windows.getAll(); assert.strictEqual(wins.length, 2); - let tabs = await browser.tabs.query({ windowId: wins[1].id }); - let url = new URL(tabs[0].url); + const tabs = await browser.tabs.query({ windowId: wins[1].id }); + const url = new URL(tabs[0].url); assert.strictEqual(url.href, 'https://example.com/') }); }); diff --git a/e2e/completion.test.ts b/e2e/completion.test.ts index e98e1c2..dd4477f 100644 --- a/e2e/completion.test.ts +++ b/e2e/completion.test.ts @@ -30,9 +30,9 @@ describe("general completion test", () => { }); it('should all commands on empty line', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); - let items = await console.getCompletions(); + const items = await console.getCompletions(); assert.strictEqual(items.length, 11); assert.deepStrictEqual(items[0], { type: 'title', text: 'Console Command' }); assert.ok(items[1].text.startsWith('set')); @@ -41,10 +41,10 @@ describe("general completion test", () => { }); it('should only commands filtered by prefix', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('b'); - let items = await console.getCompletions(); + const items = await console.getCompletions(); assert.strictEqual(items.length, 4); assert.deepStrictEqual(items[0], { type: 'title', text: 'Console Command' }); assert.ok(items[1].text.startsWith('buffer')); @@ -57,23 +57,23 @@ describe("general completion test", () => { // > bdeletes // : b it('selects completion items by <Tab>/<S-Tab> keys', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('b'); await eventually(async() => { - let items = await console.getCompletions(); + const items = await console.getCompletions(); assert.strictEqual(items.length, 4); }); await console.sendKeys(Key.TAB); await eventually(async() => { - let items = await console.getCompletions(); + const items = await console.getCompletions(); assert.ok(items[1].highlight); assert.strictEqual(await console.currentValue(), 'buffer'); }); await console.sendKeys(Key.TAB, Key.TAB); await eventually(async() => { - let items = await console.getCompletions(); + const items = await console.getCompletions(); assert.ok(items[3].highlight); assert.strictEqual(await console.currentValue(), 'bdeletes'); }); @@ -85,7 +85,7 @@ describe("general completion test", () => { await console.sendKeys(Key.SHIFT, Key.TAB); await eventually(async() => { - let items = await console.getCompletions(); + const items = await console.getCompletions(); assert.ok(items[3].highlight); assert.strictEqual(await console.currentValue(), 'bdeletes'); }); diff --git a/e2e/completion_buffers.test.ts b/e2e/completion_buffers.test.ts index b6e7de0..ac24753 100644 --- a/e2e/completion_buffers.test.ts +++ b/e2e/completion_buffers.test.ts @@ -9,7 +9,7 @@ import { WebDriver } from 'selenium-webdriver'; import Page from './lib/Page'; describe("completion on buffer/bdelete/bdeletes", () => { - let server = new TestServer().handle('/*', (req: Request, res: Response) => { + const server = new TestServer().handle('/*', (req: Request, res: Response) => { res.send(` <!DOCTYPE html> <html lang="en"> @@ -42,8 +42,8 @@ describe("completion on buffer/bdelete/bdeletes", () => { }); beforeEach(async() => { - let tabs = await browser.tabs.query({}); - for (let tab of tabs.slice(1)) { + const tabs = await browser.tabs.query({}); + for (const tab of tabs.slice(1)) { await browser.tabs.remove(tab.id); } @@ -54,7 +54,7 @@ describe("completion on buffer/bdelete/bdeletes", () => { } await eventually(async() => { - let handles = await webdriver.getAllWindowHandles(); + const handles = await webdriver.getAllWindowHandles(); assert.strictEqual(handles.length, 5); await webdriver.switchTo().window(handles[2]); }); @@ -63,11 +63,11 @@ describe("completion on buffer/bdelete/bdeletes", () => { }); it('should all tabs by "buffer" command with empty params', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('buffer '); await eventually(async() => { - let items = await console.getCompletions(); + const items = await console.getCompletions(); assert.strictEqual(items.length, 6); assert.deepStrictEqual(items[0], { type: 'title', text: 'Buffers' }); assert.ok(items[1].text.startsWith('1:')); @@ -82,11 +82,11 @@ describe("completion on buffer/bdelete/bdeletes", () => { }); it('should filter items with URLs by keywords on "buffer" command', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('buffer title_site2'); await eventually(async() => { - let items = await console.getCompletions(); + const items = await console.getCompletions(); assert.deepStrictEqual(items[0], { type: 'title', text: 'Buffers' }); assert.ok(items[1].text.startsWith('2:')); assert.ok(items[1].text.includes('title_site2')); @@ -95,22 +95,22 @@ describe("completion on buffer/bdelete/bdeletes", () => { }); it('should filter items with titles by keywords on "buffer" command', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('buffer /site2'); await eventually(async() => { - let items = await console.getCompletions(); + const items = await console.getCompletions(); assert.deepStrictEqual(items[0], { type: 'title', text: 'Buffers' }); assert.ok(items[1].text.startsWith('2:')); }); }); it('should show one item by number on "buffer" command', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('buffer 2'); await eventually(async() => { - let items = await console.getCompletions(); + const items = await console.getCompletions(); assert.strictEqual(items.length, 2); assert.deepStrictEqual(items[0], { type: 'title', text: 'Buffers' }); assert.ok(items[1].text.startsWith('2:')); @@ -118,11 +118,11 @@ describe("completion on buffer/bdelete/bdeletes", () => { }); it('should show unpinned tabs "bdelete" command', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('bdelete site'); await eventually(async() => { - let items = await console.getCompletions(); + const items = await console.getCompletions(); assert.strictEqual(items.length, 4); assert.ok(items[1].text.includes('site3')); assert.ok(items[2].text.includes('site4')); @@ -131,11 +131,11 @@ describe("completion on buffer/bdelete/bdeletes", () => { }); it('should show unpinned tabs "bdeletes" command', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('bdeletes site'); await eventually(async() => { - let items = await console.getCompletions(); + const items = await console.getCompletions(); assert.strictEqual(items.length, 4); assert.ok(items[1].text.includes('site3')); assert.ok(items[2].text.includes('site4')); @@ -144,11 +144,11 @@ describe("completion on buffer/bdelete/bdeletes", () => { }); it('should show both pinned and unpinned tabs "bdelete!" command', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('bdelete! site'); await eventually(async() => { - let items = await console.getCompletions(); + const items = await console.getCompletions(); assert.strictEqual(items.length, 6); assert.ok(items[1].text.includes('site1')); assert.ok(items[2].text.includes('site2')); @@ -159,11 +159,11 @@ describe("completion on buffer/bdelete/bdeletes", () => { }); it('should show both pinned and unpinned tabs "bdeletes!" command', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('bdeletes! site'); await eventually(async() => { - let items = await console.getCompletions(); + const items = await console.getCompletions(); assert.strictEqual(items.length, 6); assert.ok(items[1].text.includes('site1')); assert.ok(items[2].text.includes('site2')); diff --git a/e2e/completion_open.test.ts b/e2e/completion_open.test.ts index 5f8bd11..95d4175 100644 --- a/e2e/completion_open.test.ts +++ b/e2e/completion_open.test.ts @@ -10,7 +10,7 @@ import Page from './lib/Page'; import SettingRepository from "./lib/SettingRepository"; describe("completion on open/tabopen/winopen commands", () => { - let server = new TestServer().receiveContent('/*', 'ok'); + const server = new TestServer().receiveContent('/*', 'ok'); let lanthan: Lanthan; let webdriver: WebDriver; let browser: any; @@ -27,7 +27,7 @@ describe("completion on open/tabopen/winopen commands", () => { browser = lanthan.getWebExtBrowser(); // Add item into hitories - await webdriver.navigate().to(('https://i-beam.org/404')); + await webdriver.navigate().to(('https://example.com/')); }); after(async() => { @@ -42,11 +42,11 @@ describe("completion on open/tabopen/winopen commands", () => { }); it('should show completions from search engines, bookmarks, and histories by "open" command', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('open '); await eventually(async() => { - let completions = await console.getCompletions(); + const completions = await console.getCompletions(); assert.ok(completions.find(x => x.type === 'title' && x.text === 'Search Engines')); assert.ok(completions.find(x => x.type === 'title' && x.text === 'Bookmarks')); assert.ok(completions.find(x => x.type === 'title' && x.text === 'History')); @@ -54,45 +54,45 @@ describe("completion on open/tabopen/winopen commands", () => { }); it('should filter items with URLs by keywords on "open" command', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('open https://'); await eventually(async() => { - let completions = await console.getCompletions(); - let items = completions.filter(x => x.type === 'item').map(x => x.text); + const completions = await console.getCompletions(); + const items = completions.filter(x => x.type === 'item').map(x => x.text); assert.ok(items.every(x => x.includes('https://'))); }); }); it('should filter items with titles by keywords on "open" command', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('open getting'); await eventually(async() => { - let completions = await console.getCompletions(); - let items = completions.filter(x => x.type === 'item').map(x => x.text); + const completions = await console.getCompletions(); + const items = completions.filter(x => x.type === 'item').map(x => x.text); assert.ok(items.every(x => x.toLowerCase().includes('getting'))); }); }); it('should filter items with titles by keywords on "tabopen" command', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('tabopen getting'); await eventually(async() => { - let completions = await console.getCompletions(); - let items = completions.filter(x => x.type === 'item').map(x => x.text); + const completions = await console.getCompletions(); + const items = completions.filter(x => x.type === 'item').map(x => x.text); assert.ok(items.every(x => x.includes('https://'))); }); }); it('should filter items with titles by keywords on "winopen" command', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('winopen https://'); await eventually(async() => { - let completions = await console.getCompletions(); - let items = completions.filter(x => x.type === 'item').map(x => x.text); + const completions = await console.getCompletions(); + const items = completions.filter(x => x.type === 'item').map(x => x.text); assert.ok(items.every(x => x.includes('https://'))); }); }); @@ -106,8 +106,8 @@ describe("completion on open/tabopen/winopen commands", () => { await console.inputKeys('open '); await eventually(async() => { - let completions = await console.getCompletions(); - let titles = completions.filter(x => x.type === 'title').map(x => x.text); + const completions = await console.getCompletions(); + const titles = completions.filter(x => x.type === 'title').map(x => x.text); assert.deepStrictEqual(titles, ['Search Engines', 'Bookmarks', 'History']) }); @@ -120,8 +120,8 @@ describe("completion on open/tabopen/winopen commands", () => { await console.inputKeys('open '); await eventually(async() => { - let completions = await console.getCompletions(); - let titles = completions.filter(x => x.type === 'title').map(x => x.text); + const completions = await console.getCompletions(); + const titles = completions.filter(x => x.type === 'title').map(x => x.text); assert.deepStrictEqual(titles, ['Bookmarks', 'Search Engines', 'Search Engines']) }); }); @@ -135,8 +135,8 @@ describe("completion on open/tabopen/winopen commands", () => { await console.inputKeys('open '); await eventually(async() => { - let completions = await console.getCompletions(); - let titles = completions.filter(x => x.type === 'title').map(x => x.text); + const completions = await console.getCompletions(); + const titles = completions.filter(x => x.type === 'title').map(x => x.text); assert.deepStrictEqual(titles, ['Search Engines', 'Bookmarks', 'History']) }); @@ -151,8 +151,8 @@ describe("completion on open/tabopen/winopen commands", () => { await console.inputKeys('open '); await eventually(async() => { - let completions = await console.getCompletions(); - let titles = completions.filter(x => x.type === 'title').map(x => x.text); + const completions = await console.getCompletions(); + const titles = completions.filter(x => x.type === 'title').map(x => x.text); assert.deepStrictEqual(titles, ['Bookmarks', 'Search Engines', 'Search Engines']) }); }); diff --git a/e2e/completion_set.test.ts b/e2e/completion_set.test.ts index facf991..7e9714c 100644 --- a/e2e/completion_set.test.ts +++ b/e2e/completion_set.test.ts @@ -30,11 +30,11 @@ describe("completion on set commands", () => { }); it('should show all property names by "set" command with empty params', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('set '); await eventually(async() => { - let items = await console.getCompletions(); + const items = await console.getCompletions(); assert.strictEqual(items.length, 5); assert.deepStrictEqual(items[0], { type: 'title', text: 'Properties' }); assert.ok(items[1].text.startsWith('hintchars')); @@ -45,11 +45,11 @@ describe("completion on set commands", () => { }); it('should show filtered property names by "set" command', async() => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.inputKeys('set no'); await eventually(async() => { - let items = await console.getCompletions(); + const items = await console.getCompletions(); assert.strictEqual(items.length, 2); assert.ok(items[1].text.includes('nosmoothscroll')) }); diff --git a/e2e/console.test.ts b/e2e/console.test.ts index faaf695..1d441f9 100644 --- a/e2e/console.test.ts +++ b/e2e/console.test.ts @@ -7,7 +7,7 @@ import { WebDriver, Key } from 'selenium-webdriver'; import Page from './lib/Page'; describe("console test", () => { - let server = new TestServer().receiveContent('/', + const server = new TestServer().receiveContent('/', `<!DOCTYPE html><html lang="en"><head><title>Hello, world!</title></head></html>`, ); let lanthan: Lanthan; @@ -36,55 +36,55 @@ describe("console test", () => { it('open console with :', async() => { await page.sendKeys(':'); - let console = await page.getConsole(); + const console = await page.getConsole(); assert.strictEqual(await console.currentValue(), ''); }); it('open console with open command by o', async() => { await page.sendKeys('o'); - let console = await page.getConsole(); + const console = await page.getConsole(); assert.strictEqual(await console.currentValue(), 'open '); }); it('open console with open command and current URL by O', async() => { await page.sendKeys(Key.SHIFT, 'o'); - let console = await page.getConsole(); + const console = await page.getConsole(); assert.strictEqual(await console.currentValue(), `open ${server.url()}`); }); it('open console with tabopen command by t', async() => { await page.sendKeys('t'); - let console = await page.getConsole(); + const console = await page.getConsole(); assert.strictEqual(await console.currentValue(), 'tabopen '); }); it('open console with tabopen command and current URL by T', async() => { await page.sendKeys(Key.SHIFT, 't'); - let console = await page.getConsole(); + const console = await page.getConsole(); assert.strictEqual(await console.currentValue(), `tabopen ${server.url()}`); }); it('open console with winopen command by w', async() => { await page.sendKeys('w'); - let console = await page.getConsole(); + const console = await page.getConsole(); assert.strictEqual(await console.currentValue(), `winopen `); }); it('open console with winopen command and current URL by W', async() => { await page.sendKeys(Key.SHIFT, 'W'); - let console = await page.getConsole(); + const console = await page.getConsole(); assert.strictEqual(await console.currentValue(), `winopen ${server.url()}`); }); it('open console with buffer command by b', async() => { await page.sendKeys('b'); - let console = await page.getConsole(); + const console = await page.getConsole(); assert.strictEqual(await console.currentValue(), `buffer `); }); it('open console with addbookmark command with title by a', async() => { await page.sendKeys('a'); - let console = await page.getConsole(); + const console = await page.getConsole(); assert.strictEqual(await console.currentValue(), `addbookmark Hello, world!`); }); }); diff --git a/e2e/eventually.ts b/e2e/eventually.ts index 12c4552..b0a2dfc 100644 --- a/e2e/eventually.ts +++ b/e2e/eventually.ts @@ -12,8 +12,8 @@ const eventually = async ( timeout = defaultTimeout, interval = defaultInterval, ): Promise<void> => { - let start = Date.now(); - let loop = async() => { + const start = Date.now(); + const loop = async() => { try { await fn(); } catch (err) { diff --git a/e2e/follow.test.ts b/e2e/follow.test.ts index ce3f565..62a109f 100644 --- a/e2e/follow.test.ts +++ b/e2e/follow.test.ts @@ -8,7 +8,7 @@ import { WebDriver, Key } from 'selenium-webdriver'; import Page from './lib/Page'; const newApp = () => { - let server = new TestServer(); + const server = new TestServer(); server.receiveContent('/', ` <!DOCTYPE html> @@ -105,7 +105,7 @@ const newApp = () => { }; describe('follow test', () => { - let server = newApp(); + const server = newApp(); let lanthan: Lanthan; let webdriver: WebDriver; let browser: any; @@ -128,52 +128,52 @@ describe('follow test', () => { }); afterEach(async() => { - let tabs = await browser.tabs.query({}); - for (let tab of tabs.slice(1)) { + const tabs = await browser.tabs.query({}); + for (const tab of tabs.slice(1)) { await browser.tabs.remove(tab.id); } }); it('should focus an input by f', async () => { - let page = await Page.navigateTo(webdriver, server.url('/follow-input')); + const page = await Page.navigateTo(webdriver, server.url('/follow-input')); await page.sendKeys('f'); await page.waitAndGetHints(); await page.sendKeys('a'); - let tagName = await webdriver.executeScript(() => document.activeElement!!.tagName) as string; + const tagName = await webdriver.executeScript(() => document.activeElement!!.tagName) as string; assert.strictEqual(tagName.toLowerCase(), 'input'); }); it('should open a link by f', async () => { - let page = await Page.navigateTo(webdriver, server.url()); + const page = await Page.navigateTo(webdriver, server.url()); await page.sendKeys('f'); await page.waitAndGetHints(); await page.sendKeys('a'); await eventually(async() => { - let hash = await webdriver.executeScript('return location.pathname'); + const hash = await webdriver.executeScript('return location.pathname'); assert.strictEqual(hash, '/hello'); }); }); it('should focus an input by F', async () => { - let page = await Page.navigateTo(webdriver, server.url('/follow-input')); + const page = await Page.navigateTo(webdriver, server.url('/follow-input')); await page.sendKeys(Key.SHIFT, 'f'); await page.waitAndGetHints(); await page.sendKeys('a'); - let tagName = await webdriver.executeScript(() => document.activeElement!!.tagName) as string; + const tagName = await webdriver.executeScript(() => document.activeElement!!.tagName) as string; assert.strictEqual(tagName.toLowerCase(), 'input'); }); it('should open a link to new tab by F', async () => { - let page = await Page.navigateTo(webdriver, server.url()); + const page = await Page.navigateTo(webdriver, server.url()); await page.sendKeys(Key.SHIFT, 'f'); await page.waitAndGetHints(); await page.sendKeys('a'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.strictEqual(tabs.length, 2); assert.strictEqual(new URL(tabs[1].url).pathname, '/hello'); assert.strictEqual(tabs[1].openerTabId, tabs[0].id); @@ -181,36 +181,36 @@ describe('follow test', () => { }); it('should show hints of links in area', async () => { - let page = await Page.navigateTo(webdriver, server.url('/area')); + const page = await Page.navigateTo(webdriver, server.url('/area')); await page.sendKeys(Key.SHIFT, 'f'); - let hints = await page.waitAndGetHints(); + const hints = await page.waitAndGetHints(); assert.strictEqual(hints.length, 3); }); it('should shows hints only in viewport', async () => { - let page = await Page.navigateTo(webdriver, server.url('/test1')); + const page = await Page.navigateTo(webdriver, server.url('/test1')); await page.sendKeys(Key.SHIFT, 'f'); - let hints = await page.waitAndGetHints(); + const hints = await page.waitAndGetHints(); assert.strictEqual(hints.length, 1); }); it('should shows hints only in window of the frame', async () => { - let page = await Page.navigateTo(webdriver, server.url('/test2')); + const page = await Page.navigateTo(webdriver, server.url('/test2')); await page.sendKeys(Key.SHIFT, 'f'); await webdriver.switchTo().frame(0); - let hints = await page.waitAndGetHints(); + const hints = await page.waitAndGetHints(); assert.strictEqual(hints.length, 1); }); it('should shows hints only in the frame', async () => { - let page = await Page.navigateTo(webdriver, server.url('/test3')); + const page = await Page.navigateTo(webdriver, server.url('/test3')); await page.sendKeys(Key.SHIFT, 'f'); await webdriver.switchTo().frame(0); - let hints = await page.waitAndGetHints(); + const hints = await page.waitAndGetHints(); assert.strictEqual(hints.length, 1); }); }); diff --git a/e2e/follow_properties.test.ts b/e2e/follow_properties.test.ts index eaa38e2..bbd46d4 100644 --- a/e2e/follow_properties.test.ts +++ b/e2e/follow_properties.test.ts @@ -8,7 +8,7 @@ import { WebDriver, Key } from 'selenium-webdriver'; import Page from './lib/Page'; describe('follow properties test', () => { - let server = new TestServer().receiveContent('/', ` + const server = new TestServer().receiveContent('/', ` <!DOCTYPE html> <html lang="en"><body> <a href="/">link1</a> @@ -65,8 +65,8 @@ describe('follow properties test', () => { }); afterEach(async() => { - let tabs = await browser.tabs.query({}); - for (let tab of tabs.slice(1)) { + const tabs = await browser.tabs.query({}); + for (const tab of tabs.slice(1)) { await browser.tabs.remove(tab.id); } }); @@ -99,7 +99,7 @@ describe('follow properties test', () => { await page.sendKeys('jj'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.strictEqual(tabs[0].active, false); assert.strictEqual(tabs[1].active, true); }); @@ -111,14 +111,14 @@ describe('follow properties test', () => { await page.sendKeys('jj'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.strictEqual(tabs[0].active, true); assert.strictEqual(tabs[1].active, false); }); }); it('should show hints with hintchars by settings', async () => { - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand('set hintchars=abc'); await (webdriver.switchTo() as any).parentFrame(); diff --git a/e2e/lib/Console.ts b/e2e/lib/Console.ts index 233bf48..6a82387 100644 --- a/e2e/lib/Console.ts +++ b/e2e/lib/Console.ts @@ -11,13 +11,13 @@ export class Console { } async sendKeys(...keys: string[]) { - let input = await this.webdriver.findElement(By.css('input')); + const input = await this.webdriver.findElement(By.css('input')); input.sendKeys(...keys); } async currentValue() { return await this.webdriver.executeScript(() => { - let input = document.querySelector('input'); + const input = document.querySelector('input'); if (input === null) { throw new Error('could not find input element'); } @@ -26,33 +26,33 @@ export class Console { } async execCommand(command: string): Promise<void> { - let input = await this.webdriver.findElement(By.css('input.vimvixen-console-command-input')); + const input = await this.webdriver.findElement(By.css('input.vimvixen-console-command-input')); await input.sendKeys(command, Key.ENTER); } async getErrorMessage(): Promise<string> { - let p = await this.webdriver.findElement(By.css('.vimvixen-console-error')); + const p = await this.webdriver.findElement(By.css('.vimvixen-console-error')); return p.getText(); } async inputKeys(...keys: string[]) { - let input = await this.webdriver.findElement(By.css('input')); + const input = await this.webdriver.findElement(By.css('input')); await input.sendKeys(...keys); } getCompletions(): Promise<CompletionItem[]> { return this.webdriver.executeScript(() => { - let items = document.querySelectorAll('.vimvixen-console-completion > li'); + const items = document.querySelectorAll('.vimvixen-console-completion > li'); if (items.length === 0) { throw new Error('completion items not found'); } - let objs = []; - for (let li of Array.from(items)) { + const objs = []; + for (const li of Array.from(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'); + const highlight = li.classList.contains('vimvixen-completion-selected'); objs.push({ type: 'item', text: li.textContent!!.trim(), highlight }); } else { throw new Error(`unexpected class: ${li.className}`); @@ -63,7 +63,7 @@ export class Console { } async close(): Promise<void> { - let input = await this.webdriver.findElement(By.css('input')); + const input = await this.webdriver.findElement(By.css('input')); await input.sendKeys(Key.ESCAPE); // TODO remove sleep await new Promise(resolve => setTimeout(resolve, 100)); diff --git a/e2e/lib/FormOptionPage.ts b/e2e/lib/FormOptionPage.ts index 7d981f4..33ce2a7 100644 --- a/e2e/lib/FormOptionPage.ts +++ b/e2e/lib/FormOptionPage.ts @@ -9,8 +9,8 @@ export default class FormOptionPage { } async setBlacklist(nth: number, url: string): Promise<void> { - let selector = '.form-blacklist-form-row > .column-url'; - let inputs = await this.webdriver.findElements(By.css(selector)); + const selector = '.form-blacklist-form-row > .column-url'; + const inputs = await this.webdriver.findElements(By.css(selector)); if (inputs.length <= nth) { throw new RangeError('Index out of range to set a blacklist') } @@ -55,21 +55,21 @@ export default class FormOptionPage { } async addBlacklist(): Promise<void> { - let rows = await this.webdriver.findElements(By.css(`.form-blacklist-form-row`)); - let button = await this.webdriver.findElement(By.css('.form-blacklist-form .ui-add-button')) + const rows = await this.webdriver.findElements(By.css(`.form-blacklist-form-row`)); + const button = await this.webdriver.findElement(By.css('.form-blacklist-form .ui-add-button')) await button.click(); await this.webdriver.wait(until.elementLocated(By.css(`.form-blacklist-form-row:nth-child(${rows.length + 1})`))); } async addPartialBlacklist(): Promise<void> { - let rows = await this.webdriver.findElements(By.css(`.form-partial-blacklist-form-row`)); - let button = await this.webdriver.findElement(By.css('.form-partial-blacklist-form .ui-add-button')) + const rows = await this.webdriver.findElements(By.css(`.form-partial-blacklist-form-row`)); + const button = await this.webdriver.findElement(By.css('.form-partial-blacklist-form .ui-add-button')) await button.click(); await this.webdriver.wait(until.elementLocated(By.css(`.form-partial-blacklist-form-row:nth-child(${rows.length + 2})`))); } async removeBlackList(nth: number): Promise<void> { - let buttons = await this.webdriver.findElements(By.css('.form-blacklist-form-row .ui-delete-button')); + const buttons = await this.webdriver.findElements(By.css('.form-blacklist-form-row .ui-delete-button')); if (buttons.length <= nth) { throw new RangeError('Index out of range to remove blacklist') } @@ -77,7 +77,7 @@ export default class FormOptionPage { } async removePartialBlackList(nth: number): Promise<void> { - let buttons = await this.webdriver.findElements(By.css('.form-partial-blacklist-form-row .ui-delete-button')); + const buttons = await this.webdriver.findElements(By.css('.form-partial-blacklist-form-row .ui-delete-button')); if (buttons.length <= nth) { throw new RangeError('Index out of range to remove partial blacklist') } @@ -85,14 +85,14 @@ export default class FormOptionPage { } async addSearchEngine(): Promise<void> { - let rows = await this.webdriver.findElements(By.css(`.form-search-form-row > .column-name`)); - let button = await this.webdriver.findElement(By.css('.form-search-form > .ui-add-button')) + const rows = await this.webdriver.findElements(By.css(`.form-search-form-row > .column-name`)); + const button = await this.webdriver.findElement(By.css('.form-search-form > .ui-add-button')) await button.click(); await this.webdriver.wait(until.elementLocated(By.css(`.form-search-form-row:nth-child(${rows.length + 1})`))); } async setDefaultSearchEngine(nth: number): Promise<void> { - let radios = await this.webdriver.findElements(By.css('.form-search-form-row input[type=radio]')); + const radios = await this.webdriver.findElements(By.css('.form-search-form-row input[type=radio]')); if (radios.length <= nth) { throw new RangeError('Index out of range to set a default search engine'); } diff --git a/e2e/lib/JSONOptionPage.ts b/e2e/lib/JSONOptionPage.ts index ac1ae3d..d6ed7ee 100644 --- a/e2e/lib/JSONOptionPage.ts +++ b/e2e/lib/JSONOptionPage.ts @@ -9,14 +9,14 @@ export default class JSONOptionPage { } async updateSettings(value: string): Promise<void> { - let textarea = await this.webdriver.findElement(By.css('textarea')); + const textarea = await this.webdriver.findElement(By.css('textarea')); await this.webdriver.executeScript(`document.querySelector('textarea').value = '${value}'`) await textarea.sendKeys(' '); await this.webdriver.executeScript(() => document.querySelector('textarea')!!.blur()); } async getErrorMessage(): Promise<string> { - let error = await this.webdriver.findElement(By.css('.settings-ui-input-error')); + const error = await this.webdriver.findElement(By.css('.settings-ui-input-error')); return error.getText(); } } diff --git a/e2e/lib/OptionPage.ts b/e2e/lib/OptionPage.ts index c183b06..9f994a0 100644 --- a/e2e/lib/OptionPage.ts +++ b/e2e/lib/OptionPage.ts @@ -11,13 +11,13 @@ export default class OptionPage { } static async open(lanthan: Lanthan) { - let url = await lanthan.getWebExtBrowser().runtime.getURL("build/settings.html") + const url = await lanthan.getWebExtBrowser().runtime.getURL("build/settings.html") await lanthan.getWebDriver().navigate().to(url); return new OptionPage(lanthan); } async switchToForm(): Promise<FormOptionPage> { - let useFormInput = await this.webdriver.findElement(By.css('#setting-source-form')); + const useFormInput = await this.webdriver.findElement(By.css('#setting-source-form')); await useFormInput.click(); await this.webdriver.switchTo().alert().accept(); return new FormOptionPage(this.lanthan); diff --git a/e2e/lib/Page.ts b/e2e/lib/Page.ts index 7a5dd7a..ad3f454 100644 --- a/e2e/lib/Page.ts +++ b/e2e/lib/Page.ts @@ -22,7 +22,7 @@ export default class Page { } async sendKeys(...keys: Array<string|number|Promise<string|number>>): Promise<void> { - let body = await this.webdriver.findElement(By.css('body')); + const body = await this.webdriver.findElement(By.css('body')); await body.sendKeys(...keys); } @@ -33,7 +33,7 @@ export default class Page { } async showConsole(): Promise<Console> { - let iframe = this.webdriver.findElement(By.css('#vimvixen-console-frame')); + const iframe = this.webdriver.findElement(By.css('#vimvixen-console-frame')); await this.sendKeys(':'); await this.webdriver.wait(until.elementIsVisible(iframe)); @@ -43,7 +43,7 @@ export default class Page { } async getConsole(): Promise<Console> { - let iframe = this.webdriver.findElement(By.css('#vimvixen-console-frame')); + const iframe = this.webdriver.findElement(By.css('#vimvixen-console-frame')); await this.webdriver.wait(until.elementIsVisible(iframe)); await this.webdriver.switchTo().frame(0); @@ -69,11 +69,11 @@ export default class Page { async waitAndGetHints(): Promise<Hint[]> { await this.webdriver.wait(until.elementsLocated(By.css('.vimvixen-hint'))); - let elements = await this.webdriver.findElements(By.css(`.vimvixen-hint`)); - let hints = []; - for (let e of elements) { - let display = await e.getCssValue('display'); - let text = await e.getText(); + const elements = await this.webdriver.findElements(By.css(`.vimvixen-hint`)); + const hints = []; + for (const e of elements) { + const display = await e.getCssValue('display'); + const text = await e.getText(); hints.push({ displayed: display !== 'none', text: text, @@ -83,7 +83,7 @@ export default class Page { } private static async waitForConsoleLoaded(webdriver: WebDriver) { - let topFrame = await webdriver.executeScript(() => window.top === window); + const topFrame = await webdriver.executeScript(() => window.top === window); if (!topFrame) { return; } diff --git a/e2e/lib/TestServer.ts b/e2e/lib/TestServer.ts index c010e37..5b9eee3 100644 --- a/e2e/lib/TestServer.ts +++ b/e2e/lib/TestServer.ts @@ -28,12 +28,12 @@ export default class TestServer { return this; } - url(path: string = '/'): string { + url(path = '/'): string { if (!this.http) { throw new Error('http server not started'); } - let addr = this.http.address() as net.AddressInfo; + const addr = this.http.address() as net.AddressInfo; return `http://${addr.address}:${addr.port}${path}` } diff --git a/e2e/lib/clipboard.ts b/e2e/lib/clipboard.ts index c1eddbb..297b71a 100644 --- a/e2e/lib/clipboard.ts +++ b/e2e/lib/clipboard.ts @@ -3,7 +3,7 @@ import { spawn } from 'child_process'; const readLinux = (): Promise<string> => { let stdout = '', stderr = ''; return new Promise((resolve) => { - let xsel = spawn('xsel', ['--clipboard', '--output']); + const xsel = spawn('xsel', ['--clipboard', '--output']); xsel.stdout.on('data', (data) => { stdout += data; }); @@ -22,7 +22,7 @@ const readLinux = (): Promise<string> => { const writeLinux = (data: string): Promise<string> => { let stderr = ''; return new Promise((resolve) => { - let xsel = spawn('xsel', ['--clipboard', '--input']); + const xsel = spawn('xsel', ['--clipboard', '--input']); xsel.stderr.on('data', (data) => { stderr += data; }); @@ -40,7 +40,7 @@ const writeLinux = (data: string): Promise<string> => { const readDarwin = (): Promise<string> => { let stdout = '', stderr = ''; return new Promise((resolve) => { - let pbpaste = spawn('pbpaste'); + const pbpaste = spawn('pbpaste'); pbpaste.stdout.on('data', (data) => { stdout += data; }); @@ -59,7 +59,7 @@ const readDarwin = (): Promise<string> => { const writeDarwin = (data: string): Promise<string> => { let stderr = ''; return new Promise((resolve) => { - let pbcopy = spawn('pbcopy'); + const pbcopy = spawn('pbcopy'); pbcopy.stderr.on('data', (data) => { stderr += data; }); diff --git a/e2e/mark.test.ts b/e2e/mark.test.ts index f9f372b..c73423b 100644 --- a/e2e/mark.test.ts +++ b/e2e/mark.test.ts @@ -8,7 +8,7 @@ import { WebDriver } from 'selenium-webdriver'; import Page from './lib/Page'; describe("mark test", () => { - let server = new TestServer().receiveContent('/', + const server = new TestServer().receiveContent('/', `<!DOCTYPE html><html lang="en"><body style="width:10000px; height:10000px"></body></html>`, ); let lanthan: Lanthan; @@ -34,7 +34,7 @@ describe("mark test", () => { }); it('should set a local mark and jump to it', async () => { - let page = await Page.navigateTo(webdriver, server.url()); + const page = await Page.navigateTo(webdriver, server.url()); await page.scrollTo(200, 200); await page.sendKeys('m', 'a'); await page.scrollTo(500, 500); @@ -63,8 +63,8 @@ describe("mark test", () => { await page.sendKeys('\'', 'A'); await eventually(async() => { - let tab = (await browser.tabs.query({ active: true }))[0]; - let url = new URL(tab.url); + const tab = (await browser.tabs.query({ active: true }))[0]; + const url = new URL(tab.url); assert.strictEqual(url.hash, '#first'); assert.strictEqual(await page.getScrollX(), 200); @@ -77,7 +77,7 @@ describe("mark test", () => { await page.scrollTo(500, 500); await page.sendKeys('m', 'A'); - let tab = (await browser.tabs.query({ active: true }))[0]; + const tab = (await browser.tabs.query({ active: true }))[0]; await browser.tabs.create({ url: server.url('/#second') }); await browser.tabs.remove(tab.id); @@ -92,8 +92,8 @@ describe("mark test", () => { await page.sendKeys('\'', 'A'); await eventually(async() => { - let tab = (await browser.tabs.query({ active: true }))[0]; - let url = new URL(tab.url); + const tab = (await browser.tabs.query({ active: true }))[0]; + const url = new URL(tab.url); assert.strictEqual(url.hash, '#first'); }); }); diff --git a/e2e/navigate.test.ts b/e2e/navigate.test.ts index 15c5a31..37f0520 100644 --- a/e2e/navigate.test.ts +++ b/e2e/navigate.test.ts @@ -9,7 +9,7 @@ import { Options as FirefoxOptions } from 'selenium-webdriver/firefox'; import Page from './lib/Page'; const newApp = () => { - let server = new TestServer(); + const server = new TestServer(); server.handle('/pagenation-a/:page', (req, res) => { res.status(200).send(` <!DOCTYPE html> @@ -44,7 +44,7 @@ const newApp = () => { }; describe("navigate test", () => { - let server = newApp(); + const server = newApp(); let lanthan: Lanthan; let webdriver: WebDriver; let browser: any; @@ -52,7 +52,7 @@ describe("navigate test", () => { before(async() => { await server.start(); - let opts = (new FirefoxOptions() as any) + const opts = (new FirefoxOptions() as any) .setPreference('browser.startup.homepage', server.url('/#home')); lanthan = await Builder .forBrowser('firefox') @@ -71,42 +71,42 @@ describe("navigate test", () => { }); beforeEach(async() => { - let tabs = await browser.tabs.query({}); - for (let tab of tabs.slice(1)) { + const tabs = await browser.tabs.query({}); + for (const tab of tabs.slice(1)) { await browser.tabs.remove(tab.id); } }); it('should go to parent path without hash by gu', async () => { - let page = await Page.navigateTo(webdriver, server.url('/a/b/c')); + const page = await Page.navigateTo(webdriver, server.url('/a/b/c')); await page.sendKeys('g', 'u'); await eventually(async() => { - let tab = (await browser.tabs.query({}))[0]; - let url = new URL(tab.url); + const tab = (await browser.tabs.query({}))[0]; + const url = new URL(tab.url); assert.strictEqual(url.pathname, `/a/b/`) }); }); it('should remove hash by gu', async () => { - let page = await Page.navigateTo(webdriver, server.url('/a/b/c#hash')); + const page = await Page.navigateTo(webdriver, server.url('/a/b/c#hash')); await page.sendKeys('g', 'u'); await eventually(async() => { - let tab = (await browser.tabs.query({}))[0]; - let url = new URL(tab.url); + const tab = (await browser.tabs.query({}))[0]; + const url = new URL(tab.url); assert.strictEqual(url.hash, ''); assert.strictEqual(url.pathname, `/a/b/c`) }); }); it('should go to root path by gU', async () => { - let page = await Page.navigateTo(webdriver, server.url('/a/b/c#hash')); + const page = await Page.navigateTo(webdriver, server.url('/a/b/c#hash')); await page.sendKeys('g', Key.SHIFT, 'u'); await eventually(async() => { - let tab = (await browser.tabs.query({}))[0]; - let url = new URL(tab.url); + const tab = (await browser.tabs.query({}))[0]; + const url = new URL(tab.url); assert.strictEqual(url.pathname, `/`) }); }); @@ -117,8 +117,8 @@ describe("navigate test", () => { await page.sendKeys(Key.SHIFT, 'h'); await eventually(async() => { - let tab = (await browser.tabs.query({}))[0]; - let url = new URL(tab.url); + const tab = (await browser.tabs.query({}))[0]; + const url = new URL(tab.url); assert.strictEqual(url.pathname, `/first`) }); @@ -126,73 +126,73 @@ describe("navigate test", () => { page.sendKeys(Key.SHIFT, 'l'); await eventually(async() => { - let tab = (await browser.tabs.query({}))[0]; - let url = new URL(tab.url); + const tab = (await browser.tabs.query({}))[0]; + const url = new URL(tab.url); assert.strictEqual(url.pathname, `/second`) }); }); it('should go previous and next page in <a> by [[ and ]]', async () => { - let page = await Page.navigateTo(webdriver, server.url('/pagenation-a/10')); + const page = await Page.navigateTo(webdriver, server.url('/pagenation-a/10')); await page.sendKeys('[', '['); await eventually(async() => { - let tab = (await browser.tabs.query({}))[0]; - let url = new URL(tab.url); + const tab = (await browser.tabs.query({}))[0]; + const url = new URL(tab.url); assert.strictEqual(url.pathname, '/pagenation-a/9'); }); }); it('should go next page in <a> by ]]', async () => { - let page = await Page.navigateTo(webdriver, server.url('/pagenation-a/10')); + const page = await Page.navigateTo(webdriver, server.url('/pagenation-a/10')); await page.sendKeys(']', ']'); await eventually(async() => { - let tab = (await browser.tabs.query({}))[0]; - let url = new URL(tab.url); + const tab = (await browser.tabs.query({}))[0]; + const url = new URL(tab.url); assert.strictEqual(url.pathname, '/pagenation-a/11'); }); }); it('should go previous page in <link> by ]]', async () => { - let page = await Page.navigateTo(webdriver, server.url('/pagenation-link/10')); + const page = await Page.navigateTo(webdriver, server.url('/pagenation-link/10')); await page.sendKeys('[', '['); await eventually(async() => { - let tab = (await browser.tabs.query({}))[0]; - let url = new URL(tab.url); + const tab = (await browser.tabs.query({}))[0]; + const url = new URL(tab.url); assert.strictEqual(url.pathname, '/pagenation-link/9'); }); }); it('should go next page by in <link> by [[', async () => { - let page = await Page.navigateTo(webdriver, server.url('/pagenation-link/10')); + const page = await Page.navigateTo(webdriver, server.url('/pagenation-link/10')); await page.sendKeys(']', ']'); await eventually(async() => { - let tab = (await browser.tabs.query({}))[0]; - let url = new URL(tab.url); + const tab = (await browser.tabs.query({}))[0]; + const url = new URL(tab.url); assert.strictEqual(url.pathname, '/pagenation-link/11'); }); }); it('should go to home page into current tab by gh', async () => { - let page = await Page.navigateTo(webdriver, server.url()); + const page = await Page.navigateTo(webdriver, server.url()); await page.sendKeys('g', 'h'); await eventually(async() => { - let tab = (await browser.tabs.query({}))[0]; - let url = new URL(tab.url); + const tab = (await browser.tabs.query({}))[0]; + const url = new URL(tab.url); assert.strictEqual(url.hash, '#home'); }); }); it('should go to home page into current tab by gH', async () => { - let page = await Page.navigateTo(webdriver, server.url()); + const page = await Page.navigateTo(webdriver, server.url()); await page.sendKeys('g', Key.SHIFT, 'H'); await eventually(async() => { - let tabs = await browser.tabs.query({}); + const tabs = await browser.tabs.query({}); assert.strictEqual(tabs.length, 2); assert.strictEqual(new URL(tabs[0].url).hash, ''); assert.strictEqual(new URL(tabs[1].url).hash, '#home'); @@ -201,12 +201,12 @@ describe("navigate test", () => { }); it('should reload current tab by r', async () => { - let page = await Page.navigateTo(webdriver, server.url('/reload')); + const page = await Page.navigateTo(webdriver, server.url('/reload')); await page.scrollTo(500, 500); let before: number; await eventually(async() => { - let tab = (await browser.tabs.query({}))[0]; + const tab = (await browser.tabs.query({}))[0]; before = Number(new URL(tab.url).hash.split('#')[1]); assert.ok(before > 0); }); @@ -215,24 +215,24 @@ describe("navigate test", () => { let after; await eventually(async() => { - let tab = (await browser.tabs.query({}))[0]; + const tab = (await browser.tabs.query({}))[0]; after = Number(new URL(tab.url).hash.split('#')[1]); assert.ok(after > before); }); await eventually(async() => { - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); assert.strictEqual(await page.getScrollX(), 500); }); }); it('should reload current tab without cache by R', async () => { - let page = await Page.navigateTo(webdriver, server.url('/reload')); + const page = await Page.navigateTo(webdriver, server.url('/reload')); await page.scrollTo(500, 500); let before: number; await eventually(async() => { - let tab = (await browser.tabs.query({}))[0]; + const tab = (await browser.tabs.query({}))[0]; before = Number(new URL(tab.url).hash.split('#')[1]); assert.ok(before > 0); }); @@ -241,13 +241,13 @@ describe("navigate test", () => { let after; await eventually(async() => { - let tab = (await browser.tabs.query({}))[0]; + const tab = (await browser.tabs.query({}))[0]; after = Number(new URL(tab.url).hash.split('#')[1]); assert.ok(after > before); }); await eventually(async() => { - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); assert.strictEqual(await page.getScrollY(), 0); }); }); diff --git a/e2e/options.test.ts b/e2e/options.test.ts index f418dc3..91a3dde 100644 --- a/e2e/options.test.ts +++ b/e2e/options.test.ts @@ -9,7 +9,7 @@ import Page from './lib/Page'; import OptionPage from './lib/OptionPage'; describe("options page", () => { - let server = new TestServer().receiveContent('/', + const server = new TestServer().receiveContent('/', `<!DOCTYPE html><html lang="en"><body style="width:10000px; height:10000px"></body></html>`, ); let lanthan: Lanthan; @@ -35,15 +35,15 @@ describe("options page", () => { }); beforeEach(async() => { - let tabs = await browser.tabs.query({}); - for (let tab of tabs.slice(1)) { + const tabs = await browser.tabs.query({}); + for (const tab of tabs.slice(1)) { await browser.tabs.remove(tab.id); } }); it('saves current config on blur', async () => { - let page = await OptionPage.open(lanthan); - let jsonPage = await page.asJSONOptionPage(); + const page = await OptionPage.open(lanthan); + const jsonPage = await page.asJSONOptionPage(); await jsonPage.updateSettings(`{ "blacklist": [ "https://example.com" ] }`); let { settings } = await browser.storage.local.get('settings'); @@ -56,25 +56,25 @@ describe("options page", () => { assert.strictEqual(settings.source, 'json'); assert.strictEqual(settings.json, '{ "blacklist": [ "https://example.com" ] } '); - let message = await jsonPage.getErrorMessage(); + const message = await jsonPage.getErrorMessage(); assert.ok(message.startsWith('SyntaxError:')) }); it('updates keymaps without reloading', async () => { - let optionPage = await OptionPage.open(lanthan); - let jsonPage = await optionPage.asJSONOptionPage(); + const optionPage = await OptionPage.open(lanthan); + const jsonPage = await optionPage.asJSONOptionPage(); await jsonPage.updateSettings(`{ "keymaps": { "zz": { "type": "scroll.vertically", "count": 10 } } }`); await browser.tabs.create({ url: server.url(), active: false }); await new Promise((resolve) => setTimeout(resolve, 100)); - let handles = await webdriver.getAllWindowHandles(); + const handles = await webdriver.getAllWindowHandles(); await webdriver.switchTo().window(handles[1]); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys('zz'); await eventually(async() => { - let y = await page.getScrollY(); + const y = await page.getScrollY(); assert.strictEqual(y, 640); }); }) diff --git a/e2e/options_form.test.ts b/e2e/options_form.test.ts index c3dc5fb..2121348 100644 --- a/e2e/options_form.test.ts +++ b/e2e/options_form.test.ts @@ -15,8 +15,8 @@ describe("options form page", () => { .build(); browser = lanthan.getWebExtBrowser(); - let tabs = await browser.tabs.query({}); - for (let tab of tabs.slice(1)) { + const tabs = await browser.tabs.query({}); + for (const tab of tabs.slice(1)) { await browser.tabs.remove(tab.id); } }); @@ -28,18 +28,16 @@ describe("options form page", () => { }); it('switch to form settings', async () => { - let page = await OptionPage.open(lanthan); + const page = await OptionPage.open(lanthan); await page.switchToForm(); - let { settings } = await browser.storage.local.get('settings'); + const { settings } = await browser.storage.local.get('settings'); assert.strictEqual(settings.source, 'form') }); it('add blacklist item', async () => { - let page = await OptionPage.open(lanthan); - let forms = await page.switchToForm(); - // Scroll is required to click a button on Firefox 60 - await page.scrollTo(0, 1000); + const page = await OptionPage.open(lanthan); + const forms = await page.switchToForm(); // assert default let settings = (await browser.storage.local.get('settings')).settings; @@ -65,10 +63,8 @@ describe("options form page", () => { }); it('add a partial blacklist item', async () => { - let page = await OptionPage.open(lanthan); - let forms = await page.switchToForm(); - // Scroll is required to click a button on Firefox 60 - await page.scrollTo(0, 1000); + const page = await OptionPage.open(lanthan); + const forms = await page.switchToForm(); // assert default let settings = (await browser.storage.local.get('settings')).settings; @@ -112,8 +108,8 @@ describe("options form page", () => { }); it('add search engines', async () => { - let page = await OptionPage.open(lanthan); - let forms = await page.switchToForm(); + const page = await OptionPage.open(lanthan); + const forms = await page.switchToForm(); // assert default let settings = (await browser.storage.local.get('settings')).settings; diff --git a/e2e/partial_blacklist.test.ts b/e2e/partial_blacklist.test.ts index 950bb39..cb66549 100644 --- a/e2e/partial_blacklist.test.ts +++ b/e2e/partial_blacklist.test.ts @@ -9,7 +9,7 @@ import Settings from '../src/shared/settings/Settings'; import SettingRepository from './lib/SettingRepository'; describe("partial blacklist test", () => { - let server = new TestServer().receiveContent('/*', + const server = new TestServer().receiveContent('/*', `<!DOCTYPE html><html lang="en"><body style="width:10000px; height:10000px"></body></html>`, ); let lanthan: Lanthan; @@ -25,7 +25,7 @@ describe("partial blacklist test", () => { browser = lanthan.getWebExtBrowser(); await server.start(); - let url = server.url().replace('http://', ''); + const url = server.url().replace('http://', ''); await new SettingRepository(browser).saveJSON(Settings.fromJSON({ keymaps: { j: { type: 'scroll.vertically', count: 1 }, @@ -45,7 +45,7 @@ describe("partial blacklist test", () => { }); it('should disable keys in the partial blacklist', async () => { - let page = await Page.navigateTo(webdriver, server.url('/')); + const page = await Page.navigateTo(webdriver, server.url('/')); await page.sendKeys('j'); let scrollY = await page.getScrollY(); diff --git a/e2e/repeat.test.ts b/e2e/repeat.test.ts index b62272f..7c8b5e2 100644 --- a/e2e/repeat.test.ts +++ b/e2e/repeat.test.ts @@ -8,7 +8,7 @@ import { WebDriver } from 'selenium-webdriver'; import Page from './lib/Page'; describe("tab test", () => { - let server = new TestServer().receiveContent('/*', 'ok'); + const server = new TestServer().receiveContent('/*', 'ok'); let lanthan: Lanthan; let webdriver: WebDriver; let browser: any; @@ -32,11 +32,11 @@ describe("tab test", () => { it('repeats last command', async () => { let page = await Page.navigateTo(webdriver, server.url()); - let console = await page.showConsole(); + const console = await page.showConsole(); await console.execCommand(`tabopen ${server.url('/newtab')}`); await eventually(async() => { - let current = await browser.tabs.query({ url: `*://*/newtab` }); + const current = await browser.tabs.query({ url: `*://*/newtab` }); assert.strictEqual(current.length, 1); }); @@ -44,7 +44,7 @@ describe("tab test", () => { await page.sendKeys('.'); await eventually(async() => { - let current = await browser.tabs.query({ url: `*://*/newtab` }); + const current = await browser.tabs.query({ url: `*://*/newtab` }); assert.strictEqual(current.length, 2); }); }); @@ -53,13 +53,13 @@ describe("tab test", () => { for (let i = 1; i < 5; ++i) { await browser.tabs.create({ url: server.url('/#' + i) }); } - let before = await browser.tabs.query({}); + const before = await browser.tabs.query({}); let page = await Page.currentContext(webdriver); await page.sendKeys('d'); await eventually(async() => { - let current = await browser.tabs.query({}); + const current = await browser.tabs.query({}); assert.strictEqual(current.length, before.length - 1); }); @@ -68,7 +68,7 @@ describe("tab test", () => { await page.sendKeys('.'); await eventually(async() => { - let current = await browser.tabs.query({}); + const current = await browser.tabs.query({}); assert.strictEqual(current.length, before.length - 2); }); }); diff --git a/e2e/repeat_n_times.test.ts b/e2e/repeat_n_times.test.ts new file mode 100644 index 0000000..a646112 --- /dev/null +++ b/e2e/repeat_n_times.test.ts @@ -0,0 +1,60 @@ +import * as path from 'path'; +import * as assert from 'assert'; + +import TestServer from './lib/TestServer'; +import eventually from './eventually'; +import { Builder, Lanthan } from 'lanthan'; +import { WebDriver } from 'selenium-webdriver'; +import Page from './lib/Page'; + +describe("tab test", () => { + const server = new TestServer().receiveContent('/', + `<!DOCTYPE html><html lang="en"><body style="width:10000px; height:10000px"></body></html>`, + ); + let lanthan: Lanthan; + let webdriver: WebDriver; + let browser: any; + + before(async() => { + lanthan = await Builder + .forBrowser('firefox') + .spyAddon(path.join(__dirname, '..')) + .build(); + webdriver = lanthan.getWebDriver(); + browser = lanthan.getWebExtBrowser(); + await server.start(); + + browser = browser; + }); + + after(async() => { + await server.stop(); + if (lanthan) { + await lanthan.quit(); + } + }); + + it('repeats scroll 3-times', async () => { + const page = await Page.navigateTo(webdriver, server.url()); + await page.sendKeys('3', 'j'); + + const scrollY = await page.getScrollY(); + assert.strictEqual(scrollY, 64 * 3); + }); + + it('repeats tab deletion 3-times', async () => { + const win = await browser.windows.create({ url: server.url('/#0') }); + for (let i = 1; i < 5; ++i) { + await browser.tabs.create({ url: server.url('/#' + i), windowId: win.id }); + await webdriver.navigate().to(server.url('/#' + i)); + } + + const page = await Page.navigateTo(webdriver, server.url()); + await page.sendKeys('3', 'd'); + + await eventually(async() => { + const current = await browser.tabs.query({ windowId: win.id }); + assert.strictEqual(current.length, 2); + }); + }); +}); diff --git a/e2e/scroll.test.ts b/e2e/scroll.test.ts index 5145265..9b39a2e 100644 --- a/e2e/scroll.test.ts +++ b/e2e/scroll.test.ts @@ -7,7 +7,7 @@ import { WebDriver, Key } from 'selenium-webdriver'; import Page from './lib/Page'; describe("scroll test", () => { - let server = new TestServer().receiveContent('/', + const server = new TestServer().receiveContent('/', `<!DOCTYPE html><html lang="en"><body style="width:10000px; height:10000px"></body></html>`, ); let lanthan: Lanthan; @@ -39,7 +39,7 @@ describe("scroll test", () => { it('scrolls up by j', async () => { await page.sendKeys('j'); - let scrollY = await page.getScrollY(); + const scrollY = await page.getScrollY(); assert.strictEqual(scrollY, 64); }); @@ -47,7 +47,7 @@ describe("scroll test", () => { await webdriver.executeScript(() => window.scrollTo(0, 200)); await page.sendKeys('k'); - let scrollY = await page.getScrollY(); + const scrollY = await page.getScrollY(); assert.strictEqual(scrollY, 136); }); @@ -55,7 +55,7 @@ describe("scroll test", () => { await webdriver.executeScript(() => window.scrollTo(100, 100)); await page.sendKeys('h'); - let pageXOffset = await webdriver.executeScript(() => window.pageXOffset) as number; + const pageXOffset = await webdriver.executeScript(() => window.pageXOffset) as number; assert.strictEqual(pageXOffset, 36); }); @@ -63,7 +63,7 @@ describe("scroll test", () => { await webdriver.executeScript(() => window.scrollTo(100, 100)); await page.sendKeys('l'); - let pageXOffset = await webdriver.executeScript(() => window.pageXOffset) as number; + const pageXOffset = await webdriver.executeScript(() => window.pageXOffset) as number; assert.strictEqual(pageXOffset, 164); }); @@ -71,7 +71,7 @@ describe("scroll test", () => { await webdriver.executeScript(() => window.scrollTo(0, 100)); await page.sendKeys('g', 'g'); - let scrollY = await page.getScrollY(); + const scrollY = await page.getScrollY(); assert.strictEqual(scrollY, 0); }); @@ -79,7 +79,7 @@ describe("scroll test", () => { await webdriver.executeScript(() => window.scrollTo(0, 100)); await page.sendKeys(Key.SHIFT, 'g'); - let scrollY = await page.getScrollY(); + const scrollY = await page.getScrollY(); assert.ok(scrollY > 5000); }); @@ -87,7 +87,7 @@ describe("scroll test", () => { await webdriver.executeScript(() => window.scrollTo(0, 100)); await page.sendKeys(Key.SHIFT, '0'); - let pageXOffset = await webdriver.executeScript(() => window.pageXOffset) as number; + const pageXOffset = await webdriver.executeScript(() => window.pageXOffset) as number; assert.ok(pageXOffset === 0); }); @@ -95,7 +95,7 @@ describe("scroll test", () => { await webdriver.executeScript(() => window.scrollTo(0, 100)); await page.sendKeys(Key.SHIFT, '$'); - let pageXOffset = await webdriver.executeScript(() => window.pageXOffset) as number; + const pageXOffset = await webdriver.executeScript(() => window.pageXOffset) as number; assert.ok(pageXOffset > 5000); }); @@ -103,8 +103,8 @@ describe("scroll test", () => { await webdriver.executeScript(() => window.scrollTo(0, 1000)); await page.sendKeys(Key.CONTROL, 'u'); - let pageHeight = await page.pageHeight(); - let scrollY = await page.getScrollY(); + const pageHeight = await page.pageHeight(); + const scrollY = await page.getScrollY(); assert.ok(Math.abs(scrollY - (1000 - Math.floor(pageHeight / 2))) < 5); }); @@ -112,8 +112,8 @@ describe("scroll test", () => { await webdriver.executeScript(() => window.scrollTo(0, 1000)); await page.sendKeys(Key.CONTROL, 'd'); - let pageHeight = await page.pageHeight(); - let scrollY = await page.getScrollY(); + const pageHeight = await page.pageHeight(); + const scrollY = await page.getScrollY(); assert.ok(Math.abs(scrollY - (1000 + Math.floor(pageHeight / 2))) < 5); }); @@ -121,8 +121,8 @@ describe("scroll test", () => { await webdriver.executeScript(() => window.scrollTo(0, 1000)); await page.sendKeys(Key.CONTROL, 'b'); - let pageHeight = await page.pageHeight(); - let scrollY = await page.getScrollY(); + const pageHeight = await page.pageHeight(); + const scrollY = await page.getScrollY(); assert.ok(Math.abs(scrollY - (1000 - pageHeight)) < 5); }); @@ -130,8 +130,8 @@ describe("scroll test", () => { await webdriver.executeScript(() => window.scrollTo(0, 1000)); await page.sendKeys(Key.CONTROL, 'f'); - let pageHeight = await page.pageHeight(); - let scrollY = await page.getScrollY(); + const pageHeight = await page.pageHeight(); + const scrollY = await page.getScrollY(); assert.ok(Math.abs(scrollY - (1000 + pageHeight)) < 5); }); }); diff --git a/e2e/tab.test.ts b/e2e/tab.test.ts index 1a5dd5c..6a8e815 100644 --- a/e2e/tab.test.ts +++ b/e2e/tab.test.ts @@ -8,7 +8,7 @@ import { WebDriver, Key } from 'selenium-webdriver'; import Page from './lib/Page'; describe("tab test", () => { - let server = new TestServer().receiveContent('/*', 'ok'); + const server = new TestServer().receiveContent('/*', 'ok'); let lanthan: Lanthan; let webdriver: WebDriver; let browser: any; @@ -48,11 +48,11 @@ describe("tab test", () => { it('deletes tab and selects right by d', async () => { await browser.tabs.update(tabs[3].id, { active: true }); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys('d'); await eventually(async() => { - let current = await browser.tabs.query({ windowId: win.id }); + const current = await browser.tabs.query({ windowId: win.id }); assert.strictEqual(current.length, tabs.length - 1); assert.strictEqual(current[3].active, true); assert.strictEqual(current[3].id, tabs[4].id); @@ -61,11 +61,11 @@ describe("tab test", () => { it('deletes tab and selects left by D', async () => { await browser.tabs.update(tabs[3].id, { active: true }); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys(Key.SHIFT, 'D'); await eventually(async() => { - let current = await browser.tabs.query({ windowId: win.id }); + const current = await browser.tabs.query({ windowId: win.id }); assert.strictEqual(current.length, tabs.length - 1); assert.strictEqual(current[2].active, true); assert.strictEqual(current[2].id, tabs[2].id); @@ -74,20 +74,20 @@ describe("tab test", () => { it('deletes all tabs to the right by x$', async () => { await browser.tabs.update(tabs[1].id, { active: true }); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys('x', '$'); - let current = await browser.tabs.query({ windowId: win.id }); + const current = await browser.tabs.query({ windowId: win.id }); assert.strictEqual(current.length, 2); }); it('duplicates tab by zd', async () => { await browser.tabs.update(tabs[0].id, { active: true }); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys('z', 'd'); await eventually(async() => { - let current = await browser.tabs.query({ windowId: win.id }); + const current = await browser.tabs.query({ windowId: win.id }); current.sort((t1: any, t2: any) => t1.index - t2.index); assert.strictEqual(current.length, tabs.length + 1); assert.strictEqual(current[0].url, current[1].url); @@ -96,64 +96,64 @@ describe("tab test", () => { it('makes pinned by zp', async () => { await browser.tabs.update(tabs[0].id, { active: true }); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys('z', 'p'); - let current = await browser.tabs.query({ windowId: win.id }); + const current = await browser.tabs.query({ windowId: win.id }); assert.strictEqual(current[0].pinned, true); }); it('selects previous tab by K', async () => { await browser.tabs.update(tabs[2].id, { active: true }); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys(Key.SHIFT, 'K'); - let current = await browser.tabs.query({ windowId: win.id }); + const current = await browser.tabs.query({ windowId: win.id }); assert.strictEqual(current[1].active, true); }); it('selects previous tab by K rotatory', async () => { await browser.tabs.update(tabs[0].id, { active: true }); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys(Key.SHIFT, 'K'); - let current = await browser.tabs.query({ windowId: win.id }); + const current = await browser.tabs.query({ windowId: win.id }); assert.strictEqual(current[current.length - 1].active, true) }); it('selects next tab by J', async () => { await browser.tabs.update(tabs[2].id, { active: true }); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys(Key.SHIFT, 'J'); - let current = await browser.tabs.query({ windowId: win.id }); + const current = await browser.tabs.query({ windowId: win.id }); assert.strictEqual(current[3].active, true); }); it('selects previous tab by J rotatory', async () => { await browser.tabs.update(tabs[tabs.length - 1].id, { active: true }); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys(Key.SHIFT, 'J'); - let current = await browser.tabs.query({ windowId: win.id }); + const current = await browser.tabs.query({ windowId: win.id }); assert.strictEqual(current[0].active, true) }); it('selects first tab by g0', async () => { await browser.tabs.update(tabs[2].id, { active: true }); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys('g', '0'); - let current = await browser.tabs.query({ windowId: win.id }); + const current = await browser.tabs.query({ windowId: win.id }); assert.strictEqual(current[0].active, true) }); it('selects last tab by g$', async () => { await browser.tabs.update(tabs[2].id, { active: true }); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys('g', '$'); - let current = await browser.tabs.query({ windowId: win.id }); + const current = await browser.tabs.query({ windowId: win.id }); assert.strictEqual(current[current.length - 1].active, true) }); @@ -161,10 +161,10 @@ describe("tab test", () => { await browser.tabs.update(tabs[1].id, { active: true }); await browser.tabs.update(tabs[4].id, { active: true }); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys(Key.CONTROL, '6'); - let current = await browser.tabs.query({ windowId: win.id }); + const current = await browser.tabs.query({ windowId: win.id }); assert.strictEqual(current[1].active, true) }); @@ -172,38 +172,38 @@ describe("tab test", () => { // This might be a bug in Firefox. it.skip('reopen tab by u', async () => { await browser.tabs.remove(tabs[1].id); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys('u'); - let current = await browser.tabs.query({ windowId: win.id }); + const current = await browser.tabs.query({ windowId: win.id }); assert.strictEqual(current.length, tabs.length); }); it('does not delete pinned tab by d', async () => { await browser.tabs.update(tabs[0].id, { active: true, pinned: true }); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys('d'); - let current = await browser.tabs.query({ windowId: win.id }); + const current = await browser.tabs.query({ windowId: win.id }); assert.strictEqual(current.length, tabs.length); }); it('deletes pinned tab by !d', async () => { await browser.tabs.update(tabs[0].id, { active: true, pinned: true }); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys('!', 'd'); - let current = await browser.tabs.query({ windowId: win.id }); + const current = await browser.tabs.query({ windowId: win.id }); assert.strictEqual(current.length, tabs.length - 1); }); it('opens view-source by gf', async () => { await browser.tabs.update(tabs[0].id, { active: true }); - let page = await Page.currentContext(webdriver); + const page = await Page.currentContext(webdriver); await page.sendKeys('g', 'f'); await eventually(async() => { - let current = await browser.tabs.query({ windowId: win.id }); + const current = await browser.tabs.query({ windowId: win.id }); assert.strictEqual(current.length, tabs.length + 1); assert.strictEqual(current[current.length - 1].url, `view-source:${server.url('/#0')}`); }); diff --git a/e2e/zoom.test.ts b/e2e/zoom.test.ts index af5cc68..d089097 100644 --- a/e2e/zoom.test.ts +++ b/e2e/zoom.test.ts @@ -33,21 +33,21 @@ describe("zoom test", () => { }); it('should zoom in by zi', async () => { - let before = await browser.tabs.getZoom(tab.id); + const before = await browser.tabs.getZoom(tab.id); await page.sendKeys('zi'); await eventually(async() => { - let actual = await browser.tabs.getZoom(tab.id); + const actual = await browser.tabs.getZoom(tab.id); assert.ok(before < actual); }); }); it('should zoom out by zo', async () => { - let before = await browser.tabs.getZoom(tab.id); + const before = await browser.tabs.getZoom(tab.id); await page.sendKeys('zo'); await eventually(async() => { - let actual = await browser.tabs.getZoom(tab.id); + const actual = await browser.tabs.getZoom(tab.id); assert.ok(before > actual); }); }); @@ -57,7 +57,7 @@ describe("zoom test", () => { await page.sendKeys('zz'); await eventually(async() => { - let actual = await browser.tabs.getZoom(tab.id); + const actual = await browser.tabs.getZoom(tab.id); assert.strictEqual(actual, 1); }); }); diff --git a/package-lock.json b/package-lock.json index b0b1265..7cdebb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -248,6 +248,15 @@ "redux": "^4.0.0" } }, + "@types/react-test-renderer": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.9.0.tgz", + "integrity": "sha512-bN5EyjtuTY35xX7N5j0KP1vg5MpUXHpFTX6tGsqkNOthjNvet4VQOYRxFh+NT5cDSJrATmAFK9NLeYZ4mp/o0Q==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/redux-promise": { "version": "0.5.28", "resolved": "https://registry.npmjs.org/@types/redux-promise/-/redux-promise-0.5.28.tgz", @@ -581,9 +590,9 @@ "dev": true }, "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", "dev": true, "requires": { "fast-deep-equal": "^2.0.1", @@ -592,6 +601,28 @@ "uri-js": "^4.2.2" } }, + "ajv-cli": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ajv-cli/-/ajv-cli-3.0.0.tgz", + "integrity": "sha1-WCMjH2TigzBUEwaQsYCZYJ6YDyk=", + "dev": true, + "requires": { + "ajv": "^6.0.0", + "ajv-pack": "^0.3.0", + "fast-json-patch": "^0.5.6", + "glob": "^7.0.3", + "json-schema-migrate": "^0.2.0", + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, "ajv-errors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", @@ -604,6 +635,16 @@ "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", "dev": true }, + "ajv-pack": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/ajv-pack/-/ajv-pack-0.3.1.tgz", + "integrity": "sha1-tyxNQhnjko5ihC10Le2Tv1B5ZWA=", + "dev": true, + "requires": { + "js-beautify": "^1.6.4", + "require-from-string": "^1.2.0" + } + }, "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", @@ -629,10 +670,13 @@ "dev": true }, "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } }, "anymatch": { "version": "2.0.0", @@ -1355,9 +1399,9 @@ } }, "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "camelcase-keys": { @@ -1368,6 +1412,14 @@ "requires": { "camelcase": "^2.0.0", "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + } } }, "caseless": { @@ -1391,16 +1443,25 @@ } }, "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "chardet": { @@ -1556,6 +1617,12 @@ } } }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", @@ -1608,6 +1675,12 @@ "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, + "common-tags": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", + "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", + "dev": true + }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -1650,6 +1723,16 @@ "typedarray": "^0.0.6" } }, + "config-chain": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "dev": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, "connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", @@ -1836,18 +1919,30 @@ "schema-utils": "^2.0.0" }, "dependencies": { + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "dev": true + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, "json5": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", @@ -1875,13 +1970,13 @@ "dev": true }, "schema-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.1.0.tgz", - "integrity": "sha512-g6SViEZAfGNrToD82ZPUjq52KUPDYc+fN5+g6Euo5mLokl/9Yx14z0Cu4RR1m55HtBXejO0sBt+qw79axN+Fiw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.5.0.tgz", + "integrity": "sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1" } } } @@ -2076,6 +2171,12 @@ "randombytes": "^2.0.0" } }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -2194,6 +2295,36 @@ "safe-buffer": "^5.0.1" } }, + "editorconfig": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", + "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==", + "dev": true, + "requires": { + "commander": "^2.19.0", + "lru-cache": "^4.1.5", + "semver": "^5.6.0", + "sigmund": "^1.0.1" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + } + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2566,6 +2697,32 @@ } } }, + "eslint-config-prettier": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.7.0.tgz", + "integrity": "sha512-FamQVKM3jjUVwhG4hEMnbtsq7xOIDm+SY5iBPfR8gKsJoAB2IQnNF+bk1+8Fy44Nq7PPJaLvkRxILYdJWoguKQ==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + }, + "dependencies": { + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + } + } + }, + "eslint-plugin-prettier": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz", + "integrity": "sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, "eslint-plugin-react": { "version": "7.14.3", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz", @@ -2603,6 +2760,12 @@ } } }, + "eslint-plugin-standard": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", + "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", + "dev": true + }, "eslint-scope": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", @@ -3045,6 +3208,18 @@ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", "dev": true }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-json-patch": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-0.5.7.tgz", + "integrity": "sha1-taj0nSWWJFlu+YuHLz/aiVtNhmU=", + "dev": true + }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", @@ -4325,9 +4500,9 @@ } }, "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", + "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", "dev": true }, "html-minifier": { @@ -4914,6 +5089,45 @@ "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==", "dev": true }, + "js-beautify": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.10.2.tgz", + "integrity": "sha512-ZtBYyNUYJIsBWERnQP0rPN9KjkrDfJcMjuVGcvXOUJrD1zmOGwhRwQ4msG+HJ+Ni/FA7+sRQEMYVzdTQDvnzvQ==", + "dev": true, + "requires": { + "config-chain": "^1.1.12", + "editorconfig": "^0.15.3", + "glob": "^7.1.3", + "mkdirp": "~0.5.1", + "nopt": "~4.0.1" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "dev": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + } + } + }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -4948,6 +5162,41 @@ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", "dev": true }, + "json-schema-migrate": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json-schema-migrate/-/json-schema-migrate-0.2.0.tgz", + "integrity": "sha1-ukelsAcvxyOWRg4b1gtE1SF4u8Y=", + "dev": true, + "requires": { + "ajv": "^5.0.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + } + } + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -5411,14 +5660,6 @@ "pify": "^2.0.0", "pinkie-promise": "^2.0.0", "strip-bom": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } } }, "loader-runner": { @@ -5496,6 +5737,12 @@ "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", "dev": true }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -5578,6 +5825,49 @@ } } }, + "loglevel": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.6.tgz", + "integrity": "sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ==", + "dev": true + }, + "loglevel-colored-level-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz", + "integrity": "sha1-akAhj9x64V/HbD0PPmdsRlOIYD4=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "loglevel": "^1.4.1" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, "lolex": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz", @@ -6225,6 +6515,12 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -6452,9 +6748,9 @@ } }, "node-sass": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz", - "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.0.tgz", + "integrity": "sha512-W1XBrvoJ1dy7VsvTAS5q1V45lREbTlZQqFbiHb3R3OTTCma0XBtuG6xZ6Z4506nR4lmHPTqVRwxT6KgtWC97CA==", "dev": true, "requires": { "async-foreach": "^0.1.3", @@ -6464,7 +6760,7 @@ "get-stdin": "^4.0.1", "glob": "^7.0.3", "in-publish": "^2.0.0", - "lodash": "^4.17.11", + "lodash": "^4.17.15", "meow": "^3.7.0", "mkdirp": "^0.5.1", "nan": "^2.13.2", @@ -6476,16 +6772,29 @@ "true-case-path": "^1.0.2" }, "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, - "nan": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", - "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true } } @@ -7068,6 +7377,12 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", @@ -7106,14 +7421,6 @@ "graceful-fs": "^4.1.2", "pify": "^2.0.0", "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } } }, "pathval": { @@ -7148,9 +7455,9 @@ "dev": true }, "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, "pinkie": { @@ -7184,9 +7491,9 @@ "dev": true }, "postcss": { - "version": "7.0.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.17.tgz", - "integrity": "sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==", + "version": "7.0.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz", + "integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -7194,51 +7501,11 @@ "supports-color": "^6.1.0" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } } } }, @@ -7306,6 +7573,210 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true + }, + "prettier-eslint": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-9.0.1.tgz", + "integrity": "sha512-KZT65QTosSAqBBqmrC+RpXbsMRe7Os2YSR9cAfFbDlyPAopzA/S5bioiZ3rpziNQNSJaOxmtXSx07EQ+o2Dlug==", + "dev": true, + "requires": { + "@typescript-eslint/parser": "^1.10.2", + "common-tags": "^1.4.0", + "core-js": "^3.1.4", + "dlv": "^1.1.0", + "eslint": "^5.0.0", + "indent-string": "^4.0.0", + "lodash.merge": "^4.6.0", + "loglevel-colored-level-prefix": "^1.0.0", + "prettier": "^1.7.0", + "pretty-format": "^23.0.1", + "require-relative": "^0.8.7", + "typescript": "^3.2.1", + "vue-eslint-parser": "^2.0.2" + }, + "dependencies": { + "@typescript-eslint/experimental-utils": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz", + "integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "1.13.0", + "eslint-scope": "^4.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.13.0.tgz", + "integrity": "sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "1.13.0", + "@typescript-eslint/typescript-estree": "1.13.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz", + "integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==", + "dev": true, + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.5.0" + } + }, + "acorn": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", + "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "eslint": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", + "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.9.1", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^4.0.3", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^5.0.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.2.2", + "js-yaml": "^3.13.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^5.5.1", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "espree": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", + "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "dev": true, + "requires": { + "acorn": "^6.0.7", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "pretty-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", @@ -7316,6 +7787,24 @@ "utila": "~0.4" } }, + "pretty-format": { + "version": "23.6.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz", + "integrity": "sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0", + "ansi-styles": "^3.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + } + } + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -7362,6 +7851,12 @@ } } }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "dev": true + }, "proxy-addr": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", @@ -7889,12 +8384,24 @@ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, + "require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=", + "dev": true + }, "require-main-filename": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", "dev": true }, + "require-relative": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", + "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", + "dev": true + }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -7902,9 +8409,9 @@ "dev": true }, "resolve": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -8068,6 +8575,12 @@ "semver": "^6.3.0" }, "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -8338,6 +8851,12 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -8659,9 +9178,9 @@ } }, "spdx-license-ids": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", - "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", "dev": true }, "split-string": { @@ -8917,6 +9436,24 @@ "schema-utils": "^2.0.1" }, "dependencies": { + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "dev": true + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -8950,22 +9487,25 @@ "dev": true }, "schema-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.1.0.tgz", - "integrity": "sha512-g6SViEZAfGNrToD82ZPUjq52KUPDYc+fN5+g6Euo5mLokl/9Yx14z0Cu4RR1m55HtBXejO0sBt+qw79axN+Fiw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.5.0.tgz", + "integrity": "sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1" } } } }, "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } }, "symbol-observable": { "version": "1.2.0", @@ -9439,9 +9979,9 @@ } }, "tsyringe": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-3.3.0.tgz", - "integrity": "sha512-XWaZBMbYEnSCi+V9XNyzgDPuDAPdRVQuk5C0aIAJLM3jD7Z/na1b5p+/FWzZ36HfQtweH5bUCdWFlHP7DBj22Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.0.0.tgz", + "integrity": "sha512-8dG+6zoDHb/c4F5rydpRfDovoLNo+g59qN2b7KH/abD4Nz5wx+lLXtOxljB57RWnBcbC+ZvKYwrCqsZxQJO8ng==", "dev": true, "requires": { "tslib": "^1.9.3" @@ -9786,6 +10326,80 @@ "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", "dev": true }, + "vue-eslint-parser": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz", + "integrity": "sha512-ZezcU71Owm84xVF6gfurBQUGg8WQ+WZGxgDEQu1IHFBZNx7BFZg3L1yHxrCBNNwbwFtE1GuvfJKMtb6Xuwc/Bw==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "eslint-scope": "^3.7.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^3.5.2", + "esquery": "^1.0.0", + "lodash": "^4.17.4" + }, + "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "^3.0.4" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "espree": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "dev": true, + "requires": { + "acorn": "^5.5.0", + "acorn-jsx": "^3.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "watchpack": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", @@ -9804,9 +10418,9 @@ "dev": true }, "webextensions-api-fake": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/webextensions-api-fake/-/webextensions-api-fake-0.8.0.tgz", - "integrity": "sha512-6lby6Q3UwvXE/tEFcSigFNTGTEDVsO7Ud5RskvKuqJ59fJO/9OulDzdAaWV3bMjJqQ77nwVcEKoyaNWFClJ4EA==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/webextensions-api-fake/-/webextensions-api-fake-0.9.0.tgz", + "integrity": "sha512-pu0r4G1P1PPVJUpZFATAND8pAGV09l9P/CeRQkHd0wcFqsYiKwu2iIqWFxtQ0/HtPDQStnh5Rfvqw6Buv75HMw==", "dev": true, "requires": { "sinon-chrome": "^3.0.0" diff --git a/package.json b/package.json index 4fee0e8..de09a33 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,12 @@ "name": "vim-vixen", "description": "Vim vixen", "scripts": { + "schema": "ajv compile -s src/shared/settings/schema.json -o src/shared/settings/validate.js", "start": "webpack --mode development -w --debug --devtool inline-source-map", "build": "NODE_ENV=production webpack --mode production --progress --display-error-details --devtool inline-source-map", "package": "npm run build && script/package", - "lint": "eslint --ext .js,.jsx,.ts,.tsx src", + "lint": "eslint --ext .ts,.tsx .", + "lint:fix": "eslint --ext .ts,.tsx . --fix", "type-checks": "tsc --noEmit", "test": "karma start", "test:e2e": "mocha --timeout 10000 --retries 10 --require ts-node/register --extension ts e2e" @@ -30,15 +32,21 @@ "@types/react": "^16.9.2", "@types/react-dom": "^16.9.0", "@types/react-redux": "^7.1.2", + "@types/react-test-renderer": "^16.9.0", "@types/redux-promise": "^0.5.28", "@types/selenium-webdriver": "^4.0.2", "@types/sinon": "^7.0.13", "@typescript-eslint/eslint-plugin": "^2.0.0", "@typescript-eslint/parser": "^2.0.0", + "ajv": "^6.10.2", + "ajv-cli": "^3.0.0", "chai": "^4.2.0", "css-loader": "^3.2.0", "eslint": "^6.2.2", + "eslint-config-prettier": "^6.7.0", + "eslint-plugin-prettier": "^3.1.2", "eslint-plugin-react": "^7.14.3", + "eslint-plugin-standard": "^4.0.1", "express": "^4.17.1", "html-webpack-plugin": "^3.2.0", "jsonwebtoken": "^8.5.1", @@ -53,7 +61,9 @@ "karma-webpack": "^4.0.2", "lanthan": "0.0.2", "mocha": "^6.2.0", - "node-sass": "^4.12.0", + "node-sass": "^4.13.0", + "prettier": "^1.19.1", + "prettier-eslint": "^9.0.1", "react": "^16.9.0", "react-dom": "^16.9.0", "react-redux": "^7.1.1", @@ -68,10 +78,10 @@ "style-loader": "^1.0.0", "ts-loader": "^6.0.4", "ts-node": "^8.4.1", - "tsyringe": "^3.3.0", + "tsyringe": "^4.0.0", "typescript": "^3.6.2", "web-ext-types": "^3.2.1", - "webextensions-api-fake": "^0.8.0", + "webextensions-api-fake": "^0.9.0", "webpack": "^4.39.2", "webpack-cli": "^3.3.7" } diff --git a/src/background/controllers/CommandController.ts b/src/background/controllers/CommandController.ts index 11fed01..7297ef8 100644 --- a/src/background/controllers/CommandController.ts +++ b/src/background/controllers/CommandController.ts @@ -17,13 +17,13 @@ export default class CommandController { } getCompletions(line: string): Promise<CompletionGroup[]> { - let trimmed = trimStart(line); - let words = trimmed.split(/ +/); - let name = words[0]; + const trimmed = trimStart(line); + const words = trimmed.split(/ +/); + const name = words[0]; if (words.length === 1) { return this.completionsUseCase.queryConsoleCommand(name); } - let keywords = trimStart(trimmed.slice(name.length)); + const keywords = trimStart(trimmed.slice(name.length)); switch (words[0]) { case 'o': case 'open': @@ -53,14 +53,14 @@ export default class CommandController { // eslint-disable-next-line complexity exec(line: string): Promise<any> { - let trimmed = trimStart(line); - let words = trimmed.split(/ +/); - let name = words[0]; + const trimmed = trimStart(line); + const words = trimmed.split(/ +/); + const name = words[0]; if (words[0].length === 0) { return Promise.resolve(); } - let keywords = trimStart(trimmed.slice(name.length)); + const keywords = trimStart(trimmed.slice(name.length)); switch (words[0]) { case 'o': case 'open': diff --git a/src/background/controllers/OperationController.ts b/src/background/controllers/OperationController.ts index 7a10ad6..181dd19 100644 --- a/src/background/controllers/OperationController.ts +++ b/src/background/controllers/OperationController.ts @@ -21,95 +21,108 @@ export default class OperationController { ) { } - async exec(op: operations.Operation): Promise<any> { - await this.doOperation(op); + async exec(repeat: number, op: operations.Operation): Promise<any> { + await this.doOperation(repeat, op); if (this.repeatUseCase.isRepeatable(op)) { this.repeatUseCase.storeLastOperation(op); } } // eslint-disable-next-line complexity, max-lines-per-function - doOperation(operation: operations.Operation): Promise<any> { - switch (operation.type) { - case operations.TAB_CLOSE: - return this.tabUseCase.close(false, operation.select === 'left'); - case operations.TAB_CLOSE_RIGHT: - return this.tabUseCase.closeRight(); - case operations.TAB_CLOSE_FORCE: - return this.tabUseCase.close(true); - case operations.TAB_REOPEN: - return this.tabUseCase.reopen(); - case operations.TAB_PREV: - return this.tabSelectUseCase.selectPrev(1); - case operations.TAB_NEXT: - return this.tabSelectUseCase.selectNext(1); - case operations.TAB_FIRST: - return this.tabSelectUseCase.selectFirst(); - case operations.TAB_LAST: - return this.tabSelectUseCase.selectLast(); - case operations.TAB_PREV_SEL: - return this.tabSelectUseCase.selectPrevSelected(); - case operations.TAB_RELOAD: - return this.tabUseCase.reload(operation.cache); - case operations.TAB_PIN: - return this.tabUseCase.setPinned(true); - case operations.TAB_UNPIN: - return this.tabUseCase.setPinned(false); - case operations.TAB_TOGGLE_PINNED: - return this.tabUseCase.togglePinned(); - case operations.TAB_DUPLICATE: - return this.tabUseCase.duplicate(); - case operations.PAGE_SOURCE: - return this.tabUseCase.openPageSource(); - case operations.PAGE_HOME: - return this.tabUseCase.openHome(operation.newTab); - case operations.ZOOM_IN: - return this.zoomUseCase.zoomIn(); - case operations.ZOOM_OUT: - return this.zoomUseCase.zoomOut(); - case operations.ZOOM_NEUTRAL: - return this.zoomUseCase.zoomNutoral(); - case operations.COMMAND_SHOW: - return this.consoleUseCase.showCommand(); - case operations.COMMAND_SHOW_OPEN: - return this.consoleUseCase.showOpenCommand(operation.alter); - case operations.COMMAND_SHOW_TABOPEN: - return this.consoleUseCase.showTabopenCommand(operation.alter); - case operations.COMMAND_SHOW_WINOPEN: - return this.consoleUseCase.showWinopenCommand(operation.alter); - case operations.COMMAND_SHOW_BUFFER: - return this.consoleUseCase.showBufferCommand(); - case operations.COMMAND_SHOW_ADDBOOKMARK: - return this.consoleUseCase.showAddbookmarkCommand(operation.alter); - case operations.FIND_START: - return this.findUseCase.findStart(); - case operations.CANCEL: - return this.consoleUseCase.hideConsole(); - case operations.NAVIGATE_HISTORY_PREV: - return this.navigateUseCase.openHistoryPrev(); - case operations.NAVIGATE_HISTORY_NEXT: - return this.navigateUseCase.openHistoryNext(); - case operations.NAVIGATE_LINK_PREV: - return this.navigateUseCase.openLinkPrev(); - case operations.NAVIGATE_LINK_NEXT: - return this.navigateUseCase.openLinkNext(); - case operations.NAVIGATE_PARENT: - return this.navigateUseCase.openParent(); - case operations.NAVIGATE_ROOT: - return this.navigateUseCase.openRoot(); - case operations.REPEAT_LAST: - { - let last = this.repeatUseCase.getLastOperation(); - if (typeof last !== 'undefined') { - return this.doOperation(last); + async doOperation( + repeat: number, + operation: operations.Operation, + ): Promise<any> { + // eslint-disable-next-line complexity, max-lines-per-function + const opFunc = (() => { + switch (operation.type) { + case operations.TAB_CLOSE: + return () => this.tabUseCase.close(false, operation.select === 'left'); + case operations.TAB_CLOSE_RIGHT: + return () => this.tabUseCase.closeRight(); + case operations.TAB_CLOSE_FORCE: + return () => this.tabUseCase.close(true); + case operations.TAB_REOPEN: + return () => this.tabUseCase.reopen(); + case operations.TAB_PREV: + return () => this.tabSelectUseCase.selectPrev(1); + case operations.TAB_NEXT: + return () => this.tabSelectUseCase.selectNext(1); + case operations.TAB_FIRST: + return () => this.tabSelectUseCase.selectFirst(); + case operations.TAB_LAST: + return () => this.tabSelectUseCase.selectLast(); + case operations.TAB_PREV_SEL: + return () => this.tabSelectUseCase.selectPrevSelected(); + case operations.TAB_RELOAD: + return () => this.tabUseCase.reload(operation.cache); + case operations.TAB_PIN: + return () => this.tabUseCase.setPinned(true); + case operations.TAB_UNPIN: + return () => this.tabUseCase.setPinned(false); + case operations.TAB_TOGGLE_PINNED: + return () => this.tabUseCase.togglePinned(); + case operations.TAB_DUPLICATE: + return () => this.tabUseCase.duplicate(); + case operations.PAGE_SOURCE: + return () => this.tabUseCase.openPageSource(); + case operations.PAGE_HOME: + return () => this.tabUseCase.openHome(operation.newTab); + case operations.ZOOM_IN: + return () => this.zoomUseCase.zoomIn(); + case operations.ZOOM_OUT: + return () => this.zoomUseCase.zoomOut(); + case operations.ZOOM_NEUTRAL: + return () => this.zoomUseCase.zoomNutoral(); + case operations.COMMAND_SHOW: + return () => this.consoleUseCase.showCommand(); + case operations.COMMAND_SHOW_OPEN: + return () => this.consoleUseCase.showOpenCommand(operation.alter); + case operations.COMMAND_SHOW_TABOPEN: + return () => this.consoleUseCase.showTabopenCommand(operation.alter); + case operations.COMMAND_SHOW_WINOPEN: + return () => this.consoleUseCase.showWinopenCommand(operation.alter); + case operations.COMMAND_SHOW_BUFFER: + return () => this.consoleUseCase.showBufferCommand(); + case operations.COMMAND_SHOW_ADDBOOKMARK: + return () => this.consoleUseCase.showAddbookmarkCommand( + operation.alter); + case operations.FIND_START: + return () => this.findUseCase.findStart(); + case operations.CANCEL: + return () => this.consoleUseCase.hideConsole(); + case operations.NAVIGATE_HISTORY_PREV: + return () => this.navigateUseCase.openHistoryPrev(); + case operations.NAVIGATE_HISTORY_NEXT: + return () => this.navigateUseCase.openHistoryNext(); + case operations.NAVIGATE_LINK_PREV: + return () => this.navigateUseCase.openLinkPrev(); + case operations.NAVIGATE_LINK_NEXT: + return () => this.navigateUseCase.openLinkNext(); + case operations.NAVIGATE_PARENT: + return () => this.navigateUseCase.openParent(); + case operations.NAVIGATE_ROOT: + return () => this.navigateUseCase.openRoot(); + case operations.REPEAT_LAST: + return () => { + const last = this.repeatUseCase.getLastOperation(); + if (typeof last !== 'undefined') { + return this.doOperation(1, last); + } + return Promise.resolve(); + }; + case operations.INTERNAL_OPEN_URL: + return () => this.tabUseCase.openURL( + operation.url, operation.newTab, operation.newWindow); + default: + throw new Error('unknown operation: ' + operation.type); } - return Promise.resolve(); - } - case operations.INTERNAL_OPEN_URL: - return this.tabUseCase.openURL( - operation.url, operation.newTab, operation.newWindow); + })(); + + for (let i = 0; i < repeat; ++i) { + // eslint-disable-next-line no-await-in-loop + await opFunc(); } - throw new Error('unknown operation: ' + operation.type); } } diff --git a/src/background/index.ts b/src/background/index.ts index 26b0f6b..51fde56 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -2,5 +2,5 @@ import 'reflect-metadata'; import { container } from 'tsyringe'; import Application from './Application'; -let app = container.resolve(Application); +const app = container.resolve(Application); app.run(); diff --git a/src/background/infrastructures/ContentMessageClient.ts b/src/background/infrastructures/ContentMessageClient.ts index 4d2284c..b6c0c23 100644 --- a/src/background/infrastructures/ContentMessageClient.ts +++ b/src/background/infrastructures/ContentMessageClient.ts @@ -4,8 +4,8 @@ import * as messages from '../../shared/messages'; @injectable() export default class ContentMessageClient { async broadcastSettingsChanged(): Promise<void> { - let tabs = await browser.tabs.query({}); - for (let tab of tabs) { + const tabs = await browser.tabs.query({}); + for (const tab of tabs) { if (!tab.id || tab.url && tab.url.startsWith('about:')) { continue; } @@ -16,7 +16,7 @@ export default class ContentMessageClient { } async getAddonEnabled(tabId: number): Promise<boolean> { - let enabled = await browser.tabs.sendMessage(tabId, { + const enabled = await browser.tabs.sendMessage(tabId, { type: messages.ADDON_ENABLED_QUERY, }); return enabled as any as boolean; diff --git a/src/background/infrastructures/ContentMessageListener.ts b/src/background/infrastructures/ContentMessageListener.ts index f80d686..d063810 100644 --- a/src/background/infrastructures/ContentMessageListener.ts +++ b/src/background/infrastructures/ContentMessageListener.ts @@ -1,5 +1,6 @@ import { injectable } from 'tsyringe'; import * as messages from '../../shared/messages'; +import * as operations from '../../shared/operations'; import CompletionGroup from '../domains/CompletionGroup'; import CommandController from '../controllers/CommandController'; import SettingController from '../controllers/SettingController'; @@ -19,7 +20,7 @@ export default class ContentMessageListener { private findController: FindController, private addonEnabledController: AddonEnabledController, private linkController: LinkController, - private backgroundOperationController: OperationController, + private operationController: OperationController, private markController: MarkController, ) { this.consolePorts = {}; @@ -30,7 +31,7 @@ export default class ContentMessageListener { message: any, sender: browser.runtime.MessageSender, ) => { try { - let ret = this.onMessage(message, sender.tab as browser.tabs.Tab); + const ret = this.onMessage(message, sender.tab as browser.tabs.Tab); if (!(ret instanceof Promise)) { return {}; } @@ -79,7 +80,7 @@ export default class ContentMessageListener { senderTab.id as number, message.background); case messages.BACKGROUND_OPERATION: - return this.onBackgroundOperation(message.operation); + return this.onBackgroundOperation(message.repeat, message.operation); case messages.MARK_SET_GLOBAL: return this.onMarkSetGlobal(message.key, message.x, message.y); case messages.MARK_JUMP_GLOBAL: @@ -93,7 +94,7 @@ export default class ContentMessageListener { } async onConsoleQueryCompletions(line: string): Promise<CompletionGroup[]> { - let completions = await this.commandController.getCompletions(line); + const completions = await this.commandController.getCompletions(line); return Promise.resolve(completions); } @@ -126,8 +127,8 @@ export default class ContentMessageListener { return this.linkController.openToTab(url, openerId); } - onBackgroundOperation(operation: any): Promise<any> { - return this.backgroundOperationController.exec(operation); + onBackgroundOperation(count: number, op: operations.Operation): Promise<any> { + return this.operationController.exec(count, op); } onMarkSetGlobal(key: string, x: number, y: number): Promise<any> { @@ -139,7 +140,7 @@ export default class ContentMessageListener { } onConsoleFrameMessage(tabId: number, message: any): void { - let port = this.consolePorts[tabId]; + const port = this.consolePorts[tabId]; if (!port) { return; } @@ -152,7 +153,7 @@ export default class ContentMessageListener { } if (port.sender && port.sender.tab && port.sender.tab.id) { - let id = port.sender.tab.id; + const id = port.sender.tab.id; this.consolePorts[id] = port; } } diff --git a/src/background/infrastructures/MemoryStorage.ts b/src/background/infrastructures/MemoryStorage.ts index baf9ffa..af445a6 100644 --- a/src/background/infrastructures/MemoryStorage.ts +++ b/src/background/infrastructures/MemoryStorage.ts @@ -2,7 +2,7 @@ const db: {[key: string]: any} = {}; export default class MemoryStorage { set(name: string, value: any): void { - let data = JSON.stringify(value); + const data = JSON.stringify(value); if (typeof data === 'undefined') { throw new Error('value is not serializable'); } @@ -10,7 +10,7 @@ export default class MemoryStorage { } get(name: string): any { - let data = db[name]; + const data = db[name]; if (!data) { return undefined; } diff --git a/src/background/presenters/IndicatorPresenter.ts b/src/background/presenters/IndicatorPresenter.ts index 99f92b5..6a33e62 100644 --- a/src/background/presenters/IndicatorPresenter.ts +++ b/src/background/presenters/IndicatorPresenter.ts @@ -3,7 +3,7 @@ import { injectable } from 'tsyringe'; @injectable() export default class IndicatorPresenter { indicate(enabled: boolean): Promise<void> { - let path = enabled + const path = enabled ? 'resources/enabled_32x32.png' : 'resources/disabled_32x32.png'; if (typeof browser.browserAction.setIcon === 'function') { diff --git a/src/background/presenters/NotifyPresenter.ts b/src/background/presenters/NotifyPresenter.ts index defb601..b7f4f99 100644 --- a/src/background/presenters/NotifyPresenter.ts +++ b/src/background/presenters/NotifyPresenter.ts @@ -6,8 +6,8 @@ const NOTIFICATION_ID_INVALID_SETTINGS = 'vimvixen-update-invalid-settings'; @injectable() export default class NotifyPresenter { async notifyUpdated(version: string, onclick: () => void): Promise<void> { - let title = `Vim Vixen ${version} has been installed`; - let message = 'Click here to see release notes'; + const title = `Vim Vixen ${version} has been installed`; + const message = 'Click here to see release notes'; const listener = (id: string) => { if (id !== NOTIFICATION_ID_UPDATE) { @@ -27,9 +27,9 @@ export default class NotifyPresenter { } async notifyInvalidSettings(onclick: () => void): Promise<void> { - let title = `Loaded settings is invalid`; + const title = `Loaded settings is invalid`; // eslint-disable-next-line max-len - let message = 'The default settings is used due to the last saved settings is invalid. Check your current settings from the add-on preference'; + const message = 'The default settings is used due to the last saved settings is invalid. Check your current settings from the add-on preference'; const listener = (id: string) => { if (id !== NOTIFICATION_ID_INVALID_SETTINGS) { diff --git a/src/background/presenters/TabPresenter.ts b/src/background/presenters/TabPresenter.ts index ed88f26..33d8bea 100644 --- a/src/background/presenters/TabPresenter.ts +++ b/src/background/presenters/TabPresenter.ts @@ -17,7 +17,7 @@ export default class TabPresenter { } async getCurrent(): Promise<Tab> { - let tabs = await browser.tabs.query({ + const tabs = await browser.tabs.query({ active: true, currentWindow: true }); return tabs[0]; @@ -28,8 +28,8 @@ export default class TabPresenter { } async getLastSelectedId(): Promise<number | undefined> { - let cache = new MemoryStorage(); - let tabId = await cache.get(LAST_SELECTED_KEY); + const cache = new MemoryStorage(); + const tabId = await cache.get(LAST_SELECTED_KEY); if (tabId === null || typeof tabId === 'undefined') { return; } @@ -37,9 +37,9 @@ export default class TabPresenter { } async getByKeyword( - keyword: string, excludePinned: boolean = false, + keyword: string, excludePinned = false, ): Promise<Tab[]> { - let tabs = await browser.tabs.query({ currentWindow: true }); + const tabs = await browser.tabs.query({ currentWindow: true }); return tabs.filter((t) => { return t.url && t.url.toLowerCase().includes(keyword.toLowerCase()) || t.title && t.title.toLowerCase().includes(keyword.toLowerCase()); @@ -57,9 +57,9 @@ export default class TabPresenter { } async reopen(): Promise<any> { - let window = await browser.windows.getCurrent(); - let sessions = await browser.sessions.getRecentlyClosed(); - let session = sessions.find((s) => { + const window = await browser.windows.getCurrent(); + const sessions = await browser.sessions.getRecentlyClosed(); + const session = sessions.find((s) => { return s.tab && s.tab.windowId === window.id; }); if (!session) { @@ -100,11 +100,11 @@ export default class TabPresenter { } } -let tabPresenter = new TabPresenter(); +const tabPresenter = new TabPresenter(); tabPresenter.onSelected((tab: any) => { - let cache = new MemoryStorage(); + const cache = new MemoryStorage(); - let lastId = cache.get(CURRENT_SELECTED_KEY); + const lastId = cache.get(CURRENT_SELECTED_KEY); if (lastId) { cache.set(LAST_SELECTED_KEY, lastId); } diff --git a/src/background/repositories/BookmarkRepository.ts b/src/background/repositories/BookmarkRepository.ts index d266ae6..0d2a1fc 100644 --- a/src/background/repositories/BookmarkRepository.ts +++ b/src/background/repositories/BookmarkRepository.ts @@ -5,7 +5,7 @@ export default class BookmarkRepository { async create( title: string, url: string ): Promise<browser.bookmarks.BookmarkTreeNode> { - let item = await browser.bookmarks.create({ + const item = await browser.bookmarks.create({ type: 'bookmark', title, url, diff --git a/src/background/repositories/BrowserSettingRepository.ts b/src/background/repositories/BrowserSettingRepository.ts index 9cfb35e..a47b64d 100644 --- a/src/background/repositories/BrowserSettingRepository.ts +++ b/src/background/repositories/BrowserSettingRepository.ts @@ -20,7 +20,7 @@ declare namespace browser.browserSettings.homepageOverride { @injectable() export default class BrowserSettingRepository { async getHomepageUrls(): Promise<string[]> { - let { value } = await browser.browserSettings.homepageOverride.get({}); + const { value } = await browser.browserSettings.homepageOverride.get({}); return value.split('|').map(urls.normalizeUrl); } } diff --git a/src/background/repositories/CompletionsRepository.ts b/src/background/repositories/CompletionsRepository.ts index dfdbc27..dfecff0 100644 --- a/src/background/repositories/CompletionsRepository.ts +++ b/src/background/repositories/CompletionsRepository.ts @@ -6,7 +6,7 @@ type BookmarkTreeNode = browser.bookmarks.BookmarkTreeNode; @injectable() export default class CompletionsRepository { async queryBookmarks(keywords: string): Promise<BookmarkTreeNode[]> { - let items = await browser.bookmarks.search({ query: keywords }); + const items = await browser.bookmarks.search({ query: keywords }); return items.filter((item) => { if (!item.url) { return false; @@ -29,7 +29,7 @@ export default class CompletionsRepository { } async queryTabs(keywords: string, excludePinned: boolean): Promise<Tab[]> { - let tabs = await browser.tabs.query({ currentWindow: true }); + const tabs = await browser.tabs.query({ currentWindow: true }); return tabs.filter((t) => { return t.url && t.url.toLowerCase().includes(keywords.toLowerCase()) || t.title && t.title.toLowerCase().includes(keywords.toLowerCase()); diff --git a/src/background/repositories/MarkRepository.ts b/src/background/repositories/MarkRepository.ts index c106fff..1f4ab0c 100644 --- a/src/background/repositories/MarkRepository.ts +++ b/src/background/repositories/MarkRepository.ts @@ -13,17 +13,17 @@ export default class MarkRepository { } getMark(key: string): Promise<GlobalMark | undefined> { - let marks = this.getOrEmptyMarks(); - let data = marks[key]; + const marks = this.getOrEmptyMarks(); + const data = marks[key]; if (!data) { return Promise.resolve(undefined); } - let mark = { tabId: data.tabId, url: data.url, x: data.x, y: data.y }; + const mark = { tabId: data.tabId, url: data.url, x: data.x, y: data.y }; return Promise.resolve(mark); } setMark(key: string, mark: GlobalMark): Promise<any> { - let marks = this.getOrEmptyMarks(); + const marks = this.getOrEmptyMarks(); marks[key] = { tabId: mark.tabId, url: mark.url, x: mark.x, y: mark.y }; this.cache.set(MARK_KEY, marks); diff --git a/src/background/repositories/PersistentSettingRepository.ts b/src/background/repositories/PersistentSettingRepository.ts index e3b78b3..c10f2cf 100644 --- a/src/background/repositories/PersistentSettingRepository.ts +++ b/src/background/repositories/PersistentSettingRepository.ts @@ -4,7 +4,7 @@ import SettingData from '../../shared/SettingData'; @injectable() export default class SettingRepository { async load(): Promise<SettingData | null> { - let { settings } = await browser.storage.local.get('settings'); + const { settings } = await browser.storage.local.get('settings'); if (!settings) { return null; } diff --git a/src/background/repositories/SettingRepository.ts b/src/background/repositories/SettingRepository.ts index e775a32..ba24e36 100644 --- a/src/background/repositories/SettingRepository.ts +++ b/src/background/repositories/SettingRepository.ts @@ -14,7 +14,7 @@ export default class SettingRepository { } get(): Promise<Settings> { - let data = this.cache.get(CACHED_SETTING_KEY); + const data = this.cache.get(CACHED_SETTING_KEY); return Promise.resolve(Settings.fromJSON(data)); } @@ -25,7 +25,7 @@ export default class SettingRepository { async setProperty( name: string, value: string | number | boolean, ): Promise<void> { - let def = Properties.def(name); + const def = Properties.def(name); if (!def) { throw new Error('unknown property: ' + name); } @@ -37,7 +37,7 @@ export default class SettingRepository { newValue = def.defaultValue; } - let current = await this.get(); + const current = await this.get(); switch (name) { case 'hintchars': current.properties.hintchars = newValue as string; diff --git a/src/background/usecases/AddonEnabledUseCase.ts b/src/background/usecases/AddonEnabledUseCase.ts index bb5cd90..9abd3dc 100644 --- a/src/background/usecases/AddonEnabledUseCase.ts +++ b/src/background/usecases/AddonEnabledUseCase.ts @@ -27,7 +27,7 @@ export default class AddonEnabledUseCase { } async onTabSelected(tabId: number): Promise<void> { - let enabled = await this.contentMessageClient.getAddonEnabled(tabId); + const enabled = await this.contentMessageClient.getAddonEnabled(tabId); return this.indicatorPresentor.indicate(enabled); } } diff --git a/src/background/usecases/CommandUseCase.ts b/src/background/usecases/CommandUseCase.ts index d757215..fcb898d 100644 --- a/src/background/usecases/CommandUseCase.ts +++ b/src/background/usecases/CommandUseCase.ts @@ -26,7 +26,7 @@ export default class CommandIndicator { } async open(keywords: string): Promise<browser.tabs.Tab> { - let url = await this.urlOrSearch(keywords); + const url = await this.urlOrSearch(keywords); this.repeatUseCase.storeLastOperation({ type: operations.INTERNAL_OPEN_URL, url, @@ -35,7 +35,7 @@ export default class CommandIndicator { } async tabopen(keywords: string): Promise<browser.tabs.Tab> { - let url = await this.urlOrSearch(keywords); + const url = await this.urlOrSearch(keywords); this.repeatUseCase.storeLastOperation({ type: operations.INTERNAL_OPEN_URL, url, @@ -45,7 +45,7 @@ export default class CommandIndicator { } async winopen(keywords: string): Promise<browser.windows.Window> { - let url = await this.urlOrSearch(keywords); + const url = await this.urlOrSearch(keywords); this.repeatUseCase.storeLastOperation({ type: operations.INTERNAL_OPEN_URL, url, @@ -61,8 +61,8 @@ export default class CommandIndicator { } if (!isNaN(Number(keywords))) { - let tabs = await this.tabPresenter.getAll(); - let index = parseInt(keywords, 10) - 1; + const tabs = await this.tabPresenter.getAll(); + const index = parseInt(keywords, 10) - 1; if (index < 0 || tabs.length <= index) { throw new RangeError(`tab ${index + 1} does not exist`); } @@ -72,19 +72,19 @@ export default class CommandIndicator { return; } else if (keywords.trim() === '#') { // Select last selected window - let lastId = await this.tabPresenter.getLastSelectedId(); + const lastId = await this.tabPresenter.getLastSelectedId(); if (typeof lastId === 'undefined' || lastId === null) { throw new Error('No last selected tab'); } return this.tabPresenter.select(lastId); } - let current = await this.tabPresenter.getCurrent(); - let tabs = await this.tabPresenter.getByKeyword(keywords); + const current = await this.tabPresenter.getCurrent(); + const tabs = await this.tabPresenter.getByKeyword(keywords); if (tabs.length === 0) { throw new RangeError('No matching buffer for ' + keywords); } - for (let tab of tabs) { + for (const tab of tabs) { if (tab.index > current.index) { return this.tabPresenter.select(tab.id as number); } @@ -93,8 +93,8 @@ export default class CommandIndicator { } async bdelete(force: boolean, keywords: string): Promise<any> { - let excludePinned = !force; - let tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned); + const excludePinned = !force; + const tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned); if (tabs.length === 0) { throw new Error('No matching buffer for ' + keywords); } else if (tabs.length > 1) { @@ -104,27 +104,27 @@ export default class CommandIndicator { } async bdeletes(force: boolean, keywords: string): Promise<any> { - let excludePinned = !force; - let tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned); - let ids = tabs.map(tab => tab.id as number); + const excludePinned = !force; + const tabs = await this.tabPresenter.getByKeyword(keywords, excludePinned); + const ids = tabs.map(tab => tab.id as number); return this.tabPresenter.remove(ids); } async quit(): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); return this.tabPresenter.remove([tab.id as number]); } async quitAll(): Promise<any> { - let tabs = await this.tabPresenter.getAll(); - let ids = tabs.map(tab => tab.id as number); + const tabs = await this.tabPresenter.getAll(); + const ids = tabs.map(tab => tab.id as number); this.tabPresenter.remove(ids); } async addbookmark(title: string): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); - let item = await this.bookmarkRepository.create(title, tab.url as string); - let message = 'Saved current page: ' + item.url; + const tab = await this.tabPresenter.getCurrent(); + const item = await this.bookmarkRepository.create(title, tab.url as string); + const message = 'Saved current page: ' + item.url; return this.consoleClient.showInfo(tab.id as number, message); } @@ -132,7 +132,7 @@ export default class CommandIndicator { if (keywords.length === 0) { return; } - let [name, value] = parsers.parseSetOption(keywords); + const [name, value] = parsers.parseSetOption(keywords); await this.settingRepository.setProperty(name, value); return this.contentMessageClient.broadcastSettingsChanged(); @@ -143,7 +143,7 @@ export default class CommandIndicator { } private async urlOrSearch(keywords: string): Promise<any> { - let settings = await this.settingRepository.get(); + const settings = await this.settingRepository.get(); return urls.searchUrl(keywords, settings.search); } } diff --git a/src/background/usecases/CompletionsUseCase.ts b/src/background/usecases/CompletionsUseCase.ts index bfff1e6..779c61d 100644 --- a/src/background/usecases/CompletionsUseCase.ts +++ b/src/background/usecases/CompletionsUseCase.ts @@ -22,8 +22,8 @@ export default class CompletionsUseCase { } queryConsoleCommand(prefix: string): Promise<CompletionGroup[]> { - let keys = Object.keys(CommandDocs); - let items = keys + const keys = Object.keys(CommandDocs); + const items = keys .filter(name => name.startsWith(prefix)) .map(name => ({ caption: name, @@ -41,28 +41,28 @@ export default class CompletionsUseCase { // TODO This logic contains view entities. They should be defined on // content script - let settings = await this.settingRepository.get(); - let groups: CompletionGroup[] = []; + const settings = await this.settingRepository.get(); + const groups: CompletionGroup[] = []; - let complete = settings.properties.complete; - for (let c of complete) { + const complete = settings.properties.complete; + for (const c of complete) { if (c === 's') { // eslint-disable-next-line no-await-in-loop - let engines = await this.querySearchEngineItems(name, keywords); + const engines = await this.querySearchEngineItems(name, keywords); if (engines.length > 0) { groups.push({ name: 'Search Engines', items: engines }); } // browser.history not supported on Android } else if (c === 'h' && typeof browser.history === 'object') { // eslint-disable-next-line no-await-in-loop - let histories = await this.queryHistoryItems(name, keywords); + const histories = await this.queryHistoryItems(name, keywords); if (histories.length > 0) { groups.push({ name: 'History', items: histories }); } // browser.bookmarks not supported on Android } else if (c === 'b' && typeof browser.bookmarks === 'object') { // eslint-disable-next-line no-await-in-loop - let bookmarks = await this.queryBookmarkItems(name, keywords); + const bookmarks = await this.queryBookmarkItems(name, keywords); if (bookmarks.length > 0) { groups.push({ name: 'Bookmarks', items: bookmarks }); } @@ -76,23 +76,23 @@ export default class CompletionsUseCase { name: string, keywords: string, ): Promise<CompletionGroup[]> { - let lastId = await this.tabPresenter.getLastSelectedId(); - let trimmed = keywords.trim(); + const lastId = await this.tabPresenter.getLastSelectedId(); + const trimmed = keywords.trim(); let tabs: Tab[] = []; if (trimmed.length > 0 && !isNaN(Number(trimmed))) { - let all = await this.tabPresenter.getAll(); - let index = parseInt(trimmed, 10) - 1; + const all = await this.tabPresenter.getAll(); + const index = parseInt(trimmed, 10) - 1; if (index >= 0 && index < all.length) { tabs = [all[index]]; } } else if (trimmed === '%') { - let all = await this.tabPresenter.getAll(); - let tab = all.find(t => t.active) as Tab; + const all = await this.tabPresenter.getAll(); + const tab = all.find(t => t.active) as Tab; tabs = [tab]; } else if (trimmed === '#') { if (typeof lastId !== 'undefined' && lastId !== null) { - let all = await this.tabPresenter.getAll(); - let tab = all.find(t => t.id === lastId) as Tab; + const all = await this.tabPresenter.getAll(); + const tab = all.find(t => t.id === lastId) as Tab; tabs = [tab]; } } else { @@ -106,7 +106,7 @@ export default class CompletionsUseCase { } return ' '; }; - let items = tabs.map(tab => ({ + const items = tabs.map(tab => ({ caption: tab.index + 1 + ': ' + flag(tab) + ' ' + tab.title, content: name + ' ' + tab.title, url: tab.url, @@ -129,7 +129,7 @@ export default class CompletionsUseCase { } querySet(name: string, keywords: string): Promise<CompletionGroup[]> { - let items = Properties.defs().map((def) => { + const items = Properties.defs().map((def) => { if (def.type === 'boolean') { return [ { @@ -166,8 +166,8 @@ export default class CompletionsUseCase { async queryTabs( name: string, excludePinned: boolean, args: string, ): Promise<CompletionGroup[]> { - let tabs = await this.completionsRepository.queryTabs(args, excludePinned); - let items = tabs.map(tab => ({ + const tabs = await this.completionsRepository.queryTabs(args, excludePinned); + const items = tabs.map(tab => ({ caption: tab.title, content: name + ' ' + tab.title, url: tab.url, @@ -180,8 +180,8 @@ export default class CompletionsUseCase { } async querySearchEngineItems(name: string, keywords: string) { - let settings = await this.settingRepository.get(); - let engines = Object.keys(settings.search.engines) + const settings = await this.settingRepository.get(); + const engines = Object.keys(settings.search.engines) .filter(key => key.startsWith(keywords)); return engines.map(key => ({ caption: key, @@ -209,7 +209,7 @@ export default class CompletionsUseCase { } async queryBookmarkItems(name: string, keywords: string) { - let bookmarks = await this.completionsRepository.queryBookmarks(keywords); + const bookmarks = await this.completionsRepository.queryBookmarks(keywords); return bookmarks.slice(0, COMPLETION_ITEM_LIMIT) .map(page => ({ caption: page.title, diff --git a/src/background/usecases/ConsoleUseCase.ts b/src/background/usecases/ConsoleUseCase.ts index d0bd7bb..775a1e0 100644 --- a/src/background/usecases/ConsoleUseCase.ts +++ b/src/background/usecases/ConsoleUseCase.ts @@ -12,12 +12,12 @@ export default class ConsoleUseCase { } async showCommand(): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); return this.consoleClient.showCommand(tab.id as number, ''); } async showOpenCommand(alter: boolean): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); let command = 'open '; if (alter) { command += tab.url || ''; @@ -26,7 +26,7 @@ export default class ConsoleUseCase { } async showTabopenCommand(alter: boolean): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); let command = 'tabopen '; if (alter) { command += tab.url || ''; @@ -35,7 +35,7 @@ export default class ConsoleUseCase { } async showWinopenCommand(alter: boolean): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); let command = 'winopen '; if (alter) { command += tab.url || ''; @@ -44,13 +44,13 @@ export default class ConsoleUseCase { } async showBufferCommand(): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); - let command = 'buffer '; + const tab = await this.tabPresenter.getCurrent(); + const command = 'buffer '; return this.consoleClient.showCommand(tab.id as number, command); } async showAddbookmarkCommand(alter: boolean): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); let command = 'addbookmark '; if (alter) { command += tab.title || ''; @@ -59,7 +59,7 @@ export default class ConsoleUseCase { } async hideConsole(): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); return this.consoleClient.hide(tab.id as number); } } diff --git a/src/background/usecases/FindUseCase.ts b/src/background/usecases/FindUseCase.ts index 41b9cbd..b8593c6 100644 --- a/src/background/usecases/FindUseCase.ts +++ b/src/background/usecases/FindUseCase.ts @@ -21,7 +21,7 @@ export default class FindUseCase { } async findStart(): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); return this.consoleClient.showFind(tab.id as number); } } diff --git a/src/background/usecases/LinkUseCase.ts b/src/background/usecases/LinkUseCase.ts index e87867d..9c0eab5 100644 --- a/src/background/usecases/LinkUseCase.ts +++ b/src/background/usecases/LinkUseCase.ts @@ -15,9 +15,9 @@ export default class LinkUseCase { async openNewTab( url: string, openerId: number, background: boolean, ): Promise<any> { - let properties: any = { active: !background }; + const properties: any = { active: !background }; - let platform = await browser.runtime.getPlatformInfo(); + const platform = await browser.runtime.getPlatformInfo(); if (platform.os !== 'android') { // openerTabId not supported on Android properties.openerTabId = openerId; diff --git a/src/background/usecases/MarkUseCase.ts b/src/background/usecases/MarkUseCase.ts index 8cb96da..eeac40f 100644 --- a/src/background/usecases/MarkUseCase.ts +++ b/src/background/usecases/MarkUseCase.ts @@ -15,15 +15,15 @@ export default class MarkUseCase { } async setGlobal(key: string, x: number, y: number): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); - let mark = { tabId: tab.id as number, url: tab.url as string, x, y }; + const tab = await this.tabPresenter.getCurrent(); + const mark = { tabId: tab.id as number, url: tab.url as string, x, y }; return this.markRepository.setMark(key, mark); } async jumpGlobal(key: string): Promise<any> { - let current = await this.tabPresenter.getCurrent(); + const current = await this.tabPresenter.getCurrent(); - let mark = await this.markRepository.getMark(key); + const mark = await this.markRepository.getMark(key); if (!mark) { return this.consoleClient.showError( current.id as number, 'Mark is not set'); @@ -32,7 +32,7 @@ export default class MarkUseCase { await this.contentMessageClient.scrollTo(mark.tabId, mark.x, mark.y); return this.tabPresenter.select(mark.tabId); } catch (e) { - let tab = await this.tabPresenter.create(mark.url); + const tab = await this.tabPresenter.create(mark.url); return this.markRepository.setMark(key, { tabId: tab.id as number, url: mark.url, x: mark.x, y: mark.y, }); diff --git a/src/background/usecases/NavigateUseCase.ts b/src/background/usecases/NavigateUseCase.ts index 152339a..25e7f20 100644 --- a/src/background/usecases/NavigateUseCase.ts +++ b/src/background/usecases/NavigateUseCase.ts @@ -11,28 +11,28 @@ export default class NavigateUseCase { } async openHistoryNext(): Promise<void> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); await this.navigateClient.historyNext(tab.id!!); } async openHistoryPrev(): Promise<void> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); await this.navigateClient.historyPrev(tab.id!!); } async openLinkNext(): Promise<void> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); await this.navigateClient.linkNext(tab.id!!); } async openLinkPrev(): Promise<void> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); await this.navigateClient.linkPrev(tab.id!!); } async openParent(): Promise<void> { - let tab = await this.tabPresenter.getCurrent(); - let url = new URL(tab.url!!); + const tab = await this.tabPresenter.getCurrent(); + const url = new URL(tab.url!!); if (url.hash.length > 0) { url.hash = ''; } else if (url.search.length > 0) { @@ -50,8 +50,8 @@ export default class NavigateUseCase { } async openRoot(): Promise<void> { - let tab = await this.tabPresenter.getCurrent(); - let url = new URL(tab.url!!); + const tab = await this.tabPresenter.getCurrent(); + const url = new URL(tab.url!!); await this.tabPresenter.open(url.origin); } } diff --git a/src/background/usecases/TabSelectUseCase.ts b/src/background/usecases/TabSelectUseCase.ts index df3db94..62098de 100644 --- a/src/background/usecases/TabSelectUseCase.ts +++ b/src/background/usecases/TabSelectUseCase.ts @@ -9,43 +9,43 @@ export default class TabSelectUseCase { } async selectPrev(count: number): Promise<any> { - let tabs = await this.tabPresenter.getAll(); + const tabs = await this.tabPresenter.getAll(); if (tabs.length < 2) { return; } - let tab = tabs.find(t => t.active); + const tab = tabs.find(t => t.active); if (!tab) { return; } - let select = (tab.index - count + tabs.length) % tabs.length; + const select = (tab.index - count + tabs.length) % tabs.length; return this.tabPresenter.select(tabs[select].id as number); } async selectNext(count: number): Promise<any> { - let tabs = await this.tabPresenter.getAll(); + const tabs = await this.tabPresenter.getAll(); if (tabs.length < 2) { return; } - let tab = tabs.find(t => t.active); + const tab = tabs.find(t => t.active); if (!tab) { return; } - let select = (tab.index + count) % tabs.length; + const select = (tab.index + count) % tabs.length; return this.tabPresenter.select(tabs[select].id as number); } async selectFirst(): Promise<any> { - let tabs = await this.tabPresenter.getAll(); + const tabs = await this.tabPresenter.getAll(); return this.tabPresenter.select(tabs[0].id as number); } async selectLast(): Promise<any> { - let tabs = await this.tabPresenter.getAll(); + const tabs = await this.tabPresenter.getAll(); return this.tabPresenter.select(tabs[tabs.length - 1].id as number); } async selectPrevSelected(): Promise<any> { - let tabId = await this.tabPresenter.getLastSelectedId(); + const tabId = await this.tabPresenter.getLastSelectedId(); if (tabId === null || typeof tabId === 'undefined') { return Promise.resolve(); } diff --git a/src/background/usecases/TabUseCase.ts b/src/background/usecases/TabUseCase.ts index 386307e..66f8573 100644 --- a/src/background/usecases/TabUseCase.ts +++ b/src/background/usecases/TabUseCase.ts @@ -13,26 +13,26 @@ export default class TabUseCase { } async close(force: boolean, selectLeft = false): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); if (!force && tab.pinned) { return Promise.resolve(); } if (selectLeft && tab.index > 0) { - let tabs = await this.tabPresenter.getAll(); + const tabs = await this.tabPresenter.getAll(); await this.tabPresenter.select(tabs[tab.index - 1].id as number); } return this.tabPresenter.remove([tab.id as number]); } async closeRight(): Promise<any> { - let tabs = await this.tabPresenter.getAll(); + const tabs = await this.tabPresenter.getAll(); tabs.sort((t1, t2) => t1.index - t2.index); - let index = tabs.findIndex(t => t.active); + const index = tabs.findIndex(t => t.active); if (index < 0) { return; } for (let i = index + 1; i < tabs.length; ++i) { - let tab = tabs[i]; + const tab = tabs[i]; if (!tab.pinned) { this.tabPresenter.remove([tab.id as number]); } @@ -44,34 +44,34 @@ export default class TabUseCase { } async reload(cache: boolean): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); return this.tabPresenter.reload(tab.id as number, cache); } async setPinned(pinned: boolean): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); return this.tabPresenter.setPinned(tab.id as number, pinned); } async togglePinned(): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); return this.tabPresenter.setPinned(tab.id as number, !tab.pinned); } async duplicate(): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); return this.tabPresenter.duplicate(tab.id as number); } async openPageSource(): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); - let url = 'view-source:' + tab.url; + const tab = await this.tabPresenter.getCurrent(); + const url = 'view-source:' + tab.url; return this.tabPresenter.create(url); } async openHome(newTab: boolean): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); - let urls = await this.browserSettingRepository.getHomepageUrls(); + const tab = await this.tabPresenter.getCurrent(); + const urls = await this.browserSettingRepository.getHomepageUrls(); if (urls.length === 1 && urls[0] === 'about:home') { // eslint-disable-next-line max-len throw new Error('Cannot open Firefox Home (about:home) by WebExtensions, set your custom URLs'); @@ -79,7 +79,7 @@ export default class TabUseCase { if (urls.length === 1 && !newTab) { return this.tabPresenter.open(urls[0], tab.id); } - for (let url of urls) { + for (const url of urls) { this.tabPresenter.create(url); } } @@ -92,7 +92,7 @@ export default class TabUseCase { } else if (newTab) { await this.tabPresenter.create(url); } else { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); await this.tabPresenter.open(url, tab.id); } } diff --git a/src/background/usecases/VersionUseCase.ts b/src/background/usecases/VersionUseCase.ts index 0ff0e9b..645c859 100644 --- a/src/background/usecases/VersionUseCase.ts +++ b/src/background/usecases/VersionUseCase.ts @@ -11,8 +11,8 @@ export default class VersionUseCase { } notify(): Promise<void> { - let manifest = browser.runtime.getManifest(); - let url = this.releaseNoteUrl(manifest.version); + const manifest = browser.runtime.getManifest(); + const url = this.releaseNoteUrl(manifest.version); return this.notifyPresenter.notifyUpdated(manifest.version, () => { this.tabPresenter.create(url); }); diff --git a/src/background/usecases/ZoomUseCase.ts b/src/background/usecases/ZoomUseCase.ts index 32ba897..f598871 100644 --- a/src/background/usecases/ZoomUseCase.ts +++ b/src/background/usecases/ZoomUseCase.ts @@ -14,27 +14,27 @@ export default class ZoomUseCase { } async zoomIn(): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); - let tabId = tab.id as number; - let current = await this.tabPresenter.getZoom(tabId); - let factor = ZOOM_SETTINGS.find(f => f > current); + const tab = await this.tabPresenter.getCurrent(); + const tabId = tab.id as number; + const current = await this.tabPresenter.getZoom(tabId); + const factor = ZOOM_SETTINGS.find(f => f > current); if (factor) { return this.tabPresenter.setZoom(tabId as number, factor); } } async zoomOut(): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); - let tabId = tab.id as number; - let current = await this.tabPresenter.getZoom(tabId); - let factor = ZOOM_SETTINGS.slice(0).reverse().find(f => f < current); + const tab = await this.tabPresenter.getCurrent(); + const tabId = tab.id as number; + const current = await this.tabPresenter.getZoom(tabId); + const factor = ZOOM_SETTINGS.slice(0).reverse().find(f => f < current); if (factor) { return this.tabPresenter.setZoom(tabId as number, factor); } } async zoomNutoral(): Promise<any> { - let tab = await this.tabPresenter.getCurrent(); + const tab = await this.tabPresenter.getCurrent(); return this.tabPresenter.setZoom(tab.id as number, 1); } } diff --git a/src/background/usecases/filters.ts b/src/background/usecases/filters.ts index 84a42fb..98957a7 100644 --- a/src/background/usecases/filters.ts +++ b/src/background/usecases/filters.ts @@ -1,13 +1,13 @@ type Item = browser.history.HistoryItem; const filterHttp = (items: Item[]): Item[] => { - let httpsHosts = items.map(x => new URL(x.url as string)) + const httpsHosts = items.map(x => new URL(x.url as string)) .filter(x => x.protocol === 'https:') .map(x => x.host); - let hostsSet = new Set(httpsHosts); + const hostsSet = new Set(httpsHosts); return items.filter((item: Item) => { - let url = new URL(item.url as string); + const url = new URL(item.url as string); return url.protocol === 'https:' || !hostsSet.has(url.host); }); }; @@ -17,14 +17,14 @@ const filterBlankTitle = (items: Item[]): Item[] => { }; const filterByTailingSlash = (items: Item[]): Item[] => { - let urls = items.map(item => new URL(item.url as string)); - let simplePaths = urls + const urls = items.map(item => new URL(item.url as string)); + const simplePaths = urls .filter(url => url.hash === '' && url.search === '') .map(url => url.origin + url.pathname); - let pathsSet = new Set(simplePaths); + const pathsSet = new Set(simplePaths); return items.filter((item) => { - let url = new URL(item.url as string); + const url = new URL(item.url as string); if (url.hash !== '' || url.search !== '' || url.pathname.slice(-1) !== '/') { return true; @@ -34,10 +34,10 @@ const filterByTailingSlash = (items: Item[]): Item[] => { }; const filterByPathname = (items: Item[], min: number): Item[] => { - let hash: {[key: string]: Item} = {}; - for (let item of items) { - let url = new URL(item.url as string); - let pathname = url.origin + url.pathname; + const hash: {[key: string]: Item} = {}; + for (const item of items) { + const url = new URL(item.url as string); + const pathname = url.origin + url.pathname; if (!hash[pathname]) { hash[pathname] = item; } else if ((hash[pathname].url as string).length > @@ -45,7 +45,7 @@ const filterByPathname = (items: Item[], min: number): Item[] => { hash[pathname] = item; } } - let filtered = Object.values(hash); + const filtered = Object.values(hash); if (filtered.length < min) { return items; } @@ -53,9 +53,9 @@ const filterByPathname = (items: Item[], min: number): Item[] => { }; const filterByOrigin = (items: Item[], min: number): Item[] => { - let hash: {[key: string]: Item} = {}; - for (let item of items) { - let origin = new URL(item.url as string).origin; + const hash: {[key: string]: Item} = {}; + for (const item of items) { + const origin = new URL(item.url as string).origin; if (!hash[origin]) { hash[origin] = item; } else if ((hash[origin].url as string).length > @@ -63,7 +63,7 @@ const filterByOrigin = (items: Item[], min: number): Item[] => { hash[origin] = item; } } - let filtered = Object.values(hash); + const filtered = Object.values(hash); if (filtered.length < min) { return items; } diff --git a/src/background/usecases/parsers.ts b/src/background/usecases/parsers.ts index e8a1149..544dce4 100644 --- a/src/background/usecases/parsers.ts +++ b/src/background/usecases/parsers.ts @@ -1,7 +1,7 @@ import Properties from '../../shared/settings/Properties'; const mustNumber = (v: any): number => { - let num = Number(v); + const num = Number(v); if (isNaN(num)) { throw new Error('Not number: ' + v); } @@ -16,7 +16,7 @@ const parseSetOption = ( value = !key.startsWith('no'); key = value ? key : key.slice(2); } - let def = Properties.def(key); + const def = Properties.def(key); if (!def) { throw new Error('Unknown property: ' + key); } diff --git a/src/console/actions/console.ts b/src/console/actions/console.ts index d03f52c..f7fa7a2 100644 --- a/src/console/actions/console.ts +++ b/src/console/actions/console.ts @@ -69,7 +69,7 @@ const setConsoleText = (consoleText: string): actions.ConsoleAction => { }; const getCompletions = async(text: string): Promise<actions.ConsoleAction> => { - let completions = await browser.runtime.sendMessage({ + const completions = await browser.runtime.sendMessage({ type: messages.CONSOLE_QUERY_COMPLETIONS, text, }); diff --git a/src/console/components/Console.tsx b/src/console/components/Console.tsx index 1743d15..eafe2a7 100644 --- a/src/console/components/Console.tsx +++ b/src/console/components/Console.tsx @@ -34,7 +34,7 @@ class Console extends React.Component<Props> { e.stopPropagation(); e.preventDefault(); - let value = (e.target as HTMLInputElement).value; + const value = (e.target as HTMLInputElement).value; if (this.props.mode === 'command') { return this.props.dispatch(consoleActions.enterCommand(value)); } else if (this.props.mode === 'find') { @@ -101,7 +101,7 @@ class Console extends React.Component<Props> { } onChange(e: React.ChangeEvent<HTMLInputElement>) { - let text = e.target.value; + const text = e.target.value; this.props.dispatch(consoleActions.setConsoleText(text)); if (this.props.mode === 'command') { this.props.dispatch(consoleActions.getCompletions(text)); diff --git a/src/console/components/console/Completion.tsx b/src/console/components/console/Completion.tsx index 169a39c..e2fa1de 100644 --- a/src/console/components/console/Completion.tsx +++ b/src/console/components/console/Completion.tsx @@ -35,11 +35,11 @@ class Completion extends React.Component<Props, State> { return null; } - let viewSelect = (() => { + const viewSelect = (() => { let index = 0; for (let i = 0; i < nextProps.completions.length; ++i) { ++index; - let g = nextProps.completions[i]; + const g = nextProps.completions[i]; if (nextProps.select + i + 1 < index + g.items.length) { return nextProps.select + i + 1; } @@ -64,12 +64,12 @@ class Completion extends React.Component<Props, State> { let eles = []; let index = 0; - for (let group of this.props.completions) { + for (const group of this.props.completions) { eles.push(<CompletionTitle key={`group-${index}`} title={ group.name } />); - for (let item of group.items) { + for (const item of group.items) { eles.push(<CompletionItem key={`item-${index}`} icon={item.icon} @@ -81,7 +81,7 @@ class Completion extends React.Component<Props, State> { } } - let viewOffset = this.state.viewOffset; + const viewOffset = this.state.viewOffset; eles = eles.slice(viewOffset, viewOffset + this.props.size); return ( diff --git a/src/console/index.tsx b/src/console/index.tsx index b655154..1209ec2 100644 --- a/src/console/index.tsx +++ b/src/console/index.tsx @@ -14,7 +14,7 @@ const store = createStore( ); window.addEventListener('load', () => { - let wrapper = document.getElementById('vimvixen-console'); + const wrapper = document.getElementById('vimvixen-console'); ReactDOM.render( <Provider store={store} > <Console></Console> @@ -23,7 +23,7 @@ window.addEventListener('load', () => { }); const onMessage = (message: any): any => { - let msg = messages.valueOf(message); + const msg = messages.valueOf(message); switch (msg.type) { case messages.CONSOLE_SHOW_COMMAND: return store.dispatch(consoleActions.showCommand(msg.command)); @@ -39,5 +39,5 @@ const onMessage = (message: any): any => { }; browser.runtime.onMessage.addListener(onMessage); -let port = browser.runtime.connect(undefined, { name: 'vimvixen-console' }); +const port = browser.runtime.connect(undefined, { name: 'vimvixen-console' }); port.onMessage.addListener(onMessage); diff --git a/src/console/reducers/index.ts b/src/console/reducers/index.ts index b6be483..048a24f 100644 --- a/src/console/reducers/index.ts +++ b/src/console/reducers/index.ts @@ -28,7 +28,7 @@ const nextSelection = (state: State): number => { return 0; } - let length = state.completions + const length = state.completions .map(g => g.items.length) .reduce((x, y) => x + y); if (state.select + 1 < length) { @@ -38,7 +38,7 @@ const nextSelection = (state: State): number => { }; const prevSelection = (state: State): number => { - let length = state.completions + const length = state.completions .map(g => g.items.length) .reduce((x, y) => x + y); if (state.select < 0) { @@ -51,7 +51,7 @@ const nextConsoleText = (completions: any[], select: number, defaults: any) => { if (select < 0) { return defaults; } - let items = completions.map(g => g.items).reduce((g1, g2) => g1.concat(g2)); + const items = completions.map(g => g.items).reduce((g1, g2) => g1.concat(g2)); return items[select].content; }; @@ -96,14 +96,14 @@ export default function reducer( completionSource: action.completionSource, select: -1 }; case actions.CONSOLE_COMPLETION_NEXT: { - let select = nextSelection(state); + const select = nextSelection(state); return { ...state, select: select, consoleText: nextConsoleText( state.completions, select, state.completionSource) }; } case actions.CONSOLE_COMPLETION_PREV: { - let select = prevSelection(state); + const select = prevSelection(state); return { ...state, select: select, consoleText: nextConsoleText( diff --git a/src/content/Application.ts b/src/content/Application.ts index 1677655..cfa01fd 100644 --- a/src/content/Application.ts +++ b/src/content/Application.ts @@ -111,7 +111,7 @@ export default class Application { } }); - let inputDriver = new InputDriver(window.document.body); + const inputDriver = new InputDriver(window.document.body); inputDriver.onKey(key => this.followKeyController.press(key)); inputDriver.onKey(key => this.markKeyController.press(key)); inputDriver.onKey(key => this.keymapController.press(key)); diff --git a/src/content/InputDriver.ts b/src/content/InputDriver.ts index bc184d2..cf28205 100644 --- a/src/content/InputDriver.ts +++ b/src/content/InputDriver.ts @@ -25,7 +25,7 @@ const modifiedKeyName = (name: string): string => { // visible for testing export const keyFromKeyboardEvent = (e: KeyboardEvent): Key => { - let key = modifiedKeyName(e.key); + const key = modifiedKeyName(e.key); let shift = e.shiftKey; if (key.length === 1 && key.toUpperCase() === key.toLowerCase()) { // make shift false for symbols to enable key bindings by symbold keys. @@ -83,7 +83,7 @@ export default class InputDriver { // eslint-disable-next-line max-statements private capture(e: KeyboardEvent) { - let target = e.target; + const target = e.target; if (!(target instanceof HTMLElement)) { return; } @@ -98,9 +98,9 @@ export default class InputDriver { return; } - let key = keyFromKeyboardEvent(e); - for (let listener of this.onKeyListeners) { - let stop = listener(key); + const key = keyFromKeyboardEvent(e); + for (const listener of this.onKeyListeners) { + const stop = listener(key); if (stop) { e.preventDefault(); e.stopPropagation(); diff --git a/src/content/MessageListener.ts b/src/content/MessageListener.ts index d035825..e1f7c75 100644 --- a/src/content/MessageListener.ts +++ b/src/content/MessageListener.ts @@ -9,7 +9,7 @@ export default class MessageListener { listener: (msg: Message, sender: Window) => void, ) { window.addEventListener('message', (event: MessageEvent) => { - let sender = event.source; + const sender = event.source; if (!(sender instanceof Window)) { return; } diff --git a/src/content/client/FindClient.ts b/src/content/client/FindClient.ts index af0b427..8b2aca4 100644 --- a/src/content/client/FindClient.ts +++ b/src/content/client/FindClient.ts @@ -8,7 +8,7 @@ export default interface FindClient { export class FindClientImpl implements FindClient { async getGlobalLastKeyword(): Promise<string | null> { - let keyword = await browser.runtime.sendMessage({ + const keyword = await browser.runtime.sendMessage({ type: messages.FIND_GET_KEYWORD, }); return keyword as string; diff --git a/src/content/client/OperationClient.ts b/src/content/client/OperationClient.ts index 5dbe555..9c72c75 100644 --- a/src/content/client/OperationClient.ts +++ b/src/content/client/OperationClient.ts @@ -2,7 +2,7 @@ import * as operations from '../../shared/operations'; import * as messages from '../../shared/messages'; export default interface OperationClient { - execBackgroundOp(op: operations.Operation): Promise<void>; + execBackgroundOp(repeat: number, op: operations.Operation): Promise<void>; internalOpenUrl( url: string, newTab?: boolean, background?: boolean, @@ -10,9 +10,10 @@ export default interface OperationClient { } export class OperationClientImpl implements OperationClient { - execBackgroundOp(op: operations.Operation): Promise<void> { + execBackgroundOp(repeat: number, op: operations.Operation): Promise<void> { return browser.runtime.sendMessage({ type: messages.BACKGROUND_OPERATION, + repeat, operation: op, }); } @@ -22,6 +23,7 @@ export class OperationClientImpl implements OperationClient { ): Promise<void> { return browser.runtime.sendMessage({ type: messages.BACKGROUND_OPERATION, + repeat: 1, operation: { type: operations.INTERNAL_OPEN_URL, url, diff --git a/src/content/client/SettingClient.ts b/src/content/client/SettingClient.ts index fc62720..f89f3cd 100644 --- a/src/content/client/SettingClient.ts +++ b/src/content/client/SettingClient.ts @@ -7,7 +7,7 @@ export default interface SettingClient { export class SettingClientImpl { async load(): Promise<Settings> { - let settings = await browser.runtime.sendMessage({ + const settings = await browser.runtime.sendMessage({ type: messages.SETTINGS_QUERY, }); return Settings.fromJSON(settings); diff --git a/src/content/controllers/AddonEnabledController.ts b/src/content/controllers/AddonEnabledController.ts index bae95bf..59b45fa 100644 --- a/src/content/controllers/AddonEnabledController.ts +++ b/src/content/controllers/AddonEnabledController.ts @@ -12,7 +12,7 @@ export default class AddonEnabledController { getAddonEnabled( _message: messages.AddonEnabledQueryMessage, ): Promise<boolean> { - let enabled = this.addonEnabledUseCase.getEnabled(); + const enabled = this.addonEnabledUseCase.getEnabled(); return Promise.resolve(enabled); } } diff --git a/src/content/controllers/KeymapController.ts b/src/content/controllers/KeymapController.ts index 6157a71..f9c2545 100644 --- a/src/content/controllers/KeymapController.ts +++ b/src/content/controllers/KeymapController.ts @@ -23,7 +23,7 @@ export default class KeymapController { private markKeyUseCase: MarkKeyyUseCase, @inject('OperationClient') - private backgroundClient: OperationClient, + private operationClient: OperationClient, @inject('FollowMasterClient') private followMasterClient: FollowMasterClient, @@ -32,71 +32,70 @@ export default class KeymapController { // eslint-disable-next-line complexity, max-lines-per-function press(key: Key): boolean { - let op = this.keymapUseCase.nextOp(key); - if (op === null) { + const nextOp = this.keymapUseCase.nextOps(key); + if (nextOp === null) { return false; } - // do not await due to return a boolean immediately - switch (op.type) { - case operations.ADDON_ENABLE: - this.addonEnabledUseCase.enable(); - break; - case operations.ADDON_DISABLE: - this.addonEnabledUseCase.disable(); - break; - case operations.ADDON_TOGGLE_ENABLED: - this.addonEnabledUseCase.toggle(); - break; - case operations.FIND_NEXT: - this.findSlaveUseCase.findNext(); - break; - case operations.FIND_PREV: - this.findSlaveUseCase.findPrev(); - break; - case operations.SCROLL_VERTICALLY: - this.scrollUseCase.scrollVertically(op.count); - break; - case operations.SCROLL_HORIZONALLY: - this.scrollUseCase.scrollHorizonally(op.count); - break; - case operations.SCROLL_PAGES: - this.scrollUseCase.scrollPages(op.count); - break; - case operations.SCROLL_TOP: - this.scrollUseCase.scrollToTop(); - break; - case operations.SCROLL_BOTTOM: - this.scrollUseCase.scrollToBottom(); - break; - case operations.SCROLL_HOME: - this.scrollUseCase.scrollToHome(); - break; - case operations.SCROLL_END: - this.scrollUseCase.scrollToEnd(); - break; - case operations.FOLLOW_START: - this.followMasterClient.startFollow(op.newTab, op.background); - break; - case operations.MARK_SET_PREFIX: - this.markKeyUseCase.enableSetMode(); - break; - case operations.MARK_JUMP_PREFIX: - this.markKeyUseCase.enableJumpMode(); - break; - case operations.FOCUS_INPUT: - this.focusUseCase.focusFirstInput(); - break; - case operations.URLS_YANK: - this.clipbaordUseCase.yankCurrentURL(); - break; - case operations.URLS_PASTE: - this.clipbaordUseCase.openOrSearch( - op.newTab ? op.newTab : false, - ); - break; - default: - this.backgroundClient.execBackgroundOp(op); + if (!operations.isNRepeatable(nextOp.op.type)) { + nextOp.repeat = 1; + } + + const doFunc = ((op: operations.Operation) => { + switch (op.type) { + case operations.ADDON_ENABLE: + return () => this.addonEnabledUseCase.enable(); + case operations.ADDON_DISABLE: + return () => this.addonEnabledUseCase.disable(); + case operations.ADDON_TOGGLE_ENABLED: + return () => this.addonEnabledUseCase.toggle(); + case operations.FIND_NEXT: + return () => this.findSlaveUseCase.findNext(); + case operations.FIND_PREV: + return () => this.findSlaveUseCase.findPrev(); + case operations.SCROLL_VERTICALLY: + return () => this.scrollUseCase.scrollVertically(op.count); + case operations.SCROLL_HORIZONALLY: + return () => this.scrollUseCase.scrollHorizonally(op.count); + case operations.SCROLL_PAGES: + return () => this.scrollUseCase.scrollPages(op.count); + case operations.SCROLL_TOP: + return () => this.scrollUseCase.scrollToTop(); + case operations.SCROLL_BOTTOM: + return () => this.scrollUseCase.scrollToBottom(); + case operations.SCROLL_HOME: + return () => this.scrollUseCase.scrollToHome(); + case operations.SCROLL_END: + return () => this.scrollUseCase.scrollToEnd(); + case operations.FOLLOW_START: + return () => this.followMasterClient.startFollow( + op.newTab, op.background); + case operations.MARK_SET_PREFIX: + return () => this.markKeyUseCase.enableSetMode(); + case operations.MARK_JUMP_PREFIX: + return () => this.markKeyUseCase.enableJumpMode(); + case operations.FOCUS_INPUT: + return () => this.focusUseCase.focusFirstInput(); + case operations.URLS_YANK: + return () => this.clipbaordUseCase.yankCurrentURL(); + case operations.URLS_PASTE: + return () => this.clipbaordUseCase.openOrSearch( + op.newTab ? op.newTab : false, + ); + default: + return null; + } + })(nextOp.op); + + if (doFunc === null) { + // Do not await asynchronous methods to return a boolean immidiately. The + // caller requires the synchronous response from the callback to identify + // to continue of abandon the event propagations. + this.operationClient.execBackgroundOp(nextOp.repeat, nextOp.op); + } else { + for (let i = 0; i < nextOp.repeat; ++i) { + doFunc(); + } } return true; } diff --git a/src/content/controllers/SettingController.ts b/src/content/controllers/SettingController.ts index e1c7f01..9124188 100644 --- a/src/content/controllers/SettingController.ts +++ b/src/content/controllers/SettingController.ts @@ -14,9 +14,9 @@ export default class SettingController { async initSettings(): Promise<void> { try { - let current = await this.settingUseCase.reload(); - let url = new URL(window.location.href); - let disabled = current.blacklist.includesEntireBlacklist(url); + const current = await this.settingUseCase.reload(); + const url = new URL(window.location.href); + const disabled = current.blacklist.includesEntireBlacklist(url); if (disabled) { this.addonEnabledUseCase.disable(); } else { diff --git a/src/content/domains/KeySequence.ts b/src/content/domains/KeySequence.ts new file mode 100644 index 0000000..cf59125 --- /dev/null +++ b/src/content/domains/KeySequence.ts @@ -0,0 +1,91 @@ +import Key from '../../shared/settings/Key'; + +export default class KeySequence { + constructor( + public readonly keys: Key[], + ) { + } + + push(key: Key): number { + return this.keys.push(key); + } + + length(): number { + return this.keys.length; + } + + startsWith(o: KeySequence): boolean { + if (this.keys.length < o.keys.length) { + return false; + } + for (let i = 0; i < o.keys.length; ++i) { + if (!this.keys[i].equals(o.keys[i])) { + return false; + } + } + return true; + } + + isDigitOnly(): boolean { + return this.keys.every(key => key.isDigit()); + } + + repeatCount(): number { + let nonDigitAt = this.keys.findIndex(key => !key.isDigit()); + if (this.keys.length === 0 || nonDigitAt === 0) { + return 1; + } + if (nonDigitAt === -1) { + nonDigitAt = this.keys.length; + } + const digits = this.keys.slice(0, nonDigitAt) + .map(key => key.key) + .join(''); + return Number(digits); + } + + trimNumericPrefix(): KeySequence { + let nonDigitAt = this.keys.findIndex(key => !key.isDigit()); + if (nonDigitAt === -1) { + nonDigitAt = this.keys.length; + } + return new KeySequence(this.keys.slice(nonDigitAt)); + } + + splitNumericPrefix(): [KeySequence, KeySequence] { + const nonDigitIndex = this.keys.findIndex(key => !key.isDigit()); + if (nonDigitIndex === -1) { + return [this, new KeySequence([])]; + } + return [ + new KeySequence(this.keys.slice(0, nonDigitIndex)), + new KeySequence(this.keys.slice(nonDigitIndex)), + ]; + } + + static fromMapKeys(keys: string): KeySequence { + const fromMapKeysRecursive = ( + remaining: string, mappedKeys: Key[], + ): Key[] => { + if (remaining.length === 0) { + return mappedKeys; + } + + let nextPos = 1; + if (remaining.startsWith('<')) { + const ltPos = remaining.indexOf('>'); + if (ltPos > 0) { + nextPos = ltPos + 1; + } + } + + return fromMapKeysRecursive( + remaining.slice(nextPos), + mappedKeys.concat([Key.fromMapKey(remaining.slice(0, nextPos))]) + ); + }; + + const data = fromMapKeysRecursive(keys, []); + return new KeySequence(data); + } +} diff --git a/src/content/hint-key-producer.ts b/src/content/hint-key-producer.ts index 935394e..3c9482f 100644 --- a/src/content/hint-key-producer.ts +++ b/src/content/hint-key-producer.ts @@ -19,14 +19,14 @@ export default class HintKeyProducer { } private increment(): void { - let max = this.charset.length - 1; + const max = this.charset.length - 1; if (this.counter.every(x => x === max)) { this.counter = new Array(this.counter.length + 1).fill(0); return; } this.counter.reverse(); - let len = this.charset.length; + const len = this.charset.length; let num = this.counter.reduce((x, y, index) => x + y * len ** index) + 1; for (let i = 0; i < this.counter.length; ++i) { this.counter[i] = num % len; diff --git a/src/content/index.ts b/src/content/index.ts index 5b9b92c..176a157 100644 --- a/src/content/index.ts +++ b/src/content/index.ts @@ -11,10 +11,10 @@ if (window.self === window.top) { } try { - let app = container.resolve(Application); + const app = container.resolve(Application); app.run(); } catch (e) { console.error(e); } -let style = window.document.createElement('style'); +const style = window.document.createElement('style'); style.textContent = consoleFrameStyle; window.document.head.appendChild(style); diff --git a/src/content/presenters/ConsoleFramePresenter.ts b/src/content/presenters/ConsoleFramePresenter.ts index 62db2bb..63c78fb 100644 --- a/src/content/presenters/ConsoleFramePresenter.ts +++ b/src/content/presenters/ConsoleFramePresenter.ts @@ -6,7 +6,7 @@ export default interface ConsoleFramePresenter { export class ConsoleFramePresenterImpl implements ConsoleFramePresenter { initialize(): void { - let iframe = document.createElement('iframe'); + const iframe = document.createElement('iframe'); iframe.src = browser.runtime.getURL('build/console.html'); iframe.id = 'vimvixen-console-frame'; iframe.className = 'vimvixen-console-frame'; @@ -14,7 +14,7 @@ export class ConsoleFramePresenterImpl implements ConsoleFramePresenter { } blur(): void { - let ele = document.getElementById('vimvixen-console-frame'); + const ele = document.getElementById('vimvixen-console-frame'); if (!ele) { throw new Error('console frame not created'); } diff --git a/src/content/presenters/FindPresenter.ts b/src/content/presenters/FindPresenter.ts index f171d2f..98d8088 100644 --- a/src/content/presenters/FindPresenter.ts +++ b/src/content/presenters/FindPresenter.ts @@ -26,13 +26,13 @@ declare var window: MyWindow; export class FindPresenterImpl implements FindPresenter { find(keyword: string, backwards: boolean): boolean { - let caseSensitive = false; - let wrapScan = true; + const caseSensitive = false; + const wrapScan = true; // NOTE: aWholeWord dows not implemented, and aSearchInFrames does not work // because of same origin policy - let found = window.find(keyword, caseSensitive, backwards, wrapScan); + const found = window.find(keyword, caseSensitive, backwards, wrapScan); if (found) { return found; } @@ -42,7 +42,7 @@ export class FindPresenterImpl implements FindPresenter { } clearSelection(): void { - let sel = window.getSelection(); + const sel = window.getSelection(); if (sel) { sel.removeAllRanges(); } diff --git a/src/content/presenters/FocusPresenter.ts b/src/content/presenters/FocusPresenter.ts index 7e20cd6..842c41e 100644 --- a/src/content/presenters/FocusPresenter.ts +++ b/src/content/presenters/FocusPresenter.ts @@ -6,10 +6,10 @@ export default interface FocusPresenter { export class FocusPresenterImpl implements FocusPresenter { focusFirstElement(): boolean { - let inputTypes = ['email', 'number', 'search', 'tel', 'text', 'url']; - let inputSelector = inputTypes.map(type => `input[type=${type}]`).join(','); - let targets = window.document.querySelectorAll(inputSelector + ',textarea'); - let target = Array.from(targets).find(doms.isVisible); + const inputTypes = ['email', 'number', 'search', 'tel', 'text', 'url']; + const inputSelector = inputTypes.map(type => `input[type=${type}]`).join(','); + const targets = window.document.querySelectorAll(inputSelector + ',textarea'); + const target = Array.from(targets).find(doms.isVisible); if (target instanceof HTMLInputElement) { target.focus(); return true; diff --git a/src/content/presenters/FollowPresenter.ts b/src/content/presenters/FollowPresenter.ts index 0132621..fef8140 100644 --- a/src/content/presenters/FollowPresenter.ts +++ b/src/content/presenters/FollowPresenter.ts @@ -23,12 +23,12 @@ const inViewport = ( viewSize: Size, framePosition: Point, ): boolean => { - let { + const { top, left, bottom, right } = doms.viewportRect(element); - let doc = win.document; - let frameWidth = doc.documentElement.clientWidth; - let frameHeight = doc.documentElement.clientHeight; + const doc = win.document; + const frameWidth = doc.documentElement.clientWidth; + const frameHeight = doc.documentElement.clientHeight; if (right < 0 || bottom < 0 || top > frameHeight || left > frameWidth) { // out of frame @@ -47,10 +47,10 @@ const isAriaHiddenOrAriaDisabled = (win: Window, element: Element): boolean => { if (!element || win.document.documentElement === element) { return false; } - for (let attr of ['aria-hidden', 'aria-disabled']) { - let value = element.getAttribute(attr); + for (const attr of ['aria-hidden', 'aria-disabled']) { + const value = element.getAttribute(attr); if (value !== null) { - let hidden = value.toLowerCase(); + const hidden = value.toLowerCase(); if (hidden === '' || hidden === 'true') { return true; } @@ -79,15 +79,15 @@ export class FollowPresenterImpl implements FollowPresenter { } getTargetCount(viewSize: Size, framePosition: Point): number { - let targets = this.getTargets(viewSize, framePosition); + const targets = this.getTargets(viewSize, framePosition); return targets.length; } createHints(viewSize: Size, framePosition: Point, tags: string[]): void { - let targets = this.getTargets(viewSize, framePosition); - let min = Math.min(targets.length, tags.length); + const targets = this.getTargets(viewSize, framePosition); + const min = Math.min(targets.length, tags.length); for (let i = 0; i < min; ++i) { - let target = targets[i]; + const target = targets[i]; if (target instanceof HTMLAnchorElement || target instanceof HTMLAreaElement) { this.hints.push(new LinkHint(target, tags[i])); @@ -98,8 +98,8 @@ export class FollowPresenterImpl implements FollowPresenter { } filterHints(prefix: string): void { - let shown = this.hints.filter(h => h.getTag().startsWith(prefix)); - let hidden = this.hints.filter(h => !h.getTag().startsWith(prefix)); + const shown = this.hints.filter(h => h.getTag().startsWith(prefix)); + const hidden = this.hints.filter(h => !h.getTag().startsWith(prefix)); shown.forEach(h => h.show()); hidden.forEach(h => h.hide()); @@ -115,9 +115,9 @@ export class FollowPresenterImpl implements FollowPresenter { } private getTargets(viewSize: Size, framePosition: Point): HTMLElement[] { - let all = window.document.querySelectorAll(TARGET_SELECTOR); - let filtered = Array.prototype.filter.call(all, (element: HTMLElement) => { - let style = window.getComputedStyle(element); + const all = window.document.querySelectorAll(TARGET_SELECTOR); + const filtered = Array.prototype.filter.call(all, (element: HTMLElement) => { + const style = window.getComputedStyle(element); // AREA's 'display' in Browser style is 'none' return (element.tagName === 'AREA' || style.display !== 'none') && diff --git a/src/content/presenters/Hint.ts b/src/content/presenters/Hint.ts index 60c0f4c..44b8185 100644 --- a/src/content/presenters/Hint.ts +++ b/src/content/presenters/Hint.ts @@ -6,7 +6,7 @@ interface Point { } const hintPosition = (element: Element): Point => { - let { left, top, right, bottom } = doms.viewportRect(element); + const { left, top, right, bottom } = doms.viewportRect(element); if (element.tagName !== 'AREA') { return { x: left, y: top }; @@ -26,15 +26,15 @@ export default abstract class Hint { constructor(target: HTMLElement, tag: string) { this.tag = tag; - let doc = target.ownerDocument; + const doc = target.ownerDocument; if (doc === null) { throw new TypeError('ownerDocument is null'); } - let { x, y } = hintPosition(target); - let { scrollX, scrollY } = window; + const { x, y } = hintPosition(target); + const { scrollX, scrollY } = window; - let hint = doc.createElement('span'); + const hint = doc.createElement('span'); hint.className = 'vimvixen-hint'; hint.textContent = tag; hint.style.left = x + scrollX + 'px'; @@ -95,7 +95,7 @@ export class InputHint extends Hint { } activate(): void { - let target = this.target; + const target = this.target; switch (target.tagName.toLowerCase()) { case 'input': switch ((target as HTMLInputElement).type) { diff --git a/src/content/presenters/NavigationPresenter.ts b/src/content/presenters/NavigationPresenter.ts index 11d96ec..951e62a 100644 --- a/src/content/presenters/NavigationPresenter.ts +++ b/src/content/presenters/NavigationPresenter.ts @@ -49,7 +49,7 @@ export class NavigationPresenterImpl implements NavigationPresenter { // Code common to linkPrev and linkNext which navigates to the specified page. private linkRel(rel: 'prev' | 'next'): void { - let link = selectLast<HTMLLinkElement>(`link[rel~=${rel}][href]`); + const link = selectLast<HTMLLinkElement>(`link[rel~=${rel}][href]`); if (link) { window.location.href = link.href; return; @@ -57,7 +57,7 @@ export class NavigationPresenterImpl implements NavigationPresenter { const pattern = REL_PATTERN[rel]; - let a = selectLast<HTMLAnchorElement>(`a[rel~=${rel}][href]`) || + const a = selectLast<HTMLAnchorElement>(`a[rel~=${rel}][href]`) || // `innerText` is much slower than `textContent`, but produces much better // (i.e. less unexpected) results selectLast('a[href]', lnk => pattern.test(lnk.innerText)); diff --git a/src/content/presenters/ScrollPresenter.ts b/src/content/presenters/ScrollPresenter.ts index e83f172..387ab62 100644 --- a/src/content/presenters/ScrollPresenter.ts +++ b/src/content/presenters/ScrollPresenter.ts @@ -8,7 +8,7 @@ let scrolling = false; let lastTimeoutId: number | null = null; const isScrollableStyle = (element: Element): boolean => { - let { overflowX, overflowY } = window.getComputedStyle(element); + const { overflowX, overflowY } = window.getComputedStyle(element); return !(overflowX !== 'scroll' && overflowX !== 'auto' && overflowY !== 'scroll' && overflowY !== 'auto'); }; @@ -27,9 +27,9 @@ const findScrollable = (element: Element): Element | null => { return element; } - let children = Array.from(element.children).filter(doms.isVisible); - for (let child of children) { - let scrollable = findScrollable(child); + const children = Array.from(element.children).filter(doms.isVisible); + for (const child of children) { + const scrollable = findScrollable(child); if (scrollable) { return scrollable; } @@ -44,7 +44,7 @@ const scrollTarget = () => { if (isOverflowed(window.document.body)) { return window.document.body; } - let target = findScrollable(window.document.documentElement); + const target = findScrollable(window.document.documentElement); if (target) { return target; } @@ -79,8 +79,8 @@ class Scroller { } scrollBy(x: number, y: number): void { - let left = this.element.scrollLeft + x; - let top = this.element.scrollTop + y; + const left = this.element.scrollLeft + x; + const top = this.element.scrollTop + y; this.scrollTo(left, top); } @@ -110,12 +110,12 @@ export default interface ScrollPresenter { export class ScrollPresenterImpl { getScroll(): Point { - let target = scrollTarget(); + const target = scrollTarget(); return { x: target.scrollLeft, y: target.scrollTop }; } scrollVertically(count: number, smooth: boolean): void { - let target = scrollTarget(); + const target = scrollTarget(); let delta = SCROLL_DELTA_Y * count; if (scrolling) { delta = SCROLL_DELTA_Y * count * 4; @@ -124,7 +124,7 @@ export class ScrollPresenterImpl { } scrollHorizonally(count: number, smooth: boolean): void { - let target = scrollTarget(); + const target = scrollTarget(); let delta = SCROLL_DELTA_X * count; if (scrolling) { delta = SCROLL_DELTA_X * count * 4; @@ -133,8 +133,8 @@ export class ScrollPresenterImpl { } scrollPages(count: number, smooth: boolean): void { - let target = scrollTarget(); - let height = target.clientHeight; + const target = scrollTarget(); + const height = target.clientHeight; let delta = height * count; if (scrolling) { delta = height * count; @@ -143,35 +143,35 @@ export class ScrollPresenterImpl { } scrollTo(x: number, y: number, smooth: boolean): void { - let target = scrollTarget(); + const target = scrollTarget(); new Scroller(target, smooth).scrollTo(x, y); } scrollToTop(smooth: boolean): void { - let target = scrollTarget(); - let x = target.scrollLeft; - let y = 0; + const target = scrollTarget(); + const x = target.scrollLeft; + const y = 0; new Scroller(target, smooth).scrollTo(x, y); } scrollToBottom(smooth: boolean): void { - let target = scrollTarget(); - let x = target.scrollLeft; - let y = target.scrollHeight; + const target = scrollTarget(); + const x = target.scrollLeft; + const y = target.scrollHeight; new Scroller(target, smooth).scrollTo(x, y); } scrollToHome(smooth: boolean): void { - let target = scrollTarget(); - let x = 0; - let y = target.scrollTop; + const target = scrollTarget(); + const x = 0; + const y = target.scrollTop; new Scroller(target, smooth).scrollTo(x, y); } scrollToEnd(smooth: boolean): void { - let target = scrollTarget(); - let x = target.scrollWidth; - let y = target.scrollTop; + const target = scrollTarget(); + const x = target.scrollWidth; + const y = target.scrollTop; new Scroller(target, smooth).scrollTo(x, y); } } diff --git a/src/content/repositories/AddonEnabledRepository.ts b/src/content/repositories/AddonEnabledRepository.ts index 7921bdb..ce3f521 100644 --- a/src/content/repositories/AddonEnabledRepository.ts +++ b/src/content/repositories/AddonEnabledRepository.ts @@ -1,4 +1,4 @@ -let enabled: boolean = false; +let enabled = false; export default interface AddonEnabledRepository { set(on: boolean): void; diff --git a/src/content/repositories/ClipboardRepository.ts b/src/content/repositories/ClipboardRepository.ts index a046851..8219835 100644 --- a/src/content/repositories/ClipboardRepository.ts +++ b/src/content/repositories/ClipboardRepository.ts @@ -6,7 +6,7 @@ export default interface ClipboardRepository { export class ClipboardRepositoryImpl { read(): string { - let textarea = window.document.createElement('textarea'); + const textarea = window.document.createElement('textarea'); window.document.body.append(textarea); textarea.style.position = 'fixed'; @@ -14,8 +14,8 @@ export class ClipboardRepositoryImpl { textarea.contentEditable = 'true'; textarea.focus(); - let ok = window.document.execCommand('paste'); - let value = textarea.textContent!!; + const ok = window.document.execCommand('paste'); + const value = textarea.textContent!!; textarea.remove(); if (!ok) { @@ -26,7 +26,7 @@ export class ClipboardRepositoryImpl { } write(text: string): void { - let input = window.document.createElement('input'); + const input = window.document.createElement('input'); window.document.body.append(input); input.style.position = 'fixed'; @@ -34,7 +34,7 @@ export class ClipboardRepositoryImpl { input.value = text; input.select(); - let ok = window.document.execCommand('copy'); + const ok = window.document.execCommand('copy'); input.remove(); if (!ok) { diff --git a/src/content/repositories/KeymapRepository.ts b/src/content/repositories/KeymapRepository.ts index 3391229..2944723 100644 --- a/src/content/repositories/KeymapRepository.ts +++ b/src/content/repositories/KeymapRepository.ts @@ -1,5 +1,5 @@ import Key from '../../shared/settings/Key'; -import KeySequence from '../../shared/settings/KeySequence'; +import KeySequence from '../domains/KeySequence'; export default interface KeymapRepository { enqueueKey(key: Key): KeySequence; diff --git a/src/content/repositories/MarkKeyRepository.ts b/src/content/repositories/MarkKeyRepository.ts index 0b24af2..18c3e23 100644 --- a/src/content/repositories/MarkKeyRepository.ts +++ b/src/content/repositories/MarkKeyRepository.ts @@ -17,7 +17,7 @@ interface Mode { jumpMode: boolean; } -let current: Mode = { +const current: Mode = { setMode: false, jumpMode: false, }; diff --git a/src/content/repositories/MarkRepository.ts b/src/content/repositories/MarkRepository.ts index 678abc4..afa980a 100644 --- a/src/content/repositories/MarkRepository.ts +++ b/src/content/repositories/MarkRepository.ts @@ -14,7 +14,7 @@ export class MarkRepositoryImpl implements MarkRepository { } get(key: string): Mark | null { - let v = saved[key]; + const v = saved[key]; if (!v) { return null; } diff --git a/src/content/usecases/AddonEnabledUseCase.ts b/src/content/usecases/AddonEnabledUseCase.ts index 2d6fa11..608a401 100644 --- a/src/content/usecases/AddonEnabledUseCase.ts +++ b/src/content/usecases/AddonEnabledUseCase.ts @@ -23,7 +23,7 @@ export default class AddonEnabledUseCase { } async toggle(): Promise<void> { - let current = this.repository.get(); + const current = this.repository.get(); await this.setEnabled(!current); } diff --git a/src/content/usecases/ClipboardUseCase.ts b/src/content/usecases/ClipboardUseCase.ts index c8fe719..7f16f68 100644 --- a/src/content/usecases/ClipboardUseCase.ts +++ b/src/content/usecases/ClipboardUseCase.ts @@ -16,16 +16,16 @@ export default class ClipboardUseCase { } async yankCurrentURL(): Promise<string> { - let url = window.location.href; + const url = window.location.href; this.repository.write(url); await this.consoleClient.info('Yanked ' + url); return Promise.resolve(url); } async openOrSearch(newTab: boolean): Promise<void> { - let search = this.settingRepository.get().search; - let text = this.repository.read(); - let url = urls.searchUrl(text, search); + const search = this.settingRepository.get().search; + const text = this.repository.read(); + const url = urls.searchUrl(text, search); // TODO: Repeat pasting from clipboard instead of opening a certain url. // 'Repeat last' command is implemented in the background script and cannot diff --git a/src/content/usecases/FindUseCase.ts b/src/content/usecases/FindUseCase.ts index 88b516c..c6a478f 100644 --- a/src/content/usecases/FindUseCase.ts +++ b/src/content/usecases/FindUseCase.ts @@ -19,7 +19,7 @@ export default class FindUseCase { if (keyword) { this.saveKeyword(keyword); } else { - let lastKeyword = await this.getKeyword(); + const lastKeyword = await this.getKeyword(); if (!lastKeyword) { return this.showNoLastKeywordError(); } @@ -39,11 +39,11 @@ export default class FindUseCase { private async findNextPrev( backwards: boolean, ): Promise<void> { - let keyword = await this.getKeyword(); + const keyword = await this.getKeyword(); if (!keyword) { return this.showNoLastKeywordError(); } - let found = this.presenter.find(keyword, backwards); + const found = this.presenter.find(keyword, backwards); if (found) { this.consoleClient.info('Pattern found: ' + keyword); } else { diff --git a/src/content/usecases/FollowMasterUseCase.ts b/src/content/usecases/FollowMasterUseCase.ts index 7d7e875..0e7f394 100644 --- a/src/content/usecases/FollowMasterUseCase.ts +++ b/src/content/usecases/FollowMasterUseCase.ts @@ -28,24 +28,24 @@ export default class FollowMasterUseCase { } startFollow(newTab: boolean, background: boolean): void { - let hintchars = this.settingRepository.get().properties.hintchars; + const hintchars = this.settingRepository.get().properties.hintchars; this.producer = new HintKeyProducer(hintchars); this.followKeyRepository.clearKeys(); this.followMasterRepository.setCurrentFollowMode(newTab, background); - let viewWidth = window.top.innerWidth; - let viewHeight = window.top.innerHeight; + const viewWidth = window.top.innerWidth; + const viewHeight = window.top.innerHeight; this.followSlaveClientFactory.create(window.top).requestHintCount( { width: viewWidth, height: viewHeight }, { x: 0, y: 0 }, ); - let frameElements = window.document.querySelectorAll('iframe'); + const frameElements = window.document.querySelectorAll('iframe'); for (let i = 0; i < frameElements.length; ++i) { - let ele = frameElements[i] as HTMLFrameElement | HTMLIFrameElement; - let { left: frameX, top: frameY } = ele.getBoundingClientRect(); - let client = this.followSlaveClientFactory.create(ele.contentWindow!!); + const ele = frameElements[i] as HTMLFrameElement | HTMLIFrameElement; + const { left: frameX, top: frameY } = ele.getBoundingClientRect(); + const client = this.followSlaveClientFactory.create(ele.contentWindow!!); client.requestHintCount( { width: viewWidth, height: viewHeight }, { x: frameX, y: frameY }, @@ -55,28 +55,28 @@ export default class FollowMasterUseCase { // eslint-disable-next-line max-statements createSlaveHints(count: number, sender: Window): void { - let produced = []; + const produced = []; for (let i = 0; i < count; ++i) { - let tag = this.producer!!.produce(); + const tag = this.producer!!.produce(); produced.push(tag); this.followMasterRepository.addTag(tag); } - let doc = window.document; - let viewWidth = window.innerWidth || doc.documentElement.clientWidth; - let viewHeight = window.innerHeight || doc.documentElement.clientHeight; + const doc = window.document; + const viewWidth = window.innerWidth || doc.documentElement.clientWidth; + const viewHeight = window.innerHeight || doc.documentElement.clientHeight; let pos = { x: 0, y: 0 }; if (sender !== window) { - let frameElements = window.document.querySelectorAll('iframe'); - let ele = Array.from(frameElements).find(e => e.contentWindow === sender); + const frameElements = window.document.querySelectorAll('iframe'); + const ele = Array.from(frameElements).find(e => e.contentWindow === sender); if (!ele) { // elements of the sender is gone return; } - let { left: frameX, top: frameY } = ele.getBoundingClientRect(); + const { left: frameX, top: frameY } = ele.getBoundingClientRect(); pos = { x: frameX, y: frameY }; } - let client = this.followSlaveClientFactory.create(sender); + const client = this.followSlaveClientFactory.create(sender); client.createHints( { width: viewWidth, height: viewHeight }, pos, @@ -100,8 +100,8 @@ export default class FollowMasterUseCase { activate(tag: string): void { this.followMasterRepository.clearTags(); - let newTab = this.followMasterRepository.getCurrentNewTabMode(); - let background = this.followMasterRepository.getCurrentBackgroundMode(); + const newTab = this.followMasterRepository.getCurrentNewTabMode(); + const background = this.followMasterRepository.getCurrentBackgroundMode(); this.broadcastToSlaves((client) => { client.activateIfExists(tag, newTab, background); client.clearHints(); @@ -125,8 +125,8 @@ export default class FollowMasterUseCase { this.followKeyRepository.pushKey(key); - let tag = this.getCurrentTag(); - let matched = this.followMasterRepository.getTagsByPrefix(tag); + const tag = this.getCurrentTag(); + const matched = this.followMasterRepository.getTagsByPrefix(tag); if (matched.length === 0) { this.cancelFollow(); } else if (matched.length === 1) { @@ -137,9 +137,9 @@ export default class FollowMasterUseCase { } private broadcastToSlaves(handler: (client: FollowSlaveClient) => void) { - let allFrames = [window.self].concat(Array.from(window.frames as any)); - let clients = allFrames.map(w => this.followSlaveClientFactory.create(w)); - for (let client of clients) { + const allFrames = [window.self].concat(Array.from(window.frames as any)); + const clients = allFrames.map(w => this.followSlaveClientFactory.create(w)); + for (const client of clients) { handler(client); } } diff --git a/src/content/usecases/FollowSlaveUseCase.ts b/src/content/usecases/FollowSlaveUseCase.ts index d471adb..fb805b9 100644 --- a/src/content/usecases/FollowSlaveUseCase.ts +++ b/src/content/usecases/FollowSlaveUseCase.ts @@ -34,7 +34,7 @@ export default class FollowSlaveUseCase { } countTargets(viewSize: Size, framePosition: Point): void { - let count = this.presenter.getTargetCount(viewSize, framePosition); + const count = this.presenter.getTargetCount(viewSize, framePosition); this.followMasterClient.responseHintCount(count); } @@ -56,13 +56,13 @@ export default class FollowSlaveUseCase { } async activate(tag: string, newTab: boolean, background: boolean) { - let hint = this.presenter.getHint(tag); + const hint = this.presenter.getHint(tag); if (!hint) { return; } if (hint instanceof LinkHint) { - let url = hint.getLink(); + const url = hint.getLink(); let openNewTab = newTab; // Open link by background script in order to prevent a popup block if (hint.getLinkTarget() === '_blank') { diff --git a/src/content/usecases/HintKeyProducer.ts b/src/content/usecases/HintKeyProducer.ts index 241cd56..68f3fbd 100644 --- a/src/content/usecases/HintKeyProducer.ts +++ b/src/content/usecases/HintKeyProducer.ts @@ -19,14 +19,14 @@ export default class HintKeyProducer { } private increment(): void { - let max = this.charset.length - 1; + const max = this.charset.length - 1; if (this.counter.every(x => x === max)) { this.counter = new Array(this.counter.length + 1).fill(0); return; } this.counter.reverse(); - let len = this.charset.length; + const len = this.charset.length; let num = this.counter.reduce((x, y, index) => x + y * len ** index) + 1; for (let i = 0; i < this.counter.length; ++i) { this.counter[i] = num % len; diff --git a/src/content/usecases/KeymapUseCase.ts b/src/content/usecases/KeymapUseCase.ts index 67d667d..074de72 100644 --- a/src/content/usecases/KeymapUseCase.ts +++ b/src/content/usecases/KeymapUseCase.ts @@ -5,16 +5,19 @@ import AddonEnabledRepository from '../repositories/AddonEnabledRepository'; import * as operations from '../../shared/operations'; import Keymaps from '../../shared/settings/Keymaps'; import Key from '../../shared/settings/Key'; -import KeySequence from '../../shared/settings/KeySequence'; +import KeySequence from '../domains/KeySequence'; import AddressRepository from '../repositories/AddressRepository'; -type KeymapEntityMap = Map<KeySequence, operations.Operation>; - const reservedKeymaps = Keymaps.fromJSON({ '<Esc>': { type: operations.CANCEL }, '<C-[>': { type: operations.CANCEL }, }); +const enableAddonOps = [ + operations.ADDON_ENABLE, + operations.ADDON_TOGGLE_ENABLED, +]; + @injectable() export default class KeymapUseCase { constructor( @@ -32,58 +35,59 @@ export default class KeymapUseCase { ) { } - nextOp(key: Key): operations.Operation | null { - let sequence = this.repository.enqueueKey(key); - if (sequence.length() === 1 && this.blacklistKey(key)) { + // eslint-disable-next-line max-statements + nextOps(key: Key): { repeat: number, op: operations.Operation } | null { + const sequence = this.repository.enqueueKey(key); + const baseSequence = sequence.trimNumericPrefix(); + if (baseSequence.length() === 1 && this.blacklistKey(key)) { // ignore if the input starts with black list keys this.repository.clear(); return null; } - let keymaps = this.keymapEntityMap(); - let matched = Array.from(keymaps.keys()).filter( - (mapping: KeySequence) => { - return mapping.startsWith(sequence); - }); - if (!this.addonEnabledRepository.get()) { - // available keymaps are only ADDON_ENABLE and ADDON_TOGGLE_ENABLED if - // the addon disabled - matched = matched.filter((keymap) => { - let type = (keymaps.get(keymap) as operations.Operation).type; - return type === operations.ADDON_ENABLE || - type === operations.ADDON_TOGGLE_ENABLED; - }); - } - if (matched.length === 0) { - // No operations to match with inputs + const keymaps = this.keymapEntityMap(); + const matched = keymaps.filter(([seq]) => seq.startsWith(sequence)); + const baseMatched = keymaps.filter(([seq]) => seq.startsWith(baseSequence)); + + if (matched.length === 1 && + sequence.length() === matched[0][0].length()) { + // keys are matched with an operation this.repository.clear(); - return null; - } else if (matched.length > 1 || - matched.length === 1 && sequence.length() < matched[0].length()) { - // More than one operations are matched + return { repeat: 1, op: matched[0][1] }; + } else if ( + baseMatched.length === 1 && + baseSequence.length() === baseMatched[0][0].length()) { + // keys are matched with an operation with a numeric prefix + this.repository.clear(); + return { repeat: sequence.repeatCount(), op: baseMatched[0][1] }; + } else if (matched.length >= 1 || baseMatched.length >= 1) { + // keys are matched with an operation's prefix return null; } - // Exactly one operation is matched - let operation = keymaps.get(matched[0]) as operations.Operation; - this.repository.clear(); - return operation; - } - clear(): void { + // matched with no operations this.repository.clear(); + return null; } - private keymapEntityMap(): KeymapEntityMap { - let keymaps = this.settingRepository.get().keymaps.combine(reservedKeymaps); + private keymapEntityMap(): [KeySequence, operations.Operation][] { + const keymaps = this.settingRepository.get().keymaps.combine(reservedKeymaps); let entries = keymaps.entries().map( ([keys, op]) => [KeySequence.fromMapKeys(keys), op] ) as [KeySequence, operations.Operation][]; - return new Map<KeySequence, operations.Operation>(entries); + if (!this.addonEnabledRepository.get()) { + // available keymaps are only ADDON_ENABLE and ADDON_TOGGLE_ENABLED if + // the addon disabled + entries = entries.filter( + ([_seq, { type }]) => enableAddonOps.includes(type) + ); + } + return entries; } private blacklistKey(key: Key): boolean { - let url = this.addressRepository.getCurrentURL(); - let blacklist = this.settingRepository.get().blacklist; + const url = this.addressRepository.getCurrentURL(); + const blacklist = this.settingRepository.get().blacklist; return blacklist.includeKey(url, key); } } diff --git a/src/content/usecases/MarkUseCase.ts b/src/content/usecases/MarkUseCase.ts index a7d5ad8..8cd0c72 100644 --- a/src/content/usecases/MarkUseCase.ts +++ b/src/content/usecases/MarkUseCase.ts @@ -17,7 +17,7 @@ export default class MarkUseCase { } async set(key: string): Promise<void> { - let pos = this.scrollPresenter.getScroll(); + const pos = this.scrollPresenter.getScroll(); if (this.globalKey(key)) { this.client.setGloablMark(key, pos); await this.consoleClient.info(`Set global mark to '${key}'`); @@ -31,7 +31,7 @@ export default class MarkUseCase { if (this.globalKey(key)) { await this.client.jumpGlobalMark(key); } else { - let pos = this.repository.get(key); + const pos = this.repository.get(key); if (!pos) { throw new Error('Mark is not set'); } @@ -40,7 +40,7 @@ export default class MarkUseCase { } scroll(x: number, y: number): void { - let smooth = this.settingRepository.get().properties.smoothscroll; + const smooth = this.settingRepository.get().properties.smoothscroll; this.scrollPresenter.scrollTo(x, y, smooth); } diff --git a/src/content/usecases/ScrollUseCase.ts b/src/content/usecases/ScrollUseCase.ts index 32cbef1..c68c889 100644 --- a/src/content/usecases/ScrollUseCase.ts +++ b/src/content/usecases/ScrollUseCase.ts @@ -11,42 +11,42 @@ export default class ScrollUseCase { } scrollVertically(count: number): void { - let smooth = this.getSmoothScroll(); + const smooth = this.getSmoothScroll(); this.presenter.scrollVertically(count, smooth); } scrollHorizonally(count: number): void { - let smooth = this.getSmoothScroll(); + const smooth = this.getSmoothScroll(); this.presenter.scrollHorizonally(count, smooth); } scrollPages(count: number): void { - let smooth = this.getSmoothScroll(); + const smooth = this.getSmoothScroll(); this.presenter.scrollPages(count, smooth); } scrollToTop(): void { - let smooth = this.getSmoothScroll(); + const smooth = this.getSmoothScroll(); this.presenter.scrollToTop(smooth); } scrollToBottom(): void { - let smooth = this.getSmoothScroll(); + const smooth = this.getSmoothScroll(); this.presenter.scrollToBottom(smooth); } scrollToHome(): void { - let smooth = this.getSmoothScroll(); + const smooth = this.getSmoothScroll(); this.presenter.scrollToHome(smooth); } scrollToEnd(): void { - let smooth = this.getSmoothScroll(); + const smooth = this.getSmoothScroll(); this.presenter.scrollToEnd(smooth); } private getSmoothScroll(): boolean { - let settings = this.settingRepository.get(); + const settings = this.settingRepository.get(); return settings.properties.smoothscroll; } } diff --git a/src/content/usecases/SettingUseCase.ts b/src/content/usecases/SettingUseCase.ts index 4608039..67d1be6 100644 --- a/src/content/usecases/SettingUseCase.ts +++ b/src/content/usecases/SettingUseCase.ts @@ -12,7 +12,7 @@ export default class SettingUseCase { } async reload(): Promise<Settings> { - let settings = await this.client.load(); + const settings = await this.client.load(); this.repository.set(settings); return settings; } diff --git a/src/settings/actions/setting.ts b/src/settings/actions/setting.ts index 9404791..589ec36 100644 --- a/src/settings/actions/setting.ts +++ b/src/settings/actions/setting.ts @@ -5,7 +5,7 @@ import SettingData, { } from '../../shared/SettingData'; const load = async(): Promise<actions.SettingAction> => { - let data = await storages.load(); + const data = await storages.load(); return set(data); }; @@ -29,7 +29,7 @@ const save = async(data: SettingData): Promise<actions.SettingAction> => { const switchToForm = (json: JSONTextSettings): actions.SettingAction => { try { // toSettings exercise validation - let form = FormSettings.fromSettings(json.toSettings()); + const form = FormSettings.fromSettings(json.toSettings()); return { type: actions.SETTING_SWITCH_TO_FORM, form, @@ -44,7 +44,7 @@ const switchToForm = (json: JSONTextSettings): actions.SettingAction => { }; const switchToJson = (form: FormSettings): actions.SettingAction => { - let json = JSONTextSettings.fromSettings(form.toSettings()); + const json = JSONTextSettings.fromSettings(form.toSettings()); return { type: actions.SETTING_SWITCH_TO_JSON, json, @@ -52,7 +52,7 @@ const switchToJson = (form: FormSettings): actions.SettingAction => { }; const set = (data: SettingData): actions.SettingAction => { - let source = data.getSource(); + const source = data.getSource(); switch (source) { case SettingSource.JSON: return { diff --git a/src/settings/components/form/BlacklistForm.tsx b/src/settings/components/form/BlacklistForm.tsx index 4e96cbf..51c32f4 100644 --- a/src/settings/components/form/BlacklistForm.tsx +++ b/src/settings/components/form/BlacklistForm.tsx @@ -43,9 +43,9 @@ class BlacklistForm extends React.Component<Props> { } bindValue(e: any) { - let name = e.target.name; - let index = e.target.getAttribute('data-index'); - let items = this.props.value.items; + const name = e.target.name; + const index = e.target.getAttribute('data-index'); + const items = this.props.value.items; if (name === 'url') { items[index] = new BlacklistItem(e.target.value, false, []); diff --git a/src/settings/components/form/KeymapsForm.tsx b/src/settings/components/form/KeymapsForm.tsx index 94934ae..dc74de3 100644 --- a/src/settings/components/form/KeymapsForm.tsx +++ b/src/settings/components/form/KeymapsForm.tsx @@ -18,14 +18,14 @@ class KeymapsForm extends React.Component<Props> { }; render() { - let values = this.props.value.toJSON(); + const values = this.props.value.toJSON(); return <div className='form-keymaps-form'> { keymaps.fields.map((group, index) => { return <div key={index} className='form-keymaps-form-field-group'> { group.map(([name, label]) => { - let value = values[name] || ''; + const value = values[name] || ''; return <Input type='text' id={name} name={name} key={name} label={label} value={value} diff --git a/src/settings/components/form/PartialBlacklistForm.tsx b/src/settings/components/form/PartialBlacklistForm.tsx index 0702913..1807e28 100644 --- a/src/settings/components/form/PartialBlacklistForm.tsx +++ b/src/settings/components/form/PartialBlacklistForm.tsx @@ -52,15 +52,15 @@ class PartialBlacklistForm extends React.Component<Props> { } bindValue(e: any) { - let name = e.target.name; - let index = e.target.getAttribute('data-index'); - let items = this.props.value.items; + const name = e.target.name; + const index = e.target.getAttribute('data-index'); + const items = this.props.value.items; if (name === 'url') { - let current = items[index]; + const current = items[index]; items[index] = new BlacklistItem(e.target.value, true, current.keys); } else if (name === 'keys') { - let current = items[index]; + const current = items[index]; items[index] = new BlacklistItem( current.pattern, true, e.target.value.split(',')); } else if (name === 'add') { diff --git a/src/settings/components/form/PropertiesForm.tsx b/src/settings/components/form/PropertiesForm.tsx index db8c8e5..e648971 100644 --- a/src/settings/components/form/PropertiesForm.tsx +++ b/src/settings/components/form/PropertiesForm.tsx @@ -17,13 +17,13 @@ class PropertiesForm extends React.Component<Props> { }; render() { - let types = this.props.types; - let values = this.props.value; + const types = this.props.types; + const values = this.props.value; return <div className='form-properties-form'> { Object.keys(types).map((name) => { - let type = types[name]; + const type = types[name]; let inputType = ''; let onChange = this.bindValue.bind(this); if (type === 'string') { @@ -59,8 +59,8 @@ class PropertiesForm extends React.Component<Props> { } bindValue(e: React.ChangeEvent<HTMLInputElement>) { - let name = e.target.name; - let next = { ...this.props.value }; + const name = e.target.name; + const next = { ...this.props.value }; if (e.target.type.toLowerCase() === 'checkbox') { next[name] = e.target.checked; } else if (e.target.type.toLowerCase() === 'number') { diff --git a/src/settings/components/form/SearchForm.tsx b/src/settings/components/form/SearchForm.tsx index 0aaf6fd..5dc786b 100644 --- a/src/settings/components/form/SearchForm.tsx +++ b/src/settings/components/form/SearchForm.tsx @@ -18,7 +18,7 @@ class SearchForm extends React.Component<Props> { }; render() { - let value = this.props.value.toJSON(); + const value = this.props.value.toJSON(); return <div className='form-search-form'> <div className='form-search-form-header'> <div className='column-name'>Name</div> @@ -56,10 +56,10 @@ class SearchForm extends React.Component<Props> { // eslint-disable-next-line max-statements bindValue(e: any) { - let value = this.props.value.toJSON(); - let name = e.target.name; - let index = Number(e.target.getAttribute('data-index')); - let next: typeof value = { + const value = this.props.value.toJSON(); + const name = e.target.name; + const index = Number(e.target.getAttribute('data-index')); + const next: typeof value = { default: value.default, engines: value.engines.slice(), }; @@ -76,7 +76,7 @@ class SearchForm extends React.Component<Props> { } else if (name === 'delete' && value.engines.length > 1) { next.engines.splice(index, 1); if (value.engines[index][0] === value.default) { - let nextIndex = Math.min(index, next.engines.length - 1); + const nextIndex = Math.min(index, next.engines.length - 1); next.default = next.engines[nextIndex][0]; } } diff --git a/src/settings/components/index.tsx b/src/settings/components/index.tsx index 3eb2dbe..f4f0326 100644 --- a/src/settings/components/index.tsx +++ b/src/settings/components/index.tsx @@ -96,7 +96,7 @@ class SettingsComponent extends React.Component<Props> { render() { let fields = null; - let disabled = this.props.error.length > 0; + const disabled = this.props.error.length > 0; if (this.props.source === 'form') { fields = this.renderFormFields(this.props.form!!); } else if (this.props.source === 'json') { @@ -131,7 +131,7 @@ class SettingsComponent extends React.Component<Props> { } bindKeymapsForm(value: FormKeymaps) { - let data = new SettingData({ + const data = new SettingData({ source: this.props.source, form: (this.props.form as FormSettings).buildWithKeymaps(value), }); @@ -139,7 +139,7 @@ class SettingsComponent extends React.Component<Props> { } bindSearchForm(value: any) { - let data = new SettingData({ + const data = new SettingData({ source: this.props.source, form: (this.props.form as FormSettings).buildWithSearch( FormSearch.fromJSON(value)), @@ -148,7 +148,7 @@ class SettingsComponent extends React.Component<Props> { } bindBlacklistForm(blacklist: Blacklist) { - let data = new SettingData({ + const data = new SettingData({ source: this.props.source, form: (this.props.form as FormSettings).buildWithBlacklist(blacklist), }); @@ -156,7 +156,7 @@ class SettingsComponent extends React.Component<Props> { } bindPropertiesForm(value: any) { - let data = new SettingData({ + const data = new SettingData({ source: this.props.source, form: (this.props.form as FormSettings).buildWithProperties( Properties.fromJSON(value)) @@ -165,7 +165,7 @@ class SettingsComponent extends React.Component<Props> { } bindJson(_name: string, value: string) { - let data = new SettingData({ + const data = new SettingData({ source: this.props.source, json: JSONTextSettings.fromText(value), }); @@ -173,13 +173,13 @@ class SettingsComponent extends React.Component<Props> { } bindSource(_name: string, value: string) { - let from = this.props.source; + const from = this.props.source; if (from === 'form' && value === 'json') { this.props.dispatch(settingActions.switchToJson( this.props.form as FormSettings)); this.save(); } else if (from === 'json' && value === 'form') { - let b = window.confirm(DO_YOU_WANT_TO_CONTINUE); + const b = window.confirm(DO_YOU_WANT_TO_CONTINUE); if (!b) { this.forceUpdate(); return; @@ -191,7 +191,7 @@ class SettingsComponent extends React.Component<Props> { } save() { - let { source, json, form } = this.props.store.getState(); + const { source, json, form } = this.props.store.getState(); this.props.dispatch(settingActions.save( new SettingData({ source, json, form }), )); diff --git a/src/settings/components/ui/AddButton.tsx b/src/settings/components/ui/AddButton.tsx index 0577068..bb76d08 100644 --- a/src/settings/components/ui/AddButton.tsx +++ b/src/settings/components/ui/AddButton.tsx @@ -1,8 +1,7 @@ import './AddButton.scss'; import React from 'react'; -interface Props extends React.AllHTMLAttributes<HTMLInputElement> { -} +type Props = React.AllHTMLAttributes<HTMLInputElement>; class AddButton extends React.Component<Props> { render() { diff --git a/src/settings/components/ui/DeleteButton.tsx b/src/settings/components/ui/DeleteButton.tsx index f0ef6c9..e666426 100644 --- a/src/settings/components/ui/DeleteButton.tsx +++ b/src/settings/components/ui/DeleteButton.tsx @@ -1,8 +1,7 @@ import './DeleteButton.scss'; import React from 'react'; -interface Props extends React.AllHTMLAttributes<HTMLInputElement> { -} +type Props = React.AllHTMLAttributes<HTMLInputElement>; class DeleteButton extends React.Component<Props> { render() { diff --git a/src/settings/components/ui/Input.tsx b/src/settings/components/ui/Input.tsx index b7593b9..69c14b3 100644 --- a/src/settings/components/ui/Input.tsx +++ b/src/settings/components/ui/Input.tsx @@ -13,8 +13,8 @@ interface Props extends React.AllHTMLAttributes<HTMLElement> { class Input extends React.Component<Props> { renderText(props: Props) { - let inputClassName = props.error ? 'input-error' : ''; - let pp = { ...props }; + const inputClassName = props.error ? 'input-error' : ''; + const pp = { ...props }; delete pp.onValueChange; return <div className='settings-ui-input'> <label htmlFor={props.id}>{ props.label }</label> @@ -26,8 +26,8 @@ class Input extends React.Component<Props> { } renderRadio(props: Props) { - let inputClassName = props.error ? 'input-error' : ''; - let pp = { ...props }; + const inputClassName = props.error ? 'input-error' : ''; + const pp = { ...props }; delete pp.onValueChange; return <div className='settings-ui-input'> <label> @@ -41,8 +41,8 @@ class Input extends React.Component<Props> { } renderTextArea(props: Props) { - let inputClassName = props.error ? 'input-error' : ''; - let pp = { ...props }; + const inputClassName = props.error ? 'input-error' : ''; + const pp = { ...props }; delete pp.onValueChange; return <div className='settings-ui-input'> <label @@ -57,7 +57,7 @@ class Input extends React.Component<Props> { } render() { - let { type } = this.props; + const { type } = this.props; switch (this.props.type) { case 'text': diff --git a/src/settings/index.tsx b/src/settings/index.tsx index 6aec7a0..cde4488 100644 --- a/src/settings/index.tsx +++ b/src/settings/index.tsx @@ -12,7 +12,7 @@ const store = createStore( ); document.addEventListener('DOMContentLoaded', () => { - let wrapper = document.getElementById('vimvixen-settings'); + const wrapper = document.getElementById('vimvixen-settings'); ReactDOM.render( <Provider store={store}> <SettingsComponent store={store} /> diff --git a/src/settings/storage.ts b/src/settings/storage.ts index 2a983df..f375e58 100644 --- a/src/settings/storage.ts +++ b/src/settings/storage.ts @@ -1,7 +1,7 @@ import SettingData, { DefaultSettingData } from '../shared/SettingData'; export const load = async(): Promise<SettingData> => { - let { settings } = await browser.storage.local.get('settings'); + const { settings } = await browser.storage.local.get('settings'); if (!settings) { return DefaultSettingData; } diff --git a/src/shared/SettingData.ts b/src/shared/SettingData.ts index 532570e..5ad360e 100644 --- a/src/shared/SettingData.ts +++ b/src/shared/SettingData.ts @@ -13,14 +13,14 @@ export class FormKeymaps { } toKeymaps(): Keymaps { - let keymaps: { [key: string]: operations.Operation } = {}; - for (let name of Object.keys(this.data)) { - let [type, argStr] = name.split('?'); + const keymaps: { [key: string]: operations.Operation } = {}; + for (const name of Object.keys(this.data)) { + const [type, argStr] = name.split('?'); let args = {}; if (argStr) { args = JSON.parse(argStr); } - let key = this.data[name]; + const key = this.data[name]; keymaps[key] = operations.valueOf({ type, ...args }); } return Keymaps.fromJSON(keymaps); @@ -31,7 +31,7 @@ export class FormKeymaps { } buildWithOverride(op: string, keys: string): FormKeymaps { - let newData = { + const newData = { ...this.data, [op]: keys, }; @@ -39,19 +39,19 @@ export class FormKeymaps { } static fromJSON(o: ReturnType<FormKeymaps['toJSON']>): FormKeymaps { - let data: {[op: string]: string} = {}; - for (let op of Object.keys(o)) { + const data: {[op: string]: string} = {}; + for (const op of Object.keys(o)) { data[op] = o[op] as string; } return new FormKeymaps(data); } static fromKeymaps(keymaps: Keymaps): FormKeymaps { - let json = keymaps.toJSON(); - let data: {[op: string]: string} = {}; - for (let key of Object.keys(json)) { - let op = json[key]; - let args = { ...op }; + const json = keymaps.toJSON(); + const data: {[op: string]: string} = {}; + for (const key of Object.keys(json)) { + const op = json[key]; + const args = { ...op }; delete args.type; let name = op.type; @@ -75,8 +75,8 @@ export class FormSearch { } toSearchSettings(): Search { - let engines: { [name: string]: string } = {}; - for (let entry of this.engines) { + const engines: { [name: string]: string } = {}; + for (const entry of this.engines) { engines[entry[0]] = entry[1]; } return new Search(this.default, engines); @@ -103,7 +103,7 @@ export class FormSearch { } static fromSearch(search: Search): FormSearch { - let engines = Object.entries(search.engines).reduce( + const engines = Object.entries(search.engines).reduce( (o: string[][], [name, url]) => { return o.concat([[name, url]]); }, []); @@ -130,7 +130,7 @@ export class JSONTextSettings { } static fromSettings(data: Settings): JSONTextSettings { - let json = { + const json = { keymaps: data.keymaps.toJSON(), search: data.search, properties: data.properties, @@ -221,7 +221,7 @@ export class FormSettings { } static fromJSON(o: ReturnType<FormSettings['toJSON']>): FormSettings { - for (let name of ['keymaps', 'search', 'properties', 'blacklist']) { + for (const name of ['keymaps', 'search', 'properties', 'blacklist']) { if (!Object.prototype.hasOwnProperty.call(o, name)) { throw new Error(`"${name}" field not set`); } diff --git a/src/shared/messages.ts b/src/shared/messages.ts index 36a23d8..7f8bd5b 100644 --- a/src/shared/messages.ts +++ b/src/shared/messages.ts @@ -49,6 +49,7 @@ export const NAVIGATE_LINK_PREV = 'navigate.link.prev'; export interface BackgroundOperationMessage { type: typeof BACKGROUND_OPERATION; + repeat: number; operation: operations.Operation; } diff --git a/src/shared/operations.ts b/src/shared/operations.ts index 1ce5256..beca7b9 100644 --- a/src/shared/operations.ts +++ b/src/shared/operations.ts @@ -376,7 +376,7 @@ const assertOptionalBoolean = (obj: any, name: string) => { const assertOptionalString = (obj: any, name: string, values?: string[]) => { if (Object.prototype.hasOwnProperty.call(obj, name)) { - let value = obj[name]; + const value = obj[name]; if (typeof value !== 'string') { throw new TypeError( `Not a string parameter: '${name}' (${typeof value})`, @@ -508,3 +508,29 @@ export const valueOf = (o: any): Operation => { } throw new TypeError('Unknown operation type: ' + o.type); }; + +export const isNRepeatable = (type: string): boolean => { + switch (type) { + case SCROLL_VERTICALLY: + case SCROLL_HORIZONALLY: + case SCROLL_PAGES: + case NAVIGATE_HISTORY_PREV: + case NAVIGATE_HISTORY_NEXT: + case NAVIGATE_PARENT: + case TAB_CLOSE: + case TAB_CLOSE_FORCE: + case TAB_CLOSE_RIGHT: + case TAB_REOPEN: + case TAB_PREV: + case TAB_NEXT: + case TAB_DUPLICATE: + case ZOOM_IN: + case ZOOM_OUT: + case URLS_PASTE: + case FIND_NEXT: + case FIND_PREV: + case REPEAT_LAST: + return true; + } + return false; +}; diff --git a/src/shared/settings/Blacklist.ts b/src/shared/settings/Blacklist.ts index 0cfbd71..6e6b51c 100644 --- a/src/shared/settings/Blacklist.ts +++ b/src/shared/settings/Blacklist.ts @@ -8,22 +8,10 @@ export type BlacklistItemJSON = string | { export type BlacklistJSON = BlacklistItemJSON[]; const regexFromWildcard = (pattern: string): RegExp => { - let regexStr = '^' + pattern.replace(/\*/g, '.*') + '$'; + const regexStr = '^' + pattern.replace(/\*/g, '.*') + '$'; return new RegExp(regexStr); }; -const isArrayOfString = (raw: any): boolean => { - if (!Array.isArray(raw)) { - return false; - } - for (let x of Array.from(raw)) { - if (typeof x !== 'string') { - return false; - } - } - return true; -}; - export class BlacklistItem { public readonly pattern: string; @@ -47,30 +35,10 @@ export class BlacklistItem { this.keyEntities = this.keys.map(Key.fromMapKey); } - static fromJSON(raw: any): BlacklistItem { - if (typeof raw === 'string') { - return new BlacklistItem(raw, false, []); - } else if (typeof raw === 'object' && raw !== null) { - if (!('url' in raw)) { - throw new TypeError( - `missing field "url" of blacklist item: ${JSON.stringify(raw)}`); - } - if (typeof raw.url !== 'string') { - throw new TypeError( - `invalid field "url" of blacklist item: ${JSON.stringify(raw)}`); - } - if (!('keys' in raw)) { - throw new TypeError( - `missing field "keys" of blacklist item: ${JSON.stringify(raw)}`); - } - if (!isArrayOfString(raw.keys)) { - throw new TypeError( - `invalid field "keys" of blacklist item: ${JSON.stringify(raw)}`); - } - return new BlacklistItem(raw.url as string, true, raw.keys as string[]); - } - throw new TypeError( - `invalid format of blacklist item: ${JSON.stringify(raw)}`); + static fromJSON(json: BlacklistItemJSON): BlacklistItem { + return typeof json === 'string' + ? new BlacklistItem(json, false, []) + : new BlacklistItem(json.url, true, json.keys); } toJSON(): BlacklistItemJSON { @@ -103,11 +71,8 @@ export default class Blacklist { ) { } - static fromJSON(json: any): Blacklist { - if (!Array.isArray(json)) { - throw new TypeError('blacklist is not an array: ' + JSON.stringify(json)); - } - let items = Array.from(json).map(item => BlacklistItem.fromJSON(item)); + static fromJSON(json: BlacklistJSON): Blacklist { + const items = json.map(o => BlacklistItem.fromJSON(o)); return new Blacklist(items); } diff --git a/src/shared/settings/Key.ts b/src/shared/settings/Key.ts index b11eeb2..cfe1e7e 100644 --- a/src/shared/settings/Key.ts +++ b/src/shared/settings/Key.ts @@ -1,3 +1,5 @@ +const digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + export default class Key { public readonly key: string; @@ -9,12 +11,18 @@ export default class Key { public readonly meta: boolean; - constructor({ key, shift, ctrl, alt, meta }: { + constructor({ + key, + shift = false, + ctrl = false, + alt = false, + meta = false, + }: { key: string; - shift: boolean; - ctrl: boolean; - alt: boolean; - meta: boolean; + shift?: boolean; + ctrl?: boolean; + alt?: boolean; + meta?: boolean; }) { this.key = key; this.shift = shift; @@ -25,8 +33,8 @@ export default class Key { static fromMapKey(str: string): Key { if (str.startsWith('<') && str.endsWith('>')) { - let inner = str.slice(1, -1); - let shift = inner.includes('S-'); + const inner = str.slice(1, -1); + const shift = inner.includes('S-'); let base = inner.slice(inner.lastIndexOf('-') + 1); if (shift && base.length === 1) { base = base.toUpperCase(); @@ -51,6 +59,10 @@ export default class Key { }); } + isDigit(): boolean { + return digits.includes(this.key); + } + equals(key: Key) { return this.key === key.key && this.ctrl === key.ctrl && diff --git a/src/shared/settings/KeySequence.ts b/src/shared/settings/KeySequence.ts deleted file mode 100644 index abae61a..0000000 --- a/src/shared/settings/KeySequence.ts +++ /dev/null @@ -1,54 +0,0 @@ -import Key from './Key'; - -export default class KeySequence { - constructor( - public readonly keys: Key[], - ) { - } - - push(key: Key): number { - return this.keys.push(key); - } - - length(): number { - return this.keys.length; - } - - startsWith(o: KeySequence): boolean { - if (this.keys.length < o.keys.length) { - return false; - } - for (let i = 0; i < o.keys.length; ++i) { - if (!this.keys[i].equals(o.keys[i])) { - return false; - } - } - return true; - } - - static fromMapKeys(keys: string): KeySequence { - const fromMapKeysRecursive = ( - remaining: string, mappedKeys: Key[], - ): Key[] => { - if (remaining.length === 0) { - return mappedKeys; - } - - let nextPos = 1; - if (remaining.startsWith('<')) { - let ltPos = remaining.indexOf('>'); - if (ltPos > 0) { - nextPos = ltPos + 1; - } - } - - return fromMapKeysRecursive( - remaining.slice(nextPos), - mappedKeys.concat([Key.fromMapKey(remaining.slice(0, nextPos))]) - ); - }; - - let data = fromMapKeysRecursive(keys, []); - return new KeySequence(data); - } -} diff --git a/src/shared/settings/Keymaps.ts b/src/shared/settings/Keymaps.ts index a5558b0..3880654 100644 --- a/src/shared/settings/Keymaps.ts +++ b/src/shared/settings/Keymaps.ts @@ -1,23 +1,25 @@ import * as operations from '../operations'; -export type KeymapsJSON = { [key: string]: operations.Operation }; +type OperationJson = { + type: string +} | { + type: string; + [prop: string]: string | number | boolean; +}; +export type KeymapsJSON = { [key: string]: OperationJson }; export default class Keymaps { constructor( - private readonly data: KeymapsJSON, + private readonly data: { [key: string]: operations.Operation }, ) { } - static fromJSON(json: any): Keymaps { - if (typeof json !== 'object' || json === null) { - throw new TypeError('invalid keymaps type: ' + JSON.stringify(json)); + static fromJSON(json: KeymapsJSON): Keymaps { + const entries: { [key: string]: operations.Operation } = {}; + for (const key of Object.keys(json)) { + entries[key] = operations.valueOf(json[key]); } - - let data: KeymapsJSON = {}; - for (let key of Object.keys(json)) { - data[key] = operations.valueOf(json[key]); - } - return new Keymaps(data); + return new Keymaps(entries); } combine(other: Keymaps): Keymaps { diff --git a/src/shared/settings/Properties.ts b/src/shared/settings/Properties.ts index 63ff991..27fb62e 100644 --- a/src/shared/settings/Properties.ts +++ b/src/shared/settings/Properties.ts @@ -1,3 +1,4 @@ + export type PropertiesJSON = { hintchars?: string; smoothscroll?: boolean; @@ -65,22 +66,7 @@ export default class Properties { this.complete = complete || defaultValues.complete; } - static fromJSON(json: any): Properties { - let defNames: Set<string> = new Set(defs.map(def => def.name)); - let unknownName = Object.keys(json).find(name => !defNames.has(name)); - if (unknownName) { - throw new TypeError(`Unknown property name: "${unknownName}"`); - } - - for (let def of defs) { - if (!Object.prototype.hasOwnProperty.call(json, def.name)) { - continue; - } - if (typeof json[def.name] !== def.type) { - throw new TypeError( - `property "${def.name}" is not ${def.type}`); - } - } + static fromJSON(json: PropertiesJSON): Properties { return new Properties(json); } diff --git a/src/shared/settings/Search.ts b/src/shared/settings/Search.ts index 4580236..7de03de 100644 --- a/src/shared/settings/Search.ts +++ b/src/shared/settings/Search.ts @@ -12,35 +12,23 @@ export default class Search { ) { } - static fromJSON(json: any): Search { - let defaultEngine = Search.getStringField(json, 'default'); - let engines = Search.getObjectField(json, 'engines'); - - for (let [name, url] of Object.entries(engines)) { - if ((/\s/).test(name)) { - throw new TypeError( - `While space in the search engine not allowed: "${name}"`); - } - if (typeof url !== 'string') { - throw new TypeError( - `Invalid type of value in filed "engines": ${JSON.stringify(json)}`); + static fromJSON(json: SearchJSON): Search { + for (const [name, url] of Object.entries(json.engines)) { + if (!(/^[a-zA-Z0-9]+$/).test(name)) { + throw new TypeError('Search engine\'s name must be [a-zA-Z0-9]+'); } - let matches = url.match(/{}/g); + const matches = url.match(/{}/g); if (matches === null) { throw new TypeError(`No {}-placeholders in URL of "${name}"`); } else if (matches.length > 1) { throw new TypeError(`Multiple {}-placeholders in URL of "${name}"`); } } - - if (!Object.keys(engines).includes(defaultEngine)) { - throw new TypeError(`Default engine "${defaultEngine}" not found`); + if (!Object.keys(json.engines).includes(json.default)) { + throw new TypeError(`Default engine "${json.default}" not found`); } - return new Search( - json.default as string, - json.engines, - ); + return new Search(json.default, json.engines); } toJSON(): SearchJSON { @@ -49,28 +37,4 @@ export default class Search { engines: this.engines, }; } - - private static getStringField(json: any, name: string): string { - if (!Object.prototype.hasOwnProperty.call(json, name)) { - throw new TypeError( - `missing field "${name}" on search: ${JSON.stringify(json)}`); - } - if (typeof json[name] !== 'string') { - throw new TypeError( - `invalid type of filed "${name}" on search: ${JSON.stringify(json)}`); - } - return json[name]; - } - - private static getObjectField(json: any, name: string): Object { - if (!Object.prototype.hasOwnProperty.call(json, name)) { - throw new TypeError( - `missing field "${name}" on search: ${JSON.stringify(json)}`); - } - if (typeof json[name] !== 'object' || json[name] === null) { - throw new TypeError( - `invalid type of filed "${name}" on search: ${JSON.stringify(json)}`); - } - return json[name]; - } } diff --git a/src/shared/settings/Settings.ts b/src/shared/settings/Settings.ts index 2c9e37f..add5389 100644 --- a/src/shared/settings/Settings.ts +++ b/src/shared/settings/Settings.ts @@ -1,13 +1,16 @@ +import Ajv from 'ajv'; + import Keymaps, { KeymapsJSON } from './Keymaps'; import Search, { SearchJSON } from './Search'; import Properties, { PropertiesJSON } from './Properties'; import Blacklist, { BlacklistJSON } from './Blacklist'; +import validate from './validate'; export type SettingsJSON = { - keymaps: KeymapsJSON, - search: SearchJSON, - properties: PropertiesJSON, - blacklist: BlacklistJSON, + keymaps?: KeymapsJSON, + search?: SearchJSON, + properties?: PropertiesJSON, + blacklist?: BlacklistJSON, }; export default class Settings { @@ -36,25 +39,30 @@ export default class Settings { this.blacklist = blacklist; } - static fromJSON(json: any): Settings { - let settings = { ...DefaultSetting }; - for (let key of Object.keys(json)) { - switch (key) { - case 'keymaps': - settings.keymaps = Keymaps.fromJSON(json.keymaps); - break; - case 'search': - settings.search = Search.fromJSON(json.search); - break; - case 'properties': - settings.properties = Properties.fromJSON(json.properties); - break; - case 'blacklist': - settings.blacklist = Blacklist.fromJSON(json.blacklist); - break; - default: - throw new TypeError('unknown setting: ' + key); - } + static fromJSON(json: unknown): Settings { + const valid = validate(json); + if (!valid) { + const message = (validate as any).errors!! + .map((err: Ajv.ErrorObject) => { + return `'${err.dataPath}' ${err.message}`; + }) + .join('; '); + throw new TypeError(message); + } + + const obj = json as SettingsJSON; + const settings = { ...DefaultSetting }; + if (obj.keymaps) { + settings.keymaps = Keymaps.fromJSON(obj.keymaps); + } + if (obj.search) { + settings.search = Search.fromJSON(obj.search); + } + if (obj.properties) { + settings.properties = Properties.fromJSON(obj.properties); + } + if (obj.blacklist) { + settings.blacklist = Blacklist.fromJSON(obj.blacklist); } return new Settings(settings); } diff --git a/src/shared/settings/schema.json b/src/shared/settings/schema.json new file mode 100644 index 0000000..31d47f1 --- /dev/null +++ b/src/shared/settings/schema.json @@ -0,0 +1,84 @@ +{ + "type": "object", + "properties": { + "keymaps": { + "type": "object", + "patternProperties": { + ".*": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": [ + "type" + ] + } + } + }, + "search": { + "type": "object", + "properties": { + "default": { + "type": "string" + }, + "engines": { + "type": "object", + "patternProperties": { + ".*": { + "type": "string" + } + } + } + }, + "required": [ + "default", + "engines" + ] + }, + "properties": { + "type": "object", + "properties": { + "hintchars": { + "type": "string" + }, + "smoothscroll": { + "type": "boolean" + }, + "complete": { + "type": "string" + } + } + }, + "blacklist": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "keys": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "url", + "keys" + ] + } + ] + } + } + }, + "additionalProperties": false +} diff --git a/src/shared/settings/validate.js b/src/shared/settings/validate.js new file mode 100644 index 0000000..236488d --- /dev/null +++ b/src/shared/settings/validate.js @@ -0,0 +1,567 @@ +'use strict'; +var validate = (function() { + var pattern0 = new RegExp('.*'); + var refVal = []; + return function validate(data, dataPath, parentData, parentDataProperty, rootData) { + 'use strict'; + var vErrors = null; + var errors = 0; + if ((data && typeof data === "object" && !Array.isArray(data))) { + var errs__0 = errors; + var valid1 = true; + for (var key0 in data) { + var isAdditional0 = !(false || key0 == 'keymaps' || key0 == 'search' || key0 == 'properties' || key0 == 'blacklist'); + if (isAdditional0) { + valid1 = false; + validate.errors = [{ + keyword: 'additionalProperties', + dataPath: (dataPath || '') + "", + schemaPath: '#/additionalProperties', + params: { + additionalProperty: '' + key0 + '' + }, + message: 'should NOT have additional properties' + }]; + return false; + break; + } + } + if (valid1) { + var data1 = data.keymaps; + if (data1 === undefined) { + valid1 = true; + } else { + var errs_1 = errors; + if ((data1 && typeof data1 === "object" && !Array.isArray(data1))) { + var errs__1 = errors; + var valid2 = true; + for (var key1 in data1) { + if (pattern0.test(key1)) { + var data2 = data1[key1]; + var errs_2 = errors; + if ((data2 && typeof data2 === "object" && !Array.isArray(data2))) { + if (true) { + var errs__2 = errors; + var valid3 = true; + if (data2.type === undefined) { + valid3 = false; + validate.errors = [{ + keyword: 'required', + dataPath: (dataPath || '') + '.keymaps[\'' + key1 + '\']', + schemaPath: '#/properties/keymaps/patternProperties/.*/required', + params: { + missingProperty: 'type' + }, + message: 'should have required property \'type\'' + }]; + return false; + } else { + var errs_3 = errors; + if (typeof data2.type !== "string") { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.keymaps[\'' + key1 + '\'].type', + schemaPath: '#/properties/keymaps/patternProperties/.*/properties/type/type', + params: { + type: 'string' + }, + message: 'should be string' + }]; + return false; + } + var valid3 = errors === errs_3; + } + } + } else { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.keymaps[\'' + key1 + '\']', + schemaPath: '#/properties/keymaps/patternProperties/.*/type', + params: { + type: 'object' + }, + message: 'should be object' + }]; + return false; + } + var valid2 = errors === errs_2; + if (!valid2) break; + } else valid2 = true; + } + } else { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.keymaps', + schemaPath: '#/properties/keymaps/type', + params: { + type: 'object' + }, + message: 'should be object' + }]; + return false; + } + var valid1 = errors === errs_1; + } + if (valid1) { + var data1 = data.search; + if (data1 === undefined) { + valid1 = true; + } else { + var errs_1 = errors; + if ((data1 && typeof data1 === "object" && !Array.isArray(data1))) { + if (true) { + var errs__1 = errors; + var valid2 = true; + if (data1.default === undefined) { + valid2 = false; + validate.errors = [{ + keyword: 'required', + dataPath: (dataPath || '') + '.search', + schemaPath: '#/properties/search/required', + params: { + missingProperty: 'default' + }, + message: 'should have required property \'default\'' + }]; + return false; + } else { + var errs_2 = errors; + if (typeof data1.default !== "string") { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.search.default', + schemaPath: '#/properties/search/properties/default/type', + params: { + type: 'string' + }, + message: 'should be string' + }]; + return false; + } + var valid2 = errors === errs_2; + } + if (valid2) { + var data2 = data1.engines; + if (data2 === undefined) { + valid2 = false; + validate.errors = [{ + keyword: 'required', + dataPath: (dataPath || '') + '.search', + schemaPath: '#/properties/search/required', + params: { + missingProperty: 'engines' + }, + message: 'should have required property \'engines\'' + }]; + return false; + } else { + var errs_2 = errors; + if ((data2 && typeof data2 === "object" && !Array.isArray(data2))) { + var errs__2 = errors; + var valid3 = true; + for (var key2 in data2) { + if (pattern0.test(key2)) { + var errs_3 = errors; + if (typeof data2[key2] !== "string") { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.search.engines[\'' + key2 + '\']', + schemaPath: '#/properties/search/properties/engines/patternProperties/.*/type', + params: { + type: 'string' + }, + message: 'should be string' + }]; + return false; + } + var valid3 = errors === errs_3; + if (!valid3) break; + } else valid3 = true; + } + } else { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.search.engines', + schemaPath: '#/properties/search/properties/engines/type', + params: { + type: 'object' + }, + message: 'should be object' + }]; + return false; + } + var valid2 = errors === errs_2; + } + } + } + } else { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.search', + schemaPath: '#/properties/search/type', + params: { + type: 'object' + }, + message: 'should be object' + }]; + return false; + } + var valid1 = errors === errs_1; + } + if (valid1) { + var data1 = data.properties; + if (data1 === undefined) { + valid1 = true; + } else { + var errs_1 = errors; + if ((data1 && typeof data1 === "object" && !Array.isArray(data1))) { + var errs__1 = errors; + var valid2 = true; + if (data1.hintchars === undefined) { + valid2 = true; + } else { + var errs_2 = errors; + if (typeof data1.hintchars !== "string") { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.properties.hintchars', + schemaPath: '#/properties/properties/properties/hintchars/type', + params: { + type: 'string' + }, + message: 'should be string' + }]; + return false; + } + var valid2 = errors === errs_2; + } + if (valid2) { + if (data1.smoothscroll === undefined) { + valid2 = true; + } else { + var errs_2 = errors; + if (typeof data1.smoothscroll !== "boolean") { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.properties.smoothscroll', + schemaPath: '#/properties/properties/properties/smoothscroll/type', + params: { + type: 'boolean' + }, + message: 'should be boolean' + }]; + return false; + } + var valid2 = errors === errs_2; + } + if (valid2) { + if (data1.complete === undefined) { + valid2 = true; + } else { + var errs_2 = errors; + if (typeof data1.complete !== "string") { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.properties.complete', + schemaPath: '#/properties/properties/properties/complete/type', + params: { + type: 'string' + }, + message: 'should be string' + }]; + return false; + } + var valid2 = errors === errs_2; + } + } + } + } else { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.properties', + schemaPath: '#/properties/properties/type', + params: { + type: 'object' + }, + message: 'should be object' + }]; + return false; + } + var valid1 = errors === errs_1; + } + if (valid1) { + var data1 = data.blacklist; + if (data1 === undefined) { + valid1 = true; + } else { + var errs_1 = errors; + if (Array.isArray(data1)) { + var errs__1 = errors; + var valid1; + for (var i1 = 0; i1 < data1.length; i1++) { + var data2 = data1[i1]; + var errs_2 = errors; + var errs__2 = errors; + var valid2 = false; + var errs_3 = errors; + if (typeof data2 !== "string") { + var err = { + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', + schemaPath: '#/properties/blacklist/items/anyOf/0/type', + params: { + type: 'string' + }, + message: 'should be string' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } + var valid3 = errors === errs_3; + valid2 = valid2 || valid3; + if (!valid2) { + var errs_3 = errors; + if ((data2 && typeof data2 === "object" && !Array.isArray(data2))) { + if (true) { + var errs__3 = errors; + var valid4 = true; + if (data2.url === undefined) { + valid4 = false; + var err = { + keyword: 'required', + dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', + schemaPath: '#/properties/blacklist/items/anyOf/1/required', + params: { + missingProperty: 'url' + }, + message: 'should have required property \'url\'' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } else { + var errs_4 = errors; + if (typeof data2.url !== "string") { + var err = { + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist[' + i1 + '].url', + schemaPath: '#/properties/blacklist/items/anyOf/1/properties/url/type', + params: { + type: 'string' + }, + message: 'should be string' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } + var valid4 = errors === errs_4; + } + if (valid4) { + var data3 = data2.keys; + if (data3 === undefined) { + valid4 = false; + var err = { + keyword: 'required', + dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', + schemaPath: '#/properties/blacklist/items/anyOf/1/required', + params: { + missingProperty: 'keys' + }, + message: 'should have required property \'keys\'' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } else { + var errs_4 = errors; + if (Array.isArray(data3)) { + var errs__4 = errors; + var valid4; + for (var i4 = 0; i4 < data3.length; i4++) { + var errs_5 = errors; + if (typeof data3[i4] !== "string") { + var err = { + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist[' + i1 + '].keys[' + i4 + ']', + schemaPath: '#/properties/blacklist/items/anyOf/1/properties/keys/items/type', + params: { + type: 'string' + }, + message: 'should be string' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } + var valid5 = errors === errs_5; + if (!valid5) break; + } + } else { + var err = { + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist[' + i1 + '].keys', + schemaPath: '#/properties/blacklist/items/anyOf/1/properties/keys/type', + params: { + type: 'array' + }, + message: 'should be array' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } + var valid4 = errors === errs_4; + } + } + } + } else { + var err = { + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', + schemaPath: '#/properties/blacklist/items/anyOf/1/type', + params: { + type: 'object' + }, + message: 'should be object' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + } + var valid3 = errors === errs_3; + valid2 = valid2 || valid3; + } + if (!valid2) { + var err = { + keyword: 'anyOf', + dataPath: (dataPath || '') + '.blacklist[' + i1 + ']', + schemaPath: '#/properties/blacklist/items/anyOf', + params: {}, + message: 'should match some schema in anyOf' + }; + if (vErrors === null) vErrors = [err]; + else vErrors.push(err); + errors++; + validate.errors = vErrors; + return false; + } else { + errors = errs__2; + if (vErrors !== null) { + if (errs__2) vErrors.length = errs__2; + else vErrors = null; + } + } + var valid2 = errors === errs_2; + if (!valid2) break; + } + } else { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + '.blacklist', + schemaPath: '#/properties/blacklist/type', + params: { + type: 'array' + }, + message: 'should be array' + }]; + return false; + } + var valid1 = errors === errs_1; + } + } + } + } + } + } else { + validate.errors = [{ + keyword: 'type', + dataPath: (dataPath || '') + "", + schemaPath: '#/type', + params: { + type: 'object' + }, + message: 'should be object' + }]; + return false; + } + validate.errors = vErrors; + return errors === 0; + }; +})(); +validate.schema = { + "type": "object", + "properties": { + "keymaps": { + "type": "object", + "patternProperties": { + ".*": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": ["type"] + } + } + }, + "search": { + "type": "object", + "properties": { + "default": { + "type": "string" + }, + "engines": { + "type": "object", + "patternProperties": { + ".*": { + "type": "string" + } + } + } + }, + "required": ["default", "engines"] + }, + "properties": { + "type": "object", + "properties": { + "hintchars": { + "type": "string" + }, + "smoothscroll": { + "type": "boolean" + }, + "complete": { + "type": "string" + } + } + }, + "blacklist": { + "type": "array", + "items": { + "anyOf": [{ + "type": "string" + }, { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "keys": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["url", "keys"] + }] + } + } + }, + "additionalProperties": false +}; +validate.errors = null; +module.exports = validate;
\ No newline at end of file diff --git a/src/shared/urls.ts b/src/shared/urls.ts index 64ea4f2..bac929e 100644 --- a/src/shared/urls.ts +++ b/src/shared/urls.ts @@ -7,22 +7,47 @@ const trimStart = (str: string): string => { const SUPPORTED_PROTOCOLS = ['http:', 'https:', 'ftp:', 'mailto:', 'about:']; +const isLocalhost = (url: string): boolean => { + if (url === 'localhost') { + return true; + } + + const [host, port] = url.split(':', 2); + return host === 'localhost' && !isNaN(Number(port)); +}; + +const isMissingHttp = (keywords: string): boolean => { + if (keywords.includes('.') && !keywords.includes(' ')) { + return true; + } + + try { + const u = new URL('http://' + keywords); + return isLocalhost(u.host); + } catch (e) { + // fallthrough + } + return false; +}; + const searchUrl = (keywords: string, search: Search): string => { try { - let u = new URL(keywords); + const u = new URL(keywords); if (SUPPORTED_PROTOCOLS.includes(u.protocol.toLowerCase())) { return u.href; } } catch (e) { // fallthrough } - if (keywords.includes('.') && !keywords.includes(' ')) { + + if (isMissingHttp(keywords)) { return 'http://' + keywords; } + let template = search.engines[search.defaultEngine]; let query = keywords; - let first = trimStart(keywords).split(' ')[0]; + const first = trimStart(keywords).split(' ')[0]; if (Object.keys(search.engines).includes(first)) { template = search.engines[first]; query = trimStart(trimStart(keywords).slice(first.length)); @@ -32,7 +57,7 @@ const searchUrl = (keywords: string, search: Search): string => { const normalizeUrl = (url: string): string => { try { - let u = new URL(url); + const u = new URL(url); if (SUPPORTED_PROTOCOLS.includes(u.protocol.toLowerCase())) { return u.href; } diff --git a/src/shared/utils/dom.ts b/src/shared/utils/dom.ts index c1f2190..a6186cb 100644 --- a/src/shared/utils/dom.ts +++ b/src/shared/utils/dom.ts @@ -1,5 +1,5 @@ const isContentEditable = (element: Element): boolean => { - let value = element.getAttribute('contenteditable'); + const value = element.getAttribute('contenteditable'); if (value === null) { return false; } @@ -14,12 +14,12 @@ interface Rect { } const rectangleCoordsRect = (coords: string): Rect => { - let [left, top, right, bottom] = coords.split(',').map(n => Number(n)); + const [left, top, right, bottom] = coords.split(',').map(n => Number(n)); return { left, top, right, bottom }; }; const circleCoordsRect = (coords: string): Rect => { - let [x, y, r] = coords.split(',').map(n => Number(n)); + const [x, y, r] = coords.split(',').map(n => Number(n)); return { left: x - r, top: y - r, @@ -29,14 +29,14 @@ const circleCoordsRect = (coords: string): Rect => { }; const polygonCoordsRect = (coords: string): Rect => { - let params = coords.split(','); + const params = coords.split(','); let minx = Number(params[0]), maxx = Number(params[0]), miny = Number(params[1]), maxy = Number(params[1]); - let len = Math.floor(params.length / 2); + const len = Math.floor(params.length / 2); for (let i = 2; i < len; i += 2) { - let x = Number(params[i]), + const x = Number(params[i]), y = Number(params[i + 1]); if (x < minx) { minx = x; @@ -59,15 +59,15 @@ const viewportRect = (e: Element): Rect => { return e.getBoundingClientRect(); } - let mapElement = e.parentNode as HTMLMapElement; - let imgElement = document.querySelector( + const mapElement = e.parentNode as HTMLMapElement; + const imgElement = document.querySelector( `img[usemap="#${mapElement.name}"]` ) as HTMLImageElement; - let { + const { left: mapLeft, top: mapTop } = imgElement.getBoundingClientRect(); - let coords = e.getAttribute('coords'); + const coords = e.getAttribute('coords'); if (!coords) { return e.getBoundingClientRect(); } @@ -96,8 +96,8 @@ const viewportRect = (e: Element): Rect => { }; const isVisible = (element: Element): boolean => { - let rect = element.getBoundingClientRect(); - let style = window.getComputedStyle(element); + const rect = element.getBoundingClientRect(); + const style = window.getComputedStyle(element); if (style.overflow !== 'visible' && (rect.width === 0 || rect.height === 0)) { return false; @@ -113,7 +113,7 @@ const isVisible = (element: Element): boolean => { return false; } - let { display, visibility } = window.getComputedStyle(element); + const { display, visibility } = window.getComputedStyle(element); if (display === 'none' || visibility === 'hidden') { return false; } diff --git a/test/background/infrastructures/MemoryStorage.test.ts b/test/background/infrastructures/MemoryStorage.test.ts index 95d3780..ccdf9f0 100644 --- a/test/background/infrastructures/MemoryStorage.test.ts +++ b/test/background/infrastructures/MemoryStorage.test.ts @@ -2,7 +2,7 @@ import MemoryStorage from 'background/infrastructures/MemoryStorage'; describe("background/infrastructures/memory-storage", () => { it('stores values', () => { - let cache = new MemoryStorage(); + const cache = new MemoryStorage(); cache.set('number', 123); expect(cache.get('number')).to.equal(123); @@ -14,7 +14,7 @@ describe("background/infrastructures/memory-storage", () => { }); it('returns undefined if no keys', () => { - let cache = new MemoryStorage(); + const cache = new MemoryStorage(); expect(cache.get('no-keys')).to.be.undefined; }) @@ -23,22 +23,22 @@ describe("background/infrastructures/memory-storage", () => { cache.set('red', 'apple'); cache = new MemoryStorage(); - let got = cache.get('red'); + const got = cache.get('red'); expect(got).to.equal('apple'); }); it('stored cloned objects', () => { - let cache = new MemoryStorage(); - let recipe = { sugar: '300g' }; + const cache = new MemoryStorage(); + const recipe = { sugar: '300g' }; cache.set('recipe', recipe); recipe.salt = '20g' - let got = cache.get('recipe', recipe); + const got = cache.get('recipe', recipe); expect(got).to.deep.equal({ sugar: '300g' }); }); it('throws an error with unserializable objects', () => { - let cache = new MemoryStorage(); + const cache = new MemoryStorage(); expect(() => cache.set('fn', setTimeout)).to.throw(); }) }); diff --git a/test/background/repositories/Mark.test.ts b/test/background/repositories/Mark.test.ts index 167e512..ed1a68e 100644 --- a/test/background/repositories/Mark.test.ts +++ b/test/background/repositories/Mark.test.ts @@ -1,15 +1,14 @@ import MarkRepository from 'background/repositories/MarkRepository'; -import GlobalMark from 'background/domains/GlobalMark'; describe('background/repositories/mark', () => { - let repository; + let repository: MarkRepository; beforeEach(() => { - repository = new MarkRepository; + repository = new MarkRepository(); }); it('get and set', async() => { - let mark = { tabId: 1, url: 'http://example.com', x: 10, y: 30 }; + const mark = { tabId: 1, url: 'http://example.com', x: 10, y: 30 }; repository.setMark('A', mark); diff --git a/test/background/usecases/NavigateUseCase.test.ts b/test/background/usecases/NavigateUseCase.test.ts index ecbf888..48a1c5b 100644 --- a/test/background/usecases/NavigateUseCase.test.ts +++ b/test/background/usecases/NavigateUseCase.test.ts @@ -16,10 +16,10 @@ describe('NavigateUseCase', () => { describe('#openParent()', async () => { it('opens parent directory of file', async() => { - var stub = sinon.stub(tabPresenter, 'getCurrent'); + const stub = sinon.stub(tabPresenter, 'getCurrent'); stub.returns(Promise.resolve({ url: 'https://google.com/fruits/yellow/banana' })) - var mock = sinon.mock(tabPresenter); + const mock = sinon.mock(tabPresenter); mock.expects('open').withArgs('https://google.com/fruits/yellow/'); await sut.openParent(); @@ -28,10 +28,10 @@ describe('NavigateUseCase', () => { }); it('opens parent directory of directory', async() => { - var stub = sinon.stub(tabPresenter, 'getCurrent'); + const stub = sinon.stub(tabPresenter, 'getCurrent'); stub.returns(Promise.resolve({ url: 'https://google.com/fruits/yellow/' })) - var mock = sinon.mock(tabPresenter); + const mock = sinon.mock(tabPresenter); mock.expects('open').withArgs('https://google.com/fruits/'); await sut.openParent(); @@ -40,10 +40,10 @@ describe('NavigateUseCase', () => { }); it('removes hash', async() => { - var stub = sinon.stub(tabPresenter, 'getCurrent'); + const stub = sinon.stub(tabPresenter, 'getCurrent'); stub.returns(Promise.resolve({ url: 'https://google.com/#top' })) - var mock = sinon.mock(tabPresenter); + const mock = sinon.mock(tabPresenter); mock.expects('open').withArgs('https://google.com/'); await sut.openParent(); @@ -52,10 +52,10 @@ describe('NavigateUseCase', () => { }); it('removes search query', async() => { - var stub = sinon.stub(tabPresenter, 'getCurrent'); + const stub = sinon.stub(tabPresenter, 'getCurrent'); stub.returns(Promise.resolve({ url: 'https://google.com/search?q=apple' })) - var mock = sinon.mock(tabPresenter); + const mock = sinon.mock(tabPresenter); mock.expects('open').withArgs('https://google.com/search'); await sut.openParent(); @@ -66,12 +66,12 @@ describe('NavigateUseCase', () => { describe('#openRoot()', () => { it('opens root direectory', async() => { - var stub = sinon.stub(tabPresenter, 'getCurrent'); + const stub = sinon.stub(tabPresenter, 'getCurrent'); stub.returns(Promise.resolve({ url: 'https://google.com/seach?q=apple', })) - var mock = sinon.mock(tabPresenter); + const mock = sinon.mock(tabPresenter); mock.expects('open').withArgs('https://google.com'); await sut.openRoot(); diff --git a/test/background/usecases/filters.test.ts b/test/background/usecases/filters.test.ts index bdfb0be..90541ff 100644 --- a/test/background/usecases/filters.test.ts +++ b/test/background/usecases/filters.test.ts @@ -3,15 +3,15 @@ import * as filters from 'background/usecases/filters'; describe("background/usecases/filters", () => { describe('filterHttp', () => { it('filters http URLs duplicates to https hosts', () => { - let pages = [ + const pages = [ { url: 'http://i-beam.org/foo' }, { url: 'https://i-beam.org/bar' }, { url: 'http://i-beam.net/hoge' }, { url: 'http://i-beam.net/fuga' }, ]; - let filtered = filters.filterHttp(pages); + const filtered = filters.filterHttp(pages); - let urls = filtered.map(x => x.url); + const urls = filtered.map(x => x.url); expect(urls).to.deep.equal([ 'https://i-beam.org/bar', 'http://i-beam.net/hoge', 'http://i-beam.net/fuga' ]); @@ -20,12 +20,12 @@ describe("background/usecases/filters", () => { describe('filterBlankTitle', () => { it('filters blank titles', () => { - let pages = [ + const pages = [ { title: 'hello' }, { title: '' }, {}, ]; - let filtered = filters.filterBlankTitle(pages); + const filtered = filters.filterBlankTitle(pages); expect(filtered).to.deep.equal([{ title: 'hello' }]); }); @@ -33,15 +33,15 @@ describe("background/usecases/filters", () => { describe('filterByTailingSlash', () => { it('filters duplicated pathname on tailing slash', () => { - let pages = [ + const pages = [ { url: 'http://i-beam.org/content' }, { url: 'http://i-beam.org/content/' }, { url: 'http://i-beam.org/search' }, { url: 'http://i-beam.org/search?q=apple_banana_cherry' }, ]; - let filtered = filters.filterByTailingSlash(pages); + const filtered = filters.filterByTailingSlash(pages); - let urls = filtered.map(x => x.url); + const urls = filtered.map(x => x.url); expect(urls).to.deep.equal([ 'http://i-beam.org/content', 'http://i-beam.org/search', @@ -52,7 +52,7 @@ describe("background/usecases/filters", () => { describe('filterByPathname', () => { it('remains items less than minimam length', () => { - let pages = [ + const pages = [ { url: 'http://i-beam.org/search?q=apple' }, { url: 'http://i-beam.org/search?q=apple_banana' }, { url: 'http://i-beam.org/search?q=apple_banana_cherry' }, @@ -60,12 +60,12 @@ describe("background/usecases/filters", () => { { url: 'http://i-beam.org/request?q=apple_banana' }, { url: 'http://i-beam.org/request?q=apple_banana_cherry' }, ]; - let filtered = filters.filterByPathname(pages, 10); + const filtered = filters.filterByPathname(pages, 10); expect(filtered).to.have.lengthOf(6); }); it('filters by length of pathname', () => { - let pages = [ + const pages = [ { url: 'http://i-beam.org/search?q=apple' }, { url: 'http://i-beam.org/search?q=apple_banana' }, { url: 'http://i-beam.org/search?q=apple_banana_cherry' }, @@ -73,7 +73,7 @@ describe("background/usecases/filters", () => { { url: 'http://i-beam.net/search?q=apple_banana' }, { url: 'http://i-beam.net/search?q=apple_banana_cherry' }, ]; - let filtered = filters.filterByPathname(pages, 0); + const filtered = filters.filterByPathname(pages, 0); expect(filtered).to.deep.equal([ { url: 'http://i-beam.org/search?q=apple' }, { url: 'http://i-beam.net/search?q=apple' }, @@ -83,7 +83,7 @@ describe("background/usecases/filters", () => { describe('filterByOrigin', () => { it('remains items less than minimam length', () => { - let pages = [ + const pages = [ { url: 'http://i-beam.org/search?q=apple' }, { url: 'http://i-beam.org/search?q=apple_banana' }, { url: 'http://i-beam.org/search?q=apple_banana_cherry' }, @@ -91,12 +91,12 @@ describe("background/usecases/filters", () => { { url: 'http://i-beam.org/request?q=apple_banana' }, { url: 'http://i-beam.org/request?q=apple_banana_cherry' }, ]; - let filtered = filters.filterByOrigin(pages, 10); + const filtered = filters.filterByOrigin(pages, 10); expect(filtered).to.have.lengthOf(6); }); it('filters by length of pathname', () => { - let pages = [ + const pages = [ { url: 'http://i-beam.org/search?q=apple' }, { url: 'http://i-beam.org/search?q=apple_banana' }, { url: 'http://i-beam.org/search?q=apple_banana_cherry' }, @@ -104,7 +104,7 @@ describe("background/usecases/filters", () => { { url: 'http://i-beam.org/request?q=apple_banana' }, { url: 'http://i-beam.org/request?q=apple_banana_cherry' }, ]; - let filtered = filters.filterByOrigin(pages, 0); + const filtered = filters.filterByOrigin(pages, 0); expect(filtered).to.deep.equal([ { url: 'http://i-beam.org/search?q=apple' }, ]); diff --git a/test/background/usecases/parsers.test.ts b/test/background/usecases/parsers.test.ts index f3a64eb..d08de0d 100644 --- a/test/background/usecases/parsers.test.ts +++ b/test/background/usecases/parsers.test.ts @@ -3,13 +3,13 @@ import * as parsers from 'background/usecases/parsers'; describe("shared/commands/parsers", () => { describe("#parsers.parseSetOption", () => { it('parse set string', () => { - let [key, value] = parsers.parseSetOption('hintchars=abcdefgh'); + const [key, value] = parsers.parseSetOption('hintchars=abcdefgh'); expect(key).to.equal('hintchars'); expect(value).to.equal('abcdefgh'); }); it('parse set empty string', () => { - let [key, value] = parsers.parseSetOption('hintchars='); + const [key, value] = parsers.parseSetOption('hintchars='); expect(key).to.equal('hintchars'); expect(value).to.equal(''); }); diff --git a/test/console/actions/console.test.ts b/test/console/actions/console.test.ts index e45d008..583c878 100644 --- a/test/console/actions/console.test.ts +++ b/test/console/actions/console.test.ts @@ -4,13 +4,13 @@ import * as consoleActions from 'console/actions/console'; describe("console actions", () => { describe('hide', () => { it('create CONSOLE_HIDE action', () => { - let action = consoleActions.hide(); + const action = consoleActions.hide(); expect(action.type).to.equal(actions.CONSOLE_HIDE); }); }); describe("showCommand", () => { it('create CONSOLE_SHOW_COMMAND action', () => { - let action = consoleActions.showCommand('hello'); + const action = consoleActions.showCommand('hello'); expect(action.type).to.equal(actions.CONSOLE_SHOW_COMMAND); expect(action.text).to.equal('hello'); }); @@ -18,14 +18,14 @@ describe("console actions", () => { describe("showFind", () => { it('create CONSOLE_SHOW_FIND action', () => { - let action = consoleActions.showFind(); + const action = consoleActions.showFind(); expect(action.type).to.equal(actions.CONSOLE_SHOW_FIND); }); }); describe("showError", () => { it('create CONSOLE_SHOW_ERROR action', () => { - let action = consoleActions.showError('an error'); + const action = consoleActions.showError('an error'); expect(action.type).to.equal(actions.CONSOLE_SHOW_ERROR); expect(action.text).to.equal('an error'); }); @@ -33,7 +33,7 @@ describe("console actions", () => { describe("showInfo", () => { it('create CONSOLE_SHOW_INFO action', () => { - let action = consoleActions.showInfo('an info'); + const action = consoleActions.showInfo('an info'); expect(action.type).to.equal(actions.CONSOLE_SHOW_INFO); expect(action.text).to.equal('an info'); }); @@ -41,14 +41,14 @@ describe("console actions", () => { describe("hideCommand", () => { it('create CONSOLE_HIDE_COMMAND action', () => { - let action = consoleActions.hideCommand(); + const action = consoleActions.hideCommand(); expect(action.type).to.equal(actions.CONSOLE_HIDE_COMMAND); }); }); describe('setConsoleText', () => { it('create CONSOLE_SET_CONSOLE_TEXT action', () => { - let action = consoleActions.setConsoleText('hello world'); + const action = consoleActions.setConsoleText('hello world'); expect(action.type).to.equal(actions.CONSOLE_SET_CONSOLE_TEXT); expect(action.consoleText).to.equal('hello world'); }); @@ -56,14 +56,14 @@ describe("console actions", () => { describe("completionPrev", () => { it('create CONSOLE_COMPLETION_PREV action', () => { - let action = consoleActions.completionPrev(); + const action = consoleActions.completionPrev(); expect(action.type).to.equal(actions.CONSOLE_COMPLETION_PREV); }); }); describe("completionNext", () => { it('create CONSOLE_COMPLETION_NEXT action', () => { - let action = consoleActions.completionNext(); + const action = consoleActions.completionNext(); expect(action.type).to.equal(actions.CONSOLE_COMPLETION_NEXT); }); }); diff --git a/test/console/components/console/Completion.test.tsx b/test/console/components/console/Completion.test.tsx index 16bf11a..e411c4a 100644 --- a/test/console/components/console/Completion.test.tsx +++ b/test/console/components/console/Completion.test.tsx @@ -3,7 +3,7 @@ import Completion from 'console/components/console/Completion' import ReactTestRenderer from 'react-test-renderer'; describe("console/components/console/completion", () => { - let completions = [{ + const completions = [{ name: "Fruit", items: [{ caption: "apple" }, { caption: "banana" }, { caption: "cherry" }], }, { @@ -12,14 +12,14 @@ describe("console/components/console/completion", () => { }]; it('renders Completion component', () => { - let root = ReactTestRenderer.create(<Completion + const root = ReactTestRenderer.create(<Completion completions={completions} size={30} />).root; expect(root.children).to.have.lengthOf(1); - let children = root.children[0].children; + const children = root.children[0].children; expect(children).to.have.lengthOf(8); expect(children[0].props.title).to.equal('Fruit'); expect(children[1].props.caption).to.equal('apple'); @@ -32,25 +32,25 @@ describe("console/components/console/completion", () => { }); it('highlight current item', () => { - let root = ReactTestRenderer.create(<Completion + const root = ReactTestRenderer.create(<Completion completions={completions} size={30} select={3} />).root; - let children = root.children[0].children; + const children = root.children[0].children; expect(children[5].props.highlight).to.be.true; }); it('does not highlight any items', () => { - let root = ReactTestRenderer.create(<Completion + const root = ReactTestRenderer.create(<Completion completions={completions} size={30} select={-1} />).root; - let children = root.children[0].children; - for (let li of children[0].children) { + const children = root.children[0].children; + for (const li of children[0].children) { expect(li.props.highlight).not.to.be.ok; } }); @@ -79,13 +79,12 @@ describe("console/components/console/completion", () => { }) it('scrolls up to down with select', () => { - let component = ReactTestRenderer.create(<Completion + const component = ReactTestRenderer.create(<Completion completions={completions} size={3} select={1} />); - let instance = component.getInstance(); - let root = component.root; + const root = component.root; let children = root.children[0].children; expect(children).to.have.lengthOf(3); @@ -121,13 +120,12 @@ describe("console/components/console/completion", () => { }); it('scrolls down to up with select', () => { - let component = ReactTestRenderer.create(<Completion + const component = ReactTestRenderer.create(<Completion completions={completions} size={3} select={5} />); - let root = component.root; - let instance = component.getInstance(); + const root = component.root; let children = root.children[0].children; expect(children).to.have.lengthOf(3); diff --git a/test/console/reducers/console.test.ts b/test/console/reducers/console.test.ts index 47e7daf..038e712 100644 --- a/test/console/reducers/console.test.ts +++ b/test/console/reducers/console.test.ts @@ -3,7 +3,7 @@ import reducer from 'console/reducers'; describe("console reducer", () => { it('return the initial state', () => { - let state = reducer(undefined, {}); + const state = reducer(undefined, {}); expect(state).to.have.property('mode', ''); expect(state).to.have.property('messageText', ''); expect(state).to.have.property('consoleText', ''); @@ -12,34 +12,34 @@ describe("console reducer", () => { }); it('return next state for CONSOLE_HIDE', () => { - let action = { type: actions.CONSOLE_HIDE }; - let state = reducer({ mode: 'error' }, action); + const action = { type: actions.CONSOLE_HIDE }; + const state = reducer({ mode: 'error' }, action); expect(state).to.have.property('mode', ''); }) it('return next state for CONSOLE_SHOW_COMMAND', () => { - let action = { type: actions.CONSOLE_SHOW_COMMAND, text: 'open ' }; - let state = reducer({}, action); + const action = { type: actions.CONSOLE_SHOW_COMMAND, text: 'open ' }; + const state = reducer({}, action); expect(state).to.have.property('mode', 'command'); expect(state).to.have.property('consoleText', 'open '); }); it('return next state for CONSOLE_SHOW_INFO', () => { - let action = { type: actions.CONSOLE_SHOW_INFO, text: 'an info' }; - let state = reducer({}, action); + const action = { type: actions.CONSOLE_SHOW_INFO, text: 'an info' }; + const state = reducer({}, action); expect(state).to.have.property('mode', 'info'); expect(state).to.have.property('messageText', 'an info'); }); it('return next state for CONSOLE_SHOW_ERROR', () => { - let action = { type: actions.CONSOLE_SHOW_ERROR, text: 'an error' }; - let state = reducer({}, action); + const action = { type: actions.CONSOLE_SHOW_ERROR, text: 'an error' }; + const state = reducer({}, action); expect(state).to.have.property('mode', 'error'); expect(state).to.have.property('messageText', 'an error'); }); it('return next state for CONSOLE_HIDE_COMMAND', () => { - let action = { type: actions.CONSOLE_HIDE_COMMAND }; + const action = { type: actions.CONSOLE_HIDE_COMMAND }; let state = reducer({ mode: 'command' }, action); expect(state).to.have.property('mode', ''); @@ -48,11 +48,11 @@ describe("console reducer", () => { }); it('return next state for CONSOLE_SET_CONSOLE_TEXT', () => { - let action = { + const action = { type: actions.CONSOLE_SET_CONSOLE_TEXT, consoleText: 'hello world' } - let state = reducer({}, action) + const state = reducer({}, action) expect(state).to.have.property('consoleText', 'hello world'); }); @@ -62,7 +62,7 @@ describe("console reducer", () => { select: 0, completions: [], } - let action = { + const action = { type: actions.CONSOLE_SET_COMPLETIONS, completions: [{ name: 'Apple', @@ -78,7 +78,7 @@ describe("console reducer", () => { }); it ('return next state for CONSOLE_COMPLETION_NEXT', () => { - let action = { type: actions.CONSOLE_COMPLETION_NEXT }; + const action = { type: actions.CONSOLE_COMPLETION_NEXT }; let state = { select: -1, completions: [{ @@ -104,7 +104,7 @@ describe("console reducer", () => { }); it ('return next state for CONSOLE_COMPLETION_PREV', () => { - let action = { type: actions.CONSOLE_COMPLETION_PREV }; + const action = { type: actions.CONSOLE_COMPLETION_PREV }; let state = { select: -1, completions: [{ diff --git a/test/content/InputDriver.test.ts b/test/content/InputDriver.test.ts index 441d107..d3a55dd 100644 --- a/test/content/InputDriver.test.ts +++ b/test/content/InputDriver.test.ts @@ -50,7 +50,7 @@ describe('InputDriver', () => { return true; }); - let events = [ + const events = [ new KeyboardEvent('keydown', { key: 'a' }), new KeyboardEvent('keydown', { key: 'b' }), new KeyboardEvent('keypress', { key: 'a' }), @@ -58,7 +58,7 @@ describe('InputDriver', () => { new KeyboardEvent('keypress', { key: 'b' }), new KeyboardEvent('keyup', { key: 'b' }), ]; - for (let e of events) { + for (const e of events) { target.dispatchEvent(e); } @@ -102,9 +102,9 @@ describe('InputDriver', () => { it('ignores events from input elements', () => { ['input', 'textarea', 'select'].forEach((name) => { - let input = window.document.createElement(name); - let driver = new InputDriver(input); - driver.onKey((key: Key): boolean => { + const input = window.document.createElement(name); + const driver = new InputDriver(input); + driver.onKey((_key: Key): boolean => { expect.fail(); return false; }); @@ -113,8 +113,8 @@ describe('InputDriver', () => { }); it('ignores events from contenteditable elements', () => { - let div = window.document.createElement('div'); - let driver = new InputDriver(div); + const div = window.document.createElement('div'); + const driver = new InputDriver(div); driver.onKey((_key: Key): boolean => { expect.fail(); return false; @@ -130,7 +130,7 @@ describe('InputDriver', () => { describe("#keyFromKeyboardEvent", () => { it('returns from keyboard input Ctrl+X', () => { - let k = keyFromKeyboardEvent(new KeyboardEvent('keydown', { + const k = keyFromKeyboardEvent(new KeyboardEvent('keydown', { key: 'x', shiftKey: false, ctrlKey: true, altKey: false, metaKey: true, })); expect(k.key).to.equal('x'); @@ -141,7 +141,7 @@ describe("#keyFromKeyboardEvent", () => { }); it('returns from keyboard input Shift+Esc', () => { - let k = keyFromKeyboardEvent(new KeyboardEvent('keydown', { + const k = keyFromKeyboardEvent(new KeyboardEvent('keydown', { key: 'Escape', shiftKey: true, ctrlKey: false, altKey: false, metaKey: true })); expect(k.key).to.equal('Esc'); @@ -153,7 +153,7 @@ describe("#keyFromKeyboardEvent", () => { it('returns from keyboard input Ctrl+$', () => { // $ required shift pressing on most keyboards - let k = keyFromKeyboardEvent(new KeyboardEvent('keydown', { + const k = keyFromKeyboardEvent(new KeyboardEvent('keydown', { key: '$', shiftKey: true, ctrlKey: true, altKey: false, metaKey: false })); expect(k.key).to.equal('$'); @@ -164,7 +164,7 @@ describe("#keyFromKeyboardEvent", () => { }); it('returns from keyboard input Crtl+Space', () => { - let k = keyFromKeyboardEvent(new KeyboardEvent('keydown', { + const k = keyFromKeyboardEvent(new KeyboardEvent('keydown', { key: ' ', shiftKey: false, ctrlKey: true, altKey: false, metaKey: false })); expect(k.key).to.equal('Space'); diff --git a/test/content/domains/KeySequence.test.ts b/test/content/domains/KeySequence.test.ts new file mode 100644 index 0000000..5df5217 --- /dev/null +++ b/test/content/domains/KeySequence.test.ts @@ -0,0 +1,166 @@ +import KeySequence from '../../../src/content/domains/KeySequence'; +import { expect } from 'chai' +import Key from "../../../src/shared/settings/Key"; + +describe("KeySequence", () => { + describe('#push', () => { + it('append a key to the sequence', () => { + const seq = new KeySequence([]); + seq.push(Key.fromMapKey('g')); + seq.push(Key.fromMapKey('<S-U>')); + + expect(seq.keys[0].key).to.equal('g'); + expect(seq.keys[1].key).to.equal('U'); + expect(seq.keys[1].shift).to.be.true; + }) + }); + + describe('#startsWith', () => { + it('returns true if the key sequence starts with param', () => { + const seq = new KeySequence([ + Key.fromMapKey('g'), + Key.fromMapKey('<S-U>'), + ]); + + expect(seq.startsWith(new KeySequence([ + ]))).to.be.true; + expect(seq.startsWith(new KeySequence([ + Key.fromMapKey('g'), + ]))).to.be.true; + expect(seq.startsWith(new KeySequence([ + Key.fromMapKey('g'), Key.fromMapKey('<S-U>'), + ]))).to.be.true; + expect(seq.startsWith(new KeySequence([ + Key.fromMapKey('g'), Key.fromMapKey('<S-U>'), Key.fromMapKey('x'), + ]))).to.be.false; + expect(seq.startsWith(new KeySequence([ + Key.fromMapKey('h'), + ]))).to.be.false; + }); + + it('returns true if the empty sequence starts with an empty sequence', () => { + const seq = new KeySequence([]); + + expect(seq.startsWith(new KeySequence([]))).to.be.true; + expect(seq.startsWith(new KeySequence([ + Key.fromMapKey('h'), + ]))).to.be.false; + }) + }); + + describe('#isDigitOnly', () => { + it('returns true the keys are only digits', () => { + expect(new KeySequence([ + new Key({ key: '4' }), + new Key({ key: '0' }), + ]).isDigitOnly()).to.be.true; + expect(new KeySequence([ + new Key({ key: '4' }), + new Key({ key: '0' }), + new Key({ key: 'z' }), + ]).isDigitOnly()).to.be.false; + }) + }); + + describe('#repeatCount', () => { + it('returns repeat count with a numeric prefix', () => { + let seq = new KeySequence([ + new Key({ key: '1' }), new Key({ key: '0' }) , + new Key({ key: 'g' }), new Key({ key: 'g' }) , + ]); + expect(seq.repeatCount()).to.equal(10); + + seq = new KeySequence([ + new Key({ key: '0' }), new Key({ key: '5' }) , + new Key({ key: 'g' }), new Key({ key: 'g' }) , + ]); + expect(seq.repeatCount()).to.equal(5); + }); + + it('returns 1 if no numeric prefix', () => { + let seq = new KeySequence([ + new Key({ key: 'g' }), new Key({ key: 'g' }) , + ]); + expect(seq.repeatCount()).to.equal(1); + + seq = new KeySequence([]); + expect(seq.repeatCount()).to.equal(1); + }); + + it('returns whole keys if digits only sequence', () => { + let seq = new KeySequence([ + new Key({ key: '1' }), new Key({ key: '0' }) , + ]); + expect(seq.repeatCount()).to.equal(10); + + seq = new KeySequence([ + new Key({ key: '0' }), new Key({ key: '5' }) , + ]); + expect(seq.repeatCount()).to.equal(5); + }); + }); + + describe('#trimNumericPrefix', () => { + it('removes numeric prefix', () => { + const seq = new KeySequence([ + new Key({ key: '1' }), new Key({ key: '0' }) , + new Key({ key: 'g' }), new Key({ key: 'g' }) , new Key({ key: '3' }) , + ]).trimNumericPrefix(); + expect(seq.keys.map(key => key.key)).to.deep.equal(['g', 'g', '3']); + }); + + it('returns empty if keys contains only digis', () => { + const seq = new KeySequence([ + new Key({ key: '1' }), new Key({ key: '0' }) , + ]).trimNumericPrefix(); + expect(seq.trimNumericPrefix().keys).to.be.empty; + }); + + it('returns itself if no numeric prefix', () => { + const seq = new KeySequence([ + new Key({ key: 'g' }), new Key({ key: 'g' }) , new Key({ key: '3' }) , + ]).trimNumericPrefix(); + + expect(seq.keys.map(key => key.key)).to.deep.equal(['g', 'g', '3']); + }); + }); + + describe('#splitNumericPrefix', () => { + it('splits numeric prefix', () => { + expect(KeySequence.fromMapKeys('10gg').splitNumericPrefix()).to.deep.equal([ + KeySequence.fromMapKeys('10'), + KeySequence.fromMapKeys('gg'), + ]); + expect(KeySequence.fromMapKeys('10').splitNumericPrefix()).to.deep.equal([ + KeySequence.fromMapKeys('10'), + new KeySequence([]), + ]); + expect(KeySequence.fromMapKeys('gg').splitNumericPrefix()).to.deep.equal([ + new KeySequence([]), + KeySequence.fromMapKeys('gg'), + ]); + }); + }); + + describe('#fromMapKeys', () => { + it('returns mapped keys for Shift+Esc', () => { + const keys = KeySequence.fromMapKeys('<S-Esc>').keys; + expect(keys).to.have.lengthOf(1); + expect(keys[0].key).to.equal('Esc'); + expect(keys[0].shift).to.be.true; + }); + + it('returns mapped keys for a<C-B><A-C>d<M-e>', () => { + const keys = KeySequence.fromMapKeys('a<C-B><A-C>d<M-e>').keys; + expect(keys).to.have.lengthOf(5); + expect(keys[0].key).to.equal('a'); + expect(keys[1].ctrl).to.be.true; + expect(keys[1].key).to.equal('b'); + expect(keys[2].alt).to.be.true; + expect(keys[2].key).to.equal('c'); + expect(keys[3].key).to.equal('d'); + expect(keys[4].meta).to.be.true; + expect(keys[4].key).to.equal('e'); + }); + }) +}); diff --git a/test/content/presenters/Hint.test.ts b/test/content/presenters/Hint.test.ts index 7994788..7daa63d 100644 --- a/test/content/presenters/Hint.test.ts +++ b/test/content/presenters/Hint.test.ts @@ -11,43 +11,43 @@ describe('Hint', () => { describe('#constructor', () => { it('creates a hint element with tag name', () => { - let link = document.getElementById('test-link'); - let hint = new Hint(link, 'abc'); + const link = document.getElementById('test-link'); + new Hint(link, 'abc'); - let elem = document.querySelector('.vimvixen-hint'); + const elem = document.querySelector('.vimvixen-hint'); expect(elem.textContent.trim()).to.be.equal('abc'); }); }); describe('#show', () => { it('shows an element', () => { - let link = document.getElementById('test-link'); - let hint = new Hint(link, 'abc'); + const link = document.getElementById('test-link'); + const hint = new Hint(link, 'abc'); hint.hide(); hint.show(); - let elem = document.querySelector('.vimvixen-hint') as HTMLElement; + const elem = document.querySelector('.vimvixen-hint') as HTMLElement; expect(elem.style.display).to.not.equal('none'); }); }); describe('#hide', () => { it('hides an element', () => { - let link = document.getElementById('test-link') as HTMLElement; - let hint = new Hint(link, 'abc'); + const link = document.getElementById('test-link') as HTMLElement; + const hint = new Hint(link, 'abc'); hint.hide(); - let elem = document.querySelector('.vimvixen-hint') as HTMLElement; + const elem = document.querySelector('.vimvixen-hint') as HTMLElement; expect(elem.style.display).to.equal('none'); }); }); describe('#remove', () => { it('removes an element', () => { - let link = document.getElementById('test-link'); - let hint = new Hint(link, 'abc'); + const link = document.getElementById('test-link'); + const hint = new Hint(link, 'abc'); - let elem = document.querySelector('.vimvixen-hint'); + const elem = document.querySelector('.vimvixen-hint'); expect(elem.parentElement).to.not.be.null; hint.remove(); expect(elem.parentElement).to.be.null; @@ -66,8 +66,8 @@ describe('LinkHint', () => { describe('#getLink()', () => { it('returns value of "href" attribute', () => { - let link = document.getElementById('test-link1') as HTMLAnchorElement; - let hint = new LinkHint(link, 'abc'); + const link = document.getElementById('test-link1') as HTMLAnchorElement; + const hint = new LinkHint(link, 'abc'); expect(hint.getLink()).to.equal('https://google.com/'); }); @@ -89,8 +89,8 @@ describe('LinkHint', () => { describe('#click()', () => { it('clicks a element', (done) => { - let link = document.getElementById('test-link3') as HTMLAnchorElement; - let hint = new LinkHint(link, 'abc'); + const link = document.getElementById('test-link3') as HTMLAnchorElement; + const hint = new LinkHint(link, 'abc'); link.onclick = () => { done() }; hint.click(); @@ -106,8 +106,8 @@ describe('InputHint', () => { }); it('focuses to the input', () => { - let input = document.getElementById('test-input') as HTMLInputElement; - let hint = new InputHint(input, 'abc'); + const input = document.getElementById('test-input') as HTMLInputElement; + const hint = new InputHint(input, 'abc'); hint.activate(); expect(document.activeElement).to.equal(input); @@ -120,8 +120,8 @@ describe('InputHint', () => { }); it('checks and focuses to the input', () => { - let input = document.getElementById('test-input') as HTMLInputElement; - let hint = new InputHint(input, 'abc'); + const input = document.getElementById('test-input') as HTMLInputElement; + const hint = new InputHint(input, 'abc'); hint.activate(); expect(input.checked).to.be.true; @@ -133,8 +133,8 @@ describe('InputHint', () => { }); it('focuses to the textarea', () => { - let textarea = document.getElementById('test-textarea') as HTMLTextAreaElement; - let hint = new InputHint(textarea, 'abc'); + const textarea = document.getElementById('test-textarea') as HTMLTextAreaElement; + const hint = new InputHint(textarea, 'abc'); hint.activate(); expect(document.activeElement).to.equal(textarea); @@ -147,10 +147,10 @@ describe('InputHint', () => { }); it('clicks the button', (done) => { - let button = document.getElementById('test-button') as HTMLButtonElement; + const button = document.getElementById('test-button') as HTMLButtonElement; button.onclick = () => { done() }; - let hint = new InputHint(button, 'abc'); + const hint = new InputHint(button, 'abc'); hint.activate(); }); }); diff --git a/test/content/presenters/NavigationPresenter.test.ts b/test/content/presenters/NavigationPresenter.test.ts index 5b6a8ca..6aa057b 100644 --- a/test/content/presenters/NavigationPresenter.test.ts +++ b/test/content/presenters/NavigationPresenter.test.ts @@ -1,9 +1,8 @@ -import NavigationPresenter, { NavigationPresenterImpl } - from '../../../src/content/presenters/NavigationPresenter'; +import { NavigationPresenterImpl } from '../../../src/content/presenters/NavigationPresenter'; import { expect } from 'chai'; -describe('NavigationPresenter', () => { - let sut; +describe('NavigationPresenterImpl', () => { + let sut: NavigationPresenterImpl; const testRel = (done, rel, html) => { const method = rel === 'prev' ? sut.openLinkPrev.bind(sut) : sut.openLinkNext.bind(sut); diff --git a/test/content/repositories/AddonEnabledRepository.test.ts b/test/content/repositories/AddonEnabledRepository.test.ts index 3cea897..7edd50e 100644 --- a/test/content/repositories/AddonEnabledRepository.test.ts +++ b/test/content/repositories/AddonEnabledRepository.test.ts @@ -3,7 +3,7 @@ import { expect } from 'chai'; describe('AddonEnabledRepositoryImpl', () => { it('updates and gets current value', () => { - let sut = new AddonEnabledRepositoryImpl(); + const sut = new AddonEnabledRepositoryImpl(); sut.set(true); expect(sut.get()).to.be.true; diff --git a/test/content/repositories/FindRepository.test.ts b/test/content/repositories/FindRepository.test.ts index dcb2dff..0e50b0a 100644 --- a/test/content/repositories/FindRepository.test.ts +++ b/test/content/repositories/FindRepository.test.ts @@ -3,7 +3,7 @@ import { expect } from 'chai'; describe('FindRepositoryImpl', () => { it('updates and gets last keyword', () => { - let sut = new FindRepositoryImpl(); + const sut = new FindRepositoryImpl(); expect(sut.getLastKeyword()).to.be.null; diff --git a/test/content/repositories/FollowMasterRepository.test.ts b/test/content/repositories/FollowMasterRepository.test.ts index 8c3f34e..3eb172f 100644 --- a/test/content/repositories/FollowMasterRepository.test.ts +++ b/test/content/repositories/FollowMasterRepository.test.ts @@ -25,7 +25,7 @@ describe('FollowMasterRepositoryImpl', () => { describe('#getTagsByPrefix', () => { it('gets tags matched by prefix', () => { - for (let tag of ['a', 'aa', 'ab', 'b', 'ba', 'bb']) { + for (const tag of ['a', 'aa', 'ab', 'b', 'ba', 'bb']) { sut.addTag(tag); } expect(sut.getTagsByPrefix('a')).to.deep.equal(['a', 'aa', 'ab']); diff --git a/test/content/repositories/KeymapRepository.test.ts b/test/content/repositories/KeymapRepository.test.ts index df013df..68f515c 100644 --- a/test/content/repositories/KeymapRepository.test.ts +++ b/test/content/repositories/KeymapRepository.test.ts @@ -12,11 +12,11 @@ describe('KeymapRepositoryImpl', () => { describe('#enqueueKey()', () => { it('enqueues keys', () => { - sut.enqueueKey(Key.fromMapKey('a'); - sut.enqueueKey(Key.fromMapKey('b'); - let sequence = sut.enqueueKey(Key.fromMapKey('c')); + sut.enqueueKey(Key.fromMapKey('a')); + sut.enqueueKey(Key.fromMapKey('b')); + const sequence = sut.enqueueKey(Key.fromMapKey('c')); - let keys = sequence.keys; + const keys = sequence.keys; expect(keys[0].equals(Key.fromMapKey('a'))).to.be.true; expect(keys[1].equals(Key.fromMapKey('b'))).to.be.true; expect(keys[2].equals(Key.fromMapKey('c'))).to.be.true; @@ -30,7 +30,7 @@ describe('KeymapRepositoryImpl', () => { sut.enqueueKey(Key.fromMapKey('c')); sut.clear(); - let sequence = sut.enqueueKey(Key.fromMapKey('a')); + const sequence = sut.enqueueKey(Key.fromMapKey('a')); expect(sequence.length()).to.equal(1); }); }); diff --git a/test/content/repositories/MarkRepository.test.ts b/test/content/repositories/MarkRepository.test.ts index 7fced5f..6ddd38d 100644 --- a/test/content/repositories/MarkRepository.test.ts +++ b/test/content/repositories/MarkRepository.test.ts @@ -3,7 +3,7 @@ import { expect } from 'chai'; describe('MarkRepositoryImpl', () => { it('save and load marks', () => { - let sut = new MarkRepositoryImpl(); + const sut = new MarkRepositoryImpl(); sut.set('a', { x: 10, y: 20 }); expect(sut.get('a')).to.deep.equal({ x: 10, y: 20 }); diff --git a/test/content/repositories/SettingRepository.test.ts b/test/content/repositories/SettingRepository.test.ts index db4c528..e45d7c4 100644 --- a/test/content/repositories/SettingRepository.test.ts +++ b/test/content/repositories/SettingRepository.test.ts @@ -4,9 +4,9 @@ import Settings from '../../../src/shared/settings/Settings'; describe('SettingRepositoryImpl', () => { it('updates and gets current value', () => { - let sut = new SettingRepositoryImpl(); + const sut = new SettingRepositoryImpl(); - let settings = Settings.fromJSON({ + const settings = Settings.fromJSON({ keymaps: {}, search:{ default: 'google', @@ -24,7 +24,7 @@ describe('SettingRepositoryImpl', () => { sut.set(settings); - let actual = sut.get(); + const actual = sut.get(); expect(actual.properties.hintchars).to.equal('abcd1234'); }); }); diff --git a/test/content/usecases/ClipboardUseCase.test.ts b/test/content/usecases/ClipboardUseCase.test.ts index a863651..3cc82fe 100644 --- a/test/content/usecases/ClipboardUseCase.test.ts +++ b/test/content/usecases/ClipboardUseCase.test.ts @@ -6,19 +6,31 @@ import ConsoleClient from '../../../src/content/client/ConsoleClient'; import * as sinon from 'sinon'; import { expect } from 'chai'; +import {Operation} from "../../../src/shared/operations"; describe('ClipboardUseCase', () => { let clipboardRepository: ClipboardRepository; + let operationClient: OperationClient; + let consoleClient: ConsoleClient; + let sut: ClipboardUseCase; beforeEach(() => { - var modal = <ConsoleClient>{}; + clipboardRepository = new class implements ClipboardRepository { + read(): string { return ""; } + write(_text: string) {} + }; + operationClient = new class implements OperationClient { + execBackgroundOp(_repeat: number, _op: Operation): Promise<void> { return Promise.resolve() } + internalOpenUrl(_url: string, _newTab?: boolean, _background?: boolean): Promise<void> { return Promise.resolve() } + }; + consoleClient = new class implements ConsoleClient { + error(_text: string): Promise<void> { return Promise.resolve() } + info(_text: string): Promise<void> { return Promise.resolve() } + }; - clipboardRepository = <ClipboardRepository>{ read() {}, write(_) {} }; - operationClient = <OperationClient>{ internalOpenUrl(_) {} }; - consoleClient = <ConsoleClient>{ info(_) {}}; sut = new ClipboardUseCase( clipboardRepository, new SettingRepositoryImpl(), @@ -29,13 +41,13 @@ describe('ClipboardUseCase', () => { describe('#yankCurrentURL', () => { it('yanks current url', async () => { - let href = window.location.href; - var mockRepository = sinon.mock(clipboardRepository); + const href = window.location.href; + const mockRepository = sinon.mock(clipboardRepository); mockRepository.expects('write').withArgs(href); - var mockConsoleClient = sinon.mock(consoleClient); + const mockConsoleClient = sinon.mock(consoleClient); mockConsoleClient.expects('info').withArgs('Yanked ' + href); - let yanked = await sut.yankCurrentURL(); + const yanked = await sut.yankCurrentURL(); expect(yanked).to.equal(href); mockRepository.verify(); @@ -45,9 +57,9 @@ describe('ClipboardUseCase', () => { describe('#openOrSearch', () => { it('opens url from the clipboard', async () => { - let url = 'https://github.com/ueokande/vim-vixen' + const url = 'https://github.com/ueokande/vim-vixen' sinon.stub(clipboardRepository, 'read').returns(url); - let mockOperationClient = sinon.mock(operationClient); + const mockOperationClient = sinon.mock(operationClient); mockOperationClient.expects('internalOpenUrl').withArgs(url, true); await sut.openOrSearch(true); @@ -56,9 +68,9 @@ describe('ClipboardUseCase', () => { }); it('opens search results from the clipboard', async () => { - let url = 'https://google.com/search?q=banana'; + const url = 'https://google.com/search?q=banana'; sinon.stub(clipboardRepository, 'read').returns('banana'); - let mockOperationClient = sinon.mock(operationClient); + const mockOperationClient = sinon.mock(operationClient); mockOperationClient.expects('internalOpenUrl').withArgs(url, true); await sut.openOrSearch(true); diff --git a/test/content/usecases/FindUseCase.test.ts b/test/content/usecases/FindUseCase.test.ts index ddd4cd4..3978dbc 100644 --- a/test/content/usecases/FindUseCase.test.ts +++ b/test/content/usecases/FindUseCase.test.ts @@ -32,7 +32,7 @@ class MockFindPresenter implements FindPresenter { } find(keyword: string, _backward: boolean): boolean { - let found = this.document.includes(keyword); + const found = this.document.includes(keyword); this.highlighted = found; return found; } diff --git a/test/content/usecases/HintKeyProducer.test.ts b/test/content/usecases/HintKeyProducer.test.ts index feafffb..5841ae9 100644 --- a/test/content/usecases/HintKeyProducer.test.ts +++ b/test/content/usecases/HintKeyProducer.test.ts @@ -10,13 +10,13 @@ describe('HintKeyProducer class', () => { describe('#produce', () => { it('produce incremented keys', () => { - let charset = 'abc'; - let sequences = [ + const charset = 'abc'; + const sequences = [ 'a', 'b', 'c', 'aa', 'ab', 'ac', 'ba', 'bb', 'bc', 'ca', 'cb', 'cc', 'aaa', 'aab', 'aac', 'aba'] - let producer = new HintKeyProducer(charset); + const producer = new HintKeyProducer(charset); for (let i = 0; i < sequences.length; ++i) { expect(producer.produce()).to.equal(sequences[i]); } diff --git a/test/content/usecases/KeymapUseCase.test.ts b/test/content/usecases/KeymapUseCase.test.ts index 5f2feba..032d4fc 100644 --- a/test/content/usecases/KeymapUseCase.test.ts +++ b/test/content/usecases/KeymapUseCase.test.ts @@ -1,3 +1,4 @@ +import "reflect-metadata"; import KeymapUseCase from '../../../src/content/usecases/KeymapUseCase'; import {expect} from 'chai'; import SettingRepository from "../../../src/content/repositories/SettingRepository"; @@ -50,55 +51,158 @@ class MockAddressRepository implements AddressRepository { describe('KeymapUseCase', () => { - it('returns matched operation', () => { - let settings = Settings.fromJSON({ + context('with no-digis keymaps', () => { + const settings = Settings.fromJSON({ keymaps: { k: {type: 'scroll.vertically', count: -1}, j: {type: 'scroll.vertically', count: 1}, gg: {type: 'scroll.top'}, }, }); - let sut = new KeymapUseCase( - new KeymapRepositoryImpl(), - new MockSettingRepository(settings), - new MockAddonEnabledRepository(true), - new MockAddressRepository(new URL('https://example.com')), - ); - - expect(sut.nextOp(Key.fromMapKey('k'))).to.deep.equal({type: 'scroll.vertically', count: -1}); - expect(sut.nextOp(Key.fromMapKey('j'))).to.deep.equal({type: 'scroll.vertically', count: 1}); - expect(sut.nextOp(Key.fromMapKey('g'))).to.be.null; - expect(sut.nextOp(Key.fromMapKey('g'))).to.deep.equal({type: 'scroll.top'}); - expect(sut.nextOp(Key.fromMapKey('z'))).to.be.null; + + let sut: KeymapUseCase; + + before(() => { + sut = new KeymapUseCase( + new KeymapRepositoryImpl(), + new MockSettingRepository(settings), + new MockAddonEnabledRepository(true), + new MockAddressRepository(new URL('https://example.com')), + ); + }); + + it('returns matched operation', () => { + expect(sut.nextOps(Key.fromMapKey('k'))).to.deep.equal({ repeat: 1, op: {type: 'scroll.vertically', count: -1}}); + expect(sut.nextOps(Key.fromMapKey('j'))).to.deep.equal({ repeat: 1, op: {type: 'scroll.vertically', count: 1}}); + expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('g'))).to.deep.equal({ repeat: 1, op: {type: 'scroll.top'}}); + expect(sut.nextOps(Key.fromMapKey('z'))).to.be.null; + }); + + it('repeats n-times by numeric prefix and multiple key operations', () => { + expect(sut.nextOps(Key.fromMapKey('1'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('0'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('g'))).to.deep.equal({ repeat: 10, op: {type: "scroll.top"}}); + }); + }); + + context('when keymaps containing numeric mappings', () => { + const settings = Settings.fromJSON({ + keymaps: { + 20: {type: "scroll.top"}, + g5: {type: 'scroll.bottom'}, + }, + }); + + let sut: KeymapUseCase; + + before(() => { + sut = new KeymapUseCase( + new KeymapRepositoryImpl(), + new MockSettingRepository(settings), + new MockAddonEnabledRepository(true), + new MockAddressRepository(new URL('https://example.com')), + ); + }); + + it('returns the matched operation ends with digit', () => { + expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('5'))).to.be.deep.equal({ repeat: 1, op: { type: 'scroll.bottom'}}); + }); + + it('returns an operation matched the operation with digit keymaps', () => { + expect(sut.nextOps(Key.fromMapKey('2'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('0'))).to.be.deep.equal({ repeat: 1, op: { type: 'scroll.top'}}); + }); + + it('returns operations repeated by numeric prefix', () => { + expect(sut.nextOps(Key.fromMapKey('2'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('5'))).to.be.deep.equal({ repeat: 2, op: { type: 'scroll.bottom'}}); + }); + + it('does not matches with digit operation with numeric prefix', () => { + expect(sut.nextOps(Key.fromMapKey('3'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('2'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('0'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('5'))).to.be.deep.equal({ repeat: 320, op: { type: 'scroll.bottom'}}); + }); + }); + + context('when the keys are mismatched with the operations', () => { + const settings = Settings.fromJSON({ + keymaps: { + gg: {type: "scroll.top"}, + G: {type: "scroll.bottom"}, + }, + }); + + let sut: KeymapUseCase; + + before(() => { + sut = new KeymapUseCase( + new KeymapRepositoryImpl(), + new MockSettingRepository(settings), + new MockAddonEnabledRepository(true), + new MockAddressRepository(new URL('https://example.com')), + ); + }); + + it('clears input keys with no-matched operations', () => { + expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('x'))).to.be.null; // clear + expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('g'))).to.deep.equal({repeat: 1, op: {type: "scroll.top"}}); + }); + + it('clears input keys and the prefix with no-matched operations', () => { + expect(sut.nextOps(Key.fromMapKey('1'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('0'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('x'))).to.be.null; // clear + expect(sut.nextOps(Key.fromMapKey('1'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('0'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('g'))).to.deep.equal({ repeat: 10, op: {type: "scroll.top"}}); + }); }); - it('returns only ADDON_ENABLE and ADDON_TOGGLE_ENABLED operation', () => { - let settings = Settings.fromJSON({ + context('when the site matches to the blacklist', () => { + const settings = Settings.fromJSON({ keymaps: { k: {type: 'scroll.vertically', count: -1}, a: {type: 'addon.enable'}, b: {type: 'addon.toggle.enabled'}, }, }); - let sut = new KeymapUseCase( - new KeymapRepositoryImpl(), - new MockSettingRepository(settings), - new MockAddonEnabledRepository(false), - new MockAddressRepository(new URL('https://example.com')), - ); - expect(sut.nextOp(Key.fromMapKey('k'))).to.be.null; - expect(sut.nextOp(Key.fromMapKey('a'))).to.deep.equal({type: 'addon.enable'}); - expect(sut.nextOp(Key.fromMapKey('b'))).to.deep.equal({type: 'addon.toggle.enabled'}); + let sut: KeymapUseCase; + + before(() => { + sut = new KeymapUseCase( + new KeymapRepositoryImpl(), + new MockSettingRepository(settings), + new MockAddonEnabledRepository(false), + new MockAddressRepository(new URL('https://example.com')), + ); + }); + + it('returns only ADDON_ENABLE and ADDON_TOGGLE_ENABLED operation', () => { + expect(sut.nextOps(Key.fromMapKey('k'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('a'))).to.deep.equal({ repeat: 1, op: {type: 'addon.enable'}}); + expect(sut.nextOps(Key.fromMapKey('b'))).to.deep.equal({ repeat: 1, op: {type: 'addon.toggle.enabled'}}); + }); }); - it('blocks keys in the partial blacklist', () => { - let settings = Settings.fromJSON({ + context('when the site matches to the partial blacklist', () => { + const settings = Settings.fromJSON({ keymaps: { k: {type: 'scroll.vertically', count: -1}, j: {type: 'scroll.vertically', count: 1}, - gg: {"type": "scroll.top"}, - G: {"type": "scroll.bottom"}, + gg: {type: "scroll.top"}, + G: {type: "scroll.bottom"}, }, blacklist: [ { url: "example.com", keys: ['g'] }, @@ -106,28 +210,30 @@ describe('KeymapUseCase', () => { ], }); - let sut = new KeymapUseCase( - new KeymapRepositoryImpl(), - new MockSettingRepository(settings), - new MockAddonEnabledRepository(true), - new MockAddressRepository(new URL('https://example.com')), - ); - - expect(sut.nextOp(Key.fromMapKey('k'))).to.deep.equal({type: 'scroll.vertically', count: -1}); - expect(sut.nextOp(Key.fromMapKey('j'))).to.deep.equal({type: 'scroll.vertically', count: 1}); - expect(sut.nextOp(Key.fromMapKey('g'))).to.be.null; - expect(sut.nextOp(Key.fromMapKey('g'))).to.be.null; - expect(sut.nextOp(Key.fromMapKey('G'))).to.deep.equal({type: 'scroll.bottom'}); - - sut = new KeymapUseCase( - new KeymapRepositoryImpl(), - new MockSettingRepository(settings), - new MockAddonEnabledRepository(true), - new MockAddressRepository(new URL('https://example.org')), - ); - - expect(sut.nextOp(Key.fromMapKey('g'))).to.be.null; - expect(sut.nextOp(Key.fromMapKey('g'))).to.deep.equal({type: 'scroll.top'}); - expect(sut.nextOp(Key.fromMapKey('G'))).to.be.null; + it('blocks keys in the partial blacklist', () => { + let sut = new KeymapUseCase( + new KeymapRepositoryImpl(), + new MockSettingRepository(settings), + new MockAddonEnabledRepository(true), + new MockAddressRepository(new URL('https://example.com')), + ); + + expect(sut.nextOps(Key.fromMapKey('k'))).to.deep.equal({ repeat: 1, op: {type: 'scroll.vertically', count: -1}}); + expect(sut.nextOps(Key.fromMapKey('j'))).to.deep.equal({ repeat: 1, op: {type: 'scroll.vertically', count: 1}}); + expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('G'))).to.deep.equal({ repeat: 1, op: {type: 'scroll.bottom'}}); + + sut = new KeymapUseCase( + new KeymapRepositoryImpl(), + new MockSettingRepository(settings), + new MockAddonEnabledRepository(true), + new MockAddressRepository(new URL('https://example.org')), + ); + + expect(sut.nextOps(Key.fromMapKey('g'))).to.be.null; + expect(sut.nextOps(Key.fromMapKey('g'))).to.deep.equal({ repeat: 1, op: {type: 'scroll.top'}}); + expect(sut.nextOps(Key.fromMapKey('G'))).to.be.null; + }); }); }); diff --git a/test/content/usecases/SettingUseCaase.test.ts b/test/content/usecases/SettingUseCaase.test.ts index 136c5af..cf14e6e 100644 --- a/test/content/usecases/SettingUseCaase.test.ts +++ b/test/content/usecases/SettingUseCaase.test.ts @@ -38,7 +38,7 @@ describe('AddonEnabledUseCase', () => { let sut: SettingUseCase; beforeEach(() => { - let testSettings = { + const testSettings = { keymaps: {}, search: { default: 'google', @@ -61,10 +61,10 @@ describe('AddonEnabledUseCase', () => { describe('#reload', () => { it('loads settings and store to repository', async() => { - let settings = await sut.reload(); + const settings = await sut.reload(); expect(settings.properties.hintchars).to.equal('abcd1234'); - let saved = repository.get(); + const saved = repository.get(); expect(saved.properties.hintchars).to.equal('abcd1234'); }); }); diff --git a/test/main.ts b/test/main.ts index 433b531..c985546 100644 --- a/test/main.ts +++ b/test/main.ts @@ -1,8 +1,6 @@ import 'reflect-metadata'; import { expect } from 'chai'; - -const browserFake = require('webextensions-api-fake'); -const browser = browserFake(); +import browserFake from 'webextensions-api-fake'; global.expect = expect; -global.browser = browser; +global.browser = browserFake(); diff --git a/test/settings/components/form/BlacklistForm.test.tsx b/test/settings/components/form/BlacklistForm.test.tsx index 7daf513..6c329ff 100644 --- a/test/settings/components/form/BlacklistForm.test.tsx +++ b/test/settings/components/form/BlacklistForm.test.tsx @@ -10,11 +10,11 @@ import Blacklist from '../../../../src/shared/settings/Blacklist'; describe("settings/form/BlacklistForm", () => { describe('render', () => { it('renders BlacklistForm', () => { - let root = ReactTestRenderer.create( + const root = ReactTestRenderer.create( <BlacklistForm value={Blacklist.fromJSON(['*.slack.com', 'www.google.com/maps'])} />, ).root; - let children = root.children[0].children; + const children = root.children[0].children; expect(children).to.have.lengthOf(3); expect(children[0].children[0].props.value).to.equal('*.slack.com'); expect(children[1].children[0].props.value).to.equal('www.google.com/maps'); @@ -22,9 +22,9 @@ describe("settings/form/BlacklistForm", () => { }); it('renders blank value', () => { - let root = ReactTestRenderer.create(<BlacklistForm />).root; + const root = ReactTestRenderer.create(<BlacklistForm />).root; - let children = root.children[0].children; + const children = root.children[0].children; expect(children).to.have.lengthOf(1); expect(children[0].props.name).to.equal('add'); }); @@ -48,14 +48,14 @@ describe("settings/form/BlacklistForm", () => { ReactDOM.render(<BlacklistForm value={Blacklist.fromJSON(['*.slack.com', 'www.google.com/maps*'])} onChange={value => { - let urls = value.items.map(item => item.pattern); + const urls = value.items.map(item => item.pattern); expect(urls).to.have.members(['gitter.im', 'www.google.com/maps*']); done(); }} />, container) }); - let input = document.querySelectorAll('input[type=text]')[0]; + const input = document.querySelectorAll('input[type=text]')[0]; input.value = 'gitter.im'; ReactTestUtils.Simulate.change(input); }); @@ -65,14 +65,14 @@ describe("settings/form/BlacklistForm", () => { ReactDOM.render(<BlacklistForm value={Blacklist.fromJSON(['*.slack.com', 'www.google.com/maps*'])} onChange={value => { - let urls = value.items.map(item => item.pattern); + const urls = value.items.map(item => item.pattern); expect(urls).to.have.members(['www.google.com/maps*']); done(); }} />, container) }); - let button = document.querySelectorAll('input[type=button]')[0]; + const button = document.querySelectorAll('input[type=button]')[0]; ReactTestUtils.Simulate.click(button); }); @@ -81,14 +81,14 @@ describe("settings/form/BlacklistForm", () => { ReactDOM.render(<BlacklistForm value={Blacklist.fromJSON(['*.slack.com'])} onChange={value => { - let urls = value.items.map(item => item.pattern); + const urls = value.items.map(item => item.pattern); expect(urls).to.have.members(['*.slack.com', '']); done(); }} />, container); }); - let button = document.querySelector('input[type=button].ui-add-button'); + const button = document.querySelector('input[type=button].ui-add-button'); ReactTestUtils.Simulate.click(button); }); }); diff --git a/test/settings/components/form/KeymapsForm.test.tsx b/test/settings/components/form/KeymapsForm.test.tsx index 1d1e77c..ccc772c 100644 --- a/test/settings/components/form/KeymapsForm.test.tsx +++ b/test/settings/components/form/KeymapsForm.test.tsx @@ -9,23 +9,23 @@ import { expect } from 'chai'; describe("settings/form/KeymapsForm", () => { describe('render', () => { it('renders keymap fields', () => { - let root = ReactTestRenderer.create(<KeymapsForm value={FormKeymaps.fromJSON({ + const root = ReactTestRenderer.create(<KeymapsForm value={FormKeymaps.fromJSON({ 'scroll.vertically?{"count":1}': 'j', 'scroll.vertically?{"count":-1}': 'k', })} />).root - let inputj = root.findByProps({ id: 'scroll.vertically?{"count":1}' }); - let inputk = root.findByProps({ id: 'scroll.vertically?{"count":-1}' }); + const inputj = root.findByProps({ id: 'scroll.vertically?{"count":1}' }); + const inputk = root.findByProps({ id: 'scroll.vertically?{"count":-1}' }); expect(inputj.props.value).to.equal('j'); expect(inputk.props.value).to.equal('k'); }); it('renders blank value', () => { - let root = ReactTestRenderer.create(<KeymapsForm />).root; + const root = ReactTestRenderer.create(<KeymapsForm />).root; - let inputj = root.findByProps({ id: 'scroll.vertically?{"count":1}' }); - let inputk = root.findByProps({ id: 'scroll.vertically?{"count":-1}' }); + const inputj = root.findByProps({ id: 'scroll.vertically?{"count":1}' }); + const inputk = root.findByProps({ id: 'scroll.vertically?{"count":-1}' }); expect(inputj.props.value).to.be.empty; expect(inputk.props.value).to.be.empty; @@ -58,7 +58,7 @@ describe("settings/form/KeymapsForm", () => { }} />, container); }); - let input = document.getElementById('scroll.vertically?{"count":1}'); + const input = document.getElementById('scroll.vertically?{"count":1}'); input.value = 'jjj'; ReactTestUtils.Simulate.change(input); }); diff --git a/test/settings/components/form/PropertiesForm.test.tsx b/test/settings/components/form/PropertiesForm.test.tsx index 0e33cc8..4a0e25a 100644 --- a/test/settings/components/form/PropertiesForm.test.tsx +++ b/test/settings/components/form/PropertiesForm.test.tsx @@ -7,19 +7,19 @@ import PropertiesForm from 'settings/components/form/PropertiesForm' describe("settings/form/PropertiesForm", () => { describe('render', () => { it('renders PropertiesForm', () => { - let types = { + const types = { mystr: 'string', mynum: 'number', mybool: 'boolean', empty: 'string', } - let values = { + const values = { mystr: 'abc', mynum: 123, mybool: true, }; - let root = ReactTestRenderer.create( + const root = ReactTestRenderer.create( <PropertiesForm types={types} value={values} />, ).root @@ -62,7 +62,7 @@ describe("settings/form/PropertiesForm", () => { />, container); }); - let input = document.querySelector('input[name=myvalue]'); + const input = document.querySelector('input[name=myvalue]'); input.value = 'abcd' ReactTestUtils.Simulate.change(input); }); @@ -79,7 +79,7 @@ describe("settings/form/PropertiesForm", () => { />, container); }); - let input = document.querySelector('input[name=myvalue]'); + const input = document.querySelector('input[name=myvalue]'); input.value = '1234' ReactTestUtils.Simulate.change(input); }); @@ -96,7 +96,7 @@ describe("settings/form/PropertiesForm", () => { />, container); }); - let input = document.querySelector('input[name=myvalue]'); + const input = document.querySelector('input[name=myvalue]'); input.checked = true; ReactTestUtils.Simulate.change(input); }); diff --git a/test/settings/components/form/SearchEngineForm.test.tsx b/test/settings/components/form/SearchEngineForm.test.tsx index 1f0420d..b918203 100644 --- a/test/settings/components/form/SearchEngineForm.test.tsx +++ b/test/settings/components/form/SearchEngineForm.test.tsx @@ -8,17 +8,17 @@ import { FormSearch } from 'shared/SettingData'; describe("settings/form/SearchForm", () => { describe('render', () => { it('renders SearchForm', () => { - let root = ReactTestRenderer.create(<SearchForm value={FormSearch.fromJSON({ + const root = ReactTestRenderer.create(<SearchForm value={FormSearch.fromJSON({ default: 'google', engines: [['google', 'google.com'], ['yahoo', 'yahoo.com']], })} />).root; - let names = root.findAllByProps({ name: 'name' }); + const names = root.findAllByProps({ name: 'name' }); expect(names).to.have.lengthOf(2); expect(names[0].props.value).to.equal('google'); expect(names[1].props.value).to.equal('yahoo'); - let urls = root.findAllByProps({ name: 'url' }); + const urls = root.findAllByProps({ name: 'url' }); expect(urls).to.have.lengthOf(2); expect(urls[0].props.value).to.equal('google.com'); expect(urls[1].props.value).to.equal('yahoo.com'); @@ -46,7 +46,7 @@ describe("settings/form/SearchForm", () => { engines: [['google', 'google.com'], ['yahoo', 'yahoo.com']] })} onChange={value => { - let json = value.toJSON(); + const json = value.toJSON(); expect(json.default).to.equal('louvre'); expect(json.engines).to.have.lengthOf(2) expect(json.engines).to.have.deep.members( @@ -56,10 +56,10 @@ describe("settings/form/SearchForm", () => { }} />, container); }); - let radio = document.querySelectorAll('input[type=radio]'); + const radio = document.querySelectorAll('input[type=radio]'); radio.checked = true; - let name = document.querySelector('input[name=name]'); + const name = document.querySelector('input[name=name]'); name.value = 'louvre'; ReactTestUtils.Simulate.change(name); @@ -72,7 +72,7 @@ describe("settings/form/SearchForm", () => { engines: [['louvre', 'google.com'], ['yahoo', 'yahoo.com']] })} onChange={value => { - let json = value.toJSON(); + const json = value.toJSON(); expect(json.default).to.equal('yahoo'); expect(json.engines).to.have.lengthOf(1) expect(json.engines).to.have.deep.members( @@ -82,7 +82,7 @@ describe("settings/form/SearchForm", () => { }} />, container); }); - let button = document.querySelector('input[type=button]'); + const button = document.querySelector('input[type=button]'); ReactTestUtils.Simulate.click(button); }); @@ -93,7 +93,7 @@ describe("settings/form/SearchForm", () => { engines: [['google', 'google.com']] })} onChange={value => { - let json = value.toJSON(); + const json = value.toJSON(); expect(json.default).to.equal('yahoo'); expect(json.engines).to.have.lengthOf(2) expect(json.engines).to.have.deep.members( @@ -103,7 +103,7 @@ describe("settings/form/SearchForm", () => { }} />, container); }); - let button = document.querySelector('input[type=button].ui-add-button'); + const button = document.querySelector('input[type=button].ui-add-button'); ReactTestUtils.Simulate.click(button); }); }); diff --git a/test/settings/components/ui/input.test.tsx b/test/settings/components/ui/input.test.tsx index 432efcb..a3e7ff4 100644 --- a/test/settings/components/ui/input.test.tsx +++ b/test/settings/components/ui/input.test.tsx @@ -24,8 +24,8 @@ describe("settings/ui/Input", () => { container); }); - let label = document.querySelector('label'); - let input = document.querySelector('input'); + const label = document.querySelector('label'); + const input = document.querySelector('input'); expect(label.textContent).to.contain('myfield'); expect(input.type).to.contain('text'); expect(input.name).to.contain('myname'); @@ -40,7 +40,7 @@ describe("settings/ui/Input", () => { }}/>, container); }); - let input = document.querySelector('input'); + const input = document.querySelector('input'); input.value = 'newvalue'; ReactTestUtils.Simulate.change(input); }); @@ -54,8 +54,8 @@ describe("settings/ui/Input", () => { container); }); - let label = document.querySelector('label'); - let input = document.querySelector('input'); + const label = document.querySelector('label'); + const input = document.querySelector('input'); expect(label.textContent).to.contain('myfield'); expect(input.type).to.contain('radio'); expect(input.name).to.contain('myname'); @@ -71,7 +71,7 @@ describe("settings/ui/Input", () => { container); }); - let input = document.querySelector('input'); + const input = document.querySelector('input'); input.checked = true; ReactTestUtils.Simulate.change(input); }); @@ -85,9 +85,9 @@ describe("settings/ui/Input", () => { container); }); - let label = document.querySelector('label'); - let textarea = document.querySelector('textarea'); - let error = document.querySelector('.settings-ui-input-error'); + const label = document.querySelector('label'); + const textarea = document.querySelector('textarea'); + const error = document.querySelector('.settings-ui-input-error'); expect(label.textContent).to.contain('myfield'); expect(textarea.nodeName).to.contain('TEXTAREA'); expect(textarea.name).to.contain('myname'); @@ -103,7 +103,7 @@ describe("settings/ui/Input", () => { }}/>, container); }); - let input = document.querySelector('textarea'); + const input = document.querySelector('textarea'); input.value = 'newvalue' ReactTestUtils.Simulate.change(input); }); diff --git a/test/settings/reducers/setting.test.ts b/test/settings/reducers/setting.test.ts index 376d66e..60df061 100644 --- a/test/settings/reducers/setting.test.ts +++ b/test/settings/reducers/setting.test.ts @@ -3,51 +3,51 @@ import settingReducer from 'settings/reducers/setting'; describe("settings setting reducer", () => { it('return the initial state', () => { - let state = settingReducer(undefined, {}); + const state = settingReducer(undefined, {}); expect(state).to.have.deep.property('source', 'json'); expect(state).to.have.deep.property('error', ''); }); it('return next state for SETTING_SET_SETTINGS', () => { - let action = { + const action = { type: actions.SETTING_SET_SETTINGS, source: 'json', json: '{ "key": "value" }', form: {}, }; - let state = settingReducer(undefined, action); + const state = settingReducer(undefined, action); expect(state).to.have.deep.property('source', 'json'); expect(state).to.have.deep.property('json', '{ "key": "value" }'); expect(state).to.have.deep.property('form', {}); }); it('return next state for SETTING_SHOW_ERROR', () => { - let action = { + const action = { type: actions.SETTING_SHOW_ERROR, error: 'bad value', json: '{}', }; - let state = settingReducer(undefined, action); + const state = settingReducer(undefined, action); expect(state).to.have.deep.property('error', 'bad value'); expect(state).to.have.deep.property('json', '{}'); }); it('return next state for SETTING_SWITCH_TO_FORM', () => { - let action = { + const action = { type: actions.SETTING_SWITCH_TO_FORM, form: {}, }; - let state = settingReducer(undefined, action); + const state = settingReducer(undefined, action); expect(state).to.have.deep.property('form', {}); expect(state).to.have.deep.property('source', 'form'); }); it('return next state for SETTING_SWITCH_TO_JSON', () => { - let action = { + const action = { type: actions.SETTING_SWITCH_TO_JSON, json: '{}', }; - let state = settingReducer(undefined, action); + const state = settingReducer(undefined, action); expect(state).to.have.deep.property('json', '{}'); expect(state).to.have.deep.property('source', 'json'); }); diff --git a/test/shared/SettingData.test.ts b/test/shared/SettingData.test.ts index 5de7770..0632176 100644 --- a/test/shared/SettingData.test.ts +++ b/test/shared/SettingData.test.ts @@ -9,12 +9,12 @@ describe('shared/SettingData', () => { describe('FormKeymaps', () => { describe('#valueOF to #toKeymaps', () => { it('parses form keymaps and convert to operations', () => { - let data = { + const data = { 'scroll.vertically?{"count":1}': 'j', 'scroll.home': '0', }; - let keymaps = FormKeymaps.fromJSON(data).toKeymaps().toJSON(); + const keymaps = FormKeymaps.fromJSON(data).toKeymaps().toJSON(); expect(keymaps).to.deep.equal({ 'j': { type: 'scroll.vertically', count: 1 }, '0': { type: 'scroll.home' }, @@ -24,12 +24,12 @@ describe('shared/SettingData', () => { describe('#fromKeymaps to #toJSON', () => { it('create from a Keymaps and create a JSON object', () => { - let keymaps: Keymaps = Keymaps.fromJSON({ + const keymaps: Keymaps = Keymaps.fromJSON({ 'j': { type: 'scroll.vertically', count: 1 }, '0': { type: 'scroll.home' }, }); - let form = FormKeymaps.fromKeymaps(keymaps).toJSON(); + const form = FormKeymaps.fromKeymaps(keymaps).toJSON(); expect(form).to.deep.equal({ 'scroll.vertically?{"count":1}': 'j', 'scroll.home': '0', @@ -41,7 +41,7 @@ describe('shared/SettingData', () => { describe('JSONSettings', () => { describe('#valueOf to #toSettings', () => { it('parse object and create a Settings', () => { - let o = `{ + const o = `{ "keymaps": {}, "search": { "default": "google", @@ -57,14 +57,14 @@ describe('shared/SettingData', () => { "blacklist": [] }`; - let settings = JSONTextSettings.fromText(o).toSettings(); + const settings = JSONTextSettings.fromText(o).toSettings(); expect(settings.toJSON()).to.deep.equal(JSON.parse(o)); }); }); describe('#fromSettings to #toJSON', () => { it('create from a Settings and create a JSON string', () => { - let o = Settings.fromJSON({ + const o = Settings.fromJSON({ keymaps: {}, search: { default: "google", @@ -80,7 +80,7 @@ describe('shared/SettingData', () => { blacklist: [], }); - let json = JSONTextSettings.fromSettings(o).toJSONText(); + const json = JSONTextSettings.fromSettings(o).toJSONText(); expect(JSON.parse(json)).to.deep.equal(o.toJSON()); }); }); @@ -89,7 +89,7 @@ describe('shared/SettingData', () => { describe('FormSettings', () => { describe('#valueOf to #toSettings', () => { it('parse object and create a Settings', () => { - let data = { + const data = { keymaps: { 'scroll.vertically?{"count":1}': 'j', 'scroll.home': '0', @@ -108,7 +108,7 @@ describe('shared/SettingData', () => { blacklist: [] }; - let settings = FormSettings.fromJSON(data).toSettings(); + const settings = FormSettings.fromJSON(data).toSettings(); expect(settings.toJSON()).to.deep.equal({ keymaps: { 'j': { type: 'scroll.vertically', count: 1 }, @@ -132,7 +132,7 @@ describe('shared/SettingData', () => { describe('#fromSettings to #toJSON', () => { it('create from a Settings and create a JSON string', () => { - let data: Settings = Settings.fromJSON({ + const data: Settings = Settings.fromJSON({ keymaps: { 'j': { type: 'scroll.vertically', count: 1 }, '0': { type: 'scroll.home' }, @@ -151,7 +151,7 @@ describe('shared/SettingData', () => { blacklist: [], }); - let json = FormSettings.fromSettings(data).toJSON(); + const json = FormSettings.fromSettings(data).toJSON(); expect(json).to.deep.equal({ keymaps: { 'scroll.vertically?{"count":1}': 'j', @@ -177,7 +177,7 @@ describe('shared/SettingData', () => { describe('SettingData', () => { describe('#valueOf to #toJSON', () => { it('parse object from json source', () => { - let data = { + const data = { source: 'json', json: `{ "keymaps": {}, @@ -196,13 +196,13 @@ describe('shared/SettingData', () => { }`, }; - let j = SettingData.fromJSON(data).toJSON(); + const j = SettingData.fromJSON(data).toJSON(); expect(j.source).to.equal('json'); expect(j.json).to.be.a('string'); }); it('parse object from form source', () => { - let data = { + const data = { source: 'form', form: { keymaps: {}, @@ -221,7 +221,7 @@ describe('shared/SettingData', () => { }, }; - let j = SettingData.fromJSON(data).toJSON(); + const j = SettingData.fromJSON(data).toJSON(); expect(j.source).to.equal('form'); expect(j.form).to.deep.equal({ keymaps: {}, @@ -243,7 +243,7 @@ describe('shared/SettingData', () => { describe('#toSettings', () => { it('parse object from json source', () => { - let data = { + const data = { source: 'json', json: `{ "keymaps": {}, @@ -262,12 +262,12 @@ describe('shared/SettingData', () => { }`, }; - let settings = SettingData.fromJSON(data).toSettings(); + const settings = SettingData.fromJSON(data).toSettings(); expect(settings.search.defaultEngine).to.equal('google'); }); it('parse object from form source', () => { - let data = { + const data = { source: 'form', form: { keymaps: {}, @@ -286,7 +286,7 @@ describe('shared/SettingData', () => { }, }; - let settings = SettingData.fromJSON(data).toSettings(); + const settings = SettingData.fromJSON(data).toSettings(); expect(settings.search.defaultEngine).to.equal('yahoo'); }); }); diff --git a/test/shared/operations.test.ts b/test/shared/operations.test.ts index 42a3eed..fbb6193 100644 --- a/test/shared/operations.test.ts +++ b/test/shared/operations.test.ts @@ -3,7 +3,7 @@ import * as operations from 'shared/operations'; describe('operations', () => { describe('#valueOf', () => { it('returns an Operation', () => { - let op: operations.Operation = operations.valueOf({ + const op: operations.Operation = operations.valueOf({ type: operations.SCROLL_VERTICALLY, count: 10, }); @@ -18,7 +18,7 @@ describe('operations', () => { }); it('fills default valus of optional parameter', () => { - let op: operations.Operation = operations.valueOf({ + const op: operations.Operation = operations.valueOf({ type: operations.COMMAND_SHOW_OPEN, }); diff --git a/test/shared/settings/Blacklist.test.ts b/test/shared/settings/Blacklist.test.ts index 133112c..dfd036e 100644 --- a/test/shared/settings/Blacklist.test.ts +++ b/test/shared/settings/Blacklist.test.ts @@ -5,37 +5,27 @@ import Key from '../../../src/shared/settings/Key'; describe('BlacklistItem', () => { describe('#fromJSON', () => { it('parses string pattern', () => { - let item = BlacklistItem.fromJSON('example.com'); + const item = BlacklistItem.fromJSON('example.com'); expect(item.pattern).to.equal('example.com'); expect(item.partial).to.be.false; }); it('parses partial blacklist item', () => { - let item = BlacklistItem.fromJSON({ url: 'example.com', keys: ['j', 'k']}); + const item = BlacklistItem.fromJSON({ url: 'example.com', keys: ['j', 'k']}); expect(item.pattern).to.equal('example.com'); expect(item.partial).to.be.true; expect(item.keys).to.deep.equal(['j', 'k']); }); - - it('throws a TypeError', () => { - expect(() => BlacklistItem.fromJSON(null)).to.throw(TypeError); - expect(() => BlacklistItem.fromJSON(100)).to.throw(TypeError); - expect(() => BlacklistItem.fromJSON({})).to.throw(TypeError); - expect(() => BlacklistItem.fromJSON({url: 'google.com'})).to.throw(TypeError); - expect(() => BlacklistItem.fromJSON({keys: ['a']})).to.throw(TypeError); - expect(() => BlacklistItem.fromJSON({url: 'google.com', keys: 10})).to.throw(TypeError); - expect(() => BlacklistItem.fromJSON({url: 'google.com', keys: ['a', 'b', 3]})).to.throw(TypeError); - }); }); describe('#matches', () => { it('matches by "*"', () => { - let item = BlacklistItem.fromJSON('*'); + const item = BlacklistItem.fromJSON('*'); expect(item.matches(new URL('https://github.com/abc'))).to.be.true; }); it('matches by hostname', () => { - let item = BlacklistItem.fromJSON('github.com'); + const item = BlacklistItem.fromJSON('github.com'); expect(item.matches(new URL('https://github.com'))).to.be.true; expect(item.matches(new URL('https://gist.github.com'))).to.be.false; expect(item.matches(new URL('https://github.com/ueokande'))).to.be.true; @@ -44,14 +34,14 @@ describe('BlacklistItem', () => { }); it('matches by hostname with wildcard', () => { - let item = BlacklistItem.fromJSON('*.github.com'); + const item = BlacklistItem.fromJSON('*.github.com'); expect(item.matches(new URL('https://github.com'))).to.be.false; expect(item.matches(new URL('https://gist.github.com'))).to.be.true; }); it('matches by path', () => { - let item = BlacklistItem.fromJSON('github.com/abc'); + const item = BlacklistItem.fromJSON('github.com/abc'); expect(item.matches(new URL('https://github.com/abc'))).to.be.true; expect(item.matches(new URL('https://github.com/abcdef'))).to.be.false; @@ -59,7 +49,7 @@ describe('BlacklistItem', () => { }); it('matches by path with wildcard', () => { - let item = BlacklistItem.fromJSON('github.com/abc*'); + const item = BlacklistItem.fromJSON('github.com/abc*'); expect(item.matches(new URL('https://github.com/abc'))).to.be.true; expect(item.matches(new URL('https://github.com/abcdef'))).to.be.true; @@ -67,14 +57,14 @@ describe('BlacklistItem', () => { }); it('matches address and port', () => { - let item = BlacklistItem.fromJSON('127.0.0.1:8888'); + const item = BlacklistItem.fromJSON('127.0.0.1:8888'); expect(item.matches(new URL('http://127.0.0.1:8888/'))).to.be.true; expect(item.matches(new URL('http://127.0.0.1:8888/hello'))).to.be.true; }); it('matches with partial blacklist', () => { - let item = BlacklistItem.fromJSON({ url: 'google.com', keys: ['j', 'k'] }); + const item = BlacklistItem.fromJSON({ url: 'google.com', keys: ['j', 'k'] }); expect(item.matches(new URL('https://google.com'))).to.be.true; expect(item.matches(new URL('https://yahoo.com'))).to.be.false; @@ -83,7 +73,7 @@ describe('BlacklistItem', () => { describe('#includesPartialKeys', () => { it('matches with partial keys', () => { - let item = BlacklistItem.fromJSON({url: 'google.com', keys: ['j', 'k', '<C-U>']}); + const item = BlacklistItem.fromJSON({url: 'google.com', keys: ['j', 'k', '<C-U>']}); expect(item.includeKey(new URL('http://google.com/maps'), Key.fromMapKey('j'))).to.be.true; expect(item.includeKey(new URL('http://google.com/maps'), Key.fromMapKey('<C-U>'))).to.be.true; @@ -97,14 +87,14 @@ describe('BlacklistItem', () => { describe('Blacklist', () => { describe('#fromJSON', () => { it('parses string list', () => { - let blacklist = Blacklist.fromJSON(['example.com', 'example.org']); + const blacklist = Blacklist.fromJSON(['example.com', 'example.org']); expect(blacklist.toJSON()).to.deep.equals([ 'example.com', 'example.org', ]); }); it('parses mixed blacklist', () => { - let blacklist = Blacklist.fromJSON([ + const blacklist = Blacklist.fromJSON([ { url: 'example.com', keys: ['j', 'k']}, 'example.org', ]); @@ -115,29 +105,21 @@ describe('Blacklist', () => { }); it('parses empty blacklist', () => { - let blacklist = Blacklist.fromJSON([]); + const blacklist = Blacklist.fromJSON([]); expect(blacklist.toJSON()).to.deep.equals([]); }); - - it('throws a TypeError', () => { - expect(() => Blacklist.fromJSON(null)).to.throw(TypeError); - expect(() => Blacklist.fromJSON(100)).to.throw(TypeError); - expect(() => Blacklist.fromJSON({})).to.throw(TypeError); - expect(() => Blacklist.fromJSON([100])).to.throw(TypeError); - expect(() => Blacklist.fromJSON([{}])).to.throw(TypeError); - }) }); describe('#includesEntireBlacklist', () => { it('matches a url with entire blacklist', () => { - let blacklist = Blacklist.fromJSON(['google.com', '*.github.com']); + const blacklist = Blacklist.fromJSON(['google.com', '*.github.com']); expect(blacklist.includesEntireBlacklist(new URL('https://google.com'))).to.be.true; expect(blacklist.includesEntireBlacklist(new URL('https://github.com'))).to.be.false; expect(blacklist.includesEntireBlacklist(new URL('https://gist.github.com'))).to.be.true; }); it('does not matches with partial blacklist', () => { - let blacklist = Blacklist.fromJSON(['google.com', { url: 'yahoo.com', keys: ['j', 'k'] }]); + const blacklist = Blacklist.fromJSON(['google.com', { url: 'yahoo.com', keys: ['j', 'k'] }]); expect(blacklist.includesEntireBlacklist(new URL('https://google.com'))).to.be.true; expect(blacklist.includesEntireBlacklist(new URL('https://yahoo.com'))).to.be.false; }); @@ -145,7 +127,7 @@ describe('Blacklist', () => { describe('#includesKeys', () => { it('matches with entire blacklist or keys in the partial blacklist', () => { - let blacklist = Blacklist.fromJSON([ + const blacklist = Blacklist.fromJSON([ 'google.com', { url: 'github.com', keys: ['j', 'k'] }, ]); diff --git a/test/shared/settings/Key.test.ts b/test/shared/settings/Key.test.ts index 8222d5a..3a1c86e 100644 --- a/test/shared/settings/Key.test.ts +++ b/test/shared/settings/Key.test.ts @@ -4,7 +4,7 @@ import Key from '../../../src/shared/settings/Key'; describe("Key", () => { describe('fromMapKey', () => { it('return for X', () => { - let key = Key.fromMapKey('x'); + const key = Key.fromMapKey('x'); expect(key.key).to.equal('x'); expect(key.shift).to.be.false; expect(key.ctrl).to.be.false; @@ -13,7 +13,7 @@ describe("Key", () => { }); it('return for Shift+X', () => { - let key = Key.fromMapKey('X'); + const key = Key.fromMapKey('X'); expect(key.key).to.equal('X'); expect(key.shift).to.be.true; expect(key.ctrl).to.be.false; @@ -22,7 +22,7 @@ describe("Key", () => { }); it('return for Ctrl+X', () => { - let key = Key.fromMapKey('<C-X>'); + const key = Key.fromMapKey('<C-X>'); expect(key.key).to.equal('x'); expect(key.shift).to.be.false; expect(key.ctrl).to.be.true; @@ -31,7 +31,7 @@ describe("Key", () => { }); it('returns for Ctrl+Meta+X', () => { - let key = Key.fromMapKey('<C-M-X>'); + const key = Key.fromMapKey('<C-M-X>'); expect(key.key).to.equal('x'); expect(key.shift).to.be.false; expect(key.ctrl).to.be.true; @@ -40,7 +40,7 @@ describe("Key", () => { }); it('returns for Ctrl+Shift+x', () => { - let key = Key.fromMapKey('<C-S-x>'); + const key = Key.fromMapKey('<C-S-x>'); expect(key.key).to.equal('X'); expect(key.shift).to.be.true; expect(key.ctrl).to.be.true; @@ -49,7 +49,7 @@ describe("Key", () => { }); it('returns for Shift+Esc', () => { - let key = Key.fromMapKey('<S-Esc>'); + const key = Key.fromMapKey('<S-Esc>'); expect(key.key).to.equal('Esc'); expect(key.shift).to.be.true; expect(key.ctrl).to.be.false; @@ -58,7 +58,7 @@ describe("Key", () => { }); it('returns for Ctrl+Esc', () => { - let key = Key.fromMapKey('<C-Esc>'); + const key = Key.fromMapKey('<C-Esc>'); expect(key.key).to.equal('Esc'); expect(key.shift).to.be.false; expect(key.ctrl).to.be.true; @@ -67,7 +67,7 @@ describe("Key", () => { }); it('returns for Ctrl+Esc', () => { - let key = Key.fromMapKey('<C-Space>'); + const key = Key.fromMapKey('<C-Space>'); expect(key.key).to.equal('Space'); expect(key.shift).to.be.false; expect(key.ctrl).to.be.true; @@ -76,17 +76,30 @@ describe("Key", () => { }); }); + describe('idDigit', () => { + it('returns true if the key is a digit', () => { + expect(new Key({ key: '0' }).isDigit()).to.be.true; + expect(new Key({ key: '9' }).isDigit()).to.be.true; + expect(new Key({ key: '9', shift: true }).isDigit()).to.be.true; + + expect(new Key({ key: 'a' }).isDigit()).to.be.false; + expect(new Key({ key: '0' }).isDigit()).to.be.false; + }) + }); + describe('equals', () => { - expect(new Key({ - key: 'x', shift: false, ctrl: true, alt: false, meta: false, - }).equals(new Key({ - key: 'x', shift: false, ctrl: true, alt: false, meta: false, - }))).to.be.true; + it('returns true if the keys are equivalent', () => { + expect(new Key({ + key: 'x', shift: false, ctrl: true, alt: false, meta: false, + }).equals(new Key({ + key: 'x', shift: false, ctrl: true, alt: false, meta: false, + }))).to.be.true; - expect(new Key({ - key: 'x', shift: false, ctrl: false, alt: false, meta: false, - }).equals(new Key({ - key: 'X', shift: true, ctrl: false, alt: false, meta: false, - }))).to.be.false; + expect(new Key({ + key: 'x', shift: false, ctrl: false, alt: false, meta: false, + }).equals(new Key({ + key: 'X', shift: true, ctrl: false, alt: false, meta: false, + }))).to.be.false; + }) }); }); diff --git a/test/shared/settings/KeySequence.test.ts b/test/shared/settings/KeySequence.test.ts deleted file mode 100644 index 361cbd1..0000000 --- a/test/shared/settings/KeySequence.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import KeySequence from '../../../src/shared/settings/KeySequence'; -import { expect } from 'chai' -import Key from "../../../src/shared/settings/Key"; - -describe("KeySequence", () => { - describe('#push', () => { - it('append a key to the sequence', () => { - let seq = new KeySequence([]); - seq.push(Key.fromMapKey('g')); - seq.push(Key.fromMapKey('<S-U>')); - - expect(seq.keys[0].key).to.equal('g'); - expect(seq.keys[1].key).to.equal('U'); - expect(seq.keys[1].shift).to.be.true; - }) - }); - - describe('#startsWith', () => { - it('returns true if the key sequence starts with param', () => { - let seq = new KeySequence([ - Key.fromMapKey('g'), - Key.fromMapKey('<S-U>'), - ]); - - expect(seq.startsWith(new KeySequence([ - ]))).to.be.true; - expect(seq.startsWith(new KeySequence([ - Key.fromMapKey('g'), - ]))).to.be.true; - expect(seq.startsWith(new KeySequence([ - Key.fromMapKey('g'), Key.fromMapKey('<S-U>'), - ]))).to.be.true; - expect(seq.startsWith(new KeySequence([ - Key.fromMapKey('g'), Key.fromMapKey('<S-U>'), Key.fromMapKey('x'), - ]))).to.be.false; - expect(seq.startsWith(new KeySequence([ - Key.fromMapKey('h'), - ]))).to.be.false; - }); - - it('returns true if the empty sequence starts with an empty sequence', () => { - let seq = new KeySequence([]); - - expect(seq.startsWith(new KeySequence([]))).to.be.true; - expect(seq.startsWith(new KeySequence([ - Key.fromMapKey('h'), - ]))).to.be.false; - }) - }); - - describe('#fromMapKeys', () => { - it('returns mapped keys for Shift+Esc', () => { - let keys = KeySequence.fromMapKeys('<S-Esc>').keys; - expect(keys).to.have.lengthOf(1); - expect(keys[0].key).to.equal('Esc'); - expect(keys[0].shift).to.be.true; - }); - - it('returns mapped keys for a<C-B><A-C>d<M-e>', () => { - let keys = KeySequence.fromMapKeys('a<C-B><A-C>d<M-e>').keys; - expect(keys).to.have.lengthOf(5); - expect(keys[0].key).to.equal('a'); - expect(keys[1].ctrl).to.be.true; - expect(keys[1].key).to.equal('b'); - expect(keys[2].alt).to.be.true; - expect(keys[2].key).to.equal('c'); - expect(keys[3].key).to.equal('d'); - expect(keys[4].meta).to.be.true; - expect(keys[4].key).to.equal('e'); - }); - }) -}); diff --git a/test/shared/settings/Keymaps.test.ts b/test/shared/settings/Keymaps.test.ts index 7896a63..dcea6e4 100644 --- a/test/shared/settings/Keymaps.test.ts +++ b/test/shared/settings/Keymaps.test.ts @@ -4,12 +4,12 @@ import { expect } from 'chai'; describe('Keymaps', () => { describe('#valueOf', () => { it('returns empty object by empty settings', () => { - let keymaps = Keymaps.fromJSON({}).toJSON(); + const keymaps = Keymaps.fromJSON({}).toJSON(); expect(keymaps).to.be.empty; }); it('returns keymaps by valid settings', () => { - let keymaps = Keymaps.fromJSON({ + const keymaps = Keymaps.fromJSON({ k: { type: "scroll.vertically", count: -1 }, j: { type: "scroll.vertically", count: 1 }, }).toJSON(); @@ -19,7 +19,6 @@ describe('Keymaps', () => { }); it('throws a TypeError by invalid settings', () => { - expect(() => Keymaps.fromJSON(null)).to.throw(TypeError); expect(() => Keymaps.fromJSON({ k: { type: "invalid.operation" }, })).to.throw(TypeError); @@ -28,7 +27,7 @@ describe('Keymaps', () => { describe('#combine', () => { it('returns combined keymaps', () => { - let keymaps = Keymaps.fromJSON({ + const keymaps = Keymaps.fromJSON({ k: { type: "scroll.vertically", count: -1 }, j: { type: "scroll.vertically", count: 1 }, }).combine(Keymaps.fromJSON({ @@ -36,7 +35,7 @@ describe('Keymaps', () => { N: { type: "find.prev" }, })); - let entries = keymaps.entries().sort(([name1], [name2]) => name1.localeCompare(name2)); + const entries = keymaps.entries().sort(([name1], [name2]) => name1.localeCompare(name2)); expect(entries).deep.equals([ ['j', { type: "scroll.vertically", count: 1 }], ['k', { type: "scroll.vertically", count: -1 }], @@ -46,7 +45,7 @@ describe('Keymaps', () => { }); it('overrides current keymaps', () => { - let keymaps = Keymaps.fromJSON({ + const keymaps = Keymaps.fromJSON({ k: { type: "scroll.vertically", count: -1 }, j: { type: "scroll.vertically", count: 1 }, }).combine(Keymaps.fromJSON({ @@ -54,7 +53,7 @@ describe('Keymaps', () => { j: { type: "find.prev" }, })); - let entries = keymaps.entries().sort(([name1], [name2]) => name1.localeCompare(name2)); + const entries = keymaps.entries().sort(([name1], [name2]) => name1.localeCompare(name2)); expect(entries).deep.equals([ ['j', { type: "find.prev" }], ['k', { type: "scroll.vertically", count: -1 }], diff --git a/test/shared/settings/Properties.test.ts b/test/shared/settings/Properties.test.ts index 609a565..4639839 100644 --- a/test/shared/settings/Properties.test.ts +++ b/test/shared/settings/Properties.test.ts @@ -4,7 +4,7 @@ import { expect } from 'chai'; describe('Properties', () => { describe('#propertiesValueOf', () => { it('returns with default properties by empty settings', () => { - let props = Properties.fromJSON({}); + const props = Properties.fromJSON({}); expect(props).to.deep.equal({ hintchars: "abcdefghijklmnopqrstuvwxyz", smoothscroll: false, @@ -13,7 +13,7 @@ describe('Properties', () => { }); it('returns properties by valid settings', () => { - let props = Properties.fromJSON({ + const props = Properties.fromJSON({ hintchars: "abcdefgh", smoothscroll: false, complete: "sbh" diff --git a/test/shared/settings/Search.test.ts b/test/shared/settings/Search.test.ts index 7c9134d..8bd8d89 100644 --- a/test/shared/settings/Search.test.ts +++ b/test/shared/settings/Search.test.ts @@ -3,7 +3,7 @@ import { expect } from 'chai'; describe('Search', () => { it('returns search settings by valid settings', () => { - let search = Search.fromJSON({ + const search = Search.fromJSON({ default: 'google', engines: { 'google': 'https://google.com/search?q={}', @@ -26,19 +26,6 @@ describe('Search', () => { }); it('throws a TypeError by invalid settings', () => { - expect(() => Search.fromJSON(null)).to.throw(TypeError); - expect(() => Search.fromJSON({})).to.throw(TypeError); - expect(() => Search.fromJSON([])).to.throw(TypeError); - expect(() => Search.fromJSON({ - default: 123, - engines: {} - })).to.throw(TypeError); - expect(() => Search.fromJSON({ - default: 'google', - engines: { - 'google': 123456, - } - })).to.throw(TypeError); expect(() => Search.fromJSON({ default: 'wikipedia', engines: { diff --git a/test/shared/settings/Settings.test.ts b/test/shared/settings/Settings.test.ts index ab6af04..658132c 100644 --- a/test/shared/settings/Settings.test.ts +++ b/test/shared/settings/Settings.test.ts @@ -4,7 +4,7 @@ import { expect } from 'chai'; describe('Settings', () => { describe('#valueOf', () => { it('returns settings by valid settings', () => { - let x = Settings.fromJSON({ + const x = Settings.fromJSON({ keymaps: {}, "search": { "default": "google", @@ -39,7 +39,7 @@ describe('Settings', () => { }); it('sets default settings', () => { - let value = Settings.fromJSON({}); + const value = Settings.fromJSON({}); expect(value.keymaps.toJSON()).to.not.be.empty; expect(value.properties.toJSON()).to.not.be.empty; expect(value.search.defaultEngine).to.be.a('string'); diff --git a/test/shared/urls.test.ts b/test/shared/urls.test.ts index 3a3eea6..6206d03 100644 --- a/test/shared/urls.test.ts +++ b/test/shared/urls.test.ts @@ -36,6 +36,21 @@ describe("shared/commands/parsers", () => { expect(parsers.searchUrl('std::vector', config)) .to.equal('https://google.com/search?q=std%3A%3Avector'); }); + + it('localhost urls', () => { + expect(parsers.searchUrl('localhost', config)) + .to.equal('http://localhost'); + expect(parsers.searchUrl('http://localhost', config)) + .to.equal('http://localhost/'); + expect(parsers.searchUrl('localhost:8080', config)) + .to.equal('http://localhost:8080'); + expect(parsers.searchUrl('localhost:80nan', config)) + .to.equal('https://google.com/search?q=localhost%3A80nan'); + expect(parsers.searchUrl('localhost 8080', config)) + .to.equal('https://google.com/search?q=localhost%208080'); + expect(parsers.searchUrl('localhost:80/build', config)) + .to.equal('http://localhost:80/build') + }) }); describe('#normalizeUrl', () => { @@ -47,4 +62,3 @@ describe("shared/commands/parsers", () => { }); }); }); - diff --git a/tsconfig.json b/tsconfig.json index 9f90223..f3a6a33 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "module": "commonjs", "lib": ["es6", "dom", "es2017"], "allowJs": true, - "checkJs": true, + "checkJs": false, "jsx": "react", "sourceMap": true, "outDir": "./build", diff --git a/webpack.config.js b/webpack.config.js index 1cd05df..ee252bf 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -4,7 +4,7 @@ const path = require('path'); const src = path.resolve(__dirname, 'src'); const dist = path.resolve(__dirname, 'build'); -config = { +const config = { entry: { content: path.join(src, 'content'), settings: path.join(src, 'settings'), |