npm package: @nativeplatform/nollie
Description: **Nollie** is a testing framework to be used in RestAPI testing.
Author: Native Platform Ltd
Latest version: 0.0.31-dev

**Nollie** is a testing framework to be used in RestAPI testing.

## Features

### The cases filter(env variable `NOLLIE_CASES_FILTER`)
Can be used to run only specific cases:
* `normal`: all cases
* `failed`: failed cases from the last run
* `smart`: if there were failed cases in the last run - run them, otherwise - run all cases

### NollieUtils
It is a special variable(namespaces) that can be used in specs. Has the following features:
* `db`
  * `sql` - make an arbitrary SQL-query to the database specified in `TesterOptions`
* `actions`
  * `sleep` - wait for specified amount of time(in ms) before continuing the case execution
  * `mockDate` - mock the date returned by the `Date` constructor. The framework will automatically "unmock" the `Date` after case execution
  * `unmockDate` - "unmock" date manually
* `regex` - contains some useful regular expression, which, for example, can be used with Jest's `expect.stringMatching`
* `generators`
  * `randomString` - generate a random string
  * `uuidv4` - generate an UUID

### Supported databases(optional feature):
* Postgresql

### Database
* data in database automatically truncated before running each case. `TesterOptions.db.excludeTruncateOnTables` can be used to specify tables which should be excluded from truncating

### Case scope variables
For each case there is a specific namespace that can be used to store arbitrary data for that particular case.<br>
You can store data returned by an action using `ActioOptions.storeResultAs` and `RequestActionOptions.storeDataAs`.<br>
You also have access to the case scope in functional actions, where you can freely manipulate on the scope.<br>
Each stored values should be of type `string`.<br>

Example usage in actions:
```typescript
// Assuming "user" stored in the case scope previously
const updateUser: RequestAction = {
  method: "POST",
  path: `api/users/{{user.id}}`,
  payload: {
    name: "{{user.firstName}} {{user.lastName}}",
  }
};
```
Example usage in asserts:
```typescript
const assertObj: AssertObject = {
  status: 200,
  data: { name: "{{user.firstName}} {{user.lastName}}"}        
};

const assertFunc: AssertFunc = (testResult, caseScope) => {
  expect(testResult.data.name).toBe(`${caseScope.user.firstName} ${caseScope.user.lastName}`);
}
```

### Focus/Skip
You can skip/focus particular cases or specs by using `focus` and `skip` options.<br>
NOTE: might work unpredictable if you're mixing this options for cases and specs simultaneously.

### Asserting results
`Assert` can be an `AssertObject` or `AssertFunction`:
* object form is useful for cases when you want to assert only the result of the case(the last action).
* function form is much more powerful. It can be used to perform assert on different entities(e.g. database, environment, data from 3rd party application).

### Logging
There is a special option for cases - `log`.<br>
It's useful for debugging, when you want to get better understanding what is going on in some particular case.<br>
You can log the following information:
* case scope(logged before running an action)
* database queries(performed by `NollieUtils.db.sql`)
* request action headers
* request action payload.
* request action response data (e.g. "axiosResponse.data")

### Notes
* Jest's `expect` can be used in `Assert`(function or object)
* Case scope variables can be used in `Action` and `Assert`

### Tips
* Start with failure cases, then move to successful ones.
* Examples of failure cases:
  * Failed auth
  * Bad payload(missing required fields, bad format)
  * Resource not found
* You can avoid repetitions in specs by extracting some entities(e.g. payload, responses) in separate files
* The framework allow to specify "intermediate" asserts. Such assert performed between actions(in the middle of case). Make sure you do not overuse this feature. It's primary purpose is to group similar "failure" cases into single and avoid redundant case initialization(e.g. creating user for each case). Try to keep your scenarios as small as possible.
* Plain actions can be grouped together in an array of actions(`ActionGroup`), so it can be easily reused by multiple cases.

### Development

#### Publish new version
1. Update module version in package.json
2. Run `npm publish`

#### Testing module locally
1. Run `yarn build`
2. Install local Nollie version in target project: `yarn add /path-to-nollie-root-dir/`

## Credits
Igor Pavlov - the initial idea

[Denis Volok](https://github.com/denvolok) - main contributor

## [License](LICENSE)


The MIT License (MIT)

Copyright (c) 2022 Native Platform Ltd.