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