feat: reconcile WebAVP desktop parity and releases

This commit is contained in:
2026-08-19 12:14:29 +00:00
parent 407892ed9a
commit 2a275fc536
26 changed files with 2155 additions and 1508 deletions
+26
View File
@@ -0,0 +1,26 @@
import unittest
from app import Handler, HOST
class AppServerTests(unittest.TestCase):
def test_server_defaults_to_loopback(self):
self.assertEqual(HOST, "127.0.0.1")
def test_certificate_response_interpretation(self):
cases = [
(b"true", True),
(b"false", False),
(b'{"verified": false}', False),
(b'{"isValid": true}', True),
(b'{"unknown": true}', None),
(b"", None),
(b"not-json", None),
]
for body, expected in cases:
with self.subTest(body=body):
self.assertIs(Handler._interpret_verify_body(body), expected)
if __name__ == "__main__":
unittest.main()
+79
View File
@@ -0,0 +1,79 @@
'use strict';
const { app, BrowserWindow } = require('electron');
const targetUrl = process.env.WEBAVP_SMOKE_WEB_URL || 'http://127.0.0.1:5152/';
let failed = false;
app.commandLine.appendSwitch('disable-gpu');
app.whenReady().then(async () => {
const win = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true, nodeIntegration: false, sandbox: true } });
win.webContents.on('console-message', (_event, level, message) => {
if (level >= 2) {
failed = true;
console.error(`[WebAVP] Browser console error: ${message}`);
}
});
try {
await win.loadURL(targetUrl);
const result = await win.webContents.executeJavaScript(`(async () => {
const deadline = Date.now() + 12000;
while (!document.getElementById('releaseStatus')?.textContent && Date.now() < deadline) {
await new Promise(resolve => setTimeout(resolve, 100));
}
async function importCameraCount(count) {
const input = document.getElementById('fileInput');
const transfer = new DataTransfer();
for (let index = 1; index <= count; index += 1) {
transfer.items.add(new File(['x'], 'Camera' + index + '.mp4', { type: 'video/mp4' }));
}
Object.defineProperty(input, 'files', { value: transfer.files, configurable: true });
input.dispatchEvent(new Event('change', { bubbles: true }));
const importDeadline = Date.now() + 5000;
while (document.querySelectorAll('.cam-cell').length < count && Date.now() < importDeadline) {
await new Promise(resolve => setTimeout(resolve, 50));
}
return [...document.querySelectorAll('.cam-cell')]
.filter(cell => getComputedStyle(cell).display !== 'none').length;
}
document.querySelector('[data-layout="2x2"]').click();
const fixedVisible = await importCameraCount(5);
document.getElementById('newSessionBtn').click();
document.querySelector('[data-layout="auto"]').click();
const autoVisible = await importCameraCount(17);
return ({
title: document.title,
hasUtils: !!window.WebAVPUtils,
autoGrid16: window.WebAVPUtils?.getAutoGrid(16),
releaseText: document.getElementById('releaseAction')?.textContent.trim(),
releaseFallback: document.getElementById('releaseAction')?.href,
releaseStatus: document.getElementById('releaseStatus')?.textContent,
hasFourByFour: !!document.querySelector('[data-layout="4x4"]'),
hasDewarp: /dewarp|fisheye/i.test(document.documentElement.textContent),
fixedVisible,
autoVisible
});
})()`);
const passed = result.title === 'Alta Video Player'
&& result.hasUtils
&& result.autoGrid16?.cols === 4
&& result.autoGrid16?.rows === 4
&& result.releaseText === 'Download Desktop App'
&& result.releaseFallback.startsWith('https://git.pejicorp.com/peji/WebAVP/releases')
&& result.releaseStatus.length > 0
&& result.hasFourByFour
&& !result.hasDewarp
&& result.fixedVisible === 4
&& result.autoVisible === 16
&& !failed;
if (!passed) throw new Error(`Browser assertions failed: ${JSON.stringify(result)}`);
console.log(`[WebAVP] Browser smoke passed at ${targetUrl}`);
app.exit(0);
} catch (err) {
console.error(`[WebAVP] Browser smoke failed: ${err.message}`);
app.exit(1);
}
});
+54
View File
@@ -0,0 +1,54 @@
'use strict';
const assert = require('node:assert/strict');
const {
compareVersions,
evaluateUpdate,
getAutoGrid,
normalizeRelease,
selectReleaseAsset,
} = require('../static/webavp-utils.js');
assert.deepEqual(getAutoGrid(0), { cols: 1, rows: 1 });
assert.deepEqual(getAutoGrid(1), { cols: 1, rows: 1 });
assert.deepEqual(getAutoGrid(2), { cols: 2, rows: 1 });
assert.deepEqual(getAutoGrid(4), { cols: 2, rows: 2 });
assert.deepEqual(getAutoGrid(6), { cols: 3, rows: 2 });
assert.deepEqual(getAutoGrid(9), { cols: 3, rows: 3 });
assert.deepEqual(getAutoGrid(12), { cols: 4, rows: 3 });
assert.deepEqual(getAutoGrid(16), { cols: 4, rows: 4 });
assert.deepEqual(getAutoGrid(99), { cols: 4, rows: 4 });
assert.equal(compareVersions('v1.2.0', '1.1.9'), 1);
assert.equal(compareVersions('1.2.0', '1.2.0'), 0);
assert.equal(compareVersions('1.2.0-beta.1', '1.2.0'), -1);
assert.equal(compareVersions('1.2.0+linux.1', '1.2.0+build.9'), 0);
assert.equal(compareVersions('2.0', '1.99.99'), 1);
assert.throws(() => compareVersions('latest', '1.0.0'), /version/i);
const rawRelease = {
tag_name: 'v1.4.0',
html_url: 'https://git.pejicorp.com/peji/WebAVP/releases/tag/v1.4.0',
name: 'WebAVP 1.4.0',
assets: [
{ name: 'Alta Video Player-1.4.0-linux-x64.AppImage', browser_download_url: 'https://git.pejicorp.com/peji/WebAVP/releases/download/v1.4.0/linux.AppImage' },
{ name: 'Alta Video Player-1.4.0-linux-x64.deb', browser_download_url: 'https://git.pejicorp.com/peji/WebAVP/releases/download/v1.4.0/linux.deb' },
{ name: 'Alta Video Player-1.4.0-mac-arm64.dmg', browser_download_url: 'https://git.pejicorp.com/peji/WebAVP/releases/download/v1.4.0/mac-arm64.dmg' },
{ name: 'Alta Video Player-1.4.0-mac-x64.dmg', browser_download_url: 'https://git.pejicorp.com/peji/WebAVP/releases/download/v1.4.0/mac-x64.dmg' },
{ name: 'Alta Video Player-1.4.0-win-x64.exe', browser_download_url: 'https://git.pejicorp.com/peji/WebAVP/releases/download/v1.4.0/win.exe' },
],
};
const release = normalizeRelease(rawRelease);
assert.equal(release.version, '1.4.0');
assert.equal(selectReleaseAsset(release, 'linux', 'x64').name.endsWith('.AppImage'), true);
assert.equal(selectReleaseAsset(release, 'linux', 'arm64'), null);
assert.equal(selectReleaseAsset(release, 'darwin', 'arm64').name.endsWith('.dmg'), true);
assert.equal(selectReleaseAsset(release, 'win32', 'x64').name.endsWith('.exe'), true);
assert.equal(selectReleaseAsset(release, 'win32', 'arm64'), null);
assert.throws(() => normalizeRelease({ tag_name: 'v1.0.0', assets: [{ name: 'bad.exe', browser_download_url: 'https://evil.example/bad.exe' }] }), /asset/i);
assert.equal(evaluateUpdate(release, '1.4.0', 'linux', 'x64').status, 'current');
assert.equal(evaluateUpdate(release, '1.3.9', 'linux', 'x64').status, 'available');
assert.equal(evaluateUpdate(release, '1.3.9', 'linux', 'arm64').status, 'unavailable-platform');
console.log('[WebAVP] utility regression tests passed');