From ea63c5f78b4c985e9d6dd106afe4f97bfeedbcd0 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sat, 28 Mar 2020 21:35:06 +0900 Subject: Complete tabs by the completion packages --- .../completion/TabCompletionUseCase.test.ts | 105 +++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 test/background/completion/TabCompletionUseCase.test.ts (limited to 'test/background/completion/TabCompletionUseCase.test.ts') diff --git a/test/background/completion/TabCompletionUseCase.test.ts b/test/background/completion/TabCompletionUseCase.test.ts new file mode 100644 index 0000000..e2c7c19 --- /dev/null +++ b/test/background/completion/TabCompletionUseCase.test.ts @@ -0,0 +1,105 @@ +import "reflect-metadata" +import TabRepositoryImpl from "../../../src/background/completion/impl/TabRepositoryImpl"; +import {Tab} from "../../../src/background/completion/TabRepository"; +import TabPresenter from "../../../src/background/presenters/TabPresenter"; +import TabCompletionUseCase from "../../../src/background/completion/TabCompletionUseCase"; +import sinon from 'sinon'; +import { expect } from 'chai'; +import TabFlag from "../../../src/shared/TabFlag"; + +class MockTabRepository implements TabRepositoryImpl { + async queryTabs(_query: string, _excludePinned: boolean): Promise { + throw new Error("not implemented") + } +} + +class MockTabPresenter implements TabPresenter { + create(_url: string, _opts?: object): Promise { + throw new Error("not implemented") + } + + duplicate(_id: number): Promise { + throw new Error("not implemented") + } + + getAll(): Promise { + throw new Error("not implemented") + } + + getByKeyword(_keyword: string, _excludePinned: boolean): Promise { + throw new Error("not implemented") + } + + getCurrent(): Promise { + throw new Error("not implemented") + } + + getLastSelectedId(): Promise { + throw new Error("not implemented") + } + + getZoom(_tabId: number): Promise { + throw new Error("not implemented") + } + + onSelected(_listener: (arg: { tabId: number; windowId: number }) => void): void { + throw new Error("not implemented") + } + + open(_url: string, _tabId?: number): Promise { + throw new Error("not implemented") + } + + reload(_tabId: number, _cache: boolean): Promise { + throw new Error("not implemented") + } + + remove(_ids: number[]): Promise { + throw new Error("not implemented") + } + + reopen(): Promise { + throw new Error("not implemented") + } + + select(_tabId: number): Promise { + throw new Error("not implemented") + } + + setPinned(_tabId: number, _pinned: boolean): Promise { + throw new Error("not implemented") + } + + setZoom(_tabId: number, _factor: number): Promise { + throw new Error("not implemented") + } +} + +describe('TabCompletionUseCase', () => { + let tabRepository: MockTabRepository; + let tabPresenter: TabPresenter; + let sut: TabCompletionUseCase; + + beforeEach(() => { + tabRepository = new MockTabRepository(); + tabPresenter = new MockTabPresenter(); + sut = new TabCompletionUseCase(tabRepository, tabPresenter) + }); + + describe('#queryTabs', () => { + it("returns tab items", async () => { + sinon.stub(tabRepository, 'queryTabs').returns(Promise.resolve([ + { id: 10, index: 0, title: 'Google', url: 'https://google.com/', faviconUrl: 'https://google.com/favicon.ico', active: true }, + { id: 11, index: 1, title: 'Yahoo', url: 'https://yahoo.com/', faviconUrl: 'https://yahoo.com/favicon.ico', active: false }, + { id: 12, index: 2, title: 'Bing', url: 'https://bing.com/', active: false }, + ])); + sinon.stub(tabPresenter, 'getLastSelectedId').returns(Promise.resolve(11)); + + expect(await sut.queryTabs("", false)).to.deep.equal([ + { index: 0, title: 'Google', url: 'https://google.com/', faviconUrl: 'https://google.com/favicon.ico', flag: TabFlag.CurrentTab }, + { index: 1, title: 'Yahoo', url: 'https://yahoo.com/', faviconUrl: 'https://yahoo.com/favicon.ico', flag: TabFlag.LastTab }, + { index: 2, title: 'Bing', url: 'https://bing.com/', faviconUrl: undefined, flag: TabFlag.None }, + ]); + }) + }); +}); \ No newline at end of file -- cgit v1.2.3 From 1368fa2fc0f22d1ec4763c77c2ae983cf5037e92 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sun, 29 Mar 2020 10:58:10 +0900 Subject: Fix tests with TabPresenter --- .../completion/TabCompletionUseCase.test.ts | 6 +- test/background/usecases/NavigateUseCase.test.ts | 127 +++++++++++++++++---- 2 files changed, 106 insertions(+), 27 deletions(-) (limited to 'test/background/completion/TabCompletionUseCase.test.ts') diff --git a/test/background/completion/TabCompletionUseCase.test.ts b/test/background/completion/TabCompletionUseCase.test.ts index e2c7c19..254bfe6 100644 --- a/test/background/completion/TabCompletionUseCase.test.ts +++ b/test/background/completion/TabCompletionUseCase.test.ts @@ -96,9 +96,9 @@ describe('TabCompletionUseCase', () => { sinon.stub(tabPresenter, 'getLastSelectedId').returns(Promise.resolve(11)); expect(await sut.queryTabs("", false)).to.deep.equal([ - { index: 0, title: 'Google', url: 'https://google.com/', faviconUrl: 'https://google.com/favicon.ico', flag: TabFlag.CurrentTab }, - { index: 1, title: 'Yahoo', url: 'https://yahoo.com/', faviconUrl: 'https://yahoo.com/favicon.ico', flag: TabFlag.LastTab }, - { index: 2, title: 'Bing', url: 'https://bing.com/', faviconUrl: undefined, flag: TabFlag.None }, + { index: 1, title: 'Google', url: 'https://google.com/', faviconUrl: 'https://google.com/favicon.ico', flag: TabFlag.CurrentTab }, + { index: 2, title: 'Yahoo', url: 'https://yahoo.com/', faviconUrl: 'https://yahoo.com/favicon.ico', flag: TabFlag.LastTab }, + { index: 3, title: 'Bing', url: 'https://bing.com/', faviconUrl: undefined, flag: TabFlag.None }, ]); }) }); diff --git a/test/background/usecases/NavigateUseCase.test.ts b/test/background/usecases/NavigateUseCase.test.ts index 48a1c5b..7ad0e4f 100644 --- a/test/background/usecases/NavigateUseCase.test.ts +++ b/test/background/usecases/NavigateUseCase.test.ts @@ -1,26 +1,107 @@ +import "reflect-metadata"; import TabPresenter from '../../../src/background/presenters/TabPresenter'; import NavigateUseCase from '../../../src/background/usecases/NavigateUseCase'; import NavigateClient from '../../../src/background/clients/NavigateClient'; -// import { expect } from 'chai'; import * as sinon from 'sinon'; +class MockTabPresenter implements TabPresenter { + create(_url: string, _opts?: object): Promise { + throw new Error("not implemented"); + } + + duplicate(_id: number): Promise { + throw new Error("not implemented"); + } + + getAll(): Promise { + throw new Error("not implemented"); + } + + getByKeyword(_keyword: string, _excludePinned: boolean): Promise { + throw new Error("not implemented"); + } + + getCurrent(): Promise { + throw new Error("not implemented"); + } + + getLastSelectedId(): Promise { + throw new Error("not implemented"); + } + + getZoom(_tabId: number): Promise { + throw new Error("not implemented"); + } + + onSelected(_listener: (arg: { tabId: number; windowId: number }) => void): void { + throw new Error("not implemented"); + } + + open(_url: string, _tabId?: number): Promise { + throw new Error("not implemented"); + } + + reload(_tabId: number, _cache: boolean): Promise { + throw new Error("not implemented"); + } + + remove(_ids: number[]): Promise { + throw new Error("not implemented"); + } + + reopen(): Promise { + throw new Error("not implemented"); + } + + select(_tabId: number): Promise { + throw new Error("not implemented"); + } + + setPinned(_tabId: number, _pinned: boolean): Promise { + throw new Error("not implemented"); + } + + setZoom(_tabId: number, _factor: number): Promise { + throw new Error("not implemented"); + } +} + describe('NavigateUseCase', () => { let sut: NavigateUseCase; let tabPresenter: TabPresenter; let navigateClient: NavigateClient; + beforeEach(() => { - tabPresenter = new TabPresenter(); + tabPresenter = new MockTabPresenter(); navigateClient = new NavigateClient(); sut = new NavigateUseCase(tabPresenter, navigateClient); }); + const newTab = (url: string): browser.tabs.Tab => { + return { + index: 0, + title: 'dummy title', + url: url, + active: true, + hidden: false, + highlighted: false, + incognito: false, + isArticle: false, + isInReaderMode: false, + lastAccessed: 1585446733000, + pinned: false, + selected: false, + windowId: 0 + }; + }; + describe('#openParent()', async () => { it('opens parent directory of file', async() => { - const stub = sinon.stub(tabPresenter, 'getCurrent'); - stub.returns(Promise.resolve({ url: 'https://google.com/fruits/yellow/banana' })) + sinon.stub(tabPresenter, 'getCurrent') + .returns(Promise.resolve(newTab('https://google.com/fruits/yellow/banana'))); - const mock = sinon.mock(tabPresenter); - mock.expects('open').withArgs('https://google.com/fruits/yellow/'); + const mock = sinon.mock(tabPresenter) + .expects('open').withArgs('https://google.com/fruits/yellow/'); await sut.openParent(); @@ -28,11 +109,11 @@ describe('NavigateUseCase', () => { }); it('opens parent directory of directory', async() => { - const stub = sinon.stub(tabPresenter, 'getCurrent'); - stub.returns(Promise.resolve({ url: 'https://google.com/fruits/yellow/' })) + sinon.stub(tabPresenter, 'getCurrent') + .returns(Promise.resolve(newTab('https://google.com/fruits/yellow/'))); - const mock = sinon.mock(tabPresenter); - mock.expects('open').withArgs('https://google.com/fruits/'); + const mock = sinon.mock(tabPresenter) + .expects('open').withArgs('https://google.com/fruits/'); await sut.openParent(); @@ -40,11 +121,11 @@ describe('NavigateUseCase', () => { }); it('removes hash', async() => { - const stub = sinon.stub(tabPresenter, 'getCurrent'); - stub.returns(Promise.resolve({ url: 'https://google.com/#top' })) + sinon.stub(tabPresenter, 'getCurrent') + .returns(Promise.resolve(newTab('https://google.com/#top'))); - const mock = sinon.mock(tabPresenter); - mock.expects('open').withArgs('https://google.com/'); + const mock = sinon.mock(tabPresenter) + .expects('open').withArgs('https://google.com/'); await sut.openParent(); @@ -52,11 +133,11 @@ describe('NavigateUseCase', () => { }); it('removes search query', async() => { - const stub = sinon.stub(tabPresenter, 'getCurrent'); - stub.returns(Promise.resolve({ url: 'https://google.com/search?q=apple' })) + sinon.stub(tabPresenter, 'getCurrent') + .returns(Promise.resolve(newTab('https://google.com/search?q=apple'))); - const mock = sinon.mock(tabPresenter); - mock.expects('open').withArgs('https://google.com/search'); + const mock = sinon.mock(tabPresenter) + .expects('open').withArgs('https://google.com/search'); await sut.openParent(); @@ -66,13 +147,11 @@ describe('NavigateUseCase', () => { describe('#openRoot()', () => { it('opens root direectory', async() => { - const stub = sinon.stub(tabPresenter, 'getCurrent'); - stub.returns(Promise.resolve({ - url: 'https://google.com/seach?q=apple', - })) + sinon.stub(tabPresenter, 'getCurrent') + .returns(Promise.resolve(newTab('https://google.com/seach?q=apple'))); - const mock = sinon.mock(tabPresenter); - mock.expects('open').withArgs('https://google.com'); + const mock = sinon.mock(tabPresenter) + .expects('open').withArgs('https://google.com'); await sut.openRoot(); -- cgit v1.2.3 From 0340c82bc82738a63c8a374930cf39cbed5c7c8c Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sun, 29 Mar 2020 21:10:47 +0900 Subject: Complete tab by an index and a flag --- src/background/completion/TabCompletionUseCase.ts | 25 +++++++++- src/background/completion/TabRepository.ts | 2 + .../completion/impl/TabRepositoryImpl.ts | 32 +++++++++---- .../completion/TabCompletionUseCase.test.ts | 53 +++++++++++++++++++--- 4 files changed, 94 insertions(+), 18 deletions(-) (limited to 'test/background/completion/TabCompletionUseCase.test.ts') diff --git a/src/background/completion/TabCompletionUseCase.ts b/src/background/completion/TabCompletionUseCase.ts index 7e6dce3..dec86e9 100644 --- a/src/background/completion/TabCompletionUseCase.ts +++ b/src/background/completion/TabCompletionUseCase.ts @@ -1,6 +1,6 @@ import { inject, injectable } from "tsyringe"; import TabItem from "./TabItem"; -import TabRepository from "./TabRepository"; +import TabRepository, { Tab } from "./TabRepository"; import TabPresenter from "../presenters/TabPresenter"; import TabFlag from "../../shared/TabFlag"; @@ -14,7 +14,28 @@ export default class TabCompletionUseCase { async queryTabs(query: string, excludePinned: boolean): Promise { const lastTabId = await this.tabPresenter.getLastSelectedId(); - const tabs = await this.tabRepository.queryTabs(query, excludePinned); + const allTabs = await this.tabRepository.getAllTabs(excludePinned); + const num = parseInt(query, 10); + let tabs: Tab[] = []; + if (!isNaN(num)) { + const tab = allTabs.find(t => t.index === num - 1); + if (tab) { + tabs = [tab]; + } + } else if (query == '%') { + const tab = allTabs.find(t => t.active); + if (tab) { + tabs = [tab]; + } + } else if (query == '#') { + const tab = allTabs.find(t => t.id === lastTabId); + if (tab) { + tabs = [tab]; + } + } else { + tabs = await this.tabRepository.queryTabs(query, excludePinned); + } + return tabs.map(tab => { let flag = TabFlag.None; if (tab.active) { diff --git a/src/background/completion/TabRepository.ts b/src/background/completion/TabRepository.ts index 61fac3b..fe1b601 100644 --- a/src/background/completion/TabRepository.ts +++ b/src/background/completion/TabRepository.ts @@ -9,4 +9,6 @@ export type Tab = { export default interface TabRepository { queryTabs(query: string, excludePinned: boolean): Promise; + + getAllTabs(excludePinned: boolean): Promise } diff --git a/src/background/completion/impl/TabRepositoryImpl.ts b/src/background/completion/impl/TabRepositoryImpl.ts index 6692b27..adcaba7 100644 --- a/src/background/completion/impl/TabRepositoryImpl.ts +++ b/src/background/completion/impl/TabRepositoryImpl.ts @@ -15,13 +15,27 @@ export default class TabRepositoryImpl implements TabRepository { }) .filter(item => item.id && item.title && item.url) .slice(0, COMPLETION_ITEM_LIMIT) - .map(item => ({ - id: item.id!!, - url: item.url!!, - active: item.active, - title: item.title!!, - faviconUrl: item.favIconUrl, - index: item.index, - })) + .map(TabRepositoryImpl.toEntity); } -} \ No newline at end of file + + async getAllTabs(excludePinned: boolean): Promise { + if (excludePinned) { + return (await browser.tabs.query({ currentWindow: true, pinned: true })) + .map(TabRepositoryImpl.toEntity) + + } + return (await browser.tabs.query({ currentWindow: true })) + .map(TabRepositoryImpl.toEntity) + } + + private static toEntity(tab: browser.tabs.Tab,): Tab { + return { + id: tab.id!!, + url: tab.url!!, + active: tab.active, + title: tab.title!!, + faviconUrl: tab.favIconUrl, + index: tab.index, + } + } +} diff --git a/test/background/completion/TabCompletionUseCase.test.ts b/test/background/completion/TabCompletionUseCase.test.ts index 254bfe6..9df236d 100644 --- a/test/background/completion/TabCompletionUseCase.test.ts +++ b/test/background/completion/TabCompletionUseCase.test.ts @@ -11,6 +11,10 @@ class MockTabRepository implements TabRepositoryImpl { async queryTabs(_query: string, _excludePinned: boolean): Promise { throw new Error("not implemented") } + + async getAllTabs(_excludePinned: boolean): Promise { + throw new Error("not implemented") + } } class MockTabPresenter implements TabPresenter { @@ -83,22 +87,57 @@ describe('TabCompletionUseCase', () => { beforeEach(() => { tabRepository = new MockTabRepository(); tabPresenter = new MockTabPresenter(); - sut = new TabCompletionUseCase(tabRepository, tabPresenter) + sut = new TabCompletionUseCase(tabRepository, tabPresenter); + + sinon.stub(tabPresenter, 'getLastSelectedId').returns(Promise.resolve(12)); + sinon.stub(tabRepository, 'getAllTabs').returns(Promise.resolve([ + { id: 10, index: 0, title: 'Google', url: 'https://google.com/', faviconUrl: 'https://google.com/favicon.ico', active: false }, + { id: 11, index: 1, title: 'Yahoo', url: 'https://yahoo.com/', faviconUrl: 'https://yahoo.com/favicon.ico', active: true }, + { id: 12, index: 2, title: 'Bing', url: 'https://bing.com/', active: false }, + ])); }); describe('#queryTabs', () => { it("returns tab items", async () => { - sinon.stub(tabRepository, 'queryTabs').returns(Promise.resolve([ - { id: 10, index: 0, title: 'Google', url: 'https://google.com/', faviconUrl: 'https://google.com/favicon.ico', active: true }, - { id: 11, index: 1, title: 'Yahoo', url: 'https://yahoo.com/', faviconUrl: 'https://yahoo.com/favicon.ico', active: false }, + sinon.stub(tabRepository, 'queryTabs').withArgs('', false).returns(Promise.resolve([ + { id: 10, index: 0, title: 'Google', url: 'https://google.com/', faviconUrl: 'https://google.com/favicon.ico', active: false }, + { id: 11, index: 1, title: 'Yahoo', url: 'https://yahoo.com/', faviconUrl: 'https://yahoo.com/favicon.ico', active: true }, { id: 12, index: 2, title: 'Bing', url: 'https://bing.com/', active: false }, + ])).withArgs('oo', false).returns(Promise.resolve([ + { id: 10, index: 0, title: 'Google', url: 'https://google.com/', faviconUrl: 'https://google.com/favicon.ico', active: false }, + { id: 11, index: 1, title: 'Yahoo', url: 'https://yahoo.com/', faviconUrl: 'https://yahoo.com/favicon.ico', active: true }, ])); - sinon.stub(tabPresenter, 'getLastSelectedId').returns(Promise.resolve(11)); - expect(await sut.queryTabs("", false)).to.deep.equal([ + expect(await sut.queryTabs('', false)).to.deep.equal([ + { index: 1, title: 'Google', url: 'https://google.com/', faviconUrl: 'https://google.com/favicon.ico', flag: TabFlag.None }, + { index: 2, title: 'Yahoo', url: 'https://yahoo.com/', faviconUrl: 'https://yahoo.com/favicon.ico', flag: TabFlag.CurrentTab }, + { index: 3, title: 'Bing', url: 'https://bing.com/', faviconUrl: undefined, flag: TabFlag.LastTab }, + ]); + + expect(await sut.queryTabs('oo', false)).to.deep.equal([ + { index: 1, title: 'Google', url: 'https://google.com/', faviconUrl: 'https://google.com/favicon.ico', flag: TabFlag.None }, + { index: 2, title: 'Yahoo', url: 'https://yahoo.com/', faviconUrl: 'https://yahoo.com/favicon.ico', flag: TabFlag.CurrentTab }, + ]); + }); + + it("returns a tab by the index", async () => { + expect(await sut.queryTabs('1', false)).to.deep.equal([ { index: 1, title: 'Google', url: 'https://google.com/', faviconUrl: 'https://google.com/favicon.ico', flag: TabFlag.CurrentTab }, + ]); + + expect(await sut.queryTabs('10', false)).to.be.empty; + expect(await sut.queryTabs('-1', false)).to.be.empty; + }); + + it("returns the current tab by % flag", async () => { + expect(await sut.queryTabs('%', false)).to.deep.equal([ { index: 2, title: 'Yahoo', url: 'https://yahoo.com/', faviconUrl: 'https://yahoo.com/favicon.ico', flag: TabFlag.LastTab }, - { index: 3, title: 'Bing', url: 'https://bing.com/', faviconUrl: undefined, flag: TabFlag.None }, + ]); + }); + + it("returns the current tab by # flag", async () => { + expect(await sut.queryTabs('#', false)).to.deep.equal([ + { index: 3, title: 'Bing', url: 'https://bing.com/', faviconUrl: undefined, flag: TabFlag.LastTab }, ]); }) }); -- cgit v1.2.3 From ac18ad17e62834131399074b09f742266761ac2d Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sun, 29 Mar 2020 21:52:47 +0900 Subject: Complete by flags --- test/background/completion/TabCompletionUseCase.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'test/background/completion/TabCompletionUseCase.test.ts') diff --git a/test/background/completion/TabCompletionUseCase.test.ts b/test/background/completion/TabCompletionUseCase.test.ts index 9df236d..b9dc60b 100644 --- a/test/background/completion/TabCompletionUseCase.test.ts +++ b/test/background/completion/TabCompletionUseCase.test.ts @@ -122,7 +122,7 @@ describe('TabCompletionUseCase', () => { it("returns a tab by the index", async () => { expect(await sut.queryTabs('1', false)).to.deep.equal([ - { index: 1, title: 'Google', url: 'https://google.com/', faviconUrl: 'https://google.com/favicon.ico', flag: TabFlag.CurrentTab }, + { index: 1, title: 'Google', url: 'https://google.com/', faviconUrl: 'https://google.com/favicon.ico', flag: TabFlag.None }, ]); expect(await sut.queryTabs('10', false)).to.be.empty; @@ -131,7 +131,7 @@ describe('TabCompletionUseCase', () => { it("returns the current tab by % flag", async () => { expect(await sut.queryTabs('%', false)).to.deep.equal([ - { index: 2, title: 'Yahoo', url: 'https://yahoo.com/', faviconUrl: 'https://yahoo.com/favicon.ico', flag: TabFlag.LastTab }, + { index: 2, title: 'Yahoo', url: 'https://yahoo.com/', faviconUrl: 'https://yahoo.com/favicon.ico', flag: TabFlag.CurrentTab }, ]); }); -- cgit v1.2.3