release: prepare Twitch VOD Manager 1.0.2

Redesign the seven-area desktop workspace with responsive navigation, searchable settings, localized controls, and accessible update states.

Harden Electron release coverage with fully isolated user data, download paths, offline fixtures, concurrency checks, and public-release manifest validation.
This commit is contained in:
Sucukdeluxe
2026-08-10 12:29:56 +02:00
parent dacd66fe1c
commit d599593966
29 changed files with 2388 additions and 725 deletions
+97 -88
View File
@@ -1,35 +1,39 @@
const { _electron: electron } = require('playwright');
const {
createE2eEnvironment,
getElectronLaunchOptions,
verifyE2eIsolation,
installOfflineFixtures,
cleanupE2eEnvironment
} = require('./e2e-test-environment');
async function run() {
const electronPath = require('electron');
const app = await electron.launch({
executablePath: electronPath,
args: ['.'],
cwd: process.cwd()
});
const win = await app.firstWindow();
const issues = [];
const failures = [];
win.on('pageerror', (err) => {
issues.push(`pageerror: ${String(err)}`);
});
win.on('console', (msg) => {
if (msg.type() === 'error') {
issues.push(`console.error: ${msg.text()}`);
}
});
const fail = (message) => failures.push(message);
let settingsPreview = '';
let variableRows = 0;
let clipPreviewBefore = '';
let clipPreviewAfter = '';
const environment = createE2eEnvironment('template-guide');
let app = null;
try {
app = await electron.launch(getElectronLaunchOptions(environment));
const win = await app.firstWindow();
const isolation = await verifyE2eIsolation(app, win, environment);
const fixtures = await installOfflineFixtures(app);
const issues = [];
const failures = [];
win.on('pageerror', (err) => {
issues.push(`pageerror: ${String(err)}`);
});
win.on('console', (msg) => {
if (msg.type() === 'error') {
issues.push(`console.error: ${msg.text()}`);
}
});
const fail = (message) => failures.push(message);
let settingsPreview = '';
let variableRows = 0;
let clipPreviewBefore = '';
let clipPreviewAfter = '';
await win.waitForTimeout(2500);
await win.evaluate(() => {
@@ -74,73 +78,78 @@ async function run() {
await win.click('#templateGuideCloseBtn');
await win.waitForTimeout(100);
await win.evaluate(async () => {
await win.evaluate(() => {
window.showTab('vods');
await window.selectStreamer('xrohat');
window.openClipDialog(
'https://www.twitch.tv/videos/999999999999999',
'Offline fixture VOD',
'2026-02-01T00:00:00Z',
'offline_fixture',
'1h0m0s'
);
});
await win.waitForTimeout(3200);
await win.waitForTimeout(260);
const clipButtons = win.locator('.vod-card .vod-btn.secondary');
const clipCount = await clipButtons.count();
if (clipCount < 1) {
fail('No clip buttons found in VOD list');
} else {
await clipButtons.first().click();
await win.waitForTimeout(260);
await win.locator('input[name="filenameFormat"][value="template"]').check();
await win.waitForTimeout(140);
await win.locator('input[name="filenameFormat"][value="template"]').check();
await win.waitForTimeout(140);
await win.click('#clipTemplateGuideBtn');
await win.waitForTimeout(140);
await win.click('#clipTemplateGuideBtn');
await win.waitForTimeout(140);
const clipContext = await win.locator('#templateGuideContext').innerText();
if (!/clip/i.test(clipContext)) {
fail('Template guide clip context text missing');
}
await win.fill('#templateGuideInput', '{trim_start}_{part}.mp4');
await win.waitForTimeout(120);
clipPreviewBefore = await win.locator('#templateGuideOutput').innerText();
await win.fill('#clipStartTime', '00:00:10');
await win.evaluate(() => {
window.updateFromInput('start');
});
await win.waitForTimeout(240);
clipPreviewAfter = await win.locator('#templateGuideOutput').innerText();
if (clipPreviewAfter === clipPreviewBefore) {
fail('Clip template guide preview did not react to clip start time changes');
}
await win.click('#templateGuideCloseBtn');
await win.evaluate(() => {
window.closeClipDialog();
});
const clipContext = await win.locator('#templateGuideContext').innerText();
if (!/clip/i.test(clipContext)) {
fail('Template guide clip context text missing');
}
await win.fill('#templateGuideInput', '{trim_start}_{part}.mp4');
await win.waitForTimeout(120);
clipPreviewBefore = await win.locator('#templateGuideOutput').innerText();
await win.fill('#clipStartTime', '00:00:10');
await win.evaluate(() => {
window.updateFromInput('start');
});
await win.waitForTimeout(240);
clipPreviewAfter = await win.locator('#templateGuideOutput').innerText();
if (clipPreviewAfter === clipPreviewBefore) {
fail('Clip template guide preview did not react to clip start time changes');
}
await win.click('#templateGuideCloseBtn');
await win.evaluate(() => {
window.closeClipDialog();
});
const summary = {
isolation,
fixtures,
failures,
issues,
checks: {
settingsPreview,
variableRows,
clipPreviewBefore,
clipPreviewAfter
}
};
console.log(JSON.stringify(summary, null, 2));
return failures.length > 0 || issues.length > 0 ? 1 : 0;
} finally {
await app.close();
}
const summary = {
failures,
issues,
checks: {
settingsPreview,
variableRows,
clipPreviewBefore,
clipPreviewAfter
if (app) {
await app.close().catch(() => undefined);
}
};
console.log(JSON.stringify(summary, null, 2));
const hasFailure = failures.length > 0 || issues.length > 0;
process.exit(hasFailure ? 1 : 0);
cleanupE2eEnvironment(environment);
}
}
run().catch((err) => {
console.error(err);
process.exit(1);
});
run()
.then((exitCode) => {
process.exitCode = exitCode;
})
.catch((err) => {
console.error(err);
process.exitCode = 1;
});