From 5352f50e744be16b427d0a98c93e3b52c6cdda92 Mon Sep 17 00:00:00 2001 From: Yaroslav Serhieiev Date: Fri, 27 Oct 2023 12:05:20 +0300 Subject: [PATCH] feat: add metadata.unshift() operation --- .../27.x.x/env-1/hook-nesting.json | 6 +- .../27.x.x/env-N/hook-nesting.json | 6 +- .../28.x.x/env-1/hook-nesting.json | 6 +- .../28.x.x/env-N/hook-nesting.json | 6 +- .../29.x.x/env-1/hook-nesting.json | 6 +- .../29.x.x/env-N/hook-nesting.json | 6 +- e2e/__tests__/default/hook-nesting.js | 6 +- src/index.ts | 7 +++ src/metadata/containers/BaseMetadata.ts | 58 +++++++++++-------- src/metadata/dsl/MetadataDSL.ts | 10 ++++ src/metadata/events/WriteMetadataEvent.ts | 2 +- src/metadata/types/Metadata.ts | 1 + 12 files changed, 74 insertions(+), 46 deletions(-) diff --git a/e2e/__fixtures__/27.x.x/env-1/hook-nesting.json b/e2e/__fixtures__/27.x.x/env-1/hook-nesting.json index 7521a4c..e342bd4 100644 --- a/e2e/__fixtures__/27.x.x/env-1/hook-nesting.json +++ b/e2e/__fixtures__/27.x.x/env-1/hook-nesting.json @@ -141,7 +141,7 @@ "labels" ], "value": [ - "flaky" + "sanity" ], "operation": "push" }, @@ -154,9 +154,9 @@ "labels" ], "value": [ - "sanity" + "flaky" ], - "operation": "push" + "operation": "unshift" }, { "type": "finish_describe_definition", diff --git a/e2e/__fixtures__/27.x.x/env-N/hook-nesting.json b/e2e/__fixtures__/27.x.x/env-N/hook-nesting.json index 7521a4c..e342bd4 100644 --- a/e2e/__fixtures__/27.x.x/env-N/hook-nesting.json +++ b/e2e/__fixtures__/27.x.x/env-N/hook-nesting.json @@ -141,7 +141,7 @@ "labels" ], "value": [ - "flaky" + "sanity" ], "operation": "push" }, @@ -154,9 +154,9 @@ "labels" ], "value": [ - "sanity" + "flaky" ], - "operation": "push" + "operation": "unshift" }, { "type": "finish_describe_definition", diff --git a/e2e/__fixtures__/28.x.x/env-1/hook-nesting.json b/e2e/__fixtures__/28.x.x/env-1/hook-nesting.json index 7521a4c..e342bd4 100644 --- a/e2e/__fixtures__/28.x.x/env-1/hook-nesting.json +++ b/e2e/__fixtures__/28.x.x/env-1/hook-nesting.json @@ -141,7 +141,7 @@ "labels" ], "value": [ - "flaky" + "sanity" ], "operation": "push" }, @@ -154,9 +154,9 @@ "labels" ], "value": [ - "sanity" + "flaky" ], - "operation": "push" + "operation": "unshift" }, { "type": "finish_describe_definition", diff --git a/e2e/__fixtures__/28.x.x/env-N/hook-nesting.json b/e2e/__fixtures__/28.x.x/env-N/hook-nesting.json index 7521a4c..e342bd4 100644 --- a/e2e/__fixtures__/28.x.x/env-N/hook-nesting.json +++ b/e2e/__fixtures__/28.x.x/env-N/hook-nesting.json @@ -141,7 +141,7 @@ "labels" ], "value": [ - "flaky" + "sanity" ], "operation": "push" }, @@ -154,9 +154,9 @@ "labels" ], "value": [ - "sanity" + "flaky" ], - "operation": "push" + "operation": "unshift" }, { "type": "finish_describe_definition", diff --git a/e2e/__fixtures__/29.x.x/env-1/hook-nesting.json b/e2e/__fixtures__/29.x.x/env-1/hook-nesting.json index 717f61a..3f2b4e0 100644 --- a/e2e/__fixtures__/29.x.x/env-1/hook-nesting.json +++ b/e2e/__fixtures__/29.x.x/env-1/hook-nesting.json @@ -141,7 +141,7 @@ "labels" ], "value": [ - "flaky" + "sanity" ], "operation": "push" }, @@ -154,9 +154,9 @@ "labels" ], "value": [ - "sanity" + "flaky" ], - "operation": "push" + "operation": "unshift" }, { "type": "finish_describe_definition", diff --git a/e2e/__fixtures__/29.x.x/env-N/hook-nesting.json b/e2e/__fixtures__/29.x.x/env-N/hook-nesting.json index 717f61a..3f2b4e0 100644 --- a/e2e/__fixtures__/29.x.x/env-N/hook-nesting.json +++ b/e2e/__fixtures__/29.x.x/env-N/hook-nesting.json @@ -141,7 +141,7 @@ "labels" ], "value": [ - "flaky" + "sanity" ], "operation": "push" }, @@ -154,9 +154,9 @@ "labels" ], "value": [ - "sanity" + "flaky" ], - "operation": "push" + "operation": "unshift" }, { "type": "finish_describe_definition", diff --git a/e2e/__tests__/default/hook-nesting.js b/e2e/__tests__/default/hook-nesting.js index d124aa1..6baa447 100644 --- a/e2e/__tests__/default/hook-nesting.js +++ b/e2e/__tests__/default/hook-nesting.js @@ -1,4 +1,4 @@ -const { metadata, $Assign, $Push, $Set, $Merge } = require('jest-metadata'); +const { metadata, $Assign, $Push, $Set, $Merge, $Unshift } = require('jest-metadata'); let now = 1672524000000; @@ -16,7 +16,7 @@ const $Description = (text) => $Set('vendor.description', text); const $Maintainer = (name, email) => $Assign('vendor.maintainer', { name, email }); const $Lead = (name, email) => $Merge('vendor.lead', { name, email }); const $Tag = (value) => $Push(['vendor', 'labels'], value); -const $Flaky = () => $Tag('flaky'); +const $Flaky = () => $Unshift(['vendor', 'labels'], 'flaky'); const step = (text) => metadata.push('vendor.steps', [{ text, startedAt: now }]); @@ -55,8 +55,8 @@ describe('Login flow', () => { step('Assert that the login failed'); }); - $Flaky(); $Tag('sanity'); + $Flaky(); test('Happy scenario', () => { step('Enter valid credentials'); actions.sleep(100); diff --git a/src/index.ts b/src/index.ts index 2bdfc4d..5a14352 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,6 +40,13 @@ export const $Set = realm.metadataDSL.$Set; */ export const $Push = realm.metadataDSL.$Push; +/** + * Pseudo-annotation that allows to associate metadata with a test block. + * It is not an ECMAScript decorator, but it behaves similarly. + * Use it to prepend a value to an array in metadata. + */ +export const $Unshift = realm.metadataDSL.$Unshift; + /** * Pseudo-annotation that allows to associate metadata with a test block. * It is not an ECMAScript decorator, but it behaves similarly. diff --git a/src/metadata/containers/BaseMetadata.ts b/src/metadata/containers/BaseMetadata.ts index c69b2c2..67fe138 100644 --- a/src/metadata/containers/BaseMetadata.ts +++ b/src/metadata/containers/BaseMetadata.ts @@ -53,31 +53,11 @@ export abstract class BaseMetadata implements Metadata { } push(path: string | readonly string[], values: unknown[]): this { - this.#assertPath(path, 'push to'); - if (!Array.isArray(values)) { - throw new TypeError(`Cannot push a non-array value to path "${path}". Received: ${values}`); - } - - const array = lodashGet(this[symbols.data], path, []); - if (!Array.isArray(array)) { - throw new TypeError( - `Cannot push to path "${path}", because it is not an array, but: ${array}`, - ); - } - - array.push(...values); - this.#set(path, array); - - this[symbols.context].emitter.emit({ - type: 'write_metadata', - testFilePath: this[symbols.id].testFilePath, - targetId: this[symbols.id].identifier, - path, - value: values, - operation: 'push', - }); + return this.#concat('push', path, values); + } - return this; + unshift(path: string | readonly string[], values: unknown[]): this { + return this.#concat('unshift', path, values); } assign(path: undefined | string | readonly string[], value: object): this { @@ -133,4 +113,34 @@ export abstract class BaseMetadata implements Metadata { throw new TypeError(`Cannot ${operationName} metadata without a path`); } } + + #concat(operation: 'push' | 'unshift', path: string | readonly string[], values: unknown[]) { + this.#assertPath(path, `${operation} to`); + if (!Array.isArray(values)) { + throw new TypeError( + `Cannot ${operation} a non-array value to path "${path}". Received: ${values}`, + ); + } + + const array = lodashGet(this[symbols.data], path, []); + if (!Array.isArray(array)) { + throw new TypeError( + `Cannot ${operation} to path "${path}", because it is not an array, but: ${array}`, + ); + } + + array[operation](...values); + this.#set(path, array); + + this[symbols.context].emitter.emit({ + type: 'write_metadata', + testFilePath: this[symbols.id].testFilePath, + targetId: this[symbols.id].identifier, + path, + value: values, + operation, + }); + + return this; + } } diff --git a/src/metadata/dsl/MetadataDSL.ts b/src/metadata/dsl/MetadataDSL.ts index a2bef36..358eb81 100644 --- a/src/metadata/dsl/MetadataDSL.ts +++ b/src/metadata/dsl/MetadataDSL.ts @@ -56,6 +56,16 @@ export class MetadataDSL { }); }; + $Unshift = (path: string | readonly string[], ...values: unknown[]): void => { + this.#assertPath(path); + this.#assertValues(values); + + this.schedule(() => { + const metadata = this.#metadata(); + metadata.unshift(path, values); + }); + }; + $Assign = (path: string | readonly string[] | undefined, value: Data): void => { this.#assertPath(path); this.#assertValue(value); diff --git a/src/metadata/events/WriteMetadataEvent.ts b/src/metadata/events/WriteMetadataEvent.ts index 6335a5e..c7ac1fe 100644 --- a/src/metadata/events/WriteMetadataEvent.ts +++ b/src/metadata/events/WriteMetadataEvent.ts @@ -4,5 +4,5 @@ export type WriteMetadataEvent = { targetId: string; // instance ID path?: string | readonly string[]; value: unknown; - operation: 'set' | 'assign' | 'merge' | 'push'; + operation: 'set' | 'assign' | 'merge' | 'push' | 'unshift'; }; diff --git a/src/metadata/types/Metadata.ts b/src/metadata/types/Metadata.ts index 29a18e5..0172485 100644 --- a/src/metadata/types/Metadata.ts +++ b/src/metadata/types/Metadata.ts @@ -6,6 +6,7 @@ export interface Metadata { get(path?: string | readonly string[], fallbackValue?: T): T; set(path: string | readonly string[], value: unknown): this; push(path: string | readonly string[], values: unknown[]): this; + unshift(path: string | readonly string[], values: unknown[]): this; assign(path: undefined | string | readonly string[], value: Data): this; merge(path: undefined | string | readonly string[], value: Data): this; }