Größen-Limit merken
Überspringt nach zwei verdächtigen Ablehnungen auf einem Account größere Dateien dort vorab ("Bekanntes Größen-Limit"). Abschalten = jede Datei wird immer wirklich versucht.
@@ -6353,7 +6353,7 @@ function getCredsFieldsHtml(authType, account, hoster) {
Passwort
- 👁
+ 👁️
`;
}
// API key
@@ -6361,7 +6361,7 @@ function getCredsFieldsHtml(authType, account, hoster) {
API-Key
- 👁
+ 👁️
`;
}
@@ -6429,13 +6429,50 @@ function openAccountModal(editAccountId) {
onEscape: closeAccountModal,
onBackdrop: closeAccountModal
});
+ _openAccountModalMotion(modal);
+}
+
+let _accountModalMotionToken = 0;
+
+function _openAccountModalMotion(modal) {
+ const card = modal.querySelector('.modal-card');
+ const token = ++_accountModalMotionToken;
+ modal.classList.remove('account-modal-opening', 'account-modal-closing');
+ if (!card) return;
+ void card.offsetHeight;
+ modal.classList.add('account-modal-opening');
+ const finish = event => {
+ if (event && event.target !== card) return;
+ card.removeEventListener('animationend', finish);
+ if (Object.is(token, _accountModalMotionToken)) modal.classList.remove('account-modal-opening');
+ };
+ card.addEventListener('animationend', finish);
+ window.setTimeout(() => finish(), 440);
}
function closeAccountModal() {
- modalController.close('accountModal', { fallbackFocus: '#addAccountBtn' });
+ const modal = document.getElementById('accountModal');
+ if (!modalController.isOpen(modal) || modal.classList.contains('account-modal-closing')) return;
+ const card = modal.querySelector('.modal-card');
+ const token = ++_accountModalMotionToken;
+ modal.classList.remove('account-modal-opening', 'account-modal-closing');
+ if (card) void card.offsetHeight;
+ modalController.close(modal, { fallbackFocus: '#addAccountBtn' });
+ modal.style.display = 'flex';
+ modal.inert = true;
+ modal.classList.add('account-modal-closing');
_hideOtpField();
editingAccountId = null;
_resetAccountModalState();
+ const finish = event => {
+ if (event && event.target !== card) return;
+ if (card) card.removeEventListener('animationend', finish);
+ if (!Object.is(token, _accountModalMotionToken)) return;
+ modal.classList.remove('account-modal-closing');
+ modal.style.display = 'none';
+ };
+ if (card) card.addEventListener('animationend', finish);
+ window.setTimeout(() => finish(), 380);
}
function openDeleteAccountModal(accountId) {
diff --git a/renderer/i18n.js b/renderer/i18n.js
index 789d07a..f2ed63e 100644
--- a/renderer/i18n.js
+++ b/renderer/i18n.js
@@ -237,7 +237,7 @@
['Hoster-Einstellungen', 'Host settings'],
['Upload-Einstellungen', 'Upload settings'],
['Erfolgreiche Links in fileuploader.log.', 'Successful links in fileuploader.log.'],
- ['Verteilt die Dateien reihum auf alle aktiven Accounts dieses Hosters (Datei 1 → Account 1, Datei 2 → Account 2 …). Hält z. B. byse-Accounts aktiv. Nur ein Account = kein Effekt.', 'Distributes files across all active accounts for this host in round-robin order (file 1 → account 1, file 2 → account 2, and so on). Keeps accounts such as byse active. Has no effect with only one account.'],
+ ['Verteilt Dateien abwechselnd auf alle aktiven Accounts dieses Hosters. Bei nur einem Account hat die Option keinen Effekt.', 'Distributes files alternately across all active accounts for this host. The option has no effect with only one account.'],
['Größen-Limit merken', 'Remember size limit'],
['Überspringt nach zwei verdächtigen Ablehnungen auf einem Account größere Dateien dort vorab ("Bekanntes Größen-Limit"). Abschalten = jede Datei wird immer wirklich versucht.', 'After two suspicious rejections on an account, larger files are skipped there in advance ("Known size limit"). When disabled, every file is always attempted.'],
['Einstellungen einzelner Hoster', 'Settings for individual hosts'],
diff --git a/renderer/styles.css b/renderer/styles.css
index b413c57..6013c28 100644
--- a/renderer/styles.css
+++ b/renderer/styles.css
@@ -29,6 +29,7 @@
body {
font-family: 'Aptos', 'Segoe UI Variable Text', 'Bahnschrift', sans-serif;
background: var(--bg-primary);
+ cursor: default;
height: 100vh;
overflow: hidden;
color: var(--text);
@@ -1287,6 +1288,29 @@ select.hs-input { max-width: none; width: auto; min-width: 140px; }
.account-hoster-settings-header .panel-arrow { font-size: 10px; color: var(--text-dim); }
.account-hoster-settings-body-inner { padding: 4px 12px 10px; }
+.account-hoster-settings-body-inner .account-hoster-option-row {
+ grid-column: 1 / -1;
+ display: grid;
+ grid-template-columns: 150px minmax(0, 1fr) auto;
+ align-items: center;
+ column-gap: 12px;
+}
+
+.account-hoster-settings-body-inner .account-hoster-option-row label {
+ min-width: 0;
+}
+
+.account-hoster-settings-body-inner .account-hoster-option-row .hint {
+ grid-column: 2;
+ line-height: 1.45;
+}
+
+.account-hoster-settings-body-inner .account-hoster-option-row input[type="checkbox"] {
+ grid-column: 3;
+ grid-row: 1;
+ justify-self: end;
+}
+
.settings-hoster-pointer {
margin-top: 12px;
padding: 10px 14px;
@@ -3456,7 +3480,7 @@ input[type="checkbox"] {
}
#settingsSearchInput {
- font-size: 13px;
+ font-size: 11px;
}
.settings-nav-button {
@@ -3753,6 +3777,47 @@ input[type="checkbox"] {
box-shadow: 0 24px 64px rgba(0, 0, 0, .48);
}
+#accountModal.account-modal-opening {
+ animation: account-modal-dim-in 360ms cubic-bezier(.16, 1, .3, 1) both;
+}
+
+#accountModal.account-modal-opening > .modal-card {
+ transform-origin: top center;
+ animation: account-modal-unfold 360ms cubic-bezier(.16, 1, .3, 1) both;
+}
+
+#accountModal.account-modal-closing {
+ pointer-events: none;
+ animation: account-modal-dim-out 300ms cubic-bezier(.4, 0, .2, 1) both;
+}
+
+#accountModal.account-modal-closing > .modal-card {
+ transform-origin: top center;
+ animation: account-modal-fold 300ms cubic-bezier(.4, 0, .2, 1) both;
+}
+
+@keyframes account-modal-dim-in {
+ from { background: rgba(0, 0, 0, 0); }
+ to { background: rgba(0, 0, 0, .6); }
+}
+
+@keyframes account-modal-dim-out {
+ from { background: rgba(0, 0, 0, .6); }
+ to { background: rgba(0, 0, 0, 0); }
+}
+
+@keyframes account-modal-unfold {
+ 0% { opacity: 0; clip-path: inset(0 0 18% 0 round 12px); transform: translateY(-16px) scale(.985); }
+ 55% { opacity: .96; }
+ 100% { opacity: 1; clip-path: inset(0 round 12px); transform: translateY(0) scale(1); }
+}
+
+@keyframes account-modal-fold {
+ 0% { opacity: 1; clip-path: inset(0 round 12px); transform: translateY(0) scale(1); }
+ 45% { opacity: .9; }
+ 100% { opacity: 0; clip-path: inset(0 0 18% 0 round 12px); transform: translateY(-16px) scale(.985); }
+}
+
.modal-header,
.modal-footer {
padding: 16px 18px;
diff --git a/tests/startup-renderer.test.js b/tests/startup-renderer.test.js
index d8fc7b1..f59bec5 100644
--- a/tests/startup-renderer.test.js
+++ b/tests/startup-renderer.test.js
@@ -139,6 +139,32 @@ test('configureStartupRenderer disables hardware acceleration for a Windows Remo
assert.equal(calls, 1);
});
+test('configureStartupRenderer forces full motion only for hot dev', () => {
+ const devSwitches = [];
+ const releaseSwitches = [];
+ const createApp = switches => ({
+ disableHardwareAcceleration() {},
+ getPath(name) {
+ assert.equal(name, 'userData');
+ return 'C:\\ReleaseTest\\user-data';
+ },
+ commandLine: {
+ appendSwitch(name, value) {
+ switches.push({ name, value });
+ }
+ }
+ });
+
+ configureStartupRenderer(createApp(devSwitches), { SESSIONNAME: 'Console' }, 'win32', ['electron', '.', '--dev']);
+ configureStartupRenderer(createApp(releaseSwitches), { SESSIONNAME: 'Console' }, 'win32', ['Multi-Hoster-Upload.exe']);
+
+ assert.deepEqual(devSwitches, [
+ { name: 'force-prefers-no-reduced-motion', value: undefined },
+ { name: 'user-data-dir', value: 'C:\\ReleaseTest\\user-data' }
+ ]);
+ assert.deepEqual(releaseSwitches, []);
+});
+
test('resolveStartupLanguage accepts only the supported persisted language', () => {
assert.equal(resolveStartupLanguage({ globalSettings: { language: 'de' } }), 'de');
assert.equal(resolveStartupLanguage({ globalSettings: { language: 'en' } }), 'en');
@@ -147,10 +173,15 @@ test('resolveStartupLanguage accepts only the supported persisted language', ()
});
test('createStartupWindow forces the main window to start hidden', () => {
- const startup = createStartupWindow(TestBrowserWindow, { width: 1100, show: true });
+ const startup = createStartupWindow(TestBrowserWindow, {
+ width: 1100,
+ show: true,
+ disableAutoHideCursor: false
+ });
assert.equal(startup.window.options.width, 1100);
assert.equal(startup.window.options.show, false);
+ assert.equal(startup.window.options.disableAutoHideCursor, true);
});
test('main window uses the branded application icon', () => {
diff --git a/tests/ui-smoke.js b/tests/ui-smoke.js
index c946f0b..32a3fc8 100644
--- a/tests/ui-smoke.js
+++ b/tests/ui-smoke.js
@@ -167,6 +167,7 @@ setTimeout(async () => {
if (win.isMaximized()) win.unmaximize();
const wc = win.webContents;
if (typeof wc.setFrameRate === 'function') wc.setFrameRate(60);
+ const initialReducedMotion = await wc.executeJavaScript('matchMedia("(prefers-reduced-motion: reduce)").matches');
if (!wc.debugger.isAttached()) wc.debugger.attach('1.3');
await wc.debugger.sendCommand('Emulation.setEmulatedMedia', { features: [{ name: 'prefers-reduced-motion', value: 'no-preference' }] });
const setWindowBounds = async bounds => {
@@ -218,6 +219,7 @@ setTimeout(async () => {
try {
check('Every UI smoke window stays offscreen and never takes focus', hiddenWindowHarness.areNativeSurfacesSuppressed(hiddenWindowHarness.getWindows()));
+ check('Hot Dev forces full motion despite the Windows reduced-motion preference', initialReducedMotion === false);
const startupUpdateState = await wc.executeJavaScript('(() => { const button = document.getElementById("headerUpdateBtn"); return [_knownUpdateInfo?.remoteVersion, button?.hidden, getComputedStyle(button).display, document.getElementById("updateBanner")?.style.display].join("|"); })()');
check('Startup update survives pending renderer initialization', startupUpdateState === '9.9.8|false|flex|flex');
await wc.executeJavaScript('_knownUpdateInfo = null; closeUpdateDialog(); _syncHeaderUpdateState();');
@@ -317,6 +319,9 @@ setTimeout(async () => {
const appHeaderExists = await wc.executeJavaScript('Boolean(document.querySelector(".app-header"))');
check('App shell exposes the primary header', appHeaderExists);
+ const cursorContract = await wc.executeJavaScript('[getComputedStyle(document.body).cursor, getComputedStyle(document.getElementById("settings-tab")).cursor].join("|")');
+ check('Main surface keeps a visible default cursor and interactive controls keep the pointer cursor', cursorContract === 'default|pointer');
+
const appBrandText = await wc.executeJavaScript('document.querySelector(".app-brand-name")?.textContent?.trim()');
check('App header shows the Multi Hoster Uploader brand', appBrandText === 'MULTI HOSTER UPLOADER');
@@ -980,15 +985,96 @@ setTimeout(async () => {
check('Password visibility action exposes its state', passwordToggleState === 'Passwort verbergen|true');
await wc.executeJavaScript('document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", bubbles: true }))');
+ await new Promise(resolve => setTimeout(resolve, 340));
const accountModalHidden = await wc.executeJavaScript('document.getElementById("accountModal")?.style.display');
check('Escape closes account modal', accountModalHidden === 'none');
const restoredAccountFocus = await wc.executeJavaScript('document.activeElement?.hasAttribute("data-account-empty-add") || document.activeElement?.id === "addAccountBtn"');
check('Account modal restores trigger focus', restoredAccountFocus === true);
- const fallbackAccountFocus = await wc.executeJavaScript('(() => { const trigger = document.querySelector("[data-account-empty-add]") || document.getElementById("addAccountBtn"); trigger.focus(); trigger.click(); document.querySelector("[data-account-empty-add]")?.remove(); document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", bubbles: true })); return document.activeElement?.id; })()');
+ await wc.executeJavaScript('(() => { const trigger = document.querySelector("[data-account-empty-add]") || document.getElementById("addAccountBtn"); trigger.focus(); trigger.click(); document.querySelector("[data-account-empty-add]")?.remove(); document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", bubbles: true })); })()');
+ await new Promise(resolve => setTimeout(resolve, 340));
+ const fallbackAccountFocus = await wc.executeJavaScript('document.activeElement?.id');
check('Account modal restores stable focus after list rerender', fallbackAccountFocus === 'addAccountBtn');
+ ipcMain.removeHandler('validate-credentials');
+ ipcMain.handle('validate-credentials', () => ({ ok: true, status: 'ok', message: 'Credentials verified', checkedAt: '2033-01-02T03:04:05.000Z' }));
+ ipcMain.removeHandler('save-config');
+ ipcMain.handle('save-config', () => true);
+ await wc.executeJavaScript(\`(() => {
+ HOSTERS.forEach(name => { config.hosters[name] = []; });
+ config.hosters['byse.sx'] = [{ id: 'ui-animated-account', enabled: true, authType: 'api', apiKey: 'animated-key' }];
+ accountStatuses = { 'ui-animated-account': { status: 'ok', message: 'Ready' } };
+ renderAccounts();
+ document.querySelector('[data-account-edit="ui-animated-account"]')?.click();
+ })()\`);
+ await new Promise(resolve => setTimeout(resolve, 60));
+ const accountEditOpeningFrame = await wc.executeJavaScript(\`(() => {
+ const modal = document.getElementById('accountModal');
+ const card = modal?.querySelector('.modal-card');
+ const style = card ? getComputedStyle(card) : null;
+ return {
+ title: document.getElementById('accountModalTitle')?.textContent,
+ display: modal?.style.display,
+ animation: style?.animationName || 'none',
+ duration: parseFloat(style?.animationDuration || '0'),
+ transform: style?.transform || 'none',
+ clipPath: style?.clipPath || 'none',
+ opacity: Number(style?.opacity || 1),
+ overlayAnimation: getComputedStyle(modal).animationName,
+ overlayAlpha: Number((getComputedStyle(modal).backgroundColor.match(/[0-9.]+/g) || [0, 0, 0, 0])[3] || 0)
+ };
+ })()\`);
+ check('Editing an account unfolds the editor through a real opening frame', accountEditOpeningFrame.title === 'Account bearbeiten' && accountEditOpeningFrame.display === 'flex' && accountEditOpeningFrame.animation !== 'none' && accountEditOpeningFrame.duration >= .34 && (accountEditOpeningFrame.transform !== 'none' || accountEditOpeningFrame.clipPath !== 'none' || accountEditOpeningFrame.opacity < 1));
+ check('Opening the account editor softly fades in the surrounding dimming', accountEditOpeningFrame.overlayAnimation !== 'none' && accountEditOpeningFrame.overlayAlpha > 0 && accountEditOpeningFrame.overlayAlpha < .6);
+ await new Promise(resolve => setTimeout(resolve, 340));
+ const apiEyeEmoji = await wc.executeJavaScript('document.querySelector("#accountCredsFields .toggle-vis")?.textContent');
+ check('API key visibility uses the normal eye emoji', apiEyeEmoji === '👁️');
+
+ await wc.executeJavaScript('document.getElementById("closeAccountModalBtn")?.click()');
+ await new Promise(resolve => setTimeout(resolve, 60));
+ const accountEditClosingFrame = await wc.executeJavaScript(\`(() => {
+ const modal = document.getElementById('accountModal');
+ const card = modal?.querySelector('.modal-card');
+ const style = card ? getComputedStyle(card) : null;
+ return {
+ display: modal?.style.display,
+ ariaHidden: modal?.getAttribute('aria-hidden'),
+ modalInert: modal?.inert,
+ headerInert: document.querySelector('.app-header')?.inert,
+ activeViewInert: document.querySelector('.view.active')?.inert,
+ animation: style?.animationName || 'none',
+ duration: parseFloat(style?.animationDuration || '0'),
+ transform: style?.transform || 'none',
+ clipPath: style?.clipPath || 'none',
+ opacity: Number(style?.opacity || 1),
+ overlayAnimation: getComputedStyle(modal).animationName,
+ overlayAlpha: Number((getComputedStyle(modal).backgroundColor.match(/[0-9.]+/g) || [0, 0, 0, 0])[3] || 0)
+ };
+ })()\`);
+ check('The close button folds the account editor upward without blocking the application', accountEditClosingFrame.display === 'flex' && accountEditClosingFrame.ariaHidden === 'true' && accountEditClosingFrame.modalInert === true && accountEditClosingFrame.headerInert === false && accountEditClosingFrame.activeViewInert === false && accountEditClosingFrame.animation !== 'none' && accountEditClosingFrame.duration >= .28 && (accountEditClosingFrame.transform !== 'none' || accountEditClosingFrame.clipPath !== 'none' || accountEditClosingFrame.opacity < 1));
+ check('Closing the account editor softly fades out the surrounding dimming', accountEditClosingFrame.overlayAnimation !== 'none' && accountEditClosingFrame.overlayAlpha > 0 && accountEditClosingFrame.overlayAlpha < .6);
+ await new Promise(resolve => setTimeout(resolve, 340));
+ const accountEditClosed = await wc.executeJavaScript('document.getElementById("accountModal")?.style.display === "none"');
+ check('The account editor hides after its closing animation', accountEditClosed === true);
+
+ await wc.executeJavaScript('document.querySelector("[data-account-edit=ui-animated-account]")?.click()');
+ await new Promise(resolve => setTimeout(resolve, 400));
+ await wc.executeJavaScript('document.getElementById("saveAccountBtn")?.click()');
+ await new Promise(resolve => setTimeout(resolve, 680));
+ const accountSaveClosingFrame = await wc.executeJavaScript(\`(() => {
+ const modal = document.getElementById('accountModal');
+ const card = modal?.querySelector('.modal-card');
+ const style = card ? getComputedStyle(card) : null;
+ return { display: modal?.style.display, animation: style?.animationName || 'none' };
+ })()\`);
+ check('A verified saved account uses the same closing animation', accountSaveClosingFrame.display === 'flex' && accountSaveClosingFrame.animation !== 'none');
+ await new Promise(resolve => setTimeout(resolve, 340));
+ const accountSaveClosed = await wc.executeJavaScript('document.getElementById("accountModal")?.style.display === "none"');
+ check('A verified saved account hides after the closing animation', accountSaveClosed === true);
+ restoreInitialIpcHandler('validate-credentials');
+ restoreInitialIpcHandler('save-config');
+
await setWindowBounds({ ...win.getBounds(), width: 1280, height: 720 });
const emptyAccountsGeometry = await wc.executeJavaScript(\`(() => {
@@ -1037,6 +1123,28 @@ setTimeout(async () => {
check('Hoster groups visibly animate while opening and closing', hasSmoothAccountCollapse(accountCollapseMotion.hosterMotion));
check('Hoster upload settings visibly animate while opening and closing', hasSmoothAccountCollapse(accountCollapseMotion.settingsMotion));
+ const accountSettingsDescriptionLayout = await wc.executeJavaScript(\`(async () => {
+ const hoster = HOSTERS[0];
+ const settings = document.querySelector('[data-hoster-settings-toggle="' + hoster + '"]');
+ if (settings && settings.getAttribute('aria-expanded') !== 'true') settings.click();
+ await new Promise(resolve => setTimeout(resolve, 240));
+ const container = document.querySelector('.account-hoster-settings-body-inner');
+ const rows = [...document.querySelectorAll('.account-hoster-option-row')];
+ const containerRect = container?.getBoundingClientRect();
+ return rows.map(row => {
+ const rowRect = row.getBoundingClientRect();
+ const hintRect = row.querySelector('.hint')?.getBoundingClientRect();
+ const inputRect = row.querySelector('input')?.getBoundingClientRect();
+ return {
+ widthRatio: containerRect ? rowRect.width / containerRect.width : 0,
+ descriptionBeforeToggle: Boolean(hintRect && inputRect && hintRect.right <= inputRect.left)
+ };
+ });
+ })()\`);
+ const accountSettingsDescriptionLayoutClean = accountSettingsDescriptionLayout.length === 3 && accountSettingsDescriptionLayout.every(row => row.widthRatio >= .97 && row.descriptionBeforeToggle);
+ if (!accountSettingsDescriptionLayoutClean) console.log('Account settings description layout:', JSON.stringify(accountSettingsDescriptionLayout));
+ check('Account host options give descriptions a full clean row with toggles aligned right', accountSettingsDescriptionLayoutClean);
+
const accountsFooterGeometry = await wc.executeJavaScript(\`(() => {
const main = document.querySelector('#accounts-view .accounts-main')?.getBoundingClientRect();
const list = document.getElementById('accountsList')?.getBoundingClientRect();
@@ -1394,6 +1502,8 @@ setTimeout(async () => {
check('Settings search icon aligns to the input text line', settingsSearchIconAlignment === 'flex|center|true');
const settingsSearchControlGeometry = await wc.executeJavaScript('(() => { const control = document.querySelector(".settings-search-control"); const input = document.getElementById("settingsSearchInput"); const icon = document.querySelector(".settings-search-icon"); const svg = icon?.querySelector("svg"); if (!control || !input || !icon || !svg) return "missing"; const controlRect = control.getBoundingClientRect(); const inputRect = input.getBoundingClientRect(); const iconRect = icon.getBoundingClientRect(); const inputStyle = getComputedStyle(input); const iconStyle = getComputedStyle(icon); return [Math.round(controlRect.height), Math.round(inputRect.height), Math.round(Math.abs((inputRect.top + inputRect.height / 2) - (iconRect.top + iconRect.height / 2))), svg.getAttribute("viewBox"), inputStyle.lineHeight, inputStyle.paddingTop, inputStyle.paddingBottom, iconStyle.display, iconStyle.alignItems].join("|"); })()');
check('Settings search control keeps icon and text on one shared center line', settingsSearchControlGeometry === '44|44|0|0 0 24 24|18px|0px|0px|flex|center');
+ const settingsSearchPlaceholderFit = await wc.executeJavaScript('(() => { const input = document.getElementById("settingsSearchInput"); if (!input) return false; const style = getComputedStyle(input); const canvas = document.createElement("canvas"); const context = canvas.getContext("2d"); context.font = style.font; const available = input.clientWidth - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight); return ["Einstellungen durchsuchen", "Search settings"].every(text => context.measureText(text).width <= available - 12); })()');
+ check('Settings search placeholders retain visible right-side breathing room', settingsSearchPlaceholderFit === true);
await wc.executeJavaScript('document.querySelector("[data-settings-page=\\'logs\\']")?.click()');
await new Promise(resolve => setTimeout(resolve, 100));
@@ -1454,7 +1564,7 @@ setTimeout(async () => {
const plaintextCredentialOverride = await wc.executeJavaScript('(() => ({ control: document.getElementById("allowPlaintextCredentialStorageInput"), copy: document.body.textContent.includes("Unsichere Klartext-Speicherung"), bridge: typeof window.api.getSecretStoreStatus }))()');
check('Settings expose no plaintext credential storage override', plaintextCredentialOverride.control === null && plaintextCredentialOverride.copy === false && plaintextCredentialOverride.bridge === 'undefined');
const settingsTypography = await wc.executeJavaScript('(() => { const size = selector => parseFloat(getComputedStyle(document.querySelector(selector)).fontSize); return { heading: size(".settings-subpage.active .settings-page-header h3"), intro: size(".settings-subpage.active .settings-page-header p"), section: size(".settings-subpage.active .settings-section-label"), rowLabel: size(".settings-subpage.active .settings-row > label"), hint: size(".settings-subpage.active .hint"), optionLabel: size(".settings-subpage.active .settings-option-copy label"), optionDescription: size(".settings-subpage.active .settings-option-description"), navigation: size(".settings-nav-button"), search: size("#settingsSearchInput") }; })()');
- check('Settings use the enlarged readable typography scale', settingsTypography.heading >= 22 && settingsTypography.intro >= 14 && settingsTypography.section >= 12 && settingsTypography.rowLabel >= 14 && settingsTypography.hint >= 12 && settingsTypography.optionLabel >= 14 && settingsTypography.optionDescription >= 12 && settingsTypography.navigation >= 13 && settingsTypography.search >= 13);
+ check('Settings use the enlarged readable typography scale', settingsTypography.heading >= 22 && settingsTypography.intro >= 14 && settingsTypography.section >= 12 && settingsTypography.rowLabel >= 14 && settingsTypography.hint >= 12 && settingsTypography.optionLabel >= 14 && settingsTypography.optionDescription >= 12 && settingsTypography.navigation >= 13 && settingsTypography.search >= 11);
const settingsSelection = await wc.executeJavaScript('(() => ({ heading: getComputedStyle(document.querySelector(".settings-subpage.active .settings-page-header h3")).userSelect, hint: getComputedStyle(document.querySelector(".settings-subpage.active .hint")).userSelect, input: getComputedStyle(document.getElementById("globalMaxSpeedMbsInput")).userSelect }))()');
check('Settings interface copy cannot be selected while input values remain selectable', settingsSelection.heading === 'none' && settingsSelection.hint === 'none' && settingsSelection.input === 'text');
const enlargedSettingsFit = await wc.executeJavaScript('(() => { const results = [...document.querySelectorAll(".settings-nav-button")].map(button => { button.click(); const page = document.querySelector(".settings-subpage.active"); return Boolean(page && page.scrollWidth <= page.clientWidth + 1); }); document.querySelector("[data-settings-page=uploads]")?.click(); return results.every(Boolean); })()');
@@ -3564,7 +3674,7 @@ try {
const result = execFileSync(
electronPath,
- [`--user-data-dir=${userDataPath}`, '--require', injectPath, mainPath],
+ [`--user-data-dir=${userDataPath}`, '--require', injectPath, mainPath, '--dev'],
{ cwd: path.join(__dirname, '..'), timeout: 180000, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }
);
console.log(result);