mejoras en el modal de los testcases
This commit is contained in:
+189
-195
@@ -69,10 +69,12 @@
|
||||
}
|
||||
|
||||
.editable-list li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 6px;
|
||||
display: block;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#listSteps li {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.editable-list input {
|
||||
@@ -93,6 +95,47 @@
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
/* Step cards for better visual grouping in the modal */
|
||||
.step-card {
|
||||
background: var(--input);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 8px 10px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.step-card input {
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.step-header {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
min-width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.step-label {
|
||||
font-size: 10px;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 2px;
|
||||
display: block;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.step-expected input {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dep-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
@@ -182,9 +225,9 @@
|
||||
</style>
|
||||
</head>
|
||||
<body class="text-sm">
|
||||
<div class="max-w-[1600px] mx-auto">
|
||||
<!-- Header -->
|
||||
<div class="bg-[var(--bg-secondary)] border-b border-[var(--border)] px-8 py-5 flex items-center justify-between">
|
||||
<!-- Full-width Navbar -->
|
||||
<div class="bg-[var(--bg-secondary)] border-b border-[var(--border)]">
|
||||
<div class="max-w-[1600px] mx-auto px-8 py-5 flex items-center justify-between">
|
||||
<div>
|
||||
<h1 onclick="resetToHome()"
|
||||
class="text-2xl font-bold flex items-center gap-3 cursor-pointer hover:text-[var(--accent)] transition-colors">
|
||||
@@ -195,10 +238,6 @@
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<div id="status" class="text-xs px-3 py-1 rounded bg-[var(--card)] text-[var(--accent-warm)] font-medium">
|
||||
<span id="count">0</span> test cases loaded
|
||||
</div>
|
||||
|
||||
<button onclick="saveAll()"
|
||||
class="h-10 px-4 nav-button rounded-lg font-semibold flex items-center gap-2 text-sm">
|
||||
<i class="fa-solid fa-save"></i>
|
||||
@@ -219,8 +258,10 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Constrained content -->
|
||||
<div class="max-w-[1600px] mx-auto">
|
||||
|
||||
<!-- Groups Overview (high-level bird's eye view) -->
|
||||
<div class="px-8 pt-2">
|
||||
@@ -231,6 +272,15 @@
|
||||
<div id="groupsOverview" class="min-h-[120px]"></div>
|
||||
</div>
|
||||
|
||||
<!-- Tests overview -->
|
||||
<div class="px-8 pt-3 pb-1 flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm font-semibold text-[#a0a0b0]">Tests overview</span>
|
||||
<span id="visibleCount" class="text-xs text-[var(--accent-warm)] font-medium">0 tests</span>
|
||||
</div>
|
||||
<span id="saveFeedback" class="text-xs text-amber-400"></span>
|
||||
</div>
|
||||
|
||||
<!-- Table -->
|
||||
<div class="px-8 pt-4 pb-12">
|
||||
<!-- Selection Toolbar -->
|
||||
@@ -270,15 +320,10 @@
|
||||
<!-- Modal Header -->
|
||||
<div class="px-6 py-4 border-b border-[var(--border)] flex items-center justify-between bg-[var(--input)] rounded-t-2xl">
|
||||
<div>
|
||||
<div id="modalId" class="font-mono text-sm text-[#94a3b8]"></div>
|
||||
<div id="modalId" class="font-mono text-sm text-[var(--accent-warm)]"></div>
|
||||
<div id="modalGroup" class="text-xs text-[#64748b]"></div>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<select id="modalStatus" class="bg-[var(--input)] border border-[var(--border)] text-sm rounded px-3 py-1">
|
||||
<option value="automated">Automated</option>
|
||||
<option value="partial">Partial</option>
|
||||
<option value="manual">Manual</option>
|
||||
</select>
|
||||
<button onclick="resetToSuggested()"
|
||||
class="text-xs px-3 py-1 bg-[var(--input)] text-[var(--accent)] border border-[var(--accent)] hover:bg-[#2f2a3f] rounded flex items-center gap-1 whitespace-nowrap">
|
||||
<i class="fa-solid fa-magic"></i>
|
||||
@@ -289,14 +334,42 @@
|
||||
</div>
|
||||
|
||||
<div class="p-6 overflow-auto flex-1 space-y-6 text-sm">
|
||||
<!-- Description -->
|
||||
<!-- 1. Description -->
|
||||
<div>
|
||||
<div class="section-title mb-1.5">Description</div>
|
||||
<textarea id="modalDescription" rows="2"
|
||||
class="w-full bg-[var(--input)] border border-[var(--border)] rounded-lg p-3 text-sm"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Steps -->
|
||||
<!-- 2. Automatable -->
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="checkbox" id="automatableCheckbox" class="w-4 h-4" onchange="toggleAutomationTool()">
|
||||
<label for="automatableCheckbox" class="section-title text-emerald-400 cursor-pointer">Automatable</label>
|
||||
</div>
|
||||
|
||||
<!-- Automation tool selector (shown only when Automatable is checked) -->
|
||||
<div id="automationToolContainer" class="mt-2 hidden">
|
||||
<div class="text-xs text-[#64748b] mb-1">Automation tool</div>
|
||||
<select id="automationTool" class="bg-[var(--input)] border border-[var(--border)] rounded px-3 py-1 text-sm w-full">
|
||||
<option value="">Select tool...</option>
|
||||
<option value="agent-ai">Agent AI</option>
|
||||
<option value="playwright">Playwright</option>
|
||||
<option value="paramiko">Paramiko</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. Dependencies -->
|
||||
<div>
|
||||
<div class="flex justify-between items-center mb-1.5">
|
||||
<div class="section-title">Dependencies</div>
|
||||
<button onclick="addDependency()" class="text-xs px-2.5 py-0.5 bg-[var(--border)] hover:bg-[var(--border-light)] rounded">+ Add</button>
|
||||
</div>
|
||||
<div id="depContainer" class="space-y-1"></div>
|
||||
</div>
|
||||
|
||||
<!-- 4. Test Steps (action + expected result only) -->
|
||||
<div>
|
||||
<div class="flex justify-between items-center mb-1.5">
|
||||
<div class="section-title">Test Steps</div>
|
||||
@@ -304,64 +377,14 @@
|
||||
</div>
|
||||
<ul id="listSteps" class="editable-list text-sm"></ul>
|
||||
</div>
|
||||
|
||||
<!-- Automatable -->
|
||||
<div>
|
||||
<div class="flex justify-between items-center mb-1.5">
|
||||
<div class="section-title text-emerald-400">Automatable</div>
|
||||
<button onclick="addListItem('automatable')" class="text-xs px-2.5 py-0.5 bg-emerald-900/60 hover:bg-emerald-800 rounded">+ Add</button>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<!-- Code Generation (for automated tests) -->
|
||||
<div>
|
||||
<div class="section-title mb-1.5 text-[#a277ff]">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-[var(--input)] border border-[var(--border)] 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-[var(--border)] hover:bg-[var(--border-light)] rounded text-[var(--accent-warm)]">+ 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>
|
||||
<button onclick="addDependency()" class="text-xs px-2.5 py-0.5 bg-[var(--border)] hover:bg-[var(--border-light)] rounded">+ Add dependency</button>
|
||||
</div>
|
||||
<div id="depContainer" class="space-y-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-6 py-4 border-t border-[var(--border)] bg-[var(--input)] rounded-b-2xl flex justify-end gap-3">
|
||||
<button onclick="closeModal()" class="px-5 py-2 text-sm rounded-lg bg-[var(--border)] hover:bg-[var(--border-light)]">Cancel</button>
|
||||
<button onclick="saveCurrentTest()" class="px-6 py-2 text-sm rounded-lg bg-sky-600 hover:bg-sky-500 font-semibold">Save Changes to Test</button>
|
||||
<button onclick="saveCurrentTest()"
|
||||
class="px-5 py-2 text-sm rounded-lg bg-[var(--input)] text-[var(--accent)] border border-[var(--accent)] hover:bg-[#2f2a3f] font-semibold">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -381,7 +404,6 @@
|
||||
renderTable();
|
||||
renderGroupsOverview();
|
||||
updateSelectionCount();
|
||||
document.getElementById('count').textContent = allTests.length;
|
||||
} catch (e) {
|
||||
alert('Could not load test cases. Make sure the server is running and data file exists.');
|
||||
console.error(e);
|
||||
@@ -611,6 +633,7 @@
|
||||
|
||||
tbody.innerHTML = html;
|
||||
updateSelectionCount();
|
||||
updateVisibleCount();
|
||||
}
|
||||
|
||||
function resetFilters() {
|
||||
@@ -707,6 +730,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
function updateVisibleCount() {
|
||||
const el = document.getElementById('visibleCount');
|
||||
if (el) {
|
||||
const n = filteredTests.length;
|
||||
el.textContent = `${n} test${n === 1 ? '' : 's'}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Unified Plans modal (Create + Load)
|
||||
@@ -864,24 +895,26 @@
|
||||
|
||||
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 || '';
|
||||
|
||||
// Code Generation fields (Phase 1)
|
||||
const fullyAutomatedEl = document.getElementById('fullyAutomated');
|
||||
fullyAutomatedEl.checked = !!currentTest.fullyAutomated;
|
||||
// Automatable checkbox + tool selector
|
||||
const autoCb = document.getElementById('automatableCheckbox');
|
||||
if (typeof currentTest.automatable === 'boolean') {
|
||||
autoCb.checked = currentTest.automatable;
|
||||
} else {
|
||||
autoCb.checked = currentTest.status === 'automated';
|
||||
}
|
||||
|
||||
const notesEl = document.getElementById('implementationNotes');
|
||||
notesEl.value = currentTest.implementationNotes || '';
|
||||
const toolSelect = document.getElementById('automationTool');
|
||||
if (toolSelect) {
|
||||
toolSelect.value = currentTest.automationTool || '';
|
||||
}
|
||||
|
||||
// Code Steps
|
||||
renderCodeSteps(currentTest.codeSteps || []);
|
||||
toggleAutomationTool();
|
||||
|
||||
// Render lists
|
||||
renderRichSteps(currentTest.steps || []);
|
||||
renderList('listAutomatable', currentTest.automatable || []);
|
||||
renderList('listNotYet', currentTest.notYet || []);
|
||||
renderDependencies(currentTest.dependencies || []);
|
||||
// Render the 4 definition fields
|
||||
renderRichSteps(currentTest.steps || []);
|
||||
renderDependencies(currentTest.dependencies || []);
|
||||
|
||||
document.getElementById('editModal').classList.remove('hidden');
|
||||
document.getElementById('editModal').classList.add('flex');
|
||||
@@ -921,34 +954,45 @@
|
||||
renderList(id, items);
|
||||
}
|
||||
|
||||
// === Simplified step editor (automation decision moved to testcase level) ===
|
||||
// === Step editor: action + expected result only (notes removed per definition rules) ===
|
||||
function renderRichSteps(steps) {
|
||||
const ul = document.getElementById('listSteps');
|
||||
ul.innerHTML = '';
|
||||
|
||||
const rich = steps.map(s => {
|
||||
if (typeof s === 'string') {
|
||||
return { action: s, expected: '', notes: '' };
|
||||
return { action: s, expected: '' };
|
||||
}
|
||||
return {
|
||||
action: s.action || s,
|
||||
expected: s.expected || '',
|
||||
notes: s.notes || ''
|
||||
expected: s.expected || ''
|
||||
};
|
||||
});
|
||||
|
||||
rich.forEach((step, idx) => {
|
||||
const li = document.createElement('li');
|
||||
li.style.flexDirection = 'column';
|
||||
li.style.alignItems = 'stretch';
|
||||
li.style.gap = '4px';
|
||||
li.innerHTML = `
|
||||
<div style="display:flex; gap:8px; align-items:center;">
|
||||
<input value="${(step.action || '').replace(/"/g, '"')}" placeholder="Action" style="flex:1" oninput="updateRichStep(${idx}, 'action', this.value)">
|
||||
<button onclick="removeRichStep(${idx})" style="color:#f87171;"><i class="fa-solid fa-trash text-xs"></i></button>
|
||||
<div class="step-card">
|
||||
<div class="step-header">
|
||||
<span class="step-number">${idx + 1}</span>
|
||||
<input
|
||||
value="${(step.action || '').replace(/"/g, '"')}"
|
||||
placeholder="Action / Description"
|
||||
style="flex:1; font-size:13px;"
|
||||
oninput="updateRichStep(${idx}, 'action', this.value)">
|
||||
<button onclick="removeRichStep(${idx})" style="color:#f87171; padding: 2px 4px;">
|
||||
<i class="fa-solid fa-trash text-xs"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="step-expected">
|
||||
<span class="step-label">Expected result</span>
|
||||
<input
|
||||
value="${(step.expected || '').replace(/"/g, '"')}"
|
||||
placeholder="What should happen after this step"
|
||||
style="width:100%;"
|
||||
oninput="updateRichStep(${idx}, 'expected', this.value)">
|
||||
</div>
|
||||
</div>
|
||||
<input value="${(step.expected || '').replace(/"/g, '"')}" placeholder="Expected result (optional)" style="font-size:12px; margin-top:2px;" oninput="updateRichStep(${idx}, 'expected', this.value)">
|
||||
<input value="${(step.notes || '').replace(/"/g, '"')}" placeholder="Notes (optional)" style="font-size:12px; margin-top:2px; color:#94a3b8;" oninput="updateRichStep(${idx}, 'notes', this.value)">
|
||||
`;
|
||||
ul.appendChild(li);
|
||||
});
|
||||
@@ -973,10 +1017,18 @@
|
||||
function addRichStep() {
|
||||
const ul = document.getElementById('listSteps');
|
||||
const steps = JSON.parse(ul.dataset.richSteps || '[]');
|
||||
steps.push({ action: '', expected: '', notes: '' });
|
||||
steps.push({ action: '', expected: '' });
|
||||
renderRichSteps(steps);
|
||||
}
|
||||
|
||||
function toggleAutomationTool() {
|
||||
const cb = document.getElementById('automatableCheckbox');
|
||||
const container = document.getElementById('automationToolContainer');
|
||||
if (container) {
|
||||
container.classList.toggle('hidden', !cb.checked);
|
||||
}
|
||||
}
|
||||
|
||||
function renderDependencies(deps) {
|
||||
const container = document.getElementById('depContainer');
|
||||
container.innerHTML = deps.map((d, i) => `
|
||||
@@ -1010,75 +1062,16 @@
|
||||
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-[var(--input)] 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 resetToSuggested() {
|
||||
if (!currentTest) return;
|
||||
|
||||
// Build a minimal test object from current modal values
|
||||
// Build a minimal test object from current modal values (no status select anymore)
|
||||
const tempTest = {
|
||||
id: currentTest.id,
|
||||
group: currentTest.group,
|
||||
module: currentTest.module,
|
||||
action: currentTest.action,
|
||||
description: document.getElementById('modalDescription').value.trim(),
|
||||
status: document.getElementById('modalStatus').value,
|
||||
manualReason: currentTest.manualReason,
|
||||
partialNotes: currentTest.partialNotes
|
||||
description: document.getElementById('modalDescription').value.trim()
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -1089,15 +1082,19 @@
|
||||
});
|
||||
const rich = await res.json();
|
||||
|
||||
// Repopulate lists with suggested values
|
||||
// Repopulate only the fields the modal still edits
|
||||
currentTest.steps = rich.steps || [];
|
||||
currentTest.automatable = rich.automatable || [];
|
||||
currentTest.notYet = rich.notYet || [];
|
||||
currentTest.dependencies = rich.dependencies || [];
|
||||
|
||||
renderList('listSteps', currentTest.steps);
|
||||
renderList('listAutomatable', currentTest.automatable);
|
||||
renderList('listNotYet', currentTest.notYet);
|
||||
// Set the simple automatable checkbox from the suggestion if possible
|
||||
const autoCb = document.getElementById('automatableCheckbox');
|
||||
if (rich.automatable && rich.automatable.length > 0) {
|
||||
autoCb.checked = true;
|
||||
}
|
||||
|
||||
toggleAutomationTool();
|
||||
|
||||
renderRichSteps(currentTest.steps);
|
||||
renderDependencies(currentTest.dependencies);
|
||||
|
||||
} catch (e) {
|
||||
@@ -1109,33 +1106,27 @@
|
||||
function saveCurrentTest() {
|
||||
if (currentIndex < 0) return;
|
||||
|
||||
// Collect values
|
||||
// Collect the 4 definition fields (aligned with YAML)
|
||||
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;
|
||||
// Automatable as boolean + keep status in sync for the rest of the system
|
||||
const autoChecked = document.getElementById('automatableCheckbox').checked;
|
||||
currentTest.automatable = autoChecked;
|
||||
currentTest.status = autoChecked ? 'automated' : 'manual';
|
||||
|
||||
const notesEl = document.getElementById('implementationNotes');
|
||||
currentTest.implementationNotes = notesEl.value.trim() || undefined;
|
||||
// Save selected automation tool (only meaningful when automatable)
|
||||
const toolSelect = document.getElementById('automationTool');
|
||||
if (autoChecked && toolSelect) {
|
||||
currentTest.automationTool = toolSelect.value || undefined;
|
||||
} else {
|
||||
currentTest.automationTool = undefined;
|
||||
}
|
||||
|
||||
// Code Steps
|
||||
const codeStepsContainer = document.getElementById('codeStepsContainer');
|
||||
currentTest.codeSteps = JSON.parse(codeStepsContainer.dataset.codeSteps || '[]')
|
||||
.filter(s => s.stepName || s.actionKey);
|
||||
|
||||
// Collect steps (now simplified: action + expected + optional notes)
|
||||
// Steps: only action + expected (notes are never written from the modal anymore)
|
||||
const stepsUl = document.getElementById('listSteps');
|
||||
const richFromUI = JSON.parse(stepsUl.dataset.richSteps || '[]');
|
||||
currentTest.steps = richFromUI.length > 0 ? richFromUI : (JSON.parse(stepsUl.dataset.items || '[]').filter(Boolean));
|
||||
|
||||
const autoUl = document.getElementById('listAutomatable');
|
||||
currentTest.automatable = JSON.parse(autoUl.dataset.items || '[]').filter(Boolean);
|
||||
|
||||
const notUl = document.getElementById('listNotYet');
|
||||
currentTest.notYet = JSON.parse(notUl.dataset.items || '[]').filter(Boolean);
|
||||
|
||||
const depContainer = document.getElementById('depContainer');
|
||||
currentTest.dependencies = JSON.parse(depContainer.dataset.deps || '[]')
|
||||
.filter(d => d.label || d.value);
|
||||
@@ -1147,8 +1138,8 @@
|
||||
closeModal();
|
||||
renderTable();
|
||||
|
||||
document.getElementById('status').classList.add('dirty');
|
||||
document.getElementById('status').innerHTML = '<span class="text-amber-400">Unsaved changes</span>';
|
||||
const fb = document.getElementById('saveFeedback');
|
||||
if (fb) fb.textContent = 'Unsaved changes';
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
@@ -1178,10 +1169,10 @@
|
||||
|
||||
if (json.success) {
|
||||
isDirty = false;
|
||||
document.getElementById('status').classList.remove('dirty');
|
||||
document.getElementById('status').innerHTML = `<span id="count">${allTests.length}</span> test cases saved`;
|
||||
const fb = document.getElementById('saveFeedback');
|
||||
if (fb) fb.textContent = 'Saved!';
|
||||
|
||||
// Show temporary success feedback
|
||||
// Show temporary success feedback on the button
|
||||
if (saveBtn) {
|
||||
saveBtn.innerHTML = `<i class="fa-solid fa-check"></i> <span>Saved!</span>`;
|
||||
saveBtn.style.borderColor = '#6cc76f';
|
||||
@@ -1195,6 +1186,8 @@
|
||||
saveBtn.style.color = '';
|
||||
saveBtn.disabled = false;
|
||||
}
|
||||
const fb = document.getElementById('saveFeedback');
|
||||
if (fb) fb.textContent = '';
|
||||
}, 1800);
|
||||
} else {
|
||||
throw new Error(json.error || 'Unknown error');
|
||||
@@ -1222,8 +1215,8 @@
|
||||
if (isDirty && !confirm('You have unsaved changes. Reload anyway?')) return;
|
||||
await loadData();
|
||||
isDirty = false;
|
||||
document.getElementById('status').classList.remove('dirty');
|
||||
document.getElementById('status').innerHTML = `<span id="count">${allTests.length}</span> test cases loaded`;
|
||||
const fb = document.getElementById('saveFeedback');
|
||||
if (fb) fb.textContent = '';
|
||||
}
|
||||
|
||||
// Keyboard shortcuts
|
||||
@@ -1243,5 +1236,6 @@
|
||||
loadData();
|
||||
};
|
||||
</script>
|
||||
</div> <!-- close max-w content container -->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user