diff --git a/docs/api/01-docblocks.mdx b/docs/api/01-docblocks.mdx
new file mode 100644
index 00000000..f61c79c2
--- /dev/null
+++ b/docs/api/01-docblocks.mdx
@@ -0,0 +1,274 @@
+---
+sidebar_position: 2
+---
+
+# Docblocks
+
+:::info
+
+Docblocks cannot be applied to `describe` statements.
+If you want to apply a docblock to all tests in the file,
+you can put it on the first line of the file.
+
+:::
+
+Docblocks are a powerful way to add metadata to your tests using JSDoc-style comments. These annotations are parsed by `jest-allure2-reporter` to enhance your test reports with additional information.
+
+## Plain comments
+
+Plain comments act as [`@description`](#description--desc) annotations, when applied to a test case.
+
+```javascript
+/**
+ * This test demonstrates the addition operation.
+ */
+test('should add two numbers', () => {
+ expect(1 + 2).toBe(3);
+});
+```
+
+For hooks, they act as [`@displayName`](#displayname) annotations.
+
+```javascript
+/**
+ * Navigate to the home page
+ */
+beforeEach(async () => {
+ await page.goto('https://example.com');
+});
+````
+
+## `@description` / `@desc`
+
+Adds a Markdown description to a test case.
+
+```javascript
+/**
+ * @description
+ * This test demonstrates the addition operation.
+ */
+test('should add two numbers', () => {
+ expect(1 + 2).toBe(3);
+});
+```
+
+## `@descriptionHtml`
+
+Adds an HTML description to a test case.
+
+```javascript
+/**
+ * @descriptionHtml
+ * This test demonstrates the - operator.
+ */
+test('should subtract two numbers', () => {
+ expect(2 - 1).toBe(1);
+});
+```
+
+## `@displayName`
+
+Overrides test names specified in `test('...')` or `it('...')` in the test report.
+
+```javascript
+/**
+ * @displayName 1 + 1 = 2
+ */
+test('First test', () => {
+ expect(1 + 1).toBe(2);
+});
+```
+
+When applied to a hook, it sets a custom display name for the hook, similar to a [plain comment](#plain-comments):
+
+```javascript
+/**
+ * @displayName Custom "beforeEach" hook
+ */
+beforeEach(() => {
+ // Hook implementation
+});
+```
+
+## `@fullName`
+
+Sets a full name for a test case, which can be used for more detailed identification.
+By default, full names are also used for tracking test history across multiple runs or retries.
+
+```javascript
+/**
+ * @fullName Arithmetic > Addition > Valid assertion
+ */
+test('First test', () => {
+ expect(1 + 1).toBe(2);
+});
+```
+
+## `@historyId`
+
+Assigns a custom history ID to a test case, useful for tracking test history across multiple runs or retries.
+
+```javascript
+/**
+ * @historyId HISTORY-2
+ */
+test('First test', () => {
+ expect(2 + 2).toBe(3);
+});
+```
+
+## `@issue`
+
+Links an issue to a test case.
+
+```javascript
+/**
+ * @issue XMLRPC-15
+ */
+test('Proving the fix', () => {
+ expect(1 + 1).toBe(2);
+});
+```
+
+## `@owner`
+
+Specifies the owner of a test or suite.
+
+```javascript
+/**
+ * @owner John Doe
+ */
+describe('Suite maintained by John', () => {
+ test('First test', () => {
+ // Test implementation
+ });
+});
+```
+
+## `@package`
+
+Specifies the package for a test or suite, useful for organizing tests.
+
+```javascript
+/**
+ * @package e2e.pragmas
+ */
+describe('My service', () => {
+ /**
+ * @testMethod Alternative title for the test
+ */
+ test('should log a message', () => {
+ // Test implementation
+ });
+});
+```
+
+## `@severity`
+
+Sets the severity level for a test or suite.
+
+```javascript
+/**
+ * @severity critical
+ */
+describe('Test suite', () => {
+ test('Important test 1', () => {
+ expect(1 + 1).toBe(2);
+ });
+});
+```
+
+## `@epic`, `@feature`, `@story`
+
+Categorizes tests into epics and features for better organization.
+
+```javascript
+/**
+ * @epic Arithmetic operations
+ * @feature Addition
+ * @story Sane assumptions
+ */
+describe('Test suite', () => {
+ // Test cases
+});
+```
+
+## `@tag`
+
+Adds tags to a test or suite.
+
+```javascript
+/**
+ * @tag docblock, arithmetic
+ */
+describe('Test suite', () => {
+ /**
+ * @tag addition
+ */
+ test('First test', () => {
+ expect(1 + 1).toBe(2);
+ });
+});
+```
+
+## `@thread`
+
+Specifies a custom thread for concurrent tests.
+Do not use it unless you want to control tests on the [Timeline](#TODO) manually.
+
+```javascript
+/**
+ * @thread IV
+ */
+test('First test', () => {
+ expect(1 + 1).toBe(2);
+});
+```
+
+## `@tms`
+
+Links a test management system (TMS) case to a test.
+
+```javascript
+/**
+ * @tms TMS-1234
+ */
+test('should be linked to a TMS ticket', () => {
+ expect(1 + 1).toBe(2);
+});
+```
+
+## `@url`
+
+Adds a custom URL link to a test or suite.
+
+```javascript
+/**
+ * @url https://en.wikipedia.org/wiki/Arithmetic 🔢 Arithmetic
+ */
+describe('Arithmetics', () => {
+ /**
+ * @url https://en.wikipedia.org/wiki/Addition ➕ Addition
+ */
+ test('1 + 1 = 2', () => {
+ expect(1 + 1).toBe(2);
+ });
+});
+```
+
+## `@parentSuite`, `@suite`, `@subSuite`
+
+Organizes tests into a hierarchical suite structure.
+
+```javascript
+/**
+ * @parentSuite Custom Parent Suite
+ * @suite Custom Suite
+ * @subSuite Custom Sub-Suite
+ */
+test('Test outside of any suite', () => {
+ // Test implementation
+});
+```
+
+These docblock annotations provide a powerful way to enrich your tests with metadata, improving the organization and readability of your Allure reports. By using these annotations, you can create more informative and structured test reports that help in better understanding and maintaining your test suite.
diff --git a/docs/api/02-annotations.mdx b/docs/api/02-annotations.mdx
new file mode 100644
index 00000000..8aee65af
--- /dev/null
+++ b/docs/api/02-annotations.mdx
@@ -0,0 +1,354 @@
+---
+sidebar_position: 3
+---
+
+# Annotations
+
+Annotations are functions that can be called before test suites or tests to add metadata to them. They share the same purpose as [docblocks](./01-docblocks.mdx), but they execute at runtime, allowing you to add metadata dynamically. Annotations can also be used to bypass the limitation of docblocks not being applicable to `describe` statements.
+
+To use annotations, you'll need to import them first, e.g.:
+
+```typescript
+import { $Description, $Link, $Owner } from 'jest-allure2-reporter/api';
+```
+
+Alternatively, you can [configure `jest-allure2-reporter/globals`](#TODO) to make them available globally.
+
+## `$Description`
+
+Adds a Markdown description to a test or suite.
+
+```javascript
+$Description('### Suite-level description')
+describe('Test suite', () => {
+ $Description('Demonstrates a **passing** test case')
+ test('First test', () => {
+ expect(1 + 1).toBe(2);
+ });
+
+ $Description('Demonstrates a **failing** test case')
+ test('Second test', () => {
+ expect(2 + 2).toBe(3);
+ });
+});
+```
+
+## `$DescriptionHtml`
+
+Adds an HTML description to a test or suite.
+
+```javascript
+$DescriptionHtml('
Suite-level description
')
+describe('Test suite', () => {
+ $DescriptionHtml('Demonstrates a passing test case')
+ test('First test', () => {
+ expect(1 + 1).toBe(2);
+ });
+
+ $DescriptionHtml('Demonstrates a failing test case')
+ test('Second test', () => {
+ expect(2 + 2).toBe(3);
+ });
+});
+```
+
+## `$DisplayName`
+
+Overrides test names specified in `test('...')` or `it('...')` in the test report. It can also be used to set custom names for hooks.
+
+```javascript
+describe('Test suite', () => {
+ $DisplayName('Custom "beforeEach" hook')
+ beforeEach(() => {
+ // Hooks can be renamed too
+ });
+
+ $DisplayName('1 + 1 = 2')
+ test('First test', () => {
+ expect(1 + 1).toBe(2);
+ });
+
+ $DisplayName('2 + 2 = 3')
+ test('Second test', () => {
+ expect(2 + 2).toBe(3);
+ });
+});
+```
+
+## `$FullName`
+
+Sets the full name of a test, which can be used for more detailed identification or grouping.
+By default, full names are also used for tracking test history across multiple runs or retries.
+
+```javascript
+describe('Test suite', () => {
+ $FullName('Arithmetic > Addition > Valid assertion')
+ test('First test', () => {
+ expect(1 + 1).toBe(2);
+ });
+
+ $FullName('Arithmetic > Addition > Invalid assertion')
+ test('Second test', () => {
+ expect(2 + 2).toBe(3);
+ });
+});
+```
+
+## `$HistoryId`
+
+Assigns a unique identifier to a test, which can be used to track test history across multiple runs or retries.
+
+```javascript
+describe('Test suite', () => {
+ $HistoryId('HISTORY-1')
+ test('First test', () => {
+ expect(2 + 2).toBe(3);
+ });
+
+ $HistoryId('HISTORY-1')
+ test('Considered as repetition of the first test', () => {
+ // Open "Retries" tab in the report to see the history of this test
+ expect(1 + 1).toBe(2);
+ });
+});
+```
+
+## `$Issue`
+
+Links a test to an issue in your issue tracking system.
+
+URLs are built using [the template strings](#TODO) configured in the reporter options.
+
+```javascript
+describe('Regression tests', () => {
+ $Issue('XMLRPC-15')
+ test('Proving the fix', () => {
+ expect(1 + 1).toBe(2);
+ });
+
+ $Issue('XMLRPC-16')
+ test.failing('Demonstrating an existing bug', () => {
+ expect(2 + 2).toBe(3);
+ });
+});
+```
+
+## `$Label`
+
+Adds a custom label to a test or suite.
+
+```javascript
+$Label('testType', 'screenshotDiff');
+describe('Screenshot tests', () => {
+ test('What the client explained', () => {
+ allure.fileAttachment('fixtures/screenshots/expected.png', 'expected');
+ allure.fileAttachment('fixtures/screenshots/actual.png', 'actual');
+ allure.fileAttachment('fixtures/screenshots/diff.png', 'diff');
+
+ // and what the programmer coded ¯\_(ツ)_/¯
+ expect('programmer').toHaveProperty('telepathy');
+ });
+});
+```
+
+## `$Link`
+
+Adds a link to external resources related to the test.
+
+```javascript
+$Link('https://en.wikipedia.org/wiki/Arithmetic', '🔢 Arithmetic')
+describe('Arithmetics', () => {
+ $Link('https://en.wikipedia.org/wiki/Addition', '➕ Addition')
+ test('1 + 1 = 2', () => {
+ expect(1 + 1).toBe(2);
+ });
+
+ $Link('https://en.wikipedia.org/wiki/Division_(mathematics)', '➗ Division')
+ test('3 / 2 = 1.5', () => {
+ expect(3 / 2).toBe(1.5);
+ });
+});
+```
+
+The `$Link` annotation also accepts a `Link` object as an argument:
+
+```javascript
+$Link({ name: '🔢 Arithmetic', type: 'wiki', url: 'https://en.wikipedia.org/wiki/Arithmetic' });
+```
+
+Advanced users may pass empty strings to `url` if they want to [build the URL dynamically](#TODO) via a config function:
+
+```javascript
+$Link({ name: 'Arithmetic', type: 'wiki', url: '' });
+```
+
+## `$Owner`
+
+Specifies the owner of a test or suite.
+
+```javascript
+$Owner('John Doe');
+describe('Suite maintained by John', () => {
+ test('First test', () => {
+ // John maintains this test
+ });
+
+ test('Second test', () => {
+ // John maintains this test too
+ });
+
+ $Owner('Jane Doe')
+ test('Third test', () => {
+ // Unlike the other tests, Jane maintains this one
+ });
+});
+```
+
+## `$Package`, `$TestClass` `$TestMethod`
+
+Specifies the package or module that a test belongs to.
+
+```javascript
+$Package('e2e.annotations')
+$TestClass('e2e.annotations.MyService')
+describe('My service', () => {
+
+ $TestMethod('Alternative title for the test')
+ test('should log a message', () => {
+ // Open "Packages" view to see this test grouped under "e2e.annotations"
+ });
+});
+```
+
+## `$Parameter`
+
+Adds a parameter to a test or suite.
+
+```javascript
+describe('Login screen', () => {
+ $Parameter('auth.NewLoginScreen', 'on')
+ test('should display the new login screen', () => {
+ // Visit the login page
+ // Assert the new login screen is displayed
+ });
+});
+```
+
+The `$Parameter` annotation accepts any values and supports optional attributes:
+
+```javascript
+$Parameter('Some index', 2);
+$Parameter('secret', 'P/55VV0RD', { mode: 'masked' });
+$Parameter({
+ name: 'Debug Info',
+ value: {boring: "debug", info: "here"},
+ excluded: !process.env.DEBUG,
+});
+```
+
+## `$Severity`
+
+Sets the [severity level](#TODO) of a test or suite.
+
+```javascript
+$Severity('critical');
+describe('Test suite', () => {
+ test('Important test 1', () => {
+ expect(1 + 1).toBe(2);
+ });
+
+ test('Important test 2', () => {
+ expect(2 + 2).toBe(4);
+ });
+
+ $Severity('trivial');
+ test('Unimportant test', () => {
+ expect(true).toBe(true);
+ });
+});
+```
+
+## `$Tag`
+
+Adds one or more [tags](#TODO) to a test or suite.
+
+```javascript
+$Tag('dsl', 'arithmetic');
+describe('Test suite', () => {
+ $Tag('addition')
+ test('First test', () => {
+ expect(1 + 1).toBe(2);
+ });
+
+ $Tag('division')
+ test('Second test', () => {
+ expect(3 / 2).toBe(1.5);
+ });
+});
+```
+
+## `$Thread`
+
+Specifies a custom thread for concurrent tests.
+Do not use it unless you want to control tests on the [Timeline](#TODO) manually.
+
+```javascript
+describe('Test suite', () => {
+ $Thread('T1')
+ test.concurrent('First test', () => {
+ expect(1 + 1).toBe(2);
+ });
+
+ $Thread('T2')
+ test.concurrent('Second test', () => {
+ expect(3 / 2).toBe(1.5);
+ });
+});
+```
+
+## `$TmsLink`
+
+Links a test to a Test Management System (TMS) entry.
+
+URLs are built using [the template strings](#TODO) configured in the reporter options.
+
+```javascript
+$TmsLink('TMS-1234')
+test('should be linked to a TMS ticket', () => {
+ expect(1 + 1).toBe(2);
+});
+```
+
+## `$Epic`, `$Feature`, `$Story`
+
+There are also annotations for Behavior-Driven Development (BDD) style testing:
+
+```javascript
+$Epic('Arithmetic operations')
+$Feature('Addition')
+describe('Test suite', () => {
+ $Story('Sane assumptions')
+ test('First test', () => {
+ expect(1 + 1).toBe(2);
+ });
+
+ $Story('Insane assumptions')
+ test('Second test', () => {
+ expect(2 + 2).toBe(3);
+ });
+});
+```
+
+## `$ParentSuite`, `$Suite`, `$SubSuite`
+
+Annotations for organizing test suites in a hierarchical structure:
+
+```javascript
+$ParentSuite('Custom Parent Suite')
+$Suite('Custom Suite')
+$SubSuite('Custom Sub-Suite')
+test('Test outside of any suite', () => {
+ // This test will be placed under:
+ // Custom Parent Suite > Custom Suite > Custom Sub-Suite
+});
+```
diff --git a/docs/api/03-decorators.mdx b/docs/api/03-decorators.mdx
new file mode 100644
index 00000000..fb19de77
--- /dev/null
+++ b/docs/api/03-decorators.mdx
@@ -0,0 +1,266 @@
+---
+sidebar_position: 4
+---
+
+# Decorators
+
+Decorators in `jest-allure2-reporter` provide a powerful way to add metadata and behavior to your test methods, particularly when working with class-based test structures. They offer a clean, declarative syntax for enhancing your tests with Allure-specific features.
+
+To use decorators, you'll need to import them first, e.g.:
+
+```typescript
+import { Step, Attachment, FileAttachment } from 'jest-allure2-reporter/api';
+```
+
+Alternatively, you can [configure `jest-allure2-reporter/globals`](#TODO) to make them available globally.
+
+Understood. I'll focus on providing a more thorough and accurate reference for the `Attachment` and `FileAttachment` decorators based on the type definitions you've provided. Here are the revised sections:
+
+## `Attachment`
+
+The `Attachment` decorator is used to add content attachments to test cases or steps in your Allure report.
+
+### Syntax
+
+```typescript
+@Attachment(options: ContentAttachmentOptions)
+@Attachment(name: string, mimeType?: string)
+```
+
+### Parameters
+
+1. `options`: An object of type `ContentAttachmentOptions`
+ - `name`: (Required) A string representing the name of the attachment
+ - `mimeType`: (Optional) A string specifying the MIME type of the attachment
+ - `handler`: (Optional) A custom [attachment handler function](TODO) or its string alias
+
+2. `name`: A string representing the name of the attachment
+3. `mimeType`: (Optional) A string specifying the MIME type of the attachment
+
+### Usage
+
+You can use the `Attachment` decorator in two ways:
+
+1. With an options object:
+
+```typescript
+class TestClass {
+ @Attachment({
+ name: 'Attachment {{0}}',
+ mimeType: 'text/plain',
+ handler: 'gzip'
+ })
+ createAttachment(id: string): string {
+ return `Content for attachment ${id}`;
+ }
+}
+```
+
+2. With name and optional MIME type:
+
+```typescript
+class TestClass {
+ @Attachment('Attachment {{0}}', 'text/plain')
+ createAttachment(id: string): string {
+ return `Content for attachment ${id}`;
+ }
+}
+```
+
+The decorated method should return the content to be attached. The content can be a string or a Buffer.
+
+### Examples
+
+```typescript
+class HtmlGenerator {
+ @Attachment('Say: {{0}}', 'text/html')
+ static say(message: string) {
+ return `
${message}
`;
+ }
+}
+
+test('should attach HTML via a decorator', () => {
+ expect(HtmlGenerator.say('Hello, world!')).toBe('
Hello, world!
');
+});
+```
+
+This will create an HTML attachment named "Say: Hello, world!" with the content `
Hello, world!
`.
+
+## `FileAttachment`
+
+The `FileAttachment` decorator is used to add file attachments to test cases or steps in your Allure report.
+
+### Syntax
+
+```typescript
+@FileAttachment(options?: FileAttachmentOptions)
+@FileAttachment(name?: string, mimeType?: string)
+```
+
+### Parameters
+
+1. `options`: An object of type `FileAttachmentOptions`
+ - `name`: (Optional) A string representing the name of the attachment
+ - `mimeType`: (Optional) A string specifying the MIME type of the attachment
+ - `handler`: (Optional) A custom [attachment handler function](TODO) or its string alias
+
+2. `name`: (Optional) A string representing the name of the attachment
+3. `mimeType`: (Optional) A string specifying the MIME type of the attachment
+
+### Usage
+
+You can use the `FileAttachment` decorator in two ways:
+
+1. With an options object:
+
+```typescript
+class TestClass {
+ @FileAttachment({
+ name: 'File {{0}}',
+ mimeType: 'text/plain',
+ handler: 'copy'
+ })
+ attachFile(fileName: string): string {
+ return `/path/to/${fileName}`;
+ }
+}
+```
+
+2. With optional name and MIME type:
+
+```typescript
+class TestClass {
+ @FileAttachment('File {{0}}', 'text/plain')
+ attachFile(fileName: string): string {
+ return `/path/to/${fileName}`;
+ }
+}
+```
+
+The decorated method should return the path to the file that should be attached.
+
+### Examples
+
+```typescript title="source-code-attacher.test.ts"
+import path from 'node:path';
+
+class SourceCodeAttacher {
+ @FileAttachment('{{0}}', 'text/plain')
+ static thisFile() {
+ return __filename;
+ }
+}
+
+test('should attach the file itself via a decorator', () => {
+ expect(SourceCodeAttacher.thisFile()).toBe(__filename);
+});
+```
+
+This will create a file attachment with the name of the file and the content of the file at the specified path.
+
+Both `Attachment` and `FileAttachment` decorators support using handlebars notation (`{{0}}`, `{{1}}`, etc.) in the attachment name to include method parameters.
+
+## `@Step`
+
+The `@Step` decorator marks a method as a test step, which will be reported in the Allure report. It allows you to create a hierarchical structure of steps within your tests, making them more readable and easier to debug.
+
+### Syntax
+
+```typescript
+@Step(name: string, args?: UserParameter[])
+```
+
+### Parameters
+
+- `name: string` - A description template for the step. It can include placeholders for method parameters using handlebars notation (e.g., `{{0}}`, `{{1}}`, etc.).
+- `args?: UserParameter[]` (optional) - An array of parameter definitions to be included in the step report.
+
+`UserParameter` can be one of the following:
+- A string representing the parameter name
+- An object which can include:
+ - `name: string` - The name of the parameter
+ - `excluded?: boolean` - If true, the parameter will be excluded from `allure-results`
+ - `mode?: 'hidden' | 'masked' | 'default'` - Determines how the parameter is displayed in the generated report
+
+### Usage
+
+Apply the `@Step` decorator to methods in your test classes:
+
+```typescript
+class TestClass {
+ @Step('Perform action with {{0}} and {{1}}')
+ performAction(param1: string, param2: number): void {
+ // Method implementation
+ }
+}
+```
+
+### Examples
+
+Basic usage:
+
+```typescript
+class Calculator {
+ @Step('Add {{a}} and {{b}}', ['a', 'b'])
+ add(a: number, b: number): number {
+ return a + b;
+ }
+}
+
+test('addition', () => {
+ const calculator = new Calculator();
+ expect(calculator.add(2, 3)).toBe(5);
+});
+```
+
+Using parameter definitions:
+
+```typescript
+class UserService {
+ @Step('Login as {{username}}', [
+ 'username',
+ { name: 'password', mode: 'masked' }
+ ])
+ login(username: string, password: string): boolean {
+ // Login implementation
+ return true;
+ }
+}
+
+test('user login', () => {
+ const userService = new UserService();
+ expect(userService.login('john.doe', 'secret123')).toBe(true);
+});
+```
+
+In this example, the password will be masked in the Allure report.
+
+Nested steps:
+
+```typescript
+class ComplexOperation {
+ @Step('Perform complex operation')
+ performOperation(): number {
+ const result1 = this.step1();
+ const result2 = this.step2();
+ return result1 + result2;
+ }
+
+ @Step('Step 1')
+ private step1(): number {
+ return 5;
+ }
+
+ @Step('Step 2')
+ private step2(): number {
+ return 7;
+ }
+}
+
+test('complex operation', () => {
+ const operation = new ComplexOperation();
+ expect(operation.performOperation()).toBe(12);
+});
+```
+
+This will create a hierarchical structure of steps in the Allure report, showing the main operation and its substeps.
diff --git a/docs/api/04-runtime-api.mdx b/docs/api/04-runtime-api.mdx
new file mode 100644
index 00000000..fb43ba05
--- /dev/null
+++ b/docs/api/04-runtime-api.mdx
@@ -0,0 +1,205 @@
+---
+sidebar_position: 5
+---
+
+# Runtime API
+
+Runtime API provides methods to dynamically add information to your test reports during test execution. This API is accessible through the `allure` object, which is globally available when using the `jest-allure2-reporter`.
+
+:::info Note
+
+Using Runtime API outside of test code is possible, but not recommended for casual users,
+as it has caveats and undocumented behavior.
+
+:::
+
+## Metadata Methods
+
+### `allure.description(string)`
+
+Sets a Markdown description for the current test case.
+
+```javascript
+allure.description('This test verifies the login functionality.');
+```
+
+### `allure.descriptionHtml(string)`
+
+Sets an HTML description for the current test case.
+
+```javascript
+allure.descriptionHtml('
Login Test
' +
+ '
This test verifies the login functionality.
');
+```
+
+### `allure.epic(string)`
+
+Sets the epic for the current test case.
+
+```javascript
+allure.epic('User Authentication');
+```
+
+### `allure.feature(string)`
+
+Sets the feature for the current test case.
+
+```javascript
+allure.feature('Login');
+```
+
+### `allure.story(string)`
+
+Sets the user story for the current test case.
+
+```javascript
+allure.story('User can log in with valid credentials');
+```
+
+### `allure.suite(string)`
+
+Sets the suite name for the current test case.
+
+```javascript
+allure.suite('Authentication Tests');
+```
+
+### `allure.label(name, string)`
+
+Adds a custom label to the current test case.
+
+```javascript
+allure.label('custom', 'value');
+```
+
+### `allure.parameter(name, value)`
+
+Adds a parameter to the current test case or step.
+
+```javascript
+allure.parameter('username', 'testuser');
+```
+
+## Link Methods
+
+### `allure.link(url[, name])`
+
+Adds a link to the current test case.
+
+```javascript
+allure.link('https://example.com', 'Example Link');
+```
+
+### `allure.issue(name[, url])`
+
+Adds an issue link to the current test case.
+
+```javascript
+allure.issue('JIRA-123');
+```
+
+### `allure.tms(name[, url])`
+
+Adds a Test Management System (TMS) link to the current test case.
+
+```javascript
+allure.tms('TMS-456');
+```
+
+## Status Methods
+
+### `allure.status(status)`
+
+Sets the status for the current test case or step.
+
+```javascript
+allure.status('failed');
+```
+
+### `allure.statusDetails({ message, trace })`
+
+Sets additional status details for the current test case or step.
+
+```javascript
+allure.statusDetails({ message: 'Test failed due to...', trace: 'Error stack trace' });
+```
+
+## Attachments
+
+### `allure.attachment(string, content[, type])`
+
+Adds an attachment to the current test case or step.
+
+```javascript
+allure.attachment('Screenshot', Buffer.from('...'), 'image/png');
+```
+
+### `allure.createAttachment(name: string, content: () => string | Buffer, type?: string)`
+
+Creates a reusable attachment function.
+
+```javascript
+const takeScreenshot = allure.createAttachment('Screenshot', () => Buffer.from('...'), 'image/png');
+takeScreenshot();
+```
+
+## Steps
+
+### `allure.step(name, fn)`
+
+Adds a step to the current test case.
+
+```javascript
+allure.step('Enter username', () => {
+ // Step implementation
+});
+```
+
+### `allure.createStep(name, fn)`
+
+Creates a reusable step function.
+
+```javascript
+const login = allure.createStep('Login', (username, password) => {
+ // Login implementation
+});
+login('testuser', 'password123');
+```
+
+## Advanced Methods
+
+### `allure.$bind(options)`
+
+Binds an instance of the Allure Runtime API to a specific time and context in your test code.
+
+```javascript
+const boundAllure = allure.$bind({ metadata: true, time: false });
+```
+
+Use it when you need to report some metadata post-factum, e.g. add some information about
+the test after leaving its body, e.g.:
+
+```javascript
+let allure$;
+
+test('...', () => {
+ allure$ = allure.$bind();
+ // ...
+});
+
+afterEach(() => {
+ allure$.parameter('Free Memory', os.freemem());
+});
+```
+
+### `allure.$plug(callback)`
+
+Registers a runtime plugin to extend Allure functionality.
+
+```javascript
+allure.$plug((context) => {
+ // Plugin implementation
+});
+```
+
+Read more about [plugins](#TODO).
diff --git a/docs/api/05-plugin-api.mdx b/docs/api/05-plugin-api.mdx
new file mode 100644
index 00000000..3587c12a
--- /dev/null
+++ b/docs/api/05-plugin-api.mdx
@@ -0,0 +1,159 @@
+---
+sidebar_position: 7
+---
+
+# Plugin API
+
+The Plugin API in `jest-allure2-reporter` allows you to extend and customize the functionality of Allure reporting. This powerful feature enables you to add custom behaviors, modify existing ones, or integrate with other tools and services.
+
+## Overview
+
+Plugins are registered using the `allure.$plug()` method, which takes a callback function as its argument. This callback receives a context object that provides access to various aspects of the Allure runtime.
+
+```javascript
+import { allure } from 'jest-allure2-reporter/api';
+
+allure.$plug((context) => {
+ // Plugin implementation
+});
+```
+
+## Plugin Context
+
+The plugin context (`AllureRuntimePluginContext`) provides the following properties and methods:
+
+### `runtime`
+
+Type: `IAllureRuntime`
+
+Gives access to the [Allure runtime](./04-runtime-api.mdx), allowing you to interact with the core Allure functionality.
+
+### `handlebars`
+
+Type: `HandlebarsAPI`
+
+Provides access to the Handlebars templating engine, which can be useful for generating custom content.
+
+### `contentAttachmentHandlers`
+
+Type: `Record`
+
+A collection of handlers for content attachments. You can add custom handlers or modify existing ones.
+
+### `fileAttachmentHandlers`
+
+Type: `Record`
+
+A collection of handlers for file attachments. You can add custom handlers or modify existing ones.
+
+### `inferMimeType`
+
+Type: `MIMEInferer`
+
+A function to infer the MIME type of attachments. You can replace this with a custom implementation if needed.
+
+## Examples
+
+### Custom Attachment Handler
+
+Here's an example of how you might use the Plugin API to add a custom attachment handler:
+
+```javascript
+import { allure } from 'jest-allure2-reporter/api';
+
+allure.$plug((context) => {
+ context.contentAttachmentHandlers['json'] = async ({ content, name, outDir }) => {
+ const jsonContent = JSON.stringify(JSON.parse(content), null, 2);
+ const fileName = `${name}.json`;
+ const filePath = path.join(outDir, fileName);
+
+ await fs.writeFile(filePath, jsonContent);
+
+ return fileName;
+ };
+});
+
+// ...
+
+allure.attachment('{"key": "value"}', {
+ name: 'my-attachment',
+ type: 'application/json',
+ handler: 'json',
+});
+```
+
+This plugin adds a new content attachment handler for 'json' type.
+It prettifies JSON content before saving it as an attachment.
+
+### Custom MIME Type Inference
+
+You can also customize how MIME types are inferred for attachments:
+
+```javascript
+import { allure } from 'jest-allure2-reporter/api';
+
+allure.$plug((context) => {
+ context.inferMimeType = ({ sourcePath, content }) => {
+ const mimeType = 'application/vnd.allure.image.diff';
+
+ if (sourcePath && sourcePath.endsWith('.screenshot.json'))
+ return mimeType;
+
+ if (content && content.expected && content.actual && content.diff)
+ return mimeType;
+
+ return undefined; // use default inference
+ };
+});
+```
+
+### Adding a Handlebars Helper
+
+The Plugin API allows you to extend the Handlebars templating engine used by `jest-allure2-reporter`.
+This can be particularly useful for customizing how information is displayed in your reports.
+
+```javascript
+import { allure, Step } from 'jest-allure2-reporter/api';
+
+// Register the plugin
+allure.$plug((context) => {
+ // Add a custom Handlebars helper
+ context.handlebars.registerHelper('uppercase', function(str) {
+ return str.toUpperCase();
+ });
+});
+
+// Use the custom helper in a step
+class TestHelper {
+ @Step('Perform {{uppercase action}}', ['action'])
+ performAction(action) {
+ // Perform the action
+ console.log(`Performing action: ${action}`);
+ }
+}
+
+// In your test
+test('Custom Handlebars helper example', () => {
+ const helper = new TestHelper();
+ helper.performAction('click');
+});
+```
+
+This will result in a step in your Allure report with the name "Perform action: CLICK" instead of "Perform action: click".
+
+Using custom Handlebars helpers like this allows you to format and manipulate the text in your step names, descriptions, and other areas where Handlebars templates are used in Allure reporting. This can lead to more readable and informative reports, especially when dealing with complex test scenarios or when you want to highlight certain information in your steps.
+
+## Best Practices
+
+1. **Avoid Side Effects**: Your plugins should not have unintended side effects on the test execution or reporting process.
+2. **Error Handling**: Implement proper error handling in your plugins to prevent crashes or unexpected behavior.
+3. **Performance**: Be mindful of the performance impact of your plugins, especially for large test suites.
+4. **Documentation**: If you're creating plugins for others to use, provide clear documentation on how to use and configure them.
+
+## Limitations and Considerations
+
+- The Plugin API is powerful but should be used judiciously. Overusing or misusing plugins can lead to complex and hard-to-maintain test setups.
+- Be aware of potential conflicts between multiple plugins. If you're using multiple plugins, ensure they don't interfere with each other.
+- The Plugin API is subject to change in future versions of `jest-allure2-reporter`. Always refer to the latest documentation when upgrading.
+
+Remember to use this feature responsibly and in alignment with your team's testing and reporting strategies.
diff --git a/docs/api/config/01-presets.mdx b/docs/api/config/01-presets.mdx
new file mode 100644
index 00000000..33e6502a
--- /dev/null
+++ b/docs/api/config/01-presets.mdx
@@ -0,0 +1,151 @@
+# Presets
+
+## Overview
+
+The `jest-allure2-reporter` offers a powerful configuration system that allows you to customize various aspects of your Allure reports. One of the key features of this system is the ability to use presets and extend configurations, which promotes reusability and helps maintain a clean, organized configuration across different projects or test suites.
+
+## Extends
+
+The `extends` option in `jest-allure2-reporter` configuration allows you to inherit and merge configurations from other preset files or objects. This feature is particularly useful for:
+
+1. Sharing common configurations across multiple projects
+2. Creating base configurations that can be extended and customized for specific use cases
+3. Organizing complex configurations into smaller, more manageable pieces
+
+The `extends` option can accept a string (path to a preset file), an object (inline preset), or an array of strings and objects.
+
+## Usage
+
+### Basic Usage
+
+To use the `extends` feature, add it to your `jest-allure2-reporter` configuration:
+
+```javascript
+module.exports = {
+ // ... other Jest config
+ reporters: [
+ 'default',
+ ['jest-allure2-reporter', {
+ extends: './my-preset.js',
+ // Additional configurations...
+ }]
+ ]
+};
+```
+
+Accordingly, in `my-preset.js`, you can define your configurations:
+
+```javascript
+module.exports = {
+ testCase: {
+ labels: {
+ owner: 'Team A',
+ },
+ },
+};
+```
+
+### Multiple Presets
+
+You can extend multiple presets by using an array:
+
+```javascript
+module.exports = {
+ // ... other Jest config
+ reporters: [
+ 'default',
+ ['jest-allure2-reporter', {
+ extends: [
+ './base-preset.js',
+ './custom-labels-preset.js',
+ './attachment-handling-preset.js'
+ ],
+ // Additional configurations...
+ }]
+ ]
+};
+```
+
+When using `extends` multiple times, or when using `extends` array,
+configurations are merged recursively.
+
+In case of conflicts, later presets in the array override earlier ones.
+The [customizer functions](./02-customizers.mdx), however, may use `({ value })` from the context to access the computed value from the previous preset.
+
+### Inline Presets
+
+You can also use inline objects as presets:
+
+```javascript
+module.exports = {
+ // ... other Jest config
+ reporters: [
+ 'default',
+ ['jest-allure2-reporter', {
+ extends: [
+ {
+ testCase: {
+ labels: {
+ severity: 'high',
+ },
+ },
+ }
+ ],
+ // Additional configurations...
+ }]
+ ]
+};
+```
+
+## Preset Structure
+
+A preset is typically a JavaScript file that exports `jest-allure2-reporter` options.
+
+```javascript title="my-preset.js"
+module.exports = {
+ testCase: {
+ links: {
+ issue: 'https://jira.example.com/browse/{{name}}',
+ tms: 'https://tms.example.com/case/{{name}}'
+ },
+ },
+ // Other configuration options...
+};
+```
+
+## Example: Comprehensive Setup
+
+Here's an example of a comprehensive setup using presets:
+
+```javascript title="node_modules/@my-org/my-project-allure-preset/index.js"
+module.exports = {
+ testCase: {
+ labels: {
+ owner: 'John Doe',
+ },
+ links: {
+ issue: 'https://github.com/my-org/my-project/issues/{{name}}',
+ },
+ },
+};
+
+```javascript title="jest.config.js"
+const some_other_preset = require('./some-other-preset');
+
+module.exports = {
+ // ... other Jest config
+ reporters: [
+ 'default',
+ ['jest-allure2-reporter', {
+ extends: [
+ '@my-org/my-project-allure-preset',
+ some_other_preset,
+ ],
+ }]
+ ]
+};
+```
+
+In this setup, the final configuration will merge the base preset and an inline preset, allowing for a clean and organized configuration.
+
+By effectively using the `extends` feature and presets, you can create a scalable and organized configuration system for `jest-allure2-reporter`, making it easier to manage reporting settings across different projects or test suites.
diff --git a/docs/api/config/02-customizers.mdx b/docs/api/config/02-customizers.mdx
new file mode 100644
index 00000000..1f6471ec
--- /dev/null
+++ b/docs/api/config/02-customizers.mdx
@@ -0,0 +1,169 @@
+# Customizers
+
+## Overview
+
+Test customizers are powerful functions used in the configuration of `jest-allure2-reporter` to modify or enhance various aspects of test reporting. They provide a flexible way to customize how test information is processed and presented in the Allure report.
+
+A test customizer is a function with the following general structure:
+
+```typescript
+type Customizer =
+ (context: Context) => Value;
+```
+
+where:
+- `Context` is an object containing relevant information about the current test or reporting environment;
+- `Value` is the expected return type, which varies depending on the specific customizer being used.
+
+## Usage
+
+Customizers are typically used in the `jest-allure2-reporter` configuration. For example:
+
+```javascript
+module.exports = {
+ // ... other Jest config
+ reporters: [
+ 'default',
+ ['jest-allure2-reporter', {
+ testCase: {
+ name: ({ testCase }) => `Custom: ${testCase.fullName}`,
+ // Other customizers...
+ }
+ }]
+ ]
+};
+```
+
+## Helpers
+
+Helpers are utility functions available within the customizer context as `$`. They provide additional functionality to assist in customizing test reports.
+
+### Registering Helpers
+
+Helpers are registered in the `jest-allure2-reporter` configuration using customizers inside `helpers` object:
+
+```javascript
+module.exports = {
+ // ... other Jest config
+ reporters: [
+ 'default',
+ ['jest-allure2-reporter', {
+ helpers: {
+ relativize: (globalContext) => {
+ const rootDir = globalContext.globalConfig.rootDir;
+ // Return a helper function
+ return (filePath) => path.relative(rootDir, filePath.join(path.sep));
+ }
+ }
+ }]
+ ]
+};
+```
+
+### Using Helpers
+
+Once registered, helpers can be accessed within customizers via the `$` property of the context:
+
+```javascript
+module.exports = {
+ // ... other Jest config
+ reporters: [
+ 'default',
+ ['jest-allure2-reporter', {
+ testCase: {
+ name: ({ $, filePath, value }) => `${value} (${$.relativize(filePath)})`
+ }
+ }]
+ ]
+};
+```
+
+## Context
+
+The context is an object passed to customizers, providing relevant information and access to helpers. It's a crucial part of the customizer functionality, allowing access to test-specific data and utility functions.
+
+### Context Structure
+
+The context object typically includes:
+
+- Test-specific information (e.g., `testCase`, `file`, `result`)
+- Global configuration (`globalConfig`)
+- Helpers (`$`)
+- Other relevant data depending on the customizer type
+
+Example context structure:
+
+```typescript
+interface CustomizerContext {
+ testCase: TestCaseResult;
+ file: TestFile;
+ result: TestResult;
+ globalConfig: Config.GlobalConfig;
+ $: Helpers;
+ // Other properties...
+}
+```
+
+### Accessing Context in Customizers
+
+Customizers can destructure the context to access needed properties:
+
+```javascript
+module.exports = {
+ // ... other Jest config
+ reporters: [
+ 'default',
+ ['jest-allure2-reporter', {
+ testCase: {
+ name: ({ testCase, file, $ }) => {
+ // Use testCase, file, and helpers here
+ return $.customHelper(`${file.path}:${testCase.fullName}`);
+ }
+ }
+ }]
+ ]
+};
+```
+
+## Common Customizer Types
+
+1. **Test Case Customizers**
+ - Scope: Individual test cases
+ - Purpose: Modify test case reports with specific details
+ - Use case: Enhancing test case metadata (e.g., name, description, status)
+
+2. **Test Step Customizers**
+ - Scope: Individual test steps, including hooks
+ - Purpose: Adjust information for each step within a test
+ - Use case: Adding granular details to test execution flow
+
+3. **Test File Customizers**
+ - Scope: Entire test files (reported as pseudo-test cases)
+ - Purpose: Apply modifications at the file level
+ - Use case: Grouping or categorizing tests by file
+
+4. **Test Run Customizers**
+ - Scope: Overall test run (reported as a pseudo-test case)
+ - Purpose: Adjust the high-level test run report
+ - Use case: Providing summary information for the entire test suite
+
+5. **Helper Customizers**
+ - Scope: Global
+ - Purpose: Define custom utility functions
+ - Use case: Creating reusable logic for other customizers
+
+6. **Global Customizers**
+ - Scope: Global report elements
+ - Purpose: Modify overarching report components
+ - Use case: Adding environment details, categories, or executor information
+
+Each customizer type has access to a specific context and can modify various properties relevant to its scope. As you become more familiar with the system, you can explore the detailed contexts and properties available for each type.
+
+## Best Practices
+
+1. Keep customizers pure and side-effect free.
+2. Use helpers for complex operations to keep customizers clean.
+3. Leverage TypeScript for better type checking and autocompletion.
+4. Document custom helpers and complex customizers for team understanding.
+
+By understanding and effectively using test customizers, helpers, and context, you can create highly tailored and informative Allure reports that meet your specific project needs.
diff --git a/docs/api/config/index.mdx b/docs/api/config/index.mdx
new file mode 100644
index 00000000..90ee1474
--- /dev/null
+++ b/docs/api/config/index.mdx
@@ -0,0 +1,226 @@
+---
+sidebar_position: 6
+---
+
+# Configuration
+
+The `jest-allure2-reporter` can be configured using the reporter options in your Jest configuration file.
+This reference outlines the available options and their usage.
+
+## Basic Configuration
+
+To use `jest-allure2-reporter`, add it to your Jest configuration:
+
+```javascript
+/** @type {import('@jest/types').Config.InitialOptions} */
+module.exports = {
+ reporters: [
+ 'default',
+ ['jest-allure2-reporter', {
+ // Reporter options go here
+ }],
+ ],
+ // Other Jest configurations...
+};
+```
+
+## Reporter Options
+
+### `overwrite`
+
+- Type: `boolean`
+- Default: `true`
+
+Determines whether to overwrite the existing results directory.
+
+```javascript
+overwrite: false
+```
+
+### `resultsDir`
+
+- Type: `string`
+- Default: `'allure-results'`
+
+Specifies the directory where test result files will be output.
+
+```javascript
+resultsDir: 'custom-allure-results'
+```
+
+### `injectGlobals`
+
+- Type: `boolean`
+- Default: `true`
+
+Controls whether Allure's global variables are injected into the test environment.
+
+```javascript
+injectGlobals: false
+```
+
+### `attachments`
+
+- Type: `object`
+
+Configures how external attachments are attached to the report.
+
+```javascript
+attachments: {
+ subDir: 'attachments',
+ fileHandler: 'copy',
+ contentHandler: 'write'
+}
+```
+
+#### `attachments.subDir`
+
+- Type: `string`
+- Default: `'attachments'`
+
+Defines a subdirectory within the `resultsDir` where attachments will be stored.
+
+#### `attachments.fileHandler`
+
+- Type: `'copy' | 'move' | 'ref' | string`
+- Default: `'ref'`
+
+Specifies the default strategy for attaching files to the report by their path.
+
+#### `attachments.contentHandler`
+
+- Type: `'write' | string`
+- Default: `'write'`
+
+Specifies the default strategy for attaching dynamic content to the report.
+
+### `sourceCode`
+
+- Type: `object`
+
+Configures how source code and docblocks are extracted from test files.
+
+```javascript
+sourceCode: {
+ enabled: true,
+ plugins: {
+ // Plugin configurations
+ }
+}
+```
+
+### `categories`
+
+- Type: `function`
+
+Configures the defect categories for the report.
+
+```javascript
+categories: ({ $ }) => [
+ {
+ name: 'Timeouts',
+ matchedStatuses: ['broken'],
+ messageRegex: /.*Exceeded timeout of.*/
+ }
+]
+```
+
+### `environment`
+
+- Type: `function`
+
+Configures the environment information that will be reported.
+
+```javascript
+environment: async ({ manifest }) => ({
+ 'Node.js Version': process.version,
+ 'Package Version': await manifest(m => m.version)
+})
+```
+
+### `executor`
+
+- Type: `function`
+
+Configures the executor information that will be reported.
+
+```javascript
+executor: ({ value }) => ({
+ name: 'Jenkins',
+ type: 'jenkins',
+ url: process.env.BUILD_URL,
+ buildOrder: process.env.BUILD_NUMBER
+})
+```
+
+### `helpers`
+
+- Type: `function`
+
+Customizes extractor helpers object to use later in the customizers.
+
+```javascript
+helpers: ({ $ }) => ({
+ ...$.helpers,
+ customHelper: () => { /* ... */ }
+})
+```
+
+### `testRun`
+
+- Type: `object`
+
+Customizes how to report test runs (sessions) as pseudo-test cases.
+
+```javascript
+testRun: {
+ uuid: ({ aggregatedResult }) => aggregatedResult.testResults[0].testFilePath,
+ name: 'Custom Test Run Name'
+}
+```
+
+### `testFile`
+
+- Type: `object`
+
+Customizes how to report test files as pseudo-test cases.
+
+```javascript
+testFile: {
+ name: ({ filePath }) => filePath.join('/'),
+ fullName: ({ filePath }) => filePath.join('.')
+}
+```
+
+### `testCase`
+
+- Type: `object`
+
+Customizes how test cases are reported.
+
+```javascript
+testCase: {
+ name: ({ test }) => test.title,
+ fullName: ({ test }) => test.fullName,
+ labels: {
+ severity: 'normal',
+ tag: ['unit', 'integration']
+ },
+ links: {
+ issue: 'https://jira.company.com/browse/{{name}}'
+ }
+}
+```
+
+### `testStep`
+
+- Type: `object`
+
+Customizes how individual test steps are reported.
+
+```javascript
+testStep: {
+ name: ({ value }) => `Step: ${value}`,
+ status: ({ value }) => value === 'broken' ? 'failed' : value
+}
+```
diff --git a/docs/api/index.mdx b/docs/api/index.mdx
new file mode 100644
index 00000000..7cde206a
--- /dev/null
+++ b/docs/api/index.mdx
@@ -0,0 +1,32 @@
+---
+sidebar_position: 1
+slug: /api
+---
+
+# Exports
+
+`jest-allure2-reporter` package provides several **entry points**:
+
+
+
jest-allure2-reporter
+
The reporter itself, which is responsible for collecting test results and generating a report.
+
jest-allure2-reporter/api
+
DSL to add additional metadata to your test definitions: $Link, $Owner.
+
`allure` runtime API to use inside your tests: allure.step, allure.attachment.
+
jest-allure2-reporter/globals
+
Opt-in typings to use with TypeScript if you want to avoid importing API every time.
+
+ Environment packages, to enable the annotations, media attachments and provide additional test data:
+
+
+
+
jest-allure2-reporter/environment-node
+
For Node.js tests.
+
jest-allure2-reporter/environment-jsdom
+
For browser tests.
+
jest-allure2-reporter/environment-decorator
+
For advanced use cases where you need to extend an already customized test environment.
+
+
+
+
diff --git a/docs/docs/config/01-grouping/02-by-story.mdx b/docs/docs/config/01-grouping/02-by-story.mdx
index 11a4f3fd..541bed05 100644
--- a/docs/docs/config/01-grouping/02-by-story.mdx
+++ b/docs/docs/config/01-grouping/02-by-story.mdx
@@ -42,7 +42,7 @@ Before you start using this grouping option, you need to decide how exactly you
## Using annotations
-The [annotation-based approach](../../api/08-labels.mdx) gives you a fine-grained control over the names of your epic, feature and story labels, but it requires you to add annotations to _every and each test case_ (sic!) which can be tedious.
+The [annotation-based approach](../../features/08-labels.mdx) gives you a fine-grained control over the names of your epic, feature and story labels, but it requires you to add annotations to _every and each test case_ (sic!) which can be tedious.
Let's take the same project as in the previous article, where there are two parts: client and server.
Both them deal with the same functionality – authentication and restoring forgotten passwords.
diff --git a/docs/docs/config/index.mdx b/docs/docs/config/index.mdx
index 3ac1ae42..072a1d55 100644
--- a/docs/docs/config/index.mdx
+++ b/docs/docs/config/index.mdx
@@ -14,4 +14,4 @@ of the reporter, which are configurable on a per-test basis.
:::component{name="DocCardList"}
:::
-[API]: ../api/index.mdx
+[API]: ../features/index.mdx
diff --git a/docs/docs/customization/01-approaches.mdx b/docs/docs/customization/01-approaches.mdx
new file mode 100644
index 00000000..285fa163
--- /dev/null
+++ b/docs/docs/customization/01-approaches.mdx
@@ -0,0 +1,120 @@
+# Approaches
+
+jest-allure2-reporter offers several approaches to customize your test reports. Each method has its own advantages and limitations, allowing you to choose the one that best fits your specific needs. Here are the main customization approaches:
+
+## Docblock Annotations
+
+Docblock annotations provide a declarative way to add metadata to your tests directly in the source code.
+This approach is:
+
+* ✨ Agnostic way to add simple metadata
+* ✅ Familiar syntax for developers accustomed to JSDoc
+* ✅ Doesn't require importing additional modules
+* ❗ Only works at the top file level and individual test level
+* ❗ Cannot be applied to test suites or hooks
+
+```javascript
+/**
+ * @description Tests the login functionality
+ * @severity critical
+ * @owner John Doe
+ */
+test('User can log in with valid credentials', () => {
+ // Test code here
+});
+```
+
+## DSL Annotations
+
+DSL (Domain-Specific Language) annotations provide a specialized syntax for adding metadata that's more concise than the runtime API.
+This approach is:
+
+* ✨ A middle ground between a declarative and a programmatic approach
+* ✨ Targets the definition level, not the execution (runtime)
+* ✅ Works on almost every block: suites, hooks and tests themselves
+* ✅ Allows for some programmatic flexibility
+* ❗ Couples your tests to `jest-allure2-reporter`'s API
+
+```javascript
+import { $Description, $Severity, $Owner } from 'jest-allure2-reporter/api';
+
+$Description('Login functionality test suite')
+describe('Login Tests', () => {
+ $Owner('John Doe')
+ $Severity('critical')
+ test('User can log in with valid credentials', () => {
+ // Test code here
+ });
+
+ $Description('Setup test database')
+ beforeAll(() => {
+ // Setup code here
+ });
+});
+```
+
+## Runtime API
+
+The Runtime API allows you to add metadata and customize reports programmatically within your test code.
+This approach is:
+
+* ✨ Purely programmatic, targeted at runtime
+* ✅ Can be used at any point during runtime: file evaluation, suite definition, hook execution, or test execution
+* ✅ Highly flexible and dynamic
+* ❗ Couples your tests to `jest-allure2-reporter`'s API
+* ❗ Doesn't work with skipped tests
+
+```javascript
+import { allure } from 'jest-allure2-reporter/api';
+
+test('User can log in with valid credentials', () => {
+ allure.description('Tests the login functionality');
+ allure.severity('critical');
+ allure.owner('John Doe');
+
+ // Test code here
+});
+```
+
+## Reporter Config
+
+Declarative configuration allows you to set up global customizations and defaults in your Jest configuration file.
+
+Example (in `jest.config.js`):
+```javascript
+module.exports = {
+ // ... other Jest config options
+ reporters: [
+ 'default',
+ ['jest-allure2-reporter', {
+ testCase: {
+ labels: {
+ owner: ({ value }) => value ?? 'Team QA',
+ severity: 'critical',
+ },
+ },
+ }],
+ ],
+};
+```
+
+✨ Centralized configuration for the entire project
+✅ Allows setting default (fallback) behaviors and metadata
+✅ Can be shared as a preset or plugin
+❗ Less precise than other methods; applies broadly rather than to specific tests
+❗ Can override more specific customizations if not carefully managed
+
+## Choosing the Right Approach
+
+The best approach (or combination of approaches) depends on your specific needs:
+
+- Use **docblock annotations** for simple, static metadata at the file or individual test level.
+- Use **DSL annotations** when you need to add metadata to sub-suites or hooks, or to individual tests (when your is not static).
+- Use the **runtime API** when you need to add dynamic metadata based on test execution or when you need fine-grained control at any point during the test lifecycle.
+- Use **declarative configuration** for project-wide defaults and behaviors, or for massive changes that apply to all or most tests.
+
+You can combine these approaches as needed. For example, you might use declarative configuration for project-wide defaults, docblock annotations for static file-level metadata, DSL annotations for suite and hook metadata, and the runtime API for dynamic metadata that depends on test execution.
+
+Remember that the config-based approach is a powerful tool that can override other customizations. While this can be useful for implementing broad changes, it should be used judiciously to avoid unintended consequences.
+
+By understanding the strengths and limitations of each approach, you can create detailed, informative Allure reports that provide valuable insights into your test suite's performance and results.
diff --git a/docs/docs/customization/02-anatomy.mdx b/docs/docs/customization/02-anatomy.mdx
new file mode 100644
index 00000000..fa3e5a27
--- /dev/null
+++ b/docs/docs/customization/02-anatomy.mdx
@@ -0,0 +1,55 @@
+# Anatomy of a Report
+
+The Allure report generated by `jest-allure2-reporter` offers a highly customizable view of your test results. Let's explore how you can configure each component to tailor the report to your project's needs.
+
+## Overview
+
+The Overview dashboard provides a summary of your test execution. With `jest-allure2-reporter`, you can customize:
+
+1. **Test Result Summary:** Configure how test [statuses](../customization/test-metadata#statuses) are calculated and displayed.
+2. **Environment:** Control what [environment information](../customization/report-widgets#environment-information) is included in the report.
+3. **Executors:** Configure [executor information](../customization/report-widgets#executors) to track where tests were run.
+
+## Tree Views
+
+The **Tree View** is a powerful tool for navigating and understanding your test results. It appears in multiple sections of the Allure Report, including Suites, Packages, and Behaviors. With `jest-allure2-reporter`, you can customize the Tree View to match your project's structure and terminology.
+
+### Common Tree View Customizations
+
+* **Hierarchy:** Configure the hierarchy of your tests by using different [grouping mechanisms](../customization/test-grouping) such as suites, packages, or behaviors.
+* **Naming:** Customize the naming of nodes in the tree using [suite naming conventions](../customization/test-grouping#suite-naming), [package naming](../customization/test-grouping#by-package), and [BDD labels](../customization/test-grouping#by-story).
+* **Filtering:** Apply filters to the Tree View to focus on specific sets of tests.
+
+### Tree View Sections
+
+* **Suites View:** Offers a structured representation of your test organization based on test suites. You can configure [suite hierarchy](../customization/test-grouping#configuring-suite-hierarchy), [dynamic suite generation](../customization/advanced#dynamic-suites), and [suite-level metadata](../customization/test-metadata#suite-level-metadata).
+* **Packages View:** Provides a hierarchical representation of your tests based on the packages they belong to.
+* **Behaviors View:** Organizes your tests based on product features and user stories using [epics, features, and stories](../customization/test-grouping#by-story)
+
+## Timeline View
+
+Enhance the Gantt chart representation of your test execution:
+
+* Customize [test case duration reporting](../customization/test-metadata#duration).
+* Configure [parallel test execution](../customization/advanced#parallel-execution) visualization.
+
+## Categories View
+
+Improve error classification and prioritization:
+
+* Set up [custom defect categories](../customization/test-grouping#by-category) based on error patterns.
+* Configure [failure message parsing](../customization/advanced#error-parsing) for accurate categorization.
+
+## Test Case & Step Details
+
+Enrich individual test case and test step reporting:
+
+* Customize [test descriptions](../customization/test-metadata#descriptions) and [step descriptions] for clarity.
+* Configure [severity levels](../customization/test-metadata#severity-levels) to highlight critical tests.
+* Add [custom metadata](../customization/test-metadata#custom-labels) and [attachments](../customization/test-metadata#attachments) relevant to your project.
+* Set up [test step reporting](../customization/test-steps) for detailed execution flow.
+* Configure [history tracking](../customization/advanced#test-history) for long-term trend analysis.
+
+## Conclusion
+
+`jest-allure2-reporter` provides extensive customization options for every aspect of your Allure report. By leveraging these configuration possibilities, you can create a report that perfectly aligns with your project's structure, priorities, and information needs. Explore the linked documentation to learn how to tailor each component to your specifications.
diff --git a/docs/docs/customization/04-sources.mdx b/docs/docs/customization/04-sources.mdx
new file mode 100644
index 00000000..24c5ea6d
--- /dev/null
+++ b/docs/docs/customization/04-sources.mdx
@@ -0,0 +1,120 @@
+# Test Sources
+
+In `jest-allure2-reporter`, we've expanded the concept of what can be reported as a test in Allure. This flexibility allows for more comprehensive reporting, especially in scenarios where traditional test cases might not capture all the relevant information. There are three main sources of tests that can be reported:
+
+1. Test Cases
+2. Test Files
+3. Test Run
+
+Let's explore each of these in detail.
+
+## Test Cases
+
+Test cases are the most common and familiar source of tests. These are your individual `test()` or `it()` blocks in Jest.
+
+By default, all test cases are reported to Allure. However, you can customize this behavior using the `testCase` configuration option:
+
+```javascript
+/** @type {import('@jest/types').Config.InitialOptions} */
+module.exports = {
+ reporters: [
+ 'default',
+ ['jest-allure2-reporter', {
+ testCase: {
+ ignored: ({ testCase }) => testCase.title.startsWith('SKIP:'),
+ },
+ }],
+ ],
+};
+```
+
+In this example, any test case with a title starting with "SKIP:" will be ignored and not reported to Allure.
+
+## Test Files
+
+Test files can be reported as tests in Allure, which is particularly useful for capturing errors that occur at the file level, such as syntax errors or environment setup failures. By default, test files are only reported if there are execution errors.
+
+You can customize this behavior using the `testFile` configuration option:
+
+```javascript
+/** @type {import('@jest/types').Config.InitialOptions} */
+module.exports = {
+ reporters: [
+ 'default',
+ ['jest-allure2-reporter', {
+ testFile: {
+ ignored: false, // Report all test files, even without errors
+ },
+ }],
+ ],
+};
+```
+
+This configuration will report all test files to Allure, regardless of whether they contain execution errors.
+
+## Test Run
+
+The entire test run can be reported as a single test in Allure. This is useful for capturing global setup and teardown information, or for providing an overview of the entire test session.
+
+By default, test run information is not reported as a separate test. You can enable this feature using the `testRun` configuration option:
+
+```javascript
+/** @type {import('@jest/types').Config.InitialOptions} */
+module.exports = {
+ reporters: [
+ 'default',
+ ['jest-allure2-reporter', {
+ testRun: {
+ ignored: false, // Report the test run as a test
+ },
+ }],
+ ],
+};
+```
+
+This configuration will create a test in Allure representing the entire test run.
+
+## Ignoring Tests
+
+For each test source, you can use the `ignored` option to control which tests are reported to Allure. This option can be a boolean or a function that returns a boolean:
+
+```javascript
+/** @type {import('@jest/types').Config.InitialOptions} */
+module.exports = {
+ reporters: [
+ 'default',
+ ['jest-allure2-reporter', {
+ testCase: {
+ ignored: ({ testCase }) => testCase.status === 'skipped',
+ },
+ testFile: {
+ ignored: ({ testFile }) => testFile.path.includes('experimental/'),
+ },
+ testRun: {
+ ignored: false,
+ },
+ }],
+ ],
+};
+```
+
+In this example:
+- Skipped test cases are not reported to Allure.
+- Test files in the 'experimental/' directory are not reported.
+- The test run is reported as a test in Allure.
+
+## Default Behavior
+
+By default, jest-allure2-reporter follows these rules:
+
+1. All test cases are reported to Allure.
+2. Test files are only reported if they contain execution errors (e.g., syntax errors, environment setup failures).
+3. The test run is not reported as a separate test.
+
+This default behavior ensures that your Allure report contains all the necessary information about your tests, including critical errors that might prevent tests from running, without cluttering the report with unnecessary information.
+
+## Conclusion
+
+The flexibility in test sources provided by jest-allure2-reporter allows you to create more comprehensive and informative Allure reports. By customizing which test sources are reported and under what conditions, you can ensure that your reports contain exactly the information you need, whether that's detailed test case results, file-level errors, or an overview of the entire test run.
+
+Remember, the goal is to provide meaningful information in your reports. Use these options judiciously to create reports that are informative without being overwhelming.
diff --git a/docs/docs/customization/index.md b/docs/docs/customization/index.md
new file mode 100644
index 00000000..7fbb49a0
--- /dev/null
+++ b/docs/docs/customization/index.md
@@ -0,0 +1,7 @@
+import DocCardList from '@theme/DocCardList';
+
+# Customization
+
+Learn what can be customized in `jest-allure2-reporter`, and how to do it.
+
+
diff --git a/docs/docs/api/01-descriptions.mdx b/docs/docs/features/01-descriptions.mdx
similarity index 100%
rename from docs/docs/api/01-descriptions.mdx
rename to docs/docs/features/01-descriptions.mdx
diff --git a/docs/docs/api/02-steps.mdx b/docs/docs/features/02-steps.mdx
similarity index 100%
rename from docs/docs/api/02-steps.mdx
rename to docs/docs/features/02-steps.mdx
diff --git a/docs/docs/api/03-attachments.mdx b/docs/docs/features/03-attachments.mdx
similarity index 100%
rename from docs/docs/api/03-attachments.mdx
rename to docs/docs/features/03-attachments.mdx
diff --git a/docs/docs/api/04-parameters.mdx b/docs/docs/features/04-parameters.mdx
similarity index 83%
rename from docs/docs/api/04-parameters.mdx
rename to docs/docs/features/04-parameters.mdx
index 6d0129a3..32def131 100644
--- a/docs/docs/api/04-parameters.mdx
+++ b/docs/docs/features/04-parameters.mdx
@@ -37,9 +37,9 @@ export interface Parameter {
The options allow you to fine-tune the way your parameters are displayed in the report:
-* `excluded: true` - exclude the parameter from the report
-* `mode: "hidden"` - hide the parameter value, e.g. when it is too long yet still useful for debugging
-* `mode: "masked"` - mask the parameter value, e.g. when it contains sensitive information
+* `excluded: true` - exclude the parameter from allure-results (sensitive or unnecessary verbose data)
+* `mode: "masked"` - mask the parameter value with `******` in the generated report
+* `mode: "hidden"` - exclude the parameter from the generated report completely
The `allure.parameter` API can be used also on the top level, e.g.:
diff --git a/docs/docs/api/05-people.mdx b/docs/docs/features/05-people.mdx
similarity index 100%
rename from docs/docs/api/05-people.mdx
rename to docs/docs/features/05-people.mdx
diff --git a/docs/docs/api/06-severity.mdx b/docs/docs/features/06-severity.mdx
similarity index 100%
rename from docs/docs/api/06-severity.mdx
rename to docs/docs/features/06-severity.mdx
diff --git a/docs/docs/api/07-links.mdx b/docs/docs/features/07-links.mdx
similarity index 100%
rename from docs/docs/api/07-links.mdx
rename to docs/docs/features/07-links.mdx
diff --git a/docs/docs/api/08-labels.mdx b/docs/docs/features/08-labels.mdx
similarity index 100%
rename from docs/docs/api/08-labels.mdx
rename to docs/docs/features/08-labels.mdx
diff --git a/docs/docs/api/index.mdx b/docs/docs/features/index.mdx
similarity index 93%
rename from docs/docs/api/index.mdx
rename to docs/docs/features/index.mdx
index b5ba83df..3fc835ac 100644
--- a/docs/docs/api/index.mdx
+++ b/docs/docs/features/index.mdx
@@ -4,7 +4,7 @@ sidebar_position: 3
import DocCardList from '@theme/DocCardList';
-# API
+# Features
Learn about the features of `jest-allure2-reporter`.
diff --git a/docs/docs/introduction/01-installation.mdx b/docs/docs/introduction/01-installation.mdx
index fc131f01..1c7be3a8 100644
--- a/docs/docs/introduction/01-installation.mdx
+++ b/docs/docs/introduction/01-installation.mdx
@@ -26,14 +26,14 @@ To do so, make sure you have [`allure-commandline`](https://www.npmjs.com/packag
```bash
$ allure --version
-2.27.0
+2.29.0
```
:::details
::summary[If you don't have Allure CLI installed]
-First of all, make sure you have Java 17 installed on your machine:
+First of all, make sure you have Java 17 or later installed on your machine:
```bash
java -version
@@ -41,6 +41,8 @@ java -version
# OpenJDK Runtime Environment (build 17.0.10+0)
```
+If you can't install recent Java version, you can try using older Allure CLI versions.
+
To install Allure CLI globally, run:
```bash npm2yarn
diff --git a/docs/docs/introduction/index.mdx b/docs/docs/introduction/index.mdx
index b28f586d..19e9f0fb 100644
--- a/docs/docs/introduction/index.mdx
+++ b/docs/docs/introduction/index.mdx
@@ -16,58 +16,25 @@ Whether you're a developer, QA professional, or someone involved in unit, integr
## What is `jest-allure2-reporter`?
-[`jest-allure2-reporter`](https://www.npmjs.com/package/jest-allure2-reporter) is a reporting toolkit for [Jest](https://jestjs.io/), a widely popular testing framework in the JavaScript ecosystem.
-
-The produced reports look like directories with multiple JSON files.
-They can be viewed using the [`allure` command-line tool][allure-cli], a flexible, feature-rich reporting tool that provides clear and concise test result representation.
-
-It provides several **entry points**:
-
-
-
jest-allure2-reporter
-
The reporter itself, which is responsible for collecting test results and generating a report.
-
jest-allure2-reporter/api
-
DSL to add additional metadata to your test definitions: $Link, $Owner.
-
`allure` runtime API to use inside your tests: allure.step, allure.attachment.
-
- Environment packages, to enable the annotations, media attachments and provide additional test data:
-
-
-
-
jest-allure2-reporter/environment-node
-
For Node.js tests.
-
jest-allure2-reporter/environment-jsdom
-
For browser tests.
-
jest-allure2-reporter/environment-decorator
-
For advanced use cases where you need to extend an already customized test environment.
-
-
-
+[`jest-allure2-reporter`](https://www.npmjs.com/package/jest-allure2-reporter) is a highly configurable reporter for [Jest](https://jestjs.io/), a widely popular testing framework in the JavaScript ecosystem.
-## Why one more Allure reporter for Jest?
-
-Let's start from the fact that **none** of the existing Allure reporters[^1][^2][^3][^4] for Jest is a **reporter** in the Jest sense. Both official and unofficial implementations are built as custom test environment implementations in Jest, whereas _test environments were never meant to be used as reporters_.
-
-You may ask, _"So what? As long as that approach works, why should I care?"_. Valid thinking, but... try running a test file with a syntax error and see how the existing reporters handle it. You'll be surprised.
+The produced reports look like directories with multiple JSON files, `${uuid}-container.json` and `${uuid}-result.json`, with optional media attachments and a few other files.
+They can be viewed or transformed to HTML using the [`allure` command-line tool][allure-cli], a flexible, feature-rich reporting tool that provides clear and concise test result representation.
-That was just one example of issues that arise when you don't implement a reporter as a _reporter_. Let's move the next point of failure happening early – test environment setup.
-
-At Wix, we've been maintaining CI pipelines for hundreds of Detox E2E projects, and one of the reasons why a test environment would fail to initialize could be an Android emulator being unresponsive or disconnected before the test file even started. So, when a test environment itself can break, is that the best place to put your core reporting logic?
+## Why one more Allure reporter for Jest?
-A few more important questions we've been struggling to answer were:
+Other reporters are either outdated, have limited features, or are not idiomatic to Jest's new test lifecycle – they are often implemented as a custom test environment, whereas the Jest expects reporters to be _reporters_, pluggable classes that can be swapped in and out.
-* You may have executed dozens of test files, but where do you put attachments related to the whole test run?
-* How do you attach links to the test file's source code on GitHub which failed due to a syntax error and even didn't start?
+The common syndrome of existing Allure reporters is that they fall short in reporting early setup failures, tackling duplicate test names and many other edge cases, which hinders the use in large-scale projects, where it is impossible to manually fix all the issues, and it is simpler just to have a tool that works out of the box.
-This is the reason why `jest-allure2-reporter` was born, and this is why it's the _idiomatic_ Allure reporter for Jest.
+Advanced users also would be pleased to know that `jest-allure2-reporter` can be configured at any level: configuration, DSL, decorators, docblocks and runtime API level.
+It can work even without any special test environment, albeit with some limitations, because it was built originally as a _Jest reporter_.
-To sum it up, here's a comparison of the existing Allure reporters for Jest:
+## Takeaways
-| Feature | jest-allure2-reporter | allure-jest | jest-allure | jest-circus-allure-environment | jest-allure-circus |
-| --- | --- | --- | --- | --- | --- |
-| Attachments API | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
-| Early test issues reporting | :heavy_check_mark: | :x: | :x: | :x: | :x: |
-| TODO | | | | | |
+* This reporter is a powerful tool tailor-made for the **modern** Jest ecosystem, not "yet another Allure reporter".
+* Easy to understand, straightforward to adopt, and flexible to work with – it's the Jest Allure reporter you've been waiting for.
+* It is progressive by design — you can start using it like a [standard reporter][jest-reporters], but the more you invest in it, the more you get out of it.
Ready to explore what this reporter can do for you? [Start your journey here.][config]
diff --git a/e2e/demo/__fixtures__/.png b/e2e/demo/__fixtures__/.png
new file mode 100644
index 00000000..a3ebc83b
Binary files /dev/null and b/e2e/demo/__fixtures__/.png differ
diff --git a/e2e/demo/__fixtures__/menu.png b/e2e/demo/__fixtures__/menu.png
new file mode 100644
index 00000000..78d24da7
Binary files /dev/null and b/e2e/demo/__fixtures__/menu.png differ
diff --git a/e2e/demo/__fixtures__/menu_cart.png b/e2e/demo/__fixtures__/menu_cart.png
new file mode 100644
index 00000000..889fa718
Binary files /dev/null and b/e2e/demo/__fixtures__/menu_cart.png differ
diff --git a/e2e/demo/__fixtures__/products-long-sleeved-t-shirt.png b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt.png
new file mode 100644
index 00000000..bcfa0103
Binary files /dev/null and b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt.png differ
diff --git a/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_expand.png b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_expand.png
new file mode 100644
index 00000000..62dd7da4
Binary files /dev/null and b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_expand.png differ
diff --git a/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_next.png b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_next.png
new file mode 100644
index 00000000..05e4efb1
Binary files /dev/null and b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_next.png differ
diff --git a/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_product.png b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_product.png
new file mode 100644
index 00000000..6452fb56
Binary files /dev/null and b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_product.png differ
diff --git a/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review.png b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review.png
new file mode 100644
index 00000000..f00daebc
Binary files /dev/null and b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review.png differ
diff --git a/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_completed.png b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_completed.png
new file mode 100644
index 00000000..20372cda
Binary files /dev/null and b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_completed.png differ
diff --git a/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_form.png b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_form.png
new file mode 100644
index 00000000..12f366b1
Binary files /dev/null and b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_form.png differ
diff --git a/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_form_completed.actual.png b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_form_completed.actual.png
new file mode 100644
index 00000000..b7300724
Binary files /dev/null and b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_form_completed.actual.png differ
diff --git a/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_form_completed.diff.png b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_form_completed.diff.png
new file mode 100644
index 00000000..0d5ffce9
Binary files /dev/null and b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_form_completed.diff.png differ
diff --git a/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_form_completed.expected.png b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_form_completed.expected.png
new file mode 100644
index 00000000..20372cda
Binary files /dev/null and b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_form_completed.expected.png differ
diff --git a/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_form_completed.png b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_form_completed.png
new file mode 100644
index 00000000..74cbf342
Binary files /dev/null and b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_form_completed.png differ
diff --git a/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_scrolled.png b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_scrolled.png
new file mode 100644
index 00000000..d11fac80
Binary files /dev/null and b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_review_scrolled.png differ
diff --git a/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_zoom-in.png b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_zoom-in.png
new file mode 100644
index 00000000..19832522
Binary files /dev/null and b/e2e/demo/__fixtures__/products-long-sleeved-t-shirt_zoom-in.png differ
diff --git a/e2e/demo/__fixtures__/products.png b/e2e/demo/__fixtures__/products.png
new file mode 100644
index 00000000..24a5d4de
Binary files /dev/null and b/e2e/demo/__fixtures__/products.png differ
diff --git a/e2e/demo/__fixtures__/products_applied-filters.png b/e2e/demo/__fixtures__/products_applied-filters.png
new file mode 100644
index 00000000..4d825d7e
Binary files /dev/null and b/e2e/demo/__fixtures__/products_applied-filters.png differ
diff --git a/e2e/demo/__fixtures__/products_applied-sorting.actual.png b/e2e/demo/__fixtures__/products_applied-sorting.actual.png
new file mode 100644
index 00000000..9b9b289b
Binary files /dev/null and b/e2e/demo/__fixtures__/products_applied-sorting.actual.png differ
diff --git a/e2e/demo/__fixtures__/products_applied-sorting.diff.png b/e2e/demo/__fixtures__/products_applied-sorting.diff.png
new file mode 100644
index 00000000..073cde62
Binary files /dev/null and b/e2e/demo/__fixtures__/products_applied-sorting.diff.png differ
diff --git a/e2e/demo/__fixtures__/products_applied-sorting.png b/e2e/demo/__fixtures__/products_applied-sorting.png
new file mode 100644
index 00000000..b3c69d06
Binary files /dev/null and b/e2e/demo/__fixtures__/products_applied-sorting.png differ
diff --git a/e2e/demo/__fixtures__/products_filters.png b/e2e/demo/__fixtures__/products_filters.png
new file mode 100644
index 00000000..351416f7
Binary files /dev/null and b/e2e/demo/__fixtures__/products_filters.png differ
diff --git a/e2e/demo/__fixtures__/products_filters_applied-filters.png b/e2e/demo/__fixtures__/products_filters_applied-filters.png
new file mode 100644
index 00000000..13b70a6c
Binary files /dev/null and b/e2e/demo/__fixtures__/products_filters_applied-filters.png differ
diff --git a/e2e/demo/__fixtures__/products_sorting.png b/e2e/demo/__fixtures__/products_sorting.png
new file mode 100644
index 00000000..688137b8
Binary files /dev/null and b/e2e/demo/__fixtures__/products_sorting.png differ
diff --git a/e2e/demo/__fixtures__/products_sorting_applied-sorting.png b/e2e/demo/__fixtures__/products_sorting_applied-sorting.png
new file mode 100644
index 00000000..b8fd50bc
Binary files /dev/null and b/e2e/demo/__fixtures__/products_sorting_applied-sorting.png differ
diff --git a/e2e/demo/drivers/FakeBrowser.ts b/e2e/demo/drivers/FakeBrowser.ts
new file mode 100644
index 00000000..15d84f8a
--- /dev/null
+++ b/e2e/demo/drivers/FakeBrowser.ts
@@ -0,0 +1,63 @@
+import { allure, Step } from 'jest-allure2-reporter/api';
+import { kebabCase } from 'lodash';
+import fs from 'node:fs/promises';
+import path from 'node:path';
+import { sleep } from './utils';
+
+export class FakeBrowser {
+ private url = '/';
+ private state = new Set();
+
+ @Step('Navigate to: {{url}}', ['url'])
+ async navigate(url: string): Promise {
+ this.url = url;
+ // Reset state on navigation
+ this.state.clear();
+
+ await sleep(1000, 2000);
+
+ const fullUrl = 'https://publicissapient.github.io/accessible-ecommerce-demo' + url;
+ allure.link(fullUrl, url);
+
+ const screenshot = await this.takeScreenshot();
+ if (screenshot) {
+ await allure.attachment('Screenshot', screenshot, 'image/png');
+ }
+ }
+
+ async refresh() {
+ this.state.clear();
+ await sleep(1000, 2000);
+ }
+
+ async takeScreenshot(extension = '.png'): Promise {
+ const fileName = [kebabCase(this.url), ...this.state].join('_') + extension;
+ const filePath = path.join('demo/__fixtures__', fileName);
+ const exists = await fs.access(filePath).catch(() => false);
+ if (exists === false && extension === '.png') {
+ console.error(filePath);
+ }
+ return exists === false ? null : fs.readFile(filePath);
+ }
+
+ async saveScreenshot(): Promise {
+ const screenshot = await this.takeScreenshot();
+ if (screenshot) {
+ await allure.attachment('Screenshot', screenshot, 'image/png');
+ }
+ }
+
+ async toggleState(modifiers: string | string[], enabled: boolean): Promise {
+ const array = typeof modifiers === 'string' ? [modifiers] : modifiers;
+
+ for (const modifier of array) {
+ if (enabled) {
+ this.state.add(modifier);
+ } else {
+ this.state.delete(modifier);
+ }
+ }
+
+ await sleep(1000, 2000);
+ }
+}
diff --git a/e2e/demo/drivers/MenuDriver.ts b/e2e/demo/drivers/MenuDriver.ts
new file mode 100644
index 00000000..e6b79871
--- /dev/null
+++ b/e2e/demo/drivers/MenuDriver.ts
@@ -0,0 +1,16 @@
+import { Step } from 'jest-allure2-reporter/api';
+import type { FakeBrowser } from './FakeBrowser';
+
+export class MenuDriver {
+ constructor(protected readonly browser: FakeBrowser) {}
+
+ @Step('Expand menu')
+ async expandMenu() {
+ await this.browser.toggleState('menu', true);
+ }
+
+ @Step('Open Cart')
+ async openCart() {
+ await this.browser.toggleState('cart', true);
+ }
+}
diff --git a/e2e/demo/drivers/ProductDetailsDriver.ts b/e2e/demo/drivers/ProductDetailsDriver.ts
new file mode 100644
index 00000000..6bf19afb
--- /dev/null
+++ b/e2e/demo/drivers/ProductDetailsDriver.ts
@@ -0,0 +1,27 @@
+import {Step} from 'jest-allure2-reporter/api';
+import type {FakeBrowser} from './FakeBrowser';
+import {throwErrorWithChance} from "./utils";
+import {AssertionError} from "./errors";
+
+export class ProductDetailsDriver {
+ constructor(protected readonly browser: FakeBrowser) {}
+
+ @Step('Navigate to next image')
+ async navigateToNextImage() {
+ await this.browser.toggleState('next', true);
+ return this.browser.takeScreenshot();
+ }
+
+ @Step('Zoom in image')
+ async zoomInImage() {
+ await this.browser.toggleState('zoom-in', true);
+ await this.browser.saveScreenshot();
+ await throwErrorWithChance(1, 500, [new AssertionError('Expected .product-image to be visible, but it was overlayed by another element')])
+ }
+
+ @Step('Expand image')
+ async expandImage() {
+ await this.browser.toggleState('expand', true);
+ return this.browser.takeScreenshot();
+ }
+}
diff --git a/e2e/demo/drivers/ProductListingDriver.ts b/e2e/demo/drivers/ProductListingDriver.ts
new file mode 100644
index 00000000..9d09e30a
--- /dev/null
+++ b/e2e/demo/drivers/ProductListingDriver.ts
@@ -0,0 +1,49 @@
+import { Step } from 'jest-allure2-reporter/api';
+import type {FakeBrowser} from './FakeBrowser';
+import {Screenshot} from "./utils";
+
+export class ProductListingDriver {
+ constructor(protected readonly browser: FakeBrowser) {}
+
+ @Step('Open Filters')
+ @Screenshot()
+ async openFilters() {
+ await this.browser.toggleState('filters', true);
+ return this.browser.takeScreenshot();
+ }
+
+ @Step('Apply Filters - {{json criteria}}', ['criteria'])
+ @Screenshot()
+ async applyFilters(_criteria: Record) {
+ await this.browser.toggleState('applied-filters', true);
+ return this.browser.takeScreenshot();
+ }
+
+ @Step('Close Filters')
+ @Screenshot()
+ async closeFilters() {
+ await this.browser.toggleState('filters', false);
+ return this.browser.takeScreenshot();
+ }
+
+ @Step('Open Sorting')
+ @Screenshot()
+ async openSorting() {
+ await this.browser.toggleState('sorting', true);
+ return this.browser.takeScreenshot();
+ }
+
+ @Step('Apply Sorting {{json criteria}}', ['criteria'])
+ @Screenshot()
+ async applySorting(_criteria: Record) {
+ await this.browser.toggleState('applied-sorting', true);
+ return this.browser.takeScreenshot();
+ }
+
+ @Step('Close Sorting')
+ @Screenshot()
+ async closeSorting() {
+ await this.browser.toggleState('sorting', false);
+ return this.browser.takeScreenshot();
+ }
+}
diff --git a/e2e/demo/drivers/ProductReviewsDriver.ts b/e2e/demo/drivers/ProductReviewsDriver.ts
new file mode 100644
index 00000000..e287d37d
--- /dev/null
+++ b/e2e/demo/drivers/ProductReviewsDriver.ts
@@ -0,0 +1,54 @@
+import {allure, Step} from 'jest-allure2-reporter/api';
+import type {FakeBrowser} from './FakeBrowser';
+import {Screenshot, throwErrorWithChance} from "./utils";
+import {NetworkError} from "./errors";
+
+interface ReviewFormData {
+ email: string;
+ displayName: string;
+ rating: number;
+ title: string;
+ comment: string;
+}
+
+export class ProductReviewsDriver {
+ constructor(protected readonly browser: FakeBrowser) {}
+
+ @Step('Scroll to reviews section')
+ @Screenshot()
+ async scrollToReviews() {
+ await this.browser.toggleState(['review', 'scrolled'], true);
+ return this.browser.takeScreenshot();
+ }
+
+ @Step('Expand reviews section')
+ @Screenshot()
+ async expandReviewsSection() {
+ await this.browser.toggleState('scrolled', false);
+ return this.browser.takeScreenshot();
+ }
+
+ @Step('Open review form')
+ @Screenshot()
+ async openReviewForm() {
+ await this.browser.toggleState('form', true);
+ return this.browser.takeScreenshot();
+ }
+
+ @Step('Fill review form: {{stars data.rating}}', [{ name: 'data', mode: 'hidden' }])
+ @Screenshot()
+ async fillReviewForm(data: ReviewFormData) {
+ allure.parameters(data);
+ await this.browser.toggleState('completed', true);
+ return this.browser.takeScreenshot();
+ }
+
+ @Step('Submit review')
+ @Screenshot()
+ async submitReview() {
+ await throwErrorWithChance(1, 500, [new NetworkError('Failed to connect to contact form service.')])
+
+ await this.browser.toggleState('form', false);
+ return this.browser.takeScreenshot();
+ }
+}
diff --git a/e2e/demo/drivers/errors.ts b/e2e/demo/drivers/errors.ts
new file mode 100644
index 00000000..e6671f76
--- /dev/null
+++ b/e2e/demo/drivers/errors.ts
@@ -0,0 +1,18 @@
+import {JestAssertionError} from "expect";
+
+export class AssertionError extends JestAssertionError {
+ constructor(message: string) {
+ super(message);
+ this.matcherResult = {
+ pass: false,
+ message,
+ };
+ }
+}
+
+export class NetworkError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'NetworkError';
+ }
+}
diff --git a/e2e/demo/drivers/index.ts b/e2e/demo/drivers/index.ts
new file mode 100644
index 00000000..f075e763
--- /dev/null
+++ b/e2e/demo/drivers/index.ts
@@ -0,0 +1,7 @@
+export * as errors from './errors';
+export * from './FakeBrowser';
+export * from './MenuDriver';
+export * from './ProductDetailsDriver';
+export * from './ProductListingDriver';
+export * from './ProductReviewsDriver';
+export * as utils from './utils';
diff --git a/e2e/demo/drivers/utils/Screenshot.ts b/e2e/demo/drivers/utils/Screenshot.ts
new file mode 100644
index 00000000..9e57debe
--- /dev/null
+++ b/e2e/demo/drivers/utils/Screenshot.ts
@@ -0,0 +1,3 @@
+import { Attachment } from 'jest-allure2-reporter/api';
+
+export const Screenshot = Attachment.bind(null, 'Screenshot', 'image/png');
diff --git a/e2e/demo/drivers/utils/index.ts b/e2e/demo/drivers/utils/index.ts
new file mode 100644
index 00000000..d2ea3945
--- /dev/null
+++ b/e2e/demo/drivers/utils/index.ts
@@ -0,0 +1,4 @@
+export * from './sleep';
+export * from './Screenshot';
+export * from './throwErrorWithChance';
+export * from './toMatchImageSnapshot';
diff --git a/e2e/demo/drivers/utils/sleep.ts b/e2e/demo/drivers/utils/sleep.ts
new file mode 100644
index 00000000..6af8edc4
--- /dev/null
+++ b/e2e/demo/drivers/utils/sleep.ts
@@ -0,0 +1,4 @@
+export async function sleep(min: number, max = min) {
+ const ms = Math.round(Math.random() * (max - min) + min);
+ await new Promise(resolve => setTimeout(resolve, ms));
+}
diff --git a/e2e/demo/drivers/utils/throwErrorWithChance.ts b/e2e/demo/drivers/utils/throwErrorWithChance.ts
new file mode 100644
index 00000000..d4e33fc3
--- /dev/null
+++ b/e2e/demo/drivers/utils/throwErrorWithChance.ts
@@ -0,0 +1,9 @@
+import {sample} from 'lodash';
+import {sleep} from "./sleep";
+
+export async function throwErrorWithChance(chance: number, delay: number, errors: Error[]) {
+ if (Math.random() < chance) {
+ await sleep(delay, 1.5 * delay);
+ throw sample(errors);
+ }
+}
diff --git a/e2e/demo/drivers/utils/toMatchImageSnapshot.ts b/e2e/demo/drivers/utils/toMatchImageSnapshot.ts
new file mode 100644
index 00000000..994cb767
--- /dev/null
+++ b/e2e/demo/drivers/utils/toMatchImageSnapshot.ts
@@ -0,0 +1,31 @@
+import type { FakeBrowser } from '../FakeBrowser';
+import { allure } from 'jest-allure2-reporter/api';
+
+export async function toMatchImageSnapshot(browser: FakeBrowser): Promise {
+ const diffBuffer = await browser.takeScreenshot('.diff.png');
+
+ if (diffBuffer) {
+ const defaultBuffer = await browser.takeScreenshot();
+ const expectedBuffer = (await browser.takeScreenshot('.expected.png')) ?? defaultBuffer;
+ const actualBuffer = (await browser.takeScreenshot('.actual.png')) ?? defaultBuffer;
+
+ const expected = expectedBuffer!.toString('base64');
+ const actual = actualBuffer!.toString('base64');
+ const diff = diffBuffer.toString('base64');
+ const content = JSON.stringify({
+ expected: `data:image/png;base64,${expected}`,
+ actual: `data:image/png;base64,${actual}`,
+ diff: `data:image/png;base64,${diff}`,
+ });
+
+ await allure.attachment("Screenshot diff", content, "application/vnd.allure.image.diff");
+ return false;
+ } else {
+ const screenshot = await browser.takeScreenshot();
+ if (screenshot) {
+ await allure.attachment('Screenshot', screenshot, 'image/png');
+ }
+ }
+
+ return true;
+}
diff --git a/e2e/demo/jest.config.js b/e2e/demo/jest.config.js
new file mode 100644
index 00000000..9bb39ceb
--- /dev/null
+++ b/e2e/demo/jest.config.js
@@ -0,0 +1,13 @@
+/** @type {import('jest').Config} */
+module.exports = {
+ ...require('../jest.config'),
+ rootDir: '.',
+ setupFilesAfterEnv: ['/setup.ts'],
+ testMatch: ['/**/*.test.ts'],
+ transform: {},
+};
+
+/** @type {import('jest-allure2-reporter').ReporterOptions} */
+const jestAllure2ReporterOptions = module.exports.reporters[1][1];
+jestAllure2ReporterOptions.testRun.ignored = true;
+jestAllure2ReporterOptions.testFile.ignored = true;
diff --git a/e2e/demo/navigation/menu.test.ts b/e2e/demo/navigation/menu.test.ts
new file mode 100644
index 00000000..ee095c19
--- /dev/null
+++ b/e2e/demo/navigation/menu.test.ts
@@ -0,0 +1,48 @@
+/**
+ * ### Navigation test suite
+ *
+ * This suite tests the navigation elements of the website, focusing on the mobile experience.
+ */
+import { FakeBrowser, MenuDriver } from '../drivers';
+
+describe('Navigation', () => {
+ let browser: FakeBrowser;
+ let menuDriver: MenuDriver;
+
+ /** Initialize test suite */
+ beforeAll(() => {
+ browser = new FakeBrowser();
+ menuDriver = new MenuDriver(browser);
+ });
+
+ describe('Menu (Mobile)', () => {
+ /** Open Home page */
+ beforeAll(async () => {
+ await browser.navigate('/');
+ });
+
+ /**
+ * @package com.example.shop
+ * @epic User Experience
+ * @feature Site Navigation
+ * @story Mobile Menu
+ */
+ test('should expand mobile menu when tapping the Burger Icon', async () => {
+ await menuDriver.expandMenu();
+ await expect(browser).toMatchImageSnapshot();
+ });
+
+ /**
+ * @epic User Experience
+ * @feature Checkout
+ * @story Accessing Cart
+ * @package com.example.shop.cart
+ * @testClass CartController
+ * @testMethod CartController.getItems()
+ */
+ test('has an expandable Cart', async () => {
+ await menuDriver.openCart();
+ await expect(browser).toMatchImageSnapshot();
+ });
+ });
+});
diff --git a/e2e/demo/product-catalog/product-details.test.ts b/e2e/demo/product-catalog/product-details.test.ts
new file mode 100644
index 00000000..704a2464
--- /dev/null
+++ b/e2e/demo/product-catalog/product-details.test.ts
@@ -0,0 +1,51 @@
+/**
+ * @epic Product Discovery
+ * @feature View Product Details
+ * @story View Product Details
+ * @package com.example.shop.catalog
+ * @testClass ProductDetailsController
+ */
+
+import { FakeBrowser, ProductDetailsDriver } from '../drivers';
+
+describe('Product Catalog', () => {
+ let browser: FakeBrowser;
+ let productDetailsDriver: ProductDetailsDriver;
+
+ /** Initialize test suite */
+ beforeAll(() => {
+ browser = new FakeBrowser();
+ productDetailsDriver = new ProductDetailsDriver(browser);
+ });
+
+ describe('Product Details', () => {
+ /** Open Product Details page */
+ beforeAll(async () => {
+ await browser.navigate('/products/long-sleeved-t-shirt');
+ });
+
+ /** Refresh page to reset state */
+ afterEach(async () => {
+ await browser.refresh();
+ });
+
+ test('Widget loads correctly', async () => {
+ await expect(browser).toMatchImageSnapshot();
+ });
+
+ test('Image gallery navigation works correctly', async () => {
+ await productDetailsDriver.navigateToNextImage();
+ await expect(browser).toMatchImageSnapshot();
+ });
+
+ test('Image zoom in works correctly', async () => {
+ await productDetailsDriver.zoomInImage();
+ await expect(browser).toMatchImageSnapshot();
+ });
+
+ test('Expanding image works correctly', async () => {
+ await productDetailsDriver.expandImage();
+ await expect(browser).toMatchImageSnapshot();
+ });
+ });
+});
diff --git a/e2e/demo/product-catalog/product-listing.test.ts b/e2e/demo/product-catalog/product-listing.test.ts
new file mode 100644
index 00000000..beaf65ad
--- /dev/null
+++ b/e2e/demo/product-catalog/product-listing.test.ts
@@ -0,0 +1,58 @@
+/**
+ * @epic Product Discovery
+ * @feature Browse Products
+ * @severity blocker
+ * @package com.example.shop.catalog
+ */
+import { FakeBrowser, ProductListingDriver } from '../drivers';
+
+describe('Product Catalog', () => {
+ let browser: FakeBrowser;
+ let productListingDriver: ProductListingDriver;
+
+ /** Initialize test suite */
+ beforeAll(() => {
+ browser = new FakeBrowser();
+ productListingDriver = new ProductListingDriver(browser);
+ });
+
+ describe('Product Listing', () => {
+ /** Open Products page */
+ beforeEach(async () => {
+ await browser.navigate('/products');
+ });
+
+ /**
+ * @story View Product List
+ * @testClass ProductListingController
+ * @testMethod ProductListingController.loadList()
+ */
+ test('Product list loads successfully', async () => {
+ await expect(browser).toMatchImageSnapshot();
+ });
+
+ /**
+ * @story Filter Products
+ * @testClass ProductListingFilter
+ * @testMethod ProductListingFilter.applyFilters()
+ */
+ test('Product filtering works correctly', async () => {
+ await productListingDriver.openFilters();
+ await productListingDriver.applyFilters({ colors: ['Blue'] });
+ await productListingDriver.closeFilters();
+ await expect(browser).toMatchImageSnapshot();
+ });
+
+ /**
+ * @story Sort Products
+ * @testClass ProductListingSorter
+ * @testMethod ProductListingSorter.sort()
+ */
+ test('Product sorting works correctly', async () => {
+ await productListingDriver.openSorting();
+ await productListingDriver.applySorting({ by: 'Price low to high' });
+ await productListingDriver.closeSorting();
+ await expect(browser).toMatchImageSnapshot();
+ });
+ });
+});
diff --git a/e2e/demo/product-catalog/product-reviews.test.ts b/e2e/demo/product-catalog/product-reviews.test.ts
new file mode 100644
index 00000000..60342845
--- /dev/null
+++ b/e2e/demo/product-catalog/product-reviews.test.ts
@@ -0,0 +1,73 @@
+/**
+ * @epic Product Discovery
+ * @feature Read Reviews
+ * @package com.example.shop.reviews
+ */
+import { FakeBrowser, ProductReviewsDriver } from '../drivers';
+
+describe('Product Catalog', () => {
+ let browser: FakeBrowser;
+ let productReviewsDriver: ProductReviewsDriver;
+
+ /** Initialize test suite */
+ beforeAll(() => {
+ browser = new FakeBrowser();
+ productReviewsDriver = new ProductReviewsDriver(browser);
+ });
+
+ describe('Product Reviews', () => {
+ /** Open Product Details page and find reviews */
+ beforeAll(async () => {
+ await browser.navigate('/products/long-sleeved-t-shirt');
+ await productReviewsDriver.scrollToReviews();
+ });
+
+ /**
+ * @story View Reviews
+ * @testClass ReviewService
+ * @testMethod ReviewService.loadReviews()
+ */
+ test('Product reviews widget loads in collapsed state', async () => {
+ await expect(browser).toMatchImageSnapshot();
+ });
+
+ /**
+ * @story View Reviews
+ * @testClass ReviewService
+ * @testMethod ReviewService.loadReviews()
+ */
+ test('Product reviews widget can be expanded', async () => {
+ await productReviewsDriver.expandReviewsSection();
+ await expect(browser).toMatchImageSnapshot();
+ });
+
+ /**
+ * @story Submit Review
+ * @testClass ReviewService
+ * @testMethod ReviewService.submitReview()
+ */
+ test('Product reviews widget allows to submit a review', async () => {
+ await productReviewsDriver.openReviewForm();
+ await productReviewsDriver.fillReviewForm({
+ displayName: 'John Doe',
+ email: 'john.doe@example.com',
+ rating: 3,
+ title: 'Do something every day that will make you happy',
+ comment: 'Those great big fluffy clouds. ' +
+ 'There he comes. ' +
+ 'This is a happy place, little squirrels live here and play. ' +
+ 'Let your imagination just wonder around when you\'re doing these things. ' +
+ 'There isn\'t a rule. ' +
+ 'You just practice and find out which way works best for you.',
+ });
+
+ try {
+ await productReviewsDriver.submitReview();
+ } catch {
+ // Fail silently to see incorrect screenshot state
+ }
+
+ await expect(browser).toMatchImageSnapshot();
+ });
+ });
+});
diff --git a/e2e/demo/setup.ts b/e2e/demo/setup.ts
new file mode 100644
index 00000000..553f9155
--- /dev/null
+++ b/e2e/demo/setup.ts
@@ -0,0 +1,38 @@
+import util from 'node:util';
+import { allure } from 'jest-allure2-reporter/api';
+import { FakeBrowser, utils } from './drivers';
+
+jest.setTimeout(30000);
+
+allure.$plug((context) => {
+ context.handlebars.registerHelper('json', obj => {
+ return JSON.stringify(obj).replace(/['"]/g, '').slice(1, -1);
+ });
+
+ context.handlebars.registerHelper('stars', obj => {
+ return typeof obj === 'number' ? '★ '.repeat(obj) : '#STARS_ERROR';
+ });
+});
+
+expect.extend({
+ async toMatchImageSnapshot(received: unknown) {
+ if (!(received instanceof FakeBrowser)) {
+ throw new TypeError(`Expected a FakeBrowser instance, but got: ${util.inspect(received)}`);
+ }
+
+ const pass = await utils.toMatchImageSnapshot(received);
+ const pct = (10*Math.random()).toFixed(1);
+ return {
+ message: () => `Expected screenshots to match, but found ${pct}% difference`,
+ pass,
+ };
+ },
+});
+
+declare global {
+ namespace jest {
+ interface Matchers {
+ toMatchImageSnapshot(): Promise;
+ }
+ }
+}
diff --git a/e2e/demo/typings.ts b/e2e/demo/typings.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/e2e/jest.config.js b/e2e/jest.config.js
index 2654eaa1..bf42634c 100644
--- a/e2e/jest.config.js
+++ b/e2e/jest.config.js
@@ -6,8 +6,8 @@ const uuid = require('uuid');
/** @type {import('jest-allure2-reporter').ReporterOptions} */
const jestAllure2ReporterOptions = {
extends: [
- './presets/code-snippets',
- './presets/remark',
+ require.resolve('./presets/code-snippets'),
+ require.resolve('./presets/remark'),
],
categories: [
{
@@ -15,6 +15,11 @@ const jestAllure2ReporterOptions = {
matchedStatuses: ['failed'],
messageRegex: /.*\btoMatch(?:[A-Za-z]+)?Snapshot\b.*/,
},
+ {
+ name: 'Visual regressions',
+ matchedStatuses: ['failed'],
+ messageRegex: /.*\bscreenshot.*/,
+ },
{
name: 'Timeouts',
matchedStatuses: ['broken'],
diff --git a/e2e/package.json b/e2e/package.json
index 27fd8850..4a9da3bf 100644
--- a/e2e/package.json
+++ b/e2e/package.json
@@ -9,10 +9,15 @@
"start:dev": "npm run build --workspace=jest-allure2-reporter && npm test && npm start"
},
"devDependencies": {
+ "coffeescript": "^2.7.0",
"jest": "29.x.x",
- "jest-jasmine2": "29.x.x",
"jest-allure2-reporter": "..",
- "coffeescript": "^2.7.0",
+ "jest-jasmine2": "29.x.x",
+ "rehype-highlight": "^7.0.0",
+ "rehype-stringify": "^10.0.0",
+ "remark": "^15.0.1",
+ "remark-gfm": "^4.0.0",
+ "remark-rehype": "^11.1.0",
"ts-jest": "29.x.x",
"typescript": "5.x.x"
}
diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json
index 7368bb58..982682a0 100644
--- a/e2e/tsconfig.json
+++ b/e2e/tsconfig.json
@@ -7,5 +7,8 @@
"experimentalDecorators": false,
"noEmit": true,
"types": ["node", "jest", "jest-allure2-reporter/globals"]
- }
+ },
+ "include": [
+ "**/*.ts"
+ ]
}
diff --git a/src/serialization/FileAllureWriter.ts b/src/serialization/FileAllureWriter.ts
index 6d07eeb8..e777fcbe 100644
--- a/src/serialization/FileAllureWriter.ts
+++ b/src/serialization/FileAllureWriter.ts
@@ -11,7 +11,8 @@ async function writeJson(
data: unknown,
stringifier?: (key: string, value: unknown) => unknown,
) {
- await fs.writeFile(path, JSON.stringify(data, stringifier) + '\n');
+ const json = JSON.stringify(data, stringifier);
+ await fs.writeFile(path, json + '\n');
}
function regexpAwareStringifier(_key: string, value: unknown) {
diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js
index c1b60db6..daf1a5c2 100644
--- a/website/docusaurus.config.js
+++ b/website/docusaurus.config.js
@@ -70,7 +70,13 @@ module.exports = async () => {
label: 'Docs',
},
{
- href: 'https://allure-report-20231215124928.surge.sh/',
+ type: 'docSidebar',
+ sidebarId: 'apiSidebar',
+ position: 'left',
+ label: 'API',
+ },
+ {
+ href: 'https://jest-allure2-reporter-demo.surge.sh',
label: 'Demo',
position: 'left',
},
@@ -97,7 +103,7 @@ module.exports = async () => {
},
{
label: 'API',
- to: '/docs/api',
+ to: '/api',
},
],
},
diff --git a/website/package.json b/website/package.json
index d8bf48da..00488884 100644
--- a/website/package.json
+++ b/website/package.json
@@ -19,9 +19,9 @@
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
- "@docusaurus/core": "^3.2.0",
- "@docusaurus/module-type-aliases": "^3.2.0",
- "@docusaurus/preset-classic": "^3.2.0",
+ "@docusaurus/core": "^3.5.2",
+ "@docusaurus/module-type-aliases": "^3.5.2",
+ "@docusaurus/preset-classic": "^3.5.2",
"@mdx-js/react": "^3.0.0",
"@noomorph/docusaurus-search-local": "^1.1.1",
"clsx": "^2.0.0",
@@ -48,7 +48,7 @@
"node": ">=16.14"
},
"devDependencies": {
- "@docusaurus/remark-plugin-npm2yarn": "^3.2.1",
+ "@docusaurus/remark-plugin-npm2yarn": "^3.5.2",
"unist-util-visit": "^5.0.0"
}
}
diff --git a/website/sidebars.js b/website/sidebars.js
index f83a11c0..105776e4 100644
--- a/website/sidebars.js
+++ b/website/sidebars.js
@@ -19,6 +19,9 @@ const sidebars = {
aboutSidebar: [
{type: 'autogenerated', dirName: 'about'},
],
+ apiSidebar: [
+ {type: 'autogenerated', dirName: 'api'},
+ ],
// referenceSidebar: [
// 'reference/index',
// 'reference/modules/annotations',
diff --git a/website/src/components/ArticleHeader/index.jsx b/website/src/components/ArticleHeader/index.jsx
index 8debb766..20a4ef18 100644
--- a/website/src/components/ArticleHeader/index.jsx
+++ b/website/src/components/ArticleHeader/index.jsx
@@ -1,17 +1,3 @@
-import { useDoc } from '@docusaurus/theme-common/internal';
-import React from 'react';
-import Link from "@docusaurus/Link";
-import Admonition from '@theme/Admonition';
-
export function ArticleHeader() {
- const { frontMatter } = useDoc();
-
- if (!frontMatter.verified) {
- return (
-
- This page may refer to functionality that is not yet released.
- The latest beta version of the reporter can be seen on npm registry.
-
- );
- }
+ return;
}
diff --git a/website/src/pages/index.js b/website/src/pages/index.js
index 0e449fb0..bf5172fc 100644
--- a/website/src/pages/index.js
+++ b/website/src/pages/index.js
@@ -30,10 +30,10 @@ function HomepageHeader() {
+ jest-allure2-reporter{' '}
- Jest Allure 2 Reporter is an extension for Jest that
- generates test reports in the Allure Report format,
- a flexible and feature-rich reporting tool.
+ extends Jest testing framework to provide you with test reports
+ viewable with Allure, a flexible and feature-rich reporting tool.