Skip to content

Commit 0c96bc1

Browse files
authored
feat: artifacts in each step (#11)
1 parent 4ba5d46 commit 0c96bc1

File tree

11 files changed

+489
-114
lines changed

11 files changed

+489
-114
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# Logs
2-
logs
32
*.log
43
npm-debug.log*
54
yarn-debug.log*

package-e2e/test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { ReporterOptions } from 'jest-allure2-reporter';
22

33
import listener from 'detox-allure2-adapter';
4+
import type { DetoxAllure2AdapterOptions, DetoxAllure2AdapterDeviceLogsOptions, DetoxAllure2AdapterDeviceScreenshotOptions } from 'detox-allure2-adapter';
45
import DetoxAllurePathBuilder from 'detox-allure2-adapter/path-builder';
56
import presetAllure from 'detox-allure2-adapter/preset-allure';
67
import presetDetox from 'detox-allure2-adapter/preset-detox';
@@ -10,6 +11,25 @@ function assertType<T>(_actual: T): void {
1011
// no-op
1112
}
1213

14+
assertType<unknown>(presetAllure);
15+
assertType<unknown>(presetDetox);
16+
1317
assertType<EnvironmentListenerFn>(listener);
1418
assertType<DetoxAllurePathBuilder>(new DetoxAllurePathBuilder());
1519
assertType<ReporterOptions>(presetAllure);
20+
assertType<DetoxAllure2AdapterOptions>({
21+
useSteps: true,
22+
deviceLogs: true,
23+
deviceScreenshots: true,
24+
});
25+
26+
assertType<DetoxAllure2AdapterDeviceLogsOptions>({
27+
ios: () => true,
28+
android: () => true,
29+
override: true,
30+
saveAll: true,
31+
});
32+
33+
assertType<DetoxAllure2AdapterDeviceScreenshotOptions>({
34+
saveAll: true,
35+
});

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@
107107
"node 16"
108108
],
109109
"dependencies": {
110-
"archiver": "^6.0.1"
110+
"archiver": "^6.0.1",
111+
"logkitten": "^1.3.0",
112+
"screenkitten": "^1.0.0"
111113
}
112114
}

src/index.ts

Lines changed: 6 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,7 @@
1-
import fs from 'node:fs';
2-
import path from 'node:path';
1+
export type {
2+
DetoxAllure2AdapterOptions,
3+
DetoxAllure2AdapterDeviceLogsOptions,
4+
DetoxAllure2AdapterDeviceScreenshotOptions,
5+
} from './types';
36

4-
// eslint-disable-next-line import/no-internal-modules
5-
import detox from 'detox';
6-
// eslint-disable-next-line import/no-internal-modules
7-
import { worker } from 'detox/internals';
8-
// eslint-disable-next-line import/no-internal-modules
9-
import { allure, type MIMEInferer } from 'jest-allure2-reporter/api';
10-
// eslint-disable-next-line node/no-extraneous-import
11-
import type { EnvironmentListenerFn } from 'jest-environment-emit';
12-
13-
import { createLogHandler, createZipHandler } from './file-handlers';
14-
import { wrapWithSteps } from './steps';
15-
16-
export type DetoxAllure2AdapterOptions = {
17-
/**
18-
* Whether to wrap device, element and other actions in Allure steps
19-
* @default false
20-
*/
21-
useSteps?: boolean;
22-
};
23-
24-
const listener: EnvironmentListenerFn = (
25-
{ testEvents },
26-
{ useSteps = false }: DetoxAllure2AdapterOptions = {},
27-
) => {
28-
let logHandler: ReturnType<typeof createLogHandler>;
29-
let zipHandler: ReturnType<typeof createZipHandler>;
30-
let inferMimeType: MIMEInferer;
31-
let $test: ReturnType<typeof allure.$bind> | undefined;
32-
let artifactsManager: any;
33-
34-
testEvents
35-
.on('setup', () => {
36-
allure.$plug((context) => {
37-
logHandler = createLogHandler(context);
38-
zipHandler = createZipHandler(context);
39-
inferMimeType = context.inferMimeType;
40-
});
41-
42-
artifactsManager = (worker as any)._artifactsManager;
43-
artifactsManager.on('trackArtifact', onTrackArtifact);
44-
})
45-
.on('setup', async () => {
46-
if (useSteps) wrapWithSteps(detox, worker, allure);
47-
})
48-
.on('test_start', () => {
49-
$test = allure.$bind();
50-
})
51-
.on('test_done', () => {
52-
$test = undefined;
53-
})
54-
.on('teardown', flushArtifacts, -1)
55-
.on('test_environment_teardown', flushArtifacts, -1);
56-
57-
async function flushArtifacts() {
58-
await artifactsManager?._idlePromise;
59-
artifactsManager = undefined;
60-
}
61-
62-
function onTrackArtifact(artifact: any) {
63-
const $step = allure.$bind();
64-
const $$test = $test;
65-
const originalSave = artifact.doSave.bind(artifact);
66-
67-
artifact.doSave = async (artifactPath: string, ...args: unknown[]) => {
68-
const result = await originalSave(artifactPath, ...args);
69-
const isDirectory = fs.lstatSync(artifactPath).isDirectory();
70-
const isLog = path.extname(artifactPath) === '.log';
71-
const isVideo = !!inferMimeType({ sourcePath: artifactPath })?.startsWith('video/');
72-
const handler = isDirectory ? zipHandler : isLog ? logHandler : 'copy';
73-
const mimeType = isLog ? 'text/plain' : isDirectory ? 'application/zip' : undefined;
74-
const $allure = (isLog || isVideo ? $$test : $step) ?? $step;
75-
const name = path.basename(artifactPath);
76-
77-
$allure.fileAttachment(artifactPath, {
78-
name,
79-
mimeType,
80-
handler,
81-
});
82-
83-
return result;
84-
};
85-
}
86-
};
87-
88-
export default listener;
7+
export { listener as default } from './listener';

src/listener.ts

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import fs from 'node:fs';
2+
import path from 'node:path';
3+
4+
// eslint-disable-next-line import/no-internal-modules
5+
import detox from 'detox';
6+
// eslint-disable-next-line import/no-internal-modules
7+
import { worker } from 'detox/internals';
8+
// eslint-disable-next-line import/no-internal-modules
9+
import { allure, type MIMEInferer } from 'jest-allure2-reporter/api';
10+
// eslint-disable-next-line node/no-extraneous-import
11+
import type { EnvironmentListenerFn } from 'jest-environment-emit';
12+
13+
import { createLogHandler, createZipHandler } from './file-handlers';
14+
import { LogBuffer } from './logs';
15+
import { ScreenshotHelper } from './screenshots';
16+
import { wrapWithSteps } from './steps';
17+
import type { DetoxAllure2AdapterOptions } from './types';
18+
19+
export const listener: EnvironmentListenerFn = (
20+
{ testEvents },
21+
{
22+
useSteps = false,
23+
deviceLogs = false,
24+
deviceScreenshots = false,
25+
}: DetoxAllure2AdapterOptions = {},
26+
) => {
27+
let logHandler: ReturnType<typeof createLogHandler>;
28+
let zipHandler: ReturnType<typeof createZipHandler>;
29+
let inferMimeType: MIMEInferer;
30+
let $test: ReturnType<typeof allure.$bind> | undefined;
31+
let artifactsManager: any;
32+
let logs: LogBuffer | undefined;
33+
let screenshots: ScreenshotHelper | undefined;
34+
35+
testEvents
36+
.on('setup', () => {
37+
allure.$plug((context) => {
38+
logHandler = createLogHandler(context);
39+
zipHandler = createZipHandler(context);
40+
inferMimeType = context.inferMimeType;
41+
});
42+
43+
artifactsManager = (worker as any)._artifactsManager;
44+
artifactsManager.on('trackArtifact', onTrackArtifact);
45+
46+
if (deviceLogs) {
47+
logs = new LogBuffer({
48+
device: detox.device,
49+
options: deviceLogs,
50+
});
51+
}
52+
53+
if (deviceScreenshots) {
54+
screenshots = new ScreenshotHelper({
55+
device: detox.device,
56+
options: deviceScreenshots,
57+
});
58+
}
59+
})
60+
.on('setup', async () => {
61+
if (useSteps) {
62+
wrapWithSteps({ detox, worker, allure, logs, screenshots });
63+
}
64+
})
65+
.on('test_start', () => {
66+
$test = allure.$bind();
67+
logs?.attachBefore(allure);
68+
})
69+
.on('hook_start', () => {
70+
logs?.attachBefore(allure);
71+
})
72+
.on('hook_failure', async () => {
73+
await screenshots?.attachFailure(allure);
74+
logs?.attachAfterFailure(allure);
75+
})
76+
.on('hook_success', async () => {
77+
await screenshots?.attachSuccess(allure);
78+
logs?.attachAfterSuccess(allure);
79+
})
80+
.on('test_done', async ({ event }) => {
81+
await screenshots?.attach(allure, event.test.failing);
82+
logs?.attachAfter(allure, event.test.failing);
83+
$test = undefined;
84+
})
85+
.on('teardown', flushArtifacts, -1)
86+
.on('test_environment_teardown', flushArtifacts, -1);
87+
88+
async function flushArtifacts() {
89+
await artifactsManager?._idlePromise;
90+
await logs?.close();
91+
artifactsManager = undefined;
92+
logs = undefined;
93+
}
94+
95+
function onTrackArtifact(artifact: any) {
96+
const $step = allure.$bind();
97+
const $$test = $test;
98+
const originalSave = artifact.doSave.bind(artifact);
99+
100+
artifact.doSave = async (artifactPath: string, ...args: unknown[]) => {
101+
const result = await originalSave(artifactPath, ...args);
102+
const isDirectory = fs.lstatSync(artifactPath).isDirectory();
103+
const isLog = path.extname(artifactPath) === '.log';
104+
const isVideo = !!inferMimeType({ sourcePath: artifactPath })?.startsWith('video/');
105+
const handler = isDirectory ? zipHandler : isLog ? logHandler : 'copy';
106+
const mimeType = isLog ? 'text/plain' : isDirectory ? 'application/zip' : undefined;
107+
const $allure = (isLog || isVideo ? $$test : $step) ?? $step;
108+
const name = path.basename(artifactPath);
109+
110+
$allure.fileAttachment(artifactPath, {
111+
name,
112+
mimeType,
113+
handler,
114+
});
115+
116+
return result;
117+
};
118+
}
119+
};

src/logs/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './log-buffer';

0 commit comments

Comments
 (0)