diff options
200 files changed, 4887 insertions, 1988 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index 2cd4c02..22dbcfc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2.1 executors: default: docker: - - image: circleci/node:12.14.0-stretch-browsers + - image: circleci/node:14.15.1-stretch-browsers environment: - FIREFOX_VERSION: "78.3.0esr" - GECKODRIVER_VERSION: "0.27.0" @@ -18,7 +18,7 @@ For usage and more detailed information, check out our [documentations][document ## Copyright -Copyright © 2017-2019 by Shin'ya Ueoka +Copyright © 2017-2020 by Shin'ya Ueoka ## Licence diff --git a/e2e/tab.test.ts b/e2e/tab.test.ts index 98ec9f8..49d1c03 100644 --- a/e2e/tab.test.ts +++ b/e2e/tab.test.ts @@ -79,8 +79,10 @@ describe("tab test", () => { const page = await Page.currentContext(webdriver); await page.sendKeys("x", "$"); - const current = await browser.tabs.query({ windowId: win.id }); - assert.strictEqual(current.length, 2); + await eventually(async () => { + const current = await browser.tabs.query({ windowId: win.id }); + assert.strictEqual(current.length, 2); + }); }); it("duplicates tab by zd", async () => { diff --git a/package.json b/package.json index 67ae266..d4fcfb1 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "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", + "build": "NODE_ENV=production webpack --mode production --progress --devtool inline-source-map", "package": "yarn build && script/package", "lint": "eslint --ext .ts,.tsx .", "lint:fix": "eslint --ext .ts,.tsx . --fix", @@ -27,12 +27,12 @@ "@types/chai": "^4.2.8", "@types/express": "^4.17.2", "@types/mocha": "^8.0.1", - "@types/node": "^12.12.21", + "@types/node": "^14.14.13", "@types/prop-types": "^15.7.3", - "@types/react": "^16.9.46", - "@types/react-dom": "^16.9.5", + "@types/react": "^17.0.0", + "@types/react-dom": "^17.0.0", "@types/react-redux": "^7.1.7", - "@types/react-test-renderer": "^16.9.2", + "@types/react-test-renderer": "^17.0.0", "@types/redux-promise": "^0.5.28", "@types/selenium-webdriver": "^4.0.6", "@types/sinon": "^9.0.0", @@ -40,20 +40,20 @@ "@typescript-eslint/eslint-plugin": "3.9.0", "@typescript-eslint/parser": "3.10.1", "ajv": "^6.11.0", - "ajv-cli": "^3.0.0", + "ajv-cli": "^4.0.1", "chai": "^4.2.0", - "css-loader": "^4.2.1", - "eslint": "7.10.0", - "eslint-config-prettier": "6.11.0", + "css-loader": "^5.0.1", + "eslint": "7.15.0", + "eslint-config-prettier": "7.0.0", "eslint-plugin-prettier": "3.1.4", - "eslint-plugin-react": "7.21.2", - "eslint-plugin-standard": "^4.0.1", + "eslint-plugin-react": "7.21.5", + "eslint-plugin-standard": "^5.0.0", "express": "^4.17.1", - "html-webpack-plugin": "4.3.0", + "html-webpack-plugin": "4.5.0", "jsonwebtoken": "^8.5.1", "jszip": "^3.2.2", "karma": "^5.0.0", - "karma-firefox-launcher": "^1.3.0", + "karma-firefox-launcher": "^2.1.0", "karma-html2js-preprocessor": "^1.1.0", "karma-mocha": "^2.0.1", "karma-mocha-reporter": "^2.2.5", @@ -62,9 +62,9 @@ "karma-webpack": "^4.0.2", "lanthan": "0.0.2", "mocha": "^8.1.1", - "prettier": "2.1.2", - "prettier-eslint": "11.0.0", - "react": "16.13.1", + "prettier": "2.2.1", + "prettier-eslint": "12.0.0", + "react": "16.14.0", "react-dom": "16.13.1", "react-redux": "^7.1.3", "react-test-renderer": "16.13.1", @@ -74,15 +74,15 @@ "request-promise-native": "^1.0.8", "sinon": "^9.0.3", "sinon-chrome": "^3.0.1", - "style-loader": "^1.1.3", + "style-loader": "^2.0.0", "styled-components": "^5.1.1", "ts-loader": "^8.0.2", "ts-node": "^9.0.0", - "tsyringe": "4.3.0", + "tsyringe": "4.4.0", "typescript": "4.0.3", "web-ext-types": "^3.2.1", "webextensions-api-fake": "^0.9.1", - "webpack": "4.44.1", - "webpack-cli": "3.3.12" + "webpack": "4.44.2", + "webpack-cli": "4.2.0" } } diff --git a/src/background/clients/NavigateClient.ts b/src/background/clients/NavigateClient.ts index 40ceb45..af8688e 100644 --- a/src/background/clients/NavigateClient.ts +++ b/src/background/clients/NavigateClient.ts @@ -1,8 +1,16 @@ -import { injectable } from "tsyringe"; import * as messages from "../../shared/messages"; -@injectable() -export default class NavigateClient { +export default interface NavigateClient { + historyNext(tabId: number): Promise<void>; + + historyPrev(tabId: number): Promise<void>; + + linkNext(tabId: number): Promise<void>; + + linkPrev(tabId: number): Promise<void>; +} + +export class NavigateClientImpl implements NavigateClient { async historyNext(tabId: number): Promise<void> { await browser.tabs.sendMessage(tabId, { type: messages.NAVIGATE_HISTORY_NEXT, diff --git a/src/background/controllers/OperationController.ts b/src/background/controllers/OperationController.ts index 5a7047d..d606ac0 100644 --- a/src/background/controllers/OperationController.ts +++ b/src/background/controllers/OperationController.ts @@ -1,23 +1,14 @@ -import { injectable } from "tsyringe"; +import { inject, injectable } from "tsyringe"; import * as operations from "../../shared/operations"; -import FindUseCase from "../usecases/FindUseCase"; -import ConsoleUseCase from "../usecases/ConsoleUseCase"; -import TabUseCase from "../usecases/TabUseCase"; -import TabSelectUseCase from "../usecases/TabSelectUseCase"; -import ZoomUseCase from "../usecases/ZoomUseCase"; -import NavigateUseCase from "../usecases/NavigateUseCase"; +import OperatorFactory from "../operators/OperatorFactory"; import RepeatUseCase from "../usecases/RepeatUseCase"; @injectable() export default class OperationController { constructor( - private findUseCase: FindUseCase, - private consoleUseCase: ConsoleUseCase, - private tabUseCase: TabUseCase, - private tabSelectUseCase: TabSelectUseCase, - private zoomUseCase: ZoomUseCase, - private navigateUseCase: NavigateUseCase, - private repeatUseCase: RepeatUseCase + private readonly repeatUseCase: RepeatUseCase, + @inject("OperatorFactory") + private readonly operatorFactory: OperatorFactory ) {} async exec(repeat: number, op: operations.Operation): Promise<any> { @@ -27,105 +18,14 @@ export default class OperationController { } } - // eslint-disable-next-line complexity, max-lines-per-function - async doOperation( + private 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); - } - })(); - + const operator = this.operatorFactory.create(operation); for (let i = 0; i < repeat; ++i) { // eslint-disable-next-line no-await-in-loop - await opFunc(); + await operator.run(); } } } diff --git a/src/background/di.ts b/src/background/di.ts index 5e6ad03..358ff1e 100644 --- a/src/background/di.ts +++ b/src/background/di.ts @@ -11,9 +11,16 @@ import HistoryRepositoryImpl from "./completion/impl/HistoryRepositoryImpl"; import BookmarkRepositoryImpl from "./completion/impl/BookmarkRepositoryImpl"; import TabRepositoryImpl from "./completion/impl/TabRepositoryImpl"; import { TabPresenterImpl } from "./presenters/TabPresenter"; +import { OperatorFactoryImpl } from "./operators/impls/OperatorFactoryImpl"; +import { NavigateClientImpl } from "./clients/NavigateClient"; +import { ConsoleClientImpl } from "./infrastructures/ConsoleClient"; +import { BrowserSettingRepositoryImpl } from "./repositories/BrowserSettingRepository"; +import { RepeatRepositoryImpl } from "./repositories/RepeatRepository"; +import { ZoomPresenterImpl } from "./presenters/ZoomPresenter"; +import { WindowPresenterImpl } from "./presenters/WindowPresenter"; container.register("LocalSettingRepository", { - useValue: LocalSettingRepository, + useClass: LocalSettingRepository, }); container.register("SyncSettingRepository", { useClass: SyncSettingRepository, @@ -24,5 +31,14 @@ container.register("CachedSettingRepository", { container.register("Notifier", { useClass: NotifierImpl }); container.register("HistoryRepository", { useClass: HistoryRepositoryImpl }); container.register("BookmarkRepository", { useClass: BookmarkRepositoryImpl }); +container.register("BrowserSettingRepository", { + useClass: BrowserSettingRepositoryImpl, +}); +container.register("RepeatRepository", { useClass: RepeatRepositoryImpl }); container.register("TabRepository", { useClass: TabRepositoryImpl }); +container.register("ZoomPresenter", { useClass: ZoomPresenterImpl }); container.register("TabPresenter", { useClass: TabPresenterImpl }); +container.register("WindowPresenter", { useClass: WindowPresenterImpl }); +container.register("NavigateClient", { useClass: NavigateClientImpl }); +container.register("ConsoleClient", { useClass: ConsoleClientImpl }); +container.register("OperatorFactory", { useClass: OperatorFactoryImpl }); diff --git a/src/background/infrastructures/ConsoleClient.ts b/src/background/infrastructures/ConsoleClient.ts index 8d0af89..2a1df5b 100644 --- a/src/background/infrastructures/ConsoleClient.ts +++ b/src/background/infrastructures/ConsoleClient.ts @@ -1,8 +1,20 @@ import { injectable } from "tsyringe"; import * as messages from "../../shared/messages"; +export default interface ConsoleClient { + showCommand(tabId: number, command: string): Promise<any>; + + showFind(tabId: number): Promise<any>; + + showInfo(tabId: number, message: string): Promise<any>; + + showError(tabId: number, message: string): Promise<any>; + + hide(tabId: number): Promise<any>; +} + @injectable() -export default class ConsoleClient { +export class ConsoleClientImpl implements ConsoleClient { showCommand(tabId: number, command: string): Promise<any> { return browser.tabs.sendMessage(tabId, { type: messages.CONSOLE_SHOW_COMMAND, diff --git a/src/background/operators/Operator.ts b/src/background/operators/Operator.ts new file mode 100644 index 0000000..3b1fe03 --- /dev/null +++ b/src/background/operators/Operator.ts @@ -0,0 +1,5 @@ +interface Operator { + run(): Promise<void>; +} + +export default Operator; diff --git a/src/background/operators/OperatorFactory.ts b/src/background/operators/OperatorFactory.ts new file mode 100644 index 0000000..9a5234c --- /dev/null +++ b/src/background/operators/OperatorFactory.ts @@ -0,0 +1,6 @@ +import Operator from "./Operator"; +import { Operation } from "../../shared/operations"; + +export default interface OperatorFactory { + create(op: Operation): Operator; +} diff --git a/src/background/operators/OperatorFactoryChain.ts b/src/background/operators/OperatorFactoryChain.ts new file mode 100644 index 0000000..046ed42 --- /dev/null +++ b/src/background/operators/OperatorFactoryChain.ts @@ -0,0 +1,6 @@ +import Operator from "./Operator"; +import { Operation } from "../../shared/operations"; + +export default interface OperatorFactoryChain { + create(op: Operation): Operator | null; +} diff --git a/src/background/operators/impls/CancelOperator.ts b/src/background/operators/impls/CancelOperator.ts new file mode 100644 index 0000000..7ab09fb --- /dev/null +++ b/src/background/operators/impls/CancelOperator.ts @@ -0,0 +1,15 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; +import ConsoleClient from "../../infrastructures/ConsoleClient"; + +export default class CancelOperator implements Operator { + constructor( + private readonly tabPresenter: TabPresenter, + private readonly consoleClient: ConsoleClient + ) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + return this.consoleClient.hide(tab.id as number); + } +} diff --git a/src/background/operators/impls/CloseTabOperator.ts b/src/background/operators/impls/CloseTabOperator.ts new file mode 100644 index 0000000..5d8e80b --- /dev/null +++ b/src/background/operators/impls/CloseTabOperator.ts @@ -0,0 +1,22 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class CloseTabOperator implements Operator { + constructor( + private readonly tabPresenter: TabPresenter, + private readonly force: boolean = false, + private readonly selectLeft: boolean = false + ) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + if (!this.force && tab.pinned) { + return Promise.resolve(); + } + if (this.selectLeft && tab.index > 0) { + 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]); + } +} diff --git a/src/background/operators/impls/CloseTabRightOperator.ts b/src/background/operators/impls/CloseTabRightOperator.ts new file mode 100644 index 0000000..f36930e --- /dev/null +++ b/src/background/operators/impls/CloseTabRightOperator.ts @@ -0,0 +1,21 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class CloseTabRightOperator implements Operator { + constructor(private readonly tabPresenter: TabPresenter) {} + + async run(): Promise<void> { + const tabs = await this.tabPresenter.getAll(); + tabs.sort((t1, t2) => t1.index - t2.index); + const index = tabs.findIndex((t) => t.active); + if (index < 0) { + return; + } + for (let i = index + 1; i < tabs.length; ++i) { + const tab = tabs[i]; + if (!tab.pinned) { + await this.tabPresenter.remove([tab.id as number]); + } + } + } +} diff --git a/src/background/operators/impls/CommandOperatorFactoryChain.ts b/src/background/operators/impls/CommandOperatorFactoryChain.ts new file mode 100644 index 0000000..680a384 --- /dev/null +++ b/src/background/operators/impls/CommandOperatorFactoryChain.ts @@ -0,0 +1,63 @@ +import { inject, injectable } from "tsyringe"; +import Operator from "../Operator"; +import OperatorFactoryChain from "../OperatorFactoryChain"; +import ShowCommandOperator from "./ShowCommandOperator"; +import ShowOpenCommandOperator from "./ShowOpenCommandOperator"; +import ShowTabOpenCommandOperator from "./ShowTabOpenCommandOperator"; +import ShowWinOpenCommandOperator from "./ShowWinOpenCommandOperator"; +import ShowBufferCommandOperator from "./ShowBufferCommandOperator"; +import ShowAddBookmarkOperator from "./ShowAddBookmarkOperator"; +import TabPresenter from "../../presenters/TabPresenter"; +import ConsoleClient from "../../infrastructures/ConsoleClient"; +import * as operations from "../../../shared/operations"; +import StartFindOperator from "./StartFindOperator"; + +@injectable() +export default class CommandOperatorFactoryChain + implements OperatorFactoryChain { + constructor( + @inject("TabPresenter") + private readonly tabPresenter: TabPresenter, + @inject("ConsoleClient") + private readonly consoleClient: ConsoleClient + ) {} + + create(op: operations.Operation): Operator | null { + switch (op.type) { + case operations.COMMAND_SHOW: + return new ShowCommandOperator(this.tabPresenter, this.consoleClient); + case operations.COMMAND_SHOW_OPEN: + return new ShowOpenCommandOperator( + this.tabPresenter, + this.consoleClient, + op.alter + ); + case operations.COMMAND_SHOW_TABOPEN: + return new ShowTabOpenCommandOperator( + this.tabPresenter, + this.consoleClient, + op.alter + ); + case operations.COMMAND_SHOW_WINOPEN: + return new ShowWinOpenCommandOperator( + this.tabPresenter, + this.consoleClient, + op.alter + ); + case operations.COMMAND_SHOW_BUFFER: + return new ShowBufferCommandOperator( + this.tabPresenter, + this.consoleClient + ); + case operations.COMMAND_SHOW_ADDBOOKMARK: + return new ShowAddBookmarkOperator( + this.tabPresenter, + this.consoleClient, + op.alter + ); + case operations.FIND_START: + return new StartFindOperator(this.tabPresenter, this.consoleClient); + } + return null; + } +} diff --git a/src/background/operators/impls/DuplicateTabOperator.ts b/src/background/operators/impls/DuplicateTabOperator.ts new file mode 100644 index 0000000..7737cfa --- /dev/null +++ b/src/background/operators/impls/DuplicateTabOperator.ts @@ -0,0 +1,11 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class DuplicateTabOperator implements Operator { + constructor(private readonly tabPresenter: TabPresenter) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + await this.tabPresenter.duplicate(tab.id as number); + } +} diff --git a/src/background/operators/impls/InternalOpenURLOperator.ts b/src/background/operators/impls/InternalOpenURLOperator.ts new file mode 100644 index 0000000..6bf513b --- /dev/null +++ b/src/background/operators/impls/InternalOpenURLOperator.ts @@ -0,0 +1,24 @@ +import Operator from "../Operator"; +import WindowPresenter from "../../presenters/WindowPresenter"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class InternalOpenURLOperator implements Operator { + constructor( + private readonly windowPresenter: WindowPresenter, + private readonly tabPresenter: TabPresenter, + private readonly url: string, + private readonly newTab?: boolean, + private readonly newWindow?: boolean + ) {} + + async run(): Promise<void> { + if (this.newWindow) { + await this.windowPresenter.create(this.url); + } else if (this.newTab) { + await this.tabPresenter.create(this.url); + } else { + const tab = await this.tabPresenter.getCurrent(); + await this.tabPresenter.open(this.url, tab.id); + } + } +} diff --git a/src/background/operators/impls/InternalOperatorFactoryChain.ts b/src/background/operators/impls/InternalOperatorFactoryChain.ts new file mode 100644 index 0000000..05615f6 --- /dev/null +++ b/src/background/operators/impls/InternalOperatorFactoryChain.ts @@ -0,0 +1,38 @@ +import { inject, injectable } from "tsyringe"; +import Operator from "../Operator"; +import OperatorFactoryChain from "../OperatorFactoryChain"; +import CancelOperator from "./CancelOperator"; +import InternalOpenURLOperator from "./InternalOpenURLOperator"; +import TabPresenter from "../../presenters/TabPresenter"; +import ConsoleClient from "../../infrastructures/ConsoleClient"; +import WindowPresenter from "../../presenters/WindowPresenter"; +import * as operations from "../../../shared/operations"; + +@injectable() +export default class InternalOperatorFactoryChain + implements OperatorFactoryChain { + constructor( + @inject("WindowPresenter") + private readonly windowPresenter: WindowPresenter, + @inject("TabPresenter") + private readonly tabPresenter: TabPresenter, + @inject("ConsoleClient") + private readonly consoleClient: ConsoleClient + ) {} + + create(op: operations.Operation): Operator | null { + switch (op.type) { + case operations.CANCEL: + return new CancelOperator(this.tabPresenter, this.consoleClient); + case operations.INTERNAL_OPEN_URL: + return new InternalOpenURLOperator( + this.windowPresenter, + this.tabPresenter, + op.url, + op.newTab, + op.newWindow + ); + } + return null; + } +} diff --git a/src/background/operators/impls/NavigateHistoryNextOperator.ts b/src/background/operators/impls/NavigateHistoryNextOperator.ts new file mode 100644 index 0000000..b092c48 --- /dev/null +++ b/src/background/operators/impls/NavigateHistoryNextOperator.ts @@ -0,0 +1,15 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; +import NavigateClient from "../../clients/NavigateClient"; + +export default class NavigateHistoryNextOperator implements Operator { + constructor( + private readonly tabPresenter: TabPresenter, + private readonly navigateClient: NavigateClient + ) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + await this.navigateClient.historyNext(tab.id!); + } +} diff --git a/src/background/operators/impls/NavigateHistoryPrevOperator.ts b/src/background/operators/impls/NavigateHistoryPrevOperator.ts new file mode 100644 index 0000000..27d4ee9 --- /dev/null +++ b/src/background/operators/impls/NavigateHistoryPrevOperator.ts @@ -0,0 +1,15 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; +import NavigateClient from "../../clients/NavigateClient"; + +export default class NavigateHistoryPrevOperator implements Operator { + constructor( + private readonly tabPresenter: TabPresenter, + private readonly navigateClient: NavigateClient + ) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + await this.navigateClient.historyPrev(tab.id!); + } +} diff --git a/src/background/operators/impls/NavigateLinkNextOperator.ts b/src/background/operators/impls/NavigateLinkNextOperator.ts new file mode 100644 index 0000000..dbbcc45 --- /dev/null +++ b/src/background/operators/impls/NavigateLinkNextOperator.ts @@ -0,0 +1,15 @@ +import Operator from "../Operator"; +import NavigateClient from "../../clients/NavigateClient"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class NavigateLinkNextOperator implements Operator { + constructor( + private readonly tabPresenter: TabPresenter, + private readonly navigateClient: NavigateClient + ) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + await this.navigateClient.linkNext(tab.id!); + } +} diff --git a/src/background/operators/impls/NavigateLinkPrevOperator.ts b/src/background/operators/impls/NavigateLinkPrevOperator.ts new file mode 100644 index 0000000..fe41ee6 --- /dev/null +++ b/src/background/operators/impls/NavigateLinkPrevOperator.ts @@ -0,0 +1,15 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; +import NavigateClient from "../../clients/NavigateClient"; + +export default class NavigateLinkPrevOperator implements Operator { + constructor( + private readonly tabPresenter: TabPresenter, + private readonly navigateClient: NavigateClient + ) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + await this.navigateClient.linkPrev(tab.id!); + } +} diff --git a/src/background/operators/impls/NavigateOperatorFactoryChain.ts b/src/background/operators/impls/NavigateOperatorFactoryChain.ts new file mode 100644 index 0000000..618db45 --- /dev/null +++ b/src/background/operators/impls/NavigateOperatorFactoryChain.ts @@ -0,0 +1,66 @@ +import { inject, injectable } from "tsyringe"; +import Operator from "../Operator"; +import OperatorFactoryChain from "../OperatorFactoryChain"; +import NavigateHistoryPrevOperator from "./NavigateHistoryPrevOperator"; +import NavigateHistoryNextOperator from "./NavigateHistoryNextOperator"; +import NavigateLinkPrevOperator from "./NavigateLinkPrevOperator"; +import NavigateLinkNextOperator from "./NavigateLinkNextOperator"; +import NavigateParentOperator from "./NavigateParentOperator"; +import NavigateRootOperator from "./NavigateRootOperator"; +import OpenSourceOperator from "./OpenSourceOperator"; +import OpenHomeOperator from "./OpenHomeOperator"; +import TabPresenter from "../../presenters/TabPresenter"; +import NavigateClient from "../../clients/NavigateClient"; +import BrowserSettingRepository from "../../repositories/BrowserSettingRepository"; +import * as operations from "../../../shared/operations"; + +@injectable() +export default class NavigateOperatorFactoryChain + implements OperatorFactoryChain { + constructor( + @inject("TabPresenter") + private readonly tabPresenter: TabPresenter, + @inject("NavigateClient") + private readonly navigateClient: NavigateClient, + @inject("BrowserSettingRepository") + private readonly browserSettingRepository: BrowserSettingRepository + ) {} + + create(op: operations.Operation): Operator | null { + switch (op.type) { + case operations.NAVIGATE_HISTORY_PREV: + return new NavigateHistoryPrevOperator( + this.tabPresenter, + this.navigateClient + ); + case operations.NAVIGATE_HISTORY_NEXT: + return new NavigateHistoryNextOperator( + this.tabPresenter, + this.navigateClient + ); + case operations.NAVIGATE_LINK_PREV: + return new NavigateLinkPrevOperator( + this.tabPresenter, + this.navigateClient + ); + case operations.NAVIGATE_LINK_NEXT: + return new NavigateLinkNextOperator( + this.tabPresenter, + this.navigateClient + ); + case operations.NAVIGATE_PARENT: + return new NavigateParentOperator(this.tabPresenter); + case operations.NAVIGATE_ROOT: + return new NavigateRootOperator(this.tabPresenter); + case operations.PAGE_SOURCE: + return new OpenSourceOperator(this.tabPresenter); + case operations.PAGE_HOME: + return new OpenHomeOperator( + this.tabPresenter, + this.browserSettingRepository, + op.newTab + ); + } + return null; + } +} diff --git a/src/background/operators/impls/NavigateParentOperator.ts b/src/background/operators/impls/NavigateParentOperator.ts new file mode 100644 index 0000000..652cfb8 --- /dev/null +++ b/src/background/operators/impls/NavigateParentOperator.ts @@ -0,0 +1,25 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class NavigateParentOperator implements Operator { + constructor(private readonly tabPresenter: TabPresenter) {} + + async run(): Promise<void> { + 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) { + url.search = ""; + } else { + const basenamePattern = /\/[^/]+$/; + const lastDirPattern = /\/[^/]+\/$/; + if (basenamePattern.test(url.pathname)) { + url.pathname = url.pathname.replace(basenamePattern, "/"); + } else if (lastDirPattern.test(url.pathname)) { + url.pathname = url.pathname.replace(lastDirPattern, "/"); + } + } + await this.tabPresenter.open(url.href); + } +} diff --git a/src/background/operators/impls/NavigateRootOperator.ts b/src/background/operators/impls/NavigateRootOperator.ts new file mode 100644 index 0000000..b140156 --- /dev/null +++ b/src/background/operators/impls/NavigateRootOperator.ts @@ -0,0 +1,12 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class NavigateRootOperator implements Operator { + constructor(private readonly tabPresenter: TabPresenter) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + const url = new URL(tab.url!); + await this.tabPresenter.open(url.origin); + } +} diff --git a/src/background/operators/impls/OpenHomeOperator.ts b/src/background/operators/impls/OpenHomeOperator.ts new file mode 100644 index 0000000..4773be6 --- /dev/null +++ b/src/background/operators/impls/OpenHomeOperator.ts @@ -0,0 +1,29 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; +import BrowserSettingRepository from "../../repositories/BrowserSettingRepository"; + +export default class OpenHomeOperator implements Operator { + constructor( + private readonly tabPresenter: TabPresenter, + private readonly browserSettingRepository: BrowserSettingRepository, + private readonly newTab: boolean + ) {} + + async run(): Promise<void> { + 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" + ); + } + if (urls.length === 1 && !this.newTab) { + await this.tabPresenter.open(urls[0], tab.id); + return; + } + for (const url of urls) { + await this.tabPresenter.create(url); + } + } +} diff --git a/src/background/operators/impls/OpenSourceOperator.ts b/src/background/operators/impls/OpenSourceOperator.ts new file mode 100644 index 0000000..9185ba5 --- /dev/null +++ b/src/background/operators/impls/OpenSourceOperator.ts @@ -0,0 +1,12 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class OpenSourceOperator implements Operator { + constructor(private readonly tabPresenter: TabPresenter) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + const url = "view-source:" + tab.url; + await this.tabPresenter.create(url); + } +} diff --git a/src/background/operators/impls/OperatorFactoryImpl.ts b/src/background/operators/impls/OperatorFactoryImpl.ts new file mode 100644 index 0000000..34e7bb5 --- /dev/null +++ b/src/background/operators/impls/OperatorFactoryImpl.ts @@ -0,0 +1,45 @@ +import { inject, delay, injectable } from "tsyringe"; +import Operator from "../Operator"; +import OperatorFactory from "../OperatorFactory"; +import OperatorFactoryChain from "../OperatorFactoryChain"; +import CommandOperatorFactoryChain from "./CommandOperatorFactoryChain"; +import InternalOperatorFactoryChain from "./InternalOperatorFactoryChain"; +import NavigateOperatorFactoryChain from "./NavigateOperatorFactoryChain"; +import RepeatOperatorFactoryChain from "./RepeatOperatorFactoryChain"; +import TabOperatorFactoryChain from "./TabOperatorFactoryChain"; +import ZoomOperatorFactoryChain from "./ZoomOperatorFactoryChain"; +import * as operations from "../../../shared/operations"; + +@injectable() +export class OperatorFactoryImpl implements OperatorFactory { + private readonly factoryChains: OperatorFactoryChain[]; + + constructor( + commandOperatorFactoryChain: CommandOperatorFactoryChain, + internalOperatorFactoryChain: InternalOperatorFactoryChain, + navigateOperatorFactoryChain: NavigateOperatorFactoryChain, + tabOperatorFactoryChain: TabOperatorFactoryChain, + zoomOperatorFactoryChain: ZoomOperatorFactoryChain, + @inject(delay(() => RepeatOperatorFactoryChain)) + repeatOperatorFactoryChain: RepeatOperatorFactoryChain + ) { + this.factoryChains = [ + commandOperatorFactoryChain, + internalOperatorFactoryChain, + navigateOperatorFactoryChain, + repeatOperatorFactoryChain, + tabOperatorFactoryChain, + zoomOperatorFactoryChain, + ]; + } + + create(op: operations.Operation): Operator { + for (const chain of this.factoryChains) { + const operator = chain.create(op); + if (operator !== null) { + return operator; + } + } + throw new Error("unknown operation: " + op.type); + } +} diff --git a/src/background/operators/impls/PinTabOperator.ts b/src/background/operators/impls/PinTabOperator.ts new file mode 100644 index 0000000..8121725 --- /dev/null +++ b/src/background/operators/impls/PinTabOperator.ts @@ -0,0 +1,11 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class PinTabOperator implements Operator { + constructor(private readonly tabPresenter: TabPresenter) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + return this.tabPresenter.setPinned(tab.id as number, true); + } +} diff --git a/src/background/operators/impls/ReloadTabOperator.ts b/src/background/operators/impls/ReloadTabOperator.ts new file mode 100644 index 0000000..db3389e --- /dev/null +++ b/src/background/operators/impls/ReloadTabOperator.ts @@ -0,0 +1,14 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class ReloadTabOperator implements Operator { + constructor( + private readonly tabPresenter: TabPresenter, + private readonly cache: boolean + ) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + return this.tabPresenter.reload(tab.id as number, this.cache); + } +} diff --git a/src/background/operators/impls/ReopenTabOperator.ts b/src/background/operators/impls/ReopenTabOperator.ts new file mode 100644 index 0000000..507b4a8 --- /dev/null +++ b/src/background/operators/impls/ReopenTabOperator.ts @@ -0,0 +1,10 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class ReopenTabOperator implements Operator { + constructor(private readonly tabPresenter: TabPresenter) {} + + run(): Promise<void> { + return this.tabPresenter.reopen(); + } +} diff --git a/src/background/operators/impls/RepeatLastOperator.ts b/src/background/operators/impls/RepeatLastOperator.ts new file mode 100644 index 0000000..d46daab --- /dev/null +++ b/src/background/operators/impls/RepeatLastOperator.ts @@ -0,0 +1,18 @@ +import Operator from "../Operator"; +import RepeatRepository from "../../repositories/RepeatRepository"; +import OperatorFactory from "../OperatorFactory"; + +export default class RepeatLastOperator implements Operator { + constructor( + private readonly repeatRepository: RepeatRepository, + private readonly operatorFactory: OperatorFactory + ) {} + + run(): Promise<void> { + const op = this.repeatRepository.getLastOperation(); + if (typeof op === "undefined") { + return Promise.resolve(); + } + return this.operatorFactory.create(op).run(); + } +} diff --git a/src/background/operators/impls/RepeatOperatorFactoryChain.ts b/src/background/operators/impls/RepeatOperatorFactoryChain.ts new file mode 100644 index 0000000..5038d48 --- /dev/null +++ b/src/background/operators/impls/RepeatOperatorFactoryChain.ts @@ -0,0 +1,29 @@ +import { inject, injectable } from "tsyringe"; +import Operator from "../Operator"; +import OperatorFactoryChain from "../OperatorFactoryChain"; +import RepeatLastOperator from "./RepeatLastOperator"; +import RepeatRepository from "../../repositories/RepeatRepository"; +import OperatorFactory from "../OperatorFactory"; +import * as operations from "../../../shared/operations"; + +@injectable() +export default class RepeatOperatorFactoryChain + implements OperatorFactoryChain { + constructor( + @inject("RepeatRepository") + private readonly repeatRepository: RepeatRepository, + @inject("OperatorFactory") + private readonly operatorFactory: OperatorFactory + ) {} + + create(op: operations.Operation): Operator | null { + switch (op.type) { + case operations.REPEAT_LAST: + return new RepeatLastOperator( + this.repeatRepository, + this.operatorFactory + ); + } + return null; + } +} diff --git a/src/background/operators/impls/ResetZoomOperator.ts b/src/background/operators/impls/ResetZoomOperator.ts new file mode 100644 index 0000000..96af733 --- /dev/null +++ b/src/background/operators/impls/ResetZoomOperator.ts @@ -0,0 +1,10 @@ +import Operator from "../Operator"; +import ZoomPresenter from "../../presenters/ZoomPresenter"; + +export default class ResetZoomOperator implements Operator { + constructor(private readonly zoomPresenter: ZoomPresenter) {} + + run(): Promise<void> { + return this.zoomPresenter.resetZoom(); + } +} diff --git a/src/background/operators/impls/SelectFirstTabOperator.ts b/src/background/operators/impls/SelectFirstTabOperator.ts new file mode 100644 index 0000000..c04b8a9 --- /dev/null +++ b/src/background/operators/impls/SelectFirstTabOperator.ts @@ -0,0 +1,11 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class SelectFirstTabOperator implements Operator { + constructor(private readonly tabPresenter: TabPresenter) {} + + async run(): Promise<void> { + const tabs = await this.tabPresenter.getAll(); + return this.tabPresenter.select(tabs[0].id as number); + } +} diff --git a/src/background/operators/impls/SelectLastTabOperator.ts b/src/background/operators/impls/SelectLastTabOperator.ts new file mode 100644 index 0000000..e16d406 --- /dev/null +++ b/src/background/operators/impls/SelectLastTabOperator.ts @@ -0,0 +1,11 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class SelectLastTabOperator implements Operator { + constructor(private readonly tabPresenter: TabPresenter) {} + + async run(): Promise<void> { + const tabs = await this.tabPresenter.getAll(); + return this.tabPresenter.select(tabs[tabs.length - 1].id as number); + } +} diff --git a/src/background/operators/impls/SelectPreviousSelectedTabOperator.ts b/src/background/operators/impls/SelectPreviousSelectedTabOperator.ts new file mode 100644 index 0000000..03a778d --- /dev/null +++ b/src/background/operators/impls/SelectPreviousSelectedTabOperator.ts @@ -0,0 +1,14 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class SelectPreviousSelectedTabOperator implements Operator { + constructor(private readonly tabPresenter: TabPresenter) {} + + async run(): Promise<void> { + const tabId = await this.tabPresenter.getLastSelectedId(); + if (tabId === null || typeof tabId === "undefined") { + return Promise.resolve(); + } + return this.tabPresenter.select(tabId); + } +} diff --git a/src/background/operators/impls/SelectTabNextOperator.ts b/src/background/operators/impls/SelectTabNextOperator.ts new file mode 100644 index 0000000..34d37ff --- /dev/null +++ b/src/background/operators/impls/SelectTabNextOperator.ts @@ -0,0 +1,19 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class SelectTabNextOperator implements Operator { + constructor(private readonly tabPresenter: TabPresenter) {} + + async run(): Promise<void> { + const tabs = await this.tabPresenter.getAll(); + if (tabs.length < 2) { + return; + } + const tab = tabs.find((t) => t.active); + if (!tab) { + return; + } + const select = (tab.index + 1) % tabs.length; + return this.tabPresenter.select(tabs[select].id as number); + } +} diff --git a/src/background/operators/impls/SelectTabPrevOperator.ts b/src/background/operators/impls/SelectTabPrevOperator.ts new file mode 100644 index 0000000..3776c1a --- /dev/null +++ b/src/background/operators/impls/SelectTabPrevOperator.ts @@ -0,0 +1,19 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class SelectTabPrevOperator implements Operator { + constructor(private readonly tabPresenter: TabPresenter) {} + + async run(): Promise<void> { + const tabs = await this.tabPresenter.getAll(); + if (tabs.length < 2) { + return; + } + const tab = tabs.find((t) => t.active); + if (!tab) { + return; + } + const select = (tab.index - 1 + tabs.length) % tabs.length; + return this.tabPresenter.select(tabs[select].id as number); + } +} diff --git a/src/background/operators/impls/ShowAddBookmarkOperator.ts b/src/background/operators/impls/ShowAddBookmarkOperator.ts new file mode 100644 index 0000000..cce4879 --- /dev/null +++ b/src/background/operators/impls/ShowAddBookmarkOperator.ts @@ -0,0 +1,20 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; +import ConsoleClient from "../../infrastructures/ConsoleClient"; + +export default class ShowAddBookmarkOperator implements Operator { + constructor( + private readonly tabPresenter: TabPresenter, + private readonly consoleClient: ConsoleClient, + private readonly alter: boolean + ) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + let command = "addbookmark "; + if (this.alter) { + command += tab.title || ""; + } + return this.consoleClient.showCommand(tab.id as number, command); + } +} diff --git a/src/background/operators/impls/ShowBufferCommandOperator.ts b/src/background/operators/impls/ShowBufferCommandOperator.ts new file mode 100644 index 0000000..e1246e4 --- /dev/null +++ b/src/background/operators/impls/ShowBufferCommandOperator.ts @@ -0,0 +1,16 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; +import ConsoleClient from "../../infrastructures/ConsoleClient"; + +export default class ShowBufferCommandOperator implements Operator { + constructor( + private readonly tabPresenter: TabPresenter, + private readonly consoleClient: ConsoleClient + ) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + const command = "buffer "; + return this.consoleClient.showCommand(tab.id as number, command); + } +} diff --git a/src/background/operators/impls/ShowCommandOperator.ts b/src/background/operators/impls/ShowCommandOperator.ts new file mode 100644 index 0000000..a5e1765 --- /dev/null +++ b/src/background/operators/impls/ShowCommandOperator.ts @@ -0,0 +1,15 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; +import ConsoleClient from "../../infrastructures/ConsoleClient"; + +export default class ShowCommandOperator implements Operator { + constructor( + private readonly tabPresenter: TabPresenter, + private readonly consoleClient: ConsoleClient + ) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + return this.consoleClient.showCommand(tab.id as number, ""); + } +} diff --git a/src/background/operators/impls/ShowOpenCommandOperator.ts b/src/background/operators/impls/ShowOpenCommandOperator.ts new file mode 100644 index 0000000..1e78e5f --- /dev/null +++ b/src/background/operators/impls/ShowOpenCommandOperator.ts @@ -0,0 +1,20 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; +import ConsoleClient from "../../infrastructures/ConsoleClient"; + +export default class ShowOpenCommandOperator implements Operator { + constructor( + private readonly tabPresenter: TabPresenter, + private readonly consoleClient: ConsoleClient, + private readonly alter: boolean + ) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + let command = "open "; + if (this.alter) { + command += tab.url || ""; + } + return this.consoleClient.showCommand(tab.id as number, command); + } +} diff --git a/src/background/operators/impls/ShowTabOpenCommandOperator.ts b/src/background/operators/impls/ShowTabOpenCommandOperator.ts new file mode 100644 index 0000000..d734da1 --- /dev/null +++ b/src/background/operators/impls/ShowTabOpenCommandOperator.ts @@ -0,0 +1,20 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; +import ConsoleClient from "../../infrastructures/ConsoleClient"; + +export default class ShowTabOpenCommandOperator implements Operator { + constructor( + private readonly tabPresenter: TabPresenter, + private readonly consoleClient: ConsoleClient, + private readonly alter: boolean + ) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + let command = "tabopen "; + if (this.alter) { + command += tab.url || ""; + } + return this.consoleClient.showCommand(tab.id as number, command); + } +} diff --git a/src/background/operators/impls/ShowWinOpenCommandOperator.ts b/src/background/operators/impls/ShowWinOpenCommandOperator.ts new file mode 100644 index 0000000..3c5e639 --- /dev/null +++ b/src/background/operators/impls/ShowWinOpenCommandOperator.ts @@ -0,0 +1,20 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; +import ConsoleClient from "../../infrastructures/ConsoleClient"; + +export default class ShowWinOpenCommandOperator implements Operator { + constructor( + private readonly tabPresenter: TabPresenter, + private readonly consoleClient: ConsoleClient, + private readonly alter: boolean + ) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + let command = "winopen "; + if (this.alter) { + command += tab.url || ""; + } + return this.consoleClient.showCommand(tab.id as number, command); + } +} diff --git a/src/background/operators/impls/StartFindOperator.ts b/src/background/operators/impls/StartFindOperator.ts new file mode 100644 index 0000000..284ac9b --- /dev/null +++ b/src/background/operators/impls/StartFindOperator.ts @@ -0,0 +1,15 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; +import ConsoleClient from "../../infrastructures/ConsoleClient"; + +export default class StartFindOperator implements Operator { + constructor( + private readonly tabPresenter: TabPresenter, + private readonly consoleClient: ConsoleClient + ) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + return this.consoleClient.showFind(tab.id as number); + } +} diff --git a/src/background/operators/impls/TabOperatorFactoryChain.ts b/src/background/operators/impls/TabOperatorFactoryChain.ts new file mode 100644 index 0000000..edd3eaf --- /dev/null +++ b/src/background/operators/impls/TabOperatorFactoryChain.ts @@ -0,0 +1,64 @@ +import { inject, injectable } from "tsyringe"; +import OperatorFactoryChain from "../OperatorFactoryChain"; +import * as operations from "../../../shared/operations"; +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; +import CloseTabOperator from "./CloseTabOperator"; +import CloseTabRightOperator from "./CloseTabRightOperator"; +import ReopenTabOperator from "./ReopenTabOperator"; +import SelectTabPrevOperator from "./SelectTabPrevOperator"; +import SelectTabNextOperator from "./SelectTabNextOperator"; +import SelectFirstTabOperator from "./SelectFirstTabOperator"; +import SelectLastTabOperator from "./SelectLastTabOperator"; +import SelectPreviousSelectedTabOperator from "./SelectPreviousSelectedTabOperator"; +import ReloadTabOperator from "./ReloadTabOperator"; +import PinTabOperator from "./PinTabOperator"; +import UnpinTabOperator from "./UnpinTabOperator"; +import TogglePinnedTabOperator from "./TogglePinnedTabOperator"; +import DuplicateTabOperator from "./DuplicateTabOperator"; + +@injectable() +export default class TabOperatorFactoryChain implements OperatorFactoryChain { + constructor( + @inject("TabPresenter") + private readonly tabPresenter: TabPresenter + ) {} + + create(op: operations.Operation): Operator | null { + switch (op.type) { + case operations.TAB_CLOSE: + return new CloseTabOperator( + this.tabPresenter, + false, + op.select === "left" + ); + case operations.TAB_CLOSE_RIGHT: + return new CloseTabRightOperator(this.tabPresenter); + case operations.TAB_CLOSE_FORCE: + return new CloseTabOperator(this.tabPresenter, true, false); + case operations.TAB_REOPEN: + return new ReopenTabOperator(this.tabPresenter); + case operations.TAB_PREV: + return new SelectTabPrevOperator(this.tabPresenter); + case operations.TAB_NEXT: + return new SelectTabNextOperator(this.tabPresenter); + case operations.TAB_FIRST: + return new SelectFirstTabOperator(this.tabPresenter); + case operations.TAB_LAST: + return new SelectLastTabOperator(this.tabPresenter); + case operations.TAB_PREV_SEL: + return new SelectPreviousSelectedTabOperator(this.tabPresenter); + case operations.TAB_RELOAD: + return new ReloadTabOperator(this.tabPresenter, op.cache); + case operations.TAB_PIN: + return new PinTabOperator(this.tabPresenter); + case operations.TAB_UNPIN: + return new UnpinTabOperator(this.tabPresenter); + case operations.TAB_TOGGLE_PINNED: + return new TogglePinnedTabOperator(this.tabPresenter); + case operations.TAB_DUPLICATE: + return new DuplicateTabOperator(this.tabPresenter); + } + return null; + } +} diff --git a/src/background/operators/impls/TogglePinnedTabOperator.ts b/src/background/operators/impls/TogglePinnedTabOperator.ts new file mode 100644 index 0000000..c4fd4ba --- /dev/null +++ b/src/background/operators/impls/TogglePinnedTabOperator.ts @@ -0,0 +1,11 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class TogglePinnedTabOperator implements Operator { + constructor(private readonly tabPresenter: TabPresenter) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + return this.tabPresenter.setPinned(tab.id as number, !tab.pinned); + } +} diff --git a/src/background/operators/impls/UnpinTabOperator.ts b/src/background/operators/impls/UnpinTabOperator.ts new file mode 100644 index 0000000..fa50145 --- /dev/null +++ b/src/background/operators/impls/UnpinTabOperator.ts @@ -0,0 +1,11 @@ +import Operator from "../Operator"; +import TabPresenter from "../../presenters/TabPresenter"; + +export default class UnpinTabOperator implements Operator { + constructor(private readonly tabPresenter: TabPresenter) {} + + async run(): Promise<void> { + const tab = await this.tabPresenter.getCurrent(); + return this.tabPresenter.setPinned(tab.id as number, false); + } +} diff --git a/src/background/operators/impls/ZoomInOperator.ts b/src/background/operators/impls/ZoomInOperator.ts new file mode 100644 index 0000000..e900f0d --- /dev/null +++ b/src/background/operators/impls/ZoomInOperator.ts @@ -0,0 +1,10 @@ +import Operator from "../Operator"; +import ZoomPresenter from "../../presenters/ZoomPresenter"; + +export default class ZoomInOperator implements Operator { + constructor(private readonly zoomPresenter: ZoomPresenter) {} + + run(): Promise<void> { + return this.zoomPresenter.zoomIn(); + } +} diff --git a/src/background/operators/impls/ZoomOperatorFactoryChain.ts b/src/background/operators/impls/ZoomOperatorFactoryChain.ts new file mode 100644 index 0000000..bf930a7 --- /dev/null +++ b/src/background/operators/impls/ZoomOperatorFactoryChain.ts @@ -0,0 +1,28 @@ +import { inject, injectable } from "tsyringe"; +import Operator from "../Operator"; +import OperatorFactoryChain from "../OperatorFactoryChain"; +import ZoomInOperator from "./ZoomInOperator"; +import ZoomOutOperator from "./ZoomOutOperator"; +import ResetZoomOperator from "./ResetZoomOperator"; +import ZoomPresenter from "../../presenters/ZoomPresenter"; +import * as operations from "../../../shared/operations"; + +@injectable() +export default class ZoomOperatorFactoryChain implements OperatorFactoryChain { + constructor( + @inject("ZoomPresenter") + private readonly zoomPresenter: ZoomPresenter + ) {} + + create(op: operations.Operation): Operator | null { + switch (op.type) { + case operations.ZOOM_IN: + return new ZoomInOperator(this.zoomPresenter); + case operations.ZOOM_OUT: + return new ZoomOutOperator(this.zoomPresenter); + case operations.ZOOM_NEUTRAL: + return new ResetZoomOperator(this.zoomPresenter); + } + return null; + } +} diff --git a/src/background/operators/impls/ZoomOutOperator.ts b/src/background/operators/impls/ZoomOutOperator.ts new file mode 100644 index 0000000..0c0389e --- /dev/null +++ b/src/background/operators/impls/ZoomOutOperator.ts @@ -0,0 +1,10 @@ +import Operator from "../Operator"; +import ZoomPresenter from "../../presenters/ZoomPresenter"; + +export default class ZoomOutOperator implements Operator { + constructor(private readonly zoomPresenter: ZoomPresenter) {} + + run(): Promise<void> { + return this.zoomPresenter.zoomOut(); + } +} diff --git a/src/background/presenters/Notifier.ts b/src/background/presenters/Notifier.ts index 2cd3225..957572d 100644 --- a/src/background/presenters/Notifier.ts +++ b/src/background/presenters/Notifier.ts @@ -30,10 +30,10 @@ export class NotifierImpl implements NotifierImpl { } async notifyInvalidSettings(onclick: () => void): Promise<void> { - const title = `Loaded settings is invalid`; + const title = `Loading settings failed`; // eslint-disable-next-line max-len const message = - "The default settings is used due to the last saved settings is invalid. Check your current settings from the add-on preference"; + "The default settings are 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/WindowPresenter.ts b/src/background/presenters/WindowPresenter.ts index 4f37f5d..5eb0f22 100644 --- a/src/background/presenters/WindowPresenter.ts +++ b/src/background/presenters/WindowPresenter.ts @@ -1,8 +1,9 @@ -import { injectable } from "tsyringe"; +export default interface WindowPresenter { + create(url: string): Promise<void>; +} -@injectable() -export default class WindowPresenter { - create(url: string): Promise<browser.windows.Window> { - return browser.windows.create({ url }); +export class WindowPresenterImpl implements WindowPresenter { + async create(url: string): Promise<void> { + await browser.windows.create({ url }); } } diff --git a/src/background/presenters/ZoomPresenter.ts b/src/background/presenters/ZoomPresenter.ts new file mode 100644 index 0000000..5a3c64d --- /dev/null +++ b/src/background/presenters/ZoomPresenter.ts @@ -0,0 +1,60 @@ +const ZOOM_SETTINGS = [ + 0.33, + 0.5, + 0.66, + 0.75, + 0.8, + 0.9, + 1.0, + 1.1, + 1.25, + 1.5, + 1.75, + 2.0, + 2.5, + 3.0, +] as const; + +export default interface ZoomPresenter { + zoomIn(): Promise<void>; + zoomOut(): Promise<void>; + resetZoom(): Promise<void>; +} + +export class ZoomPresenterImpl implements ZoomPresenter { + async zoomIn(): Promise<void> { + const tab = await browser.tabs.query({ + active: true, + currentWindow: true, + }); + const tabId = tab[0].id as number; + const current = await browser.tabs.getZoom(tabId); + const factor = ZOOM_SETTINGS.find((f) => f > current); + if (factor) { + return browser.tabs.setZoom(tabId, factor); + } + } + + async zoomOut(): Promise<void> { + const tab = await browser.tabs.query({ + active: true, + currentWindow: true, + }); + const tabId = tab[0].id as number; + const current = await browser.tabs.getZoom(tabId); + const factor = ZOOM_SETTINGS.slice(0) + .reverse() + .find((f) => f < current); + if (factor) { + return browser.tabs.setZoom(tabId, factor); + } + } + + async resetZoom(): Promise<void> { + const tab = await browser.tabs.query({ + active: true, + currentWindow: true, + }); + return browser.tabs.setZoom(tab[0].id, 1); + } +} diff --git a/src/background/repositories/BrowserSettingRepository.ts b/src/background/repositories/BrowserSettingRepository.ts index e24874b..1dde190 100644 --- a/src/background/repositories/BrowserSettingRepository.ts +++ b/src/background/repositories/BrowserSettingRepository.ts @@ -1,8 +1,12 @@ import { injectable } from "tsyringe"; import * as urls from "../../shared/urls"; +export default interface BrowserSettingRepository { + getHomepageUrls(): Promise<string[]>; +} + @injectable() -export default class BrowserSettingRepository { +export class BrowserSettingRepositoryImpl implements BrowserSettingRepository { async getHomepageUrls(): Promise<string[]> { const { value } = await browser.browserSettings.homepageOverride.get({}); return value.split("|").map(urls.normalizeUrl); diff --git a/src/background/repositories/RepeatRepository.ts b/src/background/repositories/RepeatRepository.ts index e3ab43d..00098d3 100644 --- a/src/background/repositories/RepeatRepository.ts +++ b/src/background/repositories/RepeatRepository.ts @@ -4,8 +4,14 @@ import MemoryStorage from "../infrastructures/MemoryStorage"; const REPEAT_KEY = "repeat"; +export default interface RepeatRepository { + getLastOperation(): Operation | undefined; + + setLastOperation(op: Operation): void; +} + @injectable() -export default class RepeatRepository { +export class RepeatRepositoryImpl implements RepeatRepository { private cache: MemoryStorage; constructor() { diff --git a/src/background/usecases/AddonEnabledUseCase.ts b/src/background/usecases/AddonEnabledUseCase.ts index f563ab0..f9bafde 100644 --- a/src/background/usecases/AddonEnabledUseCase.ts +++ b/src/background/usecases/AddonEnabledUseCase.ts @@ -22,7 +22,7 @@ export default class AddonEnabledUseCase { return this.indicatorPresentor.indicate(enabled); } - onIndicatorClick(tabId: number): Promise<void> { + private onIndicatorClick(tabId: number): Promise<void> { return this.contentMessageClient.toggleAddonEnabled(tabId); } diff --git a/src/background/usecases/CommandUseCase.ts b/src/background/usecases/CommandUseCase.ts index 811ec77..18ddd4d 100644 --- a/src/background/usecases/CommandUseCase.ts +++ b/src/background/usecases/CommandUseCase.ts @@ -12,17 +12,20 @@ import ContentMessageClient from "../infrastructures/ContentMessageClient"; import RepeatUseCase from "../usecases/RepeatUseCase"; @injectable() -export default class CommandIndicator { +export default class CommandUseCase { constructor( - @inject("TabPresenter") private tabPresenter: TabPresenter, - private windowPresenter: WindowPresenter, - private helpPresenter: HelpPresenter, + @inject("TabPresenter") + private readonly tabPresenter: TabPresenter, + @inject("WindowPresenter") + private readonly windowPresenter: WindowPresenter, + private readonly helpPresenter: HelpPresenter, @inject("CachedSettingRepository") - private cachedSettingRepository: CachedSettingRepository, - private bookmarkRepository: BookmarkRepository, - private consoleClient: ConsoleClient, - private contentMessageClient: ContentMessageClient, - private repeatUseCase: RepeatUseCase + private readonly cachedSettingRepository: CachedSettingRepository, + private readonly bookmarkRepository: BookmarkRepository, + @inject("ConsoleClient") + private readonly consoleClient: ConsoleClient, + private readonly contentMessageClient: ContentMessageClient, + private readonly repeatUseCase: RepeatUseCase ) {} async open(keywords: string): Promise<browser.tabs.Tab> { @@ -44,7 +47,7 @@ export default class CommandIndicator { return this.tabPresenter.create(url); } - async winopen(keywords: string): Promise<browser.windows.Window> { + async winopen(keywords: string): Promise<void> { const url = await this.urlOrSearch(keywords); this.repeatUseCase.storeLastOperation({ type: operations.INTERNAL_OPEN_URL, diff --git a/src/background/usecases/ConsoleUseCase.ts b/src/background/usecases/ConsoleUseCase.ts deleted file mode 100644 index 195c70f..0000000 --- a/src/background/usecases/ConsoleUseCase.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { inject, injectable } from "tsyringe"; -import TabPresenter from "../presenters/TabPresenter"; -import ConsoleClient from "../infrastructures/ConsoleClient"; - -@injectable() -export default class ConsoleUseCase { - constructor( - @inject("TabPresenter") private tabPresenter: TabPresenter, - private consoleClient: ConsoleClient - ) {} - - async showCommand(): Promise<any> { - const tab = await this.tabPresenter.getCurrent(); - return this.consoleClient.showCommand(tab.id as number, ""); - } - - async showOpenCommand(alter: boolean): Promise<any> { - const tab = await this.tabPresenter.getCurrent(); - let command = "open "; - if (alter) { - command += tab.url || ""; - } - return this.consoleClient.showCommand(tab.id as number, command); - } - - async showTabopenCommand(alter: boolean): Promise<any> { - const tab = await this.tabPresenter.getCurrent(); - let command = "tabopen "; - if (alter) { - command += tab.url || ""; - } - return this.consoleClient.showCommand(tab.id as number, command); - } - - async showWinopenCommand(alter: boolean): Promise<any> { - const tab = await this.tabPresenter.getCurrent(); - let command = "winopen "; - if (alter) { - command += tab.url || ""; - } - return this.consoleClient.showCommand(tab.id as number, command); - } - - async showBufferCommand(): Promise<any> { - const tab = await this.tabPresenter.getCurrent(); - const command = "buffer "; - return this.consoleClient.showCommand(tab.id as number, command); - } - - async showAddbookmarkCommand(alter: boolean): Promise<any> { - const tab = await this.tabPresenter.getCurrent(); - let command = "addbookmark "; - if (alter) { - command += tab.title || ""; - } - return this.consoleClient.showCommand(tab.id as number, command); - } - - async hideConsole(): Promise<any> { - 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 facc461..ce96e03 100644 --- a/src/background/usecases/FindUseCase.ts +++ b/src/background/usecases/FindUseCase.ts @@ -1,15 +1,9 @@ -import { inject, injectable } from "tsyringe"; +import { injectable } from "tsyringe"; import FindRepository from "../repositories/FindRepository"; -import TabPresenter from "../presenters/TabPresenter"; -import ConsoleClient from "../infrastructures/ConsoleClient"; @injectable() export default class FindUseCase { - constructor( - @inject("TabPresenter") private tabPresenter: TabPresenter, - private findRepository: FindRepository, - private consoleClient: ConsoleClient - ) {} + constructor(private readonly findRepository: FindRepository) {} getKeyword(): Promise<string> { return this.findRepository.getKeyword(); @@ -18,9 +12,4 @@ export default class FindUseCase { setKeyword(keyword: string): Promise<any> { return this.findRepository.setKeyword(keyword); } - - async findStart(): Promise<any> { - const tab = await this.tabPresenter.getCurrent(); - return this.consoleClient.showFind(tab.id as number); - } } diff --git a/src/background/usecases/MarkUseCase.ts b/src/background/usecases/MarkUseCase.ts index 9da9a21..57e04b4 100644 --- a/src/background/usecases/MarkUseCase.ts +++ b/src/background/usecases/MarkUseCase.ts @@ -1,16 +1,18 @@ import { inject, injectable } from "tsyringe"; import TabPresenter from "../presenters/TabPresenter"; import MarkRepository from "../repositories/MarkRepository"; -import ConsoleClient from "../infrastructures/ConsoleClient"; import ContentMessageClient from "../infrastructures/ContentMessageClient"; +import ConsoleClient from "../infrastructures/ConsoleClient"; @injectable() export default class MarkUseCase { constructor( - @inject("TabPresenter") private tabPresenter: TabPresenter, - private markRepository: MarkRepository, - private consoleClient: ConsoleClient, - private contentMessageClient: ContentMessageClient + @inject("TabPresenter") + private readonly tabPresenter: TabPresenter, + private readonly markRepository: MarkRepository, + @inject("ConsoleClient") + private readonly consoleClient: ConsoleClient, + private readonly contentMessageClient: ContentMessageClient ) {} async setGlobal(key: string, x: number, y: number): Promise<any> { diff --git a/src/background/usecases/NavigateUseCase.ts b/src/background/usecases/NavigateUseCase.ts deleted file mode 100644 index 974606c..0000000 --- a/src/background/usecases/NavigateUseCase.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { inject, injectable } from "tsyringe"; -import NavigateClient from "../clients/NavigateClient"; -import TabPresenter from "../presenters/TabPresenter"; - -@injectable() -export default class NavigateUseCase { - constructor( - @inject("TabPresenter") private tabPresenter: TabPresenter, - private navigateClient: NavigateClient - ) {} - - async openHistoryNext(): Promise<void> { - const tab = await this.tabPresenter.getCurrent(); - await this.navigateClient.historyNext(tab.id!); - } - - async openHistoryPrev(): Promise<void> { - const tab = await this.tabPresenter.getCurrent(); - await this.navigateClient.historyPrev(tab.id!); - } - - async openLinkNext(): Promise<void> { - const tab = await this.tabPresenter.getCurrent(); - await this.navigateClient.linkNext(tab.id!); - } - - async openLinkPrev(): Promise<void> { - const tab = await this.tabPresenter.getCurrent(); - await this.navigateClient.linkPrev(tab.id!); - } - - async openParent(): Promise<void> { - 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) { - url.search = ""; - } else { - const basenamePattern = /\/[^/]+$/; - const lastDirPattern = /\/[^/]+\/$/; - if (basenamePattern.test(url.pathname)) { - url.pathname = url.pathname.replace(basenamePattern, "/"); - } else if (lastDirPattern.test(url.pathname)) { - url.pathname = url.pathname.replace(lastDirPattern, "/"); - } - } - await this.tabPresenter.open(url.href); - } - - async openRoot(): Promise<void> { - const tab = await this.tabPresenter.getCurrent(); - const url = new URL(tab.url!); - await this.tabPresenter.open(url.origin); - } -} diff --git a/src/background/usecases/RepeatUseCase.ts b/src/background/usecases/RepeatUseCase.ts index d7235ee..992e76b 100644 --- a/src/background/usecases/RepeatUseCase.ts +++ b/src/background/usecases/RepeatUseCase.ts @@ -1,4 +1,4 @@ -import { injectable } from "tsyringe"; +import { inject, injectable } from "tsyringe"; import * as operations from "../../shared/operations"; import RepeatRepository from "../repositories/RepeatRepository"; @@ -6,7 +6,10 @@ type Operation = operations.Operation; @injectable() export default class RepeatUseCase { - constructor(private repeatRepository: RepeatRepository) {} + constructor( + @inject("RepeatRepository") + private readonly repeatRepository: RepeatRepository + ) {} storeLastOperation(op: Operation): void { this.repeatRepository.setLastOperation(op); diff --git a/src/background/usecases/TabSelectUseCase.ts b/src/background/usecases/TabSelectUseCase.ts deleted file mode 100644 index 663ceb8..0000000 --- a/src/background/usecases/TabSelectUseCase.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { inject, injectable } from "tsyringe"; -import TabPresenter from "../presenters/TabPresenter"; - -@injectable() -export default class TabSelectUseCase { - constructor(@inject("TabPresenter") private tabPresenter: TabPresenter) {} - - async selectPrev(count: number): Promise<any> { - const tabs = await this.tabPresenter.getAll(); - if (tabs.length < 2) { - return; - } - const tab = tabs.find((t) => t.active); - if (!tab) { - return; - } - const select = (tab.index - count + tabs.length) % tabs.length; - return this.tabPresenter.select(tabs[select].id as number); - } - - async selectNext(count: number): Promise<any> { - const tabs = await this.tabPresenter.getAll(); - if (tabs.length < 2) { - return; - } - const tab = tabs.find((t) => t.active); - if (!tab) { - return; - } - const select = (tab.index + count) % tabs.length; - return this.tabPresenter.select(tabs[select].id as number); - } - - async selectFirst(): Promise<any> { - const tabs = await this.tabPresenter.getAll(); - return this.tabPresenter.select(tabs[0].id as number); - } - - async selectLast(): Promise<any> { - const tabs = await this.tabPresenter.getAll(); - return this.tabPresenter.select(tabs[tabs.length - 1].id as number); - } - - async selectPrevSelected(): Promise<any> { - const tabId = await this.tabPresenter.getLastSelectedId(); - if (tabId === null || typeof tabId === "undefined") { - return Promise.resolve(); - } - return this.tabPresenter.select(tabId); - } -} diff --git a/src/background/usecases/TabUseCase.ts b/src/background/usecases/TabUseCase.ts deleted file mode 100644 index 1439107..0000000 --- a/src/background/usecases/TabUseCase.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { inject, injectable } from "tsyringe"; -import TabPresenter from "../presenters/TabPresenter"; -import WindowPresenter from "../presenters/WindowPresenter"; -import BrowserSettingRepository from "../repositories/BrowserSettingRepository"; - -@injectable() -export default class TabUseCase { - constructor( - @inject("TabPresenter") private tabPresenter: TabPresenter, - private windowPresenter: WindowPresenter, - private browserSettingRepository: BrowserSettingRepository - ) {} - - async close(force: boolean, selectLeft = false): Promise<any> { - const tab = await this.tabPresenter.getCurrent(); - if (!force && tab.pinned) { - return Promise.resolve(); - } - if (selectLeft && tab.index > 0) { - 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> { - const tabs = await this.tabPresenter.getAll(); - tabs.sort((t1, t2) => t1.index - t2.index); - const index = tabs.findIndex((t) => t.active); - if (index < 0) { - return; - } - for (let i = index + 1; i < tabs.length; ++i) { - const tab = tabs[i]; - if (!tab.pinned) { - this.tabPresenter.remove([tab.id as number]); - } - } - } - - reopen(): Promise<any> { - return this.tabPresenter.reopen(); - } - - async reload(cache: boolean): Promise<any> { - const tab = await this.tabPresenter.getCurrent(); - return this.tabPresenter.reload(tab.id as number, cache); - } - - async setPinned(pinned: boolean): Promise<any> { - const tab = await this.tabPresenter.getCurrent(); - return this.tabPresenter.setPinned(tab.id as number, pinned); - } - - async togglePinned(): Promise<any> { - const tab = await this.tabPresenter.getCurrent(); - return this.tabPresenter.setPinned(tab.id as number, !tab.pinned); - } - - async duplicate(): Promise<any> { - const tab = await this.tabPresenter.getCurrent(); - return this.tabPresenter.duplicate(tab.id as number); - } - - async openPageSource(): Promise<any> { - const tab = await this.tabPresenter.getCurrent(); - const url = "view-source:" + tab.url; - return this.tabPresenter.create(url); - } - - async openHome(newTab: boolean): Promise<any> { - 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" - ); - } - if (urls.length === 1 && !newTab) { - return this.tabPresenter.open(urls[0], tab.id); - } - for (const url of urls) { - this.tabPresenter.create(url); - } - } - - async openURL( - url: string, - newTab?: boolean, - newWindow?: boolean - ): Promise<void> { - if (newWindow) { - await this.windowPresenter.create(url); - } else if (newTab) { - await this.tabPresenter.create(url); - } else { - const tab = await this.tabPresenter.getCurrent(); - await this.tabPresenter.open(url, tab.id); - } - } -} diff --git a/src/background/usecases/ZoomUseCase.ts b/src/background/usecases/ZoomUseCase.ts deleted file mode 100644 index 173e4d7..0000000 --- a/src/background/usecases/ZoomUseCase.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { inject, injectable } from "tsyringe"; -import TabPresenter from "../presenters/TabPresenter"; - -const ZOOM_SETTINGS: number[] = [ - 0.33, - 0.5, - 0.66, - 0.75, - 0.8, - 0.9, - 1.0, - 1.1, - 1.25, - 1.5, - 1.75, - 2.0, - 2.5, - 3.0, -]; - -@injectable() -export default class ZoomUseCase { - constructor(@inject("TabPresenter") private tabPresenter: TabPresenter) {} - - async zoomIn(): Promise<any> { - 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> { - 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> { - const tab = await this.tabPresenter.getCurrent(); - return this.tabPresenter.setZoom(tab.id as number, 1); - } -} diff --git a/src/content/controllers/KeymapController.ts b/src/content/controllers/KeymapController.ts index 092e55c..01ac8b5 100644 --- a/src/content/controllers/KeymapController.ts +++ b/src/content/controllers/KeymapController.ts @@ -1,32 +1,15 @@ import { injectable, inject } from "tsyringe"; -import * as operations from "../../shared/operations"; import KeymapUseCase from "../usecases/KeymapUseCase"; -import AddonEnabledUseCase from "../usecases/AddonEnabledUseCase"; -import FindSlaveUseCase from "../usecases/FindSlaveUseCase"; -import ScrollUseCase from "../usecases/ScrollUseCase"; -import FocusUseCase from "../usecases/FocusUseCase"; -import ClipboardUseCase from "../usecases/ClipboardUseCase"; -import OperationClient from "../client/OperationClient"; -import MarkKeyyUseCase from "../usecases/MarkKeyUseCase"; -import FollowMasterClient from "../client/FollowMasterClient"; import Key from "../../shared/settings/Key"; +import OperatorFactory from "../operators/OperatorFactory"; @injectable() export default class KeymapController { constructor( private keymapUseCase: KeymapUseCase, - private addonEnabledUseCase: AddonEnabledUseCase, - private findSlaveUseCase: FindSlaveUseCase, - private scrollUseCase: ScrollUseCase, - private focusUseCase: FocusUseCase, - private clipbaordUseCase: ClipboardUseCase, - private markKeyUseCase: MarkKeyyUseCase, - @inject("OperationClient") - private operationClient: OperationClient, - - @inject("FollowMasterClient") - private followMasterClient: FollowMasterClient + @inject("OperatorFactory") + private readonly operatorFactory: OperatorFactory ) {} // eslint-disable-next-line complexity, max-lines-per-function @@ -36,65 +19,14 @@ export default class KeymapController { return false; } - 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); + // 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 propagation. + this.operatorFactory + .create(nextOp.op, nextOp.repeat) + .run() + .catch(console.error); - 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/di.ts b/src/content/di.ts index cc10c78..e74d7ac 100644 --- a/src/content/di.ts +++ b/src/content/di.ts @@ -17,6 +17,7 @@ import { FollowMasterRepositoryImpl } from "./repositories/FollowMasterRepositor import { FollowPresenterImpl } from "./presenters/FollowPresenter"; import { FollowSlaveClientFactoryImpl } from "./client/FollowSlaveClientFactory"; import { FollowSlaveRepositoryImpl } from "./repositories/FollowSlaveRepository"; +import { HintKeyRepositoryImpl } from "./repositories/HintKeyRepository"; import { KeymapRepositoryImpl } from "./repositories/KeymapRepository"; import { MarkClientImpl } from "./client/MarkClient"; import { MarkKeyRepositoryImpl } from "./repositories/MarkKeyRepository"; @@ -28,6 +29,8 @@ import { SettingClientImpl } from "./client/SettingClient"; import { SettingRepositoryImpl } from "./repositories/SettingRepository"; import { TabsClientImpl } from "./client/TabsClient"; import { container } from "tsyringe"; +import OperatorFactoryImpl from "./operators/impls/OperatorFactoryImpl"; +import { URLRepositoryImpl } from "./operators/impls/URLRepository"; container.register("FollowMasterClient", { useValue: new FollowMasterClientImpl(window.top), @@ -64,6 +67,9 @@ container.register("FollowSlaveClientFactory", { container.register("FollowSlaveRepository", { useClass: FollowSlaveRepositoryImpl, }); +container.register("HintKeyRepository", { + useClass: HintKeyRepositoryImpl, +}); container.register("KeymapRepository", { useClass: KeymapRepositoryImpl }); container.register("MarkClient", { useClass: MarkClientImpl }); container.register("MarkKeyRepository", { useClass: MarkKeyRepositoryImpl }); @@ -75,4 +81,6 @@ container.register("OperationClient", { useClass: OperationClientImpl }); container.register("ScrollPresenter", { useClass: ScrollPresenterImpl }); container.register("SettingClient", { useClass: SettingClientImpl }); container.register("SettingRepository", { useClass: SettingRepositoryImpl }); +container.register("URLRepository", { useClass: URLRepositoryImpl }); container.register("TabsClient", { useClass: TabsClientImpl }); +container.register("OperatorFactory", { useClass: OperatorFactoryImpl }); diff --git a/src/content/hint-key-producer.ts b/src/content/hint-key-producer.ts deleted file mode 100644 index a5e2877..0000000 --- a/src/content/hint-key-producer.ts +++ /dev/null @@ -1,37 +0,0 @@ -export default class HintKeyProducer { - private charset: string; - - private counter: number[]; - - constructor(charset: string) { - if (charset.length === 0) { - throw new TypeError("charset is empty"); - } - - this.charset = charset; - this.counter = []; - } - - produce(): string { - this.increment(); - - return this.counter.map((x) => this.charset[x]).join(""); - } - - private increment(): void { - 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(); - 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; - num = ~~(num / len); - } - this.counter.reverse(); - } -} diff --git a/src/content/operators/Operator.ts b/src/content/operators/Operator.ts new file mode 100644 index 0000000..3b1fe03 --- /dev/null +++ b/src/content/operators/Operator.ts @@ -0,0 +1,5 @@ +interface Operator { + run(): Promise<void>; +} + +export default Operator; diff --git a/src/content/operators/OperatorFactory.ts b/src/content/operators/OperatorFactory.ts new file mode 100644 index 0000000..f45973b --- /dev/null +++ b/src/content/operators/OperatorFactory.ts @@ -0,0 +1,6 @@ +import * as operations from "../../shared/operations"; +import Operator from "./Operator"; + +export default interface OperatorFactory { + create(op: operations.Operation, repeat: number): Operator; +} diff --git a/src/content/operators/OperatorFactoryChain.ts b/src/content/operators/OperatorFactoryChain.ts new file mode 100644 index 0000000..98dedb5 --- /dev/null +++ b/src/content/operators/OperatorFactoryChain.ts @@ -0,0 +1,6 @@ +import * as operations from "../../shared/operations"; +import Operator from "./Operator"; + +export default interface OperatorFactoryChain { + create(op: operations.Operation, repeat: number): Operator | null; +} diff --git a/src/content/operators/impls/AbstractScrollOperator.ts b/src/content/operators/impls/AbstractScrollOperator.ts new file mode 100644 index 0000000..f8d9f70 --- /dev/null +++ b/src/content/operators/impls/AbstractScrollOperator.ts @@ -0,0 +1,10 @@ +import SettingRepository from "../../repositories/SettingRepository"; + +export default class AbstractScrollOperator { + constructor(private readonly settingRepository: SettingRepository) {} + + protected getSmoothScroll(): boolean { + const settings = this.settingRepository.get(); + return settings.properties.smoothscroll; + } +} diff --git a/src/content/operators/impls/AddonOperatorFactoryChain.ts b/src/content/operators/impls/AddonOperatorFactoryChain.ts new file mode 100644 index 0000000..54880c4 --- /dev/null +++ b/src/content/operators/impls/AddonOperatorFactoryChain.ts @@ -0,0 +1,40 @@ +import { inject, injectable } from "tsyringe"; +import OperatorFactoryChain from "../OperatorFactoryChain"; +import AddonIndicatorClient from "../../client/AddonIndicatorClient"; +import AddonEnabledRepository from "../../repositories/AddonEnabledRepository"; +import * as operations from "../../../shared/operations"; +import Operator from "../Operator"; +import EnableAddonOperator from "./EnableAddonOperator"; +import DisableAddonOperator from "./DisableAddonOperator"; +import ToggleAddonOperator from "./ToggleAddonOperator"; + +@injectable() +export default class AddonOperatorFactoryChain implements OperatorFactoryChain { + constructor( + @inject("AddonIndicatorClient") + private readonly addonIndicatorClient: AddonIndicatorClient, + @inject("AddonEnabledRepository") + private readonly addonEnabledRepository: AddonEnabledRepository + ) {} + + create(op: operations.Operation, _repeat: number): Operator | null { + switch (op.type) { + case operations.ADDON_ENABLE: + return new EnableAddonOperator( + this.addonIndicatorClient, + this.addonEnabledRepository + ); + case operations.ADDON_DISABLE: + return new DisableAddonOperator( + this.addonIndicatorClient, + this.addonEnabledRepository + ); + case operations.ADDON_TOGGLE_ENABLED: + return new ToggleAddonOperator( + this.addonIndicatorClient, + this.addonEnabledRepository + ); + } + return null; + } +} diff --git a/src/content/operators/impls/BackgroundOperationOperator.ts b/src/content/operators/impls/BackgroundOperationOperator.ts new file mode 100644 index 0000000..dd86559 --- /dev/null +++ b/src/content/operators/impls/BackgroundOperationOperator.ts @@ -0,0 +1,15 @@ +import Operator from "../Operator"; +import OperationClient from "../../client/OperationClient"; +import * as operations from "../../../shared/operations"; + +export default class BackgroundOperationOperator implements Operator { + constructor( + private readonly operationClient: OperationClient, + private readonly repeat: number, + private readonly op: operations.Operation + ) {} + + async run(): Promise<void> { + await this.operationClient.execBackgroundOp(this.repeat, this.op); + } +} diff --git a/src/content/operators/impls/ClipboardOperatorFactoryChain.ts b/src/content/operators/impls/ClipboardOperatorFactoryChain.ts new file mode 100644 index 0000000..454aea1 --- /dev/null +++ b/src/content/operators/impls/ClipboardOperatorFactoryChain.ts @@ -0,0 +1,47 @@ +import { inject, injectable } from "tsyringe"; +import YankURLOperator from "./YankURLOperator"; +import PasteOperator from "./PasteOperator"; +import Operator from "../Operator"; +import OperatorFactoryChain from "../OperatorFactoryChain"; +import ClipboardRepository from "../../repositories/ClipboardRepository"; +import ConsoleClient from "../../client/ConsoleClient"; +import OperationClient from "../../client/OperationClient"; +import SettingRepository from "../../repositories/SettingRepository"; +import * as operations from "../../../shared/operations"; +import URLRepository from "./URLRepository"; + +@injectable() +export default class ClipboardOperatorFactoryChain + implements OperatorFactoryChain { + constructor( + @inject("ClipboardRepository") + private readonly clipboardRepository: ClipboardRepository, + @inject("ConsoleClient") + private readonly consoleClient: ConsoleClient, + @inject("OperationClient") + private readonly operationClinet: OperationClient, + @inject("SettingRepository") + private readonly settingRepository: SettingRepository, + @inject("URLRepository") + private readonly urlRepository: URLRepository + ) {} + + create(op: operations.Operation, _repeat: number): Operator | null { + switch (op.type) { + case operations.URLS_YANK: + return new YankURLOperator( + this.clipboardRepository, + this.consoleClient, + this.urlRepository + ); + case operations.URLS_PASTE: + return new PasteOperator( + this.clipboardRepository, + this.settingRepository, + this.operationClinet, + op.newTab + ); + } + return null; + } +} diff --git a/src/content/operators/impls/DisableAddonOperator.ts b/src/content/operators/impls/DisableAddonOperator.ts new file mode 100644 index 0000000..28811fe --- /dev/null +++ b/src/content/operators/impls/DisableAddonOperator.ts @@ -0,0 +1,15 @@ +import Operator from "../Operator"; +import AddonIndicatorClient from "../../client/AddonIndicatorClient"; +import AddonEnabledRepository from "../../repositories/AddonEnabledRepository"; + +export default class DisableAddonOperator implements Operator { + constructor( + private readonly indicator: AddonIndicatorClient, + private readonly repository: AddonEnabledRepository + ) {} + + async run(): Promise<void> { + this.repository.set(false); + await this.indicator.setEnabled(false); + } +} diff --git a/src/content/operators/impls/EnableAddonOperator.ts b/src/content/operators/impls/EnableAddonOperator.ts new file mode 100644 index 0000000..b5b1d79 --- /dev/null +++ b/src/content/operators/impls/EnableAddonOperator.ts @@ -0,0 +1,15 @@ +import Operator from "../Operator"; +import AddonIndicatorClient from "../../client/AddonIndicatorClient"; +import AddonEnabledRepository from "../../repositories/AddonEnabledRepository"; + +export default class EnableAddonOperator implements Operator { + constructor( + private readonly indicator: AddonIndicatorClient, + private readonly repository: AddonEnabledRepository + ) {} + + async run(): Promise<void> { + this.repository.set(true); + await this.indicator.setEnabled(true); + } +} diff --git a/src/content/operators/impls/EnableJumpMarkOperator.ts b/src/content/operators/impls/EnableJumpMarkOperator.ts new file mode 100644 index 0000000..42ca8ee --- /dev/null +++ b/src/content/operators/impls/EnableJumpMarkOperator.ts @@ -0,0 +1,10 @@ +import Operator from "../Operator"; +import MarkKeyRepository from "../../repositories/MarkKeyRepository"; + +export default class EnableJumpMarkOperator implements Operator { + constructor(private readonly repository: MarkKeyRepository) {} + + async run(): Promise<void> { + this.repository.enableJumpMode(); + } +} diff --git a/src/content/operators/impls/EnableSetMarkOperator.ts b/src/content/operators/impls/EnableSetMarkOperator.ts new file mode 100644 index 0000000..3d0daf4 --- /dev/null +++ b/src/content/operators/impls/EnableSetMarkOperator.ts @@ -0,0 +1,10 @@ +import Operator from "../Operator"; +import MarkKeyRepository from "../../repositories/MarkKeyRepository"; + +export default class EnableSetMarkOperator implements Operator { + constructor(private readonly repository: MarkKeyRepository) {} + + async run(): Promise<void> { + this.repository.enableSetMode(); + } +} diff --git a/src/content/operators/impls/FindNextOperator.ts b/src/content/operators/impls/FindNextOperator.ts new file mode 100644 index 0000000..c67f6d9 --- /dev/null +++ b/src/content/operators/impls/FindNextOperator.ts @@ -0,0 +1,15 @@ +import Operator from "../Operator"; +import FindMasterClient from "../../client/FindMasterClient"; + +export default class FindNextOperator implements Operator { + constructor( + private readonly findMasterClient: FindMasterClient, + private readonly repeat: number + ) {} + + async run(): Promise<void> { + for (let i = 0; i < this.repeat; ++i) { + this.findMasterClient.findNext(); + } + } +} diff --git a/src/content/operators/impls/FindOperatorFactoryChain.ts b/src/content/operators/impls/FindOperatorFactoryChain.ts new file mode 100644 index 0000000..b3524c1 --- /dev/null +++ b/src/content/operators/impls/FindOperatorFactoryChain.ts @@ -0,0 +1,25 @@ +import { inject, injectable } from "tsyringe"; +import Operator from "../Operator"; +import OperatorFactoryChain from "../OperatorFactoryChain"; +import FindNextOperator from "./FindNextOperator"; +import FindPrevOperator from "./FindPrevOperator"; +import FindMasterClient from "../../client/FindMasterClient"; +import * as operations from "../../../shared/operations"; + +@injectable() +export default class FindOperatorFactoryChain implements OperatorFactoryChain { + constructor( + @inject("FindMasterClient") + private readonly findMasterClient: FindMasterClient + ) {} + + create(op: operations.Operation, repeat: number): Operator | null { + switch (op.type) { + case operations.FIND_NEXT: + return new FindNextOperator(this.findMasterClient, repeat); + case operations.FIND_PREV: + return new FindPrevOperator(this.findMasterClient, repeat); + } + return null; + } +} diff --git a/src/content/operators/impls/FindPrevOperator.ts b/src/content/operators/impls/FindPrevOperator.ts new file mode 100644 index 0000000..f73e605 --- /dev/null +++ b/src/content/operators/impls/FindPrevOperator.ts @@ -0,0 +1,15 @@ +import Operator from "../Operator"; +import FindMasterClient from "../../client/FindMasterClient"; + +export default class FindPrevOperator implements Operator { + constructor( + private readonly findMasterClient: FindMasterClient, + private readonly repeat: number + ) {} + + async run(): Promise<void> { + for (let i = 0; i < this.repeat; ++i) { + this.findMasterClient.findPrev(); + } + } +} diff --git a/src/content/operators/impls/FocusOperator.ts b/src/content/operators/impls/FocusOperator.ts new file mode 100644 index 0000000..51d6fec --- /dev/null +++ b/src/content/operators/impls/FocusOperator.ts @@ -0,0 +1,10 @@ +import Operator from "../Operator"; +import FocusPresenter from "../../presenters/FocusPresenter"; + +export default class FocusOperator implements Operator { + constructor(private readonly presenter: FocusPresenter) {} + + async run(): Promise<void> { + this.presenter.focusFirstElement(); + } +} diff --git a/src/content/operators/impls/FocusOperatorFactoryChain.ts b/src/content/operators/impls/FocusOperatorFactoryChain.ts new file mode 100644 index 0000000..c89c1e5 --- /dev/null +++ b/src/content/operators/impls/FocusOperatorFactoryChain.ts @@ -0,0 +1,22 @@ +import { inject, injectable } from "tsyringe"; +import OperatorFactoryChain from "../OperatorFactoryChain"; +import Operator from "../Operator"; +import FocusOperator from "./FocusOperator"; +import FocusPresenter from "../../presenters/FocusPresenter"; +import * as operations from "../../../shared/operations"; + +@injectable() +export default class FocusOperatorFactoryChain implements OperatorFactoryChain { + constructor( + @inject("FocusPresenter") + private readonly focusPresenter: FocusPresenter + ) {} + + create(op: operations.Operation, _repeat: number): Operator | null { + switch (op.type) { + case operations.FOCUS_INPUT: + return new FocusOperator(this.focusPresenter); + } + return null; + } +} diff --git a/src/content/operators/impls/FollowOperatorFactoryChain.ts b/src/content/operators/impls/FollowOperatorFactoryChain.ts new file mode 100644 index 0000000..588e1a4 --- /dev/null +++ b/src/content/operators/impls/FollowOperatorFactoryChain.ts @@ -0,0 +1,27 @@ +import { inject, injectable } from "tsyringe"; +import StartFollowOperator from "./StartFollowOperator"; +import Operator from "../Operator"; +import OperatorFactoryChain from "../OperatorFactoryChain"; +import FollowMasterClient from "../../client/FollowMasterClient"; +import * as operations from "../../../shared/operations"; + +@injectable() +export default class FollowOperatorFactoryChain + implements OperatorFactoryChain { + constructor( + @inject("FollowMasterClient") + private followMasterClient: FollowMasterClient + ) {} + + create(op: operations.Operation, _repeat: number): Operator | null { + switch (op.type) { + case operations.FOLLOW_START: + return new StartFollowOperator( + this.followMasterClient, + op.newTab, + op.background + ); + } + return null; + } +} diff --git a/src/content/operators/impls/HorizontalScrollOperator.ts b/src/content/operators/impls/HorizontalScrollOperator.ts new file mode 100644 index 0000000..f813f85 --- /dev/null +++ b/src/content/operators/impls/HorizontalScrollOperator.ts @@ -0,0 +1,21 @@ +import AbstractScrollOperator from "./AbstractScrollOperator"; +import Operator from "../Operator"; +import ScrollPresenter from "../../presenters/ScrollPresenter"; +import SettingRepository from "../../repositories/SettingRepository"; + +export default class HorizontalScrollOperator + extends AbstractScrollOperator + implements Operator { + constructor( + private readonly presenter: ScrollPresenter, + settingRepository: SettingRepository, + private readonly count: number + ) { + super(settingRepository); + } + + async run(): Promise<void> { + const smooth = this.getSmoothScroll(); + this.presenter.scrollHorizonally(this.count, smooth); + } +} diff --git a/src/content/operators/impls/MarkOperatorFactoryChain.ts b/src/content/operators/impls/MarkOperatorFactoryChain.ts new file mode 100644 index 0000000..7e6c513 --- /dev/null +++ b/src/content/operators/impls/MarkOperatorFactoryChain.ts @@ -0,0 +1,25 @@ +import { inject, injectable } from "tsyringe"; +import EnableSetMarkOperator from "./EnableSetMarkOperator"; +import EnableJumpMarkOperator from "./EnableJumpMarkOperator"; +import Operator from "../Operator"; +import OperatorFactoryChain from "../OperatorFactoryChain"; +import MarkKeyRepository from "../../repositories/MarkKeyRepository"; +import * as operations from "../../../shared/operations"; + +@injectable() +export default class MarkOperatorFactoryChain implements OperatorFactoryChain { + constructor( + @inject("MarkKeyRepository") + private readonly markKeyRepository: MarkKeyRepository + ) {} + + create(op: operations.Operation, _repeat: number): Operator | null { + switch (op.type) { + case operations.MARK_SET_PREFIX: + return new EnableSetMarkOperator(this.markKeyRepository); + case operations.MARK_JUMP_PREFIX: + return new EnableJumpMarkOperator(this.markKeyRepository); + } + return null; + } +} diff --git a/src/content/operators/impls/OperatorFactoryImpl.ts b/src/content/operators/impls/OperatorFactoryImpl.ts new file mode 100644 index 0000000..22b35c8 --- /dev/null +++ b/src/content/operators/impls/OperatorFactoryImpl.ts @@ -0,0 +1,51 @@ +import { inject, injectable } from "tsyringe"; +import OperatorFactory from "../OperatorFactory"; +import BackgroundOperationOperator from "./BackgroundOperationOperator"; +import Operator from "../Operator"; +import OperatorFactoryChain from "../OperatorFactoryChain"; +import { Operation } from "../../../shared/operations"; +import OperationClient from "../../client/OperationClient"; +import AddonOperatorFactoryChain from "./AddonOperatorFactoryChain"; +import ClipboardOperatorFactoryChain from "./ClipboardOperatorFactoryChain"; +import FindOperatorFactoryChain from "./FindOperatorFactoryChain"; +import FocusOperatorFactoryChain from "./FocusOperatorFactoryChain"; +import FollowOperatorFactoryChain from "./FollowOperatorFactoryChain"; +import MarkOperatorFactoryChain from "./MarkOperatorFactoryChain"; +import ScrollOperatorFactoryChain from "./ScrollOperatorFactoryChain"; + +@injectable() +export default class OperatorFactoryImpl implements OperatorFactory { + private readonly factoryChains: OperatorFactoryChain[]; + + constructor( + addonOperatorFactoryChain: AddonOperatorFactoryChain, + clipboardOperatorFactoryChain: ClipboardOperatorFactoryChain, + findOperatorFactoryChain: FindOperatorFactoryChain, + focusOperatorFactoryChain: FocusOperatorFactoryChain, + followOperatorFactoryChain: FollowOperatorFactoryChain, + markOperatorFactoryChain: MarkOperatorFactoryChain, + scrollOperatorFactoryChain: ScrollOperatorFactoryChain, + @inject("OperationClient") + private readonly operationClient: OperationClient + ) { + this.factoryChains = [ + addonOperatorFactoryChain, + clipboardOperatorFactoryChain, + findOperatorFactoryChain, + focusOperatorFactoryChain, + followOperatorFactoryChain, + markOperatorFactoryChain, + scrollOperatorFactoryChain, + ]; + } + + create(op: Operation, repeat: number): Operator { + for (const chain of this.factoryChains) { + const operator = chain.create(op, repeat); + if (operator != null) { + return operator; + } + } + return new BackgroundOperationOperator(this.operationClient, repeat, op); + } +} diff --git a/src/content/operators/impls/PageScrollOperator.ts b/src/content/operators/impls/PageScrollOperator.ts new file mode 100644 index 0000000..377bf92 --- /dev/null +++ b/src/content/operators/impls/PageScrollOperator.ts @@ -0,0 +1,21 @@ +import AbstractScrollOperator from "./AbstractScrollOperator"; +import Operator from "../Operator"; +import ScrollPresenter from "../../presenters/ScrollPresenter"; +import SettingRepository from "../../repositories/SettingRepository"; + +export default class PageScrollOperator + extends AbstractScrollOperator + implements Operator { + constructor( + private readonly presenter: ScrollPresenter, + settingRepository: SettingRepository, + private readonly count: number + ) { + super(settingRepository); + } + + async run(): Promise<void> { + const smooth = this.getSmoothScroll(); + this.presenter.scrollPages(this.count, smooth); + } +} diff --git a/src/content/operators/impls/PasteOperator.ts b/src/content/operators/impls/PasteOperator.ts new file mode 100644 index 0000000..592da66 --- /dev/null +++ b/src/content/operators/impls/PasteOperator.ts @@ -0,0 +1,25 @@ +import Operator from "../Operator"; +import ClipboardRepository from "../../repositories/ClipboardRepository"; +import SettingRepository from "../../repositories/SettingRepository"; +import OperationClient from "../../client/OperationClient"; +import * as urls from "../../../shared/urls"; + +export default class PasteOperator implements Operator { + constructor( + private readonly repository: ClipboardRepository, + private readonly settingRepository: SettingRepository, + private readonly operationClient: OperationClient, + private readonly newTab: boolean + ) {} + + async run(): Promise<void> { + const search = this.settingRepository.get().search; + const text = this.repository.read(); + const url = urls.searchUrl(text, search); + + // NOTE: Repeat pasting from clipboard instead of opening a certain url. + // 'Repeat last' command is implemented in the background script and cannot + // access to clipboard until Firefox 63. + await this.operationClient.internalOpenUrl(url, this.newTab); + } +} diff --git a/src/content/operators/impls/ScrollOperatorFactoryChain.ts b/src/content/operators/impls/ScrollOperatorFactoryChain.ts new file mode 100644 index 0000000..6847aea --- /dev/null +++ b/src/content/operators/impls/ScrollOperatorFactoryChain.ts @@ -0,0 +1,68 @@ +import { inject, injectable } from "tsyringe"; +import OperatorFactoryChain from "../OperatorFactoryChain"; +import ScrollPresenter from "../../presenters/ScrollPresenter"; +import SettingRepository from "../../repositories/SettingRepository"; +import * as operations from "../../../shared/operations"; +import Operator from "../Operator"; +import VerticalScrollOperator from "./VerticalScrollOperator"; +import HorizontalScrollOperator from "./HorizontalScrollOperator"; +import PageScrollOperator from "./PageScrollOperator"; +import ScrollToTopOperator from "./ScrollToTopOperator"; +import ScrollToBottomOperator from "./ScrollToBottomOperator"; +import ScrollToHomeOperator from "./ScrollToHomeOperator"; +import ScrollToEndOperator from "./ScrollToEndOperator"; + +@injectable() +export default class ScrollOperatorFactoryChain + implements OperatorFactoryChain { + constructor( + @inject("ScrollPresenter") + private readonly scrollPresenter: ScrollPresenter, + @inject("SettingRepository") + private readonly settingRepository: SettingRepository + ) {} + + create(op: operations.Operation, repeat: number): Operator | null { + switch (op.type) { + case operations.SCROLL_VERTICALLY: + return new VerticalScrollOperator( + this.scrollPresenter, + this.settingRepository, + op.count * repeat + ); + case operations.SCROLL_HORIZONALLY: + return new HorizontalScrollOperator( + this.scrollPresenter, + this.settingRepository, + op.count * repeat + ); + case operations.SCROLL_PAGES: + return new PageScrollOperator( + this.scrollPresenter, + this.settingRepository, + op.count * repeat + ); + case operations.SCROLL_TOP: + return new ScrollToTopOperator( + this.scrollPresenter, + this.settingRepository + ); + case operations.SCROLL_BOTTOM: + return new ScrollToBottomOperator( + this.scrollPresenter, + this.settingRepository + ); + case operations.SCROLL_HOME: + return new ScrollToHomeOperator( + this.scrollPresenter, + this.settingRepository + ); + case operations.SCROLL_END: + return new ScrollToEndOperator( + this.scrollPresenter, + this.settingRepository + ); + } + return null; + } +} diff --git a/src/content/operators/impls/ScrollToBottomOperator.ts b/src/content/operators/impls/ScrollToBottomOperator.ts new file mode 100644 index 0000000..4db521b --- /dev/null +++ b/src/content/operators/impls/ScrollToBottomOperator.ts @@ -0,0 +1,20 @@ +import AbstractScrollOperator from "./AbstractScrollOperator"; +import Operator from "../Operator"; +import ScrollPresenter from "../../presenters/ScrollPresenter"; +import SettingRepository from "../../repositories/SettingRepository"; + +export default class ScrollToBottomOperator + extends AbstractScrollOperator + implements Operator { + constructor( + private readonly presenter: ScrollPresenter, + settingRepository: SettingRepository + ) { + super(settingRepository); + } + + async run(): Promise<void> { + const smooth = this.getSmoothScroll(); + this.presenter.scrollToBottom(smooth); + } +} diff --git a/src/content/operators/impls/ScrollToEndOperator.ts b/src/content/operators/impls/ScrollToEndOperator.ts new file mode 100644 index 0000000..8217e15 --- /dev/null +++ b/src/content/operators/impls/ScrollToEndOperator.ts @@ -0,0 +1,20 @@ +import AbstractScrollOperator from "./AbstractScrollOperator"; +import Operator from "../Operator"; +import ScrollPresenter from "../../presenters/ScrollPresenter"; +import SettingRepository from "../../repositories/SettingRepository"; + +export default class ScrollToEndOperator + extends AbstractScrollOperator + implements Operator { + constructor( + private readonly presenter: ScrollPresenter, + settingRepository: SettingRepository + ) { + super(settingRepository); + } + + async run(): Promise<void> { + const smooth = this.getSmoothScroll(); + this.presenter.scrollToEnd(smooth); + } +} diff --git a/src/content/operators/impls/ScrollToHomeOperator.ts b/src/content/operators/impls/ScrollToHomeOperator.ts new file mode 100644 index 0000000..a0d7701 --- /dev/null +++ b/src/content/operators/impls/ScrollToHomeOperator.ts @@ -0,0 +1,20 @@ +import AbstractScrollOperator from "./AbstractScrollOperator"; +import Operator from "../Operator"; +import ScrollPresenter from "../../presenters/ScrollPresenter"; +import SettingRepository from "../../repositories/SettingRepository"; + +export default class ScrollToHomeOperator + extends AbstractScrollOperator + implements Operator { + constructor( + private readonly presenter: ScrollPresenter, + settingRepository: SettingRepository + ) { + super(settingRepository); + } + + async run(): Promise<void> { + const smooth = this.getSmoothScroll(); + this.presenter.scrollToHome(smooth); + } +} diff --git a/src/content/operators/impls/ScrollToTopOperator.ts b/src/content/operators/impls/ScrollToTopOperator.ts new file mode 100644 index 0000000..6075758 --- /dev/null +++ b/src/content/operators/impls/ScrollToTopOperator.ts @@ -0,0 +1,20 @@ +import AbstractScrollOperator from "./AbstractScrollOperator"; +import Operator from "../Operator"; +import ScrollPresenter from "../../presenters/ScrollPresenter"; +import SettingRepository from "../../repositories/SettingRepository"; + +export default class ScrollToTopOperator + extends AbstractScrollOperator + implements Operator { + constructor( + private readonly presenter: ScrollPresenter, + settingRepository: SettingRepository + ) { + super(settingRepository); + } + + async run(): Promise<void> { + const smooth = this.getSmoothScroll(); + this.presenter.scrollToTop(smooth); + } +} diff --git a/src/content/operators/impls/StartFollowOperator.ts b/src/content/operators/impls/StartFollowOperator.ts new file mode 100644 index 0000000..6f30058 --- /dev/null +++ b/src/content/operators/impls/StartFollowOperator.ts @@ -0,0 +1,14 @@ +import Operator from "../Operator"; +import FollowMasterClient from "../../client/FollowMasterClient"; + +export default class StartFollowOperator implements Operator { + constructor( + private readonly followMasterClient: FollowMasterClient, + private readonly newTab: boolean, + private readonly background: boolean + ) {} + + async run(): Promise<void> { + this.followMasterClient.startFollow(this.newTab, this.background); + } +} diff --git a/src/content/operators/impls/ToggleAddonOperator.ts b/src/content/operators/impls/ToggleAddonOperator.ts new file mode 100644 index 0000000..2a249d6 --- /dev/null +++ b/src/content/operators/impls/ToggleAddonOperator.ts @@ -0,0 +1,16 @@ +import Operator from "../Operator"; +import AddonIndicatorClient from "../../client/AddonIndicatorClient"; +import AddonEnabledRepository from "../../repositories/AddonEnabledRepository"; + +export default class ToggleAddonOperator implements Operator { + constructor( + private readonly indicator: AddonIndicatorClient, + private readonly repository: AddonEnabledRepository + ) {} + + async run(): Promise<void> { + const current = this.repository.get(); + this.repository.set(!current); + await this.indicator.setEnabled(!current); + } +} diff --git a/src/content/operators/impls/URLRepository.ts b/src/content/operators/impls/URLRepository.ts new file mode 100644 index 0000000..a1efc2e --- /dev/null +++ b/src/content/operators/impls/URLRepository.ts @@ -0,0 +1,9 @@ +export default interface URLRepository { + getCurrentURL(): string; +} + +export class URLRepositoryImpl implements URLRepository { + getCurrentURL(): string { + return window.location.href; + } +} diff --git a/src/content/operators/impls/VerticalScrollOperator.ts b/src/content/operators/impls/VerticalScrollOperator.ts new file mode 100644 index 0000000..4ab336c --- /dev/null +++ b/src/content/operators/impls/VerticalScrollOperator.ts @@ -0,0 +1,21 @@ +import AbstractScrollOperator from "./AbstractScrollOperator"; +import Operator from "../Operator"; +import ScrollPresenter from "../../presenters/ScrollPresenter"; +import SettingRepository from "../../repositories/SettingRepository"; + +export default class VerticalScrollOperator + extends AbstractScrollOperator + implements Operator { + constructor( + private readonly presenter: ScrollPresenter, + settingRepository: SettingRepository, + private readonly count: number + ) { + super(settingRepository); + } + + async run(): Promise<void> { + const smooth = this.getSmoothScroll(); + this.presenter.scrollVertically(this.count, smooth); + } +} diff --git a/src/content/operators/impls/YankURLOperator.ts b/src/content/operators/impls/YankURLOperator.ts new file mode 100644 index 0000000..2e774eb --- /dev/null +++ b/src/content/operators/impls/YankURLOperator.ts @@ -0,0 +1,18 @@ +import Operator from "../Operator"; +import ClipboardRepository from "../../repositories/ClipboardRepository"; +import ConsoleClient from "../../client/ConsoleClient"; +import URLRepository from "./URLRepository"; + +export default class YankURLOperator implements Operator { + constructor( + private readonly repository: ClipboardRepository, + private readonly consoleClient: ConsoleClient, + private readonly urlRepository: URLRepository + ) {} + + async run(): Promise<void> { + const url = this.urlRepository.getCurrentURL(); + this.repository.write(url); + await this.consoleClient.info("Yanked " + url); + } +} diff --git a/src/content/presenters/ScrollPresenter.ts b/src/content/presenters/ScrollPresenter.ts index a8b2034..508532c 100644 --- a/src/content/presenters/ScrollPresenter.ts +++ b/src/content/presenters/ScrollPresenter.ts @@ -31,7 +31,7 @@ const isScrollableStyle = (element: Element): boolean => { return canBeScrolled(element); }; -// Find a visiable and scrollable element by depth-first search. Currently +// Find a visible and scrollable element by depth-first search. Currently // this method is called by each scrolling, and the returned value of this // method is not cached. That does not cause performance issue because in the // most pages, the window is root element i,e, documentElement. diff --git a/src/content/repositories/HintKeyRepository.ts b/src/content/repositories/HintKeyRepository.ts new file mode 100644 index 0000000..342b003 --- /dev/null +++ b/src/content/repositories/HintKeyRepository.ts @@ -0,0 +1,49 @@ +export default interface HintKeyRepository { + reset(charset: string): void; + + produce(): string; +} + +const current: { + charset: string; + counter: number[]; +} = { + charset: "", + counter: [], +}; + +export class HintKeyRepositoryImpl implements HintKeyRepository { + reset(charset: string): void { + if (charset.length === 0) { + throw new TypeError("charset is empty"); + } + current.charset = charset; + current.counter = []; + } + + produce(): string { + if (current.charset === "") { + throw new Error("charset is not set"); + } + this.increment(); + + return current.counter.map((x) => current.charset[x]).join(""); + } + + private increment(): void { + const max = current.charset.length - 1; + if (current.counter.every((x) => x === max)) { + current.counter = new Array(current.counter.length + 1).fill(0); + return; + } + + current.counter.reverse(); + const len = current.charset.length; + let num = current.counter.reduce((x, y, index) => x + y * len ** index) + 1; + for (let i = 0; i < current.counter.length; ++i) { + current.counter[i] = num % len; + num = ~~(num / len); + } + current.counter.reverse(); + } +} diff --git a/src/content/usecases/ClipboardUseCase.ts b/src/content/usecases/ClipboardUseCase.ts deleted file mode 100644 index 875fc11..0000000 --- a/src/content/usecases/ClipboardUseCase.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { injectable, inject } from "tsyringe"; -import * as urls from "../../shared/urls"; -import ClipboardRepository from "../repositories/ClipboardRepository"; -import SettingRepository from "../repositories/SettingRepository"; -import ConsoleClient from "../client/ConsoleClient"; -import OperationClient from "../client/OperationClient"; - -@injectable() -export default class ClipboardUseCase { - constructor( - @inject("ClipboardRepository") private repository: ClipboardRepository, - @inject("SettingRepository") private settingRepository: SettingRepository, - @inject("ConsoleClient") private consoleClient: ConsoleClient, - @inject("OperationClient") private operationClinet: OperationClient - ) {} - - async yankCurrentURL(): Promise<string> { - 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> { - 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 - // access to clipboard until Firefox 63. - await this.operationClinet.internalOpenUrl(url, newTab); - } -} diff --git a/src/content/usecases/FindSlaveUseCase.ts b/src/content/usecases/FindSlaveUseCase.ts deleted file mode 100644 index 3b8c4b4..0000000 --- a/src/content/usecases/FindSlaveUseCase.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { injectable, inject } from "tsyringe"; -import FindMasterClient from "../client/FindMasterClient"; - -@injectable() -export default class FindSlaveUseCase { - constructor( - @inject("FindMasterClient") private findMasterClient: FindMasterClient - ) {} - - findNext() { - this.findMasterClient.findNext(); - } - - findPrev() { - this.findMasterClient.findPrev(); - } -} diff --git a/src/content/usecases/FocusUseCase.ts b/src/content/usecases/FocusUseCase.ts deleted file mode 100644 index 8c62003..0000000 --- a/src/content/usecases/FocusUseCase.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { injectable, inject } from "tsyringe"; -import FocusPresenter from "../presenters/FocusPresenter"; - -@injectable() -export default class FocusUseCases { - constructor(@inject("FocusPresenter") private presenter: FocusPresenter) {} - - focusFirstInput() { - this.presenter.focusFirstElement(); - } -} diff --git a/src/content/usecases/FollowMasterUseCase.ts b/src/content/usecases/FollowMasterUseCase.ts index 88c682e..f4705e1 100644 --- a/src/content/usecases/FollowMasterUseCase.ts +++ b/src/content/usecases/FollowMasterUseCase.ts @@ -4,13 +4,10 @@ import FollowMasterRepository from "../repositories/FollowMasterRepository"; import FollowSlaveClient from "../client/FollowSlaveClient"; import FollowSlaveClientFactory from "../client/FollowSlaveClientFactory"; import SettingRepository from "../repositories/SettingRepository"; -import HintKeyProducer from "./HintKeyProducer"; +import HintKeyRepository from "../repositories/HintKeyRepository"; @injectable() export default class FollowMasterUseCase { - // TODO Make repository - private producer: HintKeyProducer | null; - constructor( @inject("FollowKeyRepository") private followKeyRepository: FollowKeyRepository, @@ -22,14 +19,15 @@ export default class FollowMasterUseCase { private settingRepository: SettingRepository, @inject("FollowSlaveClientFactory") - private followSlaveClientFactory: FollowSlaveClientFactory - ) { - this.producer = null; - } + private followSlaveClientFactory: FollowSlaveClientFactory, + + @inject("HintKeyRepository") + private hintKeyRepository: HintKeyRepository + ) {} startFollow(newTab: boolean, background: boolean): void { const hintchars = this.settingRepository.get().properties.hintchars; - this.producer = new HintKeyProducer(hintchars); + this.hintKeyRepository.reset(hintchars); this.followKeyRepository.clearKeys(); this.followMasterRepository.setCurrentFollowMode(newTab, background); @@ -59,7 +57,7 @@ export default class FollowMasterUseCase { createSlaveHints(count: number, sender: Window): void { const produced = []; for (let i = 0; i < count; ++i) { - const tag = this.producer!.produce(); + const tag = this.hintKeyRepository.produce(); produced.push(tag); this.followMasterRepository.addTag(tag); } diff --git a/src/content/usecases/HintKeyProducer.ts b/src/content/usecases/HintKeyProducer.ts deleted file mode 100644 index a5e2877..0000000 --- a/src/content/usecases/HintKeyProducer.ts +++ /dev/null @@ -1,37 +0,0 @@ -export default class HintKeyProducer { - private charset: string; - - private counter: number[]; - - constructor(charset: string) { - if (charset.length === 0) { - throw new TypeError("charset is empty"); - } - - this.charset = charset; - this.counter = []; - } - - produce(): string { - this.increment(); - - return this.counter.map((x) => this.charset[x]).join(""); - } - - private increment(): void { - 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(); - 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; - num = ~~(num / len); - } - this.counter.reverse(); - } -} diff --git a/src/content/usecases/MarkKeyUseCase.ts b/src/content/usecases/MarkKeyUseCase.ts index b807c74..61ed135 100644 --- a/src/content/usecases/MarkKeyUseCase.ts +++ b/src/content/usecases/MarkKeyUseCase.ts @@ -15,18 +15,10 @@ export default class MarkKeyUseCase { return this.repository.isJumpMode(); } - enableSetMode(): void { - this.repository.enableSetMode(); - } - disableSetMode(): void { this.repository.disabeSetMode(); } - enableJumpMode(): void { - this.repository.enableJumpMode(); - } - disableJumpMode(): void { this.repository.disabeJumpMode(); } diff --git a/src/content/usecases/ScrollUseCase.ts b/src/content/usecases/ScrollUseCase.ts deleted file mode 100644 index 319c8b4..0000000 --- a/src/content/usecases/ScrollUseCase.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { injectable, inject } from "tsyringe"; -import ScrollPresenter from "../presenters/ScrollPresenter"; -import SettingRepository from "../repositories/SettingRepository"; - -@injectable() -export default class ScrollUseCase { - constructor( - @inject("ScrollPresenter") private presenter: ScrollPresenter, - @inject("SettingRepository") private settingRepository: SettingRepository - ) {} - - scrollVertically(count: number): void { - const smooth = this.getSmoothScroll(); - this.presenter.scrollVertically(count, smooth); - } - - scrollHorizonally(count: number): void { - const smooth = this.getSmoothScroll(); - this.presenter.scrollHorizonally(count, smooth); - } - - scrollPages(count: number): void { - const smooth = this.getSmoothScroll(); - this.presenter.scrollPages(count, smooth); - } - - scrollToTop(): void { - const smooth = this.getSmoothScroll(); - this.presenter.scrollToTop(smooth); - } - - scrollToBottom(): void { - const smooth = this.getSmoothScroll(); - this.presenter.scrollToBottom(smooth); - } - - scrollToHome(): void { - const smooth = this.getSmoothScroll(); - this.presenter.scrollToHome(smooth); - } - - scrollToEnd(): void { - const smooth = this.getSmoothScroll(); - this.presenter.scrollToEnd(smooth); - } - - private getSmoothScroll(): boolean { - const settings = this.settingRepository.get(); - return settings.properties.smoothscroll; - } -} diff --git a/test/background/mock/MockBrowserSettingRepository.ts b/test/background/mock/MockBrowserSettingRepository.ts new file mode 100644 index 0000000..22e7084 --- /dev/null +++ b/test/background/mock/MockBrowserSettingRepository.ts @@ -0,0 +1,10 @@ +import BrowserSettingRepository from "../../../src/background/repositories/BrowserSettingRepository"; + +export default class MockBrowserSettingRepository + implements BrowserSettingRepository { + constructor(private readonly homepageUrls: string[]) {} + + getHomepageUrls(): Promise<string[]> { + return Promise.resolve(this.homepageUrls); + } +} diff --git a/test/background/mock/MockConsoleClient.ts b/test/background/mock/MockConsoleClient.ts new file mode 100644 index 0000000..d1f8fc3 --- /dev/null +++ b/test/background/mock/MockConsoleClient.ts @@ -0,0 +1,23 @@ +import ConsoleClient from "../../../src/background/infrastructures/ConsoleClient"; + +export default class MockConsoleClient implements ConsoleClient { + hide(_tabId: number): Promise<any> { + throw new Error("not implemented"); + } + + showCommand(_tabId: number, _command: string): Promise<any> { + throw new Error("not implemented"); + } + + showError(_tabId: number, _message: string): Promise<any> { + throw new Error("not implemented"); + } + + showFind(_tabId: number): Promise<any> { + throw new Error("not implemented"); + } + + showInfo(_tabId: number, _message: string): Promise<any> { + throw new Error("not implemented"); + } +} diff --git a/test/background/mock/MockNavigateClient.ts b/test/background/mock/MockNavigateClient.ts new file mode 100644 index 0000000..d9442a4 --- /dev/null +++ b/test/background/mock/MockNavigateClient.ts @@ -0,0 +1,19 @@ +import NavigateClient from "../../../src/background/clients/NavigateClient"; + +export default class MockNavigateClient implements NavigateClient { + historyNext(_tabId: number): Promise<void> { + throw new Error("not implemented"); + } + + historyPrev(_tabId: number): Promise<void> { + throw new Error("not implemented"); + } + + linkNext(_tabId: number): Promise<void> { + throw new Error("not implemented"); + } + + linkPrev(_tabId: number): Promise<void> { + throw new Error("not implemented"); + } +} diff --git a/test/background/mock/MockRepeatRepository.ts b/test/background/mock/MockRepeatRepository.ts new file mode 100644 index 0000000..1a686c8 --- /dev/null +++ b/test/background/mock/MockRepeatRepository.ts @@ -0,0 +1,14 @@ +import RepeatRepository from "../../../src/background/repositories/RepeatRepository"; +import { Operation } from "../../../src/shared/operations"; + +export default class MockRepeatRepository implements RepeatRepository { + private op: Operation | undefined = undefined; + + getLastOperation(): Operation | undefined { + return this.op; + } + + setLastOperation(op: Operation): void { + this.op = op; + } +} diff --git a/test/background/mock/MockTabPresenter.ts b/test/background/mock/MockTabPresenter.ts new file mode 100644 index 0000000..22fb947 --- /dev/null +++ b/test/background/mock/MockTabPresenter.ts @@ -0,0 +1,179 @@ +import TabPresenter from "../../../src/background/presenters/TabPresenter"; + +export default class MockTabPresenter implements TabPresenter { + private readonly tabs: browser.tabs.Tab[] = []; + private readonly zooms: number[] = []; + private nextid = 0; + + private readonly lastSelectedId: number | undefined; + + private static defaultTabOptions = { + hidden: false, + highlighted: false, + incognito: false, + isArticle: false, + isInReaderMode: false, + lastAccessed: 0, + pinned: false, + selected: false, + windowId: 0, + }; + + create( + url: string, + opts?: { + active?: boolean; + cookieStoreId?: string; + index?: number; + openerTabId?: number; + pinned?: boolean; + windowId?: number; + } + ): Promise<browser.tabs.Tab> { + const tab = { + ...MockTabPresenter.defaultTabOptions, + ...opts, + id: this.nextid++, + active: false, + title: "welcome, world", + url, + index: this.tabs.length, + }; + if (opts?.active || this.tabs.length === 0) { + this.tabs.forEach((t) => (t.active = false)); + tab.active = true; + } + this.tabs.push(tab); + this.zooms.push(1); + return Promise.resolve(tab); + } + + duplicate(id: number): Promise<browser.tabs.Tab> { + const src = this.tabs.find((t) => t.id === id); + if (!src) { + throw new Error(`tab ${id} not found`); + } + this.tabs.forEach((t) => (t.active = false)); + const tab = { ...src, id: this.nextid++, active: true }; + this.tabs.push(tab); + this.zooms.push(1); + + return Promise.resolve(tab); + } + + getAll(): Promise<browser.tabs.Tab[]> { + return Promise.resolve([...this.tabs]); + } + + getByKeyword( + keyword: string, + excludePinned: boolean + ): Promise<browser.tabs.Tab[]> { + const tabs = this.tabs + + .filter((t) => { + return ( + (t.url && t.url.toLowerCase().includes(keyword.toLowerCase())) || + (t.title && t.title.toLowerCase().includes(keyword.toLowerCase())) + ); + }) + .filter((t) => { + return !(excludePinned && t.pinned); + }); + return Promise.resolve(tabs); + } + + getCurrent(): Promise<browser.tabs.Tab> { + const tab = this.tabs.find((t) => t.active); + if (!tab) { + throw new Error("active tab not found"); + } + return Promise.resolve(tab); + } + + getLastSelectedId(): Promise<number | undefined> { + return Promise.resolve(this.lastSelectedId); + } + + getZoom(tabId: number): Promise<number> { + const index = this.tabs.findIndex((t) => t.id === tabId); + if (index === -1) { + throw new Error(`tab ${tabId} not found`); + } + return Promise.resolve(this.zooms[index]); + } + + onSelected( + _listener: (arg: { tabId: number; windowId: number }) => void + ): void { + throw new Error("not implemented"); + } + + open(url: string, tabId?: number): Promise<browser.tabs.Tab> { + let tab = this.tabs.find((t) => t.active); + if (!tab) { + throw new Error(`active tab not found`); + } + if (tabId !== undefined) { + tab = this.tabs.find((t) => t.id === tabId); + } + if (!tab) { + throw new Error(`tab ${tabId} not found`); + } + tab.url = url; + return Promise.resolve(tab); + } + + reload(_tabId: number, _cache: boolean): Promise<void> { + throw new Error("not implemented"); + } + + remove(ids: number[]): Promise<void> { + for (const id of ids) { + const index = this.tabs.findIndex((t) => t.id === id); + if (index === -1) { + throw new Error(`tab ${id} not found`); + } + const tab = this.tabs[index]; + this.tabs.splice(index, 1); + this.zooms.splice(index, 1); + if (tab.active) { + this.tabs[Math.min(index, this.tabs.length - 1)].active = true; + } + } + + return Promise.resolve(undefined); + } + + reopen(): Promise<void> { + throw new Error("not implemented"); + } + + select(tabId: number): Promise<void> { + const tab = this.tabs.find((t) => t.id === tabId); + if (!tab) { + throw new Error(`tab ${tabId} not found`); + } + this.tabs.forEach((t) => (t.active = false)); + tab.active = true; + return Promise.resolve(undefined); + } + + setPinned(tabId: number, pinned: boolean): Promise<void> { + const tab = this.tabs.find((t) => t.id === tabId); + if (!tab) { + throw new Error(`tab ${tabId} not found`); + } + tab.pinned = pinned; + return Promise.resolve(); + } + + setZoom(tabId: number, factor: number): Promise<void> { + const index = this.tabs.findIndex((t) => t.id === tabId); + if (index === -1) { + throw new Error(`tab ${tabId} not found`); + } + this.zooms[index] = factor; + return Promise.resolve(); + } +} diff --git a/test/background/mock/MockWindowPresenter.ts b/test/background/mock/MockWindowPresenter.ts new file mode 100644 index 0000000..420ae8b --- /dev/null +++ b/test/background/mock/MockWindowPresenter.ts @@ -0,0 +1,7 @@ +import WindowPresenter from "../../../src/background/presenters/WindowPresenter"; + +export default class MockWindowPresenter implements WindowPresenter { + create(_url: string): Promise<void> { + throw new Error("not implemented"); + } +} diff --git a/test/background/mock/MockZoomPresenter.ts b/test/background/mock/MockZoomPresenter.ts new file mode 100644 index 0000000..53d1980 --- /dev/null +++ b/test/background/mock/MockZoomPresenter.ts @@ -0,0 +1,15 @@ +import ZoomPresenter from "../../../src/background/presenters/ZoomPresenter"; + +export default class MockZoomPresenter implements ZoomPresenter { + resetZoom(): Promise<void> { + throw new Error("not implemented"); + } + + zoomIn(): Promise<void> { + throw new Error("not implemented"); + } + + zoomOut(): Promise<void> { + throw new Error("not implemented"); + } +} diff --git a/test/background/operators/impls/CancelOperator.test.ts b/test/background/operators/impls/CancelOperator.test.ts new file mode 100644 index 0000000..915becf --- /dev/null +++ b/test/background/operators/impls/CancelOperator.test.ts @@ -0,0 +1,24 @@ +import sinon from "sinon"; +import CancelOperator from "../../../../src/background/operators/impls/CancelOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import MockConsoleClient from "../../mock/MockConsoleClient"; + +describe("CancelOperator", () => { + describe("#run", () => { + it("hides console", async () => { + const tabPresenter = new MockTabPresenter(); + const currenTab = await tabPresenter.create("https://example.com/"); + + const consoleClient = new MockConsoleClient(); + const mock = sinon + .mock(consoleClient) + .expects("hide") + .withArgs(currenTab?.id); + const sut = new CancelOperator(tabPresenter, consoleClient); + + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/CloseTabOperator.test.ts b/test/background/operators/impls/CloseTabOperator.test.ts new file mode 100644 index 0000000..ba9cbfe --- /dev/null +++ b/test/background/operators/impls/CloseTabOperator.test.ts @@ -0,0 +1,61 @@ +import { expect } from "chai"; +import CloseTabOperator from "../../../../src/background/operators/impls/CloseTabOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; + +describe("CloseTabOperator", () => { + describe("#run", () => { + it("close a current tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const sut = new CloseTabOperator(tabPresenter); + + await sut.run(); + + const tabs = await tabPresenter.getAll(); + expect(tabs.map((t) => t.url)).to.deep.equal([ + "https://example.com/1", + "https://example.com/3", + ]); + }); + + it("close a current tab forcely", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { + pinned: true, + active: false, + }); + await tabPresenter.create("https://example.com/2", { + pinned: true, + active: true, + }); + await tabPresenter.create("https://example.com/3", { + pinned: true, + active: false, + }); + const sut = new CloseTabOperator(tabPresenter, true); + + await sut.run(); + + const tabs = await tabPresenter.getAll(); + expect(tabs.map((t) => t.url)).to.deep.equal([ + "https://example.com/1", + "https://example.com/3", + ]); + }); + + it("close a current tab and select left of the closed tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const sut = new CloseTabOperator(tabPresenter, false, true); + + await sut.run(); + + const tab = await tabPresenter.getCurrent(); + expect(tab.url).to.equal("https://example.com/1"); + }); + }); +}); diff --git a/test/background/operators/impls/CloseTabRightOperator.test.ts b/test/background/operators/impls/CloseTabRightOperator.test.ts new file mode 100644 index 0000000..c2a106c --- /dev/null +++ b/test/background/operators/impls/CloseTabRightOperator.test.ts @@ -0,0 +1,24 @@ +import { expect } from "chai"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import CloseTabRightOperator from "../../../../src/background/operators/impls/CloseTabRightOperator"; + +describe("CloseTabRightOperator", () => { + describe("#run", () => { + it("close the right of the current tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + await tabPresenter.create("https://example.com/4", { active: false }); + const sut = new CloseTabRightOperator(tabPresenter); + + await sut.run(); + + const tabs = await tabPresenter.getAll(); + expect(tabs.map((t) => t.url)).to.deep.equal([ + "https://example.com/1", + "https://example.com/2", + ]); + }); + }); +}); diff --git a/test/background/operators/impls/CommandOperatorFactoryChain.test.ts b/test/background/operators/impls/CommandOperatorFactoryChain.test.ts new file mode 100644 index 0000000..e481c5a --- /dev/null +++ b/test/background/operators/impls/CommandOperatorFactoryChain.test.ts @@ -0,0 +1,42 @@ +import "reflect-metadata"; +import { expect } from "chai"; +import CommandOperatorFactoryChain from "../../../../src/background/operators/impls/CommandOperatorFactoryChain"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import MockConsoleClient from "../../mock/MockConsoleClient"; +import * as operations from "../../../../src/shared/operations"; +import ShowCommandOperator from "../../../../src/background/operators/impls/ShowCommandOperator"; +import ShowTabOpenCommandOperator from "../../../../src/background/operators/impls/ShowTabOpenCommandOperator"; +import ShowWinOpenCommandOperator from "../../../../src/background/operators/impls/ShowWinOpenCommandOperator"; +import ShowBufferCommandOperator from "../../../../src/background/operators/impls/ShowBufferCommandOperator"; +import ShowAddBookmarkOperator from "../../../../src/background/operators/impls/ShowAddBookmarkOperator"; +import StartFindOperator from "../../../../src/background/operators/impls/StartFindOperator"; + +describe("CommandOperatorFactoryChain", () => { + describe("#create", () => { + it("returns a operator for the operation", async () => { + const tabPresenter = new MockTabPresenter(); + const consoleClient = new MockConsoleClient(); + const sut = new CommandOperatorFactoryChain(tabPresenter, consoleClient); + + expect(sut.create({ type: operations.COMMAND_SHOW })).to.be.instanceOf( + ShowCommandOperator + ); + expect( + sut.create({ type: operations.COMMAND_SHOW_TABOPEN, alter: true }) + ).to.be.instanceOf(ShowTabOpenCommandOperator); + expect( + sut.create({ type: operations.COMMAND_SHOW_WINOPEN, alter: true }) + ).to.be.instanceOf(ShowWinOpenCommandOperator); + expect( + sut.create({ type: operations.COMMAND_SHOW_BUFFER }) + ).to.be.instanceOf(ShowBufferCommandOperator); + expect( + sut.create({ type: operations.COMMAND_SHOW_ADDBOOKMARK, alter: true }) + ).to.be.instanceOf(ShowAddBookmarkOperator); + expect(sut.create({ type: operations.FIND_START })).to.be.instanceOf( + StartFindOperator + ); + expect(sut.create({ type: operations.CANCEL })).to.be.null; + }); + }); +}); diff --git a/test/background/operators/impls/DuplicateTabOperator.test.ts b/test/background/operators/impls/DuplicateTabOperator.test.ts new file mode 100644 index 0000000..ce2c19d --- /dev/null +++ b/test/background/operators/impls/DuplicateTabOperator.test.ts @@ -0,0 +1,25 @@ +import { expect } from "chai"; +import DuplicateTabOperator from "../../../../src/background/operators/impls/DuplicateTabOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; + +describe("DuplicateTabOperator", () => { + describe("#run", () => { + it("duplicate a tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const sut = new DuplicateTabOperator(tabPresenter); + + await sut.run(); + + const tabs = await tabPresenter.getAll(); + expect(tabs.map((t) => t.url)).to.deep.equal([ + "https://example.com/1", + "https://example.com/2", + "https://example.com/3", + "https://example.com/2", + ]); + }); + }); +}); diff --git a/test/background/operators/impls/InternalOpenURLOperator.test.ts b/test/background/operators/impls/InternalOpenURLOperator.test.ts new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/background/operators/impls/InternalOpenURLOperator.test.ts diff --git a/test/background/operators/impls/InternalOperatorFactoryChain.test.ts b/test/background/operators/impls/InternalOperatorFactoryChain.test.ts new file mode 100644 index 0000000..09029db --- /dev/null +++ b/test/background/operators/impls/InternalOperatorFactoryChain.test.ts @@ -0,0 +1,37 @@ +import "reflect-metadata"; +import { expect } from "chai"; +import InternalOperatorFactoryChain from "../../../../src/background/operators/impls/InternalOperatorFactoryChain"; +import MockWindowPresenter from "../../mock/MockWindowPresenter"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import MockConsoleClient from "../../mock/MockConsoleClient"; +import CancelOperator from "../../../../src/background/operators/impls/CancelOperator"; +import InternalOpenURLOperator from "../../../../src/background/operators/impls/InternalOpenURLOperator"; +import * as operations from "../../../../src/shared/operations"; + +describe("InternalOperatorFactoryChain", () => { + describe("#create", () => { + it("returns a operator for the operation", async () => { + const windowPresenter = new MockWindowPresenter(); + const tabPresenter = new MockTabPresenter(); + const consoleClient = new MockConsoleClient(); + const sut = new InternalOperatorFactoryChain( + windowPresenter, + tabPresenter, + consoleClient + ); + + expect(sut.create({ type: operations.CANCEL })).to.be.instanceOf( + CancelOperator + ); + expect( + sut.create({ + type: operations.INTERNAL_OPEN_URL, + url: "https://example.com", + newTab: false, + newWindow: false, + }) + ).to.be.instanceOf(InternalOpenURLOperator); + expect(sut.create({ type: operations.COMMAND_SHOW })).to.be.null; + }); + }); +}); diff --git a/test/background/operators/impls/NavigateHistoryNextOperator.test.ts b/test/background/operators/impls/NavigateHistoryNextOperator.test.ts new file mode 100644 index 0000000..de8f597 --- /dev/null +++ b/test/background/operators/impls/NavigateHistoryNextOperator.test.ts @@ -0,0 +1,25 @@ +import sinon from "sinon"; +import NavigateHistoryNextOperator from "../../../../src/background/operators/impls/NavigateHistoryNextOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import MockNavigateClient from "../../mock/MockNavigateClient"; + +describe("NavigateHistoryNextOperator", () => { + describe("#run", () => { + it("send a message to navigate next in the history", async () => { + const navigateClient = new MockNavigateClient(); + const mock = sinon + .mock(navigateClient) + .expects("historyNext") + .withArgs(1); + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const sut = new NavigateHistoryNextOperator(tabPresenter, navigateClient); + + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/NavigateHistoryPrevOperator.test.ts b/test/background/operators/impls/NavigateHistoryPrevOperator.test.ts new file mode 100644 index 0000000..6ebe71e --- /dev/null +++ b/test/background/operators/impls/NavigateHistoryPrevOperator.test.ts @@ -0,0 +1,25 @@ +import sinon from "sinon"; +import NavigateHistoryPrevOperator from "../../../../src/background/operators/impls/NavigateHistoryPrevOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import MockNavigateClient from "../../mock/MockNavigateClient"; + +describe("NavigateHistoryPrevOperator", () => { + describe("#run", () => { + it("send a message to navigate previous in the history", async () => { + const navigateClient = new MockNavigateClient(); + const mock = sinon + .mock(navigateClient) + .expects("historyPrev") + .withArgs(1); + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const sut = new NavigateHistoryPrevOperator(tabPresenter, navigateClient); + + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/NavigateLinkNextOperator.test.ts b/test/background/operators/impls/NavigateLinkNextOperator.test.ts new file mode 100644 index 0000000..09c4907 --- /dev/null +++ b/test/background/operators/impls/NavigateLinkNextOperator.test.ts @@ -0,0 +1,22 @@ +import sinon from "sinon"; +import NavigateLinkNextOperator from "../../../../src/background/operators/impls/NavigateLinkNextOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import MockNavigateClient from "../../mock/MockNavigateClient"; + +describe("NavigateLinkNextOperator", () => { + describe("#run", () => { + it("send a message to navigate next page", async () => { + const navigateClient = new MockNavigateClient(); + const mock = sinon.mock(navigateClient).expects("linkNext").withArgs(1); + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const sut = new NavigateLinkNextOperator(tabPresenter, navigateClient); + + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/NavigateLinkPrevOperator.test.ts b/test/background/operators/impls/NavigateLinkPrevOperator.test.ts new file mode 100644 index 0000000..6b7f791 --- /dev/null +++ b/test/background/operators/impls/NavigateLinkPrevOperator.test.ts @@ -0,0 +1,22 @@ +import sinon from "sinon"; +import NavigateLinkPrevOperator from "../../../../src/background/operators/impls/NavigateLinkPrevOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import MockNavigateClient from "../../mock/MockNavigateClient"; + +describe("NavigateLinkPrevOperator", () => { + describe("#run", () => { + it("send a message to navigate next page", async () => { + const navigateClient = new MockNavigateClient(); + const mock = sinon.mock(navigateClient).expects("linkPrev").withArgs(1); + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const sut = new NavigateLinkPrevOperator(tabPresenter, navigateClient); + + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/NavigateOperatorFactoryChain.test.ts b/test/background/operators/impls/NavigateOperatorFactoryChain.test.ts new file mode 100644 index 0000000..dfb5654 --- /dev/null +++ b/test/background/operators/impls/NavigateOperatorFactoryChain.test.ts @@ -0,0 +1,56 @@ +import "reflect-metadata"; +import { expect } from "chai"; +import NavigateOperatorFactoryChain from "../../../../src/background/operators/impls/NavigateOperatorFactoryChain"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import MockNavigateClient from "../../mock/MockNavigateClient"; +import MockBrowserSettingRepository from "../../mock/MockBrowserSettingRepository"; +import NavigateHistoryPrevOperator from "../../../../src/background/operators/impls/NavigateHistoryPrevOperator"; +import NavigateHistoryNextOperator from "../../../../src/background/operators/impls/NavigateHistoryNextOperator"; +import NavigateLinkPrevOperator from "../../../../src/background/operators/impls/NavigateLinkPrevOperator"; +import NavigateLinkNextOperator from "../../../../src/background/operators/impls/NavigateLinkNextOperator"; +import NavigateParentOperator from "../../../../src/background/operators/impls/NavigateParentOperator"; +import NavigateRootOperator from "../../../../src/background/operators/impls/NavigateRootOperator"; +import OpenHomeOperator from "../../../../src/background/operators/impls/OpenHomeOperator"; +import OpenSourceOperator from "../../../../src/background/operators/impls/OpenSourceOperator"; +import * as operations from "../../../../src/shared/operations"; + +describe("NavigateOperatorFactoryChain", () => { + describe("#create", () => { + it("returns a operator for the operation", async () => { + const tabPresenter = new MockTabPresenter(); + const navigateClient = new MockNavigateClient(); + const browserSettingRepository = new MockBrowserSettingRepository([]); + const sut = new NavigateOperatorFactoryChain( + tabPresenter, + navigateClient, + browserSettingRepository + ); + + expect( + sut.create({ type: operations.NAVIGATE_HISTORY_PREV }) + ).to.be.instanceOf(NavigateHistoryPrevOperator); + expect( + sut.create({ type: operations.NAVIGATE_HISTORY_NEXT }) + ).to.be.instanceOf(NavigateHistoryNextOperator); + expect( + sut.create({ type: operations.NAVIGATE_LINK_PREV }) + ).to.be.instanceOf(NavigateLinkPrevOperator); + expect( + sut.create({ type: operations.NAVIGATE_LINK_NEXT }) + ).to.be.instanceOf(NavigateLinkNextOperator); + expect(sut.create({ type: operations.NAVIGATE_PARENT })).to.be.instanceOf( + NavigateParentOperator + ); + expect(sut.create({ type: operations.NAVIGATE_ROOT })).to.be.instanceOf( + NavigateRootOperator + ); + expect(sut.create({ type: operations.PAGE_SOURCE })).to.be.instanceOf( + OpenSourceOperator + ); + expect( + sut.create({ type: operations.PAGE_HOME, newTab: false }) + ).to.be.instanceOf(OpenHomeOperator); + expect(sut.create({ type: operations.CANCEL })).to.be.null; + }); + }); +}); diff --git a/test/background/operators/impls/NavigateParentOperator.test.ts b/test/background/operators/impls/NavigateParentOperator.test.ts new file mode 100644 index 0000000..cc57f17 --- /dev/null +++ b/test/background/operators/impls/NavigateParentOperator.test.ts @@ -0,0 +1,53 @@ +import { expect } from "chai"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import NavigateParentOperator from "../../../../src/background/operators/impls/NavigateParentOperator"; + +describe("NavigateParentOperator", () => { + describe("#run", () => { + it("opens a parent directory of the file in the URL", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/fruits/yellow/banana", { + active: true, + }); + const sut = new NavigateParentOperator(tabPresenter); + + await sut.run(); + + const url = (await tabPresenter.getCurrent()).url; + expect(url).to.be.equal("https://example.com/fruits/yellow/"); + }); + + it("opens a parent directory of the directoryin the URL", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/fruits/yellow/"); + const sut = new NavigateParentOperator(tabPresenter); + + await sut.run(); + + const url = (await tabPresenter.getCurrent()).url; + expect(url).to.be.equal("https://example.com/fruits/"); + }); + + it("removes a hash in the URL", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/fruits/yellow/#top"); + const sut = new NavigateParentOperator(tabPresenter); + + await sut.run(); + + const url = (await tabPresenter.getCurrent()).url; + expect(url).to.be.equal("https://example.com/fruits/yellow/"); + }); + + it("removes query parameters in the URL", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/search?q=apple"); + const sut = new NavigateParentOperator(tabPresenter); + + await sut.run(); + + const url = (await tabPresenter.getCurrent()).url; + expect(url).to.be.equal("https://example.com/search"); + }); + }); +}); diff --git a/test/background/operators/impls/NavigateRootOperator.test.ts b/test/background/operators/impls/NavigateRootOperator.test.ts new file mode 100644 index 0000000..bbe574c --- /dev/null +++ b/test/background/operators/impls/NavigateRootOperator.test.ts @@ -0,0 +1,18 @@ +import { expect } from "chai"; +import NavigateRootOperator from "../../../../src/background/operators/impls/NavigateRootOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; + +describe("NavigateRootOperator", () => { + describe("#run", () => { + it("opens root directory in the URL", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/search?q=apple#top"); + const sut = new NavigateRootOperator(tabPresenter); + + await sut.run(); + + const url = (await tabPresenter.getCurrent()).url; + expect(url).to.be.equal("https://example.com"); + }); + }); +}); diff --git a/test/background/operators/impls/OpenHomeOperator.test.ts b/test/background/operators/impls/OpenHomeOperator.test.ts new file mode 100644 index 0000000..3c9288f --- /dev/null +++ b/test/background/operators/impls/OpenHomeOperator.test.ts @@ -0,0 +1,70 @@ +import { expect } from "chai"; +import OpenHomeOperator from "../../../../src/background/operators/impls/OpenHomeOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import MockBrowserSettingRepository from "../../mock/MockBrowserSettingRepository"; + +describe("OpenHomeOperator", () => { + describe("#run", () => { + it("opens a home page of the browser into the current tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/"); + const browserSettingRepository = new MockBrowserSettingRepository([ + "https://example.net/", + ]); + const sut = new OpenHomeOperator( + tabPresenter, + browserSettingRepository, + false + ); + + await sut.run(); + + const url = (await tabPresenter.getCurrent()).url; + expect(url).to.be.equal("https://example.net/"); + }); + + it("opens a home page of the browser into a new tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/"); + const browserSettingRepository = new MockBrowserSettingRepository([ + "https://example.net/", + ]); + const sut = new OpenHomeOperator( + tabPresenter, + browserSettingRepository, + true + ); + + await sut.run(); + + const urls = (await tabPresenter.getAll()).map((t) => t.url); + expect(urls).to.be.deep.equal([ + "https://example.com/", + "https://example.net/", + ]); + }); + + it("opens home pages of the browser", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/"); + const browserSettingRepository = new MockBrowserSettingRepository([ + "https://example.net/", + "https://example.org/", + ]); + const sut = new OpenHomeOperator( + tabPresenter, + browserSettingRepository, + false + ); + + await sut.run(); + + const urls = (await tabPresenter.getAll()).map((t) => t.url); + expect(urls).to.be.deep.equal([ + "https://example.com/", + "https://example.net/", + "https://example.org/", + ]); + }); + }); +}); diff --git a/test/background/operators/impls/OpenSourceOperator.test.ts b/test/background/operators/impls/OpenSourceOperator.test.ts new file mode 100644 index 0000000..541032b --- /dev/null +++ b/test/background/operators/impls/OpenSourceOperator.test.ts @@ -0,0 +1,21 @@ +import { expect } from "chai"; +import OpenSourceOperator from "../../../../src/background/operators/impls/OpenSourceOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; + +describe("OpenSourceOperator", () => { + describe("#run", () => { + it("opens view-source URL of the current tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/"); + const sut = new OpenSourceOperator(tabPresenter); + + await sut.run(); + + const urls = (await tabPresenter.getAll()).map((t) => t.url); + expect(urls).to.be.deep.equal([ + "https://example.com/", + "view-source:https://example.com/", + ]); + }); + }); +}); diff --git a/test/background/operators/impls/PinTabOperator.test.ts b/test/background/operators/impls/PinTabOperator.test.ts new file mode 100644 index 0000000..0c940b6 --- /dev/null +++ b/test/background/operators/impls/PinTabOperator.test.ts @@ -0,0 +1,25 @@ +import { expect } from "chai"; +import PinTabOperator from "../../../../src/background/operators/impls/PinTabOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; + +describe("PinTabOperator", () => { + describe("#run", () => { + it("make pinned to the current tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/", { + active: true, + pinned: false, + }); + await tabPresenter.create("https://example.com/", { + active: false, + pinned: false, + }); + const sut = new PinTabOperator(tabPresenter); + + await sut.run(); + + const pins = (await tabPresenter.getAll()).map((t) => t.pinned); + expect(pins).to.deep.equal([true, false]); + }); + }); +}); diff --git a/test/background/operators/impls/ReloadTabOperator.test.ts b/test/background/operators/impls/ReloadTabOperator.test.ts new file mode 100644 index 0000000..e87782b --- /dev/null +++ b/test/background/operators/impls/ReloadTabOperator.test.ts @@ -0,0 +1,34 @@ +import sinon from "sinon"; +import ReloadTabOperator from "../../../../src/background/operators/impls/ReloadTabOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; + +describe("ReloadTabOperator", () => { + describe("#run", () => { + it("reloads the current tab with cache", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/", { active: true }); + await tabPresenter.create("https://example.com/", { active: false }); + const mock = sinon.mock(tabPresenter).expects("reload").withArgs(0, true); + + const sut = new ReloadTabOperator(tabPresenter, true); + await sut.run(); + + mock.verify(); + }); + + it("reloads the current tab without cache", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/", { active: true }); + await tabPresenter.create("https://example.com/", { active: false }); + const mock = sinon + .mock(tabPresenter) + .expects("reload") + .withArgs(0, false); + + const sut = new ReloadTabOperator(tabPresenter, false); + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/ReopenTabOperator.test.ts b/test/background/operators/impls/ReopenTabOperator.test.ts new file mode 100644 index 0000000..43b1575 --- /dev/null +++ b/test/background/operators/impls/ReopenTabOperator.test.ts @@ -0,0 +1,17 @@ +import sinon from "sinon"; +import ReopenTabOperator from "../../../../src/background/operators/impls/ReopenTabOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; + +describe("ReopenTabOperator", () => { + describe("#run", () => { + it("reopens closed tabs", async () => { + const tabPresenter = new MockTabPresenter(); + const mock = sinon.mock(tabPresenter).expects("reopen"); + + const sut = new ReopenTabOperator(tabPresenter); + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/RepeatLastOperator.test.ts b/test/background/operators/impls/RepeatLastOperator.test.ts new file mode 100644 index 0000000..57f1227 --- /dev/null +++ b/test/background/operators/impls/RepeatLastOperator.test.ts @@ -0,0 +1,56 @@ +import RepeatLastOperator from "../../../../src/background/operators/impls/RepeatLastOperator"; +import MockRepeatRepository from "../../mock/MockRepeatRepository"; +import OperatorFactory from "../../../../src/background/operators/OperatorFactory"; +import * as operations from "../../../../src/shared/operations"; +import Operator from "../../../../src/background/operators/Operator"; +import sinon from "sinon"; + +class MockOperatorFactory implements OperatorFactory { + create(_op: operations.Operation): Operator { + throw new Error("not implemented"); + } +} + +class MockOperator implements Operator { + run(): Promise<void> { + throw new Error("not implemented"); + } +} + +describe("RepeatLastOperator", () => { + describe("#run", () => { + it("repeat last operation", async () => { + const operator = new MockOperator(); + const operatorMock = sinon.mock(operator).expects("run").once(); + const repeatRepository = new MockRepeatRepository(); + repeatRepository.setLastOperation({ type: operations.CANCEL }); + + const operatorFactory = new MockOperatorFactory(); + const operatorFactoryMock = sinon + .mock(operatorFactory) + .expects("create") + .withArgs({ type: operations.CANCEL }); + operatorFactoryMock.returns(operator); + + const sut = new RepeatLastOperator(repeatRepository, operatorFactory); + await sut.run(); + + operatorFactoryMock.verify(); + operatorMock.verify(); + }); + + it("does nothing if no last operations", async () => { + const repeatRepository = new MockRepeatRepository(); + const operatorFactory = new MockOperatorFactory(); + const operatorFactoryMock = sinon + .mock(operatorFactory) + .expects("create") + .never(); + + const sut = new RepeatLastOperator(repeatRepository, operatorFactory); + await sut.run(); + + operatorFactoryMock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/RepeatOperatorFactoryChain.test.ts b/test/background/operators/impls/RepeatOperatorFactoryChain.test.ts new file mode 100644 index 0000000..e12d788 --- /dev/null +++ b/test/background/operators/impls/RepeatOperatorFactoryChain.test.ts @@ -0,0 +1,32 @@ +import "reflect-metadata"; +import { expect } from "chai"; +import RepeatOperatorFactoryChain from "../../../../src/background/operators/impls/RepeatOperatorFactoryChain"; +import RepeatLastOperator from "../../../../src/background/operators/impls/RepeatLastOperator"; +import OperatorFactory from "../../../../src/background/operators/OperatorFactory"; +import MockRepeatRepository from "../../mock/MockRepeatRepository"; +import Operator from "../../../../src/content/operators/Operator"; +import * as operations from "../../../../src/shared/operations"; + +class MockOperatorFactory implements OperatorFactory { + create(_op: operations.Operation): Operator { + throw new Error("not implemented"); + } +} + +describe("RepeatOperatorFactoryChain", () => { + describe("#create", () => { + it("returns a operator for the operation", async () => { + const repeatRepository = new MockRepeatRepository(); + const operatorFactory = new MockOperatorFactory(); + const sut = new RepeatOperatorFactoryChain( + repeatRepository, + operatorFactory + ); + + expect(sut.create({ type: operations.REPEAT_LAST })).to.be.instanceOf( + RepeatLastOperator + ); + expect(sut.create({ type: operations.CANCEL })).to.be.null; + }); + }); +}); diff --git a/test/background/operators/impls/ResetZoomOperator.test.ts b/test/background/operators/impls/ResetZoomOperator.test.ts new file mode 100644 index 0000000..68cda05 --- /dev/null +++ b/test/background/operators/impls/ResetZoomOperator.test.ts @@ -0,0 +1,17 @@ +import sinon from "sinon"; +import ResetZoomOperator from "../../../../src/background/operators/impls/ResetZoomOperator"; +import MockZoomPresenter from "../../mock/MockZoomPresenter"; + +describe("ResetZoomOperator", () => { + describe("#run", () => { + it("resets zoom on the tab", async () => { + const zoomPresenter = new MockZoomPresenter(); + const mock = sinon.mock(zoomPresenter).expects("resetZoom").once(); + + const sut = new ResetZoomOperator(zoomPresenter); + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/SelectFirstTabOperator.test.ts b/test/background/operators/impls/SelectFirstTabOperator.test.ts new file mode 100644 index 0000000..a3f1d7e --- /dev/null +++ b/test/background/operators/impls/SelectFirstTabOperator.test.ts @@ -0,0 +1,20 @@ +import { expect } from "chai"; +import SelectFirstTabOperator from "../../../../src/background/operators/impls/SelectFirstTabOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; + +describe("SelectFirstTabOperator", () => { + describe("#run", () => { + it("select the leftmost tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + + const sut = new SelectFirstTabOperator(tabPresenter); + await sut.run(); + + const url = (await tabPresenter.getCurrent()).url; + expect(url).to.equal("https://example.com/1"); + }); + }); +}); diff --git a/test/background/operators/impls/SelectLastTabOperator.test.ts b/test/background/operators/impls/SelectLastTabOperator.test.ts new file mode 100644 index 0000000..b8cf5c4 --- /dev/null +++ b/test/background/operators/impls/SelectLastTabOperator.test.ts @@ -0,0 +1,20 @@ +import { expect } from "chai"; +import SelectLastTabOperator from "../../../../src/background/operators/impls/SelectLastTabOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; + +describe("SelectLastTabOperator", () => { + describe("#run", () => { + it("select the rightmost tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + + const sut = new SelectLastTabOperator(tabPresenter); + await sut.run(); + + const url = (await tabPresenter.getCurrent()).url; + expect(url).to.equal("https://example.com/3"); + }); + }); +}); diff --git a/test/background/operators/impls/SelectPreviousSelectedTabOperator.test.ts b/test/background/operators/impls/SelectPreviousSelectedTabOperator.test.ts new file mode 100644 index 0000000..5e6cc73 --- /dev/null +++ b/test/background/operators/impls/SelectPreviousSelectedTabOperator.test.ts @@ -0,0 +1,38 @@ +import { expect } from "chai"; +import sinon from "sinon"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import SelectPreviousSelectedTabOperator from "../../../../src/background/operators/impls/SelectPreviousSelectedTabOperator"; + +describe("SelectPreviousSelectedTabOperator", () => { + describe("#run", () => { + it("select the last-selected tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + sinon.stub(tabPresenter, "getLastSelectedId").returns(Promise.resolve(0)); + + const sut = new SelectPreviousSelectedTabOperator(tabPresenter); + await sut.run(); + + const url = (await tabPresenter.getCurrent()).url; + expect(url).to.equal("https://example.com/1"); + }); + + it("do nothing if no last-selected tabs", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + sinon + .stub(tabPresenter, "getLastSelectedId") + .returns(Promise.resolve(undefined)); + const mock = sinon.mock(tabPresenter).expects("select").never(); + + const sut = new SelectPreviousSelectedTabOperator(tabPresenter); + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/SelectTabNextOperator.test.ts b/test/background/operators/impls/SelectTabNextOperator.test.ts new file mode 100644 index 0000000..5952d92 --- /dev/null +++ b/test/background/operators/impls/SelectTabNextOperator.test.ts @@ -0,0 +1,35 @@ +import { expect } from "chai"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import SelectTabNextOperator from "../../../../src/background/operators/impls/SelectTabNextOperator"; + +describe("SelectTabNextOperator", () => { + describe("#run", () => { + it("select a right tab of the current tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + + const sut = new SelectTabNextOperator(tabPresenter); + await sut.run(); + + const url = (await tabPresenter.getCurrent()).url; + expect(url).to.equal("https://example.com/3"); + }); + }); + + describe("#run", () => { + it("select a right tab of the current tab in rotation", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: false }); + await tabPresenter.create("https://example.com/3", { active: true }); + + const sut = new SelectTabNextOperator(tabPresenter); + await sut.run(); + + const url = (await tabPresenter.getCurrent()).url; + expect(url).to.equal("https://example.com/1"); + }); + }); +}); diff --git a/test/background/operators/impls/SelectTabPrevOperator.test.ts b/test/background/operators/impls/SelectTabPrevOperator.test.ts new file mode 100644 index 0000000..c9092fa --- /dev/null +++ b/test/background/operators/impls/SelectTabPrevOperator.test.ts @@ -0,0 +1,35 @@ +import { expect } from "chai"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import SelectTabPrevOperator from "../../../../src/background/operators/impls/SelectTabPrevOperator"; + +describe("SelectTabPrevOperator", () => { + describe("#run", () => { + it("select a left tab of the current tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + + const sut = new SelectTabPrevOperator(tabPresenter); + await sut.run(); + + const url = (await tabPresenter.getCurrent()).url; + expect(url).to.equal("https://example.com/1"); + }); + }); + + describe("#run", () => { + it("select a left tab of the current tab in rotation", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: true }); + await tabPresenter.create("https://example.com/2", { active: false }); + await tabPresenter.create("https://example.com/3", { active: false }); + + const sut = new SelectTabPrevOperator(tabPresenter); + await sut.run(); + + const url = (await tabPresenter.getCurrent()).url; + expect(url).to.equal("https://example.com/3"); + }); + }); +}); diff --git a/test/background/operators/impls/ShowAddBookmarkOperator.test.ts b/test/background/operators/impls/ShowAddBookmarkOperator.test.ts new file mode 100644 index 0000000..1e083c2 --- /dev/null +++ b/test/background/operators/impls/ShowAddBookmarkOperator.test.ts @@ -0,0 +1,50 @@ +import sinon from "sinon"; +import ShowAddBookmarkOperator from "../../../../src/background/operators/impls/ShowAddBookmarkOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import MockConsoleClient from "../../mock/MockConsoleClient"; + +describe("ShowAddBookmarkOperator", () => { + describe("#run", () => { + it("show command with addbookmark command", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const consoleClient = new MockConsoleClient(); + const mock = sinon + .mock(consoleClient) + .expects("showCommand") + .withArgs(1, "addbookmark "); + + const sut = new ShowAddBookmarkOperator( + tabPresenter, + consoleClient, + false + ); + await sut.run(); + + mock.verify(); + }); + + it("show command with addbookmark command and an URL of the current tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const consoleClient = new MockConsoleClient(); + const mock = sinon + .mock(consoleClient) + .expects("showCommand") + .withArgs(1, "addbookmark welcome, world"); + + const sut = new ShowAddBookmarkOperator( + tabPresenter, + consoleClient, + true + ); + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/ShowBufferCommandOperator.test.ts b/test/background/operators/impls/ShowBufferCommandOperator.test.ts new file mode 100644 index 0000000..91455b3 --- /dev/null +++ b/test/background/operators/impls/ShowBufferCommandOperator.test.ts @@ -0,0 +1,25 @@ +import sinon from "sinon"; +import ShowBufferCommandOperator from "../../../../src/background/operators/impls/ShowBufferCommandOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import MockConsoleClient from "../../mock/MockConsoleClient"; + +describe("ShowBufferCommandOperator", () => { + describe("#run", () => { + it("show command with buffer command", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const consoleClient = new MockConsoleClient(); + const mock = sinon + .mock(consoleClient) + .expects("showCommand") + .withArgs(1, "buffer "); + + const sut = new ShowBufferCommandOperator(tabPresenter, consoleClient); + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/ShowCommandOperator.test.ts b/test/background/operators/impls/ShowCommandOperator.test.ts new file mode 100644 index 0000000..83b028c --- /dev/null +++ b/test/background/operators/impls/ShowCommandOperator.test.ts @@ -0,0 +1,25 @@ +import sinon from "sinon"; +import ShowCommandOperator from "../../../../src/background/operators/impls/ShowCommandOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import MockConsoleClient from "../../mock/MockConsoleClient"; + +describe("ShowCommandOperator", () => { + describe("#run", () => { + it("show command with addbookmark command", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const consoleClient = new MockConsoleClient(); + const mock = sinon + .mock(consoleClient) + .expects("showCommand") + .withArgs(1, ""); + + const sut = new ShowCommandOperator(tabPresenter, consoleClient); + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/ShowOpenCommandOperator.test.ts b/test/background/operators/impls/ShowOpenCommandOperator.test.ts new file mode 100644 index 0000000..2c2105a --- /dev/null +++ b/test/background/operators/impls/ShowOpenCommandOperator.test.ts @@ -0,0 +1,50 @@ +import sinon from "sinon"; +import ShowOpenCommandOperator from "../../../../src/background/operators/impls/ShowOpenCommandOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import MockConsoleClient from "../../mock/MockConsoleClient"; + +describe("ShowOpenCommandOperator", () => { + describe("#run", () => { + it("show command with open command", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const consoleClient = new MockConsoleClient(); + const mock = sinon + .mock(consoleClient) + .expects("showCommand") + .withArgs(1, "open "); + + const sut = new ShowOpenCommandOperator( + tabPresenter, + consoleClient, + false + ); + await sut.run(); + + mock.verify(); + }); + + it("show command with open command and an URL of the current tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const consoleClient = new MockConsoleClient(); + const mock = sinon + .mock(consoleClient) + .expects("showCommand") + .withArgs(1, "open https://example.com/2"); + + const sut = new ShowOpenCommandOperator( + tabPresenter, + consoleClient, + true + ); + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/ShowTabOpenCommandOperator.test.ts b/test/background/operators/impls/ShowTabOpenCommandOperator.test.ts new file mode 100644 index 0000000..e291d05 --- /dev/null +++ b/test/background/operators/impls/ShowTabOpenCommandOperator.test.ts @@ -0,0 +1,50 @@ +import sinon from "sinon"; +import ShowTabOpenCommandOperator from "../../../../src/background/operators/impls/ShowTabOpenCommandOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import MockConsoleClient from "../../mock/MockConsoleClient"; + +describe("ShowTabOpenCommandOperator", () => { + describe("#run", () => { + it("show command with tabopen command", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const consoleClient = new MockConsoleClient(); + const mock = sinon + .mock(consoleClient) + .expects("showCommand") + .withArgs(1, "tabopen "); + + const sut = new ShowTabOpenCommandOperator( + tabPresenter, + consoleClient, + false + ); + await sut.run(); + + mock.verify(); + }); + + it("show command with tabopen command and an URL of the current tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const consoleClient = new MockConsoleClient(); + const mock = sinon + .mock(consoleClient) + .expects("showCommand") + .withArgs(1, "tabopen https://example.com/2"); + + const sut = new ShowTabOpenCommandOperator( + tabPresenter, + consoleClient, + true + ); + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/ShowWinOpenCommandOperator.test.ts b/test/background/operators/impls/ShowWinOpenCommandOperator.test.ts new file mode 100644 index 0000000..c81a2d4 --- /dev/null +++ b/test/background/operators/impls/ShowWinOpenCommandOperator.test.ts @@ -0,0 +1,50 @@ +import sinon from "sinon"; +import ShowWinOpenCommandOperator from "../../../../src/background/operators/impls/ShowWinOpenCommandOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import MockConsoleClient from "../../mock/MockConsoleClient"; + +describe("ShowWinOpenCommandOperator", () => { + describe("#run", () => { + it("show command with winopen command", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const consoleClient = new MockConsoleClient(); + const mock = sinon + .mock(consoleClient) + .expects("showCommand") + .withArgs(1, "winopen "); + + const sut = new ShowWinOpenCommandOperator( + tabPresenter, + consoleClient, + false + ); + await sut.run(); + + mock.verify(); + }); + + it("show command with winopen command and an URL of the current tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const consoleClient = new MockConsoleClient(); + const mock = sinon + .mock(consoleClient) + .expects("showCommand") + .withArgs(1, "winopen https://example.com/2"); + + const sut = new ShowWinOpenCommandOperator( + tabPresenter, + consoleClient, + true + ); + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/StartFindOperator.test.ts b/test/background/operators/impls/StartFindOperator.test.ts new file mode 100644 index 0000000..7764520 --- /dev/null +++ b/test/background/operators/impls/StartFindOperator.test.ts @@ -0,0 +1,22 @@ +import sinon from "sinon"; +import StartFindOperator from "../../../../src/background/operators/impls/StartFindOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import MockConsoleClient from "../../mock/MockConsoleClient"; + +describe("StartFindOperator", () => { + describe("#run", () => { + it("show find console", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/1", { active: false }); + await tabPresenter.create("https://example.com/2", { active: true }); + await tabPresenter.create("https://example.com/3", { active: false }); + const consoleClient = new MockConsoleClient(); + const mock = sinon.mock(consoleClient).expects("showFind").withArgs(1); + + const sut = new StartFindOperator(tabPresenter, consoleClient); + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/TabOperatorFactoryChain.test.ts b/test/background/operators/impls/TabOperatorFactoryChain.test.ts new file mode 100644 index 0000000..7ab5de0 --- /dev/null +++ b/test/background/operators/impls/TabOperatorFactoryChain.test.ts @@ -0,0 +1,71 @@ +import "reflect-metadata"; +import { expect } from "chai"; +import TabOperatorFactoryChain from "../../../../src/background/operators/impls/TabOperatorFactoryChain"; +import MockTabPresenter from "../../mock/MockTabPresenter"; +import DuplicateTabOperator from "../../../../src/background/operators/impls/DuplicateTabOperator"; +import TogglePinnedTabOperator from "../../../../src/background/operators/impls/TogglePinnedTabOperator"; +import UnpinTabOperator from "../../../../src/background/operators/impls/UnpinTabOperator"; +import PinTabOperator from "../../../../src/background/operators/impls/PinTabOperator"; +import ReloadTabOperator from "../../../../src/background/operators/impls/ReloadTabOperator"; +import SelectPreviousSelectedTabOperator from "../../../../src/background/operators/impls/SelectPreviousSelectedTabOperator"; +import SelectLastTabOperator from "../../../../src/background/operators/impls/SelectLastTabOperator"; +import SelectFirstTabOperator from "../../../../src/background/operators/impls/SelectFirstTabOperator"; +import SelectTabNextOperator from "../../../../src/background/operators/impls/SelectTabNextOperator"; +import SelectTabPrevOperator from "../../../../src/background/operators/impls/SelectTabPrevOperator"; +import ReopenTabOperator from "../../../../src/background/operators/impls/ReopenTabOperator"; +import CloseTabOperator from "../../../../src/background/operators/impls/CloseTabOperator"; +import CloseTabRightOperator from "../../../../src/background/operators/impls/CloseTabRightOperator"; +import * as operations from "../../../../src/shared/operations"; + +describe("TabOperatorFactoryChain", () => { + describe("#create", () => { + it("returns a operator for the operation", async () => { + const tabPresenter = new MockTabPresenter(); + const sut = new TabOperatorFactoryChain(tabPresenter); + + expect(sut.create({ type: operations.TAB_CLOSE })).to.be.instanceOf( + CloseTabOperator + ); + expect(sut.create({ type: operations.TAB_CLOSE_RIGHT })).to.be.instanceOf( + CloseTabRightOperator + ); + expect(sut.create({ type: operations.TAB_CLOSE_FORCE })).to.be.instanceOf( + CloseTabOperator + ); + expect(sut.create({ type: operations.TAB_REOPEN })).to.be.instanceOf( + ReopenTabOperator + ); + expect(sut.create({ type: operations.TAB_PREV })).to.be.instanceOf( + SelectTabPrevOperator + ); + expect(sut.create({ type: operations.TAB_NEXT })).to.be.instanceOf( + SelectTabNextOperator + ); + expect(sut.create({ type: operations.TAB_FIRST })).to.be.instanceOf( + SelectFirstTabOperator + ); + expect( + sut.create({ type: operations.TAB_LAST, newTab: false }) + ).to.be.instanceOf(SelectLastTabOperator); + expect( + sut.create({ type: operations.TAB_PREV_SEL, newTab: false }) + ).to.be.instanceOf(SelectPreviousSelectedTabOperator); + expect( + sut.create({ type: operations.TAB_RELOAD, cache: false }) + ).to.be.instanceOf(ReloadTabOperator); + expect(sut.create({ type: operations.TAB_PIN })).to.be.instanceOf( + PinTabOperator + ); + expect(sut.create({ type: operations.TAB_UNPIN })).to.be.instanceOf( + UnpinTabOperator + ); + expect( + sut.create({ type: operations.TAB_TOGGLE_PINNED }) + ).to.be.instanceOf(TogglePinnedTabOperator); + expect(sut.create({ type: operations.TAB_DUPLICATE })).to.be.instanceOf( + DuplicateTabOperator + ); + expect(sut.create({ type: operations.CANCEL })).to.be.null; + }); + }); +}); diff --git a/test/background/operators/impls/TogglePinnedTabOperator.test.ts b/test/background/operators/impls/TogglePinnedTabOperator.test.ts new file mode 100644 index 0000000..f155f83 --- /dev/null +++ b/test/background/operators/impls/TogglePinnedTabOperator.test.ts @@ -0,0 +1,32 @@ +import { expect } from "chai"; +import TogglePinnedTabOperator from "../../../../src/background/operators/impls/TogglePinnedTabOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; + +describe("TogglePinnedTabOperator", () => { + describe("#run", () => { + it("toggle pinned to the current tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/", { + active: true, + pinned: false, + }); + await tabPresenter.create("https://example.com/", { + active: false, + pinned: false, + }); + const sut = new TogglePinnedTabOperator(tabPresenter); + + await sut.run(); + expect((await tabPresenter.getAll()).map((t) => t.pinned)).to.deep.equal([ + true, + false, + ]); + + await sut.run(); + expect((await tabPresenter.getAll()).map((t) => t.pinned)).to.deep.equal([ + false, + false, + ]); + }); + }); +}); diff --git a/test/background/operators/impls/UnpinTabOperator.test.ts b/test/background/operators/impls/UnpinTabOperator.test.ts new file mode 100644 index 0000000..745f48c --- /dev/null +++ b/test/background/operators/impls/UnpinTabOperator.test.ts @@ -0,0 +1,25 @@ +import { expect } from "chai"; +import UnpinTabOperator from "../../../../src/background/operators/impls/UnpinTabOperator"; +import MockTabPresenter from "../../mock/MockTabPresenter"; + +describe("UnpinTabOperator", () => { + describe("#run", () => { + it("make unpinned to the current tab", async () => { + const tabPresenter = new MockTabPresenter(); + await tabPresenter.create("https://example.com/", { + active: true, + pinned: true, + }); + await tabPresenter.create("https://example.com/", { + active: false, + pinned: true, + }); + const sut = new UnpinTabOperator(tabPresenter); + + await sut.run(); + + const pins = (await tabPresenter.getAll()).map((t) => t.pinned); + expect(pins).to.deep.equal([false, true]); + }); + }); +}); diff --git a/test/background/operators/impls/ZoomInOperator.test.ts b/test/background/operators/impls/ZoomInOperator.test.ts new file mode 100644 index 0000000..097e760 --- /dev/null +++ b/test/background/operators/impls/ZoomInOperator.test.ts @@ -0,0 +1,17 @@ +import sinon from "sinon"; +import ZoomInOperator from "../../../../src/background/operators/impls/ZoomInOperator"; +import MockZoomPresenter from "../../mock/MockZoomPresenter"; + +describe("ZoomInOperator", () => { + describe("#run", () => { + it("zoom-out the current tab", async () => { + const zoomPresenter = new MockZoomPresenter(); + const mock = sinon.mock(zoomPresenter).expects("zoomIn").once(); + + const sut = new ZoomInOperator(zoomPresenter); + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/operators/impls/ZoomOperatorFactoryChain.test.ts b/test/background/operators/impls/ZoomOperatorFactoryChain.test.ts new file mode 100644 index 0000000..10c1cee --- /dev/null +++ b/test/background/operators/impls/ZoomOperatorFactoryChain.test.ts @@ -0,0 +1,28 @@ +import "reflect-metadata"; +import { expect } from "chai"; +import ZoomOperatorFactoryChain from "../../../../src/background/operators/impls/ZoomOperatorFactoryChain"; +import MockZoomPresenter from "../../mock/MockZoomPresenter"; +import ZoomInOperator from "../../../../src/background/operators/impls/ZoomInOperator"; +import ZoomOutOperator from "../../../../src/background/operators/impls/ZoomOutOperator"; +import ResetZoomOperator from "../../../../src/background/operators/impls/ResetZoomOperator"; +import * as operations from "../../../../src/shared/operations"; + +describe("ZoomOperatorFactoryChain", () => { + describe("#create", () => { + it("returns a operator for the operation", async () => { + const zoomPresenter = new MockZoomPresenter(); + const sut = new ZoomOperatorFactoryChain(zoomPresenter); + + expect(sut.create({ type: operations.ZOOM_IN })).to.be.instanceOf( + ZoomInOperator + ); + expect(sut.create({ type: operations.ZOOM_OUT })).to.be.instanceOf( + ZoomOutOperator + ); + expect(sut.create({ type: operations.ZOOM_NEUTRAL })).to.be.instanceOf( + ResetZoomOperator + ); + expect(sut.create({ type: operations.CANCEL })).to.be.null; + }); + }); +}); diff --git a/test/background/operators/impls/ZoomOutOperator.test.ts b/test/background/operators/impls/ZoomOutOperator.test.ts new file mode 100644 index 0000000..e0bbcd9 --- /dev/null +++ b/test/background/operators/impls/ZoomOutOperator.test.ts @@ -0,0 +1,17 @@ +import sinon from "sinon"; +import ZoomOutOperator from "../../../../src/background/operators/impls/ZoomOutOperator"; +import MockZoomPresenter from "../../mock/MockZoomPresenter"; + +describe("ZoomOutOperator", () => { + describe("#run", () => { + it("zoom-in the current tab", async () => { + const zoomPresenter = new MockZoomPresenter(); + const mock = sinon.mock(zoomPresenter).expects("zoomOut").once(); + + const sut = new ZoomOutOperator(zoomPresenter); + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/background/usecases/NavigateUseCase.test.ts b/test/background/usecases/NavigateUseCase.test.ts deleted file mode 100644 index 086d6cd..0000000 --- a/test/background/usecases/NavigateUseCase.test.ts +++ /dev/null @@ -1,183 +0,0 @@ -import "reflect-metadata"; -import TabPresenter from "../../../src/background/presenters/TabPresenter"; -import NavigateUseCase from "../../../src/background/usecases/NavigateUseCase"; -import NavigateClient from "../../../src/background/clients/NavigateClient"; -import * as sinon from "sinon"; - -class MockTabPresenter implements TabPresenter { - create(_url: string, _opts?: unknown): Promise<browser.tabs.Tab> { - throw new Error("not implemented"); - } - - duplicate(_id: number): Promise<browser.tabs.Tab> { - throw new Error("not implemented"); - } - - getAll(): Promise<browser.tabs.Tab[]> { - throw new Error("not implemented"); - } - - getByKeyword( - _keyword: string, - _excludePinned: boolean - ): Promise<browser.tabs.Tab[]> { - throw new Error("not implemented"); - } - - getCurrent(): Promise<browser.tabs.Tab> { - throw new Error("not implemented"); - } - - getLastSelectedId(): Promise<number | undefined> { - throw new Error("not implemented"); - } - - getZoom(_tabId: number): Promise<number> { - throw new Error("not implemented"); - } - - onSelected( - _listener: (arg: { tabId: number; windowId: number }) => void - ): void { - throw new Error("not implemented"); - } - - open(_url: string, _tabId?: number): Promise<browser.tabs.Tab> { - throw new Error("not implemented"); - } - - reload(_tabId: number, _cache: boolean): Promise<void> { - throw new Error("not implemented"); - } - - remove(_ids: number[]): Promise<void> { - throw new Error("not implemented"); - } - - reopen(): Promise<void> { - throw new Error("not implemented"); - } - - select(_tabId: number): Promise<void> { - throw new Error("not implemented"); - } - - setPinned(_tabId: number, _pinned: boolean): Promise<void> { - throw new Error("not implemented"); - } - - setZoom(_tabId: number, _factor: number): Promise<void> { - throw new Error("not implemented"); - } -} - -describe("NavigateUseCase", () => { - let sut: NavigateUseCase; - let tabPresenter: TabPresenter; - let navigateClient: NavigateClient; - - beforeEach(() => { - tabPresenter = new MockTabPresenter(); - navigateClient = new NavigateClient(); - sut = new NavigateUseCase(tabPresenter, navigateClient); - }); - - const newTab = (url: string): browser.tabs.Tab => { - return { - index: 0, - title: "dummy title", - url: url, - active: true, - hidden: false, - highlighted: false, - incognito: false, - isArticle: false, - isInReaderMode: false, - lastAccessed: 1585446733000, - pinned: false, - selected: false, - windowId: 0, - }; - }; - - describe("#openParent()", async () => { - it("opens parent directory of file", async () => { - sinon - .stub(tabPresenter, "getCurrent") - .returns( - Promise.resolve(newTab("https://google.com/fruits/yellow/banana")) - ); - - const mock = sinon - .mock(tabPresenter) - .expects("open") - .withArgs("https://google.com/fruits/yellow/"); - - await sut.openParent(); - - mock.verify(); - }); - - it("opens parent directory of directory", async () => { - sinon - .stub(tabPresenter, "getCurrent") - .returns(Promise.resolve(newTab("https://google.com/fruits/yellow/"))); - - const mock = sinon - .mock(tabPresenter) - .expects("open") - .withArgs("https://google.com/fruits/"); - - await sut.openParent(); - - mock.verify(); - }); - - it("removes hash", async () => { - sinon - .stub(tabPresenter, "getCurrent") - .returns(Promise.resolve(newTab("https://google.com/#top"))); - - const mock = sinon - .mock(tabPresenter) - .expects("open") - .withArgs("https://google.com/"); - - await sut.openParent(); - - mock.verify(); - }); - - it("removes search query", async () => { - sinon - .stub(tabPresenter, "getCurrent") - .returns(Promise.resolve(newTab("https://google.com/search?q=apple"))); - - const mock = sinon - .mock(tabPresenter) - .expects("open") - .withArgs("https://google.com/search"); - - await sut.openParent(); - - mock.verify(); - }); - }); - - describe("#openRoot()", () => { - it("opens root direectory", async () => { - sinon - .stub(tabPresenter, "getCurrent") - .returns(Promise.resolve(newTab("https://google.com/seach?q=apple"))); - - const mock = sinon - .mock(tabPresenter) - .expects("open") - .withArgs("https://google.com"); - - await sut.openRoot(); - - mock.verify(); - }); - }); -}); diff --git a/test/content/mock/MockAddonEnabledRepository.ts b/test/content/mock/MockAddonEnabledRepository.ts new file mode 100644 index 0000000..cbe248b --- /dev/null +++ b/test/content/mock/MockAddonEnabledRepository.ts @@ -0,0 +1,18 @@ +import AddonEnabledRepository from "../../../src/content/repositories/AddonEnabledRepository"; + +export default class MockAddonEnabledRepository + implements AddonEnabledRepository { + public enabled: boolean; + + constructor(initialValue = false) { + this.enabled = initialValue; + } + + get(): boolean { + return this.enabled; + } + + set(on: boolean): void { + this.enabled = on; + } +} diff --git a/test/content/mock/MockAddonIndicatorClient.ts b/test/content/mock/MockAddonIndicatorClient.ts new file mode 100644 index 0000000..6ea9798 --- /dev/null +++ b/test/content/mock/MockAddonIndicatorClient.ts @@ -0,0 +1,13 @@ +import AddonIndicatorClient from "../../../src/content/client/AddonIndicatorClient"; + +export default class MockAddonIndicatorClient implements AddonIndicatorClient { + public enabled: boolean; + + constructor(initialValue = false) { + this.enabled = initialValue; + } + + async setEnabled(enabled: boolean): Promise<void> { + this.enabled = enabled; + } +} diff --git a/test/content/mock/MockClipboardRepository.ts b/test/content/mock/MockClipboardRepository.ts new file mode 100644 index 0000000..c6e53bd --- /dev/null +++ b/test/content/mock/MockClipboardRepository.ts @@ -0,0 +1,16 @@ +import ClipboardRepository from "../../../src/content/repositories/ClipboardRepository"; + +export default class MockClipboardRepository implements ClipboardRepository { + private value: string; + + constructor(initValue = "") { + this.value = initValue; + } + read(): string { + return this.value; + } + + write(text: string): void { + this.value = text; + } +} diff --git a/test/content/mock/MockFindMasterClient.ts b/test/content/mock/MockFindMasterClient.ts new file mode 100644 index 0000000..a035cc5 --- /dev/null +++ b/test/content/mock/MockFindMasterClient.ts @@ -0,0 +1,11 @@ +import FindMasterClient from "../../../src/content/client/FindMasterClient"; + +export default class MockFindMasterClient implements FindMasterClient { + findNext(): void { + throw new Error("not implemented"); + } + + findPrev(): void { + throw new Error("not implemented"); + } +} diff --git a/test/content/mock/MockFocusPresenter.ts b/test/content/mock/MockFocusPresenter.ts new file mode 100644 index 0000000..43454d0 --- /dev/null +++ b/test/content/mock/MockFocusPresenter.ts @@ -0,0 +1,7 @@ +import FocusPresenter from "../../../src/content/presenters/FocusPresenter"; + +export default class MockFocusPresenter implements FocusPresenter { + focusFirstElement(): boolean { + throw new Error("not implemented"); + } +} diff --git a/test/content/mock/MockFollowMasterClient.ts b/test/content/mock/MockFollowMasterClient.ts new file mode 100644 index 0000000..fc660d5 --- /dev/null +++ b/test/content/mock/MockFollowMasterClient.ts @@ -0,0 +1,16 @@ +import FollowMasterClient from "../../../src/content/client/FollowMasterClient"; +import Key from "../../../src/shared/settings/Key"; + +export default class MockFollowMasterClient implements FollowMasterClient { + responseHintCount(_count: number): void { + throw new Error("not implemented"); + } + + sendKey(_key: Key): void { + throw new Error("not implemented"); + } + + startFollow(_newTab: boolean, _background: boolean): void { + throw new Error("not implemented"); + } +} diff --git a/test/content/mock/MockMarkKeyRepository.ts b/test/content/mock/MockMarkKeyRepository.ts new file mode 100644 index 0000000..12bc75c --- /dev/null +++ b/test/content/mock/MockMarkKeyRepository.ts @@ -0,0 +1,43 @@ +import MarkKeyRepository from "../../../src/content/repositories/MarkKeyRepository"; + +export default class MockMarkKeyRepository implements MarkKeyRepository { + public jumpMode: boolean; + public setMode: boolean; + + constructor( + initialValue: { + jumpMode: boolean; + setMode: boolean; + } = { + jumpMode: false, + setMode: false, + } + ) { + this.jumpMode = initialValue.jumpMode; + this.setMode = initialValue.setMode; + } + + disabeJumpMode(): void { + this.jumpMode = false; + } + + disabeSetMode(): void { + this.setMode = false; + } + + enableJumpMode(): void { + this.jumpMode = true; + } + + enableSetMode(): void { + this.setMode = true; + } + + isJumpMode(): boolean { + return this.jumpMode; + } + + isSetMode(): boolean { + return this.setMode; + } +} diff --git a/test/content/mock/MockOperationClient.ts b/test/content/mock/MockOperationClient.ts new file mode 100644 index 0000000..2f50f77 --- /dev/null +++ b/test/content/mock/MockOperationClient.ts @@ -0,0 +1,16 @@ +import OperationClient from "../../../src/content/client/OperationClient"; +import * as operations from "../../../src/shared/operations"; + +export default class MockOperationClient implements OperationClient { + execBackgroundOp(_repeat: number, _op: operations.Operation): Promise<void> { + throw new Error("not implemented"); + } + + internalOpenUrl( + _url: string, + _newTab?: boolean, + _background?: boolean + ): Promise<void> { + throw new Error("not implemented"); + } +} diff --git a/test/content/mock/MockScrollPresenter.ts b/test/content/mock/MockScrollPresenter.ts index c802227..8b4cf2a 100644 --- a/test/content/mock/MockScrollPresenter.ts +++ b/test/content/mock/MockScrollPresenter.ts @@ -3,10 +3,10 @@ import ScrollPresenter, { } from "../../../src/content/presenters/ScrollPresenter"; export default class MockScrollPresenter implements ScrollPresenter { - private pos: Point; + private readonly pos: Point; - constructor() { - this.pos = { x: 0, y: 0 }; + constructor(initX = 0, initY = 0) { + this.pos = { x: initX, y: initY }; } getScroll(): Point { diff --git a/test/content/mock/MockSettingRepository.ts b/test/content/mock/MockSettingRepository.ts new file mode 100644 index 0000000..5242713 --- /dev/null +++ b/test/content/mock/MockSettingRepository.ts @@ -0,0 +1,20 @@ +import SettingRepository from "../../../src/content/repositories/SettingRepository"; +import Settings, { + DefaultSetting, +} from "../../../src/shared/settings/Settings"; + +export default class MockSettingRepository implements SettingRepository { + private value: Settings; + + constructor(initValue: Settings = DefaultSetting) { + this.value = initValue; + } + + get(): Settings { + return this.value; + } + + set(setting: Settings): void { + this.value = setting; + } +} diff --git a/test/content/mock/MockURLRepository.ts b/test/content/mock/MockURLRepository.ts new file mode 100644 index 0000000..731a7fb --- /dev/null +++ b/test/content/mock/MockURLRepository.ts @@ -0,0 +1,9 @@ +import URLRepository from "../../../src/content/operators/impls/URLRepository"; + +export default class MockURLRepository implements URLRepository { + constructor(private url: string = "https://example.com/") {} + + getCurrentURL(): string { + return this.url; + } +} diff --git a/test/content/operators/impls/AddonOperatorFactoryChain.test.ts b/test/content/operators/impls/AddonOperatorFactoryChain.test.ts new file mode 100644 index 0000000..c064bb9 --- /dev/null +++ b/test/content/operators/impls/AddonOperatorFactoryChain.test.ts @@ -0,0 +1,29 @@ +import AddonOperatorFactoryChain from "../../../../src/content/operators/impls/AddonOperatorFactoryChain"; +import EnableAddonOperator from "../../../../src/content/operators/impls/EnableAddonOperator"; +import DisableAddonOperator from "../../../../src/content/operators/impls/DisableAddonOperator"; +import ToggleAddonOperator from "../../../../src/content/operators/impls/ToggleAddonOperator"; +import * as operations from "../../../../src/shared/operations"; +import { expect } from "chai"; +import MockAddonIndicatorClient from "../../mock/MockAddonIndicatorClient"; +import MockAddonEnabledRepository from "../../mock/MockAddonEnabledRepository"; + +describe("AddonOperatorFactoryChain", () => { + describe("#create", () => { + it("returns an operator", () => { + const sut = new AddonOperatorFactoryChain( + new MockAddonIndicatorClient(), + new MockAddonEnabledRepository() + ); + expect(sut.create({ type: operations.ADDON_ENABLE }, 0)).to.be.instanceOf( + EnableAddonOperator + ); + expect( + sut.create({ type: operations.ADDON_DISABLE }, 0) + ).to.be.instanceOf(DisableAddonOperator); + expect( + sut.create({ type: operations.ADDON_TOGGLE_ENABLED }, 0) + ).to.be.instanceOf(ToggleAddonOperator); + expect(sut.create({ type: operations.SCROLL_TOP }, 0)).to.be.null; + }); + }); +}); diff --git a/test/content/operators/impls/BackgroundOperationOperator.test.ts b/test/content/operators/impls/BackgroundOperationOperator.test.ts new file mode 100644 index 0000000..b8b1fbd --- /dev/null +++ b/test/content/operators/impls/BackgroundOperationOperator.test.ts @@ -0,0 +1,38 @@ +import * as operations from "../../../../src/shared/operations"; +import BackgroundOperationOperator from "../../../../src/content/operators/impls/BackgroundOperationOperator"; +import OperationClient from "../../../../src/content/client/OperationClient"; +import { expect } from "chai"; + +class MockOperationClient implements OperationClient { + public readonly executedOps: { + op: operations.Operation; + repeat: number; + }[] = []; + async execBackgroundOp( + repeat: number, + op: operations.Operation + ): Promise<void> { + this.executedOps.push({ repeat, op }); + } + + internalOpenUrl(): Promise<void> { + throw new Error("not implemented"); + } +} + +describe("BackgroundOperationOperator", () => { + describe("#run", () => { + it("returns an operator", async () => { + const client = new MockOperationClient(); + const sut = new BackgroundOperationOperator(client, 2, { + type: operations.TAB_CLOSE, + }); + + await sut.run(); + + expect(client.executedOps).to.deep.equal([ + { op: { type: operations.TAB_CLOSE }, repeat: 2 }, + ]); + }); + }); +}); diff --git a/test/content/operators/impls/ClipboardOperatorFactoryChain.test.ts b/test/content/operators/impls/ClipboardOperatorFactoryChain.test.ts new file mode 100644 index 0000000..9ddc229 --- /dev/null +++ b/test/content/operators/impls/ClipboardOperatorFactoryChain.test.ts @@ -0,0 +1,31 @@ +import * as operations from "../../../../src/shared/operations"; +import { expect } from "chai"; +import ClipboardOperatorFactoryChain from "../../../../src/content/operators/impls/ClipboardOperatorFactoryChain"; +import YankURLOperator from "../../../../src/content/operators/impls/YankURLOperator"; +import PasteOperator from "../../../../src/content/operators/impls/PasteOperator"; +import MockClipboardRepository from "../../mock/MockClipboardRepository"; +import MockOperationClient from "../../mock/MockOperationClient"; +import MockSettingRepository from "../../mock/MockSettingRepository"; +import MockConsoleClient from "../../mock/MockConsoleClient"; +import MockURLRepository from "../../mock/MockURLRepository"; + +describe("ClipboardOperatorFactoryChain", () => { + describe("#create", () => { + it("returns an operator", () => { + const sut = new ClipboardOperatorFactoryChain( + new MockClipboardRepository(), + new MockConsoleClient(), + new MockOperationClient(), + new MockSettingRepository(), + new MockURLRepository() + ); + expect(sut.create({ type: operations.URLS_YANK }, 0)).to.be.instanceOf( + YankURLOperator + ); + expect( + sut.create({ type: operations.URLS_PASTE, newTab: false }, 0) + ).to.be.instanceOf(PasteOperator); + expect(sut.create({ type: operations.SCROLL_TOP }, 0)).to.be.null; + }); + }); +}); diff --git a/test/content/operators/impls/DisableAddonOperator.test.ts b/test/content/operators/impls/DisableAddonOperator.test.ts new file mode 100644 index 0000000..358ae44 --- /dev/null +++ b/test/content/operators/impls/DisableAddonOperator.test.ts @@ -0,0 +1,19 @@ +import { expect } from "chai"; +import DisableAddonOperator from "../../../../src/content/operators/impls/DisableAddonOperator"; +import MockAddonIndicatorClient from "../../mock/MockAddonIndicatorClient"; +import MockAddonEnabledRepository from "../../mock/MockAddonEnabledRepository"; + +describe("DisableAddonOperator", () => { + describe("#run", () => { + it("disables addon", async () => { + const client = new MockAddonIndicatorClient(true); + const repository = new MockAddonEnabledRepository(true); + const sut = new DisableAddonOperator(client, repository); + + await sut.run(); + + expect(client.enabled).to.be.false; + expect(repository.enabled).to.be.false; + }); + }); +}); diff --git a/test/content/operators/impls/EnableAddonOperator.test.ts b/test/content/operators/impls/EnableAddonOperator.test.ts new file mode 100644 index 0000000..a6ca31b --- /dev/null +++ b/test/content/operators/impls/EnableAddonOperator.test.ts @@ -0,0 +1,19 @@ +import { expect } from "chai"; +import EnableAddonOperator from "../../../../src/content/operators/impls/EnableAddonOperator"; +import MockAddonIndicatorClient from "../../mock/MockAddonIndicatorClient"; +import MockAddonEnabledRepository from "../../mock/MockAddonEnabledRepository"; + +describe("EnableAddonOperator", () => { + describe("#run", () => { + it("enabled addon", async () => { + const client = new MockAddonIndicatorClient(false); + const repository = new MockAddonEnabledRepository(false); + const sut = new EnableAddonOperator(client, repository); + + await sut.run(); + + expect(client.enabled).to.be.true; + expect(repository.enabled).to.be.true; + }); + }); +}); diff --git a/test/content/operators/impls/EnableJumpMarkOperator.test.ts b/test/content/operators/impls/EnableJumpMarkOperator.test.ts new file mode 100644 index 0000000..66b4ecd --- /dev/null +++ b/test/content/operators/impls/EnableJumpMarkOperator.test.ts @@ -0,0 +1,19 @@ +import { expect } from "chai"; +import EnableJumpMarkOperator from "../../../../src/content/operators/impls/EnableJumpMarkOperator"; +import MockMarkKeyRepository from "../../mock/MockMarkKeyRepository"; + +describe("EnableJumpMarkOperator", () => { + describe("#run", () => { + it("starts mark jump mode", async () => { + const repository = new MockMarkKeyRepository({ + jumpMode: false, + setMode: false, + }); + const sut = new EnableJumpMarkOperator(repository); + + await sut.run(); + + expect(repository.jumpMode).to.be.true; + }); + }); +}); diff --git a/test/content/operators/impls/EnableSetMarkOperator.test.ts b/test/content/operators/impls/EnableSetMarkOperator.test.ts new file mode 100644 index 0000000..b28874d --- /dev/null +++ b/test/content/operators/impls/EnableSetMarkOperator.test.ts @@ -0,0 +1,19 @@ +import { expect } from "chai"; +import EnableSetMarkOperator from "../../../../src/content/operators/impls/EnableSetMarkOperator"; +import MockMarkKeyRepository from "../../mock/MockMarkKeyRepository"; + +describe("EnableSetMarkOperator", () => { + describe("#run", () => { + it("starts mark set mode", async () => { + const repository = new MockMarkKeyRepository({ + jumpMode: false, + setMode: false, + }); + const sut = new EnableSetMarkOperator(repository); + + await sut.run(); + + expect(repository.setMode).to.be.true; + }); + }); +}); diff --git a/test/content/operators/impls/FindNextOperator.test.ts b/test/content/operators/impls/FindNextOperator.test.ts new file mode 100644 index 0000000..d93d45e --- /dev/null +++ b/test/content/operators/impls/FindNextOperator.test.ts @@ -0,0 +1,17 @@ +import sinon from "sinon"; +import FindNextOperator from "../../../../src/content/operators/impls/FindNextOperator"; +import MockFindMasterClient from "../../mock/MockFindMasterClient"; + +describe("FindNextOperator", () => { + describe("#run", () => { + it("find next keyword", async () => { + const client = new MockFindMasterClient(); + const mock = sinon.mock(client).expects("findNext").exactly(3); + const sut = new FindNextOperator(client, 3); + + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/content/operators/impls/FindOperatorFactoryChain.test.ts b/test/content/operators/impls/FindOperatorFactoryChain.test.ts new file mode 100644 index 0000000..6c599ae --- /dev/null +++ b/test/content/operators/impls/FindOperatorFactoryChain.test.ts @@ -0,0 +1,21 @@ +import * as operations from "../../../../src/shared/operations"; +import { expect } from "chai"; +import FindOperatorFactoryChain from "../../../../src/content/operators/impls/FindOperatorFactoryChain"; +import MockFindMasterClient from "../../mock/MockFindMasterClient"; +import FindNextOperator from "../../../../src/content/operators/impls/FindNextOperator"; +import FindPrevOperator from "../../../../src/content/operators/impls/FindPrevOperator"; + +describe("FindOperatorFactoryChain", () => { + describe("#create", () => { + it("returns an operator", () => { + const sut = new FindOperatorFactoryChain(new MockFindMasterClient()); + expect(sut.create({ type: operations.FIND_NEXT }, 0)).to.be.instanceOf( + FindNextOperator + ); + expect(sut.create({ type: operations.FIND_PREV }, 0)).to.be.instanceOf( + FindPrevOperator + ); + expect(sut.create({ type: operations.SCROLL_TOP }, 0)).to.be.null; + }); + }); +}); diff --git a/test/content/operators/impls/FindPrevOperator.test.ts b/test/content/operators/impls/FindPrevOperator.test.ts new file mode 100644 index 0000000..1ebde8d --- /dev/null +++ b/test/content/operators/impls/FindPrevOperator.test.ts @@ -0,0 +1,17 @@ +import sinon from "sinon"; +import FindPrevOperator from "../../../../src/content/operators/impls/FindPrevOperator"; +import MockFindMasterClient from "../../mock/MockFindMasterClient"; + +describe("FindPrevOperator", () => { + describe("#run", () => { + it("find previous keyword", async () => { + const client = new MockFindMasterClient(); + const mock = sinon.mock(client).expects("findPrev").exactly(3); + const sut = new FindPrevOperator(client, 3); + + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/content/operators/impls/FocusOperator.test.ts b/test/content/operators/impls/FocusOperator.test.ts new file mode 100644 index 0000000..a0eb53b --- /dev/null +++ b/test/content/operators/impls/FocusOperator.test.ts @@ -0,0 +1,17 @@ +import sinon from "sinon"; +import FocusOperator from "../../../../src/content/operators/impls/FocusOperator"; +import MockFocusPresenter from "../../mock/MockFocusPresenter"; + +describe("FocusOperator", () => { + describe("#run", () => { + it("focus a first input", async () => { + const presenter = new MockFocusPresenter(); + const mock = sinon.mock(presenter).expects("focusFirstElement"); + const sut = new FocusOperator(presenter); + + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/content/operators/impls/FocusOperatorFactoryChain.test.ts b/test/content/operators/impls/FocusOperatorFactoryChain.test.ts new file mode 100644 index 0000000..91f734b --- /dev/null +++ b/test/content/operators/impls/FocusOperatorFactoryChain.test.ts @@ -0,0 +1,17 @@ +import * as operations from "../../../../src/shared/operations"; +import { expect } from "chai"; +import FocusOperatorFactoryChain from "../../../../src/content/operators/impls/FocusOperatorFactoryChain"; +import FocusOperator from "../../../../src/content/operators/impls/FocusOperator"; +import MockFocusPresenter from "../../mock/MockFocusPresenter"; + +describe("FocusOperatorFactoryChain", () => { + describe("#create", () => { + it("returns an operator", () => { + const sut = new FocusOperatorFactoryChain(new MockFocusPresenter()); + expect(sut.create({ type: operations.FOCUS_INPUT }, 0)).to.be.instanceOf( + FocusOperator + ); + expect(sut.create({ type: operations.SCROLL_TOP }, 0)).to.be.null; + }); + }); +}); diff --git a/test/content/operators/impls/FollowOperatorFactoryChain.test.ts b/test/content/operators/impls/FollowOperatorFactoryChain.test.ts new file mode 100644 index 0000000..526a93c --- /dev/null +++ b/test/content/operators/impls/FollowOperatorFactoryChain.test.ts @@ -0,0 +1,20 @@ +import * as operations from "../../../../src/shared/operations"; +import { expect } from "chai"; +import FocusOperatorFactoryChain from "../../../../src/content/operators/impls/FocusOperatorFactoryChain"; +import FocusOperator from "../../../../src/content/operators/impls/FocusOperator"; +import MockFocusPresenter from "../../mock/MockFocusPresenter"; + +describe("FocusOperatorFactoryChain", () => { + describe("#create", () => { + it("returns an operator", () => { + const sut = new FocusOperatorFactoryChain(new MockFocusPresenter()); + expect( + sut.create( + { type: operations.FOCUS_INPUT, newTab: false, background: false }, + 0 + ) + ).to.be.instanceOf(FocusOperator); + expect(sut.create({ type: operations.SCROLL_TOP }, 0)).to.be.null; + }); + }); +}); diff --git a/test/content/operators/impls/HorizontalScrollOperator.test.ts b/test/content/operators/impls/HorizontalScrollOperator.test.ts new file mode 100644 index 0000000..f77a34e --- /dev/null +++ b/test/content/operators/impls/HorizontalScrollOperator.test.ts @@ -0,0 +1,28 @@ +import { expect } from "chai"; +import HorizontalScrollOperator from "../../../../src/content/operators/impls/HorizontalScrollOperator"; +import MockScrollPresenter from "../../mock/MockScrollPresenter"; +import MockSettingRepository from "../../mock/MockSettingRepository"; + +describe("HorizontalScrollOperator", () => { + describe("#run", () => { + it("scroll horizontally", async () => { + const presenter = new MockScrollPresenter(); + const settingRepository = new MockSettingRepository(); + const sut = new HorizontalScrollOperator(presenter, settingRepository, 1); + + await sut.run(); + + expect(presenter.getScroll()).to.deep.equal({ x: 1, y: 0 }); + }); + + it("scroll horizontally with repeats", async () => { + const presenter = new MockScrollPresenter(); + const settingRepository = new MockSettingRepository(); + const sut = new HorizontalScrollOperator(presenter, settingRepository, 5); + + await sut.run(); + + expect(presenter.getScroll()).to.deep.equal({ x: 5, y: 0 }); + }); + }); +}); diff --git a/test/content/operators/impls/MarkOperatorFactoryChain.test.ts b/test/content/operators/impls/MarkOperatorFactoryChain.test.ts new file mode 100644 index 0000000..1f094dd --- /dev/null +++ b/test/content/operators/impls/MarkOperatorFactoryChain.test.ts @@ -0,0 +1,21 @@ +import * as operations from "../../../../src/shared/operations"; +import { expect } from "chai"; +import MarkOperatorFactoryChain from "../../../../src/content/operators/impls/MarkOperatorFactoryChain"; +import MockMarkKeyRepository from "../../mock/MockMarkKeyRepository"; +import EnableSetMarkOperator from "../../../../src/content/operators/impls/EnableSetMarkOperator"; +import EnableJumpMarkOperator from "../../../../src/content/operators/impls/EnableJumpMarkOperator"; + +describe("MarkOperatorFactoryChain", () => { + describe("#create", () => { + it("returns an operator", () => { + const sut = new MarkOperatorFactoryChain(new MockMarkKeyRepository()); + expect( + sut.create({ type: operations.MARK_SET_PREFIX }, 0) + ).to.be.instanceOf(EnableSetMarkOperator); + expect( + sut.create({ type: operations.MARK_JUMP_PREFIX }, 0) + ).to.be.instanceOf(EnableJumpMarkOperator); + expect(sut.create({ type: operations.SCROLL_TOP }, 0)).to.be.null; + }); + }); +}); diff --git a/test/content/operators/impls/PageScrollOperator.test.ts b/test/content/operators/impls/PageScrollOperator.test.ts new file mode 100644 index 0000000..80c9185 --- /dev/null +++ b/test/content/operators/impls/PageScrollOperator.test.ts @@ -0,0 +1,28 @@ +import { expect } from "chai"; +import PageScrollOperator from "../../../../src/content/operators/impls/PageScrollOperator"; +import MockScrollPresenter from "../../mock/MockScrollPresenter"; +import MockSettingRepository from "../../mock/MockSettingRepository"; + +describe("PageScrollOperator", () => { + describe("#run", () => { + it("scroll by a page", async () => { + const presenter = new MockScrollPresenter(); + const settingRepository = new MockSettingRepository(); + const sut = new PageScrollOperator(presenter, settingRepository, 1); + + await sut.run(); + + expect(presenter.getScroll()).to.deep.equal({ x: 1, y: 0 }); + }); + + it("scroll by a page with repeats", async () => { + const presenter = new MockScrollPresenter(); + const settingRepository = new MockSettingRepository(); + const sut = new PageScrollOperator(presenter, settingRepository, 5); + + await sut.run(); + + expect(presenter.getScroll()).to.deep.equal({ x: 5, y: 0 }); + }); + }); +}); diff --git a/test/content/operators/impls/PasteOperator.test.ts b/test/content/operators/impls/PasteOperator.test.ts new file mode 100644 index 0000000..8a3a374 --- /dev/null +++ b/test/content/operators/impls/PasteOperator.test.ts @@ -0,0 +1,51 @@ +import sinon from "sinon"; +import PasteOperator from "../../../../src/content/operators/impls/PasteOperator"; +import MockClipboardRepository from "../../mock/MockClipboardRepository"; +import MockSettingRepository from "../../mock/MockSettingRepository"; +import MockOperationClient from "../../mock/MockOperationClient"; + +describe("PasteOperator", () => { + describe("#run", () => { + it("open a search url", async () => { + const clipboardRepository = new MockClipboardRepository("apple"); + const settingRepository = new MockSettingRepository(); + const operationClient = new MockOperationClient(); + const mockOperationClient = sinon + .mock(operationClient) + .expects("internalOpenUrl") + .withArgs("https://google.com/search?q=apple"); + const sut = new PasteOperator( + clipboardRepository, + settingRepository, + operationClient, + false + ); + + await sut.run(); + + mockOperationClient.verify(); + }); + + it("open a url", async () => { + const clipboardRepository = new MockClipboardRepository( + "https://example.com/" + ); + const settingRepository = new MockSettingRepository(); + const operationClient = new MockOperationClient(); + const mockOperationClient = sinon + .mock(operationClient) + .expects("internalOpenUrl") + .withArgs("https://example.com/"); + const sut = new PasteOperator( + clipboardRepository, + settingRepository, + operationClient, + false + ); + + await sut.run(); + + mockOperationClient.verify(); + }); + }); +}); diff --git a/test/content/operators/impls/ScrollOperatorFactoryChain.test.ts b/test/content/operators/impls/ScrollOperatorFactoryChain.test.ts new file mode 100644 index 0000000..08034cb --- /dev/null +++ b/test/content/operators/impls/ScrollOperatorFactoryChain.test.ts @@ -0,0 +1,46 @@ +import { expect } from "chai"; +import ScrollOperatorFactoryChain from "../../../../src/content/operators/impls/ScrollOperatorFactoryChain"; +import MockScrollPresenter from "../../mock/MockScrollPresenter"; +import MockSettingRepository from "../../mock/MockSettingRepository"; +import HorizontalScrollOperator from "../../../../src/content/operators/impls/HorizontalScrollOperator"; +import VerticalScrollOperator from "../../../../src/content/operators/impls/VerticalScrollOperator"; +import PageScrollOperator from "../../../../src/content/operators/impls/PageScrollOperator"; +import ScrollToTopOperator from "../../../../src/content/operators/impls/ScrollToTopOperator"; +import ScrollToBottomOperator from "../../../../src/content/operators/impls/ScrollToBottomOperator"; +import ScrollToHomeOperator from "../../../../src/content/operators/impls/ScrollToHomeOperator"; +import ScrollToEndOperator from "../../../../src/content/operators/impls/ScrollToEndOperator"; +import * as operations from "../../../../src/shared/operations"; + +describe("ScrollOperatorFactoryChain", () => { + describe("#create", () => { + it("returns an operator", () => { + const sut = new ScrollOperatorFactoryChain( + new MockScrollPresenter(), + new MockSettingRepository() + ); + expect( + sut.create({ type: operations.SCROLL_HORIZONALLY, count: 10 }, 0) + ).to.be.instanceOf(HorizontalScrollOperator); + expect( + sut.create({ type: operations.SCROLL_VERTICALLY, count: 10 }, 0) + ).to.be.instanceOf(VerticalScrollOperator); + expect( + sut.create({ type: operations.SCROLL_PAGES, count: 10 }, 0) + ).to.be.instanceOf(PageScrollOperator); + expect(sut.create({ type: operations.SCROLL_TOP }, 0)).to.be.instanceOf( + ScrollToTopOperator + ); + expect( + sut.create({ type: operations.SCROLL_BOTTOM }, 0) + ).to.be.instanceOf(ScrollToBottomOperator); + expect(sut.create({ type: operations.SCROLL_HOME }, 0)).to.be.instanceOf( + ScrollToHomeOperator + ); + expect(sut.create({ type: operations.SCROLL_END }, 0)).to.be.instanceOf( + ScrollToEndOperator + ); + expect(sut.create({ type: operations.PAGE_HOME, newTab: false }, 0)).to.be + .null; + }); + }); +}); diff --git a/test/content/operators/impls/ScrollToBottomOperator.test.ts b/test/content/operators/impls/ScrollToBottomOperator.test.ts new file mode 100644 index 0000000..500c8f2 --- /dev/null +++ b/test/content/operators/impls/ScrollToBottomOperator.test.ts @@ -0,0 +1,18 @@ +import { expect } from "chai"; +import ScrollToBottomOperator from "../../../../src/content/operators/impls/ScrollToBottomOperator"; +import MockScrollPresenter from "../../mock/MockScrollPresenter"; +import MockSettingRepository from "../../mock/MockSettingRepository"; + +describe("ScrollToBottomOperator", () => { + describe("#run", () => { + it("scroll to bottom", async () => { + const presenter = new MockScrollPresenter(); + const settingRepository = new MockSettingRepository(); + const sut = new ScrollToBottomOperator(presenter, settingRepository); + + await sut.run(); + + expect(presenter.getScroll()).to.deep.equal({ x: 0, y: Infinity }); + }); + }); +}); diff --git a/test/content/operators/impls/ScrollToEndOperator.test.ts b/test/content/operators/impls/ScrollToEndOperator.test.ts new file mode 100644 index 0000000..0c98c8d --- /dev/null +++ b/test/content/operators/impls/ScrollToEndOperator.test.ts @@ -0,0 +1,18 @@ +import { expect } from "chai"; +import ScrollToEndOperator from "../../../../src/content/operators/impls/ScrollToEndOperator"; +import MockScrollPresenter from "../../mock/MockScrollPresenter"; +import MockSettingRepository from "../../mock/MockSettingRepository"; + +describe("ScrollToEndOperator", () => { + describe("#run", () => { + it("scroll to rightmost", async () => { + const presenter = new MockScrollPresenter(); + const settingRepository = new MockSettingRepository(); + const sut = new ScrollToEndOperator(presenter, settingRepository); + + await sut.run(); + + expect(presenter.getScroll()).to.deep.equal({ x: Infinity, y: 0 }); + }); + }); +}); diff --git a/test/content/operators/impls/ScrollToHomeOperator.test.ts b/test/content/operators/impls/ScrollToHomeOperator.test.ts new file mode 100644 index 0000000..f8614d2 --- /dev/null +++ b/test/content/operators/impls/ScrollToHomeOperator.test.ts @@ -0,0 +1,18 @@ +import { expect } from "chai"; +import ScrollToHomeOperator from "../../../../src/content/operators/impls/ScrollToHomeOperator"; +import MockScrollPresenter from "../../mock/MockScrollPresenter"; +import MockSettingRepository from "../../mock/MockSettingRepository"; + +describe("ScrollToHomeOperator", () => { + describe("#run", () => { + it("scroll to leftmost", async () => { + const presenter = new MockScrollPresenter(10, 10); + const settingRepository = new MockSettingRepository(); + const sut = new ScrollToHomeOperator(presenter, settingRepository); + + await sut.run(); + + expect(presenter.getScroll()).to.deep.equal({ x: 0, y: 10 }); + }); + }); +}); diff --git a/test/content/operators/impls/ScrollToTopOperator.test.ts b/test/content/operators/impls/ScrollToTopOperator.test.ts new file mode 100644 index 0000000..25a84ba --- /dev/null +++ b/test/content/operators/impls/ScrollToTopOperator.test.ts @@ -0,0 +1,18 @@ +import { expect } from "chai"; +import ScrollToTopOperator from "../../../../src/content/operators/impls/ScrollToTopOperator"; +import MockScrollPresenter from "../../mock/MockScrollPresenter"; +import MockSettingRepository from "../../mock/MockSettingRepository"; + +describe("ScrollToTopOperator", () => { + describe("#run", () => { + it("scroll to top", async () => { + const presenter = new MockScrollPresenter(10, 10); + const settingRepository = new MockSettingRepository(); + const sut = new ScrollToTopOperator(presenter, settingRepository); + + await sut.run(); + + expect(presenter.getScroll()).to.deep.equal({ x: 10, y: 0 }); + }); + }); +}); diff --git a/test/content/operators/impls/StartFollowOperator.test.ts b/test/content/operators/impls/StartFollowOperator.test.ts new file mode 100644 index 0000000..8f9bd2d --- /dev/null +++ b/test/content/operators/impls/StartFollowOperator.test.ts @@ -0,0 +1,20 @@ +import sinon from "sinon"; +import StartFollowOperator from "../../../../src/content/operators/impls/StartFollowOperator"; +import MockFollowMasterClient from "../../mock/MockFollowMasterClient"; + +describe("StartFollowOperator", () => { + describe("#run", () => { + it("starts following links", async () => { + const client = new MockFollowMasterClient(); + const mock = sinon + .mock(client) + .expects("startFollow") + .withArgs(true, false); + const sut = new StartFollowOperator(client, true, false); + + await sut.run(); + + mock.verify(); + }); + }); +}); diff --git a/test/content/operators/impls/ToggleAddonOperator.test.ts b/test/content/operators/impls/ToggleAddonOperator.test.ts new file mode 100644 index 0000000..6026eb1 --- /dev/null +++ b/test/content/operators/impls/ToggleAddonOperator.test.ts @@ -0,0 +1,24 @@ +import { expect } from "chai"; +import ToggleAddonOperator from "../../../../src/content/operators/impls/ToggleAddonOperator"; +import MockAddonIndicatorClient from "../../mock/MockAddonIndicatorClient"; +import MockAddonEnabledRepository from "../../mock/MockAddonEnabledRepository"; + +describe("ToggleAddonOperator", () => { + describe("#run", () => { + it("toggles addon-enabled state", async () => { + const client = new MockAddonIndicatorClient(true); + const repository = new MockAddonEnabledRepository(true); + const sut = new ToggleAddonOperator(client, repository); + + await sut.run(); + + expect(client.enabled).to.be.false; + expect(repository.enabled).to.be.false; + + await sut.run(); + + expect(client.enabled).to.be.true; + expect(repository.enabled).to.be.true; + }); + }); +}); diff --git a/test/content/operators/impls/VerticalScrollOperator.test.ts b/test/content/operators/impls/VerticalScrollOperator.test.ts new file mode 100644 index 0000000..05b15d2 --- /dev/null +++ b/test/content/operators/impls/VerticalScrollOperator.test.ts @@ -0,0 +1,28 @@ +import { expect } from "chai"; +import VerticalScrollOperator from "../../../../src/content/operators/impls/VerticalScrollOperator"; +import MockScrollPresenter from "../../mock/MockScrollPresenter"; +import MockSettingRepository from "../../mock/MockSettingRepository"; + +describe("VerticalScrollOperator", () => { + describe("#run", () => { + it("scroll vertically", async () => { + const presenter = new MockScrollPresenter(); + const settingRepository = new MockSettingRepository(); + const sut = new VerticalScrollOperator(presenter, settingRepository, 1); + + await sut.run(); + + expect(presenter.getScroll()).to.deep.equal({ x: 0, y: 1 }); + }); + + it("scroll vertically with repeats", async () => { + const presenter = new MockScrollPresenter(); + const settingRepository = new MockSettingRepository(); + const sut = new VerticalScrollOperator(presenter, settingRepository, 5); + + await sut.run(); + + expect(presenter.getScroll()).to.deep.equal({ x: 0, y: 5 }); + }); + }); +}); diff --git a/test/content/operators/impls/YankURLOperator.test.ts b/test/content/operators/impls/YankURLOperator.test.ts new file mode 100644 index 0000000..46e3d06 --- /dev/null +++ b/test/content/operators/impls/YankURLOperator.test.ts @@ -0,0 +1,26 @@ +import { expect } from "chai"; +import MockClipboardRepository from "../../mock/MockClipboardRepository"; +import YankURLOperator from "../../../../src/content/operators/impls/YankURLOperator"; +import MockURLRepository from "../../mock/MockURLRepository"; +import MockConsoleClient from "../../mock/MockConsoleClient"; + +describe("YankOperation", () => { + describe("#run", () => { + it("copy current URL", async () => { + const clipboardRepository = new MockClipboardRepository(); + const consoleClient = new MockConsoleClient(); + const urlRepository = new MockURLRepository("https://example.com/"); + const sut = new YankURLOperator( + clipboardRepository, + consoleClient, + urlRepository + ); + + await sut.run(); + + expect(clipboardRepository.read()).to.equal("https://example.com/"); + expect(consoleClient.text).to.equal("Yanked https://example.com/"); + expect(consoleClient.isError).to.be.false; + }); + }); +}); diff --git a/test/content/presenters/NavigationPresenter.test.ts b/test/content/presenters/NavigationPresenter.test.ts index af3b487..8d003c4 100644 --- a/test/content/presenters/NavigationPresenter.test.ts +++ b/test/content/presenters/NavigationPresenter.test.ts @@ -1,7 +1,9 @@ import { NavigationPresenterImpl } from "../../../src/content/presenters/NavigationPresenter"; import { expect } from "chai"; -describe("NavigationPresenterImpl", () => { +describe("NavigationPresenterImpl", function () { + this.timeout(5000); + let sut: NavigationPresenterImpl; const testRel = (done: () => void, rel: string, html: string) => { diff --git a/test/content/usecases/ClipboardUseCase.test.ts b/test/content/usecases/ClipboardUseCase.test.ts deleted file mode 100644 index 5de3e69..0000000 --- a/test/content/usecases/ClipboardUseCase.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -import ClipboardRepository from "../../../src/content/repositories/ClipboardRepository"; -import { SettingRepositoryImpl } from "../../../src/content/repositories/SettingRepository"; -import ClipboardUseCase from "../../../src/content/usecases/ClipboardUseCase"; -import OperationClient from "../../../src/content/client/OperationClient"; -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(() => { - 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(); - } - })(); - - sut = new ClipboardUseCase( - clipboardRepository, - new SettingRepositoryImpl(), - consoleClient, - operationClient - ); - }); - - describe("#yankCurrentURL", () => { - it("yanks current url", async () => { - const href = window.location.href; - const mockRepository = sinon.mock(clipboardRepository); - mockRepository.expects("write").withArgs(href); - const mockConsoleClient = sinon.mock(consoleClient); - mockConsoleClient.expects("info").withArgs("Yanked " + href); - - const yanked = await sut.yankCurrentURL(); - - expect(yanked).to.equal(href); - mockRepository.verify(); - mockConsoleClient.verify(); - }); - }); - - describe("#openOrSearch", () => { - it("opens url from the clipboard", async () => { - const url = "https://github.com/ueokande/vim-vixen"; - sinon.stub(clipboardRepository, "read").returns(url); - const mockOperationClient = sinon.mock(operationClient); - mockOperationClient.expects("internalOpenUrl").withArgs(url, true); - - await sut.openOrSearch(true); - - mockOperationClient.verify(); - }); - - it("opens search results from the clipboard", async () => { - const url = "https://google.com/search?q=banana"; - sinon.stub(clipboardRepository, "read").returns("banana"); - const mockOperationClient = sinon.mock(operationClient); - mockOperationClient.expects("internalOpenUrl").withArgs(url, true); - - await sut.openOrSearch(true); - - mockOperationClient.verify(); - }); - }); -}); diff --git a/test/content/usecases/HintKeyProducer.test.ts b/test/content/usecases/HintKeyProducer.test.ts index f7e02ea..9d320b4 100644 --- a/test/content/usecases/HintKeyProducer.test.ts +++ b/test/content/usecases/HintKeyProducer.test.ts @@ -1,13 +1,7 @@ -import HintKeyProducer from "../../../src/content/usecases/HintKeyProducer"; +import { HintKeyRepositoryImpl } from "../../../src/content/repositories/HintKeyRepository"; import { expect } from "chai"; -describe("HintKeyProducer class", () => { - describe("#constructor", () => { - it("throws an exception on empty charset", () => { - expect(() => new HintKeyProducer("")).to.throw(TypeError); - }); - }); - +describe("HintKeyProducerImpl class", () => { describe("#produce", () => { it("produce incremented keys", () => { const charset = "abc"; @@ -30,10 +24,29 @@ describe("HintKeyProducer class", () => { "aba", ]; - const producer = new HintKeyProducer(charset); + const sut = new HintKeyRepositoryImpl(); + sut.reset(charset); for (let i = 0; i < sequences.length; ++i) { - expect(producer.produce()).to.equal(sequences[i]); + expect(sut.produce()).to.equal(sequences[i]); } }); }); + + describe("#reset", () => { + it("resets charset", () => { + const sut = new HintKeyRepositoryImpl(); + + sut.reset("ab"); + expect(sut.produce()).to.equal("a"); + expect(sut.produce()).to.equal("b"); + + sut.reset("xy"); + expect(sut.produce()).to.equal("x"); + expect(sut.produce()).to.equal("y"); + }); + it("throws an exception on empty charset", () => { + const sut = new HintKeyRepositoryImpl(); + expect(() => sut.reset("")).to.throw(TypeError); + }); + }); }); @@ -74,10 +74,10 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.3.tgz#9e1eae46738bcd08e23e867bab43e7b95299a8f9" integrity sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA== -"@babel/runtime@^7.5.5": - version "7.11.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" - integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== +"@babel/runtime@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" + integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== dependencies: regenerator-runtime "^0.13.4" @@ -136,10 +136,10 @@ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== -"@eslint/eslintrc@^0.1.3": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.1.3.tgz#7d1a2b2358552cc04834c0979bd4275362e37085" - integrity sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA== +"@eslint/eslintrc@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.2.2.tgz#d01fc791e2fc33e88a29d6f3dc7e93d0cd784b76" + integrity sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ== dependencies: ajv "^6.12.4" debug "^4.1.1" @@ -152,7 +152,7 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.7.2": +"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": version "1.8.1" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" integrity sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw== @@ -191,10 +191,10 @@ array-from "^2.1.1" lodash "^4.17.15" -"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.1.0.tgz#3afe719232b541bb6cf3411a4c399a188de21ec0" - integrity sha512-42nyaQOVunX5Pm6GRJobmzbS7iLI+fhERITnETXzzwDZh+TtDr/Au3yAvXVjFmZ4wEUaE4Y3NFZfKv0bV0cbtg== +"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.3.0.tgz#1d2f0743dc54bf13fe9d508baefacdffa25d4329" + integrity sha512-hXpcfx3aq+ETVBwPlRFICld5EnrkexXuXDwqUNhDdr5L8VjvMeSRwyOa0qL7XFmR+jVWR4rUZtnxlG7RX72sBg== dependencies: "@sinonjs/commons" "^1.6.0" lodash.get "^4.4.2" @@ -211,9 +211,9 @@ integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== "@types/assert@^1.4.6": - version "1.5.1" - resolved "https://registry.yarnpkg.com/@types/assert/-/assert-1.5.1.tgz#9c4b57935d446ffcfb31d583799c47272161e270" - integrity sha512-W34OXyUAXEg0IvkMBr6WHTsvxh4veqNVJJGni/6pp13BabT/nuiEtUVkf+EYo8FIT4CQSVYfZ05XtUo5nRSWfQ== + version "1.5.2" + resolved "https://registry.yarnpkg.com/@types/assert/-/assert-1.5.2.tgz#dbc440f6bd7a83b03c37c65e81076d07cf8becdc" + integrity sha512-DLsoZH9z5DLDi6qMbXKqeqlQLK1h3rfR9dK+KX8UJSGHJylvIZPOCQEKr/d/FClPoZE/eHOa3+e270eUJCUTog== "@types/body-parser@*": version "1.19.0" @@ -224,9 +224,9 @@ "@types/node" "*" "@types/chai@^4.2.8": - version "4.2.12" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.12.tgz#6160ae454cd89dae05adc3bb97997f488b608201" - integrity sha512-aN5IAC8QNtSUdQzxu7lGBgYAOuU1tmRU4c9dIq5OKGf/SBVjXo+ffM2wEjudAWbgpOhy60nLoAGH1xm8fpCKFQ== + version "4.2.14" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.14.tgz#44d2dd0b5de6185089375d976b4ec5caf6861193" + integrity sha512-G+ITQPXkwTrslfG5L/BksmbLUA0M1iybEsmCWPqzSxsRRhJZimBKJkoMi8fr/CPygPTj4zO5pJH7I2/cm9M7SQ== "@types/color-name@^1.1.1": version "1.1.1" @@ -255,9 +255,9 @@ "@types/range-parser" "*" "@types/express@^4.17.2": - version "4.17.8" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.8.tgz#3df4293293317e61c60137d273a2e96cd8d5f27a" - integrity sha512-wLhcKh3PMlyA2cNAB9sjM1BntnhPMiM0JOBwPBqttjHev2428MLEB4AYVN+d8s2iyCVZac+o41Pflm/ZH5vLXQ== + version "4.17.9" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.9.tgz#f5f2df6add703ff28428add52bdec8a1091b0a78" + integrity sha512-SDzEIZInC4sivGIFY4Sz1GG6J9UObPwCInYJjko2jzOf/Imx/dlpume6Xxwj1ORL82tBbmN4cPDIDkLbWHk9hw== dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "*" @@ -282,7 +282,7 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== -"@types/json-schema@^7.0.5": +"@types/json-schema@^7.0.6": version "7.0.6" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== @@ -293,19 +293,14 @@ integrity sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q== "@types/mocha@^8.0.1": - version "8.0.3" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.0.3.tgz#51b21b6acb6d1b923bbdc7725c38f9f455166402" - integrity sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg== - -"@types/node@*": - version "14.0.27" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.27.tgz#a151873af5a5e851b51b3b065c9e63390a9e0eb1" - integrity sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g== + version "8.0.4" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.0.4.tgz#b840c2dce46bacf286e237bfb59a29e843399148" + integrity sha512-M4BwiTJjHmLq6kjON7ZoI2JMlBvpY3BYSdiP6s/qCT3jb1s9/DeJF0JELpAxiVSIxXDzfNKe+r7yedMIoLbknQ== -"@types/node@^12.12.21": - version "12.12.54" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.54.tgz#a4b58d8df3a4677b6c08bfbc94b7ad7a7a5f82d1" - integrity sha512-ge4xZ3vSBornVYlDnk7yZ0gK6ChHf/CHB7Gl1I0Jhah8DDnEQqBzgohYG4FX4p81TNirSETOiSyn+y1r9/IR6w== +"@types/node@*", "@types/node@^14.14.13": + version "14.14.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.13.tgz#9e425079799322113ae8477297ae6ef51b8e0cdf" + integrity sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ== "@types/prop-types@*", "@types/prop-types@^15.7.3": version "15.7.3" @@ -322,41 +317,34 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== -"@types/react-dom@^16.9.5": - version "16.9.8" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.8.tgz#fe4c1e11dfc67155733dfa6aa65108b4971cb423" - integrity sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA== - dependencies: - "@types/react" "*" - -"@types/react-native@*": - version "0.63.8" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.63.8.tgz#73ec087122c64c309eeaf150b565b8d755f0fb1f" - integrity sha512-QRwGFRTyGafRVTUS+0GYyJrlpmS3boyBaFI0ULSc+mh/lQNxrzbdQvoL2k5X7+t9hxyqA4dTQAlP6l0ir/fNJQ== +"@types/react-dom@^17.0.0": + version "17.0.0" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.0.tgz#b3b691eb956c4b3401777ee67b900cb28415d95a" + integrity sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g== dependencies: "@types/react" "*" "@types/react-redux@^7.1.7": - version "7.1.9" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.9.tgz#280c13565c9f13ceb727ec21e767abe0e9b4aec3" - integrity sha512-mpC0jqxhP4mhmOl3P4ipRsgTgbNofMRXJb08Ms6gekViLj61v1hOZEKWDCyWsdONr6EjEA6ZHXC446wdywDe0w== + version "7.1.13" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.13.tgz#8766a0078433621957cd78a6096d316bccbcb3ca" + integrity sha512-+Z8C22kL/fkEWFETc+duRyJT6P23G1GbowdLgRsjPyimJdpBv52Uqg4p3vCW6qagBj4EUi5jKhe+by+Uv3+QTQ== dependencies: "@types/hoist-non-react-statics" "^3.3.0" "@types/react" "*" hoist-non-react-statics "^3.3.0" redux "^4.0.0" -"@types/react-test-renderer@^16.9.2": - version "16.9.3" - resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.9.3.tgz#96bab1860904366f4e848b739ba0e2f67bcae87e" - integrity sha512-wJ7IlN5NI82XMLOyHSa+cNN4Z0I+8/YaLl04uDgcZ+W+ExWCmCiVTLT/7fRNqzy4OhStZcUwIqLNF7q+AdW43Q== +"@types/react-test-renderer@^17.0.0": + version "17.0.0" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-17.0.0.tgz#9be47b375eeb906fced37049e67284a438d56620" + integrity sha512-nvw+F81OmyzpyIE1S0xWpLonLUZCMewslPuA8BtjSKc5XEbn8zEQBXS7KuOLHTNnSOEM2Pum50gHOoZ62tqTRg== dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^16.9.46": - version "16.9.51" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.51.tgz#f8aa51ffa9996f1387f63686696d9b59713d2b60" - integrity sha512-lQa12IyO+DMlnSZ3+AGHRUiUcpK47aakMMoBG8f7HGxJT8Yfe+WE128HIXaHOHVPReAW0oDS3KAI0JI2DDe1PQ== +"@types/react@*", "@types/react@^17.0.0": + version "17.0.0" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.0.tgz#5af3eb7fad2807092f0046a1302b7823e27919b8" + integrity sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw== dependencies: "@types/prop-types" "*" csstype "^3.0.2" @@ -382,9 +370,9 @@ "@types/mime" "*" "@types/sinon@^9.0.0": - version "9.0.7" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.7.tgz#c277e19cf9eb0c71106e785650f1e5c299262302" - integrity sha512-uyFiy2gp4P/FK9pmU3WIbT5ZzH54hCswwRkQFhxX7xl8jzhW3g+xOkVqk5YP4cIO//Few8UDAX0MtzFpqBEqwA== + version "9.0.9" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.9.tgz#115843b491583f924080f684b6d0d7438344f73c" + integrity sha512-z/y8maYOQyYLyqaOB+dYQ6i0pxKLOsfwCmHmn4T7jS/SDHicIslr37oE3Dg8SCqKrKeBy6Lemu7do2yy+unLrw== dependencies: "@types/sinonjs__fake-timers" "*" @@ -399,13 +387,12 @@ integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== "@types/styled-components@^5.1.2": - version "5.1.3" - resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-5.1.3.tgz#6fab3d9c8f7d9a15cbb89d379d850c985002f363" - integrity sha512-HGpirof3WOhiX17lb61Q/tpgqn48jxO8EfZkdJ8ueYqwLbK2AHQe/G08DasdA2IdKnmwOIP1s9X2bopxKXgjRw== + version "5.1.7" + resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-5.1.7.tgz#3cd10b088c1cb1acde2e4b166b3e8275a3083710" + integrity sha512-BJzPhFygYspyefAGFZTZ/8lCEY4Tk+Iqktvnko3xmJf9LrLqs3+grxPeU3O0zLl6yjbYBopD0/VikbHgXDbJtA== dependencies: "@types/hoist-non-react-statics" "*" "@types/react" "*" - "@types/react-native" "*" csstype "^3.0.2" "@types/tapable@*", "@types/tapable@^1.0.5": @@ -538,6 +525,11 @@ dependencies: eslint-visitor-keys "^1.1.0" +"@ungap/promise-all-settled@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" + integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -683,6 +675,18 @@ "@webassemblyjs/wast-parser" "1.9.0" "@xtuc/long" "4.2.2" +"@webpack-cli/info@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.1.0.tgz#c596d5bc48418b39df00c5ed7341bf0f102dbff1" + integrity sha512-uNWSdaYHc+f3LdIZNwhdhkjjLDDl3jP2+XBqAq9H8DjrJUvlOKdP8TNruy1yEaDfgpAIgbSAN7pye4FEHg9tYQ== + dependencies: + envinfo "^7.7.3" + +"@webpack-cli/serve@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.1.0.tgz#13ad38f89b6e53d1133bac0006a128217a6ebf92" + integrity sha512-7RfnMXCpJ/NThrhq4gYQYILB18xWyoQcBey81oIyVbmgbc6m5ZHHyFK+DyH7pLHJf0p14MxL4mTsoPAgBSTpIg== + "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -693,11 +697,6 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - accepts@~1.3.4, accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -711,6 +710,11 @@ acorn-jsx@^5.2.0: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== +acorn-jsx@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== + acorn@^6.4.1: version "6.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" @@ -726,17 +730,16 @@ after@0.8.2: resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= -ajv-cli@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/ajv-cli/-/ajv-cli-3.3.0.tgz#dd72207f5395051db7d620ab7d4da1cec3d50f06" - integrity sha512-4eiNNjDlainn5Rm+rU3egxLUUxzevcB+gviBLs9sm6etHNrE7l2JSQ3yoz5hE+eqrth3pTygELOafhSIO8Hiig== +ajv-cli@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/ajv-cli/-/ajv-cli-4.0.1.tgz#00543699942f8bec8b2e7aadc62909139c788cf8" + integrity sha512-vRV3Qrqq9yiKZbjX2uaqjykE2QI08/SlY/fPR7e6hloTTCOGXXcGXghLVgBkqeN7Ts/+Xfc21CjOX0p41Hskcw== dependencies: - ajv "^6.7.0" - ajv-pack "^0.3.0" + ajv "^7.0.0" fast-json-patch "^2.0.0" glob "^7.1.0" - js-yaml "^3.13.1" - json-schema-migrate "^0.2.0" + js-yaml "^3.14.0" + json-schema-migrate "^1.0.0" json5 "^2.1.3" minimist "^1.2.0" @@ -750,32 +753,24 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv-pack@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/ajv-pack/-/ajv-pack-0.3.1.tgz#b72c4d4219e3928e62842d742ded93bf50796560" - integrity sha1-tyxNQhnjko5ihC10Le2Tv1B5ZWA= +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.11.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: - js-beautify "^1.6.4" - require-from-string "^1.2.0" - -ajv@^5.0.0: - version "5.5.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" - integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= - dependencies: - co "^4.6.0" - fast-deep-equal "^1.0.0" + fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.11.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.7.0: - version "6.12.5" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da" - integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag== +ajv@^7.0.0, ajv@^7.0.0-beta.9: + version "7.0.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.0.2.tgz#04ccc89a93449c64382fe0846d45a7135d986dbc" + integrity sha512-qIaxO9RXjaXyO21tJr0EvwPcZa49m64GcXCU8ZrLjCAlFyMuOcPpI560So4A11M1WsKslJYIXn6jSyG5P0xIeg== dependencies: fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" uri-js "^4.2.2" ansi-colors@4.1.1, ansi-colors@^4.1.1: @@ -788,13 +783,6 @@ ansi-colors@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== -ansi-escapes@^4.2.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" - integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== - dependencies: - type-fest "^0.11.0" - ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -883,6 +871,11 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= +array-back@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.1.tgz#9b80312935a52062e1a233a9c7abeb5481b30e90" + integrity sha512-Z/JnaVEXv+A9xabHzN43FiiiWEE7gPCRXMrVmRm00tWbjZRul1iHm7ECzlyNq1p4a4ATXz+G9FJ3GqGOkOV3fg== + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -916,16 +909,6 @@ array.prototype.flatmap@^1.2.3: es-abstract "^1.17.0-next.1" function-bind "^1.1.1" -array.prototype.map@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array.prototype.map/-/array.prototype.map-1.0.2.tgz#9a4159f416458a23e9483078de1106b2ef68f8ec" - integrity sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - es-array-method-boxes-properly "^1.0.0" - is-string "^1.0.4" - arraybuffer.slice@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" @@ -975,6 +958,11 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + async-each@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" @@ -1325,15 +1313,15 @@ camel-case@^4.1.1: pascal-case "^3.1.1" tslib "^1.10.0" -camelcase@^5.0.0, camelcase@^5.3.1: +camelcase@^5.0.0: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e" - integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== +camelcase@^6.0.0, camelcase@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== camelize@^1.0.0: version "1.0.0" @@ -1377,7 +1365,7 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0: +chalk@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== @@ -1385,20 +1373,15 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - check-error@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= -chokidar@3.4.2, chokidar@^3.4.1, chokidar@^3.4.2: - version "3.4.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" - integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== +chokidar@3.4.3, chokidar@^3.4.1, chokidar@^3.4.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" + integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ== dependencies: anymatch "~3.1.1" braces "~3.0.2" @@ -1406,7 +1389,7 @@ chokidar@3.4.2, chokidar@^3.4.1, chokidar@^3.4.2: is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" - readdirp "~3.4.0" + readdirp "~3.5.0" optionalDependencies: fsevents "~2.1.2" @@ -1466,18 +1449,6 @@ clean-css@^4.2.3: dependencies: source-map "~0.6.0" -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== - cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -1505,11 +1476,6 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -1542,6 +1508,11 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colorette@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" + integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== + colors@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" @@ -1554,7 +1525,17 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.19.0, commander@^2.20.0: +command-line-usage@^6.1.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.1.tgz#c908e28686108917758a49f45efb4f02f76bc03f" + integrity sha512-F59pEuAR9o1SF/bD0dQBDluhpT4jJQNWUHEuVBqpDmCUo6gPjCi+m9fCWnWZVR/oG6cMTUms4h+3NPl74wGXvA== + dependencies: + array-back "^4.0.1" + chalk "^2.4.2" + table-layout "^1.0.1" + typical "^5.2.0" + +commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -1564,6 +1545,11 @@ commander@^4.1.1: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== +commander@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.0.tgz#b990bfb8ac030aedc6d11bc04d1488ffef56db75" + integrity sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q== + common-tags@^1.4.0: version "1.8.0" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" @@ -1609,14 +1595,6 @@ concat-stream@^1.5.0: readable-stream "^2.2.2" typedarray "^0.0.6" -config-chain@^1.1.12: - version "1.1.12" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" - integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - connect@^3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" @@ -1717,18 +1695,12 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-spawn@^7.0.2: +cross-spawn@^7.0.0, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1759,22 +1731,22 @@ css-color-keywords@^1.0.0: resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU= -css-loader@^4.2.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-4.3.0.tgz#c888af64b2a5b2e85462c72c0f4a85c7e2e0821e" - integrity sha512-rdezjCjScIrsL8BSYszgT4s476IcNKt6yX69t0pHjJVnPUTDpn4WfIpDQTN3wCJvUvfsz/mFjuGOekf3PY3NUg== +css-loader@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.0.1.tgz#9e4de0d6636a6266a585bd0900b422c85539d25f" + integrity sha512-cXc2ti9V234cq7rJzFKhirb2L2iPy8ZjALeVJAozXYz9te3r4eqLSixNAbMDJSgJEQywqXzs8gonxaboeKqwiw== dependencies: - camelcase "^6.0.0" + camelcase "^6.2.0" cssesc "^3.0.0" - icss-utils "^4.1.1" + icss-utils "^5.0.0" loader-utils "^2.0.0" - postcss "^7.0.32" - postcss-modules-extract-imports "^2.0.0" - postcss-modules-local-by-default "^3.0.3" - postcss-modules-scope "^2.2.0" - postcss-modules-values "^3.0.0" + postcss "^8.1.4" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" postcss-value-parser "^4.1.0" - schema-utils "^2.7.1" + schema-utils "^3.0.0" semver "^7.3.2" css-select@^1.1.0: @@ -1845,12 +1817,12 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4.1.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== +debug@4.2.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== dependencies: - ms "^2.1.1" + ms "2.1.2" debug@~3.1.0: version "3.1.0" @@ -1859,11 +1831,23 @@ debug@~3.1.0: dependencies: ms "2.0.0" +debug@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -1876,7 +1860,12 @@ deep-eql@^3.0.1: dependencies: type-detect "^4.0.0" -deep-is@^0.1.3, deep-is@~0.1.3: +deep-extend@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= @@ -1933,11 +1922,6 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= -detect-file@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" - integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= - di@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" @@ -2077,16 +2061,6 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" -editorconfig@^0.15.3: - version "0.15.3" - resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5" - integrity sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g== - dependencies: - commander "^2.19.0" - lru-cache "^4.1.5" - semver "^5.6.0" - sigmund "^1.0.1" - ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -2172,7 +2146,7 @@ engine.io@~3.4.0: engine.io-parser "~2.2.0" ws "^7.1.2" -enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.1, enhanced-resolve@^4.3.0: +enhanced-resolve@^4.0.0, enhanced-resolve@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== @@ -2181,7 +2155,7 @@ enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.1, enhanced-resolve@^4.3.0: memory-fs "^0.5.0" tapable "^1.0.0" -enquirer@^2.3.5: +enquirer@^2.3.5, enquirer@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== @@ -2203,6 +2177,11 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f" integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ== +envinfo@^7.7.3: + version "7.7.3" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.7.3.tgz#4b2d8622e3e7366afb8091b23ed95569ea0208cc" + integrity sha512-46+j5QxbPWza0PB1i15nZx0xQ4I/EfQxg9J8Had3b408SV63nEtor2e+oiY63amTo9KTuh2a3XLObNwduxYwwA== + errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" @@ -2210,7 +2189,7 @@ errno@^0.1.3, errno@~0.1.7: dependencies: prr "~1.0.1" -es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.4, es-abstract@^1.17.5: +es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: version "1.17.6" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== @@ -2227,23 +2206,23 @@ es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.4, es-abstrac string.prototype.trimend "^1.0.1" string.prototype.trimstart "^1.0.1" -es-array-method-boxes-properly@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" - integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== - -es-get-iterator@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8" - integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ== +es-abstract@^1.18.0-next.0: + version "1.18.0-next.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" + integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== dependencies: - es-abstract "^1.17.4" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" has-symbols "^1.0.1" - is-arguments "^1.0.4" - is-map "^2.0.1" - is-set "^2.0.1" - is-string "^1.0.5" - isarray "^2.0.5" + is-callable "^1.2.2" + is-negative-zero "^2.0.0" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.1" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" es-to-primitive@^1.2.1: version "1.2.1" @@ -2269,12 +2248,10 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -eslint-config-prettier@6.11.0: - version "6.11.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1" - integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA== - dependencies: - get-stdin "^6.0.0" +eslint-config-prettier@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-7.0.0.tgz#c1ae4106f74e6c0357f44adb076771d032ac0e97" + integrity sha512-8Y8lGLVPPZdaNA7JXqnvETVC7IiVRgAP6afQu9gOQRn90YY3otMNh+x7Vr2vMePQntF+5erdSUBqSzCmU/AxaQ== eslint-plugin-prettier@3.1.4: version "3.1.4" @@ -2283,27 +2260,27 @@ eslint-plugin-prettier@3.1.4: dependencies: prettier-linter-helpers "^1.0.0" -eslint-plugin-react@7.21.2: - version "7.21.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.21.2.tgz#3bd5d2c4c36d5a0428d0d6dda301ac9a84d681b2" - integrity sha512-j3XKvrK3rpBzveKFbgAeGsWb9uz6iUOrR0jixRfjwdFeGSRsXvVTFtHDQYCjsd1/6Z/xvb8Vy3LiI5Reo7fDrg== +eslint-plugin-react@7.21.5: + version "7.21.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.21.5.tgz#50b21a412b9574bfe05b21db176e8b7b3b15bff3" + integrity sha512-8MaEggC2et0wSF6bUeywF7qQ46ER81irOdWS4QWxnnlAEsnzeBevk1sWh7fhpCghPpXb+8Ks7hvaft6L/xsR6g== dependencies: array-includes "^3.1.1" array.prototype.flatmap "^1.2.3" doctrine "^2.1.0" has "^1.0.3" - jsx-ast-utils "^2.4.1" + jsx-ast-utils "^2.4.1 || ^3.0.0" object.entries "^1.1.2" object.fromentries "^2.0.2" object.values "^1.1.1" prop-types "^15.7.2" - resolve "^1.17.0" + resolve "^1.18.1" string.prototype.matchall "^4.0.2" -eslint-plugin-standard@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz#ff0519f7ffaff114f76d1bd7c3996eef0f6e20b4" - integrity sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ== +eslint-plugin-standard@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-5.0.0.tgz#c43f6925d669f177db46f095ea30be95476b1ee4" + integrity sha512-eSIXPc9wBM4BrniMzJRBm2uoVuXz2EPa+NXPk2+itrVt+r5SbKFERx/IgrK/HmfjddyKVz2f+j+7gBRvu19xLg== eslint-scope@^4.0.3: version "4.0.3" @@ -2321,13 +2298,6 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" - integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== - dependencies: - eslint-visitor-keys "^1.1.0" - eslint-utils@^2.0.0, eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" @@ -2340,13 +2310,18 @@ eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== -eslint@7.10.0: - version "7.10.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.10.0.tgz#494edb3e4750fb791133ca379e786a8f648c72b9" - integrity sha512-BDVffmqWl7JJXqCjAK6lWtcQThZB/aP1HXSH1JKwGwv0LQEdvpR7qzNrUT487RM39B5goWuboFad5ovMBmD8yA== +eslint-visitor-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" + integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== + +eslint@7.15.0: + version "7.15.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.15.0.tgz#eb155fb8ed0865fcf5d903f76be2e5b6cd7e0bc7" + integrity sha512-Vr64xFDT8w30wFll643e7cGrIkPEU50yIiI36OdSIDoSGguIeaLzBo0vpGvzo9RECUqq7htURfwEtKqwytkqzA== dependencies: "@babel/code-frame" "^7.0.0" - "@eslint/eslintrc" "^0.1.3" + "@eslint/eslintrc" "^0.2.2" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -2355,11 +2330,11 @@ eslint@7.10.0: enquirer "^2.3.5" eslint-scope "^5.1.1" eslint-utils "^2.1.0" - eslint-visitor-keys "^1.3.0" - espree "^7.3.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.1" esquery "^1.2.0" esutils "^2.0.2" - file-entry-cache "^5.0.1" + file-entry-cache "^6.0.0" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" globals "^12.1.0" @@ -2383,50 +2358,50 @@ eslint@7.10.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -eslint@^6.8.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" - integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== +eslint@^7.9.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.16.0.tgz#a761605bf9a7b32d24bb7cde59aeb0fd76f06092" + integrity sha512-iVWPS785RuDA4dWuhhgXTNrGxHHK3a8HLSMBgbbU59ruJDubUraXN8N5rn7kb8tG6sjg74eE0RA3YWT51eusEw== dependencies: "@babel/code-frame" "^7.0.0" + "@eslint/eslintrc" "^0.2.2" ajv "^6.10.0" - chalk "^2.1.0" - cross-spawn "^6.0.5" + chalk "^4.0.0" + cross-spawn "^7.0.2" debug "^4.0.1" doctrine "^3.0.0" - eslint-scope "^5.0.0" - eslint-utils "^1.4.3" - eslint-visitor-keys "^1.1.0" - espree "^6.1.2" - esquery "^1.0.1" + enquirer "^2.3.5" + eslint-scope "^5.1.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.1" + esquery "^1.2.0" esutils "^2.0.2" - file-entry-cache "^5.0.1" + file-entry-cache "^6.0.0" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" globals "^12.1.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" - inquirer "^7.0.0" is-glob "^4.0.0" js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.14" + levn "^0.4.1" + lodash "^4.17.19" minimatch "^3.0.4" - mkdirp "^0.5.1" natural-compare "^1.4.0" - optionator "^0.8.3" + optionator "^0.9.1" progress "^2.0.0" - regexpp "^2.0.1" - semver "^6.1.2" - strip-ansi "^5.2.0" - strip-json-comments "^3.0.1" - table "^5.2.3" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^6.0.4" text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^6.1.2, espree@^6.2.1: +espree@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== @@ -2435,13 +2410,13 @@ espree@^6.1.2, espree@^6.2.1: acorn-jsx "^5.2.0" eslint-visitor-keys "^1.1.0" -espree@^7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348" - integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw== +espree@^7.3.0, espree@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== dependencies: acorn "^7.4.0" - acorn-jsx "^5.2.0" + acorn-jsx "^5.3.1" eslint-visitor-keys "^1.3.0" esprima@^4.0.0: @@ -2508,6 +2483,21 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" +execa@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" @@ -2521,13 +2511,6 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expand-tilde@^2.0.0, expand-tilde@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" - integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= - dependencies: - homedir-polyfill "^1.0.1" - express@^4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" @@ -2584,15 +2567,6 @@ extend@^3.0.0, extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" @@ -2617,11 +2591,6 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -fast-deep-equal@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" - integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= - fast-deep-equal@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" @@ -2649,7 +2618,7 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= @@ -2659,19 +2628,12 @@ figgy-pudding@^3.5.1: resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== -figures@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" - integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== +file-entry-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.0.tgz#7921a89c391c6d93efec2169ac6bf300c527ea0a" + integrity sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA== dependencies: - flat-cache "^2.0.1" + flat-cache "^3.0.4" file-uri-to-path@1.0.0: version "1.0.0" @@ -2732,7 +2694,7 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@^4.1.0: +find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -2740,37 +2702,29 @@ find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -findup-sync@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" - integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== - dependencies: - detect-file "^1.0.0" - is-glob "^4.0.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" + flatted "^3.1.0" + rimraf "^3.0.2" -flat@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" - integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== - dependencies: - is-buffer "~2.0.3" +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -flatted@^2.0.0, flatted@^2.0.1: +flatted@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== +flatted@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.0.tgz#a5d06b4a8b01e3a63771daa5cb7a1903e2e57067" + integrity sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA== + flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" @@ -2892,10 +2846,12 @@ get-func-name@^2.0.0: resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= -get-stdin@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" - integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== +get-stream@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" @@ -2936,42 +2892,6 @@ glob@7.1.6, glob@^7.1.0, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -global-modules@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" - integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== - dependencies: - global-prefix "^1.0.1" - is-windows "^1.0.1" - resolve-dir "^1.0.0" - -global-modules@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" - integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== - dependencies: - global-prefix "^3.0.0" - -global-prefix@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" - integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= - dependencies: - expand-tilde "^2.0.2" - homedir-polyfill "^1.0.1" - ini "^1.3.4" - is-windows "^1.0.1" - which "^1.2.14" - -global-prefix@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" - integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== - dependencies: - ini "^1.3.5" - kind-of "^6.0.2" - which "^1.3.1" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -3110,20 +3030,13 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0: +hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== dependencies: react-is "^16.7.0" -homedir-polyfill@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" - html-minifier-terser@^5.0.1: version "5.1.1" resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#922e96f1f3bb60832c2634b79884096389b1f054" @@ -3137,10 +3050,10 @@ html-minifier-terser@^5.0.1: relateurl "^0.2.7" terser "^4.6.3" -html-webpack-plugin@4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.3.0.tgz#53bf8f6d696c4637d5b656d3d9863d89ce8174fd" - integrity sha512-C0fzKN8yQoVLTelcJxZfJCE+aAvQiY2VUf3UuKrR4a9k5UMWYOtpDLsaXwATbcVCnI05hUS7L9ULQHWLZhyi3w== +html-webpack-plugin@4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.5.0.tgz#625097650886b97ea5dae331c320e3238f6c121c" + integrity sha512-MouoXEYSjTzCrjIxWwg8gxL5fE2X2WZJLmBYXlaJhQUH5K/b5OrqmV7T4dB7iu0xkmJ6JlUuV6fFVtnqbPopZw== dependencies: "@types/html-minifier-terser" "^5.0.0" "@types/tapable" "^1.0.5" @@ -3209,19 +3122,22 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -iconv-lite@0.4.24, iconv-lite@^0.4.24: +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + +iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" -icss-utils@^4.0.0, icss-utils@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" - integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== - dependencies: - postcss "^7.0.14" +icss-utils@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== ieee754@^1.1.4: version "1.1.13" @@ -3251,13 +3167,13 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -import-local@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" - integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== +import-local@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" + integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== dependencies: - pkg-dir "^3.0.0" - resolve-cwd "^2.0.0" + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" imurmurhash@^0.1.4: version "0.1.4" @@ -3307,30 +3223,6 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.4, ini@^1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" - integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== - -inquirer@^7.0.0: - version "7.3.3" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" - integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.19" - mute-stream "0.0.8" - run-async "^2.4.0" - rxjs "^6.6.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - internal-slot@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.2.tgz#9c2e9fb3cd8e5e4256c6f45fe310067fcfa378a3" @@ -3340,10 +3232,10 @@ internal-slot@^1.0.2: has "^1.0.3" side-channel "^1.0.2" -interpret@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +interpret@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" + integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== ipaddr.js@1.9.1: version "1.9.1" @@ -3364,11 +3256,6 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-arguments@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" - integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== - is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" @@ -3388,16 +3275,23 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" - integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== - is-callable@^1.1.4, is-callable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb" integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw== +is-callable@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" + integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== + +is-core-module@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" + integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -3481,10 +3375,10 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-map@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" - integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw== +is-negative-zero@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" + integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= is-number@^3.0.0: version "3.0.0" @@ -3498,10 +3392,10 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-plain-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" @@ -3515,19 +3409,19 @@ is-promise@^2.1.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== -is-regex@^1.1.0: +is-regex@^1.1.0, is-regex@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== dependencies: has-symbols "^1.0.1" -is-set@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" - integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA== +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-string@^1.0.4, is-string@^1.0.5: +is-string@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== @@ -3544,7 +3438,7 @@ is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-windows@^1.0.1, is-windows@^1.0.2: +is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== @@ -3554,7 +3448,7 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= -is-wsl@^2.1.0: +is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== @@ -3576,11 +3470,6 @@ isarray@2.0.1: resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= -isarray@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - isbinaryfile@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.6.tgz#edcb62b224e2b4710830b67498c8e4e5a4d2610b" @@ -3608,36 +3497,12 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -iterate-iterator@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/iterate-iterator/-/iterate-iterator-1.0.1.tgz#1693a768c1ddd79c969051459453f082fe82e9f6" - integrity sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw== - -iterate-value@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/iterate-value/-/iterate-value-1.0.2.tgz#935115bd37d006a52046535ebc8d07e9c9337f57" - integrity sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ== - dependencies: - es-get-iterator "^1.0.2" - iterate-iterator "^1.0.1" - -js-beautify@^1.6.4: - version "1.11.0" - resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.11.0.tgz#afb873dc47d58986360093dcb69951e8bcd5ded2" - integrity sha512-a26B+Cx7USQGSWnz9YxgJNMmML/QG2nqIaL7VVYPCXbqiKz8PN0waSNvroMtvAK6tY7g/wPdNWGEP+JTNIBr6A== - dependencies: - config-chain "^1.1.12" - editorconfig "^0.15.3" - glob "^7.1.3" - mkdirp "~1.0.3" - nopt "^4.0.3" - "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@3.14.0, js-yaml@^3.13.1: +js-yaml@3.14.0: version "3.14.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== @@ -3645,6 +3510,14 @@ js-yaml@3.14.0, js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^3.13.1, js-yaml@^3.14.0: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -3660,23 +3533,23 @@ json-parse-better-errors@^1.0.2: resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== -json-schema-migrate@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/json-schema-migrate/-/json-schema-migrate-0.2.0.tgz#ba47a5b0072fc72396460e1bd60b44d52178bbc6" - integrity sha1-ukelsAcvxyOWRg4b1gtE1SF4u8Y= +json-schema-migrate@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-migrate/-/json-schema-migrate-1.0.0.tgz#9169a3f9c0b0c5dfc749d01bb7c76d31ad081599" + integrity sha512-Ze3G4zCBi0ZHKQaK3EEFv7Q0d3/LxZldrH/6rFNNegkGGolMp6Deg6rLWyOaRApPABeZfMyZXbIiAFyRP1bHQA== dependencies: - ajv "^5.0.0" - -json-schema-traverse@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" - integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A= + ajv "^7.0.0-beta.9" json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -3739,13 +3612,13 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -jsx-ast-utils@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz#1114a4c1209481db06c690c2b4f488cc665f657e" - integrity sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w== +"jsx-ast-utils@^2.4.1 || ^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.0.0.tgz#0f49d5093bafa4b45d3fe02147d8b40ffc6c7438" + integrity sha512-sPuicm6EPKYI/UnWpOatvg4pI50qaBo4dSOMGUPutmJ26ttedFKXr0It0XXPk4HKnQ/1X0st4eSS2w2jhFk9Ow== dependencies: array-includes "^3.1.1" - object.assign "^4.1.0" + object.assign "^4.1.1" jszip@^3.2.2: version "3.5.0" @@ -3779,12 +3652,13 @@ jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" -karma-firefox-launcher@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/karma-firefox-launcher/-/karma-firefox-launcher-1.3.0.tgz#ebcbb1d1ddfada6be900eb8fae25bcf2dcdc8171" - integrity sha512-Fi7xPhwrRgr+94BnHX0F5dCl1miIW4RHnzjIGxF8GaIEp7rNqX7LSi7ok63VXs3PS/5MQaQMhGxw+bvD+pibBQ== +karma-firefox-launcher@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/karma-firefox-launcher/-/karma-firefox-launcher-2.1.0.tgz#d0d328c93dfcf9b46f1ac83b4bb32f43aadb2050" + integrity sha512-dkiyqN2R6fCWt78rciOXJLFDWcQ7QEQi++HgebPJlw1y0ycDjGNDHuSrhdh48QG02fzZKK20WHFWVyBZ6CPngg== dependencies: - is-wsl "^2.1.0" + is-wsl "^2.2.0" + which "^2.0.1" karma-html2js-preprocessor@^1.1.0: version "1.1.0" @@ -3895,13 +3769,10 @@ lanthan@0.0.2: request-promise-native "^1.0.7" selenium-webdriver "^4.0.0-alpha.5" -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== levn@^0.4.1: version "0.4.1" @@ -3923,7 +3794,7 @@ loader-runner@^2.4.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== -loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: +loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3: version "1.4.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== @@ -4018,6 +3889,11 @@ lodash@^4.16.3, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17. resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== +lodash@^4.17.20: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + log-symbols@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" @@ -4082,14 +3958,6 @@ lower-case@^2.0.1: dependencies: tslib "^1.10.0" -lru-cache@^4.1.5: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -4157,12 +4025,17 @@ merge-descriptors@1.0.1: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: +micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -4277,20 +4150,16 @@ mkdirp@^0.5.1, mkdirp@^0.5.3: dependencies: minimist "^1.2.5" -mkdirp@~1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - mocha@^8.1.1: - version "8.1.3" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.1.3.tgz#5e93f873e35dfdd69617ea75f9c68c2ca61c2ac5" - integrity sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw== + version "8.2.1" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.2.1.tgz#f2fa68817ed0e53343d989df65ccd358bc3a4b39" + integrity sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w== dependencies: + "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" - chokidar "3.4.2" - debug "4.1.1" + chokidar "3.4.3" + debug "4.2.0" diff "4.0.2" escape-string-regexp "4.0.0" find-up "5.0.0" @@ -4301,17 +4170,16 @@ mocha@^8.1.1: log-symbols "4.0.0" minimatch "3.0.4" ms "2.1.2" - object.assign "4.1.0" - promise.allsettled "1.0.2" - serialize-javascript "4.0.0" - strip-json-comments "3.0.1" - supports-color "7.1.0" + nanoid "3.1.12" + serialize-javascript "5.0.1" + strip-json-comments "3.1.1" + supports-color "7.2.0" which "2.0.2" wide-align "1.1.3" - workerpool "6.0.0" + workerpool "6.0.2" yargs "13.3.2" yargs-parser "13.1.2" - yargs-unparser "1.6.1" + yargs-unparser "2.0.0" move-concurrently@^1.0.1: version "1.0.1" @@ -4340,16 +4208,21 @@ ms@2.1.2, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -mute-stream@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== - nan@^2.12.1: version "2.14.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== +nanoid@3.1.12: + version "3.1.12" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.12.tgz#6f7736c62e8d39421601e4a0c77623a97ea69654" + integrity sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A== + +nanoid@^3.1.20: + version "3.1.20" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" + integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -4382,11 +4255,6 @@ neo-async@^2.5.0, neo-async@^2.6.1: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - nise@^1.5.2: version "1.5.3" resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.3.tgz#9d2cfe37d44f57317766c6e9408a359c5d3ac1f7" @@ -4446,14 +4314,6 @@ node-libs-browser@^2.2.1: util "^0.11.0" vm-browserify "^1.0.1" -nopt@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" - integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== - dependencies: - abbrev "1" - osenv "^0.1.4" - normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -4466,6 +4326,13 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + nth-check@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" @@ -4497,7 +4364,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.7.0: +object-inspect@^1.7.0, object-inspect@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== @@ -4514,7 +4381,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@4.1.0, object.assign@^4.1.0: +object.assign@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== @@ -4524,6 +4391,16 @@ object.assign@4.1.0, object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" +object.assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.1.tgz#303867a666cdd41936ecdedfb1f8f3e32a478cdd" + integrity sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.18.0-next.0" + has-symbols "^1.0.1" + object-keys "^1.1.1" + object.entries@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.2.tgz#bc73f00acb6b6bb16c203434b10f9a7e797d3add" @@ -4589,18 +4466,6 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -optionator@^0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - optionator@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" @@ -4618,24 +4483,11 @@ os-browserify@^0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - -os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: +os-tmpdir@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -4717,11 +4569,6 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= - parseqs@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" @@ -4779,12 +4626,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - -path-key@^3.1.0: +path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -4844,45 +4686,47 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= -postcss-modules-extract-imports@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" - integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== - dependencies: - postcss "^7.0.5" +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== -postcss-modules-local-by-default@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" - integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== +postcss-modules-local-by-default@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" + integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== dependencies: - icss-utils "^4.1.1" - postcss "^7.0.32" + icss-utils "^5.0.0" postcss-selector-parser "^6.0.2" postcss-value-parser "^4.1.0" -postcss-modules-scope@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" - integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== +postcss-modules-scope@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" + integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== dependencies: - postcss "^7.0.6" - postcss-selector-parser "^6.0.0" + postcss-selector-parser "^6.0.4" -postcss-modules-values@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" - integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== dependencies: - icss-utils "^4.0.0" - postcss "^7.0.6" + icss-utils "^5.0.0" -postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: +postcss-selector-parser@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== @@ -4891,39 +4735,44 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: indexes-of "^1.0.1" uniq "^1.0.1" +postcss-selector-parser@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3" + integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw== + dependencies: + cssesc "^3.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + util-deprecate "^1.0.2" + postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== -postcss@^7.0.14, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: - version "7.0.32" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" - integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== +postcss@^8.1.4: + version "8.2.1" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.1.tgz#eabc5557c4558059b9d9e5b15bce7ffa9089c2a8" + integrity sha512-RhsqOOAQzTgh1UB/IZdca7F9WDb7SUCR2Vnv1x7DbvuuggQIpoDwjK+q0rzoPffhYvWNKX5JSwS4so4K3UC6vA== dependencies: - chalk "^2.4.2" + colorette "^1.2.1" + nanoid "^3.1.20" source-map "^0.6.1" - supports-color "^6.1.0" prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - -prettier-eslint@11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/prettier-eslint/-/prettier-eslint-11.0.0.tgz#b19b97c8a1fc4c898b7d1a54b948ef957ceb101d" - integrity sha512-ACjL7T8m10HCO7DwYdXwhNWuZzQv86JkZAhVpzFV9brTMWi3i6LhqoELFaXf6RetDngujz89tnbDmGyvDl+rzA== +prettier-eslint@12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/prettier-eslint/-/prettier-eslint-12.0.0.tgz#b4dab5111aad1c0dca062eb7f92a69d5fb1ac1d3" + integrity sha512-N8SGGQwAosISXTNl1E57sBbtnqUGlyRWjcfIUxyD3HF4ynehA9GZ8IfJgiep/OfYvCof/JEpy9ZqSl250Wia7A== dependencies: "@typescript-eslint/parser" "^3.0.0" common-tags "^1.4.0" dlv "^1.1.0" - eslint "^6.8.0" + eslint "^7.9.0" indent-string "^4.0.0" lodash.merge "^4.6.0" loglevel-colored-level-prefix "^1.0.0" @@ -4940,10 +4789,10 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@2.1.2, prettier@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.2.tgz#3050700dae2e4c8b67c4c3f666cdb8af405e1ce5" - integrity sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg== +prettier@2.2.1, prettier@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" + integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== pretty-error@^2.1.1: version "2.1.1" @@ -4981,17 +4830,6 @@ promise-inflight@^1.0.1: resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= -promise.allsettled@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/promise.allsettled/-/promise.allsettled-1.0.2.tgz#d66f78fbb600e83e863d893e98b3d4376a9c47c9" - integrity sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg== - dependencies: - array.prototype.map "^1.0.1" - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - function-bind "^1.1.1" - iterate-value "^1.0.0" - prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" @@ -5001,11 +4839,6 @@ prop-types@^15.6.2, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= - proxy-addr@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" @@ -5019,11 +4852,6 @@ prr@~1.0.1: resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - psl@^1.1.28: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -5146,21 +4974,21 @@ react-dom@16.13.1: prop-types "^15.6.2" scheduler "^0.19.1" -react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6, react-is@^16.9.0: +react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== react-redux@^7.1.3: - version "7.2.1" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.1.tgz#8dedf784901014db2feca1ab633864dee68ad985" - integrity sha512-T+VfD/bvgGTUA74iW9d2i5THrDQWbweXP0AVNI8tNd1Rk5ch1rnMiJkDD67ejw7YBKM4+REvcvqRuWJb7BLuEg== + version "7.2.2" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.2.tgz#03862e803a30b6b9ef8582dadcc810947f74b736" + integrity sha512-8+CQ1EvIVFkYL/vu6Olo7JFLWop1qRUeb46sGtIMDCSpgwPQq8fPLpirIB0iTqFe9XYEFPHssdX8/UwN6pAkEA== dependencies: - "@babel/runtime" "^7.5.5" - hoist-non-react-statics "^3.3.0" + "@babel/runtime" "^7.12.1" + hoist-non-react-statics "^3.3.2" loose-envify "^1.4.0" prop-types "^15.7.2" - react-is "^16.9.0" + react-is "^16.13.1" react-test-renderer@16.13.1: version "16.13.1" @@ -5172,10 +5000,10 @@ react-test-renderer@16.13.1: react-is "^16.8.6" scheduler "^0.19.1" -react@16.13.1: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" - integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== +react@16.14.0: + version "16.14.0" + resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" + integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -5212,13 +5040,25 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" -readdirp@~3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" - integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== dependencies: picomatch "^2.2.1" +rechoir@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.0.tgz#32650fd52c21ab252aa5d65b19310441c7e03aca" + integrity sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q== + dependencies: + resolve "^1.9.0" + +reduce-flatten@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" + integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== + redux-promise@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/redux-promise/-/redux-promise-0.6.0.tgz#c64723b5213bb5603c11b74302883b682e06b319" @@ -5271,11 +5111,6 @@ regexp.prototype.flags@^1.3.0: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" -regexpp@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" - integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== - regexpp@^3.0.0, regexpp@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" @@ -5359,10 +5194,10 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-from-string@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" - integrity sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg= +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== require-main-filename@^2.0.0: version "2.0.0" @@ -5379,51 +5214,36 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= -resolve-cwd@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" - integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= - dependencies: - resolve-from "^3.0.0" - -resolve-dir@^1.0.0, resolve-dir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" - integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= - dependencies: - expand-tilde "^2.0.0" - global-modules "^1.0.0" - -resolve-from@^3.0.0: +resolve-cwd@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - integrity sha1-six699nWiBvItuZTM17rywoYh0g= + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" - integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== +resolve@^1.18.1, resolve@^1.9.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== dependencies: + is-core-module "^2.1.0" path-parse "^1.0.6" -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -5434,13 +5254,6 @@ rfdc@^1.1.4: resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" integrity sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug== -rimraf@2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - rimraf@^2.5.4, rimraf@^2.6.3, rimraf@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -5463,11 +5276,6 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -run-async@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== - run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" @@ -5475,13 +5283,6 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.6.0: - version "6.6.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2" - integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg== - dependencies: - tslib "^1.9.0" - safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -5521,13 +5322,13 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^2.6.6, schema-utils@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" - integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== +schema-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" + integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== dependencies: - "@types/json-schema" "^7.0.5" - ajv "^6.12.4" + "@types/json-schema" "^7.0.6" + ajv "^6.12.5" ajv-keywords "^3.5.2" selenium-webdriver@^4.0.0-alpha.5: @@ -5539,12 +5340,12 @@ selenium-webdriver@^4.0.0-alpha.5: rimraf "^2.7.1" tmp "0.0.30" -semver@^5.5.0, semver@^5.6.0: +semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0, semver@^6.1.2: +semver@^6.0.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -5573,10 +5374,10 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" -serialize-javascript@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" - integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== +serialize-javascript@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== dependencies: randombytes "^2.1.0" @@ -5647,13 +5448,6 @@ shallowequal@^1.1.0: resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -5661,11 +5455,6 @@ shebang-command@^2.0.0: dependencies: shebang-regex "^3.0.0" -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" @@ -5679,11 +5468,6 @@ side-channel@^1.0.2: es-abstract "^1.17.0-next.1" object-inspect "^1.7.0" -sigmund@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" - integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= - signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" @@ -5712,14 +5496,14 @@ sinon@^7.2.3: supports-color "^5.5.0" sinon@^9.0.3: - version "9.0.3" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.0.3.tgz#bffc3ec28c936332cd2a41833547b9eed201ecff" - integrity sha512-IKo9MIM111+smz9JGwLmw5U1075n1YXeAq8YeSFlndCLhAL5KGn6bLgu7b/4AYHTV/LcEMcRm2wU2YiL55/6Pg== + version "9.2.2" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.2.tgz#b83cf5d43838f99cfa3644453f4c7db23e7bd535" + integrity sha512-9Owi+RisvCZpB0bdOVFfL314I6I4YoRlz6Isi4+fr8q8YQsDPoCe5UnmNtKHRThX3negz2bXHWIuiPa42vM8EQ== dependencies: - "@sinonjs/commons" "^1.7.2" + "@sinonjs/commons" "^1.8.1" "@sinonjs/fake-timers" "^6.0.1" "@sinonjs/formatio" "^5.0.1" - "@sinonjs/samsam" "^5.1.0" + "@sinonjs/samsam" "^5.3.0" diff "^4.0.2" nise "^4.0.4" supports-color "^7.1.0" @@ -5733,6 +5517,15 @@ slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -6051,28 +5844,28 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" -strip-json-comments@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" - integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-json-comments@^3.0.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -style-loader@^1.1.3: - version "1.2.1" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.2.1.tgz#c5cbbfbf1170d076cfdd86e0109c5bba114baa1a" - integrity sha512-ByHSTQvHLkWE9Ir5+lGbVOXhxX10fbprhLvdg96wedFZb4NDekDPxVKv5Fwmio+QcMlkkNfuK+5W1peQ5CUhZg== +style-loader@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c" + integrity sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ== dependencies: loader-utils "^2.0.0" - schema-utils "^2.6.6" + schema-utils "^3.0.0" styled-components@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.1.1.tgz#96dfb02a8025794960863b9e8e365e3b6be5518d" - integrity sha512-1ps8ZAYu2Husx+Vz8D+MvXwEwvMwFv+hqqUwhNlDN5ybg6A+3xyW1ECrAgywhvXapNfXiz79jJyU0x22z0FFTg== + version "5.2.1" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.2.1.tgz#6ed7fad2dc233825f64c719ffbdedd84ad79101a" + integrity sha512-sBdgLWrCFTKtmZm/9x7jkIabjFNVzCUeKfoQsM6R3saImkUnjx0QYdLwJHBjY9ifEcmjDamJDVfknWm1yxZPxQ== dependencies: "@babel/helper-module-imports" "^7.0.0" "@babel/traverse" "^7.4.5" @@ -6085,10 +5878,10 @@ styled-components@^5.1.1: shallowequal "^1.1.0" supports-color "^5.5.0" -supports-color@7.1.0, supports-color@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" - integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== +supports-color@7.2.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" @@ -6104,18 +5897,21 @@ supports-color@^5.3.0, supports-color@^5.5.0: dependencies: has-flag "^3.0.0" -supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - symbol-observable@^1.0.3, symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== +table-layout@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.1.tgz#8411181ee951278ad0638aea2f779a9ce42894f9" + integrity sha512-dEquqYNJiGwY7iPfZ3wbXDI944iqanTSchrACLL2nOB+1r+h1Nzu2eH+DuPPvWvm5Ry7iAPeFlgEtP5bIp5U7Q== + dependencies: + array-back "^4.0.1" + deep-extend "~0.6.0" + typical "^5.2.0" + wordwrapjs "^4.0.0" + table@^5.2.3: version "5.4.6" resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" @@ -6126,6 +5922,16 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" +table@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/table/-/table-6.0.4.tgz#c523dd182177e926c723eb20e1b341238188aa0d" + integrity sha512-sBT4xRLdALd+NFBvwOz8bw4b15htyythha+q+DVZqy2RS08PPC8O2sZFgJYEY7bJvbCFKccs+WIZ/cd+xxTWCw== + dependencies: + ajv "^6.12.4" + lodash "^4.17.20" + slice-ansi "^4.0.0" + string-width "^4.2.0" + tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" @@ -6168,11 +5974,6 @@ through2@^2.0.0: readable-stream "~2.3.6" xtend "~4.0.1" -through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - timers-browserify@^2.0.4: version "2.0.11" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f" @@ -6194,13 +5995,6 @@ tmp@0.2.1: dependencies: rimraf "^3.0.0" -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - to-array@0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" @@ -6262,9 +6056,9 @@ tough-cookie@^2.3.3, tough-cookie@~2.5.0: punycode "^2.1.1" ts-loader@^8.0.2: - version "8.0.4" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.4.tgz#02b9c91fbcfdb3114d8b1e98a3829265270eee7a" - integrity sha512-5u8KF1SW8eCUb/Ff7At81e3wznPmT/27fvaGRO9CziVy+6NlPVRvrzSox4OwU0/e6OflOUB32Err4VquysCSAQ== + version "8.0.12" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.12.tgz#1de9f1de65176318c1e6d187bfc496182f8dc2a0" + integrity sha512-UIivVfGVJDdwwjgSrbtcL9Nf10c1BWnL1mxAQUVcnhNIn/P9W3nP5v60Z0aBMtc7ZrE11lMmU6+5jSgAXmGaYw== dependencies: chalk "^2.3.0" enhanced-resolve "^4.0.0" @@ -6273,11 +6067,12 @@ ts-loader@^8.0.2: semver "^6.0.0" ts-node@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.0.0.tgz#e7699d2a110cc8c0d3b831715e417688683460b3" - integrity sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg== + version "9.1.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" + integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== dependencies: arg "^4.1.0" + create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" source-map-support "^0.5.17" @@ -6295,10 +6090,10 @@ tsutils@^3.17.1: dependencies: tslib "^1.8.1" -tsyringe@4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.3.0.tgz#50ce2ec56c83569a2dfafb7233a5265a2a10bde4" - integrity sha512-Vzty1M/EQXSsEE8aoIOLl1l793chcPyQAnFJaS1mFDtFvNPY+jknSPwMIF6yfcjua+2GTgwxWFzuA3cjIx4NZA== +tsyringe@4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/tsyringe/-/tsyringe-4.4.0.tgz#cd8a6866316b4cf643ee5e72c41c8a221ccea73d" + integrity sha512-SlMApe1lhIq546CDp7bF+IdF4RB6d+9C5T7B0AS0P/Bm+Qpizj/gEmZzvw9J/KlXPEt4qHTbi1TRvX3rCPSdTg== dependencies: tslib "^1.9.3" @@ -6326,23 +6121,11 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" - integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== - type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" @@ -6371,6 +6154,11 @@ typescript@^3.9.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== +typical@^5.0.0, typical@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" + integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== + ua-parser-js@0.7.22: version "0.7.22" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.22.tgz#960df60a5f911ea8f1c818f3747b99c6e177eae3" @@ -6458,7 +6246,7 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -util-deprecate@^1.0.1, util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -6500,10 +6288,10 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -v8-compile-cache@^2.0.3, v8-compile-cache@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" - integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== +v8-compile-cache@^2.0.3, v8-compile-cache@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" + integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q== vary@~1.1.2: version "1.1.2" @@ -6571,22 +6359,24 @@ webextensions-api-fake@^0.9.1: dependencies: sinon-chrome "^3.0.0" -webpack-cli@3.3.12: - version "3.3.12" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a" - integrity sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag== - dependencies: - chalk "^2.4.2" - cross-spawn "^6.0.5" - enhanced-resolve "^4.1.1" - findup-sync "^3.0.0" - global-modules "^2.0.0" - import-local "^2.0.0" - interpret "^1.4.0" - loader-utils "^1.4.0" - supports-color "^6.1.0" - v8-compile-cache "^2.1.1" - yargs "^13.3.2" +webpack-cli@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.2.0.tgz#10a09030ad2bd4d8b0f78322fba6ea43ec56aaaa" + integrity sha512-EIl3k88vaF4fSxWSgtAQR+VwicfLMTZ9amQtqS4o+TDPW9HGaEpbFBbAZ4A3ZOT5SOnMxNOzROsSTPiE8tBJPA== + dependencies: + "@webpack-cli/info" "^1.1.0" + "@webpack-cli/serve" "^1.1.0" + colorette "^1.2.1" + command-line-usage "^6.1.0" + commander "^6.2.0" + enquirer "^2.3.6" + execa "^4.1.0" + import-local "^3.0.2" + interpret "^2.2.0" + leven "^3.1.0" + rechoir "^0.7.0" + v8-compile-cache "^2.2.0" + webpack-merge "^4.2.2" webpack-dev-middleware@^3.7.0: version "3.7.2" @@ -6607,6 +6397,13 @@ webpack-log@^2.0.0: ansi-colors "^3.0.0" uuid "^3.3.2" +webpack-merge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + webpack-sources@^1.4.0, webpack-sources@^1.4.1: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" @@ -6615,10 +6412,10 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1: source-list-map "^2.0.0" source-map "~0.6.1" -webpack@4.44.1: - version "4.44.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.1.tgz#17e69fff9f321b8f117d1fda714edfc0b939cc21" - integrity sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ== +webpack@4.44.2: + version "4.44.2" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.2.tgz#6bfe2b0af055c8b2d1e90ed2cd9363f841266b72" + integrity sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-module-context" "1.9.0" @@ -6656,13 +6453,6 @@ which@2.0.2, which@^2.0.1: dependencies: isexe "^2.0.0" -which@^1.2.14, which@^1.2.9, which@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - wide-align@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" @@ -6670,11 +6460,19 @@ wide-align@1.1.3: dependencies: string-width "^1.0.2 || 2" -word-wrap@^1.2.3, word-wrap@~1.2.3: +word-wrap@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +wordwrapjs@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.0.tgz#9aa9394155993476e831ba8e59fb5795ebde6800" + integrity sha512-Svqw723a3R34KvsMgpjFBYCgNOSdcW3mQFK4wIfhGQhtaFVOJmdYoXgi63ne3dTlWgatVcUc7t4HtQ/+bUVIzQ== + dependencies: + reduce-flatten "^2.0.0" + typical "^5.0.0" + worker-farm@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" @@ -6682,10 +6480,10 @@ worker-farm@^1.7.0: dependencies: errno "~0.1.7" -workerpool@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.0.tgz#85aad67fa1a2c8ef9386a1b43539900f61d03d58" - integrity sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA== +workerpool@6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.2.tgz#e241b43d8d033f1beb52c7851069456039d1d438" + integrity sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q== wrap-ansi@^5.1.0: version "5.1.0" @@ -6710,13 +6508,6 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" - ws@^7.1.2: version "7.3.1" resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" @@ -6744,11 +6535,6 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" @@ -6762,14 +6548,6 @@ yargs-parser@13.1.2, yargs-parser@^13.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^15.0.1: - version "15.0.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.1.tgz#54786af40b820dcb2fb8025b11b4d659d76323b3" - integrity sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" @@ -6778,18 +6556,17 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-unparser@1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.1.tgz#bd4b0ee05b4c94d058929c32cb09e3fce71d3c5f" - integrity sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA== +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== dependencies: - camelcase "^5.3.1" - decamelize "^1.2.0" - flat "^4.1.0" - is-plain-obj "^1.1.0" - yargs "^14.2.3" + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" -yargs@13.3.2, yargs@^13.3.2: +yargs@13.3.2: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== @@ -6805,23 +6582,6 @@ yargs@13.3.2, yargs@^13.3.2: y18n "^4.0.0" yargs-parser "^13.1.2" -yargs@^14.2.3: - version "14.2.3" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414" - integrity sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg== - dependencies: - cliui "^5.0.0" - decamelize "^1.2.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^15.0.1" - yargs@^15.3.1: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" |