Skip to content

Commit 278b3ef

Browse files
committed
feat(scenario): "take-screenshot" command
Can either save the screenshot to a file, or compare it with an existing PNG file on disk
1 parent 8001265 commit 278b3ef

File tree

4 files changed

+93
-0
lines changed

4 files changed

+93
-0
lines changed

package-lock.json

+21
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,12 @@
4141
"arg": "^5.0.2",
4242
"chalk": "^5.3.0",
4343
"chalk-template": "^1.1.0",
44+
"pngjs": "^7.0.0",
4445
"ws": "^8.13.0",
4546
"yaml": "^2.3.1"
4647
},
4748
"devDependencies": {
49+
"@types/pngjs": "^6.0.5",
4850
"@types/ws": "^8.5.4",
4951
"@typescript-eslint/eslint-plugin": "^5.59.1",
5052
"@yao-pkg/pkg": "^5.11.5",

src/main.ts

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { SetControlCommand } from './scenario/SetControlCommand.js';
2020
import { WaitSerialCommand } from './scenario/WaitSerialCommand.js';
2121
import { WriteSerialCommand } from './scenario/WriteSerialCommand.js';
2222
import { uploadFirmware } from './uploadFirmware.js';
23+
import { TakeScreenshotCommand } from './scenario/TakeScreenshotCommand.js';
2324

2425
const millis = 1_000_000;
2526

@@ -215,6 +216,7 @@ async function main() {
215216
'set-control': new SetControlCommand(),
216217
'wait-serial': new WaitSerialCommand(expectEngine),
217218
'write-serial': new WriteSerialCommand(),
219+
'take-screenshot': new TakeScreenshotCommand(path.dirname(resolvedScenarioFile)),
218220
});
219221
scenario.validate();
220222
}

src/scenario/TakeScreenshotCommand.ts

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { readFile, writeFile } from 'fs/promises';
2+
import path from 'path';
3+
import { PNG } from 'pngjs';
4+
import { type APIClient } from '../APIClient.js';
5+
import { type TestScenario } from '../TestScenario.js';
6+
7+
export interface ITakeScreenshotParams {
8+
'part-id': string;
9+
'save-to'?: string;
10+
'compare-with'?: string;
11+
}
12+
13+
export class TakeScreenshotCommand {
14+
constructor(readonly scenarioDir: string) {}
15+
16+
validate(value: ITakeScreenshotParams) {
17+
if (!value['part-id']) {
18+
throw new Error('take-screenshot: `part-id` is required');
19+
}
20+
if (!value['save-to'] && !value['compare-with']) {
21+
throw new Error('take-screenshot: `save-to` or `compare-with` is required');
22+
}
23+
}
24+
25+
async run(scenario: TestScenario, client: APIClient, params: ITakeScreenshotParams) {
26+
const framebuffer = await client.framebufferRead(params['part-id']);
27+
const png = Buffer.from(framebuffer.png, 'base64');
28+
const saveTo = params['save-to'];
29+
const compareWith = params['compare-with'];
30+
if (saveTo) {
31+
await writeFile(path.join(this.scenarioDir, saveTo), png);
32+
}
33+
if (compareWith) {
34+
const compareWithPath = path.join(this.scenarioDir, compareWith);
35+
const originalPng = PNG.sync.read(png);
36+
37+
let compareWithData;
38+
try {
39+
compareWithData = await readFile(compareWithPath);
40+
} catch (error) {
41+
scenario.fail(`Failed to read comparison file for screenshot: ${compareWith}`);
42+
return;
43+
}
44+
45+
const compareWithPng = PNG.sync.read(compareWithData);
46+
47+
if (
48+
compareWithPng.width !== originalPng.width ||
49+
compareWithPng.height !== originalPng.height
50+
) {
51+
scenario.fail(
52+
`Comparison file for screenshot (${compareWith}) has a different size (${compareWithPng.width}x${compareWithPng.height}) than the part's screen (${originalPng.width}x${originalPng.height})`,
53+
);
54+
return;
55+
}
56+
57+
for (let i = 0; i < originalPng.data.length; i++) {
58+
const original = originalPng.data[i];
59+
const compare = compareWithPng.data[i];
60+
if (original !== compare) {
61+
const x = (i >> 2) % originalPng.width;
62+
const y = Math.floor((i >> 2) / originalPng.width);
63+
scenario.fail(`Screenshot mismatch for ${compareWith} at x,y=(${x},${y})`);
64+
}
65+
}
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)