docs-v2/cypress/e2e/content/api-reference.cy.js

432 lines
14 KiB
JavaScript

/// <reference types="cypress" />
/**
* API Reference Documentation E2E Tests
*
* Tests both:
* 1. Legacy API reference pages (link validation, content structure)
* 2. New 3-column layout with tabs and TOC (for InfluxDB 3 Core/Enterprise)
*
* Run with:
* node cypress/support/run-e2e-specs.js --spec "cypress/e2e/content/api-reference.cy.js" content/influxdb3/core/reference/api/_index.md
*/
const fakeGoogleTagManager = {
trackingOptIn: () => {},
trackingOptOut: () => {},
};
describe('API reference content', () => {
const subjects = [
'/influxdb/cloud/api/',
'/influxdb/cloud/api/v1/',
'/influxdb/cloud/api/v1-compatibility/',
'/influxdb/cloud/api/v2/',
'/influxdb/v2/api/',
'/influxdb/v2/api/v1/',
'/influxdb/v2/api/v1-compatibility/',
'/influxdb/v2/api/v2/',
'/influxdb3/cloud-dedicated/api/',
'/influxdb3/cloud-dedicated/api/management/',
'/influxdb3/cloud-dedicated/api/v1/',
'/influxdb3/cloud-dedicated/api/v1-compatibility/',
'/influxdb3/cloud-dedicated/api/v2/',
'/influxdb3/cloud-serverless/api/',
'/influxdb3/cloud-serverless/api/v1/',
'/influxdb3/cloud-serverless/api/v1-compatibility/',
'/influxdb3/cloud-serverless/api/v2/',
'/influxdb3/clustered/api/',
// TODO '/influxdb3/clustered/api/management/',
'/influxdb3/clustered/api/v1/',
'/influxdb3/clustered/api/v1-compatibility/',
'/influxdb3/clustered/api/v2/',
'/influxdb3/core/api/',
'/influxdb3/enterprise/api/',
];
subjects.forEach((subject) => {
describe(subject, () => {
beforeEach(() => {
// Intercept and modify the page HTML before it loads
cy.intercept('GET', '**', (req) => {
req.continue((res) => {
if (res.headers['content-type']?.includes('text/html')) {
// Modify the Kapa widget script attributes
// Avoid socket errors from fpjs in tests by disabling fingerprinting
res.body = res.body.replace(
/data-user-analytics-fingerprint-enabled="true"/,
'data-user-analytics-fingerprint-enabled="false"'
);
}
});
});
cy.visit(subject);
window.fcdsc = fakeGoogleTagManager;
cy.stub(window.fcdsc, 'trackingOptIn').as('trackingOptIn');
cy.stub(window.fcdsc, 'trackingOptOut').as('trackingOptOut');
});
it(`has API info`, function () {
cy.get('script[data-user-analytics-fingerprint-enabled=false]').should(
'have.length',
1
);
cy.get('h1').first().should('have.length', 1);
// Check for description element (either article--description class or data-role attribute)
cy.get('.article--description, [data-role$=description]').should(
'have.length.at.least',
1
);
});
it('links back to the version home page', function () {
cy.get('a.back').contains('Docs').should('have.length', 1).click();
// Path should be the first two segments and trailing slash in $subject
cy.location('pathname').should(
'eq',
subject.replace(/^(\/[^/]+\/[^/]+\/).*/, '$1')
);
cy.get('h1').should('have.length', 1);
});
it('contains valid internal links', function () {
// The following conditional test isn't the cypress way, but the doc might not have internal links.
cy.get('body').then(($body) => {
if ($body.find('p a[href^="/"]').length === 0) {
cy.log('No internal links found');
return;
}
cy.get('p a[href^="/"]').as('internal-links');
cy.get('@internal-links').each(($a) => {
cy.log(`** Testing link ${$a.attr('href')} **`);
// cy.request doesn't show in your browser's Developer Tools
// because the request comes from Node, not from the browser.
cy.request($a.attr('href')).its('status').should('eq', 200);
});
});
});
it('contains valid external links', function () {
// The following conditional test isn't the cypress way, but the doc might not have external links.
cy.get('body').then(($body) => {
if ($body.find('p a[href^="http"]').length === 0) {
cy.log('No external links found');
return;
}
cy.get('p a[href^="http"]').as('external-links');
cy.get('@external-links').each(($a) => {
cy.log(`** Testing link ${$a.attr('href')} **`);
cy.request($a.attr('href')).its('status').should('eq', 200);
});
});
});
});
});
});
/**
* 3-Column API Reference Layout Tests
* Tests the new layout for InfluxDB 3 Core/Enterprise API documentation
* Tests individual API endpoint pages which use the 3-column layout with tabs
*/
describe('API reference 3-column layout', () => {
// Individual API endpoint pages (not index pages) have the 3-column layout
const layoutSubjects = [
'/influxdb3/core/api/v3/engine/',
'/influxdb3/enterprise/api/v3/engine/',
];
layoutSubjects.forEach((subject) => {
describe(`${subject} layout`, () => {
beforeEach(() => {
cy.intercept('GET', '**', (req) => {
req.continue((res) => {
if (res.headers['content-type']?.includes('text/html')) {
res.body = res.body.replace(
/data-user-analytics-fingerprint-enabled="true"/,
'data-user-analytics-fingerprint-enabled="false"'
);
}
});
});
cy.visit(subject);
});
describe('Layout Structure', () => {
it('displays sidebar', () => {
cy.get('.sidebar').should('be.visible');
});
it('displays API content area', () => {
cy.get('.api-content, .content-wrapper').should('exist');
});
it('displays TOC on desktop viewport', () => {
cy.viewport(1280, 800);
cy.get('.api-toc').should('be.visible');
});
it('hides TOC on mobile viewport', () => {
cy.viewport(375, 667);
cy.get('.api-toc').should('not.be.visible');
});
});
describe('API Navigation', () => {
it('displays API navigation section', () => {
cy.get('.api-nav').should('exist');
});
it('has collapsible navigation groups', () => {
cy.get('.api-nav-group').should('have.length.at.least', 1);
});
it('toggles group expand/collapse', () => {
cy.get('.api-nav-group-header').first().as('header');
cy.get('@header').click();
cy.get('@header')
.should('have.attr', 'aria-expanded')
.and('match', /true|false/);
});
});
describe('Tab Navigation', () => {
it('displays tabs', () => {
cy.get('.api-tabs-wrapper').should('exist');
});
it('shows Operations tab content by default', () => {
cy.get('[data-tab-panel="operations"]').should('be.visible');
});
it('switches tabs on click without page jump', () => {
// Get initial scroll position
cy.window().then((win) => {
const initialScroll = win.scrollY;
// Click the second tab
cy.get('.api-tabs-nav a').eq(1).click();
// Verify tabs are still visible (not jumped away)
cy.get('.api-tabs-wrapper').should('be.visible');
// Verify the clicked tab is now active
cy.get('.api-tabs-nav a').eq(1).should('have.class', 'is-active');
// Verify the first tab is no longer active
cy.get('.api-tabs-nav a')
.eq(0)
.should('not.have.class', 'is-active');
});
});
it('updates URL hash when switching tabs', () => {
cy.get('.api-tabs-nav a[data-tab="server"]').click();
cy.url().should('include', '#server');
});
it('restores tab from URL hash on page load', () => {
// Use the current subject URL with hash instead of hardcoded old reference URL
cy.visit(`${subject}#authentication`);
cy.get('.api-tabs-nav a[data-tab="authentication"]').should(
'have.class',
'is-active'
);
cy.get('[data-tab-panel="authentication"]').should('be.visible');
});
});
describe('Table of Contents', () => {
it('displays TOC header', () => {
cy.viewport(1280, 800);
cy.get('.api-toc-header').should('contain', 'ON THIS PAGE');
});
it('generates TOC from headings', () => {
cy.viewport(1280, 800);
cy.wait(500); // Wait for component initialization
cy.get('.api-toc-nav').should('exist');
});
});
describe('API Renderer', () => {
it('loads API documentation renderer', () => {
cy.get(
'.api-reference-container, rapi-doc, .api-reference-wrapper'
).should('exist');
});
it('displays spec download link', () => {
cy.get('.api-spec-download').should('exist');
});
});
describe('Accessibility', () => {
it('has ARIA attributes on nav groups', () => {
cy.get('.api-nav-group-header').each(($header) => {
cy.wrap($header).should('have.attr', 'aria-expanded');
});
});
});
});
});
});
/**
* RapiDoc Mini Component Tests
* Tests the api-operation shortcode and RapiDoc Mini component behavior
*/
describe('RapiDoc Mini component', () => {
// Operation pages use RapiDoc Mini for single operation rendering
const operationPages = [
'/influxdb3/core/api/write/post/',
'/influxdb3/core/api/api/v3/write_lp/post/',
];
operationPages.forEach((page) => {
describe(`Operation page ${page}`, () => {
beforeEach(() => {
cy.intercept('GET', '**', (req) => {
req.continue((res) => {
if (res.headers['content-type']?.includes('text/html')) {
res.body = res.body.replace(
/data-user-analytics-fingerprint-enabled="true"/,
'data-user-analytics-fingerprint-enabled="false"'
);
}
});
});
cy.visit(page);
});
describe('Component initialization', () => {
it('renders rapidoc-mini component container', () => {
cy.get('[data-component="rapidoc-mini"]').should('exist');
});
it('has data-spec-url attribute', () => {
cy.get('[data-component="rapidoc-mini"]')
.should('have.attr', 'data-spec-url')
.and('match', /\.ya?ml$/);
});
it('has data-match-paths attribute', () => {
cy.get('[data-component="rapidoc-mini"]')
.should('have.attr', 'data-match-paths')
.and('match', /^(get|post|put|patch|delete)\s+\//i);
});
it('includes machine-readable spec links', () => {
cy.get('link[rel="alternate"][type="application/x-yaml"]').should(
'exist'
);
cy.get('link[rel="alternate"][type="application/json"]').should(
'exist'
);
});
});
describe('RapiDoc element creation', () => {
it('creates rapi-doc-mini custom element', () => {
// Wait for component to initialize and create the element
cy.get('rapi-doc-mini', { timeout: 10000 }).should('exist');
});
it('rapi-doc-mini has spec-url attribute', () => {
cy.get('rapi-doc-mini', { timeout: 10000 })
.should('have.attr', 'spec-url')
.and('match', /\.ya?ml$/);
});
it('rapi-doc-mini has theme attributes', () => {
cy.get('rapi-doc-mini', { timeout: 10000 }).should(
'have.attr',
'theme'
);
});
});
});
});
describe('api-operation shortcode on example page', () => {
beforeEach(() => {
cy.intercept('GET', '**', (req) => {
req.continue((res) => {
if (res.headers['content-type']?.includes('text/html')) {
res.body = res.body.replace(
/data-user-analytics-fingerprint-enabled="true"/,
'data-user-analytics-fingerprint-enabled="false"'
);
}
});
});
cy.visit('/example/');
});
describe('Multiple instances', () => {
it('renders multiple rapidoc-mini containers', () => {
cy.get('[data-component="rapidoc-mini"]').should(
'have.length.at.least',
2
);
});
it('each instance has unique match-paths', () => {
cy.get('[data-component="rapidoc-mini"]').then(($containers) => {
const matchPaths = [];
$containers.each((_, el) => {
const path = el.getAttribute('data-match-paths');
expect(matchPaths).to.not.include(path);
matchPaths.push(path);
});
});
});
it('each instance creates its own rapi-doc-mini element', () => {
cy.get('rapi-doc-mini', { timeout: 10000 }).should(
'have.length.at.least',
2
);
});
});
});
describe('Theme synchronization', () => {
beforeEach(() => {
cy.intercept('GET', '**', (req) => {
req.continue((res) => {
if (res.headers['content-type']?.includes('text/html')) {
res.body = res.body.replace(
/data-user-analytics-fingerprint-enabled="true"/,
'data-user-analytics-fingerprint-enabled="false"'
);
}
});
});
cy.visit('/influxdb3/core/api/write/post/');
});
it('applies light theme by default', () => {
cy.get('rapi-doc-mini', { timeout: 10000 })
.should('have.attr', 'theme')
.and('match', /light|dark/);
});
it('rapi-doc-mini has background color attribute', () => {
cy.get('rapi-doc-mini', { timeout: 10000 }).should(
'have.attr',
'bg-color'
);
});
it('rapi-doc-mini has text color attribute', () => {
cy.get('rapi-doc-mini', { timeout: 10000 }).should(
'have.attr',
'text-color'
);
});
});
});