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 .card {17 margin-bottom: 20px;18 transition: transform 0.2s;19 }20 .card:hover {21 transform: translateY(-3px);22 }23 .navbar-brand img {24 height: 40px;25 }26 .theme-toggle {27 cursor: pointer;28 }29 .sidebar {30 position: fixed;31 top: 56px;32 bottom: 0;33 left: 0;34 padding: 20px;35 width: 250px;36 background-color: #f8f9fa;37 overflow-y: auto;38 }39 .content {40 margin-left: 270px;41 padding: 20px;42 }43 .nav-pills .nav-link.active {44 background-color: #0d6efd;45 }46 .accordion-button:not(.collapsed) {47 color: #0d6efd;48 background-color: #e7f1ff;49 }50 @media (max-width: 767px) {51 .sidebar {52 position: relative;53 width: 100%;54 margin-bottom: 20px;55 }56 .content {57 margin-left: 0;58 }59 }60 </style>61 </head>62 <body>63 <!-- Barra de navegación -->64 <nav class="navbar navbar-expand-lg navbar-dark bg-primary">65 <div class="container-fluid">66 <a class="navbar-brand" href="#">67 <!-- Logo -->68 <img src="https://via.placeholder.com/150x40?text=Logo" alt="Logo">69 </a>70 <span class="navbar-text text-white">71 Plataforma de Testing Automatizado de APIs72 </span>73 <button class="btn btn-secondary ms-auto theme-toggle" id="themeToggle">74 <i class="bi bi-moon-fill"></i>75 </button>76 </div>77 </nav>78 79 <!-- Barra lateral -->80 <div class="sidebar">81 <h5>Flujo de Prueba</h5>82 <ul class="nav nav-pills flex-column">83 <li class="nav-item">84 <a class="nav-link active" href="#variables" @click="selectTab('variables')">85 <i class="bi bi-sliders"></i> Variables86 </a>87 </li>88 <li class="nav-item">89 <a class="nav-link" href="#request" @click="selectTab('request')">90 <i class="bi bi-arrow-up-right-circle"></i> Request91 </a>92 </li>93 <li class="nav-item">94 <a class="nav-link" href="#assertions" @click="selectTab('assertions')">95 <i class="bi bi-check-circle"></i> Aserciones96 </a>97 </li>98 <li class="nav-item">99 <a class="nav-link" href="#sleep" @click="selectTab('sleep')">100 <i class="bi bi-hourglass-split"></i> Sleep101 </a>102 </li>103 <li class="nav-item">104 <a class="nav-link" href="#repeat" @click="selectTab('repeat')">105 <i class="bi bi-repeat"></i> Repeat106 </a>107 </li>108 <li class="nav-item">109 <a class="nav-link" href="#call" @click="selectTab('call')">110 <i class="bi bi-box-arrow-in-right"></i> Call111 </a>112 </li>113 </ul>114 </div>115 116 <!-- Contenido principal -->117 <div id="app" class="content">118 <!-- Pestañas principales -->119 <ul class="nav nav-tabs" id="mainTabs" role="tablist">120 <li class="nav-item" role="presentation" v-for="tab in tabs">121 <button class="nav-link" :class="{ active: activeTab === tab.id }" :id="tab.id + '-tab'" data-bs-toggle="tab" :data-bs-target="'#' + tab.id" type="button" role="tab" @click="selectTab(tab.id)">122 {{ tab.name }}123 </button>124 </li>125 </ul>126 <div class="tab-content mt-4">127 <!-- Variables -->128 <div class="tab-pane fade" :class="{ 'show active': activeTab === 'variables' }" id="variables" role="tabpanel">129 <div class="card">130 <div class="card-body">131 <!-- Variable principal -->132 <div class="row g-3">133 <div class="col-md-6">134 <label class="form-label">Nombre de la Variable</label>135 <input type="text" class="form-control" v-model="variables.name">136 </div>137 <div class="col-md-6">138 <label class="form-label">Valor</label>139 <input type="text" class="form-control" v-model="variables.value">140 </div>141 </div>142 <!-- Aserción de variable -->143 <div class="form-check form-switch mt-3">144 <input class="form-check-input" type="checkbox" id="assertVariable" v-model="variables.assert">145 <label class="form-check-label" for="assertVariable">146 Assert variable147 </label>148 </div>149 <div class="mt-2" v-if="variables.assert">150 <label class="form-label">Valores Esperados (separados por coma)</label>151 <input type="text" class="form-control" v-model="variables.assertValues">152 </div>153 </div>154 </div>155 </div>156 <!-- Request -->157 <div class="tab-pane fade" :class="{ 'show active': activeTab === 'request' }" id="request" role="tabpanel">158 <div class="card">159 <div class="card-body">160 <!-- Método y URL -->161 <div class="row g-3 align-items-end">162 <div class="col-md-3">163 <label class="form-label">Método HTTP</label>164 <select class="form-select" v-model="request.method">165 <option>GET</option>166 <option>POST</option>167 <option>PUT</option>168 <option>DELETE</option>169 </select>170 </div>171 <div class="col-md-9">172 <label class="form-label">URL</label>173 <input type="text" class="form-control" v-model="request.url" @input="validateURL">174 <div class="invalid-feedback" v-if="!validURL">175 Por favor, introduce una URL válida.176 </div>177 </div>178 </div>179 <!-- Headers y Body en Accordions -->180 <div class="accordion mt-4" id="requestAccordion">181 <!-- Headers -->182 <div class="accordion-item">183 <h2 class="accordion-header" id="headingHeaders">184 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseHeaders" aria-expanded="false" aria-controls="collapseHeaders">185 Headers186 </button>187 </h2>188 <div id="collapseHeaders" class="accordion-collapse collapse" aria-labelledby="headingHeaders" data-bs-parent="#requestAccordion">189 <div class="accordion-body">190 <div v-for="(header, index) in request.headers" :key="index" class="row g-2 mb-2 align-items-center">191 <div class="col-md-5">192 <input type="text" class="form-control" v-model="header.key" placeholder="Header Key">193 </div>194 <div class="col-md-5">195 <input type="text" class="form-control" v-model="header.value" placeholder="Header Value">196 </div>197 <div class="col-md-2">198 <button class="btn btn-outline-danger w-100" @click="removeHeader(index)">199 <i class="bi bi-trash"></i>200 </button>201 </div>202 </div>203 <button class="btn btn-outline-primary add-button" @click="addHeader">204 <i class="bi bi-plus-lg"></i> Agregar Header205 </button>206 </div>207 </div>208 </div>209 <!-- Body -->210 <div class="accordion-item" v-if="request.method !== 'GET'">211 <h2 class="accordion-header" id="headingBody">212 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseBody" aria-expanded="false" aria-controls="collapseBody">213 Body214 </button>215 </h2>216 <div id="collapseBody" class="accordion-collapse collapse" aria-labelledby="headingBody" data-bs-parent="#requestAccordion">217 <div class="accordion-body">218 <textarea class="form-control" rows="5" v-model="request.body" placeholder="Cuerpo de la solicitud"></textarea>219 </div>220 </div>221 </div>222 </div>223 </div>224 </div>225 </div>226 <!-- Aserciones -->227 <div class="tab-pane fade" :class="{ 'show active': activeTab === 'assertions' }" id="assertions" role="tabpanel">228 <div class="card">229 <div class="card-body">230 <!-- Aserción de respuesta -->231 <div class="form-check form-switch">232 <input class="form-check-input" type="checkbox" id="assertResponseBody" v-model="assertion.enabled">233 <label class="form-check-label" for="assertResponseBody">Activar Aserción de Respuesta</label>234 </div>235 <div class="mt-3" v-if="assertion.enabled">236 <label class="form-label">Condición</label>237 <input type="text" class="form-control" v-model="assertion.condition" placeholder="Ejemplo: response.body.data.status is success">238 </div>239 </div>240 </div>241 </div>242 <!-- Sleep -->243 <div class="tab-pane fade" :class="{ 'show active': activeTab === 'sleep' }" id="sleep" role="tabpanel">244 <div class="card">245 <div class="card-body">246 <label class="form-label">Tiempo de Espera</label>247 <input type="text" class="form-control" v-model="sleepTime" placeholder="Ejemplo: 10s">248 </div>249 </div>250 </div>251 <!-- Repeat -->252 <div class="tab-pane fade" :class="{ 'show active': activeTab === 'repeat' }" id="repeat" role="tabpanel">253 <div class="card">254 <div class="card-body">255 <label class="form-label">Número de Iteraciones</label>256 <input type="number" class="form-control" v-model.number="repeatCount" min="1">257 </div>258 </div>259 </div>260 <!-- Call -->261 <div class="tab-pane fade" :class="{ 'show active': activeTab === 'call' }" id="call" role="tabpanel">262 <div class="card">263 <div class="card-body">264 <h5>Parámetros para Create Payment</h5>265 <div v-for="(param, index) in callParams" :key="index" class="row g-3 align-items-center mb-2">266 <div class="col-md-4">267 <label class="form-label">{{ param.name }}</label>268 </div>269 <div class="col-md-8">270 <input type="text" class="form-control" v-model="param.value">271 </div>272 </div>273 </div>274 </div>275 </div>276 </div>277 278 <!-- Botón de Ejecución -->279 <div class="text-center my-4">280 <button class="btn btn-lg btn-primary" @click="executeTest" :disabled="!validURL">281 <i class="bi bi-play-fill"></i> Ejecutar Prueba282 </button>283 </div>284 285 <!-- Result -->286 <div class="card" v-if="result">287 <div class="card-header" :class="{'bg-success text-white': result.success, 'bg-danger text-white': !result.success}">288 <h5>Resultado</h5>289 </div>290 <div class="card-body">291 <p><strong>STATUS:</strong> {{ result.status }}</p>292 <p><strong>Mensaje:</strong> {{ result.message }}</p>293 </div>294 </div>295 296 </div>297 298 <!-- Enlaces a Vue.js y Bootstrap JS -->299 <script src="https://cdn.jsdelivr.net/npm/vue@3.3.4/dist/vue.global.prod.js"></script>300 <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>301 302 <!-- Script Vue.js -->303 <script>304 const { createApp } = Vue;305 306 createApp({307 data() {308 return {309 activeTab: 'variables',310 tabs: [311 { id: 'variables', name: 'Variables' },312 { id: 'request', name: 'Request' },313 { id: 'assertions', name: 'Aserciones' },314 { id: 'sleep', name: 'Sleep' },315 { id: 'repeat', name: 'Repeat' },316 { id: 'call', name: 'Call' },317 ],318 variables: {319 name: 'user-id',320 value: 'uuid',321 assert: false,322 assertValues: ''323 },324 request: {325 method: 'GET',326 url: '',327 headers: [],328 body: ''329 },330 assertion: {331 enabled: false,332 condition: ''333 },334 sleepTime: '',335 repeatCount: 1,336 callParams: [337 { name: 'payment-id', value: '[uuid]' },338 { name: 'amount', value: '' },339 { name: 'IBAN', value: 'my-iban' }340 ],341 result: null,342 validURL: true,343 darkTheme: false344 }345 },346 methods: {347 selectTab(tabId) {348 this.activeTab = tabId;349 },350 addHeader() {351 this.request.headers.push({ key: '', value: '' });352 },353 removeHeader(index) {354 this.request.headers.splice(index, 1);355 },356 validateURL() {357 try {358 new URL(this.request.url);359 this.validURL = true;360 } catch (_) {361 this.validURL = false;362 }363 },364 executeTest() {365 // Simulación de ejecución de prueba366 this.result = {367 success: true,368 status: 'ACCEPTED',369 message: 'SUBMISSION ID posted: 521-2021-3215-33'370 };371 // Animación de scroll al resultado372 this.$nextTick(() => {373 const resultCard = document.querySelector('.card:last-child');374 resultCard.scrollIntoView({ behavior: 'smooth' });375 });376 }377 },378 mounted() {379 // Inicializar el tema según las preferencias del usuario380 const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;381 if (prefersDark) {382 this.toggleTheme();383 }384 }385 }).mount('#app');386 387 // Manejo del botón de cambio de tema fuera de Vue388 document.getElementById('themeToggle').addEventListener('click', () => {389 document.body.classList.toggle('bg-dark');390 document.body.classList.toggle('text-white');391 const icon = document.getElementById('themeToggle').querySelector('i');392 icon.classList.toggle('bi-moon-fill');393 icon.classList.toggle('bi-sun-fill');394 });395 </script>396 </body>397 </html>398
Enlace
El enlace para compartir es: