Polish navigation, settings, streamer and queue workflows; add safe pause and partial-file lifecycle handling; apply the product identity across Windows surfaces; and replace the public README with a complete English product guide and isolated screenshot.
159 lines
4.6 KiB
JavaScript
159 lines
4.6 KiB
JavaScript
const { _electron: electron } = require('playwright');
|
|
const {
|
|
createE2eEnvironment,
|
|
getElectronLaunchOptions,
|
|
verifyE2eIsolation,
|
|
installOfflineFixtures,
|
|
cleanupE2eEnvironment
|
|
} = require('./e2e-test-environment');
|
|
|
|
async function run() {
|
|
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(() => {
|
|
window.showTab('settings');
|
|
});
|
|
await win.waitForTimeout(200);
|
|
|
|
await win.click('[data-context-for="settings"] [data-settings-pane="downloads"]');
|
|
await win.waitForTimeout(180);
|
|
|
|
await win.click('#settingsTemplateGuideBtn');
|
|
await win.waitForTimeout(180);
|
|
|
|
const guideVisibleFromSettings = await win.evaluate(() => {
|
|
return document.getElementById('templateGuideModal')?.classList.contains('show') || false;
|
|
});
|
|
|
|
if (!guideVisibleFromSettings) {
|
|
fail('Template guide did not open from settings');
|
|
}
|
|
|
|
await win.fill('#templateGuideInput', '{title}_{part_padded}_{date_custom="yyyy-MM-dd"}.mp4');
|
|
await win.waitForTimeout(160);
|
|
|
|
settingsPreview = await win.locator('#templateGuideOutput').innerText();
|
|
if (!settingsPreview.includes('.mp4')) {
|
|
fail('Settings template preview missing .mp4 output');
|
|
}
|
|
if (settingsPreview.includes('{title}') || settingsPreview.includes('{part_padded}') || settingsPreview.includes('{date_custom=')) {
|
|
fail('Settings template preview did not replace placeholders');
|
|
}
|
|
|
|
variableRows = await win.locator('#templateGuideBody tr').count();
|
|
if (variableRows < 12) {
|
|
fail(`Template variable table too short (${variableRows})`);
|
|
}
|
|
|
|
await win.click('#templateGuideUseParts');
|
|
await win.waitForTimeout(150);
|
|
const partsContext = await win.locator('#templateGuideContext').innerText();
|
|
if (!/part|teil/i.test(partsContext)) {
|
|
fail('Template guide parts context text missing');
|
|
}
|
|
|
|
await win.click('#templateGuideCloseBtn');
|
|
await win.waitForTimeout(100);
|
|
|
|
await win.evaluate(() => {
|
|
window.showTab('vods');
|
|
window.openClipDialog(
|
|
'https://www.twitch.tv/videos/999999999999999',
|
|
'Offline fixture VOD',
|
|
'2026-02-01T00:00:00Z',
|
|
'offline_fixture',
|
|
'1h0m0s'
|
|
);
|
|
});
|
|
await win.waitForTimeout(260);
|
|
|
|
await win.locator('input[name="filenameFormat"][value="template"]').check();
|
|
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 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 {
|
|
if (app) {
|
|
await app.close().catch(() => undefined);
|
|
}
|
|
cleanupE2eEnvironment(environment);
|
|
}
|
|
}
|
|
|
|
run()
|
|
.then((exitCode) => {
|
|
process.exitCode = exitCode;
|
|
})
|
|
.catch((err) => {
|
|
console.error(err);
|
|
process.exitCode = 1;
|
|
});
|