aboutsummaryrefslogtreecommitdiff
path: root/test/background
diff options
context:
space:
mode:
authorShin'ya Ueoka <ueokande@i-beam.org>2021-06-14 23:14:51 +0900
committerShin'ya Ueoka <ueokande@i-beam.org>2021-07-05 21:32:43 +0900
commit65cf6f0842d8d5933dc13b3767b1baf398d68cd5 (patch)
treedf9a8b139fd98adb79f075ba655d1303bdf3fd1d /test/background
parentcaced372415a944c4297157397d0027ba629fff0 (diff)
Implement FindNextOperator
Diffstat (limited to 'test/background')
-rw-r--r--test/background/mock/MockFindClient.ts24
-rw-r--r--test/background/mock/MockFindRepository.ts26
-rw-r--r--test/background/operators/impls/FindNextOperator.test.ts90
-rw-r--r--test/background/operators/impls/FindOperatorFactoryChain.ts23
-rw-r--r--test/background/operators/impls/FindPrevOperator.test.ts90
-rw-r--r--test/background/repositories/FindRepository.test.ts37
-rw-r--r--test/background/usecases/FindUseCase.test.ts121
7 files changed, 411 insertions, 0 deletions
diff --git a/test/background/mock/MockFindClient.ts b/test/background/mock/MockFindClient.ts
new file mode 100644
index 0000000..bd25a27
--- /dev/null
+++ b/test/background/mock/MockFindClient.ts
@@ -0,0 +1,24 @@
+import FindClient, {
+ FindResult,
+} from "../../../src/background/clients/FindClient";
+
+export default class MockFindClient implements FindClient {
+ highlightAll(): Promise<void> {
+ throw new Error("not implemented");
+ }
+
+ removeHighlights(): Promise<void> {
+ throw new Error("not implemented");
+ }
+
+ selectKeyword(
+ _tabId: number,
+ _rangeData: browser.find.RangeData
+ ): Promise<void> {
+ throw new Error("not implemented");
+ }
+
+ startFind(_keyword: string): Promise<FindResult> {
+ throw new Error("not implemented");
+ }
+}
diff --git a/test/background/mock/MockFindRepository.ts b/test/background/mock/MockFindRepository.ts
new file mode 100644
index 0000000..af552c8
--- /dev/null
+++ b/test/background/mock/MockFindRepository.ts
@@ -0,0 +1,26 @@
+import FindRepository, {
+ FindState,
+} from "../../../src/background/repositories/FindRepository";
+
+export default class MockFindRepository implements FindRepository {
+ private globalKeyword: string | undefined;
+ private localStates: { [tabId: number]: FindState } = {};
+
+ getGlobalKeyword(): Promise<string | undefined> {
+ return Promise.resolve(this.globalKeyword);
+ }
+
+ setGlobalKeyword(keyword: string): Promise<void> {
+ this.globalKeyword = keyword;
+ return Promise.resolve();
+ }
+
+ getLocalState(tabId: number): Promise<FindState | undefined> {
+ return Promise.resolve(this.localStates[tabId]);
+ }
+
+ setLocalState(tabId: number, state: FindState): Promise<void> {
+ this.localStates[tabId] = state;
+ return Promise.resolve();
+ }
+}
diff --git a/test/background/operators/impls/FindNextOperator.test.ts b/test/background/operators/impls/FindNextOperator.test.ts
new file mode 100644
index 0000000..20208ae
--- /dev/null
+++ b/test/background/operators/impls/FindNextOperator.test.ts
@@ -0,0 +1,90 @@
+import sinon from "sinon";
+import MockTabPresenter from "../../mock/MockTabPresenter";
+import FindNextOperator from "../../../../src/background/operators/impls/FindNextOperator";
+import { FindState } from "../../../../src/background/repositories/FindRepository";
+import MockFindRepository from "../../mock/MockFindRepository";
+import MockFindClient from "../../mock/MockFindClient";
+
+describe("FindNextOperator", () => {
+ describe("#run", () => {
+ it("throws an error on no previous keywords", async () => {
+ const tabPresenter = new MockTabPresenter();
+ const findRepository = new MockFindRepository();
+ const findClient = new MockFindClient();
+ await tabPresenter.create("https://example.com/");
+
+ const sut = new FindNextOperator(
+ tabPresenter,
+ findRepository,
+ findClient
+ );
+ try {
+ await sut.run();
+ } catch (e) {
+ return;
+ }
+ throw new Error("unexpected reach");
+ });
+
+ it("select a next next", async () => {
+ const tabPresenter = new MockTabPresenter();
+ const findRepository = new MockFindRepository();
+ const findClient = new MockFindClient();
+ const currentTab = await tabPresenter.create("https://example.com/");
+
+ const state: FindState = {
+ keyword: "Hello, world",
+ rangeData: [
+ {
+ framePos: 0,
+ startOffset: 0,
+ endOffset: 10,
+ startTextNodePos: 0,
+ endTextNodePos: 0,
+ text: "Hello, world",
+ },
+ {
+ framePos: 1,
+ startOffset: 0,
+ endOffset: 10,
+ startTextNodePos: 1,
+ endTextNodePos: 1,
+ text: "Hello, world",
+ },
+ {
+ framePos: 2,
+ startOffset: 2,
+ endOffset: 10,
+ startTextNodePos: 1,
+ endTextNodePos: 1,
+ text: "Hello, world",
+ },
+ ],
+ highlightPosition: 0,
+ };
+
+ await findRepository.setLocalState(currentTab.id!, state);
+ const mock = sinon.mock(findClient);
+ mock
+ .expects("selectKeyword")
+ .withArgs(currentTab?.id, state.rangeData[1]);
+ mock
+ .expects("selectKeyword")
+ .withArgs(currentTab?.id, state.rangeData[2]);
+ mock
+ .expects("selectKeyword")
+ .withArgs(currentTab?.id, state.rangeData[0]);
+ const sut = new FindNextOperator(
+ tabPresenter,
+ findRepository,
+ findClient
+ );
+
+ await sut.run();
+ await sut.run();
+ await sut.run();
+
+ mock.verify();
+ });
+ });
+});
diff --git a/test/background/operators/impls/FindOperatorFactoryChain.ts b/test/background/operators/impls/FindOperatorFactoryChain.ts
new file mode 100644
index 0000000..0fd234f
--- /dev/null
+++ b/test/background/operators/impls/FindOperatorFactoryChain.ts
@@ -0,0 +1,23 @@
+import "reflect-metadata";
+import { expect } from "chai";
+import TabOperatorFactoryChain from "../../../../src/background/operators/impls/TabOperatorFactoryChain";
+import MockTabPresenter from "../../mock/MockTabPresenter";
+import * as operations from "../../../../src/shared/operations";
+import FindNextOperator from "../../../../src/background/operators/impls/FindNextOperator";
+import FindPrevOperator from "../../../../src/background/operators/impls/FindPrevOperator";
+
+describe("FindOperatorFactoryChain", () => {
+ describe("#create", () => {
+ it("returns a operator for the operation", async () => {
+ const tabPresenter = new MockTabPresenter();
+ const sut = new TabOperatorFactoryChain(tabPresenter);
+
+ expect(sut.create({ type: operations.FIND_NEXT })).to.be.instanceOf(
+ FindNextOperator
+ );
+ expect(sut.create({ type: operations.FIND_PREV })).to.be.instanceOf(
+ FindPrevOperator
+ );
+ });
+ });
+});
diff --git a/test/background/operators/impls/FindPrevOperator.test.ts b/test/background/operators/impls/FindPrevOperator.test.ts
new file mode 100644
index 0000000..409c26d
--- /dev/null
+++ b/test/background/operators/impls/FindPrevOperator.test.ts
@@ -0,0 +1,90 @@
+import sinon from "sinon";
+import MockTabPresenter from "../../mock/MockTabPresenter";
+import FindNextOperator from "../../../../src/background/operators/impls/FindNextOperator";
+import { FindState } from "../../../../src/background/repositories/FindRepository";
+import MockFindRepository from "../../mock/MockFindRepository";
+import MockFindClient from "../../mock/MockFindClient";
+
+describe("FindPrevOperator", () => {
+ describe("#run", () => {
+ it("throws an error on no previous keywords", async () => {
+ const tabPresenter = new MockTabPresenter();
+ const findRepository = new MockFindRepository();
+ const findClient = new MockFindClient();
+ await tabPresenter.create("https://example.com/");
+
+ const sut = new FindNextOperator(
+ tabPresenter,
+ findRepository,
+ findClient
+ );
+ try {
+ await sut.run();
+ } catch (e) {
+ return;
+ }
+ throw new Error("unexpected reach");
+ });
+
+ it("select a next next", async () => {
+ const tabPresenter = new MockTabPresenter();
+ const findRepository = new MockFindRepository();
+ const findClient = new MockFindClient();
+ const currentTab = await tabPresenter.create("https://example.com/");
+
+ const state: FindState = {
+ keyword: "Hello, world",
+ rangeData: [
+ {
+ framePos: 0,
+ startOffset: 0,
+ endOffset: 10,
+ startTextNodePos: 0,
+ endTextNodePos: 0,
+ text: "Hello, world",
+ },
+ {
+ framePos: 1,
+ startOffset: 0,
+ endOffset: 10,
+ startTextNodePos: 1,
+ endTextNodePos: 1,
+ text: "Hello, world",
+ },
+ {
+ framePos: 2,
+ startOffset: 2,
+ endOffset: 10,
+ startTextNodePos: 1,
+ endTextNodePos: 1,
+ text: "Hello, world",
+ },
+ ],
+ highlightPosition: 1,
+ };
+
+ await findRepository.setLocalState(currentTab.id!, state);
+ const mock = sinon.mock(findClient);
+ mock
+ .expects("selectKeyword")
+ .withArgs(currentTab?.id, state.rangeData[0]);
+ mock
+ .expects("selectKeyword")
+ .withArgs(currentTab?.id, state.rangeData[2]);
+ mock
+ .expects("selectKeyword")
+ .withArgs(currentTab?.id, state.rangeData[1]);
+ const sut = new FindNextOperator(
+ tabPresenter,
+ findRepository,
+ findClient
+ );
+
+ await sut.run();
+ await sut.run();
+ await sut.run();
+
+ mock.verify();
+ });
+ });
+});
diff --git a/test/background/repositories/FindRepository.test.ts b/test/background/repositories/FindRepository.test.ts
new file mode 100644
index 0000000..ecb0fed
--- /dev/null
+++ b/test/background/repositories/FindRepository.test.ts
@@ -0,0 +1,37 @@
+import { expect } from "chai";
+import { FindRepositoryImpl } from "../../../src/background/repositories/FindRepository";
+
+describe("background/repositories/FindRepositoryImpl", () => {
+ let sut: FindRepositoryImpl;
+
+ beforeEach(() => {
+ sut = new FindRepositoryImpl();
+ });
+
+ describe("global keyword", () => {
+ it("get and set a keyword", async () => {
+ expect(await sut.getGlobalKeyword()).to.be.undefined;
+
+ await sut.setGlobalKeyword("Hello, world");
+
+ const keyword = await sut.getGlobalKeyword();
+ expect(keyword).to.equal("Hello, world");
+ });
+ });
+
+ describe("local state", () => {
+ it("get and set a keyword", async () => {
+ expect(await sut.getLocalState(10)).to.be.undefined;
+
+ await sut.setLocalState(10, {
+ keyword: "Hello, world",
+ frameId: 20,
+ });
+
+ const state = await sut.getLocalState(10);
+ expect(state?.keyword).to.equal("Hello, world");
+
+ expect(await sut.getLocalState(20)).to.be.undefined;
+ });
+ });
+});
diff --git a/test/background/usecases/FindUseCase.test.ts b/test/background/usecases/FindUseCase.test.ts
new file mode 100644
index 0000000..eef211b
--- /dev/null
+++ b/test/background/usecases/FindUseCase.test.ts
@@ -0,0 +1,121 @@
+import "reflect-metadata";
+import sinon from "sinon";
+import FindClient from "../../../src/background/clients/FindClient";
+import StartFindUseCase from "../../../src/background/usecases/StartFindUseCase";
+import FindRepository from "../../../src/background/repositories/FindRepository";
+import { expect } from "chai";
+import MockFindClient from "../mock/MockFindClient";
+import MockFindRepository from "../mock/MockFindRepository";
+
+describe("FindUseCase", () => {
+ let findClient: FindClient;
+ let findRepository: FindRepository;
+ let sut: StartFindUseCase;
+
+ const rangeData = (count: number): browser.find.RangeData[] => {
+ const data = {
+ text: "Hello, world",
+ framePos: 0,
+ startTextNodePos: 0,
+ endTextNodePos: 0,
+ startOffset: 0,
+ endOffset: 0,
+ };
+ return Array(count).fill(data);
+ };
+
+ beforeEach(() => {
+ findClient = new MockFindClient();
+ findRepository = new MockFindRepository();
+ sut = new StartFindUseCase(findClient, findRepository);
+ });
+
+ describe("startFind", function () {
+ context("with a search keyword", () => {
+ it("starts find and store last used keyword", async () => {
+ const startFind = sinon
+ .stub(findClient, "startFind")
+ .returns(Promise.resolve({ count: 10, rangeData: rangeData(10) }));
+ const highlightAll = sinon
+ .mock(findClient)
+ .expects("highlightAll")
+ .once();
+ const selectKeyword = sinon
+ .mock(findClient)
+ .expects("selectKeyword")
+ .once();
+
+ await sut.startFind(10, "Hello, world");
+
+ expect(startFind.calledWith("Hello, world")).to.be.true;
+ expect(await findRepository.getGlobalKeyword()).to.equals(
+ "Hello, world"
+ );
+ expect((await findRepository.getLocalState(10))?.keyword).to.equal(
+ "Hello, world"
+ );
+ highlightAll.verify();
+ selectKeyword.verify();
+ });
+
+ it("throws an error if no matched", (done) => {
+ sinon
+ .stub(findClient, "startFind")
+ .returns(Promise.resolve({ count: 0, rangeData: [] }));
+
+ sut.startFind(10, "Hello, world").catch((e) => {
+ expect(e).instanceof(Error);
+ done();
+ });
+ });
+ });
+
+ context("without a search keyword", () => {
+ it("starts find with last used keyword in the tab", async () => {
+ const startFind = sinon
+ .stub(findClient, "startFind")
+ .returns(Promise.resolve({ count: 10, rangeData: rangeData(10) }));
+ await findRepository.setLocalState(10, {
+ keyword: "Hello, world",
+ rangeData: rangeData(10),
+ highlightPosition: 0,
+ });
+ const highlightAll = sinon
+ .mock(findClient)
+ .expects("highlightAll")
+ .once();
+ const selectKeyword = sinon
+ .mock(findClient)
+ .expects("selectKeyword")
+ .once();
+
+ await sut.startFind(10, undefined);
+
+ expect(startFind.calledWith("Hello, world")).to.be.true;
+ highlightAll.verify();
+ selectKeyword.verify();
+ });
+
+ it("starts find with last used keyword in global", async () => {
+ const startFind = sinon
+ .stub(findClient, "startFind")
+ .returns(Promise.resolve({ count: 10, rangeData: rangeData(10) }));
+ await findRepository.setGlobalKeyword("Hello, world");
+ const highlightAll = sinon
+ .mock(findClient)
+ .expects("highlightAll")
+ .once();
+ const selectKeyword = sinon
+ .mock(findClient)
+ .expects("selectKeyword")
+ .once();
+
+ await sut.startFind(10, undefined);
+
+ expect(startFind.calledWith("Hello, world")).to.be.true;
+ highlightAll.verify();
+ selectKeyword.verify();
+ });
+ });
+ });
+});