@alejandrojca/elmulo-reporter 2.0.0-beta.8 → 2.0.0-beta.9

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.
package/VERSION.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Elmulo Reporter",
3
- "version": "2.0.0-beta.8",
3
+ "version": "2.0.0-beta.9",
4
4
  "schemaVersion": 3,
5
5
  "source": "elmulo-reporter",
6
6
  "results": "elmulo-results",
package/assets/app.css CHANGED
@@ -850,19 +850,59 @@ button {
850
850
  margin: 1rem 0;
851
851
  }
852
852
 
853
- .trendHistoryStats span {
854
- background: var(--pw-blue-050);
855
- border-radius: 999px;
856
- color: var(--pw-gray-700);
857
- font-size: 0.78rem;
858
- padding: 0.45rem 0.65rem;
859
- }
860
-
861
- .trendHistoryStats .passed { color: #11694f; }
862
- .trendHistoryStats .failed { color: #b42143; }
863
- .trendHistoryStats .otherErrors { color: #875100; }
864
-
865
- .historicalTestList {
853
+ .historicalExecutionCount {
854
+ background: var(--pw-blue-050);
855
+ border-radius: 999px;
856
+ color: var(--pw-gray-700);
857
+ font-size: 0.78rem;
858
+ padding: 0.45rem 0.65rem;
859
+ }
860
+
861
+ .historicalStatusFilters {
862
+ display: flex;
863
+ flex: 1 1 420px;
864
+ flex-wrap: wrap;
865
+ gap: 0.45rem;
866
+ }
867
+
868
+ .historicalStatusFilter {
869
+ --historical-status-color: var(--pw-gray-700);
870
+ background: color-mix(in srgb, var(--historical-status-color) 7%, var(--pw-white));
871
+ border: 1px solid color-mix(in srgb, var(--historical-status-color) 55%, var(--pw-gray-200));
872
+ border-radius: 999px;
873
+ color: var(--historical-status-color);
874
+ font-size: 0.75rem;
875
+ font-weight: 700;
876
+ padding: 0.42rem 0.65rem;
877
+ transition:
878
+ background 140ms ease,
879
+ box-shadow 140ms ease,
880
+ transform 140ms ease;
881
+ }
882
+
883
+ .historicalStatusFilter.passed { --historical-status-color: var(--pw-success); }
884
+ .historicalStatusFilter.failed { --historical-status-color: var(--pw-danger); }
885
+ .historicalStatusFilter.environment_error { --historical-status-color: var(--pw-warning); }
886
+ .historicalStatusFilter.precondition_error { --historical-status-color: #e87922; }
887
+ .historicalStatusFilter.outdated_test { --historical-status-color: #7c5ce5; }
888
+ .historicalStatusFilter.reported { --historical-status-color: var(--pw-blue-600); }
889
+ .historicalStatusFilter.skipped,
890
+ .historicalStatusFilter.pending { --historical-status-color: #71859a; }
891
+
892
+ .historicalStatusFilter:hover,
893
+ .historicalStatusFilter:focus-visible {
894
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--historical-status-color) 18%, transparent);
895
+ transform: translateY(-1px);
896
+ }
897
+
898
+ .historicalStatusFilter.active {
899
+ background: var(--historical-status-color);
900
+ border-color: var(--historical-status-color);
901
+ color: #fff;
902
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--historical-status-color) 22%, transparent);
903
+ }
904
+
905
+ .historicalTestList {
866
906
  display: grid;
867
907
  gap: 0.55rem;
868
908
  max-height: 480px;
package/assets/app.js CHANGED
@@ -201,9 +201,10 @@
201
201
  tag: initialUrlState.get("tag") || "all",
202
202
  trendStatus: "total",
203
203
  trendEnvironment: initialTrendEnvironment,
204
- selectedTrendRunId: null,
205
- historicalRun: null,
206
- historicalRerun: null,
204
+ selectedTrendRunId: null,
205
+ historicalRun: null,
206
+ historicalRerun: null,
207
+ historicalStatusFilter: "",
207
208
  historyJiraId: null,
208
209
  historyTestTitle: "",
209
210
  historyCurrentTestId: null,
@@ -1119,7 +1120,7 @@
1119
1120
  </div>`;
1120
1121
  }
1121
1122
 
1122
- function renderHistoricalRun() {
1123
+ function renderHistoricalRun() {
1123
1124
  const historicalRun = state.historicalRun;
1124
1125
  if (!state.selectedTrendRunId) {
1125
1126
  return `<div class="trendHistoryPlaceholder">
@@ -1130,11 +1131,39 @@
1130
1131
  return `<div class="trendHistoryPlaceholder">Cargando resultados históricos…</div>`;
1131
1132
  }
1132
1133
 
1133
- const tests = historicalRun.tests || [];
1134
- const statusCount = (status) =>
1135
- tests.filter((test) => test.status === status).length;
1136
- const otherErrors = tests.filter((test) =>
1137
- otherErrorStatuses.has(test.status)).length;
1134
+ const tests = historicalRun.tests || [];
1135
+ const historicalStatusOrder = [
1136
+ "passed",
1137
+ "failed",
1138
+ "environment_error",
1139
+ "precondition_error",
1140
+ "outdated_test",
1141
+ "reported",
1142
+ "skipped",
1143
+ "pending",
1144
+ "unknown",
1145
+ ];
1146
+ const statusCounts = tests.reduce((counts, test) => {
1147
+ const status = String(test.status || "unknown");
1148
+ counts.set(status, (counts.get(status) || 0) + 1);
1149
+ return counts;
1150
+ }, new Map());
1151
+ const visibleStatuses = [...statusCounts.entries()]
1152
+ .sort(([left], [right]) => {
1153
+ const leftIndex = historicalStatusOrder.indexOf(left);
1154
+ const rightIndex = historicalStatusOrder.indexOf(right);
1155
+ return (leftIndex < 0 ? historicalStatusOrder.length : leftIndex) -
1156
+ (rightIndex < 0 ? historicalStatusOrder.length : rightIndex);
1157
+ });
1158
+ if (
1159
+ state.historicalStatusFilter &&
1160
+ !statusCounts.has(state.historicalStatusFilter)
1161
+ ) {
1162
+ state.historicalStatusFilter = "";
1163
+ }
1164
+ const filteredTests = state.historicalStatusFilter
1165
+ ? tests.filter((test) => test.status === state.historicalStatusFilter)
1166
+ : tests;
1138
1167
  const rerun = state.historicalRerun || historicalRun.rerun || {};
1139
1168
  const rerunBusy = ["starting", "running"].includes(rerun.status);
1140
1169
  const rerunLabel = rerun.status === "starting"
@@ -1172,18 +1201,23 @@
1172
1201
  >×</button>
1173
1202
  </div>
1174
1203
  </header>
1175
- ${rerunFeedback
1176
- ? `<div class="trendRerunFeedback ${rerun.status === "failed" || rerun.status === "unavailable" ? "error" : "success"}" role="status">${escapeHtml(rerunFeedback)}</div>`
1177
- : ""}
1178
- <div class="trendHistoryStats">
1179
- <span><strong>${tests.length}</strong> ejecuciones</span>
1180
- <span class="passed"><strong>${statusCount("passed")}</strong> exitosas</span>
1181
- <span class="failed"><strong>${statusCount("failed")}</strong> fallidas</span>
1182
- <span class="otherErrors"><strong>${otherErrors}</strong> otros errores</span>
1183
- </div>
1184
- <div class="historicalTestList">
1185
- ${tests.length
1186
- ? tests.map((test) => `<article class="historicalTestRow">
1204
+ ${rerunFeedback
1205
+ ? `<div class="trendRerunFeedback ${rerun.status === "failed" || rerun.status === "unavailable" ? "error" : "success"}" role="status">${escapeHtml(rerunFeedback)}</div>`
1206
+ : ""}
1207
+ <div class="trendHistoryStats">
1208
+ <span class="historicalExecutionCount"><strong>${tests.length}</strong> ejecuciones</span>
1209
+ <div class="historicalStatusFilters" aria-label="Filtrar pruebas históricas por estado">
1210
+ ${visibleStatuses.map(([status, count]) => `<button
1211
+ class="historicalStatusFilter ${escapeHtml(status)} ${state.historicalStatusFilter === status ? "active" : ""}"
1212
+ type="button"
1213
+ data-historical-status-filter="${escapeHtml(status)}"
1214
+ aria-pressed="${state.historicalStatusFilter === status}"
1215
+ ><strong>${escapeHtml(count)}</strong> ${escapeHtml(statusLabel[status] || status)}</button>`).join("")}
1216
+ </div>
1217
+ </div>
1218
+ <div class="historicalTestList">
1219
+ ${filteredTests.length
1220
+ ? filteredTests.map((test) => `<article class="historicalTestRow">
1187
1221
  <span class="statusDot ${escapeHtml(test.status)}" aria-hidden="true"></span>
1188
1222
  <span>
1189
1223
  <strong>${escapeHtml(test.title)}</strong>
@@ -1196,7 +1230,7 @@
1196
1230
  <small>${escapeHtml(formatDuration(test.duration_ms))}</small>
1197
1231
  </span>
1198
1232
  </article>`).join("")
1199
- : `<div class="emptyState"><strong>Sin resultados</strong><span>Esta corrida no tiene pruebas registradas.</span></div>`}
1233
+ : `<div class="emptyState"><strong>Sin resultados</strong><span>Esta corrida no tiene pruebas registradas.</span></div>`}
1200
1234
  </div>
1201
1235
  </section>`;
1202
1236
  }
@@ -1225,11 +1259,20 @@
1225
1259
  const closeButton = document.querySelector("[data-close-trend-history]");
1226
1260
  if (!closeButton) return;
1227
1261
  closeButton.addEventListener("click", () => {
1228
- state.selectedTrendRunId = null;
1229
- state.historicalRun = null;
1230
- state.historicalRerun = null;
1231
- renderTrendArea();
1232
- });
1262
+ state.selectedTrendRunId = null;
1263
+ state.historicalRun = null;
1264
+ state.historicalRerun = null;
1265
+ state.historicalStatusFilter = "";
1266
+ renderTrendArea();
1267
+ });
1268
+ document.querySelectorAll("[data-historical-status-filter]").forEach((button) => {
1269
+ button.addEventListener("click", () => {
1270
+ const status = button.dataset.historicalStatusFilter;
1271
+ state.historicalStatusFilter =
1272
+ state.historicalStatusFilter === status ? "" : status;
1273
+ renderTrendHistoryPanel();
1274
+ });
1275
+ });
1233
1276
  document.querySelector("[data-rerun-trend-report]")?.addEventListener(
1234
1277
  "click",
1235
1278
  startHistoricalRerun,
@@ -1302,10 +1345,11 @@
1302
1345
  function bindTrendEvents() {
1303
1346
  document.querySelectorAll("[data-trend-run-id]").forEach((button) => {
1304
1347
  button.addEventListener("click", async () => {
1305
- const requestedRunId = button.dataset.trendRunId;
1306
- state.selectedTrendRunId = requestedRunId;
1307
- state.historicalRun = null;
1308
- state.historicalRerun = null;
1348
+ const requestedRunId = button.dataset.trendRunId;
1349
+ state.selectedTrendRunId = requestedRunId;
1350
+ state.historicalRun = null;
1351
+ state.historicalRerun = null;
1352
+ state.historicalStatusFilter = "";
1309
1353
  renderTrendArea();
1310
1354
 
1311
1355
  try {
@@ -1332,9 +1376,10 @@
1332
1376
 
1333
1377
  async function loadTrendEnvironment(environment) {
1334
1378
  state.trendEnvironment = environment;
1335
- state.selectedTrendRunId = null;
1336
- state.historicalRun = null;
1337
- state.historicalRerun = null;
1379
+ state.selectedTrendRunId = null;
1380
+ state.historicalRun = null;
1381
+ state.historicalRerun = null;
1382
+ state.historicalStatusFilter = "";
1338
1383
  const trendContent = document.getElementById("trend-content");
1339
1384
  const trendHistory = document.getElementById("trend-history");
1340
1385
  const trendCounter = document.getElementById("trend-run-count");
package/finalize.cjs CHANGED
@@ -435,7 +435,7 @@ function persistRun(database, run) {
435
435
  system: run.system || {},
436
436
  source: run.source || {},
437
437
  execution: run.execution || null,
438
- reporterVersion: "2.0.0-beta.8",
438
+ reporterVersion: "2.0.0-beta.9",
439
439
  }),
440
440
  ],
441
441
  );
@@ -932,7 +932,7 @@ async function finalizeRun(options = {}) {
932
932
  if (!run) throw new Error(`No se encontró ${rawPath}`);
933
933
 
934
934
  run.schemaVersion = DATABASE_SCHEMA_VERSION;
935
- run.reporterVersion = "2.0.0-beta.8";
935
+ run.reporterVersion = "2.0.0-beta.9";
936
936
  run.lifecycle = run.lifecycle || "completed";
937
937
  run.source = run.source || {
938
938
  branch: process.env.CI_COMMIT_REF_NAME || "",
package/jira.cjs CHANGED
@@ -303,12 +303,12 @@ async function jiraRequest(config, pathname, options = {}) {
303
303
  }
304
304
 
305
305
  async function findMatchingIssues(config, fingerprint) {
306
- const jql = [
306
+ const conditions = [
307
307
  `project = "${config.projectKey}"`,
308
308
  `issuetype = "${config.issueTypeName}"`,
309
309
  `labels = "${fingerprint}"`,
310
- "ORDER BY created DESC",
311
310
  ].join(" AND ");
311
+ const jql = `${conditions} ORDER BY created DESC`;
312
312
  const query = new URLSearchParams({
313
313
  jql,
314
314
  fields: "summary,status,labels",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alejandrojca/elmulo-reporter",
3
- "version": "2.0.0-beta.8",
3
+ "version": "2.0.0-beta.9",
4
4
  "description": "Reporter independiente para ejecuciones de Cypress con dashboard, historial SQLite y evidencias.",
5
5
  "type": "commonjs",
6
6
  "main": "plugin.cjs",
package/plugin.cjs CHANGED
@@ -57,7 +57,7 @@ function registerElmuloReporter(on, config, options = {}) {
57
57
  "unknown",
58
58
  tagExpression: config.env?.TAGS || process.env.CYPRESS_TAGS || "",
59
59
  lifecycle: "running",
60
- reporterVersion: "2.0.0-beta.8",
60
+ reporterVersion: "2.0.0-beta.9",
61
61
  execution: executionScript
62
62
  ? { runner: "npm", script: executionScript, args: executionArgs }
63
63
  : null,
package/server.cjs CHANGED
@@ -182,7 +182,7 @@ async function serveReport(options = {}) {
182
182
  if (request.method === "GET" && url.pathname === "/api/health") {
183
183
  sendJson(response, 200, {
184
184
  ok: true,
185
- version: "2.0.0-beta.8",
185
+ version: "2.0.0-beta.9",
186
186
  schemaVersion: 3,
187
187
  });
188
188
  return;