From 2f3084b045561fd368958d7645907da2de9c45a4 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 30 Aug 2024 18:17:07 +0700 Subject: [PATCH 01/10] feat(allure-cypress): command step parameter limits Fixes #1070 --- packages/allure-cypress/src/model.ts | 10 ++ packages/allure-cypress/src/reporter.ts | 29 ++-- packages/allure-cypress/src/runtime.ts | 5 +- packages/allure-cypress/src/state.ts | 4 + packages/allure-cypress/src/utils.ts | 37 +++++ .../allure-cypress/test/spec/commands.test.ts | 150 +++++++++++++++++- 6 files changed, 221 insertions(+), 14 deletions(-) diff --git a/packages/allure-cypress/src/model.ts b/packages/allure-cypress/src/model.ts index d8152ebe6..2f7ff4f98 100644 --- a/packages/allure-cypress/src/model.ts +++ b/packages/allure-cypress/src/model.ts @@ -8,6 +8,10 @@ export const ALLURE_REPORT_STEP_COMMAND = "__allure_report_step_command__"; export type AllureCypressConfig = ReporterConfig & { videoOnFailOnly?: boolean; + stepsFromCommands?: { + maxArgumentLength?: number; + maxArgumentDepth?: number; + }; }; export type CypressSuite = Mocha.Suite & { @@ -174,6 +178,12 @@ export type SpecContext = { }; export type AllureSpecState = { + config: { + stepsFromCommands: { + maxArgumentLength: number; + maxArgumentDepth: number; + }; + }; initialized: boolean; testPlan: TestPlanV1 | null | undefined; messages: CypressMessage[]; diff --git a/packages/allure-cypress/src/reporter.ts b/packages/allure-cypress/src/reporter.ts index 702114514..7bdda66f9 100644 --- a/packages/allure-cypress/src/reporter.ts +++ b/packages/allure-cypress/src/reporter.ts @@ -32,7 +32,7 @@ import type { CypressTestStartMessage, SpecContext, } from "./model.js"; -import { last } from "./utils.js"; +import { defaultRuntimeConfig, last } from "./utils.js"; export class AllureCypress { allureRuntime: ReporterRuntime; @@ -489,19 +489,28 @@ export class AllureCypress { }; } -const getInitialSpecState = (): AllureSpecState => ({ +const createRuntimeState = (allureConfig?: AllureCypressConfig): AllureSpecState => ({ + config: applyDefaultsToRuntimeConfig(allureConfig), initialized: false, messages: [], testPlan: parseTestPlan(), }); -/** - * Explicitly enables the selective run feature. - * @param config The Cypress configuration. - */ -export const enableTestPlan = (config: Cypress.PluginConfigOptions) => { - config.env.allure = getInitialSpecState(); - return config; +const applyDefaultsToRuntimeConfig = ({ + stepsFromCommands: { + maxArgumentLength = defaultRuntimeConfig.stepsFromCommands.maxArgumentLength, + maxArgumentDepth = defaultRuntimeConfig.stepsFromCommands.maxArgumentDepth, + } = defaultRuntimeConfig.stepsFromCommands, +}: AllureCypressConfig = defaultRuntimeConfig): AllureSpecState["config"] => ({ + stepsFromCommands: { + maxArgumentDepth, + maxArgumentLength, + }, +}); + +const initializeRuntimeState = (cypressConfig: Cypress.PluginConfigOptions, allureConfig?: AllureCypressConfig) => { + cypressConfig.env.allure = createRuntimeState(allureConfig); + return cypressConfig; }; /** @@ -539,7 +548,7 @@ export const allureCypress = ( allureCypressReporter.attachToCypress(on); if (cypressConfig && "env" in cypressConfig) { - enableTestPlan(cypressConfig); + initializeRuntimeState(cypressConfig, allureConfig); } return allureCypressReporter; diff --git a/packages/allure-cypress/src/runtime.ts b/packages/allure-cypress/src/runtime.ts index d4a0cae5d..f0182e2c7 100644 --- a/packages/allure-cypress/src/runtime.ts +++ b/packages/allure-cypress/src/runtime.ts @@ -26,6 +26,7 @@ import { ALLURE_REPORT_STEP_COMMAND } from "./model.js"; import { dropCurrentTest, enqueueRuntimeMessage, + getConfig, getCurrentTest, getRuntimeMessages, setCurrentTest, @@ -40,6 +41,7 @@ import { isTestReported, iterateTests, markTestAsReported, + stringifyCommandArgument, uint8ArrayToBase64, } from "./utils.js"; @@ -361,11 +363,12 @@ export const reportTestSkip = (test: CypressTest) => { }; export const reportCommandStart = (command: CypressCommand) => { + const { maxArgumentDepth, maxArgumentLength } = getConfig().stepsFromCommands; enqueueRuntimeMessage({ type: "cypress_command_start", data: { name: `Command "${command.attributes.name}"`, - args: command.attributes.args.map((arg) => (typeof arg === "string" ? arg : JSON.stringify(arg, null, 2))), + args: command.attributes.args.map((arg) => stringifyCommandArgument(arg, maxArgumentLength, maxArgumentDepth)), start: Date.now(), }, }); diff --git a/packages/allure-cypress/src/state.ts b/packages/allure-cypress/src/state.ts index bc4cd9367..316308dfd 100644 --- a/packages/allure-cypress/src/state.ts +++ b/packages/allure-cypress/src/state.ts @@ -1,9 +1,11 @@ import type { AllureSpecState, CypressMessage, CypressTest } from "./model.js"; +import { defaultRuntimeConfig } from "./utils.js"; export const getAllureState = () => { let state = Cypress.env("allure") as AllureSpecState; if (!state) { state = { + config: defaultRuntimeConfig, initialized: false, messages: [], testPlan: undefined, @@ -41,3 +43,5 @@ export const setCurrentTest = (test: CypressTest) => { export const dropCurrentTest = () => { getAllureState().currentTest = undefined; }; + +export const getConfig = () => getAllureState().config; diff --git a/packages/allure-cypress/src/utils.ts b/packages/allure-cypress/src/utils.ts index 84d6e73bc..a960eadc4 100644 --- a/packages/allure-cypress/src/utils.ts +++ b/packages/allure-cypress/src/utils.ts @@ -5,6 +5,13 @@ import { ALLURE_REPORT_STEP_COMMAND, ALLURE_REPORT_SYSTEM_HOOK } from "./model.j import type { CypressCommand, CypressHook, CypressSuite, CypressTest } from "./model.js"; import { getAllureTestPlan } from "./state.js"; +export const defaultRuntimeConfig = { + stepsFromCommands: { + maxArgumentLength: 128, + maxArgumentDepth: 3, + }, +}; + export const uint8ArrayToBase64 = (data: unknown) => { // @ts-ignore const u8arrayLike = Array.isArray(data) || data.buffer; @@ -159,3 +166,33 @@ const removeSortedIndices = (arr: T[], indices: readonly number[]) => { arr.splice(indices[i], 1); } }; + +export const stringifyCommandArgument = (value: any, maxLength: number, maxDepth: number) => { + if (typeof value === "string") { + return limitString(value, maxLength); + } + + const parents: object[] = []; + const circularReferenceRemover = function (this: object, _: string, v: unknown) { + if (typeof v !== "object" || v === null) { + return v; + } + + while (parents.length > 0 && !Object.is(parents.at(-1), this)) { + parents.pop(); + } + + if (parents.length >= maxDepth + 1 || parents.includes(v)) { + return undefined; + } + + parents.push(v); + return v; + }; + + const argumentText = JSON.stringify(value, circularReferenceRemover); + return limitString(argumentText, maxLength); +}; + +const limitString = (value: string, maxLength: number) => + value.length <= maxLength ? value : `${value.substring(0, maxLength)}...`; diff --git a/packages/allure-cypress/test/spec/commands.test.ts b/packages/allure-cypress/test/spec/commands.test.ts index 0d8836579..9ef60d4d1 100644 --- a/packages/allure-cypress/test/spec/commands.test.ts +++ b/packages/allure-cypress/test/spec/commands.test.ts @@ -1,5 +1,6 @@ import { expect, it } from "vitest"; import { Stage, Status } from "allure-js-commons"; +import { issue } from "allure-js-commons"; import { runCypressInlineTest } from "../utils.js"; it("reports test with cypress command", async () => { @@ -25,7 +26,7 @@ it("reports test with cypress command", async () => { parameters: expect.arrayContaining([ expect.objectContaining({ name: String.raw`Argument [0]`, - value: JSON.stringify(1, null, 2), + value: JSON.stringify(1), }), ]), }), @@ -43,7 +44,7 @@ it("reports test with cypress command", async () => { parameters: expect.arrayContaining([ expect.objectContaining({ name: String.raw`Argument [0]`, - value: JSON.stringify([1, 2, 3], null, 2), + value: JSON.stringify([1, 2, 3]), }), ]), }), @@ -52,7 +53,7 @@ it("reports test with cypress command", async () => { parameters: expect.arrayContaining([ expect.objectContaining({ name: String.raw`Argument [0]`, - value: JSON.stringify({ foo: 1, bar: 2, baz: 3 }, null, 2), + value: JSON.stringify({ foo: 1, bar: 2, baz: 3 }), }), ]), }), @@ -77,3 +78,146 @@ it("doesn't report cypress command when they shouldn't be reported", async () => expect(tests[0].stage).toBe(Stage.FINISHED); expect(tests[0].steps).toHaveLength(0); }); + +it("should impose limits on command arguments", async () => { + issue("1070"); + const { tests } = await runCypressInlineTest({ + "cypress/e2e/sample.cy.js": () => ` + it("foo", () => { + const obj1 = {}; + obj1.ref = obj1; // should remove a direct circular reference 'ref' + cy.wrap(obj1); + + const sibling = {}; + cy.wrap({ ref: { foo: sibling, bar: sibling } }); // it's okay to have the same object on different paths + + const obj2 = { ref: {} }; + obj2.ref.ref = obj2; + cy.wrap(obj2); // should remove an indirect circular reference 'ref.ref' + + cy.wrap("A".repeat(1000)); // should truncate string values + cy.wrap(Array(1000).fill("A")); // should truncate objects + cy.wrap({ foo: { bar: { baz: { qux: {}, qut: 10 } } } }) // should remove 'qux' at nesting level 4 but keep 'qut' + }); + `, + }); + + expect(tests).toEqual([ + expect.objectContaining({ + steps: [ + expect.objectContaining({ + parameters: [ + { + name: "Argument [0]", + value: JSON.stringify({}), + }, + ], + }), + expect.objectContaining({ + parameters: [ + { + name: "Argument [0]", + value: JSON.stringify({ ref: { foo: {}, bar: {} } }), + }, + ], + }), + expect.objectContaining({ + parameters: [ + { + name: "Argument [0]", + value: JSON.stringify({ ref: {} }), + }, + ], + }), + expect.objectContaining({ + parameters: [ + { + name: "Argument [0]", + value: `${"A".repeat(128)}...`, + }, + ], + }), + expect.objectContaining({ + parameters: [ + { + name: "Argument [0]", + value: `[${String.raw`"A",`.repeat(31)}"A"...`, + }, + ], + }), + expect.objectContaining({ + parameters: [ + { + name: "Argument [0]", + value: JSON.stringify({ foo: { bar: { baz: { qut: 10 } } } }), + }, + ], + }), + ], + }), + ]); +}); + +it("should take the limits from the config", async () => { + issue("1070"); + const { tests } = await runCypressInlineTest({ + "cypress/e2e/sample.cy.js": () => ` + it("foo", () => { + cy.wrap("A".repeat(100)); // should truncate string values + cy.wrap(Array(100).fill("A")); // should truncate objects + cy.wrap({ foo: { bar: { }, baz: "qux" } }) // should remove 'bar' at nesting level 2 but keep 'baz' + }); + `, + "cypress.config.js": ({ allureCypressModuleBasePath }) => ` + const { allureCypress } = require("${allureCypressModuleBasePath}/reporter.js"); + + module.exports = { + e2e: { + baseUrl: "https://allurereport.org", + viewportWidth: 1240, + setupNodeEvents: (on, config) => { + allureCypress(on, config, { + stepsFromCommands: { + maxArgumentLength: 25, + maxArgumentDepth: 1, + } + }); + + return config; + }, + }, + }; + `, + }); + + expect(tests).toEqual([ + expect.objectContaining({ + steps: [ + expect.objectContaining({ + parameters: [ + { + name: "Argument [0]", + value: `${"A".repeat(25)}...`, + }, + ], + }), + expect.objectContaining({ + parameters: [ + { + name: "Argument [0]", + value: `[${String.raw`"A",`.repeat(6)}...`, + }, + ], + }), + expect.objectContaining({ + parameters: [ + { + name: "Argument [0]", + value: JSON.stringify({ foo: { baz: "qux" } }), + }, + ], + }), + ], + }), + ]); +}); From c66ed9fef31095562592fab0e8154ef9d658bdd3 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 30 Aug 2024 19:44:42 +0700 Subject: [PATCH 02/10] docs(allure-cypress): document new proeprties, add testplan comment --- packages/allure-cypress/README.md | 69 +++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/packages/allure-cypress/README.md b/packages/allure-cypress/README.md index 5ecc3677a..d2afaaacd 100644 --- a/packages/allure-cypress/README.md +++ b/packages/allure-cypress/README.md @@ -71,13 +71,30 @@ Learn more about Allure Cypress from the official documentation at ## Allure Cypress options -| Option | Description | Default | -|-----------------|----------------------------------------------------------------------------------------------------------------------|--------------------| -| resultsDir | The path of the results folder. | `./allure-results` | -| videoOnFailOnly | When video capturing is enabled, set this option to `true` to attach the video to failed specs only. | `undefined` | -| links | Allure Runtime API link templates. | `undefined` | -| environmentInfo | A set of key-value pairs to display in the Environment section of the report | `undefined` | -| categories | An array of category definitions, each describing a [category of defects](https://allurereport.org/docs/categories/) | `undefined` | + +Customize Allure Cypress by providing a configuration object as the third argument +of `allureCypress`. + +The following options are supported: + +| Option | Description | Default | +|-------------------|----------------------------------------------------------------------------------------------------------------------|---------------------------------| +| resultsDir | The path of the results folder. | `./allure-results` | +| videoOnFailOnly | When video capturing is enabled, set this option to `true` to attach the video to failed specs only. | `false` | +| links | Allure Runtime API link templates. | `undefined` | +| stepsFromCommands | Options that affect how Allure creates steps from Cypress commands | See [below](#stepsfromcommands) | +| environmentInfo | A set of key-value pairs to display in the Environment section of the report | `undefined` | +| categories | An array of category definitions, each describing a [category of defects](https://allurereport.org/docs/categories/) | `undefined` | + +### stepsFromCommands + +| Property | Description | Default | +|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------|---------| +| maxArgumentLength | The maximum length of the parameter value created from Cypress command argument. The rest of the characters are replaces with `...`. | 128 | +| maxArgumentDepth | The maximum depth of the Cypress command argument (an array or an object) that will be converted to the corresponding step parameter value. | 3 | + + +### Example Here is an example of the Allure Cypress configuration: @@ -100,6 +117,10 @@ export default defineConfig({ nameTemplate: "ISSUE-%s", }, }, + stepsFromCommands: { + maxArgumentLength: 64, + maxArgumentDepth: 5, + }, environmentInfo: { OS: os.platform(), Architecture: os.arch(), @@ -157,7 +178,39 @@ export default defineConfig({ }); ``` -## Known limitations +## Common issues + +### The test plan feature doesn't work + +Make sure you pass the Cypress config as the second argument of `allureCypress`. + +Correct: + +```javascript +allureCypress(on, config); +``` + +Also correct: + +```javascript +allureCypress(on, config, { + resultsDir: "output", +}); +``` + +Incorrect (the test plan won't work): + +```javascript +allureCypress(on); +``` + +Also incorrect (the legacy style; the test plan won't work either): + +```javascript +allureCypress(on, { + resultsDir: "output", +}); +``` ### `setupNodeEvents` limitations From bdcc0ef6f0d4527ba9998185211f2172cfa74bb7 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Mon, 2 Sep 2024 17:14:33 +0700 Subject: [PATCH 03/10] feat(allure-js-commons): move 'serialize' to /sdk/utils This will allow using 'serialize' from environments other than Node.js. --- packages/allure-js-commons/src/sdk/index.ts | 1 + .../src/sdk/reporter/utils.ts | 12 ---- packages/allure-js-commons/src/sdk/utils.ts | 12 ++++ .../test/sdk/reporter/utils.spec.ts | 57 +------------------ .../allure-js-commons/test/sdk/utils.spec.ts | 56 ++++++++++++++++++ packages/allure-mocha/src/legacy.ts | 2 +- 6 files changed, 71 insertions(+), 69 deletions(-) diff --git a/packages/allure-js-commons/src/sdk/index.ts b/packages/allure-js-commons/src/sdk/index.ts index 6139f8dfa..991a68fea 100644 --- a/packages/allure-js-commons/src/sdk/index.ts +++ b/packages/allure-js-commons/src/sdk/index.ts @@ -25,4 +25,5 @@ export { isPromise, hasLabel, stripAnsi, + serialize, } from "./utils.js"; diff --git a/packages/allure-js-commons/src/sdk/reporter/utils.ts b/packages/allure-js-commons/src/sdk/reporter/utils.ts index 38bd739c6..870905964 100644 --- a/packages/allure-js-commons/src/sdk/reporter/utils.ts +++ b/packages/allure-js-commons/src/sdk/reporter/utils.ts @@ -137,18 +137,6 @@ export const getRelativePath = (filepath: string) => { export const deepClone = (obj: T): T => JSON.parse(JSON.stringify(obj)); -export const serialize = (val: unknown): string => { - if (typeof val === "object" && !(val instanceof Map || val instanceof Set)) { - return JSON.stringify(val); - } - - if (val === undefined) { - return "undefined"; - } - - return (val as any).toString(); -}; - export const getSuiteLabels = (suites: readonly string[]): Label[] => { if (suites.length === 0) { return []; diff --git a/packages/allure-js-commons/src/sdk/utils.ts b/packages/allure-js-commons/src/sdk/utils.ts index 595087c87..63867dcfb 100644 --- a/packages/allure-js-commons/src/sdk/utils.ts +++ b/packages/allure-js-commons/src/sdk/utils.ts @@ -141,3 +141,15 @@ export const getUnfinishedStepsMessages = (messages: RuntimeMessage[]) => { export const isPromise = (obj: any): obj is PromiseLike => !!obj && (typeof obj === "object" || typeof obj === "function") && typeof obj.then === "function"; + +export const serialize = (val: unknown): string => { + if (typeof val === "object" && !(val instanceof Map || val instanceof Set)) { + return JSON.stringify(val); + } + + if (val === undefined) { + return "undefined"; + } + + return (val as any).toString(); +}; diff --git a/packages/allure-js-commons/test/sdk/reporter/utils.spec.ts b/packages/allure-js-commons/test/sdk/reporter/utils.spec.ts index c7a3893ec..0dc63b072 100644 --- a/packages/allure-js-commons/test/sdk/reporter/utils.spec.ts +++ b/packages/allure-js-commons/test/sdk/reporter/utils.spec.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; import { LabelName } from "../../../src/model.js"; -import { getSuiteLabels, serialize } from "../../../src/sdk/reporter/utils.js"; +import { getSuiteLabels } from "../../../src/sdk/reporter/utils.js"; describe("getSuiteLabels", () => { describe("with empty suites", () => { @@ -54,58 +54,3 @@ describe("getSuiteLabels", () => { }); }); }); - -describe("serialize", () => { - describe("with object", () => { - it("returns JSON string", () => { - // eslint-disable-next-line @stylistic/quotes - expect(serialize({ foo: "bar" })).toBe('{"foo":"bar"}'); - }); - }); - - describe("with array", () => { - it("returns JSON string", () => { - // eslint-disable-next-line @stylistic/quotes - expect(serialize(["foo", "bar"])).toBe('["foo","bar"]'); - }); - }); - - describe("with map", () => { - it("returns JSON string", () => { - expect(serialize(new Map([["foo", "bar"]]))).toBe("[object Map]"); - }); - }); - - describe("with set", () => { - it("returns JSON string", () => { - expect(serialize(new Set(["foo", "bar"]))).toBe("[object Set]"); - }); - }); - - describe("with undefined", () => { - it("returns undefined string", () => { - expect(serialize(undefined)).toBe("undefined"); - }); - }); - - describe("with null", () => { - it("returns null string", () => { - expect(serialize(null)).toBe("null"); - }); - }); - - describe("with function", () => { - it("returns function string", () => { - expect(serialize(() => {})).toBe("() => {\n }"); - }); - }); - - describe("with primitives", () => { - it("returns stringified value", () => { - // eslint-disable-next-line @stylistic/quotes - expect(serialize("foo")).toBe("foo"); - expect(serialize(123)).toBe("123"); - expect(serialize(true)).toBe("true"); - }); - }); -}); diff --git a/packages/allure-js-commons/test/sdk/utils.spec.ts b/packages/allure-js-commons/test/sdk/utils.spec.ts index 04961565c..cb0b4ea7b 100644 --- a/packages/allure-js-commons/test/sdk/utils.spec.ts +++ b/packages/allure-js-commons/test/sdk/utils.spec.ts @@ -8,6 +8,7 @@ import { getStatusFromError, isAnyStepFailed, isMetadataTag, + serialize, } from "../../src/sdk/utils.js"; type Executable = StepResult | TestResult | FixtureResult; @@ -266,3 +267,58 @@ describe("isMetadataTag", () => { expect(isMetadataTag("@allure.label.x=y")).toBeTruthy(); }); }); + +describe("serialize", () => { + describe("with object", () => { + it("returns JSON string", () => { + // eslint-disable-next-line @stylistic/quotes + expect(serialize({ foo: "bar" })).toBe('{"foo":"bar"}'); + }); + }); + + describe("with array", () => { + it("returns JSON string", () => { + // eslint-disable-next-line @stylistic/quotes + expect(serialize(["foo", "bar"])).toBe('["foo","bar"]'); + }); + }); + + describe("with map", () => { + it("returns JSON string", () => { + expect(serialize(new Map([["foo", "bar"]]))).toBe("[object Map]"); + }); + }); + + describe("with set", () => { + it("returns JSON string", () => { + expect(serialize(new Set(["foo", "bar"]))).toBe("[object Set]"); + }); + }); + + describe("with undefined", () => { + it("returns undefined string", () => { + expect(serialize(undefined)).toBe("undefined"); + }); + }); + + describe("with null", () => { + it("returns null string", () => { + expect(serialize(null)).toBe("null"); + }); + }); + + describe("with function", () => { + it("returns function string", () => { + expect(serialize(() => {})).toBe("() => {\n }"); + }); + }); + + describe("with primitives", () => { + it("returns stringified value", () => { + // eslint-disable-next-line @stylistic/quotes + expect(serialize("foo")).toBe("foo"); + expect(serialize(123)).toBe("123"); + expect(serialize(true)).toBe("true"); + }); + }); +}); diff --git a/packages/allure-mocha/src/legacy.ts b/packages/allure-mocha/src/legacy.ts index 13a64f562..c4d6a8901 100644 --- a/packages/allure-mocha/src/legacy.ts +++ b/packages/allure-mocha/src/legacy.ts @@ -3,7 +3,7 @@ import type { ContentType, ParameterOptions } from "allure-js-commons"; import { Status } from "allure-js-commons"; import type { Category } from "allure-js-commons/sdk"; import { getStatusFromError, isPromise } from "allure-js-commons/sdk"; -import { serialize } from "allure-js-commons/sdk/reporter"; +import { serialize } from "allure-js-commons/sdk"; import { getLegacyApiRuntime } from "./legacyUtils.js"; interface StepInterface { From a4147f739424bbf378dc5af76c49757448d1f239 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Mon, 2 Sep 2024 20:33:00 +0700 Subject: [PATCH 04/10] fix(commons): serialize returning [object ] for maps and sets --- packages/allure-js-commons/src/sdk/utils.ts | 5 +- .../allure-js-commons/test/sdk/utils.spec.ts | 101 ++++++++++++------ 2 files changed, 70 insertions(+), 36 deletions(-) diff --git a/packages/allure-js-commons/src/sdk/utils.ts b/packages/allure-js-commons/src/sdk/utils.ts index 63867dcfb..97e65f9d6 100644 --- a/packages/allure-js-commons/src/sdk/utils.ts +++ b/packages/allure-js-commons/src/sdk/utils.ts @@ -143,7 +143,10 @@ export const isPromise = (obj: any): obj is PromiseLike => !!obj && (typeof obj === "object" || typeof obj === "function") && typeof obj.then === "function"; export const serialize = (val: unknown): string => { - if (typeof val === "object" && !(val instanceof Map || val instanceof Set)) { + if (typeof val === "object") { + if (val instanceof Map || val instanceof Set) { + val = Array.from(val); + } return JSON.stringify(val); } diff --git a/packages/allure-js-commons/test/sdk/utils.spec.ts b/packages/allure-js-commons/test/sdk/utils.spec.ts index cb0b4ea7b..30c749a05 100644 --- a/packages/allure-js-commons/test/sdk/utils.spec.ts +++ b/packages/allure-js-commons/test/sdk/utils.spec.ts @@ -269,56 +269,87 @@ describe("isMetadataTag", () => { }); describe("serialize", () => { - describe("with object", () => { - it("returns JSON string", () => { - // eslint-disable-next-line @stylistic/quotes - expect(serialize({ foo: "bar" })).toBe('{"foo":"bar"}'); + describe("called with a primitive", () => { + describe("undefined", () => { + it("should return the 'undefined' constant", () => { + expect(serialize(undefined)).toBe("undefined"); + }); }); - }); - describe("with array", () => { - it("returns JSON string", () => { - // eslint-disable-next-line @stylistic/quotes - expect(serialize(["foo", "bar"])).toBe('["foo","bar"]'); + describe("null", () => { + it("should return the 'null' constant", () => { + expect(serialize(null)).toBe("null"); + }); }); - }); - describe("with map", () => { - it("returns JSON string", () => { - expect(serialize(new Map([["foo", "bar"]]))).toBe("[object Map]"); + describe("symbol", () => { + it("should return the same value as .toString()", () => { + const symbol = Symbol("foo"); + expect(serialize(symbol)).toBe(symbol.toString()); + }); }); - }); - describe("with set", () => { - it("returns JSON string", () => { - expect(serialize(new Set(["foo", "bar"]))).toBe("[object Set]"); + describe("number", () => { + it("should return the text of the number", () => { + expect(serialize(1)).toBe("1"); + expect(serialize(1.5)).toBe("1.5"); + }); }); - }); - describe("with undefined", () => { - it("returns undefined string", () => { - expect(serialize(undefined)).toBe("undefined"); + describe("bigint", () => { + it("should return the text of the bit int", () => { + expect(serialize(BigInt(1000000000000000) * BigInt(1000000000000000))).toBe("1000000000000000000000000000000"); + }); }); - }); - describe("with null", () => { - it("returns null string", () => { - expect(serialize(null)).toBe("null"); + describe("boolean", () => { + it("should return the 'true' or 'false' constant", () => { + expect(serialize(true)).toBe("true"); + expect(serialize(false)).toBe("false"); + }); }); - }); - describe("with function", () => { - it("returns function string", () => { - expect(serialize(() => {})).toBe("() => {\n }"); + describe("string", () => { + it("should return the string itself", () => { + expect(serialize("")).toBe(""); + expect(serialize("foo")).toBe("foo"); + }); }); }); - describe("with primitives", () => { - it("returns stringified value", () => { - // eslint-disable-next-line @stylistic/quotes - expect(serialize("foo")).toBe("foo"); - expect(serialize(123)).toBe("123"); - expect(serialize(true)).toBe("true"); + describe("called with an object", () => { + it("should return the same serialized object as JSON.stringify", () => { + expect(serialize({})).toBe(JSON.stringify({})); + expect(serialize({ foo: "bar" })).toBe(JSON.stringify({ foo: "bar" })); + expect(serialize({ foo: "bar", baz: "qux" })).toBe(JSON.stringify({ foo: "bar", baz: "qux" })); + expect(serialize({ foo: {bar: "qux"} })).toBe(JSON.stringify({ foo: {bar: "qux"} })); + }); + + describe("of type Array", () => { + it("should return the same serialized array as JSON.stringify", () => { + expect(serialize([])).toBe(JSON.stringify([])); + expect(serialize([1])).toBe(JSON.stringify([1])); + expect(serialize([1, "foo"])).toBe(JSON.stringify([1, "foo"])); + expect(serialize([1, "foo", [2, {bar: "baz"}]])).toBe(JSON.stringify([1, "foo", [2, {bar: "baz"}]])); + }); + }); + + describe("of type Map", () => { + it("should return array of the key-value pairs", () => { + expect(serialize(new Map())).toBe("[]"); + expect(serialize(new Map([]))).toBe("[]"); + expect(serialize(new Map([["foo", "bar"]]))).toBe(String.raw`[["foo","bar"]]`); + expect(serialize(new Map([[1, "foo"], [2, "bar"]]))).toBe(String.raw`[[1,"foo"],[2,"bar"]]`); + }); + }); + + describe("of type Set", () => { + it("should return array of the set elements", () => { + expect(serialize(new Set())).toBe("[]"); + expect(serialize(new Set([]))).toBe("[]"); + expect(serialize(new Set([1]))).toBe("[1]"); + expect(serialize(new Set([1, "foo", 2]))).toBe(String.raw`[1,"foo",2]`); + }); }); }); }); From 35f3fc43ff3023a10296e7486a815a1060a697ff Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Tue, 3 Sep 2024 18:51:05 +0700 Subject: [PATCH 05/10] feat(commons): circular ref, depth, and length limits on serialize allure-cypress now uses the new implementation of allure-js-commons'es serialize instead of its own version. --- packages/allure-cypress/src/runtime.ts | 6 +- packages/allure-cypress/src/utils.ts | 30 --------- .../allure-cypress/test/spec/commands.test.ts | 8 +-- packages/allure-js-commons/src/sdk/types.ts | 5 ++ packages/allure-js-commons/src/sdk/utils.ts | 41 ++++++++---- .../allure-js-commons/test/sdk/utils.spec.ts | 63 ++++++++++++++++++- 6 files changed, 103 insertions(+), 50 deletions(-) diff --git a/packages/allure-cypress/src/runtime.ts b/packages/allure-cypress/src/runtime.ts index f0182e2c7..3dc82339e 100644 --- a/packages/allure-cypress/src/runtime.ts +++ b/packages/allure-cypress/src/runtime.ts @@ -5,6 +5,7 @@ import { getStatusFromError, getUnfinishedStepsMessages, isPromise, + serialize, } from "allure-js-commons/sdk"; import type { RuntimeMessage } from "allure-js-commons/sdk"; import { getGlobalTestRuntime, setGlobalTestRuntime } from "allure-js-commons/sdk/runtime"; @@ -41,7 +42,6 @@ import { isTestReported, iterateTests, markTestAsReported, - stringifyCommandArgument, uint8ArrayToBase64, } from "./utils.js"; @@ -368,7 +368,9 @@ export const reportCommandStart = (command: CypressCommand) => { type: "cypress_command_start", data: { name: `Command "${command.attributes.name}"`, - args: command.attributes.args.map((arg) => stringifyCommandArgument(arg, maxArgumentLength, maxArgumentDepth)), + args: command.attributes.args.map((arg) => + serialize(arg, { maxDepth: maxArgumentDepth, maxLength: maxArgumentLength }), + ), start: Date.now(), }, }); diff --git a/packages/allure-cypress/src/utils.ts b/packages/allure-cypress/src/utils.ts index a960eadc4..5ce182831 100644 --- a/packages/allure-cypress/src/utils.ts +++ b/packages/allure-cypress/src/utils.ts @@ -166,33 +166,3 @@ const removeSortedIndices = (arr: T[], indices: readonly number[]) => { arr.splice(indices[i], 1); } }; - -export const stringifyCommandArgument = (value: any, maxLength: number, maxDepth: number) => { - if (typeof value === "string") { - return limitString(value, maxLength); - } - - const parents: object[] = []; - const circularReferenceRemover = function (this: object, _: string, v: unknown) { - if (typeof v !== "object" || v === null) { - return v; - } - - while (parents.length > 0 && !Object.is(parents.at(-1), this)) { - parents.pop(); - } - - if (parents.length >= maxDepth + 1 || parents.includes(v)) { - return undefined; - } - - parents.push(v); - return v; - }; - - const argumentText = JSON.stringify(value, circularReferenceRemover); - return limitString(argumentText, maxLength); -}; - -const limitString = (value: string, maxLength: number) => - value.length <= maxLength ? value : `${value.substring(0, maxLength)}...`; diff --git a/packages/allure-cypress/test/spec/commands.test.ts b/packages/allure-cypress/test/spec/commands.test.ts index 9ef60d4d1..19b910c5b 100644 --- a/packages/allure-cypress/test/spec/commands.test.ts +++ b/packages/allure-cypress/test/spec/commands.test.ts @@ -97,7 +97,7 @@ it("should impose limits on command arguments", async () => { cy.wrap("A".repeat(1000)); // should truncate string values cy.wrap(Array(1000).fill("A")); // should truncate objects - cy.wrap({ foo: { bar: { baz: { qux: {}, qut: 10 } } } }) // should remove 'qux' at nesting level 4 but keep 'qut' + cy.wrap({ foo: { bar: { baz: {}, qux: "qut" } } }) // should remove 'baz' because it creates nesting level 4 }); `, }); @@ -149,7 +149,7 @@ it("should impose limits on command arguments", async () => { parameters: [ { name: "Argument [0]", - value: JSON.stringify({ foo: { bar: { baz: { qut: 10 } } } }), + value: JSON.stringify({ foo: { bar: { qux: "qut" } } }), }, ], }), @@ -165,7 +165,7 @@ it("should take the limits from the config", async () => { it("foo", () => { cy.wrap("A".repeat(100)); // should truncate string values cy.wrap(Array(100).fill("A")); // should truncate objects - cy.wrap({ foo: { bar: { }, baz: "qux" } }) // should remove 'bar' at nesting level 2 but keep 'baz' + cy.wrap({ foo: { bar: { }, baz: "qux" } }) // should remove 'bar' that creates nesting level 3 but keep 'baz' }); `, "cypress.config.js": ({ allureCypressModuleBasePath }) => ` @@ -179,7 +179,7 @@ it("should take the limits from the config", async () => { allureCypress(on, config, { stepsFromCommands: { maxArgumentLength: 25, - maxArgumentDepth: 1, + maxArgumentDepth: 2, } }); diff --git a/packages/allure-js-commons/src/sdk/types.ts b/packages/allure-js-commons/src/sdk/types.ts index c1a865b8a..d5d8521ee 100644 --- a/packages/allure-js-commons/src/sdk/types.ts +++ b/packages/allure-js-commons/src/sdk/types.ts @@ -110,3 +110,8 @@ export interface AllureResults { envInfo?: EnvironmentInfo; categories?: Category[]; } + +export type SerializeOptions = { + maxDepth?: number; + maxLength?: number; +}; diff --git a/packages/allure-js-commons/src/sdk/utils.ts b/packages/allure-js-commons/src/sdk/utils.ts index 97e65f9d6..84ceb0f1e 100644 --- a/packages/allure-js-commons/src/sdk/utils.ts +++ b/packages/allure-js-commons/src/sdk/utils.ts @@ -1,6 +1,6 @@ import type { FixtureResult, Label, StatusDetails, StepResult, TestResult } from "../model.js"; import { LabelName, Status } from "../model.js"; -import type { RuntimeMessage } from "./types.js"; +import type { RuntimeMessage, SerializeOptions } from "./types.js"; export const getStatusFromError = (error: Error): Status => { switch (true) { @@ -142,17 +142,36 @@ export const getUnfinishedStepsMessages = (messages: RuntimeMessage[]) => { export const isPromise = (obj: any): obj is PromiseLike => !!obj && (typeof obj === "object" || typeof obj === "function") && typeof obj.then === "function"; -export const serialize = (val: unknown): string => { - if (typeof val === "object") { - if (val instanceof Map || val instanceof Set) { - val = Array.from(val); +export const serialize = (value: any, { maxDepth = 0, maxLength = 0 }: SerializeOptions = {}): string => + limitString( + typeof value === "object" + ? JSON.stringify( + value instanceof Map || value instanceof Set ? Array.from(value) : value, + createSerializeReplacer(maxDepth), + ) + : String(value), + maxLength, + ); + +const createSerializeReplacer = (maxDepth: number) => { + const parents: any[] = []; + return function (this: any, _: string, v: unknown) { + if (typeof v !== "object" || v === null) { + return v; } - return JSON.stringify(val); - } - if (val === undefined) { - return "undefined"; - } + while (parents.length > 0 && !Object.is(parents.at(-1), this)) { + parents.pop(); + } - return (val as any).toString(); + if ((maxDepth && parents.length >= maxDepth) || parents.includes(v)) { + return undefined; + } + + parents.push(v); + return v; + }; }; + +const limitString = (value: string, maxLength: number) => + maxLength && value.length > maxLength ? `${value.substring(0, maxLength)}...` : value; diff --git a/packages/allure-js-commons/test/sdk/utils.spec.ts b/packages/allure-js-commons/test/sdk/utils.spec.ts index 30c749a05..b6584bc24 100644 --- a/packages/allure-js-commons/test/sdk/utils.spec.ts +++ b/packages/allure-js-commons/test/sdk/utils.spec.ts @@ -314,6 +314,10 @@ describe("serialize", () => { expect(serialize("")).toBe(""); expect(serialize("foo")).toBe("foo"); }); + + it("should limit the maximum length of the serialized string", () => { + expect(serialize("foobar".repeat(2), { maxLength: 5 })).toBe("fooba..."); + }); }); }); @@ -322,7 +326,53 @@ describe("serialize", () => { expect(serialize({})).toBe(JSON.stringify({})); expect(serialize({ foo: "bar" })).toBe(JSON.stringify({ foo: "bar" })); expect(serialize({ foo: "bar", baz: "qux" })).toBe(JSON.stringify({ foo: "bar", baz: "qux" })); - expect(serialize({ foo: {bar: "qux"} })).toBe(JSON.stringify({ foo: {bar: "qux"} })); + expect(serialize({ foo: { bar: "qux" } })).toBe(JSON.stringify({ foo: { bar: "qux" } })); + }); + + it("should preclude circular references", () => { + const obj1: any = {}; + obj1.ref = obj1; // a reference to the direct parent + expect(serialize(obj1)).toBe(JSON.stringify({})); + + const obj2: any = {}; + obj2.ref = { ref: obj2 }; // a reference to the parent of the parent + expect(serialize(obj2)).toBe(JSON.stringify({ ref: {} })); + + const sharedObject = { baz: "qux" }; + const obj3 = { foo: sharedObject, bar: sharedObject }; // diamond-shaped refs; no cycles though + expect(serialize(obj3)).toBe(JSON.stringify({ foo: { baz: "qux" }, bar: { baz: "qux" } })); + + const obj4: any = { foo: "Lorem", bar: { baz: "Ipsum" } }; + obj4.bar.ref = obj4.bar; // A reference to the parent, but the node also has another property + expect(serialize(obj4)).toBe(JSON.stringify({ foo: "Lorem", bar: { baz: "Ipsum" } })); + + // Arrays adhere to the same rules (but 'null' is inserted for each excluded circular reference; + // otherwise the result may be confusing) + const obj5: any[] = []; + obj5.push(obj5); + expect(serialize(obj5)).toBe(JSON.stringify([null])); + + const obj6: any = [1, "Lorem", ["Ipsum"]]; + obj6[2].push(obj6); + expect(serialize(obj6)).toBe(JSON.stringify([1, "Lorem", ["Ipsum", null]])); + }); + + it("should limit the maximum size of the serialized object", () => { + expect(serialize(Array(1000).fill(1), { maxLength: 5 })).toBe("[1,1,..."); + }); + + it("should limit the maximum nesting level of the object", () => { + expect(serialize({ foo: 1, bar: { baz: {}, qux: 2 } }, { maxDepth: 2 })).toBe( + JSON.stringify({ + // this is nesting level 1 + foo: 1, + bar: { + // this is nesting level 2 + // only primitives are included + qux: 2, + }, + }), + ); }); describe("of type Array", () => { @@ -330,7 +380,7 @@ describe("serialize", () => { expect(serialize([])).toBe(JSON.stringify([])); expect(serialize([1])).toBe(JSON.stringify([1])); expect(serialize([1, "foo"])).toBe(JSON.stringify([1, "foo"])); - expect(serialize([1, "foo", [2, {bar: "baz"}]])).toBe(JSON.stringify([1, "foo", [2, {bar: "baz"}]])); + expect(serialize([1, "foo", [2, { bar: "baz" }]])).toBe(JSON.stringify([1, "foo", [2, { bar: "baz" }]])); }); }); @@ -339,7 +389,14 @@ describe("serialize", () => { expect(serialize(new Map())).toBe("[]"); expect(serialize(new Map([]))).toBe("[]"); expect(serialize(new Map([["foo", "bar"]]))).toBe(String.raw`[["foo","bar"]]`); - expect(serialize(new Map([[1, "foo"], [2, "bar"]]))).toBe(String.raw`[[1,"foo"],[2,"bar"]]`); + expect( + serialize( + new Map([ + [1, "foo"], + [2, "bar"], + ]), + ), + ).toBe(String.raw`[[1,"foo"],[2,"bar"]]`); }); }); From 29c1f69298bc830cda42df7a75a47acf8fea53ad Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Tue, 3 Sep 2024 20:10:00 +0700 Subject: [PATCH 06/10] fix(commons): improve maps and sets handling by serialize --- packages/allure-js-commons/src/sdk/utils.ts | 34 ++++++++++++------- .../allure-js-commons/test/sdk/utils.spec.ts | 28 +++++++++++++++ 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/packages/allure-js-commons/src/sdk/utils.ts b/packages/allure-js-commons/src/sdk/utils.ts index 84ceb0f1e..86a04d333 100644 --- a/packages/allure-js-commons/src/sdk/utils.ts +++ b/packages/allure-js-commons/src/sdk/utils.ts @@ -144,34 +144,44 @@ export const isPromise = (obj: any): obj is PromiseLike => export const serialize = (value: any, { maxDepth = 0, maxLength = 0 }: SerializeOptions = {}): string => limitString( - typeof value === "object" - ? JSON.stringify( - value instanceof Map || value instanceof Set ? Array.from(value) : value, - createSerializeReplacer(maxDepth), - ) - : String(value), + typeof value === "object" ? JSON.stringify(value, createSerializeReplacer(maxDepth)) : String(value), maxLength, ); const createSerializeReplacer = (maxDepth: number) => { const parents: any[] = []; - return function (this: any, _: string, v: unknown) { - if (typeof v !== "object" || v === null) { - return v; + return function (this: any, _: string, value: unknown) { + if (typeof value !== "object" || value === null) { + return value; } while (parents.length > 0 && !Object.is(parents.at(-1), this)) { parents.pop(); } - if ((maxDepth && parents.length >= maxDepth) || parents.includes(v)) { + if ((maxDepth && parents.length >= maxDepth) || parents.includes(value)) { return undefined; } - parents.push(v); - return v; + parents.push(value); + + return value instanceof Map + ? excludeCircularRefsFromMap(parents, value) + : value instanceof Set + ? excludeCircularRefsFromSet(parents, value) + : value; }; }; +const excludeCircularRefsFromMap = (parents: any[], map: Map) => { + return Array.from(map) + .filter(([k]) => !parents.includes(k)) + .map(([k, v]) => [k, parents.includes(v) ? undefined : v]); +}; + +const excludeCircularRefsFromSet = (parents: any[], set: Set) => { + return Array.from(set).map((v) => (parents.includes(v) ? undefined : v)); +}; + const limitString = (value: string, maxLength: number) => maxLength && value.length > maxLength ? `${value.substring(0, maxLength)}...` : value; diff --git a/packages/allure-js-commons/test/sdk/utils.spec.ts b/packages/allure-js-commons/test/sdk/utils.spec.ts index b6584bc24..38e873620 100644 --- a/packages/allure-js-commons/test/sdk/utils.spec.ts +++ b/packages/allure-js-commons/test/sdk/utils.spec.ts @@ -355,6 +355,14 @@ describe("serialize", () => { const obj6: any = [1, "Lorem", ["Ipsum"]]; obj6[2].push(obj6); expect(serialize(obj6)).toBe(JSON.stringify([1, "Lorem", ["Ipsum", null]])); + + const obj7: Map = new Map(); + obj7.set(1, obj7); + expect(serialize(obj7)).toBe(JSON.stringify([[1, null]])); + + const obj8: Set = new Set(); + obj8.add(obj8); + expect(serialize(obj8)).toBe(JSON.stringify([null])); }); it("should limit the maximum size of the serialized object", () => { @@ -375,6 +383,26 @@ describe("serialize", () => { ); }); + it("should replace nested maps and sets with arrays", () => { + expect( + serialize({ + foo: new Map([ + [1, "a"], + [2, "b"], + ]), + bar: new Set([1, 2]), + }), + ).toBe( + JSON.stringify({ + foo: [ + [1, "a"], + [2, "b"], + ], + bar: [1, 2], + }), + ); + }); + describe("of type Array", () => { it("should return the same serialized array as JSON.stringify", () => { expect(serialize([])).toBe(JSON.stringify([])); From b27c997a438f88a5358d1e72ba4b4a6ad3fc01df Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:33:01 +0700 Subject: [PATCH 07/10] refactor(allure-cypress): reuse stepsFromCommands type --- packages/allure-cypress/src/model.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/allure-cypress/src/model.ts b/packages/allure-cypress/src/model.ts index 2f7ff4f98..50ce4ab18 100644 --- a/packages/allure-cypress/src/model.ts +++ b/packages/allure-cypress/src/model.ts @@ -8,10 +8,7 @@ export const ALLURE_REPORT_STEP_COMMAND = "__allure_report_step_command__"; export type AllureCypressConfig = ReporterConfig & { videoOnFailOnly?: boolean; - stepsFromCommands?: { - maxArgumentLength?: number; - maxArgumentDepth?: number; - }; + stepsFromCommands?: Partial; }; export type CypressSuite = Mocha.Suite & { From 775bc58b86d843df3350116dd11504379b992a54 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:38:49 +0700 Subject: [PATCH 08/10] refactor(allure-cypress): rename applyDefaultsToRuntimeConfig The new name is getRuntimeConfigDefaults --- packages/allure-cypress/src/reporter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/allure-cypress/src/reporter.ts b/packages/allure-cypress/src/reporter.ts index 7bdda66f9..cc1db82cc 100644 --- a/packages/allure-cypress/src/reporter.ts +++ b/packages/allure-cypress/src/reporter.ts @@ -490,13 +490,13 @@ export class AllureCypress { } const createRuntimeState = (allureConfig?: AllureCypressConfig): AllureSpecState => ({ - config: applyDefaultsToRuntimeConfig(allureConfig), + config: getRuntimeConfigDefaults(allureConfig), initialized: false, messages: [], testPlan: parseTestPlan(), }); -const applyDefaultsToRuntimeConfig = ({ +const getRuntimeConfigDefaults = ({ stepsFromCommands: { maxArgumentLength = defaultRuntimeConfig.stepsFromCommands.maxArgumentLength, maxArgumentDepth = defaultRuntimeConfig.stepsFromCommands.maxArgumentDepth, From 9332fbca21519d96a4ae7be554039927d15fff66 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:43:29 +0700 Subject: [PATCH 09/10] refactor(allure-cypress): use deep destructuring --- packages/allure-cypress/src/runtime.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/allure-cypress/src/runtime.ts b/packages/allure-cypress/src/runtime.ts index 3dc82339e..65ac9ffe2 100644 --- a/packages/allure-cypress/src/runtime.ts +++ b/packages/allure-cypress/src/runtime.ts @@ -363,7 +363,9 @@ export const reportTestSkip = (test: CypressTest) => { }; export const reportCommandStart = (command: CypressCommand) => { - const { maxArgumentDepth, maxArgumentLength } = getConfig().stepsFromCommands; + const { + stepsFromCommands: { maxArgumentDepth, maxArgumentLength }, + } = getConfig(); enqueueRuntimeMessage({ type: "cypress_command_start", data: { From c52933aec84dbd3b64dad8a84bc77f1fc409cf69 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:47:16 +0700 Subject: [PATCH 10/10] refactor(allure-cypress): rename default config to upper-snake-case --- packages/allure-cypress/src/reporter.ts | 10 +++++----- packages/allure-cypress/src/state.ts | 4 ++-- packages/allure-cypress/src/utils.ts | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/allure-cypress/src/reporter.ts b/packages/allure-cypress/src/reporter.ts index cc1db82cc..135f20995 100644 --- a/packages/allure-cypress/src/reporter.ts +++ b/packages/allure-cypress/src/reporter.ts @@ -32,7 +32,7 @@ import type { CypressTestStartMessage, SpecContext, } from "./model.js"; -import { defaultRuntimeConfig, last } from "./utils.js"; +import { DEFAULT_RUNTIME_CONFIG, last } from "./utils.js"; export class AllureCypress { allureRuntime: ReporterRuntime; @@ -498,10 +498,10 @@ const createRuntimeState = (allureConfig?: AllureCypressConfig): AllureSpecState const getRuntimeConfigDefaults = ({ stepsFromCommands: { - maxArgumentLength = defaultRuntimeConfig.stepsFromCommands.maxArgumentLength, - maxArgumentDepth = defaultRuntimeConfig.stepsFromCommands.maxArgumentDepth, - } = defaultRuntimeConfig.stepsFromCommands, -}: AllureCypressConfig = defaultRuntimeConfig): AllureSpecState["config"] => ({ + maxArgumentLength = DEFAULT_RUNTIME_CONFIG.stepsFromCommands.maxArgumentLength, + maxArgumentDepth = DEFAULT_RUNTIME_CONFIG.stepsFromCommands.maxArgumentDepth, + } = DEFAULT_RUNTIME_CONFIG.stepsFromCommands, +}: AllureCypressConfig = DEFAULT_RUNTIME_CONFIG): AllureSpecState["config"] => ({ stepsFromCommands: { maxArgumentDepth, maxArgumentLength, diff --git a/packages/allure-cypress/src/state.ts b/packages/allure-cypress/src/state.ts index 316308dfd..03b525847 100644 --- a/packages/allure-cypress/src/state.ts +++ b/packages/allure-cypress/src/state.ts @@ -1,11 +1,11 @@ import type { AllureSpecState, CypressMessage, CypressTest } from "./model.js"; -import { defaultRuntimeConfig } from "./utils.js"; +import { DEFAULT_RUNTIME_CONFIG } from "./utils.js"; export const getAllureState = () => { let state = Cypress.env("allure") as AllureSpecState; if (!state) { state = { - config: defaultRuntimeConfig, + config: DEFAULT_RUNTIME_CONFIG, initialized: false, messages: [], testPlan: undefined, diff --git a/packages/allure-cypress/src/utils.ts b/packages/allure-cypress/src/utils.ts index 5ce182831..6bf131cf3 100644 --- a/packages/allure-cypress/src/utils.ts +++ b/packages/allure-cypress/src/utils.ts @@ -5,7 +5,7 @@ import { ALLURE_REPORT_STEP_COMMAND, ALLURE_REPORT_SYSTEM_HOOK } from "./model.j import type { CypressCommand, CypressHook, CypressSuite, CypressTest } from "./model.js"; import { getAllureTestPlan } from "./state.js"; -export const defaultRuntimeConfig = { +export const DEFAULT_RUNTIME_CONFIG = { stepsFromCommands: { maxArgumentLength: 128, maxArgumentDepth: 3,