aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/content/actions/index.ts11
-rw-r--r--src/content/actions/mark.ts31
-rw-r--r--src/content/client/MarkClient.ts28
-rw-r--r--src/content/components/common/mark.ts48
-rw-r--r--src/content/domains/Mark.ts (renamed from src/content/Mark.ts)0
-rw-r--r--src/content/presenters/ScrollPresenter.ts2
-rw-r--r--src/content/reducers/mark.ts8
-rw-r--r--src/content/repositories/MarkRepository.ts25
-rw-r--r--src/content/usecases/MarkUseCase.ts62
-rw-r--r--test/content/actions/mark.test.ts10
-rw-r--r--test/content/mock/MockConsoleClient.ts26
-rw-r--r--test/content/mock/MockScrollPresenter.ts47
-rw-r--r--test/content/reducers/mark.test.ts10
-rw-r--r--test/content/repositories/MarkRepository.test.ts13
-rw-r--r--test/content/usecases/FindUseCase.test.ts25
-rw-r--r--test/content/usecases/MarkUseCase.test.ts107
16 files changed, 316 insertions, 137 deletions
diff --git a/src/content/actions/index.ts b/src/content/actions/index.ts
index f6d19aa..eb826fc 100644
--- a/src/content/actions/index.ts
+++ b/src/content/actions/index.ts
@@ -20,7 +20,6 @@ export const FOLLOW_CONTROLLER_BACKSPACE = 'follow.controller.backspace';
export const MARK_START_SET = 'mark.start.set';
export const MARK_START_JUMP = 'mark.start.jump';
export const MARK_CANCEL = 'mark.cancel';
-export const MARK_SET_LOCAL = 'mark.set.local';
export const NOOP = 'noop';
@@ -64,13 +63,6 @@ export interface MarkCancelAction extends Redux.Action {
type: typeof MARK_CANCEL;
}
-export interface MarkSetLocalAction extends Redux.Action {
- type: typeof MARK_SET_LOCAL;
- key: string;
- x: number;
- y: number;
-}
-
export interface NoopAction extends Redux.Action {
type: typeof NOOP;
}
@@ -80,8 +72,7 @@ export type FollowAction =
FollowControllerEnableAction | FollowControllerDisableAction |
FollowControllerKeyPressAction | FollowControllerBackspaceAction;
export type MarkAction =
- MarkStartSetAction | MarkStartJumpAction |
- MarkCancelAction | MarkSetLocalAction | NoopAction;
+ MarkStartSetAction | MarkStartJumpAction | MarkCancelAction | NoopAction;
export type Action =
InputAction |
diff --git a/src/content/actions/mark.ts b/src/content/actions/mark.ts
index 5eb9554..1068507 100644
--- a/src/content/actions/mark.ts
+++ b/src/content/actions/mark.ts
@@ -1,5 +1,4 @@
import * as actions from './index';
-import * as messages from '../../shared/messages';
const startSet = (): actions.MarkAction => {
return { type: actions.MARK_START_SET };
@@ -13,34 +12,6 @@ const cancel = (): actions.MarkAction => {
return { type: actions.MARK_CANCEL };
};
-const setLocal = (key: string, x: number, y: number): actions.MarkAction => {
- return {
- type: actions.MARK_SET_LOCAL,
- key,
- x,
- y,
- };
-};
-
-const setGlobal = (key: string, x: number, y: number): actions.MarkAction => {
- browser.runtime.sendMessage({
- type: messages.MARK_SET_GLOBAL,
- key,
- x,
- y,
- });
- return { type: actions.NOOP };
-};
-
-const jumpGlobal = (key: string): actions.MarkAction => {
- browser.runtime.sendMessage({
- type: messages.MARK_JUMP_GLOBAL,
- key,
- });
- return { type: actions.NOOP };
-};
-
export {
- startSet, startJump, cancel, setLocal,
- setGlobal, jumpGlobal,
+ startSet, startJump, cancel,
};
diff --git a/src/content/client/MarkClient.ts b/src/content/client/MarkClient.ts
new file mode 100644
index 0000000..b7cf535
--- /dev/null
+++ b/src/content/client/MarkClient.ts
@@ -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,
+ });
+ }
+}
diff --git a/src/content/components/common/mark.ts b/src/content/components/common/mark.ts
index ddd1a38..eec95d6 100644
--- a/src/content/components/common/mark.ts
+++ b/src/content/components/common/mark.ts
@@ -1,22 +1,15 @@
import * as markActions from '../../actions/mark';
import * as consoleFrames from '../..//console-frames';
import * as keyUtils from '../../../shared/utils/keys';
-import Mark from '../../Mark';
-import { SettingRepositoryImpl } from '../../repositories/SettingRepository';
-import { ScrollPresenterImpl } from '../../presenters/ScrollPresenter';
+import MarkUseCase from '../../usecases/MarkUseCase';
-let settingRepository = new SettingRepositoryImpl();
-let scrollPresenter = new ScrollPresenterImpl();
+let markUseCase = new MarkUseCase();
const cancelKey = (key: keyUtils.Key): boolean => {
return key.key === 'Esc' || key.key === '[' && Boolean(key.ctrlKey);
};
-const globalKey = (key: string): boolean => {
- return (/^[A-Z0-9]$/).test(key);
-};
-
export default class MarkComponent {
private store: any;
@@ -26,7 +19,6 @@ export default class MarkComponent {
// eslint-disable-next-line max-statements
key(key: keyUtils.Key) {
- let smoothscroll = settingRepository.get().properties.smoothscroll;
let { mark: markState } = this.store.getState();
if (!markState.setMode && !markState.jumpMode) {
@@ -40,45 +32,13 @@ export default class MarkComponent {
if (key.ctrlKey || key.metaKey || key.altKey) {
consoleFrames.postError('Unknown mark');
- } else if (globalKey(key.key) && markState.setMode) {
- this.doSetGlobal(key);
- } else if (globalKey(key.key) && markState.jumpMode) {
- this.doJumpGlobal(key);
} else if (markState.setMode) {
- this.doSet(key);
+ markUseCase.set(key.key);
} else if (markState.jumpMode) {
- this.doJump(markState.marks, key, smoothscroll);
+ markUseCase.jump(key.key);
}
this.store.dispatch(markActions.cancel());
return true;
}
-
- doSet(key: keyUtils.Key) {
- let { x, y } = scrollPresenter.getScroll();
- this.store.dispatch(markActions.setLocal(key.key, x, y));
- }
-
- doJump(
- marks: { [key: string]: Mark },
- key: keyUtils.Key,
- smoothscroll: boolean,
- ) {
- if (!marks[key.key]) {
- consoleFrames.postError('Mark is not set');
- return;
- }
-
- let { x, y } = marks[key.key];
- scrollPresenter.scrollTo(x, y, smoothscroll);
- }
-
- doSetGlobal(key: keyUtils.Key) {
- let { x, y } = scrollPresenter.getScroll();
- this.store.dispatch(markActions.setGlobal(key.key, x, y));
- }
-
- doJumpGlobal(key: keyUtils.Key) {
- this.store.dispatch(markActions.jumpGlobal(key.key));
- }
}
diff --git a/src/content/Mark.ts b/src/content/domains/Mark.ts
index f1282fc..f1282fc 100644
--- a/src/content/Mark.ts
+++ b/src/content/domains/Mark.ts
diff --git a/src/content/presenters/ScrollPresenter.ts b/src/content/presenters/ScrollPresenter.ts
index 9f47394..9286fb0 100644
--- a/src/content/presenters/ScrollPresenter.ts
+++ b/src/content/presenters/ScrollPresenter.ts
@@ -94,7 +94,7 @@ class Scroller {
}
}
-type Point = { x: number, y: number };
+export type Point = { x: number, y: number };
export default interface ScrollPresenter {
getScroll(): Point;
diff --git a/src/content/reducers/mark.ts b/src/content/reducers/mark.ts
index 7409938..a8f2f1b 100644
--- a/src/content/reducers/mark.ts
+++ b/src/content/reducers/mark.ts
@@ -1,16 +1,13 @@
-import Mark from '../Mark';
import * as actions from '../actions';
export interface State {
setMode: boolean;
jumpMode: boolean;
- marks: { [key: string]: Mark };
}
const defaultState: State = {
setMode: false,
jumpMode: false,
- marks: {},
};
export default function reducer(
@@ -24,11 +21,6 @@ export default function reducer(
return { ...state, jumpMode: true };
case actions.MARK_CANCEL:
return { ...state, setMode: false, jumpMode: false };
- case actions.MARK_SET_LOCAL: {
- let marks = { ...state.marks };
- marks[action.key] = { x: action.x, y: action.y };
- return { ...state, setMode: false, marks };
- }
default:
return state;
}
diff --git a/src/content/repositories/MarkRepository.ts b/src/content/repositories/MarkRepository.ts
new file mode 100644
index 0000000..ed5afe2
--- /dev/null
+++ b/src/content/repositories/MarkRepository.ts
@@ -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 };
+ }
+}
diff --git a/src/content/usecases/MarkUseCase.ts b/src/content/usecases/MarkUseCase.ts
new file mode 100644
index 0000000..ec63f2b
--- /dev/null
+++ b/src/content/usecases/MarkUseCase.ts
@@ -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);
+ }
+}
diff --git a/test/content/actions/mark.test.ts b/test/content/actions/mark.test.ts
index 6c6d59e..f2df367 100644
--- a/test/content/actions/mark.test.ts
+++ b/test/content/actions/mark.test.ts
@@ -22,14 +22,4 @@ describe('mark actions', () => {
expect(action.type).to.equal(actions.MARK_CANCEL);
});
});
-
- describe('setLocal', () => {
- it('create setLocal action', () => {
- let action = markActions.setLocal('a', 20, 30);
- expect(action.type).to.equal(actions.MARK_SET_LOCAL);
- expect(action.key).to.equal('a');
- expect(action.x).to.equal(20);
- expect(action.y).to.equal(30);
- });
- });
});
diff --git a/test/content/mock/MockConsoleClient.ts b/test/content/mock/MockConsoleClient.ts
new file mode 100644
index 0000000..8de2d83
--- /dev/null
+++ b/test/content/mock/MockConsoleClient.ts
@@ -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();
+ }
+}
+
+
diff --git a/test/content/mock/MockScrollPresenter.ts b/test/content/mock/MockScrollPresenter.ts
new file mode 100644
index 0000000..819569a
--- /dev/null
+++ b/test/content/mock/MockScrollPresenter.ts
@@ -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;
+ }
+}
+
diff --git a/test/content/reducers/mark.test.ts b/test/content/reducers/mark.test.ts
index 1a51c3e..918a560 100644
--- a/test/content/reducers/mark.test.ts
+++ b/test/content/reducers/mark.test.ts
@@ -6,7 +6,6 @@ describe("mark reducer", () => {
let state = reducer(undefined, {});
expect(state.setMode).to.be.false;
expect(state.jumpMode).to.be.false;
- expect(state.marks).to.be.empty;
});
it('starts set mode', () => {
@@ -29,13 +28,4 @@ describe("mark reducer", () => {
state = reducer({ jumpMode: true }, action);
expect(state.jumpMode).to.be.false;
});
-
- it('stores local mark', () => {
- let action = { type: actions.MARK_SET_LOCAL, key: 'a', x: 20, y: 30};
- let state = reducer({ setMode: true }, action);
- expect(state.setMode).to.be.false;
- expect(state.marks['a']).to.be.an('object')
- expect(state.marks['a'].x).to.equal(20)
- expect(state.marks['a'].y).to.equal(30)
- });
});
diff --git a/test/content/repositories/MarkRepository.test.ts b/test/content/repositories/MarkRepository.test.ts
new file mode 100644
index 0000000..7fced5f
--- /dev/null
+++ b/test/content/repositories/MarkRepository.test.ts
@@ -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;
+ });
+});
+
diff --git a/test/content/usecases/FindUseCase.test.ts b/test/content/usecases/FindUseCase.test.ts
index 2f966ae..c7bfd39 100644
--- a/test/content/usecases/FindUseCase.test.ts
+++ b/test/content/usecases/FindUseCase.test.ts
@@ -1,8 +1,8 @@
import FindRepository from '../../../src/content/repositories/FindRepository';
import FindPresenter from '../../../src/content/presenters/FindPresenter';
-import ConsoleClient from '../../../src/content/client/ConsoleClient';
import FindClient from '../../../src/content/client/FindClient';
import FindUseCase from '../../../src/content/usecases/FindUseCase';
+import MockConsoleClient from '../mock/MockConsoleClient';
import { expect } from 'chai';
class MockFindRepository implements FindRepository {
@@ -59,29 +59,6 @@ class MockFindClient implements FindClient {
}
}
-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();
- }
-}
-
describe('FindUseCase', () => {
let repository: MockFindRepository;
let presenter: MockFindPresenter;
diff --git a/test/content/usecases/MarkUseCase.test.ts b/test/content/usecases/MarkUseCase.test.ts
new file mode 100644
index 0000000..4f2dee4
--- /dev/null
+++ b/test/content/usecases/MarkUseCase.test.ts
@@ -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')
+ });
+ });
+});