generated from coulomb/repo-seed
Complete integration of refactored testdrive-jsui capability: ## Refactored Architecture - js/ - All JavaScript source (controls, components, core) - static/ - CSS, images, templates - src/testdrive_jsui/ - Python package - tests/ - Python tests ## Plugin Self-Declaration - get_plugin_source_dir() - plugin declares own location - get_asset_paths() - organized asset paths - No hardcoded discovery logic ## Merged Content - Baseline UI scaffold (tutorials, LICENSE, INTRODUCTION.md) - Refactored capability implementation - Comprehensive documentation Ready for standalone use or integration with markitect. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
267 lines
7.3 KiB
JavaScript
267 lines
7.3 KiB
JavaScript
/**
|
|
* Section Splitting Functionality Tests
|
|
*
|
|
* Tests dynamic section splitting when headings are detected
|
|
* Based on functionality from history/javascript-dev-tests/test_section_splitting.js
|
|
*/
|
|
|
|
describe('Section Splitting', () => {
|
|
let sectionManager;
|
|
|
|
beforeEach(() => {
|
|
// Setup DOM
|
|
document.body.innerHTML = `
|
|
<div id="content">
|
|
<div class="section" data-section-id="main-section">
|
|
<div class="section-content">
|
|
<p>Original content</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// Load components
|
|
require('../core/section-manager.js');
|
|
|
|
if (global.SectionManager) {
|
|
sectionManager = new global.SectionManager();
|
|
}
|
|
});
|
|
|
|
afterEach(() => {
|
|
document.body.innerHTML = '';
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
describe('Heading detection', () => {
|
|
test('should detect new headings in content', () => {
|
|
const textWithHeading = `
|
|
This is some content.
|
|
|
|
# New Heading
|
|
|
|
This should be a new section.
|
|
`;
|
|
|
|
// Test heading detection with regex
|
|
const lines = textWithHeading.trim().split('\n');
|
|
const headingLine = lines.find(line => /^#+ /.test(line.trim()));
|
|
expect(headingLine).toBeTruthy();
|
|
expect(headingLine.trim()).toBe('# New Heading');
|
|
});
|
|
|
|
test('should identify different heading levels', () => {
|
|
const headingTests = [
|
|
{ text: '# Heading 1', level: 1 },
|
|
{ text: '## Heading 2', level: 2 },
|
|
{ text: '### Heading 3', level: 3 },
|
|
{ text: '#### Heading 4', level: 4 }
|
|
];
|
|
|
|
headingTests.forEach(({ text, level }) => {
|
|
const match = text.match(/^(#+) /);
|
|
expect(match).toBeTruthy();
|
|
if (match) {
|
|
expect(match[1].length).toBe(level);
|
|
}
|
|
});
|
|
});
|
|
|
|
test('should distinguish headings from regular text', () => {
|
|
const testCases = [
|
|
{ text: '# This is a heading', isHeading: true },
|
|
{ text: 'This is not a heading', isHeading: false },
|
|
{ text: 'Neither is this # hash in middle', isHeading: false },
|
|
{ text: '## Another heading', isHeading: true }
|
|
];
|
|
|
|
testCases.forEach(({ text, isHeading }) => {
|
|
const match = /^#+\s/.test(text.trim());
|
|
expect(match).toBe(isHeading);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Section splitting logic', () => {
|
|
test('should split content when heading is detected', () => {
|
|
const originalContent = 'Original content without headings';
|
|
const newContent = `
|
|
${originalContent}
|
|
|
|
# New Section
|
|
|
|
New section content
|
|
`;
|
|
|
|
// Simulate section splitting logic
|
|
const parts = newContent.split(/\n(?=#)/);
|
|
|
|
if (parts.length > 1) {
|
|
expect(parts.length).toBeGreaterThan(1);
|
|
expect(parts[0]).toContain('Original content');
|
|
expect(parts[1]).toContain('# New Section');
|
|
}
|
|
});
|
|
|
|
test('should preserve content when no headings are present', () => {
|
|
const content = 'Just regular content without any headings';
|
|
const parts = content.split(/\n(?=#)/);
|
|
|
|
expect(parts.length).toBe(1);
|
|
expect(parts[0]).toBe(content);
|
|
});
|
|
|
|
test('should handle multiple headings correctly', () => {
|
|
const contentWithMultipleHeadings = `Initial content
|
|
|
|
# First Heading
|
|
First section content
|
|
|
|
## Second Heading
|
|
Second section content
|
|
|
|
# Third Heading
|
|
Third section content`;
|
|
|
|
// Split on lines that start with headings
|
|
const parts = contentWithMultipleHeadings.split(/\n(?=#)/);
|
|
|
|
// Should split into multiple sections
|
|
expect(parts.length).toBeGreaterThanOrEqual(2);
|
|
|
|
// Find heading lines
|
|
const headings = contentWithMultipleHeadings.match(/^#+.*$/gm);
|
|
expect(headings).toBeTruthy();
|
|
expect(headings.length).toBe(3);
|
|
});
|
|
});
|
|
|
|
describe('SectionManager integration', () => {
|
|
test('should have handleSectionSplit method', () => {
|
|
if (!sectionManager) {
|
|
console.warn('SectionManager not available, skipping test');
|
|
return;
|
|
}
|
|
|
|
expect(typeof sectionManager.handleSectionSplit).toBe('function');
|
|
});
|
|
|
|
test('should maintain section state during splits', () => {
|
|
if (!sectionManager) return;
|
|
|
|
const originalSectionCount = document.querySelectorAll('.section').length;
|
|
|
|
// Mock section splitting
|
|
const mockNewSection = document.createElement('div');
|
|
mockNewSection.className = 'section';
|
|
mockNewSection.setAttribute('data-section-id', 'split-section');
|
|
|
|
if (originalSectionCount > 0) {
|
|
expect(originalSectionCount).toBeGreaterThan(0);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Dynamic section creation', () => {
|
|
test('should create new section elements when splitting', () => {
|
|
const sectionContent = `
|
|
Original content
|
|
|
|
# New Section Title
|
|
|
|
New section content
|
|
`;
|
|
|
|
// Simulate section creation
|
|
const newSection = document.createElement('div');
|
|
newSection.className = 'section';
|
|
newSection.setAttribute('data-section-id', 'generated-section-id');
|
|
|
|
const contentDiv = document.createElement('div');
|
|
contentDiv.className = 'section-content';
|
|
contentDiv.textContent = 'New section content';
|
|
|
|
newSection.appendChild(contentDiv);
|
|
|
|
expect(newSection.className).toBe('section');
|
|
expect(newSection.getAttribute('data-section-id')).toBeTruthy();
|
|
expect(newSection.querySelector('.section-content')).toBeTruthy();
|
|
});
|
|
|
|
test('should generate unique section IDs', () => {
|
|
const headingText = 'My New Section';
|
|
|
|
// Simulate ID generation from heading
|
|
const sectionId = headingText
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9]+/g, '-')
|
|
.replace(/^-+|-+$/g, '');
|
|
|
|
expect(sectionId).toBe('my-new-section');
|
|
});
|
|
|
|
test('should preserve section hierarchy', () => {
|
|
const hierarchicalContent = `
|
|
# Main Section
|
|
Main content
|
|
|
|
## Subsection
|
|
Sub content
|
|
|
|
### Sub-subsection
|
|
Sub-sub content
|
|
`;
|
|
|
|
const headings = hierarchicalContent.match(/^#+.*$/gm);
|
|
|
|
if (headings) {
|
|
expect(headings.length).toBe(3);
|
|
expect(headings[0]).toMatch(/^# /);
|
|
expect(headings[1]).toMatch(/^## /);
|
|
expect(headings[2]).toMatch(/^### /);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Section splitting edge cases', () => {
|
|
test('should handle empty headings gracefully', () => {
|
|
const contentWithEmptyHeading = `
|
|
Content before
|
|
|
|
#
|
|
|
|
Content after
|
|
`;
|
|
|
|
const parts = contentWithEmptyHeading.split(/\n(?=#)/);
|
|
expect(parts.length).toBeGreaterThanOrEqual(1);
|
|
});
|
|
|
|
test('should handle headings at the start of content', () => {
|
|
const contentStartingWithHeading = `# First Heading
|
|
Content for first section
|
|
|
|
# Second Heading
|
|
Content for second section
|
|
`;
|
|
|
|
const parts = contentStartingWithHeading.split(/\n(?=#)/);
|
|
expect(parts[0]).toContain('# First Heading');
|
|
});
|
|
|
|
test('should handle malformed headings', () => {
|
|
const malformedHeadings = [
|
|
'#NoSpace',
|
|
'# ',
|
|
'########## Too many hashes',
|
|
'Not a heading # at all'
|
|
];
|
|
|
|
malformedHeadings.forEach(text => {
|
|
const isValidHeading = /^#{1,6}\s+\S/.test(text);
|
|
// Most should be invalid except properly formatted ones
|
|
expect(typeof isValidHeading).toBe('boolean');
|
|
});
|
|
});
|
|
});
|
|
}); |