chore: establish YCode replication baseline and TODO
This commit is contained in:
commit
6dc59f89ba
1118 files changed
+196002
No files matched your search
@@ -0,0 +1,102 @@
|
||||
// Exercise the actual YCode WebContentsView host against a loopback-only HTTP server.
|
||||
if (!process.versions.electron) {
|
||||
const path = require('node:path')
|
||||
require('esbuild').buildSync({ entryPoints: ['src/main/browser-host.ts'], platform: 'node', format: 'cjs', bundle: true,
|
||||
external: ['electron'], outfile: '.tmp/browser-host-test-module.cjs', logLevel: 'silent' })
|
||||
require('esbuild').buildSync({ entryPoints: ['src/shared/browser-host.ts'], platform: 'node', format: 'cjs', bundle: true,
|
||||
outfile: '.tmp/browser-host-contract.cjs', logLevel: 'silent' })
|
||||
const env = { ...process.env }
|
||||
delete env.ELECTRON_RUN_AS_NODE
|
||||
const result = require('node:child_process').spawnSync(require('electron'), [__filename], { env, windowsHide: true, stdio: 'inherit', timeout: 60000 })
|
||||
process.exitCode = result.status ?? 1
|
||||
} else {
|
||||
const { app, BrowserWindow, WebContentsView } = require('electron')
|
||||
const assert = require('node:assert/strict')
|
||||
const path = require('node:path')
|
||||
const http = require('node:http')
|
||||
app.setPath('userData', path.resolve('.tmp/browser-host-regression-data'))
|
||||
app.disableHardwareAcceleration()
|
||||
const server = http.createServer((req, res) => {
|
||||
const page = req.url === '/b' ? 'B' : 'A'
|
||||
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })
|
||||
res.end(`<html><head><title>Local ${page}</title></head><body>Local ${page}<a href="/b">Next</a></body></html>`)
|
||||
})
|
||||
let owner, other
|
||||
async function waitFor(check, label) {
|
||||
for (let attempt = 0; attempt < 200; attempt++) { if (await check()) return; await new Promise(resolve => setTimeout(resolve, 25)) }
|
||||
throw Error('Browser condition timed out: ' + label)
|
||||
}
|
||||
app.whenReady().then(async () => {
|
||||
const host = require(path.resolve('.tmp/browser-host-test-module.cjs'))
|
||||
const contract = require(path.resolve('.tmp/browser-host-contract.cjs'))
|
||||
assert.equal(contract.normalizeBrowserAddressInput('127.0.0.1:1234/a'), 'http://127.0.0.1:1234/a')
|
||||
assert.equal(contract.normalizeBrowserAddressInput('example.com'), 'https://example.com/')
|
||||
assert.equal(contract.normalizeBrowserAddressInput('hello world'), 'https://www.sogou.com/web?query=hello%20world')
|
||||
assert.throws(() => contract.normalizeBrowserHostUrl('https://user:secret@example.com'), /仅支持/)
|
||||
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve))
|
||||
const base = `http://127.0.0.1:${server.address().port}`
|
||||
owner = new BrowserWindow({ width: 800, height: 600, show: false, webPreferences: { sandbox: true, nodeIntegration: false, contextIsolation: true } })
|
||||
other = new BrowserWindow({ width: 800, height: 600, show: false })
|
||||
const first = host.createBrowser(owner, 'task-a')
|
||||
const second = host.createBrowser(owner, 'task-b')
|
||||
const result = await host.navigateBrowser(owner, first.id, base + '/a', 'task-a')
|
||||
assert.equal(result.title, 'Local A')
|
||||
assert.equal(result.error, undefined)
|
||||
assert.throws(() => host.getBrowserState(owner, first.id, 'task-b'), /不属于/)
|
||||
assert.throws(() => host.getBrowserState(other, first.id, 'task-a'), /不属于/)
|
||||
assert.throws(() => host.getBrowserState(owner, first.id), /不属于/)
|
||||
await assert.rejects(() => host.navigateBrowser(owner, first.id, 'file:///C:/Windows/win.ini', 'task-a'), /仅支持/)
|
||||
await assert.rejects(() => host.navigateBrowser(owner, first.id, 'javascript:alert(1)', 'task-a'), /仅支持/)
|
||||
assert.equal(host.getBrowserState(owner, first.id, 'task-a').url, base + '/a')
|
||||
const views = owner.contentView.children.filter(view => view instanceof WebContentsView)
|
||||
assert.equal(views.length, 2)
|
||||
const primary = views.find(view => view.webContents.getURL() === base + '/a')
|
||||
const secondary = views.find(view => view !== primary)
|
||||
const secondaryContents = secondary.webContents
|
||||
const prefs = primary.webContents.getLastWebPreferences()
|
||||
assert.equal(prefs.sandbox, true); assert.equal(prefs.nodeIntegration, false); assert.equal(prefs.contextIsolation, true)
|
||||
assert.ok(!prefs.preload)
|
||||
assert.equal(await primary.webContents.executeJavaScript('typeof require'), 'undefined')
|
||||
assert.equal(await primary.webContents.executeJavaScript('typeof process'), 'undefined')
|
||||
assert.equal(await primary.webContents.executeJavaScript('Notification.requestPermission()'), 'denied')
|
||||
await primary.webContents.session.cookies.set({ url: base, name: 'isolation', value: 'private-to-first' })
|
||||
assert.equal((await secondary.webContents.session.cookies.get({ url: base, name: 'isolation' })).length, 0)
|
||||
host.setBrowserBounds(owner, first.id, { x: -20, y: 50, width: 2000, height: 2000 }, 'task-a')
|
||||
const [width, height] = owner.getContentSize()
|
||||
assert.deepEqual(primary.getBounds(), { x: 0, y: 50, width, height: height - 50 })
|
||||
assert.equal(host.getBrowserState(owner, first.id, 'task-a').visible, true)
|
||||
assert.throws(() => host.setBrowserBounds(owner, first.id, { x: .5, y: 0, width: 10, height: 10 }, 'task-a'), /整数/)
|
||||
host.hideBrowser(owner, first.id, 'task-a')
|
||||
assert.equal(host.getBrowserState(owner, first.id, 'task-a').visible, false)
|
||||
await host.navigateBrowser(owner, first.id, base + '/b', 'task-a')
|
||||
assert.equal(host.getBrowserState(owner, first.id, 'task-a').canGoBack, true)
|
||||
host.backBrowser(owner, first.id, 'task-a')
|
||||
await waitFor(() => host.getBrowserState(owner, first.id, 'task-a').title === 'Local A' && !host.getBrowserState(owner, first.id, 'task-a').loading, 'back to A')
|
||||
assert.equal(host.getBrowserState(owner, first.id, 'task-a').canGoForward, true)
|
||||
host.forwardBrowser(owner, first.id, 'task-a')
|
||||
await waitFor(() => host.getBrowserState(owner, first.id, 'task-a').title === 'Local B' && !host.getBrowserState(owner, first.id, 'task-a').loading, 'forward to B')
|
||||
host.reloadBrowser(owner, first.id, 'task-a')
|
||||
await waitFor(() => !host.getBrowserState(owner, first.id, 'task-a').loading, 'reload')
|
||||
await primary.webContents.executeJavaScript(`window.open(${JSON.stringify(base + '/a')}, '_blank'); undefined`)
|
||||
await waitFor(() => host.getBrowserState(owner, first.id, 'task-a').title === 'Local A', 'popup stays in owned browser')
|
||||
assert.equal(BrowserWindow.getAllWindows().length, 2)
|
||||
host.closeBrowser(owner, first.id, 'task-a')
|
||||
assert.throws(() => host.getBrowserState(owner, first.id, 'task-a'), /不存在/)
|
||||
const draft = host.createBrowser(owner)
|
||||
assert.throws(() => host.rebindBrowser(other, draft.id, undefined, 'created-session'), /不属于/)
|
||||
const rebound = host.rebindBrowser(owner, draft.id, undefined, 'created-session')
|
||||
assert.equal(rebound.sessionId, 'created-session')
|
||||
assert.equal(rebound.id, draft.id)
|
||||
assert.throws(() => host.getBrowserState(owner, draft.id), /不属于/)
|
||||
assert.equal(host.getBrowserState(owner, draft.id, 'created-session').sessionId, 'created-session')
|
||||
host.closeBrowser(owner, draft.id, 'created-session')
|
||||
owner.destroy()
|
||||
await waitFor(() => secondaryContents.isDestroyed(), 'owner closes child browser')
|
||||
assert.throws(() => host.getBrowserState(owner, second.id, 'task-b'), /不存在/)
|
||||
console.log('Actual Electron browser host: loopback navigation/back/forward/reload, sandbox, cookie/window/session isolation, bounds, popup routing and lifecycle passed')
|
||||
}).catch(error => { console.error(error); process.exitCode = 1 }).finally(() => {
|
||||
if (owner && !owner.isDestroyed()) owner.destroy()
|
||||
if (other && !other.isDestroyed()) other.destroy()
|
||||
server.close(() => app.exit(process.exitCode || 0))
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user