Skip to content

Commit

Permalink
[#120] [patch] Fix screenshots for tests with failed hooks (#150)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmisty authored Jun 27, 2024
1 parent e56b333 commit ce414af
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 37 deletions.
3 changes: 2 additions & 1 deletion src/plugins/allure-global-hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ export class GlobalHooks {
}
hook.attachments?.forEach(attach => {
log('process attach');
this.reporter.testFileAttachment({ name: attach.name, file: attach?.file, type: attach.type });
this.reporter.testFileAttachment({ name: attach.name, file: attach.file, type: attach.type });
this.reporter.setAttached(attach.file);
});
});
}
Expand Down
95 changes: 69 additions & 26 deletions src/plugins/allure-reporter-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,20 @@ import getUuid from 'uuid-by-string';
import { parseAllure } from 'allure-js-parser';
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
import { readFile } from 'fs/promises';
import path, { basename } from 'path';
import path, { basename, dirname } from 'path';
import glob from 'fast-glob';
import { ReporterOptions } from './allure';
import Debug from 'debug';
import { GlobalHooks } from './allure-global-hook';
import { AfterSpecScreenshots, AllureTaskArgs, LabelName, Stage, StatusType, UNKNOWN } from './allure-types';
import {
AfterSpecScreenshots,
AllureTaskArgs,
AutoScreen,
LabelName,
Stage,
StatusType,
UNKNOWN,
} from './allure-types';
import { extname, packageLog } from '../common';
import type { ContentType } from '../common/types';
import { randomUUID } from 'crypto';
Expand Down Expand Up @@ -162,7 +170,7 @@ export class AllureReporter {
allureRuntime: AllureRuntime;
descriptionHtml: string[] = [];

private screenshotsTest: { [testId: string]: { [testAttemptIndex: string]: string[] } } = {};
private screenshotsTest: (AutoScreen & { attached?: boolean })[] = [];

testStatusStored: AllureTaskArgs<'testStatus'> | undefined;
testDetailsStored: AllureTaskArgs<'testDetails'> | undefined;
Expand Down Expand Up @@ -452,6 +460,7 @@ export class AllureReporter {
});
}

// after spec attach
attachScreenshots(arg: AfterSpecScreenshots) {
// attach auto screenshots for fails
const { screenshots } = arg;
Expand All @@ -463,22 +472,53 @@ export class AllureReporter {

log('screenshotsTest:');
log(JSON.stringify(this.screenshotsTest));
log('screenshots arg:');
log(JSON.stringify(screenshots));

const arr = [...screenshots, ...this.screenshotsTest.filter(x => !x.attached)];

const uniqueScreenshotsArr = arr.reduce(
(acc: { map: Map<string, boolean>; list: AutoScreen[] }, current) => {
const key = `${current.path}`;

if (!acc.map.has(key)) {
acc.map.set(key, true);
current.specName = basename(dirname(current.path));
acc.list.push(current);
} else {
const existing = acc.list.find(t => t.path === current.path);
const merged = { ...existing, ...current };
acc.list = acc.list.map(item => (item.path === current.path ? merged : item));
}

return acc;
},
{ map: new Map(), list: [] },
).list;

screenshots.forEach(x => {
log(`attachScreenshots:${x.path}`);
uniqueScreenshotsArr.forEach(afterSpecRes => {
log(`attachScreenshots: ${afterSpecRes.path}`);

const uuids = allTests
.filter(t => t.retryIndex === x.testAttemptIndex && t.mochaId === x.testId && t.status !== Status.PASSED)
.map(t => t.uuid);
const getUuiToAdd = () => {
return allTests.filter(
t =>
t.status !== Status.PASSED &&
t.retryIndex === afterSpecRes.testAttemptIndex &&
basename(t.specRelative ?? '') === afterSpecRes.specName &&
(afterSpecRes.testId ? t.mochaId === afterSpecRes.testId : true),
);
};

const uuids = getUuiToAdd().map(t => t.uuid);

if (uuids.length === 0) {
log('no attach auto screens, only for non-success tests tests');

return;
}

if (!uuids[x.testAttemptIndex ?? 0]) {
log(`no attach, current attempt ${x.testAttemptIndex}`);
if (afterSpecRes.testAttemptIndex && afterSpecRes.testId && !uuids[afterSpecRes.testAttemptIndex ?? 0]) {
log(`no attach, current attempt ${afterSpecRes.testAttemptIndex}`);

// test passed or no
return;
Expand All @@ -489,16 +529,17 @@ export class AllureReporter {

try {
const contents = readFileSync(testFile);
const ext = path.extname(x.path);
const name = path.basename(x.path);
const ext = path.extname(afterSpecRes.path);
const name = path.basename(afterSpecRes.path);

type ParsedAttachment = { name: string; type: ContentType; source: string };
const testCon: { attachments: ParsedAttachment[] } = JSON.parse(contents.toString());
const uuidNew = randomUUID();
const nameAttach = `${uuidNew}-attachment${ext}`; // todo not copy same image
const newPath = path.join(this.allureResults, nameAttach);

if (!existsSync(newPath)) {
copyFileSync(x.path, path.join(this.allureResults, nameAttach));
copyFileSync(afterSpecRes.path, path.join(this.allureResults, nameAttach));
}

if (!testCon.attachments) {
Expand All @@ -513,7 +554,7 @@ export class AllureReporter {

writeFileSync(testFile, JSON.stringify(testCon));
} catch (e) {
console.log(`${packageLog} Could not attach screenshot ${x.screenshotId}`);
console.log(`${packageLog} Could not attach screenshot ${afterSpecRes.screenshotId ?? afterSpecRes.path}`);
}
});
});
Expand All @@ -524,16 +565,9 @@ export class AllureReporter {
}

screenshotAttachment(arg: AllureTaskArgs<'screenshotAttachment'>) {
const { testId, path, testAttemptIndex } = arg;
const { testId, path, testAttemptIndex, specName, testFailure } = arg;

if (!this.screenshotsTest[this.keyWhenNoTest(testId)]) {
this.screenshotsTest[this.keyWhenNoTest(testId)] = {};
}

if (!this.screenshotsTest[this.keyWhenNoTest(testId)][testAttemptIndex ?? 0]) {
this.screenshotsTest[this.keyWhenNoTest(testId)][testAttemptIndex ?? 0] = [];
}
this.screenshotsTest[this.keyWhenNoTest(testId)][testAttemptIndex ?? 0].push(path);
this.screenshotsTest.push({ testId, path, testAttemptIndex, specName, testFailure });
}

screenshotOne(arg: AllureTaskArgs<'screenshotOne'>) {
Expand Down Expand Up @@ -1050,6 +1084,14 @@ export class AllureReporter {
exec.addAttachment(arg.name, arg.type, file);
}

public setAttached(file: string) {
const screen = this.screenshotsTest.find(t => t.path === file);

if (screen) {
screen.attached = true;
}
}

private executableFileAttachment(exec: ExecutableItemWrapper | undefined, arg: AllureTaskArgs<'fileAttachment'>) {
if (!this.currentExecutable && this.globalHooks.currentHook) {
log('No current executable, test or hook - add to global hook');
Expand Down Expand Up @@ -1078,12 +1120,13 @@ export class AllureReporter {
mkdirSync(this.allureResults, { recursive: true });
}

// how to understand where to attach
const currExec = exec ?? this.currentExecutable;

if (exec ?? this.currentExecutable) {
if (currExec) {
copyFileSync(arg.file, `${this.allureResults}/${fileNew}`);
(exec ?? this.currentExecutable)?.addAttachment(arg.name, arg.type, fileNew);
currExec.addAttachment(arg.name, arg.type, fileNew);
log(`added attachment: ${fileNew} ${arg.file}`);
this.setAttached(arg.file);
}
} catch (err) {
console.error(`${packageLog} Could not attach ${arg.file}`);
Expand Down
12 changes: 7 additions & 5 deletions src/plugins/allure-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ import type { StatusDetails } from 'allure-js-commons';
import type { ContentType } from '../common/types';

export interface AutoScreen {
screenshotId: string;
screenshotId?: string;
specName?: string;
testId: string | undefined;
testAttemptIndex: number;
takenAt: string; // date
testAttemptIndex?: number;
takenAt?: string; // date
path: string; // abs path
height: number;
width: number;
height?: number;
width?: number;
testFailure?: boolean;
}

export type AfterSpecScreenshots = {
screenshots: AutoScreen[];
};
Expand Down
2 changes: 1 addition & 1 deletion src/setup/screenshots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const registerScreenshotHandler = (message: MessageManager, testMsg: (msg
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(Cypress.Screenshot as any).onAfterScreenshot = (_$el: unknown, ...args: AutoScreen[]) => {
debug('Screenshot handler');
// testAttemptIndex, takenAt, name
// testAttemptIndex, takenAt, name, specName, testFailure
const [screensArgs] = args;
const [{ path }] = args;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
} from '../../../cy-helper/utils';
import { AllureTest, parseAllure } from 'allure-js-parser';

// this doesn't work
describe('test screenshot when global before hook fails', () => {
const res = createResTest2(
[
Expand Down Expand Up @@ -53,10 +52,15 @@ describe('screenshot when global before hook fails @screen', () => {
t => !t.name.includes('after each') && !t.name.includes('before each'),
);

// todo: expected to have screenshots
expect(obj).toEqual([
{
attachments: [],
attachments: [
{
name: '01 test -- before all hook (failed).png',
source: 'source.png',
type: 'image/png',
},
],
name: '01 test',
parents: [
{
Expand Down
23 changes: 22 additions & 1 deletion tests/test-folder/mocha-events/hooks/regression-steps.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,28 @@ describe('hooks test - failed global hook step', () => {
});

it('check attachments', async () => {
expect(resFixed.flatMap(t => t.attachments).sort()).toEqual([]);
expect(resFixed.flatMap(t => t.attachments).sort()).toEqual([
{
name: 'test 1 -- before all hook Global Setup (failed).png',
source: 'source.png',
type: 'image/png',
},
{
name: 'test 1 -- before all hook Global Setup (failed).png',
source: 'source.png',
type: 'image/png',
},
{
name: 'test 1 -- before all hook Global Setup (failed).png',
source: 'source.png',
type: 'image/png',
},
]);
expect(
resFixed
.map(t => t.parent?.befores?.flatMap(x => x.attachments))
.sort(),
).toEqual([[], [], []]);
expect(
resFixed.map(t => t.parent?.afters?.flatMap(x => x.attachments)).sort(),
).toEqual([
Expand Down

0 comments on commit ce414af

Please sign in to comment.