vease 1.54.0 → 1.55.0-rc.2
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.
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { waitForActionSettled } from "./wait_for_action_settled.js";
|
|
1
2
|
import { navigateToApp } from "./navigate.js";
|
|
2
3
|
import { expect, test as test$1 } from "@playwright/test";
|
|
3
4
|
import { consola } from "consola";
|
|
@@ -66,7 +67,10 @@ const test = test$1.extend({
|
|
|
66
67
|
autoScreenshot: [async ({ app, screenshotMask }, use, testInfo) => {
|
|
67
68
|
await use(void 0);
|
|
68
69
|
const { window } = app;
|
|
69
|
-
if (testInfo.status === testInfo.expectedStatus)
|
|
70
|
+
if (testInfo.status === testInfo.expectedStatus) {
|
|
71
|
+
await waitForActionSettled(window);
|
|
72
|
+
await expect(window).toHaveScreenshot({ mask: [window.getByTestId("accountNavButton"), ...screenshotMask.locators] });
|
|
73
|
+
}
|
|
70
74
|
}, { auto: true }]
|
|
71
75
|
});
|
|
72
76
|
//#endregion
|
|
@@ -4,13 +4,15 @@ function noopCleanup() {}
|
|
|
4
4
|
const BUSY_INDICATOR_TEST_ID = "microservicesBusyIndicator";
|
|
5
5
|
const REQUEST_DISPATCH_GRACE_MS = 50;
|
|
6
6
|
const BUSY_INDICATOR_HARD_CEILING_MS = 1e4;
|
|
7
|
-
async function waitForMicroservicesIdle(window,
|
|
8
|
-
await window.waitForTimeout(Math.min(REQUEST_DISPATCH_GRACE_MS, budgetMs));
|
|
7
|
+
async function waitForMicroservicesIdle(window, timeoutMs) {
|
|
9
8
|
await window.getByTestId(BUSY_INDICATOR_TEST_ID).waitFor({
|
|
10
9
|
state: "hidden",
|
|
11
|
-
timeout:
|
|
10
|
+
timeout: timeoutMs
|
|
12
11
|
}).catch(noopCleanup);
|
|
13
12
|
}
|
|
13
|
+
async function isMicroservicesBusy(window) {
|
|
14
|
+
return await window.getByTestId(BUSY_INDICATOR_TEST_ID).isVisible().catch(() => false);
|
|
15
|
+
}
|
|
14
16
|
const MUTATION_QUIET_MS = 250;
|
|
15
17
|
const MUTATION_POLL_INTERVAL_MS = 20;
|
|
16
18
|
async function waitForDomSettled(window, budgetMs) {
|
|
@@ -45,10 +47,16 @@ async function waitForNextPaint(window) {
|
|
|
45
47
|
});
|
|
46
48
|
}).catch(noopCleanup);
|
|
47
49
|
}
|
|
50
|
+
async function waitForIdleAndSettled(window, startedAt, ceilingMs, deadline) {
|
|
51
|
+
await waitForMicroservicesIdle(window, Math.max(1, deadline - Date.now()));
|
|
52
|
+
await waitForDomSettled(window, Math.max(MIN_DOM_SETTLE_BUDGET_MS, ceilingMs - (Date.now() - startedAt)));
|
|
53
|
+
if (Date.now() < deadline && await isMicroservicesBusy(window)) await waitForIdleAndSettled(window, startedAt, ceilingMs, deadline);
|
|
54
|
+
}
|
|
48
55
|
async function waitForActionSettled(window, ceilingMs = afterActionWait) {
|
|
49
56
|
const startedAt = Date.now();
|
|
50
|
-
|
|
51
|
-
await
|
|
57
|
+
const deadline = startedAt + Math.max(ceilingMs, BUSY_INDICATOR_HARD_CEILING_MS);
|
|
58
|
+
await window.waitForTimeout(Math.min(REQUEST_DISPATCH_GRACE_MS, ceilingMs));
|
|
59
|
+
await waitForIdleAndSettled(window, startedAt, ceilingMs, deadline);
|
|
52
60
|
await waitForNextPaint(window);
|
|
53
61
|
}
|
|
54
62
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vease",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.55.0-rc.2",
|
|
4
4
|
"description": "Desktop and cloud software for data visualization",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Desktop",
|
|
@@ -88,12 +88,12 @@
|
|
|
88
88
|
"@ai-sdk/vue": "4.0.93",
|
|
89
89
|
"@fontsource/michroma": "5.2.6",
|
|
90
90
|
"@fontsource/roboto": "5.2.10",
|
|
91
|
-
"@geode/cloud-api": "
|
|
92
|
-
"@geode/opengeodeweb-back": "
|
|
93
|
-
"@geode/opengeodeweb-front": "
|
|
94
|
-
"@geode/opengeodeweb-viewer": "
|
|
95
|
-
"@geode/vease-back": "
|
|
96
|
-
"@geode/vease-viewer": "
|
|
91
|
+
"@geode/cloud-api": "next",
|
|
92
|
+
"@geode/opengeodeweb-back": "next",
|
|
93
|
+
"@geode/opengeodeweb-front": "next",
|
|
94
|
+
"@geode/opengeodeweb-viewer": "next",
|
|
95
|
+
"@geode/vease-back": "next",
|
|
96
|
+
"@geode/vease-viewer": "next",
|
|
97
97
|
"@nuxtjs/mcp-toolkit": "0.18.1",
|
|
98
98
|
"ai": "7.0.93",
|
|
99
99
|
"bowser": "2.14.1",
|
|
@@ -12,6 +12,7 @@ import { consola } from "consola";
|
|
|
12
12
|
|
|
13
13
|
// Local imports
|
|
14
14
|
import { navigateToApp } from "./navigate";
|
|
15
|
+
import { waitForActionSettled } from "./wait_for_action_settled";
|
|
15
16
|
|
|
16
17
|
const MILLISECONDS_PER_SECOND = 1000;
|
|
17
18
|
|
|
@@ -133,6 +134,8 @@ const test = base.extend<TestFixtures, WorkerFixtures>({
|
|
|
133
134
|
// Read at teardown: the test may have restarted the app
|
|
134
135
|
const { window } = app;
|
|
135
136
|
if (testInfo.status === testInfo.expectedStatus) {
|
|
137
|
+
// Never capture while the microservices are still busy (busy indicator bar, half-rendered viewer)
|
|
138
|
+
await waitForActionSettled(window);
|
|
136
139
|
// The account icon depends on login state (only cloud is logged in)
|
|
137
140
|
await expect(window).toHaveScreenshot({
|
|
138
141
|
mask: [window.getByTestId("accountNavButton"), ...screenshotMask.locators],
|
|
@@ -21,14 +21,21 @@ const REQUEST_DISPATCH_GRACE_MS = 50;
|
|
|
21
21
|
// Some backend RPCs (attribute-name lookups on a component with many items, for example) occasionally take several seconds under CI load, so the indicator gets generous headroom to clear.
|
|
22
22
|
const BUSY_INDICATOR_HARD_CEILING_MS = 10_000;
|
|
23
23
|
|
|
24
|
-
async function waitForMicroservicesIdle(window: Page,
|
|
25
|
-
await window.waitForTimeout(Math.min(REQUEST_DISPATCH_GRACE_MS, budgetMs));
|
|
24
|
+
async function waitForMicroservicesIdle(window: Page, timeoutMs: number): Promise<void> {
|
|
26
25
|
await window
|
|
27
26
|
.getByTestId(BUSY_INDICATOR_TEST_ID)
|
|
28
|
-
.waitFor({ state: "hidden", timeout:
|
|
27
|
+
.waitFor({ state: "hidden", timeout: timeoutMs })
|
|
29
28
|
.catch(noopCleanup);
|
|
30
29
|
}
|
|
31
30
|
|
|
31
|
+
async function isMicroservicesBusy(window: Page): Promise<boolean> {
|
|
32
|
+
const busy = await window
|
|
33
|
+
.getByTestId(BUSY_INDICATOR_TEST_ID)
|
|
34
|
+
.isVisible()
|
|
35
|
+
.catch(() => false);
|
|
36
|
+
return busy;
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
const MUTATION_QUIET_MS = 250;
|
|
33
40
|
const MUTATION_POLL_INTERVAL_MS = 20;
|
|
34
41
|
|
|
@@ -73,11 +80,28 @@ async function waitForNextPaint(window: Page): Promise<void> {
|
|
|
73
80
|
.catch(noopCleanup);
|
|
74
81
|
}
|
|
75
82
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
83
|
+
// Multi-step actions (project import, for example) chain several requests with short gaps in between: the indicator briefly hides, then shows again.
|
|
84
|
+
// The indeterminate bar animates through CSS only, so the DOM can look quiet while it is visible: re-check it after the DOM settles and start over if it came back.
|
|
85
|
+
async function waitForIdleAndSettled(
|
|
86
|
+
window: Page,
|
|
87
|
+
startedAt: number,
|
|
88
|
+
ceilingMs: number,
|
|
89
|
+
deadline: number,
|
|
90
|
+
): Promise<void> {
|
|
91
|
+
// A zero timeout means "no timeout" in Playwright
|
|
92
|
+
await waitForMicroservicesIdle(window, Math.max(1, deadline - Date.now()));
|
|
79
93
|
const remainingMs = Math.max(MIN_DOM_SETTLE_BUDGET_MS, ceilingMs - (Date.now() - startedAt));
|
|
80
94
|
await waitForDomSettled(window, remainingMs);
|
|
95
|
+
if (Date.now() < deadline && (await isMicroservicesBusy(window))) {
|
|
96
|
+
await waitForIdleAndSettled(window, startedAt, ceilingMs, deadline);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function waitForActionSettled(window: Page, ceilingMs = afterActionWait): Promise<void> {
|
|
101
|
+
const startedAt = Date.now();
|
|
102
|
+
const deadline = startedAt + Math.max(ceilingMs, BUSY_INDICATOR_HARD_CEILING_MS);
|
|
103
|
+
await window.waitForTimeout(Math.min(REQUEST_DISPATCH_GRACE_MS, ceilingMs));
|
|
104
|
+
await waitForIdleAndSettled(window, startedAt, ceilingMs, deadline);
|
|
81
105
|
await waitForNextPaint(window);
|
|
82
106
|
}
|
|
83
107
|
|