Skip to content

Commit 8c4c54f

Browse files
committed
Merge branch 'next' of https://github.com/Geode-solutions/OpenGeodeWeb-Front into feat/object-tree-tests
2 parents 81955a6 + 7ea9a53 commit 8c4c54f

3 files changed

Lines changed: 134 additions & 66 deletions

File tree

app/components/Viewer/ObjectTree/Base/Controls.vue

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ watch(
3232
:class="{ 'is-expanded': showSearch }"
3333
>
3434
<ActionButton
35-
data-testid="searchObjectsButton"
3635
:tooltip="showSearch ? 'Hide search' : 'Show search'"
3736
icon="mdi-magnify"
3837
tooltipLocation="bottom"
@@ -44,7 +43,6 @@ watch(
4443
<v-expand-x-transition>
4544
<div v-if="showSearch" class="flex-grow-1 ms-1 text-no-wrap overflow-hidden">
4645
<SearchBar
47-
data-testid="searchObjectsInput"
4846
:model-value="search"
4947
placeholder="Search objects..."
5048
color="black"
@@ -69,7 +67,6 @@ watch(
6967
/>
7068
</v-fade-transition>
7169
<ActionButton
72-
data-testid="sortObjectsButton"
7370
:tooltip="'Sort by ' + (sortType === 'name' ? 'ID' : 'Name')"
7471
:icon="
7572
sortType === 'name' ? 'mdi-sort-alphabetical-ascending' : 'mdi-sort-numeric-ascending'
@@ -82,7 +79,6 @@ watch(
8279
<v-menu :close-on-content-click="false">
8380
<template #activator="{ props: menuProps }">
8481
<ActionButton
85-
data-testid="filterObjectsButton"
8682
tooltip="Filter options"
8783
icon="mdi-filter-variant"
8884
variant="text"
@@ -98,30 +94,18 @@ watch(
9894
:label="category_id"
9995
hide-details
10096
density="compact"
101-
:data-testid="'filterCheckbox-' + category_id"
10297
/>
10398
</v-list-item>
10499
</v-list>
105100
</v-menu>
106101
<ActionButton
107-
v-if="!isCollapsed"
108-
data-testid="collapseAllObjectsButton"
109-
tooltip="Collapse All"
110-
icon="mdi-collapse-all-outline"
102+
data-testid="CollapseOrExpandAll"
103+
:tooltip="isCollapsed ? 'Expand All' : 'Collapse All'"
104+
:icon="isCollapsed ? 'mdi-expand-all-outline' : 'mdi-collapse-all-outline'"
111105
variant="text"
112106
color="black"
113107
tooltipLocation="bottom"
114-
@click="emit('collapse-all')"
115-
/>
116-
<ActionButton
117-
v-else
118-
data-testid="expandAllObjectsButton"
119-
tooltip="Expand All"
120-
icon="mdi-expand-all-outline"
121-
variant="text"
122-
color="black"
123-
tooltipLocation="bottom"
124-
@click="emit('expand-all')"
108+
@click="isCollapsed ? emit('expand-all') : emit('collapse-all')"
125109
/>
126110
</div>
127111
</div>

app/utils/local/microservices.js

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,18 @@ import path from "node:path";
55

66
// Third party imports
77
import back_schemas from "@geode/opengeodeweb-back/opengeodeweb_back_schemas.json" with { type: "json" };
8-
import { getPort } from "get-port-please";
9-
import pTimeout from "p-timeout";
108

119
// Local imports
12-
import { commandExistsSync, waitForReady } from "./scripts.js";
10+
import { commandExistsSync, getAvailablePort, waitForReady } from "./scripts.js";
1311
import { microservicesMetadatasPath, projectMicroservices } from "./cleanup.js";
1412
import { executablePath } from "./path.js";
1513

16-
const DEFAULT_TIMEOUT_SECONDS = 120;
1714
const MILLISECONDS_PER_SECOND = 1000;
15+
const DEFAULT_TIMEOUT_SECONDS = 30;
1816

19-
function getAvailablePort() {
20-
return getPort({
21-
host: "localhost",
22-
random: true,
23-
});
17+
function resolveCommand(execPath, execName) {
18+
const command = commandExistsSync(execName) ? execName : executablePath(execPath, execName);
19+
return command;
2420
}
2521

2622
async function runScript(
@@ -30,33 +26,31 @@ async function runScript(
3026
expectedResponse,
3127
timeoutSeconds = DEFAULT_TIMEOUT_SECONDS,
3228
) {
33-
let command = "";
34-
if (commandExistsSync(execName)) {
35-
command = execName;
36-
} else {
37-
command = path.join(executablePath(execPath, execName));
38-
}
29+
const command = resolveCommand(execPath, execName);
3930
console.log("runScript", command, args);
4031

41-
const child = child_process.spawn(process.platform === "win32" ? command : `"${command}"`, args, {
42-
encoding: "utf8",
43-
shell: true,
32+
const child = child_process.spawn(command, args, {
33+
stdio: ["ignore", "pipe", "pipe"],
4434
});
45-
child.stdout.on("data", (data) => console.log(`[${execName}] ${data.toString()}`));
46-
child.stderr.on("data", (data) => console.log(`[${execName}] ${data.toString()}`));
4735

48-
child.on("close", (code) => console.log(`[${execName}] exited with code ${code}`));
49-
child.on("kill", () => {
50-
console.log(`[${execName}] process killed`);
51-
});
5236
child.name = command.replace(/^.*[\\/]/u, "");
5337

38+
child.on("spawn", () => {
39+
console.log(`[${child.name}] spawned, pid=${child.pid}`);
40+
});
41+
42+
const controller = new AbortController();
43+
const timer = setTimeout(() => controller.abort(), timeoutSeconds * MILLISECONDS_PER_SECOND);
44+
if (typeof timer.unref === "function") {
45+
timer.unref();
46+
}
47+
5448
try {
55-
return await pTimeout(waitForReady(child, expectedResponse), {
56-
milliseconds: timeoutSeconds * MILLISECONDS_PER_SECOND,
57-
message: `Timed out after ${timeoutSeconds} seconds`,
58-
});
49+
const result = await waitForReady(child, expectedResponse, controller.signal);
50+
clearTimeout(timer);
51+
return result;
5952
} catch (error) {
53+
clearTimeout(timer);
6054
child.kill();
6155
throw error;
6256
}
@@ -73,11 +67,16 @@ async function runBack(execName, execPath, args = {}) {
7367
}
7468
const port = await getAvailablePort();
7569
const backArgs = [
76-
`--port ${port}`,
77-
`--data_folder_path ${projectFolderPath}`,
78-
`--upload_folder_path ${uploadFolderPath}`,
79-
`--allowed_origin http://localhost:*`,
80-
`--timeout ${0}`,
70+
"--port",
71+
String(port),
72+
"--data_folder_path",
73+
projectFolderPath,
74+
"--upload_folder_path",
75+
uploadFolderPath,
76+
"--allowed_origin",
77+
"http://localhost:*",
78+
"--timeout",
79+
"0",
8180
];
8281
if (process.env.NODE_ENV === "development" || !process.env.NODE_ENV) {
8382
backArgs.push("--debug");
@@ -94,9 +93,12 @@ async function runViewer(execName, execPath, args = {}) {
9493
}
9594
const port = await getAvailablePort();
9695
const viewerArgs = [
97-
`--port ${port}`,
98-
`--data_folder_path ${projectFolderPath}`,
99-
`--timeout ${0}`,
96+
"--port",
97+
String(port),
98+
"--data_folder_path",
99+
projectFolderPath,
100+
"--timeout",
101+
"0",
100102
];
101103
console.log("runViewer", execPath, execName, viewerArgs);
102104
await runScript(execPath, execName, viewerArgs, "Starting factory");
@@ -121,4 +123,4 @@ function addMicroserviceMetadatas(projectFolderPath, serviceObj) {
121123
);
122124
}
123125

124-
export { addMicroserviceMetadatas, getAvailablePort, runBack, runViewer };
126+
export { addMicroserviceMetadatas, runBack, runViewer };

app/utils/local/scripts.js

Lines changed: 91 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,100 @@ import child_process from "node:child_process";
33
import fs from "node:fs";
44
import { on } from "node:events";
55
import path from "node:path";
6+
import readline from "node:readline";
67

8+
// Third party imports
9+
import { getPort } from "get-port-please";
10+
11+
// Local imports
712
import { appMode } from "./app_mode.js";
813

14+
const BYTES_PER_KIBIBYTE = 1024;
15+
const MAX_ERROR_BUFFER_KIBIBYTES = 64;
16+
const MAX_ERROR_BUFFER_BYTES = MAX_ERROR_BUFFER_KIBIBYTES * BYTES_PER_KIBIBYTE;
17+
18+
function getAvailablePort() {
19+
return getPort({
20+
host: "localhost",
21+
random: true,
22+
});
23+
}
24+
925
function commandExistsSync(execName) {
1026
const envPath = process.env.PATH || "";
11-
return envPath.split(path.delimiter).some((dir) => {
12-
const filePath = path.join(dir, execName);
27+
return envPath.split(path.delimiter).some((directory) => {
28+
const filePath = path.join(directory, execName);
1329
return fs.existsSync(filePath) && fs.statSync(filePath).isFile();
1430
});
1531
}
1632

17-
async function waitForReady(child, expectedResponse) {
18-
for await (const [data] of on(child.stdout, "data")) {
19-
if (data.toString().includes(expectedResponse)) {
20-
return child;
33+
function waitForReady(child, expectedResponse, signal) {
34+
// oxlint-disable-next-line promise/avoid-new
35+
return new Promise((resolve, reject) => {
36+
const readlineStdout = readline.createInterface({ input: child.stdout });
37+
const readlineStderr = readline.createInterface({ input: child.stderr });
38+
39+
let recentOutput = "";
40+
function recordOutput(line) {
41+
recentOutput = `${recentOutput} ${line} \n`.slice(-MAX_ERROR_BUFFER_BYTES);
2142
}
22-
}
23-
throw new Error("Process closed before signal");
43+
44+
function cleanup() {
45+
readlineStdout.removeAllListeners();
46+
readlineStdout.close();
47+
readlineStderr.removeAllListeners();
48+
readlineStderr.close();
49+
child.removeListener("error", onError);
50+
child.removeListener("close", onClose);
51+
if (signal) {
52+
signal.removeEventListener("abort", onAbort);
53+
}
54+
}
55+
56+
function onLine(line) {
57+
console.log(`[${child.name}] ${line}`);
58+
recordOutput(line);
59+
if (line.includes(expectedResponse)) {
60+
cleanup();
61+
resolve(child);
62+
}
63+
}
64+
65+
function onErrLine(line) {
66+
console.log(`[${child.name}] ${line}`);
67+
recordOutput(line);
68+
}
69+
70+
function onError(err) {
71+
cleanup();
72+
reject(err);
73+
}
74+
75+
function onClose(code) {
76+
console.log(`[${child.name}] exited with code ${code}`);
77+
cleanup();
78+
reject(
79+
new Error(
80+
`[${child.name}] exited with code ${code} before becoming ready.${
81+
recentOutput ? `\nRecent output:\n${recentOutput}` : ""
82+
}`,
83+
),
84+
);
85+
}
86+
87+
function onAbort() {
88+
cleanup();
89+
reject(new Error(`[${child.name}] timed out waiting for "${expectedResponse}"`));
90+
}
91+
92+
readlineStdout.on("line", onLine);
93+
readlineStderr.on("line", onErrLine);
94+
child.once("error", onError);
95+
child.once("close", onClose);
96+
if (signal) {
97+
signal.addEventListener("abort", onAbort, { once: true });
98+
}
99+
});
24100
}
25101

26102
async function waitNuxt(nuxtProcess) {
@@ -49,11 +125,17 @@ async function waitNuxt(nuxtProcess) {
49125
async function runBrowser(scriptName) {
50126
process.env.MODE = appMode.BROWSER;
51127

128+
const port = await getAvailablePort();
129+
52130
const nuxtProcess = child_process.spawn("npm", ["run", scriptName], {
53131
shell: true,
54132
FORCE_COLOR: true,
133+
env: {
134+
...process.env,
135+
PORT: port,
136+
},
55137
});
56138
return await waitNuxt(nuxtProcess);
57139
}
58140

59-
export { runBrowser, waitForReady, commandExistsSync };
141+
export { commandExistsSync, getAvailablePort, runBrowser, waitForReady };

0 commit comments

Comments
 (0)