Add npm test:e2e command and harden smoke checks (v4.0.2)

Expose a single runnable end-to-end test command for fast regression sweeps and update the smoke test to validate preflight and localization-aware clip errors reliably across language modes.
This commit is contained in:
xRangerDE 2026-02-14 06:17:02 +01:00
parent 089e0085ee
commit 614e5cd20f
5 changed files with 156 additions and 6 deletions

View File

@ -1,12 +1,12 @@
{
"name": "twitch-vod-manager",
"version": "4.0.1",
"version": "4.0.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "twitch-vod-manager",
"version": "4.0.1",
"version": "4.0.2",
"license": "MIT",
"dependencies": {
"axios": "^1.6.0",

View File

@ -1,6 +1,6 @@
{
"name": "twitch-vod-manager",
"version": "4.0.1",
"version": "4.0.2",
"description": "Twitch VOD Manager - Download Twitch VODs easily",
"main": "dist/main.js",
"author": "xRangerDE",
@ -8,6 +8,7 @@
"scripts": {
"build": "tsc",
"start": "npm run build && electron .",
"test:e2e": "npm exec --yes --package=playwright -- node scripts/smoke-test.js",
"pack": "npm run build && electron-builder --dir",
"dist": "npm run build && electron-builder",
"dist:win": "npm run build && electron-builder --win"

View File

@ -0,0 +1,149 @@
const { _electron: electron } = require('playwright');
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 = [];
win.on('pageerror', (err) => {
issues.push(`pageerror: ${String(err)}`);
});
win.on('console', (msg) => {
if (msg.type() === 'error') {
issues.push(`console.error: ${msg.text()}`);
}
});
await win.waitForTimeout(2500);
const globals = await win.evaluate(async () => {
const names = [
'showTab',
'addStreamer',
'refreshVODs',
'downloadClip',
'selectCutterVideo',
'startCutting',
'addMergeFiles',
'startMerging',
'saveSettings',
'checkUpdate',
'downloadUpdate',
'updateFromInput',
'updateFromSlider',
'runPreflight',
'retryFailedDownloads',
'toggleDebugAutoRefresh'
];
const map = {};
for (const n of names) map[n] = typeof window[n];
return map;
});
await win.evaluate(() => {
window.showTab('clips');
window.showTab('cutter');
window.showTab('merge');
window.showTab('settings');
window.showTab('vods');
});
const input = win.locator('#newStreamer');
const randomName = `smoketest_${Date.now()}`;
await input.fill(randomName);
await win.evaluate(async () => {
await window.addStreamer();
});
const hasTempStreamer = await win.locator('#streamerList').innerText();
await win.evaluate(async (name) => {
await window.removeStreamer(name);
}, randomName);
await win.evaluate(async () => {
await window.selectStreamer('xrohat');
});
await win.waitForTimeout(3500);
const vodCount = await win.locator('.vod-card').count();
if (vodCount > 0) {
await win.locator('.vod-card .vod-btn.primary').first().click();
await win.waitForTimeout(500);
}
const queueCountAfterAdd = await win.locator('#queueCount').innerText();
const queueRemove = win.locator('#queueList .remove').first();
if (await queueRemove.count()) {
await queueRemove.click();
await win.waitForTimeout(300);
}
await win.evaluate(() => {
window.showTab('clips');
});
await win.fill('#clipUrl', '');
await win.evaluate(async () => {
await window.downloadClip();
});
const clipStatus = await win.locator('#clipStatus').innerText();
await win.evaluate(async () => {
await window.runPreflight(false);
await window.startCutting();
await window.startMerging();
});
const mergeButtonDisabled = await win.locator('#btnMerge').isDisabled();
const preflightText = await win.locator('#preflightResult').innerText();
const healthBadge = await win.locator('#healthBadge').innerText();
await app.close();
const failedGlobals = Object.entries(globals)
.filter(([, type]) => type !== 'function')
.map(([name, type]) => `${name}=${type}`);
const summary = {
failedGlobals,
hasTempStreamer: hasTempStreamer.includes(randomName),
vodCount,
queueCountAfterAdd,
clipStatus,
mergeButtonDisabled,
preflightText,
healthBadge,
issues
};
console.log(JSON.stringify(summary, null, 2));
const hasFailure =
failedGlobals.length > 0 ||
!summary.hasTempStreamer ||
summary.vodCount < 1 ||
!(summary.clipStatus.includes('Bitte URL eingeben') || summary.clipStatus.includes('Please enter a URL')) ||
!summary.mergeButtonDisabled ||
!summary.preflightText ||
!summary.healthBadge ||
summary.issues.length > 0;
process.exit(hasFailure ? 1 : 0);
}
run().catch((err) => {
console.error(err);
process.exit(1);
});

View File

@ -345,7 +345,7 @@
<div class="settings-card">
<h3 id="updateTitle">Updates</h3>
<p id="versionInfo" style="margin-bottom: 10px; color: var(--text-secondary);">Version: v4.0.1</p>
<p id="versionInfo" style="margin-bottom: 10px; color: var(--text-secondary);">Version: v4.0.2</p>
<button class="btn-secondary" id="checkUpdateBtn" onclick="checkUpdate()">Nach Updates suchen</button>
</div>
@ -377,7 +377,7 @@
<div class="status-dot" id="statusDot"></div>
<span id="statusText">Nicht verbunden</span>
</div>
<span id="versionText">v4.0.1</span>
<span id="versionText">v4.0.2</span>
</div>
</main>
</div>

View File

@ -8,7 +8,7 @@ import { autoUpdater } from 'electron-updater';
// ==========================================
// CONFIG & CONSTANTS
// ==========================================
const APP_VERSION = '4.0.1';
const APP_VERSION = '4.0.2';
const UPDATE_CHECK_URL = 'http://24-music.de/version.json';
// Paths