# Getting Started

This guide will help you quickly get to know TestMace interface and its main features.

{% hint style="info" %}
In this guide we're going to test a back-end server running the following scenario for post records:

* request all records from the server;
* add a new record;
* check if the record was added correctly;
* update the record and check the update with a server response;
* request the updated record from the server;
* check if the record on the server is updated;
* delete the record;
* check if the record was deleted from the server.

**You'll need about 10 min to do that after running the app.**
{% endhint %}

## Installation

Go to our site <https://client.testmace.com>  or use the following links to download TestMace:

* Windows <https://download.testmace.com/TestMace.exe>
* Mac OS <https://download.testmace.com/TestMace.dmg>
* Linux <https://download.testmace.com/TestMace.AppImage>

Run the installer and follow the instructions. You will be prompted to choose an installation directory.

{% hint style="warning" %}
&#x20;*To install TestMace on Windows run the installer **as an administrator**.*
{% endhint %}

Once the installation is complete, run the app. You'll see a new project.

## Interface Overview

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFUOLTSKsreXOFBI39%2F-LhFUVgv2a4VQpeSBF9V%2Fmain_screen_1.png?alt=media\&token=8d31cd2a-360a-4bf5-9c7e-496ae4dd0694)

## Your First GET Request

To make your first request create a new tab by clicking on **+**. При этом в зоне  You'll see a new **Scratch 1** node in the Scratches Area. Insert this address in the URL field: <https://testmace-stage.herokuapp.com/posts>. You can now test server response right from Scratches Area or add your scratch to the project. Rename the node to **getPosts** for convenience.

{% hint style="info" %}
Note that all changes to the project are automatically saved in real-time.
{% endhint %}

![Creating a GET request template](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhJpQ_vCIZWrro6TJZk%2F-LhJpUafiA17-CXuQAp9%2Fgetting_started_1.gif?alt=media\&token=844a0955-59f9-404d-aa2b-2bc95667611e)

In your project create a [Folder](/node-types/folder) node named **posts** and move the scratch **getPosts** from the Scratches Area to the Project Area.

![Creating a Folder node and GET request template moving](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhJpQ_vCIZWrro6TJZk%2F-LhJq2k6iMb_jD3VQw8e%2Fgetting_started_2.gif?alt=media\&token=403bb656-0f43-498a-b52a-4e50ab2fc092)

Double click on the created request **getPosts** to open it and then run it with the Run button.

![Running a GET request](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhJpQ_vCIZWrro6TJZk%2F-LhJqgooGAEXyLj0QBA7%2Fgetting_started_3.gif?alt=media\&token=5b1e83b5-197d-46aa-9baf-0b7b89d0f9f7)

As you see, the request was successfully run, and you have a list of existing records in **Response Area**. Let's take a closer look at this window:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFUOLTSKsreXOFBI39%2F-LhFV_hy2ssJx_CXd937%2Frun%20screen.png?alt=media\&token=af2309ed-b0ae-4ca3-8284-130ed9b81c54)

{% hint style="info" %}

#### Request parameters

Here you can specify http headers and pass parameters to the request with the help of the autocomplete feature and variables.

#### Request type

* **GET** — get a resource
* **POST** — create a resource
* **PUT** — update a resource
* **DELETE** — delete a resource
* **PATCH** — make some changes to a resource
* **OPTIONS** — specify resource connection parameters

#### URL

The URL field supports autocompletion and using variables. We'll take advantage of these features later.

#### Make Request

Sending a request or a group of requests while running from the project root or a folder node.

#### Response area

A server response area. The Response Body tab contains parsed, JSON, and text representations of a response. On the tabs next to it you can see Response Headers and create or view existing Assertion nodes to make a request.
{% endhint %}

## POST Request and Assertion

Now let's add a new post record to the server, and to do that we need to create a new [RequestStep](/node-types/request-step) node.&#x20;

{% hint style="info" %}
A new node can be created in three different ways:

1. Create a Scratch by clicking on **+** and drag and drop it to the project later.
2. Right-click on the parent node and choose **Add node -> Request step.**
3. Click on the button **Add project node-> Add node -> Request step**.&#x20;
   {% endhint %}

Use any of these ways to create a node and name it **createPost**.&#x20;

1. Set POST as its **Request type**.
2. Insert <https://testmace-stage.herokuapp.com/posts> in the URL field.
3. Chose JSON on the response body tab and add `{"title": "Testing post", "content": "Sendt via TestMace"}`
4. Click on the RUN button and send the request.&#x20;

You'll get a response telling you that the record has been successfully added, but we need to check if it was added correctly. And to do that we'll use the quickest way of creating [Assertion](/node-types/assertion-node) nodes. We'll compare the sent data with the data received from the server.

In Response Area open the tab with the parsed response and right-click on the **title value**, that you passed in the request and choose **Create Assertion -> Compare -> Equal.** It'll create and open the [Assertion](/node-types/assertion-node) node, and as you're not going to configure it, just close it. Create an Assertion node for the Content value in the same way.

Now send the **createPost** request, and you'll see that the test was successful. The task is really easy. Have a look at the animation below:

![Sending POST request and creating an Assertion node](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhJpQ_vCIZWrro6TJZk%2F-LhK0GR6pYAbucRUToRZ%2Fgetting_started_4.gif?alt=media\&token=94e64916-b469-4eaa-8575-925e4922f0a6)

### Dynamic Variables

To be able to interact with the newly added node you should pass its **Id** to all subsequent [Request step](/node-types/request-step) nodes. Define a dynamic variable **postId** and assign to it the Id value, received in the record after running **CreatePost**.&#x20;

1. &#x20;Right-click on the **Id** value in the **CreatePost** node's response body.&#x20;
2. Choose **Assign to variable**.&#x20;
3. In a pop-up window choose the **posts** project directory as a node, enter the variable name (**postId**) and press **OK**.

To access the variable use a[ default variable](/variables/variables) `$dynamicVar`:

```
${$dynamicVar.postId}
```

![Creating a dynamic variable](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhK0SAV-59GBQjAg6hd%2F-LhK1BRwGcPnJZvbxbS4%2Fgetting_started_5.gif?alt=media\&token=b89564f3-3b67-40ee-853a-537d4a89cea0)

## PUT Request

It's time we sent a PUT request.  Let's access the previously created record using a dynamic variable `${$dynamicVar.postId}` and update its **title** and **content** values.

1. Create a [RequestStep](/node-types/request-step) node named **updatePost**.
2. Set **PUT** as its request type.
3. URL: <https://testmace-stage.herokuapp.com/posts/${$dynamicVar.postId}>
4. Body: `{"title": "Testing post updated", "content": "Updated via TestMace"}`
5. &#x20;Send the request and, just like we did with the **POST** request, create two [Assertion](/node-types/assertion-node) nodes to compare sent and received **title** and **content** values.

![Creating a PUT request](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhK0SAV-59GBQjAg6hd%2F-LhK3Y9OgAtw8zYool2I%2Fgetting_started_6.gif?alt=media\&token=7c828698-04e5-4264-8182-857fc11fae20)

## Verifying Changes

In some situations you might want to additionally verify changes in the record, because the server may give a successful response of PUT, but sending a GET request you receive the old version.&#x20;

To do that create a GET request at the record's URL using a dynamic variable.

1. Create a new [RequestStep](/node-types/request-step) node named **getPost**.
2. Request type: GET.
3. URL: <https://testmace-stage.herokuapp.com/posts/${$dynamicVar.postId}>
4. Send the request and create 2 [Assertion](/node-types/assertion-node) nodes to compare **title** and **content** values.

![Verifying changes using a GET request](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhK0SAV-59GBQjAg6hd%2F-LhK4Oj-daBXeUD9mVGk%2Fgetting_started_7.gif?alt=media\&token=2152423d-0a73-4d9b-beb8-7226cfa0e1c5)

## DELETE Request

Our next step is deleting our record at the provided URL using a dynamic variable.

1. Create a [RequestStep](/node-types/request-step) node named **deletePost**.
2. Request type: DELETE.
3. URL: <https://testmace-stage.herokuapp.com/posts/${$dynamicVar.postId}>

![DELETE request](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhK59c6K51LChtOFa3G%2F-LhK5CKZiNZ92uvFMQqo%2Fgetting_started_8.gif?alt=media\&token=fc0eb164-f908-4991-a96b-6c6ecb33e8c0)

## DELETE Verifying

To ensure that the record was deleted from the server, create a GET request at that record's URL using a dynamic variable. We expect to get a 404 response from the server, and so we create an Assertion node:

1. Create a new [RequestStep](/node-types/request-step) node named **checkIfNodeExists**
2. Request type: GET
3. URL: <https://testmace-stage.herokuapp.com/posts/${$dynamicVar.postId}>
4. Send the request, open the **Assertions** and add a new [Assertion](/node-types/assertion-node) node, by clicking ADD. Set the node data:
   1. Actual value: `${$response.code}`
   2. Operator: `=`
   3. Expected value: `404`

![Verifying if the record was deleted via the server response](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhK6MDYKbETOBDIIY1v%2F-LhKAIKXMtt4o0-LWhwL%2Fgetting_started_9.gif?alt=media\&token=c80f7d78-f462-4fb7-8e0a-97fed0a858c9)

## Summary

In the end we've got a test set for our server that we can run in a scenario. Just go to **posts** and click RUN.&#x20;

![Running a scenario](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhK6MDYKbETOBDIIY1v%2F-LhKB-vkAz_pWK1QmcFi%2Fgetting_started_10.gif?alt=media\&token=e0a178fb-008e-4145-acd2-3e1095d875ab)

## Video Instruction

Watch a video of the entire process of creating a scenario described in this guide.

{% embed url="<https://youtu.be/Gyg_4w78KBo>" %}

## Getting started code for a [shared](/other-features/import/shared) import

{% file src="/files/-LhKOt6yJc6\_lHN7SVh1" %}
Getting Started Share Code
{% endfile %}

## Download the Project

Unzip in the TestMace projects directory.

{% file src="/files/-LfxYBWoktxgpSwpf33U" %}
Quick start project
{% endfile %}


# Cloud Synchronization

Test Mace allows you to organize team work and synchronize your projects in the cloud

## Test Mace Plans

|                       | FREE   | PROFESSIONAL | ENTERPRISE |
| --------------------- | ------ | ------------ | ---------- |
| Users                 | 1      | 1-25         | ∞          |
| Disc space            | 200 МБ | 2000 МБ      | ∞          |
| Simultaneous sessions | 1      | 1-25         | ∞          |

## To use cloud sync you'll need to:

1. Sign up in the [control panel](https://dashboard.testmace.com/)
2. Create a team and a project
3. Select and activate your Test Mace pricing plan
4. Add relevant users to the team and project (they should also sign up in the control panel)
5. Sign in  to your account in the Test Mace application
6. Choose the project and open it

Now let's discuss each step in detail.

{% hint style="info" %}
Before using the cloud sync feature, you need to create an account in the [control panel](https://dashboard.testmace.com/), create a new project and add users to it.
{% endhint %}

## Signing up

Follow the link [https://dashboard.testmace.com/](https://dashboard.testmace.com/register). To create an account, use the "Sign in with GitHub" or "Sign in with Google" buttons. You can also do that via your email: click on the "Click here to create one" link, fill in the registration forms, and log in the control panel.

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LqkpbLVFpxlCsLvGB6d%2F-Lqkq-1Y0ZqJhfFbZPHp%2Fsign_up2.png?alt=media\&token=72025082-ea41-45ef-b0ae-cf7e357fbdbe)

## Teams and projects

After you've logged in the system, select the Teams menu item and create a new team. Then select it and add a new project.

![Creating a team and adding a new project](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LqkpbLVFpxlCsLvGB6d%2F-Lqkq2feHuqv7oromi9i%2Fcloud-1.gif?alt=media\&token=735a07d3-06ab-4750-81e1-d2b9bedec05a)

### Adding users to the team

{% hint style="info" %}
The users you want to add to the team should be signed in the [control panel](https://dashboard.testmace.com/).
{% endhint %}

By default, after you've created a team and a new project you already have one user in the team, which is the creator. To add up to 25 users to the team you have to activate the **Professional** plan.

Go to your team, select the plan and activate it.

![Professional plan activation ](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LqkpbLVFpxlCsLvGB6d%2F-LqkqAQU7tWsJUiPZCfs%2Fcloud-2.gif?alt=media\&token=2714cd21-88cb-40b7-bb47-4481dc148033)

{% hint style="info" %}
Add users to your team and then add them to the projects.
{% endhint %}

![Adding users to a team and a project](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LqkpbLVFpxlCsLvGB6d%2F-LqkqH-B7XZm0j6fWmwW%2Fcloud-3.gif?alt=media\&token=ec1b047d-98f3-4947-a202-345c56c0fd46)

## Signing in to the app and choosing a project

Open the Test Mace app and select the <img src="https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LqkpbLVFpxlCsLvGB6d%2F-LqkqqoEh9MTYUtq-Y-F%2Fsign_up3.png?alt=media&amp;token=185d0b7e-8e06-4c83-a319-820114d86ac8" alt="" data-size="original"> icon to sign in with the data you entered earlier. If it is successful, you'll see your account name instead the  <img src="https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LqkpbLVFpxlCsLvGB6d%2F-LqkqqoEh9MTYUtq-Y-F%2Fsign_up3.png?alt=media&amp;token=185d0b7e-8e06-4c83-a319-820114d86ac8" alt="" data-size="original"> icon. Select it and choose the Teams item. You'll see all the projects available for this particular team. Choose the project from the list and click "Open".

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LqkpbLVFpxlCsLvGB6d%2F-LqkqNi_wGIFz7fUcZyD%2Fcloud-4.gif?alt=media\&token=76b5ffb4-b48c-4f63-8efd-c8ffbd650af6)

## Project synchronization

To syncronize a project, it is required to log in the system first. Then just select the <img src="https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LqkpbLVFpxlCsLvGB6d%2F-LqkqhjpjRq46AYgMbNR%2Fsync4.png?alt=media&amp;token=c29fad5c-5f9f-4ad0-b8f1-658f2afe4246" alt="" data-size="original"> button.

![Project synchronization](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LqkpbLVFpxlCsLvGB6d%2F-LqkqRznTib8dic8YHXa%2Fcloud-5.gif?alt=media\&token=0e9630ca-7437-428a-afca-f5c484b96aa0)

### Synchronization status&#x20;

| Synchronization status                                                                                                                                                                                                                                                                                                                       | Description                                         |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| <img src="broken://files/-LqjWIWUwMOxXE8QuXna" alt="" data-size="original"> <img src="https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LqkpbLVFpxlCsLvGB6d%2F-LqkqXeOGvDFEEkDE2T6%2Fsync1.png?alt=media&amp;token=873758ec-79b7-45b4-bb61-58dee77929ad" alt="" data-size="original"> | No changes in the project                           |
| <img src="broken://files/-LqjWW1MGSfSAbEJPvUE" alt="" data-size="original"> <img src="https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LqkpbLVFpxlCsLvGB6d%2F-LqkqaaP56RdDPPk9lKy%2Fsync2.png?alt=media&amp;token=7987257d-fa4c-4c7d-af50-a7ebd9df3cce" alt="" data-size="original"> | Some changes are made in the cloud                  |
| <img src="broken://files/-LqjW_wEKrqxwOoYD_cx" alt="" data-size="original"> <img src="https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LqkpbLVFpxlCsLvGB6d%2F-Lqkqdx8lyocMdSHuaAD%2Fsync3.png?alt=media&amp;token=af0d2ebb-c031-433c-90b3-8a09d3c039a0" alt="" data-size="original"> | Some changes are made both locally and in the cloud |
| <img src="broken://files/-LqjWfZbctuasuLG3O50" alt="" data-size="original"> <img src="https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LqkpbLVFpxlCsLvGB6d%2F-LqkqhjpjRq46AYgMbNR%2Fsync4.png?alt=media&amp;token=c29fad5c-5f9f-4ad0-b8f1-658f2afe4246" alt="" data-size="original"> | There are some local changes                        |


# The Menu

![The menu](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LaP7qYcgXBIoVT_vHWF%2F-LaPBhGcxMBnMx5ThBwe%2F2.png?alt=media\&token=519f2efa-c526-4180-a4b1-e00e23a704dd)

* [~~**Undo и Redo**~~](broken://pages/-Lgfjvr-InvpkHaf1i3X) - undo and redo your actions. At the moment all actions on changing projects and nodes are supported.
* [**Cookies**](/working-with-project/cookie) - a dialog to work with cookies.
* [**Environments**](/variables/env) - configure and choose the environment.<br>


# Interface Overview

### The application interface is divided into 3 main parts:

1. Project tree
2. Scratches area
3. Main area

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFUOLTSKsreXOFBI39%2F-LhFUVgv2a4VQpeSBF9V%2Fmain_screen_1.png?alt=media\&token=8d31cd2a-360a-4bf5-9c7e-496ae4dd0694)

### The main area (or request area) also has several elements:

1. Request type
2. URL
3. The RUN button
4. Request parameters
5. Response area

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFUOLTSKsreXOFBI39%2F-LhFV_hy2ssJx_CXd937%2Frun%20screen.png?alt=media\&token=af2309ed-b0ae-4ca3-8284-130ed9b81c54)


# Scratches

{% hint style="info" %}
**These are node scratches that you can move to the main project tree.**
{% endhint %}

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFWbNzd7R5t5rhjodi%2F-LhFWcos4ux27Ba5nTTm%2Fscratches.gif?alt=media\&token=d0dc9247-810c-4aa0-a4ec-3283b88b3e5d)


# Node Types

{% hint style="info" %}
A node is an element of the project or scratches tree.
{% endhint %}

### Node Types

* [**Project**](/node-types/project)**.** This is a root node, that is created automatically when you start a new project. It is functionally identical to a Folder node.
* [**Folder**](/node-types/folder)**.** It allows to group Folder and RequestStep nodes under it.
* [**RequestStep**](/node-types/request-step). This is a node that you can use to send a request. It can have only one Assertion node as a child element.
* [**Assertion**](/node-types/assertion-node). This one is used for writing tests. It can be a child node only for a RequestStep node.
* [**Script**](/node-types/script). It allows to run a JavaScript script and access the app's API.
* [**Link**](/node-types/link). Allows to access an existing node.
* [**Api description**](/node-types/api-description)
  * [**ApiRootFolder**](/node-types/api-description/apirootfolder)**.** A root element (folder) for API description.
  * [**ApiFolder**](/node-types/api-description/apifolder)**.** Groups logically close API description endpoints (e.g. two endpoints with the same urls but different methods).
  * [**ApiRoute**](/node-types/api-description/apiroute)**.** A specific endpoint description.
* [**Broken**](/node-types/broken)**.**  It is used for descripting nodes that were loaded unsuccessfully. Can't be created manually and is not stored in the file system.


# Keyboard Shortcuts

Keyboard shortcuts in TestMace

| Action                            | Keyboard shortcuts |
| --------------------------------- | ------------------ |
| **Navigation**                    |                    |
| Set the focus to the project tree | Ctrl + 1           |
| Set the focus to scratches        | Ctrl + 2           |
| Set the focus to the main area    | Ctrl + 3           |
| Open settings                     | Ctrl + Alt + S     |
| **Tabs**                          |                    |
| The previous tab                  | Ctrl + Shift + Tab |
| The next tab                      | Ctrl + Tab         |
| Close the tab                     | Ctrl + W           |
| Create a new scratch              | Ctrl + T           |
| **Project Tree**                  |                    |
| Set the focus to the search field | Ctrl + F           |
| Open the node                     | Enter              |
| Open the node menu                | Alt + Insert       |
| Delete the node                   | Delete             |
| Rename the node                   | Ctrl + F6          |
| The next node                     | ↓                  |
| The previous node                 | ↑                  |
| Expand the node                   | →                  |
| Collapse the node                 | ←                  |
| **Project**                       |                    |
| Run Node                          | Ctrl + Enter       |
| Focus Url                         | Ctrl + E           |
| Save Project                      | Ctrl + S           |
| Save Project as                   | Ctrl + Shift + S   |
| Open Project                      | Ctrl + O           |
| Create New Project                | Ctrl + N           |
| Undo                              | Ctrl + Z           |
| Redo                              | Ctrl + Shift + Z   |


# Project

A **Project** node is a root element of the project. It is created automatically after creating a new project and its functionally identical to a [Folder](/node-types/folder) node. A **Project** node can't be created manually and can't be used as a child of other node types.

{% hint style="warning" %}
For now, you can not rename the Project folder in the app. Moreover, if you do that in the file system directly, it'll crash the whole project.
{% endhint %}


# Folder

It is used to group other nodes and can be a child of [Project](/node-types/project) and **Folder** nodes. This is how it looks like in the project tree:

![A Folder node in the project tree](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgWwgka3rZBZgHDvNjd%2F-LgWyItsMGOzq1NvoQl7%2F1.png?alt=media\&token=c7712323-de2a-4872-b743-811ee97d469b)

Here are the actions available for this node type in the project tree:

![A Folder node context menu](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgWwgka3rZBZgHDvNjd%2F-LgWyvYOvLbHw3fujBhs%2F2.png?alt=media\&token=eeae0e6b-a4fc-4a11-a47b-330c92754426)

* **Add node.** Ckick on it to add a child node. You can choose a node type in the submenu.
* **Rename.** Change the node name.
* **Duplicate.** Make a copy of the node.  The new node will be named **NodeName \[Copy \[number]]**.
* **Remove node.** Use it to delete the node.
* **Run.** Run the node.
* [**Share**](/other-features/import/shared)**.** Share the node. Note that the link with information about the node will be created in the clipboard.
* **Show in explorer.** Open the folder with the node in the file manager.

You can open the node with a double-click on it in the project tree. A **Folder** node tab looks like this:

![A Folder node tab](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhpB3Wy9BwwfjEcUimT%2F-LhpB8XEmTJvBFFyGPNx%2F8.png?alt=media\&token=9a2b5343-a1d1-4f37-9738-87d51a2d7f00)

In the screenshot you can see the following parts of the tab:

1. The **Run** button to run the nodes under the Folder node.
2. The control panel
3. The **Headers** button to specify inheritable HTTP headers.
4. The button to open the [**variables dialog**](/variables/user-variables)**.**
5. **Child nodes** area
6. Checks if the node has a valid SSL certificate.  It is used as an inherited parameter in [RequestStep](/node-types/request-step) nodes.
7. **Authorization**.

Let's describe these parts in detail.

### Control Panel

The **Run** button function is described above. It's worth mentioning that when running a node the button looks different:

![The Run button after running a node](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgXGmrChTZiPq8y7nxp%2F-LgXJs1-z6f1cboc500J%2F4.png?alt=media\&token=f5679900-3e5e-4317-9f48-e6315c4f4dea)

You can stop the node by clicking **Abort**.

The **Headers** button allows to specify [inheritable HTTP headers](/other-features/default-http-headers).

Variables editing is shown in the [Custom variables](/variables/user-variables) section.

### File Representation

A **Folder** node is a folder with the node name, containing the index.yml file with the following format:

```javascript
{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of Folder node",
      "const": "Folder",
      "type": "string"
    },
    "authData": {
      "$ref": "#/definitions/IAuthorizationData",
      "description": "Authorization parameters"
    },
    "requestData": {
      "$ref": "#/definitions/IRequestParametersData",
      "description": "Request parameters"
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "authData",
    "children",
    "name",
    "requestData",
    "type",
    "variables"
  ],
  "definitions": {
    "IAuthorizationData": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string"
        }
      },
      "required": [
        "type"
      ]
    },
    "IRequestParametersData": {
      "type": "object",
      "properties": {
        "headers": {
          "description": "Headers",
          "type": "array",
          "items": {
            "$ref": "#/definitions/NameValueParam"
          }
        },
        "disabledInheritedHeaders": {
          "description": "Names of disabled headers",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "strictSSL": {
          "$ref": "#/definitions/StrictSSLOptions",
          "description": "Requires SSL certificates be valid"
        }
      },
      "required": [
        "disabledInheritedHeaders",
        "headers",
        "strictSSL"
      ]
    },
    "NameValueParam": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "value": {
          "type": "string"
        },
        "isChecked": {
          "type": "boolean"
        }
      },
      "required": [
        "name",
        "value"
      ]
    },
    "StrictSSLOptions": {
      "enum": [
        "Inherit",
        "No",
        "Yes"
      ],
      "type": "string"
    },
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
```


# RequestStep

A **RequestStep** node is used for sending HTTP requests. TestMace allows to configure a request and use it as a single request or as a part of a scenario.

### A RequestStep node in the project tree

To create a **RequestStep** node right-click on a  [Folder](/node-types/folder) or [Project](/node-types/project) node and choose **Add node -> RequestStep**.

This is how a **RequestStep** node looks like in the project tree:

![A RequestStep node in the project tree](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LlfdO6jxRXXJvT27opm%2F-LlfdY5N7y3_Ejz_7yjg%2Fimage.png?alt=media\&token=06f38d34-f9c9-44f7-925a-0c4671c252a8)

Let's discuss its features in more detail. The color of the top left circle signifies an HTTP request status: gray - if the request hasn't been sent yet, green - if the HTTP code is successful (e. g. 200, 201, etc), red - if the HTTP code is unsuccessful (e. g. 404, 500, etc). The sheet icon color signifies a child [Assertion](/node-types/assertion-node) node status: gray - if the node hasn't been run yet, green - if after running the [Assertion](/node-types/assertion-node) node either doesn't exist or was successfully run, red - if the [Assertion](/node-types/assertion-node) node was run unsuccessfully (some tests were failed).

There are several actions you can do with this node type:

![Context menu for the RequestStep node](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LlfdO6jxRXXJvT27opm%2F-LlfdnSjXlQrJrODLKd4%2Fimage.png?alt=media\&token=0312686c-e434-4330-933a-ccee2808f7ae)

* **Add node.** Click on it to add a child node. You can choose a node type in the submenu.
* **Rename.** Change the node name.
* **Duplicate.** Make a copy of the node. The new node will be named *NodeName \[Copy \[number]]*.
* **Remove node.** Use it to delete the node.
* **Run.** Run the node.
* [**Share**](/other-features/import/shared) the node. Note that the link with information about the node will be created in the clipboard.
* **Show in explorer.** Open the folder with the node in the file manager.

**RequestStep Node Tab**

After you create a **RequestStep** node (or double click on it) you'll see the RequestStep node tab. It looks like this:

![The RequestStep node tab](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhEe9KoXsq-0TMyMIKr%2F-LhEeFBacAIdCWd1c1LG%2F7.png?alt=media\&token=809e17f4-78b8-4ff7-a4b9-d821a341480f)

Let's discuss every interface part in detail.

#### Request Configuration Area

The top area of the tab looks like this:

![The top area of the tab](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lgl2bpuFMbqWPyxebVZ%2F-Lgg6pFo1MUmwO3TlI2e%2F3.png?alt=media\&token=9e9d18da-7c42-4d7a-ba13-4af46bdfa203)

In the screenshot you can see the following parts of the tab:

1. Request method. There are several methods you can choose from:
   * **GET** — get a resource
   * **POST** — create a resource
   * **PUT** — update a resource
   * **DELETE** — delete a resource
   * **PATCH** — make some changes to a resource
   * **OPTIONS** — specify resource connection parameters
2. The URL field.
3. The Run button.
4. The button for [editing variables](https://app.gitbook.com/@testmace/s/project/~/drafts/-Ljtp7mh1Q7dEcGEFMwr/primary/variables/user-variables/staticheski-opredelyaemye-peremennye)

‌You can see the edit panel for headers, query parameters, authorization and request body down below. This is the panel for POST requests:

![Request parameters edit panel](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhEe9KoXsq-0TMyMIKr%2F-LhEeSfnbk-XB_Cp-SYq%2F8.png?alt=media\&token=58f36e77-d1ad-471e-a749-148d9c81bbf1)

The panel has the following tabs:

* **Headers** - edits an HTTP headers list;
* **Query parameters** - edits a query parameters list;
* **Body** - configures a request body;
* **Authorization** - configures [authorizations](/working-with-project/authorization);
* **Other** - configures other request parameters.

The **Headers** and **Query** parameters tabs have similar interfaces - they are both tables with [bulk editing](/other-features/bulk-edit) and row disabling options. Moreover, HTTP headers can be set [by default](/other-features/default-http-headers).

The **Other** tab has the following interface:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LholopcaA6HBvjCALCd%2F-LholrtdZoWLvhle3Aqt%2Fother_RS.jpg?alt=media\&token=61b0ab6c-d788-4bd4-a296-4d3c905a7f9e)

You can set the **Requires SSL certificates be valid** parameter. It is **Inherit** by default, which means that the value of the parent node is inherited. If this parameter of the parent node is **Inherit**, the parameter is disabled. You can choose from:

* Yes
* No
* Inherit

Now look at the **Body** tab:

![The Body tab](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhEfGEQf9EJ_mvdBLUe%2F-LhEfIwPsMSA3HRokWb5%2F10.png?alt=media\&token=6fc44bf9-a1af-48f8-ba8a-c58ea0d4621e)

You can choose your body type from a dropdown menu:

* **JSON** - to send JSON data. You can use the editor with JSON syntax highlighting and the [variables mechanism](/variables/user-variables) support to edit the data. When sending a request a `Content-Type`  header with the `application/json`

  value is added to the HTTP headers list.
* **Form data** - to edit `multipart/form-data` forms. It is a table with [bulk edit ](/other-features/bulk-edit)option. The table rows may contain either plain strings or file links as their values.
* **Form URL encoded** - to edit `application/x-www-form-urlencoded` forms. It is a table with [bulk edit ](/other-features/bulk-edit)option.
* **File** - to send a file content in the request body.
* **XML** - to send XML data. You can use the editor with XML syntax highlighting and the [variables mechanism](/variables/user-variables) support to edit the data. When sending a request a `Content-Type` header with `application/xml` value is added to the HTTP headers list.
* **Text** - to send text data. You can use the editor with the [variables mechanism](/variables/user-variables) support to edit the data. When sending a request a `Content-Type` header with `text/plain` value is added to the HTTP headers list.

**Response Configuration Area**

Let's send a request to the <https://testmace-stage.herokuapp.com/posts> url and look at the response area:

![The RequestStep node response area](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lgl2bpuFMbqWPyxebVZ%2F-Lgl8De35KiGku03AgVu%2F6.png?alt=media\&token=3d716437-3fbe-4d07-bd08-338ee1dd8c12)

You can see short info about the response at the top of the area: a successful or unsuccessful response code, time and size.

There are the following tabs at the bottom of the response area:

* **Response body** - contains the response body in different formats, such as:
  * **Parsed** - a tree form of the response. Each tree element has a context menu, so that you could create [Assertion](/node-types/assertion-node) nodes and work with [dynamic variables](/variables/user-variables/dynamic-variables#variable-assignment).
  * **JSON** - JSON syntax highlighting of the response body. It is available only if you have a JSON response body.
  * **XML** - XML syntax highlighting of the response body. It is available only if you have an XML response body.
  * **HTML** - HTML syntax highlighting of the response body. It is shown if the response body is an HTML page.
  * **Text** - plain text with no highlighting.
  * **Preview** - a rendered response body version. It is shown if the response body is an HTML page.
* **Response headers** - a response HTTP headers list.
* **Assertions** - a list of assertions that a child [Assertion](/node-types/assertion-node) node has.

**File Representation**

A **RequestStep** node is a folder with the node name, containing the index.yml file with the following format:

```javascript
{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of Folder node",
      "const": "RequestStep",
      "type": "string"
    },
    "assignVariables": {
      "description": "List of variables assignments",
      "type": "array",
      "items": {
        "$ref": "#/definitions/AssignVariable"
      },
      "default": []
    },
    "requestData": {
      "$ref": "#/definitions/IRequestData"
    },
    "authData": {
      "$ref": "#/definitions/IAuthorizationData",
      "description": "Authorization parameters"
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "assignVariables",
    "authData",
    "children",
    "name",
    "requestData",
    "type",
    "variables"
  ],
  "definitions": {
    "AssignVariable": {
      "type": "object",
      "properties": {
        "path": {
          "description": "Path in $response variable (e.g. body.id)",
          "type": "string"
        },
        "assign": {
          "$ref": "#/definitions/NodeReference",
          "description": "Link on target node (one of parents)"
        },
        "variable": {
          "description": "Name of dynamic variable in target node",
          "type": "string"
        }
      },
      "required": [
        "assign",
        "path",
        "variable"
      ]
    },
    "NodeReference": {
      "type": "object",
      "properties": {
        "refNodePath": {
          "description": "Absolute path to node",
          "type": "string"
        },
        "type": {
          "description": "Marker of reference entity",
          "const": "reference",
          "type": "string",
          "default": "reference"
        }
      },
      "required": [
        "refNodePath",
        "type"
      ]
    },
    "IRequestData": {
      "type": "object",
      "properties": {
        "request": {
          "description": "Common request parameters",
          "type": "object",
          "properties": {
            "method": {
              "$ref": "#/definitions/RequestMethod",
              "description": "HTTP-method"
            },
            "url": {
              "type": "string"
            }
          },
          "required": [
            "method",
            "url"
          ]
        },
        "params": {
          "description": "Query parameters",
          "type": "array",
          "items": {
            "$ref": "#/definitions/NameValueParam"
          }
        },
        "body": {
          "$ref": "#/definitions/IRequestBody",
          "description": "Body parameters"
        },
        "headers": {
          "description": "Headers",
          "type": "array",
          "items": {
            "$ref": "#/definitions/NameValueParam"
          }
        },
        "disabledInheritedHeaders": {
          "description": "Names of disabled headers",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "strictSSL": {
          "$ref": "#/definitions/StrictSSLOptions",
          "description": "Requires SSL certificates be valid"
        }
      },
      "required": [
        "body",
        "disabledInheritedHeaders",
        "headers",
        "params",
        "request",
        "strictSSL"
      ]
    },
    "RequestMethod": {
      "enum": [
        "DELETE",
        "GET",
        "OPTIONS",
        "PATCH",
        "POST",
        "PUT"
      ],
      "type": "string"
    },
    "NameValueParam": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "value": {
          "type": "string"
        },
        "isChecked": {
          "type": "boolean"
        }
      },
      "required": [
        "name",
        "value"
      ]
    },
    "IRequestBody": {
      "type": "object",
      "properties": {
        "type": {
          "$ref": "#/definitions/RequestBodyType",
          "description": "Type of body"
        },
        "jsonBody": {
          "description": "JSON string of body",
          "type": "string"
        },
        "xmlBody": {
          "description": "XML string of body",
          "type": "string"
        },
        "textBody": {
          "type": "string"
        },
        "formData": {
          "description": "multipart/form-data form",
          "type": "array",
          "items": {
            "$ref": "#/definitions/RequestStepFormData"
          }
        },
        "formURLEncoded": {
          "description": "application/x-www-form-urlencoded form",
          "type": "array",
          "items": {
            "$ref": "#/definitions/NameValueParam"
          }
        },
        "file": {
          "description": "Link on file, which will be used as a content for body",
          "type": "string"
        }
      },
      "required": [
        "file",
        "formData",
        "formURLEncoded",
        "jsonBody",
        "textBody",
        "type",
        "xmlBody"
      ]
    },
    "RequestBodyType": {
      "enum": [
        "File",
        "FormData",
        "FormURLEncoded",
        "Json",
        "Text",
        "Xml"
      ],
      "type": "string"
    },
    "RequestStepFormData": {
      "type": "object",
      "properties": {
        "type": {
          "$ref": "#/definitions/FormDataField"
        },
        "name": {
          "type": "string"
        },
        "value": {
          "type": "string"
        },
        "isChecked": {
          "type": "boolean"
        }
      },
      "required": [
        "name",
        "type",
        "value"
      ]
    },
    "FormDataField": {
      "enum": [
        "File",
        "Text"
      ],
      "type": "string"
    },
    "StrictSSLOptions": {
      "enum": [
        "Inherit",
        "No",
        "Yes"
      ],
      "type": "string"
    },
    "IAuthorizationData": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string"
        }
      },
      "required": [
        "type"
      ]
    },
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
```


# Assertion

**Assertion** nodes are used in writing tests. Each **Assertion** node consists of several assertions - statements that allow you to test certain assumptions. When you run an **Assertion** node, you run all assertions. If any test raises an error, the entire **Assertion** node run is unsuccessful.

An **Assertion** node can only be created as a child of a [RequestStep](/node-types/request-step) node. At the same time, a [RequestStep](/node-types/request-step) node can have only one **Assertion** node as a child.

There are two ways to create an **Assertion** node. First, choose **Add node -> Assertion** from a [RequestStep](/node-types/request-step) node context menu. Second, click **+ CREATE NEW ASSERTION NODE** on the Assertion tab of the [RequestStep](/node-types/request-step) node response area.

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-M4OT249rvL-_C8vTTtA%2F-M4OUEAQYHhMlYdD6sWU%2Fimage.png?alt=media\&token=d2cd2214-3fda-4c63-8c19-f68174c9ec37)

This is how an **Assertion** node looks like in the project tree:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-M4OT249rvL-_C8vTTtA%2F-M4OUH0tP1IpwYRfZOYL%2Fimage.png?alt=media\&token=c2aa7ec6-baa9-4b00-933c-5a86f6cf32ed)

If an **Assertion** node was run successfully, its icon in the project tree looks like that:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-M4OT249rvL-_C8vTTtA%2F-M4OUJgFbOIDapcawe0x%2Fimage.png?alt=media\&token=57f77d49-6e4d-4fa8-a423-1bd7c3c74e1b)

If an **Assertion** node was run unsuccessfully, its icon in the project tree changes to this:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-M4OT249rvL-_C8vTTtA%2F-M4OULyt2yu1CEMyRZBL%2Fimage.png?alt=media\&token=3c7142b5-1516-41dd-bd6f-3444a951bbe8)

There are several actions you can do with this node type:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-M4OT249rvL-_C8vTTtA%2F-M4OUOfsA0JDHoaWVS2h%2Fimage.png?alt=media\&token=801bc255-9336-4df6-917e-8048b60d4d64)

* **Remove node.** Use it to delete the node.
* **Run.** Run the node.
* **Show in explorer.** Open the folder with the node in the file manager.

The **Assertion** node tab has the following interface:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-M4OT249rvL-_C8vTTtA%2F-M4OUSpJR6DqVlUXyNuj%2Fimage.png?alt=media\&token=eff860f2-e3b4-41c0-b9d6-641eaa4e7216)

In the screenshot you can see the following parts of the interface:

1. The control panel
2. The settings panel for the chosen **assertion**
3. The list of all **assertions**

You can see the following buttons on the control panel:

* **RUN** - runs all assertions in the list.
* **FIX ERRORS** - fixes assertions errors if possible The button is available if there are some errors in the assertions. The **error fixing** algorithms are described for each assertion type separately.
* **DISABLE ERRORS** - disables the failed assertions. They won't be included in further runs. The button is available if there are some errors in the assertions.
* **+ ADD ASSERTION** - adds an assertion to the list.

The assertions list is right under the control panel. Each element of the list has the looks like this:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-M4OT249rvL-_C8vTTtA%2F-M4OUadm0yLAC_9Q_EVX%2Fimage.png?alt=media\&token=598cb0b1-3ed1-44b4-836f-e56c4f83a5df)

In the screenshot you can see the following parts of the interface:

1. Status highlighting. Its status is gray, if the **assertion** hasn't been run yet, red - if the run failed, green - if it was successful.
2. Drag-and-drop point.
3. The **assertion** type icon.
4. The **assertion** text representation.
5. Show/hide error details button.
6. Delete the **assertion**.
7. Disable the **assertion**. It won't be included in the further runs.
8. Run the **assertion**.
9. Fix the **assertion**.

{% hint style="info" %}
Note that the controls 6, 7, 8, and 9 appear when you hover the mouse over the assertion.
{% endhint %}

The interface of the settings panel depends on the chosen assertion type. We'll discuss all of them in the next sections.

Each **Assertion** has a `Name` field to specify what is being tested. If the field is empty, a default description is set according to the assertion properties.

### Assertion Template

Starting from the 1.0.0 version, an Assertion node may be created without any connection to a RequestStep node, as any other node in the project. It can be used for checking results of multiple requests or as a template where a set of standard assertions is defined.

As compared to an Assertion which is related to a RequestStep node and inherits its context (variables, dynamic variables, response), an assertion template inherits the context of the node which refers to this template while running. The context is saved to the `$host` built-in variable and is initialized only after running.

Running an assertion template separately doesn't make any sense, since there is no context available and all assertions are going to be failed. For this reason, we eliminate this possibility in TestMace, and nodes of this type will be skipped while running scenarios.

For debugging purposes, in the template interface you'll see the name of the node that was run last and sent its context to the template. In this case, values highlighting and autocomplete for every expression containing the `$host` variable will be available.

To learn more about using independent Assertion nodes as templates and to see some examples of that, please refer to the **Link Assertion** section.

### File Representation

An **Assertion** node is stored in the \<nodename>.yml file, where \<nodename> is the **Assertion** node name. The file has the following format:

```javascript
{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of Assertion node",
      "const": "Assertion",
      "type": "string"
    },
    "assertions": {
      "description": "List of assertions",
      "type": "array",
      "items": {
        "$ref": "#/definitions/AbstractAssertion"
      },
      "default": []
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "assertions",
    "children",
    "name",
    "type",
    "variables"
  ],
  "definitions": {
    "AbstractAssertion": {
      "oneOf": [
        {
          "$ref": "#/definitions/CompareAssertion"
        },
        {
          "$ref": "#/definitions/ContainsAssertion"
        },
        {
          "$ref": "#/definitions/XPathAssertion"
        },
        {
          "$ref": "#/definitions/ScriptAssertion"
        }
      ]
    },
    "CompareAssertion": {
      "type": "object",
      "properties": {
        "type": {
          "description": "Type of Compare assertion",
          "const": "compare",
          "type": "string"
        },
        "actualValue": {
          "description": "Actual value",
          "type": "string",
          "default": "${$response.body}"
        },
        "operator": {
          "$ref": "#/definitions/CompareOperator",
          "description": "Operator",
          "default": "equal"
        },
        "expectedValue": {
          "description": "Expected value",
          "type": "string"
        },
        "disabled": {
          "type": "boolean",
          "default": false
        }
      },
      "required": [
        "actualValue",
        "disabled",
        "expectedValue",
        "operator",
        "type"
      ]
    },
    "CompareOperator": {
      "enum": [
        "equal",
        "greater",
        "greater or equal",
        "less",
        "less or equal",
        "not equal"
      ],
      "type": "string"
    },
    "ContainsAssertion": {
      "type": "object",
      "properties": {
        "type": {
          "description": "Type of Contains assertion",
          "const": "contains",
          "type": "string"
        },
        "text": {
          "description": "Text to be searched",
          "type": "string",
          "default": "${$response.body}"
        },
        "value": {
          "description": "Value for search in text",
          "type": "string"
        },
        "disabled": {
          "type": "boolean",
          "default": false
        }
      },
      "required": [
        "disabled",
        "text",
        "type",
        "value"
      ]
    },
    "XPathAssertion": {
      "type": "object",
      "properties": {
        "type": {
          "description": "Type of Xpath assertion",
          "const": "xpath",
          "type": "string"
        },
        "text": {
          "description": "Text to be searched",
          "type": "string",
          "default": "${$response.body}"
        },
        "path": {
          "description": "XPath selector",
          "type": "string"
        },
        "expectedValue": {
          "description": "Expected value",
          "type": "string"
        },
        "disabled": {
          "type": "boolean",
          "default": false
        }
      },
      "required": [
        "disabled",
        "expectedValue",
        "path",
        "text",
        "type"
      ]
    },
    "ScriptAssertion": {
      "type": "object",
      "properties": {
        "type": {
          "description": "Type of Script assertion",
          "const": "script",
          "type": "string"
        },
        "script": {
          "description": "Assertion script",
          "type": "string",
          "default": "`function test(assertion, variables) {\n  // It should return true if test is passed\n  // return true;\n}`"
        },
        "disabled": {
          "type": "boolean",
          "default": false
        }
      },
      "required": [
        "disabled",
        "script",
        "type"
      ]
    },
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
```


# Compare

The **Compare assertion** interface is as follows:

![](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-M2TJOc1QgQA4tU8BtyI%2F-M2TMFw7YXNfGyuQR4Pa%2FTestMace%202020-03-15%2017.23.04.png?alt=media\&token=722ff142-b16c-45a8-874f-8e21d55dbae8)

A **Compare assertion** is used for comparing 2 values. In the `Expected value type` field you should specify the type of values you want to compare (strings, numbers or JSON objects).

A set of available operations that you can select in the `Operator` field depends on the type you chose. For strings and numbers, you can use the following operators:

* **equal** - checks if the values are equal
* **not equal** - checks if the values are not equal
* **greater** - checks if one value is greater than another one
* **greater or equal** - checks if one value is greater than another or equal to it
* **less** - checks if one value is less than another one
* **less or equal** - checks if one value is less than another or equal to it

{% hint style="warning" %}
It is important to note that the result of comparing two values that can be represented both by strings and numbers might seem rather surprising. For example, let's assert that the number 100 is greater than 2. The assertion will be successful. However if you compare the same string values ("100" is greater than "2"), the assertion will fail. That's because strings are compared symbol-by-symbol in alphabetical order.
{% endhint %}

The following operators are available for comparing objects:

* **equal** - checks if two object structures are equal
* **not equal** - checks if the object structures are different
* **is subset of** - checks if one structure is a subset of another structure
* **is superset of** - checks if one structure is a superset of another structure

{% hint style="success" %}
Examples of the **is subset of** and **is superset of** operators

Let's say a current value is:

```typescript
[
    {
        "name": "Mike"
    }
]
```

And an expected value is:

```typescript
[
    { 
        "id": 0,
        "name": "John",
        "name": 24
    },
    {
        "id": 1,
        "name": "Mike",
        "age": 28
    }
]
```

In this example the current value is a subset of the expected value. Conversely, the expected value is a superset of the current value.

The structures are compared recursively, every nesting level is checked. In the above example the fact that at the top level both values are arrays is verified first, then each element of the current array is compared to each element of the expected one until the first match is encountered. The objects are compared by matching string keys and their values.

Note that while comparing array elements the algorithm is looking for a subsequence rather than a single element. For example, the `[2,4]` array is a subset of the `[1,2,3,4]`, but the `[4, 2]` array is not.
{% endhint %}

### Error Fixing

The error fixing algorithm depends on the comparator type.

* **equal** - the current value is assigned to the expected one
* **not equal** - the comparator type changes to **equal**
* **greater** - the comparator type changes to **greater or equal** and the current value is assigned to the expected one
* **greater or equal** - the current value is assigned to the expected one
* **less** - the comparator type changes to **less or equal** and the current value is assigned to the expected one
* **less or equal** - the current value is assigned to the expected one

### File Representation

The **Assertion** is of the `compare` type in the file. The type description can be found in the [Assertion file representation documentation](https://docs.testmace.com/node-types/assertion-node#file-representation) in the `#/definitions/CompareAssertion` definition.


# In range

{% hint style="warning" %}
The feature is available for [paid TestMace subscribers](https://testmace.com/pricing/) only.
{% endhint %}

The **In range assertion** interface is as follows:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-M4O7N4o8xDbwHs_cI5h%2F-M4O7lT6do2HwiYRhNVu%2Fimage.png?alt=media\&token=075dbaad-a422-4cb6-9e1b-9d40df3f2331)

This assertion type is used to check if a current value is within a range. In the `Expected value type` field you should specify the type of values you want to compare (strings or numbers).

In the field labeled as `Expected value` you can see the fields for defining upper and lower bounds of the range and two buttons to specify whether the bounds should be inclusive or not.

For instance, all possible ranges for the values 0 and 10 are:

* \[ 0 \~ 10 ] - a range from 0 to 10, both bounds are inclusive.
* \[ 0 \~ 10 ) - a range from 0 to 10, the upper bound is exclusive.
* ( 0 \~ 10 ] - a range from 0 to 10, the lower bound is exclusive.
* ( 0 \~ 10 ) - a range from 0 to 10, both bounds are exclusive.

{% hint style="info" %}
Note that you can use strings as compared values. In this case, comparison with range bounds is performed symbol by symbol in alphabetical order.
{% endhint %}

The `Use negative statement` flag is used for getting the opposite result, i.e. the current value shouldn't exist in the specified range.

### Error Fixing

The error fixing algorithm depends on the `Use negative statement` flag's state:

* false - sets a \[current \~ current] range
* true - sets a (current \~ current) range


# One of set

{% hint style="warning" %}
The feature is available for [paid TestMace subscribers](https://testmace.com/pricing/) only.
{% endhint %}

The **One of set assertion** interface is as follows:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-M4OIW9UBqfD2Ba8hxxb%2F-M4OJnBF8LwjQ12YgXnK%2Fimage.png?alt=media\&token=a7f6b2b8-fcbd-445e-b327-839ba25bbaa9)

This assertion type is used to ensure if the current value equals to one value from the specified set. In the `Expected value type` field you should specify the type of values you want to compare (strings, numbers or JSON objects).

In the `Expected value` field you should set a list of pissible expected values

The `Use negative statement` flag is used for getting the opposite result, i.e. the current value shouldn't exist in the specified set.

### Error Fixing

The error fixing algorithm depends on the `Use negative statement` flag's state:

* false - the current value is added to the list
* true - the current value is removed from the list


# Contains

A **Contains assertion** can be used to check whether a substring is present in a string or whether a string equals to a regular expression.

This **Assertion** interface is as follows:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-M4OKquMmkk-bwr0AM-W%2F-M4OLbmJPvI4d7bO1Rva%2Fimage.png?alt=media\&token=e6a0a45d-1fe0-466c-8dd4-43d3575f0f60)

The following **assertion** fields are shown in the screenshot:

* **Text** - the text to search the value in
* **Value** - the value that needs to be found
* The **Use negative statement** flag means using the opposite assertion result.
* The **Use value as Regular Expression** flag means that the value will be used as a regular expression template that the current value should match.

### Error Fixing

This **Assertion** doesn't have any error fixing algorithm.

### File Representation

The **assertion** is of the `contains` type in the file. The type description can be found in [Assertion file representation](https://docs.testmace.com/node-types/assertion-node#file-representation) documentation in the `#/definitions/CompareAssertion` definition.


# XPath

**XPath assertion** allows to verify the response message with an XPath expression match.

This assertion interface is as follows:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgrKP3zFd8Pe9rytuu8%2F-LgrOGxjrh9Cw-cjXRFN%2F10.png?alt=media\&token=9e0c10ae-4a20-48ad-b946-23c284ba3b7d)

This assertion interface is as follows:

* **Text** - the text to find the value in;
* **Path** - an XPath selector;
* **Expected value** - the value that should be matched.
* The **Use negative statement** flag means using the opposite assertion result, i.e. there should be no values for the specified selector in the text.

### Error Fixing

To get rid of an error, the XPath selector value is assigned to the expected value.

### File Representation

The assertion has the `xpath` type in the file. The type description can be found in [Assertion file representation](https://docs.testmace.com/node-types/assertion-node#file-representation) Assertion file representation documentation in the `#/definitions/ScriptAssertion` definition.


# JSONPath

{% hint style="warning" %}
The feature is available for [paid TestMace subscribers](https://testmace.com/pricing/) only.
{% endhint %}

A **JSONPath assertion** allows to check a value using a JSONPath selector.

This **assertion** interface is as follows:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-M4ONG6-KMi4Z0M3D3Jb%2F-M4ONblhlQI5NIT-edAd%2Fimage.png?alt=media\&token=211e1219-3de8-407a-82d7-78130dff512f)

The following **JSONPath assertion** fields are shown in the screenshot:

* **Text** - the text to search the value in
* **Path** - a JSONPath selector
* **Expected value** - an expected value for this selector. The value should be represented as an array of expected elements.
* The **Use negative statement** flag means using the opposite assertion result, i.e. there should be no values for the specified selector in the text.

### Error Fixing

To get rid of an error, the XPath selector value is assigned to the expected value.


# Script

A **Script assertion** allows to write a JavaScript script to check an assertion. The script itself is a function named `test` which takes an assertion object and an object with variables (in a key-value format) as arguments. If the function returns `true`, the assertion was successful. If it returns `false` or raises an exception, the **Script assertion** failed.

{% hint style="warning" %}
**Deprecated syntax:**

We strongly recommend you use a new way of writing test scripts instead of defining the `test` function. You'll have an opportunity to apply the API to all entities in your project, use built-in helper libraries (e.g. `lodash` and `chai`), and define a detailed description for every test script, which will be shown in the error list and in the console.

Your tests written via the `test` function will still be working, but in future major releases this syntax won't be supported.
{% endhint %}

### New API

**Script assertions** support the same API as **Script** nodes do.

{% content-ref url="/pages/-LgrLaD6CE1ahNm\_mV9h" %}
[Script](/node-types/assertion-node/script)
{% endcontent-ref %}

If while running a script an exception of any kind was raised, this **Script assertion** run is considered to be unsuccessful and successful otherwise.

### User Interface

This **Assertion** interface is similar to the one of a **Script** node:

![](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-M2Th-pSAXk8IEvHySm4%2F-M2ThBMbiJ2D7aKV15Di%2FTestMace%202020-03-15%2018.58.58.png?alt=media\&token=a133ee76-b15e-4e6a-a6bd-38b454c17fbd)

The **Name** field allows to set a description for test of this kind.

### Error Fixing

This **Assertion** doesn't have any error fixing algorithm.

### File Representation

The **assertion** is of the `script` type in the file. The type description can be found in the [Assertion file representation](https://docs.testmace.com/node-types/assertion-node#file-representation) documentation in the `#/definitions/CompareAssertion` definition.


# Link

{% hint style="warning" %}
The feature is available for [paid TestMace subscribers](https://testmace.com/pricing/) only.
{% endhint %}

A **Link assertion** allows to create a link to another template assertion node in the project tree and run it with this node's context setting variables as parameters. This is how you can put some amount of standard tests for your requests into one node and reuse it later.

This **assertion** interface is as follows:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-M4hx6p6F3GMaeePCJwX%2F-M4i57b2GZk6WLOpfra3%2Fimage.png?alt=media\&token=d5dad94d-7686-41c6-bda7-9cd884bda625)

First, you'll need to create an [Assertion node](https://docs.testmace.com/node-types/assertion-node) in your project or folder and add some assertions there. For example:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-M4hx6p6F3GMaeePCJwX%2F-M4i5B8cVwG9Le5YlNKD%2Fimage.png?alt=media\&token=a2e65fcf-0477-45a3-b321-b7c4fbf7a28e)

{% hint style="info" %}
Note that assertion expressions contain the `$host` variable - the context of the node from which the Assertion node will be run via a link, i.e. an object that contains variables, dynamic variables, and response.
{% endhint %}

Second, add a link assertion and click the Choose Assertion button to choose the node you've just created. You can create a variable right in an assertion and then use it in template expressions, for instance `${$host.my_var}`.

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-M4hx6p6F3GMaeePCJwX%2F-M4i5H68qkJLolNeYjcQ%2Fimage.png?alt=media\&token=1885021e-836c-4afc-aab9-df49e44032f6)

If you send a request, you'll notice that the template assertion node's state is changed. If you open this node, you'll see the last node that passed the context to it while the run. After this, variables highlighting and autocomplete will be available.

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-M4hx6p6F3GMaeePCJwX%2F-M4i5JiIyH--IvbH2I3m%2Fimage.png?alt=media\&token=ecff7971-6523-41ef-84af-d6bd52baf009)

### Error Fixing

This **Assertion** doesn't have any error fixing algorithm.


# Link

A Link node allows to reuse another RequestStep (including Assertion) and Folder nodes.

## How It Works&#x20;

A **Link** node allows to override the values of the running node variables. A **Link** node runs another node, passing the variables set by a user to it. After the run, dynamic variables of the node are set as dynamic variables of the **Link** node parent group. Thus the result of the run can be seen from any of the neighboring **Link** node.

#### You can refer to:

* a [RequestStep](/node-types/request-step) node;
* a [Folder](/node-types/folder) node.

#### You can NOT refer to:

* Another **Link** node (including self);
* Any child of a **Link** node (since it'll cause an endless loop).

{% hint style="info" %}
A Link node allows to override the variables values of the parent node.
{% endhint %}

{% hint style="warning" %}
If you delete the link a Link node refers to, the Link node wouldn't run until you provide a proper link.‌
{% endhint %}

## Parent Node

Create a parent node you need to refer to and create all necessary [static variables](/variables/user-variables/static-variables), for example `postID`. You can leave value fields empty.

![Creating variables of the parent node](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh4Cqr7blB3FIHuJKO_%2F-Lh4ImcwyK7QPl4D0WmO%2Flink-parent-var.jpg?alt=media\&token=e7b7f466-1fb2-4924-ad13-21d465e236ae)

## Link Node

Create a **Link** node and specify its parent, and you'll see all the variables you created for the parent earlier. You can use any variables or a static value as an overriden value.

![Creating a Link node and specifying its parent](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh4SwfExBEdfIX5P1HC%2F-Lh4VLCDdb58k9EvDvPd%2Flink-node-ex2.gif?alt=media\&token=98246c48-371b-43f2-9e7c-cbcb3abcce80)

## Scenario Example

Let's see an example of running a [RequestStep](/node-types/request-step) node as a **Link** node to delete a record.

### Creating a parent node

1. Create a new [RequestStep](/node-types/request-step) node named deletePost.
2. Request type - DELETE.
3. Use the URL:[ https://testmace-stage.herokuapp.com/posts/${id}](< https://testmace-stage.herokuapp.com/posts/${id}>)
4. Create for this node a [static ](/variables/user-variables/static-variables)`id` variable without specifying its value.

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFNVvYEIb0YxX8C1eJ%2F-LhFNaKST0F0_1ymsVjY%2Flin-sc-1.gif?alt=media\&token=7e93b682-f174-4ea0-b928-3714bd0fd476)

### **Creating a scenario**

* Create a [Folder](/node-types/folder) node named **scenario**
* Add a [RequestStep](/node-types/request-step) node named **createPost** to the scenario:
  * Request type: POST;
  * URL: [https://testmace-stage.herokuapp.com/posts/](< https://testmace-stage.herokuapp.com/posts/${id}>)
  * Body: JSON request `{"title":"will delete with link node"}`
  * Run the request and the `id` of the created record to the [dynamic variable ](/variables/user-variables/dynamic-variables)`postid` of the **Scenario** node.

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFNVvYEIb0YxX8C1eJ%2F-LhFQ274uAmdp39J7uz7%2Flin-sc-2.gif?alt=media\&token=6b7597c8-32dc-4598-8e42-94ac76ad3cbe)

* Create a **Link** node named **deleteLink**.
  * Specify the **project/deletePost** node as a parent.
  * &#x20;Set the`${$dynamicVar.postId}` as an overridden value for the `id` variable of the parent **deletePost** node.
* Create a [RequestStep](/node-types/request-step) node **checkIfExists** to check if the record was deleted:
  * Request type: GET;
  * URL: <https://testmace-stage.herokuapp.com/posts/${$dynamicVar.postId}>
  * 404 is an expected server response.

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFNVvYEIb0YxX8C1eJ%2F-LhFRaKVohkgWjndBb3E%2Flin-sc-3.gif?alt=media\&token=d61d5fe1-334b-429c-b7d0-3aa78766f533)

## Import the Project [by URL](/other-features/import/shared)

{% file src="/files/-LglQkEld\_hqNoia9IpW" %}

### File Representation

A **Link** node is a folder with the node name, containing the index.yml file with the following format:

```javascript
{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of Link node",
      "const": "Link",
      "type": "string"
    },
    "linkedNode": {
      "$ref": "#/definitions/NodeReference",
      "description": "Link to node"
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "children",
    "linkedNode",
    "name",
    "type",
    "variables"
  ],
  "definitions": {
    "NodeReference": {
      "type": "object",
      "properties": {
        "refNodePath": {
          "description": "Absolute path to node",
          "type": "string"
        },
        "type": {
          "description": "Marker of reference entity",
          "const": "reference",
          "type": "string",
          "default": "reference"
        }
      },
      "required": [
        "refNodePath",
        "type"
      ]
    },
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
```


# API description

TestMace provides powerful features for API description, including import from Swagger 2.0/ Openapi 3.0. The following nodes are responsible for these features:

* [ApiRootFolder](/node-types/api-description/apirootfolder) - a root node for API description;
* [ApiFolder](/node-types/api-description/apifolder) -  a node for grouping another API nodes;
* [ApiRoute](/node-types/api-description/apiroute) - a node for describing a specific endpoint.

You'll learn more about each of these nodes in the next sections.


# ApiRootFolder

**ApiRootFolder** - a root node of the API description subnode. Just like the [Project](/node-types/project) node, it is a root element, and in the subnode there can be only one element of this type. It is functionally identical to an [ApiFolder](/node-types/api-description/apifolder) node.

You can create this node by:

* Adding it from the [Project](/node-types/project) node context menu;
* Using import from API description format.

### File Representation

A **ApiRootFolder** node is a folder with the node name, containing the index.yml file with the following format:

```javascript
{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of ApiRootFolder node",
      "const": "ApiRootFolder",
      "type": "string"
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "children",
    "name",
    "type",
    "variables"
  ],
  "definitions": {
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
```


# ApiFolder

An **ApiFolder** is identical to a Follder node and is used to group nodes of another types (ApiRoute in this case).

There are two ways to create an **ApiFolder** node:

* Adding it from the [ApiRootFolder](/node-types/api-description/apirootfolder) node context menu;
* Using import from API description format.

This is how an ApiFolder node looks like in the project tree:

![An ApiFolder node in the project tree](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgsCA8b3GaVRHvOyk3b%2F-LgsCm8cE2tYZ-qYFFWc%2F1.png?alt=media\&token=9a7b2f6f-24f1-455c-abc7-e0ce036e7172)

An **ApiFolder** node has the following context menu:

![The ApiFolder context menu](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LlffkWjAr-L0Akq3f5g%2F-LlfflYo-YCjuMYdtA1b%2Fimage.png?alt=media\&token=d6e70623-09b6-49ce-8360-f1f311509720)

* **Add node.** Click on it to add a child node. You can choose a node type in the submenu.
* **Rename.** Change the node name.
* **Duplicate.** Make a copy of the node. The new node will be named NodeName \[Copy \[number]].
* **Remove node.** Use it to delete the node.
* **Show in explorer.** Open the folder with the node in the file manager.

‌The **ApiFolder** node tab has the following interface:

![The ApiFolder node tab interface](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgsCA8b3GaVRHvOyk3b%2F-LgsGJ-A7inEue6zqfAI%2F3.png?alt=media\&token=702d4b06-f2b1-4cc6-9b8b-da4887fee8c4)

In the screenshot you can see the following parts of the tab:

* The [user-defined variables](/variables/user-variables) managing dialog;
* Child nodes list.

### File Representation

An **ApiFolder** node is a folder with the node name, containing the index.yml file with the following format:

```javascript
{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of ApiFolder node",
      "const": "ApiFolder",
      "type": "string"
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "children",
    "name",
    "type",
    "variables"
  ],
  "definitions": {
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
```


# ApiRoute

The node is used for the specific endpoint description. Its interface is smilar to the one of a [RequestStep](/node-types/request-step) node. No surprises, in both cases we deal with HTTP requests.

The main features of an ApiRoute node are:

* Describing request HTTP headers, query parameters, body parameters and response HTTP codes, HTTP headers and body parameters;
* Using types for describing every header, query parameter, and body parameter. Supported types: `string`, `number`, `integer`, `boolean`, `array`, and `object`;
* Describing every entity;
* Describing a number of request body parameters (based on content-type);
* Describing a number of possible responses;
* Creating a request out of the description;
* Autocompletion of URLs, HTTP headers, query and body parameters in [RequestStep](/node-types/request-step) nodes.

## Interface Overview

To create an **ApiRoute** node right-click in the [ApiFolder](/node-types/api-description/apifolder) node and choose **Add node** -> **ApiRoute.**

### An ApiRoute node in the project tree

This is how it looks like in the project tree:

![An ApiRoute node in the project tree](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh-8_-iIrkPlQPxdRGh%2F-Lh0BtMevPi0x9AEv_1R%2F1.png?alt=media\&token=5d8a9b58-fcab-4508-be4b-01e09b6ea59d)

The icon of these nodes is the HTTP method name. An **ApiRoute** node has the following context menu:

![The ApiRoute context menu](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh-8_-iIrkPlQPxdRGh%2F-Lh0CEFd52BzWxfzlx-f%2F2.png?alt=media\&token=e1bec327-9045-4e8a-975c-359648e846cf)

* **Rename.** Change the node name.
* **Duplicate.** Make a copy of the node. The new node will be named NodeName \[Copy \[number]].
* **Remove node.** Use it to delete the node.
* **Show in explorer.** Open the folder with the node in the file manager.

### The **ApiRoute** Tab Interface

The **ApiRoute** node tab looks like this:

![The ApiRoute tab](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh-8_-iIrkPlQPxdRGh%2F-Lh0DZRq-N6KeKpQN0Mg%2F3.png?alt=media\&token=2a87fbb8-10f7-4e67-b495-756c654835bc)

#### General Request Parameters

Look a little closer at the top part of the tab:

![The top part of the ApiRoute node tab](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh-8_-iIrkPlQPxdRGh%2F-Lh0GJ0lG6uWS8DImhSP%2F4.png?alt=media\&token=666d82ea-0637-40bb-b3e1-582ef7b8013e)

In the screenshot you can see the following parts of the interface.

1. HTTP method. The list of available methods is identical to the one of [RequestStep](/node-types/request-step) nodes.
2. URL with the [variables mechanism support](/variables/variables).
3. The [Variables dialog](/variables/user-variables) button.
4. The button to create a request out of the current API description.
5. The request description (text).

#### Request parameters description area

At the bottom left of the screen you can see the request description area. There are 3 tabs - **Headers**, **Query parameters**, and **Body**, that you can use to edit HTTP headers, query parameters, and request body parameters accordingly.

Let's look at the **Headers** tab. You can see that it is a table with the following fields:

* Header name;
* Header value type (as described above);
* Description.

All possible standard operations are supported.

The **Query Parameters** tab is used for editing query parameters, and it is functually identical to the **Headers** tab.

As mentioned before, in an **ApiRoute** node you can describe several bodies for a single request. For example, a single endpoint can take the data both with the `application/json` and `application/xml` `Content-Type`. There are several different content-type tabs on the **Body** tab:

![The Body tab of the request description interface](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh-8_-iIrkPlQPxdRGh%2F-Lh0NcNadYlwdkCel30V%2F5.png?alt=media\&token=c8398894-1259-49f9-9eb9-5f1311d5f353)

In the screenshot you can see the following parts of the interface:

1. The button that edits the current `content-type` value. If you click on it, you'll see a text box instead, where you can enter your `content-type` value.
2. Delete the request body button.
3. Add the request body button.
4. The current `content-type` of the node.
5. The request body editing area.

The request body editing area changes according to the `content-type` value:  if the `content-type` is`application/x-www-form-urlencoded` or`multipart/form-data`, then the editing area looks like a table (like in the **Headers** tab), otherwise it has the text representation like in the screenshot above. The description format is [OpenAPI](https://swagger.io/specification/#requestBodyObject) in this case.

#### Request parameters description area

There is a response editing panel at the bottom right of the **ApiRoute** tab interface. As mentioned before, TestMace allows you to describe several responses for a single endpoint. Look at the interface:

![The server response editing area](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh-8_-iIrkPlQPxdRGh%2F-Lh0aybnLxCtZilFBOpr%2F6.png?alt=media\&token=af6ab711-6523-4a2c-b2f2-e70f53c44538)

There is a separate tab for each response. On these tabs you can edit response codes, descriptions, and even response HTTP headers and bodies. The interface is similar to ones in the request area.

### Integration with RequestStep nodes

TestMace provides integration with **ApiRoute** nodes in **RequestStep** nodes. The integration is implemented in autocompletion of URLs, HTTP headers, query parameters, **RequestStep** nodes' request body parameters. Autocompletion works for all **ApiRoute** nodes URLs, but for other parameters the following algorithm is applied:

* Take the **RequestStep** node method and URL;
* Find all **ApiRoute** nodes with this method and URL;
* Run the specified parameter (e. g. an HTTP header) search among the **ApiRoute** nodes.

## File Representation

An **ApiRoute** node is a folder with the node name, containing the index.yml file with the following format:

```javascript
{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of ApiRoute node",
      "const": "ApiRoute",
      "type": "string"
    },
    "url": {
      "type": "string",
      "default": ""
    },
    "method": {
      "$ref": "#/definitions/RequestMethod"
    },
    "description": {
      "type": "string",
      "default": ""
    },
    "requests": {
      "$ref": "#/definitions/ApiRequests",
      "description": "List of requests"
    },
    "responses": {
      "description": "List of responses",
      "type": "array",
      "items": {
        "$ref": "#/definitions/ResponseParameters"
      },
      "default": []
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "children",
    "description",
    "method",
    "name",
    "requests",
    "responses",
    "type",
    "url",
    "variables"
  ],
  "definitions": {
    "RequestMethod": {
      "enum": [
        "DELETE",
        "GET",
        "OPTIONS",
        "PATCH",
        "POST",
        "PUT"
      ],
      "type": "string"
    },
    "ApiRequests": {
      "type": "object",
      "properties": {
        "queryParameters": {
          "description": "List of query parameters",
          "type": "array",
          "items": {
            "$ref": "#/definitions/QueryParameter"
          }
        },
        "headers": {
          "description": "List of headers",
          "type": "array",
          "items": {
            "$ref": "#/definitions/QueryParameter"
          }
        },
        "cookies": {
          "description": "List of cookies",
          "type": "array",
          "items": {
            "$ref": "#/definitions/QueryParameter"
          }
        },
        "bodies": {
          "description": "List of bodies",
          "type": "array",
          "items": {
            "$ref": "#/definitions/RequestParameters"
          },
          "default": []
        }
      },
      "required": [
        "bodies",
        "cookies",
        "headers",
        "queryParameters"
      ]
    },
    "QueryParameter": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "type": {
          "enum": [
            "array",
            "boolean",
            "integer",
            "number",
            "object",
            "string"
          ],
          "type": "string"
        },
        "description": {
          "type": "string"
        }
      },
      "required": [
        "name",
        "type"
      ]
    },
    "RequestParameters": {
      "type": "object",
      "properties": {
        "contentType": {
          "type": "string"
        },
        "schema": {
          "anyOf": [
            {
              "$ref": "#/definitions/SchemaRef"
            },
            {
              "$ref": "#/definitions/OneOf"
            },
            {
              "$ref": "#/definitions/AllOf"
            },
            {
              "$ref": "#/definitions/AnyOf"
            },
            {
              "$ref": "#/definitions/ObjectMember"
            },
            {
              "$ref": "#/definitions/ArrayMember"
            },
            {
              "$ref": "#/definitions/ScalarMember"
            }
          ]
        }
      },
      "required": [
        "contentType",
        "schema"
      ]
    },
    "SchemaRef": {
      "type": "object",
      "properties": {
        "$ref": {
          "type": "string"
        }
      },
      "required": [
        "$ref"
      ]
    },
    "OneOf": {
      "type": "object",
      "properties": {
        "oneOf": {
          "type": "array",
          "items": {
            "anyOf": [
              {
                "$ref": "#/definitions/SchemaRef"
              },
              {
                "$ref": "#/definitions/OneOf"
              },
              {
                "$ref": "#/definitions/AllOf"
              },
              {
                "$ref": "#/definitions/AnyOf"
              },
              {
                "$ref": "#/definitions/ObjectMember"
              },
              {
                "$ref": "#/definitions/ArrayMember"
              },
              {
                "$ref": "#/definitions/ScalarMember"
              }
            ]
          }
        }
      },
      "required": [
        "oneOf"
      ]
    },
    "AllOf": {
      "type": "object",
      "properties": {
        "allOf": {
          "type": "array",
          "items": {
            "anyOf": [
              {
                "$ref": "#/definitions/SchemaRef"
              },
              {
                "$ref": "#/definitions/OneOf"
              },
              {
                "$ref": "#/definitions/AllOf"
              },
              {
                "$ref": "#/definitions/AnyOf"
              },
              {
                "$ref": "#/definitions/ObjectMember"
              },
              {
                "$ref": "#/definitions/ArrayMember"
              },
              {
                "$ref": "#/definitions/ScalarMember"
              }
            ]
          }
        }
      },
      "required": [
        "allOf"
      ]
    },
    "AnyOf": {
      "type": "object",
      "properties": {
        "anyOf": {
          "type": "array",
          "items": {
            "anyOf": [
              {
                "$ref": "#/definitions/SchemaRef"
              },
              {
                "$ref": "#/definitions/OneOf"
              },
              {
                "$ref": "#/definitions/AllOf"
              },
              {
                "$ref": "#/definitions/AnyOf"
              },
              {
                "$ref": "#/definitions/ObjectMember"
              },
              {
                "$ref": "#/definitions/ArrayMember"
              },
              {
                "$ref": "#/definitions/ScalarMember"
              }
            ]
          }
        }
      },
      "required": [
        "anyOf"
      ]
    },
    "ObjectMember": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "object"
          ]
        },
        "properties": {
          "$ref": "#/definitions/SchemaMember"
        },
        "required": {
          "type": "boolean"
        },
        "additionalProperties": {
          "$ref": "#/definitions/ScalarMember"
        },
        "description": {
          "type": "string"
        }
      },
      "required": [
        "type"
      ]
    },
    "SchemaMember": {
      "type": "object",
      "additionalProperties": {
        "anyOf": [
          {
            "$ref": "#/definitions/SchemaRef"
          },
          {
            "$ref": "#/definitions/OneOf"
          },
          {
            "$ref": "#/definitions/AllOf"
          },
          {
            "$ref": "#/definitions/AnyOf"
          },
          {
            "$ref": "#/definitions/ObjectMember"
          },
          {
            "$ref": "#/definitions/ArrayMember"
          },
          {
            "$ref": "#/definitions/ScalarMember"
          }
        ]
      }
    },
    "ArrayMember": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "array"
          ]
        },
        "items": {
          "anyOf": [
            {
              "$ref": "#/definitions/SchemaRef"
            },
            {
              "$ref": "#/definitions/OneOf"
            },
            {
              "$ref": "#/definitions/AllOf"
            },
            {
              "$ref": "#/definitions/AnyOf"
            },
            {
              "$ref": "#/definitions/ObjectMember"
            },
            {
              "$ref": "#/definitions/ArrayMember"
            },
            {
              "$ref": "#/definitions/ScalarMember"
            }
          ]
        },
        "description": {
          "type": "string"
        }
      },
      "required": [
        "items",
        "type"
      ]
    },
    "ScalarMember": {
      "type": "object",
      "properties": {
        "type": {
          "$ref": "#/definitions/ScalarSchemaType"
        },
        "description": {
          "type": "string"
        }
      },
      "required": [
        "type"
      ]
    },
    "ScalarSchemaType": {
      "enum": [
        "boolean",
        "integer",
        "number",
        "string"
      ],
      "type": "string"
    },
    "ResponseParameters": {
      "type": "object",
      "properties": {
        "code": {
          "description": "Http-code (e.g. 200, 404)",
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "headers": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/QueryParameter"
          }
        },
        "content": {
          "$ref": "#/definitions/RequestParameters",
          "description": "Response body"
        }
      },
      "required": [
        "code",
        "content"
      ]
    },
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
```


# API Description Import

TestMace allows not only to create API description manually, but also to import an existing documentation from Swagger 2.0 and OpenAPI 3.0.

&#x20;You can import API description right from the + context menu by choosing **Import -> Swagger** (there is the same menu in the Scratches area as well):

![The project context menu](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh0oCq6Wm4ISZdLGZoy%2F-Lh0pQi2448lA3Sg_HP2%2F7.png?alt=media\&token=9dc8386d-779f-47c7-abd3-bdbad3aae3c4)

You'll see the next dialog:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh0oCq6Wm4ISZdLGZoy%2F-Lh0pb1rAlqZWHqkw6yb%2F8.png?alt=media\&token=99d91af3-8945-4f38-80e6-99f73c840c1f)

As you can see, both import from file and downloading API from the remote server at a specific URL are supported. The imported description is added to the project tree after you click on the **OK** button.

### API Description Update

You can also update an existing API description. To do that, choose **Update api** from the [ApiRootFolder](/node-types/api-description/apirootfolder) context menu. You'll see a dialog similar to the import API dialog. All changes you make in API description will be canceled after the update.


# Broken

When you open the project, you might see that some nodes can't be loaded. For instance, some merge conflicts can cause that. At this point, all possible nodes are loaded to the project, but when you open it, the following warning is shown:

![The warning about nodes loading failure](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh45m0ccQGFTGtebHna%2F-Lh47_hFU3Hzq6tvEfAJ%2F1.png?alt=media\&token=19706564-df33-4e96-b2bd-7c6fff405f12)

In the project you'll see the nodes with these icons:

![A Broken node in the project tree](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh45m0ccQGFTGtebHna%2F-Lh47v9x1oGTldp6cMow%2F2.png?alt=media\&token=99552415-da4b-4051-95d5-32b54d6e5f80)

This is a **Broken** node. It can not be created manually, it is shown if some node can't be loaded into the project. A **Broken** node has the following context menu:

![The Broken node context menu](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh45m0ccQGFTGtebHna%2F-Lh4Bqau5j1oKGEsBKpY%2F3.png?alt=media\&token=e7bf5b87-1cd7-455c-85d9-700d05a798ef)

* **Show in explorer.** Open the folder with the node in the file manager.

There is no tab for a **Broken** node to be opened in, and it can't have any child nodes. The main purpose of this node type is to help you fix the error.


# Script

This is a node that runs JavaScript scripts. It is going to help you solve the following tasks:

* create complex tests to check the results of one or several nodes;
* generate test data;
* change other nodes' variables;
* perform operations to bring the tested system to a specific state (set\_up, tear\_down);
* debug and access all nodes' states.

## Editing a Script&#x20;

The script node editing window is divided into two parts: the code editor and the console output window. To close the console window click the <img src="https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LkTrcfYjzYT8him1F5A%2F-LkTtHTOFH08FuWDRtuM%2FTestMace%202019-07-19%2015.42.04.png?alt=media&amp;token=c964fc73-7da1-4bbb-b8a9-57f1557c97c2" alt="" data-size="original"> button.

You can use the toolbar above the output window to manage the console behavior:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LkTrcfYjzYT8him1F5A%2F-LkTuOuoBQywn7OdMTcW%2FTestMace%202019-07-19%2015.43.23.png?alt=media\&token=1d946a3b-1754-4a50-96de-a220986ca1fe)/![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LkTrcfYjzYT8him1F5A%2F-LkTtnth9AoCIoum4eB6%2FTestMace%202019-07-19%2015.44.34.png?alt=media\&token=0916f637-f555-4574-848d-5a3af3389b73)- choose what to do with the console output. ![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LkTrcfYjzYT8him1F5A%2F-LkTuOuoBQywn7OdMTcW%2FTestMace%202019-07-19%2015.43.23.png?alt=media\&token=1d946a3b-1754-4a50-96de-a220986ca1fe) -  clear the window every time you run a script, ![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LkTrcfYjzYT8him1F5A%2F-LkTtnth9AoCIoum4eB6%2FTestMace%202019-07-19%2015.44.34.png?alt=media\&token=0916f637-f555-4574-848d-5a3af3389b73) -  display all previous outputs.

* ![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LkTrcfYjzYT8him1F5A%2F-LkTuLaA994Reilwvjy7%2FTestMace%202019-07-19%2015.44.14.png?alt=media\&token=22602ea9-9fb4-4572-8be9-1dc105e40f5f) - auto scroll to the last line of the output
* ![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LkTrcfYjzYT8him1F5A%2F-LkTuK4OIWVEjikfBLgc%2FTestMace%202019-07-19%2015.43.48.png?alt=media\&token=6ffc25fe-ef66-48f3-90e6-7830da850d82) - clear the console window

## Running a Script

Click the `RUN` button to execute the script. The execution continues to the last line of the code and finishes when all asynchronous tasks are done (e.g.`setTimeout`). The execution is successful if:

* there are no syntax errors in the code;
* the exceptions raised had been handled;
* the execution took no more than 30 seconds (otherwise the execution will be terminated).

{% hint style="success" %}
Since the call to the script is wrapped in a function, you need to use the `return;`command to avoid errors while terminating the script.
{% endhint %}

{% hint style="danger" %}
To get an error after script termination raise an exception with`throw new Error('Something went wrong');`
{% endhint %}

## Libraries

The script is executed in the Node.js virtual environment. Some Node.js modules and all standard JavaScript features supported by V8 are available.

{% hint style="info" %}
The ECMAScript 6 standard is supported as well.
{% endhint %}

### Available Node.js modules

* [fs](https://nodejs.org/docs/latest-v10.x/api/fs.html) - a module for interacting with the file system

### Available third party libraries

* [lodash](https://lodash.com/) - a library that provides lots of utility functions
* [moment.js](https://momentjs.com/) - a library for managing dates
* [CryptoJS](https://cryptojs.gitbook.io/docs/) - a collection of cryptographic algorithms
* [random-js](https://github.com/ckknight/random-js) - a mathematically correct random number generator library
* [faker.js](https://github.com/marak/Faker.js/) - a library that lets you generate random data for different entities properties
* [chai.js](https://www.chaijs.com/) - a library that provides a convenient API for making assertions&#x20;
* [request](https://github.com/request/request) - a library that provides a powerful HTTP client
* [axios](https://github.com/axios/axios) - a library for sending HTTP requests

## Execution Context

Objects and functions of the script's global scope are listed below.

### Accessing third party modules

Every module stated above is automatically added to the execution context and is available in the global scope. <br>

#### lodash

```javascript
_.sum([1, 2, 3, 4]) // 10
```

![](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MD08neqs5aFh8TZkEs7%2F-MD0EAHlkmZtuluuck_W%2FScreenshot_4.png?alt=media\&token=b6ea0bfa-a801-4242-8c82-624172f483db)

#### moment.js

```javascript
const now = moment(new Date()).format();
```

![](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MD08neqs5aFh8TZkEs7%2F-MD0ETQnY_XPrga2xRPK%2FScreenshot_5.png?alt=media\&token=f544c991-dde6-468a-b9a4-f44ae0d1b925)

#### CryptoJS

```javascript
const hash = crypto.MD5('Message');
```

![](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MD08neqs5aFh8TZkEs7%2F-MD0Eak46akTPZogvz11%2FScreenshot_6.png?alt=media\&token=00aa3337-d369-46e0-9640-2d7d9b551647)

#### random-js

```javascript
const randomEngine = new random.Random();
const shuffledArray = randomEngine.shuffle([1,2,3,4,5]);
```

![](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MD08neqs5aFh8TZkEs7%2F-MD0ExVPoMTldJQ5YdE5%2FScreenshot_7.png?alt=media\&token=78cc6b64-fc7b-4d8a-9356-d31ee891380b)

#### faker.js

```javascript
const person = { 
    'name': faker.name.findName(),
    'email': faker.internet.email()
};
```

![](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MD08neqs5aFh8TZkEs7%2F-MD0F9FhT-nl0baS5UYx%2FScreenshot_8.png?alt=media\&token=1ca76381-0486-45c4-8e4b-b67d1083fea9)

#### chai.js

```javascript
const foo = 'bar';

// success
assert.equal(foo, 'bar');
expect(foo).to.equal('bar');

// failure
assert.equal(1, 0);
```

![](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MD08neqs5aFh8TZkEs7%2F-MD0FGgjs00FJqqXQtjO%2FScreenshot_9.png?alt=media\&token=4670ea94-7a63-4c2e-ac7c-984440eecd79)

#### request

The`request` library doesn't provide an interface for working with async/await. One possible solution is wrapping `request` call in [`Promise`](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Promise)object

```javascript
await new Promise((resolve, reject) => {
  request('https://docs-ru.testmace.com', (error, response, body) => {
    try {
      assert.equal(error, null);
      assert.equal(response.statusCode, 206);
      assert.notEqual(body, null);
      resolve();
    } catch(e) {
      reject(e);
    }
  });
})
```

![](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MD08neqs5aFh8TZkEs7%2F-MD0Ge5Hy1HEuhKHDJ1X%2FScreenshot_10.png?alt=media\&token=29019855-830e-4fce-9cf5-f89168467554)

#### axios

```javascript
await axios.get('https://api.ipify.org?format=json')
```

![](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MD08neqs5aFh8TZkEs7%2F-MD0IIXrpj4NjgXWp9Jl%2FScreenshot_12.png?alt=media\&token=fb4ca8c9-0198-4937-ad17-3ed9bc6ee1a2)

### console.\*

There are different console output methods, such as log, info, warn, error, debug, exception.

Their signatures are equal to the signatures of their standard versions. In the console every method type is highlighted in different color. Each colored line contains the particular row and column where the output method is called. Events of the exception type are displayed along with the stack trace.

![](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MD08neqs5aFh8TZkEs7%2F-MD0Gynf0rNTjhcb0kV3%2FScreenshot_11.png?alt=media\&token=0d2ec781-7b5c-4573-996d-6967efc4d75c)

### Asynchronous Code <a href="#assinkhronnyi-kod" id="assinkhronnyi-kod"></a>

{% hint style="warning" %}
In previous versions, TestMace tried to automatically determine if the script had been finished. In new versions, this feature and finish() function were removed.
{% endhint %}

A script can contain asynchronous calls (e.g. Promise, setTimeout, addEventListener, built-in modules callbacks, etc.). JavaScript provides a convenient mechanism for working with asynchronous operations - [async/await](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await). TestMace has full support of async/await and use this mechanism while performing asynchronous operations to determine if the script had been finished. Let's consider the following example:

```javascript
console.log('1');
setTimeout(() => console.log('2'), 100);
console.log('3');
```

Console output:

```javascript
1
3
```

To fix this you should explicitly mark the operation as an asynchronous usind `await` keyword. For convenience, we've added `delay` function that will allow you to pause the script. This function is compatible with async/await:

```javascript
console.log('1');
await delay(100);
console.log('2');
console.log('3');
```

New console output:

```javascript
1
2
3
```

For further information about async/await we recommend to read a [documentation](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await). Following modules support async/await:

* `fs.promises`
* `axios`

Finally, it can be mentioned that script execution can take up to 30 seconds. If it takes more then the error will be thrown.

### Navigating your project

There is an object used to access the project and the current Script node. It is called `tm` and it is available in the global scope.&#x20;

#### tm

* `currentNode: nodeAPI` - the current Script node API&#x20;
* `project: nodeAPI` - the project node API
* `env: envAPI` - the API for accessing the environment variables
* `cookies: cookie[]` - a list of cookies used in the project
* `system: object` - an object, which contains the system's environment variables

#### nodeAPI

* `parent: nodeAPI` - returns a parent node API and null for the project node
* `name: string` - the given node name
* `type: string` - the given node type
* `path: string` - the path to the given node starting with the project root
* `children: nodeAPI[]` - a list of child nodes APIs
* `findChild(name: string): nodeAPI` - searches for the child node using its name and returns null if the node doesn't exist
* `next: nodeAPI` - the API of the next node in the group. If the given node is the last one, null is returned
* `prev: nodeAPI` - the API of the previous node in the group. If the given node is the first one, null is returned
* `nextNodes: nodeAPI[]`  - a list of all next nodes in the group. If the given node is the last one, an empty list is returned
* `prevNodes: nodeAPI[]` - a list of all previous nodes in the group. If the given node is the first one, an empty list is returned
* `vars: object` - the object that stores all static variables of the given node
* `dynamicVars: object` - the object that stores all dynamic variables of the given node
* `setDynamicVar(name: string, value: any): void` - sets the name dynamic variable with a certain value for the given node

#### requestNodeAPI

The interface of a `RequestStep` node is more advanced.

* `request: object` - the object that stores the node's request configuration
* `response: object` - the object that stores the results of the last request

#### envAPI

* `active: string` - the active environment title
* `vars: object` - the object that stores the current environment variables

## Code examples

### Recursive traversal of the node's children

The code below shows how to pass data to all children of the current node.

```javascript
const current = tm.currentNode;
const parent = current.parent;
if (!parent) {
  console.warn(`Parent of ${current.path} not found`);
  return;
}

const value = parent.vars['ID'];
if (!value) {
  console.warn(`Node ${parent.path} hasn't have value for ID`);
  return;
}
console.log(`Parent ID = ${value}`);

const setIDToNode = (node) => {
  node.setDynamicVar('ID', value);
};

const traverseDescendants = (node, func, depth) => {
  node.children.forEach((child) => {
    func(node);
    
    indent = '\t'.repeat(depth);
    console.debug(
      `${indent}${child.path}`,
      `${indent}Value: ${child.dynamicVars['ID']}`
    );
    
    traverseDescendants(child, func, depth+1);
  });
};

traverseDescendants(parent, setIDToNode, 0);
```

### Searching a node by its name

You can find a child node by its name using the `findChild` method:

```javascript
const current = tm.currentNode;
const scriptNode = current.parent.findChild(current.name);
assert.equal(current, scriptNode);
```

### Data generation and storing it to the variable

Here is how you can generate a random string identifier using the `faker` library and setting as the next node's dynamic variable `UUI`. Once you've run the script, you can reference this variable in an URL, request body, etc.

```javascript
const current = tm.currentNode;
const uuid = faker.random.uuid();
const anotherNode = current.next;
anotherNode.setDynamicVar('UUID', uuid);
```

## File Representation

```javascript
{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of Script node",
      "const": "Script",
      "type": "string"
    },
    "script": {
      "description": "Javascript code",
      "type": "string"
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "children",
    "name",
    "script",
    "type",
    "variables"
  ],
  "definitions": {
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
```


# User-Defined Variables

The Variables section is a key-value storage for storing and reusing some data. It is often used for removing code duplicates and improving readability: the greetingUrl variable says more than just the line <https://next.json-generator.com/api/json/get/EJvQVEVGL> for sure.

The variables mechanism is very well integrated to all application parts and has several features:

* You can use strings, objects, arrays, and links to other variables as values.
* Variables are defined for each node and are inherited from parent nodes.
* Variables values can refer to other variables.
* [Default variables](/variables/variables) names start with $.

### Using Variables

You can use variables in any string parameters of the node - URLs, headers names, authorization tokens, etc. To do that, use the `${variableName}` syntax, where `variableName` is a link to the variable. Here are some examples:

* `${id}`
* `${$dynamicVar.id}`
* `${$response.body.name}`

It's possible to combine strings and links to other variables in the node parameters fields. For instance, you can use  `http://${host}/posts/${$dynamicVar.id}` as an URL.&#x20;

To access an array element, that is stored in a variable, you can use the `${variableName[index]}` syntax. For example, to access the third response entity id, you'll write `${$response.body[2].id}`. Note that it the index is zero-based.

Autocompletion works for variables:

![](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MEY9ibfnYvDKaURxlxy%2F-MEY9mDK7gTKvDfOGiWm%2Fvariables1.gif?alt=media\&token=3a1d85be-cefd-4daf-ae05-57107f4c3b4b)

Variable value highlighting works as well:

![](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MEY9ibfnYvDKaURxlxy%2F-MEYAHlXOuh-KHuhndW9%2FScreenshot%202020-08-12%20at%2018.57.17.png?alt=media\&token=1a488086-2e8c-4597-999f-513c474ccfb0)

There is the Variables tab in every node interface, that contains the variables list. This is how the tab looks like:

![](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MEY9ibfnYvDKaURxlxy%2F-MEYAgxCEmpEv54HZrYP%2FScreenshot%202020-08-12%20at%2018.58.23.png?alt=media\&token=0bacf5d4-f58c-4055-8033-bdcef09631f7)

The tab looks the same for all node types. You'll learn more about how to work with variables in the next sections.


# Static Variables

You can define your own variables, that will be associated with a specific node. Those variables names can't start with the $ symbol, because by convention it is reserved for [default variables](/variables/variables). The variables mechanism also supports variables inheritance and overriding in child elements.

To edit your variables, you need to open the [Variables tab](/variables/user-variables). On the Variables tab you can see a table with variables, associated with this node. The tab has the following interface:

![The user-defined variables editing tab](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MEYB10kLLxJWzufBShs%2F-MEYB_a3VEhHgs5O52in%2FScreenshot%202020-08-12%20at%2018.58.23.png?alt=media\&token=ca5376f3-5e1c-42aa-905c-90b80c4e1773)

In variables values you can also reference other variables. Let's rewrite the previous example using variables:

![Referencing variables in values](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MEYB10kLLxJWzufBShs%2F-MEYItyLxBHaJYSaNA9p%2FScreenshot%202020-08-12%20at%2019.33.18.png?alt=media\&token=38f1de23-1fc9-4114-a036-da71de60306d)


# Dynamic Variables

A dynamic variable is a variable which value is determined when the scenario is run. Apart from other tasks, you can effectively use them for storing authorization tokens or newly created entities. The mechanism consists of two parts - variable assignment and dynamic variables themselves.

### Variable assignment

You can assign a part of your request to a dynamic variable. For now you can do it in [RequestStep](/node-types/request-step) nodes only. To illustrate this, let's create a request, that adds a new post and save its id into a dynamic variable.

So let's create the request and send it. Send a POST request to <https://testmace-stage.herokuapp.com/posts> with `{"title":"Our cool post!"}` in its body. The RequestStep node will look like this:

![The RequestStep node after sending a POST request](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MEYIyEBJFryLAfjaAZw%2F-MEYK_GMtft1g8RmC33S%2FScreenshot%202020-08-12%20at%2019.42.02.png?alt=media\&token=6eee86f7-43b1-4f13-b525-31b96ebd2af9)

Open the parsed response and click on the id parameter to see the dynamic variables assignment dialog:

![The id parameter context menu](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MEYIyEBJFryLAfjaAZw%2F-MEYKqNfCuDUvy9N5T7T%2FScreenshot%202020-08-12%20at%2019.43.21.png?alt=media\&token=2fb21c8d-1c87-42ab-8be3-a094c9d17da1)

Choose Assign to variable. You'll see the variable assignment dialog:

![The dynamic variable assignment dialog](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MEYIyEBJFryLAfjaAZw%2F-MEYMwrhUFZ4cGMZD37F%2FScreenshot%202020-08-12%20at%2019.47.53%20copy.png?alt=media\&token=4aa1c973-d83b-4ca3-8a90-7f3ed8c51d28)

You can see the following interface parts.

1. The path to the `$request`where the value is stored.
2. The drop-down list with parents, that you can assign the dynamic variable to.
3. The current value at the given path.
4. The dynamic variable name.

Let's create a variable named `id` at this node.

After assignment you can find the dynamic variable in the list of the chosen node (RequestStep) dynamic variables. See the list in the [variables tab](/variables/user-variables) in the Dynamic section:

![The RequestStep node dynamic variables list](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MEYIyEBJFryLAfjaAZw%2F-MEYNacfor97fWXr626N%2FScreenshot%202020-08-12%20at%2019.55.26.png?alt=media\&token=63f820db-7c40-40d2-9297-7ffaafa6599b)

To view dynamic variables assigned to a certain node, choose the **Values** section from the **Variables** tab. Here’s how this tab looks like for the **MyService** node from the previous example:

![](https://gblobscdn.gitbook.com/assets%2F-Lh_FaVh9XfQJ0p1KqZ1%2F-MEYIyEBJFryLAfjaAZw%2F-MEYO_ymsIrJ2EG8Jiqb%2FScreenshot%202020-08-12%20at%2019.58.16.png?alt=media\&token=96957808-4530-490c-a1b7-375f38c0a086)

#### Using Dynamic Variables

All dynamic variables available for the chosen node are stored in the `$dynamicVar` variable. For example, to access the `id` variable you should write  `$dynamicVar.id`. Just like with other variables, dynamic variables can be inherited from parents or overridden in child elements.


# Default Variables

Default Variables are special variables that can not be overridden. You can use them just like any other variables.

* `$parent` - a reference to a parent node;
* `$prevStep` - a reference to the previous node within a [Folder](/node-types/folder) node;
* `$nextStep` - a reference to the next node within a [Folder](/node-types/folder) node;
* `$dynamicVar` - a [dynamic variables](/variables/user-variables/dynamic-variables) object;
* `$response` - a reference to a response in a [RequestStep](/node-types/request-step) node;
* `$env` - an [environment variables](/variables/env) object.
* `$systemVar`- an object for accessing system environment variables


# Environment Variables

Change variables values used in the project in one click.

In TestMace you can create changeable variables, for instanсe, to switch between **stage** and **prod** environments.

### Creating environment variables

In this example we'll create a single variable for stage and prod environments.

1. Click on the settings icon.
2. In the pop-up window choose a new environment by clicking on the Add environment button and name it stage.
   * Create the **serverUrl** variable and set the stage server url as its value.
3. Add the prod environment by clicking on the Add environment button.
   * Create the **serverUrl** variable and set the prod server url as its value.

### Importing environment from Postman

TestMace allows you to import your environments from [Postman](https://learning.getpostman.com/docs/postman/environments_and_globals/manage_environments/). To do that, click the **+ Import environment** button, that may be found in the variables editing dialog right under the list with all available environments. After you click the + Import environment button, you'll see the dialog where you should enter the path to the file.

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Ll_vQSRFIKGoT1wgKkR%2F-Ll_vhhwuZP7Rsl1UjJq%2FKSpgFN9.png?alt=media\&token=c3d1f146-0737-4639-96e8-ad671fb00b4b)

### Using Environment Variables

To create a changeable variable use the `${$env.%VARIABLE%}` reference. Change the value of our variable in every node to `${$env.serverUrl}`. Now you can change this variable value anytime.

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFYBoa_ywlkIBu028u%2F-LhFYqaW5QfteLxqdRug%2Fenv-2.gif?alt=media\&token=f1f13c40-e23a-4600-9b03-07d647169091)

#### Where to use environment variables?

Just like plain variables, you can use environment variables in any string field of the node.

### Local Environments <a href="#local-environments" id="local-environments"></a>

Local environments are just usual environments that are not saved into project files, but are stored in the application's local storage. We reccomend you use this environment while working with local and privite data, such as logins, passwords, API-tokens, etc.

You can see local environments at the bottom of the sidebar. Each local environment has the `(local)` prefix, so that you could distinguish from other environments.

![Local environments in the environments variables dialog](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LmFMvhKF3ewgTh2gIYo%2F-LmFN0AzzPc2yQWwQqUW%2Fscreenshot_2.png?alt=media\&token=e84e4812-ff08-4409-901e-b88761f581b1)

Drag and drop your local environments to turn them into ordinary ones and vice versa.

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LmFMvhKF3ewgTh2gIYo%2F-LmFN2QmhcHNQbUo8K4m%2FPeek%202019-08-14%2016-00.gif?alt=media\&token=48748bbb-cb33-43ff-9c7d-12fb7ea69e03)


# Cookie

A cookie is a small piece of data that is send by a server and stored on the user's machine.

## Creating a Cookie

TestMace allows you to manage host's cookies. Click on the Cookie button at the top menu to open the modal for managing cookies. You'll see a list of all existing records. Click the Add button to create a new one and fill in the following fields:

|   Field type  | Purpose                                                                                                                        |
| :-----------: | ------------------------------------------------------------------------------------------------------------------------------ |
|    **Key**    | The cookie name                                                                                                                |
|   **Value**   | The cookie value                                                                                                               |
|   **Domain**  | Sets the cookie domain                                                                                                         |
|    **Path**   | Sets the cookie path                                                                                                           |
|  **Expires**  | Sets the date the cookie will expire. The date format should be able to return the `toGMTString()` method of the `Date` object |
|   **Secure**  | The checked Secure box means that SSL is used to send the cookie to the server                                                 |
| **Http only** | Forbids any JavaScript access to the cookie.                                                                                   |

You can also create a cookie from a row string like this:

`isLogged=1; Expires=31/12/2019 00:00:00; Domain=testmace-stage.herokuapp.com; Path=/posts/; Secure;`

![Creating a Cookie](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgbT475LBwT9rFHt0TK%2F-LgfYRYJEPKgk_EHce0O%2Fcookie-1.jpg?alt=media\&token=16e3b4e6-f9ad-4400-a774-c02dcbe4ad9c)

## Editing a Cookie

Will be available in the next release of Testmace.

## Deleting a Cookie

Open the managing cookie window and click on the close icon on the right of the chosen cookie.

![Deleting a Cookie](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgbT475LBwT9rFHt0TK%2F-Lgfa14IctOJbbL4MsJ0%2Fdelete_cookie.jpg?alt=media\&token=ecb49ea8-3261-4bbf-8a96-1edbdfd684d1)


# Authorization

Verifies if you have permission to the resource queried.

## Authorization Types

* [No auth](/working-with-project/authorization#no-auth)&#x20;
* [Inherit from parent](/working-with-project/authorization#inherit-from-parent)&#x20;
* [Basic auth ](/working-with-project/authorization#basic-auth)
* [Bearer auth](/working-with-project/authorization#bearer-auth)&#x20;
* [Digest Auth](/working-with-project/authorization#digest-auth)&#x20;
* [OAuth 1.0](/working-with-project/authorization#oauth-1-0)

{% hint style="info" %}
You can use [environment variables](/variables/env) as the Username, Password, Token, and other authorization parameters.
{% endhint %}

## No auth&#x20;

Use the No Auth type if you don't need authorization while sending a request.

## Inherit from parent&#x20;

**By default** authorization parameters are inherited from the parent node. If the parameters are not specified, the [No auth](/working-with-project/authorization#no-auth) type is used.

## Basic auth

The type is used when login and password are required for sending a request.

#### Using Basic auth

Open the request and click on the Authorization tab and choose Digest auth. Set the Username and Password in the corresponding fields.

![Using Basic auth](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFk_4qC8J17tr9_76X%2F-LhFkbR2YI_AGpENjeVK%2Fbasic%20auth.jpg?alt=media\&token=aa594234-64ea-4626-9e17-5bf1eb27667d)

## Bearer auth&#x20;

Bearer auth is a token based authorization. Any user with the bearer token can use it to get the access to the resources.

#### Using Bearer auth

Open the request and click on the Authorization tab and choose Bearer auth. Set the token in the token field.

![Using Bearer auth](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFk_4qC8J17tr9_76X%2F-LhFktGKTScYA6iltlB5%2FBearer%20auth.jpg?alt=media\&token=2f9ca009-3372-4b05-99d5-9228092afe5a)

## Digest Auth&#x20;

Using this method, you send a hashed login, password, the server url and nonce values, which provides a higher security level than basic authorization, where you send data with no encryption.

#### Using Digest Auth

Open the request and click on the Authorization tab and choose Digest auth. Set the Username and Password in the corresponding fields.

![Using environment variables in Digest Auth](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFk_4qC8J17tr9_76X%2F-LhFl7UguhAzny4ZIv--%2Fdigest.jpg?alt=media\&token=6ddba7ac-b6fd-43af-9d27-942f871da5d5)

## OAuth 1.0

OAuth 1.0 allows you to access protected resources without exposing your login and password.

#### Using OAuth 1.0

Open the request and click on the Authorization tab and choose OAuth 1.0. Input required data in the fields.

#### The OAuth 1.0 parameters supported in TestMace

| Parameters       | Description                                          |
| ---------------- | ---------------------------------------------------- |
| Consumer Key     | The key                                              |
| Consumer Secret  | The consumer code                                    |
| Access Token     | The token                                            |
| Token Secret     | The token code                                       |
| Signature Method | The signature Method: PLAINTEXT, HMAC-SHA1, RSA-SHA1 |
| Version          | 1.0                                                  |
| Realm            | The server the request is sent to                    |

![Using OAuth 1.0](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFk_4qC8J17tr9_76X%2F-LhFlV94xsc8cmlGmUbe%2Foauth1.jpg?alt=media\&token=c02c4bb2-8547-426f-a0d5-b8791b4d89f2)


# Proxy

You can find proxy settings in **File -> Settings**. Click **Enable Proxy** and enter the Proxy variables values.&#x20;

{% hint style="info" %}
**You can configare the proxy server using the following variables:**

* **http\_proxy** — the proxy IP address for sending requests without SSL;
* **https\_proxy** — the proxy IP address for sending requests with SSL;
* **no\_proxy** — a list of hosts that don't need to use a proxy.

**no\_proxy values examples:**

* **`*google.com`** - don't send HTTP / HTTPS requests to Google;
* **`google.com:443`** - google.com:443 - don't send HTTPS requests to Google, but send HTTP requests to Google;
* **`google.com:443, yahoo.com:80`** - google.com:443, yahoo.com:80 - don't send HTTPS requests to Google, but send HTTP requests to Yahoo!
* **`*`**- ignore the https\_proxy / http\_proxy environment variables.
  {% endhint %}

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgbD2VkSljAvrWg9_vW%2F-LgbD4eHI_TmFMXQwnvN%2Fproxy-settings.jpg?alt=media\&token=2d007c8d-76e3-4698-a86f-ea63b5a451ac)


# Bulk Edit

Some tables in the app have the bulk edit feature.  You can see the **BULK EDIT** button above these tables.When you enable the bulk edit mode, the table content transforms into text, where values are separated by the **:** symbol and lines break with the newline character. To disable the line, text **//** in the beginning.

Let's take headers bulk editing at RequestStep node. Standard headers editing looks like this:

![Standard headers editing](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lgbqgh9bnW1m83nMgCa%2F-Lgby1qDO-3IJLEI7wkn%2F1.png?alt=media\&token=42b068d5-755f-4a03-a69d-0bc5f4c94c03)

When you click on the **BULK EDIT** button, the widget changes to this:

![Headers bulk editing](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lgbqgh9bnW1m83nMgCa%2F-LgbyQsJjDD5ufO8yPkJ%2F2.png?alt=media\&token=f27a6c72-2cf0-4dd0-b919-fb970b9e9610)

See how values are separated by : symbol and lines break with the newline character. Let's disable the first line. Add **//** in the beginning.

![Disabling a line in the bulk edit mode](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lgbqgh9bnW1m83nMgCa%2F-Lgbzdf23GWVbN30MAH4%2F3.png?alt=media\&token=7495f1b3-fe0f-47f0-872a-1268fb009000)

Let's turn back to the table mode by clicking on the **TABLE EDIT** button. The table will look like this:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lgbqgh9bnW1m83nMgCa%2F-Lgc--dqOyKM2y0l_Rx_%2F4.png?alt=media\&token=35964872-efa3-4578-a39f-20db534a8761)

As you can see, the first line checkbox is unchecked, which means the line won't be used in the request.


# Import & Export

In this section we'll discuss the **Import** menu option features. You can find it by clicking the **+** button above the project tree (or at the top part of the **Scratches** area). This is how it looks like:

![The Import context menu](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh4FOBJ2iDdG8NA1Fm-%2F-Lh4FsXGrFh4ST697Eyd%2F4.png?alt=media\&token=8bd8cb88-cd66-4e92-a8a6-973636033978)

The **Import** menu includes the following options:

* [**Shared**](/other-features/import/shared) - loads previously exported nodes;
* [**cURL**](/other-features/import/curl) - imports a request from cURL;
* [**Swagger**](/other-features/import/swagger) - imports API description from [Swagger/OpenAPI](https://swagger.io/specification/);
* [**Postman**](/other-features/import/postman) - imports collections from [Postman](https://learning.getpostman.com/docs/postman/collections/sharing_collections/).

You'll learn how to use them all in the next sections.


# Shared

TestMace provides a very convenient way of sharing nodes and even project subtrees. To import a subtree, choose the **Share** option in the chosen node context menu.

Let's export the project from the [Getting Started](/) section as an example.&#x20;

![A Shared export](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFn6WYyKpdhWkt4-Mj%2F-LhFn8HlqNDKKJ63Gfvt%2Fshare-1.gif?alt=media\&token=3518fe27-e015-49d8-bf89-34938ba63781)

The `testmace://....` URL is copied into the clipboard.

Now you can import this URL into the chosen node. There are two ways to do that:

* import from the project context menu: **Import** -> **Shared**;
* import from the [Folder](/node-types/folder) or [Project](/node-types/project) nodes: **Import** -> **Shared**;

The Import dialog has the following interface:

![The Import dialog](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh4GkVAVvrxlBstzDgQ%2F-Lh4NVKvy8XoGrzrS-UF%2F1.png?alt=media\&token=ab5f441b-9bfe-4adf-ba62-7a4d6a67096a)

You can set the root node name of the imported subtree in the **Name** field. Enter the previously exported URL in the URL field. If the root node name you specified already exists in the tree, the name will be changed to NodeName \[Copy \[number]].

You can see the animation of the import process down below:

![A Shared import](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFn6WYyKpdhWkt4-Mj%2F-LhFoT9YtiZwh5P1doqK%2Fshare-2.gif?alt=media\&token=4db859a2-3e6c-4c47-a568-8ee87c04f615)


# cURL

[cURL](https://curl.haxx.se/) - is a command line tool allowing you to interact with services via different protocols with URL syntax. It is widely used today, including sending HTTP requests from the command line. TestMace allows you to import the curl command with parameters into a [RequestStep](/node-types/request-step) node request.

There are two ways to import a request from cURL:

* import from the project context menu: **Import -> cURL**;
* import from the [Folder](/node-types/folder) or [Project](/node-types/project) node context menu: **Import** -> **cURL.**

You'll see the next dialog:

![The dialog for import from cURL](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh4OL_nOZ71Ot6O67fi%2F-Lh4SwiyIkwkiHHhCKAR%2F2.png?alt=media\&token=6fd1e66c-c3ac-4796-bf9b-5ee538f24f62)

Here you need to set the new [RequestStep](/node-types/request-step) node name and the import command.

Take a look at how a request is copied from the browser requests list as cURL:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lgfzj3UBNJfmoUFpmJU%2F-Lgg1WxiamPf0Yx6ENPN%2FcURL%20import.gif?alt=media\&token=2a561be6-1620-468b-8a40-517305acb314)


# Swagger

Import from Swagger/OpenAPI is shown in the [API Description Import](/node-types/api-description/import-api) section.


# Postman

Postman allows you to [share a collection of requests](https://learning.getpostman.com/docs/postman/collections/sharing_collections/). TestMace supports import this format. You can import Postman collections in these two ways:

* import from the project context menu: **Import** -> **Postman**;
* import from the [Folder](/node-types/folder) or [Project](/node-types/project) nodes context menu: **Import** -> **Postman**.

You'll see the next dialog:

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LtTeIl0NmO2yjDRT-rP%2F-LtTeNk_JofK1oyqaoDt%2Fscreenshot_1.png?alt=media\&token=59f854c3-18d0-4ab6-999b-edd51a41eb22)

Let’s look at this dialog in detail. You can specify 3 import parameters

* **Path to collection file**: a path to the file with an exported Postman collection. Only this field is required foe import. See the collection export instruction in the [Postman documentation](https://learning.getpostman.com/docs/postman/collections/data-formats/#collections)
* **Path to environment file**: a path to the file with an exported environment. See the environment export instruction in the [Postman documentation](https://learning.getpostman.com/docs/postman/environments-and-globals/manage-environments/)
* **Path to globals file**: a path to the file with exported Postman global variables. See the global variables export instruction in the [Postman documentation](https://learning.getpostman.com/docs/postman/environments-and-globals/manage-globals/)

{% hint style="info" %}
While importing from Postman you may need only a collection, but we highly recommend you specify the paths to the files with environment and global variables. There are a couple of reasons for that:

* Better variables resolving. If you import a collection only, all variables belong to the collection, even if they are not defined in the collection’s variables list
* A new environment will be added to you project if you specify a path to the file with the environment
  {% endhint %}

If the root node name you specified already exists in the tree, the name will be changed to NodeName \[Copy \[number]].


# Default HTTP headers

You can set the HTTP headers of Folder and Project nodes, that will be inherited by child nodes and used in RequestStep nodes requests by default. Let's see how we can set and use default headers.

## Defining Default HTTP Headers

You can define your default headers in the Folder node. To do this, click on the **Headers** button on the Folder node toolbar.

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgfhSVUoFJEM98_tht0%2F-LgfjSwuSwYIys2vQFkC%2F1.png?alt=media\&token=9618cd40-7584-4c98-a124-1eef64d32ff4)

You'll see the default HTTP headers editing dialog:

![ The default HTTP headers editing dialog](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgfhSVUoFJEM98_tht0%2F-LgfjsCplgf70WxxwuHA%2F2.png?alt=media\&token=4f6a6ea5-7b5f-43c8-94bd-b2594cf86c8f)

At the top of the dialog you can see a read-only headers list inherited from parents. Next the chosen Folder node headers are specified. Apart from adding, deleting, editing (including [bulk edit](/other-features/bulk-edit)), it is possible to disable certain headers which will not be included in the resulting request. Headers state (enabled/disabled) is inherited as well.

You can also override headers in child nodes. For example, setting the `Hello, TestMace` value to the `RootDefaultHeader1` header will override the inherited header, and in child nodes of the chosen Folder node the  `RootDefaultHeader1` header will have the `Hello, TestMace` value. Note that the value of the **parent** `RootDefaultHeader1`  header will remain unchanged (`Hello, world`).

### Using Default Headers

Default headers are used in RequestStep nodes requests. They are inserted automatically and don't require user interaction. The interface of RequestStep requests headers editing is identical to the one of a Folder node.

### Default Headers in the File System

See the file representation of a [Folder](/node-types/folder#failovoe-predstavlenie) node. The `requestData.headers` field is used to store a headers list, and the `requestData.disabledInheritedHeaders` is used to store disabled headers. The same format applies to [RequestStep](/node-types/request-step#failovoe-predstavlenie) nodes.


# Command line tool

## Use Cases

We've added the  `testmace-cli` console utility for running tests right from a TestMace project for you to improve automated processes, including API testing. Its possible use cases are:

* Quick testing without GUI after changing the API implementation;
* Running tests automatically before making a commit in the version control system;
* Running tests while automatic integration or CI/CD delivery;
* Regular scheduled testing for API operativity and stability monitoring.

## Installation

### NPM package

Make sure your system has [node.js](https://nodejs.org/en/) version 10 or higher installed.

To install the tool, run the following command:

```
npm install --global @testmace/cli
```

## Running the tool

The simplest way to run the tests is shown below.

Use the following command to test a local project:

```
testmace-cli <full_path_to_project>
```

Use the following command to test a cloud project:

```
testmace-cli -p <project_id> -t <access_token>
```

If all tests are successful, the 0 code will be returned, otherwise you'll see the 1 code. By default, tests results are printed to stdout.

### Run parameters

The tool supports the following run parameters:

* `-e <project_environment_name>` and `--environment <project_environment_name>` - allow to specify what environment variables to use while running tests. The name should correspond to one of the project's environments.
* `-r <reporter_type>` and `--reporter <reporter_type>` - allow to specify what kind of test reports you want to see.  You can use `junit`

  as `reporter_type`, which generates the result in the JUnit XML format.
* `-p <project_id>` or `--project <project_id>` - a cloud project identifier . You can find it on the particular project cloud sync control panel.
* `-t <access_token>` or `--token <access_token>` - an authorization token that you need to use the cloud sync feature. You can manage authorisation tokens in the user's profile on the cloud sync control panel.

## JUnit XML report

If you run the program with the  `--reporter=junit` key, the report will be generated in the JUnit XML format. It gives you a more detailed report of successful and failed requests and tests. Reports of this kind can be visualized by special programs (e. g. [XUnit Viewer](https://github.com/lukejpreston/xunit-viewer)) and CI/CD systems (e.g. [Jenkins](https://jenkins.io/)).

In reports each running node in the project tree is represented as *TestSuite*.The name corresponds to the relative path to your project root. For the `Link` node you should specify the relative path to the link node.  *properties*  here are the keys and values of this node's variables. Every test and HTTP-request is represented as*TestCase* in report&#x73;*.*

### An example of report visualization

![](https://1550208191-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LnHM6UDZMA0GaeWzDIJ%2F-LnHMEVGfotlyDmYMRCZ%2FXunit%2BViewer%2B-%2BGoogle%2BChrome%2B2019-08-18%2B19.34.59.png?alt=media\&token=2eb578ea-cce1-4515-8c00-9035192aba9c)


# Getting Started

This guide will help you quickly get to know TestMace interface and its main features.

{% hint style="info" %}
In this guide we're going to test a back-end server running the following scenario for post records:

* request all records from the server;
* add a new record;
* check if the record was added correctly;
* update the record and check the update with a server response;
* request the updated record from the server;
* check if the record on the server is updated;
* delete the record;
* check if the record was deleted from the server.

**You'll need about 10 min to do that after running the app.**
{% endhint %}

## Installation

Go to our site <https://client.testmace.com>  or use the following links to download TestMace:

* Windows <https://download.testmace.com/TestMace.exe>
* Mac OS <https://download.testmace.com/TestMace.dmg>
* Linux <https://download.testmace.com/TestMace.AppImage>

Run the installer and follow the instructions. You will be prompted to choose an installation directory.

{% hint style="warning" %}
&#x20;*To install TestMace on Windows run the installer **as an administrator**.*
{% endhint %}

Once the installation is complete, run the app. You'll see a new project.

## Interface Overview

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFUOLTSKsreXOFBI39%2F-LhFUVgv2a4VQpeSBF9V%2Fmain_screen_1.png?alt=media\&token=8d31cd2a-360a-4bf5-9c7e-496ae4dd0694)

## Your First GET Request

To make your first request create a new tab by clicking on **+**. При этом в зоне  You'll see a new **Scratch 1** node in the Scratches Area. Insert this address in the URL field: <https://testmace-stage.herokuapp.com/posts>. You can now test server response right from Scratches Area or add your scratch to the project. Rename the node to **getPosts** for convenience.

{% hint style="info" %}
Note that all changes to the project are automatically saved in real-time.
{% endhint %}

![Creating a GET request template](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhJpQ_vCIZWrro6TJZk%2F-LhJpUafiA17-CXuQAp9%2Fgetting_started_1.gif?alt=media\&token=844a0955-59f9-404d-aa2b-2bc95667611e)

In your project create a [Folder](/master/node-types/folder) node named **posts** and move the scratch **getPosts** from the Scratches Area to the Project Area.

![Creating a Folder node and GET request template moving](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhJpQ_vCIZWrro6TJZk%2F-LhJq2k6iMb_jD3VQw8e%2Fgetting_started_2.gif?alt=media\&token=403bb656-0f43-498a-b52a-4e50ab2fc092)

Double click on the created request **getPosts** to open it and then run it with the Run button.

![Running a GET request](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhJpQ_vCIZWrro6TJZk%2F-LhJqgooGAEXyLj0QBA7%2Fgetting_started_3.gif?alt=media\&token=5b1e83b5-197d-46aa-9baf-0b7b89d0f9f7)

As you see, the request was successfully run, and you have a list of existing records in **Response Area**. Let's take a closer look at this window:

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFUOLTSKsreXOFBI39%2F-LhFV_hy2ssJx_CXd937%2Frun%20screen.png?alt=media\&token=af2309ed-b0ae-4ca3-8284-130ed9b81c54)

{% hint style="info" %}

#### Request parameters

Here you can specify http headers and pass parameters to the request with the help of the autocomplete feature and variables.

#### Request type

* **GET** — get a resource
* **POST** — create a resource
* **PUT** — update a resource
* **DELETE** — delete a resource
* **PATCH** — make some changes to a resource
* **OPTIONS** — specify resource connection parameters

#### URL

The URL field supports autocompletion and using variables. We'll take advantage of these features later.

#### Make Request

Sending a request or a group of requests while running from the project root or a folder node.

#### Response area

A server response area. The Response Body tab contains parsed, JSON, and text representations of a response. On the tabs next to it you can see Response Headers and create or view existing Assertion nodes to make a request.
{% endhint %}

## POST Request and Assertion

Now let's add a new post record to the server, and to do that we need to create a new [RequestStep](/master/node-types/request-step) node.&#x20;

{% hint style="info" %}
A new node can be created in three different ways:

1. Create a Scratch by clicking on **+** and drag and drop it to the project later.
2. Right-click on the parent node and choose **Add node -> Request step.**
3. Click on the button **Add project node-> Add node -> Request step**.&#x20;
   {% endhint %}

Use any of these ways to create a node and name it **createPost**.&#x20;

1. Set POST as its **Request type**.
2. Insert <https://testmace-stage.herokuapp.com/posts> in the URL field.
3. Chose JSON on the response body tab and add `{"title": "Testing post", "content": "Sendt via TestMace"}`
4. Click on the RUN button and send the request.&#x20;

You'll get a response telling you that the record has been successfully added, but we need to check if it was added correctly. And to do that we'll use the quickest way of creating [Assertion](/master/node-types/assertion-node) nodes. We'll compare the sent data with the data received from the server.

In Response Area open the tab with the parsed response and right-click on the **title value**, that you passed in the request and choose **Create Assertion -> Compare -> Equal.** It'll create and open the [Assertion](/master/node-types/assertion-node) node, and as you're not going to configure it, just close it. Create an Assertion node for the Content value in the same way.

Now send the **createPost** request, and you'll see that the test was successful. The task is really easy. Have a look at the animation below:

![Sending POST request and creating an Assertion node](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhJpQ_vCIZWrro6TJZk%2F-LhK0GR6pYAbucRUToRZ%2Fgetting_started_4.gif?alt=media\&token=94e64916-b469-4eaa-8575-925e4922f0a6)

### Dynamic Variables

To be able to interact with the newly added node you should pass its **Id** to all subsequent [Request step](/master/node-types/request-step) nodes. Define a dynamic variable **postId** and assign to it the Id value, received in the record after running **CreatePost**.&#x20;

1. &#x20;Right-click on the **Id** value in the **CreatePost** node's response body.&#x20;
2. Choose **Assign to variable**.&#x20;
3. In a pop-up window choose the **posts** project directory as a node, enter the variable name (**postId**) and press **OK**.

To access the variable use a[ default variable](/master/variables/variables) `$dynamicVar`:

```
${$dynamicVar.postId}
```

![Creating a dynamic variable](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhK0SAV-59GBQjAg6hd%2F-LhK1BRwGcPnJZvbxbS4%2Fgetting_started_5.gif?alt=media\&token=b89564f3-3b67-40ee-853a-537d4a89cea0)

## PUT Request

It's time we sent a PUT request.  Let's access the previously created record using a dynamic variable `${$dynamicVar.postId}` and update its **title** and **content** values.

1. Create a [RequestStep](/master/node-types/request-step) node named **updatePost**.
2. Set **PUT** as its request type.
3. URL: <https://testmace-stage.herokuapp.com/posts/${$dynamicVar.postId}>
4. Body: `{"title": "Testing post updated", "content": "Updated via TestMace"}`
5. &#x20;Send the request and, just like we did with the **POST** request, create two [Assertion](/master/node-types/assertion-node) nodes to compare sent and received **title** and **content** values.

![Creating a PUT request](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhK0SAV-59GBQjAg6hd%2F-LhK3Y9OgAtw8zYool2I%2Fgetting_started_6.gif?alt=media\&token=7c828698-04e5-4264-8182-857fc11fae20)

## Verifying Changes

In some situations you might want to additionally verify changes in the record, because the server may give a successful response of PUT, but sending a GET request you receive the old version.&#x20;

To do that create a GET request at the record's URL using a dynamic variable.

1. Create a new [RequestStep](/master/node-types/request-step) node named **getPost**.
2. Request type: GET.
3. URL: <https://testmace-stage.herokuapp.com/posts/${$dynamicVar.postId}>
4. Send the request and create 2 [Assertion](/master/node-types/assertion-node) nodes to compare **title** and **content** values.

![Verifying changes using a GET request](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhK0SAV-59GBQjAg6hd%2F-LhK4Oj-daBXeUD9mVGk%2Fgetting_started_7.gif?alt=media\&token=2152423d-0a73-4d9b-beb8-7226cfa0e1c5)

## DELETE Request

Our next step is deleting our record at the provided URL using a dynamic variable.

1. Create a [RequestStep](/master/node-types/request-step) node named **deletePost**.
2. Request type: DELETE.
3. URL: <https://testmace-stage.herokuapp.com/posts/${$dynamicVar.postId}>

![DELETE request](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhK59c6K51LChtOFa3G%2F-LhK5CKZiNZ92uvFMQqo%2Fgetting_started_8.gif?alt=media\&token=fc0eb164-f908-4991-a96b-6c6ecb33e8c0)

## DELETE Verifying

To ensure that the record was deleted from the server, create a GET request at that record's URL using a dynamic variable. We expect to get a 404 response from the server, and so we create an Assertion node:

1. Create a new [RequestStep](/master/node-types/request-step) node named **checkIfNodeExists**
2. Request type: GET
3. URL: <https://testmace-stage.herokuapp.com/posts/${$dynamicVar.postId}>
4. Send the request, open the **Assertions** and add a new [Assertion](/master/node-types/assertion-node) node, by clicking ADD. Set the node data:
   1. Actual value: `${$response.code}`
   2. Operator: `=`
   3. Expected value: `404`

![Verifying if the record was deleted via the server response](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhK6MDYKbETOBDIIY1v%2F-LhKAIKXMtt4o0-LWhwL%2Fgetting_started_9.gif?alt=media\&token=c80f7d78-f462-4fb7-8e0a-97fed0a858c9)

## Summary

In the end we've got a test set for our server that we can run in a scenario. Just go to **posts** and click RUN.&#x20;

![Running a scenario](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhK6MDYKbETOBDIIY1v%2F-LhKB-vkAz_pWK1QmcFi%2Fgetting_started_10.gif?alt=media\&token=e0a178fb-008e-4145-acd2-3e1095d875ab)

## Video Instruction

Watch a video of the entire process of creating a scenario described in this guide.

{% embed url="<https://youtu.be/Gyg_4w78KBo>" %}

## Getting started code for a [shared](/master/other-features/import/shared) import

{% file src="/files/-LhKOt6yJc6\_lHN7SVh1" %}
Getting Started Share Code
{% endfile %}

## Download the Project

Unzip in the TestMace projects directory.

{% file src="/files/-LfxYBWoktxgpSwpf33U" %}
Quick start project
{% endfile %}


# The Menu

![The menu](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LaP7qYcgXBIoVT_vHWF%2F-LaPBhGcxMBnMx5ThBwe%2F2.png?alt=media\&token=519f2efa-c526-4180-a4b1-e00e23a704dd)

* [~~**Undo и Redo**~~](broken://pages/-Lgfjvr-InvpkHaf1i3X) - undo and redo your actions. At the moment all actions on changing projects and nodes are supported.
* [**Cookies**](/master/working-with-project/cookie) - a dialog to work with cookies.
* [**Environments**](/master/variables/env) - configure and choose the environment.<br>


# Interface Overview

### The application interface is divided into 3 main parts:

1. Project tree
2. Scratches area
3. Main area

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFUOLTSKsreXOFBI39%2F-LhFUVgv2a4VQpeSBF9V%2Fmain_screen_1.png?alt=media\&token=8d31cd2a-360a-4bf5-9c7e-496ae4dd0694)

### The main area (or request area) also has several elements:

1. Request type
2. URL
3. The RUN button
4. Request parameters
5. Response area

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFUOLTSKsreXOFBI39%2F-LhFV_hy2ssJx_CXd937%2Frun%20screen.png?alt=media\&token=af2309ed-b0ae-4ca3-8284-130ed9b81c54)


# Scratches

{% hint style="info" %}
**These are node scratches that you can move to the main project tree.**
{% endhint %}

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFWbNzd7R5t5rhjodi%2F-LhFWcos4ux27Ba5nTTm%2Fscratches.gif?alt=media\&token=d0dc9247-810c-4aa0-a4ec-3283b88b3e5d)


# Node Types

{% hint style="info" %}
A node is an element of the project or scratches tree.
{% endhint %}

### Node Types

* [**Project**](/master/node-types/project)**.** This is a root node, that is created automatically when you start a new project. It is functionally identical to a Folder node.
* [**Folder**](/master/node-types/folder)**.** It allows to group Folder and RequestStep nodes under it.
* [**RequestStep**](/master/node-types/request-step). This is a node that you can use to send a request. It can have only one Assertion node as a child element.
* [**Assertion**](/master/node-types/assertion-node). This one is used for writing tests. It can be a child node only for a RequestStep node.
* [**Script**](/master/node-types/script). It allows to run a JavaScript script and access the app's API.
* [**Link**](/master/node-types/link). Allows to access an existing node.
* [**Api description**](/master/node-types/api-description)
  * [**ApiRootFolder**](/master/node-types/api-description/apirootfolder)**.** A root element (folder) for API description.
  * [**ApiFolder**](/master/node-types/api-description/apifolder)**.** Groups logically close API description endpoints (e.g. two endpoints with the same urls but different methods).
  * [**ApiRoute**](/master/node-types/api-description/apiroute)**.** A specific endpoint description.
* [**Broken**](/master/node-types/broken)**.**  It is used for descripting nodes that were loaded unsuccessfully. Can't be created manually and is not stored in the file system.


# Keyboard Shortcuts

Keyboard shortcuts in TestMace

| Action                            | Keyboard shortcuts |
| --------------------------------- | ------------------ |
| **Navigation**                    |                    |
| Set the focus to the project tree | Ctrl + 1           |
| Set the focus to scratches        | Ctrl + 2           |
| Set the focus to the main area    | Ctrl + 3           |
| Open settings                     | Ctrl + Alt + S     |
| **Tabs**                          |                    |
| The previous tab                  | Ctrl + Shift + Tab |
| The next tab                      | Ctrl + Tab         |
| Close the tab                     | Ctrl + W           |
| Create a new scratch              | Ctrl + T           |
| **Project Tree**                  |                    |
| Set the focus to the search field | Ctrl + F           |
| Open the node                     | Enter              |
| Open the node menu                | Alt + Insert       |
| Delete the node                   | Delete             |
| Rename the node                   | Ctrl + F6          |
| The next node                     | ↓                  |
| The previous node                 | ↑                  |
| Expand the node                   | →                  |
| Collapse the node                 | ←                  |
| **Project**                       |                    |
| Run Node                          | Ctrl + Enter       |
| Focus Url                         | Ctrl + E           |
| Save Project                      | Ctrl + S           |
| Save Project as                   | Ctrl + Shift + S   |
| Open Project                      | Ctrl + O           |
| Create New Project                | Ctrl + N           |
| Undo                              | Ctrl + Z           |
| Redo                              | Ctrl + Shift + Z   |


# Project

A **Project** node is a root element of the project. It is created automatically after creating a new project and its functionally identical to a [Folder](/master/node-types/folder) node. A **Project** node can't be created manually and can't be used as a child of other node types.

{% hint style="warning" %}
For now, you can not rename the Project folder in the app. Moreover, if you do that in the file system directly, it'll crash the whole project.
{% endhint %}


# Folder

It is used to group other nodes and can be a child of [Project](/master/node-types/project) and **Folder** nodes. This is how it looks like in the project tree:

![A Folder node in the project tree](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgWwgka3rZBZgHDvNjd%2F-LgWyItsMGOzq1NvoQl7%2F1.png?alt=media\&token=c7712323-de2a-4872-b743-811ee97d469b)

Here are the actions available for this node type in the project tree:

![A Folder node context menu](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgWwgka3rZBZgHDvNjd%2F-LgWyvYOvLbHw3fujBhs%2F2.png?alt=media\&token=eeae0e6b-a4fc-4a11-a47b-330c92754426)

* **Add node.** Ckick on it to add a child node. You can choose a node type in the submenu.
* **Rename.** Change the node name.
* **Duplicate.** Make a copy of the node.  The new node will be named **NodeName \[Copy \[number]]**.
* **Remove node.** Use it to delete the node.
* **Run.** Run the node.
* [**Share**](/master/other-features/import/shared)**.** Share the node. Note that the link with information about the node will be created in the clipboard.
* **Show in explorer.** Open the folder with the node in the file manager.

You can open the node with a double-click on it in the project tree. A **Folder** node tab looks like this:

![A Folder node tab](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhpB3Wy9BwwfjEcUimT%2F-LhpB8XEmTJvBFFyGPNx%2F8.png?alt=media\&token=9a2b5343-a1d1-4f37-9738-87d51a2d7f00)

In the screenshot you can see the following parts of the tab:

1. The **Run** button to run the nodes under the Folder node.
2. The control panel
3. The **Headers** button to specify inheritable HTTP headers.
4. The button to open the [**variables dialog**](/master/variables/user-variables)**.**
5. **Child nodes** area
6. Checks if the node has a valid SSL certificate.  It is used as an inherited parameter in [RequestStep](/master/node-types/request-step) nodes.
7. **Authorization**.

Let's describe these parts in detail.

### Control Panel

The **Run** button function is described above. It's worth mentioning that when running a node the button looks different:

![The Run button after running a node](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgXGmrChTZiPq8y7nxp%2F-LgXJs1-z6f1cboc500J%2F4.png?alt=media\&token=f5679900-3e5e-4317-9f48-e6315c4f4dea)

You can stop the node by clicking **Abort**.

The **Headers** button allows to specify [inheritable HTTP headers](/master/other-features/default-http-headers).

Variables editing is shown in the [Custom variables](/master/variables/user-variables) section.

### File Representation

A **Folder** node is a folder with the node name, containing the index.yml file with the following format:

```javascript
{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of Folder node",
      "const": "Folder",
      "type": "string"
    },
    "authData": {
      "$ref": "#/definitions/IAuthorizationData",
      "description": "Authorization parameters"
    },
    "requestData": {
      "$ref": "#/definitions/IRequestParametersData",
      "description": "Request parameters"
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "authData",
    "children",
    "name",
    "requestData",
    "type",
    "variables"
  ],
  "definitions": {
    "IAuthorizationData": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string"
        }
      },
      "required": [
        "type"
      ]
    },
    "IRequestParametersData": {
      "type": "object",
      "properties": {
        "headers": {
          "description": "Headers",
          "type": "array",
          "items": {
            "$ref": "#/definitions/NameValueParam"
          }
        },
        "disabledInheritedHeaders": {
          "description": "Names of disabled headers",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "strictSSL": {
          "$ref": "#/definitions/StrictSSLOptions",
          "description": "Requires SSL certificates be valid"
        }
      },
      "required": [
        "disabledInheritedHeaders",
        "headers",
        "strictSSL"
      ]
    },
    "NameValueParam": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "value": {
          "type": "string"
        },
        "isChecked": {
          "type": "boolean"
        }
      },
      "required": [
        "name",
        "value"
      ]
    },
    "StrictSSLOptions": {
      "enum": [
        "Inherit",
        "No",
        "Yes"
      ],
      "type": "string"
    },
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
```


# RequestStep

A **RequestStep** node is used for sending HTTP requests. TestMace allows to configure a request and use it as a single request or as a part of a scenario.

### A RequestStep node in the project tree

To create a **RequestStep** node right-click on a  [Folder](/master/node-types/folder) or [Project](/master/node-types/project) node and choose **Add node -> RequestStep**.

This is how a **RequestStep** node looks like in the project tree:

![A RequestStep node in the project tree](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LlfdO6jxRXXJvT27opm%2F-LlfdY5N7y3_Ejz_7yjg%2Fimage.png?alt=media\&token=06f38d34-f9c9-44f7-925a-0c4671c252a8)

Let's discuss its features in more detail. The color of the top left circle signifies an HTTP request status: gray - if the request hasn't been sent yet, green - if the HTTP code is successful (e. g. 200, 201, etc), red - if the HTTP code is unsuccessful (e. g. 404, 500, etc). The sheet icon color signifies a child [Assertion](/master/node-types/assertion-node) node status: gray - if the node hasn't been run yet, green - if after running the [Assertion](/master/node-types/assertion-node) node either doesn't exist or was successfully run, red - if the [Assertion](/master/node-types/assertion-node) node was run unsuccessfully (some tests were failed).

There are several actions you can do with this node type:

![Context menu for the RequestStep node](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LlfdO6jxRXXJvT27opm%2F-LlfdnSjXlQrJrODLKd4%2Fimage.png?alt=media\&token=0312686c-e434-4330-933a-ccee2808f7ae)

* **Add node.** Click on it to add a child node. You can choose a node type in the submenu.
* **Rename.** Change the node name.
* **Duplicate.** Make a copy of the node. The new node will be named *NodeName \[Copy \[number]]*.
* **Remove node.** Use it to delete the node.
* **Run.** Run the node.
* [**Share**](/master/other-features/import/shared) the node. Note that the link with information about the node will be created in the clipboard.
* **Show in explorer.** Open the folder with the node in the file manager.

**RequestStep Node Tab**

After you create a **RequestStep** node (or double click on it) you'll see the RequestStep node tab. It looks like this:

![The RequestStep node tab](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhEe9KoXsq-0TMyMIKr%2F-LhEeFBacAIdCWd1c1LG%2F7.png?alt=media\&token=809e17f4-78b8-4ff7-a4b9-d821a341480f)

Let's discuss every interface part in detail.

#### Request Configuration Area

The top area of the tab looks like this:

![The top area of the tab](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lgl2bpuFMbqWPyxebVZ%2F-Lgg6pFo1MUmwO3TlI2e%2F3.png?alt=media\&token=9e9d18da-7c42-4d7a-ba13-4af46bdfa203)

In the screenshot you can see the following parts of the tab:

1. Request method. There are several methods you can choose from:
   * **GET** — get a resource
   * **POST** — create a resource
   * **PUT** — update a resource
   * **DELETE** — delete a resource
   * **PATCH** — make some changes to a resource
   * **OPTIONS** — specify resource connection parameters
2. The URL field.
3. The Run button.
4. The button for [editing variables](https://app.gitbook.com/@testmace/s/project/~/drafts/-Ljtp7mh1Q7dEcGEFMwr/primary/variables/user-variables/staticheski-opredelyaemye-peremennye)

‌You can see the edit panel for headers, query parameters, authorization and request body down below. This is the panel for POST requests:

![Request parameters edit panel](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhEe9KoXsq-0TMyMIKr%2F-LhEeSfnbk-XB_Cp-SYq%2F8.png?alt=media\&token=58f36e77-d1ad-471e-a749-148d9c81bbf1)

The panel has the following tabs:

* **Headers** - edits an HTTP headers list;
* **Query parameters** - edits a query parameters list;
* **Body** - configures a request body;
* **Authorization** - configures [authorizations](/master/working-with-project/authorization);
* **Other** - configures other request parameters.

The **Headers** and **Query** parameters tabs have similar interfaces - they are both tables with [bulk editing](/master/other-features/bulk-edit) and row disabling options. Moreover, HTTP headers can be set [by default](/master/other-features/default-http-headers).

The **Other** tab has the following interface:

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LholopcaA6HBvjCALCd%2F-LholrtdZoWLvhle3Aqt%2Fother_RS.jpg?alt=media\&token=61b0ab6c-d788-4bd4-a296-4d3c905a7f9e)

You can set the **Requires SSL certificates be valid** parameter. It is **Inherit** by default, which means that the value of the parent node is inherited. If this parameter of the parent node is **Inherit**, the parameter is disabled. You can choose from:

* Yes
* No
* Inherit

Now look at the **Body** tab:

![The Body tab](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhEfGEQf9EJ_mvdBLUe%2F-LhEfIwPsMSA3HRokWb5%2F10.png?alt=media\&token=6fc44bf9-a1af-48f8-ba8a-c58ea0d4621e)

You can choose your body type from a dropdown menu:

* **JSON** - to send JSON data. You can use the editor with JSON syntax highlighting and the [variables mechanism](/master/variables/user-variables) support to edit the data. When sending a request a `Content-Type`  header with the `application/json`

  value is added to the HTTP headers list.
* **Form data** - to edit `multipart/form-data` forms. It is a table with [bulk edit ](/master/other-features/bulk-edit)option. The table rows may contain either plain strings or file links as their values.
* **Form URL encoded** - to edit `application/x-www-form-urlencoded` forms. It is a table with [bulk edit ](/master/other-features/bulk-edit)option.
* **File** - to send a file content in the request body.
* **XML** - to send XML data. You can use the editor with XML syntax highlighting and the [variables mechanism](/master/variables/user-variables) support to edit the data. When sending a request a `Content-Type` header with `application/xml` value is added to the HTTP headers list.
* **Text** - to send text data. You can use the editor with the [variables mechanism](/master/variables/user-variables) support to edit the data. When sending a request a `Content-Type` header with `text/plain` value is added to the HTTP headers list.

**Response Configuration Area**

Let's send a request to the <https://testmace-stage.herokuapp.com/posts> url and look at the response area:

![The RequestStep node response area](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lgl2bpuFMbqWPyxebVZ%2F-Lgl8De35KiGku03AgVu%2F6.png?alt=media\&token=3d716437-3fbe-4d07-bd08-338ee1dd8c12)

You can see short info about the response at the top of the area: a successful or unsuccessful response code, time and size.

There are the following tabs at the bottom of the response area:

* **Response body** - contains the response body in different formats, such as:
  * **Parsed** - a tree form of the response. Each tree element has a context menu, so that you could create [Assertion](/master/node-types/assertion-node) nodes and work with [dynamic variables](/master/variables/user-variables/dynamic-variables#variable-assignment).
  * **JSON** - JSON syntax highlighting of the response body. It is available only if you have a JSON response body.
  * **XML** - XML syntax highlighting of the response body. It is available only if you have an XML response body.
  * **HTML** - HTML syntax highlighting of the response body. It is shown if the response body is an HTML page.
  * **Text** - plain text with no highlighting.
  * **Preview** - a rendered response body version. It is shown if the response body is an HTML page.
* **Response headers** - a response HTTP headers list.
* **Assertions** - a list of assertions that a child [Assertion](/master/node-types/assertion-node) node has.

**File Representation**

A **RequestStep** node is a folder with the node name, containing the index.yml file with the following format:

```javascript
{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of Folder node",
      "const": "RequestStep",
      "type": "string"
    },
    "assignVariables": {
      "description": "List of variables assignments",
      "type": "array",
      "items": {
        "$ref": "#/definitions/AssignVariable"
      },
      "default": []
    },
    "requestData": {
      "$ref": "#/definitions/IRequestData"
    },
    "authData": {
      "$ref": "#/definitions/IAuthorizationData",
      "description": "Authorization parameters"
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "assignVariables",
    "authData",
    "children",
    "name",
    "requestData",
    "type",
    "variables"
  ],
  "definitions": {
    "AssignVariable": {
      "type": "object",
      "properties": {
        "path": {
          "description": "Path in $response variable (e.g. body.id)",
          "type": "string"
        },
        "assign": {
          "$ref": "#/definitions/NodeReference",
          "description": "Link on target node (one of parents)"
        },
        "variable": {
          "description": "Name of dynamic variable in target node",
          "type": "string"
        }
      },
      "required": [
        "assign",
        "path",
        "variable"
      ]
    },
    "NodeReference": {
      "type": "object",
      "properties": {
        "refNodePath": {
          "description": "Absolute path to node",
          "type": "string"
        },
        "type": {
          "description": "Marker of reference entity",
          "const": "reference",
          "type": "string",
          "default": "reference"
        }
      },
      "required": [
        "refNodePath",
        "type"
      ]
    },
    "IRequestData": {
      "type": "object",
      "properties": {
        "request": {
          "description": "Common request parameters",
          "type": "object",
          "properties": {
            "method": {
              "$ref": "#/definitions/RequestMethod",
              "description": "HTTP-method"
            },
            "url": {
              "type": "string"
            }
          },
          "required": [
            "method",
            "url"
          ]
        },
        "params": {
          "description": "Query parameters",
          "type": "array",
          "items": {
            "$ref": "#/definitions/NameValueParam"
          }
        },
        "body": {
          "$ref": "#/definitions/IRequestBody",
          "description": "Body parameters"
        },
        "headers": {
          "description": "Headers",
          "type": "array",
          "items": {
            "$ref": "#/definitions/NameValueParam"
          }
        },
        "disabledInheritedHeaders": {
          "description": "Names of disabled headers",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "strictSSL": {
          "$ref": "#/definitions/StrictSSLOptions",
          "description": "Requires SSL certificates be valid"
        }
      },
      "required": [
        "body",
        "disabledInheritedHeaders",
        "headers",
        "params",
        "request",
        "strictSSL"
      ]
    },
    "RequestMethod": {
      "enum": [
        "DELETE",
        "GET",
        "OPTIONS",
        "PATCH",
        "POST",
        "PUT"
      ],
      "type": "string"
    },
    "NameValueParam": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "value": {
          "type": "string"
        },
        "isChecked": {
          "type": "boolean"
        }
      },
      "required": [
        "name",
        "value"
      ]
    },
    "IRequestBody": {
      "type": "object",
      "properties": {
        "type": {
          "$ref": "#/definitions/RequestBodyType",
          "description": "Type of body"
        },
        "jsonBody": {
          "description": "JSON string of body",
          "type": "string"
        },
        "xmlBody": {
          "description": "XML string of body",
          "type": "string"
        },
        "textBody": {
          "type": "string"
        },
        "formData": {
          "description": "multipart/form-data form",
          "type": "array",
          "items": {
            "$ref": "#/definitions/RequestStepFormData"
          }
        },
        "formURLEncoded": {
          "description": "application/x-www-form-urlencoded form",
          "type": "array",
          "items": {
            "$ref": "#/definitions/NameValueParam"
          }
        },
        "file": {
          "description": "Link on file, which will be used as a content for body",
          "type": "string"
        }
      },
      "required": [
        "file",
        "formData",
        "formURLEncoded",
        "jsonBody",
        "textBody",
        "type",
        "xmlBody"
      ]
    },
    "RequestBodyType": {
      "enum": [
        "File",
        "FormData",
        "FormURLEncoded",
        "Json",
        "Text",
        "Xml"
      ],
      "type": "string"
    },
    "RequestStepFormData": {
      "type": "object",
      "properties": {
        "type": {
          "$ref": "#/definitions/FormDataField"
        },
        "name": {
          "type": "string"
        },
        "value": {
          "type": "string"
        },
        "isChecked": {
          "type": "boolean"
        }
      },
      "required": [
        "name",
        "type",
        "value"
      ]
    },
    "FormDataField": {
      "enum": [
        "File",
        "Text"
      ],
      "type": "string"
    },
    "StrictSSLOptions": {
      "enum": [
        "Inherit",
        "No",
        "Yes"
      ],
      "type": "string"
    },
    "IAuthorizationData": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string"
        }
      },
      "required": [
        "type"
      ]
    },
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
```


# Assertion

**Assertion** nodes are used in writing tests. Each **Assertion** node consists of several assertions - statements that allow you to test certain assumptions. When you run an **Assertion** node, you run all assertions. If any test raises an error, the entire **Assertion** node run is unsuccessful.

An **Assertion** node can only be created as a child of a [RequestStep](/master/node-types/request-step) node. At the same time, a [RequestStep](/master/node-types/request-step) node can have only one **Assertion** node as a child.

There are two ways to create an **Assertion** node. First, choose **Add node -> Assertion** from a [RequestStep](/master/node-types/request-step) node context menu. Second, click **+ CREATE NEW ASSERTION NODE** on the Assertion tab of the [RequestStep](/master/node-types/request-step) node response area.

![Creating an Assertion node in RequestStep node response area](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LglKEp1bR5zbag8DHT5%2F-LglNkc9IYC5Z5npsofj%2F1.png?alt=media\&token=3708e65c-0629-4a4d-be19-d6e1f7e25f00)

This is how an **Assertion** node looks like in the project tree:

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LglKEp1bR5zbag8DHT5%2F-LglWSOff34C1u6ONDNC%2F2.png?alt=media\&token=fbe8e5ff-8fb5-4d97-b3fd-22dc984fe32c)

If an **Assertion** node was run successfully, its icon in the project tree looks like that:

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LglKEp1bR5zbag8DHT5%2F-LglWqJsXhc_nr4PDNhL%2F3.png?alt=media\&token=e2e3a2ab-01b0-466d-b5af-a74031348ce9)

If an **Assertion** node was run unsuccessfully, its icon in the project tree changes to this:

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LglKEp1bR5zbag8DHT5%2F-LglXTbrsgXUyMQNolOG%2F4.png?alt=media\&token=cb5a1ebf-2504-49b9-b0a3-fba5e7908d13)

There are several actions you can do with this node type:

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgrQs60raNlT-LTs16T%2F-LgrR_jGcOtaB-mjPOUu%2F11.png?alt=media\&token=cc66afd5-b77b-4e50-b6b5-84a0bdd996bd)

* **Remove node.** Use it to delete the node.
* **Run.** Run the node.
* **Show in explorer.** Open the folder with the node in the file manager.

The **Assertion** node tab has the following interface:

![The Assertion node interface](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LglKEp1bR5zbag8DHT5%2F-Lgld-aq-pWwX-HKCfKO%2F5.png?alt=media\&token=dad6bee7-5d42-46b7-9c38-d1bd4bfbccd0)

In the screenshot you can see the following parts of the interface:

1. The control panel
2. The settings panel for the chosen assertion
3. The list of all assertions

You can see the following buttons on the control panel:

* **RUN** - runs all assertions in the list.
* **FIX ERRORS** - fixes assertions errors if possible The button is available if there are some errors in the assertions. The **error fixing** algorithms are described for each assertion type separately.
* **DISABLE ERRORS** - disables the failed assertions. They won't be included in further runs. The button is available if there are some errors in the assertions.
* **+ ADD ASSERTION** - adds an assertion to the list.

The assertions list is right under the control panel. Each element of the list has the looks like this:

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgqpgXPdLrM2Ecs9J0d%2F-Lgr-8n8zOAAdDhDpJ9O%2F6.png?alt=media\&token=87cf4813-dc69-43f9-a1b7-7cbfa698d7db)

In the screenshot you can see the following parts of the interface:

1. Status highlighting. Its status is gray, if the assertion hasn't been run yet, red - if the run failed, green - if it was successful.
2. The assertion type icon.
3. The assertion type.
4. Delete the assertion.
5. Disable the assertion. It won't be included in the further runs.
6. Run the assertion.
7. Fix the assertion.

Note that the controls 4, 5, 6, and 7 appear when you hover the mouse over the assertion.

The interface of the settings panel depends on the chosen assertion type. We'll discuss all of them in the next sections.

### File Representation

An **Assertion** node is stored in the \<nodename>.yml file, where \<nodename> is the **Assertion** node name. The file has the following format:

```javascript
{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of Assertion node",
      "const": "Assertion",
      "type": "string"
    },
    "assertions": {
      "description": "List of assertions",
      "type": "array",
      "items": {
        "$ref": "#/definitions/AbstractAssertion"
      },
      "default": []
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "assertions",
    "children",
    "name",
    "type",
    "variables"
  ],
  "definitions": {
    "AbstractAssertion": {
      "oneOf": [
        {
          "$ref": "#/definitions/CompareAssertion"
        },
        {
          "$ref": "#/definitions/ContainsAssertion"
        },
        {
          "$ref": "#/definitions/XPathAssertion"
        },
        {
          "$ref": "#/definitions/ScriptAssertion"
        }
      ]
    },
    "CompareAssertion": {
      "type": "object",
      "properties": {
        "type": {
          "description": "Type of Compare assertion",
          "const": "compare",
          "type": "string"
        },
        "actualValue": {
          "description": "Actual value",
          "type": "string",
          "default": "${$response.body}"
        },
        "operator": {
          "$ref": "#/definitions/CompareOperator",
          "description": "Operator",
          "default": "equal"
        },
        "expectedValue": {
          "description": "Expected value",
          "type": "string"
        },
        "disabled": {
          "type": "boolean",
          "default": false
        }
      },
      "required": [
        "actualValue",
        "disabled",
        "expectedValue",
        "operator",
        "type"
      ]
    },
    "CompareOperator": {
      "enum": [
        "equal",
        "greater",
        "greater or equal",
        "less",
        "less or equal",
        "not equal"
      ],
      "type": "string"
    },
    "ContainsAssertion": {
      "type": "object",
      "properties": {
        "type": {
          "description": "Type of Contains assertion",
          "const": "contains",
          "type": "string"
        },
        "text": {
          "description": "Text to be searched",
          "type": "string",
          "default": "${$response.body}"
        },
        "value": {
          "description": "Value for search in text",
          "type": "string"
        },
        "disabled": {
          "type": "boolean",
          "default": false
        }
      },
      "required": [
        "disabled",
        "text",
        "type",
        "value"
      ]
    },
    "XPathAssertion": {
      "type": "object",
      "properties": {
        "type": {
          "description": "Type of Xpath assertion",
          "const": "xpath",
          "type": "string"
        },
        "text": {
          "description": "Text to be searched",
          "type": "string",
          "default": "${$response.body}"
        },
        "path": {
          "description": "XPath selector",
          "type": "string"
        },
        "expectedValue": {
          "description": "Expected value",
          "type": "string"
        },
        "disabled": {
          "type": "boolean",
          "default": false
        }
      },
      "required": [
        "disabled",
        "expectedValue",
        "path",
        "text",
        "type"
      ]
    },
    "ScriptAssertion": {
      "type": "object",
      "properties": {
        "type": {
          "description": "Type of Script assertion",
          "const": "script",
          "type": "string"
        },
        "script": {
          "description": "Assertion script",
          "type": "string",
          "default": "`function test(assertion, variables) {\n  // It should return true if test is passed\n  // return true;\n}`"
        },
        "disabled": {
          "type": "boolean",
          "default": false
        }
      },
      "required": [
        "disabled",
        "script",
        "type"
      ]
    },
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
```


# Compare

A **compare assertion** is used to compare 2 values. There is such thing as in this assertion type as comparator - an operation that compares two values. There are several comparator types:

* **equal** - checks if the values are equal;
* **not equal** - checks if the values are not equal;
* **greater** - checks if the current value is greater than the expected one;
* **greater or equal** - checks if the current value is greater than or equal to the expected one;
* **less** - checks if the current value is less than the expected one;
* **less or equal** - checks if the current value is less than or equal to the expected one.

The **compare** assertion interface is as follows:

![The compare assertion interface](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgrEVNgUqXS1jfYYcE8%2F-LgrGVxEU1ocjiTPcusu%2F7.png?alt=media\&token=831cae9a-2148-4cfc-a831-b85af79487d2)

The following fields are shown in the screenshot:

* **Actual value**;
* **Operator** - the comparator type;
* **Expected value**.

### Error Fixing

The error fixing algorithm depends on the comparator type.

* **equal** -  the current value is assigned to the expected one;
* **not equal** - the comparator type changes to equal;
* **greater** - the comparator type changes to greater or equal and the current value is assigned to the expected one;
* **greater or equal** - the current value is assigned to the expected one;
* **less** - the comparator type changes to less or equal and the current value is assigned to the expected one;
* **less or equal** - the current value is assigned to the expected one.

### File Representation

The assertion has the `compare` type in the file. The type description can be found in the [Assertion file representation](/master/node-types/assertion-node#failovoe-predstavlenie) documentation in the `#/definitions/CompareAssertion` definition.


# Contains

**Contains assertion** is used to check if a string contains another string.

This assertion interface is as follows:

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgrKP3zFd8Pe9rytuu8%2F-LgrKr5Hl7BG0PaWPWPs%2F8.png?alt=media\&token=6767e697-c67f-4331-a7ff-4fb68dc8aabd)

The following assertion fields are shown in the screenshot:

* **Text** - the text to find the value in;
* **Value** - the value that needs to be found.

### Error Fixing

This assertion doesn't have any error fixing algorythm.

### File Representation

The assertion has the `contains` type in the file. The type description can be found in the[ Assertion file representation](/master/node-types/assertion-node#failovoe-predstavlenie) documentation in the `#/definitions/ContainsAssertion` definition.


# Script

A **Script assertion** allows to write a JavaScript test script. The script itself is a function named `test`, that takes an assertion object and an object with variables (a key/value pair). If the function returns `true` the test is successfull. If it returns `false` or raises an exception, then the **Script assertion** failed.

The **Script** assertion interface is as follows:

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgrKP3zFd8Pe9rytuu8%2F-LgrMjndSzO9ONPPuSAl%2F9.png?alt=media\&token=d18d4f82-07f7-4b8e-a441-98a9f34200fd)

**Script** is the only field this assertion has, and it contains the script described above.

### Error Fixing

This assertion doesn't have any error fixing algorythm.

### File Representation

The assertion has the `script` type in the file. The type description can be found in [Assertion file representation](/master/node-types/assertion-node#failovoe-predstavlenie) Assertion file representation documentation in the `#/definitions/ScriptAssertion` definition.


# XPath

**XPath assertion** allows to verify the response message with an XPath expression match.

This assertion interface is as follows:

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgrKP3zFd8Pe9rytuu8%2F-LgrOGxjrh9Cw-cjXRFN%2F10.png?alt=media\&token=9e0c10ae-4a20-48ad-b946-23c284ba3b7d)

This assertion interface is as follows:

* **Text** - the text to find the value in;
* **Path** - an XPath selector;
* **Expected value** - the value that should be matched.

### Fixing Errors

To fix an error, the XPath selector value is assigned to the expected value.

### File Representation

The assertion has the `xpath` type in the file. The type description can be found in [Assertion file representation](/master/node-types/assertion-node#failovoe-predstavlenie) Assertion file representation documentation in the `#/definitions/ScriptAssertion` definition.


# Link

A Link node allows to reuse another RequestStep (including Assertion) and Folder nodes.

## How It Works&#x20;

A **Link** node allows to override the values of the running node variables. A **Link** node runs another node, passing the variables set by a user to it. After the run, dynamic variables of the node are set as dynamic variables of the **Link** node parent group. Thus the result of the run can be seen from any of the neighboring **Link** node.

#### You can refer to:

* a [RequestStep](/master/node-types/request-step) node;
* a [Folder](/master/node-types/folder) node.

#### You can NOT refer to:

* Another **Link** node (including self);
* Any child of a **Link** node (since it'll cause an endless loop).

{% hint style="info" %}
A Link node allows to override the variables values of the parent node.
{% endhint %}

{% hint style="warning" %}
If you delete the link a Link node refers to, the Link node wouldn't run until you provide a proper link.‌
{% endhint %}

## Parent Node

Create a parent node you need to refer to and create all necessary [static variables](/master/variables/user-variables/static-variables), for example `postID`. You can leave value fields empty.

![Creating variables of the parent node](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh4Cqr7blB3FIHuJKO_%2F-Lh4ImcwyK7QPl4D0WmO%2Flink-parent-var.jpg?alt=media\&token=e7b7f466-1fb2-4924-ad13-21d465e236ae)

## Link Node

Create a **Link** node and specify its parent, and you'll see all the variables you created for the parent earlier. You can use any variables or a static value as an overriden value.

![Creating a Link node and specifying its parent](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh4SwfExBEdfIX5P1HC%2F-Lh4VLCDdb58k9EvDvPd%2Flink-node-ex2.gif?alt=media\&token=98246c48-371b-43f2-9e7c-cbcb3abcce80)

## Scenario Example

Let's see an example of running a [RequestStep](/master/node-types/request-step) node as a **Link** node to delete a record.

### Creating a parent node

1. Create a new [RequestStep](/master/node-types/request-step) node named deletePost.
2. Request type - DELETE.
3. Use the URL:[ https://testmace-stage.herokuapp.com/posts/${id}](< https://testmace-stage.herokuapp.com/posts/${id}>)
4. Create for this node a [static ](/master/variables/user-variables/static-variables)`id` variable without specifying its value.

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFNVvYEIb0YxX8C1eJ%2F-LhFNaKST0F0_1ymsVjY%2Flin-sc-1.gif?alt=media\&token=7e93b682-f174-4ea0-b928-3714bd0fd476)

### **Creating a scenario**

* Create a [Folder](/master/node-types/folder) node named **scenario**
* Add a [RequestStep](/master/node-types/request-step) node named **createPost** to the scenario:
  * Request type: POST;
  * URL: [https://testmace-stage.herokuapp.com/posts/](< https://testmace-stage.herokuapp.com/posts/${id}>)
  * Body: JSON request `{"title":"will delete with link node"}`
  * Run the request and the `id` of the created record to the [dynamic variable ](/master/variables/user-variables/dynamic-variables)`postid` of the **Scenario** node.

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFNVvYEIb0YxX8C1eJ%2F-LhFQ274uAmdp39J7uz7%2Flin-sc-2.gif?alt=media\&token=6b7597c8-32dc-4598-8e42-94ac76ad3cbe)

* Create a **Link** node named **deleteLink**.
  * Specify the **project/deletePost** node as a parent.
  * &#x20;Set the`${$dynamicVar.postId}` as an overridden value for the `id` variable of the parent **deletePost** node.
* Create a [RequestStep](/master/node-types/request-step) node **checkIfExists** to check if the record was deleted:
  * Request type: GET;
  * URL: <https://testmace-stage.herokuapp.com/posts/${$dynamicVar.postId}>
  * 404 is an expected server response.

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFNVvYEIb0YxX8C1eJ%2F-LhFRaKVohkgWjndBb3E%2Flin-sc-3.gif?alt=media\&token=d61d5fe1-334b-429c-b7d0-3aa78766f533)

## Import the Project [by URL](/master/other-features/import/shared)

{% file src="/files/-LglQkEld\_hqNoia9IpW" %}

### File Representation

A **Link** node is a folder with the node name, containing the index.yml file with the following format:

```javascript
{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of Link node",
      "const": "Link",
      "type": "string"
    },
    "linkedNode": {
      "$ref": "#/definitions/NodeReference",
      "description": "Link to node"
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "children",
    "linkedNode",
    "name",
    "type",
    "variables"
  ],
  "definitions": {
    "NodeReference": {
      "type": "object",
      "properties": {
        "refNodePath": {
          "description": "Absolute path to node",
          "type": "string"
        },
        "type": {
          "description": "Marker of reference entity",
          "const": "reference",
          "type": "string",
          "default": "reference"
        }
      },
      "required": [
        "refNodePath",
        "type"
      ]
    },
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
```


# API description

TestMace provides powerful features for API description, including import from Swagger 2.0/ Openapi 3.0. The following nodes are responsible for these features:

* [ApiRootFolder](/master/node-types/api-description/apirootfolder) - a root node for API description;
* [ApiFolder](/master/node-types/api-description/apifolder) -  a node for grouping another API nodes;
* [ApiRoute](/master/node-types/api-description/apiroute) - a node for describing a specific endpoint.

You'll learn more about each of these nodes in the next sections.


# ApiRootFolder

**ApiRootFolder** - a root node of the API description subnode. Just like the [Project](/master/node-types/project) node, it is a root element, and in the subnode there can be only one element of this type. It is functionally identical to an [ApiFolder](/master/node-types/api-description/apifolder) node.

You can create this node by:

* Adding it from the [Project](/master/node-types/project) node context menu;
* Using import from API description format.

### File Representation

A **ApiRootFolder** node is a folder with the node name, containing the index.yml file with the following format:

```javascript
{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of ApiRootFolder node",
      "const": "ApiRootFolder",
      "type": "string"
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "children",
    "name",
    "type",
    "variables"
  ],
  "definitions": {
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
```


# ApiFolder

An **ApiFolder** is identical to a Follder node and is used to group nodes of another types (ApiRoute in this case).

There are two ways to create an **ApiFolder** node:

* Adding it from the [ApiRootFolder](/master/node-types/api-description/apirootfolder) node context menu;
* Using import from API description format.

This is how an ApiFolder node looks like in the project tree:

![An ApiFolder node in the project tree](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgsCA8b3GaVRHvOyk3b%2F-LgsCm8cE2tYZ-qYFFWc%2F1.png?alt=media\&token=9a7b2f6f-24f1-455c-abc7-e0ce036e7172)

An **ApiFolder** node has the following context menu:

![The ApiFolder context menu](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LlffkWjAr-L0Akq3f5g%2F-LlfflYo-YCjuMYdtA1b%2Fimage.png?alt=media\&token=d6e70623-09b6-49ce-8360-f1f311509720)

* **Add node.** Click on it to add a child node. You can choose a node type in the submenu.
* **Rename.** Change the node name.
* **Duplicate.** Make a copy of the node. The new node will be named NodeName \[Copy \[number]].
* **Remove node.** Use it to delete the node.
* **Show in explorer.** Open the folder with the node in the file manager.

‌The **ApiFolder** node tab has the following interface:

![The ApiFolder node tab interface](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgsCA8b3GaVRHvOyk3b%2F-LgsGJ-A7inEue6zqfAI%2F3.png?alt=media\&token=702d4b06-f2b1-4cc6-9b8b-da4887fee8c4)

In the screenshot you can see the following parts of the tab:

* The [user-defined variables](/master/variables/user-variables) managing dialog;
* Child nodes list.

### File Representation

An **ApiFolder** node is a folder with the node name, containing the index.yml file with the following format:

```javascript
{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of ApiFolder node",
      "const": "ApiFolder",
      "type": "string"
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "children",
    "name",
    "type",
    "variables"
  ],
  "definitions": {
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
```


# ApiRoute

The node is used for the specific endpoint description. Its interface is smilar to the one of a [RequestStep](/master/node-types/request-step) node. No surprises, in both cases we deal with HTTP requests.

The main features of an ApiRoute node are:

* Describing request HTTP headers, query parameters, body parameters and response HTTP codes, HTTP headers and body parameters;
* Using types for describing every header, query parameter, and body parameter. Supported types: `string`, `number`, `integer`, `boolean`, `array`, and `object`;
* Describing every entity;
* Describing a number of request body parameters (based on content-type);
* Describing a number of possible responses;
* Creating a request out of the description;
* Autocompletion of URLs, HTTP headers, query and body parameters in [RequestStep](/master/node-types/request-step) nodes.

## Interface Overview

To create an **ApiRoute** node right-click in the [ApiFolder](/master/node-types/api-description/apifolder) node and choose **Add node** -> **ApiRoute.**

### An ApiRoute node in the project tree

This is how it looks like in the project tree:

![An ApiRoute node in the project tree](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh-8_-iIrkPlQPxdRGh%2F-Lh0BtMevPi0x9AEv_1R%2F1.png?alt=media\&token=5d8a9b58-fcab-4508-be4b-01e09b6ea59d)

The icon of these nodes is the HTTP method name. An **ApiRoute** node has the following context menu:

![The ApiRoute context menu](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh-8_-iIrkPlQPxdRGh%2F-Lh0CEFd52BzWxfzlx-f%2F2.png?alt=media\&token=e1bec327-9045-4e8a-975c-359648e846cf)

* **Rename.** Change the node name.
* **Duplicate.** Make a copy of the node. The new node will be named NodeName \[Copy \[number]].
* **Remove node.** Use it to delete the node.
* **Show in explorer.** Open the folder with the node in the file manager.

### The **ApiRoute** Tab Interface

The **ApiRoute** node tab looks like this:

![The ApiRoute tab](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh-8_-iIrkPlQPxdRGh%2F-Lh0DZRq-N6KeKpQN0Mg%2F3.png?alt=media\&token=2a87fbb8-10f7-4e67-b495-756c654835bc)

#### General Request Parameters

Look a little closer at the top part of the tab:

![The top part of the ApiRoute node tab](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh-8_-iIrkPlQPxdRGh%2F-Lh0GJ0lG6uWS8DImhSP%2F4.png?alt=media\&token=666d82ea-0637-40bb-b3e1-582ef7b8013e)

In the screenshot you can see the following parts of the interface.

1. HTTP method. The list of available methods is identical to the one of [RequestStep](/master/node-types/request-step) nodes.
2. URL with the [variables mechanism support](/master/variables/variables).
3. The [Variables dialog](/master/variables/user-variables) button.
4. The button to create a request out of the current API description.
5. The request description (text).

#### Request parameters description area

At the bottom left of the screen you can see the request description area. There are 3 tabs - **Headers**, **Query parameters**, and **Body**, that you can use to edit HTTP headers, query parameters, and request body parameters accordingly.

Let's look at the **Headers** tab. You can see that it is a table with the following fields:

* Header name;
* Header value type (as described above);
* Description.

All possible standard operations are supported.

The **Query Parameters** tab is used for editing query parameters, and it is functually identical to the **Headers** tab.

As mentioned before, in an **ApiRoute** node you can describe several bodies for a single request. For example, a single endpoint can take the data both with the `application/json` and `application/xml` `Content-Type`. There are several different content-type tabs on the **Body** tab:

![The Body tab of the request description interface](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh-8_-iIrkPlQPxdRGh%2F-Lh0NcNadYlwdkCel30V%2F5.png?alt=media\&token=c8398894-1259-49f9-9eb9-5f1311d5f353)

In the screenshot you can see the following parts of the interface:

1. The button that edits the current `content-type` value. If you click on it, you'll see a text box instead, where you can enter your `content-type` value.
2. Delete the request body button.
3. Add the request body button.
4. The current `content-type` of the node.
5. The request body editing area.

The request body editing area changes according to the `content-type` value:  if the `content-type` is`application/x-www-form-urlencoded` or`multipart/form-data`, then the editing area looks like a table (like in the **Headers** tab), otherwise it has the text representation like in the screenshot above. The description format is [OpenAPI](https://swagger.io/specification/#requestBodyObject) in this case.

#### Request parameters description area

There is a response editing panel at the bottom right of the **ApiRoute** tab interface. As mentioned before, TestMace allows you to describe several responses for a single endpoint. Look at the interface:

![The server response editing area](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh-8_-iIrkPlQPxdRGh%2F-Lh0aybnLxCtZilFBOpr%2F6.png?alt=media\&token=af6ab711-6523-4a2c-b2f2-e70f53c44538)

There is a separate tab for each response. On these tabs you can edit response codes, descriptions, and even response HTTP headers and bodies. The interface is similar to ones in the request area.

### Integration with RequestStep nodes

TestMace provides integration with **ApiRoute** nodes in **RequestStep** nodes. The integration is implemented in autocompletion of URLs, HTTP headers, query parameters, **RequestStep** nodes' request body parameters. Autocompletion works for all **ApiRoute** nodes URLs, but for other parameters the following algorithm is applied:

* Take the **RequestStep** node method and URL;
* Find all **ApiRoute** nodes with this method and URL;
* Run the specified parameter (e. g. an HTTP header) search among the **ApiRoute** nodes.

## File Representation

An **ApiRoute** node is a folder with the node name, containing the index.yml file with the following format:

```javascript
{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of ApiRoute node",
      "const": "ApiRoute",
      "type": "string"
    },
    "url": {
      "type": "string",
      "default": ""
    },
    "method": {
      "$ref": "#/definitions/RequestMethod"
    },
    "description": {
      "type": "string",
      "default": ""
    },
    "requests": {
      "$ref": "#/definitions/ApiRequests",
      "description": "List of requests"
    },
    "responses": {
      "description": "List of responses",
      "type": "array",
      "items": {
        "$ref": "#/definitions/ResponseParameters"
      },
      "default": []
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "children",
    "description",
    "method",
    "name",
    "requests",
    "responses",
    "type",
    "url",
    "variables"
  ],
  "definitions": {
    "RequestMethod": {
      "enum": [
        "DELETE",
        "GET",
        "OPTIONS",
        "PATCH",
        "POST",
        "PUT"
      ],
      "type": "string"
    },
    "ApiRequests": {
      "type": "object",
      "properties": {
        "queryParameters": {
          "description": "List of query parameters",
          "type": "array",
          "items": {
            "$ref": "#/definitions/QueryParameter"
          }
        },
        "headers": {
          "description": "List of headers",
          "type": "array",
          "items": {
            "$ref": "#/definitions/QueryParameter"
          }
        },
        "cookies": {
          "description": "List of cookies",
          "type": "array",
          "items": {
            "$ref": "#/definitions/QueryParameter"
          }
        },
        "bodies": {
          "description": "List of bodies",
          "type": "array",
          "items": {
            "$ref": "#/definitions/RequestParameters"
          },
          "default": []
        }
      },
      "required": [
        "bodies",
        "cookies",
        "headers",
        "queryParameters"
      ]
    },
    "QueryParameter": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "type": {
          "enum": [
            "array",
            "boolean",
            "integer",
            "number",
            "object",
            "string"
          ],
          "type": "string"
        },
        "description": {
          "type": "string"
        }
      },
      "required": [
        "name",
        "type"
      ]
    },
    "RequestParameters": {
      "type": "object",
      "properties": {
        "contentType": {
          "type": "string"
        },
        "schema": {
          "anyOf": [
            {
              "$ref": "#/definitions/SchemaRef"
            },
            {
              "$ref": "#/definitions/OneOf"
            },
            {
              "$ref": "#/definitions/AllOf"
            },
            {
              "$ref": "#/definitions/AnyOf"
            },
            {
              "$ref": "#/definitions/ObjectMember"
            },
            {
              "$ref": "#/definitions/ArrayMember"
            },
            {
              "$ref": "#/definitions/ScalarMember"
            }
          ]
        }
      },
      "required": [
        "contentType",
        "schema"
      ]
    },
    "SchemaRef": {
      "type": "object",
      "properties": {
        "$ref": {
          "type": "string"
        }
      },
      "required": [
        "$ref"
      ]
    },
    "OneOf": {
      "type": "object",
      "properties": {
        "oneOf": {
          "type": "array",
          "items": {
            "anyOf": [
              {
                "$ref": "#/definitions/SchemaRef"
              },
              {
                "$ref": "#/definitions/OneOf"
              },
              {
                "$ref": "#/definitions/AllOf"
              },
              {
                "$ref": "#/definitions/AnyOf"
              },
              {
                "$ref": "#/definitions/ObjectMember"
              },
              {
                "$ref": "#/definitions/ArrayMember"
              },
              {
                "$ref": "#/definitions/ScalarMember"
              }
            ]
          }
        }
      },
      "required": [
        "oneOf"
      ]
    },
    "AllOf": {
      "type": "object",
      "properties": {
        "allOf": {
          "type": "array",
          "items": {
            "anyOf": [
              {
                "$ref": "#/definitions/SchemaRef"
              },
              {
                "$ref": "#/definitions/OneOf"
              },
              {
                "$ref": "#/definitions/AllOf"
              },
              {
                "$ref": "#/definitions/AnyOf"
              },
              {
                "$ref": "#/definitions/ObjectMember"
              },
              {
                "$ref": "#/definitions/ArrayMember"
              },
              {
                "$ref": "#/definitions/ScalarMember"
              }
            ]
          }
        }
      },
      "required": [
        "allOf"
      ]
    },
    "AnyOf": {
      "type": "object",
      "properties": {
        "anyOf": {
          "type": "array",
          "items": {
            "anyOf": [
              {
                "$ref": "#/definitions/SchemaRef"
              },
              {
                "$ref": "#/definitions/OneOf"
              },
              {
                "$ref": "#/definitions/AllOf"
              },
              {
                "$ref": "#/definitions/AnyOf"
              },
              {
                "$ref": "#/definitions/ObjectMember"
              },
              {
                "$ref": "#/definitions/ArrayMember"
              },
              {
                "$ref": "#/definitions/ScalarMember"
              }
            ]
          }
        }
      },
      "required": [
        "anyOf"
      ]
    },
    "ObjectMember": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "object"
          ]
        },
        "properties": {
          "$ref": "#/definitions/SchemaMember"
        },
        "required": {
          "type": "boolean"
        },
        "additionalProperties": {
          "$ref": "#/definitions/ScalarMember"
        },
        "description": {
          "type": "string"
        }
      },
      "required": [
        "type"
      ]
    },
    "SchemaMember": {
      "type": "object",
      "additionalProperties": {
        "anyOf": [
          {
            "$ref": "#/definitions/SchemaRef"
          },
          {
            "$ref": "#/definitions/OneOf"
          },
          {
            "$ref": "#/definitions/AllOf"
          },
          {
            "$ref": "#/definitions/AnyOf"
          },
          {
            "$ref": "#/definitions/ObjectMember"
          },
          {
            "$ref": "#/definitions/ArrayMember"
          },
          {
            "$ref": "#/definitions/ScalarMember"
          }
        ]
      }
    },
    "ArrayMember": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "array"
          ]
        },
        "items": {
          "anyOf": [
            {
              "$ref": "#/definitions/SchemaRef"
            },
            {
              "$ref": "#/definitions/OneOf"
            },
            {
              "$ref": "#/definitions/AllOf"
            },
            {
              "$ref": "#/definitions/AnyOf"
            },
            {
              "$ref": "#/definitions/ObjectMember"
            },
            {
              "$ref": "#/definitions/ArrayMember"
            },
            {
              "$ref": "#/definitions/ScalarMember"
            }
          ]
        },
        "description": {
          "type": "string"
        }
      },
      "required": [
        "items",
        "type"
      ]
    },
    "ScalarMember": {
      "type": "object",
      "properties": {
        "type": {
          "$ref": "#/definitions/ScalarSchemaType"
        },
        "description": {
          "type": "string"
        }
      },
      "required": [
        "type"
      ]
    },
    "ScalarSchemaType": {
      "enum": [
        "boolean",
        "integer",
        "number",
        "string"
      ],
      "type": "string"
    },
    "ResponseParameters": {
      "type": "object",
      "properties": {
        "code": {
          "description": "Http-code (e.g. 200, 404)",
          "type": "string"
        },
        "description": {
          "type": "string"
        },
        "headers": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/QueryParameter"
          }
        },
        "content": {
          "$ref": "#/definitions/RequestParameters",
          "description": "Response body"
        }
      },
      "required": [
        "code",
        "content"
      ]
    },
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
```


# API Description Import

TestMace allows not only to create API description manually, but also to import an existing documentation from Swagger 2.0 and OpenAPI 3.0.

&#x20;You can import API description right from the + context menu by choosing **Import -> Swagger** (there is the same menu in the Scratches area as well):

![The project context menu](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh0oCq6Wm4ISZdLGZoy%2F-Lh0pQi2448lA3Sg_HP2%2F7.png?alt=media\&token=9dc8386d-779f-47c7-abd3-bdbad3aae3c4)

You'll see the next dialog:

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh0oCq6Wm4ISZdLGZoy%2F-Lh0pb1rAlqZWHqkw6yb%2F8.png?alt=media\&token=99d91af3-8945-4f38-80e6-99f73c840c1f)

As you can see, both import from file and downloading API from the remote server at a specific URL are supported. The imported description is added to the project tree after you click on the **OK** button.

### API Description Update

You can also update an existing API description. To do that, choose **Update api** from the [ApiRootFolder](/master/node-types/api-description/apirootfolder) context menu. You'll see a dialog similar to the import API dialog. All changes you make in API description will be canceled after the update.


# Broken

When you open the project, you might see that some nodes can't be loaded. For instance, some merge conflicts can cause that. At this point, all possible nodes are loaded to the project, but when you open it, the following warning is shown:

![The warning about nodes loading failure](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh45m0ccQGFTGtebHna%2F-Lh47_hFU3Hzq6tvEfAJ%2F1.png?alt=media\&token=19706564-df33-4e96-b2bd-7c6fff405f12)

In the project you'll see the nodes with these icons:

![A Broken node in the project tree](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh45m0ccQGFTGtebHna%2F-Lh47v9x1oGTldp6cMow%2F2.png?alt=media\&token=99552415-da4b-4051-95d5-32b54d6e5f80)

This is a **Broken** node. It can not be created manually, it is shown if some node can't be loaded into the project. A **Broken** node has the following context menu:

![The Broken node context menu](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh45m0ccQGFTGtebHna%2F-Lh4Bqau5j1oKGEsBKpY%2F3.png?alt=media\&token=e7bf5b87-1cd7-455c-85d9-700d05a798ef)

* **Show in explorer.** Open the folder with the node in the file manager.

There is no tab for a **Broken** node to be opened in, and it can't have any child nodes. The main purpose of this node type is to help you fix the error.


# Script

This is a node that runs JavaScript scripts. It is going to help you solve the following tasks:

* create complex tests to check the results of one or several nodes;
* generate test data;
* change other nodes' variables;
* perform operations to bring the tested system to a specific state (set\_up, tear\_down);
* debug and access all nodes' states.

## Editing a Script&#x20;

The script node editing window is divided into two parts: the code editor and the console output window. To close the console window click the <img src="https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LkTrcfYjzYT8him1F5A%2F-LkTtHTOFH08FuWDRtuM%2FTestMace%202019-07-19%2015.42.04.png?alt=media&amp;token=c964fc73-7da1-4bbb-b8a9-57f1557c97c2" alt="" data-size="original"> button.

You can use the toolbar above the output window to manage the console behavior:

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LkTrcfYjzYT8him1F5A%2F-LkTuOuoBQywn7OdMTcW%2FTestMace%202019-07-19%2015.43.23.png?alt=media\&token=1d946a3b-1754-4a50-96de-a220986ca1fe)/![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LkTrcfYjzYT8him1F5A%2F-LkTtnth9AoCIoum4eB6%2FTestMace%202019-07-19%2015.44.34.png?alt=media\&token=0916f637-f555-4574-848d-5a3af3389b73)- choose what to do with the console output. ![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LkTrcfYjzYT8him1F5A%2F-LkTuOuoBQywn7OdMTcW%2FTestMace%202019-07-19%2015.43.23.png?alt=media\&token=1d946a3b-1754-4a50-96de-a220986ca1fe) -  clear the window every time you run a script, ![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LkTrcfYjzYT8him1F5A%2F-LkTtnth9AoCIoum4eB6%2FTestMace%202019-07-19%2015.44.34.png?alt=media\&token=0916f637-f555-4574-848d-5a3af3389b73) -  display all previous outputs.

* ![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LkTrcfYjzYT8him1F5A%2F-LkTuLaA994Reilwvjy7%2FTestMace%202019-07-19%2015.44.14.png?alt=media\&token=22602ea9-9fb4-4572-8be9-1dc105e40f5f) - auto scroll to the last line of the output
* ![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LkTrcfYjzYT8him1F5A%2F-LkTuK4OIWVEjikfBLgc%2FTestMace%202019-07-19%2015.43.48.png?alt=media\&token=6ffc25fe-ef66-48f3-90e6-7830da850d82) - clear the console window

## Running a Script

Click the `RUN` button to execute the script. The execution continues to the last line of the code and finishes when all asynchronous tasks are done (e.g.`setTimeout`). The execution is successful if:

* there are no syntax errors in the code;
* the exceptions raised had been handled;
* the execution took no more than 30 seconds (otherwise the execution will be terminated).

{% hint style="success" %}
Since the call to the script is wrapped in a function, you need to use the `return;`command to avoid errors while terminating the script.
{% endhint %}

{% hint style="danger" %}
To get an error after script termination raise an exception with`throw new Error('Something went wrong');`
{% endhint %}

## Libraries

The script is executed in the Node.js virtual environment. Some Node.js modules and all standard JavaScript features supported by V8 are available.

{% hint style="info" %}
The ECMAScript 6 standard is supported as well.
{% endhint %}

### Available Node.js modules

* [fs](https://nodejs.org/docs/latest-v10.x/api/fs.html) - a module for interacting with the file system

### Available third party libraries

* [lodash](https://lodash.com/) - a library that provides lots of utility functions
* [moment.js](https://momentjs.com/) - a library for managing dates
* [CryptoJS](https://cryptojs.gitbook.io/docs/) - a collection of cryptographic algorithms
* [random-js](https://github.com/ckknight/random-js) - a mathematically correct random number generator library
* [faker.js](https://github.com/marak/Faker.js/) - a library that lets you generate random data for different entities properties
* [chai.js](https://www.chaijs.com/) - a library that provides a convenient API for making assertions&#x20;
* [request](https://github.com/request/request) - a library that provides a simplified HTTP client

## Execution Context

Objects and functions of the script's global scope are listed below.

### Accessing third party modules

Every module stated above is automatically added to the execution context and is available in the global scope. <br>

#### lodash

```javascript
const chunks = _.chunk([1, 2, 3, 4], 2);
```

![](https://lh6.googleusercontent.com/Bbf94MZLNaIftEDsdeVw9CrnH5vtcKVYOCJ0guCoyiH8PYTk1rNUUBFTRO5-H1d5gbmtIaYrBMMop9iJZht4DEeoQWWbuQnFZ8nDy9jA2xjBKbsJ8lBycwldHUqQPBU_lJvlY1_e)

#### moment.js

```javascript
const now = moment();
```

![](https://lh3.googleusercontent.com/1wRRxPY02Fp81Ismh5K6lydzKrHSs4Jtvd6XasUVv798jV4TO9ECbEx6h2_ed_ovsKDgml4N6qyv9nfyoZXVHQ_m-OxrcjO8bQZ_T3DSEs8uJKGl3YWFcPJWyeGMIq3bVJRTFcJ2)

#### CryptoJS

```javascript
const hash = crypto.MD5('Message');
```

![](https://lh6.googleusercontent.com/3YzYNVXeUR3R9YcarDMP9FiFzAmypv4tAo2sFFYhdd6uNWRmXUqmTM6iA1Tmob7h266DMRgOzacajJkQ0nlshEBJcqeUCmh9oNPIO4eZI1GkhWJD41s8mSZdzbE0hDoTPl4QL2UY)

#### random-js

```javascript
const randomEngine = new random.Random();
const shuffledArray = randomEngine.shuffle([1,2,3,4,5]);
```

![](https://lh3.googleusercontent.com/e1BiGi1jMy7eTYVbyNZp_A3veHRJeBUh0D-n-rvemlQ6LaJB7oTDF456Hvvn3DpmnDUlO1pSVQM1SzcphIQd33Nj1lcZjfPzRZTltl_whoboKX2jFnuyTzJfm65cXBbS5wfo8RJ5)

#### faker.js

```javascript
const person = { 
    'name': faker.name.findName(),
    'email': faker.internet.email()
};
```

![](https://lh4.googleusercontent.com/XXeDedNzlFvzx-9o7-3ubb9Mtc3iOS21fs0gVyUpeJ-eikRl9E6uRYb81pmZG2vZMHjpCMuxjz6om3w0PkQNp78C5d1C0MmIFZGRk-tBGmC8UT3EAYK4YT5bxdha3JIde7AygmBv)

#### chai.js

```javascript
const foo = 'bar';

// success
assert.equal(foo, 'bar');
expect(foo).to.equal('bar');

// failure
assert.equal(1, 0);
```

![](https://lh3.googleusercontent.com/esHpUUaeRWLbbjJdBoZlsUQS3OaqK2gTDBgIne41dsAM3ekFNS3AixFhDmJQfJbM_7Ibt2XTMxX1UaCbuBdmPPEgiQARHzcVwvdkJVcLwwjjynVQsnPiDIF5zpskmJIwyBiomgMC)

#### request

```javascript
request('https://docs-ru.testmace.com', (error, response, body) => {
  assert.equal(error, null);
  assert.equal(response.statusCode, 200);
  assert.notEqual(body, null);
});
```

![](https://lh5.googleusercontent.com/5xhSJgXEJPciYXsxy9lCFhikDBHZB1QyYGiAGx8ESkAYY9mbbzWiZ_MlVVeI6CKWi2FAW-78ACtGDilzYA-yU74ZLmayE258v5UZnqLqh6yEhhRD5OXifc7yZ0DKbNJVRnziohBQ)

### console.\*

There are different console output methods, such as log, info, warn, error, debug, exception.

Their signatures are equal to the signatures of their standard versions. In the console every method type is highlighted in different color. Each colored line contains the particular row and column where the output method is called. Events of the exception type are displayed along with the stack trace.

![](https://lh6.googleusercontent.com/wXycl_ul2Ck-AODiyKugRkFcxcwUuRDKVPKAhb9BaQH_gv2lHD8cM9n-xT7Mqj8AN2-7TRr3otwQ55kiMMVQq7XVNO4O6hTyKka74K3XllkvrgOCmyAELyDar2ABbMwuxLDTQXJ-)

### Navigating your project

There is an object used to access the project and the current Script node. It is called `tm` and it is available in the global scope.&#x20;

#### tm

* `currentNode: nodeAPI` - the current Script node API&#x20;
* `project: nodeAPI` - the project node API
* `env: envAPI` - the API for accessing the environment variables
* `cookies: cookie[]` - a list of cookies used in the project

#### nodeAPI

* `parent: nodeAPI` - returns a parent node API and null for the project node
* `name: string` - the given node name
* `type: string` - the given node type
* `path: string` - the path to the given node starting with the project root
* `children: nodeAPI[]` - a list of child nodes APIs
* `findChild(name: string): nodeAPI` - searches for the child node using its name and returns null if the node doesn't exist
* `next: nodeAPI` - the API of the next node in the group. If the given node is the last one, null is returned
* `prev: nodeAPI` - the API of the previous node in the group. If the given node is the first one, null is returned
* `nextNodes: nodeAPI[]`  - a list of all next nodes in the group. If the given node is the last one, an empty list is returned
* `prevNodes: nodeAPI[]` - a list of all previous nodes in the group. If the given node is the first one, an empty list is returned
* `vars: object` - the object that stores all static variables of the given node
* `dynamicVars: object` - the object that stores all dynamic variables of the given node
* `setDynamicVar(name: string, value: any): void` - sets the name dynamic variable with a certain value for the given node

#### requestNodeAPI

The interface of a `RequestStep` node is more advanced.

* `request: object` - the object that stores the node's request configuration
* `response: object` - the object that stores the results of the last request

#### envAPI

* `active: string` - the active environment title
* `vars: object` - the object that stores the current environment variables

## Code examples

### Recursive traversal of the node's children

```javascript
const current = tm.currentNode;
const parent = current.parent;
if (!parent) {
  console.warn(`Parent of ${current.path} not found`);
  return;
}

const value = parent.vars['ID'];
if (!value) {
  console.warn(`Node ${parent.path} hasn't have value for ID`);
  return;
}
console.log(`Parent ID = ${value}`);

const setIDToNode = (node) => {
  node.setDynamicVar('ID', value);
};

const traverseDescendants = (node, func, depth) => {
  node.children.forEach((child) => {
    func(node);
    
    indent = '\t'.repeat(depth);
    console.debug(
      `${indent}${child.path}`,
      `${indent}Value: ${child.dynamicVars['ID']}`
    );
    
    traverseDescendants(child, func, depth+1);
  });
};

traverseDescendants(parent, setIDToNode, 0);
```

### Searching a node by its name

```javascript
const current = tm.currentNode;
const scriptNode = current.parent.findChild(current.name);
assert.equal(current, scriptNode);
```

## File Representation

```javascript
{
  "type": "object",
  "properties": {
    "type": {
      "description": "Type of Script node",
      "const": "Script",
      "type": "string"
    },
    "script": {
      "description": "Javascript code",
      "type": "string"
    },
    "children": {
      "description": "List of children names",
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": []
    },
    "variables": {
      "$ref": "#/definitions/NodeVariables",
      "description": "Node variables dictionary"
    },
    "name": {
      "description": "Node name",
      "type": "string"
    }
  },
  "required": [
    "children",
    "name",
    "script",
    "type",
    "variables"
  ],
  "definitions": {
    "NodeVariables": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "$schema": "http://json-schema.org/draft-07/schema#"
}
```


# User-Defined Variables

The Variables section is a key-value storage for storing and reusing some data. It is often used for removing code duplicates and improving readability: the greetingUrl variable says more than just the line <https://next.json-generator.com/api/json/get/EJvQVEVGL> for sure.

The variables mechanism is very well integrated to all application parts and has several features:

* You can use strings, objects, arrays, and links to other variables as values.
* Variables are defined for each node and are inherited from parent nodes.
* Variables values can refer to other variables.
* [Default variables](/master/variables/variables) names start with $.

### Using Variables

You can use variables in any string parameters of the node - URLs, headers names, authorization tokens, etc. To do that, use the `${variableName}` syntax, where `variableName` is a link to the variable. Here are some examples:

* `${id}`
* `${$dynamicVar.id}`
* `${$response.body.name}`

It's possible to combine strings and links to other variables in the node parameters fields. For instance, you can use  `http://${host}/posts/${$dynamicVar.id}` as an URL.&#x20;

To access an array element, that is stored in a variable, you can use the `${variableName[index]}` syntax. For example, to access the third response entity id, you'll write `${$response.body[2].id}`. Note that it the index is zero-based.

Autocompletion works for variables:

![Variables autocompletion](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgbIUKFExtTnLYtXZVP%2F-LgbNeBxeiKn0jQ0n6-X%2F7.gif?alt=media\&token=cd9aa811-9a11-488c-9c52-676c577ccc80)

Variable value highlighting works as well:

![Variable value highlighting](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgbIUKFExtTnLYtXZVP%2F-LgbO0UDKSouje5vnu0f%2F7.png?alt=media\&token=878cba16-caf1-4847-a757-8c3f0a0d943a)

There is the Variables button in every node interface, that opens the variables dialog. This is how the button looks like:

![The button to open the variables dialog](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgXQ94dC-kuYXsYS_fy%2F-LgXRS7WppaA8EhJwsVI%2F7.png?alt=media\&token=285448ac-d72a-4ab1-aafe-95ed7735c6a8)

The button looks the same for all node types. You'll learn more about how to work with variables in the next sections.


# Static Variables

You can define your own variables, that will be associated with a specific node. Those variables names can't start with the $ symbol, because by convention it is reserved for [default variables](/master/variables/variables). The variables mechanism also supports variables inheritance and overriding in child elements.

To edit your variables, you need to open the [Variables dialog](/master/variables/user-variables). On the Variables tab you can see a table with variables, associated with this node. The tab has the following interface:

![The user-defined variables editing dialog](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgaWGjuumy_M959_5bY%2F-LgaZ_b0iSsui_CHYVQN%2F1.png?alt=media\&token=93423354-fcc9-42e3-a98b-7d431042c495)

In variables values you can also reference other variables. Let's rewrite the previous example using variables:

![Referencing variables in values](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgaWGjuumy_M959_5bY%2F-Lga_ZhMYSpLUnZDtWY9%2F2.png?alt=media\&token=7af30b15-b9ec-4aa0-b5dd-9207a9feb7a4)


# Dynamic Variables

A dynamic variable is a variable which value is determined when the scenario is run. Apart from other tasks, you can effectively use them for storing authorization tokens or newly created entities. The mechanism consists of two parts - variable assignment and dynamic variables themselves.

### Variable assignment

You can assign a part of your request to a dynamic variable. For now you can do it in [RequestStep](/master/node-types/request-step) nodes only. To illustrate this, let's create a request, that adds a new post and save its id into a dynamic variable.

So let's create the request and send it. Send a POST request to <https://testmace-stage.herokuapp.com/posts> with `{"title":"Our cool post!"}` in its body. The RequestStep node will look like this:

![The RequestStep node after sending a POST request](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgaWGjuumy_M959_5bY%2F-Lgag79C5hDnmxNQeIcM%2F3.png?alt=media\&token=88db291d-e78a-44f5-89be-eaa0fdd035d2)

Open the parsed response and click on the id parameter to see the dynamic variables assignment dialog:

![The id parameter context menu](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgaWGjuumy_M959_5bY%2F-LgahEfbukfEaJwIXEOR%2F4.png?alt=media\&token=b82eafd1-1e65-45f8-bd58-88788beccc37)

Choose Assign to variable. You'll see the variable assignment dialog:

![The dynamic variable assignment dialog](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgaWGjuumy_M959_5bY%2F-Lgavn1pYgzftHeAoEbm%2F5.png?alt=media\&token=23e0700a-b26a-441e-9d0b-a6db4b043675)

You can see the following interface parts.

1. The path to the `$request`where the value is stored.
2. The drop-down list with parents, that you can assign the dynamic variable to.
3. The current value at the given path.
4. The dynamic variable name.

Let's create a variable named `id` at this node.

After assignment you can find the dynamic variable in the list of the chosen node (RequestStep) dynamic variables. See the list in the [variables dialog](/master/variables/user-variables) on the Dynamic variables tab:

![The RequestStep node dynamic variables list](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgaWGjuumy_M959_5bY%2F-LgbGZgD3meAwVNT4mg5%2F6.png?alt=media\&token=b8dea6e3-f655-4a14-8111-157eb45a7106)

#### Using Dynamic Variables

All dynamic variables available for the chosen node are stored in the `$dynamicVar` variable. For example, to access the `id` variable you should write  `$dynamicVar.id`. Just like with other variables, dynamic variables can be inherited from parents or overridden in child elements.


# Default Variables

Default Variables are special variables that can not be overridden. You can use them just like any other variables.

* `$parent` - a reference to a parent node;
* `$prevStep` - a reference to the previous node within a [Folder](/master/node-types/folder) node;
* `$nextStep` - a reference to the next node within a [Folder](/master/node-types/folder) node;
* `$dynamicVar` - a [dynamic variables](/master/variables/user-variables/dynamic-variables) object;
* `$response` - a reference to a response in a [RequestStep](/master/node-types/request-step) node;
* `$env` - an [environment variables](/master/variables/env) object.
* `$systemVar`- an object for accessing system environment variables


# Environment Variables

Change variables values used in the project in one click.

In TestMace you can create changeable variables, for instanсe, to switch between **stage** and **prod** environments.

### Creating environment variables

In this example we'll create a single variable for stage and prod environments.

1. Click on the settings icon.
2. In the pop-up window choose a new environment by clicking on the Add environment button and name it stage.
   * Create the **serverUrl** variable and set the stage server url as its value.
3. Add the prod environment by clicking on the Add environment button.
   * Create the **serverUrl** variable and set the prod server url as its value.

### Importing environment from Postman

TestMace allows you to import your environments from [Postman](https://learning.getpostman.com/docs/postman/environments_and_globals/manage_environments/). To do that, click the **+ Import environment** button, that may be found in the variables editing dialog right under the list with all available environments. After you click the + Import environment button, you'll see the dialog where you should enter the path to the file.

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Ll_vQSRFIKGoT1wgKkR%2F-Ll_vhhwuZP7Rsl1UjJq%2FKSpgFN9.png?alt=media\&token=c3d1f146-0737-4639-96e8-ad671fb00b4b)

### Using Environment Variables

To create a changeable variable use the `${$env.%VARIABLE%}` reference. Change the value of our variable in every node to `${$env.serverUrl}`. Now you can change this variable value anytime.

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFYBoa_ywlkIBu028u%2F-LhFYqaW5QfteLxqdRug%2Fenv-2.gif?alt=media\&token=f1f13c40-e23a-4600-9b03-07d647169091)

#### Where to use environment variables?

Just like plain variables, you can use environment variables in any string field of the node.

### Local Environments <a href="#local-environments" id="local-environments"></a>

Local environments are just usual environments that are not saved into project files, but are stored in the application's local storage. We reccomend you use this environment while working with local and privite data, such as logins, passwords, API-tokens, etc.

You can see local environments at the bottom of the sidebar. Each local environment has the `(local)` prefix, so that you could distinguish from other environments.

![Local environments in the environments variables dialog](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LmFMvhKF3ewgTh2gIYo%2F-LmFN0AzzPc2yQWwQqUW%2Fscreenshot_2.png?alt=media\&token=e84e4812-ff08-4409-901e-b88761f581b1)

Drag and drop your local environments to turn them into ordinary ones and vice versa.

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LmFMvhKF3ewgTh2gIYo%2F-LmFN2QmhcHNQbUo8K4m%2FPeek%202019-08-14%2016-00.gif?alt=media\&token=48748bbb-cb33-43ff-9c7d-12fb7ea69e03)


# Cookie

A cookie is a small piece of data that is send by a server and stored on the user's machine.

## Creating a Cookie

TestMace allows you to manage host's cookies. Click on the Cookie button at the top menu to open the modal for managing cookies. You'll see a list of all existing records. Click the Add button to create a new one and fill in the following fields:

|   Field type  | Purpose                                                                                                                        |
| :-----------: | ------------------------------------------------------------------------------------------------------------------------------ |
|    **Key**    | The cookie name                                                                                                                |
|   **Value**   | The cookie value                                                                                                               |
|   **Domain**  | Sets the cookie domain                                                                                                         |
|    **Path**   | Sets the cookie path                                                                                                           |
|  **Expires**  | Sets the date the cookie will expire. The date format should be able to return the `toGMTString()` method of the `Date` object |
|   **Secure**  | The checked Secure box means that SSL is used to send the cookie to the server                                                 |
| **Http only** | Forbids any JavaScript access to the cookie.                                                                                   |

You can also create a cookie from a row string like this:

`isLogged=1; Expires=31/12/2019 00:00:00; Domain=testmace-stage.herokuapp.com; Path=/posts/; Secure;`

![Creating a Cookie](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgbT475LBwT9rFHt0TK%2F-LgfYRYJEPKgk_EHce0O%2Fcookie-1.jpg?alt=media\&token=16e3b4e6-f9ad-4400-a774-c02dcbe4ad9c)

## Editing a Cookie

Will be available in the next release of Testmace.

## Deleting a Cookie

Open the managing cookie window and click on the close icon on the right of the chosen cookie.

![Deleting a Cookie](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgbT475LBwT9rFHt0TK%2F-Lgfa14IctOJbbL4MsJ0%2Fdelete_cookie.jpg?alt=media\&token=ecb49ea8-3261-4bbf-8a96-1edbdfd684d1)


# Authorization

Verifies if you have permission to the resource queried.

## Authorization Types

* [No auth](/master/working-with-project/authorization#no-auth)&#x20;
* [Inherit from parent](/master/working-with-project/authorization#inherit-from-parent)&#x20;
* [Basic auth ](/master/working-with-project/authorization#basic-auth)
* [Bearer auth](/master/working-with-project/authorization#bearer-auth)&#x20;
* [Digest Auth](/master/working-with-project/authorization#digest-auth)&#x20;
* [OAuth 1.0](/master/working-with-project/authorization#oauth-1-0)

{% hint style="info" %}
You can use [environment variables](/master/variables/env) as the Username, Password, Token, and other authorization parameters.
{% endhint %}

## No auth&#x20;

Use the No Auth type if you don't need authorization while sending a request.

## Inherit from parent&#x20;

**By default** authorization parameters are inherited from the parent node. If the parameters are not specified, the [No auth](/master/working-with-project/authorization#no-auth) type is used.

## Basic auth

The type is used when login and password are required for sending a request.

#### Using Basic auth

Open the request and click on the Authorization tab and choose Digest auth. Set the Username and Password in the corresponding fields.

![Using Basic auth](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFk_4qC8J17tr9_76X%2F-LhFkbR2YI_AGpENjeVK%2Fbasic%20auth.jpg?alt=media\&token=aa594234-64ea-4626-9e17-5bf1eb27667d)

## Bearer auth&#x20;

Bearer auth is a token based authorization. Any user with the bearer token can use it to get the access to the resources.

#### Using Bearer auth

Open the request and click on the Authorization tab and choose Bearer auth. Set the token in the token field.

![Using Bearer auth](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFk_4qC8J17tr9_76X%2F-LhFktGKTScYA6iltlB5%2FBearer%20auth.jpg?alt=media\&token=2f9ca009-3372-4b05-99d5-9228092afe5a)

## Digest Auth&#x20;

Using this method, you send a hashed login, password, the server url and nonce values, which provides a higher security level than basic authorization, where you send data with no encryption.

#### Using Digest Auth

Open the request and click on the Authorization tab and choose Digest auth. Set the Username and Password in the corresponding fields.

![Using environment variables in Digest Auth](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFk_4qC8J17tr9_76X%2F-LhFl7UguhAzny4ZIv--%2Fdigest.jpg?alt=media\&token=6ddba7ac-b6fd-43af-9d27-942f871da5d5)

## OAuth 1.0

OAuth 1.0 allows you to access protected resources without exposing your login and password.

#### Using OAuth 1.0

Open the request and click on the Authorization tab and choose OAuth 1.0. Input required data in the fields.

#### The OAuth 1.0 parameters supported in TestMace

| Parameters       | Description                                          |
| ---------------- | ---------------------------------------------------- |
| Consumer Key     | The key                                              |
| Consumer Secret  | The consumer code                                    |
| Access Token     | The token                                            |
| Token Secret     | The token code                                       |
| Signature Method | The signature Method: PLAINTEXT, HMAC-SHA1, RSA-SHA1 |
| Version          | 1.0                                                  |
| Realm            | The server the request is sent to                    |

![Using OAuth 1.0](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFk_4qC8J17tr9_76X%2F-LhFlV94xsc8cmlGmUbe%2Foauth1.jpg?alt=media\&token=c02c4bb2-8547-426f-a0d5-b8791b4d89f2)


# Proxy

You can find proxy settings in **File -> Settings**. Click **Enable Proxy** and enter the Proxy variables values.&#x20;

{% hint style="info" %}
**You can configare the proxy server using the following variables:**

* **http\_proxy** — the proxy IP address for sending requests without SSL;
* **https\_proxy** — the proxy IP address for sending requests with SSL;
* **no\_proxy** — a list of hosts that don't need to use a proxy.

**no\_proxy values examples:**

* **`*google.com`** - don't send HTTP / HTTPS requests to Google;
* **`google.com:443`** - google.com:443 - don't send HTTPS requests to Google, but send HTTP requests to Google;
* **`google.com:443, yahoo.com:80`** - google.com:443, yahoo.com:80 - don't send HTTPS requests to Google, but send HTTP requests to Yahoo!
* **`*`**- ignore the https\_proxy / http\_proxy environment variables.
  {% endhint %}

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgbD2VkSljAvrWg9_vW%2F-LgbD4eHI_TmFMXQwnvN%2Fproxy-settings.jpg?alt=media\&token=2d007c8d-76e3-4698-a86f-ea63b5a451ac)


# Bulk Edit

Some tables in the app have the bulk edit feature.  You can see the **BULK EDIT** button above these tables.When you enable the bulk edit mode, the table content transforms into text, where values are separated by the **:** symbol and lines break with the newline character. To disable the line, text **//** in the beginning.

Let's take headers bulk editing at RequestStep node. Standard headers editing looks like this:

![Standard headers editing](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lgbqgh9bnW1m83nMgCa%2F-Lgby1qDO-3IJLEI7wkn%2F1.png?alt=media\&token=42b068d5-755f-4a03-a69d-0bc5f4c94c03)

When you click on the **BULK EDIT** button, the widget changes to this:

![Headers bulk editing](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lgbqgh9bnW1m83nMgCa%2F-LgbyQsJjDD5ufO8yPkJ%2F2.png?alt=media\&token=f27a6c72-2cf0-4dd0-b919-fb970b9e9610)

See how values are separated by : symbol and lines break with the newline character. Let's disable the first line. Add **//** in the beginning.

![Disabling a line in the bulk edit mode](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lgbqgh9bnW1m83nMgCa%2F-Lgbzdf23GWVbN30MAH4%2F3.png?alt=media\&token=7495f1b3-fe0f-47f0-872a-1268fb009000)

Let's turn back to the table mode by clicking on the **TABLE EDIT** button. The table will look like this:

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lgbqgh9bnW1m83nMgCa%2F-Lgc--dqOyKM2y0l_Rx_%2F4.png?alt=media\&token=35964872-efa3-4578-a39f-20db534a8761)

As you can see, the first line checkbox is unchecked, which means the line won't be used in the request.


# Import & Export

In this section we'll discuss the **Import** menu option features. You can find it by clicking the **+** button above the project tree (or at the top part of the **Scratches** area). This is how it looks like:

![The Import context menu](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh4FOBJ2iDdG8NA1Fm-%2F-Lh4FsXGrFh4ST697Eyd%2F4.png?alt=media\&token=8bd8cb88-cd66-4e92-a8a6-973636033978)

The **Import** menu includes the following options:

* [**Shared**](/master/other-features/import/shared) - loads previously exported nodes;
* [**cURL**](/master/other-features/import/curl) - imports a request from cURL;
* [**Swagger**](/master/other-features/import/swagger) - imports API description from [Swagger/OpenAPI](https://swagger.io/specification/);
* [**Postman**](/master/other-features/import/postman) - imports collections from [Postman](https://learning.getpostman.com/docs/postman/collections/sharing_collections/).

You'll learn how to use them all in the next sections.


# Shared

TestMace provides a very convenient way of sharing nodes and even project subtrees. To import a subtree, choose the **Share** option in the chosen node context menu.

Let's export the project from the [Getting Started](/master) section as an example.&#x20;

![A Shared export](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFn6WYyKpdhWkt4-Mj%2F-LhFn8HlqNDKKJ63Gfvt%2Fshare-1.gif?alt=media\&token=3518fe27-e015-49d8-bf89-34938ba63781)

The `testmace://....` URL is copied into the clipboard.

Now you can import this URL into the chosen node. There are two ways to do that:

* import from the project context menu: **Import** -> **Shared**;
* import from the [Folder](/master/node-types/folder) or [Project](/master/node-types/project) nodes: **Import** -> **Shared**;

The Import dialog has the following interface:

![The Import dialog](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh4GkVAVvrxlBstzDgQ%2F-Lh4NVKvy8XoGrzrS-UF%2F1.png?alt=media\&token=ab5f441b-9bfe-4adf-ba62-7a4d6a67096a)

You can set the root node name of the imported subtree in the **Name** field. Enter the previously exported URL in the URL field. If the root node name you specified already exists in the tree, the name will be changed to NodeName \[Copy \[number]].

You can see the animation of the import process down below:

![A Shared import](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LhFn6WYyKpdhWkt4-Mj%2F-LhFoT9YtiZwh5P1doqK%2Fshare-2.gif?alt=media\&token=4db859a2-3e6c-4c47-a568-8ee87c04f615)


# cURL

[cURL](https://curl.haxx.se/) - is a command line tool allowing you to interact with services via different protocols with URL syntax. It is widely used today, including sending HTTP requests from the command line. TestMace allows you to import the curl command with parameters into a [RequestStep](/master/node-types/request-step) node request.

There are two ways to import a request from cURL:

* import from the project context menu: **Import -> cURL**;
* import from the [Folder](/master/node-types/folder) or [Project](/master/node-types/project) node context menu: **Import** -> **cURL.**

You'll see the next dialog:

![The dialog for import from cURL](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh4OL_nOZ71Ot6O67fi%2F-Lh4SwiyIkwkiHHhCKAR%2F2.png?alt=media\&token=6fd1e66c-c3ac-4796-bf9b-5ee538f24f62)

Here you need to set the new [RequestStep](/master/node-types/request-step) node name and the import command.

Take a look at how a request is copied from the browser requests list as cURL:

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lgfzj3UBNJfmoUFpmJU%2F-Lgg1WxiamPf0Yx6ENPN%2FcURL%20import.gif?alt=media\&token=2a561be6-1620-468b-8a40-517305acb314)


# Swagger

Import from Swagger/OpenAPI is shown in the [API Description Import](/master/node-types/api-description/import-api) section.


# Postman

Postman allows you to [share a collection of requests](https://learning.getpostman.com/docs/postman/collections/sharing_collections/). TestMace supports import this format. You can import Postman collections in these two ways:

* import from the project context menu: **Import** -> **Postman**;
* import from the [Folder](/master/node-types/folder) or [Project](/master/node-types/project) nodes context menu: **Import** -> **Postman**.

You'll see the next dialog:

![Importing a collection from Postman](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-Lh4Thti0uTBrC-JxbJB%2F-Lh4XVreJ3j1inGWM0Yj%2F3.png?alt=media\&token=b3d647cc-a916-4068-b860-d19924c1664b)

You need to specify the path to the file with the imported collection here. If the root node name you specified already exists in the tree, the name will be changed to NodeName \[Copy \[number]].


# Default HTTP headers

You can set the HTTP headers of Folder and Project nodes, that will be inherited by child nodes and used in RequestStep nodes requests by default. Let's see how we can set and use default headers.

## Defining Default HTTP Headers

You can define your default headers in the Folder node. To do this, click on the **Headers** button on the Folder node toolbar.

![](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgfhSVUoFJEM98_tht0%2F-LgfjSwuSwYIys2vQFkC%2F1.png?alt=media\&token=9618cd40-7584-4c98-a124-1eef64d32ff4)

You'll see the default HTTP headers editing dialog:

![ The default HTTP headers editing dialog](https://1914040845-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LYfhIaTTw23Sf3L5jfe%2F-LgfhSVUoFJEM98_tht0%2F-LgfjsCplgf70WxxwuHA%2F2.png?alt=media\&token=4f6a6ea5-7b5f-43c8-94bd-b2594cf86c8f)

At the top of the dialog you can see a read-only headers list inherited from parents. Next the chosen Folder node headers are specified. Apart from adding, deleting, editing (including [bulk edit](/master/other-features/bulk-edit)), it is possible to disable certain headers which will not be included in the resulting request. Headers state (enabled/disabled) is inherited as well.

You can also override headers in child nodes. For example, setting the `Hello, TestMace` value to the `RootDefaultHeader1` header will override the inherited header, and in child nodes of the chosen Folder node the  `RootDefaultHeader1` header will have the `Hello, TestMace` value. Note that the value of the **parent** `RootDefaultHeader1`  header will remain unchanged (`Hello, world`).

### Using Default Headers

Default headers are used in RequestStep nodes requests. They are inserted automatically and don't require user interaction. The interface of RequestStep requests headers editing is identical to the one of a Folder node.

### Default Headers in the File System

See the file representation of a [Folder](/master/node-types/folder#failovoe-predstavlenie) node. The `requestData.headers` field is used to store a headers list, and the `requestData.disabledInheritedHeaders` is used to store disabled headers. The same format applies to [RequestStep](/master/node-types/request-step#failovoe-predstavlenie) nodes.


