# Cypress E2e Testing

> Run, validate, and analyze Cypress E2E tests for the InfluxData documentation site. Covers Hugo server management, test execution modes, and failure analysis. Use when writing or running Cypress tests, verifying rendered pages, checking JSON-LD/structured data, or debugging E2E failures after template, layout, or content changes.

- Skill: `influxdata/cypress-e2e-testing` (Agent Skill)
- Install (CLI): `npx skillmds@latest add influxdata/cypress-e2e-testing`
- Raw SKILL.md: https://api.skillmd.com/api/skills/influxdata/cypress-e2e-testing/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: influxdata (https://skillmd.com/u/influxdata)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/influxdata/cypress-e2e-testing

---


# Cypress E2E Testing Skill

## Purpose

This skill guides agents through running Cypress end-to-end tests for the documentation site, including understanding when Hugo starts automatically vs. manually, interpreting test results, and debugging failures.

For comprehensive testing documentation, see **[DOCS-TESTING.md](../../../DOCS-TESTING.md)**.

## Key Insight: Hugo Server Management

**The test runner (`run-e2e-specs.js`) automatically manages Hugo.**

- **Port 1315** is used for testing (not 1313)
- If port 1315 is free → starts Hugo automatically
- If port 1315 is in use → checks if it's a working Hugo server and reuses it
- Hugo logs written to `/tmp/hugo_server.log`

**You do NOT need to start Hugo separately** unless you want to keep it running between test runs for faster iteration.

## Quick Reference

| Task                            | Command                                                                                                 |
| ------------------------------- | ------------------------------------------------------------------------------------------------------- |
| Test content file               | `node cypress/support/run-e2e-specs.js content/path/to/file.md`                                         |
| Test with specific spec         | `node cypress/support/run-e2e-specs.js --spec "cypress/e2e/content/spec.cy.js" content/path/to/file.md` |
| Functionality test (no content) | `node cypress/support/run-e2e-specs.js --spec "cypress/e2e/page-context.cy.js" --no-mapping`            |
| Test shortcode examples         | `yarn test:shortcode-examples`                                                                          |

## Prerequisites

```bash
# Install dependencies (required)
yarn install

# Verify Cypress is available
yarn cypress --version
```

### API Reference Tests: Additional Prerequisites

**API reference pages require generation before testing.** The pages don't exist until you run:

```bash
# Generate API documentation content from OpenAPI specs
yarn build:api-docs
```

This step:

- Processes OpenAPI specs in `api-docs/` directories
- Generates Hugo content pages in `content/*/api/`
- Creates operation pages, tag pages, and index pages

**Without this step**, all API reference tests will fail with 404 errors.

**Quick check** - verify API content exists:

```bash
# Should list generated API content directories
ls content/influxdb3/core/api/

# If "No such file or directory", run: yarn build:api-docs
```

### Markdown Validation Tests: Additional Prerequisites

**Markdown validation tests require generated markdown files.** Run:

```bash
# Build Hugo site first (generates HTML in public/)
npx hugo --quiet

# Generate LLM-friendly markdown from HTML
yarn build:md
```

This creates `.md` files in the `public/` directory that the markdown validation tests check.

**Without this step**, markdown validation tests will fail with missing file errors.

## Test Execution Modes

### Mode 1: Content-Specific Tests (Default)

Tests specific content files by mapping them to URLs.

```bash
# Single file
node cypress/support/run-e2e-specs.js content/influxdb3/core/_index.md

# Multiple files
node cypress/support/run-e2e-specs.js content/influxdb3/core/_index.md content/influxdb3/enterprise/_index.md

# With specific test spec
node cypress/support/run-e2e-specs.js \
  --spec "cypress/e2e/content/api-reference.cy.js" \
  content/influxdb3/core/reference/api/_index.md
```

**What happens:**

1. Maps content files to URLs (e.g., `content/influxdb3/core/_index.md` → `/influxdb3/core/`)
2. Starts Hugo on port 1315 (if not running)
3. Runs Cypress tests against mapped URLs
4. Stops Hugo when done

### Mode 2: Functionality Tests (`--no-mapping`)

Tests UI functionality without requiring content file paths.

```bash
# Run functionality test
node cypress/support/run-e2e-specs.js \
  --spec "cypress/e2e/page-context.cy.js" \
  --no-mapping
```

**Use when:** Testing JavaScript components, theme switching, navigation, or other UI behavior not tied to specific content.

### Mode 3: Reusing an Existing Hugo Server

For faster iteration during development:

```bash
# Terminal 1: Start Hugo manually on port 1315
npx hugo server --port 1315 --environment testing --noHTTPCache

# Terminal 2: Run tests (will detect and reuse existing server)
node cypress/support/run-e2e-specs.js \
  --spec "cypress/e2e/content/api-reference.cy.js" \
  content/influxdb3/core/reference/api/_index.md
```

## Available Test Specs

| Spec File                                               | Purpose                                                     |
| ------------------------------------------------------- | ----------------------------------------------------------- |
| `cypress/e2e/content/api-reference.cy.js`               | API reference pages (Hugo-native templates, layouts, links) |
| `cypress/e2e/content/index.cy.js`                       | General content validation                                  |
| `cypress/e2e/content/markdown-content-validation.cy.js` | LLM markdown generation                                     |
| `cypress/e2e/page-context.cy.js`                        | Page context and navigation                                 |

## Understanding Test Output

### Success Output

```
✅ e2e tests completed successfully
📊 Detailed Test Results:
   • Total Tests: 25
   • Tests Passed: 25
   • Tests Failed: 0
```

### Failure Output

```
ℹ️ Note: 3 test(s) failed.
📊 Detailed Test Results:
   • Total Tests: 25
   • Tests Passed: 22
   • Tests Failed: 3

📋 Failed Spec Files:
   • cypress/e2e/content/api-reference.cy.js
     - Failures: 3
     - Failed Tests:
       * has API info
         Error: Expected to find element '.article--description'
```

### Common Failure Patterns

| Error                               | Likely Cause                        | Solution                         |
| ----------------------------------- | ----------------------------------- | -------------------------------- |
| All API tests fail with 404         | API content not generated           | Run `yarn build:api-docs` first  |
| `Expected to find element 'X'`      | Selector changed or element removed | Update test or fix template      |
| `Timed out waiting for element`     | Page load issue or JS error         | Check Hugo logs, browser console |
| `cy.request() failed`               | Broken link or 404                  | Fix the link in content          |
| `Hugo server died during execution` | Build error or memory issue         | Check `/tmp/hugo_server.log`     |

## Debugging Failures

### Step 1: Check Hugo Logs

```bash
cat /tmp/hugo_server.log | tail -50
```

Look for:

- Template errors (`error calling partial`)
- Build failures
- Missing data files

### Step 2: Run Test in Interactive Mode

```bash
# Start Hugo manually
npx hugo server --port 1315 --environment testing

# In another terminal, open Cypress interactively
yarn cypress open
```

### Step 3: Inspect the Page

Visit `http://localhost:1315/path/to/page/` in a browser and:

- Open DevTools Console for JavaScript errors
- Inspect elements to verify selectors
- Check Network tab for failed requests

### Step 4: Run Single Test with Verbose Output

```bash
DEBUG=cypress:* node cypress/support/run-e2e-specs.js \
  --spec "cypress/e2e/content/api-reference.cy.js" \
  content/influxdb3/core/reference/api/_index.md
```

## Test Configuration

The test runner uses these settings:

```javascript
{
  browser: 'chrome',
  baseUrl: 'http://localhost:1315',
  video: false,  // Disabled in CI
  defaultCommandTimeout: 10000,  // 15000 in CI
  pageLoadTimeout: 30000,  // 45000 in CI
}
```

## Writing New Tests

### Basic Test Structure

```javascript
describe('Feature Name', () => {
  beforeEach(() => {
    cy.visit('/path/to/page/');
  });

  it('validates expected behavior', () => {
    cy.get('.selector').should('exist');
    cy.get('.selector').should('be.visible');
    cy.get('.selector').contains('Expected text');
  });
});
```

### Testing Components

```javascript
describe('Component Name', () => {
  it('initializes correctly', () => {
    cy.visit('/path/with/component/');

    // Wait for component initialization
    cy.get('[data-component="my-component"]', { timeout: 5000 })
      .should('be.visible');

    // Verify component rendered expected elements
    cy.get('[data-component="my-component"] .child-element')
      .should('have.length.at.least', 1);
  });
});
```

### Using Real Configuration Data

Import real configuration data (from `data/*.yml`) via `cy.task('getData')` instead of hardcoding expected values. This keeps tests in sync with the source of truth.

```javascript
describe('Product shortcodes', function () {
  let products;

  before(function () {
    // Load products.yml via the getData task defined in cypress.config.js
    cy.task('getData', 'products').then((data) => {
      products = data;
    });
  });

  it('renders the correct product name', function () {
    cy.visit('/influxdb3/core/_test/shortcodes/');
    // Assert against YAML data, not a hardcoded string
    cy.get('[data-testid="product-name"]').should(
      'contain.text',
      products.influxdb3_core.name
    );
  });

  it('renders current-version from YAML', function () {
    cy.visit('/influxdb/v2/_test/shortcodes/');
    // Derive expected value the same way the Hugo shortcode does
    const patch = products.influxdb.latest_patches?.v2;
    const expected = patch ? patch.replace(/\.\d+$/, '') : '';
    cy.get('[data-testid="current-version"] .current-version').should(
      'have.text',
      expected
    );
  });
});
```

**Key principles:**

- Load YAML data in `before()` — available to all tests in the suite
- Derive expected values from the data, mirroring shortcode logic
- Only hardcode what you must: content paths and test page URLs
- Derive boolean flags from data fields (e.g., `product.distributed_architecture`, `product.limits`)

See `cypress/e2e/content/shortcodes.cy.js` and `cypress/e2e/content/latest-patch-shortcode.cy.js` for full examples.

### Testing Links

```javascript
it('contains valid internal links', () => {
  cy.get('body').then(($body) => {
    if ($body.find('a[href^="/"]').length === 0) {
      cy.log('No internal links found');
      return;
    }

    cy.get('a[href^="/"]').each(($a) => {
      cy.request($a.attr('href')).its('status').should('eq', 200);
    });
  });
});
```

### Testing structured data (JSON-LD)

The `layouts/partials/header/*-jsonld.html` partials emit schema.org JSON-LD.
The right assertions depend on the node's scope:

- **Page-scoped nodes** (`TechArticle`, `SoftwareApplication`) describe a
  specific page or product. Assert presence/shape where they belong and
  **absence** where they don't — the absence check guards against over-emission
  (e.g. a SoftwareApplication node leaking onto deep pages instead of only
  product landing roots).
- **Global nodes** (`Organization`) describe the site's single entity and are
  emitted site-wide with a stable `@id`. Assert **exactly one** per page across
  page classes — that catches both omission (a page class emitting nothing) and
  accidental duplicate emission.

Parse `<script type="application/ld+json">` by `@type`, then assert. See
`cypress/e2e/content/jsonld-organization.cy.js` and `jsonld-techarticle.cy.js`
for the established pattern:

```javascript
function ldByType(win$, doc, type) {
  return [...win$(doc).find('script[type="application/ld+json"]')]
    .map((s) => { try { return JSON.parse(s.textContent); } catch { return null; } })
    .filter((j) => j && j['@type'] === type);
}

// Page-scoped: present on the landing root, absent on a deep page.
it('emits SoftwareApplication on the product root, none on deep pages', () => {
  cy.visit('/influxdb3/core/');
  cy.document().then((doc) => {
    expect(ldByType(Cypress.$, doc, 'SoftwareApplication')).to.have.length(1);
  });
  cy.visit('/influxdb3/core/admin/');
  cy.document().then((doc) => {
    expect(ldByType(Cypress.$, doc, 'SoftwareApplication')).to.have.length(0);
  });
});

// Global: exactly one on every page class (hub, root, deep article).
it('emits exactly one Organization on a deep page', () => {
  cy.visit('/influxdb3/core/admin/');
  cy.document().then((doc) => {
    expect(ldByType(Cypress.$, doc, 'Organization')).to.have.length(1);
  });
});
```

**Cypress proves the markup is emitted where intended. It does not validate
schema correctness.** For that, use the Schema Markup Validator
(`https://validator.schema.org`) — **not** the Google Rich Results Test, which
reports "no items detected" for `Organization`, `TechArticle`, and
`SoftwareApplication` because they aren't rich-result types (only `FAQPage`
is). See the hugo-template-dev skill, "Validating structured data (JSON-LD)".

## CI/CD Considerations

In CI environments:

- Video recording is disabled to save resources
- Timeouts are increased (15s command, 45s page load)
- Memory management is enabled
- Only 1 test kept in memory at a time

## Related Files

- **Test runner**: `cypress/support/run-e2e-specs.js`
- **Hugo server helper**: `cypress/support/hugo-server.js`
- **URL mapper**: `cypress/support/map-files-to-urls.js`
- **Config**: `cypress.config.js`
- **Comprehensive docs**: `DOCS-TESTING.md`

## Checklist for Test Validation

Before concluding test analysis:

- [ ] For API tests: Verify `yarn build:api-docs` was run (check `ls content/*/api/`)
- [ ] All tests passed, or failures are understood
- [ ] Hugo logs checked for build errors
- [ ] Failed selectors verified against current templates
- [ ] Broken links identified and reported
- [ ] JavaScript console errors investigated (if relevant)
- [ ] For JSON-LD changes: presence/absence asserted in Cypress, and schema validated via `validator.schema.org` (not the Rich Results Test)

## Related Skills

- **hugo-template-dev** - For Hugo template syntax, data access patterns, and runtime testing. Includes the **PR preview-pages mechanism** — when the change is visual or structural (canonical/meta tags, JSON-LD, head fragments, layout reflows) and Cypress is overkill, list affected URLs in the PR description so the preview workflow lands reviewers on the exact pages without local setup.
- **docs-cli-workflow** - For creating/editing documentation content with CLI tools
- **ts-component-dev** (agent) - TypeScript component behavior and interactivity
- **hugo-ui-dev** (agent) - Hugo templates and SASS/CSS styling

