🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@allurereport/core-api

Package Overview
Dependencies
Maintainers
2
Versions
47
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@allurereport/core-api - npm Package Compare versions

Comparing version
3.3.1
to
3.4.0
+2
dist/utils/path.d.ts
export declare const toPosixPath: (path: string) => string;
export declare const joinPosixPath: (...parts: string[]) => string;
export const toPosixPath = (path) => path.replace(/\\/g, "/");
export const joinPosixPath = (...parts) => {
const segments = parts.map(toPosixPath).join("/").split("/");
const nonEmptySegments = [];
for (const segment of segments) {
if (segment.length > 0) {
nonEmptySegments.push(segment);
}
}
return nonEmptySegments.join("/");
};
export declare const sanitizeExternalUrl: (value: unknown) => string | undefined;
const ALLOWED_EXTERNAL_URL_PROTOCOLS = new Set(["http:", "https:", "mailto:", "tel:"]);
export const sanitizeExternalUrl = (value) => {
if (typeof value !== "string") {
return undefined;
}
const normalized = value.trim();
if (normalized.length === 0) {
return undefined;
}
const schemeMatch = normalized.match(/^([A-Za-z][A-Za-z0-9+.-]*):/);
if (!schemeMatch) {
return undefined;
}
const protocol = `${schemeMatch[1].toLowerCase()}:`;
if (!ALLOWED_EXTERNAL_URL_PROTOCOLS.has(protocol)) {
return undefined;
}
try {
return new URL(normalized).toString();
}
catch {
return undefined;
}
};
+2
-0

@@ -41,2 +41,3 @@ import type { Statistic } from "./aggregate.js";

export type CategoryRule = {
id?: string;
name: string;

@@ -59,2 +60,3 @@ matchers?: CategoryMatcher;

export interface CategoryDefinition extends Pick<CategoryRule, "name" | "expand" | "hide" | "groupEnvironments"> {
id: string;
matchers: Matcher[];

@@ -61,0 +63,0 @@ groupBy: CategoryGroupSelector[];

@@ -35,2 +35,24 @@ export const EMPTY_VALUE = "<Empty>";

const isMatcherArray = (value) => Array.isArray(value);
const hasControlChars = (value) => {
for (let index = 0; index < value.length; index++) {
const code = value.charCodeAt(index);
if (code <= 0x1f || (code >= 0x7f && code <= 0x9f)) {
return true;
}
}
return false;
};
const normalizeCategoryId = (id) => {
if (typeof id !== "string") {
return { valid: false, reason: "id must be a string" };
}
const normalized = id.trim();
if (normalized.length === 0) {
return { valid: false, reason: "id must not be empty" };
}
if (hasControlChars(normalized)) {
return { valid: false, reason: "id must not contain control characters" };
}
return { valid: true, normalized };
};
const normalizeMatchers = (rule, index) => {

@@ -89,2 +111,3 @@ const compatKeysUsed = rule.matchedStatuses !== undefined ||

const seen = new Map();
const sourceIdsByNormalizedId = new Map();
const applyRule = (rule, index) => {

@@ -97,4 +120,12 @@ if (!isPlainObject(rule)) {

}
const idValidationResult = normalizeCategoryId(rule.id ?? rule.name);
if (!idValidationResult.valid) {
throw new Error(`categories[${index}].id ${idValidationResult.reason}`);
}
const normalizedId = idValidationResult.normalized;
const sourceIds = sourceIdsByNormalizedId.get(normalizedId) ?? new Set();
sourceIds.add(rule.id ?? rule.name);
sourceIdsByNormalizedId.set(normalizedId, sourceIds);
const matchers = normalizeMatchers(rule, index);
const existing = seen.get(rule.name);
const existing = seen.get(normalizedId);
if (existing) {

@@ -124,2 +155,3 @@ existing.matchers.push(...matchers);

const norm = {
id: normalizedId,
name: rule.name,

@@ -134,3 +166,3 @@ matchers,

};
seen.set(rule.name, norm);
seen.set(normalizedId, norm);
normalized.push(norm);

@@ -140,2 +172,10 @@ };

DEFAULT_ERROR_CATEGORIES.forEach((rule, index) => applyRule(rule, rules.length + index));
sourceIdsByNormalizedId.forEach((sourceIds, normalizedId) => {
if (sourceIds.size <= 1) {
return;
}
throw new Error(`categories: normalized id ${JSON.stringify(normalizedId)} is produced by source ids [${Array.from(sourceIds)
.map((id) => JSON.stringify(id))
.join(",")}]`);
});
return normalized;

@@ -142,0 +182,0 @@ };

@@ -6,2 +6,3 @@ import type { Statistic } from "./aggregate.js";

export declare const severityLabelName = "severity";
export declare const fallbackTestCaseIdLabelName = "_fallbackTestCaseId";
export declare const unsuccessfulStatuses: Set<TestStatus>;

@@ -8,0 +9,0 @@ export declare const successfulStatuses: Set<TestStatus>;

export const statusesList = ["failed", "broken", "passed", "skipped", "unknown"];
export const severityLevels = ["blocker", "critical", "normal", "minor", "trivial"];
export const severityLabelName = "severity";
export const fallbackTestCaseIdLabelName = "_fallbackTestCaseId";
export const unsuccessfulStatuses = new Set(["failed", "broken"]);

@@ -5,0 +6,0 @@ export const successfulStatuses = new Set(["passed"]);

@@ -6,2 +6,6 @@ import type { TestLabel } from "./metadata.js";

}
export interface EnvironmentIdentity {
id: string;
name: string;
}
export type ReportVariables = Record<string, string>;

@@ -12,2 +16,3 @@ export type EnvironmentMatcherPayload = {

export type EnvironmentDescriptor = {
name?: string;
variables?: ReportVariables;

@@ -14,0 +19,0 @@ matcher: (payload: EnvironmentMatcherPayload) => boolean;

@@ -26,1 +26,3 @@ export type * from "./aggregate.js";

export * from "./utils/dictionary.js";
export * from "./utils/path.js";
export * from "./utils/url.js";

@@ -16,1 +16,3 @@ export * from "./constants.js";

export * from "./utils/dictionary.js";
export * from "./utils/path.js";
export * from "./utils/url.js";

@@ -9,2 +9,3 @@ export declare const createScriptTag: (src: string, options?: {

export declare const createBaseUrlScript: () => string;
export declare const stringifyForInlineScript: (value: unknown) => string;
export declare const createReportDataScript: (reportFiles?: {

@@ -11,0 +12,0 @@ name: string;

@@ -26,2 +26,10 @@ export const createScriptTag = (src, options) => {

};
export const stringifyForInlineScript = (value) => {
return JSON.stringify(value)
.replaceAll("<", "\\u003C")
.replaceAll(">", "\\u003E")
.replaceAll("&", "\\u0026")
.replaceAll("\u2028", "\\u2028")
.replaceAll("\u2029", "\\u2029");
};
export const createReportDataScript = (reportFiles = []) => {

@@ -35,3 +43,5 @@ if (!reportFiles?.length) {

}
const reportFilesDeclaration = reportFiles.map(({ name, value }) => `d('${name}','${value}')`).join(",");
const reportFilesDeclaration = reportFiles
.map(({ name, value }) => `d(${JSON.stringify(name)},${JSON.stringify(value)})`)
.join(",");
return `

@@ -38,0 +48,0 @@ <script async>

@@ -1,5 +0,18 @@

import type { EnvironmentsConfig } from "../environment.js";
import type { TestEnvGroup, TestResult } from "../model.js";
import type { EnvironmentIdentity } from "../environment.js";
import type { TestEnvGroup } from "../model.js";
export declare const DEFAULT_ENVIRONMENT = "default";
export declare const matchEnvironment: (envConfig: EnvironmentsConfig, tr: Pick<TestResult, "labels">) => string;
export declare const MAX_ENVIRONMENT_NAME_LENGTH = 64;
export declare const MAX_ENVIRONMENT_ID_LENGTH = 64;
export declare const DEFAULT_ENVIRONMENT_IDENTITY: EnvironmentIdentity;
export type EnvironmentValidationResult = {
valid: true;
normalized: string;
} | {
valid: false;
reason: string;
};
export declare const validateEnvironmentName: (name: unknown) => EnvironmentValidationResult;
export declare const validateEnvironmentId: (environmentId: unknown) => EnvironmentValidationResult;
export declare const assertValidEnvironmentName: (name: unknown, source?: string) => string;
export declare const formatNormalizedEnvironmentCollision: (sourcePath: string, normalized: string, originalKeys: string[]) => string;
export declare const getRealEnvsCount: (group: TestEnvGroup) => number;
export const DEFAULT_ENVIRONMENT = "default";
export const matchEnvironment = (envConfig, tr) => {
return (Object.entries(envConfig).find(([, { matcher }]) => matcher({ labels: tr.labels }))?.[0] ?? DEFAULT_ENVIRONMENT);
export const MAX_ENVIRONMENT_NAME_LENGTH = 64;
export const MAX_ENVIRONMENT_ID_LENGTH = 64;
export const DEFAULT_ENVIRONMENT_IDENTITY = {
id: DEFAULT_ENVIRONMENT,
name: DEFAULT_ENVIRONMENT,
};
const hasControlChars = (value) => {
for (let i = 0; i < value.length; i++) {
const code = value.charCodeAt(i);
if (code <= 0x1f || (code >= 0x7f && code <= 0x9f)) {
return true;
}
}
return false;
};
const hasPathLikeSegments = (value) => {
if (value.includes("/") || value.includes("\\")) {
return true;
}
return value === "." || value === "..";
};
export const validateEnvironmentName = (name) => {
if (typeof name !== "string") {
return { valid: false, reason: "name must be a string" };
}
const normalized = name.trim();
if (normalized.length === 0) {
return { valid: false, reason: "name must not be empty" };
}
if (normalized.length > MAX_ENVIRONMENT_NAME_LENGTH) {
return {
valid: false,
reason: `name must not exceed ${MAX_ENVIRONMENT_NAME_LENGTH} characters`,
};
}
if (hasControlChars(normalized)) {
return { valid: false, reason: "name must not contain control characters" };
}
if (hasPathLikeSegments(normalized)) {
return { valid: false, reason: "name must not contain path-like segments" };
}
return { valid: true, normalized };
};
export const validateEnvironmentId = (environmentId) => {
if (typeof environmentId !== "string") {
return { valid: false, reason: "id must be a string" };
}
const normalized = environmentId.trim();
if (normalized.length === 0) {
return { valid: false, reason: "id must not be empty" };
}
if (normalized.length > MAX_ENVIRONMENT_ID_LENGTH) {
return {
valid: false,
reason: `id must not exceed ${MAX_ENVIRONMENT_ID_LENGTH} characters`,
};
}
if (!/^[A-Za-z0-9_-]+$/.test(normalized)) {
return {
valid: false,
reason: "id must contain only latin letters, digits, underscores, and hyphens",
};
}
return { valid: true, normalized };
};
export const assertValidEnvironmentName = (name, source = "environment name") => {
const validationResult = validateEnvironmentName(name);
if (!validationResult.valid) {
throw new Error(`Invalid ${source} ${JSON.stringify(name)}: ${validationResult.reason}`);
}
return validationResult.normalized;
};
export const formatNormalizedEnvironmentCollision = (sourcePath, normalized, originalKeys) => `${sourcePath}: normalized key ${JSON.stringify(normalized)} is produced by original keys [${originalKeys.map((key) => JSON.stringify(key)).join(",")}]`;
export const getRealEnvsCount = (group) => {
const { testResultsByEnv = {} } = group ?? {};
const envsCount = Object.keys(testResultsByEnv).length ?? 0;
const envsCount = Object.keys(testResultsByEnv).length;
if (envsCount <= 1 && DEFAULT_ENVIRONMENT in testResultsByEnv) {

@@ -9,0 +79,0 @@ return 0;

import type { HistoryDataPoint, HistoryTestResult } from "../history.js";
import type { TestParameter } from "../metadata.js";
import type { TestResult } from "../model.js";
export declare const stringifyHistoryParams: (parameters?: TestParameter[]) => string;
export declare const getFallbackHistoryId: (tr: Pick<TestResult, "labels" | "parameters">) => string | undefined;
export declare const getHistoryIdCandidates: (tr: Pick<TestResult, "historyId" | "labels" | "parameters">) => string[];
export declare const filterUnknownByKnownIssues: (trs: TestResult[], knownIssueHistoryIds: ReadonlySet<string>) => TestResult[];
export declare const normalizeHistoryDataPointUrls: (historyDataPoint: HistoryDataPoint) => HistoryDataPoint;
export declare const selectHistoryTestResults: (historyDataPoints: HistoryDataPoint[], historyIdCandidates: readonly string[]) => HistoryTestResult[];
export declare const htrsByTr: (hdps: HistoryDataPoint[], tr: TestResult | HistoryTestResult) => HistoryTestResult[];

@@ -1,19 +0,80 @@

export const htrsByTr = (hdps, tr) => {
if (!tr?.historyId) {
import { createHash } from "node:crypto";
import { fallbackTestCaseIdLabelName } from "../constants.js";
import { findLastByLabelName } from "./label.js";
const md5 = (data) => createHash("md5").update(data).digest("hex");
const parametersCompare = (a, b) => {
return (a.name ?? "").localeCompare(b.name ?? "") || (a.value ?? "").localeCompare(b.value ?? "");
};
export const stringifyHistoryParams = (parameters = []) => {
return [...parameters]
.filter((parameter) => !parameter?.excluded)
.sort(parametersCompare)
.map((parameter) => `${parameter.name}:${parameter.value}`)
.join(",");
};
export const getFallbackHistoryId = (tr) => {
const fallbackTestCaseId = findLastByLabelName(tr.labels ?? [], fallbackTestCaseIdLabelName);
if (!fallbackTestCaseId) {
return undefined;
}
return `${fallbackTestCaseId}.${md5(stringifyHistoryParams(tr.parameters ?? []))}`;
};
export const getHistoryIdCandidates = (tr) => {
const result = [];
if (tr.historyId) {
result.push(tr.historyId);
}
const fallbackHistoryId = getFallbackHistoryId(tr);
if (fallbackHistoryId && !result.includes(fallbackHistoryId)) {
result.push(fallbackHistoryId);
}
return result;
};
export const filterUnknownByKnownIssues = (trs, knownIssueHistoryIds) => {
return trs.filter((tr) => {
const historyIdCandidates = getHistoryIdCandidates(tr);
if (historyIdCandidates.length === 0) {
return true;
}
return historyIdCandidates.every((historyId) => !knownIssueHistoryIds.has(historyId));
});
};
export const normalizeHistoryDataPointUrls = (historyDataPoint) => {
const { url } = historyDataPoint;
if (!url) {
return historyDataPoint;
}
let testResults = historyDataPoint.testResults;
for (const [historyId, historyTestResult] of Object.entries(historyDataPoint.testResults)) {
if (historyTestResult.url) {
continue;
}
if (testResults === historyDataPoint.testResults) {
testResults = { ...historyDataPoint.testResults };
}
testResults[historyId] = {
...historyTestResult,
url,
};
}
if (testResults === historyDataPoint.testResults) {
return historyDataPoint;
}
return {
...historyDataPoint,
testResults,
};
};
export const selectHistoryTestResults = (historyDataPoints, historyIdCandidates) => {
if (historyIdCandidates.length === 0) {
return [];
}
return hdps.reduce((acc, dp) => {
const htr = dp.testResults[tr.historyId];
if (htr) {
if (dp.url) {
const url = new URL(dp.url);
url.hash = tr.id;
acc.push({
...htr,
url: url.toString(),
});
return historyDataPoints.reduce((acc, historyDataPoint) => {
for (const historyId of historyIdCandidates) {
const historyTestResult = historyDataPoint.testResults[historyId];
if (!historyTestResult) {
continue;
}
else {
acc.push(htr);
}
acc.push(historyTestResult);
break;
}

@@ -23,1 +84,7 @@ return acc;

};
export const htrsByTr = (hdps, tr) => {
if (!tr?.historyId) {
return [];
}
return selectHistoryTestResults(hdps, [tr.historyId]);
};
import type { TestLabel } from "../index.js";
export declare const findByLabelName: (labels: TestLabel[], name: string) => string | undefined;
export declare const findLastByLabelName: (labels: TestLabel[], name: string) => string | undefined;
export declare const shouldHideLabel: (labelName: string, matchers?: readonly (string | RegExp)[]) => boolean;

@@ -12,1 +12,12 @@ export const findByLabelName = (labels, name) => {

};
export const shouldHideLabel = (labelName, matchers = []) => {
if (labelName.startsWith("_")) {
return true;
}
return matchers.some((matcher) => {
if (typeof matcher === "string") {
return matcher === labelName;
}
return new RegExp(matcher.source, matcher.flags).test(labelName);
});
};
+11
-22
{
"name": "@allurereport/core-api",
"version": "3.3.1",
"version": "3.4.0",
"description": "Allure Core API",

@@ -8,21 +8,21 @@ "keywords": [

],
"repository": "https://github.com/allure-framework/allure3",
"license": "Apache-2.0",
"author": "Qameta Software",
"repository": "https://github.com/allure-framework/allure3",
"files": [
"dist"
],
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": "./dist/index.js"
},
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "run clean && tsc --project ./tsconfig.json",
"clean": "rimraf ./dist",
"eslint": "eslint ./src/**/*.{js,jsx,ts,tsx}",
"eslint:format": "eslint --fix ./src/**/*.{js,jsx,ts,tsx}",
"test": "rimraf ./out && vitest run"
"test": "rimraf ./out && vitest run",
"lint": "oxlint --import-plugin src test features stories",
"lint:fix": "oxlint --import-plugin --fix src test features stories"
},

@@ -33,18 +33,7 @@ "dependencies": {

"devDependencies": {
"@stylistic/eslint-plugin": "^2.6.1",
"@types/d3-shape": "^3.1.6",
"@types/eslint": "^8.56.11",
"@types/node": "^20.17.9",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"@vitest/runner": "^2.1.9",
"@vitest/snapshot": "^2.1.9",
"allure-vitest": "^3.3.3",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsdoc": "^50.0.0",
"eslint-plugin-n": "^17.10.1",
"eslint-plugin-no-null": "^1.0.2",
"eslint-plugin-prefer-arrow": "^1.2.3",
"rimraf": "^6.0.1",

@@ -51,0 +40,0 @@ "typescript": "^5.6.3",