@zaip-ai/cli 1.3.2 → 1.3.4
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/LICENSE +6 -3
- package/README.md +8 -2
- package/dist/services/codex-pre-tool-hook-template.js +1 -1
- package/dist/services/runtime-template.test.js +9 -8
- package/dist/services/runtime-template.test.js.map +1 -1
- package/package.json +2 -2
- package/templates/.agents/dashboard/.zaip-managed.json +401 -381
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-ovxurkIk.js → CostCalculationGuidePage-BZWjXQB-.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-BXy6nti1.js → DashboardHelpDialog-D8qqeIPE.js} +30 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-vSvvOHQp.js → PricingSettingsDialog-Cm12_A0v.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-zH758vfs.js → ProjectSettingsPage-DxO9F8B4.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CTH2wDs5.js → TraceabilityWorkspace-_x0twOcS.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Cpp8xFij.js +68 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-B_lDDHn1.js → icons-vendor-CR4Fbf3g.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-ncC1rxap.js +2 -0
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-B3TJrD4O.js → react-vendor-DRJQbKnp.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-7f1j5wx3.js → traceability-CMrDYqjR.js} +1 -1
- package/templates/.agents/dashboard/apps/frontend/dist/index.html +5 -5
- package/templates/.agents/dashboard/apps/frontend/src/App.tsx +48 -3
- package/templates/.agents/dashboard/apps/frontend/src/AppRequestGuards.tsx +44 -0
- package/templates/.agents/dashboard/apps/frontend/src/cost-period-refresh.test.ts +92 -1
- package/templates/.agents/dashboard/apps/frontend/src/snapshot-coherence.test.ts +2 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionPanel.test.ts +60 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostDeveloperAttributionPanel.tsx +26 -9
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorNavigation.tsx +189 -0
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.test.ts +122 -2
- package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostMonitorPage.tsx +70 -155
- package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.test.ts +59 -0
- package/templates/.agents/dashboard/modules/operations/src/react/export/ExcelExportAction.tsx +55 -2
- package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +274 -246
- package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +255 -227
- package/templates/.agents/dashboard/packages/local-runtime/server.mjs +263 -235
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-dimension-facts.test.ts +282 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-dimension-facts.ts +71 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-launch-progress.ts +18 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model-stale-seed.ts +50 -0
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.test.ts +9 -6
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-read-model.ts +45 -73
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-snapshot-seed.test.ts +79 -2
- package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-work-item-index.test.ts +56 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.mjs +26 -8
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-launch-page.test.mjs +45 -4
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-lifecycle.mjs +58 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard-lifecycle.test.mjs +81 -0
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.mjs +171 -71
- package/templates/.agents/dashboard/packages/local-runtime/start-dashboard.test.mjs +152 -11
- package/templates/.agents/dashboard/packages/windows-launcher/Program.cs +4 -9
- package/templates/.agents/dashboard/tests/e2e/playwright.config.ts +9 -1
- package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-period-transition.spec.ts +232 -0
- package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +26 -5
- package/templates/.agents/requirements/autonomous-assurance.md +1 -1
- package/templates/.agents/requirements/comparative-audit.md +1 -1
- package/templates/.agents/requirements/context-engine.md +1 -1
- package/templates/.agents/requirements/cost-observability.md +1 -1
- package/templates/.agents/requirements/dashboard-launch.md +1 -1
- package/templates/.agents/requirements/data-export.md +1 -1
- package/templates/.agents/requirements/design-system.md +1 -1
- package/templates/.agents/requirements/execution-engine.md +1 -1
- package/templates/.agents/requirements/kanban-dashboard.md +1 -1
- package/templates/.agents/requirements/model-orchestration.md +1 -1
- package/templates/.agents/requirements/public-distribution.md +1 -1
- package/templates/.agents/requirements/software-architecture.md +1 -1
- package/templates/.agents/requirements/strategic-improvements.md +1 -1
- package/templates/.agents/requirements/work-lifecycle.md +1 -1
- package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
- package/templates/.agents/runtime/bin/zaip.mjs +7 -7
- package/templates/.agents/runtime/checksums.sha256 +3 -3
- package/templates/.agents/runtime/provenance.json +3 -3
- package/templates/.agents/runtime/sbom.cdx.json +1595 -3491
- package/templates/.agents/skills/zaip-skill-lifecycle-closure/SKILL.md +16 -0
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.mjs +19 -20
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-local-telemetry.test.mjs +143 -0
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-lib.mjs +14 -0
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.mjs +3 -1
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/reconcile-telemetry-remeasurement.test.mjs +21 -0
- package/templates/.agents/skills/zaip-skill-reconcile-telemetry/scripts/source-size-telemetry/model-identity-reconciliation.mjs +3 -0
- package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +29 -0
- package/templates/template-manifest.json +536 -932
- package/templates/.agents/dashboard/.tmp-run-list.json +0 -254
- package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-DKwP1stV.js +0 -68
- package/templates/.agents/dashboard/apps/frontend/dist/assets/index-CuMcoUni.js +0 -2
|
@@ -79,14 +79,10 @@ namespace ZaipDashboardLauncher
|
|
|
79
79
|
|
|
80
80
|
private static int WaitForLaunchStatus(Process process, string statusPath, string workspaceRoot)
|
|
81
81
|
{
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
// the current leader, then still needs the bounded 180 s server
|
|
87
|
-
// startup window if that leader fails before publishing a session.
|
|
88
|
-
var deadline = DateTime.UtcNow.AddSeconds(420);
|
|
89
|
-
while (DateTime.UtcNow < deadline)
|
|
82
|
+
// A cold local read model has no useful fixed wall-clock limit.
|
|
83
|
+
// Keep this wrapper attached until Node publishes a real result;
|
|
84
|
+
// errors still surface when Node reports them or exits.
|
|
85
|
+
while (true)
|
|
90
86
|
{
|
|
91
87
|
try
|
|
92
88
|
{
|
|
@@ -125,7 +121,6 @@ namespace ZaipDashboardLauncher
|
|
|
125
121
|
}
|
|
126
122
|
Thread.Sleep(100);
|
|
127
123
|
}
|
|
128
|
-
return ReportFailure("O dashboard ainda não publicou uma sessão pronta após 420 segundos. O processo local pode continuar registrando detalhes no log temporário; feche esta mensagem e tente novamente após verificar o log.");
|
|
129
124
|
}
|
|
130
125
|
|
|
131
126
|
private static bool VerifyDashboardLaunch(string workspaceRoot, out string error)
|
|
@@ -11,6 +11,9 @@ export default defineConfig({
|
|
|
11
11
|
timeout: 180_000,
|
|
12
12
|
expect: { timeout: 60_000 },
|
|
13
13
|
retries: process.env.CI ? 1 : 0,
|
|
14
|
+
// CI shares one SQLite runtime across the suite. Serializing browser workers
|
|
15
|
+
// keeps the measured latency gates independent from runner CPU count.
|
|
16
|
+
...(process.env.CI ? { workers: 1 } : {}),
|
|
14
17
|
reporter: process.env.CI ? "github" : "list",
|
|
15
18
|
use: {
|
|
16
19
|
baseURL: `${baseURL}/?token=e2e-token`,
|
|
@@ -22,7 +25,10 @@ export default defineConfig({
|
|
|
22
25
|
cwd: workspaceRoot,
|
|
23
26
|
url: `${baseURL}/api/v2/health`,
|
|
24
27
|
reuseExistingServer: false,
|
|
25
|
-
|
|
28
|
+
// The canonical SQLite rebuild is intentionally unbounded in production.
|
|
29
|
+
// Keep only a broad test-runner safety envelope so a large clean workspace
|
|
30
|
+
// is not killed by the former two-minute startup deadline.
|
|
31
|
+
timeout: 3_000_000,
|
|
26
32
|
env: {
|
|
27
33
|
ZAIP_UI_TOKEN: "e2e-token",
|
|
28
34
|
ZAIP_SERVER_PORT: serverPort,
|
|
@@ -31,6 +37,8 @@ export default defineConfig({
|
|
|
31
37
|
// Match the Windows launcher: first paint uses the latest validated
|
|
32
38
|
// immutable seed while canonical refresh work stays off the served path.
|
|
33
39
|
ZAIP_SQLITE_PREFER_LAST_VALID_SEED: "1",
|
|
40
|
+
// Keep pre-bind progress observable if the web-server health gate times out.
|
|
41
|
+
ZAIP_DASHBOARD_LAUNCH_PROGRESS: "1",
|
|
34
42
|
},
|
|
35
43
|
},
|
|
36
44
|
});
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { expect, type Page, test } from "@playwright/test";
|
|
2
|
+
|
|
3
|
+
const COST_MONITOR_PATH = "/api/v2/cost-monitor";
|
|
4
|
+
const STALE_SUMMARY_SENTINEL = "SENTINELA-RESPOSTA-ANTIGA";
|
|
5
|
+
const fiveSeconds = 5_000;
|
|
6
|
+
|
|
7
|
+
test("refreshes Cost Monitor within five seconds when Todo o Período is selected in place", async ({
|
|
8
|
+
page,
|
|
9
|
+
}) => {
|
|
10
|
+
await openDashboard(page);
|
|
11
|
+
await openCostMonitor(page);
|
|
12
|
+
|
|
13
|
+
const summary = waitForCostSummary(page, "all");
|
|
14
|
+
const startedAt = Date.now();
|
|
15
|
+
await periodControl(page).selectOption("all");
|
|
16
|
+
await summary;
|
|
17
|
+
await expectCostMonitorReady(page);
|
|
18
|
+
|
|
19
|
+
expect(Date.now() - startedAt).toBeLessThan(fiveSeconds);
|
|
20
|
+
await expect(periodControl(page)).toHaveValue("all");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("loads Cost Monitor within five seconds when the user enters after choosing Todo o Período", async ({
|
|
24
|
+
page,
|
|
25
|
+
}) => {
|
|
26
|
+
await openDashboard(page);
|
|
27
|
+
|
|
28
|
+
await periodControl(page).selectOption("all");
|
|
29
|
+
await expect(page.locator(".period-transition-overlay")).toHaveCount(0, { timeout: 60_000 });
|
|
30
|
+
|
|
31
|
+
const summary = waitForCostSummary(page, "all");
|
|
32
|
+
const startedAt = Date.now();
|
|
33
|
+
await page
|
|
34
|
+
.getByRole("navigation", { name: "Áreas do dashboard" })
|
|
35
|
+
.getByRole("button", { name: /Monitor de Custos/ })
|
|
36
|
+
.click();
|
|
37
|
+
await summary;
|
|
38
|
+
await expectCostMonitorReady(page);
|
|
39
|
+
|
|
40
|
+
expect(Date.now() - startedAt).toBeLessThan(fiveSeconds);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("shows one retry after a summary 503 and does not retry in a loop", async ({ page }) => {
|
|
44
|
+
let summaries = 0;
|
|
45
|
+
await page.route("**/api/v2/cost-monitor**", async (route) => {
|
|
46
|
+
const url = new URL(route.request().url());
|
|
47
|
+
if (url.searchParams.get("scope") !== "summary") return route.continue();
|
|
48
|
+
summaries += 1;
|
|
49
|
+
if (summaries === 1)
|
|
50
|
+
return route.fulfill({
|
|
51
|
+
status: 503,
|
|
52
|
+
contentType: "application/json",
|
|
53
|
+
body: JSON.stringify({ error: "summary temporarily unavailable" }),
|
|
54
|
+
});
|
|
55
|
+
return route.continue();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
await openDashboard(page);
|
|
59
|
+
await openCostMonitor(page, false);
|
|
60
|
+
const retry = page.locator(".app-loading-panel-error").getByRole("button", {
|
|
61
|
+
name: "Tentar novamente",
|
|
62
|
+
});
|
|
63
|
+
await expect(retry).toBeVisible();
|
|
64
|
+
|
|
65
|
+
await retry.click();
|
|
66
|
+
await expectCostMonitorReady(page);
|
|
67
|
+
await page.waitForTimeout(750);
|
|
68
|
+
expect(summaries).toBe(2);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("does not let a late all-time summary overwrite the newer period", async ({ page }) => {
|
|
72
|
+
let priorPeriodBody: string | null = null;
|
|
73
|
+
page.on("response", (response) => {
|
|
74
|
+
const url = new URL(response.url());
|
|
75
|
+
if (
|
|
76
|
+
response.status() === 200 &&
|
|
77
|
+
url.pathname === COST_MONITOR_PATH &&
|
|
78
|
+
url.searchParams.get("scope") === "summary" &&
|
|
79
|
+
url.searchParams.has("from")
|
|
80
|
+
)
|
|
81
|
+
void response.text().then((body) => {
|
|
82
|
+
priorPeriodBody ??= body;
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
await page.addInitScript(() => {
|
|
86
|
+
type Probe = {
|
|
87
|
+
allTimeBody: string | null;
|
|
88
|
+
allTimeFinished: boolean;
|
|
89
|
+
allTimeStarted: boolean;
|
|
90
|
+
releaseAllTime: null | (() => Promise<void>);
|
|
91
|
+
};
|
|
92
|
+
const probe: Probe = {
|
|
93
|
+
allTimeBody: null,
|
|
94
|
+
allTimeFinished: false,
|
|
95
|
+
allTimeStarted: false,
|
|
96
|
+
releaseAllTime: null,
|
|
97
|
+
};
|
|
98
|
+
const originalFetch = window.fetch.bind(window);
|
|
99
|
+
Object.assign(window, { __costSummaryStaleProbe: probe });
|
|
100
|
+
window.fetch = (input, init) => {
|
|
101
|
+
const url = new URL(
|
|
102
|
+
typeof input === "string" ? input : input instanceof URL ? input.href : input.url,
|
|
103
|
+
window.location.href,
|
|
104
|
+
);
|
|
105
|
+
const isAllTimeSummary =
|
|
106
|
+
url.pathname === "/api/v2/cost-monitor" &&
|
|
107
|
+
url.searchParams.get("scope") === "summary" &&
|
|
108
|
+
!url.searchParams.has("from") &&
|
|
109
|
+
!url.searchParams.has("to");
|
|
110
|
+
if (!isAllTimeSummary || probe.allTimeStarted) return originalFetch(input, init);
|
|
111
|
+
|
|
112
|
+
probe.allTimeStarted = true;
|
|
113
|
+
return new Promise<Response>((resolve, reject) => {
|
|
114
|
+
probe.releaseAllTime = async () => {
|
|
115
|
+
try {
|
|
116
|
+
resolve(
|
|
117
|
+
new Response(probe.allTimeBody, {
|
|
118
|
+
headers: { "content-type": "application/json" },
|
|
119
|
+
status: 200,
|
|
120
|
+
}),
|
|
121
|
+
);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
reject(error);
|
|
124
|
+
} finally {
|
|
125
|
+
probe.allTimeFinished = true;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
await openDashboard(page);
|
|
133
|
+
await openCostMonitor(page);
|
|
134
|
+
await expect.poll(() => priorPeriodBody).not.toBeNull();
|
|
135
|
+
const staleBody = withStaleSummarySentinel(priorPeriodBody!);
|
|
136
|
+
await page.evaluate((body) => {
|
|
137
|
+
window.__costSummaryStaleProbe.allTimeBody = body;
|
|
138
|
+
}, staleBody);
|
|
139
|
+
|
|
140
|
+
await periodControl(page).selectOption("all");
|
|
141
|
+
await expect
|
|
142
|
+
.poll(() => page.evaluate(() => window.__costSummaryStaleProbe.allTimeStarted))
|
|
143
|
+
.toBe(true);
|
|
144
|
+
|
|
145
|
+
const latestSummary = waitForCostSummary(page, "this-month");
|
|
146
|
+
await periodControl(page).selectOption("this-month");
|
|
147
|
+
await page.evaluate(async () => await window.__costSummaryStaleProbe.releaseAllTime?.());
|
|
148
|
+
await expect
|
|
149
|
+
.poll(() => page.evaluate(() => window.__costSummaryStaleProbe.allTimeFinished))
|
|
150
|
+
.toBe(true);
|
|
151
|
+
await latestSummary;
|
|
152
|
+
await expect(periodControl(page)).toHaveValue("this-month");
|
|
153
|
+
await expectCostMonitorReady(page);
|
|
154
|
+
await expect(page.locator(".cost-content-daily")).not.toContainText(STALE_SUMMARY_SENTINEL);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
declare global {
|
|
158
|
+
interface Window {
|
|
159
|
+
__costSummaryStaleProbe: {
|
|
160
|
+
allTimeBody: string | null;
|
|
161
|
+
allTimeFinished: boolean;
|
|
162
|
+
allTimeStarted: boolean;
|
|
163
|
+
releaseAllTime: null | (() => Promise<void>);
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function openDashboard(page: Page) {
|
|
169
|
+
const operations = page.waitForResponse(
|
|
170
|
+
(response) =>
|
|
171
|
+
response.status() === 200 &&
|
|
172
|
+
response.url().includes("/api/v2/operations") &&
|
|
173
|
+
response.url().includes("scope=quick"),
|
|
174
|
+
);
|
|
175
|
+
await page.goto("/?token=e2e-token");
|
|
176
|
+
await operations;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function openCostMonitor(page: Page, expectReady = true) {
|
|
180
|
+
const summary = expectReady ? waitForCostSummary(page, "this-month") : null;
|
|
181
|
+
await page
|
|
182
|
+
.getByRole("navigation", { name: "Áreas do dashboard" })
|
|
183
|
+
.getByRole("button", { name: /Monitor de Custos/ })
|
|
184
|
+
.click();
|
|
185
|
+
if (!expectReady) return;
|
|
186
|
+
await summary;
|
|
187
|
+
await expectCostMonitorReady(page);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function periodControl(page: Page) {
|
|
191
|
+
return page.locator(".dashboard-global-controls").getByRole("combobox", { name: "Período" });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function waitForCostSummary(page: Page, preset: "all" | "this-month") {
|
|
195
|
+
return page.waitForResponse((response) => {
|
|
196
|
+
if (response.status() !== 200) return false;
|
|
197
|
+
const url = new URL(response.url());
|
|
198
|
+
if (url.pathname !== COST_MONITOR_PATH || url.searchParams.get("scope") !== "summary")
|
|
199
|
+
return false;
|
|
200
|
+
return preset === "all"
|
|
201
|
+
? !url.searchParams.has("from") && !url.searchParams.has("to")
|
|
202
|
+
: url.searchParams.has("from") && url.searchParams.has("to");
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function expectCostMonitorReady(page: Page) {
|
|
207
|
+
await expect(page.locator(".cost-tabs")).toBeVisible({ timeout: fiveSeconds });
|
|
208
|
+
const dailyTable = page.locator(".cost-content-daily table");
|
|
209
|
+
await expect(dailyTable).toBeVisible({ timeout: fiveSeconds });
|
|
210
|
+
await expect(dailyTable).not.toHaveAttribute("aria-busy", "true");
|
|
211
|
+
await expect(page.locator(".cost-content-daily tbody tr").first()).toBeVisible({
|
|
212
|
+
timeout: fiveSeconds,
|
|
213
|
+
});
|
|
214
|
+
await expect(page.locator(".app-loading-panel[aria-busy='true']")).toHaveCount(0);
|
|
215
|
+
await expect(page.locator(".period-transition-overlay")).toHaveCount(0);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function withStaleSummarySentinel(body: string) {
|
|
219
|
+
const projection = JSON.parse(body) as {
|
|
220
|
+
groups?: { daily?: Array<{ key: string; label: string }> };
|
|
221
|
+
};
|
|
222
|
+
const firstDailyRow = projection.groups?.daily?.[0];
|
|
223
|
+
if (firstDailyRow === undefined)
|
|
224
|
+
throw new Error(
|
|
225
|
+
"The Cost summary fixture must contain a daily row for stale-response detection.",
|
|
226
|
+
);
|
|
227
|
+
projection.groups!.daily = [
|
|
228
|
+
{ ...firstDailyRow, key: STALE_SUMMARY_SENTINEL, label: STALE_SUMMARY_SENTINEL },
|
|
229
|
+
...projection.groups!.daily!,
|
|
230
|
+
];
|
|
231
|
+
return JSON.stringify(projection);
|
|
232
|
+
}
|
|
@@ -164,6 +164,9 @@ test("exports Kanban, Lista and Cost Monitor through a save picker while the das
|
|
|
164
164
|
|
|
165
165
|
test("renders the modern read-only dashboard and meets accessibility gates", async ({ page }) => {
|
|
166
166
|
await openDashboard(page);
|
|
167
|
+
const globalPeriod = page.getByRole("combobox", { name: /Per/ }).first();
|
|
168
|
+
await expect(globalPeriod).toHaveValue("this-month");
|
|
169
|
+
await selectPopulatedAllTime(page);
|
|
167
170
|
await expect(page.getByRole("navigation", { name: "Dashboard Zaip AIOS menu" })).toBeVisible();
|
|
168
171
|
const capabilityNav = page.getByRole("navigation", { name: "Áreas do dashboard" });
|
|
169
172
|
const globalControls = page.locator(".dashboard-global-controls");
|
|
@@ -233,7 +236,7 @@ test("renders the modern read-only dashboard and meets accessibility gates", asy
|
|
|
233
236
|
const period = globalControls.getByRole("combobox", { name: "Período" });
|
|
234
237
|
await expect(startDate).toHaveValue(/^\d{4}-\d{2}-\d{2}$/);
|
|
235
238
|
await expect(endDate).toHaveValue(/^\d{4}-\d{2}-\d{2}$/);
|
|
236
|
-
await expect(period).toHaveValue("
|
|
239
|
+
await expect(period).toHaveValue("all");
|
|
237
240
|
const initialStart = await startDate.inputValue();
|
|
238
241
|
const today = await endDate.inputValue();
|
|
239
242
|
await expect(page.getByLabel("Data inicial nos filtros")).toHaveValue(initialStart);
|
|
@@ -274,6 +277,7 @@ test("loads a quick Operations summary without full snapshot hydration", async (
|
|
|
274
277
|
});
|
|
275
278
|
|
|
276
279
|
await openDashboard(page);
|
|
280
|
+
await selectPopulatedAllTime(page);
|
|
277
281
|
expect(operationsRequests.some((url) => url.includes("scope=quick"))).toBe(true);
|
|
278
282
|
expect(operationsRequests.some((url) => url.includes("scope=detail"))).toBe(false);
|
|
279
283
|
expect(operationsRequests.some((url) => url.includes("/snapshot"))).toBe(false);
|
|
@@ -403,6 +407,7 @@ test("embeds traceability and keeps global settings and help available", async (
|
|
|
403
407
|
|
|
404
408
|
test("groups macro work in All and reveals descendant Tasks only in Tasks", async ({ page }) => {
|
|
405
409
|
await openDashboard(page);
|
|
410
|
+
await selectPopulatedAllTime(page);
|
|
406
411
|
await selectOperations(page);
|
|
407
412
|
const board = page.getByLabel("Kanban de trabalho");
|
|
408
413
|
await expect(board.locator(".operations-task-card").first()).toBeVisible();
|
|
@@ -448,10 +453,12 @@ test("groups macro work in All and reveals descendant Tasks only in Tasks", asyn
|
|
|
448
453
|
}
|
|
449
454
|
});
|
|
450
455
|
|
|
451
|
-
test("shows the observed
|
|
456
|
+
test("shows the observed all-time macro duration consistently for SPEC and PRD", async ({
|
|
452
457
|
page,
|
|
453
458
|
}) => {
|
|
454
|
-
|
|
459
|
+
await openDashboard(page);
|
|
460
|
+
await expect(page.getByRole("combobox", { name: /Per/ }).first()).toHaveValue("this-month");
|
|
461
|
+
const response = await selectPopulatedAllTime(page);
|
|
455
462
|
const projection = (await response.json()) as {
|
|
456
463
|
summary: {
|
|
457
464
|
overview_metrics?: {
|
|
@@ -466,8 +473,6 @@ test("shows the observed default-month macro duration consistently for SPEC and
|
|
|
466
473
|
};
|
|
467
474
|
};
|
|
468
475
|
};
|
|
469
|
-
await expect(page.getByRole("combobox", { name: /Per/ }).first()).toHaveValue("this-month");
|
|
470
|
-
|
|
471
476
|
for (const kind of ["SPECs", "PRDs"] as const) {
|
|
472
477
|
const macroKey = kind === "SPECs" ? "specs" : "prds";
|
|
473
478
|
const macroMetric = projection.summary.overview_metrics?.macro_metrics?.[macroKey];
|
|
@@ -654,6 +659,22 @@ async function openDashboard(page: import("@playwright/test").Page) {
|
|
|
654
659
|
return operations;
|
|
655
660
|
}
|
|
656
661
|
|
|
662
|
+
async function selectPopulatedAllTime(page: import("@playwright/test").Page) {
|
|
663
|
+
const response = page.waitForResponse((candidate) => {
|
|
664
|
+
if (
|
|
665
|
+
candidate.status() !== 200 ||
|
|
666
|
+
!candidate.url().includes("/api/v2/operations") ||
|
|
667
|
+
!candidate.url().includes("scope=quick")
|
|
668
|
+
) {
|
|
669
|
+
return false;
|
|
670
|
+
}
|
|
671
|
+
const url = new URL(candidate.url());
|
|
672
|
+
return !url.searchParams.has("from") && !url.searchParams.has("to");
|
|
673
|
+
});
|
|
674
|
+
await page.getByRole("combobox", { name: /Per/ }).first().selectOption("all");
|
|
675
|
+
return response;
|
|
676
|
+
}
|
|
677
|
+
|
|
657
678
|
async function selectOperations(page: import("@playwright/test").Page) {
|
|
658
679
|
await page
|
|
659
680
|
.getByRole("navigation", { name: "Áreas do dashboard" })
|