@allurereport/plugin-api 3.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # Plugin API
2
+
3
+ > Collection of interfaces for Allure plugins
4
+
5
+ [<img src="https://allurereport.org/public/img/allure-report.svg" height="85px" alt="Allure Report logo" align="right" />](https://allurereport.org "Allure Report")
6
+
7
+ - Learn more about Allure Report at https://allurereport.org
8
+ - 📚 [Documentation](https://allurereport.org/docs/) – discover official documentation for Allure Report
9
+ - ❓ [Questions and Support](https://github.com/orgs/allure-framework/discussions/categories/questions-support) – get help from the team and community
10
+ - 📢 [Official announcements](https://github.com/orgs/allure-framework/discussions/categories/announcements) – be in touch with the latest updates
11
+ - 💬 [General Discussion ](https://github.com/orgs/allure-framework/discussions/categories/general-discussion) – engage in casual conversations, share insights and ideas with the community
12
+
13
+ ---
14
+
15
+ ## Overview
16
+
17
+ The interfaces in the package describe the entities used for building Allure Plugins.
18
+
19
+ ## Install
20
+
21
+ Use your favorite package manager to install the package:
22
+
23
+ ```shell
24
+ npm add @allurereport/plugin-api
25
+ yarn add @allurereport/plugin-api
26
+ pnpm add @allurereport/plugin-api
27
+ ```
@@ -0,0 +1,11 @@
1
+ import type { PluginDescriptor } from "./plugin.js";
2
+ import type { QualityGateConfig } from "./qualityGate.js";
3
+ export interface Config {
4
+ name?: string;
5
+ output?: string;
6
+ historyPath?: string;
7
+ knownIssuesPath?: string;
8
+ qualityGate?: QualityGateConfig;
9
+ plugins?: Record<string, PluginDescriptor>;
10
+ }
11
+ export declare const defineConfig: (allureConfig: Config) => Promise<Config>;
package/dist/config.js ADDED
@@ -0,0 +1,3 @@
1
+ export const defineConfig = async (allureConfig) => {
2
+ return allureConfig;
3
+ };
@@ -0,0 +1,7 @@
1
+ export * from "./config.js";
2
+ export * from "./plugin.js";
3
+ export * from "./qualityGate.js";
4
+ export * from "./store.js";
5
+ export * from "./resultFile.js";
6
+ export * from "./utils/misc.js";
7
+ export * from "./utils/tree.js";
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./config.js";
2
+ export * from "./plugin.js";
3
+ export * from "./qualityGate.js";
4
+ export * from "./store.js";
5
+ export * from "./resultFile.js";
6
+ export * from "./utils/misc.js";
7
+ export * from "./utils/tree.js";
@@ -0,0 +1,34 @@
1
+ import { AllureStore } from "./store.js";
2
+ export interface PluginDescriptor {
3
+ import?: string;
4
+ enabled?: boolean;
5
+ options?: Record<string, any>;
6
+ }
7
+ export interface ReportFiles {
8
+ addFile(path: string, data: Buffer): Promise<void>;
9
+ }
10
+ export interface PluginState {
11
+ set(key: string, value: any): Promise<void>;
12
+ get(key: string): Promise<void>;
13
+ unset(key: string): Promise<void>;
14
+ }
15
+ export interface PluginContext {
16
+ state: PluginState;
17
+ allureVersion: string;
18
+ reportUuid: string;
19
+ reportName: string;
20
+ reportFiles: ReportFiles;
21
+ }
22
+ export interface BatchOptions {
23
+ maxTimeout?: number;
24
+ }
25
+ export interface Realtime {
26
+ onTestResults(listener: (trIds: string[]) => Promise<void>, options?: BatchOptions): void;
27
+ onTestFixtureResults(listener: (tfrIds: string[]) => Promise<void>, options?: BatchOptions): void;
28
+ onAttachmentFiles(listener: (afIds: string[]) => Promise<void>, options?: BatchOptions): void;
29
+ }
30
+ export interface Plugin {
31
+ start?(context: PluginContext, store: AllureStore, realtime: Realtime): Promise<void>;
32
+ update?(context: PluginContext, store: AllureStore): Promise<void>;
33
+ done?(context: PluginContext, store: AllureStore): Promise<void>;
34
+ }
package/dist/plugin.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,44 @@
1
+ import type { AllureStore } from "./store.js";
2
+ export type QualityGateRules = Record<string, any>;
3
+ export type QualityGateRulesBaseMeta<T> = {
4
+ type: T;
5
+ };
6
+ export type QualityGateLabelsRulesMeta = QualityGateRulesBaseMeta<"label"> & {
7
+ name: string;
8
+ value: string;
9
+ };
10
+ export type QualityGateParametersRulesMeta = QualityGateRulesBaseMeta<"parameter"> & {
11
+ name: string;
12
+ value: string;
13
+ };
14
+ export type QualityGateLabelsEnforceConfig = {
15
+ type: "label";
16
+ name: string;
17
+ value: string;
18
+ rules: QualityGateRules;
19
+ };
20
+ export type QualityGateParametersEnforceConfig = {
21
+ type: "parameter";
22
+ name: string;
23
+ value: string;
24
+ rules: QualityGateRules;
25
+ };
26
+ export type QualityGateRulesMeta = Omit<QualityGateLabelsRulesMeta, "rules"> | Omit<QualityGateParametersRulesMeta, "rules">;
27
+ export type QualityGateEnforceConfig = QualityGateLabelsEnforceConfig | QualityGateParametersEnforceConfig;
28
+ export type QualityGateValidationResult = {
29
+ success: boolean;
30
+ rule: string;
31
+ meta?: QualityGateRulesMeta;
32
+ expected?: number;
33
+ actual?: number;
34
+ message?: string;
35
+ };
36
+ export interface QualityGateValidator {
37
+ validate(store: AllureStore): Promise<QualityGateValidationResult>;
38
+ }
39
+ export type QualityGateValidatorConstructor = new (limit: number, meta?: QualityGateRulesMeta) => QualityGateValidator;
40
+ export type QualityGateConfig = {
41
+ rules?: QualityGateRules;
42
+ enforce?: QualityGateEnforceConfig[];
43
+ validators?: Record<string, QualityGateValidatorConstructor>;
44
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ import "node:fs/promises";
2
+ import { Readable } from "node:stream";
3
+ export interface ResultFile {
4
+ readContent: <T>(transform: (stream: Readable) => Promise<T | undefined>) => Promise<T | undefined>;
5
+ getOriginalFileName: () => string;
6
+ getContentType: () => string | undefined;
7
+ getContentLength: () => number | undefined;
8
+ asJson: <T>() => Promise<T | undefined>;
9
+ asUtf8String: () => Promise<string | undefined>;
10
+ asBuffer: () => Promise<Buffer | undefined>;
11
+ writeTo: (path: string) => Promise<void>;
12
+ }
@@ -0,0 +1 @@
1
+ import "node:fs/promises";
@@ -0,0 +1,30 @@
1
+ import type { AttachmentLink, HistoryDataPoint, HistoryTestResult, KnownTestFailure, Statistic, TestCase, TestFixtureResult, TestResult } from "@allurereport/core-api";
2
+ import { ResultFile } from "./resultFile.js";
3
+ export interface AllureStore {
4
+ allTestCases: () => Promise<TestCase[]>;
5
+ allTestResults: (options?: {
6
+ includeHidden: boolean;
7
+ }) => Promise<TestResult[]>;
8
+ allAttachments: () => Promise<AttachmentLink[]>;
9
+ allMetadata: () => Promise<Record<string, any>>;
10
+ allFixtures: () => Promise<TestFixtureResult[]>;
11
+ allHistoryDataPoints: () => Promise<HistoryDataPoint[]>;
12
+ allKnownIssues: () => Promise<KnownTestFailure[]>;
13
+ testCaseById: (tcId: string) => Promise<TestCase | undefined>;
14
+ testResultById: (trId: string) => Promise<TestResult | undefined>;
15
+ attachmentById: (attachmentId: string) => Promise<AttachmentLink | undefined>;
16
+ attachmentContentById: (attachmentId: string) => Promise<ResultFile | undefined>;
17
+ metadataByKey: <T>(key: string) => Promise<T | undefined>;
18
+ testResultsByTcId: (tcId: string) => Promise<TestResult[]>;
19
+ attachmentsByTrId: (trId: string) => Promise<AttachmentLink[]>;
20
+ retriesByTrId: (trId: string) => Promise<TestResult[]>;
21
+ historyByTrId: (trId: string) => Promise<HistoryTestResult[]>;
22
+ fixturesByTrId: (trId: string) => Promise<TestFixtureResult[]>;
23
+ failedTestResults: () => Promise<TestResult[]>;
24
+ unknownFailedTestResults: () => Promise<TestResult[]>;
25
+ testResultsByLabel: (labelName: string) => Promise<{
26
+ _: TestResult[];
27
+ [x: string]: TestResult[];
28
+ }>;
29
+ testsStatistic: (filter?: (testResult: TestResult) => boolean) => Promise<Statistic>;
30
+ }
package/dist/store.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare const md5: (data: string) => string;
@@ -0,0 +1,2 @@
1
+ import { createHash } from "node:crypto";
2
+ export const md5 = (data) => createHash("md5").update(data).digest("hex");
@@ -0,0 +1,2 @@
1
+ import type { DefaultTreeGroup, DefaultTreeLeaf, TestResult, TreeData } from "@allurereport/core-api";
2
+ export declare const createTreeByLabels: (data: TestResult[], labelNames: string[]) => TreeData<DefaultTreeLeaf, DefaultTreeGroup>;
@@ -0,0 +1,78 @@
1
+ import { emptyStatistic, incrementStatistic } from "@allurereport/core-api";
2
+ import { md5 } from "./misc.js";
3
+ const addLeaf = (node, nodeId) => {
4
+ if (node.leaves === undefined) {
5
+ node.leaves = [];
6
+ }
7
+ if (node.leaves.find((value) => value === nodeId)) {
8
+ return;
9
+ }
10
+ node.leaves.push(nodeId);
11
+ };
12
+ const addGroup = (node, nodeId) => {
13
+ if (node.groups === undefined) {
14
+ node.groups = [];
15
+ }
16
+ if (node.groups.find((value) => value === nodeId)) {
17
+ return;
18
+ }
19
+ node.groups.push(nodeId);
20
+ };
21
+ const createTree = (data, classifier, leafFactory, groupFactory, addLeafToGroup = () => { }) => {
22
+ const groupsByClassifier = {};
23
+ const leavesById = {};
24
+ const groupsById = {};
25
+ const root = { groups: [], leaves: [] };
26
+ for (const item of data) {
27
+ const leaf = leafFactory(item);
28
+ leavesById[leaf.nodeId] = leaf;
29
+ const itemGroups = classifier(item);
30
+ let parentGroups = [root];
31
+ for (const layer of itemGroups) {
32
+ if (layer.length === 0) {
33
+ break;
34
+ }
35
+ parentGroups = layer.flatMap((group) => {
36
+ return parentGroups.map((parentGroup) => {
37
+ const parentId = "nodeId" in parentGroup ? parentGroup.nodeId : "";
38
+ if (groupsByClassifier[parentId] === undefined) {
39
+ groupsByClassifier[parentId] = {};
40
+ }
41
+ if (groupsByClassifier[parentId][group] === undefined) {
42
+ const newGroup = groupFactory(parentId, group);
43
+ groupsByClassifier[parentId][group] = newGroup;
44
+ groupsById[newGroup.nodeId] = newGroup;
45
+ }
46
+ const currentGroup = groupsByClassifier[parentId][group];
47
+ addGroup(parentGroup, currentGroup.nodeId);
48
+ addLeafToGroup(currentGroup, leaf);
49
+ return currentGroup;
50
+ });
51
+ });
52
+ }
53
+ parentGroups.forEach((parentGroup) => addLeaf(parentGroup, leaf.nodeId));
54
+ }
55
+ return {
56
+ root,
57
+ groupsById,
58
+ leavesById,
59
+ };
60
+ };
61
+ const byLabels = (item, labelNames) => {
62
+ return labelNames.map((labelName) => item.labels.filter((label) => labelName === label.name).map((label) => label.value ?? "__unknown") ?? []);
63
+ };
64
+ export const createTreeByLabels = (data, labelNames) => {
65
+ return createTree(data, (item) => byLabels(item, labelNames), ({ id, name, status, duration, flaky, retries }) => ({
66
+ nodeId: id,
67
+ name,
68
+ status,
69
+ duration,
70
+ flaky,
71
+ }), (parentId, groupClassifier) => ({
72
+ nodeId: md5((parentId ? `${parentId}.` : "") + groupClassifier),
73
+ name: groupClassifier,
74
+ statistic: emptyStatistic(),
75
+ }), (group, leaf) => {
76
+ incrementStatistic(group.statistic, leaf.status);
77
+ });
78
+ };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@allurereport/plugin-api",
3
+ "version": "3.0.0-beta.3",
4
+ "description": "Allure Plugin API",
5
+ "keywords": [
6
+ "allure"
7
+ ],
8
+ "repository": "https://github.com/allure-framework/allure3",
9
+ "license": "Apache-2.0",
10
+ "author": "Qameta Software",
11
+ "type": "module",
12
+ "exports": {
13
+ ".": "./dist/index.js"
14
+ },
15
+ "main": "./dist/index.js",
16
+ "module": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "scripts": {
22
+ "build": "run clean && tsc --project ./tsconfig.json",
23
+ "clean": "rimraf ./dist",
24
+ "test": "rimraf ./out && vitest run"
25
+ },
26
+ "dependencies": {
27
+ "@allurereport/core-api": "3.0.0-beta.3"
28
+ },
29
+ "devDependencies": {
30
+ "@stylistic/eslint-plugin": "^2.6.1",
31
+ "@types/eslint": "^8.56.11",
32
+ "@types/node": "^20.17.9",
33
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
34
+ "@typescript-eslint/parser": "^8.0.0",
35
+ "@vitest/runner": "^2.1.8",
36
+ "allure-vitest": "^3.0.7",
37
+ "eslint": "^8.57.0",
38
+ "eslint-config-prettier": "^9.1.0",
39
+ "eslint-plugin-import": "^2.29.1",
40
+ "eslint-plugin-jsdoc": "^50.0.0",
41
+ "eslint-plugin-n": "^17.10.1",
42
+ "eslint-plugin-no-null": "^1.0.2",
43
+ "eslint-plugin-prefer-arrow": "^1.2.3",
44
+ "rimraf": "^6.0.1",
45
+ "typescript": "^5.6.3",
46
+ "vitest": "^2.1.8"
47
+ }
48
+ }