viberadar 0.3.209 → 0.3.211

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1784,6 +1784,11 @@ let loadScriptNameDraft = ''; // save name input draft
1784
1784
  let loadBaseUrlDraft = 'http://localhost:5000';
1785
1785
  let loadVusDraft = 10;
1786
1786
  let loadDurationDraft = '30s';
1787
+ let loadDataDirDraft = '';
1788
+ let loadResultDirDraft = '';
1789
+ let loadExecutionModeDraft = 'auto';
1790
+ let loadVusEnvNameDraft = 'SMOKE_VUS';
1791
+ let loadDurationEnvNameDraft = 'SMOKE_DURATION';
1787
1792
  let loadView = 'library'; // 'library' | 'editor'
1788
1793
 
1789
1794
  function toggleObsHint(id) {
@@ -7759,12 +7764,27 @@ function applyLoadConfigToFields(cfg) {
7759
7764
  loadBaseUrlDraft = cfg.baseUrl || loadBaseUrlDraft;
7760
7765
  loadVusDraft = cfg.vus || loadVusDraft;
7761
7766
  loadDurationDraft = cfg.duration || loadDurationDraft;
7767
+ loadDataDirDraft = cfg.dataDir || loadDataDirDraft;
7768
+ loadResultDirDraft = cfg.resultDir || loadResultDirDraft;
7769
+ loadExecutionModeDraft = cfg.executionMode || loadExecutionModeDraft;
7770
+ loadVusEnvNameDraft = cfg.vusEnvName || loadVusEnvNameDraft;
7771
+ loadDurationEnvNameDraft = cfg.durationEnvName || loadDurationEnvNameDraft;
7762
7772
  const baseEl = document.getElementById('loadBaseUrl');
7763
7773
  const vusEl = document.getElementById('loadVus');
7764
7774
  const durEl = document.getElementById('loadDuration');
7775
+ const dataEl = document.getElementById('loadDataDir');
7776
+ const resultEl = document.getElementById('loadResultDir');
7777
+ const modeEl = document.getElementById('loadExecutionMode');
7778
+ const vusEnvEl = document.getElementById('loadVusEnvName');
7779
+ const durationEnvEl = document.getElementById('loadDurationEnvName');
7765
7780
  if (baseEl) baseEl.value = loadBaseUrlDraft;
7766
7781
  if (vusEl) vusEl.value = loadVusDraft;
7767
7782
  if (durEl) durEl.value = loadDurationDraft;
7783
+ if (dataEl) dataEl.value = loadDataDirDraft;
7784
+ if (resultEl) resultEl.value = loadResultDirDraft;
7785
+ if (modeEl) modeEl.value = loadExecutionModeDraft;
7786
+ if (vusEnvEl) vusEnvEl.value = loadVusEnvNameDraft;
7787
+ if (durationEnvEl) durationEnvEl.value = loadDurationEnvNameDraft;
7768
7788
  }
7769
7789
 
7770
7790
  function renderLoad(c) {
@@ -7810,7 +7830,7 @@ function renderLoad(c) {
7810
7830
  <div class="load-run-name">${escapeHtml(r.scriptName || 'Без названия')}</div>
7811
7831
  <div class="load-run-meta">${formatLoadRunDate(r.createdAt || r.startTime)} · ${cfg.vus || '—'} VU · ${escapeHtml(cfg.duration || '—')} · ${loadStatusLabel(r.status)}</div>
7812
7832
  </div>
7813
- <div class="load-run-kpi">${summary.rps != null ? Number(summary.rps || 0).toFixed(1) : '—'} RPS</div>
7833
+ <div class="load-run-kpi" title="${escapeHtml(cfg.resultPath || '')}">${summary.rps != null ? Number(summary.rps || 0).toFixed(1) : '—'} RPS</div>
7814
7834
  <div class="load-run-kpi">p95 ${summary.p95Duration != null ? Math.round(summary.p95Duration || 0) + 'ms' : '—'}</div>
7815
7835
  <div class="load-run-kpi" style="color:${Number(summary.errorPct || 0) > 5 ? 'var(--red)' : 'var(--muted)'}">${err}</div>
7816
7836
  </div>`;
@@ -7894,6 +7914,36 @@ function renderLoad(c) {
7894
7914
  </div>
7895
7915
  </div>
7896
7916
  <div class="load-config-hint">VUs и Duration применяются через CLI k6 и переопределяют значения из <code>export const options</code>. Base URL доступен в скрипте как <code>__ENV.BASE_URL</code>.</div>
7917
+ <div class="load-config-row" style="margin-top:8px">
7918
+ <div class="load-config-field">
7919
+ <label>Execution</label>
7920
+ <select id="loadExecutionMode" style="width:150px">
7921
+ <option value="auto" ${loadExecutionModeDraft === 'auto' ? 'selected' : ''}>Auto</option>
7922
+ <option value="cli" ${loadExecutionModeDraft === 'cli' ? 'selected' : ''}>CLI override</option>
7923
+ <option value="script" ${loadExecutionModeDraft === 'script' ? 'selected' : ''}>Script scenarios</option>
7924
+ </select>
7925
+ </div>
7926
+ <div class="load-config-field">
7927
+ <label>VUs env</label>
7928
+ <input id="loadVusEnvName" type="text" value="${escapeHtml(loadVusEnvNameDraft)}" style="width:130px" placeholder="SMOKE_VUS" />
7929
+ </div>
7930
+ <div class="load-config-field">
7931
+ <label>Duration env</label>
7932
+ <input id="loadDurationEnvName" type="text" value="${escapeHtml(loadDurationEnvNameDraft)}" style="width:150px" placeholder="SMOKE_DURATION" />
7933
+ </div>
7934
+ </div>
7935
+ <div class="load-config-hint">Auto: если скрипт содержит <code>options.scenarios</code>, VibeRadar не передаёт <code>--vus/--duration</code>, а кладёт значения в env-переменные, например <code>SMOKE_VUS</code> и <code>SMOKE_DURATION</code>.</div>
7936
+ <div class="load-config-row" style="margin-top:8px">
7937
+ <div class="load-config-field" style="flex:1;min-width:260px">
7938
+ <label>Data dir / file <span style="color:var(--dim);font-weight:400">(для <code>open('./users.csv')</code> и локальных lib)</span></label>
7939
+ <input id="loadDataDir" type="text" value="${escapeHtml(loadDataDirDraft)}" placeholder="load-tests/k6 или C:\\path\\to\\k6-data" />
7940
+ </div>
7941
+ <div class="load-config-field" style="flex:1;min-width:260px">
7942
+ <label>Results dir <span style="color:var(--dim);font-weight:400">(необязательно)</span></label>
7943
+ <input id="loadResultDir" type="text" value="${escapeHtml(loadResultDirDraft)}" placeholder=".viberadar/load-runs или C:\\path\\to\\results" />
7944
+ </div>
7945
+ </div>
7946
+ <div class="load-config-hint">Если указать Data dir, VibeRadar скопирует его содержимое рядом со скриптом перед запуском. Результаты сохраняются в <code>results/&lt;runId&gt;</code>: summary.json, metrics.ndjson, k6.log, config.json.</div>
7897
7947
  <div class="load-config-row" style="margin-top:8px">
7898
7948
  <div class="load-config-field" style="flex:1;min-width:300px">
7899
7949
  <label>Bearer Token <span style="color:var(--dim);font-weight:400;font-size:11px">(→ <code style="background:var(--bg2);padding:1px 4px;border-radius:3px">__ENV.TOKEN</code>)</span></label>
@@ -7983,9 +8033,19 @@ function renderLoad(c) {
7983
8033
  const baseInput = document.getElementById('loadBaseUrl');
7984
8034
  const vusInput = document.getElementById('loadVus');
7985
8035
  const durationInput = document.getElementById('loadDuration');
8036
+ const dataInput = document.getElementById('loadDataDir');
8037
+ const resultInput = document.getElementById('loadResultDir');
8038
+ const modeInput = document.getElementById('loadExecutionMode');
8039
+ const vusEnvInput = document.getElementById('loadVusEnvName');
8040
+ const durationEnvInput = document.getElementById('loadDurationEnvName');
7986
8041
  if (baseInput) baseInput.addEventListener('input', () => { loadBaseUrlDraft = baseInput.value || 'http://localhost:5000'; });
7987
8042
  if (vusInput) vusInput.addEventListener('input', () => { loadVusDraft = parseInt(vusInput.value || '10', 10) || 10; });
7988
8043
  if (durationInput) durationInput.addEventListener('input', () => { loadDurationDraft = durationInput.value || '30s'; });
8044
+ if (dataInput) dataInput.addEventListener('input', () => { loadDataDirDraft = dataInput.value || ''; });
8045
+ if (resultInput) resultInput.addEventListener('input', () => { loadResultDirDraft = resultInput.value || ''; });
8046
+ if (modeInput) modeInput.addEventListener('change', () => { loadExecutionModeDraft = modeInput.value || 'auto'; });
8047
+ if (vusEnvInput) vusEnvInput.addEventListener('input', () => { loadVusEnvNameDraft = vusEnvInput.value || 'SMOKE_VUS'; });
8048
+ if (durationEnvInput) durationEnvInput.addEventListener('input', () => { loadDurationEnvNameDraft = durationEnvInput.value || 'SMOKE_DURATION'; });
7989
8049
 
7990
8050
  // Feature selector auto-generates script
7991
8051
  const featSel = document.getElementById('loadFeature');
@@ -8023,6 +8083,11 @@ function loadOpenScript(name) {
8023
8083
  if (s.baseUrl) loadBaseUrlDraft = s.baseUrl;
8024
8084
  if (s.vus) loadVusDraft = s.vus;
8025
8085
  if (s.duration) loadDurationDraft = s.duration;
8086
+ loadDataDirDraft = s.dataDir || loadDataDirDraft;
8087
+ loadResultDirDraft = s.resultDir || loadResultDirDraft;
8088
+ loadExecutionModeDraft = s.executionMode || loadExecutionModeDraft;
8089
+ loadVusEnvNameDraft = s.vusEnvName || loadVusEnvNameDraft;
8090
+ loadDurationEnvNameDraft = s.durationEnvName || loadDurationEnvNameDraft;
8026
8091
  loadView = 'editor';
8027
8092
  renderContent();
8028
8093
  }
@@ -8051,10 +8116,21 @@ async function runLoadTest() {
8051
8116
  const vus = parseInt(document.getElementById('loadVus')?.value || String(loadVusDraft || 10), 10) || 10;
8052
8117
  const duration = (document.getElementById('loadDuration')?.value || loadDurationDraft || '30s').trim();
8053
8118
  const scriptName = (document.getElementById('loadScriptName')?.value || loadScriptNameDraft || 'Новый тест').trim();
8119
+ const dataDir = (document.getElementById('loadDataDir')?.value || loadDataDirDraft || '').trim();
8120
+ const resultDir = (document.getElementById('loadResultDir')?.value || loadResultDirDraft || '').trim();
8121
+ const executionModeRaw = (document.getElementById('loadExecutionMode')?.value || loadExecutionModeDraft || 'auto').trim();
8122
+ const executionMode = executionModeRaw === 'auto' ? undefined : executionModeRaw;
8123
+ const vusEnvName = (document.getElementById('loadVusEnvName')?.value || loadVusEnvNameDraft || 'SMOKE_VUS').trim();
8124
+ const durationEnvName = (document.getElementById('loadDurationEnvName')?.value || loadDurationEnvNameDraft || 'SMOKE_DURATION').trim();
8054
8125
  loadBaseUrlDraft = baseUrl;
8055
8126
  loadVusDraft = vus;
8056
8127
  loadDurationDraft = duration;
8057
8128
  loadScriptNameDraft = scriptName;
8129
+ loadDataDirDraft = dataDir;
8130
+ loadResultDirDraft = resultDir;
8131
+ loadExecutionModeDraft = executionModeRaw;
8132
+ loadVusEnvNameDraft = vusEnvName;
8133
+ loadDurationEnvNameDraft = durationEnvName;
8058
8134
 
8059
8135
  loadLogLines = [];
8060
8136
  loadBuckets = [];
@@ -8066,7 +8142,7 @@ async function runLoadTest() {
8066
8142
  const r = await fetch('/api/load/run', {
8067
8143
  method: 'POST',
8068
8144
  headers: { 'Content-Type': 'application/json' },
8069
- body: JSON.stringify({ script, vus, duration, baseUrl, scriptName, envVars }),
8145
+ body: JSON.stringify({ script, vus, duration, baseUrl, scriptName, dataDir, resultDir, executionMode, vusEnvName, durationEnvName, envVars }),
8070
8146
  });
8071
8147
  const d = await r.json();
8072
8148
  if (!r.ok) { alert('Ошибка запуска: ' + (d.error || r.status)); return; }
@@ -8147,13 +8223,15 @@ ${featureList || '(нет данных)'}
8147
8223
  **Шаг 2 — составь k6 скрипт.**
8148
8224
  Требования:
8149
8225
  1. Валидный JavaScript для k6 (import from 'k6/http', 'k6', 'k6/data')
8150
- 2. VUs и duration задаёт VibeRadar через CLI, поэтому не хардкодь нагрузку как единственный источник истины; если добавляешь \`export const options\`, оставь там только thresholds/summaryTrendStats или безопасные дефолты
8226
+ 2. Можно использовать \`options.scenarios\`; тогда VibeRadar в Auto-режиме не будет передавать \`--vus/--duration\`, а прокинет значения UI в \`__ENV.SMOKE_VUS\` и \`__ENV.SMOKE_DURATION\`
8151
8227
  3. \`export default function() { ... }\` с проверками \`check()\`
8152
8228
  4. Base URL бери из \`__ENV.BASE_URL || '${baseUrl}'\`
8153
8229
  5. Если нужна авторизация — добавь заголовок Authorization (Bearer-токен как переменную __ENV.TOKEN)
8154
8230
  6. Если нужна загрузка файла — используй \`http.file()\` и \`open()\`
8155
- 7. Добавь \`sleep(1)\` между запросами
8156
- 8. Добавь комментарий в начале: что тестируется и какой эндпоинт
8231
+ 7. Если нужны CSV/JSON/локальные helper-модули, используй относительные пути от Data dir: \`open('./users.csv')\`, \`import './lib/helper.js'\`
8232
+ 8. Не добавляй \`handleSummary\` и не пиши результаты сам: VibeRadar сохраняет summary.json, metrics.ndjson, k6.log
8233
+ 9. Добавь \`sleep(1)\` между запросами
8234
+ 10. Добавь комментарий в начале: что тестируется и какой эндпоинт
8157
8235
 
8158
8236
  **Шаг 3 — сохрани ТОЛЬКО скрипт в файл.**
8159
8237
  Запиши итоговый JavaScript-код в файл: \`.viberadar/load-script-generated.js\`
@@ -8264,15 +8342,25 @@ async function loadSaveScript() {
8264
8342
  const baseUrl = (document.getElementById('loadBaseUrl')?.value || loadBaseUrlDraft || 'http://localhost:5000').trim();
8265
8343
  const vus = parseInt(document.getElementById('loadVus')?.value || String(loadVusDraft || 10), 10) || 10;
8266
8344
  const duration = (document.getElementById('loadDuration')?.value || loadDurationDraft || '30s').trim();
8345
+ const dataDir = (document.getElementById('loadDataDir')?.value || loadDataDirDraft || '').trim();
8346
+ const resultDir = (document.getElementById('loadResultDir')?.value || loadResultDirDraft || '').trim();
8347
+ const executionMode = (document.getElementById('loadExecutionMode')?.value || loadExecutionModeDraft || 'auto').trim();
8348
+ const vusEnvName = (document.getElementById('loadVusEnvName')?.value || loadVusEnvNameDraft || 'SMOKE_VUS').trim();
8349
+ const durationEnvName = (document.getElementById('loadDurationEnvName')?.value || loadDurationEnvNameDraft || 'SMOKE_DURATION').trim();
8267
8350
  loadScriptNameDraft = name;
8268
8351
  loadBaseUrlDraft = baseUrl;
8269
8352
  loadVusDraft = vus;
8270
8353
  loadDurationDraft = duration;
8354
+ loadDataDirDraft = dataDir;
8355
+ loadResultDirDraft = resultDir;
8356
+ loadExecutionModeDraft = executionMode;
8357
+ loadVusEnvNameDraft = vusEnvName;
8358
+ loadDurationEnvNameDraft = durationEnvName;
8271
8359
  try {
8272
8360
  const r = await fetch('/api/load/scripts', {
8273
8361
  method: 'POST',
8274
8362
  headers: { 'Content-Type': 'application/json' },
8275
- body: JSON.stringify({ name, script, baseUrl, vus, duration }),
8363
+ body: JSON.stringify({ name, script, baseUrl, vus, duration, dataDir, resultDir, executionMode, vusEnvName, durationEnvName }),
8276
8364
  });
8277
8365
  if (!r.ok) { const d = await r.json().catch(() => ({})); alert('Ошибка: ' + (d.error || r.status)); return; }
8278
8366
  await loadRefreshScripts();
@@ -8290,6 +8378,11 @@ async function loadLoadScript(name) {
8290
8378
  if (s.baseUrl) loadBaseUrlDraft = s.baseUrl;
8291
8379
  if (s.vus) loadVusDraft = s.vus;
8292
8380
  if (s.duration) loadDurationDraft = s.duration;
8381
+ loadDataDirDraft = s.dataDir || loadDataDirDraft;
8382
+ loadResultDirDraft = s.resultDir || loadResultDirDraft;
8383
+ loadExecutionModeDraft = s.executionMode || loadExecutionModeDraft;
8384
+ loadVusEnvNameDraft = s.vusEnvName || loadVusEnvNameDraft;
8385
+ loadDurationEnvNameDraft = s.durationEnvName || loadDurationEnvNameDraft;
8293
8386
  const ta = document.getElementById('loadScriptEditor');
8294
8387
  if (ta) { ta.value = s.script; ta.style.borderColor = 'var(--blue)'; setTimeout(() => { ta.style.borderColor = ''; }, 1500); }
8295
8388
  const nameEl = document.getElementById('loadScriptName');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viberadar",
3
- "version": "0.3.209",
3
+ "version": "0.3.211",
4
4
  "description": "Live module map with test coverage for vibecoding projects",
5
5
  "main": "./dist/cli.js",
6
6
  "bin": {