Skip to content

Commit 59fe63a

Browse files
authored
Merge pull request #191 from nut-tree/feature/154/screenshot-of-region
(#154) added "captureRegion" to Screen API
2 parents 8564832 + 92a1e6a commit 59fe63a

File tree

3 files changed

+140
-12
lines changed

3 files changed

+140
-12
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## upcomming release
6+
- Feature: Create screenshot from region [(#154)](https://github.com/nut-tree/nut.js/issues/154)
7+
58
## 1.5.0
69

710
- Enhancement: Window support [(#5)](https://github.com/nut-tree/nut.js/issues/5)

lib/screen.class.spec.ts

+92-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import {join} from "path";
2-
import {cwd} from "process";
3-
import {VisionAdapter} from "./adapter/vision.adapter.class";
4-
import {Image} from "./image.class";
5-
import {LocationParameters} from "./locationparameters.class";
6-
import {MatchRequest} from "./match-request.class";
7-
import {MatchResult} from "./match-result.class";
8-
import {Region} from "./region.class";
9-
import {Screen} from "./screen.class";
1+
import { join } from "path";
2+
import { cwd } from "process";
3+
import { VisionAdapter } from "./adapter/vision.adapter.class";
4+
import { Image } from "./image.class";
5+
import { LocationParameters } from "./locationparameters.class";
6+
import { MatchRequest } from "./match-request.class";
7+
import { MatchResult } from "./match-result.class";
8+
import { Region } from "./region.class";
9+
import { Screen } from "./screen.class";
10+
import { mockPartial } from "sneer";
11+
import { FileType } from "./file-type.enum";
1012

1113
jest.mock("./adapter/native.adapter.class");
1214
jest.mock("./adapter/vision.adapter.class");
@@ -269,4 +271,85 @@ describe("Screen.", () => {
269271
expect(matchRegion).toEqual(expectedMatchRegion);
270272
})
271273

274+
describe("capture",() => {
275+
it("should capture the whole screen and save image", async() => {
276+
277+
// GIVEN
278+
const screenshot = mockPartial<Image>({data: "pretty pretty image"});
279+
VisionAdapter.prototype.grabScreen = jest.fn(() => Promise.resolve(screenshot));
280+
VisionAdapter.prototype.saveImage = jest.fn();
281+
const visionAdapterMock = new VisionAdapter();
282+
const SUT = new Screen(visionAdapterMock);
283+
const imageName = "foobar.png"
284+
const expectedImagePath = join(cwd(), imageName)
285+
286+
// WHEN
287+
const imagePath = await SUT.capture(imageName)
288+
289+
// THEN
290+
expect(imagePath).toBe(expectedImagePath)
291+
expect(VisionAdapter.prototype.grabScreen).toHaveBeenCalled()
292+
expect(VisionAdapter.prototype.saveImage).toHaveBeenCalledWith(screenshot,expectedImagePath)
293+
})
294+
295+
it("should consider output configuration", async () => {
296+
297+
// GIVEN
298+
const visionAdapterMock = new VisionAdapter();
299+
const SUT = new Screen(visionAdapterMock);
300+
const imageName = "foobar"
301+
const filePath = "/path/to/file"
302+
const prefix = "answer_"
303+
const postfix = "_42"
304+
const expectedImagePath = join(filePath, `${prefix}${imageName}${postfix}${FileType.JPG.toString()}`)
305+
306+
// WHEN
307+
const imagePath = await SUT.capture(imageName, FileType.JPG, filePath, prefix, postfix)
308+
309+
// THEN
310+
expect(imagePath).toBe(expectedImagePath)
311+
})
312+
})
313+
314+
describe("captureRegion", () => {
315+
316+
it("should capture the specified region of the screen and save image", async () => {
317+
// GIVEN
318+
const screenshot = mockPartial<Image>({data: "pretty partial image"});
319+
const regionToCapture = mockPartial<Region>({top:42, left:9, height: 10, width: 3.14159265359})
320+
VisionAdapter.prototype.grabScreenRegion = jest.fn(() => Promise.resolve(screenshot));
321+
VisionAdapter.prototype.saveImage = jest.fn();
322+
const visionAdapterMock = new VisionAdapter();
323+
const SUT = new Screen(visionAdapterMock);
324+
const imageName = "foobar.png"
325+
const expectedImagePath = join(cwd(), imageName)
326+
327+
// WHEN
328+
const imagePath = await SUT.captureRegion(imageName, regionToCapture)
329+
330+
// THEN
331+
expect(imagePath).toBe(expectedImagePath)
332+
expect(VisionAdapter.prototype.grabScreenRegion).toHaveBeenCalledWith(regionToCapture)
333+
expect(VisionAdapter.prototype.saveImage).toHaveBeenCalledWith(screenshot,expectedImagePath)
334+
})
335+
336+
it("should consider output configuration", async () => {
337+
338+
// GIVEN
339+
const regionToCapture = mockPartial<Region>({top:42, left:9, height: 10, width: 3.14159265359})
340+
const visionAdapterMock = new VisionAdapter();
341+
const SUT = new Screen(visionAdapterMock);
342+
const imageName = "foobar"
343+
const filePath = "/path/to/file"
344+
const prefix = "answer_"
345+
const postfix = "_42"
346+
const expectedImagePath = join(filePath, `${prefix}${imageName}${postfix}${FileType.JPG.toString()}`)
347+
348+
// WHEN
349+
const imagePath = await SUT.captureRegion(imageName, regionToCapture, FileType.JPG, filePath, prefix, postfix)
350+
351+
// THEN
352+
expect(imagePath).toBe(expectedImagePath)
353+
})
354+
})
272355
});

lib/screen.class.ts

+45-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {MatchRequest} from "./match-request.class";
88
import {MatchResult} from "./match-result.class";
99
import {Region} from "./region.class";
1010
import {timeout} from "./util/poll-action.function";
11+
import { Image } from "./image.class";
1112

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

@@ -181,15 +182,56 @@ export class Screen {
181182
filePath: string = cwd(),
182183
fileNamePrefix: string = "",
183184
fileNamePostfix: string = ""): Promise<string> {
185+
const currentScreen = await this.vision.grabScreen();
186+
return this.saveImage(
187+
currentScreen,
188+
fileName,
189+
fileFormat,
190+
filePath,
191+
fileNamePrefix,
192+
fileNamePostfix);
193+
}
194+
195+
/**
196+
* {@link captureRegion} captures a screenshot of a region on the systems main display
197+
* @param fileName Basename for the generated screenshot
198+
* @param regionToCapture The region of the screen to capture in the screenshot
199+
* @param fileFormat The {@link FileType} for the generated screenshot
200+
* @param filePath The output path for the generated screenshot (Default: {@link cwd})
201+
* @param fileNamePrefix Filename prefix for the generated screenshot (Default: empty)
202+
* @param fileNamePostfix Filename postfix for the generated screenshot (Default: empty)
203+
*/
204+
public async captureRegion(
205+
fileName: string,
206+
regionToCapture: Region,
207+
fileFormat: FileType = FileType.PNG,
208+
filePath: string = cwd(),
209+
fileNamePrefix: string = "",
210+
fileNamePostfix: string = ""): Promise<string> {
211+
const regionImage = await this.vision.grabScreenRegion(regionToCapture);
212+
return this.saveImage(
213+
regionImage,
214+
fileName,
215+
fileFormat,
216+
filePath,
217+
fileNamePrefix,
218+
fileNamePostfix);
219+
}
220+
221+
private async saveImage(
222+
image: Image,
223+
fileName: string,
224+
fileFormat: FileType,
225+
filePath: string,
226+
fileNamePrefix: string ,
227+
fileNamePostfix: string){
184228
const outputPath = generateOutputPath(fileName, {
185229
path: filePath,
186230
postfix: fileNamePostfix,
187231
prefix: fileNamePrefix,
188232
type: fileFormat,
189233
});
190-
191-
const currentScreen = await this.vision.grabScreen();
192-
await this.vision.saveImage(currentScreen, outputPath);
234+
await this.vision.saveImage(image, outputPath);
193235
return outputPath;
194236
}
195237
}

0 commit comments

Comments
 (0)