@samsara-dev/appwright 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
package/dist/reporter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAUhF,cAAM,eAAgB,YAAW,QAAQ;IACvC,OAAO,CAAC,gBAAgB,CAAsB;IAE9C,OAAO;IAQP,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU;IAU9C,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU;
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAUhF,cAAM,eAAgB,YAAW,QAAQ;IACvC,OAAO,CAAC,gBAAgB,CAAsB;IAE9C,OAAO;IAQP,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU;IAU9C,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU;IA2FtC,KAAK;YAKG,kCAAkC;IA6ChD,OAAO,CAAC,4BAA4B;IAyBpC,OAAO,CAAC,qBAAqB;CAI9B;AA8ED,eAAe,eAAe,CAAC"}
|
package/dist/reporter.js
CHANGED
|
@@ -51,7 +51,6 @@ class VideoDownloader {
|
|
|
51
51
|
type: "workerInfo",
|
|
52
52
|
description: `Ran on worker #${workerIndex}.`,
|
|
53
53
|
});
|
|
54
|
-
const expectedVideoPath = path_1.default.join((0, utils_1.basePath)(), `worker-${workerIndex}-video.mp4`);
|
|
55
54
|
// The `onTestEnd` is method is called before the worker ends and
|
|
56
55
|
// the worker's `endTime` is saved to disk. We add a 5 secs delay
|
|
57
56
|
// to prevent a harmful race condition.
|
|
@@ -68,10 +67,11 @@ class VideoDownloader {
|
|
|
68
67
|
if (!this.providerSupportsVideo(providerName)) {
|
|
69
68
|
return; // Nothing to do here
|
|
70
69
|
}
|
|
70
|
+
const workerVideoBaseName = `worker-${workerIndex}-${sessionId}-video`;
|
|
71
71
|
if (endTime) {
|
|
72
72
|
// This is the last test in the worker, so let's download the video
|
|
73
73
|
const provider = (0, providers_1.getProviderClass)(providerName);
|
|
74
|
-
const downloaded = await provider.downloadVideo(sessionId, (0, utils_1.basePath)(),
|
|
74
|
+
const downloaded = await provider.downloadVideo(sessionId, (0, utils_1.basePath)(), workerVideoBaseName);
|
|
75
75
|
if (!downloaded) {
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
@@ -80,8 +80,9 @@ class VideoDownloader {
|
|
|
80
80
|
else {
|
|
81
81
|
// This is an intermediate test in the worker, so let's wait for the
|
|
82
82
|
// video file to be found on disk. Once it is, we trim and attach it.
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
const expectedWorkerVideoPath = path_1.default.join((0, utils_1.basePath)(), `${workerVideoBaseName}.mp4`);
|
|
84
|
+
await waitFor(() => fs_1.default.existsSync(expectedWorkerVideoPath));
|
|
85
|
+
return this.trimAndAttachPersistentDeviceVideo(test, result, expectedWorkerVideoPath);
|
|
85
86
|
}
|
|
86
87
|
})
|
|
87
88
|
.catch((e) => {
|
|
@@ -111,7 +112,8 @@ class VideoDownloader {
|
|
|
111
112
|
}
|
|
112
113
|
else {
|
|
113
114
|
const trimSkipPoint = (testStart.getTime() - workerStart.getTime()) / 1000;
|
|
114
|
-
const
|
|
115
|
+
const retryIndex = result.retry ?? 0;
|
|
116
|
+
const trimmedFileName = `worker-${workerIdx}-trimmed-${test.id}-retry-${retryIndex}.mp4`;
|
|
115
117
|
try {
|
|
116
118
|
pathToAttach = await trimVideo({
|
|
117
119
|
originalVideoPath: workerVideoPath,
|
|
@@ -135,7 +137,7 @@ class VideoDownloader {
|
|
|
135
137
|
});
|
|
136
138
|
}
|
|
137
139
|
downloadAndAttachDeviceVideo(test, result, providerClass, sessionId) {
|
|
138
|
-
const videoFileName = `${test.id}`;
|
|
140
|
+
const videoFileName = `${sessionId}-${test.id}`;
|
|
139
141
|
if (!providerClass.downloadVideo) {
|
|
140
142
|
return;
|
|
141
143
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.spec.d.ts","sourceRoot":"","sources":["../../src/tests/reporter.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const vitest_1 = require("vitest");
|
|
7
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const os_1 = __importDefault(require("os"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
vitest_1.vi.mock("@ffmpeg-installer/ffmpeg", () => {
|
|
11
|
+
return {
|
|
12
|
+
default: { path: "/fake/ffmpeg" },
|
|
13
|
+
__esModule: true,
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
vitest_1.vi.mock("fluent-ffmpeg", () => {
|
|
17
|
+
return {
|
|
18
|
+
default: () => {
|
|
19
|
+
const handlers = {};
|
|
20
|
+
const chain = {
|
|
21
|
+
setFfmpegPath: () => chain,
|
|
22
|
+
setStartTime: () => chain,
|
|
23
|
+
setDuration: () => chain,
|
|
24
|
+
output: () => chain,
|
|
25
|
+
on: (event, cb) => {
|
|
26
|
+
handlers[event] = cb;
|
|
27
|
+
return chain;
|
|
28
|
+
},
|
|
29
|
+
run: () => {
|
|
30
|
+
void Promise.resolve().then(() => handlers.end?.());
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
return chain;
|
|
34
|
+
},
|
|
35
|
+
__esModule: true,
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
let mockBasePath = "";
|
|
39
|
+
const downloadVideoMock = vitest_1.vi.fn();
|
|
40
|
+
const getProviderClassMock = vitest_1.vi.fn(() => ({
|
|
41
|
+
downloadVideo: downloadVideoMock,
|
|
42
|
+
}));
|
|
43
|
+
vitest_1.vi.mock("../providers", () => {
|
|
44
|
+
return {
|
|
45
|
+
getProviderClass: getProviderClassMock,
|
|
46
|
+
__esModule: true,
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
vitest_1.vi.mock("../utils", () => {
|
|
50
|
+
return {
|
|
51
|
+
basePath: () => mockBasePath,
|
|
52
|
+
__esModule: true,
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
vitest_1.vi.mock("../logger", () => {
|
|
56
|
+
return {
|
|
57
|
+
logger: {
|
|
58
|
+
log: vitest_1.vi.fn(),
|
|
59
|
+
warn: vitest_1.vi.fn(),
|
|
60
|
+
error: vitest_1.vi.fn(),
|
|
61
|
+
},
|
|
62
|
+
__esModule: true,
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
let VideoDownloader;
|
|
66
|
+
(0, vitest_1.beforeAll)(async () => {
|
|
67
|
+
const reporterModule = await import("../reporter.js");
|
|
68
|
+
VideoDownloader = reporterModule.default;
|
|
69
|
+
});
|
|
70
|
+
(0, vitest_1.afterEach)(async () => {
|
|
71
|
+
const basePathToDelete = mockBasePath;
|
|
72
|
+
downloadVideoMock.mockReset();
|
|
73
|
+
mockBasePath = "";
|
|
74
|
+
getProviderClassMock.mockClear();
|
|
75
|
+
vitest_1.vi.useRealTimers();
|
|
76
|
+
if (basePathToDelete) {
|
|
77
|
+
await promises_1.default.rm(basePathToDelete, { recursive: true, force: true });
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
(0, vitest_1.describe)("VideoDownloader", () => {
|
|
81
|
+
(0, vitest_1.test)("downloads device videos with session-scoped filename", async () => {
|
|
82
|
+
mockBasePath = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "appwright-videos-"));
|
|
83
|
+
const sessionId = "session-123";
|
|
84
|
+
const testId = "test-abc";
|
|
85
|
+
downloadVideoMock.mockResolvedValueOnce({
|
|
86
|
+
path: path_1.default.join(mockBasePath, `${sessionId}-${testId}.mp4`),
|
|
87
|
+
contentType: "video/mp4",
|
|
88
|
+
});
|
|
89
|
+
const reporter = new VideoDownloader();
|
|
90
|
+
const testCase = {
|
|
91
|
+
id: testId,
|
|
92
|
+
title: "example",
|
|
93
|
+
annotations: [
|
|
94
|
+
{ type: "sessionId", description: sessionId },
|
|
95
|
+
{ type: "providerName", description: "browserstack" },
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
const testResult = {
|
|
99
|
+
workerIndex: 0,
|
|
100
|
+
duration: 1,
|
|
101
|
+
startTime: new Date(),
|
|
102
|
+
attachments: [],
|
|
103
|
+
};
|
|
104
|
+
reporter.onTestEnd(testCase, testResult);
|
|
105
|
+
(0, vitest_1.expect)(getProviderClassMock).toHaveBeenCalledWith("browserstack");
|
|
106
|
+
(0, vitest_1.expect)(downloadVideoMock).toHaveBeenCalledWith(sessionId, mockBasePath, `${sessionId}-${testId}`);
|
|
107
|
+
await reporter.onEnd();
|
|
108
|
+
(0, vitest_1.expect)(testResult.attachments).toEqual([
|
|
109
|
+
{
|
|
110
|
+
path: path_1.default.join(mockBasePath, `${sessionId}-${testId}.mp4`),
|
|
111
|
+
contentType: "video/mp4",
|
|
112
|
+
name: "video",
|
|
113
|
+
},
|
|
114
|
+
]);
|
|
115
|
+
});
|
|
116
|
+
(0, vitest_1.test)("scopes persistentDevice worker video base name by sessionId", async () => {
|
|
117
|
+
vitest_1.vi.useFakeTimers();
|
|
118
|
+
mockBasePath = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "appwright-videos-"));
|
|
119
|
+
const workerIndex = 0;
|
|
120
|
+
const sessionId = "session-xyz";
|
|
121
|
+
const providerName = "browserstack";
|
|
122
|
+
const workerVideoBaseName = `worker-${workerIndex}-${sessionId}-video`;
|
|
123
|
+
const workerStart = new Date("2025-01-01T00:00:00.000Z");
|
|
124
|
+
const testStart = new Date("2025-01-01T00:00:10.000Z");
|
|
125
|
+
await promises_1.default.writeFile(path_1.default.join(mockBasePath, `worker-info-${workerIndex}.json`), JSON.stringify({
|
|
126
|
+
idx: workerIndex,
|
|
127
|
+
sessionId,
|
|
128
|
+
providerName,
|
|
129
|
+
startTime: {
|
|
130
|
+
beforeAppiumSession: workerStart.toISOString(),
|
|
131
|
+
afterAppiumSession: workerStart.toISOString(),
|
|
132
|
+
},
|
|
133
|
+
endTime: new Date("2025-01-01T00:00:02.000Z").toISOString(),
|
|
134
|
+
tests: [],
|
|
135
|
+
}, null, 2));
|
|
136
|
+
const downloadedVideoPath = path_1.default.join(mockBasePath, `${workerVideoBaseName}.mp4`);
|
|
137
|
+
await promises_1.default.writeFile(downloadedVideoPath, "video-bytes");
|
|
138
|
+
downloadVideoMock.mockResolvedValueOnce({
|
|
139
|
+
path: downloadedVideoPath,
|
|
140
|
+
contentType: "video/mp4",
|
|
141
|
+
});
|
|
142
|
+
const reporter = new VideoDownloader();
|
|
143
|
+
const testCase = {
|
|
144
|
+
id: "test-1",
|
|
145
|
+
title: "persistent",
|
|
146
|
+
annotations: [],
|
|
147
|
+
};
|
|
148
|
+
const testResult = {
|
|
149
|
+
workerIndex,
|
|
150
|
+
duration: 1,
|
|
151
|
+
startTime: testStart,
|
|
152
|
+
retry: 1,
|
|
153
|
+
attachments: [],
|
|
154
|
+
};
|
|
155
|
+
reporter.onTestEnd(testCase, testResult);
|
|
156
|
+
await vitest_1.vi.advanceTimersByTimeAsync(5000);
|
|
157
|
+
await reporter.onEnd();
|
|
158
|
+
(0, vitest_1.expect)(downloadVideoMock).toHaveBeenCalledWith(sessionId, mockBasePath, workerVideoBaseName);
|
|
159
|
+
(0, vitest_1.expect)(testResult.attachments).toMatchObject([
|
|
160
|
+
{
|
|
161
|
+
contentType: "video/mp4",
|
|
162
|
+
name: "video",
|
|
163
|
+
path: vitest_1.expect.stringContaining(`-retry-1.mp4`),
|
|
164
|
+
},
|
|
165
|
+
]);
|
|
166
|
+
});
|
|
167
|
+
});
|