release: v2.1.17
Render upload progress from fractional transfer values while keeping the visible percentage rounded. Move the green fill with a linear compositor transform and cover the 92-to-93 percent transition with monotonic frame-level Electron assertions. Keep Electron UI verification listeners loopback-only so development validation no longer raises a Windows Firewall prompt under electron.exe. Extend the public source allowlist with the focused network safety regression coverage.
This commit is contained in:
@@ -8,7 +8,7 @@ Multi-Hoster-Upload is a Windows desktop application for sending file batches to
|
|||||||
|
|
||||||
Download the current Setup or Portable build from the [latest GitHub release](https://github.com/Sucukdeluxe/Multi-Hoster-Upload/releases/latest).
|
Download the current Setup or Portable build from the [latest GitHub release](https://github.com/Sucukdeluxe/Multi-Hoster-Upload/releases/latest).
|
||||||
|
|
||||||
The latest public release is version 2.1.16. Use the release page for the executables and the full English changelog.
|
The latest public release is version 2.1.17. Use the release page for the executables and the full English changelog.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ The latest public release is version 2.1.16. Use the release page for the execut
|
|||||||
- Filter the workspace by all, active, queued, completed, or failed jobs.
|
- Filter the workspace by all, active, queued, completed, or failed jobs.
|
||||||
- Search and filter queue entries by file name, host, and status.
|
- Search and filter queue entries by file name, host, and status.
|
||||||
- Open per-upload diagnostics with the selected account, retry count, and safe error details.
|
- Open per-upload diagnostics with the selected account, retry count, and safe error details.
|
||||||
- Track status, progress, transferred size, speed, and the selected host account.
|
- Track status, smoothly interpolated progress, transferred size, speed, and the selected host account.
|
||||||
- Read total, remaining, running, completed, and failed upload activity from the persistent sidebar telemetry.
|
- Read total, remaining, running, completed, and failed upload activity from the persistent sidebar telemetry.
|
||||||
- Follow current upload speed in the sidebar and the synchronized header graph.
|
- Follow current upload speed in the sidebar and the synchronized header graph.
|
||||||
- Reorder selected jobs, start selected jobs, retry finished jobs, or stop active work.
|
- Reorder selected jobs, start selected jobs, retry finished jobs, or stop active work.
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "multi-hoster-uploader",
|
"name": "multi-hoster-uploader",
|
||||||
"version": "2.1.16",
|
"version": "2.1.17",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "multi-hoster-uploader",
|
"name": "multi-hoster-uploader",
|
||||||
"version": "2.1.16",
|
"version": "2.1.17",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^3.6.0",
|
"chokidar": "^3.6.0",
|
||||||
"undici": "^7.29.0",
|
"undici": "^7.29.0",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "multi-hoster-uploader",
|
"name": "multi-hoster-uploader",
|
||||||
"version": "2.1.16",
|
"version": "2.1.17",
|
||||||
"description": "Upload files to doodstream, voe, vidmoly, byse simultaneously",
|
"description": "Upload files to doodstream, voe, vidmoly, byse simultaneously",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+7
-5
@@ -1820,7 +1820,8 @@ function buildRowHtml(job) {
|
|||||||
const elapsed = formatTime(job.elapsed);
|
const elapsed = formatTime(job.elapsed);
|
||||||
const remaining = formatTime(job.remaining);
|
const remaining = formatTime(job.remaining);
|
||||||
const speed = job.speedKbs > 0 ? `${formatSpeed(job.speedKbs)}` : '';
|
const speed = job.speedKbs > 0 ? `${formatSpeed(job.speedKbs)}` : '';
|
||||||
const pct = Math.min(100, Math.round((job.progress || 0) * 100));
|
const progress = Math.min(1, Math.max(0, Number(job.progress) || 0));
|
||||||
|
const pct = Math.round(progress * 100);
|
||||||
const link = job.result ? (job.result.download_url || job.result.embed_url || '') : '';
|
const link = job.result ? (job.result.download_url || job.result.embed_url || '') : '';
|
||||||
|
|
||||||
return `<tr class="${rowClass}" data-job-id="${job.id}" data-link="${escapeAttr(link)}" tabindex="0" aria-selected="${selectedJobIds.has(job.id)}" style="height:${VIRTUAL_ROW_HEIGHT}px">
|
return `<tr class="${rowClass}" data-job-id="${job.id}" data-link="${escapeAttr(link)}" tabindex="0" aria-selected="${selectedJobIds.has(job.id)}" style="height:${VIRTUAL_ROW_HEIGHT}px">
|
||||||
@@ -1834,7 +1835,7 @@ function buildRowHtml(job) {
|
|||||||
<td class="col-progress">
|
<td class="col-progress">
|
||||||
<div class="progress-cell">
|
<div class="progress-cell">
|
||||||
<div class="progress-bar-bg">
|
<div class="progress-bar-bg">
|
||||||
<div class="progress-bar-fill ${statusClass}" style="width:${pct}%"></div>
|
<div class="progress-bar-fill ${statusClass}" style="transform:scaleX(${progress})"></div>
|
||||||
</div>
|
</div>
|
||||||
<span class="progress-pct">${job.status === 'preview' ? '' : pct + '%'}</span>
|
<span class="progress-pct">${job.status === 'preview' ? '' : pct + '%'}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -1850,7 +1851,8 @@ function _updateRowInPlace(tr, job) {
|
|||||||
const elapsed = formatTime(job.elapsed);
|
const elapsed = formatTime(job.elapsed);
|
||||||
const remaining = formatTime(job.remaining);
|
const remaining = formatTime(job.remaining);
|
||||||
const speed = job.speedKbs > 0 ? `${formatSpeed(job.speedKbs)}` : '';
|
const speed = job.speedKbs > 0 ? `${formatSpeed(job.speedKbs)}` : '';
|
||||||
const pct = Math.min(100, Math.round((job.progress || 0) * 100));
|
const progress = Math.min(1, Math.max(0, Number(job.progress) || 0));
|
||||||
|
const pct = Math.round(progress * 100);
|
||||||
const link = job.result ? (job.result.download_url || job.result.embed_url || '') : '';
|
const link = job.result ? (job.result.download_url || job.result.embed_url || '') : '';
|
||||||
|
|
||||||
// Write DOM only when the target value actually changes — a no-op progress
|
// Write DOM only when the target value actually changes — a no-op progress
|
||||||
@@ -1881,8 +1883,8 @@ function _updateRowInPlace(tr, job) {
|
|||||||
|
|
||||||
const fill = cells[7].querySelector('.progress-bar-fill');
|
const fill = cells[7].querySelector('.progress-bar-fill');
|
||||||
if (fill) {
|
if (fill) {
|
||||||
const pctStr = pct + '%';
|
const progressTransform = `scaleX(${progress})`;
|
||||||
if (fill.style.width !== pctStr) fill.style.width = pctStr;
|
if (fill.style.transform !== progressTransform) fill.style.transform = progressTransform;
|
||||||
const fillClass = `progress-bar-fill ${statusClass}`;
|
const fillClass = `progress-bar-fill ${statusClass}`;
|
||||||
if (fill.className !== fillClass) fill.className = fillClass;
|
if (fill.className !== fillClass) fill.className = fillClass;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-3
@@ -518,10 +518,12 @@ body.col-resizing, body.col-resizing * { cursor: col-resize !important; user-sel
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.progress-bar-fill {
|
.progress-bar-fill {
|
||||||
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
will-change: width;
|
transform-origin: left center;
|
||||||
transition: width 220ms ease-out;
|
will-change: transform;
|
||||||
|
transition: transform 360ms linear;
|
||||||
}
|
}
|
||||||
.progress-bar-fill.status-uploading { background: linear-gradient(90deg, var(--success), var(--success-end)); }
|
.progress-bar-fill.status-uploading { background: linear-gradient(90deg, var(--success), var(--success-end)); }
|
||||||
.progress-bar-fill.status-getting-server { background: var(--accent); }
|
.progress-bar-fill.status-getting-server { background: var(--accent); }
|
||||||
@@ -1749,7 +1751,7 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
|
|||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar-fill {
|
.progress-bar-fill {
|
||||||
transition-duration: 220ms !important;
|
transition-duration: 360ms !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-opening {
|
.menu-opening {
|
||||||
|
|||||||
@@ -138,10 +138,12 @@ const sourceFiles = [
|
|||||||
'tests/startup-renderer.test.js',
|
'tests/startup-renderer.test.js',
|
||||||
'tests/stats.test.js',
|
'tests/stats.test.js',
|
||||||
'tests/support-bundle.test.js',
|
'tests/support-bundle.test.js',
|
||||||
|
'tests/support/ui-network-safety.js',
|
||||||
'tests/suspect-reject-alternates.test.js',
|
'tests/suspect-reject-alternates.test.js',
|
||||||
'tests/throttle-timer.test.js',
|
'tests/throttle-timer.test.js',
|
||||||
'tests/throttle.test.js',
|
'tests/throttle.test.js',
|
||||||
'tests/throttled-cache.test.js',
|
'tests/throttled-cache.test.js',
|
||||||
|
'tests/ui-network-safety.test.js',
|
||||||
'tests/ui-smoke.js',
|
'tests/ui-smoke.js',
|
||||||
'tests/updater-version.test.js',
|
'tests/updater-version.test.js',
|
||||||
'tests/upload-log.test.js',
|
'tests/upload-log.test.js',
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ describe('RemoteServer', () => {
|
|||||||
|
|
||||||
await server.start({
|
await server.start({
|
||||||
port: 0, // random available port
|
port: 0, // random available port
|
||||||
|
host: '127.0.0.1',
|
||||||
token: 'test-token-123',
|
token: 'test-token-123',
|
||||||
allowInput: true,
|
allowInput: true,
|
||||||
mainWindow: mockMainWindow,
|
mainWindow: mockMainWindow,
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
const LOOPBACK_HOST = '127.0.0.1';
|
||||||
|
|
||||||
|
function listenOnLoopback(server, port = 0) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const handleError = error => reject(error);
|
||||||
|
server.once('error', handleError);
|
||||||
|
server.listen(port, LOOPBACK_HOST, () => {
|
||||||
|
server.off('error', handleError);
|
||||||
|
resolve(server.address());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function installLoopbackRemoteServerGuard(RemoteServer, onListening = () => {}) {
|
||||||
|
const originalStart = RemoteServer.prototype.start;
|
||||||
|
const guardedStart = async function (options) {
|
||||||
|
const result = await originalStart.call(this, { ...options, host: LOOPBACK_HOST });
|
||||||
|
const address = this._wss && this._wss.address();
|
||||||
|
onListening(address && typeof address === 'object' ? address.address : '');
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
RemoteServer.prototype.start = guardedStart;
|
||||||
|
return () => {
|
||||||
|
if (RemoteServer.prototype.start === guardedStart) RemoteServer.prototype.start = originalStart;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { listenOnLoopback, installLoopbackRemoteServerGuard };
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
const { test } = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const net = require('node:net');
|
||||||
|
const RemoteServer = require('../lib/remote-server');
|
||||||
|
|
||||||
|
let networkSafety = {};
|
||||||
|
try {
|
||||||
|
networkSafety = require('./support/ui-network-safety');
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
test('Electron UI test listeners bind only to loopback', async () => {
|
||||||
|
assert.equal(typeof networkSafety.listenOnLoopback, 'function');
|
||||||
|
|
||||||
|
const server = net.createServer();
|
||||||
|
try {
|
||||||
|
await networkSafety.listenOnLoopback(server);
|
||||||
|
const address = server.address();
|
||||||
|
assert.equal(address.address, '127.0.0.1');
|
||||||
|
assert.ok(address.port > 0);
|
||||||
|
} finally {
|
||||||
|
if (server.listening) await new Promise(resolve => server.close(resolve));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Electron UI remote server guard keeps the real server on loopback', async () => {
|
||||||
|
assert.equal(typeof networkSafety.installLoopbackRemoteServerGuard, 'function');
|
||||||
|
|
||||||
|
const observedAddresses = [];
|
||||||
|
const restore = networkSafety.installLoopbackRemoteServerGuard(RemoteServer, address => observedAddresses.push(address));
|
||||||
|
const server = new RemoteServer();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await server.start({
|
||||||
|
port: 0,
|
||||||
|
token: 'ui-network-safety-token',
|
||||||
|
allowInput: true,
|
||||||
|
onSignalingToCapture: () => {},
|
||||||
|
onCreateCaptureWindow: () => {},
|
||||||
|
onDestroyCaptureWindow: () => {}
|
||||||
|
});
|
||||||
|
assert.equal(server._wss.address().address, '127.0.0.1');
|
||||||
|
assert.deepEqual(observedAddresses, ['127.0.0.1']);
|
||||||
|
} finally {
|
||||||
|
server.stop();
|
||||||
|
restore();
|
||||||
|
}
|
||||||
|
});
|
||||||
+42
-19
@@ -31,7 +31,11 @@ const fs = require('fs');
|
|||||||
const net = require('net');
|
const net = require('net');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const ConfigStore = require(path.join(process.cwd(), 'lib', 'config-store'));
|
const ConfigStore = require(path.join(process.cwd(), 'lib', 'config-store'));
|
||||||
|
const RemoteServer = require(path.join(process.cwd(), 'lib', 'remote-server'));
|
||||||
|
const { listenOnLoopback, installLoopbackRemoteServerGuard } = require(path.join(process.cwd(), 'tests', 'support', 'ui-network-safety'));
|
||||||
const updaterModule = require(path.join(process.cwd(), 'lib', 'updater'));
|
const updaterModule = require(path.join(process.cwd(), 'lib', 'updater'));
|
||||||
|
const uiRemoteBindAddresses = [];
|
||||||
|
installLoopbackRemoteServerGuard(RemoteServer, address => uiRemoteBindAddresses.push(address));
|
||||||
let preparedUpdateMockCalls = 0;
|
let preparedUpdateMockCalls = 0;
|
||||||
let launchedUpdateMockCalls = 0;
|
let launchedUpdateMockCalls = 0;
|
||||||
let updateCheckMockCalls = 0;
|
let updateCheckMockCalls = 0;
|
||||||
@@ -534,28 +538,48 @@ setTimeout(async () => {
|
|||||||
check('Upload sidebar drops hidden selections when changing filters', uploadFilterState.active.selected.join('|') === 'ui-active-z');
|
check('Upload sidebar drops hidden selections when changing filters', uploadFilterState.active.selected.join('|') === 'ui-active-z');
|
||||||
|
|
||||||
const uploadProgressMotion = await wc.executeJavaScript(\`(async () => {
|
const uploadProgressMotion = await wc.executeJavaScript(\`(async () => {
|
||||||
const track = document.createElement('div');
|
const table = document.createElement('table');
|
||||||
track.className = 'progress-bar-bg';
|
table.style.cssText = 'position:fixed;left:20px;top:20px;width:700px;';
|
||||||
track.style.cssText = 'position:fixed;left:20px;top:20px;width:300px;';
|
const tbody = document.createElement('tbody');
|
||||||
const fill = document.createElement('div');
|
const job = { id: 'ui-smooth-progress', file: 'C:/ui/smooth.bin', fileName: 'smooth.bin', hoster: 'byse.sx', status: 'uploading', bytesUploaded: 920, bytesTotal: 1000, speedKbs: 1, elapsed: 1, remaining: 1, progress: .92 };
|
||||||
fill.className = 'progress-bar-fill status-uploading';
|
tbody.innerHTML = buildRowHtml(job);
|
||||||
fill.style.width = '10%';
|
table.append(tbody);
|
||||||
track.append(fill);
|
document.body.append(table);
|
||||||
document.body.append(track);
|
const row = tbody.querySelector('.queue-row');
|
||||||
|
const track = row.querySelector('.progress-bar-bg');
|
||||||
|
const fill = row.querySelector('.progress-bar-fill');
|
||||||
|
track.style.cssText = 'flex:none;width:400px;';
|
||||||
const ratio = () => fill.getBoundingClientRect().width / track.getBoundingClientRect().width;
|
const ratio = () => fill.getBoundingClientRect().width / track.getBoundingClientRect().width;
|
||||||
const background = getComputedStyle(fill).backgroundImage;
|
const background = getComputedStyle(fill).backgroundImage;
|
||||||
await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
|
await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
|
||||||
const start = ratio();
|
const start = ratio();
|
||||||
fill.style.width = '80%';
|
job.progress = .924;
|
||||||
await new Promise(resolve => setTimeout(resolve, 70));
|
job.bytesUploaded = 924;
|
||||||
const middle = ratio();
|
_updateRowInPlace(row, job);
|
||||||
await new Promise(resolve => setTimeout(resolve, 260));
|
await new Promise(resolve => setTimeout(resolve, 80));
|
||||||
|
const fractionalMiddle = ratio();
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 240));
|
||||||
|
const fractionalEnd = ratio();
|
||||||
|
const fractionalLabel = row.querySelector('.progress-pct').textContent;
|
||||||
|
job.progress = .93;
|
||||||
|
job.bytesUploaded = 930;
|
||||||
|
_updateRowInPlace(row, job);
|
||||||
|
const nextFrames = [];
|
||||||
|
for (let frame = 0; frame < 12; frame++) {
|
||||||
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
||||||
|
nextFrames.push(ratio());
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 240));
|
||||||
const end = ratio();
|
const end = ratio();
|
||||||
track.remove();
|
table.remove();
|
||||||
return { background, start, middle, end };
|
return { background, start, fractionalMiddle, fractionalEnd, fractionalLabel, nextFrames, end };
|
||||||
})()\`);
|
})()\`);
|
||||||
check('Active upload progress uses the green success gradient', uploadProgressMotion.background === 'linear-gradient(90deg, rgb(117, 211, 155), rgb(156, 226, 184))');
|
check('Active upload progress uses the green success gradient', uploadProgressMotion.background === 'linear-gradient(90deg, rgb(117, 211, 155), rgb(156, 226, 184))');
|
||||||
check('Active upload progress visibly interpolates percentage changes', uploadProgressMotion.start > 0.08 && uploadProgressMotion.start < 0.12 && uploadProgressMotion.middle > uploadProgressMotion.start + 0.02 && uploadProgressMotion.middle < 0.78 && uploadProgressMotion.end > 0.78 && uploadProgressMotion.end < 0.82);
|
check('Active upload progress moves continuously before the rounded percentage changes', uploadProgressMotion.start > .919 && uploadProgressMotion.start < .921 && uploadProgressMotion.fractionalMiddle > uploadProgressMotion.start && uploadProgressMotion.fractionalMiddle < .924 && uploadProgressMotion.fractionalEnd > .923 && uploadProgressMotion.fractionalEnd < .925 && uploadProgressMotion.fractionalLabel === '92%');
|
||||||
|
const smoothFrameCount = uploadProgressMotion.nextFrames.slice(1).filter((value, index) => value > uploadProgressMotion.nextFrames[index] + .00001).length;
|
||||||
|
const monotonicFrames = uploadProgressMotion.nextFrames.every((value, index, values) => index === 0 || value >= values[index - 1] - .00001);
|
||||||
|
console.log('Upload progress frame trace: ' + [uploadProgressMotion.fractionalEnd, ...uploadProgressMotion.nextFrames, uploadProgressMotion.end].map(value => (value * 100).toFixed(3)).join(' -> '));
|
||||||
|
check('Active upload progress glides through the next whole percentage across real frames', uploadProgressMotion.nextFrames.length === 12 && smoothFrameCount >= 8 && monotonicFrames && uploadProgressMotion.nextFrames.at(-1) > uploadProgressMotion.fractionalEnd && uploadProgressMotion.nextFrames.at(-1) < .93 && uploadProgressMotion.end > .929 && uploadProgressMotion.end < .931);
|
||||||
|
|
||||||
const uploadSelectionScope = await wc.executeJavaScript(\`(() => {
|
const uploadSelectionScope = await wc.executeJavaScript(\`(() => {
|
||||||
const makeJob = (id, status) => ({ id, file: 'C:/ui/' + id + '.bin', fileName: id + '.bin', hoster: 'byse.sx', status, bytesUploaded: 0, bytesTotal: 1024, speedKbs: 0, elapsed: 0, remaining: 0, progress: status === 'done' ? 1 : 0 });
|
const makeJob = (id, status) => ({ id, file: 'C:/ui/' + id + '.bin', fileName: id + '.bin', hoster: 'byse.sx', status, bytesUploaded: 0, bytesTotal: 1024, speedKbs: 0, elapsed: 0, remaining: 0, progress: status === 'done' ? 1 : 0 });
|
||||||
@@ -1279,10 +1303,8 @@ setTimeout(async () => {
|
|||||||
try { fs.unlinkSync(importPersistFailurePath); } catch {}
|
try { fs.unlinkSync(importPersistFailurePath); } catch {}
|
||||||
|
|
||||||
const occupiedRemotePortServer = net.createServer();
|
const occupiedRemotePortServer = net.createServer();
|
||||||
await new Promise((resolve, reject) => {
|
await listenOnLoopback(occupiedRemotePortServer);
|
||||||
occupiedRemotePortServer.once('error', reject);
|
const occupiedRemoteBindAddress = occupiedRemotePortServer.address().address;
|
||||||
occupiedRemotePortServer.listen(0, resolve);
|
|
||||||
});
|
|
||||||
const occupiedRemotePort = occupiedRemotePortServer.address().port;
|
const occupiedRemotePort = occupiedRemotePortServer.address().port;
|
||||||
const configBeforeRemoteFailure = JSON.parse(fs.readFileSync(activeConfigStore.filePath, 'utf-8'));
|
const configBeforeRemoteFailure = JSON.parse(fs.readFileSync(activeConfigStore.filePath, 'utf-8'));
|
||||||
const alwaysOnTopAfterRemoteFailure = !Boolean(configBeforeRemoteFailure.globalSettings.alwaysOnTop);
|
const alwaysOnTopAfterRemoteFailure = !Boolean(configBeforeRemoteFailure.globalSettings.alwaysOnTop);
|
||||||
@@ -1309,6 +1331,7 @@ setTimeout(async () => {
|
|||||||
|
|
||||||
const generatedRemoteSettings = await wc.executeJavaScript('saveRemoteSettingsTracked(' + JSON.stringify({ ...restoredRemoteSettings, enabled: true, token: '' }) + ')');
|
const generatedRemoteSettings = await wc.executeJavaScript('saveRemoteSettingsTracked(' + JSON.stringify({ ...restoredRemoteSettings, enabled: true, token: '' }) + ')');
|
||||||
const generatedRemoteToken = generatedRemoteSettings?.settings?.token || '';
|
const generatedRemoteToken = generatedRemoteSettings?.settings?.token || '';
|
||||||
|
check('Electron UI smoke keeps every network listener on loopback', occupiedRemoteBindAddress === '127.0.0.1' && uiRemoteBindAddresses.length > 0 && uiRemoteBindAddresses.every(address => address === '127.0.0.1'));
|
||||||
const canonicalRemoteAfterFullSave = await wc.executeJavaScript('(async () => { config.globalSettings = { ...(config.globalSettings || {}), remote: { ...' + JSON.stringify(restoredRemoteSettings) + ', enabled: false, token: "" } }; renderSettings(); const tokenInput = document.getElementById("remoteTokenInput"); if (tokenInput) tokenInput.value = ""; await saveSettings({ feedbackText: "Gespeichert" }); return config.globalSettings.remote?.token || ""; })()');
|
const canonicalRemoteAfterFullSave = await wc.executeJavaScript('(async () => { config.globalSettings = { ...(config.globalSettings || {}), remote: { ...' + JSON.stringify(restoredRemoteSettings) + ', enabled: false, token: "" } }; renderSettings(); const tokenInput = document.getElementById("remoteTokenInput"); if (tokenInput) tokenInput.value = ""; await saveSettings({ feedbackText: "Gespeichert" }); return config.globalSettings.remote?.token || ""; })()');
|
||||||
const configAfterGeneratedTokenSave = JSON.parse(fs.readFileSync(activeConfigStore.filePath, 'utf-8'));
|
const configAfterGeneratedTokenSave = JSON.parse(fs.readFileSync(activeConfigStore.filePath, 'utf-8'));
|
||||||
check('Full settings save preserves the canonical remote token', generatedRemoteToken.length > 0 && canonicalRemoteAfterFullSave === generatedRemoteToken && configAfterGeneratedTokenSave.globalSettings.remote?.token === generatedRemoteToken);
|
check('Full settings save preserves the canonical remote token', generatedRemoteToken.length > 0 && canonicalRemoteAfterFullSave === generatedRemoteToken && configAfterGeneratedTokenSave.globalSettings.remote?.token === generatedRemoteToken);
|
||||||
|
|||||||
Reference in New Issue
Block a user