Add updater skip-version regression coverage and shared version utils (v4.1.13)
This commit is contained in:
parent
9c7de22c3a
commit
d1fcbfaadb
4
typescript-version/package-lock.json
generated
4
typescript-version/package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "twitch-vod-manager",
|
"name": "twitch-vod-manager",
|
||||||
"version": "4.1.12",
|
"version": "4.1.13",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "twitch-vod-manager",
|
"name": "twitch-vod-manager",
|
||||||
"version": "4.1.12",
|
"version": "4.1.13",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.6.0",
|
"axios": "^1.6.0",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "twitch-vod-manager",
|
"name": "twitch-vod-manager",
|
||||||
"version": "4.1.12",
|
"version": "4.1.13",
|
||||||
"description": "Twitch VOD Manager - Download Twitch VODs easily",
|
"description": "Twitch VOD Manager - Download Twitch VODs easily",
|
||||||
"main": "dist/main.js",
|
"main": "dist/main.js",
|
||||||
"author": "xRangerDE",
|
"author": "xRangerDE",
|
||||||
@ -8,10 +8,11 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"start": "npm run build && electron .",
|
"start": "npm run build && electron .",
|
||||||
|
"test:e2e:update-logic": "node scripts/smoke-test-update-version-logic.js",
|
||||||
"test:e2e": "npm exec --yes --package=playwright -- node scripts/smoke-test.js",
|
"test:e2e": "npm exec --yes --package=playwright -- node scripts/smoke-test.js",
|
||||||
"test:e2e:guide": "npm exec --yes --package=playwright -- node scripts/smoke-test-template-guide.js",
|
"test:e2e:guide": "npm exec --yes --package=playwright -- node scripts/smoke-test-template-guide.js",
|
||||||
"test:e2e:full": "npm exec --yes --package=playwright -- node scripts/smoke-test-full.js",
|
"test:e2e:full": "npm exec --yes --package=playwright -- node scripts/smoke-test-full.js",
|
||||||
"test:e2e:release": "npm run build && npm run test:e2e && npm run test:e2e:guide && npm run test:e2e:full",
|
"test:e2e:release": "npm run build && npm run test:e2e:update-logic && npm run test:e2e && npm run test:e2e:guide && npm run test:e2e:full",
|
||||||
"test:e2e:stress": "npm run test:e2e:release && npm run test:e2e:release && npm run test:e2e:release",
|
"test:e2e:stress": "npm run test:e2e:release && npm run test:e2e:release && npm run test:e2e:release",
|
||||||
"pack": "npm run build && electron-builder --dir",
|
"pack": "npm run build && electron-builder --dir",
|
||||||
"dist": "npm run build && electron-builder",
|
"dist": "npm run build && electron-builder",
|
||||||
|
|||||||
@ -0,0 +1,84 @@
|
|||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const {
|
||||||
|
normalizeUpdateVersion,
|
||||||
|
compareUpdateVersions,
|
||||||
|
isNewerUpdateVersion
|
||||||
|
} = require(path.join(process.cwd(), 'dist', 'update-version-utils.js'));
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
const failures = [];
|
||||||
|
|
||||||
|
const assert = (condition, message) => {
|
||||||
|
if (!condition) failures.push(message);
|
||||||
|
};
|
||||||
|
|
||||||
|
const comparisons = [
|
||||||
|
{ left: '4.1.18', right: '4.1.10', expected: 1 },
|
||||||
|
{ left: '4.1.10', right: '4.1.18', expected: -1 },
|
||||||
|
{ left: 'v4.1.12', right: '4.1.12', expected: 0 },
|
||||||
|
{ left: '4.1.12', right: '4.1.12.1', expected: -1 },
|
||||||
|
{ left: '4.2.0', right: '4.1.999', expected: 1 },
|
||||||
|
{ left: '4.1.12-beta', right: '4.1.12', expected: 0 }
|
||||||
|
];
|
||||||
|
|
||||||
|
const compareResults = comparisons.map((testCase) => {
|
||||||
|
const actual = compareUpdateVersions(testCase.left, testCase.right);
|
||||||
|
const pass = actual === testCase.expected;
|
||||||
|
assert(pass, `compare failed: ${testCase.left} vs ${testCase.right} expected ${testCase.expected}, got ${actual}`);
|
||||||
|
return { ...testCase, actual, pass };
|
||||||
|
});
|
||||||
|
|
||||||
|
const skipVersionScenarios = [
|
||||||
|
{
|
||||||
|
name: 'old downloaded, newer available',
|
||||||
|
downloaded: '4.1.11',
|
||||||
|
latestKnown: '4.1.18',
|
||||||
|
expectedNeedsNewer: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'already latest downloaded',
|
||||||
|
downloaded: '4.1.18',
|
||||||
|
latestKnown: '4.1.18',
|
||||||
|
expectedNeedsNewer: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'downgrade should not trigger',
|
||||||
|
downloaded: '4.1.18',
|
||||||
|
latestKnown: '4.1.11',
|
||||||
|
expectedNeedsNewer: false
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const scenarioResults = skipVersionScenarios.map((scenario) => {
|
||||||
|
const needsNewer = isNewerUpdateVersion(scenario.latestKnown, scenario.downloaded);
|
||||||
|
const pass = needsNewer === scenario.expectedNeedsNewer;
|
||||||
|
assert(pass, `${scenario.name} expected ${scenario.expectedNeedsNewer}, got ${needsNewer}`);
|
||||||
|
return { ...scenario, needsNewer, pass };
|
||||||
|
});
|
||||||
|
|
||||||
|
const normalizationChecks = {
|
||||||
|
fromVPrefix: normalizeUpdateVersion('v4.1.12') === '4.1.12',
|
||||||
|
trimmed: normalizeUpdateVersion(' 4.1.12 ') === '4.1.12'
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(normalizationChecks.fromVPrefix, 'normalize did not remove v prefix');
|
||||||
|
assert(normalizationChecks.trimmed, 'normalize did not trim whitespace');
|
||||||
|
|
||||||
|
const summary = {
|
||||||
|
checks: {
|
||||||
|
compareResults,
|
||||||
|
scenarioResults,
|
||||||
|
normalizationChecks
|
||||||
|
},
|
||||||
|
failures
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(JSON.stringify(summary, null, 2));
|
||||||
|
|
||||||
|
if (failures.length) {
|
||||||
|
process.exitCode = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
@ -457,7 +457,7 @@
|
|||||||
|
|
||||||
<div class="settings-card">
|
<div class="settings-card">
|
||||||
<h3 id="updateTitle">Updates</h3>
|
<h3 id="updateTitle">Updates</h3>
|
||||||
<p id="versionInfo" style="margin-bottom: 10px; color: var(--text-secondary);">Version: v4.1.12</p>
|
<p id="versionInfo" style="margin-bottom: 10px; color: var(--text-secondary);">Version: v4.1.13</p>
|
||||||
<button class="btn-secondary" id="checkUpdateBtn" onclick="checkUpdate()">Nach Updates suchen</button>
|
<button class="btn-secondary" id="checkUpdateBtn" onclick="checkUpdate()">Nach Updates suchen</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -502,7 +502,7 @@
|
|||||||
<div class="status-dot" id="statusDot"></div>
|
<div class="status-dot" id="statusDot"></div>
|
||||||
<span id="statusText">Nicht verbunden</span>
|
<span id="statusText">Nicht verbunden</span>
|
||||||
</div>
|
</div>
|
||||||
<span id="versionText">v4.1.12</span>
|
<span id="versionText">v4.1.13</span>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,11 +4,12 @@ import * as fs from 'fs';
|
|||||||
import { spawn, ChildProcess, execSync, exec, spawnSync } from 'child_process';
|
import { spawn, ChildProcess, execSync, exec, spawnSync } from 'child_process';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { autoUpdater } from 'electron-updater';
|
import { autoUpdater } from 'electron-updater';
|
||||||
|
import { compareUpdateVersions, isNewerUpdateVersion, normalizeUpdateVersion } from './update-version-utils';
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// CONFIG & CONSTANTS
|
// CONFIG & CONSTANTS
|
||||||
// ==========================================
|
// ==========================================
|
||||||
const APP_VERSION = '4.1.12';
|
const APP_VERSION = '4.1.13';
|
||||||
const UPDATE_CHECK_URL = 'http://24-music.de/version.json';
|
const UPDATE_CHECK_URL = 'http://24-music.de/version.json';
|
||||||
|
|
||||||
// Paths
|
// Paths
|
||||||
@ -2944,45 +2945,12 @@ function createWindow(): void {
|
|||||||
// ==========================================
|
// ==========================================
|
||||||
// AUTO-UPDATER (electron-updater)
|
// AUTO-UPDATER (electron-updater)
|
||||||
// ==========================================
|
// ==========================================
|
||||||
function normalizeUpdateVersion(version: string | null | undefined): string {
|
|
||||||
return (version || '').trim().replace(/^v/i, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
function compareUpdateVersions(left: string | null | undefined, right: string | null | undefined): number {
|
|
||||||
const a = normalizeUpdateVersion(left);
|
|
||||||
const b = normalizeUpdateVersion(right);
|
|
||||||
|
|
||||||
if (!a && !b) return 0;
|
|
||||||
if (!a) return -1;
|
|
||||||
if (!b) return 1;
|
|
||||||
|
|
||||||
const aParts = a.split('.').map((part) => {
|
|
||||||
const numeric = Number(part.replace(/[^0-9].*$/, ''));
|
|
||||||
return Number.isFinite(numeric) ? numeric : 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
const bParts = b.split('.').map((part) => {
|
|
||||||
const numeric = Number(part.replace(/[^0-9].*$/, ''));
|
|
||||||
return Number.isFinite(numeric) ? numeric : 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
const maxLength = Math.max(aParts.length, bParts.length);
|
|
||||||
for (let i = 0; i < maxLength; i += 1) {
|
|
||||||
const av = aParts[i] || 0;
|
|
||||||
const bv = bParts[i] || 0;
|
|
||||||
if (av > bv) return 1;
|
|
||||||
if (av < bv) return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasNewerKnownUpdateThanDownloaded(): boolean {
|
function hasNewerKnownUpdateThanDownloaded(): boolean {
|
||||||
if (!latestKnownUpdateVersion || !downloadedUpdateVersion) {
|
if (!latestKnownUpdateVersion || !downloadedUpdateVersion) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return compareUpdateVersions(latestKnownUpdateVersion, downloadedUpdateVersion) > 0;
|
return isNewerUpdateVersion(latestKnownUpdateVersion, downloadedUpdateVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function requestUpdateCheck(source: UpdateCheckSource, force = false): Promise<{ started: boolean; reason?: string }> {
|
async function requestUpdateCheck(source: UpdateCheckSource, force = false): Promise<{ started: boolean; reason?: string }> {
|
||||||
|
|||||||
34
typescript-version/src/update-version-utils.ts
Normal file
34
typescript-version/src/update-version-utils.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
export function normalizeUpdateVersion(version: string | null | undefined): string {
|
||||||
|
return (version || '').trim().replace(/^v/i, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseVersionPart(part: string): number {
|
||||||
|
const numeric = Number(part.replace(/[^0-9].*$/, ''));
|
||||||
|
return Number.isFinite(numeric) ? numeric : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compareUpdateVersions(left: string | null | undefined, right: string | null | undefined): number {
|
||||||
|
const a = normalizeUpdateVersion(left);
|
||||||
|
const b = normalizeUpdateVersion(right);
|
||||||
|
|
||||||
|
if (!a && !b) return 0;
|
||||||
|
if (!a) return -1;
|
||||||
|
if (!b) return 1;
|
||||||
|
|
||||||
|
const aParts = a.split('.').map(parseVersionPart);
|
||||||
|
const bParts = b.split('.').map(parseVersionPart);
|
||||||
|
const maxLength = Math.max(aParts.length, bParts.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < maxLength; i += 1) {
|
||||||
|
const av = aParts[i] || 0;
|
||||||
|
const bv = bParts[i] || 0;
|
||||||
|
if (av > bv) return 1;
|
||||||
|
if (av < bv) return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNewerUpdateVersion(candidate: string | null | undefined, baseline: string | null | undefined): boolean {
|
||||||
|
return compareUpdateVersions(candidate, baseline) > 0;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user