@allurereport/plugin-awesome 3.0.0-beta.13 → 3.0.0-beta.15

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/dist/charts.d.ts CHANGED
@@ -1,14 +1,115 @@
1
- import type { Statistic, TestStatus } from "@allurereport/core-api";
1
+ import type { HistoryDataPoint, SeverityLevel, Statistic, TestResult, TestStatus } from "@allurereport/core-api";
2
+ import type { AllureStore, PluginContext } from "@allurereport/plugin-api";
2
3
  import type { PieArcDatum } from "d3-shape";
3
- export type TestResultSlice = {
4
+ import type { AwesomeOptions } from "./model.js";
5
+ import type { AwesomeDataWriter } from "./writer.js";
6
+ export type BasePieSlice = Pick<PieSlice, "status" | "count">;
7
+ export declare const d3Arc: import("d3-shape").Arc<any, PieArcDatum<BasePieSlice>>;
8
+ export declare const d3Pie: import("d3-shape").Pie<any, BasePieSlice>;
9
+ export declare const getPercentage: (value: number, total: number) => number;
10
+ export declare enum ChartType {
11
+ Trend = "trend",
12
+ Pie = "pie"
13
+ }
14
+ export declare enum ChartData {
15
+ Status = "status",
16
+ Severity = "severity"
17
+ }
18
+ export type ChartMode = "raw" | "percent";
19
+ export type ChartId = string;
20
+ export type ExecutionIdFn = (executionOrder: number) => string;
21
+ export type ExecutionNameFn = (executionOrder: number) => string;
22
+ export type TrendMetadataFnOverrides = {
23
+ executionIdAccessor?: ExecutionIdFn;
24
+ executionNameAccessor?: ExecutionNameFn;
25
+ };
26
+ export type TrendChartOptions = {
27
+ type: ChartType.Trend;
28
+ dataType: ChartData;
29
+ mode?: ChartMode;
30
+ title?: string;
31
+ limit?: number;
32
+ metadata?: TrendMetadataFnOverrides;
33
+ };
34
+ export type TrendPointId = string;
35
+ export type TrendSliceId = string;
36
+ export type BaseMetadata = Record<string, unknown>;
37
+ export interface BaseTrendSliceMetadata extends Record<string, unknown> {
38
+ executionId: string;
39
+ executionName: string;
40
+ }
41
+ export type TrendSliceMetadata<Metadata extends BaseMetadata> = BaseTrendSliceMetadata & Metadata;
42
+ export type TrendPoint = {
43
+ x: string;
44
+ y: number;
45
+ };
46
+ export type TrendSlice<Metadata extends BaseMetadata> = {
47
+ min: number;
48
+ max: number;
49
+ metadata: TrendSliceMetadata<Metadata>;
50
+ };
51
+ export type GenericTrendChartData<Metadata extends BaseMetadata, SeriesType extends string> = {
52
+ type: ChartType.Trend;
53
+ dataType: ChartData;
54
+ title?: string;
55
+ points: Record<TrendPointId, TrendPoint>;
56
+ slices: Record<TrendSliceId, TrendSlice<Metadata>>;
57
+ series: Record<SeriesType, TrendPointId[]>;
58
+ min: number;
59
+ max: number;
60
+ };
61
+ export interface StatusMetadata extends BaseTrendSliceMetadata {
62
+ }
63
+ export type StatusTrendSliceMetadata = TrendSliceMetadata<StatusMetadata>;
64
+ export type StatusTrendSlice = TrendSlice<StatusTrendSliceMetadata>;
65
+ export type StatusTrendChartData = GenericTrendChartData<StatusTrendSliceMetadata, TestStatus>;
66
+ export interface SeverityMetadata extends BaseTrendSliceMetadata {
67
+ }
68
+ export type SeverityTrendSliceMetadata = TrendSliceMetadata<SeverityMetadata>;
69
+ export type SeverityTrendSlice = TrendSlice<SeverityTrendSliceMetadata>;
70
+ export type SeverityTrendChartData = GenericTrendChartData<SeverityTrendSliceMetadata, SeverityLevel>;
71
+ export type TrendChartData = StatusTrendChartData | SeverityTrendChartData;
72
+ export type PieChartOptions = {
73
+ type: ChartType.Pie;
74
+ title?: string;
75
+ };
76
+ export type PieSlice = {
4
77
  status: TestStatus;
5
78
  count: number;
79
+ d: string | null;
6
80
  };
7
- export type TestResultChartData = {
81
+ export type PieChartData = {
82
+ type: ChartType.Pie;
83
+ title?: string;
84
+ slices: PieSlice[];
8
85
  percentage: number;
9
- slices: TestResultSlice[];
10
86
  };
11
- export declare const d3Arc: import("d3-shape").Arc<any, PieArcDatum<TestResultSlice>>;
12
- export declare const d3Pie: import("d3-shape").Pie<any, TestResultSlice>;
13
- export declare const getPercentage: (value: number, total: number) => number;
14
- export declare const getPieChartData: (stats: Statistic) => TestResultChartData;
87
+ export type GeneratedChartData = TrendChartData | PieChartData;
88
+ export type GeneratedChartsData = Record<ChartId, GeneratedChartData>;
89
+ export type ChartOptions = TrendChartOptions | PieChartOptions;
90
+ export type DashboardOptions = {
91
+ reportName?: string;
92
+ singleFile?: boolean;
93
+ logo?: string;
94
+ theme?: "light" | "dark";
95
+ reportLanguage?: "en" | "ru";
96
+ layout?: ChartOptions[];
97
+ filter?: (testResult: TestResult) => boolean;
98
+ };
99
+ export type TrendDataType = TestStatus | SeverityLevel;
100
+ export type TrendCalculationResult<T extends TrendDataType> = {
101
+ points: Record<TrendPointId, TrendPoint>;
102
+ series: Record<T, TrendPointId[]>;
103
+ };
104
+ export declare const createEmptyStats: <T extends TrendDataType>(items: readonly T[]) => Record<T, number>;
105
+ export declare const createEmptySeries: <T extends TrendDataType>(items: readonly T[]) => Record<T, string[]>;
106
+ export declare const normalizeStatistic: <T extends TrendDataType>(statistic: Partial<Record<T, number>>, itemType: readonly T[]) => Record<T, number>;
107
+ export declare const mergeTrendDataGeneric: <M extends BaseTrendSliceMetadata, T extends TrendDataType>(trendData: GenericTrendChartData<M, T>, trendDataPart: GenericTrendChartData<M, T>, itemType: readonly T[]) => GenericTrendChartData<M, T>;
108
+ export declare const getTrendDataGeneric: <M extends BaseTrendSliceMetadata, T extends TrendDataType>(stats: Record<T, number>, reportName: string, executionOrder: number, itemType: readonly T[], chartOptions: TrendChartOptions) => GenericTrendChartData<M, T>;
109
+ export declare const generateCharts: (options: AwesomeOptions, store: AllureStore, context: PluginContext) => Promise<GeneratedChartsData | undefined>;
110
+ export declare const generateTrendChart: (options: TrendChartOptions, stores: {
111
+ historyDataPoints: HistoryDataPoint[];
112
+ statistic: Statistic;
113
+ testResults: TestResult[];
114
+ }, context: PluginContext) => TrendChartData | undefined;
115
+ export declare const generateAllCharts: (writer: AwesomeDataWriter, store: AllureStore, options: AwesomeOptions, context: PluginContext) => Promise<void>;
package/dist/charts.js CHANGED
@@ -1,4 +1,5 @@
1
- import { statusesList } from "@allurereport/core-api";
1
+ import { getPieChartDataDashboard, getSeverityTrendData, getStatusTrendData, DEFAULT_CHART_HISTORY_LIMIT } from "@allurereport/web-commons";
2
+ import { randomUUID } from "crypto";
2
3
  import { arc, pie } from "d3-shape";
3
4
  export const d3Arc = arc().innerRadius(40).outerRadius(50).cornerRadius(2).padAngle(0.03);
4
5
  export const d3Pie = pie()
@@ -6,21 +7,161 @@ export const d3Pie = pie()
6
7
  .padAngle(0.03)
7
8
  .sortValues((a, b) => a - b);
8
9
  export const getPercentage = (value, total) => Math.floor((value / total) * 10000) / 100;
9
- export const getPieChartData = (stats) => {
10
- const convertedStatuses = statusesList
11
- .filter((status) => !!stats?.[status])
12
- .map((status) => ({
13
- status,
14
- count: stats[status],
15
- }));
16
- const arcsData = d3Pie(convertedStatuses);
17
- const slices = arcsData.map((arcData) => ({
18
- d: d3Arc(arcData),
19
- ...arcData.data,
20
- }));
21
- const percentage = getPercentage(stats.passed ?? 0, stats.total);
10
+ export var ChartType;
11
+ (function (ChartType) {
12
+ ChartType["Trend"] = "trend";
13
+ ChartType["Pie"] = "pie";
14
+ })(ChartType || (ChartType = {}));
15
+ export var ChartData;
16
+ (function (ChartData) {
17
+ ChartData["Status"] = "status";
18
+ ChartData["Severity"] = "severity";
19
+ })(ChartData || (ChartData = {}));
20
+ export const createEmptyStats = (items) => items.reduce((acc, item) => ({ ...acc, [item]: 0 }), {});
21
+ export const createEmptySeries = (items) => items.reduce((acc, item) => ({ ...acc, [item]: [] }), {});
22
+ export const normalizeStatistic = (statistic, itemType) => {
23
+ return itemType.reduce((acc, item) => {
24
+ acc[item] = statistic[item] ?? 0;
25
+ return acc;
26
+ }, {});
27
+ };
28
+ const calculateRawValues = (stats, executionId, itemType) => {
29
+ const points = {};
30
+ const series = createEmptySeries(itemType);
31
+ itemType.forEach((item) => {
32
+ const pointId = `${executionId}-${item}`;
33
+ const value = stats[item] ?? 0;
34
+ points[pointId] = {
35
+ x: executionId,
36
+ y: value,
37
+ };
38
+ series[item].push(pointId);
39
+ });
40
+ return { points, series };
41
+ };
42
+ const calculatePercentValues = (stats, executionId, itemType) => {
43
+ const points = {};
44
+ const series = createEmptySeries(itemType);
45
+ const values = Object.values(stats);
46
+ const total = values.reduce((sum, value) => sum + value, 0);
47
+ if (total === 0) {
48
+ return { points, series };
49
+ }
50
+ itemType.forEach((item) => {
51
+ const pointId = `${executionId}-${item}`;
52
+ const value = stats[item] ?? 0;
53
+ points[pointId] = {
54
+ x: executionId,
55
+ y: (value / total) * 100,
56
+ };
57
+ series[item].push(pointId);
58
+ });
59
+ return { points, series };
60
+ };
61
+ export const mergeTrendDataGeneric = (trendData, trendDataPart, itemType) => {
62
+ return {
63
+ ...trendData,
64
+ points: {
65
+ ...trendData.points,
66
+ ...trendDataPart.points,
67
+ },
68
+ slices: {
69
+ ...trendData.slices,
70
+ ...trendDataPart.slices,
71
+ },
72
+ series: Object.entries(trendDataPart.series).reduce((series, [group, pointIds]) => {
73
+ if (Array.isArray(pointIds)) {
74
+ return {
75
+ ...series,
76
+ [group]: [...(trendData.series?.[group] || []), ...pointIds],
77
+ };
78
+ }
79
+ return series;
80
+ }, trendData.series || createEmptySeries(itemType)),
81
+ min: Math.min(trendData.min ?? Infinity, trendDataPart.min),
82
+ max: Math.max(trendData.max ?? -Infinity, trendDataPart.max),
83
+ };
84
+ };
85
+ export const getTrendDataGeneric = (stats, reportName, executionOrder, itemType, chartOptions) => {
86
+ const { type, dataType, title, mode = "raw", metadata = {} } = chartOptions;
87
+ const { executionIdAccessor, executionNameAccessor } = metadata;
88
+ const executionId = executionIdAccessor ? executionIdAccessor(executionOrder) : `execution-${executionOrder}`;
89
+ const { points, series } = mode === "percent"
90
+ ? calculatePercentValues(stats, executionId, itemType)
91
+ : calculateRawValues(stats, executionId, itemType);
92
+ const slices = {};
93
+ const pointsAsArray = Object.values(points);
94
+ const pointsCount = pointsAsArray.length;
95
+ const values = pointsAsArray.map((point) => point.y);
96
+ const min = pointsCount ? Math.min(...values) : 0;
97
+ const max = pointsCount ? Math.max(...values) : 0;
98
+ if (pointsCount > 0) {
99
+ const executionName = executionNameAccessor ? executionNameAccessor(executionOrder) : reportName;
100
+ slices[executionId] = {
101
+ min,
102
+ max,
103
+ metadata: {
104
+ executionId,
105
+ executionName,
106
+ },
107
+ };
108
+ }
22
109
  return {
110
+ type,
111
+ dataType,
112
+ title,
113
+ points,
23
114
  slices,
24
- percentage,
115
+ series,
116
+ min,
117
+ max,
25
118
  };
26
119
  };
120
+ const generatePieChart = (options, stores) => {
121
+ const { statistic } = stores;
122
+ return getPieChartDataDashboard(statistic, options);
123
+ };
124
+ export const generateCharts = async (options, store, context) => {
125
+ const { charts } = options;
126
+ if (!charts) {
127
+ return undefined;
128
+ }
129
+ const historyDataPoints = await store.allHistoryDataPoints();
130
+ const statistic = await store.testsStatistic();
131
+ const testResults = await store.allTestResults();
132
+ return charts.reduce((acc, chartOptions) => {
133
+ const chartId = randomUUID();
134
+ let chart;
135
+ if (chartOptions.type === ChartType.Trend) {
136
+ chart = generateTrendChart(chartOptions, {
137
+ historyDataPoints,
138
+ statistic,
139
+ testResults,
140
+ }, context);
141
+ }
142
+ else if (chartOptions.type === ChartType.Pie) {
143
+ chart = generatePieChart(chartOptions, { statistic });
144
+ }
145
+ if (chart) {
146
+ acc[chartId] = chart;
147
+ }
148
+ return acc;
149
+ }, {});
150
+ };
151
+ export const generateTrendChart = (options, stores, context) => {
152
+ const newOptions = { limit: DEFAULT_CHART_HISTORY_LIMIT, ...options };
153
+ const { dataType } = newOptions;
154
+ const { statistic, historyDataPoints, testResults } = stores;
155
+ if (dataType === ChartData.Status) {
156
+ return getStatusTrendData(statistic, context.reportName, historyDataPoints, newOptions);
157
+ }
158
+ else if (dataType === ChartData.Severity) {
159
+ return getSeverityTrendData(testResults, context.reportName, historyDataPoints, newOptions);
160
+ }
161
+ };
162
+ export const generateAllCharts = async (writer, store, options, context) => {
163
+ const charts = await generateCharts(options, store, context);
164
+ if (charts && Object.keys(charts).length > 0) {
165
+ await writer.writeWidget("charts.json", charts);
166
+ }
167
+ };
@@ -1,12 +1,11 @@
1
1
  import { compareBy, incrementStatistic, nullsLast, ordinal, } from "@allurereport/core-api";
2
2
  import { filterTree, } from "@allurereport/plugin-api";
3
3
  import { createTreeByLabels, sortTree, transformTree } from "@allurereport/plugin-api";
4
- import { createBaseUrlScript, createFontLinkTag, createReportDataScript, createScriptTag, createStylesLinkTag, } from "@allurereport/web-commons";
4
+ import { createBaseUrlScript, createFontLinkTag, createReportDataScript, createScriptTag, createStylesLinkTag, getPieChartData, } from "@allurereport/web-commons";
5
5
  import Handlebars from "handlebars";
6
6
  import { readFile } from "node:fs/promises";
7
7
  import { createRequire } from "node:module";
8
8
  import { basename, join } from "node:path";
9
- import { getPieChartData } from "./charts.js";
10
9
  import { convertFixtureResult, convertTestResult } from "./converters.js";
11
10
  import { filterEnv } from "./environments.js";
12
11
  const require = createRequire(import.meta.url);
@@ -199,11 +198,15 @@ export const generateHistoryDataPoints = async (writer, store) => {
199
198
  return result;
200
199
  };
201
200
  export const generateStaticFiles = async (payload) => {
202
- const { reportName = "Allure Report", reportLanguage = "en", singleFile, logo = "", theme = "light", groupBy, reportFiles, reportDataFiles, reportUuid, allureVersion, layout = "base", } = payload;
201
+ const { reportName = "Allure Report", reportLanguage = "en", singleFile, logo = "", theme = "light", groupBy, reportFiles, reportDataFiles, reportUuid, allureVersion, layout = "base", charts = [], defaultSection = "", } = payload;
203
202
  const compile = Handlebars.compile(template);
204
203
  const manifest = await readTemplateManifest(payload.singleFile);
205
204
  const headTags = [];
206
205
  const bodyTags = [];
206
+ const sections = [];
207
+ if (charts.length) {
208
+ sections.push("charts");
209
+ }
207
210
  if (!payload.singleFile) {
208
211
  for (const key in manifest) {
209
212
  const fileName = manifest[key];
@@ -230,16 +233,20 @@ export const generateStaticFiles = async (payload) => {
230
233
  const mainJsContentBuffer = await readFile(mainJsSource);
231
234
  bodyTags.push(createScriptTag(`data:text/javascript;base64,${mainJsContentBuffer.toString("base64")}`));
232
235
  }
236
+ const now = Date.now();
233
237
  const reportOptions = {
234
238
  reportName,
235
239
  logo,
236
240
  theme,
237
241
  reportLanguage,
238
- createdAt: Date.now(),
242
+ createdAt: now,
239
243
  reportUuid,
240
244
  groupBy: groupBy?.length ? groupBy : ["parentSuite", "suite", "subSuite"],
245
+ cacheKey: now.toString(),
241
246
  layout,
242
247
  allureVersion,
248
+ sections,
249
+ defaultSection,
243
250
  };
244
251
  const html = compile({
245
252
  headTags: headTags.join("\n"),
package/dist/model.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { EnvironmentsConfig, TestResult } from "@allurereport/core-api";
2
+ import type { ChartOptions } from "./charts.js";
2
3
  export type AwesomeOptions = {
3
4
  reportName?: string;
4
5
  singleFile?: boolean;
@@ -14,6 +15,9 @@ export type AwesomeOptions = {
14
15
  name: string;
15
16
  };
16
17
  filter?: (testResult: TestResult) => boolean;
18
+ charts?: ChartOptions[];
19
+ sections?: string[];
20
+ defaultSection?: string;
17
21
  };
18
22
  export type TemplateManifest = Record<string, string>;
19
23
  export type AwesomePluginOptions = AwesomeOptions;
package/dist/plugin.js CHANGED
@@ -13,6 +13,7 @@ var _AwesomePlugin_writer, _AwesomePlugin_generate;
13
13
  import { getWorstStatus } from "@allurereport/core-api";
14
14
  import { preciseTreeLabels } from "@allurereport/plugin-api";
15
15
  import { join } from "node:path";
16
+ import { generateAllCharts } from "./charts.js";
16
17
  import { generateAttachmentsFiles, generateEnvironmentJson, generateEnvirontmentsList, generateHistoryDataPoints, generateNav, generatePieChart, generateStaticFiles, generateStatistic, generateTestCases, generateTestEnvGroups, generateTestResults, generateTree, generateVariables, } from "./generators.js";
17
18
  import { InMemoryReportDataWriter, ReportFileDataWriter } from "./writer.js";
18
19
  export class AwesomePlugin {
@@ -26,6 +27,7 @@ export class AwesomePlugin {
26
27
  const attachments = await store.allAttachments();
27
28
  await generateStatistic(__classPrivateFieldGet(this, _AwesomePlugin_writer, "f"), store, this.options.filter);
28
29
  await generatePieChart(__classPrivateFieldGet(this, _AwesomePlugin_writer, "f"), store, this.options.filter);
30
+ await generateAllCharts(__classPrivateFieldGet(this, _AwesomePlugin_writer, "f"), store, this.options, context);
29
31
  const convertedTrs = await generateTestResults(__classPrivateFieldGet(this, _AwesomePlugin_writer, "f"), store, this.options.filter);
30
32
  const treeLabels = preciseTreeLabels(!groupBy.length ? ["parentSuite", "suite", "subSuite"] : groupBy, convertedTrs, ({ labels }) => labels.map(({ name }) => name));
31
33
  await generateHistoryDataPoints(__classPrivateFieldGet(this, _AwesomePlugin_writer, "f"), store);
@@ -84,11 +86,14 @@ export class AwesomePlugin {
84
86
  const allTrs = (await store.allTestResults()).filter((tr) => this.options.filter ? this.options.filter(tr) : true);
85
87
  const duration = allTrs.reduce((acc, { duration: trDuration = 0 }) => acc + trDuration, 0);
86
88
  const worstStatus = getWorstStatus(allTrs.map(({ status }) => status));
89
+ const createdAt = allTrs.reduce((acc, { stop }) => Math.max(acc, stop || 0), 0);
87
90
  return {
88
91
  name: this.options.reportName || context.reportName,
89
92
  stats: await store.testsStatistic(this.options.filter),
90
93
  status: worstStatus ?? "passed",
91
94
  duration,
95
+ createdAt,
96
+ plugin: "Awesome",
92
97
  };
93
98
  }
94
99
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/plugin-awesome",
3
- "version": "3.0.0-beta.13",
3
+ "version": "3.0.0-beta.15",
4
4
  "description": "Allure Awesome Plugin – brand new HTML report with modern design and new features",
5
5
  "keywords": [
6
6
  "allure",
@@ -30,10 +30,10 @@
30
30
  "test": "rimraf ./out && vitest run"
31
31
  },
32
32
  "dependencies": {
33
- "@allurereport/core-api": "3.0.0-beta.13",
34
- "@allurereport/plugin-api": "3.0.0-beta.13",
35
- "@allurereport/web-awesome": "3.0.0-beta.13",
36
- "@allurereport/web-commons": "3.0.0-beta.13",
33
+ "@allurereport/core-api": "3.0.0-beta.15",
34
+ "@allurereport/plugin-api": "3.0.0-beta.15",
35
+ "@allurereport/web-awesome": "3.0.0-beta.15",
36
+ "@allurereport/web-commons": "3.0.0-beta.15",
37
37
  "d3-shape": "^3.2.0",
38
38
  "handlebars": "^4.7.8"
39
39
  },