@allurereport/reader 3.0.0-beta.3 → 3.0.0-beta.4

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.
@@ -1,5 +1,7 @@
1
1
  import { XMLParser } from "fast-xml-parser";
2
- import { cleanBadXmlCharacters, ensureInt, ensureString, isStringAnyRecord, isStringAnyRecordArray, } from "../xml-utils.js";
2
+ import * as console from "node:console";
3
+ import { ensureInt, ensureString } from "../utils.js";
4
+ import { cleanBadXmlCharacters, isStringAnyRecord, isStringAnyRecordArray } from "../xml-utils.js";
3
5
  const arrayTags = new Set([
4
6
  "test-suite.test-cases.test-case",
5
7
  "test-suite.test-cases.test-case.steps.step",
@@ -16,7 +18,7 @@ const xmlParser = new XMLParser({
16
18
  });
17
19
  const readerId = "allure1";
18
20
  export const allure1 = {
19
- async read(visitor, data) {
21
+ read: async (visitor, data) => {
20
22
  if (data.getOriginalFileName().endsWith("-testsuite.xml")) {
21
23
  try {
22
24
  const asBuffer = await data.asBuffer();
@@ -25,6 +27,9 @@ export const allure1 = {
25
27
  }
26
28
  const content = cleanBadXmlCharacters(asBuffer).toString("utf-8");
27
29
  const parsed = xmlParser.parse(content);
30
+ if (!isStringAnyRecord(parsed)) {
31
+ return false;
32
+ }
28
33
  return await parseRootElement(visitor, parsed);
29
34
  }
30
35
  catch (e) {
@@ -93,7 +98,7 @@ const parseSteps = (element) => {
93
98
  const { start, stop, duration } = parseTime(startElement, stopElement);
94
99
  const steps = parseSteps(stepsElement);
95
100
  return {
96
- name: ensureString(title, ensureString(name)),
101
+ name: ensureString(title) ?? ensureString(name),
97
102
  status: convertStatus(ensureString(status)),
98
103
  start,
99
104
  stop,
@@ -1,8 +1,10 @@
1
1
  import { notNull } from "@allurereport/core-api";
2
2
  import { XMLParser } from "fast-xml-parser";
3
+ import * as console from "node:console";
3
4
  import { randomUUID } from "node:crypto";
4
5
  import { parseProperties } from "../properties.js";
5
- import { cleanBadXmlCharacters, ensureBoolean, ensureInt, ensureString, isStringAnyRecord, isStringAnyRecordArray, } from "../xml-utils.js";
6
+ import { ensureBoolean, ensureInt, ensureString } from "../utils.js";
7
+ import { cleanBadXmlCharacters, isStringAnyRecord, isStringAnyRecordArray } from "../xml-utils.js";
6
8
  import { ParameterMode } from "./model.js";
7
9
  const arrayTags = new Set(["environment.parameter"]);
8
10
  const xmlParser = new XMLParser({
@@ -15,7 +17,7 @@ const xmlParser = new XMLParser({
15
17
  });
16
18
  const readerId = "allure2";
17
19
  export const allure2 = {
18
- async read(visitor, data) {
20
+ read: async (visitor, data) => {
19
21
  if (data.getOriginalFileName().match(/.*-attachment(\..+)?/)) {
20
22
  await visitor.visitAttachmentFile(data, { readerId });
21
23
  return true;
@@ -153,10 +155,7 @@ const processCategories = async (visitor, result) => {
153
155
  messageRegex: ensureString(value.messageRegex),
154
156
  traceRegex: ensureString(value.traceRegex),
155
157
  matchedStatuses: Array.isArray(value.matchedStatuses)
156
- ? value.matchedStatuses
157
- .map((v) => ensureString(v))
158
- .filter(notNull)
159
- .map((v) => v)
158
+ ? value.matchedStatuses.map((v) => ensureString(v)).filter(notNull)
160
159
  : [],
161
160
  flaky: ensureBoolean(value.flaky),
162
161
  }));
@@ -203,14 +202,14 @@ const processEnvironment = async (visitor, result) => {
203
202
  readerId,
204
203
  });
205
204
  };
206
- async function processFixtures(visitor, fixtures, type, children) {
205
+ const processFixtures = async (visitor, fixtures, type, children) => {
207
206
  if (fixtures) {
208
207
  for (const fixture of fixtures) {
209
208
  const dist = convertFixture(type, children, fixture);
210
209
  await visitor.visitTestFixtureResult(dist, { readerId });
211
210
  }
212
211
  }
213
- }
212
+ };
214
213
  const processTestResultContainer = async (visitor, result) => {
215
214
  if (result.children && result.children.length > 0) {
216
215
  await processFixtures(visitor, result.befores, "before", result.children);
@@ -1,2 +1,2 @@
1
- import { ResultsReader } from "@allurereport/reader-api";
1
+ import type { ResultsReader } from "@allurereport/reader-api";
2
2
  export declare const attachments: ResultsReader;
@@ -1,6 +1,6 @@
1
1
  const readerId = "attachments";
2
2
  export const attachments = {
3
- async read(visitor, data) {
3
+ read: async (visitor, data) => {
4
4
  await visitor.visitAttachmentFile(data, { readerId });
5
5
  return true;
6
6
  },
@@ -0,0 +1,2 @@
1
+ import type { ResultsReader } from "@allurereport/reader-api";
2
+ export declare const cucumberjson: ResultsReader;
@@ -0,0 +1,285 @@
1
+ import { BufferResultFile } from "@allurereport/reader-api";
2
+ import { randomUUID } from "node:crypto";
3
+ import { ensureArray, ensureInt, ensureString, isArray, isNonNullObject, isString } from "../utils.js";
4
+ import { STEP_NAME_PLACEHOLDER, TEST_NAME_PLACEHOLDER } from "./model.js";
5
+ const NS_IN_MS = 1000000;
6
+ const readerId = "cucumberjson";
7
+ const allureStepStatusPriorityOrder = {
8
+ failed: 0,
9
+ broken: 1,
10
+ unknown: 2,
11
+ skipped: 3,
12
+ passed: 4,
13
+ };
14
+ const cucumberStatusToAllureStatus = {
15
+ unknown: "unknown",
16
+ passed: "passed",
17
+ skipped: "skipped",
18
+ pending: "skipped",
19
+ ["undefined"]: "broken",
20
+ ambiguous: "broken",
21
+ failed: "failed",
22
+ };
23
+ const allureStepMessages = {
24
+ unknown: "The result of the step is unknown",
25
+ passed: "The step passed",
26
+ skipped: "The step was skipped because the previous step hadn't passed",
27
+ pending: "The step signalled pending during execution",
28
+ ["undefined"]: "The step didn't match any definition",
29
+ ambiguous: "The step matched more than one definition",
30
+ failed: "The step failed",
31
+ };
32
+ export const cucumberjson = {
33
+ read: async (visitor, data) => {
34
+ const originalFileName = data.getOriginalFileName();
35
+ try {
36
+ const parsed = await data.asJson();
37
+ if (parsed) {
38
+ let oneOrMoreFeaturesParsed = false;
39
+ for (const feature of parsed) {
40
+ oneOrMoreFeaturesParsed || (oneOrMoreFeaturesParsed = await processFeature(visitor, originalFileName, feature));
41
+ }
42
+ return oneOrMoreFeaturesParsed;
43
+ }
44
+ }
45
+ catch (e) {
46
+ console.error("error parsing", originalFileName, e);
47
+ return false;
48
+ }
49
+ return false;
50
+ },
51
+ readerId: () => readerId,
52
+ };
53
+ const processFeature = async (visitor, originalFileName, feature) => {
54
+ if (isCucumberFeature(feature)) {
55
+ const preProcessedFeature = preProcessFeature(feature);
56
+ for (const scenario of feature.elements) {
57
+ await processScenario(visitor, originalFileName, preProcessedFeature, scenario);
58
+ }
59
+ return true;
60
+ }
61
+ return false;
62
+ };
63
+ const processScenario = async (visitor, originalFileName, feature, scenario) => {
64
+ const preProcessedScenario = preProcessScenario(scenario);
65
+ if (shouldProcessScenario(preProcessedScenario)) {
66
+ const preProcessedSteps = await preProcessSteps(visitor, scenario.steps ?? []);
67
+ await visitor.visitTestResult(mapCucumberScenarioToAllureTestResult(feature, preProcessedScenario, preProcessedSteps), {
68
+ readerId,
69
+ metadata: { originalFileName },
70
+ });
71
+ }
72
+ };
73
+ const shouldProcessScenario = ({ type }) => type !== "background";
74
+ const preProcessSteps = async (visitor, steps) => {
75
+ const preProcessedSteps = [];
76
+ for (const step of steps) {
77
+ preProcessedSteps.push(await preProcessOneStep(visitor, step));
78
+ }
79
+ return preProcessedSteps;
80
+ };
81
+ const preProcessOneStep = async (visitor, step) => {
82
+ const { keyword, name, result } = step;
83
+ const { status, duration, error_message: errorMessage } = result ?? {};
84
+ return {
85
+ name: ensureString(name)?.trim(),
86
+ keyword: ensureString(keyword)?.trim(),
87
+ status: status ?? "unknown",
88
+ duration: ensureInt(duration),
89
+ errorMessage,
90
+ attachments: await processStepAttachments(visitor, step),
91
+ };
92
+ };
93
+ const processStepAttachments = async (visitor, step) => [
94
+ await processStepDocStringAttachment(visitor, step),
95
+ await processStepDataTableAttachment(visitor, step),
96
+ ...(await processStepEmbeddingAttachments(visitor, step)),
97
+ ].filter((s) => typeof s !== "undefined");
98
+ const processStepDocStringAttachment = async (visitor, { doc_string: docString }) => {
99
+ if (docString) {
100
+ const { value, content_type: contentType } = docString;
101
+ if (value && value.trim()) {
102
+ return await visitBufferAttachment(visitor, "Description", Buffer.from(value), contentType || "text/markdown");
103
+ }
104
+ }
105
+ };
106
+ const processStepDataTableAttachment = async (visitor, { rows }) => {
107
+ if (isArray(rows)) {
108
+ const content = formatDataTable(rows);
109
+ return await visitBufferAttachment(visitor, "Data", Buffer.from(content), "text/csv");
110
+ }
111
+ };
112
+ const processStepEmbeddingAttachments = async (visitor, { embeddings }) => {
113
+ const attachments = [];
114
+ const checkedEmbeddings = ensureArray(embeddings) ?? [];
115
+ const getName = checkedEmbeddings.length > 1 ? (i) => `Embedding ${i}` : () => "Embedding";
116
+ const embeddingsWithNames = checkedEmbeddings.map((e, i) => [e, getName(i + 1)]);
117
+ for (const [embedding, fallbackName] of embeddingsWithNames) {
118
+ if (isNonNullObject(embedding)) {
119
+ attachments.push(await visitBufferAttachment(visitor, ensureString(embedding.name, fallbackName), Buffer.from(ensureString(embedding.data, ""), "base64"), ensureString(embedding.mime_type, "application/octet-stream")));
120
+ }
121
+ }
122
+ return attachments;
123
+ };
124
+ const visitBufferAttachment = async (visitor, name, content, contentType) => {
125
+ const fileName = randomUUID();
126
+ await visitor.visitAttachmentFile(new BufferResultFile(content, fileName), { readerId });
127
+ return {
128
+ type: "attachment",
129
+ contentType,
130
+ originalFileName: fileName,
131
+ name,
132
+ };
133
+ };
134
+ const formatDataTable = (rows) => {
135
+ return rows
136
+ .filter((isNonNullObject))
137
+ .map(formatDataTableRow)
138
+ .filter(isString)
139
+ .join("\r\n");
140
+ };
141
+ const formatDataTableRow = ({ cells }) => {
142
+ const checkedCells = ensureArray(cells);
143
+ return checkedCells ? checkedCells.map(formatDataTableCell).join(",") : undefined;
144
+ };
145
+ const formatDataTableCell = (cell) => {
146
+ const escapedCell = ensureString(cell, "").replaceAll(String.raw `"`, String.raw `""`);
147
+ return `"${escapedCell}"`;
148
+ };
149
+ const isCucumberFeature = ({ keyword, elements }) => typeof keyword === "string" && keyword.toLowerCase() === "feature" && Array.isArray(elements);
150
+ const pairWithAllureSteps = (preProcessedCucumberSteps) => preProcessedCucumberSteps.map((c) => {
151
+ return {
152
+ preProcessedStep: c,
153
+ allureStep: createAllureStepResult(c),
154
+ };
155
+ });
156
+ const mapCucumberScenarioToAllureTestResult = (preProcessedFeature, scenario, preProcessedSteps) => {
157
+ const postProcessedSteps = pairWithAllureSteps(preProcessedSteps);
158
+ return {
159
+ fullName: calculateFullName(preProcessedFeature, scenario),
160
+ name: scenario.name ?? TEST_NAME_PLACEHOLDER,
161
+ description: scenario.description,
162
+ duration: convertDuration(calculateTestDuration(postProcessedSteps)),
163
+ steps: postProcessedSteps.map(({ allureStep }) => allureStep),
164
+ labels: calculateTestLabels(preProcessedFeature, scenario),
165
+ ...resolveTestResultStatusProps(postProcessedSteps),
166
+ };
167
+ };
168
+ const calculateTestLabels = ({ name: featureName, tags: featureTags }, { tags: scenarioTags }) => {
169
+ const labels = [];
170
+ if (featureName) {
171
+ labels.push({ name: "feature", value: featureName });
172
+ }
173
+ labels.push(...featureTags.map((value) => ({ name: "tag", value })), ...scenarioTags.map((value) => ({ name: "tag", value })));
174
+ return labels;
175
+ };
176
+ const preProcessFeature = (feature) => {
177
+ return {
178
+ id: ensureString(feature.id),
179
+ name: ensureString(feature.name),
180
+ uri: ensureString(feature.uri),
181
+ tags: parseTags(feature.tags),
182
+ };
183
+ };
184
+ const parseTags = (tags) => {
185
+ return (ensureArray(tags) ?? [])
186
+ .filter((isNonNullObject))
187
+ .map(({ name }) => name)
188
+ .filter(isString);
189
+ };
190
+ const preProcessScenario = (scenario) => {
191
+ return {
192
+ id: ensureString(scenario.id),
193
+ name: ensureString(scenario.name),
194
+ description: ensureString(scenario.description),
195
+ tags: parseTags(scenario.tags),
196
+ type: scenario.type,
197
+ };
198
+ };
199
+ const calculateFullName = ({ uri: featureUri, name: featureName, id: featureId }, { name: scenarioName, id: scenarioId }) => {
200
+ if (!scenarioName && !scenarioId) {
201
+ return randomUUID();
202
+ }
203
+ const featurePart = featureUri || featureName || featureId;
204
+ if (featurePart) {
205
+ const scenarioPart = scenarioName || scenarioId;
206
+ return `${featurePart}#${scenarioPart}`;
207
+ }
208
+ return scenarioId || scenarioName;
209
+ };
210
+ const calculateTestDuration = (cucumberAllureStepData) => cucumberAllureStepData.reduce((testDuration, { preProcessedStep: { duration } }) => typeof testDuration === "undefined" ? duration : testDuration + (duration ?? 0), undefined);
211
+ const resolveTestResultStatusProps = (cucumberAllureSteps) => {
212
+ const stepsData = getCucumberAllureStepWithMaxPriorityStatus(cucumberAllureSteps);
213
+ return stepsData
214
+ ? resolveResultOfTestFromStepsData(stepsData)
215
+ : {
216
+ status: "unknown",
217
+ message: "Step results are missing",
218
+ };
219
+ };
220
+ const resolveResultOfTestFromStepsData = ({ preProcessedStep: { status: cucumberStatus, errorMessage }, allureStep: { name, status }, }) => ({
221
+ status: status ?? "unknown",
222
+ ...resolveTestMessageAndTrace(name, cucumberStatus, errorMessage),
223
+ });
224
+ const resolveTestMessageAndTrace = (allureStepName, status, errorMessage) => status !== "passed"
225
+ ? {
226
+ message: resolveTestMessage(status, allureStepName),
227
+ trace: errorMessage,
228
+ }
229
+ : {};
230
+ const resolveTestMessage = (cucumberStepStatus, allureStepName) => {
231
+ switch (cucumberStepStatus) {
232
+ case "failed":
233
+ return `The step '${allureStepName}' failed`;
234
+ case "skipped":
235
+ return "One or more steps of the scenario were skipped";
236
+ case "pending":
237
+ return `The step '${allureStepName}' signalled pending during execution`;
238
+ case "undefined":
239
+ return `The step '${allureStepName}' didn't match any definition`;
240
+ case "ambiguous":
241
+ return `The step '${allureStepName}' matched more than one definition`;
242
+ case "unknown":
243
+ default:
244
+ return `The result of the step '${allureStepName}' is unknown`;
245
+ }
246
+ };
247
+ const getCucumberAllureStepWithMaxPriorityStatus = (cucumberAllureSteps) => {
248
+ switch (cucumberAllureSteps.length) {
249
+ case 0:
250
+ return undefined;
251
+ case 1:
252
+ return cucumberAllureSteps[0];
253
+ default:
254
+ return cucumberAllureSteps.reduce(statusPriorityReducingFn);
255
+ }
256
+ };
257
+ const statusPriorityReducingFn = (testDefiningStep, currentStep) => allureStepStatusPriorityOrder[testDefiningStep.allureStep.status] <=
258
+ allureStepStatusPriorityOrder[currentStep.allureStep.status]
259
+ ? testDefiningStep
260
+ : currentStep;
261
+ const createAllureStepResult = ({ keyword, name, status, duration, errorMessage, attachments, }) => ({
262
+ type: "step",
263
+ name: getAllureStepName(keyword, name),
264
+ steps: attachments,
265
+ ...mapCucumberStepResultToStepProps(status, duration, errorMessage),
266
+ });
267
+ const getAllureStepName = (keyword, name) => {
268
+ if (!name) {
269
+ return keyword ? `${keyword} <${STEP_NAME_PLACEHOLDER.toLowerCase()}>` : STEP_NAME_PLACEHOLDER;
270
+ }
271
+ return keyword ? `${keyword} ${name}` : name;
272
+ };
273
+ const mapCucumberStepResultToStepProps = (status, duration, errorMessage) => ({
274
+ status: cucumberStatusToAllureStatus[status ?? "unknown"] ?? "unknown",
275
+ duration: convertDuration(duration),
276
+ ...resolveStepMessageAndTrace(status, errorMessage),
277
+ });
278
+ const resolveStepMessageAndTrace = (status, errorMessage) => status !== "passed" || errorMessage
279
+ ? {
280
+ message: allureStepMessages[status ?? "unknown"] ?? allureStepMessages.unknown,
281
+ trace: errorMessage,
282
+ }
283
+ : {};
284
+ const convertDuration = (duration) => typeof duration !== "undefined" ? nsToMs(duration) : undefined;
285
+ const nsToMs = (ns) => Math.round(ns / NS_IN_MS);
@@ -0,0 +1,60 @@
1
+ export declare const TEST_NAME_PLACEHOLDER = "The scenario's name is not defined";
2
+ export declare const STEP_NAME_PLACEHOLDER = "The step's name is not defined";
3
+ export type CucumberFeature = {
4
+ description: string;
5
+ elements: CucumberFeatureElement[];
6
+ id: string;
7
+ keyword: string;
8
+ line: number;
9
+ name: string;
10
+ tags?: unknown;
11
+ uri: string;
12
+ };
13
+ export type CucumberFeatureElement = {
14
+ after?: CucumberStep[];
15
+ before?: CucumberStep[];
16
+ description: string;
17
+ id?: string;
18
+ keyword: string;
19
+ line: number;
20
+ name: string;
21
+ steps?: CucumberStep[];
22
+ tags?: unknown;
23
+ type: string;
24
+ };
25
+ export type CucumberStep = {
26
+ doc_string?: CucumberDocString;
27
+ embeddings?: unknown;
28
+ keyword?: string;
29
+ line?: number;
30
+ match?: CucumberStepMatch;
31
+ name?: string;
32
+ output?: string[];
33
+ result: CucumberStepResult;
34
+ rows?: unknown;
35
+ };
36
+ export type CucumberDocString = {
37
+ content_type?: string;
38
+ line?: number;
39
+ value?: string;
40
+ };
41
+ export type CucumberDatatableRow = {
42
+ cells: unknown;
43
+ };
44
+ export type CucumberStepResult = {
45
+ duration?: number;
46
+ error_message?: string;
47
+ status: string;
48
+ };
49
+ export type CucumberStepMatch = {
50
+ location: string;
51
+ };
52
+ export type CucumberTag = {
53
+ line: number;
54
+ name: unknown;
55
+ };
56
+ export type CucumberEmbedding = {
57
+ data: unknown;
58
+ mime_type: unknown;
59
+ name?: unknown;
60
+ };
@@ -0,0 +1,2 @@
1
+ export const TEST_NAME_PLACEHOLDER = "The scenario's name is not defined";
2
+ export const STEP_NAME_PLACEHOLDER = "The step's name is not defined";
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./allure1/index.js";
2
2
  export * from "./allure2/index.js";
3
+ export * from "./cucumberjson/index.js";
3
4
  export * from "./junitxml/index.js";
4
5
  export * from "./attachments/index.js";
5
- export * from "./model.js";
6
+ export type * from "./model.js";
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export * from "./allure1/index.js";
2
2
  export * from "./allure2/index.js";
3
+ export * from "./cucumberjson/index.js";
3
4
  export * from "./junitxml/index.js";
4
5
  export * from "./attachments/index.js";
5
- export * from "./model.js";
@@ -1,6 +1,7 @@
1
1
  import { XMLParser } from "fast-xml-parser";
2
2
  import * as console from "node:console";
3
- import { ensureString, isEmptyElement, isStringAnyRecord, isStringAnyRecordArray } from "../xml-utils.js";
3
+ import { ensureString } from "../utils.js";
4
+ import { isEmptyElement, isStringAnyRecord, isStringAnyRecordArray } from "../xml-utils.js";
4
5
  const arrayTags = new Set(["testsuite.testcase", "testsuites.testsuite", "testsuites.testsuite.testcase"]);
5
6
  const xmlParser = new XMLParser({
6
7
  parseTagValue: false,
@@ -12,7 +13,7 @@ const xmlParser = new XMLParser({
12
13
  });
13
14
  const readerId = "junit";
14
15
  export const junitXml = {
15
- async read(visitor, data) {
16
+ read: async (visitor, data) => {
16
17
  if (data.getOriginalFileName().endsWith(".xml")) {
17
18
  try {
18
19
  const content = await data.asUtf8String();
@@ -20,6 +21,9 @@ export const junitXml = {
20
21
  return false;
21
22
  }
22
23
  const parsed = xmlParser.parse(content);
24
+ if (!isStringAnyRecord(parsed)) {
25
+ return false;
26
+ }
23
27
  return await parseRootElement(visitor, parsed);
24
28
  }
25
29
  catch (e) {
@@ -72,7 +76,7 @@ const parseTestSuite = async (visitor, testSuite) => {
72
76
  }
73
77
  };
74
78
  const parseTestCase = async (visitor, suite, testCase) => {
75
- const { name, classname, time, failure, skipped } = testCase;
79
+ const { name, failure, skipped } = testCase;
76
80
  const { status, message, trace } = getStatus(failure, skipped);
77
81
  await visitor.visitTestResult({
78
82
  name: ensureString(name),
@@ -202,63 +202,6 @@ const hex = (char) => {
202
202
  }
203
203
  throw new Error(`Non-hex char ${char}`);
204
204
  };
205
- const escapeKey = (key) => {
206
- return mapcatChars(key, (char, code) => {
207
- if (isSeparator(char)) {
208
- return `\\${char}`;
209
- }
210
- return escapeCharacter(char, code, true);
211
- });
212
- };
213
- const escapeValue = (value) => {
214
- let escapeWhitespace = true;
215
- return mapcatChars(value, (char, code) => {
216
- if (!isWhitespace(char)) {
217
- escapeWhitespace = false;
218
- }
219
- return escapeCharacter(char, code, escapeWhitespace);
220
- });
221
- };
222
- const mapcatChars = (value, fn) => {
223
- let result = "";
224
- for (let i = 0; i < value.length; i++) {
225
- const char = value[i];
226
- const code = value.charCodeAt(i);
227
- result += fn(char, code);
228
- }
229
- return result;
230
- };
231
- const escapeCharacter = (char, code, escapeWhitespace) => {
232
- if (isAsciiPrintable(code)) {
233
- if (char === " " && escapeWhitespace) {
234
- return "\\ ";
235
- }
236
- else if (char === "\\") {
237
- return "\\\\";
238
- }
239
- else {
240
- return char;
241
- }
242
- }
243
- else if (char === "\t") {
244
- return "\\t";
245
- }
246
- else if (char === "\n") {
247
- return "\\n";
248
- }
249
- else if (char === "\f") {
250
- return "\\f";
251
- }
252
- else if (char === "\r") {
253
- return "\\r";
254
- }
255
- else if (code < 160 || code >= 256) {
256
- return escapeUnicode(code);
257
- }
258
- else {
259
- return char;
260
- }
261
- };
262
205
  const isWhitespace = (char) => {
263
206
  switch (char) {
264
207
  case "\t":
@@ -268,12 +211,6 @@ const isWhitespace = (char) => {
268
211
  }
269
212
  return false;
270
213
  };
271
- const isAsciiPrintable = (code) => code > 31 && code < 127;
272
- const escapeUnicode = (code) => {
273
- const unicode = code.toString(16);
274
- const prefix = "0".repeat(4 - unicode.length);
275
- return `\\u${prefix}${unicode}`;
276
- };
277
214
  const isSeparator = (char) => {
278
215
  switch (char) {
279
216
  case "=":
@@ -0,0 +1,13 @@
1
+ export declare const isBoolean: (value: unknown) => value is boolean;
2
+ export declare const isString: (value: unknown) => value is string;
3
+ export declare const isArray: <T = unknown>(value: unknown) => value is T[];
4
+ export declare const isNonNullObject: <T extends object = object>(value: unknown) => value is T;
5
+ export declare function ensureBoolean(value: unknown): boolean | undefined;
6
+ export declare function ensureBoolean(value: unknown, fallback: boolean): boolean;
7
+ export declare const ensureInt: (obj: unknown) => number | undefined;
8
+ export declare function ensureString(value: unknown): string | undefined;
9
+ export declare function ensureString(value: unknown, fallback: string): string;
10
+ export declare function ensureArray<T = unknown>(value: unknown): T[] | undefined;
11
+ export declare function ensureArray<T = unknown>(value: unknown, fallback: T[]): T[];
12
+ export declare function ensureObject<T extends object = object>(value: unknown): T | undefined;
13
+ export declare function ensureObject<T extends object = object>(value: unknown, fallback: T): T;
package/dist/utils.js ADDED
@@ -0,0 +1,27 @@
1
+ export const isBoolean = (value) => typeof value === "boolean";
2
+ export const isString = (value) => typeof value === "string";
3
+ export const isArray = (value) => Array.isArray(value);
4
+ export const isNonNullObject = (value) => typeof value === "object" && value !== null;
5
+ export function ensureBoolean(value, fallback) {
6
+ return isBoolean(value) ? value : fallback;
7
+ }
8
+ export const ensureInt = (obj) => {
9
+ if (typeof obj === "number") {
10
+ return obj;
11
+ }
12
+ const stringValue = ensureString(obj);
13
+ if (!stringValue) {
14
+ return undefined;
15
+ }
16
+ const parsed = parseInt(stringValue, 10);
17
+ return isNaN(parsed) ? undefined : parsed;
18
+ };
19
+ export function ensureString(value, fallback) {
20
+ return isString(value) ? value : fallback;
21
+ }
22
+ export function ensureArray(value, fallback) {
23
+ return isArray(value) ? value : fallback;
24
+ }
25
+ export function ensureObject(value, fallback) {
26
+ return isNonNullObject(value) ? value : fallback;
27
+ }
@@ -1,7 +1,4 @@
1
1
  export declare const isEmptyElement: (obj: unknown) => obj is "";
2
- export declare const ensureBoolean: (obj: unknown, fallback?: boolean) => boolean | undefined;
3
- export declare const ensureString: (obj: unknown, fallback?: string) => string | undefined;
4
- export declare const ensureInt: (obj: unknown) => number | undefined;
5
2
  export declare const isStringAnyRecord: (obj: unknown) => obj is Record<string, any>;
6
3
  export declare const isStringAnyRecordArray: (obj: unknown) => obj is Record<string, any>[];
7
4
  export declare const isBadXmlCharacter: (c: number) => boolean;
package/dist/xml-utils.js CHANGED
@@ -1,23 +1,6 @@
1
1
  export const isEmptyElement = (obj) => {
2
2
  return obj === "";
3
3
  };
4
- export const ensureBoolean = (obj, fallback) => {
5
- return typeof obj === "boolean" ? obj : fallback;
6
- };
7
- export const ensureString = (obj, fallback) => {
8
- return typeof obj === "string" ? obj : fallback;
9
- };
10
- export const ensureInt = (obj) => {
11
- if (typeof obj === "number") {
12
- return obj;
13
- }
14
- const stringValue = ensureString(obj);
15
- if (!stringValue) {
16
- return undefined;
17
- }
18
- const parsed = parseInt(stringValue);
19
- return isNaN(parsed) ? undefined : parsed;
20
- };
21
4
  export const isStringAnyRecord = (obj) => {
22
5
  if (typeof obj !== "object") {
23
6
  return false;
@@ -31,9 +14,9 @@ export const isStringAnyRecordArray = (obj) => {
31
14
  return Array.isArray(obj) && obj.every((item) => isStringAnyRecord(item));
32
15
  };
33
16
  export const isBadXmlCharacter = (c) => {
34
- let cDataCharacter = c < 32 && c != 9 && c != 13 && c != 10;
17
+ let cDataCharacter = c < 32 && c !== 9 && c !== 13 && c !== 10;
35
18
  cDataCharacter || (cDataCharacter = c >= 55296 && c < 57344);
36
- cDataCharacter || (cDataCharacter = c == 65534 || c == 65535);
19
+ cDataCharacter || (cDataCharacter = c === 65534 || c === 65535);
37
20
  return cDataCharacter;
38
21
  };
39
22
  export const cleanBadXmlCharacters = (input) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allurereport/reader",
3
- "version": "3.0.0-beta.3",
3
+ "version": "3.0.0-beta.4",
4
4
  "description": "Collection of utilities which helps to process different kind of test results as Allure Results",
5
5
  "keywords": [
6
6
  "allure",
@@ -21,12 +21,14 @@
21
21
  "scripts": {
22
22
  "build": "run clean && tsc --project ./tsconfig.json",
23
23
  "clean": "rimraf ./dist",
24
+ "eslint": "eslint ./src/**/*.{js,jsx,ts,tsx}",
25
+ "eslint:format": "eslint --fix ./src/**/*.{js,jsx,ts,tsx}",
24
26
  "test": "vitest run"
25
27
  },
26
28
  "dependencies": {
27
- "@allurereport/core-api": "3.0.0-beta.3",
28
- "@allurereport/plugin-api": "3.0.0-beta.3",
29
- "@allurereport/reader-api": "3.0.0-beta.3",
29
+ "@allurereport/core-api": "3.0.0-beta.4",
30
+ "@allurereport/plugin-api": "3.0.0-beta.4",
31
+ "@allurereport/reader-api": "3.0.0-beta.4",
30
32
  "fast-xml-parser": "^4.5.0"
31
33
  },
32
34
  "devDependencies": {