@allurereport/web-commons 3.16.1 → 3.18.0

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.
@@ -201,6 +201,7 @@ export const attachmentType = (type) => {
201
201
  case "image/jpeg":
202
202
  case "image/jpg":
203
203
  case "image/png":
204
+ case "image/webp":
204
205
  case "image/*":
205
206
  return "image";
206
207
  case "text/xml":
@@ -2,6 +2,7 @@ import type { TreeMapDataAccessor, TreeMapNode } from "@allurereport/charts-api"
2
2
  import type { TestResult } from "@allurereport/core-api";
3
3
  type SubtreeMetrics = {
4
4
  totalTests: number;
5
+ eligibleCount: number;
5
6
  passedTests: number;
6
7
  failedTests: number;
7
8
  otherTests: number;
@@ -1,3 +1,4 @@
1
+ import { getSuccessRate, includedInSuccessRate } from "@allurereport/core-api";
1
2
  import { createTreeByLabels, md5 } from "@allurereport/plugin-api";
2
3
  import { isChildrenLeavesOnly } from "../chart-utils.js";
3
4
  import { convertTreeDataToTreeMapNode, transformTreeMapNode } from "../treeMapUtils.js";
@@ -11,6 +12,7 @@ const leafFactoryFn = ({ id, name, status }) => ({
11
12
  const groupFactoryFn = (parentId, groupClassifier) => ({
12
13
  nodeId: md5((parentId ? `${parentId}.` : "") + groupClassifier),
13
14
  name: groupClassifier,
15
+ eligibleCount: 0,
14
16
  value: 0,
15
17
  passedTests: 0,
16
18
  failedTests: 0,
@@ -18,34 +20,36 @@ const groupFactoryFn = (parentId, groupClassifier) => ({
18
20
  });
19
21
  const addLeafToGroupFn = (group, leaf) => {
20
22
  group.value += leaf.value;
23
+ group.eligibleCount += includedInSuccessRate.has(leaf.status) ? 1 : 0;
21
24
  group.passedTests += leaf.status === "passed" ? 1 : 0;
22
25
  group.failedTests += leaf.status === "failed" ? 1 : 0;
23
26
  group.otherTests += leaf?.status && !["passed", "failed"].includes(leaf.status) ? 1 : 0;
24
27
  };
25
- const calculateColorValue = ({ totalTests, passedTests }) => {
26
- return totalTests > 0 ? passedTests / totalTests : 0;
27
- };
28
+ const calculateColorValue = ({ eligibleCount, passedTests }) => getSuccessRate({ passed: passedTests, failed: eligibleCount - passedTests });
28
29
  const calculateSubtreeMetrics = (node) => {
29
30
  if (!node.children || node.children.length === 0) {
30
31
  return {
31
- totalTests: 1,
32
+ totalTests: node.status ? 1 : 0,
33
+ eligibleCount: node.status && includedInSuccessRate.has(node.status) ? 1 : 0,
32
34
  passedTests: node?.status === "passed" ? 1 : 0,
33
35
  failedTests: node?.status === "failed" ? 1 : 0,
34
36
  otherTests: node?.status && !["passed", "failed"].includes(node.status) ? 1 : 0,
35
37
  };
36
38
  }
37
39
  let totalTests = 0;
40
+ let eligibleCount = 0;
38
41
  let passedTests = 0;
39
42
  let failedTests = 0;
40
43
  let otherTests = 0;
41
44
  for (const child of node.children) {
42
45
  const childMetrics = calculateSubtreeMetrics(child);
43
46
  totalTests += childMetrics.totalTests;
47
+ eligibleCount += childMetrics.eligibleCount;
44
48
  passedTests += childMetrics.passedTests;
45
49
  failedTests += childMetrics.failedTests;
46
50
  otherTests += childMetrics.otherTests;
47
51
  }
48
- return { totalTests, passedTests, failedTests, otherTests };
52
+ return { totalTests, eligibleCount, passedTests, failedTests, otherTests };
49
53
  };
50
54
  export const createSuccessRateDistributionTreeMap = (testResults) => {
51
55
  const treeByLabels = createTreeByLabels(testResults, behaviorLabels, leafFactoryFn, groupFactoryFn, addLeafToGroupFn);
@@ -58,6 +62,7 @@ export const createSuccessRateDistributionTreeMap = (testResults) => {
58
62
  const group = node;
59
63
  return {
60
64
  ...baseNode,
65
+ eligibleCount: group.eligibleCount,
61
66
  passedTests: group.passedTests,
62
67
  failedTests: group.failedTests,
63
68
  otherTests: group.otherTests,
@@ -68,6 +73,7 @@ export const createSuccessRateDistributionTreeMap = (testResults) => {
68
73
  return {
69
74
  ...baseNode,
70
75
  status: leaf.status,
76
+ eligibleCount: includedInSuccessRate.has(leaf.status) ? 1 : 0,
71
77
  passedTests: leaf?.status === "passed" ? 1 : 0,
72
78
  failedTests: leaf?.status === "failed" ? 1 : 0,
73
79
  otherTests: leaf?.status && !["passed", "failed"].includes(leaf.status) ? 1 : 0,
@@ -75,6 +81,7 @@ export const createSuccessRateDistributionTreeMap = (testResults) => {
75
81
  }
76
82
  }, () => ({
77
83
  id: "root",
84
+ eligibleCount: 0,
78
85
  passedTests: 0,
79
86
  failedTests: 0,
80
87
  otherTests: 0,
@@ -82,7 +89,7 @@ export const createSuccessRateDistributionTreeMap = (testResults) => {
82
89
  return transformTreeMapNode(convertedTree, (node) => {
83
90
  const subtreeMetrics = calculateSubtreeMetrics(node);
84
91
  const colorValue = calculateColorValue(subtreeMetrics);
85
- const { totalTests, ...restSubtreeMetrics } = subtreeMetrics;
92
+ const { totalTests: _totalTests, ...restSubtreeMetrics } = subtreeMetrics;
86
93
  if (isChildrenLeavesOnly(node)) {
87
94
  const value = node.children?.reduce((acc, child) => {
88
95
  return acc + (child.value ?? 0);
@@ -1,6 +1,5 @@
1
- import { statusesList } from "@allurereport/core-api";
1
+ import { getSuccessRate, statusesList } from "@allurereport/core-api";
2
2
  import { arc, pie } from "d3-shape";
3
- const getPercentage = (value, total) => Math.floor((value / total) * 10000) / 100;
4
3
  const createD3ArcGenerator = () => arc().innerRadius(40).outerRadius(50).cornerRadius(2).padAngle(0.03);
5
4
  const d3Pie = pie()
6
5
  .value((d) => d.count)
@@ -44,7 +43,7 @@ export const getPieChartValues = (stats) => {
44
43
  };
45
44
  })
46
45
  .filter((item) => item !== null);
47
- const percentage = getPercentage(stats.passed ?? 0, stats.total);
46
+ const percentage = Math.floor(getSuccessRate(stats) * 10000) / 100;
48
47
  return {
49
48
  slices,
50
49
  percentage,
@@ -1,4 +1,5 @@
1
1
  import { ChartType } from "@allurereport/charts-api";
2
+ import { emptyStatistic, getSuccessRate, getSuccessRateTotal, incrementStatistic, } from "@allurereport/core-api";
2
3
  const DEFAULT_LAYERS = ["unit", "integration", "e2e"];
3
4
  const getPercentage = (value, total) => {
4
5
  if (total === 0 || value === 0) {
@@ -14,13 +15,7 @@ export const generateTestingPyramidChart = (chartOptions, storeData) => {
14
15
  layersMap.set(layer, layer);
15
16
  layersMap.set(layer.toLocaleLowerCase(), layer);
16
17
  });
17
- const statsByLayers = new Map();
18
- layers.forEach((layer) => {
19
- statsByLayers.set(layer, {
20
- passed: 0,
21
- total: 0,
22
- });
23
- });
18
+ const statsByLayers = new Map(layers.map((layer) => [layer, emptyStatistic()]));
24
19
  for (const testResult of testResults) {
25
20
  const trLayer = testResult.labels.find((label) => label.name === "layer")?.value;
26
21
  if (!trLayer) {
@@ -31,10 +26,7 @@ export const generateTestingPyramidChart = (chartOptions, storeData) => {
31
26
  continue;
32
27
  }
33
28
  const data = statsByLayers.get(layer);
34
- data.total++;
35
- if (testResult.status === "passed") {
36
- data.passed++;
37
- }
29
+ incrementStatistic(data, testResult.status);
38
30
  }
39
31
  return {
40
32
  type: ChartType.TestingPyramid,
@@ -44,7 +36,8 @@ export const generateTestingPyramidChart = (chartOptions, storeData) => {
44
36
  return {
45
37
  layer,
46
38
  testCount: data.total,
47
- successRate: getPercentage(data.passed, data.total),
39
+ successRate: Math.floor(getSuccessRate(data) * 10000) / 100,
40
+ eligibleCount: getSuccessRateTotal(data),
48
41
  percentage: getPercentage(data.total, testResults.length),
49
42
  };
50
43
  }),
@@ -13,9 +13,9 @@ const getSeverity = (tr) => {
13
13
  export const generateTrSeveritiesChart = (props) => {
14
14
  const { options, storeData } = props;
15
15
  const { levels, statuses, includeUnset = true } = options;
16
- let trSeverityLevels = levels;
16
+ let trSeverityLevels = levels ? [...levels] : [];
17
17
  let trStatuses = statuses;
18
- if (!trSeverityLevels || trSeverityLevels.length === 0) {
18
+ if (trSeverityLevels.length === 0) {
19
19
  trSeverityLevels = DEFAULT_SEVERITY_LEVELS;
20
20
  }
21
21
  if (includeUnset) {
@@ -0,0 +1 @@
1
+ export declare const copyToClipboard: (text: string) => Promise<boolean>;
@@ -0,0 +1,21 @@
1
+ export const copyToClipboard = async (text) => {
2
+ try {
3
+ if (navigator.clipboard) {
4
+ await navigator.clipboard.writeText(text);
5
+ return true;
6
+ }
7
+ const textarea = document.createElement("textarea");
8
+ textarea.value = text;
9
+ textarea.style.position = "fixed";
10
+ textarea.style.opacity = "0";
11
+ document.body.appendChild(textarea);
12
+ textarea.focus();
13
+ textarea.select();
14
+ document.execCommand("copy");
15
+ document.body.removeChild(textarea);
16
+ return true;
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ };
@@ -44,6 +44,12 @@ const buildAqlFromFieldFilter = (field) => {
44
44
  valueType = "STRING";
45
45
  break;
46
46
  }
47
+ case "null": {
48
+ expressionValue = "null";
49
+ valueType = "NULL";
50
+ operator = "EQ";
51
+ break;
52
+ }
47
53
  default: {
48
54
  const exhaustiveCheck = type;
49
55
  throw new Error(`Unsupported field type: ${String(exhaustiveCheck)}`);
@@ -1,3 +1,3 @@
1
1
  export * from "./builders.js";
2
- export type { ArrayField, BooleanField, Field, FieldFilter, FieldFilterGroup, Filter, LogicalOperator, NumberField, StringField, } from "./model.js";
2
+ export type { ArrayField, BooleanField, Field, FieldFilter, FieldFilterGroup, Filter, LogicalOperator, NullField, NumberField, StringField, } from "./model.js";
3
3
  export { MAX_ARRAY_FIELD_VALUES } from "./model.js";
@@ -27,13 +27,19 @@ export type BooleanField<T extends string = string> = {
27
27
  strict?: never;
28
28
  type: "boolean";
29
29
  };
30
+ export type NullField<T extends string = string> = {
31
+ key: T;
32
+ value: null;
33
+ strict?: never;
34
+ type: "null";
35
+ };
30
36
  export type ArrayField<T extends string = string> = {
31
37
  key: T;
32
38
  value: string[];
33
39
  strict?: boolean;
34
40
  type: "array";
35
41
  };
36
- export type Field<T extends string = string> = StringField<T> | NumberField<T> | BooleanField<T> | ArrayField<T>;
42
+ export type Field<T extends string = string> = StringField<T> | NumberField<T> | BooleanField<T> | ArrayField<T> | NullField<T>;
37
43
  export type LogicalOperator = "AND" | "OR";
38
- export type AqlValueType = "STRING" | "NUMBER" | "BOOLEAN";
44
+ export type AqlValueType = "STRING" | "NUMBER" | "BOOLEAN" | "NULL";
39
45
  export declare const MAX_ARRAY_FIELD_VALUES = 20;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./data.js";
2
2
  export * from "./attachments.js";
3
+ export * from "./clipboard.js";
3
4
  export * from "./environments.js";
4
5
  export * from "./i18n.js";
5
6
  export * from "./charts/index.js";
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./data.js";
2
2
  export * from "./attachments.js";
3
+ export * from "./clipboard.js";
3
4
  export * from "./environments.js";
4
5
  export * from "./i18n.js";
5
6
  export * from "./charts/index.js";
@@ -12,3 +12,7 @@ export declare const scrollFocusIntoView: (target: HTMLElement, options?: {
12
12
  inset?: number;
13
13
  }) => void;
14
14
  export declare const scrollTreePaneToTop: (anchor?: HTMLElement | null) => void;
15
+ export declare const isFocusInView: (target: HTMLElement, options?: {
16
+ containerAttribute?: string;
17
+ inset?: number;
18
+ }) => boolean;
@@ -80,3 +80,10 @@ export const scrollTreePaneToTop = (anchor) => {
80
80
  }
81
81
  }
82
82
  };
83
+ export const isFocusInView = (target, options) => {
84
+ const containerAttribute = options?.containerAttribute ?? "data-tree-scroll-container";
85
+ const scrollRoot = findFocusScrollRoot(target, containerAttribute);
86
+ const rootRect = scrollRoot ? scrollRoot.getBoundingClientRect() : { top: 0, bottom: window.innerHeight };
87
+ const inset = scrollRoot ? computeStickyInset(scrollRoot, options?.inset ?? 4) : (options?.inset ?? 4);
88
+ return !isOutOfScrollport(target.getBoundingClientRect(), rootRect, inset);
89
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/web-commons",
3
- "version": "3.16.1",
3
+ "version": "3.18.0",
4
4
  "description": "Collection of utilities used across the web Allure reports",
5
5
  "keywords": [
6
6
  "allure",
@@ -26,10 +26,10 @@
26
26
  "lint:fix": "oxlint --import-plugin --fix src test features stories"
27
27
  },
28
28
  "dependencies": {
29
- "@allurereport/aql": "3.16.1",
30
- "@allurereport/charts-api": "3.16.1",
31
- "@allurereport/core-api": "3.16.1",
32
- "@allurereport/plugin-api": "3.16.1",
29
+ "@allurereport/aql": "3.18.0",
30
+ "@allurereport/charts-api": "3.18.0",
31
+ "@allurereport/core-api": "3.18.0",
32
+ "@allurereport/plugin-api": "3.18.0",
33
33
  "@preact/signals": "^2.6.1",
34
34
  "@preact/signals-core": "^1.12.2",
35
35
  "ansi-to-html": "^0.7.2",
@@ -37,7 +37,7 @@
37
37
  "d3-scale": "^4.0.2",
38
38
  "d3-shape": "^3.2.0",
39
39
  "dompurify": "^3.4.0",
40
- "nanoid": "^5.1.6"
40
+ "nanoid": "^5.1.16"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/d3-interpolate": "^3.0.4",