Initial commit: Clean DSS implementation

Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm

Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)

Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability

Migration completed: $(date)
🤖 Clean migration with full functionality preserved
This commit is contained in:
Digital Production Factory
2025-12-09 18:45:48 -03:00
commit 276ed71f31
884 changed files with 373737 additions and 0 deletions

View File

@@ -0,0 +1,352 @@
/**
* ds-test-results.js
* Test results viewer with polling for Jest/test runner output
*/
import toolBridge from '../../services/tool-bridge.js';
import { ComponentHelpers } from '../../utils/component-helpers.js';
class DSTestResults extends HTMLElement {
constructor() {
super();
this.testResults = null;
this.isRunning = false;
this.pollInterval = null;
this.autoRefresh = false;
}
async connectedCallback() {
this.render();
this.setupEventListeners();
await this.loadTestResults();
}
disconnectedCallback() {
if (this.pollInterval) {
clearInterval(this.pollInterval);
}
}
setupEventListeners() {
const runBtn = this.querySelector('#run-tests-btn');
if (runBtn) {
runBtn.addEventListener('click', () => this.runTests());
}
const refreshBtn = this.querySelector('#refresh-tests-btn');
if (refreshBtn) {
refreshBtn.addEventListener('click', () => this.loadTestResults());
}
const autoRefreshToggle = this.querySelector('#auto-refresh-tests');
if (autoRefreshToggle) {
autoRefreshToggle.addEventListener('change', (e) => {
this.autoRefresh = e.target.checked;
if (this.autoRefresh) {
this.pollInterval = setInterval(() => this.loadTestResults(), 3000);
} else {
if (this.pollInterval) {
clearInterval(this.pollInterval);
this.pollInterval = null;
}
}
});
}
}
/**
* Load test results from localStorage or file system
* In a real implementation, this would call an MCP tool to read test output files
*/
async loadTestResults() {
const content = this.querySelector('#test-results-content');
if (!content) return;
try {
// Try to load from localStorage (mock data for now)
const stored = localStorage.getItem('ds-test-results');
if (stored) {
this.testResults = JSON.parse(stored);
this.renderResults();
} else {
// No results yet
content.innerHTML = ComponentHelpers.renderEmpty(
'No test results available. Run tests to see results.',
'🧪'
);
}
} catch (error) {
console.error('Failed to load test results:', error);
content.innerHTML = ComponentHelpers.renderError('Failed to load test results', error);
}
}
/**
* Run tests (would call npm test or similar via MCP)
*/
async runTests() {
if (this.isRunning) return;
this.isRunning = true;
const runBtn = this.querySelector('#run-tests-btn');
if (runBtn) {
runBtn.disabled = true;
runBtn.textContent = '🧪 Running Tests...';
}
const content = this.querySelector('#test-results-content');
if (content) {
content.innerHTML = ComponentHelpers.renderLoading('Running tests...');
}
try {
// MVP1: Execute real npm test command via MCP
// Note: This requires project configuration with test scripts
const context = toolBridge.getContext();
// Call backend API to run tests
// The backend will execute `npm test` and return parsed results
const response = await fetch('/api/test/run', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
projectId: context.projectId,
testCommand: 'npm test'
})
});
if (!response.ok) {
throw new Error(`Test execution failed: ${response.statusText}`);
}
const testResults = await response.json();
// Validate results structure
if (!testResults || !testResults.summary) {
throw new Error('Invalid test results format');
}
this.testResults = {
...testResults,
timestamp: new Date().toISOString()
};
// Save to localStorage for offline viewing
localStorage.setItem('ds-test-results', JSON.stringify(this.testResults));
this.renderResults();
ComponentHelpers.showToast?.(
`Tests completed: ${this.testResults.summary.passed}/${this.testResults.summary.total} passed`,
this.testResults.summary.failed > 0 ? 'error' : 'success'
);
} catch (error) {
console.error('Failed to run tests:', error);
ComponentHelpers.showToast?.(`Test execution failed: ${error.message}`, 'error');
if (content) {
content.innerHTML = ComponentHelpers.renderError('Test execution failed', error);
}
} finally {
this.isRunning = false;
if (runBtn) {
runBtn.disabled = false;
runBtn.textContent = '🧪 Run Tests';
}
}
}
getStatusIcon(status) {
const icons = {
passed: '✅',
failed: '❌',
skipped: '⏭️'
};
return icons[status] || '⚪';
}
getStatusBadge(status) {
const types = {
passed: 'success',
failed: 'error',
skipped: 'warning'
};
return ComponentHelpers.createBadge(status, types[status] || 'info');
}
renderResults() {
const content = this.querySelector('#test-results-content');
if (!content || !this.testResults) return;
const { summary, suites, coverage, timestamp } = this.testResults;
// Calculate pass rate
const passRate = ((summary.passed / summary.total) * 100).toFixed(1);
content.innerHTML = `
<!-- Summary Stats -->
<div style="background-color: var(--vscode-sidebar); border: 1px solid var(--vscode-border); border-radius: 4px; padding: 16px; margin-bottom: 16px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
<h4 style="font-size: 12px; font-weight: 600;">Test Summary</h4>
${summary.failed === 0 ? ComponentHelpers.createBadge('All Tests Passed', 'success') : ComponentHelpers.createBadge(`${summary.failed} Failed`, 'error')}
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 16px; font-size: 11px; margin-bottom: 12px;">
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: 600; color: var(--vscode-text);">${summary.total}</div>
<div style="color: var(--vscode-text-dim); margin-top: 4px;">Total Tests</div>
</div>
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: 600; color: #89d185;">${summary.passed}</div>
<div style="color: var(--vscode-text-dim); margin-top: 4px;">Passed</div>
</div>
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: 600; color: #f48771;">${summary.failed}</div>
<div style="color: var(--vscode-text-dim); margin-top: 4px;">Failed</div>
</div>
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: 600; color: #ffbf00;">${summary.skipped}</div>
<div style="color: var(--vscode-text-dim); margin-top: 4px;">Skipped</div>
</div>
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: 600; color: var(--vscode-text);">${passRate}%</div>
<div style="color: var(--vscode-text-dim); margin-top: 4px;">Pass Rate</div>
</div>
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: 600; color: var(--vscode-text);">${summary.duration}s</div>
<div style="color: var(--vscode-text-dim); margin-top: 4px;">Duration</div>
</div>
</div>
<div style="font-size: 10px; color: var(--vscode-text-dim);">
Last run: ${ComponentHelpers.formatRelativeTime(new Date(timestamp))}
</div>
</div>
${coverage ? `
<!-- Coverage Stats -->
<div style="background-color: var(--vscode-sidebar); border: 1px solid var(--vscode-border); border-radius: 4px; padding: 16px; margin-bottom: 16px;">
<h4 style="font-size: 12px; font-weight: 600; margin-bottom: 12px;">Code Coverage</h4>
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;">
${this.renderCoverageBar('Lines', coverage.lines)}
${this.renderCoverageBar('Functions', coverage.functions)}
${this.renderCoverageBar('Branches', coverage.branches)}
${this.renderCoverageBar('Statements', coverage.statements)}
</div>
</div>
` : ''}
<!-- Test Suites -->
<div style="background-color: var(--vscode-sidebar); border: 1px solid var(--vscode-border); border-radius: 4px; padding: 16px;">
<h4 style="font-size: 12px; font-weight: 600; margin-bottom: 12px;">Test Suites</h4>
${suites.map(suite => this.renderSuite(suite)).join('')}
</div>
`;
}
renderCoverageBar(label, percentage) {
let color = '#f48771'; // Red
if (percentage >= 80) color = '#89d185'; // Green
else if (percentage >= 60) color = '#ffbf00'; // Yellow
return `
<div>
<div style="display: flex; justify-content: space-between; margin-bottom: 4px; font-size: 11px;">
<span>${label}</span>
<span style="font-weight: 600;">${percentage}%</span>
</div>
<div style="height: 8px; background-color: var(--vscode-bg); border-radius: 4px; overflow: hidden;">
<div style="height: 100%; width: ${percentage}%; background-color: ${color}; transition: width 0.3s;"></div>
</div>
</div>
`;
}
renderSuite(suite) {
const suiteId = `suite-${suite.name.replace(/\s+/g, '-').toLowerCase()}`;
const passedCount = suite.tests.filter(t => t.status === 'passed').length;
const failedCount = suite.tests.filter(t => t.status === 'failed').length;
return `
<div style="margin-bottom: 16px; border: 1px solid var(--vscode-border); border-radius: 4px; overflow: hidden;">
<div
style="padding: 12px; background-color: var(--vscode-bg); cursor: pointer; display: flex; justify-content: space-between; align-items: center;"
onclick="document.getElementById('${suiteId}').style.display = document.getElementById('${suiteId}').style.display === 'none' ? 'block' : 'none'"
>
<div>
<div style="font-size: 12px; font-weight: 600; margin-bottom: 4px;">${ComponentHelpers.escapeHtml(suite.name)}</div>
<div style="font-size: 10px; color: var(--vscode-text-dim);">
${passedCount} passed, ${failedCount} failed of ${suite.tests.length} tests
</div>
</div>
<div style="font-size: 18px;">▼</div>
</div>
<div id="${suiteId}" style="display: none;">
${suite.tests.map(test => this.renderTest(test)).join('')}
</div>
</div>
`;
}
renderTest(test) {
const icon = this.getStatusIcon(test.status);
const badge = this.getStatusBadge(test.status);
return `
<div style="padding: 12px; border-top: 1px solid var(--vscode-border); display: flex; justify-content: space-between; align-items: start;">
<div style="flex: 1;">
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px;">
<span style="font-size: 14px;">${icon}</span>
<span style="font-size: 11px; font-family: 'Courier New', monospace;">${ComponentHelpers.escapeHtml(test.name)}</span>
${badge}
</div>
${test.error ? `
<div style="margin-top: 8px; padding: 8px; background-color: rgba(244, 135, 113, 0.1); border-left: 3px solid #f48771; border-radius: 2px;">
<div style="font-size: 10px; font-family: 'Courier New', monospace; color: #f48771;">
${ComponentHelpers.escapeHtml(test.error)}
</div>
</div>
` : ''}
</div>
<div style="font-size: 10px; color: var(--vscode-text-dim); white-space: nowrap; margin-left: 12px;">
${test.duration}s
</div>
</div>
`;
}
render() {
this.innerHTML = `
<div style="padding: 16px; height: 100%; display: flex; flex-direction: column;">
<div style="margin-bottom: 16px; display: flex; gap: 12px; align-items: center; justify-content: flex-end;">
<label style="display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--vscode-text);">
<input type="checkbox" id="auto-refresh-tests" />
Auto-refresh
</label>
<button id="refresh-tests-btn" class="button" style="padding: 4px 12px; font-size: 11px;">
🔄 Refresh
</button>
<button id="run-tests-btn" class="button" style="padding: 4px 12px; font-size: 11px;">
🧪 Run Tests
</button>
</div>
<div id="test-results-content" style="flex: 1; overflow-y: auto;">
${ComponentHelpers.renderLoading('Loading test results...')}
</div>
</div>
`;
}
}
customElements.define('ds-test-results', DSTestResults);
export default DSTestResults;