Improves event layout and stacking logic
Refactors the event layout and stacking logic based on review feedback. This includes: - Merging conflicting event groups to prevent inconsistencies. - Implementing minimal stack level assignment using a min-heap. - Consolidating styling and using DateService for drag operations. - Adding reflow after drag and drop. - Improving the column event filtering to include events overlapping midnight. - Ensuring explicit sorting of events for grid layout.
This commit is contained in:
parent
b590467f60
commit
faa59f6a3c
19 changed files with 1502 additions and 55 deletions
152
scenarios/scenario-test-runner.js
Normal file
152
scenarios/scenario-test-runner.js
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/**
|
||||
* Scenario Test Runner
|
||||
* Validates that rendered events match expected layout
|
||||
*/
|
||||
|
||||
export class ScenarioTestRunner {
|
||||
/**
|
||||
* Validate a scenario's rendered output
|
||||
* @param {string} scenarioId - e.g., "scenario-1"
|
||||
* @param {Array} expectedResults - Array of {eventId, stackLevel, cols?, type: 'grid'|'stacked'}
|
||||
* @returns {object} - {passed: boolean, results: Array, message: string}
|
||||
*/
|
||||
static validateScenario(scenarioId, expectedResults) {
|
||||
const results = [];
|
||||
let allPassed = true;
|
||||
|
||||
for (const expected of expectedResults) {
|
||||
const result = this.validateEvent(expected.eventId, expected);
|
||||
results.push(result);
|
||||
if (!result.passed) {
|
||||
allPassed = false;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
passed: allPassed,
|
||||
results,
|
||||
message: allPassed ? 'All tests passed ✅' : 'Some tests failed ❌'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a single event
|
||||
*/
|
||||
static validateEvent(eventId, expected) {
|
||||
const eventEl = document.querySelector(`swp-event[data-event-id="${eventId}"]`);
|
||||
|
||||
if (!eventEl) {
|
||||
return {
|
||||
passed: false,
|
||||
eventId,
|
||||
message: `Event ${eventId} not found in DOM`
|
||||
};
|
||||
}
|
||||
|
||||
const errors = [];
|
||||
|
||||
// Check if in grid group
|
||||
const gridGroup = eventEl.closest('swp-event-group');
|
||||
|
||||
if (expected.type === 'grid') {
|
||||
if (!gridGroup) {
|
||||
errors.push(`Expected to be in grid group, but found as stacked event`);
|
||||
} else {
|
||||
// Validate grid group properties
|
||||
const groupStackLevel = this.getStackLevel(gridGroup);
|
||||
if (groupStackLevel !== expected.stackLevel) {
|
||||
errors.push(`Grid group stack level: expected ${expected.stackLevel}, got ${groupStackLevel}`);
|
||||
}
|
||||
|
||||
if (expected.cols) {
|
||||
const cols = this.getColumnCount(gridGroup);
|
||||
if (cols !== expected.cols) {
|
||||
errors.push(`Grid columns: expected ${expected.cols}, got ${cols}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (expected.type === 'stacked') {
|
||||
if (gridGroup) {
|
||||
errors.push(`Expected to be stacked, but found in grid group`);
|
||||
} else {
|
||||
// Validate stacked event properties
|
||||
const stackLevel = this.getStackLevel(eventEl);
|
||||
if (stackLevel !== expected.stackLevel) {
|
||||
errors.push(`Stack level: expected ${expected.stackLevel}, got ${stackLevel}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
passed: errors.length === 0,
|
||||
eventId,
|
||||
message: errors.length === 0 ? '✅' : errors.join('; ')
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stack level from element
|
||||
*/
|
||||
static getStackLevel(element) {
|
||||
const stackLink = element.getAttribute('data-stack-link');
|
||||
if (stackLink) {
|
||||
try {
|
||||
const parsed = JSON.parse(stackLink);
|
||||
return parsed.stackLevel;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Try class name fallback
|
||||
const classMatch = element.className.match(/stack-level-(\d+)/);
|
||||
return classMatch ? parseInt(classMatch[1]) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get column count from grid group
|
||||
*/
|
||||
static getColumnCount(gridGroup) {
|
||||
const classMatch = gridGroup.className.match(/cols-(\d+)/);
|
||||
return classMatch ? parseInt(classMatch[1]) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display test results in the DOM
|
||||
*/
|
||||
static displayResults(containerId, results) {
|
||||
const container = document.getElementById(containerId);
|
||||
if (!container) return;
|
||||
|
||||
const badge = document.createElement('span');
|
||||
badge.className = `test-badge ${results.passed ? 'test-passed' : 'test-failed'}`;
|
||||
badge.textContent = results.message;
|
||||
container.appendChild(badge);
|
||||
|
||||
// Add detailed results
|
||||
const details = document.createElement('div');
|
||||
details.className = 'test-details';
|
||||
details.innerHTML = '<h4>Test Results:</h4>';
|
||||
|
||||
results.results.forEach(r => {
|
||||
const line = document.createElement('div');
|
||||
line.className = `test-result-line ${r.passed ? 'passed' : 'failed'}`;
|
||||
line.textContent = `${r.eventId}: ${r.message}`;
|
||||
details.appendChild(line);
|
||||
});
|
||||
|
||||
container.appendChild(details);
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-run tests if window.scenarioTests is defined
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
if (window.scenarioTests) {
|
||||
const results = ScenarioTestRunner.validateScenario(
|
||||
window.scenarioTests.id,
|
||||
window.scenarioTests.expected
|
||||
);
|
||||
|
||||
ScenarioTestRunner.displayResults('test-results', results);
|
||||
}
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue