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:
@@ -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;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user