aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorShin'ya Ueoka <ueokande@i-beam.org>2020-12-03 23:15:24 +0900
committerShin'ya Ueoka <ueokande@i-beam.org>2020-12-09 23:09:34 +0900
commit8bbc3d9d159237f280c3e952cd1cb124601ed6d6 (patch)
tree4e2d9c23d0a25209fdd8ba9dd6849ce66df8d2df /test
parent7305bc1c34ceced31db943c8f23ed826d3d522f0 (diff)
Add operator tests
Diffstat (limited to 'test')
-rw-r--r--test/background/mock/MockBrowserSettingRepository.ts10
-rw-r--r--test/background/mock/MockConsoleClient.ts23
-rw-r--r--test/background/mock/MockNavigateClient.ts19
-rw-r--r--test/background/mock/MockRepeatRepository.ts14
-rw-r--r--test/background/mock/MockTabPresenter.ts179
-rw-r--r--test/background/mock/MockWindowPresenter.ts7
-rw-r--r--test/background/mock/MockZoomPresenter.ts15
-rw-r--r--test/background/operators/impls/CancelOperator.test.ts24
-rw-r--r--test/background/operators/impls/CloseTabOperator.test.ts61
-rw-r--r--test/background/operators/impls/CloseTabRightOperator.test.ts24
-rw-r--r--test/background/operators/impls/CommandOperatorFactoryChain.test.ts42
-rw-r--r--test/background/operators/impls/DuplicateTabOperator.test.ts25
-rw-r--r--test/background/operators/impls/InternalOpenURLOperator.test.ts0
-rw-r--r--test/background/operators/impls/InternalOperatorFactoryChain.test.ts37
-rw-r--r--test/background/operators/impls/NavigateHistoryNextOperator.test.ts25
-rw-r--r--test/background/operators/impls/NavigateHistoryPrevOperator.test.ts25
-rw-r--r--test/background/operators/impls/NavigateLinkNextOperator.test.ts22
-rw-r--r--test/background/operators/impls/NavigateLinkPrevOperator.test.ts22
-rw-r--r--test/background/operators/impls/NavigateOperatorFactoryChain.test.ts56
-rw-r--r--test/background/operators/impls/NavigateParentOperator.test.ts53
-rw-r--r--test/background/operators/impls/NavigateRootOperator.test.ts18
-rw-r--r--test/background/operators/impls/OpenHomeOperator.test.ts70
-rw-r--r--test/background/operators/impls/OpenSourceOperator.test.ts21
-rw-r--r--test/background/operators/impls/PinTabOperator.test.ts25
-rw-r--r--test/background/operators/impls/ReloadTabOperator.test.ts34
-rw-r--r--test/background/operators/impls/ReopenTabOperator.test.ts17
-rw-r--r--test/background/operators/impls/RepeatLastOperator.test.ts56
-rw-r--r--test/background/operators/impls/RepeatOperatorFactoryChain.test.ts32
-rw-r--r--test/background/operators/impls/ResetZoomOperator.test.ts17
-rw-r--r--test/background/operators/impls/SelectFirstTabOperator.test.ts20
-rw-r--r--test/background/operators/impls/SelectLastTabOperator.test.ts20
-rw-r--r--test/background/operators/impls/SelectPreviousSelectedTabOperator.test.ts38
-rw-r--r--test/background/operators/impls/SelectTabNextOperator.test.ts35
-rw-r--r--test/background/operators/impls/SelectTabPrevOperator.test.ts35
-rw-r--r--test/background/operators/impls/ShowAddBookmarkOperator.test.ts50
-rw-r--r--test/background/operators/impls/ShowBufferCommandOperator.test.ts25
-rw-r--r--test/background/operators/impls/ShowCommandOperator.test.ts25
-rw-r--r--test/background/operators/impls/ShowOpenCommandOperator.test.ts50
-rw-r--r--test/background/operators/impls/ShowTabOpenCommandOperator.test.ts50
-rw-r--r--test/background/operators/impls/ShowWinOpenCommandOperator.test.ts50
-rw-r--r--test/background/operators/impls/StartFindOperator.test.ts22
-rw-r--r--test/background/operators/impls/TabOperatorFactoryChain.test.ts71
-rw-r--r--test/background/operators/impls/TogglePinnedTabOperator.test.ts32
-rw-r--r--test/background/operators/impls/UnpinTabOperator.test.ts25
-rw-r--r--test/background/operators/impls/ZoomInOperator.test.ts17
-rw-r--r--test/background/operators/impls/ZoomOperatorFactoryChain.test.ts28
-rw-r--r--test/background/operators/impls/ZoomOutOperator.test.ts17
47 files changed, 1583 insertions, 0 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..a1ca100
--- /dev/null
+++ b/test/background/mock/MockZoomPresenter.ts
@@ -0,0 +1,15 @@
+import ZoomPresenter from "../../../src/background/usecases/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();
+ });
+ });
+});