pinokiod 7.5.38 → 7.5.40
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/package.json +1 -1
- package/server/views/app.ejs +19 -0
- package/server/views/index.ejs +31 -2
- package/server/views/partials/home_action_modal.ejs +18 -0
- package/server/views/partials/home_run_menu.ejs +7 -4
- package/test/launch-settings-ui.test.js +145 -0
- package/test/shell-conda-runtime-guard.test.js +75 -0
package/package.json
CHANGED
package/server/views/app.ejs
CHANGED
|
@@ -16183,10 +16183,20 @@ const rerenderMenuSection = (container, html) => {
|
|
|
16183
16183
|
}
|
|
16184
16184
|
|
|
16185
16185
|
const desktopPeekMq = window.matchMedia("(min-width: 769px) and (hover: hover) and (pointer: fine)")
|
|
16186
|
+
const shouldAutoPeekOnIdle = <%- JSON.stringify(type === "run") %>
|
|
16186
16187
|
let peekCloseTimer = 0
|
|
16187
16188
|
|
|
16188
16189
|
const isCollapsed = () => appcanvas.classList.contains("sidebar-collapsed")
|
|
16189
16190
|
const isPeeking = () => appcanvas.classList.contains("sidebar-peeking")
|
|
16191
|
+
const hasVisibleBrowserSurface = () => {
|
|
16192
|
+
const browserview = document.querySelector("main.browserview")
|
|
16193
|
+
if (!browserview) return false
|
|
16194
|
+
if (browserview.querySelector("iframe:not(.hidden)")) return true
|
|
16195
|
+
const networkStatus = document.getElementById("browserview-network-status")
|
|
16196
|
+
if (networkStatus && !networkStatus.hidden) return true
|
|
16197
|
+
const launchStatus = document.querySelector("[data-launch-requirements-status]")
|
|
16198
|
+
return Boolean(launchStatus && !launchStatus.hidden)
|
|
16199
|
+
}
|
|
16190
16200
|
const syncSidebarVisibility = () => {
|
|
16191
16201
|
const peeking = isPeeking()
|
|
16192
16202
|
aside.setAttribute("aria-hidden", isCollapsed() && !peeking ? "true" : "false")
|
|
@@ -16243,6 +16253,11 @@ const rerenderMenuSection = (container, html) => {
|
|
|
16243
16253
|
setCollapsed(initialCollapsed, { persist: false })
|
|
16244
16254
|
requestAnimationFrame(() => {
|
|
16245
16255
|
appcanvas.classList.add("sidebar-motion-ready")
|
|
16256
|
+
requestAnimationFrame(() => {
|
|
16257
|
+
if (shouldAutoPeekOnIdle && initialCollapsed && !hasVisibleBrowserSurface()) {
|
|
16258
|
+
setPeeking(true)
|
|
16259
|
+
}
|
|
16260
|
+
})
|
|
16246
16261
|
})
|
|
16247
16262
|
|
|
16248
16263
|
toggle.addEventListener("click", (event) => {
|
|
@@ -16268,6 +16283,10 @@ const rerenderMenuSection = (container, html) => {
|
|
|
16268
16283
|
})
|
|
16269
16284
|
aside.addEventListener("pointerleave", closePeekSoon)
|
|
16270
16285
|
aside.addEventListener("focusout", closePeekSoon)
|
|
16286
|
+
aside.addEventListener("click", () => {
|
|
16287
|
+
if (!isPeeking()) return
|
|
16288
|
+
window.setTimeout(() => setPeeking(false), 0)
|
|
16289
|
+
})
|
|
16271
16290
|
document.addEventListener("pointerdown", (event) => {
|
|
16272
16291
|
if (!isPeeking()) return
|
|
16273
16292
|
const target = event.target
|
package/server/views/index.ejs
CHANGED
|
@@ -865,7 +865,7 @@ body.dark .menu-btns .toggle-star.is-starred {
|
|
|
865
865
|
padding: 0;
|
|
866
866
|
border: 1px solid rgba(15, 23, 42, 0.14);
|
|
867
867
|
border-radius: 8px;
|
|
868
|
-
background:
|
|
868
|
+
background: var(--home-page-nav-bg, #ffffff);
|
|
869
869
|
color: rgba(15, 23, 42, 0.94);
|
|
870
870
|
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.24);
|
|
871
871
|
}
|
|
@@ -886,7 +886,7 @@ body.dark .menu-btns .toggle-star.is-starred {
|
|
|
886
886
|
}
|
|
887
887
|
body.dark .home-actions-dialog {
|
|
888
888
|
border-color: rgba(255, 255, 255, 0.12);
|
|
889
|
-
background:
|
|
889
|
+
background: var(--home-page-nav-bg, #1b1c1d);
|
|
890
890
|
color: rgba(248, 250, 252, 0.94);
|
|
891
891
|
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.55);
|
|
892
892
|
}
|
|
@@ -1125,6 +1125,35 @@ body.dark .home-mode-command {
|
|
|
1125
1125
|
font-size: 10px;
|
|
1126
1126
|
opacity: 0.6;
|
|
1127
1127
|
}
|
|
1128
|
+
.home-run-command.has-block-label,
|
|
1129
|
+
.home-run-menu-summary.has-block-label {
|
|
1130
|
+
min-height: 52px;
|
|
1131
|
+
padding-top: 8px;
|
|
1132
|
+
padding-bottom: 8px;
|
|
1133
|
+
}
|
|
1134
|
+
.home-run-menu-label {
|
|
1135
|
+
display: flex;
|
|
1136
|
+
align-items: center;
|
|
1137
|
+
gap: 9px;
|
|
1138
|
+
}
|
|
1139
|
+
.home-run-menu-label.has-block-content {
|
|
1140
|
+
align-items: flex-start;
|
|
1141
|
+
white-space: normal;
|
|
1142
|
+
line-height: 1.18;
|
|
1143
|
+
}
|
|
1144
|
+
.home-run-menu-label.has-block-content > i:first-child {
|
|
1145
|
+
margin-top: 1px;
|
|
1146
|
+
}
|
|
1147
|
+
.home-run-menu-label.has-block-content > div {
|
|
1148
|
+
min-width: 0;
|
|
1149
|
+
display: flex;
|
|
1150
|
+
flex-direction: column;
|
|
1151
|
+
gap: 2px;
|
|
1152
|
+
}
|
|
1153
|
+
.home-run-menu-label.has-block-content > div > strong,
|
|
1154
|
+
.home-run-menu-label.has-block-content > div > div {
|
|
1155
|
+
line-height: 1.18;
|
|
1156
|
+
}
|
|
1128
1157
|
.home-run-stop {
|
|
1129
1158
|
width: 38px;
|
|
1130
1159
|
min-height: 38px;
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
<%
|
|
2
|
+
const homeActionsAppId = item && (item.uri || item.path || item.name) ? String(item.uri || item.path || item.name) : ""
|
|
3
|
+
const homeActionsSettingsHref = homeActionsAppId
|
|
4
|
+
? `/v/${encodeURIComponent(homeActionsAppId)}?${new URLSearchParams({
|
|
5
|
+
pinokio_home_select: JSON.stringify({
|
|
6
|
+
target: "env-settings",
|
|
7
|
+
hrefAttr: `/env/api/${homeActionsAppId}?host=app`,
|
|
8
|
+
textValue: "Settings"
|
|
9
|
+
})
|
|
10
|
+
}).toString()}`
|
|
11
|
+
: ""
|
|
12
|
+
%>
|
|
1
13
|
<dialog id='<%= id %>' class='home-actions-dialog' aria-labelledby='<%= id %>-title'>
|
|
2
14
|
<div class='home-actions-shell'>
|
|
3
15
|
<div class='home-actions-header'>
|
|
@@ -49,6 +61,12 @@
|
|
|
49
61
|
<span class='home-actions-count'><%= item.terminal_online_count %> online</span>
|
|
50
62
|
<% } %>
|
|
51
63
|
</button>
|
|
64
|
+
<% if (homeActionsSettingsHref) { %>
|
|
65
|
+
<a class='home-mode-command' href='<%= homeActionsSettingsHref %>'>
|
|
66
|
+
<i class='fa-solid fa-gear' aria-hidden='true'></i>
|
|
67
|
+
<span>Settings</span>
|
|
68
|
+
</a>
|
|
69
|
+
<% } %>
|
|
52
70
|
<button type='button' class='home-mode-command' data-filepath='<%= item.filepath %>'>
|
|
53
71
|
<i class='fa-solid fa-folder-open' aria-hidden='true'></i>
|
|
54
72
|
<span>Open in file explorer</span>
|
|
@@ -9,6 +9,7 @@ const stripHtml = (value) => String(value ?? "")
|
|
|
9
9
|
.replace(/<[^>]*>/g, " ")
|
|
10
10
|
.replace(/\s+/g, " ")
|
|
11
11
|
.trim()
|
|
12
|
+
const hasBlockContent = (value) => /<\s*(div|p|section|article|ul|ol|li|h[1-6])(?:\s|>)/i.test(String(value ?? ""))
|
|
12
13
|
const buildHomeSelectionPayload = (menuItem, index) => {
|
|
13
14
|
const dataFilepath = (menuItem.fs || menuItem.command) ? (menuItem.href || "") : ""
|
|
14
15
|
const payload = {
|
|
@@ -49,9 +50,10 @@ const buildHomeSelectionUrl = (menuItem, index) => {
|
|
|
49
50
|
%>
|
|
50
51
|
<% menu.forEach((item, index) => { %>
|
|
51
52
|
<% if (item && item.menu) { %>
|
|
53
|
+
<% const itemBtnHasBlock = hasBlockContent(item.btn) %>
|
|
52
54
|
<details class='home-run-menu-group' open>
|
|
53
|
-
<summary class='home-run-menu-summary'>
|
|
54
|
-
<
|
|
55
|
+
<summary class='home-run-menu-summary <%= itemBtnHasBlock ? "has-block-label" : "" %>'>
|
|
56
|
+
<div class='home-run-menu-label <%= itemBtnHasBlock ? "has-block-content" : "" %>'><%- item.btn %></div>
|
|
55
57
|
<i class='fa-solid fa-chevron-down' aria-hidden='true'></i>
|
|
56
58
|
</summary>
|
|
57
59
|
<div class='home-run-menu-children'>
|
|
@@ -60,13 +62,14 @@ const buildHomeSelectionUrl = (menuItem, index) => {
|
|
|
60
62
|
</details>
|
|
61
63
|
<% } else if (item && item.btn) { %>
|
|
62
64
|
<% const openUrl = buildHomeSelectionUrl(item, index) %>
|
|
65
|
+
<% const itemBtnHasBlock = hasBlockContent(item.btn) %>
|
|
63
66
|
<div class='home-run-menu-row <%= item.running ? "is-running" : "" %>'>
|
|
64
67
|
<a
|
|
65
|
-
class='home-run-command'
|
|
68
|
+
class='home-run-command <%= itemBtnHasBlock ? "has-block-label" : "" %>'
|
|
66
69
|
href='<%= openUrl %>'
|
|
67
70
|
<% if (item.confirm) { %>data-confirm="<%- escapeAttr(item.confirm) %>"<% } %>
|
|
68
71
|
>
|
|
69
|
-
<
|
|
72
|
+
<div class='home-run-menu-label <%= itemBtnHasBlock ? "has-block-content" : "" %>'><%- item.btn %></div>
|
|
70
73
|
<% if (item.running) { %><span class='home-run-menu-status'>Running</span><% } %>
|
|
71
74
|
<i class='fa-solid fa-chevron-right' aria-hidden='true'></i>
|
|
72
75
|
</a>
|
|
@@ -2,6 +2,8 @@ const assert = require("node:assert/strict")
|
|
|
2
2
|
const fs = require("node:fs/promises")
|
|
3
3
|
const path = require("node:path")
|
|
4
4
|
const test = require("node:test")
|
|
5
|
+
const ejs = require("ejs")
|
|
6
|
+
const { JSDOM } = require("jsdom")
|
|
5
7
|
|
|
6
8
|
const root = path.resolve(__dirname, "..")
|
|
7
9
|
|
|
@@ -370,6 +372,11 @@ test("static guard: open without launching is not wired to launch requirement st
|
|
|
370
372
|
assert.match(homeActionModal, /Open without launching/)
|
|
371
373
|
assert.match(homeActionModal, /class='home-mode-command home-browse' data-src='<%= item\.view_url %>'/)
|
|
372
374
|
assert.match(homeActionModal, /Open dev mode/)
|
|
375
|
+
assert.match(homeActionModal, /const homeActionsAppId = item && \(item\.uri \|\| item\.path \|\| item\.name\)/)
|
|
376
|
+
assert.match(homeActionModal, /pinokio_home_select: JSON\.stringify/)
|
|
377
|
+
assert.match(homeActionModal, /target: "env-settings"/)
|
|
378
|
+
assert.match(homeActionModal, /href='<%= homeActionsSettingsHref %>'/)
|
|
379
|
+
assert.match(homeActionModal, /Settings/)
|
|
373
380
|
assert.doesNotMatch(homeActionModal, /home-actions-tabs|home-actions-tab|role='tab'|role='tabpanel'/)
|
|
374
381
|
assert.doesNotMatch(homeActionModal, /Open files mode/)
|
|
375
382
|
assert.doesNotMatch(homeActionModal, /launch.requirements|data-launch-requirements|frame-link/)
|
|
@@ -378,6 +385,66 @@ test("static guard: open without launching is not wired to launch requirement st
|
|
|
378
385
|
assert.doesNotMatch(statusClient, /saved requirement/)
|
|
379
386
|
})
|
|
380
387
|
|
|
388
|
+
test("home actions modal renders an app settings link", async () => {
|
|
389
|
+
const html = await ejs.renderFile(path.resolve(root, "server/views/partials/home_action_modal.ejs"), {
|
|
390
|
+
id: "home-actions-test",
|
|
391
|
+
item: {
|
|
392
|
+
icon: "",
|
|
393
|
+
name: "Test App",
|
|
394
|
+
filepath: "/tmp/pinokio/api/test app.git",
|
|
395
|
+
uri: "test app.git",
|
|
396
|
+
menu: [],
|
|
397
|
+
view_url: "/v/test%20app.git",
|
|
398
|
+
dev_url: "/p/test%20app.git/dev",
|
|
399
|
+
url: "/p/test%20app.git",
|
|
400
|
+
terminal_online_count: 0,
|
|
401
|
+
running: false
|
|
402
|
+
}
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
const hrefMatch = html.match(/href='([^']+)'/)
|
|
406
|
+
assert.ok(hrefMatch)
|
|
407
|
+
const href = new URL(hrefMatch[1], "http://127.0.0.1")
|
|
408
|
+
assert.equal(href.pathname, "/v/test%20app.git")
|
|
409
|
+
const payload = JSON.parse(href.searchParams.get("pinokio_home_select"))
|
|
410
|
+
assert.deepEqual(payload, {
|
|
411
|
+
target: "env-settings",
|
|
412
|
+
hrefAttr: "/env/api/test app.git?host=app",
|
|
413
|
+
textValue: "Settings"
|
|
414
|
+
})
|
|
415
|
+
assert.match(html, />\s*Settings\s*</)
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
test("home actions drawer background follows the home page surface", async () => {
|
|
419
|
+
const homeView = await fs.readFile(path.resolve(root, "server/views/index.ejs"), "utf8")
|
|
420
|
+
|
|
421
|
+
assert.match(homeView, /body\.is-home \{[\s\S]*--home-page-nav-bg: #ffffff;[\s\S]*background: #ffffff;/)
|
|
422
|
+
assert.match(homeView, /body\.dark\.is-home \{[\s\S]*--home-page-nav-bg: #1b1c1d;[\s\S]*background: #1b1c1d;/)
|
|
423
|
+
assert.match(homeView, /\.home-actions-dialog \{[\s\S]*background: var\(--home-page-nav-bg, #ffffff\);/)
|
|
424
|
+
assert.match(homeView, /body\.dark \.home-actions-dialog \{[\s\S]*background: var\(--home-page-nav-bg, #1b1c1d\);/)
|
|
425
|
+
assert.doesNotMatch(homeView, /body\.dark \.home-actions-dialog \{[\s\S]*background: rgba\(18, 20, 25, 0\.98\);/)
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
test("home run menu gives block labels valid padded layout", async () => {
|
|
429
|
+
const homeView = await fs.readFile(path.resolve(root, "server/views/index.ejs"), "utf8")
|
|
430
|
+
const html = await ejs.renderFile(path.resolve(root, "server/views/partials/home_run_menu.ejs"), {
|
|
431
|
+
app: {
|
|
432
|
+
view_url: "/v/test-app"
|
|
433
|
+
},
|
|
434
|
+
menu: [{
|
|
435
|
+
icon: "fa-regular fa-circle-xmark",
|
|
436
|
+
btn: '<i class="fa-regular fa-circle-xmark"></i><div><strong>Reset</strong><div>Revert to pre-install state</div></div>',
|
|
437
|
+
href: "reset.js"
|
|
438
|
+
}]
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
assert.match(html, /class='home-run-command has-block-label'/)
|
|
442
|
+
assert.match(html, /<div class='home-run-menu-label has-block-content'>/)
|
|
443
|
+
assert.doesNotMatch(html, /<span class='home-run-menu-label/)
|
|
444
|
+
assert.match(homeView, /\.home-run-command\.has-block-label,[\s\S]*\.home-run-menu-summary\.has-block-label \{[\s\S]*padding-top: 8px;[\s\S]*padding-bottom: 8px;/)
|
|
445
|
+
assert.match(homeView, /\.home-run-menu-label\.has-block-content \{[\s\S]*white-space: normal;/)
|
|
446
|
+
})
|
|
447
|
+
|
|
381
448
|
test("static guard: open without launching disables page-load script frame selection", async () => {
|
|
382
449
|
const appView = await fs.readFile(path.resolve(root, "server/views/app.ejs"), "utf8")
|
|
383
450
|
|
|
@@ -392,6 +459,84 @@ test("static guard: open without launching disables page-load script frame selec
|
|
|
392
459
|
assert.match(appView, /if \(!target && !automaticSelectionDisabled && preselected && preselected !== devTab\)/)
|
|
393
460
|
})
|
|
394
461
|
|
|
462
|
+
test("static guard: collapsed idle run page peeks without persisting sidebar state", async () => {
|
|
463
|
+
const appView = await fs.readFile(path.resolve(root, "server/views/app.ejs"), "utf8")
|
|
464
|
+
|
|
465
|
+
assert.match(appView, /const shouldAutoPeekOnIdle = <%- JSON\.stringify\(type === "run"\) %>/)
|
|
466
|
+
assert.match(appView, /const hasVisibleBrowserSurface = \(\) => \{[\s\S]*browserview\.querySelector\("iframe:not\(\.hidden\)"\)[\s\S]*browserview-network-status[\s\S]*data-launch-requirements-status/)
|
|
467
|
+
assert.match(appView, /if \(shouldAutoPeekOnIdle && initialCollapsed && !hasVisibleBrowserSurface\(\)\) \{[\s\S]*setPeeking\(true\)/)
|
|
468
|
+
assert.match(appView, /setCollapsed\(initialCollapsed, \{ persist: false \}\)/)
|
|
469
|
+
assert.match(appView, /aside\.addEventListener\("click", \(\) => \{[\s\S]*window\.setTimeout\(\(\) => setPeeking\(false\), 0\)/)
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
test("app sidebar auto-peeks on collapsed idle run page and dismisses after click", async () => {
|
|
473
|
+
const appView = await fs.readFile(path.resolve(root, "server/views/app.ejs"), "utf8")
|
|
474
|
+
const start = appView.indexOf('<script>\n(() => {\n const appcanvas = document.querySelector(".appcanvas")')
|
|
475
|
+
const end = appView.indexOf('</script>\n<script src="/tab-idle-notifier.js"></script>', start)
|
|
476
|
+
assert.notEqual(start, -1)
|
|
477
|
+
assert.notEqual(end, -1)
|
|
478
|
+
|
|
479
|
+
const sidebarScript = appView
|
|
480
|
+
.slice(start + "<script>\n".length, end)
|
|
481
|
+
.replace(/<%- JSON\.stringify\(typeof name === "string" \? name : ""\) %>/g, '"test-app"')
|
|
482
|
+
.replace(/<%- JSON\.stringify\(type === "run"\) %>/g, "true")
|
|
483
|
+
|
|
484
|
+
const createDom = (browserSurface = "") => {
|
|
485
|
+
return new JSDOM(`<!doctype html>
|
|
486
|
+
<button id="sidebar-toggle"></button>
|
|
487
|
+
<div class="appcanvas vertical">
|
|
488
|
+
<button type="button" data-app-sidebar-peek-trigger></button>
|
|
489
|
+
<aside id="app-sidebar"><button type="button" id="sidebar-action">Start</button></aside>
|
|
490
|
+
<main class="browserview">${browserSurface}</main>
|
|
491
|
+
</div>
|
|
492
|
+
<div id="browserview-network-status" hidden></div>
|
|
493
|
+
<div data-launch-requirements-status hidden></div>`, {
|
|
494
|
+
url: "http://127.0.0.1:42000/v/test-app",
|
|
495
|
+
runScripts: "outside-only",
|
|
496
|
+
pretendToBeVisual: true,
|
|
497
|
+
beforeParse(window) {
|
|
498
|
+
window.matchMedia = () => ({
|
|
499
|
+
matches: true,
|
|
500
|
+
addEventListener() {},
|
|
501
|
+
removeEventListener() {}
|
|
502
|
+
})
|
|
503
|
+
}
|
|
504
|
+
})
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const wait = (ms = 75) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
508
|
+
const storageKey = "pinokio.sidebar-collapsed:test-app"
|
|
509
|
+
|
|
510
|
+
const dom = createDom()
|
|
511
|
+
dom.window.localStorage.setItem(storageKey, "1")
|
|
512
|
+
dom.window.eval(sidebarScript)
|
|
513
|
+
await wait()
|
|
514
|
+
|
|
515
|
+
const appcanvas = dom.window.document.querySelector(".appcanvas")
|
|
516
|
+
const aside = dom.window.document.getElementById("app-sidebar")
|
|
517
|
+
assert.equal(appcanvas.classList.contains("sidebar-collapsed"), true)
|
|
518
|
+
assert.equal(appcanvas.classList.contains("sidebar-peeking"), true)
|
|
519
|
+
assert.equal(aside.getAttribute("aria-hidden"), "false")
|
|
520
|
+
assert.equal(dom.window.localStorage.getItem(storageKey), "1")
|
|
521
|
+
|
|
522
|
+
aside.querySelector("#sidebar-action").dispatchEvent(new dom.window.MouseEvent("click", { bubbles: true }))
|
|
523
|
+
await wait(5)
|
|
524
|
+
|
|
525
|
+
assert.equal(appcanvas.classList.contains("sidebar-collapsed"), true)
|
|
526
|
+
assert.equal(appcanvas.classList.contains("sidebar-peeking"), false)
|
|
527
|
+
assert.equal(dom.window.localStorage.getItem(storageKey), "1")
|
|
528
|
+
|
|
529
|
+
const activeDom = createDom('<iframe src="about:blank"></iframe>')
|
|
530
|
+
activeDom.window.localStorage.setItem(storageKey, "1")
|
|
531
|
+
activeDom.window.eval(sidebarScript)
|
|
532
|
+
await wait()
|
|
533
|
+
|
|
534
|
+
const activeCanvas = activeDom.window.document.querySelector(".appcanvas")
|
|
535
|
+
assert.equal(activeCanvas.classList.contains("sidebar-collapsed"), true)
|
|
536
|
+
assert.equal(activeCanvas.classList.contains("sidebar-peeking"), false)
|
|
537
|
+
assert.equal(activeDom.window.localStorage.getItem(storageKey), "1")
|
|
538
|
+
})
|
|
539
|
+
|
|
395
540
|
test("static guard: home run command selection opens without launching before selecting command", async () => {
|
|
396
541
|
const homeRunMenu = await fs.readFile(path.resolve(root, "server/views/partials/home_run_menu.ejs"), "utf8")
|
|
397
542
|
|
|
@@ -949,6 +949,81 @@ test('Windows cmd suppresses echo only for explicit conda shell.run commands', (
|
|
|
949
949
|
}, command), { command })
|
|
950
950
|
})
|
|
951
951
|
|
|
952
|
+
test('Windows cmd array shell.run marks only the conda launch quiet', async () => {
|
|
953
|
+
const { context, root } = createContext('win32')
|
|
954
|
+
const records = []
|
|
955
|
+
const originalPrompt = Shell.prototype.prompt
|
|
956
|
+
const originalExec = Shell.prototype.exec
|
|
957
|
+
Shell.prototype.prompt = async () => 'PINOKIO_PROMPT'
|
|
958
|
+
Shell.prototype.exec = async function (params) {
|
|
959
|
+
this.platform = 'win32'
|
|
960
|
+
this.shell = 'cmd.exe'
|
|
961
|
+
const originalParams = {
|
|
962
|
+
input: params && params.input,
|
|
963
|
+
message: params && params.message,
|
|
964
|
+
}
|
|
965
|
+
const activatedParams = await this.activate(params)
|
|
966
|
+
const command = this.build(activatedParams)
|
|
967
|
+
records.push({
|
|
968
|
+
message: originalParams.message,
|
|
969
|
+
prepared: this.prepareCommandExecution(originalParams, command),
|
|
970
|
+
})
|
|
971
|
+
return command
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
try {
|
|
975
|
+
const kernel = {
|
|
976
|
+
homedir: root,
|
|
977
|
+
platform: 'win32',
|
|
978
|
+
bracketedPasteSupport: { 'cmd.exe': false },
|
|
979
|
+
envs: {},
|
|
980
|
+
exists: async () => false,
|
|
981
|
+
which: () => null,
|
|
982
|
+
path: (...parts) => path.join(root, ...parts),
|
|
983
|
+
bin: {
|
|
984
|
+
envs: (env = {}) => env,
|
|
985
|
+
path: (...parts) => path.join(root, 'bin', ...parts),
|
|
986
|
+
activationCommands: () => [],
|
|
987
|
+
},
|
|
988
|
+
api: {
|
|
989
|
+
resolvePath: (_cwd, target) => target,
|
|
990
|
+
running: {},
|
|
991
|
+
},
|
|
992
|
+
git: {
|
|
993
|
+
repos: async () => [],
|
|
994
|
+
restoreNewReposForActiveSnapshot: async () => {},
|
|
995
|
+
},
|
|
996
|
+
template: {
|
|
997
|
+
render: (value) => value,
|
|
998
|
+
},
|
|
999
|
+
}
|
|
1000
|
+
kernel.shell = new Shells(kernel)
|
|
1001
|
+
|
|
1002
|
+
await kernel.shell.run({
|
|
1003
|
+
path: context.appPath,
|
|
1004
|
+
message: [
|
|
1005
|
+
'echo before',
|
|
1006
|
+
'conda install -y -c conda-forge ffmpeg',
|
|
1007
|
+
'python main.py',
|
|
1008
|
+
],
|
|
1009
|
+
}, {}, () => {})
|
|
1010
|
+
|
|
1011
|
+
assert.deepEqual(records.map((record) => record.message), [
|
|
1012
|
+
'echo before',
|
|
1013
|
+
'conda install -y -c conda-forge ffmpeg',
|
|
1014
|
+
'python main.py',
|
|
1015
|
+
])
|
|
1016
|
+
assert.deepEqual(records.map((record) => !!record.prepared.quietCmd), [
|
|
1017
|
+
false,
|
|
1018
|
+
true,
|
|
1019
|
+
false,
|
|
1020
|
+
])
|
|
1021
|
+
} finally {
|
|
1022
|
+
Shell.prototype.prompt = originalPrompt
|
|
1023
|
+
Shell.prototype.exec = originalExec
|
|
1024
|
+
}
|
|
1025
|
+
})
|
|
1026
|
+
|
|
952
1027
|
test('rewrite warning is scoped to each user-visible shell flow', async () => {
|
|
953
1028
|
CondaRuntimeGuard.resetNoticeSessionsForTest()
|
|
954
1029
|
const { context, events, root } = createContext()
|