Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: added support parameters from data files #694

Merged
merged 2 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions qase-newman/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ https://app.qase.io/run/QASE_PROJECT_CODE
<img src="./screenshots/demo.gif">
</p>

You can find more information about using the reporter [here](./docs/usage.md).

## Configuration

Qase Newman reporter can be configured in multiple ways:
Expand Down
7 changes: 7 additions & 0 deletions qase-newman/changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# [email protected]

## What's new

Added support parameters from data files in Newman.
How to use parameters from data files in Newman, see [here](./docs/usage.md).

# [email protected]

## What's new
Expand Down
59 changes: 59 additions & 0 deletions qase-newman/docs/usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# How to Use Parameters from Data Files in Newman

Newman allows you to leverage parameters from data files to make your API tests more dynamic and efficient. By utilizing
the `--data` or `-d` option when running a collection, you can feed your tests with various input sets. The data files
can be formatted as either JSON or CSV.

### Example Data File

Consider the following `data.json` file, which contains user data structured as complex objects:

```json
[
{
"userid": 1,
"user": {
"name": "John",
"age": 30
}
},
{
"userid": 2,
"user": {
"name": "Jane",
"age": 25
}
}
]
```

### Example Tests

Below are example tests that utilize the data parameters defined in the data file:

```javascript
// qase.parameters: userId, user.name
pm.test("Status code is 201", function() {
pm.response.to.have.status(201);
});

// qase.parameters: userId
pm.test("Response has correct userId", function() {
var jsonData = pm.response.json();
pm.expect(jsonData.userId).to.eql(pm.iterationData.get("userid"));
});

pm.test("Response has correct name", function() {
var jsonData = pm.response.json();
pm.expect(jsonData.user.name).to.eql(pm.iterationData.get("user.name"));
});
```

### Expected Behavior

When you run the tests, the following behavior is expected:

- In the **`Status code is 201`** test, both `userId` and `user.name` will be passed as parameters.
- In the **`Response has correct userId`** test, only the `userId` parameter will be passed.
- In the **`Response has correct name`** test, all relevant parameters from the data file will be passed, including
`userId`, `user.name`, and `user.age`.
4 changes: 2 additions & 2 deletions qase-newman/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "newman-reporter-qase",
"version": "2.0.1",
"version": "2.1.0",
"description": "Qase TMS Newman Reporter",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand Down Expand Up @@ -45,7 +45,7 @@
"devDependencies": {
"@jest/globals": "^29.5.0",
"@types/jest": "^29.5.2",
"@types/newman": "^5.3.3",
"@types/newman": "^5.3.6",
"@types/postman-collection": "^3.5.7",
"jest": "^29.5.0",
"postman-collection": "^4.1.7",
Expand Down
132 changes: 124 additions & 8 deletions qase-newman/src/reporter.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { EventEmitter } from 'events';

import semver from 'semver';
import { NewmanRunExecution } from 'newman';
import {
EventList, PropertyBase, PropertyBaseDefinition,
} from 'postman-collection';
import { NewmanRunExecution, NewmanRunOptions } from 'newman';
import { EventList, PropertyBase, PropertyBaseDefinition } from 'postman-collection';
import {
ConfigType,
QaseReporter,
Expand All @@ -13,7 +11,9 @@
TestResultType,
getPackageVersion,
ConfigLoader,
composeOptions, Relation, SuiteData,
composeOptions,
Relation,
SuiteData,
} from 'qase-javascript-commons';

export type NewmanQaseOptionsType = ConfigType;
Expand All @@ -27,6 +27,11 @@
*/
static qaseIdRegExp = /\/\/\s*?[qQ]ase:\s?((?:[\d]+[\s,]{0,})+)/;

/**
* @type {RegExp}
*/
static qaseParamRegExp = /qase\.parameters:\s*([\w.]+(?:\s*,\s*[\w.]+)*)/i;

/**
* @param {EventList} eventList
* @returns {number[]}
Expand All @@ -50,9 +55,33 @@
return ids;
}

/**
* @param {EventList} eventList
* @returns {string[]}
* @private
*/
private static getParameters(eventList: EventList) {
const params: string[] = [];

eventList.each((event) => {
if (event.listen === 'test' && event.script.exec) {
event.script.exec.forEach((line) => {
const match = line.match(NewmanQaseReporter.qaseParamRegExp);

if (match) {
const parameters: string[] = match[1]?.split(/\s*,\s*/) ?? [];

params.push(...parameters);
}
});
}
});

return params;
}

/**
* @param {PropertyBase<PropertyBaseDefinition>} item
* @param {string[]} titles
* @returns {string[]}
* @private
*/
Expand Down Expand Up @@ -88,17 +117,22 @@
* @private
*/
private timerMap = new Map<string, number>();
/**
* @type {Record<string, string>[]}
* @private
*/
private parameters: Record<string, string>[] = [];

/**
* @param {EventEmitter} emitter
* @param {NewmanQaseOptionsType} options
* @param {unknown} _
* @param {NewmanRunOptions} collectionOptions
* @param {ConfigLoaderInterface} configLoader
*/
public constructor(
emitter: EventEmitter,
options: NewmanQaseOptionsType,
_: unknown,
collectionOptions: NewmanRunOptions,
configLoader = new ConfigLoader(),
) {
const config = configLoader.load();
Expand All @@ -110,6 +144,7 @@
reporterName: 'newman-reporter-qase',
});

this.parameters = this.getParameters(collectionOptions.iterationData);
this.addRunnerListeners(emitter);
}

Expand Down Expand Up @@ -199,6 +234,8 @@
pendingResult.execution.duration = now - timer;
}

pendingResult.params = this.prepareParameters(item.events, exec.cursor.iteration);

void this.reporter.addTestResult(pendingResult);
}
});
Expand Down Expand Up @@ -252,4 +289,83 @@

return signature;
}

/**
* @param {EventList} events
* @param {number} iteration
* @returns {Record<string, string>}
* @private
*/
private prepareParameters(events: EventList, iteration: number): Record<string, string> {
if (this.parameters.length === 0) {
return {};
}

const availableParameters = this.parameters[iteration] ?? {};
const params = NewmanQaseReporter.getParameters(events);

if (params.length === 0) {
return availableParameters;
}

return params.reduce<Record<string, string>>((filteredParams, param) => {
const value = availableParameters[param];
if (value) {
filteredParams[param] = value;
}
return filteredParams;
}, {});
}


/**
* @param {any} iterationData
* @private
*/
private getParameters(iterationData: any): Record<string, string>[] {

Check warning on line 325 in qase-newman/src/reporter.ts

View workflow job for this annotation

GitHub Actions / Project qase-newman - Node 16

Unexpected any. Specify a different type

Check warning on line 325 in qase-newman/src/reporter.ts

View workflow job for this annotation

GitHub Actions / Project qase-newman - Node 16

Unexpected any. Specify a different type

Check warning on line 325 in qase-newman/src/reporter.ts

View workflow job for this annotation

GitHub Actions / Project qase-newman - Node 18

Unexpected any. Specify a different type

Check warning on line 325 in qase-newman/src/reporter.ts

View workflow job for this annotation

GitHub Actions / Project qase-newman - Node 18

Unexpected any. Specify a different type
if (!iterationData) {
return [];
}

if (Array.isArray(iterationData) && iterationData.every(item => typeof item === 'object' && item !== null)) {
return iterationData.map((item: Record<string, any>) => this.convertToRecord(item));

Check warning on line 331 in qase-newman/src/reporter.ts

View workflow job for this annotation

GitHub Actions / Project qase-newman - Node 16

Unexpected any. Specify a different type

Check warning on line 331 in qase-newman/src/reporter.ts

View workflow job for this annotation

GitHub Actions / Project qase-newman - Node 16

Unexpected any. Specify a different type

Check warning on line 331 in qase-newman/src/reporter.ts

View workflow job for this annotation

GitHub Actions / Project qase-newman - Node 18

Unexpected any. Specify a different type

Check warning on line 331 in qase-newman/src/reporter.ts

View workflow job for this annotation

GitHub Actions / Project qase-newman - Node 18

Unexpected any. Specify a different type
}

return [];
}

/**
* @param {unknown} obj
* @param parentKey
* @returns {Record<string, string>}
* @private
*/
private convertToRecord(obj: unknown, parentKey = ''): Record<string, string> {
const record: Record<string, string> = {};

if (this.isRecord(obj)) {
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
const value = obj[key];
const newKey = parentKey ? `${parentKey}.${key}` : key;

if (this.isRecord(value)) {
Object.assign(record, this.convertToRecord(value, newKey));
} else {
record[newKey] = String(value);
}
}
}
}

return record;
}

/**
* @param {unknown} obj
* @private
*/
private isRecord(obj: unknown): obj is Record<string, unknown> {
return typeof obj === 'object' && obj !== null;
}
}
Loading