Skip to content

Commit

Permalink
feat: refined @step, @attachment and @FileAttachment (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
noomorph authored Oct 20, 2023
1 parent f3a447b commit 2fc8193
Show file tree
Hide file tree
Showing 31 changed files with 708 additions and 358 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ module.exports = {
"unicorn/no-array-method-this-argument": "off",
"unicorn/no-array-reduce": "off",
"unicorn/no-null": "off",
"prefer-rest-params": "off",
"@typescript-eslint/ban-types": "off",
"unicorn/no-this-assignment": "off",
"unicorn/prefer-event-target": "off",
"unicorn/prefer-module": "off",
Expand Down
2 changes: 2 additions & 0 deletions e2e/src/programmatic/grouping/client/auth/LoginScreen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ describe('Login screen', () => {
describe('Form Submission', () => {
it('should show error on invalid e-mail format', async () => {
await LoginHelper.typeEmail('someone#example.com');
await LoginHelper.typePassword('123456');
expect(LoginHelper.snapshotForm()).toContain('someone#example.com');
expect(LoginHelper.getValidationSummary()).toBe('fixtures/invalid-email.xml');
});

Expand Down
18 changes: 16 additions & 2 deletions e2e/src/utils/LoginHelper.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
import {Attachment, FileAttachment, Step} from 'jest-allure2-reporter';

class LoginHelper {
@Step('Type e-mail', ['E-mail'])
@Attachment('email.txt')
#email?: string;
#password?: string;

@Step('Type e-mail: %s', ['email'])
async typeEmail(email: string) {
this.#email = email;
return 'Entered: ' + email;
}

@Step('Type password', [{ name: 'password', mode: 'masked' }])
async typePassword(password: string) {
this.#password = password;
return 'Entered: ' + password;
}

@Attachment('form.json', 'application/json')
snapshotForm() {
return JSON.stringify({ email: this.#email, password: this.#password }, null, 2);
}

@FileAttachment('summary.xml', 'text/xml')
getValidationSummary() {
return 'fixtures/invalid-email.xml';
Expand Down
2 changes: 1 addition & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const PREFIX = 'allure2' as const;

export const OUT_DIR = [PREFIX, 'config', 'outDir'] as const;
export const SHARED_CONFIG = [PREFIX, 'config'] as const;

export const CODE = [PREFIX, 'code'] as const;
export const WORKER_ID = [PREFIX, 'workerId'] as const;
Expand Down
11 changes: 5 additions & 6 deletions src/decorators/Attachment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@ import realm from '../realms';

const allure = realm.runtime;

export function Attachment(filename: string, contentType?: string) {
export function Attachment(name: string, mimeType?: string) {
return function (
_target: object,
_propertyName: string,
descriptor: TypedPropertyDescriptor<(...arguments_: any[]) => any>,

Check warning on line 9 in src/decorators/Attachment.ts

View workflow job for this annotation

GitHub Actions / Sanity

Unexpected any. Specify a different type

Check warning on line 9 in src/decorators/Attachment.ts

View workflow job for this annotation

GitHub Actions / Sanity

Unexpected any. Specify a different type
) {
descriptor.value = allure.createAttachment(
filename,
descriptor.value!,
contentType,
);
descriptor.value = allure.createAttachment(descriptor.value!, {

Check warning on line 11 in src/decorators/Attachment.ts

View workflow job for this annotation

GitHub Actions / Sanity

Forbidden non-null assertion
name,
mimeType,
});

return descriptor;
};
Expand Down
11 changes: 5 additions & 6 deletions src/decorators/FileAttachment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@ import realm from '../realms';

const allure = realm.runtime;

export function FileAttachment(fileName: string, contentType: string) {
export function FileAttachment(name: string, mimeType?: string) {
return function (
_target: object,
_propertyName: string,
descriptor: TypedPropertyDescriptor<(...arguments_: any[]) => any>,

Check warning on line 9 in src/decorators/FileAttachment.ts

View workflow job for this annotation

GitHub Actions / Sanity

Unexpected any. Specify a different type

Check warning on line 9 in src/decorators/FileAttachment.ts

View workflow job for this annotation

GitHub Actions / Sanity

Unexpected any. Specify a different type
) {
descriptor.value = allure.createFileAttachment(
fileName,
descriptor.value!,
contentType,
);
descriptor.value = allure.createFileAttachment(descriptor.value!, {

Check warning on line 11 in src/decorators/FileAttachment.ts

View workflow job for this annotation

GitHub Actions / Sanity

Forbidden non-null assertion
name,
mimeType,
});

return descriptor;
};
Expand Down
2 changes: 1 addition & 1 deletion src/decorators/Step.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import realm from '../realms';
import type { ParameterOrString } from '../utils/types';
import type { ParameterOrString } from '../runtime';

const allure = realm.runtime;

Expand Down
7 changes: 6 additions & 1 deletion src/environment/decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ export function WithAllure2<E extends WithEmitter>(
.on('hook_success', this.#executableSuccess.bind(this))
.on('test_fn_start', this.#executableStart.bind(this))
.on('test_fn_success', this.#executableSuccess.bind(this))
.on('test_fn_failure', this.#executableFailure.bind(this));
.on('test_fn_failure', this.#executableFailure.bind(this))
.on('teardown', this.#flushFiles.bind(this));
}

#addHook({
Expand Down Expand Up @@ -103,6 +104,10 @@ export function WithAllure2<E extends WithEmitter>(
state.currentMetadata.assign(PREFIX, metadata);
}

async #flushFiles() {
await realm.runtime.flush();
}

// eslint-disable-next-line no-empty-pattern
#executableSuccess({}: ForwardedCircusEvent) {
const metadata: AllureTestStepMetadata = {
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import realm from './realms';
import type { IAllureRuntime } from './runtime';

export { JestAllure2Reporter } from './reporter/JestAllure2Reporter';
export { JestAllure2Reporter as default } from './reporter/JestAllure2Reporter';
export { ReporterOptions } from './options/ReporterOptions';
export * from './annotations';
export * from './decorators';

export const allure = realm.runtime;
export const allure = realm.runtime as IAllureRuntime;
30 changes: 30 additions & 0 deletions src/options/ReporterOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ export type ReporterOptions = {
* @default 'allure-results'
*/
resultsDir?: string;
/**
* Configures how external attachments are attached to the report.
*/
attachments?: AttachmentsOptions;
/**
* Configures the defect categories for the report.
*
Expand Down Expand Up @@ -90,13 +94,39 @@ export type ReporterOptions = {
};

export type ReporterConfig = Required<ReporterOptions> & {
attachments: Required<AttachmentsOptions>;
testCase: ResolvedTestCaseCustomizer;
testStep: ResolvedTestStepCustomizer;
categories: CategoriesCustomizer;
environment: EnvironmentCustomizer;
executor: ExecutorCustomizer;
};

export type SharedReporterConfig = Pick<
ReporterConfig,
'resultsDir' | 'overwrite' | 'attachments'
>;

export type AttachmentsOptions = {
/**
* Defines a subdirectory within the {@link ReporterOptions#resultsDir} where attachments will be stored.
* @default 'attachments'
*/
subDir?: string;
/**
* Specifies strategy for attaching files to the report by their path.
* - `copy` - copy the file to {@link AttachmentsOptions#subDir}
* - `move` - move the file to {@link AttachmentsOptions#subDir}
* - `ref` - use the file path as is
* @default 'ref'
* @see {@link AllureRuntime#createFileAttachment}
*/
fileHandler?: BuiltinFileHandler;
};

/** @see {@link AttachmentsOptions#fileHandler} */
export type BuiltinFileHandler = 'copy' | 'move' | 'ref';

/**
* Global customizations for how test cases are reported
*/
Expand Down
16 changes: 16 additions & 0 deletions src/options/composeOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
ResolvedTestCaseCustomizer,
TestCaseCustomizer,
TestStepCustomizer,
AttachmentsOptions,
} from './ReporterOptions';
import { aggregateLabelCustomizers } from './aggregateLabelCustomizers';
import { aggregateLinkCustomizers } from './aggregateLinkCustomizers';
Expand All @@ -23,6 +24,7 @@ export function composeOptions(

overwrite: custom.overwrite ?? base.overwrite,
resultsDir: custom.resultsDir ?? base.resultsDir,
attachments: composeAttachments(base.attachments, custom.attachments),
testCase: composeTestCaseCustomizers(base.testCase, custom.testCase),
testStep: composeTestStepCustomizers(
base.testStep as TestStepCustomizer,
Expand All @@ -40,6 +42,20 @@ export function composeOptions(
};
}

function composeAttachments(
base: Required<AttachmentsOptions>,
custom: AttachmentsOptions | undefined,
): Required<AttachmentsOptions> {
if (!custom) {
return base;
}

return {
subDir: custom?.subDir ?? base.subDir,
fileHandler: custom?.fileHandler ?? base.fileHandler,
};
}

function composeTestCaseCustomizers(
base: ResolvedTestCaseCustomizer,
custom: Partial<TestCaseCustomizer> | undefined,
Expand Down
4 changes: 4 additions & 0 deletions src/options/defaultOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ export function defaultOptions(): ReporterConfig {
const config: ReporterConfig = {
overwrite: true,
resultsDir: 'allure-results',
attachments: {
subDir: 'attachments',
fileHandler: 'ref',
},
testCase,
testStep,
environment: identity,
Expand Down
26 changes: 6 additions & 20 deletions src/realms/AllureRealm.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,16 @@
import { randomUUID } from 'node:crypto';
import path from 'node:path';
import fs from 'node:fs';
import os from 'node:os';

import { state } from 'jest-metadata';

import { AllureRuntime } from '../runtime';
import { OUT_DIR } from '../constants';

const fallbackAttachmentsFolder = path.join(os.tmpdir(), 'allure-attachments');
import { SHARED_CONFIG } from '../constants';
import { AttachmentsHandler } from '../runtime/AttachmentsHandler';
import type { SharedReporterConfig } from '../options';

export class AllureRealm {
runtime = new AllureRuntime({
metadataProvider: () => state.currentMetadata,
nowProvider: () => Date.now(),
placeAttachment: (name) => {
const attachmentsFolder = state.get(
OUT_DIR,
fallbackAttachmentsFolder,
) as string;
const extension = path.extname(name);
return path.join(attachmentsFolder, randomUUID() + extension);
},
writeAttachment: async (filePath, content) => {
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
await fs.promises.writeFile(filePath, content);
},
attachmentsHandler: new AttachmentsHandler(() => {
return state.get(SHARED_CONFIG) as SharedReporterConfig;
}),
});
}
16 changes: 9 additions & 7 deletions src/reporter/JestAllure2Reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ import type {
ReporterConfig,
ReporterOptions,
ResolvedTestStepCustomizer,
SharedReporterConfig,
TestCaseExtractorContext,
} from '../options/ReporterOptions';
} from '../options';
import { resolveOptions } from '../options';
import type { AllureTestStepMetadata } from '../metadata';
import { MetadataSquasher, StepExtractor } from '../metadata';
import { OUT_DIR, STOP, WORKER_ID } from '../constants';
import { SHARED_CONFIG, STOP, WORKER_ID } from '../constants';
import attempt from '../utils/attempt';
import isError from '../utils/isError';
import { ThreadService } from '../utils/ThreadService';
Expand All @@ -43,14 +44,19 @@ export class JestAllure2Reporter extends JestMetadataReporter {
this._globalConfig = globalConfig;
this._config = resolveOptions(options);

state.set(OUT_DIR, path.join(this._config.resultsDir, 'attachments'));
state.set(SHARED_CONFIG, {
resultsDir: this._config.resultsDir,
overwrite: this._config.overwrite,
attachments: this._config.attachments,
} as SharedReporterConfig);
}

async onRunStart(
results: AggregatedResult,
options: ReporterOnStartOptions,
): Promise<void> {
await super.onRunStart(results, options);

if (this._config.overwrite) {
await rimraf(this._config.resultsDir);
}
Expand Down Expand Up @@ -259,8 +265,4 @@ export class JestAllure2Reporter extends JestMetadataReporter {
}
}
}

getLastError(): void {
// TODO: investigate what this method is for
}
}
Loading

0 comments on commit 2fc8193

Please sign in to comment.