parent
ebfb172520
commit
c6288f19d9
16 changed files with 316 additions and 137 deletions
@ -0,0 +1,28 @@ |
|||||||
|
import Mark from '../domains/Mark'; |
||||||
|
import * as messages from '../../shared/messages'; |
||||||
|
|
||||||
|
export default interface MarkClient { |
||||||
|
setGloablMark(key: string, mark: Mark): Promise<void>; |
||||||
|
|
||||||
|
jumpGlobalMark(key: string): Promise<void>; |
||||||
|
|
||||||
|
// eslint-disable-next-line semi
|
||||||
|
} |
||||||
|
|
||||||
|
export class MarkClientImpl implements MarkClient { |
||||||
|
async setGloablMark(key: string, mark: Mark): Promise<void> { |
||||||
|
await browser.runtime.sendMessage({ |
||||||
|
type: messages.MARK_SET_GLOBAL, |
||||||
|
key, |
||||||
|
x: mark.x, |
||||||
|
y: mark.y, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
async jumpGlobalMark(key: string): Promise<void> { |
||||||
|
await browser.runtime.sendMessage({ |
||||||
|
type: messages.MARK_JUMP_GLOBAL, |
||||||
|
key, |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
import Mark from '../domains/Mark'; |
||||||
|
|
||||||
|
export default interface MarkRepository { |
||||||
|
set(key: string, mark: Mark): void; |
||||||
|
|
||||||
|
get(key: string): Mark | null; |
||||||
|
|
||||||
|
// eslint-disable-next-line semi
|
||||||
|
} |
||||||
|
|
||||||
|
const saved: {[key: string]: Mark} = {}; |
||||||
|
|
||||||
|
export class MarkRepositoryImpl implements MarkRepository { |
||||||
|
set(key: string, mark: Mark): void { |
||||||
|
saved[key] = mark; |
||||||
|
} |
||||||
|
|
||||||
|
get(key: string): Mark | null { |
||||||
|
let v = saved[key]; |
||||||
|
if (!v) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return { ...v }; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,62 @@ |
|||||||
|
import ScrollPresenter, { ScrollPresenterImpl } |
||||||
|
from '../presenters/ScrollPresenter'; |
||||||
|
import MarkClient, { MarkClientImpl } from '../client/MarkClient'; |
||||||
|
import MarkRepository, { MarkRepositoryImpl } |
||||||
|
from '../repositories/MarkRepository'; |
||||||
|
import SettingRepository, { SettingRepositoryImpl } |
||||||
|
from '../repositories/SettingRepository'; |
||||||
|
import ConsoleClient, { ConsoleClientImpl } from '../client/ConsoleClient'; |
||||||
|
|
||||||
|
export default class MarkUseCase { |
||||||
|
private scrollPresenter: ScrollPresenter; |
||||||
|
|
||||||
|
private client: MarkClient; |
||||||
|
|
||||||
|
private repository: MarkRepository; |
||||||
|
|
||||||
|
private settingRepository: SettingRepository; |
||||||
|
|
||||||
|
private consoleClient: ConsoleClient; |
||||||
|
|
||||||
|
constructor({ |
||||||
|
scrollPresenter = new ScrollPresenterImpl(), |
||||||
|
client = new MarkClientImpl(), |
||||||
|
repository = new MarkRepositoryImpl(), |
||||||
|
settingRepository = new SettingRepositoryImpl(), |
||||||
|
consoleClient = new ConsoleClientImpl(), |
||||||
|
} = {}) { |
||||||
|
this.scrollPresenter = scrollPresenter; |
||||||
|
this.client = client; |
||||||
|
this.repository = repository; |
||||||
|
this.settingRepository = settingRepository; |
||||||
|
this.consoleClient = consoleClient; |
||||||
|
} |
||||||
|
|
||||||
|
async set(key: string): Promise<void> { |
||||||
|
let pos = this.scrollPresenter.getScroll(); |
||||||
|
if (this.globalKey(key)) { |
||||||
|
this.client.setGloablMark(key, pos); |
||||||
|
await this.consoleClient.info(`Set global mark to '${key}'`); |
||||||
|
} else { |
||||||
|
this.repository.set(key, pos); |
||||||
|
await this.consoleClient.info(`Set local mark to '${key}'`); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
async jump(key: string): Promise<void> { |
||||||
|
if (this.globalKey(key)) { |
||||||
|
await this.client.jumpGlobalMark(key); |
||||||
|
} else { |
||||||
|
let pos = this.repository.get(key); |
||||||
|
if (!pos) { |
||||||
|
throw new Error('Mark is not set'); |
||||||
|
} |
||||||
|
let smooth = this.settingRepository.get().properties.smoothscroll; |
||||||
|
this.scrollPresenter.scrollTo(pos.x, pos.y, smooth); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private globalKey(key: string) { |
||||||
|
return (/^[A-Z0-9]$/).test(key); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
import ConsoleClient from '../../../src/content/client/ConsoleClient'; |
||||||
|
|
||||||
|
export default class MockConsoleClient implements ConsoleClient { |
||||||
|
public isError: boolean; |
||||||
|
|
||||||
|
public text: string; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
this.isError = false; |
||||||
|
this.text = ''; |
||||||
|
} |
||||||
|
|
||||||
|
info(text: string): Promise<void> { |
||||||
|
this.isError = false; |
||||||
|
this.text = text; |
||||||
|
return Promise.resolve(); |
||||||
|
} |
||||||
|
|
||||||
|
error(text: string): Promise<void> { |
||||||
|
this.isError = true; |
||||||
|
this.text = text; |
||||||
|
return Promise.resolve(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
@ -0,0 +1,47 @@ |
|||||||
|
import ScrollPresenter, { Point } from '../../../src/content/presenters/ScrollPresenter'; |
||||||
|
|
||||||
|
export default class MockScrollPresenter implements ScrollPresenter { |
||||||
|
private pos: Point; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
this.pos = { x: 0, y: 0 }; |
||||||
|
} |
||||||
|
|
||||||
|
getScroll(): Point { |
||||||
|
return this.pos; |
||||||
|
} |
||||||
|
|
||||||
|
scrollVertically(amount: number, _smooth: boolean): void { |
||||||
|
this.pos.y += amount; |
||||||
|
} |
||||||
|
|
||||||
|
scrollHorizonally(amount: number, _smooth: boolean): void { |
||||||
|
this.pos.x += amount; |
||||||
|
} |
||||||
|
|
||||||
|
scrollPages(amount: number, _smooth: boolean): void { |
||||||
|
this.pos.x += amount; |
||||||
|
} |
||||||
|
|
||||||
|
scrollTo(x: number, y: number, _smooth: boolean): void { |
||||||
|
this.pos.x = x; |
||||||
|
this.pos.y = y; |
||||||
|
} |
||||||
|
|
||||||
|
scrollToTop(_smooth: boolean): void { |
||||||
|
this.pos.y = 0; |
||||||
|
} |
||||||
|
|
||||||
|
scrollToBottom(_smooth: boolean): void { |
||||||
|
this.pos.y = Infinity; |
||||||
|
} |
||||||
|
|
||||||
|
scrollToHome(_smooth: boolean): void { |
||||||
|
this.pos.x = 0; |
||||||
|
} |
||||||
|
|
||||||
|
scrollToEnd(_smooth: boolean): void { |
||||||
|
this.pos.x = Infinity; |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,13 @@ |
|||||||
|
import { MarkRepositoryImpl } from '../../../src/content/repositories/MarkRepository'; |
||||||
|
import { expect } from 'chai'; |
||||||
|
|
||||||
|
describe('MarkRepositoryImpl', () => { |
||||||
|
it('save and load marks', () => { |
||||||
|
let sut = new MarkRepositoryImpl(); |
||||||
|
|
||||||
|
sut.set('a', { x: 10, y: 20 }); |
||||||
|
expect(sut.get('a')).to.deep.equal({ x: 10, y: 20 }); |
||||||
|
expect(sut.get('b')).to.be.null; |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
@ -0,0 +1,107 @@ |
|||||||
|
import MarkRepository from '../../../src/content/repositories/MarkRepository'; |
||||||
|
import MarkUseCase from '../../../src/content/usecases/MarkUseCase'; |
||||||
|
import MarkClient from '../../../src/content/client/MarkClient'; |
||||||
|
import MockConsoleClient from '../mock/MockConsoleClient'; |
||||||
|
import MockScrollPresenter from '../mock/MockScrollPresenter'; |
||||||
|
import Mark from '../../../src/content/domains/Mark'; |
||||||
|
import { expect } from 'chai'; |
||||||
|
|
||||||
|
class MockMarkRepository implements MarkRepository { |
||||||
|
private current: {[key: string]: Mark}; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
this.current = {}; |
||||||
|
} |
||||||
|
|
||||||
|
set(key: string, mark: Mark): void { |
||||||
|
this.current[key] = mark; |
||||||
|
} |
||||||
|
|
||||||
|
get(key: string): Mark | null { |
||||||
|
return this.current[key]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class MockMarkClient implements MarkClient { |
||||||
|
public marks: {[key: string]: Mark}; |
||||||
|
public last: string; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
this.marks = {}; |
||||||
|
this.last = ''; |
||||||
|
} |
||||||
|
|
||||||
|
setGloablMark(key: string, mark: Mark): Promise<void> { |
||||||
|
this.marks[key] = mark; |
||||||
|
return Promise.resolve(); |
||||||
|
} |
||||||
|
|
||||||
|
jumpGlobalMark(key: string): Promise<void> { |
||||||
|
this.last = key |
||||||
|
return Promise.resolve(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
describe('MarkUseCase', () => { |
||||||
|
let repository: MockMarkRepository; |
||||||
|
let client: MockMarkClient; |
||||||
|
let consoleClient: MockConsoleClient; |
||||||
|
let scrollPresenter: MockScrollPresenter; |
||||||
|
let sut: MarkUseCase; |
||||||
|
|
||||||
|
beforeEach(() => { |
||||||
|
repository = new MockMarkRepository(); |
||||||
|
client = new MockMarkClient(); |
||||||
|
consoleClient = new MockConsoleClient(); |
||||||
|
scrollPresenter = new MockScrollPresenter(); |
||||||
|
sut = new MarkUseCase({ |
||||||
|
repository, client, consoleClient, scrollPresenter, |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('#set', () => { |
||||||
|
it('sets local mark', async() => { |
||||||
|
scrollPresenter.scrollTo(10, 20, false); |
||||||
|
|
||||||
|
await sut.set('x'); |
||||||
|
|
||||||
|
expect(repository.get('x')).to.deep.equals({ x: 10, y: 20 }); |
||||||
|
expect(consoleClient.text).to.equal("Set local mark to 'x'"); |
||||||
|
}); |
||||||
|
|
||||||
|
it('sets global mark', async() => { |
||||||
|
scrollPresenter.scrollTo(30, 40, false); |
||||||
|
|
||||||
|
await sut.set('Z'); |
||||||
|
|
||||||
|
expect(client.marks['Z']).to.deep.equals({ x: 30, y: 40 }); |
||||||
|
expect(consoleClient.text).to.equal("Set global mark to 'Z'"); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('#jump', () => { |
||||||
|
it('jumps to local mark', async() => { |
||||||
|
repository.set('x', { x: 20, y: 40 }); |
||||||
|
|
||||||
|
await sut.jump('x'); |
||||||
|
|
||||||
|
expect(scrollPresenter.getScroll()).to.deep.equals({ x: 20, y: 40 }); |
||||||
|
}); |
||||||
|
|
||||||
|
it('throws an error when no local marks', () => { |
||||||
|
return sut.jump('a').then(() => { |
||||||
|
throw new Error('error'); |
||||||
|
}).catch((e) => { |
||||||
|
expect(e).to.be.instanceof(Error); |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
it('jumps to global mark', async() => { |
||||||
|
client.marks['Z'] = { x: 20, y: 0 }; |
||||||
|
|
||||||
|
await sut.jump('Z'); |
||||||
|
|
||||||
|
expect(client.last).to.equal('Z') |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
Reference in new issue