diff options
author | Shin'ya Ueoka <ueokande@i-beam.org> | 2021-06-14 23:14:51 +0900 |
---|---|---|
committer | Shin'ya Ueoka <ueokande@i-beam.org> | 2021-07-05 21:32:43 +0900 |
commit | 65cf6f0842d8d5933dc13b3767b1baf398d68cd5 (patch) | |
tree | df9a8b139fd98adb79f075ba655d1303bdf3fd1d /test/background | |
parent | caced372415a944c4297157397d0027ba629fff0 (diff) |
Implement FindNextOperator
Diffstat (limited to 'test/background')
-rw-r--r-- | test/background/mock/MockFindClient.ts | 24 | ||||
-rw-r--r-- | test/background/mock/MockFindRepository.ts | 26 | ||||
-rw-r--r-- | test/background/operators/impls/FindNextOperator.test.ts | 90 | ||||
-rw-r--r-- | test/background/operators/impls/FindOperatorFactoryChain.ts | 23 | ||||
-rw-r--r-- | test/background/operators/impls/FindPrevOperator.test.ts | 90 | ||||
-rw-r--r-- | test/background/repositories/FindRepository.test.ts | 37 | ||||
-rw-r--r-- | test/background/usecases/FindUseCase.test.ts | 121 |
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(); + }); + }); + }); +}); |