vibe coded maxx
This commit is contained in:
+170
-16
@@ -66,6 +66,13 @@
|
||||
<span>Generate Test Plan</span>
|
||||
</button>
|
||||
|
||||
<!-- Export Automated Playwright Tests -->
|
||||
<button onclick="exportAutomatedTests()"
|
||||
class="px-4 py-2 bg-[#0b1120] text-orange-400 border border-orange-400 hover:bg-orange-900/30 rounded-lg text-sm font-semibold flex items-center gap-2">
|
||||
<i class="fa-solid fa-file-code"></i>
|
||||
<span>Export Automated Tests</span>
|
||||
</button>
|
||||
|
||||
<!-- Load existing Test Plan -->
|
||||
<button onclick="openLoadTestPlanModal()"
|
||||
class="px-4 py-2 bg-[#0b1120] text-orange-400 border border-orange-400 hover:bg-orange-900/30 rounded-lg text-sm font-semibold flex items-center gap-2">
|
||||
@@ -128,6 +135,7 @@
|
||||
<button onclick="selectAllCore()" class="px-2 py-1 bg-[#1e2937] hover:bg-orange-600 rounded">All Core</button>
|
||||
<button onclick="selectAllPbx()" class="px-2 py-1 bg-[#1e2937] hover:bg-orange-600 rounded">All PBX</button>
|
||||
<button onclick="clearSelection()" class="px-2 py-1 bg-[#1e2937] hover:bg-red-600 rounded text-red-300">Clear</button>
|
||||
<button onclick="selectOnlyFullyAutomated()" class="px-2 py-1 bg-[#1e2937] hover:bg-orange-600 rounded text-orange-300">Only Fully Automated</button>
|
||||
<span class="ml-3 text-amber-300 font-medium" id="selectionCount"></span>
|
||||
</div>
|
||||
|
||||
@@ -201,16 +209,42 @@
|
||||
<ul id="listAutomatable" class="editable-list text-sm"></ul>
|
||||
</div>
|
||||
|
||||
<!-- Not Yet -->
|
||||
<div>
|
||||
<div class="flex justify-between items-center mb-1.5">
|
||||
<div class="section-title text-red-400">Not Automatable Yet / Manual</div>
|
||||
<button onclick="addListItem('notYet')" class="text-xs px-2.5 py-0.5 bg-red-900/60 hover:bg-red-800 rounded">+ Add</button>
|
||||
</div>
|
||||
<ul id="listNotYet" class="editable-list text-sm"></ul>
|
||||
</div>
|
||||
<!-- Not Yet -->
|
||||
<div>
|
||||
<div class="flex justify-between items-center mb-1.5">
|
||||
<div class="section-title text-red-400">Not Automatable Yet / Manual</div>
|
||||
<button onclick="addListItem('notYet')" class="text-xs px-2.5 py-0.5 bg-red-900/60 hover:bg-red-800 rounded">+ Add</button>
|
||||
</div>
|
||||
<ul id="listNotYet" class="editable-list text-sm"></ul>
|
||||
</div>
|
||||
|
||||
<!-- Dependencies -->
|
||||
<!-- Code Generation (for automated tests) -->
|
||||
<div>
|
||||
<div class="section-title mb-1.5 text-orange-400">Code Generation (Automated)</div>
|
||||
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<input type="checkbox" id="fullyAutomated" class="w-4 h-4">
|
||||
<label for="fullyAutomated" class="text-sm">Mark as <strong>fullyAutomated</strong> (ready for code generation)</label>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<div class="text-xs text-[#64748b] mb-1">Implementation Notes (for generator / LLM)</div>
|
||||
<textarea id="implementationNotes" rows="2"
|
||||
class="w-full bg-[#0f172a] border border-[#334155] rounded p-2 text-sm"
|
||||
placeholder="Hints for the code generator..."></textarea>
|
||||
</div>
|
||||
|
||||
<!-- codeSteps editor -->
|
||||
<div>
|
||||
<div class="flex justify-between items-center mb-1">
|
||||
<div class="text-xs text-[#64748b]">Code Steps (for generator)</div>
|
||||
<button onclick="addCodeStep()" class="text-xs px-2 py-0.5 bg-orange-900/60 hover:bg-orange-800 rounded">+ Add Step</button>
|
||||
</div>
|
||||
<div id="codeStepsContainer" class="space-y-2 text-sm"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dependencies -->
|
||||
<div>
|
||||
<div class="flex justify-between items-center mb-1.5">
|
||||
<div class="section-title">Dependencies & Prerequisites</div>
|
||||
@@ -417,6 +451,15 @@
|
||||
updateSelectionCount();
|
||||
}
|
||||
|
||||
function selectOnlyFullyAutomated() {
|
||||
selectedIds.clear();
|
||||
allTests.forEach(t => {
|
||||
if (t.fullyAutomated === true) selectedIds.add(t.id);
|
||||
});
|
||||
renderTable();
|
||||
updateSelectionCount();
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
selectedIds.clear();
|
||||
renderTable();
|
||||
@@ -485,20 +528,63 @@
|
||||
if (m) m.remove();
|
||||
}
|
||||
|
||||
async function exportAutomatedTests() {
|
||||
const automatedTests = allTests.filter(t => t.fullyAutomated === true || t.status === 'automated');
|
||||
|
||||
if (automatedTests.length === 0) {
|
||||
alert('No fully automated tests found.');
|
||||
return;
|
||||
}
|
||||
|
||||
// For now we just generate the whole group of the first selected test (simple approach)
|
||||
const firstGroup = automatedTests[0]?.group;
|
||||
|
||||
const confirmed = confirm(`Generate automated tests for group "${firstGroup}"?\n\nFiles will be written to tests/generated/.`);
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/export/automated-tests', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ group: firstGroup })
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
alert(`✅ Automated tests generated for group "${firstGroup}".\nCheck tests/generated/ folder.\n\nPlease review before committing.`);
|
||||
} else {
|
||||
alert('Generation failed: ' + (data.error || 'Unknown error'));
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Error exporting automated tests: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function openEditor(globalIndex) {
|
||||
currentIndex = globalIndex;
|
||||
currentTest = JSON.parse(JSON.stringify(allTests[globalIndex])); // deep clone
|
||||
|
||||
document.getElementById('modalId').textContent = currentTest.id;
|
||||
document.getElementById('modalGroup').textContent = `${currentTest.group} • ${currentTest.file}`;
|
||||
document.getElementById('modalDescription').value = currentTest.description || '';
|
||||
document.getElementById('modalStatus').value = currentTest.status;
|
||||
document.getElementById('modalDescription').value = currentTest.description || '';
|
||||
document.getElementById('modalStatus').value = currentTest.status;
|
||||
|
||||
// Render lists
|
||||
renderList('listSteps', currentTest.steps || []);
|
||||
renderList('listAutomatable', currentTest.automatable || []);
|
||||
renderList('listNotYet', currentTest.notYet || []);
|
||||
renderDependencies(currentTest.dependencies || []);
|
||||
// Code Generation fields (Phase 1)
|
||||
const fullyAutomatedEl = document.getElementById('fullyAutomated');
|
||||
fullyAutomatedEl.checked = !!currentTest.fullyAutomated;
|
||||
|
||||
const notesEl = document.getElementById('implementationNotes');
|
||||
notesEl.value = currentTest.implementationNotes || '';
|
||||
|
||||
// Code Steps
|
||||
renderCodeSteps(currentTest.codeSteps || []);
|
||||
|
||||
// Render lists
|
||||
renderList('listSteps', currentTest.steps || []);
|
||||
renderList('listAutomatable', currentTest.automatable || []);
|
||||
renderList('listNotYet', currentTest.notYet || []);
|
||||
renderDependencies(currentTest.dependencies || []);
|
||||
|
||||
document.getElementById('editModal').classList.remove('hidden');
|
||||
document.getElementById('editModal').classList.add('flex');
|
||||
@@ -571,6 +657,62 @@
|
||||
renderDependencies(deps);
|
||||
}
|
||||
|
||||
// === Code Steps Editor (for automated generation) ===
|
||||
|
||||
function renderCodeSteps(codeSteps = []) {
|
||||
const container = document.getElementById('codeStepsContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
codeSteps.forEach((step, index) => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'flex gap-2 items-center bg-[#0f172a] p-2 rounded';
|
||||
const paramsValue = step.params ? JSON.stringify(step.params) : '';
|
||||
div.innerHTML = `
|
||||
<input placeholder="Step Name" value="${step.stepName || ''}" class="flex-1 text-xs" oninput="updateCodeStep(${index}, 'stepName', this.value)">
|
||||
<input placeholder="Action Key" value="${step.actionKey || ''}" class="flex-1 text-xs" oninput="updateCodeStep(${index}, 'actionKey', this.value)">
|
||||
<input placeholder='Params (JSON)' value='${paramsValue}' class="flex-1 text-xs" oninput="updateCodeStepParams(${index}, this.value)">
|
||||
<button onclick="removeCodeStep(${index})" class="text-red-400 hover:text-red-500 px-1">×</button>
|
||||
`;
|
||||
container.appendChild(div);
|
||||
});
|
||||
|
||||
container.dataset.codeSteps = JSON.stringify(codeSteps);
|
||||
}
|
||||
|
||||
function updateCodeStep(index, field, value) {
|
||||
const container = document.getElementById('codeStepsContainer');
|
||||
const steps = JSON.parse(container.dataset.codeSteps || '[]');
|
||||
if (!steps[index]) steps[index] = {};
|
||||
steps[index][field] = value;
|
||||
container.dataset.codeSteps = JSON.stringify(steps);
|
||||
}
|
||||
|
||||
function updateCodeStepParams(index, value) {
|
||||
const container = document.getElementById('codeStepsContainer');
|
||||
const steps = JSON.parse(container.dataset.codeSteps || '[]');
|
||||
if (!steps[index]) steps[index] = {};
|
||||
try {
|
||||
steps[index].params = value ? JSON.parse(value) : undefined;
|
||||
} catch (e) {
|
||||
// keep previous value if JSON is invalid
|
||||
}
|
||||
container.dataset.codeSteps = JSON.stringify(steps);
|
||||
}
|
||||
|
||||
function removeCodeStep(index) {
|
||||
const container = document.getElementById('codeStepsContainer');
|
||||
const steps = JSON.parse(container.dataset.codeSteps || '[]');
|
||||
steps.splice(index, 1);
|
||||
renderCodeSteps(steps);
|
||||
}
|
||||
|
||||
function addCodeStep() {
|
||||
const container = document.getElementById('codeStepsContainer');
|
||||
const steps = JSON.parse(container.dataset.codeSteps || '[]');
|
||||
steps.push({ stepName: '', actionKey: '' });
|
||||
renderCodeSteps(steps);
|
||||
}
|
||||
|
||||
async function openGenerateTestPlanModal() {
|
||||
if (selectedIds.size === 0) {
|
||||
alert('Please select at least one test case using the checkboxes.');
|
||||
@@ -650,6 +792,18 @@
|
||||
currentTest.description = document.getElementById('modalDescription').value.trim();
|
||||
currentTest.status = document.getElementById('modalStatus').value;
|
||||
|
||||
// Code Generation fields (Phase 1)
|
||||
const fullyAutomatedEl = document.getElementById('fullyAutomated');
|
||||
currentTest.fullyAutomated = fullyAutomatedEl.checked;
|
||||
|
||||
const notesEl = document.getElementById('implementationNotes');
|
||||
currentTest.implementationNotes = notesEl.value.trim() || undefined;
|
||||
|
||||
// Code Steps
|
||||
const codeStepsContainer = document.getElementById('codeStepsContainer');
|
||||
currentTest.codeSteps = JSON.parse(codeStepsContainer.dataset.codeSteps || '[]')
|
||||
.filter(s => s.stepName || s.actionKey);
|
||||
|
||||
// Collect lists
|
||||
const stepsUl = document.getElementById('listSteps');
|
||||
currentTest.steps = JSON.parse(stepsUl.dataset.items || '[]').filter(Boolean);
|
||||
|
||||
Reference in New Issue
Block a user