diff options
Diffstat (limited to 'test/background')
48 files changed, 1583 insertions, 183 deletions
| 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(); -    }); -  }); -}); | 
