1 document.addEventListener('DOMContentLoaded', function() {2 document.getElementById('welcome-modal').classList.remove('hidden');3 makeSortable();4 renderTestTable();5 });6 7 function closeWelcomeModal() {8 document.getElementById('welcome-modal').classList.add('hidden');9 }10 11 const testTable = [12 { my_step: 'GRANTED', expected_status: 200 },13 { my_step: 'PENDING', expected_status: 200 },14 { my_step: 'FINISHED', expected_status: 200 },15 { my_step: 'ERROR', expected_status: 200 },16 { my_step: 'INVALID', expected_status: 400 }17 ];18 19 function renderTestTable() {20 const tbody = document.querySelector('#test-table tbody');21 tbody.innerHTML = '';22 testTable.forEach((testCase, index) => {23 const row = document.createElement('tr');24 row.innerHTML = `25 <td class="border px-4 py-2"><input type="text" value="${testCase.my_step}" onchange="updateTestCase(${index}, 'my_step', this.value)" class="w-full"></td>26 <td class="border px-4 py-2"><input type="number" value="${testCase.expected_status}" onchange="updateTestCase(${index}, 'expected_status', this.value)" class="w-full"></td>27 `;28 tbody.appendChild(row);29 });30 }31 32 function updateTestCase(index, key, value) {33 testTable[index][key] = value;34 }35 36 function addTestCase() {37 testTable.push({ my_step: '', expected_status: '' });38 renderTestTable();39 }40 41 function showInsertMenu(button) {42 const menu = document.createElement('div');43 menu.classList.add('absolute', 'bg-white', 'shadow-md', 'rounded', 'mt-2', 'z-10');44 menu.innerHTML = `45 <ul class="space-y-2 p-2">46 <li><button type="button" onclick="insertVariable(this)" class="bg-blue-500 text-white px-4 py-2 rounded">+ Variable</button></li>47 <li><button type="button" onclick="insertRequest(this)" class="bg-blue-500 text-white px-4 py-2 rounded">+ Request</button></li>48 <li><button type="button" onclick="insertAssert(this)" class="bg-blue-500 text-white px-4 py-2 rounded">+ Assert</button></li>49 <li><button type="button" onclick="insertSleep(this)" class="bg-blue-500 text-white px-4 py-2 rounded">+ Sleep</button></li>50 </ul>51 `;52 button.parentElement.appendChild(menu);53 54 document.addEventListener('click', function(event) {55 if (!menu.contains(event.target) && !button.contains(event.target)) {56 menu.remove();57 }58 }, { once: true });59 }60 61 function insertElement(button, elementFunction) {62 const container = button.closest('#elements-list');63 const buttonDiv = button.parentElement;64 const newElement = elementFunction();65 container.insertBefore(newElement, buttonDiv.nextSibling);66 button.parentElement.querySelector('.absolute').remove();67 addInsertButton(container, buttonDiv.nextSibling);68 }69 70 function insertVariable(button) {71 insertElement(button, createVariableElement);72 }73 74 function insertRequest(button) {75 insertElement(button, createRequestElement);76 }77 78 function insertAssert(button) {79 insertElement(button, createAssertElement);80 }81 82 function insertSleep(button) {83 insertElement(button, createSleepElement);84 }85 86 function createVariableElement() {87 const div = document.createElement('div');88 div.classList.add('element', 'bg-white', 'p-4', 'rounded-lg', 'shadow-md', 'flex', 'items-center', 'space-x-4');89 div.innerHTML = `90 <span class="text-gray-700">Variable</span>91 <input type="text" placeholder="Nombre" class="flex-1 p-2 border border-gray-300 rounded" />92 <input type="text" placeholder="Valor" class="flex-1 p-2 border border-gray-300 rounded" />93 <button type="button" onclick="removeElement(this)" class="text-red-500">x</button>94 <button type="button" onclick="duplicateElement(this)" class="text-blue-500">⧉</button>95 `;96 return div;97 }98 99 function createRequestElement() {100 const div = document.createElement('div');101 div.classList.add('element', 'bg-white', 'p-4', 'rounded-lg', 'shadow-md', 'flex', 'items-center', 'space-x-4');102 div.innerHTML = `103 <span class="text-gray-700">Request</span>104 <select class="flex-1 p-2 border border-gray-300 rounded">105 <option>GET</option>106 <option>POST</option>107 <option>PUT</option>108 <option>DELETE</option>109 </select>110 <input type="text" placeholder="URL" class="flex-1 p-2 border border-gray-300 rounded" />111 <textarea placeholder="Headers" class="flex-1 p-2 border border-gray-300 rounded"></textarea>112 <textarea placeholder="Body" class="flex-1 p-2 border border-gray-300 rounded"></textarea>113 <button type="button" onclick="removeElement(this)" class="text-red-500">x</button>114 <button type="button" onclick="duplicateElement(this)" class="text-blue-500">⧉</button>115 `;116 return div;117 }118 119 function createAssertElement() {120 const div = document.createElement('div');121 div.classList.add('element', 'bg-white', 'p-4', 'rounded-lg', 'shadow-md', 'space-y-4');122 div.innerHTML = `123 <span class="text-gray-700">Assert</span>124 <div class="flex items-center space-x-4">125 <input type="text" placeholder="Referencia" class="flex-1 p-2 border border-gray-300 rounded assert-reference" list="references-datalist"/>126 <datalist id="references-datalist">127 <option value="response.status">128 <option value="response.headers.Content-Type">129 <option value="response.body.user.name">130 </datalist>131 <select class="flex-1 p-2 border border-gray-300 rounded" onchange="updateAssertInput(this)">132 <option value="equals">Igual a</option>133 <option value="not_equals">Distinto a</option>134 <option value="in_list">En lista</option>135 <option value="not_in_list">No en lista</option>136 </select>137 <input type="text" placeholder="Valor" class="flex-1 p-2 border border-gray-300 rounded assert-value" />138 <button type="button" onclick="removeElement(this)" class="text-red-500">x</button>139 <button type="button" onclick="duplicateElement(this)" class="text-blue-500">⧉</button>140 </div>141 `;142 return div;143 }144 145 function createSleepElement() {146 const div = document.createElement('div');147 div.classList.add('element', 'bg-white', 'p-4', 'rounded-lg', 'shadow-md', 'flex', 'items-center', 'space-x-4');148 div.innerHTML = `149 <span class="text-gray-700">Sleep</span>150 <input type="number" placeholder="Segundos" class="flex-1 p-2 border border-gray-300 rounded" />151 <button type="button" onclick="removeElement(this)" class="text-red-500">x</button>152 <button type="button" onclick="duplicateElement(this)" class="text-blue-500">⧉</button>153 `;154 return div;155 }156 157 function addInsertButton(container, afterElement) {158 const buttonDiv = document.createElement('div');159 buttonDiv.classList.add('flex', 'justify-center', 'my-2');160 buttonDiv.innerHTML = `161 <button type="button" onclick="showInsertMenu(this)" class="bg-gray-200 text-gray-600 px-2 py-1 rounded">+</button>162 `;163 container.insertBefore(buttonDiv, afterElement);164 }165 166 function duplicateElement(button) {167 const container = button.closest('#elements-list');168 const element = button.parentElement.cloneNode(true);169 container.insertBefore(element, button.parentElement.nextSibling);170 addInsertButton(container, button.parentElement.nextSibling);171 }172 173 function removeElement(button) {174 const container = button.closest('#elements-list');175 button.parentElement.remove();176 if (!container.lastElementChild || !container.lastElementChild.querySelector('button')) {177 addInsertButton(container);178 }179 }180 181 function saveTest() {182 const elements = [];183 const listItems = document.getElementById('elements-list').children;184 for (let i = 0; i < listItems.length; i++) {185 const element = listItems[i];186 if (!element.querySelector('span')) continue; // Skip insert buttons187 const type = element.querySelector('span').textContent;188 if (type === 'Variable') {189 const name = element.children[1].value;190 const value = element.children[2].value;191 if (name && value) {192 elements.push({ type, name, value });193 } else {194 alert('Please fill out both name and value for all variables.');195 return;196 }197 } else if (type === 'Request') {198 const method = element.children[1].value;199 const url = element.children[2].value;200 const headers = element.children[3].value;201 const body = element.children[4].value;202 if (method && url) {203 elements.push({ type, method, url, headers, body });204 } else {205 alert('Please fill out method and URL for all requests.');206 return;207 }208 } else if (type === 'Assert') {209 const assertType = element.children[2].value;210 const reference = element.children[1].value;211 const value = element.children[3].value;212 if (reference && value) {213 elements.push({ type, assertType, reference, value });214 } else {215 alert('Please fill out reference and value for all asserts.');216 return;217 }218 } else if (type === 'Sleep') {219 const seconds = element.children[1].value;220 if (seconds) {221 elements.push({ type, seconds });222 } else {223 alert('Please fill out the number of seconds for all sleeps.');224 return;225 }226 }227 }228 console.log('Elements:', elements);229 alert('Test saved!');230 }231 232 function updateAssertInput(select) {233 const valueInput = select.parentElement.querySelector('.assert-value');234 if (select.value === 'in_list' || select.value === 'not_in_list') {235 valueInput.placeholder = 'Valores (separados por comas)';236 } else {237 valueInput.placeholder = 'Valor';238 }239 }240 241 function makeSortable() {242 const container = document.getElementById('elements-list');243 Sortable.create(container, {244 handle: '.element',245 animation: 150,246 onEnd: function (evt) {247 if (!container.lastElementChild || !container.lastElementChild.querySelector('button')) {248 addInsertButton(container);249 }250 },251 });252 }253 254 document.getElementById('start-execution').addEventListener('click', startExecution);255 256 const environments = {257 Local: {258 origin: 'http://localhost:8080',259 user_id: '12345',260 app_id: 'abcd1234'261 },262 Staging: {263 origin: 'https://staging.myservice.com',264 user_id: '67890',265 app_id: 'wxyz5678'266 }267 };268 269 async function startExecution() {270 const logContainer = document.getElementById('execution-log');271 logContainer.innerHTML = ''; // Clear previous logs272 273 const selectedEnvironment = document.getElementById('environment-select').value;274 const variables = environments[selectedEnvironment];275 276 const steps = [277 {278 type: 'request',279 method: 'PATCH',280 url: '{{ origin }}/thing/{{ my-thing }}',281 headers: {},282 body: {283 step: '{{ my_step }}'284 },285 asserts: [286 { path: 'response.status', operator: 'equals', value: '{{ expected_status }}' }287 ]288 },289 // Add more steps here...290 ];291 292 for (const testCase of testTable) {293 const testVariables = { ...variables, ...testCase };294 logContainer.innerHTML += `<div class="log-entry"><p><strong>Executing test case:</strong> ${JSON.stringify(testCase)}</p></div>`;295 for (const step of steps) {296 let logEntry = document.createElement('div');297 logEntry.classList.add('log-entry');298 logEntry.innerHTML = `<p><strong>${step.method}</strong> ${replaceVariables(step.url, testVariables)}</p>`;299 logContainer.appendChild(logEntry);300 301 try {302 const response = await executeStep(step, testVariables);303 logEntry.classList.add('success');304 logEntry.innerHTML += `<p>Response: ${JSON.stringify(response)}</p>`;305 306 if (step.asserts) {307 for (const assert of step.asserts) {308 const result = evaluateAssert(response, assert, testVariables);309 if (!result.passed) {310 throw new Error(`Assert failed: ${result.message}`);311 }312 }313 }314 } catch (error) {315 logEntry.classList.add('error');316 logEntry.innerHTML += `<p>Error: ${error.message}</p>`;317 break; // Stop execution on error318 }319 }320 }321 }322 323 async function executeStep(step, variables) {324 const url = replaceVariables(step.url, variables);325 const headers = Object.keys(step.headers).reduce((acc, key) => {326 acc[key] = replaceVariables(step.headers[key], variables);327 return acc;328 }, {});329 const body = step.body ? JSON.stringify(replaceVariables(JSON.stringify(step.body), variables)) : null;330 331 const options = {332 method: step.method,333 headers: headers,334 body: body335 };336 337 const response = await fetch(url, options);338 339 if (!response.ok) {340 throw new Error(`HTTP error! status: ${response.status}`);341 }342 343 const data = await response.json();344 return { status: response.status, headers: response.headers, body: data };345 }346 347 function replaceVariables(template, variables) {348 return template.replace(/{{s*([^}]+)s*}}/g, (match, p1) => variables[p1.trim()] || match);349 }350 351 function evaluateAssert(response, assert, variables) {352 const value = getValueFromPath(response, assert.path);353 const expectedValue = replaceVariables(assert.value, variables);354 let passed = false;355 let message = '';356 357 switch (assert.operator) {358 case 'equals':359 passed = value == expectedValue;360 message = `${assert.path} should be ${expectedValue}, but was ${value}`;361 break;362 case 'is_not':363 passed = value != expectedValue;364 message = `${assert.path} should not be ${expectedValue}, but was ${value}`;365 break;366 // Add more operators as needed367 default:368 message = `Unknown operator ${assert.operator}`;369 }370 371 return { passed, message };372 }373 374 function getValueFromPath(obj, path) {375 return path.split('.').reduce((acc, part) => acc && acc[part], obj);376 }377
Enlace
El enlace para compartir es: