1 <!DOCTYPE html>2 <html lang="es">3 <head>4 <meta charset="UTF-8">5 <title>Plataforma de Testing Automatizado de APIs</title>6 <!-- Enlaces a Bootstrap CSS y Bootstrap Icons -->7 <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">8 <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">9 <!-- Estilos personalizados -->10 <style>11 body {12 background-color: var(--bs-body-bg);13 color: var(--bs-body-color);14 transition: background-color 0.3s, color 0.3s;15 }16 .step {17 display: flex;18 align-items: center;19 padding: 8px 12px;20 margin-bottom: 8px;21 position: relative;22 }23 .step:hover {24 background-color: #f8f9fa;25 }26 .step:hover .step-controls {27 display: flex;28 }29 .step-type {30 font-weight: bold;31 color: #0d6efd;32 margin-right: 10px;33 min-width: 60px;34 }35 .step-content {36 flex: 1;37 display: flex;38 align-items: center;39 }40 .step-content input,41 .step-content select,42 .step-content textarea {43 margin-right: 8px;44 }45 .step-controls {46 display: none;47 align-items: center;48 }49 .step-controls button {50 background: none;51 border: none;52 margin-left: 5px;53 color: #6c757d;54 }55 .step-controls button:hover {56 color: #000;57 }58 .add-step {59 text-align: center;60 margin: 20px 0;61 }62 .theme-toggle {63 cursor: pointer;64 }65 .execute-btn {66 background: none;67 border: none;68 color: #198754;69 font-size: 1.2em;70 }71 .execute-btn:hover {72 color: #145c32;73 }74 </style>75 </head>76 <body>77 <!-- Barra de navegación -->78 <nav class="navbar navbar-expand-lg navbar-dark bg-primary">79 <div class="container-fluid">80 <a class="navbar-brand" href="#">81 <!-- Logo -->82 <img src="https://via.placeholder.com/150x40?text=Logo" alt="Logo">83 </a>84 <span class="navbar-text text-white">85 Plataforma de Testing Automatizado de APIs86 </span>87 <button class="btn btn-secondary ms-auto theme-toggle" id="themeToggle">88 <i class="bi bi-moon-fill"></i>89 </button>90 </div>91 </nav>92 93 <!-- Contenido principal -->94 <div id="app" class="container my-4">95 <!-- Lista de pasos -->96 <div v-for="(step, index) in steps" :key="index" class="step">97 <span class="step-type" v-if="step.type === 'variable'">VAR</span>98 <span class="step-type" v-else-if="step.type === 'request'">{{ step.data.method }}</span>99 <span class="step-type" v-else-if="step.type === 'assertion'">ASSERT</span>100 101 <div class="step-content">102 <!-- Contenido según el tipo de paso -->103 <component :is="getStepComponent(step.type)" v-model="step.data"></component>104 </div>105 <div class="step-controls">106 <button @click="moveUp(index)" :disabled="index === 0"><i class="bi bi-arrow-up"></i></button>107 <button @click="moveDown(index)" :disabled="index === steps.length - 1"><i class="bi bi-arrow-down"></i></button>108 <button v-if="step.type === 'request'" @click="executeRequestStep(index)" class="execute-btn"><i class="bi bi-play-circle"></i></button>109 <button @click="removeStep(index)"><i class="bi bi-trash"></i></button>110 </div>111 </div>112 113 <!-- Botones para añadir nuevos pasos -->114 <div class="add-step">115 <button class="btn btn-outline-primary me-2" @click="addStep('variable')">116 <i class="bi bi-sliders"></i> Variable117 </button>118 <button class="btn btn-outline-primary me-2" @click="addStep('request')">119 <i class="bi bi-arrow-up-right-circle"></i> Solicitud HTTP120 </button>121 <button class="btn btn-outline-primary me-2" @click="addStep('assertion')">122 <i class="bi bi-check-circle"></i> Aserción123 </button>124 <!-- Agrega más botones si es necesario -->125 </div>126 127 <!-- Botón de Ejecución -->128 <div class="text-center my-4">129 <button class="btn btn-lg btn-success" @click="executeTest" :disabled="!isExecutable">130 <i class="bi bi-play-fill"></i> Ejecutar Prueba Completa131 </button>132 </div>133 134 <!-- Resultados -->135 <div class="alert" :class="{'alert-success': result?.success, 'alert-danger': !result?.success}" v-if="result">136 <strong>Resultado:</strong> {{ result.message }}137 </div>138 </div>139 140 <!-- Enlaces a Vue.js y Bootstrap JS -->141 <script src="https://cdn.jsdelivr.net/npm/vue@3.3.4/dist/vue.global.prod.js"></script>142 <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>143 144 <!-- Componentes para los pasos -->145 <script>146 const VariableStep = {147 template: `148 <div class="d-flex align-items-center">149 <input type="text" class="form-control me-2" v-model="data.name" placeholder="Nombre" style="max-width: 150px;">150 <span>=</span>151 <input type="text" class="form-control ms-2" v-model="data.value" placeholder="Valor" style="max-width: 150px;">152 </div>153 `,154 props: ['modelValue'],155 computed: {156 data: {157 get() { return this.modelValue; },158 set(value) { this.$emit('update:modelValue', value); }159 }160 }161 };162 163 const RequestStep = {164 template: `165 <div class="d-flex align-items-center flex-wrap">166 <select class="form-select me-2" v-model="data.method" style="max-width: 100px;">167 <option>GET</option>168 <option>POST</option>169 <option>PUT</option>170 <option>DELETE</option>171 </select>172 <input type="text" class="form-control me-2" v-model="data.url" placeholder="URL" style="flex: 1;">173 <!-- Botón para mostrar detalles -->174 <button class="btn btn-sm btn-outline-secondary me-2" @click="toggleDetails">175 <i class="bi" :class="showDetails ? 'bi-chevron-up' : 'bi-chevron-down'"></i>176 </button>177 <!-- Detalles opcionales -->178 <div v-if="showDetails" class="w-100 mt-2">179 <!-- Headers -->180 <button class="btn btn-sm btn-outline-secondary me-2" @click="toggleHeaders">181 <i class="bi" :class="showHeaders ? 'bi-dash' : 'bi-plus'"></i> Headers182 </button>183 <!-- Body -->184 <button class="btn btn-sm btn-outline-secondary" @click="toggleBody" v-if="data.method !== 'GET'">185 <i class="bi" :class="showBody ? 'bi-dash' : 'bi-plus'"></i> Body186 </button>187 <!-- Headers -->188 <div v-if="showHeaders" class="mt-2">189 <div v-for="(header, index) in data.headers" :key="index" class="d-flex align-items-center mb-2">190 <input type="text" class="form-control me-2" v-model="header.key" placeholder="Header Key" style="max-width: 150px;">191 <input type="text" class="form-control me-2" v-model="header.value" placeholder="Header Value" style="max-width: 150px;">192 <button class="btn btn-sm btn-outline-danger" @click="removeHeader(index)">193 <i class="bi bi-trash"></i>194 </button>195 </div>196 <button class="btn btn-outline-primary btn-sm" @click="addHeader">197 <i class="bi bi-plus-lg"></i> Agregar Header198 </button>199 </div>200 <!-- Body -->201 <div v-if="showBody" class="mt-2">202 <label class="form-label">Body</label>203 <textarea class="form-control" rows="3" v-model="data.body" placeholder="Cuerpo de la solicitud"></textarea>204 </div>205 </div>206 </div>207 `,208 props: ['modelValue'],209 data() {210 return {211 showDetails: false,212 showHeaders: false,213 showBody: false,214 }215 },216 computed: {217 data: {218 get() { return this.modelValue; },219 set(value) { this.$emit('update:modelValue', value); }220 }221 },222 methods: {223 toggleDetails() {224 this.showDetails = !this.showDetails;225 },226 toggleHeaders() {227 this.showHeaders = !this.showHeaders;228 },229 toggleBody() {230 this.showBody = !this.showBody;231 },232 addHeader() {233 this.data.headers.push({ key: '', value: '' });234 },235 removeHeader(index) {236 this.data.headers.splice(index, 1);237 }238 }239 };240 241 const AssertionStep = {242 template: `243 <div class="d-flex align-items-center">244 <input type="text" class="form-control" v-model="data.condition" placeholder="Condición">245 </div>246 `,247 props: ['modelValue'],248 computed: {249 data: {250 get() { return this.modelValue; },251 set(value) { this.$emit('update:modelValue', value); }252 }253 }254 };255 256 // Instancia de Vue257 const { createApp } = Vue;258 259 createApp({260 components: {261 'variable-step': VariableStep,262 'request-step': RequestStep,263 'assertion-step': AssertionStep,264 // Agrega más componentes si es necesario265 },266 data() {267 return {268 steps: [],269 result: null,270 darkTheme: false,271 }272 },273 computed: {274 isExecutable() {275 // Verifica si hay al menos un paso para ejecutar276 return this.steps.length > 0;277 }278 },279 methods: {280 getStepComponent(type) {281 switch (type) {282 case 'variable':283 return 'variable-step';284 case 'request':285 return 'request-step';286 case 'assertion':287 return 'assertion-step';288 // Otros casos...289 }290 },291 moveUp(index) {292 if (index > 0) {293 [this.steps[index - 1], this.steps[index]] = [this.steps[index], this.steps[index - 1]];294 }295 },296 moveDown(index) {297 if (index < this.steps.length - 1) {298 [this.steps[index], this.steps[index + 1]] = [this.steps[index + 1], this.steps[index]];299 }300 },301 removeStep(index) {302 this.steps.splice(index, 1);303 },304 addStep(type) {305 let newStep = {306 type: type,307 data: {}308 };309 switch (type) {310 case 'variable':311 newStep.data = { name: '', value: '' };312 break;313 case 'request':314 newStep.data = { method: 'GET', url: '', headers: [], body: '' };315 break;316 case 'assertion':317 newStep.data = { condition: '' };318 break;319 // Otros casos...320 }321 this.steps.push(newStep);322 },323 executeTest() {324 // Simulación de ejecución de prueba completa325 this.result = {326 success: true,327 message: 'Prueba completa ejecutada exitosamente.'328 };329 // Animación de scroll al resultado330 this.$nextTick(() => {331 window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });332 });333 },334 executeRequestStep(index) {335 // Simulación de ejecución individual del paso Request336 alert(`Ejecutando solicitud: ${this.steps[index].data.method} ${this.steps[index].data.url}`);337 // Aquí podrías implementar la lógica para ejecutar solo este paso y mostrar resultados338 }339 },340 mounted() {341 // Inicializar el tema según las preferencias del usuario342 const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;343 if (prefersDark) {344 this.toggleTheme();345 }346 }347 }).mount('#app');348 349 // Manejo del botón de cambio de tema fuera de Vue350 document.getElementById('themeToggle').addEventListener('click', () => {351 document.body.classList.toggle('bg-dark');352 document.body.classList.toggle('text-white');353 const icon = document.getElementById('themeToggle').querySelector('i');354 icon.classList.toggle('bi-moon-fill');355 icon.classList.toggle('bi-sun-fill');356 });357 </script>358 </body>359 </html>360
Enlace
El enlace para compartir es: