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