pinokiod 8.0.21 → 8.0.23
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/index.js +0 -13
- package/server/public/common.js +26 -14
- package/server/public/style.css +0 -22
- package/server/views/file_explorer.ejs +0 -6
- package/server/views/index.ejs +0 -6
- package/server/views/index2.ejs +0 -6
- package/test/directory-readme-rendering.test.js +125 -0
- package/test/explore-lan-install-navigation.test.js +69 -15
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -3606,7 +3606,6 @@ class Server {
|
|
|
3606
3606
|
|
|
3607
3607
|
let error
|
|
3608
3608
|
let items
|
|
3609
|
-
let readme
|
|
3610
3609
|
// if (pathComponents.length === 0) {
|
|
3611
3610
|
// let files = await fs.promises.readdir(filepath, { withFileTypes: true })
|
|
3612
3611
|
// items = files
|
|
@@ -3629,18 +3628,8 @@ class Server {
|
|
|
3629
3628
|
}
|
|
3630
3629
|
}
|
|
3631
3630
|
|
|
3632
|
-
// look for README.md
|
|
3633
|
-
|
|
3634
3631
|
let config
|
|
3635
3632
|
for(let file of f.files) {
|
|
3636
|
-
if (file.name.toLowerCase() === "readme.md") {
|
|
3637
|
-
let p = path.resolve(filepath, file.name)
|
|
3638
|
-
let md = await fs.promises.readFile(p, "utf8")
|
|
3639
|
-
readme = marked.parse(md, {
|
|
3640
|
-
baseUrl: req._parsedUrl.pathname.replace(/^\/_api/, "/raw/") + "/"
|
|
3641
|
-
//baseUrl: req.originalUrl + "/"
|
|
3642
|
-
})
|
|
3643
|
-
}
|
|
3644
3633
|
if (file.name === "pinokio.js") {
|
|
3645
3634
|
let p = path.resolve(filepath, file.name)
|
|
3646
3635
|
config = (await this.kernel.loader.load(p)).resolved
|
|
@@ -4297,7 +4286,6 @@ class Server {
|
|
|
4297
4286
|
home_sort: homeSortMode,
|
|
4298
4287
|
running,
|
|
4299
4288
|
notRunning,
|
|
4300
|
-
readme,
|
|
4301
4289
|
filepath,
|
|
4302
4290
|
mode: null,
|
|
4303
4291
|
kernel: this.kernel,
|
|
@@ -4329,7 +4317,6 @@ class Server {
|
|
|
4329
4317
|
ishome: meta,
|
|
4330
4318
|
running,
|
|
4331
4319
|
notRunning,
|
|
4332
|
-
readme,
|
|
4333
4320
|
filepath,
|
|
4334
4321
|
mode: null,
|
|
4335
4322
|
kernel: this.kernel,
|
package/server/public/common.js
CHANGED
|
@@ -3013,15 +3013,19 @@ if (typeof window !== 'undefined' && !window.__pinokioNavigateListenerInstalled)
|
|
|
3013
3013
|
return;
|
|
3014
3014
|
}
|
|
3015
3015
|
}
|
|
3016
|
+
// Only the registry iframe rendered by the Explore page uses this stable name.
|
|
3017
|
+
const exploreFrame = document.querySelector('iframe[name="pinokio-explore"]');
|
|
3018
|
+
// Trust the message only when it comes from that exact iframe and its configured origin.
|
|
3019
|
+
const fromExplore = Boolean(
|
|
3020
|
+
exploreFrame &&
|
|
3021
|
+
event.source === exploreFrame.contentWindow &&
|
|
3022
|
+
event.origin === new URL(exploreFrame.src).origin
|
|
3023
|
+
);
|
|
3016
3024
|
let frame = document.activeElement;
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
) {
|
|
3022
|
-
const exploreFrame = document.querySelector('main > iframe');
|
|
3023
|
-
if (exploreFrame && event.source === exploreFrame.contentWindow) frame = exploreFrame;
|
|
3024
|
-
}
|
|
3025
|
+
// Mobile Safari can leave the parent body focused after a tap inside the registry iframe.
|
|
3026
|
+
const exploreFallback = (!frame || frame.tagName !== 'IFRAME') && fromExplore;
|
|
3027
|
+
if (exploreFallback) frame = exploreFrame;
|
|
3028
|
+
// Preserve the existing generic behavior when there is no identifiable sending iframe.
|
|
3025
3029
|
if (!frame || frame.tagName !== 'IFRAME') {
|
|
3026
3030
|
try {
|
|
3027
3031
|
console.warn('[pinokio:navigate] no active iframe');
|
|
@@ -3037,23 +3041,31 @@ if (typeof window !== 'undefined' && !window.__pinokioNavigateListenerInstalled)
|
|
|
3037
3041
|
} catch (_) {}
|
|
3038
3042
|
return;
|
|
3039
3043
|
}
|
|
3044
|
+
// Only translate the two local Pinokio actions emitted by the registry.
|
|
3040
3045
|
const localInstall =
|
|
3041
3046
|
target.origin === 'http://localhost:42000' &&
|
|
3042
3047
|
(
|
|
3048
|
+
// A normal launcher install must include the repository URI.
|
|
3043
3049
|
(target.pathname === '/pinokio/download' && Boolean(target.searchParams.get('uri'))) ||
|
|
3044
3050
|
(
|
|
3051
|
+
// A snapshot restore must include its registry and a canonical SHA-256 hash.
|
|
3045
3052
|
target.pathname === '/checkpoints' &&
|
|
3046
3053
|
Boolean(target.searchParams.get('registry')) &&
|
|
3047
3054
|
/^(?:sha256:)?[0-9a-f]{64}$/i.test(target.searchParams.get('hash') || '')
|
|
3048
3055
|
)
|
|
3049
3056
|
);
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3057
|
+
// Handle trusted Explore installs here so Pinokio replaces the parent page, not the iframe.
|
|
3058
|
+
if (fromExplore && localInstall) {
|
|
3059
|
+
// LAN/Home Server clients must use the Pinokio origin they can actually reach.
|
|
3060
|
+
if (target.origin !== window.location.origin) {
|
|
3061
|
+
target = new URL(`${target.pathname}${target.search}${target.hash}`, window.location.origin);
|
|
3062
|
+
}
|
|
3063
|
+
window.location.href = target.toString();
|
|
3064
|
+
return;
|
|
3056
3065
|
}
|
|
3066
|
+
// If focus was recovered through the Explore fallback, reject every non-allowlisted action.
|
|
3067
|
+
if (exploreFallback) return;
|
|
3068
|
+
// Keep the existing generic iframe navigation restricted to the current Pinokio origin.
|
|
3057
3069
|
if (target.origin !== window.location.origin) {
|
|
3058
3070
|
try {
|
|
3059
3071
|
console.warn('[pinokio:navigate] blocked origin', target.origin);
|
package/server/public/style.css
CHANGED
|
@@ -2637,13 +2637,6 @@ pre {
|
|
|
2637
2637
|
pre:empty {
|
|
2638
2638
|
display: none;
|
|
2639
2639
|
}
|
|
2640
|
-
body.dark .readme {
|
|
2641
|
-
background: none;
|
|
2642
|
-
color: var(--dark-color);
|
|
2643
|
-
}
|
|
2644
|
-
.readme .content {
|
|
2645
|
-
padding: 0 20px 20px;
|
|
2646
|
-
}
|
|
2647
2640
|
body.dark .empty {
|
|
2648
2641
|
background: var(--dark-bg);
|
|
2649
2642
|
}
|
|
@@ -2653,21 +2646,6 @@ body.dark .empty {
|
|
|
2653
2646
|
opacity: 0.5;
|
|
2654
2647
|
background: var(--light-bg);
|
|
2655
2648
|
}
|
|
2656
|
-
.readme {
|
|
2657
|
-
max-width: var(--content-width);
|
|
2658
|
-
box-sizing: border-box;
|
|
2659
|
-
margin: 0 auto;
|
|
2660
|
-
}
|
|
2661
|
-
body.dark .readme a {
|
|
2662
|
-
color: var(--dark-link-color);
|
|
2663
|
-
}
|
|
2664
|
-
.readme a {
|
|
2665
|
-
color: var(--light-link-color);
|
|
2666
|
-
text-decoration: none;
|
|
2667
|
-
}
|
|
2668
|
-
.readme ol, .readme ul {
|
|
2669
|
-
padding-inline-start: 20px;
|
|
2670
|
-
}
|
|
2671
2649
|
.runnables .row {
|
|
2672
2650
|
display: flex;
|
|
2673
2651
|
padding: 10px;
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
<link href="/css/solid.min.css" rel="stylesheet">
|
|
9
9
|
<link href="/css/regular.min.css" rel="stylesheet">
|
|
10
10
|
<link href="/css/brands.min.css" rel="stylesheet">
|
|
11
|
-
<link href="/markdown.css" rel="stylesheet"/>
|
|
12
11
|
<link href="/noty.css" rel="stylesheet"/>
|
|
13
12
|
<link href="/style.css" rel="stylesheet"/>
|
|
14
13
|
<% if (agent === "electron") { %>
|
|
@@ -230,11 +229,6 @@ body.dark .browser-options-row {
|
|
|
230
229
|
<div class='description'><%=item.description%></div>
|
|
231
230
|
</a>
|
|
232
231
|
<% }) %>
|
|
233
|
-
<% if (readme) { %>
|
|
234
|
-
<div class='readme markdown-body'>
|
|
235
|
-
<div class='content'><%-readme%></div>
|
|
236
|
-
</div>
|
|
237
|
-
<% } %>
|
|
238
232
|
</div>
|
|
239
233
|
</main>
|
|
240
234
|
<script>
|
package/server/views/index.ejs
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
<link href="/css/solid.min.css" rel="stylesheet">
|
|
9
9
|
<link href="/css/regular.min.css" rel="stylesheet">
|
|
10
10
|
<link href="/css/brands.min.css" rel="stylesheet">
|
|
11
|
-
<link href="/markdown.css" rel="stylesheet"/>
|
|
12
11
|
<link href="/noty.css" rel="stylesheet"/>
|
|
13
12
|
<link href="/filepond.min.css" rel="stylesheet" />
|
|
14
13
|
<link href="/filepond-plugin-image-preview.min.css" rel="stylesheet" />
|
|
@@ -2000,11 +1999,6 @@ body.dark aside .current.selected {
|
|
|
2000
1999
|
</a>
|
|
2001
2000
|
<% }) %>
|
|
2002
2001
|
<% } %>
|
|
2003
|
-
<% if (readme) { %>
|
|
2004
|
-
<div class='readme markdown-body'>
|
|
2005
|
-
<div class='content'><%-readme%></div>
|
|
2006
|
-
</div>
|
|
2007
|
-
<% } %>
|
|
2008
2002
|
</div>
|
|
2009
2003
|
<%- include('partials/main_sidebar', { selected: 'home' }) %>
|
|
2010
2004
|
</main>
|
package/server/views/index2.ejs
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
<link href="/css/solid.min.css" rel="stylesheet">
|
|
9
9
|
<link href="/css/regular.min.css" rel="stylesheet">
|
|
10
10
|
<link href="/css/brands.min.css" rel="stylesheet">
|
|
11
|
-
<link href="/markdown.css" rel="stylesheet"/>
|
|
12
11
|
<link href="/noty.css" rel="stylesheet"/>
|
|
13
12
|
<link href="/filepond.min.css" rel="stylesheet" />
|
|
14
13
|
<link href="/filepond-plugin-image-preview.min.css" rel="stylesheet" />
|
|
@@ -500,11 +499,6 @@ body.dark .open-menu, body.dark .browse {
|
|
|
500
499
|
</a>
|
|
501
500
|
<% }) %>
|
|
502
501
|
<% } %>
|
|
503
|
-
<% if (readme) { %>
|
|
504
|
-
<div class='readme markdown-body'>
|
|
505
|
-
<div class='content'><%-readme%></div>
|
|
506
|
-
</div>
|
|
507
|
-
<% } %>
|
|
508
502
|
<% if (display.includes("onboarding")) { %>
|
|
509
503
|
<!--
|
|
510
504
|
<div class='container'>
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const assert = require('node:assert/strict')
|
|
2
|
+
const fs = require('node:fs')
|
|
3
|
+
const http = require('node:http')
|
|
4
|
+
const os = require('node:os')
|
|
5
|
+
const path = require('node:path')
|
|
6
|
+
const test = require('node:test')
|
|
7
|
+
const express = require('express')
|
|
8
|
+
const Server = require('../server')
|
|
9
|
+
|
|
10
|
+
const viewsPath = path.resolve(__dirname, '..', 'server', 'views')
|
|
11
|
+
|
|
12
|
+
async function createDirectoryRendererFixture() {
|
|
13
|
+
const homedir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'pinokio-directory-readme-'))
|
|
14
|
+
const apiRoot = path.resolve(homedir, 'api')
|
|
15
|
+
const appRoot = path.resolve(apiRoot, 'demo')
|
|
16
|
+
await fs.promises.mkdir(appRoot, { recursive: true })
|
|
17
|
+
await fs.promises.writeFile(path.resolve(apiRoot, 'README.md'), '# ROOT README SENTINEL\n')
|
|
18
|
+
await fs.promises.writeFile(path.resolve(appRoot, 'README.md'), '# APP README SENTINEL\n')
|
|
19
|
+
await fs.promises.writeFile(path.resolve(appRoot, 'keep.txt'), 'keep\n')
|
|
20
|
+
|
|
21
|
+
const renderer = Object.create(Server.prototype)
|
|
22
|
+
renderer.kernel = {
|
|
23
|
+
homedir,
|
|
24
|
+
path: (...segments) => path.resolve(homedir, ...segments),
|
|
25
|
+
api: {
|
|
26
|
+
userdir: apiRoot,
|
|
27
|
+
proxies: {},
|
|
28
|
+
running: {},
|
|
29
|
+
launcher: async (name) => ({
|
|
30
|
+
script: null,
|
|
31
|
+
launcher_root: ''
|
|
32
|
+
})
|
|
33
|
+
},
|
|
34
|
+
dns: async ({ name }) => {
|
|
35
|
+
renderer.kernel.pinokio_configs[name] = { dns: { '@': [] } }
|
|
36
|
+
},
|
|
37
|
+
peer: {
|
|
38
|
+
host: '127.0.0.1',
|
|
39
|
+
check_peers: async () => {}
|
|
40
|
+
},
|
|
41
|
+
launch_complete: true,
|
|
42
|
+
pinokio_configs: {}
|
|
43
|
+
}
|
|
44
|
+
renderer.autolaunch = {
|
|
45
|
+
applyHomeStartingState: async () => false
|
|
46
|
+
}
|
|
47
|
+
renderer.portal = ''
|
|
48
|
+
renderer.install = ''
|
|
49
|
+
renderer.port = 0
|
|
50
|
+
renderer.theme = 'light'
|
|
51
|
+
renderer.logo = ''
|
|
52
|
+
renderer.cloudflare_pub = null
|
|
53
|
+
renderer.composePeerAccessPayload = async () => ({
|
|
54
|
+
peer_access_points: [],
|
|
55
|
+
peer_url: 'http://127.0.0.1:42000',
|
|
56
|
+
peer_qr: null,
|
|
57
|
+
peer_access_router_installed: false
|
|
58
|
+
})
|
|
59
|
+
renderer.current_urls = async () => ({})
|
|
60
|
+
renderer.getPeers = () => []
|
|
61
|
+
|
|
62
|
+
const app = express()
|
|
63
|
+
app.set('views', viewsPath)
|
|
64
|
+
app.set('view engine', 'ejs')
|
|
65
|
+
app.get('/home', (req, res, next) => {
|
|
66
|
+
renderer.render(req, res, [], {}).catch(next)
|
|
67
|
+
})
|
|
68
|
+
app.get('/_api/demo', (req, res, next) => {
|
|
69
|
+
req.query.mode = 'source'
|
|
70
|
+
renderer.render(req, res, ['demo']).catch(next)
|
|
71
|
+
})
|
|
72
|
+
app.get('/api/demo', (req, res, next) => {
|
|
73
|
+
renderer.render(req, res, ['demo']).catch(next)
|
|
74
|
+
})
|
|
75
|
+
app.use((error, req, res, next) => {
|
|
76
|
+
res.status(500).send(error.stack || error.message)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const httpServer = http.createServer(app)
|
|
80
|
+
await new Promise((resolve, reject) => {
|
|
81
|
+
httpServer.once('error', reject)
|
|
82
|
+
httpServer.listen(0, '127.0.0.1', resolve)
|
|
83
|
+
})
|
|
84
|
+
const address = httpServer.address()
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
baseUrl: `http://127.0.0.1:${address.port}`,
|
|
88
|
+
close: async () => {
|
|
89
|
+
await new Promise((resolve, reject) => {
|
|
90
|
+
httpServer.close((error) => error ? reject(error) : resolve())
|
|
91
|
+
})
|
|
92
|
+
await fs.promises.rm(homedir, { recursive: true, force: true })
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
test('directory pages list README files without auto-rendering their contents', async () => {
|
|
98
|
+
const fixture = await createDirectoryRendererFixture()
|
|
99
|
+
try {
|
|
100
|
+
const [homeResponse, sourceResponse, runResponse] = await Promise.all([
|
|
101
|
+
fetch(`${fixture.baseUrl}/home`),
|
|
102
|
+
fetch(`${fixture.baseUrl}/_api/demo`),
|
|
103
|
+
fetch(`${fixture.baseUrl}/api/demo`)
|
|
104
|
+
])
|
|
105
|
+
const [homeHtml, sourceHtml, runHtml] = await Promise.all([
|
|
106
|
+
homeResponse.text(),
|
|
107
|
+
sourceResponse.text(),
|
|
108
|
+
runResponse.text()
|
|
109
|
+
])
|
|
110
|
+
|
|
111
|
+
assert.equal(homeResponse.status, 200, homeHtml)
|
|
112
|
+
assert.equal(sourceResponse.status, 200, sourceHtml)
|
|
113
|
+
assert.equal(runResponse.status, 200, runHtml)
|
|
114
|
+
|
|
115
|
+
assert.doesNotMatch(homeHtml, /ROOT README SENTINEL/)
|
|
116
|
+
for (const html of [sourceHtml, runHtml]) {
|
|
117
|
+
assert.doesNotMatch(html, /APP README SENTINEL/)
|
|
118
|
+
assert.match(html, /data-name="README\.md"/)
|
|
119
|
+
assert.match(html, /data-name="keep\.txt"/)
|
|
120
|
+
assert.doesNotMatch(html, /class=['"]readme markdown-body['"]/)
|
|
121
|
+
}
|
|
122
|
+
} finally {
|
|
123
|
+
await fixture.close()
|
|
124
|
+
}
|
|
125
|
+
})
|
|
@@ -7,18 +7,34 @@ const { JSDOM, VirtualConsole } = require("jsdom")
|
|
|
7
7
|
const commonScriptPath = path.resolve(__dirname, "../server/public/common.js")
|
|
8
8
|
const exploreTemplatePath = path.resolve(__dirname, "../server/views/explore.ejs")
|
|
9
9
|
|
|
10
|
-
async function createHarness(
|
|
10
|
+
async function createHarness(
|
|
11
|
+
url = "http://192.168.1.50:42000/home?mode=explore",
|
|
12
|
+
frameUrl = "https://pinokio.co/?embed=1&theme=dark",
|
|
13
|
+
) {
|
|
11
14
|
const source = await fs.readFile(commonScriptPath, "utf8")
|
|
12
15
|
const virtualConsole = new VirtualConsole()
|
|
16
|
+
const navigationErrors = []
|
|
13
17
|
virtualConsole.on("jsdomError", (error) => {
|
|
18
|
+
if (error && /Not implemented: navigation/.test(error.message || "")) {
|
|
19
|
+
navigationErrors.push(error)
|
|
20
|
+
return
|
|
21
|
+
}
|
|
14
22
|
throw error
|
|
15
23
|
})
|
|
16
|
-
const dom = new JSDOM(
|
|
24
|
+
const dom = new JSDOM('<!doctype html><body><main><iframe name="pinokio-explore"></iframe></main></body>', {
|
|
17
25
|
runScripts: "dangerously",
|
|
18
26
|
pretendToBeVisual: true,
|
|
19
27
|
url,
|
|
20
28
|
virtualConsole,
|
|
21
29
|
beforeParse(window) {
|
|
30
|
+
const NativeURL = window.URL
|
|
31
|
+
window.__pinokioConstructedUrls = []
|
|
32
|
+
window.URL = class extends NativeURL {
|
|
33
|
+
constructor(value, base) {
|
|
34
|
+
super(value, base)
|
|
35
|
+
window.__pinokioConstructedUrls.push(this.toString())
|
|
36
|
+
}
|
|
37
|
+
}
|
|
22
38
|
window.fetch = async () => ({ ok: true, json: async () => ({}) })
|
|
23
39
|
window.matchMedia = () => ({
|
|
24
40
|
matches: false,
|
|
@@ -35,13 +51,14 @@ async function createHarness(url = "http://192.168.1.50:42000/home?mode=explore"
|
|
|
35
51
|
script.textContent = source
|
|
36
52
|
dom.window.document.body.appendChild(script)
|
|
37
53
|
const frame = dom.window.document.querySelector("iframe")
|
|
38
|
-
frame.src =
|
|
54
|
+
frame.src = frameUrl
|
|
39
55
|
frame.focus()
|
|
40
56
|
assert.equal(dom.window.document.activeElement, frame)
|
|
41
|
-
return { dom, frame }
|
|
57
|
+
return { dom, frame, navigationErrors }
|
|
42
58
|
}
|
|
43
59
|
|
|
44
60
|
function navigate(dom, frame, target, options = {}) {
|
|
61
|
+
dom.window.__pinokioConstructedUrls.length = 0
|
|
45
62
|
dom.window.dispatchEvent(new dom.window.MessageEvent("message", {
|
|
46
63
|
data: { e: "pinokio:navigate", url: target },
|
|
47
64
|
origin: options.origin || "https://pinokio.co",
|
|
@@ -49,6 +66,12 @@ function navigate(dom, frame, target, options = {}) {
|
|
|
49
66
|
}))
|
|
50
67
|
}
|
|
51
68
|
|
|
69
|
+
function assertParentNavigation(dom, frame, navigationErrors, originalFrameUrl, expectedUrl) {
|
|
70
|
+
assert.ok(dom.window.__pinokioConstructedUrls.includes(expectedUrl))
|
|
71
|
+
assert.equal(navigationErrors.length, 1)
|
|
72
|
+
assert.equal(frame.src, originalFrameUrl)
|
|
73
|
+
}
|
|
74
|
+
|
|
52
75
|
test("Explore uses a stable frame identity instead of the launcher schema", async () => {
|
|
53
76
|
const source = await fs.readFile(exploreTemplatePath, "utf8")
|
|
54
77
|
|
|
@@ -56,46 +79,77 @@ test("Explore uses a stable frame identity instead of the launcher schema", asyn
|
|
|
56
79
|
assert.doesNotMatch(source, /name="<%=schema%>"/)
|
|
57
80
|
})
|
|
58
81
|
|
|
59
|
-
test("LAN Explore
|
|
60
|
-
const { dom, frame } = await createHarness(
|
|
82
|
+
test("LAN Explore trusts the configured registry origin", async (t) => {
|
|
83
|
+
const { dom, frame, navigationErrors } = await createHarness(
|
|
84
|
+
undefined,
|
|
85
|
+
"https://beta.pinokio.co/?embed=1&theme=dark",
|
|
86
|
+
)
|
|
61
87
|
t.after(() => dom.window.close())
|
|
88
|
+
const originalFrameUrl = frame.src
|
|
62
89
|
|
|
63
|
-
navigate(
|
|
90
|
+
navigate(
|
|
91
|
+
dom,
|
|
92
|
+
frame,
|
|
93
|
+
"http://localhost:42000/pinokio/download?uri=https%3A%2F%2Fgithub.com%2Facme%2Fdemo&path=api&branch=dev",
|
|
94
|
+
{ origin: "https://beta.pinokio.co" },
|
|
95
|
+
)
|
|
64
96
|
|
|
65
|
-
|
|
66
|
-
|
|
97
|
+
assertParentNavigation(
|
|
98
|
+
dom,
|
|
99
|
+
frame,
|
|
100
|
+
navigationErrors,
|
|
101
|
+
originalFrameUrl,
|
|
67
102
|
"http://192.168.1.50:42000/pinokio/download?uri=https%3A%2F%2Fgithub.com%2Facme%2Fdemo&path=api&branch=dev",
|
|
68
103
|
)
|
|
69
104
|
})
|
|
70
105
|
|
|
71
106
|
test("LAN Explore handles registry installs when mobile Safari leaves the parent body focused", async (t) => {
|
|
72
|
-
const { dom, frame } = await createHarness()
|
|
107
|
+
const { dom, frame, navigationErrors } = await createHarness()
|
|
73
108
|
t.after(() => dom.window.close())
|
|
109
|
+
const originalFrameUrl = frame.src
|
|
74
110
|
dom.window.document.body.tabIndex = -1
|
|
75
111
|
dom.window.document.body.focus()
|
|
76
112
|
assert.equal(dom.window.document.activeElement, dom.window.document.body)
|
|
77
113
|
|
|
78
114
|
navigate(dom, frame, "http://localhost:42000/pinokio/download?uri=https%3A%2F%2Fgithub.com%2Facme%2Fdemo&path=api")
|
|
79
115
|
|
|
80
|
-
|
|
81
|
-
|
|
116
|
+
assertParentNavigation(
|
|
117
|
+
dom,
|
|
118
|
+
frame,
|
|
119
|
+
navigationErrors,
|
|
120
|
+
originalFrameUrl,
|
|
82
121
|
"http://192.168.1.50:42000/pinokio/download?uri=https%3A%2F%2Fgithub.com%2Facme%2Fdemo&path=api",
|
|
83
122
|
)
|
|
84
123
|
})
|
|
85
124
|
|
|
86
125
|
test("LAN Explore rebases valid checkpoint installs onto the Home Server origin", async (t) => {
|
|
87
|
-
const { dom, frame } = await createHarness()
|
|
126
|
+
const { dom, frame, navigationErrors } = await createHarness()
|
|
88
127
|
t.after(() => dom.window.close())
|
|
128
|
+
const originalFrameUrl = frame.src
|
|
89
129
|
const hash = `sha256%3A${"a".repeat(64)}`
|
|
90
130
|
|
|
91
131
|
navigate(dom, frame, `http://localhost:42000/checkpoints?registry=https%3A%2F%2Fapi.pinokio.co&hash=${hash}&path=api`)
|
|
92
132
|
|
|
93
|
-
|
|
94
|
-
|
|
133
|
+
assertParentNavigation(
|
|
134
|
+
dom,
|
|
135
|
+
frame,
|
|
136
|
+
navigationErrors,
|
|
137
|
+
originalFrameUrl,
|
|
95
138
|
`http://192.168.1.50:42000/checkpoints?registry=https%3A%2F%2Fapi.pinokio.co&hash=${hash}&path=api`,
|
|
96
139
|
)
|
|
97
140
|
})
|
|
98
141
|
|
|
142
|
+
test("localhost Explore replaces itself for registry installs instead of nesting Pinokio", async (t) => {
|
|
143
|
+
const { dom, frame, navigationErrors } = await createHarness("http://localhost:42000/home?mode=explore")
|
|
144
|
+
t.after(() => dom.window.close())
|
|
145
|
+
const originalFrameUrl = frame.src
|
|
146
|
+
const target = "http://localhost:42000/pinokio/download?uri=https%3A%2F%2Fgithub.com%2Facme%2Fdemo&path=api"
|
|
147
|
+
|
|
148
|
+
navigate(dom, frame, target)
|
|
149
|
+
|
|
150
|
+
assertParentNavigation(dom, frame, navigationErrors, originalFrameUrl, target)
|
|
151
|
+
})
|
|
152
|
+
|
|
99
153
|
const blockedCases = [
|
|
100
154
|
["unrelated local route", "http://localhost:42000/tasker?vars=url", {}],
|
|
101
155
|
["download without a URI", "http://localhost:42000/pinokio/download", {}],
|