From 95c234b99b8dc370ab97dc62b657f8a1b156f6c6 Mon Sep 17 00:00:00 2001 From: Anudeep Date: Sat, 26 Oct 2024 14:15:05 +0530 Subject: [PATCH] chore: spec store options (#383) --- package-lock.json | 4 +- package.json | 2 +- src/helpers/toss.helper.js | 24 ++++++++--- src/internal.types.d.ts | 11 +++++ src/models/Spec.d.ts | 5 ++- src/models/Spec.js | 35 +++++++++------- src/models/Tosser.js | 34 +++++++++++++++- test/component/stores.spec.js | 77 ++++++++++++++++++++++++++++++----- 8 files changed, 155 insertions(+), 37 deletions(-) create mode 100644 src/internal.types.d.ts diff --git a/package-lock.json b/package-lock.json index d986bd0..b2b4b83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pactum", - "version": "3.7.2", + "version": "3.7.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "pactum", - "version": "3.7.2", + "version": "3.7.3", "license": "MIT", "dependencies": { "@exodus/schemasafe": "^1.3.0", diff --git a/package.json b/package.json index 301ec59..d07c042 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pactum", - "version": "3.7.2", + "version": "3.7.3", "description": "REST API Testing Tool for all levels in a Test Pyramid", "main": "./src/index.js", "types": "./src/index.d.ts", diff --git a/src/helpers/toss.helper.js b/src/helpers/toss.helper.js index ccbc575..d8d062f 100644 --- a/src/helpers/toss.helper.js +++ b/src/helpers/toss.helper.js @@ -51,22 +51,34 @@ function getPathValueFromRequestResponse(path, request, response) { return jqy(path, { data }).value; } +/** + * + * @param {*} spec + * @param {import('../internal.types').ISpecStore[]} stores + */ function storeSpecData(spec, stores) { const ctx = { req: spec._request, res: spec._response, store: stash.getDataStore() }; for (let i = 0; i < stores.length; i++) { const store = stores[i]; - if (typeof store === 'function') { - const specData = store(spec._request, spec._response); - stash.addDataStore(specData); + if (store.cb) { + stash.addDataStore(store.cb(spec._request, spec._response)); continue; } - const specData = {}; + let data_to_store; + const captureHandler = getCaptureHandlerName(store.path); if (captureHandler) { - specData[store.name] = hr.capture(captureHandler, ctx); + data_to_store = hr.capture(captureHandler, ctx); } else { - specData[store.name] = getPathValueFromRequestResponse(store.path, spec._request, spec._response); + data_to_store = getPathValueFromRequestResponse(store.path, spec._request, spec._response); } + if(store.options && store.options.append) { + ctx.store[store.name] = ctx.store[store.name] || []; + ctx.store[store.name].push(data_to_store); + continue; + } + const specData = {}; + specData[store.name] = data_to_store; stash.addDataStore(specData); } } diff --git a/src/internal.types.d.ts b/src/internal.types.d.ts new file mode 100644 index 0000000..53b0280 --- /dev/null +++ b/src/internal.types.d.ts @@ -0,0 +1,11 @@ +export type ISpecStore = { + name?: string + path?: string + options?: ISpecStoreOptions + cb?: Function +} + +export type ISpecStoreOptions = { + status?: 'PASSED' | 'FAILED' + append?: boolean +} \ No newline at end of file diff --git a/src/models/Spec.d.ts b/src/models/Spec.d.ts index ff01c43..0d757f0 100644 --- a/src/models/Spec.d.ts +++ b/src/models/Spec.d.ts @@ -4,6 +4,7 @@ import { Expect } from '../exports/expect'; import { CaptureHandlerFunction, ExpectHandlerFunction, RetryHandlerFunction } from '../exports/handler'; import { Interaction } from '../exports/mock'; import { LogLevel } from '../exports/settings'; +import { ISpecStoreOptions } from '../internal.types'; declare interface RetryOptions { /** maximum number of retries - defaults to 3 */ @@ -382,8 +383,8 @@ declare class Spec { * stores spec request & response data * @see https://pactumjs.github.io/api/requests/stores.html */ - stores(name: string, path: string): Spec; - stores(name: string, handlerName: string): Spec; + stores(name: string, path: string, options?: ISpecStoreOptions): Spec; + stores(name: string, handlerName: string, options?: ISpecStoreOptions): Spec; stores(cb: (request: Request, response: IncomingMessage & {body: Record, json: Record}) => T): Spec; /** diff --git a/src/models/Spec.js b/src/models/Spec.js index 094fa32..5e9d2da 100644 --- a/src/models/Spec.js +++ b/src/models/Spec.js @@ -483,20 +483,27 @@ class Spec { } stores(...args) { - if (args.length === 1 && typeof args[0] === 'function') { - const fn = args[0]; - if (this._response) { - th.storeSpecData(this, [fn]); - } else { - this._stores.push(fn); - } - } else if (args.length === 2) { - const [name, path] = args; - if (this._response) { - th.storeSpecData(this, [{ name, path }]); - } else { - this._stores.push({ name, path }); - } + if (args.length === 0) { + return this; + } + + /** + * @type {import('../internal.types').ISpecStore} + */ + const spec_store = {}; + + if (typeof args[0] === 'function') { + spec_store.cb = args[0]; + } else { + spec_store.name = args[0]; + spec_store.path = args[1]; + spec_store.options = args[2]; + } + + if (this._response) { + th.storeSpecData(this, [spec_store]); + } else { + this._stores.push(spec_store); } return this; } diff --git a/src/models/Tosser.js b/src/models/Tosser.js index a25f832..2c3284b 100644 --- a/src/models/Tosser.js +++ b/src/models/Tosser.js @@ -42,7 +42,7 @@ class Tosser { await this.getInteractionsFromServer(); this.saveDataInFileSystem(); this.recordData(); - th.storeSpecData(this.spec, this.spec._stores); + this.storeSpecData(); await this.validate(); if (hasBackgroundInteractions(this.interactions) || (this.spec._wait && typeof this.spec._wait.arg1 === 'string')) { await this.dynamicWait(); @@ -209,9 +209,11 @@ class Tosser { this.validateNonBackgroundInteractions(); await this.validateResponse(); this.spec.status = 'PASSED'; + this.storeSpecData(); this.runReport(); } catch (error) { this.spec.status = 'FAILED'; + this.storeSpecData(); this.spec.failure = error.toString(); this.runReport(); this.printRequestAndResponse(); @@ -255,6 +257,36 @@ class Tosser { } } + storeSpecData() { + const stores = []; + switch (this.spec.status) { + case 'PASSED': + for (const store of this.spec._stores) { + if (store && store.options && store.options.status === 'PASSED') { + stores.push(store); + } + } + break; + case 'FAILED': + for (const store of this.spec._stores) { + if (store && store.options && store.options.status === 'FAILED') { + stores.push(store); + } + } + break; + default: + for (const store of this.spec._stores) { + if (store && (!store.options || !store.options.status)) { + stores.push(store); + } + } + break; + } + if (stores.length > 0) { + th.storeSpecData(this.spec, stores); + } + } + } async function getResponse(tosser) { diff --git a/test/component/stores.spec.js b/test/component/stores.spec.js index 45b7098..597abfe 100644 --- a/test/component/stores.spec.js +++ b/test/component/stores.spec.js @@ -1,3 +1,4 @@ +const expect = require('chai').expect; const pactum = require('../../src/index'); const stash = require('../../src/exports/stash'); const { addInteractionHandler } = pactum.handler; @@ -111,7 +112,7 @@ describe('Stores', () => { } }) .post('http://localhost:9393/api/stores') - .withCookies('token','$S{token}') + .withCookies('token', '$S{token}') .withJson({ UserId: '$S{FirstUser.id}' }) @@ -177,7 +178,7 @@ describe('Stores', () => { .expectStatus(200); }); - it ('store single value by custom function', async () => { + it('store single value by custom function', async () => { await pactum.spec() .useInteraction({ request: { @@ -195,10 +196,10 @@ describe('Stores', () => { .expectStatus(200) .stores((request, response) => { return { - custom_func_id: response.body.id + custom_func_id: response.body.id }; }); - await pactum.spec() + await pactum.spec() .useInteraction({ request: { method: 'POST', @@ -218,7 +219,7 @@ describe('Stores', () => { .expectStatus(200); }); - it ('store multiple value by custom function', async () => { + it('store multiple value by custom function', async () => { await pactum.spec() .useInteraction({ request: { @@ -237,11 +238,11 @@ describe('Stores', () => { .expectStatus(200) .stores((request, response) => { return { - custom_func_id: response.body.id, - custom_func_email: response.body.email + custom_func_id: response.body.id, + custom_func_email: response.body.email }; }); - await pactum.spec() + await pactum.spec() .useInteraction({ request: { method: 'POST', @@ -290,9 +291,9 @@ describe('Stores', () => { .get('http://localhost:9393/api/stores') .expectStatus(200); spec.stores((request, response) => { - return { - UserId: response.body.id - }; + return { + UserId: response.body.id + }; }); await pactum.spec() .useInteraction('post stores') @@ -307,6 +308,60 @@ describe('Stores', () => { .expectStatus(200); }); + it('store on failure', async () => { + await pactum.spec() + .useInteraction('get stores') + .get('http://localhost:9393/api/stores') + .expectStatus(200) + .stores('FailedUserId', 'id', { status: 'FAILED' }); + expect(stash.getDataStore('FailedUserId')).to.be.undefined; + try { + await pactum.spec() + .useInteraction('get stores') + .get('http://localhost:9393/api/stores') + .expectStatus(500) + .stores('FailedUserId', 'id', { status: 'FAILED' }) + .inspect(false); + } catch (error) { + + } + expect(stash.getDataStore('FailedUserId')).equals(1); + }); + + it('store on success', async () => { + try { + await pactum.spec() + .useInteraction('get stores') + .get('http://localhost:9393/api/stores') + .expectStatus(500) + .stores('FailedUserId', 'id', { status: 'PASSED' }) + .inspect(false); + } catch (error) { + + } + expect(stash.getDataStore('FailedUserId')).to.be.undefined; + await pactum.spec() + .useInteraction('get stores') + .get('http://localhost:9393/api/stores') + .expectStatus(200) + .stores('FailedUserId', 'id', { status: 'PASSED' }); + expect(stash.getDataStore('FailedUserId')).equals(1); + }); + + it('store append', async () => { + await pactum.spec() + .useInteraction('get stores') + .get('http://localhost:9393/api/stores') + .expectStatus(200) + .stores('UserId', 'id', { append: true }); + await pactum.spec() + .useInteraction('get stores') + .get('http://localhost:9393/api/stores') + .expectStatus(200) + .stores('UserId', 'id', { append: true }); + expect(stash.getDataStore('UserId')).deep.equals([1,1]); + }) + afterEach(() => { stash.clearDataStores(); });