pinokiod 8.0.17 → 8.0.18
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/kernel/bin/conda.js +1 -21
- package/kernel/bin/index.js +0 -15
- package/kernel/path_removal.js +51 -50
- package/kernel/windows_restart_manager.js +4 -38
- package/package.json +1 -1
- package/server/index.js +13 -86
- package/server/public/common.js +27 -39
- package/server/public/style.css +3 -26
- package/server/views/app.ejs +1 -7
- package/server/views/download.ejs +1 -7
- package/server/views/editor.ejs +1 -7
- package/server/views/env_editor.ejs +1 -7
- package/server/views/file_explorer.ejs +1 -7
- package/server/views/index.ejs +1 -7
- package/server/views/install.ejs +2 -21
- package/server/views/net.ejs +1 -7
- package/server/views/settings.ejs +2 -14
- package/server/views/terminal.ejs +1 -7
- package/test/conda-bin.test.js +0 -31
- package/test/path-removal-ui.test.js +56 -27
- package/test/path-removal.test.js +203 -121
- package/test/shell-conda-runtime-guard.test.js +8 -2
- package/test/startup-git-index-route.test.js +0 -52
- package/test/windows-path-removal.integration.test.js +2 -12
package/server/views/app.ejs
CHANGED
|
@@ -11866,13 +11866,7 @@ const rerenderMenuSection = (container, html) => {
|
|
|
11866
11866
|
actions: "hidden"
|
|
11867
11867
|
}
|
|
11868
11868
|
});
|
|
11869
|
-
let res = await PinokioPathRemoval.
|
|
11870
|
-
method: "post",
|
|
11871
|
-
headers: {
|
|
11872
|
-
"Content-Type": "application/json"
|
|
11873
|
-
},
|
|
11874
|
-
body: JSON.stringify({ name, background })
|
|
11875
|
-
}))
|
|
11869
|
+
let res = await PinokioPathRemoval.remove({ name })
|
|
11876
11870
|
console.log("res", res)
|
|
11877
11871
|
Swal.close()
|
|
11878
11872
|
if (res) {
|
|
@@ -231,13 +231,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
231
231
|
if (proceed) {
|
|
232
232
|
document.querySelector(".reset-bin-loading").classList.remove("hidden")
|
|
233
233
|
document.querySelector("#del-bin").classList.add("hidden")
|
|
234
|
-
let res = await PinokioPathRemoval.
|
|
235
|
-
method: "post",
|
|
236
|
-
headers: {
|
|
237
|
-
"Content-Type": "application/json"
|
|
238
|
-
},
|
|
239
|
-
body: JSON.stringify({ type: "bin", background })
|
|
240
|
-
}))
|
|
234
|
+
let res = await PinokioPathRemoval.remove({ type: "bin" })
|
|
241
235
|
console.log(res)
|
|
242
236
|
document.querySelector(".reset-bin-loading").classList.add("hidden")
|
|
243
237
|
if (res.cancelled) {
|
package/server/views/editor.ejs
CHANGED
|
@@ -940,13 +940,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
940
940
|
if (proceed) {
|
|
941
941
|
document.querySelector(".reset-bin-loading").classList.remove("hidden")
|
|
942
942
|
document.querySelector("#del-bin").classList.add("hidden")
|
|
943
|
-
let res = await PinokioPathRemoval.
|
|
944
|
-
method: "post",
|
|
945
|
-
headers: {
|
|
946
|
-
"Content-Type": "application/json"
|
|
947
|
-
},
|
|
948
|
-
body: JSON.stringify({ type: "bin", background })
|
|
949
|
-
}))
|
|
943
|
+
let res = await PinokioPathRemoval.remove({ type: "bin" })
|
|
950
944
|
console.log(res)
|
|
951
945
|
document.querySelector(".reset-bin-loading").classList.add("hidden")
|
|
952
946
|
if (res.cancelled) {
|
|
@@ -447,13 +447,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
447
447
|
}
|
|
448
448
|
})
|
|
449
449
|
try {
|
|
450
|
-
const res = await PinokioPathRemoval.
|
|
451
|
-
method: "post",
|
|
452
|
-
headers: {
|
|
453
|
-
"Content-Type": "application/json"
|
|
454
|
-
},
|
|
455
|
-
body: JSON.stringify({ name: projectName, background })
|
|
456
|
-
}))
|
|
450
|
+
const res = await PinokioPathRemoval.remove({ name: projectName })
|
|
457
451
|
Swal.close()
|
|
458
452
|
if (res && res.success) {
|
|
459
453
|
navigateHome()
|
|
@@ -636,13 +636,7 @@ document.addEventListener("click", async (e) => {
|
|
|
636
636
|
actions: "hidden"
|
|
637
637
|
}
|
|
638
638
|
});
|
|
639
|
-
let res = await PinokioPathRemoval.
|
|
640
|
-
method: "post",
|
|
641
|
-
headers: {
|
|
642
|
-
"Content-Type": "application/json"
|
|
643
|
-
},
|
|
644
|
-
body: JSON.stringify({ name, background })
|
|
645
|
-
}))
|
|
639
|
+
let res = await PinokioPathRemoval.remove({ name })
|
|
646
640
|
console.log("res", res)
|
|
647
641
|
Swal.close()
|
|
648
642
|
if (res) {
|
package/server/views/index.ejs
CHANGED
|
@@ -4031,13 +4031,7 @@ document.addEventListener("click", async (e) => {
|
|
|
4031
4031
|
actions: "hidden"
|
|
4032
4032
|
}
|
|
4033
4033
|
});
|
|
4034
|
-
let res = await PinokioPathRemoval.
|
|
4035
|
-
method: "post",
|
|
4036
|
-
headers: {
|
|
4037
|
-
"Content-Type": "application/json"
|
|
4038
|
-
},
|
|
4039
|
-
body: JSON.stringify({ name, background })
|
|
4040
|
-
}))
|
|
4034
|
+
let res = await PinokioPathRemoval.remove({ name })
|
|
4041
4035
|
console.log("res", res)
|
|
4042
4036
|
Swal.close()
|
|
4043
4037
|
if (res) {
|
package/server/views/install.ejs
CHANGED
|
@@ -822,11 +822,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
822
822
|
if (options && options.fresh === "1") {
|
|
823
823
|
setInstallStatus("<b>Removing existing tools...</b><br>This can take a minute on Windows.")
|
|
824
824
|
try {
|
|
825
|
-
const result = await PinokioPathRemoval.
|
|
826
|
-
method: "post",
|
|
827
|
-
headers: { "Content-Type": "application/json" },
|
|
828
|
-
body: JSON.stringify({ type: "bin", background })
|
|
829
|
-
}))
|
|
825
|
+
const result = await PinokioPathRemoval.remove({ type: "bin" })
|
|
830
826
|
if (result.cancelled) {
|
|
831
827
|
navigateToCallback()
|
|
832
828
|
return
|
|
@@ -891,22 +887,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
891
887
|
} else if (packet.type === "path.remove.blocked") {
|
|
892
888
|
const result = await PinokioPathRemoval.show({
|
|
893
889
|
details: packet.data,
|
|
894
|
-
retry:
|
|
895
|
-
const response = await fetch("/pinokio/delete", {
|
|
896
|
-
method: "post",
|
|
897
|
-
headers: {
|
|
898
|
-
"Content-Type": "application/json"
|
|
899
|
-
},
|
|
900
|
-
body: JSON.stringify({ type: "conda-runtime", background: !manual })
|
|
901
|
-
})
|
|
902
|
-
const payload = await response.json()
|
|
903
|
-
const blocked = PinokioPathRemoval.blockedDetails(payload)
|
|
904
|
-
if (!blocked && (!response.ok || (payload && payload.error))) {
|
|
905
|
-
const message = payload && payload.error
|
|
906
|
-
throw new Error(typeof message === "string" ? message : `Cleanup failed (${response.status})`)
|
|
907
|
-
}
|
|
908
|
-
return payload
|
|
909
|
-
}
|
|
890
|
+
retry: () => PinokioPathRemoval.tryRemove({ type: "conda-runtime" })
|
|
910
891
|
})
|
|
911
892
|
if (result && result.success) {
|
|
912
893
|
resumeInstall()
|
package/server/views/net.ejs
CHANGED
|
@@ -2361,13 +2361,7 @@ document.addEventListener("click", async (e) => {
|
|
|
2361
2361
|
actions: "hidden"
|
|
2362
2362
|
}
|
|
2363
2363
|
});
|
|
2364
|
-
let res = await PinokioPathRemoval.
|
|
2365
|
-
method: "post",
|
|
2366
|
-
headers: {
|
|
2367
|
-
"Content-Type": "application/json"
|
|
2368
|
-
},
|
|
2369
|
-
body: JSON.stringify({ name, background })
|
|
2370
|
-
}))
|
|
2364
|
+
let res = await PinokioPathRemoval.remove({ name })
|
|
2371
2365
|
console.log("res", res)
|
|
2372
2366
|
Swal.close()
|
|
2373
2367
|
if (res) {
|
|
@@ -1537,13 +1537,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
1537
1537
|
if (proceed) {
|
|
1538
1538
|
document.querySelector(".reset-bin-loading").classList.remove("hidden")
|
|
1539
1539
|
document.querySelector("#del-bin").classList.add("hidden")
|
|
1540
|
-
let res = await PinokioPathRemoval.
|
|
1541
|
-
method: "post",
|
|
1542
|
-
headers: {
|
|
1543
|
-
"Content-Type": "application/json"
|
|
1544
|
-
},
|
|
1545
|
-
body: JSON.stringify({ type: "bin", background })
|
|
1546
|
-
}))
|
|
1540
|
+
let res = await PinokioPathRemoval.remove({ type: "bin" })
|
|
1547
1541
|
console.log(res)
|
|
1548
1542
|
document.querySelector(".reset-bin-loading").classList.add("hidden")
|
|
1549
1543
|
if (res.cancelled) {
|
|
@@ -1561,13 +1555,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
1561
1555
|
if (proceed) {
|
|
1562
1556
|
document.querySelector(".reset-cache-loading").classList.remove("hidden")
|
|
1563
1557
|
document.querySelector("#del-cache").classList.add("hidden")
|
|
1564
|
-
let res = await PinokioPathRemoval.
|
|
1565
|
-
method: "post",
|
|
1566
|
-
headers: {
|
|
1567
|
-
"Content-Type": "application/json"
|
|
1568
|
-
},
|
|
1569
|
-
body: JSON.stringify({ type: "cache", background })
|
|
1570
|
-
}))
|
|
1558
|
+
let res = await PinokioPathRemoval.remove({ type: "cache" })
|
|
1571
1559
|
console.log(res)
|
|
1572
1560
|
document.querySelector(".reset-cache-loading").classList.add("hidden")
|
|
1573
1561
|
if (res.cancelled) {
|
|
@@ -3339,13 +3339,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
3339
3339
|
if (proceed) {
|
|
3340
3340
|
document.querySelector(".reset-bin-loading").classList.remove("hidden")
|
|
3341
3341
|
document.querySelector("#del-bin").classList.add("hidden")
|
|
3342
|
-
let res = await PinokioPathRemoval.
|
|
3343
|
-
method: "post",
|
|
3344
|
-
headers: {
|
|
3345
|
-
"Content-Type": "application/json"
|
|
3346
|
-
},
|
|
3347
|
-
body: JSON.stringify({ type: "bin", background })
|
|
3348
|
-
}))
|
|
3342
|
+
let res = await PinokioPathRemoval.remove({ type: "bin" })
|
|
3349
3343
|
console.log(res)
|
|
3350
3344
|
document.querySelector(".reset-bin-loading").classList.add("hidden")
|
|
3351
3345
|
if (res.cancelled) {
|
package/test/conda-bin.test.js
CHANGED
|
@@ -472,37 +472,6 @@ test('Conda waits for runtime removal before starting the installer', async () =
|
|
|
472
472
|
assert.match(output.join(''), /existing Miniforge installation removed/)
|
|
473
473
|
})
|
|
474
474
|
|
|
475
|
-
test('Bin serializes concurrent installs without dropping either request', async () => {
|
|
476
|
-
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-bin-install-single-flight-'))
|
|
477
|
-
const bin = createBin(root)
|
|
478
|
-
const events = []
|
|
479
|
-
let release
|
|
480
|
-
const gate = new Promise((resolve) => { release = resolve })
|
|
481
|
-
|
|
482
|
-
bin.installRequirements = async (req) => {
|
|
483
|
-
events.push(`start:${req.id}`)
|
|
484
|
-
if (req.id === 'first') await gate
|
|
485
|
-
events.push(`finish:${req.id}`)
|
|
486
|
-
return req.id
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
const first = bin.install({ id: 'first' }, () => {})
|
|
490
|
-
const second = bin.install({ id: 'second' }, () => {})
|
|
491
|
-
await Promise.resolve()
|
|
492
|
-
assert.deepEqual(events, ['start:first'])
|
|
493
|
-
|
|
494
|
-
release()
|
|
495
|
-
assert.deepEqual(await Promise.all([first, second]), ['first', 'second'])
|
|
496
|
-
assert.deepEqual(events, ['start:first', 'finish:first', 'start:second', 'finish:second'])
|
|
497
|
-
|
|
498
|
-
bin.installRequirements = async (req) => {
|
|
499
|
-
if (req.id === 'failure') throw new Error('install failed')
|
|
500
|
-
return req.id
|
|
501
|
-
}
|
|
502
|
-
await assert.rejects(bin.install({ id: 'failure' }, () => {}), /install failed/)
|
|
503
|
-
assert.equal(await bin.install({ id: 'after-failure' }, () => {}), 'after-failure')
|
|
504
|
-
})
|
|
505
|
-
|
|
506
475
|
test('Conda install propagates bootstrap failure after replacing the runtime', async () => {
|
|
507
476
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-bootstrap-fails-'))
|
|
508
477
|
await createMiniforge(root)
|
|
@@ -142,10 +142,9 @@ test("path-removal modal renders identified blockers and escapes backend text",
|
|
|
142
142
|
|
|
143
143
|
const shown = api.show({
|
|
144
144
|
details: {
|
|
145
|
-
operation: "Delete <app>",
|
|
146
145
|
blockers: [{
|
|
147
146
|
name: "G <HUB>",
|
|
148
|
-
|
|
147
|
+
serviceName: "lghub-service",
|
|
149
148
|
pid: 4321,
|
|
150
149
|
}],
|
|
151
150
|
remainingCount: 25,
|
|
@@ -154,10 +153,11 @@ test("path-removal modal renders identified blockers and escapes backend text",
|
|
|
154
153
|
retry: async () => ({ ok: true }),
|
|
155
154
|
})
|
|
156
155
|
|
|
157
|
-
assert.equal(state.fireOptions.title, "
|
|
156
|
+
assert.equal(state.fireOptions.title, "File removal paused")
|
|
158
157
|
assert.match(state.htmlContainer.innerHTML, /G <HUB>/)
|
|
159
158
|
assert.doesNotMatch(state.htmlContainer.innerHTML, /G <HUB>/)
|
|
160
|
-
assert.match(state.htmlContainer.textContent, /
|
|
159
|
+
assert.match(state.htmlContainer.textContent, /lghub-service · PID 4321/)
|
|
160
|
+
assert.match(state.htmlContainer.textContent, /because one or more files are still in use/)
|
|
161
161
|
assert.match(state.htmlContainer.textContent, /Show first 20 of 25 remaining items/)
|
|
162
162
|
assert.match(state.htmlContainer.textContent, /Close the app above/)
|
|
163
163
|
assert.equal(state.htmlContainer.querySelector(".pinokio-path-blocker-icon"), null)
|
|
@@ -175,7 +175,6 @@ test("path-removal modal reports unattributed EACCES without naming an applicati
|
|
|
175
175
|
|
|
176
176
|
const shown = api.show({
|
|
177
177
|
details: {
|
|
178
|
-
operation: "Delete app",
|
|
179
178
|
causeCode: "EACCES",
|
|
180
179
|
blockers: [],
|
|
181
180
|
remainingCount: 1,
|
|
@@ -198,20 +197,19 @@ test("path-removal request retries automatically without overlapping and resolve
|
|
|
198
197
|
t.after(() => dom.window.close())
|
|
199
198
|
|
|
200
199
|
let requestCount = 0
|
|
201
|
-
|
|
200
|
+
let firstRequest
|
|
202
201
|
let resolveRetry
|
|
203
202
|
const retryResponse = new Promise((resolve) => {
|
|
204
203
|
resolveRetry = resolve
|
|
205
204
|
})
|
|
206
|
-
|
|
205
|
+
dom.window.fetch = async (url, options) => {
|
|
207
206
|
requestCount += 1
|
|
208
|
-
|
|
207
|
+
if (requestCount === 1) firstRequest = { url, options }
|
|
209
208
|
if (requestCount === 1) {
|
|
210
209
|
return response({
|
|
211
210
|
success: false,
|
|
212
211
|
error: {
|
|
213
212
|
code: "PINOKIO_PATH_REMOVE_BLOCKED",
|
|
214
|
-
operation: "Replace Miniforge",
|
|
215
213
|
blockers: [{ name: "First blocker", pid: 10 }],
|
|
216
214
|
remainingCount: 1,
|
|
217
215
|
remaining: ["C:\\runtime\\first.dll"],
|
|
@@ -220,9 +218,13 @@ test("path-removal request retries automatically without overlapping and resolve
|
|
|
220
218
|
}
|
|
221
219
|
if (requestCount === 2) return await retryResponse
|
|
222
220
|
return response({ ok: true }, { ok: true, status: 200 })
|
|
223
|
-
}
|
|
221
|
+
}
|
|
222
|
+
const resultPromise = api.remove({ type: "bin" })
|
|
224
223
|
|
|
225
224
|
await waitFor(() => state.fireOptions, "path-removal modal")
|
|
225
|
+
assert.equal(firstRequest.url, "/pinokio/delete")
|
|
226
|
+
assert.equal(firstRequest.options.method, "post")
|
|
227
|
+
assert.deepEqual(JSON.parse(firstRequest.options.body), { type: "bin" })
|
|
226
228
|
const intervalId = state.getRetryIntervalId()
|
|
227
229
|
assert.equal(state.intervals.get(intervalId).delay, 2000)
|
|
228
230
|
state.htmlContainer.querySelector(".pinokio-path-blocker-files").open = true
|
|
@@ -237,7 +239,6 @@ test("path-removal request retries automatically without overlapping and resolve
|
|
|
237
239
|
success: false,
|
|
238
240
|
error: {
|
|
239
241
|
code: "PINOKIO_PATH_REMOVE_BLOCKED",
|
|
240
|
-
operation: "Replace Miniforge",
|
|
241
242
|
blockers: [{ name: "Second blocker", pid: 20 }],
|
|
242
243
|
remainingCount: 1,
|
|
243
244
|
remaining: ["C:\\runtime\\second.dll"],
|
|
@@ -246,45 +247,52 @@ test("path-removal request retries automatically without overlapping and resolve
|
|
|
246
247
|
await firstAttempt
|
|
247
248
|
assert.equal(state.buttonsDisabled, false)
|
|
248
249
|
assert.equal(state.htmlContainer.querySelector(".pinokio-path-blocker-files").open, true)
|
|
249
|
-
assert.match(state.htmlContainer.textContent, /
|
|
250
|
-
assert.doesNotMatch(state.htmlContainer.textContent, /
|
|
250
|
+
assert.match(state.htmlContainer.textContent, /First blocker/)
|
|
251
|
+
assert.doesNotMatch(state.htmlContainer.textContent, /Second blocker/)
|
|
251
252
|
|
|
252
253
|
await state.runInterval()
|
|
253
254
|
const result = await resultPromise
|
|
254
255
|
assert.equal(result.ok, true)
|
|
255
256
|
assert.equal(requestCount, 3)
|
|
256
|
-
assert.deepEqual(backgroundRequests, [false, true, true])
|
|
257
257
|
assert.equal(state.closeCount, 1)
|
|
258
258
|
assert.equal(state.intervals.has(intervalId), false)
|
|
259
259
|
assert.ok(state.clearedIntervals.has(intervalId))
|
|
260
260
|
})
|
|
261
261
|
|
|
262
|
-
test("background
|
|
262
|
+
test("background blocked response keeps the current modal without rerendering", async (t) => {
|
|
263
263
|
const { api, dom, state } = await createHarness()
|
|
264
264
|
t.after(() => dom.window.close())
|
|
265
265
|
|
|
266
266
|
let requestCount = 0
|
|
267
|
-
|
|
267
|
+
dom.window.fetch = async () => {
|
|
268
268
|
requestCount += 1
|
|
269
269
|
if (requestCount === 1) {
|
|
270
270
|
return response({
|
|
271
271
|
error: {
|
|
272
272
|
code: "PINOKIO_PATH_REMOVE_BLOCKED",
|
|
273
|
-
operation: "Delete app",
|
|
274
273
|
blockers: [{ name: "Original blocker", pid: 10 }],
|
|
275
274
|
remainingCount: 1,
|
|
276
275
|
remaining: ["C:\\app\\locked.dll"],
|
|
277
276
|
},
|
|
278
277
|
})
|
|
279
278
|
}
|
|
280
|
-
return response({
|
|
281
|
-
|
|
279
|
+
return response({
|
|
280
|
+
error: {
|
|
281
|
+
code: "PINOKIO_PATH_REMOVE_BLOCKED",
|
|
282
|
+
blockers: [{ name: "Updated blocker", pid: 20 }],
|
|
283
|
+
remainingCount: 1,
|
|
284
|
+
remaining: ["C:\\app\\updated.dll"],
|
|
285
|
+
},
|
|
286
|
+
})
|
|
287
|
+
}
|
|
288
|
+
const shown = api.remove({ name: "app" })
|
|
282
289
|
|
|
283
290
|
await waitFor(() => state.fireOptions, "path-removal modal")
|
|
284
291
|
await state.runInterval()
|
|
285
292
|
assert.equal(requestCount, 2)
|
|
286
293
|
assert.equal(state.visible, true)
|
|
287
294
|
assert.match(state.htmlContainer.textContent, /Original blocker/)
|
|
295
|
+
assert.doesNotMatch(state.htmlContainer.textContent, /Updated blocker/)
|
|
288
296
|
|
|
289
297
|
state.dismiss()
|
|
290
298
|
assert.equal((await shown).cancelled, true)
|
|
@@ -300,7 +308,6 @@ test("Stop remains available during a background retry and ignores its late resp
|
|
|
300
308
|
})
|
|
301
309
|
const shown = api.show({
|
|
302
310
|
details: {
|
|
303
|
-
operation: "Delete app",
|
|
304
311
|
blockers: [{ name: "Original blocker", pid: 10 }],
|
|
305
312
|
remainingCount: 1,
|
|
306
313
|
remaining: ["C:\\app\\original.dll"],
|
|
@@ -313,7 +320,6 @@ test("Stop remains available during a background retry and ignores its late resp
|
|
|
313
320
|
assert.equal(state.dismiss(), true)
|
|
314
321
|
resolveRetry({
|
|
315
322
|
code: "PINOKIO_PATH_REMOVE_BLOCKED",
|
|
316
|
-
operation: "Delete app",
|
|
317
323
|
blockers: [{ name: "Late blocker", pid: 20 }],
|
|
318
324
|
remainingCount: 1,
|
|
319
325
|
remaining: ["C:\\app\\late.dll"],
|
|
@@ -332,7 +338,6 @@ test("manual retry keeps the modal open when blocked and Stop cancels future ret
|
|
|
332
338
|
let retryCount = 0
|
|
333
339
|
const shown = api.show({
|
|
334
340
|
details: {
|
|
335
|
-
operation: "Delete app",
|
|
336
341
|
blockers: [{ name: "Lock holder", pid: 99 }],
|
|
337
342
|
remainingCount: 1,
|
|
338
343
|
remaining: ["C:\\app\\locked.dll"],
|
|
@@ -341,7 +346,6 @@ test("manual retry keeps the modal open when blocked and Stop cancels future ret
|
|
|
341
346
|
retryCount += 1
|
|
342
347
|
return {
|
|
343
348
|
code: "PINOKIO_PATH_REMOVE_BLOCKED",
|
|
344
|
-
operation: "Delete app",
|
|
345
349
|
blockers: [{ name: "Lock holder", pid: 99 }],
|
|
346
350
|
remainingCount: 1,
|
|
347
351
|
remaining: ["C:\\app\\locked.dll"],
|
|
@@ -369,7 +373,6 @@ test("manual retry escapes unexpected error messages", async (t) => {
|
|
|
369
373
|
|
|
370
374
|
const shown = api.show({
|
|
371
375
|
details: {
|
|
372
|
-
operation: "Delete app",
|
|
373
376
|
blockers: [],
|
|
374
377
|
remainingCount: 1,
|
|
375
378
|
remaining: ["C:\\app\\locked.dll"],
|
|
@@ -387,6 +390,27 @@ test("manual retry escapes unexpected error messages", async (t) => {
|
|
|
387
390
|
assert.equal((await shown).cancelled, true)
|
|
388
391
|
})
|
|
389
392
|
|
|
393
|
+
test("ordinary retry errors keep the modal open", async (t) => {
|
|
394
|
+
const { api, dom, state } = await createHarness()
|
|
395
|
+
t.after(() => dom.window.close())
|
|
396
|
+
|
|
397
|
+
const shown = api.show({
|
|
398
|
+
details: {
|
|
399
|
+
blockers: [],
|
|
400
|
+
remainingCount: 1,
|
|
401
|
+
remaining: ["C:\\app\\locked.dll"],
|
|
402
|
+
},
|
|
403
|
+
retry: async () => ({ error: "Cleanup failed" }),
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
assert.equal(await state.fireOptions.preConfirm(), false)
|
|
407
|
+
assert.equal(state.visible, true)
|
|
408
|
+
assert.equal(state.validationMessage, "Cleanup failed")
|
|
409
|
+
|
|
410
|
+
state.dismiss()
|
|
411
|
+
assert.equal((await shown).cancelled, true)
|
|
412
|
+
})
|
|
413
|
+
|
|
390
414
|
test("successful runtime cleanup resubmits the interrupted install", async (t) => {
|
|
391
415
|
const source = await fs.readFile(installViewPath, "utf8")
|
|
392
416
|
const requirements = '[{"name":"conda"}]'
|
|
@@ -398,6 +422,7 @@ test("successful runtime cleanup resubmits the interrupted install", async (t) =
|
|
|
398
422
|
}, { filename: installViewPath })
|
|
399
423
|
|
|
400
424
|
let rpc
|
|
425
|
+
let cleanupPayload
|
|
401
426
|
let submission
|
|
402
427
|
let submitted
|
|
403
428
|
const submittedPromise = new Promise((resolve) => { submitted = resolve })
|
|
@@ -424,8 +449,11 @@ test("successful runtime cleanup resubmits the interrupted install", async (t) =
|
|
|
424
449
|
window.xtermTheme = { FrontEndDelight: {} }
|
|
425
450
|
window.PinokioTouch = { bindTerminalFocus() {} }
|
|
426
451
|
window.PinokioPathRemoval = {
|
|
427
|
-
|
|
428
|
-
|
|
452
|
+
show: async ({ retry }) => await retry(),
|
|
453
|
+
tryRemove: async (payload) => {
|
|
454
|
+
cleanupPayload = payload
|
|
455
|
+
return { success: true }
|
|
456
|
+
},
|
|
429
457
|
}
|
|
430
458
|
window.Socket = class {
|
|
431
459
|
emit() {}
|
|
@@ -451,6 +479,7 @@ test("successful runtime cleanup resubmits the interrupted install", async (t) =
|
|
|
451
479
|
|
|
452
480
|
await submittedPromise
|
|
453
481
|
assert.equal(rpc.params, requirements)
|
|
482
|
+
assert.equal(cleanupPayload.type, "conda-runtime")
|
|
454
483
|
assert.deepEqual(submission, {
|
|
455
484
|
method: "post",
|
|
456
485
|
pathname: "/pinokio/install",
|
|
@@ -494,7 +523,7 @@ test("fresh install shows removal status and waits for cleanup before starting",
|
|
|
494
523
|
window.xtermTheme = { FrontEndDelight: {} }
|
|
495
524
|
window.PinokioTouch = { bindTerminalFocus() {} }
|
|
496
525
|
window.PinokioPathRemoval = {
|
|
497
|
-
|
|
526
|
+
remove: async () => {
|
|
498
527
|
cleanupStarted = true
|
|
499
528
|
return await cleanup
|
|
500
529
|
},
|