npm.io
0.2.0 • Published yesterdayCLI

cypress-json-tests

Licence
MIT
Version
0.2.0
Deps
1
Size
56 kB
Vulns
0
Weekly
0

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):

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)
npx cypress-json-tests open --config "fixturesFolder=./fixtures,baseUrl=https://example.com" --expose drush_prefix=ddev
Headless Mode (CLI)
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:

{
  "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.