docs-v2/cypress/e2e/content/llm-format-selector.cy.js

290 lines
9.7 KiB
JavaScript

/**
* E2E tests for LLM-friendly format selector component
* These tests validate the format selector dropdown for both leaf nodes (single pages)
* and branch nodes (sections with children).
*/
describe('LLM Format Selector', () => {
// Test configuration
const LEAF_PAGE_URL = '/influxdb3/core/get-started/setup/';
const SMALL_SECTION_URL = '/influxdb3/core/get-started/'; // Section with ≤10 pages
const LARGE_SECTION_URL = '/influxdb3/core/query-data/'; // Section with >10 pages (if exists)
/**
* Setup: Generate markdown files for test paths
* This runs once before all tests in this suite
*/
before(() => {
cy.log('Generating markdown files for test paths...');
// Generate markdown for get-started section (small section + leaf page)
cy.exec(
'node scripts/html-to-markdown.js --path influxdb3/core/get-started',
{
failOnNonZeroExit: false,
timeout: 60000,
}
).then((result) => {
if (result.code !== 0) {
cy.log(
'Warning: get-started markdown generation had issues:',
result.stderr
);
}
});
// Generate markdown for query-data section (large section)
cy.exec(
'node scripts/html-to-markdown.js --path influxdb3/core/query-data --limit 15',
{
failOnNonZeroExit: false,
timeout: 60000,
}
).then((result) => {
if (result.code !== 0) {
cy.log(
'Warning: query-data markdown generation had issues:',
result.stderr
);
}
cy.log('Markdown files generated successfully');
});
});
describe('Format Selector - Leaf Nodes (Single Pages)', () => {
beforeEach(() => {
cy.visit(LEAF_PAGE_URL);
// Wait for component initialization
cy.window().should((win) => {
expect(win.influxdatadocs).to.exist;
expect(win.influxdatadocs.instances).to.exist;
expect(win.influxdatadocs.instances['format-selector']).to.exist;
});
});
it('should display format selector button with correct label', () => {
cy.get('[data-component="format-selector"]')
.should('exist')
.should('be.visible');
cy.get(
'[data-component="format-selector"] .format-selector__button'
).should('contain', 'Copy page for AI');
});
describe('Dropdown functionality', () => {
beforeEach(() => {
// Open dropdown once for all tests in this block
cy.get(
'[data-component="format-selector"] .format-selector__button'
).trigger('click');
// Wait for dropdown animation (0.2s transition + small buffer)
cy.wait(300);
// Verify dropdown is open
cy.get('[data-dropdown-menu].is-open')
.should('exist')
.should('be.visible');
});
it('should display dropdown menu with all options', () => {
// Check that dropdown has options
cy.get('[data-dropdown-menu].is-open [data-option]').should(
'have.length.at.least',
3
); // copy-page, open-chatgpt, open-claude
});
it('should display "Copy page for AI" option', () => {
cy.get('[data-dropdown-menu].is-open [data-option="copy-page"]')
.should('be.visible')
.should('contain', 'Copy page for AI')
.should('contain', 'Clean Markdown optimized for AI assistants');
});
it('should display "Open in ChatGPT" option with external link indicator', () => {
cy.get('[data-dropdown-menu].is-open [data-option="open-chatgpt"]')
.should('be.visible')
.should('contain', 'Open in ChatGPT')
.should('contain', 'Ask questions about this page')
.should('contain', '↗')
.should('have.attr', 'href')
.and('include', 'chatgpt.com');
});
it('should display "Open in Claude" option with external link indicator', () => {
cy.get('[data-dropdown-menu].is-open [data-option="open-claude"]')
.should('be.visible')
.should('contain', 'Open in Claude')
.should('contain', 'Ask questions about this page')
.should('contain', '↗')
.should('have.attr', 'href')
.and('include', 'claude.ai');
});
it('should display icons for each option', () => {
cy.get('[data-dropdown-menu].is-open [data-option]').each(($option) => {
cy.wrap($option).find('.format-selector__icon').should('exist');
});
});
it('should open AI integration links in new tab', () => {
cy.get(
'[data-dropdown-menu].is-open [data-option="open-chatgpt"]'
).should('have.attr', 'target', '_blank');
cy.get(
'[data-dropdown-menu].is-open [data-option="open-claude"]'
).should('have.attr', 'target', '_blank');
});
});
});
describe('Format Selector - Branch Nodes (Small Sections)', () => {
beforeEach(() => {
cy.visit(SMALL_SECTION_URL);
// Wait for component initialization
cy.window().should((win) => {
expect(win.influxdatadocs).to.exist;
expect(win.influxdatadocs.instances).to.exist;
expect(win.influxdatadocs.instances['format-selector']).to.exist;
});
});
it('should show "Copy section for AI" label for branch nodes', () => {
cy.get(
'[data-component="format-selector"] .format-selector__button'
).should('contain', 'Copy section for AI');
});
describe('Dropdown functionality', () => {
beforeEach(() => {
// Open dropdown once for all tests in this block
cy.get(
'[data-component="format-selector"] .format-selector__button'
).trigger('click');
// Wait for dropdown animation
cy.wait(300);
// Verify dropdown is open
cy.get('[data-dropdown-menu].is-open')
.should('exist')
.should('be.visible');
});
it('should display "Copy section for AI" option with page count', () => {
cy.get('[data-dropdown-menu].is-open [data-option="copy-section"]')
.should('be.visible')
.should('contain', 'Copy section for AI')
.should(
'contain',
'pages combined as clean Markdown for AI assistants'
);
});
it('should NOT show "Download section" option for small sections', () => {
cy.get(
'[data-dropdown-menu].is-open [data-option="download-section"]'
).should('not.exist');
});
it('should display ChatGPT and Claude options', () => {
cy.get(
'[data-dropdown-menu].is-open [data-option="open-chatgpt"]'
).should('be.visible');
cy.get(
'[data-dropdown-menu].is-open [data-option="open-claude"]'
).should('be.visible');
});
});
});
describe('Format Selector - Branch Nodes (Large Sections)', () => {
beforeEach(() => {
// Skip if large section doesn't exist
cy.visit(LARGE_SECTION_URL, { failOnStatusCode: false });
// Wait for component initialization if it exists
cy.window().then((win) => {
if (win.influxdatadocs && win.influxdatadocs.instances) {
expect(win.influxdatadocs.instances['format-selector']).to.exist;
}
});
});
it('should show "Download section" option for large sections (>10 pages)', () => {
// First check if this is actually a large section
cy.get('[data-component="format-selector"]').then(($selector) => {
const childCount = $selector.data('child-count');
if (childCount && childCount > 10) {
cy.get('[data-component="format-selector"] button').trigger('click');
cy.wait(300);
cy.get(
'[data-dropdown-menu].is-open [data-option="download-section"]'
)
.should('be.visible')
.should('contain', 'Download section')
.should('contain', '.zip');
} else {
cy.log('Skipping: This section has ≤10 pages');
}
});
});
});
describe('Markdown Content Quality', () => {
it('should contain actual page content from HTML version', () => {
// First, get the HTML version and extract some text
cy.visit(LEAF_PAGE_URL);
// Get the page title from h1
cy.get('h1')
.first()
.invoke('text')
.then((pageTitle) => {
// Get some body content from the article
cy.get('article')
.first()
.invoke('text')
.then((articleText) => {
// Extract a meaningful snippet (first 50 chars of article text, trimmed)
const contentSnippet = articleText.trim().substring(0, 50).trim();
// Now fetch the markdown version
cy.request(LEAF_PAGE_URL + 'index.md').then((response) => {
expect(response.status).to.eq(200);
const markdown = response.body;
// Basic structure checks
expect(markdown).to.include('---'); // Frontmatter delimiter
expect(markdown).to.match(/^#+ /m); // Has headings
// Content from HTML should appear in markdown
expect(markdown).to.include(pageTitle.trim());
expect(markdown).to.include(contentSnippet);
// Clean markdown (no raw HTML or Hugo syntax)
expect(markdown).to.not.include('<!DOCTYPE html>');
expect(markdown).to.not.include('<div');
expect(markdown).to.not.match(/\{\{[<%]/); // No shortcodes
expect(markdown).to.not.include('<!--'); // No HTML comments
expect(markdown).to.not.match(/\{\{-?\s*end\s*-?\}\}/); // No {{end}}
// Not empty
expect(markdown.length).to.be.greaterThan(100);
});
});
});
});
});
});