# cypress-json-tests

> Cypress tests defined by JSON file.

Latest version **0.2.0** (published 2026-09-23) · MIT license · 0 weekly downloads

## Install

```sh
npm install cypress-json-tests
pnpm add cypress-json-tests
yarn add cypress-json-tests
bun add cypress-json-tests
```

Provides the command `cypress-json-tests`.

## Health

**Score 55/100 (C)** — status: active.

Positive: no vulnerabilities; recently updated; high maintenance score.

Warnings: low downloads; no types; no esm support; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.2.0 |
| Published | 2026-09-23 |
| First published | 2025-10-09 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 1 |
| Unpacked size | 55.9 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | jardasmahel |
| Maintainers | jaromir.smahel |

## Links

- npm: https://www.npmjs.com/package/cypress-json-tests
- npm.io page: https://npm.io/package/cypress-json-tests

## Dependencies (1)

- [json5](https://npm.io/package/json5.md) ^2.2.3

## Recent versions

- 0.2.0 (latest) — 2026-09-23
- 0.1.8 — 2026-05-07
- 0.1.7 — 2026-05-07
- 0.1.6 — 2026-05-07
- 0.1.5 — 2026-05-07
- 0.1.4 — 2025-11-05
- 0.1.3 — 2025-10-14
- 0.1.2 — 2025-10-14
- 0.1.1 — 2025-10-09
- 0.1.0 — 2025-10-09

## README

# Cypress JSON Tests

A Cypress-based testing framework specifically designed for Drupal projects. This tool allows you to define end-to-end (E2E) workflows and HTTP status code validation tests entirely through a single JSON/JSON5 configuration file, without needing to write custom Cypress JavaScript code for most common test scenarios.

## Features
- **JSON / JSON5-Driven Testing:** Define your test cases, UI interactions, and assertions entirely in a `config.json5` (or `config.json`) file. Supports JSON5 features such as single-line and multi-line comments, trailing commas, and single-quoted strings.
- **Drupal Integration:** Built-in commands to interact with Drupal via Drush (supports DDEV, Lando, and Ahoy environments). Includes automated user login, blocking/unblocking, and role management.
- **Status Code Validation:** Easily verify that specific URLs return expected HTTP status codes for different user roles (e.g., ensuring anonymous users get 403 on admin pages).
- **UI Workflow Validation:** Simulate complex user journeys including filling forms, interacting with CKEditor 5, changing moderation states, validating text and meta tags, and managing content creation/deletion.
- **Resilient Execution:** Each scenario runs as an individual test so a failure does not halt the entire suite (though workflow scenarios may be order-coupled when sharing content such as `/testing-page`). Status checks evaluate all URLs in a list and report all mismatches together rather than stopping at the first failure.
- **Consolidated Reporting:** Generates a formatted summary report in the console upon completion and outputs detailed report artifacts (`reports/validation-report.md` and `reports/validation-report.json`).

## Installation
Install the package as a development dependency alongside Cypress (requires Cypress `>=15.10.0`):

```bash
npm install cypress-json-tests --save-dev
```

## Running Cypress Tests
You can run the tests directly from your project root without extracting or copying files from `node_modules`. Point the runner to your project's fixtures folder containing `config.json5` or `config.json`, set your site's `baseUrl`, and optionally specify your local environment's Drush prefix (e.g. `ddev`, `lando`, `ahoy`):

### Interactive Mode (GUI)
```bash
npx cypress-json-tests open --config "fixturesFolder=./fixtures,baseUrl=https://example.com" --expose drush_prefix=ddev
```

### Headless Mode (CLI)
```bash
npx cypress-json-tests run --config "fixturesFolder=./fixtures,baseUrl=https://example.com" --expose drush_prefix=ddev
```

### Options Reference:
- `fixturesFolder`: Relative or absolute path to the folder containing your `config.json5` or `config.json` file.
- `baseUrl`: The base URL of your target Drupal site (replace `https://example.com` with your actual site URL).
- `--expose drush_prefix=<env>`: *(Optional)* Environment wrapper to prefix Drush commands (`ddev`, `lando`, `ahoy`). Omit if `drush` is available directly on your host `PATH`.

### Hot Reloading in Interactive Mode (`cypress open`)
The test runner watches your fixtures folder for changes and syncs the configuration automatically:
- **Step & check modifications:** Edits to scenario steps, URLs, form actions, and check assertions in `config.json5` (or `config.json`) reload and re-run automatically upon saving.
- **Scenario list changes:** Adding, renaming, or removing top-level scenarios is synchronized via a local cache file required by the spec runner, allowing Mocha to register updated scenarios immediately upon saving.
- **Parse errors:** A missing or invalid config file fails the run with the parser message rather than silently re-running the last good configuration.

## Configuration Format (`config.json5` / `config.json`)

Create a `config.json5` (or `config.json`) file in your fixtures folder to define your test suite. JSON5 syntax is supported, allowing comments and trailing commas. Example structure:

```json5
{
  "skip_antibot": false,
  "users": {
    "author": "author",
    "approver": "approver",
    "structure": "structure",
    "site_administrator": "site-administrator"
  },
  "scenarios": {
    "Anonymous user status": [
      {
        "url": "/",
        "role": "anonymous",
        "status": 200
      },
      {
        "url": "/admin",
        "role": "anonymous",
        "status": 403
      },
      {
        "url": "/zzz",
        "role": "anonymous",
        "status": 404
      }
    ],
    "Structure user status": [
      {
        "url": "/admin/structure/taxonomy",
        "role": "structure",
        "status": 200
      }
    ],
    "Article URL": [
      {
        "url": "/node/add/article",
        "role": "author",
        "action": {
          "fill_values": {
            "#edit-title-0-value": "Testing article",
            "#edit-field-summary-0-value": "Test article summary."
          },
          "select_values": {
            "#edit-field-article-type": "429"
          },
          "click": [
            "#edit-submit"
          ]
        }
      },
      {
        "clean_this": true,
        "url": "/news/testing-article",
        "role": "author",
        "checks": [
          {
            "contain": {
              "h1": "Testing article"
            }
          },
          {
            "meta": {
              "title": "Testing article | Drupal 11",
              "description": "Test article summary."
            }
          }
        ]
      }
    ],
    "Workflow: Changing Page State": [
      {
        "name": "Open edit page",
        "clean_this": true,
        "url": "/testing-page",
        "role": "author",
        "action": {
          "click": [
            ".page__admin .btn:contains('Edit')"
          ]
        }
      },
      {
        "name": "Update moderation state and menu settings",
        "role": "author",
        "action": {
          "select_values": {
            "#edit-moderation-state-0-state": "needs_review"
          },
          "click": [
            "#edit-menu",
            "#edit-menu-enabled",
            "#edit-submit"
          ]
        }
      }
    ]
  }
}
```

### Top-Level Properties:
- `skip_antibot`: Boolean. If `true`, grants the `"skip antibot"` permission to the anonymous role before testing and removes it during cleanup.
- `users`: Key-value map of **role identifiers** to actual **Drupal usernames** (e.g. `"site_administrator": "site-administrator"`).
  - **Account Lifecycle**: In the test setup (`before()` hook), all mapped accounts are automatically unblocked via Drush (`cy.unblockUserByUsername()`) and Two-Factor Authentication (TFA) settings are cleared if the `tfa` module is active. Once all tests complete (`after()` hook), all test accounts are re-blocked via Drush (`cy.blockUserByUsername()`).
  - **Session Authentication**: During tests, Cypress logs in as the mapped username using Drush and caches the session via `cy.session()`. Subsequent logins for that role are restored instantly from cache.
- `scenarios`: Map of validation scenario names to arrays of step objects.

### Core Data Structures & Concepts

The test suite is structured around four primary concepts: **Scenario**, **Request**, **Action**, and **Check**.

```
Scenario (Array of Requests)
 └── Request (Single navigation, probe, or page interaction)
      ├── Action (User input, dropdowns, clicks)
      └── Checks (Array of Check assertion objects: status, content, metadata)
```

---

#### 1. Scenario
A **Scenario** represents an end-to-end user journey or validation suite registered as an individual Cypress test (though scenarios within a workflow may be order-coupled).
- **Location**: Defined as keys under the top-level `"scenarios"` object.
- **Structure**:
  | Field | Type | Required | Description |
  | :--- | :--- | :--- | :--- |
  | **Scenario Name** *(Key)* | `string` | **Required** | Unique human-readable title of the test (e.g. `"Workflow: Changing from Draft to Needs review as Author"`). |
  | **Value** | `Request[]` | **Required** | Ordered array of **Request** objects executed in sequence. |

---

#### 2. Request
A **Request** represents a single step within a Scenario. It can probe an HTTP endpoint, navigate to a page, perform actions, and/or assert conditions.
- **Location**: Items within a Scenario array.
- **Structure**:
  | Field | Type | Required | Description |
  | :--- | :--- | :--- | :--- |
  | `name` | `string` | Optional | Descriptive label for the step (logged in test execution reports). |
  | `url` | `string` | Optional | Path or URI to visit or check (e.g. `"/testing-page"`, `"/node/add/page"`). Omit to continue on the current page. |
  | `role` | `string` | Optional | User role for authentication (maps to `users`, or `"anonymous"`). Defaults to `"anonymous"` when omitted. |
  | `status` | `number` | Optional | Expected HTTP status code (e.g. `200`, `403`, `404`). The URL is probed via fast headless `cy.request()` without DOM rendering, not visited — put `action`/`checks` for that page in a separate request. |
  | `clean_this` | `boolean` | Optional | If `true`, the URL alias is deleted via Drush before tests run and cleaned up after completion. |
  | `action` | `Action` | Optional | **Action** object containing user interactions to perform on the page. |
  | `checks` | `Check[]` | Optional | Array of **Check** objects containing assertions to verify on the page. |

> **Note on Sessions**: The authenticated session persists across consecutive requests that share the same role. `cy.session()` is only called when the `role` changes or on the first request.

---

#### 3. Action
An **Action** defines form inputs, dropdown selections, rich text edits, and click interactions to execute on the active page.
- **Location**: The `action` property of a `Request`.
- **Structure**:
  | Field | Type | Required | Description | Shape / Example |
  | :--- | :--- | :--- | :--- | :--- |
  | `fill_values` | `object` | Optional | Map of CSS selectors to text strings to type into input or textarea fields (clears existing field content before typing). | `{ "<css_selector>": "text value" }` |
  | `select_values` | `object` | Optional | Map of CSS selectors to option values to select in `<select>` elements. | `{ "<css_selector>": "option_value" }` |
  | `ck5_values` | `object` | Optional | Map of CKEditor 5 element IDs to HTML strings to set. | `{ "<editor_id>": "<p>Content</p>" }` |
  | `click` | `string[]` \| `string` | Optional | CSS selector(s) to click sequentially (e.g. clicking a `<details>` summary like `"#edit-menu summary"` to expand a section before clicking inputs inside it). Confirmation modals (e.g. "Yes") are confirmed automatically. | `["#edit-menu summary", "#edit-menu-enabled", "#edit-submit"]` |

---

#### 4. Check
A **Check** defines assertions to verify against the current page or HTTP response.
- **Location**: Items within the `checks` array property of a `Request`.
- **Structure**:
  | Field | Type | Required | Description | Shape / Example |
  | :--- | :--- | :--- | :--- | :--- |
  | `contain` | `object` | Optional | Asserts that element matching selector contains specified text. | `{ "h1": "Testing page" }` |
  | `not_contain` | `object` | Optional | Asserts that element matching selector does *not* contain specified text (safe if absent). | `{ ".body": "Draft" }` |
  | `exists_and_contain` | `object` | Optional | Asserts that element exists in the DOM **and** contains specified text. | `{ ".alert": "Success" }` |
  | `exists_but_not_contain` | `object` | Optional | Asserts that element exists in the DOM **but does not** contain specified text. | `{ ".page__admin .btn": "Delete" }` |
  | `meta` | `object` | Optional | Validates `<head>` tags: `title` and/or `description`. | `{ "title": "Page Title", "description": "Summary" }` |
  | `select` | `object` | Optional | Asserts that `<select>` element contains all specified option values. | `{ "#edit-state": ["draft", "needs_review"] }` |

### Step Execution Sequence

Within each step (`Request`), operations execute in this deterministic sequence:

1. **Session / Role Switch**: If `role` differs from the active session, logs in via `cy.drupalLogin()` or clears cookies/storage for `anonymous`.
2. **Navigation or HTTP Probe**:
   - If `status` is set: executes a fast headless HTTP check via `cy.request(step.url)`. The URL is probed, not visited — put `action`/`checks` for that page in a separate request.
   - If `status` is omitted: navigates the browser to the page via `cy.visit(step.url)`.
3. **Form Inputs (`action.fill_values`)**: Overwrites existing field values by clearing `<input>` and `<textarea>` fields first and then typing the specified value via `cy.get().clear()` and `cy.get().type()`.
4. **Rich Text (`action.ck5_values`)**: Sets HTML content in CKEditor 5 instances via `editor.setData()`.
5. **Dropdown Selections (`action.select_values`)**: Selects options in `<select>` elements via `cy.get().select()`.
6. **Checks & Assertions (`checks`)**: Validates DOM content, options, and `<head>` metadata. Runs *before* clicks so that form states and available dropdown choices can be verified before form submission.
7. **Clicks (`action.click`)**: Sequentially clicks specified CSS selectors (e.g. clicking a collapsible summary `"#edit-menu summary"` to expand an accordion, checkboxes, buttons). Automatically confirms modal confirmation dialogs ("Yes").

## Test Reports
Upon test completion, a consolidated summary report is printed to the terminal console and saved as artifacts:
- `reports/validation-report.md`: Markdown summary table with status, duration, and failure details (ideal for PR comments or CI summaries).
- `reports/validation-report.json`: JSON report for CI/CD metrics.

## Available Cypress Commands/Helpers
This package provides several custom Cypress commands/helpers specifically for Drupal testing:
- `cy.drush(command, args, options)`: Executes a Drush command with the environment prefix.
- `cy.drupalLogin(username)`: Generates a random password, sets it via Drush, and logs the user in via the login form.
- `cy.drupalLoginWithUID(uid)`: Logs a user in by user ID via Drush one-time login link (`user:login`).
- `cy.loginUserByUsername(username)`: Logs a user in by username via Drush one-time login link (`uli`).
- `cy.drupalLogout(username)`: Logs the user out by removing their session via Drush.
- `cy.drupalLogoutAll()`: Logs out all users by clearing cookies and truncating the sessions table via Drush.
- `cy.setPasswordToUsername(username, password)`: Sets a user's password via Drush `user:password` (`upwd`).
- `cy.blockUserByUsername(username)` / `cy.unblockUserByUsername(username)`: Blocks/unblocks user accounts via Drush (resets TFA user data when TFA is enabled).
- `cy.isModuleEnabled(module_machine_name)`: Checks whether a Drupal module is currently enabled via Drush.
- `cy.deletePageByAlias(pageUrl)`: Resolves the given URL alias to a node ID via Drush and deletes it, if a matching node exists.

---
_Source: https://npm.io/package/cypress-json-tests · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
