Skip to content

Commit b554233

Browse files
committed
(#241) Made Screen#waitFor cancellable
1 parent 6c9de33 commit b554233

File tree

2 files changed

+135
-106
lines changed

2 files changed

+135
-106
lines changed

Diff for: lib/screen.class.e2e.spec.ts

+130-104
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,138 @@
1-
import { existsSync } from "fs";
2-
import { VisionAdapter } from "./adapter/vision.adapter.class";
3-
import { FileType } from "./file-type.enum";
4-
import { Screen } from "./screen.class";
5-
import { sleep } from "./sleep.function";
1+
import {existsSync} from "fs";
2+
import {VisionAdapter} from "./adapter/vision.adapter.class";
3+
import {FileType} from "./file-type.enum";
4+
import {Screen} from "./screen.class";
5+
import {sleep} from "./sleep.function";
6+
import AbortController from "node-abort-controller";
67

78
describe("Screen.", () => {
8-
it("should capture the screen", () => {
9-
// GIVEN
10-
const visionAdapter = new VisionAdapter();
11-
const SUT = new Screen(visionAdapter);
12-
13-
// WHEN
14-
SUT.capture("asdf", FileType.PNG).then(filename => {
15-
// THEN
16-
expect(filename).not.toBeNull();
17-
sleep(1000).then(() => {
18-
expect(existsSync(filename)).toBeTruthy();
19-
});
9+
it("should capture the screen", () => {
10+
// GIVEN
11+
const visionAdapter = new VisionAdapter();
12+
const SUT = new Screen(visionAdapter);
13+
14+
// WHEN
15+
SUT.capture("asdf", FileType.PNG).then(filename => {
16+
// THEN
17+
expect(filename).not.toBeNull();
18+
sleep(1000).then(() => {
19+
expect(existsSync(filename)).toBeTruthy();
20+
});
21+
});
2022
});
21-
});
22-
23-
it("should capture the screen and save to JPG", () => {
24-
// GIVEN
25-
const visionAdapter = new VisionAdapter();
26-
const SUT = new Screen(visionAdapter);
27-
28-
// WHEN
29-
SUT.capture("asdf", FileType.JPG).then(filename => {
30-
// THEN
31-
expect(filename).not.toBeNull();
32-
sleep(1000).then(() => {
33-
expect(existsSync(filename)).toBeTruthy();
34-
});
23+
24+
it("should capture the screen and save to JPG", () => {
25+
// GIVEN
26+
const visionAdapter = new VisionAdapter();
27+
const SUT = new Screen(visionAdapter);
28+
29+
// WHEN
30+
SUT.capture("asdf", FileType.JPG).then(filename => {
31+
// THEN
32+
expect(filename).not.toBeNull();
33+
sleep(1000).then(() => {
34+
expect(existsSync(filename)).toBeTruthy();
35+
});
36+
});
3537
});
36-
});
37-
38-
it("should capture the screen and save file with prefix", () => {
39-
// GIVEN
40-
const visionAdapter = new VisionAdapter();
41-
const SUT = new Screen(visionAdapter);
42-
const prefix = "foo_";
43-
44-
// WHEN
45-
SUT.capture("asdf", FileType.JPG, "./", prefix).then(filename => {
46-
// THEN
47-
expect(filename.includes(prefix)).toBeTruthy();
48-
expect(filename).not.toBeNull();
49-
sleep(1000).then(() => {
50-
expect(existsSync(filename)).toBeTruthy();
51-
});
38+
39+
it("should capture the screen and save file with prefix", () => {
40+
// GIVEN
41+
const visionAdapter = new VisionAdapter();
42+
const SUT = new Screen(visionAdapter);
43+
const prefix = "foo_";
44+
45+
// WHEN
46+
SUT.capture("asdf", FileType.JPG, "./", prefix).then(filename => {
47+
// THEN
48+
expect(filename.includes(prefix)).toBeTruthy();
49+
expect(filename).not.toBeNull();
50+
sleep(1000).then(() => {
51+
expect(existsSync(filename)).toBeTruthy();
52+
});
53+
});
5254
});
53-
});
54-
55-
it("should capture the screen and save file with postfix", () => {
56-
// GIVEN
57-
const visionAdapter = new VisionAdapter();
58-
const SUT = new Screen(visionAdapter);
59-
const postfix = "_bar";
60-
61-
// WHEN
62-
SUT.capture("asdf", FileType.JPG, "./", "", postfix).then(filename => {
63-
// THEN
64-
expect(filename.includes(postfix)).toBeTruthy();
65-
expect(filename).not.toBeNull();
66-
sleep(1000).then(() => {
67-
expect(existsSync(filename)).toBeTruthy();
68-
});
55+
56+
it("should capture the screen and save file with postfix", () => {
57+
// GIVEN
58+
const visionAdapter = new VisionAdapter();
59+
const SUT = new Screen(visionAdapter);
60+
const postfix = "_bar";
61+
62+
// WHEN
63+
SUT.capture("asdf", FileType.JPG, "./", "", postfix).then(filename => {
64+
// THEN
65+
expect(filename.includes(postfix)).toBeTruthy();
66+
expect(filename).not.toBeNull();
67+
sleep(1000).then(() => {
68+
expect(existsSync(filename)).toBeTruthy();
69+
});
70+
});
6971
});
70-
});
71-
72-
it("should capture the screen and save file with pre- and postfix", () => {
73-
// GIVEN
74-
const visionAdapter = new VisionAdapter();
75-
const SUT = new Screen(visionAdapter);
76-
const filename = "asdf";
77-
const prefix = "foo_";
78-
const postfix = "_bar";
79-
80-
// WHEN
81-
SUT.capture("asdf", FileType.JPG, "./", prefix, postfix).then(output => {
82-
// THEN
83-
expect(output.includes(`${prefix}${filename}${postfix}`)).toBeTruthy();
84-
expect(output).not.toBeNull();
85-
sleep(1000).then(() => {
86-
expect(existsSync(output)).toBeTruthy();
87-
});
72+
73+
it("should capture the screen and save file with pre- and postfix", () => {
74+
// GIVEN
75+
const visionAdapter = new VisionAdapter();
76+
const SUT = new Screen(visionAdapter);
77+
const filename = "asdf";
78+
const prefix = "foo_";
79+
const postfix = "_bar";
80+
81+
// WHEN
82+
SUT.capture("asdf", FileType.JPG, "./", prefix, postfix).then(output => {
83+
// THEN
84+
expect(output.includes(`${prefix}${filename}${postfix}`)).toBeTruthy();
85+
expect(output).not.toBeNull();
86+
sleep(1000).then(() => {
87+
expect(existsSync(output)).toBeTruthy();
88+
});
89+
});
90+
});
91+
92+
it("should reject after timeout", async () => {
93+
// GIVEN
94+
jest.setTimeout(10000);
95+
const timeout = 5000;
96+
const visionAdapter = new VisionAdapter();
97+
const SUT = new Screen(visionAdapter);
98+
SUT.config.resourceDirectory = "./e2e/assets";
99+
100+
// WHEN
101+
const start = Date.now();
102+
try {
103+
await SUT.waitFor("calculator.png", timeout);
104+
} catch (e) {
105+
// THEN
106+
expect(e).toBe(`Action timed out after ${timeout} ms`);
107+
}
108+
const end = Date.now();
109+
110+
// THEN
111+
expect(end - start).toBeGreaterThanOrEqual(timeout);
112+
});
113+
114+
it("should abort via signal", (done) => {
115+
// GIVEN
116+
jest.setTimeout(10000);
117+
const timeout = 5000;
118+
const abortAfterMs = 1000;
119+
const controller = new AbortController();
120+
const signal = controller.signal;
121+
const visionAdapter = new VisionAdapter();
122+
const SUT = new Screen(visionAdapter);
123+
SUT.config.resourceDirectory = "./e2e/assets";
124+
125+
// WHEN
126+
const start = Date.now();
127+
SUT.waitFor("calculator.png", timeout, undefined, signal).catch(e => {
128+
const end = Date.now();
129+
130+
// THEN
131+
expect(e).toBe(`Action aborted by signal`);
132+
expect(end - start).toBeGreaterThanOrEqual(abortAfterMs);
133+
expect(end - start).toBeLessThan(timeout);
134+
done();
135+
});
136+
setTimeout(() => controller.abort(), abortAfterMs);
88137
});
89-
});
90-
91-
it("should reject after timeout", async () => {
92-
// GIVEN
93-
jest.setTimeout(10000);
94-
const timeout = 5000;
95-
const visionAdapter = new VisionAdapter();
96-
const SUT = new Screen(visionAdapter);
97-
SUT.config.resourceDirectory = "./e2e/assets";
98-
99-
// WHEN
100-
const start = Date.now();
101-
try {
102-
await SUT.waitFor("calculator.png", timeout);
103-
} catch (e) {
104-
// THEN
105-
expect(e).toBe(`Action timed out after ${timeout} ms`);
106-
}
107-
const end = Date.now();
108-
109-
// THEN
110-
expect(end - start).toBeGreaterThanOrEqual(timeout);
111-
});
112138
});

Diff for: lib/screen.class.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import {LocationParameters} from "./locationparameters.class";
77
import {MatchRequest} from "./match-request.class";
88
import {MatchResult} from "./match-result.class";
99
import {Region} from "./region.class";
10-
import {timeout} from "./util/poll-action.function";
10+
import {timeout} from "./util/timeout.function";
1111
import { Image } from "./image.class";
12+
import {AbortSignal} from "node-abort-controller";
1213

1314
export type FindHookCallback = (target: MatchResult) => Promise<void>;
1415

@@ -165,13 +166,15 @@ export class Screen {
165166
* @param templateImageFilename Filename of the template image, relative to {@link Screen.config.resourceDirectory}
166167
* @param timeoutMs Timeout in milliseconds after which {@link waitFor} fails
167168
* @param params {@link LocationParameters} which are used to fine tune search region and / or match confidence
169+
* @param abort An {@link AbortSignal} to cancel an ongoing call to `waitFor`
168170
*/
169171
public async waitFor(
170172
templateImageFilename: string,
171173
timeoutMs: number = 5000,
172174
params?: LocationParameters,
175+
abort?: AbortSignal
173176
): Promise<Region> {
174-
return timeout(500, timeoutMs, () => this.find(templateImageFilename, params));
177+
return timeout(500, timeoutMs, () => this.find(templateImageFilename, params), {signal: abort});
175178
}
176179

177180
/**

0 commit comments

Comments
 (0)