diff --git a/docs/content/docs/cli/command-overview.mdx b/docs/content/docs/cli/command-overview.mdx new file mode 100644 index 0000000..09948ee --- /dev/null +++ b/docs/content/docs/cli/command-overview.mdx @@ -0,0 +1,48 @@ +--- +title: Command Overview +description: An overview of all available CLI commands +--- + +The Restflow CLI is the primary way to execute your API tests. The base command is `restflow`. + +You can get help at any time by running: +```bash +restflow --help +``` + +### Main Commands + +Restflow has a few main commands for different tasks. + +#### `restflow run` + +This is the most important command. It is used to execute one or more `.flow` files. + +```bash +# Run a single flow +restflow run path/to/your.flow + +# Run all flows in a directory +restflow run path/to/your/flows/ +``` + +This command has many options for controlling the execution, which are detailed on the `run` command page. + +#### `restflow init` + +The `init` command helps you set up a new Restflow project or add Restflow to an existing one. It can create a sample project structure with example flows and an environment file. + +```bash +# Initialize a new Restflow project in the current directory +restflow init +``` + +This is a great way to get started quickly. + +### Global Options + +These options can be used with any command. + +- `--help`: Show help information. +- `--version`: Show the installed version of Restflow. +- `--verbose`: Enable detailed logging, which can be useful for debugging. diff --git a/docs/content/docs/cli/meta.json b/docs/content/docs/cli/meta.json new file mode 100644 index 0000000..d42120d --- /dev/null +++ b/docs/content/docs/cli/meta.json @@ -0,0 +1,7 @@ +{ + "title": "CLI Reference", + "pages": [ + "command-overview", + "run-command" + ] +} diff --git a/docs/content/docs/cli/run-command.mdx b/docs/content/docs/cli/run-command.mdx new file mode 100644 index 0000000..0025afc --- /dev/null +++ b/docs/content/docs/cli/run-command.mdx @@ -0,0 +1,72 @@ +--- +title: The 'run' Command +description: Running flows with various options +--- + +The `restflow run` command is used to execute your `.flow` files. You can specify one or more files or directories as arguments. + +### Basic Usage + +```bash +# Run a single flow file +restflow run my-test.flow + +# Run multiple specific flow files +restflow run test1.flow test2.flow + +# Run all flow files in a directory (and its subdirectories) +restflow run ./tests/ +``` + +### Command Options + +The `run` command has several options to customize its behavior. + +#### `--env ` or `-e ` + +Load environment variables from a specific file. + +```bash +restflow run tests/ --env .env.staging +``` + +#### `--format ` or `-f ` + +Specify the output format. +- `console` (default): Human-readable, colored output. +- `json`: Machine-readable JSON output. +- `summary`: A concise table of results. + +```bash +restflow run tests/ --format json > results.json +``` + +#### `--timeout ` + +Set a timeout in milliseconds for each HTTP request. The default is typically 30 seconds (30000 ms). + +```bash +# Set a 10-second timeout +restflow run tests/ --timeout 10000 +``` + +#### `--show-body` + +Include the request and response bodies in the output. This is very useful for debugging. + +#### `--show-headers` + +Include the request and response headers in the output. + +#### `--verbose` + +Enable verbose logging for more detailed output about the execution process. + +### Combining Options + +You can combine these options to suit your needs. + +```bash +# Run tests against staging, with a 5s timeout, and show bodies and headers +restflow run tests/ --env .env.staging --timeout 5000 --show-body --show-headers +``` diff --git a/docs/content/docs/core-concepts/assertions-basics.mdx b/docs/content/docs/core-concepts/assertions-basics.mdx new file mode 100644 index 0000000..ac55157 --- /dev/null +++ b/docs/content/docs/core-concepts/assertions-basics.mdx @@ -0,0 +1,68 @@ +--- +title: Assertions Basics +description: Writing basic assertions and validations +--- + +Assertions are used to verify that your API is behaving as expected. In Restflow, you use the `assert` directive to check the status code, headers, and body of a response. + +### The `assert` Directive + +The `assert` directive has the following structure: + +`> assert ` + +- **source**: The part of the response to check (e.g., `status`, `body.id`, `headers["content-type"]`). +- **operator**: The comparison to perform (e.g., `==`, `!=`, `>`, `contains`). +- **value**: The expected value. + +If an assertion fails, the step is marked as failed, and the execution of the flow may stop depending on your configuration. + +### Common Assertions + +Here are some of the most common assertions you'll use: + +#### Checking the Status Code + +This is the most fundamental assertion. You should almost always check the status code. + +```flow +### Get a resource +GET https://api.example.com/items/1 + +> assert status == 200 +``` + +#### Checking the Response Body + +You can use JSONPath to access values in a JSON response body. + +```flow +### Get a user +GET https://api.example.com/users/123 + +> assert body.id == 123 +> assert body.name == "John Doe" +> assert body.address.city == "New York" +``` + +#### Checking for Existence + +You can check if a field exists in the response. + +```flow +> assert body.id != null +``` + +#### Checking Array Length + +```flow +> assert body.items.length > 0 +``` + +#### Checking Headers + +```flow +> assert headers["content-type"] contains "application/json" +``` + +These are just the basics. The "DSL Reference" section has a complete guide to all the available assertion operators and advanced techniques. diff --git a/docs/content/docs/core-concepts/dsl-basics.mdx b/docs/content/docs/core-concepts/dsl-basics.mdx new file mode 100644 index 0000000..b1dd6c4 --- /dev/null +++ b/docs/content/docs/core-concepts/dsl-basics.mdx @@ -0,0 +1,48 @@ +--- +title: DSL Basics +description: Introduction to the .flow file syntax +--- + +The Restflow DSL (Domain-Specific Language) is designed to be simple, readable, and easy to write. All tests are written in `.flow` files using a plain text format. + +### Basic Structure + +A `.flow` file is composed of one or more steps. Each step has the following structure: + +``` +### Step Name +HTTP_METHOD URL +Header-Name: Header-Value + +Request Body + +> directive +``` + +- **Step Name**: A descriptive name for your step, starting with `###`. +- **HTTP Method and URL**: The HTTP method (e.g., `GET`, `POST`) and the endpoint URL. +- **Headers**: Optional HTTP headers, one per line. +- **Request Body**: The body of the request, separated from the headers by a blank line. +- **Directives**: Actions to perform after the request, such as assertions or capturing variables. Each directive starts with `>`. + +### Example + +Here's a complete example of a `.flow` file with a single step: + +```flow +### Create a new user +POST https://api.example.com/users +Content-Type: application/json +Authorization: Bearer {{api_key}} + +{ + "name": "John Doe", + "email": "john.doe@example.com" +} + +> assert status == 201 +> assert body.id != null +> capture userId = body.id +``` + +This example demonstrates all the basic components of the DSL in action. As you can see, it's designed to be self-documenting and easy to understand at a glance. diff --git a/docs/content/docs/core-concepts/environment-management.mdx b/docs/content/docs/core-concepts/environment-management.mdx new file mode 100644 index 0000000..c23c4f6 --- /dev/null +++ b/docs/content/docs/core-concepts/environment-management.mdx @@ -0,0 +1,48 @@ +--- +title: Environment Management +description: Managing environments and configurations +--- + +Real-world API testing requires running the same tests against different environments, such as development, staging, and production. Restflow makes this easy with its support for `.env` files. + +### What is an `.env` file? + +An `.env` file is a simple text file that contains key-value pairs for your environment-specific variables. + +Here is an example `.env` file: + +```dotenv +# .env.staging +BASE_URL=https://staging.api.example.com +API_KEY=staging-secret-key +TEST_USER=staging_user +``` + +### Using Environment Variables + +You can use the variables from your `.env` file in your flows using the `{{variable_name}}` syntax. + +```flow +### Get data from a protected endpoint +GET {{BASE_URL}}/data +Api-Key: {{API_KEY}} + +> assert status == 200 +``` + +### Running with an Environment + +To run your flows with a specific environment file, use the `--env` flag on the command line: + +```bash +restflow run my-flow.flow --env .env.staging +``` + +When you run this command, Restflow will load the variables from `.env.staging` and make them available in your flow. + +This allows you to keep your `.flow` files clean and free of environment-specific details. You can have multiple `.env` files for your different environments: +- `.env.dev` +- `.env.staging` +- `.env.prod` + +And switch between them easily using the `--env` flag. This is a powerful feature for creating portable and maintainable API tests. diff --git a/docs/content/docs/core-concepts/flows-and-steps.mdx b/docs/content/docs/core-concepts/flows-and-steps.mdx new file mode 100644 index 0000000..5ba0adf --- /dev/null +++ b/docs/content/docs/core-concepts/flows-and-steps.mdx @@ -0,0 +1,45 @@ +--- +title: Flows and Steps +description: Understanding the basic structure of Restflow tests +--- + +In Restflow, your API tests are organized into **flows** and **steps**. This structure helps you create readable, maintainable, and powerful tests. + +### Flows + +A **flow** is a single `.flow` file that contains a sequence of steps. It represents a complete user journey or a set of related API tests. For example, a `login.flow` file could contain all the steps required to authenticate a user. + +Flows are executed from top to bottom. You can run a single flow or multiple flows at once. + +### Steps + +A **step** is a single API request within a flow. Each step has a name, an HTTP request, and optional directives for assertions and variable capturing. + +A step is defined using `###` followed by the step name. + +Here is an example of a flow with two steps: + +```flow +### Step 1: Get a post +GET https://jsonplaceholder.typicode.com/posts/1 + +> assert status == 200 + +### Step 2: Create a new post +POST https://jsonplaceholder.typicode.com/posts +Content-Type: application/json + +{ + "title": "My new post", + "body": "A great post" +} + +> assert status == 201 +``` + +In this example: +- The file itself represents the flow. +- "Get a post" is the first step. +- "Create a new post" is the second step. + +Each step is independent by default, but you can use variables to pass data between steps, creating powerful testing workflows. diff --git a/docs/content/docs/core-concepts/meta.json b/docs/content/docs/core-concepts/meta.json new file mode 100644 index 0000000..827250c --- /dev/null +++ b/docs/content/docs/core-concepts/meta.json @@ -0,0 +1,10 @@ +{ + "title": "Core Concepts", + "pages": [ + "flows-and-steps", + "dsl-basics", + "variables-overview", + "assertions-basics", + "environment-management" + ] +} diff --git a/docs/content/docs/core-concepts/variables-overview.mdx b/docs/content/docs/core-concepts/variables-overview.mdx new file mode 100644 index 0000000..61fc2de --- /dev/null +++ b/docs/content/docs/core-concepts/variables-overview.mdx @@ -0,0 +1,55 @@ +--- +title: Variables Overview +description: A brief introduction to variables in Restflow +--- + +Variables are a key feature of Restflow, allowing you to create dynamic and flexible API tests. You can use variables to pass data between steps, use environment-specific values, and generate random data. + +### Variable Syntax + +Variables in Restflow use the `{{variable_name}}` syntax. When a flow is executed, Restflow replaces these placeholders with their corresponding values. + +```flow +### Get user profile +GET https://api.example.com/users/{{userId}} +Authorization: Bearer {{auth_token}} +``` + +### Types of Variables + +Restflow supports several types of variables: + +- **Environment Variables**: Loaded from `.env` files. Useful for storing environment-specific data like API keys and base URLs. +- **Captured Variables**: Extracted from the response of a previous step using the `capture` directive. This is how you chain requests together. +- **Built-in Variables**: Provided by Restflow for generating dynamic data like `{{uuid}}`, `{{timestamp}}`, and `{{randomString}}`. +- **CLI Variables**: Passed from the command line when running a flow. + +### A Simple Workflow + +Here's an example of how variables connect steps in a workflow: + +```flow +### 1. Login and get a token +POST https://api.example.com/login +Content-Type: application/json + +{ + "username": "{{test_user}}", + "password": "{{test_password}}" +} + +> capture auth_token = body.token + +### 2. Use the token to access a protected resource +GET https://api.example.com/profile +Authorization: Bearer {{auth_token}} + +> assert status == 200 +``` + +In this example: +1. `{{test_user}}` and `{{test_password}}` are likely defined in an environment file. +2. The `capture` directive saves the `token` from the login response into the `auth_token` variable. +3. The `auth_token` variable is then used in the `Authorization` header of the next request. + +This is just a brief overview. The "Variables & Environment" section provides more in-depth information on each variable type. diff --git a/docs/content/docs/dsl-reference/assertions-reference.mdx b/docs/content/docs/dsl-reference/assertions-reference.mdx new file mode 100644 index 0000000..1968d11 --- /dev/null +++ b/docs/content/docs/dsl-reference/assertions-reference.mdx @@ -0,0 +1,92 @@ +--- +title: Assertions Reference +description: A complete guide to assertions in Restflow +--- + +Assertions are the core of API testing in Restflow. They allow you to validate the responses from your API. This page covers all the types of assertions available. + +### Assertion Syntax + +All assertions use the `assert` directive: + +`> assert ` + +### Status Code Assertions + +You can use standard comparison operators to check the HTTP status code. + +```flow +> assert status == 200 +> assert status != 404 +> assert status >= 200 +> assert status < 500 +``` + +### Response Body Assertions + +For JSON responses, you can use JSONPath to access nested values in the response body. The `body` keyword represents the root of the JSON response. + +```flow +# For a response like: { "user": { "id": 123, "name": "John" }, "posts": [...] } +> assert body.user.id == 123 +> assert body.user.name == "John" +> assert body.posts.length > 0 +``` + +### Response Header Assertions + +You can access response headers using the `headers` keyword. Header names are case-insensitive. + +```flow +> assert headers["content-type"] contains "application/json" +> assert headers["x-request-id"] != null +``` + +### Regular Expression Assertions + +The `matches` operator allows you to use regular expressions for more complex string validations. + +```flow +# Check that a message starts with "Success" +> assert body.message matches "^Success" + +# Check that an ID is a number +> assert body.id matches "\\d+" +``` + +### Existence and Type Assertions + +You can check for the existence or non-existence of a field, or check its type. + +```flow +# Check that a token field exists +> assert body.token != null + +# Check that an error field does not exist +> assert body.error == null + +# Check the type of a value +> assert body.user.id is number +> assert body.user.name is string +> assert body.user.isActive is boolean +> assert body.posts is array +``` + +### Comparison Operators + +Restflow supports a rich set of comparison operators: + +| Operator | Description | +|---|---| +| `==` | Equal | +| `!=` | Not equal | +| `>` | Greater than | +| `>=` | Greater than or equal to | +| `<` | Less than | +| `<=` | Less than or equal to | +| `contains` | String or array contains a value | +| `not contains` | String or array does not contain a value | +| `matches` | Regular expression match | +| `not matches` | Regular expression non-match | +| `is` | Type check (e.g., `is number`, `is string`) | +| `is not`| Negated type check | diff --git a/docs/content/docs/dsl-reference/meta.json b/docs/content/docs/dsl-reference/meta.json new file mode 100644 index 0000000..17a9a8e --- /dev/null +++ b/docs/content/docs/dsl-reference/meta.json @@ -0,0 +1,7 @@ +{ + "title": "DSL Reference", + "pages": [ + "syntax-overview", + "assertions-reference" + ] +} diff --git a/docs/content/docs/dsl-reference/syntax-overview.mdx b/docs/content/docs/dsl-reference/syntax-overview.mdx new file mode 100644 index 0000000..2d862a8 --- /dev/null +++ b/docs/content/docs/dsl-reference/syntax-overview.mdx @@ -0,0 +1,85 @@ +--- +title: Syntax Overview +description: A complete reference for the Restflow DSL syntax +--- + +The Restflow DSL is designed to be as readable and intuitive as possible. This page provides a comprehensive reference for all the components of the `.flow` file syntax. + +### File Structure + +A `.flow` file is a plain text file that consists of one or more steps. Comments can be added on any line by starting the line with a `#`. + +### Step Declaration + +Every step must begin with a level-3 markdown header (`###`). + +```flow +### This is a valid step name +``` + +### HTTP Request + +The line immediately following the step name defines the HTTP request. + +```flow +METHOD /path/to/resource +``` + +- **Supported Methods**: `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`. +- **URL**: Can be a relative path (e.g., `/users/1`) or a full URL (e.g., `https://api.example.com/users/1`). Relative paths are automatically prefixed with the `BASE_URL` from your environment file. + +### Headers + +HTTP headers are specified as key-value pairs, one per line, after the request line. + +```flow +Content-Type: application/json +Authorization: Bearer {{token}} +``` + +### Request Body + +The request body is placed after the headers, separated by a single blank line. + +```flow +{ + "name": "John Doe", + "email": "john@example.com" +} +``` + +### Directives + +Directives are special commands that are executed after the HTTP request is complete. They always start with a `>` character. + +The two main directives are `assert` and `capture`. + +```flow +> assert status == 200 +> capture userId = body.id +``` + +### A Complete Example + +Putting it all together, here is a complete, valid step: + +```flow +# This flow registers a new user and captures their ID. +### Register New User +POST /users +Content-Type: application/json + +{ + "name": "Jane Doe", + "email": "jane.doe@example.com", + "password": "a-secure-password" +} + +# Verify that the user was created successfully +> assert status == 201 +> assert body.id != null +> assert body.email == "jane.doe@example.com" + +# Capture the new user's ID for use in subsequent steps +> capture userId = body.id +``` diff --git a/docs/content/docs/getting-started/installation.mdx b/docs/content/docs/getting-started/installation.mdx index a5141cd..0213274 100644 --- a/docs/content/docs/getting-started/installation.mdx +++ b/docs/content/docs/getting-started/installation.mdx @@ -92,4 +92,4 @@ npm cache clean --force ## Next Steps -Ready to create your first flow? Continue to [Quick Start](/docs/getting-started/quick-start). \ No newline at end of file +Ready to create your first flow? Continue to [Quick Start](/docs/getting-started/quick-start). diff --git a/docs/content/docs/getting-started/quick-start.mdx b/docs/content/docs/getting-started/quick-start.mdx index 8fc4ab7..af63dd9 100644 --- a/docs/content/docs/getting-started/quick-start.mdx +++ b/docs/content/docs/getting-started/quick-start.mdx @@ -11,8 +11,7 @@ Create a new file called `api-test.flow`: ```flow ### Test User API -GET /users/1 -Host: jsonplaceholder.typicode.com +GET https://jsonplaceholder.typicode.com/users/1 > assert status == 200 > assert body.id == 1 @@ -33,7 +32,7 @@ You should see output like: ✓ Test User API ✓ GET /users/1 (200ms) ✓ status == 200 - ✓ body.id == 1 + ✓ body.id == 1 ✓ body.name != null Tests: 1 passed, 0 failed @@ -44,9 +43,8 @@ Tests: 1 passed, 0 failed Make your tests dynamic with variables: ```flow -### Create and Get User -POST /users -Host: jsonplaceholder.typicode.com +### Create a User +POST https://jsonplaceholder.typicode.com/users Content-Type: application/json { @@ -54,11 +52,10 @@ Content-Type: application/json "email": "test-{{uuid}}@example.com" } -> capture body.id as userId +> capture userId = body.id ### Get Created User -GET /users/{{userId}} -Host: jsonplaceholder.typicode.com +GET https://jsonplaceholder.typicode.com/users/{{userId}} > assert status == 200 > assert body.id == {{userId}} @@ -66,25 +63,25 @@ Host: jsonplaceholder.typicode.com ## 4. Environment Configuration -Create `.env` file for environment-specific settings: +Create a `.env` file for environment-specific settings: -```bash +```dotenv # .env BASE_URL=https://jsonplaceholder.typicode.com API_KEY=your-api-key ``` -Update your flow: +Update your flow to use the `BASE_URL`: ```flow ### Get User with Environment -GET /users/1 +GET {{BASE_URL}}/users/1 Authorization: Bearer {{API_KEY}} > assert status == 200 ``` -Run with environment: +Run with the environment: ```bash restflow run api-test.flow --env .env @@ -94,44 +91,16 @@ restflow run api-test.flow --env .env **JSON Output:** ```bash -restflow run api-test.flow --output json +restflow run api-test.flow --format json ``` **Summary View:** ```bash -restflow run api-test.flow --output summary -``` - -## Common Patterns - -**Testing API Endpoints:** -```flow -### Test Multiple Endpoints -GET /users -> assert status == 200 -> assert body.length > 0 - -### Check Individual User -GET /users/1 -> assert body.name == "Leanne Graham" -``` - -**Using Built-in Variables:** -```flow -### Dynamic Test Data -POST /posts -Content-Type: application/json - -{ - "title": "Test Post {{timestamp}}", - "userId": {{randomNumber}} -} - -> assert status == 201 +restflow run api-test.flow --format summary ``` ## Next Steps - [Set up a complete project](/docs/getting-started/project-setup) -- [Learn the Flow syntax](/docs/writing-flows/basics) -- [Explore variables and environments](/docs/writing-flows/variables) \ No newline at end of file +- [Learn the DSL syntax](/docs/dsl-reference/syntax-overview) +- [Explore variables and environments](/docs/variables/variable-types) diff --git a/docs/content/docs/index.mdx b/docs/content/docs/index.mdx index c76264f..001cc75 100644 --- a/docs/content/docs/index.mdx +++ b/docs/content/docs/index.mdx @@ -1,5 +1,5 @@ --- -title: Documentation +title: Welcome description: Complete guide to using Restflow for API testing --- diff --git a/docs/content/docs/meta.json b/docs/content/docs/meta.json index 2553a00..1b313c1 100644 --- a/docs/content/docs/meta.json +++ b/docs/content/docs/meta.json @@ -1,4 +1,23 @@ { - "title": "Documentation", - "pages": ["getting-started", "dsl-reference", "variables", "cli", "examples"] -} \ No newline at end of file + "pages": [ + "getting-started/introduction", + "getting-started/installation", + "getting-started/quick-start", + "getting-started/project-setup", + "---Core Concepts---", + "core-concepts/flows-and-steps", + "core-concepts/dsl-basics", + "core-concepts/variables-overview", + "core-concepts/assertions-basics", + "core-concepts/environment-management", + "---DSL Reference---", + "dsl-reference", + "---Variables & Environment---", + "variables/variable-types", + "variables/built-in-variables", + "variables/environment-files", + "---CLI---", + "cli" + ], + "defaultOpen": true +} diff --git a/docs/content/docs/variables/built-in-variables.mdx b/docs/content/docs/variables/built-in-variables.mdx new file mode 100644 index 0000000..fe69b9a --- /dev/null +++ b/docs/content/docs/variables/built-in-variables.mdx @@ -0,0 +1,82 @@ +--- +title: Built-in Variables +description: Using Restflow's built-in dynamic variables +--- + +Restflow includes a set of built-in variables that you can use to generate dynamic data for your tests. This is incredibly useful for creating unique resources or for testing with random values. + +**Important Note**: A new, unique value is generated every time a built-in variable is referenced. If you need to use the same generated value in multiple places, you should capture it first. + +### `{{uuid}}` + +Generates a standard Version 4 UUID (Universally Unique Identifier). + +**Use case**: Creating resources that require a unique identifier, or for `X-Request-ID` headers. + +```flow +### Create a new user with a unique email +POST /users +Content-Type: application/json + +{ + "email": "user-{{uuid}}@example.com", + "password": "password123" +} +``` + +### `{{timestamp}}` + +Generates the current Unix timestamp (the number of seconds since the Unix epoch). + +**Use case**: Timestamps for created resources, or for testing time-based functionality. + +```flow +### Create a post with a dynamic title +POST /posts +Content-Type: application/json + +{ + "title": "Post created at {{timestamp}}", + "body": "This is the content." +} +``` + +### `{{randomString}}` + +Generates a random alphanumeric string of a variable length. + +**Use case**: Creating unique usernames, names, or other text fields. + +```flow +### Register a user with a random username +POST /register +Content-Type: application/json + +{ + "username": "user_{{randomString}}", + "email": "email_{{randomString}}@example.com" +} +``` + +### `{{randomNumber}}` + +Generates a random integer between 0 and 999999. + +**Use case**: Testing with random numerical data, such as quantities or ratings. + +```flow +### Add an item to a cart with a random quantity +POST /cart/items +Content-Type: application/json + +{ + "item_id": 123, + "quantity": {{randomNumber}} +} +``` + +### Ensuring Consistency + +If you need to use the same random value in multiple places, capture it from a header or body. A common pattern is to send it in a request header and capture it from the response if the API echoes it back. + +A simpler way is to use it in an environment variable, but that is a more advanced topic. diff --git a/docs/content/docs/variables/environment-files.mdx b/docs/content/docs/variables/environment-files.mdx new file mode 100644 index 0000000..1b09887 --- /dev/null +++ b/docs/content/docs/variables/environment-files.mdx @@ -0,0 +1,51 @@ +--- +title: Environment Files +description: .env file structure and best practices +--- + +Environment files (or `.env` files) are the standard way to manage environment-specific configuration in Restflow. They allow you to keep your `.flow` files clean and portable across different environments like development, staging, and production. + +### File Format + +An `.env` file is a simple text file with `KEY=VALUE` pairs. + +```dotenv +# This is a comment +BASE_URL=https://api.example.com +API_KEY=your-secret-api-key +TEST_USER_EMAIL=test@example.com +``` + +- Comments start with `#`. +- Keys are typically uppercase, but this is not required. +- Values are everything after the `=`. Do not use quotes unless they are part of the value. + +### Naming Conventions + +While you can name your environment files anything, a common convention is to use `.env.`. + +- `.env.development` +- `.env.staging` +- `.env.production` +- `.env.local` (for local overrides) + +### Loading an Environment File + +Use the `--env` or `-e` flag when running Restflow to specify which environment file to load. + +```bash +# Run tests against the staging environment +restflow run tests/ --env .env.staging + +# Run tests against the production environment +restflow run tests/ --env .env.production +``` + +If you don't specify an `--env` flag, Restflow will automatically look for a file named `.env` in the current directory. + +### Best Practices + +1. **Never commit secrets**: Add your `.env` files with sensitive information (like API keys and passwords) to your `.gitignore` file. +2. **Create a template file**: Create an example file like `.env.example` with all the required keys but with placeholder values. Commit this file to your repository so other developers know what variables are needed. +3. **Use `BASE_URL`**: Define a `BASE_URL` in your environment files and use relative paths in your flows. This makes your tests highly portable. +4. **Keep it flat for now**: While Restflow supports advanced features like variable chaining in `.env` files, it's best to start with a simple, flat list of key-value pairs. diff --git a/docs/content/docs/variables/meta.json b/docs/content/docs/variables/meta.json new file mode 100644 index 0000000..97b7ed1 --- /dev/null +++ b/docs/content/docs/variables/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Variables & Environment", + "pages": [ + "variable-types", + "built-in-variables", + "environment-files" + ] +} diff --git a/docs/content/docs/variables/variable-types.mdx b/docs/content/docs/variables/variable-types.mdx new file mode 100644 index 0000000..ac135fc --- /dev/null +++ b/docs/content/docs/variables/variable-types.mdx @@ -0,0 +1,59 @@ +--- +title: Variable Types +description: Understanding the different types of variables in Restflow +--- + +Restflow has a powerful variable system with several types of variables. Understanding each type will help you write more dynamic and maintainable tests. + +### Variable Resolution Priority + +When a variable is used, Restflow searches for its value in the following order of priority (from highest to lowest): + +1. **CLI Variables**: Passed via the command line. +2. **Captured Variables**: Extracted from a previous step's response. +3. **Environment Variables**: Loaded from `.env` files. +4. **Built-in Variables**: Provided by Restflow for dynamic data generation. + +This means that a CLI variable will override an environment variable with the same name. + +### 1. CLI Variables + +You can pass variables directly from the command line when you run a flow. This is useful for overriding default values or for use in CI/CD pipelines. + +*(Note: The exact syntax for passing CLI variables will be covered in the CLI Reference section.)* + +### 2. Captured Variables + +These variables are created using the `capture` directive. They allow you to extract a value from an HTTP response and use it in subsequent requests. This is the primary way to chain requests together. + +```flow +### Login and capture token +POST /login +... +> capture auth_token = body.token + +### Use the captured token +GET /profile +Authorization: Bearer {{auth_token}} +``` + +### 3. Environment Variables + +Environment variables are loaded from `.env` files and are typically used for environment-specific configuration like API keys, base URLs, or test user credentials. + +```dotenv +# .env.staging +BASE_URL=https://staging.api.example.com +API_KEY=my-secret-key +``` + +### 4. Built-in Variables + +Restflow provides a set of built-in variables for generating common types of dynamic data. + +- `{{uuid}}`: A version 4 UUID. +- `{{timestamp}}`: A Unix timestamp. +- `{{randomString}}`: A random alphanumeric string. +- `{{randomNumber}}`: A random number. + +Each time a built-in variable is used, it generates a new, unique value. diff --git a/docs/source.config.ts b/docs/source.config.ts index 6c88a47..1f179fb 100644 --- a/docs/source.config.ts +++ b/docs/source.config.ts @@ -16,6 +16,7 @@ export default defineConfig({ "javascript", "typescript", "json", + "dotenv", { name: "flow", scopeName: "source.flow", diff --git a/docs/src/routeTree.gen.ts b/docs/src/routeTree.gen.ts index 4597ec3..c657276 100644 --- a/docs/src/routeTree.gen.ts +++ b/docs/src/routeTree.gen.ts @@ -11,15 +11,15 @@ import { createServerRootRoute } from '@tanstack/react-start/server' import { Route as rootRouteImport } from './routes/__root' -import { Route as IndexRouteImport } from './routes/index' +import { Route as SplatRouteImport } from './routes/$' import { Route as DocsSplatRouteImport } from './routes/docs/$' import { ServerRoute as ApiSearchServerRouteImport } from './routes/api/search' const rootServerRouteImport = createServerRootRoute() -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', +const SplatRoute = SplatRouteImport.update({ + id: '/$', + path: '/$', getParentRoute: () => rootRouteImport, } as any) const DocsSplatRoute = DocsSplatRouteImport.update({ @@ -34,28 +34,28 @@ const ApiSearchServerRoute = ApiSearchServerRouteImport.update({ } as any) export interface FileRoutesByFullPath { - '/': typeof IndexRoute + '/$': typeof SplatRoute '/docs/$': typeof DocsSplatRoute } export interface FileRoutesByTo { - '/': typeof IndexRoute + '/$': typeof SplatRoute '/docs/$': typeof DocsSplatRoute } export interface FileRoutesById { __root__: typeof rootRouteImport - '/': typeof IndexRoute + '/$': typeof SplatRoute '/docs/$': typeof DocsSplatRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/docs/$' + fullPaths: '/$' | '/docs/$' fileRoutesByTo: FileRoutesByTo - to: '/' | '/docs/$' - id: '__root__' | '/' | '/docs/$' + to: '/$' | '/docs/$' + id: '__root__' | '/$' | '/docs/$' fileRoutesById: FileRoutesById } export interface RootRouteChildren { - IndexRoute: typeof IndexRoute + SplatRoute: typeof SplatRoute DocsSplatRoute: typeof DocsSplatRoute } export interface FileServerRoutesByFullPath { @@ -82,11 +82,11 @@ export interface RootServerRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport + '/$': { + id: '/$' + path: '/$' + fullPath: '/$' + preLoaderRoute: typeof SplatRouteImport parentRoute: typeof rootRouteImport } '/docs/$': { @@ -111,7 +111,7 @@ declare module '@tanstack/react-start/server' { } const rootRouteChildren: RootRouteChildren = { - IndexRoute: IndexRoute, + SplatRoute: SplatRoute, DocsSplatRoute: DocsSplatRoute, } export const routeTree = rootRouteImport diff --git a/docs/src/routes/$.tsx b/docs/src/routes/$.tsx new file mode 100644 index 0000000..8c2f245 --- /dev/null +++ b/docs/src/routes/$.tsx @@ -0,0 +1,100 @@ +import { createFileRoute, notFound } from "@tanstack/react-router"; +import { createServerFn } from "@tanstack/react-start"; +import type { PageTree } from "fumadocs-core/server"; +import { createClientLoader } from "fumadocs-mdx/runtime/vite"; +import { DocsLayout } from "fumadocs-ui/layouts/docs"; +import defaultMdxComponents from "fumadocs-ui/mdx"; +import { + DocsBody, + DocsDescription, + DocsPage, + DocsTitle, +} from "fumadocs-ui/page"; +import { useMemo } from "react"; +import { baseOptions } from "@/lib/layout.shared"; +import { source } from "@/lib/source"; +import { docs } from "../../source.generated"; + +export const Route = createFileRoute("/$")({ + component: Page, + loader: async ({ params }) => { + const data = await loader({ data: params._splat?.split("/") ?? [] }); + await clientLoader.preload(data.path); + return data; + }, +}); + +const loader = createServerFn({ + method: "GET", +}) + .validator((slugs: string[]) => slugs) + .handler(async ({ data: slugs }) => { + const page = source.getPage(slugs); + if (!page) throw notFound(); + + return { + tree: source.pageTree as object, + path: page.path, + }; + }); + +const clientLoader = createClientLoader(docs.doc, { + id: "docs", + component({ toc, frontmatter, default: MDX }) { + return ( + + {frontmatter.title} + {frontmatter.description} + + + + + ); + }, +}); + +function Page() { + const data = Route.useLoaderData(); + const Content = clientLoader.getComponent(data.path); + const tree = useMemo( + () => transformPageTree(data.tree as PageTree.Folder), + [data.tree], + ); + + return ( + + + + ); +} + +function transformPageTree(tree: PageTree.Folder): PageTree.Folder { + function transform(item: T) { + if (typeof item.icon !== "string") return item; + + return { + ...item, + icon: ( + + ), + }; + } + + return { + ...tree, + index: tree.index ? transform(tree.index) : undefined, + children: tree.children.map((item) => { + if (item.type === "folder") return transformPageTree(item); + return transform(item); + }), + }; +} diff --git a/docs/src/routes/index.tsx b/docs/src/routes/index.tsx deleted file mode 100644 index c1c7ce2..0000000 --- a/docs/src/routes/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { createFileRoute, Link } from "@tanstack/react-router"; -import { HomeLayout } from "fumadocs-ui/layouts/home"; -import { baseOptions } from "@/lib/layout.shared"; - -export const Route = createFileRoute("/")({ - component: Home, -}); - -function Home() { - return ( - -

Fumadocs on Tanstack Start.

- - Open Docs - -
- ); -}