Skip to content

Commit aee6adf

Browse files
committed
Updated keyboard interface
1 parent 311390a commit aee6adf

File tree

5 files changed

+128
-105
lines changed

5 files changed

+128
-105
lines changed

lib/adapter/native.adapter.class.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,11 @@ export class NativeAdapter {
182182
/**
183183
* click clicks a single Key via native keyboard event
184184
*
185-
* @param {Key} key The Key to click
185+
* @param {Key[]} keys The keys to click
186186
* @memberof NativeAdapter
187187
*/
188-
public click(key: Key): void {
189-
this.keyboard.click(key);
188+
public click(...keys: Key[]): void {
189+
this.keyboard.click(...keys);
190190
}
191191

192192
/**

lib/keyboard.class.spec.ts

Lines changed: 101 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,109 @@
1-
import {NativeAdapter} from "./adapter/native.adapter.class";
2-
import {Key} from "./key.enum";
3-
import {Keyboard} from "./keyboard.class";
1+
import { NativeAdapter } from "./adapter/native.adapter.class";
2+
import { Key } from "./key.enum";
3+
import { Keyboard } from "./keyboard.class";
44

55
jest.mock("./adapter/native.adapter.class");
66

77
beforeEach(() => {
8-
jest.resetAllMocks();
8+
jest.resetAllMocks();
99
});
1010

1111
describe("Keyboard", () => {
12-
it("should have a default delay of 20 ms", () => {
13-
// GIVEN
14-
const adapterMock = new NativeAdapter();
15-
const SUT = new Keyboard(adapterMock);
16-
17-
// WHEN
18-
19-
// THEN
20-
expect(SUT.config.autoDelayMs).toEqual(20);
21-
});
22-
23-
it("should pass input strings down to the type call.", () => {
24-
// GIVEN
25-
const adapterMock = new NativeAdapter();
26-
const SUT = new Keyboard(adapterMock);
27-
const payload = "Test input!";
28-
29-
// WHEN
30-
SUT.type(payload);
31-
32-
// THEN
33-
expect(adapterMock.type).toHaveBeenCalledTimes(1);
34-
expect(adapterMock.type).toHaveBeenCalledWith(payload);
35-
});
36-
37-
it("should pass input keys down to the click call.", () => {
38-
// GIVEN
39-
const adapterMock = new NativeAdapter();
40-
const SUT = new Keyboard(adapterMock);
41-
const payload = Key.A;
42-
43-
// WHEN
44-
SUT.type(payload);
45-
46-
// THEN
47-
expect(adapterMock.click).toHaveBeenCalledTimes(1);
48-
expect(adapterMock.click).toHaveBeenCalledWith(payload);
49-
});
50-
51-
it("should pass a list of input keys down to the click call.", () => {
52-
// GIVEN
53-
const adapterMock = new NativeAdapter();
54-
const SUT = new Keyboard(adapterMock);
55-
const payload = [Key.A, Key.S, Key.D, Key.F];
56-
57-
// WHEN
58-
for (const key of payload) {
59-
SUT.type(key);
60-
}
61-
62-
// THEN
63-
expect(adapterMock.click).toHaveBeenCalledTimes(payload.length);
64-
});
65-
66-
it("should pass a list of input keys down to the pressKey call.", () => {
67-
// GIVEN
68-
const adapterMock = new NativeAdapter();
69-
const SUT = new Keyboard(adapterMock);
70-
const payload = [Key.A, Key.S, Key.D, Key.F];
71-
72-
// WHEN
73-
for (const key of payload) {
74-
SUT.pressKey(key);
75-
}
76-
77-
// THEN
78-
expect(adapterMock.pressKey).toHaveBeenCalledTimes(payload.length);
79-
});
80-
81-
it("should pass a list of input keys down to the releaseKey call.", () => {
82-
// GIVEN
83-
const adapterMock = new NativeAdapter();
84-
const SUT = new Keyboard(adapterMock);
85-
const payload = [Key.A, Key.S, Key.D, Key.F];
86-
87-
// WHEN
88-
for (const key of payload) {
89-
SUT.releaseKey(key);
90-
}
91-
92-
// THEN
93-
expect(adapterMock.releaseKey).toHaveBeenCalledTimes(payload.length);
94-
});
12+
it("should have a default delay of 20 ms", () => {
13+
// GIVEN
14+
const adapterMock = new NativeAdapter();
15+
const SUT = new Keyboard(adapterMock);
16+
17+
// WHEN
18+
19+
// THEN
20+
expect(SUT.config.autoDelayMs).toEqual(20);
21+
});
22+
23+
it("should pass input strings down to the type call.", () => {
24+
// GIVEN
25+
const adapterMock = new NativeAdapter();
26+
const SUT = new Keyboard(adapterMock);
27+
const payload = "Test input!";
28+
29+
// WHEN
30+
SUT.type(payload);
31+
32+
// THEN
33+
expect(adapterMock.type).toHaveBeenCalledTimes(1);
34+
expect(adapterMock.type).toHaveBeenCalledWith(payload);
35+
});
36+
37+
it("should pass multiple input strings down to the type call.", () => {
38+
// GIVEN
39+
const adapterMock = new NativeAdapter();
40+
const SUT = new Keyboard(adapterMock);
41+
const payload = ["Test input!", "Array test2"];
42+
43+
// WHEN
44+
SUT.type(...payload);
45+
46+
// THEN
47+
expect(adapterMock.type).toHaveBeenCalledTimes(1);
48+
expect(adapterMock.type).toHaveBeenCalledWith(payload.join(" "));
49+
});
50+
51+
it("should pass input keys down to the click call.", () => {
52+
// GIVEN
53+
const adapterMock = new NativeAdapter();
54+
const SUT = new Keyboard(adapterMock);
55+
const payload = [Key.A, Key.S, Key.D, Key.F];
56+
57+
// WHEN
58+
SUT.type(...payload);
59+
60+
// THEN
61+
expect(adapterMock.click).toHaveBeenCalledTimes(1);
62+
expect(adapterMock.click).toHaveBeenCalledWith(...payload);
63+
});
64+
65+
it("should pass a list of input keys down to the click call.", () => {
66+
// GIVEN
67+
const adapterMock = new NativeAdapter();
68+
const SUT = new Keyboard(adapterMock);
69+
const payload = [Key.A, Key.S, Key.D, Key.F];
70+
71+
// WHEN
72+
for (const key of payload) {
73+
SUT.type(key);
74+
}
75+
76+
// THEN
77+
expect(adapterMock.click).toHaveBeenCalledTimes(payload.length);
78+
});
79+
80+
it("should pass a list of input keys down to the pressKey call.", () => {
81+
// GIVEN
82+
const adapterMock = new NativeAdapter();
83+
const SUT = new Keyboard(adapterMock);
84+
const payload = [Key.A, Key.S, Key.D, Key.F];
85+
86+
// WHEN
87+
for (const key of payload) {
88+
SUT.pressKey(key);
89+
}
90+
91+
// THEN
92+
expect(adapterMock.pressKey).toHaveBeenCalledTimes(payload.length);
93+
});
94+
95+
it("should pass a list of input keys down to the releaseKey call.", () => {
96+
// GIVEN
97+
const adapterMock = new NativeAdapter();
98+
const SUT = new Keyboard(adapterMock);
99+
const payload = [Key.A, Key.S, Key.D, Key.F];
100+
101+
// WHEN
102+
for (const key of payload) {
103+
SUT.releaseKey(key);
104+
}
105+
106+
// THEN
107+
expect(adapterMock.releaseKey).toHaveBeenCalledTimes(payload.length);
108+
});
95109
});

lib/keyboard.class.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { Key } from "./key.enum";
33

44
export class Keyboard {
55

6-
private static keyIsString(input: string | Key): input is string {
7-
return typeof input === "string";
6+
private static inputIsString(input: string[] | Key[]): boolean {
7+
return input.every((elem: string | Key) => typeof elem === "string");
88
}
9+
910
public config = {
1011
autoDelayMs: 20,
1112
};
@@ -14,11 +15,11 @@ export class Keyboard {
1415
this.nativeAdapter.setKeyboardDelay(this.config.autoDelayMs);
1516
}
1617

17-
public type(input: string | Key): Keyboard {
18-
if (Keyboard.keyIsString(input)) {
19-
this.nativeAdapter.type(input);
18+
public type(...input: string[] | Key[]): Keyboard {
19+
if (Keyboard.inputIsString(input)) {
20+
this.nativeAdapter.type(input.join(" "));
2021
} else {
21-
this.nativeAdapter.click(input);
22+
this.nativeAdapter.click(...input as Key[]);
2223
}
2324
return this;
2425
}

lib/provider/native/keyboard-action-provider.interface.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ export interface KeyboardActionProvider {
2525
/**
2626
* Click should allow to press a single key via OS level keyboard event
2727
*
28-
* @param {Key} key The key to click
28+
* @param {Key[]} keys The keys to click
2929
* @memberof KeyboardActionProvider
3030
*/
31-
click(key: Key): void;
31+
click(...keys: Key[]): void;
3232

3333
/**
3434
* pressKey should allow to press and hold a key via OS level keyboard event

lib/provider/native/robotjs-keyboard-action.class.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { KeyboardActionProvider } from "./keyboard-action-provider.interface";
44

55
export class KeyboardAction implements KeyboardActionProvider {
66

7-
public static KeyLookupMap = new Map<Key, any>([
7+
public static KeyLookupMap = new Map<Key, string | null>([
88
[Key.A, "a"],
99
[Key.B, "b"],
1010
[Key.C, "c"],
@@ -118,15 +118,20 @@ export class KeyboardAction implements KeyboardActionProvider {
118118
[Key.ScrollLock, null],
119119
[Key.NumLock, null],
120120
]);
121+
121122
public static keyLookup(key: Key): any {
122123
return this.KeyLookupMap.get(key);
123124
}
124125

125-
private static key(key: Key, event: "up" | "down", ...modifiers: Key[]) {
126-
const nativeKey = KeyboardAction.keyLookup(key);
127-
const modifierKeys = modifiers
126+
private static mapModifierKeys(...keys: Key[]): string[] {
127+
return keys
128128
.map(modifier => KeyboardAction.keyLookup(modifier))
129129
.filter(modifierKey => modifierKey != null && modifierKey.length > 1);
130+
}
131+
132+
private static key(key: Key, event: "up" | "down", ...modifiers: Key[]) {
133+
const nativeKey = KeyboardAction.keyLookup(key);
134+
const modifierKeys = this.mapModifierKeys(...modifiers);
130135
if (nativeKey) {
131136
robot.keyToggle(nativeKey, event, modifierKeys);
132137
}
@@ -136,13 +141,16 @@ export class KeyboardAction implements KeyboardActionProvider {
136141
}
137142

138143
public type(input: string): void {
139-
robot.typeString(input);
144+
robot.typeStringDelayed(input, 200);
140145
}
141146

142-
public click(key: Key): void {
147+
public click(...keys: Key[]): void {
148+
keys.reverse();
149+
const [key, ...modifiers] = keys;
143150
const nativeKey = KeyboardAction.keyLookup(key);
151+
const modifierKeys = KeyboardAction.mapModifierKeys(...modifiers);
144152
if (nativeKey) {
145-
robot.keyTap(nativeKey);
153+
robot.keyTap(nativeKey, modifierKeys);
146154
}
147155
}
148156

0 commit comments

Comments
 (0)