const assert = require('node:assert/strict') const { app, BrowserWindow, screen } = require('electron') const root = require('node:path').resolve(__dirname, '..') app.setAppPath(root) app.setPath( 'userData', require('node:fs').mkdtempSync( require('node:path').join(require('node:os').tmpdir(), 'screen-smoke-'), ), ) const delay = (ms) => new Promise((r) => setTimeout(r, ms)) app.whenReady().then(() => { const original = screen.getAllDisplays.bind(screen) screen.getAllDisplays = () => [ ...original(), { ...original()[0], id: 424242, label: 'Synthetic test display', bounds: { x: 1280, y: 0, width: 1280, height: 720 }, size: { width: 1280, height: 720 }, }, ] }) require(root + '/dist-electron/electron/main.js') async function poll(check, label) { for (let i = 0; i < 150; i++) { const result = await check() if (result) return result await delay(100) } throw new Error('Timeout: ' + label) } app.whenReady().then(async () => { try { const control = await poll(() => BrowserWindow.getAllWindows()[0], 'control') control.webContents.on('console-message', (_event, details) => console.log('renderer:', details.message), ) await poll( async () => !control.webContents.isLoading() && (await control.webContents.executeJavaScript( '!!window.screenBridge && !!document.querySelector("h1")', )), 'control UI', ) const evaluate = (s) => control.webContents.executeJavaScript(s) assert.equal(await evaluate('typeof window.require'), 'undefined') await evaluate('window.screenBridge.show(424242)') const output = await poll( () => BrowserWindow.getAllWindows().find((w) => w !== control), 'output', ) output.webContents.on('console-message', (_event, details) => console.log('output:', details.message), ) await poll( async () => !output.webContents.isLoading() && (await output.webContents.executeJavaScript( 'document.querySelector("video")?.readyState >= 1', )), 'metadata', ) const range = await output.webContents.executeJavaScript( `fetch('screen-app://app/media/video', {headers: {Range: 'bytes=100-199'}}).then(async r => ({status:r.status, size:(await r.arrayBuffer()).byteLength, range:r.headers.get('content-range')}))`, ) assert.equal(range.status, 206) assert.equal(range.size, 100) const command = async (id, action, media = 'audio', position = 0) => { await evaluate( `window.screenBridge.command(${JSON.stringify({ id, action, media, position })})`, ) return poll(async () => { const s = await evaluate('window.screenBridge.state()') if (s.status === 'error') throw new Error(s.error) return s.status !== 'pending' && s.command.id === id && s }, id) } assert.equal((await command('audio-play', 'play')).status, 'playing') assert.equal((await command('audio-pause', 'pause')).status, 'paused') assert.equal((await command('audio-stop', 'stop')).status, 'stopped') assert.equal((await command('video-play', 'play', 'video')).status, 'playing') await command('video-seek', 'seek', 'video', 30) const position = await output.webContents.executeJavaScript( 'document.querySelector("video").currentTime', ) assert.ok(position >= 29.9 && position < 35, 'video seek actual position') await command('video-pause', 'pause', 'video') assert.equal( await output.webContents.executeJavaScript('document.querySelector("audio").paused'), true, ) assert.equal( await output.webContents.executeJavaScript('document.querySelector("video").paused'), true, ) await assert.rejects( output.webContents.executeJavaScript( `window.screenBridge.command({id:'forbidden', action:'play', media:'audio', position:0})`, ), ) const network = (await evaluate('window.screenBridge.host()')).lan[0] assert.ok(network, 'LAN interface available for browser smoke') await evaluate(`window.screenBridge.lan(${JSON.stringify(network.address)})`) const host = await evaluate('window.screenBridge.host()') const phone = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: false, contextIsolation: true, sandbox: true, backgroundThrottling: false, partition: 'screen-smoke-phone', }, }) assert.equal((await fetch(host.url)).status, 200) await phone.webContents.session.setProxy({ mode: 'direct' }) await phone.loadURL(host.url) const phoneEval = (s) => phone.webContents.executeJavaScript(s) await poll(async () => await phoneEval('!!document.querySelector("input")'), 'phone UI') assert.equal(await phoneEval('typeof window.screenBridge'), 'undefined') await phoneEval( `(() => {const input = document.querySelector('input'); input.value = ${JSON.stringify(host.code)}; input.dispatchEvent(new Event('input', {bubbles:true}));})()`, ) await delay(100) await phoneEval( `Array.from(document.querySelectorAll('button')).find(b => b.textContent.includes('Подключиться')).click()`, ) const pending = await poll( async () => (await evaluate('window.screenBridge.host()')).pending[0], 'pair request from phone UI', ) await evaluate(`window.screenBridge.approve(${JSON.stringify(pending)})`) await poll( async () => await phoneEval( `!Array.from(document.querySelectorAll('button')).find(b => b.textContent.includes('Пуск с начала')).disabled`, ), 'phone connected', ) await phoneEval( `Array.from(document.querySelectorAll('button')).find(b => b.textContent.includes('Пуск с начала')).click()`, ) await poll(async () => { const state = await evaluate('window.screenBridge.state()') return state.status === 'playing' && state.samples.some((s) => s.origin === 'phone') }, 'phone play') await command('computer-after-phone', 'pause') await evaluate('window.screenBridge.lan(null)') await poll( async () => await phoneEval(`document.body.textContent.includes('Нет связи')`), 'phone revoked', ) phone.close() output.close() await evaluate('window.screenBridge.show(424242)') const reopened = await poll( () => BrowserWindow.getAllWindows().find((w) => w !== control), 'reopened output', ) await poll( async () => !reopened.webContents.isLoading() && (await reopened.webContents.executeJavaScript('!!document.querySelector("video")')), 'reopened ready', ) assert.equal( await reopened.webContents.executeJavaScript('document.querySelector("video").paused'), true, ) console.log( 'SCREEN_SMOKE_PASS', JSON.stringify({ range, seek: position, samples: (await evaluate('window.screenBridge.state()')).samples, electron: process.versions.electron, }), ) app.exit(0) } catch (error) { console.error('SCREEN_SMOKE_FAIL', error) app.exit(1) } }) setTimeout(() => { console.error('Global timeout') app.exit(1) }, 45000)