fix: serialize managed backup actions
Queue copy and delete operations by sanitized backup ID so confirmations survive renderer refreshes and deletes follow in-flight copies without silent cancellation. Reload the managed list exactly once after successful create and delete mutations, and invalidate stale UI state when the authoritative refresh fails.
This commit is contained in:
+83
-46
@@ -63,8 +63,9 @@ let hosterSettings = {};
|
||||
let uploading = false;
|
||||
let healthCheckRunning = false;
|
||||
let managedOnlineBackups = [];
|
||||
let managedOnlineBackupsAuthoritative = false;
|
||||
let managedOnlineBackupLoadGeneration = 0;
|
||||
const managedOnlineBackupPendingActions = new Map();
|
||||
const managedOnlineBackupOperationQueues = new Map();
|
||||
|
||||
let _rLongTasks = 0, _rLongTaskMax = 0, _rFrameLast = 0, _rFrameWorst = 0, _rFrameCount = 0, _rFrameJank = 0, _rPerfLastLog = 0, _rPerfWindowStart = 0;
|
||||
function _rElLabel(el) {
|
||||
@@ -2667,6 +2668,13 @@ function normalizeManagedOnlineBackups(entries) {
|
||||
|
||||
function replaceManagedOnlineBackups(entries) {
|
||||
managedOnlineBackups = normalizeManagedOnlineBackups(entries);
|
||||
managedOnlineBackupsAuthoritative = true;
|
||||
renderManagedOnlineBackups();
|
||||
}
|
||||
|
||||
function invalidateManagedOnlineBackups() {
|
||||
managedOnlineBackups = [];
|
||||
managedOnlineBackupsAuthoritative = false;
|
||||
renderManagedOnlineBackups();
|
||||
}
|
||||
|
||||
@@ -2674,6 +2682,10 @@ function renderManagedOnlineBackups() {
|
||||
const list = document.getElementById('managedOnlineBackupList');
|
||||
if (!list) return;
|
||||
const content = document.createDocumentFragment();
|
||||
if (!managedOnlineBackupsAuthoritative) {
|
||||
list.replaceChildren(content);
|
||||
return;
|
||||
}
|
||||
if (managedOnlineBackups.length === 0) {
|
||||
const empty = document.createElement('p');
|
||||
empty.className = 'online-backup-managed-empty';
|
||||
@@ -2699,11 +2711,11 @@ function renderManagedOnlineBackups() {
|
||||
deleteButton.type = 'button';
|
||||
deleteButton.className = 'btn btn-danger online-backup-delete-btn';
|
||||
deleteButton.textContent = localizeUiText('Online-Backup löschen');
|
||||
const pendingAction = managedOnlineBackupPendingActions.get(entry.id);
|
||||
copyButton.disabled = pendingAction === 'copy' || pendingAction === 'delete';
|
||||
deleteButton.disabled = pendingAction === 'delete';
|
||||
copyButton.addEventListener('click', () => copyManagedOnlineBackup(entry, copyButton));
|
||||
deleteButton.addEventListener('click', () => deleteManagedOnlineBackup(entry, row));
|
||||
const pending = managedOnlineBackupOperationQueues.has(entry.id);
|
||||
copyButton.disabled = pending;
|
||||
deleteButton.disabled = pending;
|
||||
copyButton.addEventListener('click', () => copyManagedOnlineBackup(entry));
|
||||
deleteButton.addEventListener('click', () => deleteManagedOnlineBackup(entry));
|
||||
actions.append(copyButton, deleteButton);
|
||||
row.append(key, created, actions);
|
||||
content.append(row);
|
||||
@@ -2718,63 +2730,90 @@ async function loadManagedOnlineBackups() {
|
||||
const result = await window.api.listManagedOnlineBackups();
|
||||
if (generation !== managedOnlineBackupLoadGeneration) return false;
|
||||
if (!result?.ok || !Array.isArray(result.entries)) {
|
||||
invalidateManagedOnlineBackups();
|
||||
setOnlineBackupStatus('Online-Sicherungen konnten nicht geladen werden', 'error');
|
||||
return false;
|
||||
}
|
||||
replaceManagedOnlineBackups(result.entries);
|
||||
setOnlineBackupStatus('', '');
|
||||
return true;
|
||||
} catch {
|
||||
if (generation === managedOnlineBackupLoadGeneration) setOnlineBackupStatus('Online-Sicherungen konnten nicht geladen werden', 'error');
|
||||
if (generation === managedOnlineBackupLoadGeneration) {
|
||||
invalidateManagedOnlineBackups();
|
||||
setOnlineBackupStatus('Online-Sicherungen konnten nicht geladen werden', 'error');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function copyManagedOnlineBackup(entry, button) {
|
||||
if (button.disabled || managedOnlineBackupPendingActions.has(entry.id)) return;
|
||||
managedOnlineBackupPendingActions.set(entry.id, 'copy');
|
||||
button.disabled = true;
|
||||
try {
|
||||
const result = await window.api.copyManagedOnlineBackup(entry.id);
|
||||
if (!result?.ok) {
|
||||
setOnlineBackupStatus('Online-Sicherung konnte nicht kopiert werden', 'error');
|
||||
return;
|
||||
}
|
||||
showCopyToast('Online-Schlüssel kopiert');
|
||||
} catch {
|
||||
setOnlineBackupStatus('Online-Sicherung konnte nicht kopiert werden', 'error');
|
||||
} finally {
|
||||
managedOnlineBackupPendingActions.delete(entry.id);
|
||||
renderManagedOnlineBackups();
|
||||
}
|
||||
function hasManagedOnlineBackup(id) {
|
||||
return managedOnlineBackups.some(entry => entry.id === id);
|
||||
}
|
||||
|
||||
async function deleteManagedOnlineBackup(entry, row) {
|
||||
function enqueueManagedOnlineBackupOperation(id, action, operation) {
|
||||
const previous = managedOnlineBackupOperationQueues.get(id)?.promise || Promise.resolve();
|
||||
const promise = previous.catch(() => {}).then(operation);
|
||||
const state = { action, promise };
|
||||
managedOnlineBackupOperationQueues.set(id, state);
|
||||
renderManagedOnlineBackups();
|
||||
return promise.finally(() => {
|
||||
if (managedOnlineBackupOperationQueues.get(id) !== state) return;
|
||||
managedOnlineBackupOperationQueues.delete(id);
|
||||
renderManagedOnlineBackups();
|
||||
});
|
||||
}
|
||||
|
||||
async function copyManagedOnlineBackup(entry) {
|
||||
const id = entry.id;
|
||||
if (!hasManagedOnlineBackup(id) || managedOnlineBackupOperationQueues.has(id)) return;
|
||||
await enqueueManagedOnlineBackupOperation(id, 'copy', async () => {
|
||||
try {
|
||||
const result = await window.api.copyManagedOnlineBackup(id);
|
||||
if (!result?.ok) {
|
||||
setOnlineBackupStatus('Online-Sicherung konnte nicht kopiert werden', 'error');
|
||||
return;
|
||||
}
|
||||
showCopyToast('Online-Schlüssel kopiert');
|
||||
} catch {
|
||||
setOnlineBackupStatus('Online-Sicherung konnte nicht kopiert werden', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function deleteManagedOnlineBackup(entry) {
|
||||
const id = entry.id;
|
||||
const confirmed = await showAppConfirm({
|
||||
title: 'Online-Backup löschen',
|
||||
message: 'Dieses verschlüsselte Online-Backup wird dauerhaft vom Server gelöscht.',
|
||||
confirmText: 'Löschen',
|
||||
danger: true
|
||||
});
|
||||
if (!confirmed || !row.isConnected) return;
|
||||
if (managedOnlineBackupPendingActions.has(entry.id)) return;
|
||||
managedOnlineBackupPendingActions.set(entry.id, 'delete');
|
||||
const controls = [...row.querySelectorAll('button')];
|
||||
controls.forEach(button => { button.disabled = true; });
|
||||
try {
|
||||
const result = await window.api.deleteManagedOnlineBackup(entry.id);
|
||||
if (!result?.ok || result.removedId !== entry.id) {
|
||||
if (!confirmed) return;
|
||||
if (!hasManagedOnlineBackup(id)) {
|
||||
setOnlineBackupStatus('Online-Sicherung konnte nicht gelöscht werden', 'error');
|
||||
return;
|
||||
}
|
||||
if (managedOnlineBackupOperationQueues.get(id)?.action === 'delete') {
|
||||
setOnlineBackupStatus('Online-Sicherung konnte nicht gelöscht werden', 'error');
|
||||
return;
|
||||
}
|
||||
await enqueueManagedOnlineBackupOperation(id, 'delete', async () => {
|
||||
if (!hasManagedOnlineBackup(id)) {
|
||||
setOnlineBackupStatus('Online-Sicherung konnte nicht gelöscht werden', 'error');
|
||||
return;
|
||||
}
|
||||
managedOnlineBackupLoadGeneration++;
|
||||
replaceManagedOnlineBackups(managedOnlineBackups.filter(item => item.id !== result.removedId));
|
||||
setOnlineBackupStatus('Schlüssel gelöscht', 'success');
|
||||
} catch {
|
||||
setOnlineBackupStatus('Online-Sicherung konnte nicht gelöscht werden', 'error');
|
||||
} finally {
|
||||
managedOnlineBackupPendingActions.delete(entry.id);
|
||||
renderManagedOnlineBackups();
|
||||
}
|
||||
try {
|
||||
const result = await window.api.deleteManagedOnlineBackup(id);
|
||||
if (!result?.ok || result.removedId !== id) {
|
||||
setOnlineBackupStatus('Online-Sicherung konnte nicht gelöscht werden', 'error');
|
||||
return;
|
||||
}
|
||||
if (!await loadManagedOnlineBackups()) return;
|
||||
setOnlineBackupStatus('Schlüssel gelöscht', 'success');
|
||||
} catch {
|
||||
setOnlineBackupStatus('Online-Sicherung konnte nicht gelöscht werden', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function doOnlineBackupCreate() {
|
||||
@@ -2783,7 +2822,6 @@ async function doOnlineBackupCreate() {
|
||||
try {
|
||||
await flushPendingSettingsSaves();
|
||||
openOnlineBackupView();
|
||||
await loadManagedOnlineBackups();
|
||||
} catch {
|
||||
openOnlineBackupView();
|
||||
setOnlineBackupStatus('Nicht alle Einstellungen konnten gespeichert werden', 'error');
|
||||
@@ -2795,12 +2833,11 @@ async function doOnlineBackupCreate() {
|
||||
setOnlineBackupStatus('Verschlüssele und speichere Einstellungen…', 'busy');
|
||||
try {
|
||||
const result = await window.api.createManagedOnlineBackup();
|
||||
if (!result?.ok || normalizeManagedOnlineBackups([result.entry]).length !== 1) {
|
||||
if (!result?.ok) {
|
||||
setOnlineBackupStatus('Online-Sicherung konnte nicht erstellt werden', 'error');
|
||||
return;
|
||||
}
|
||||
managedOnlineBackupLoadGeneration++;
|
||||
replaceManagedOnlineBackups([...managedOnlineBackups, result.entry]);
|
||||
if (!await loadManagedOnlineBackups()) return;
|
||||
setOnlineBackupStatus('Neuer Schlüssel erstellt. Ältere Schlüssel bleiben gültig.', 'success');
|
||||
showCopyToast('Online-Schlüssel erstellt');
|
||||
} catch {
|
||||
|
||||
+113
-25
@@ -63,6 +63,25 @@ test('Windows compositor paints the full hidden surface with an RDP session envi
|
||||
fs.writeFileSync(preloadPath, `
|
||||
const { contextBridge } = require('electron');
|
||||
const managedOnlineBackupProbeCalls = [];
|
||||
const managedOnlineBackupListResponses = [
|
||||
{ ok: true, entries: [
|
||||
{ id: 'AAAAAAAAAAAAAAAAAAAAAA', displayKey: 'MHU2-ABCD…1234', createdAt: '2026-08-20T08:00:00.000Z' },
|
||||
{ id: 'BBBBBBBBBBBBBBBBBBBBBB', displayKey: 'MHU2-ZYXW…9876', createdAt: '2026-08-22T10:00:00.000Z' }
|
||||
] },
|
||||
{ ok: true, entries: [
|
||||
{ id: 'DDDDDDDDDDDDDDDDDDDDDD', displayKey: 'MHU2-DFGH…2468', createdAt: '2026-08-25T10:00:00.000Z' },
|
||||
{ id: 'AAAAAAAAAAAAAAAAAAAAAA', displayKey: 'MHU2-ABCD…1234', createdAt: '2026-08-20T08:00:00.000Z' }
|
||||
] },
|
||||
{ ok: true, entries: [
|
||||
{ id: 'CCCCCCCCCCCCCCCCCCCCCC', displayKey: 'MHU2-QWER…4321', createdAt: '2026-08-23T12:00:00.000Z' },
|
||||
{ id: 'EEEEEEEEEEEEEEEEEEEEEE', displayKey: 'MHU2-ETYU…1357', createdAt: '2026-08-26T10:00:00.000Z' },
|
||||
{ id: 'DDDDDDDDDDDDDDDDDDDDDD', displayKey: 'MHU2-DFGH…2468', createdAt: '2026-08-25T10:00:00.000Z' }
|
||||
] },
|
||||
{ ok: false, error: 'Online-Sicherungen konnten nicht geladen werden' }
|
||||
];
|
||||
let managedOnlineBackupListIndex = 0;
|
||||
let managedOnlineBackupCreateIndex = 0;
|
||||
let pendingManagedOnlineBackupCopy = null;
|
||||
let pendingManagedOnlineBackupDelete = null;
|
||||
contextBridge.exposeInMainWorld('api', {
|
||||
onUpdateAvailable() {},
|
||||
@@ -70,19 +89,26 @@ contextBridge.exposeInMainWorld('api', {
|
||||
onPrepareClose() {},
|
||||
getConfig() { return new Promise(() => {}); },
|
||||
listManagedOnlineBackups() {
|
||||
managedOnlineBackupProbeCalls.push(['list']);
|
||||
return Promise.resolve({ ok: true, entries: [
|
||||
{ id: 'AAAAAAAAAAAAAAAAAAAAAA', displayKey: 'MHU2-ABCD…1234', createdAt: '2026-08-20T08:00:00.000Z' },
|
||||
{ id: 'BBBBBBBBBBBBBBBBBBBBBB', displayKey: 'MHU2-ZYXW…9876', createdAt: '2026-08-22T10:00:00.000Z' }
|
||||
] });
|
||||
managedOnlineBackupListIndex++;
|
||||
managedOnlineBackupProbeCalls.push(['list', managedOnlineBackupListIndex]);
|
||||
return Promise.resolve(managedOnlineBackupListResponses[managedOnlineBackupListIndex - 1]);
|
||||
},
|
||||
createManagedOnlineBackup() {
|
||||
managedOnlineBackupProbeCalls.push(['create']);
|
||||
return Promise.resolve({ ok: true, entry: { id: 'CCCCCCCCCCCCCCCCCCCCCC', displayKey: 'MHU2-QWER…4321', createdAt: '2026-08-23T12:00:00.000Z' } });
|
||||
managedOnlineBackupCreateIndex++;
|
||||
managedOnlineBackupProbeCalls.push(['create', managedOnlineBackupCreateIndex]);
|
||||
const entry = managedOnlineBackupCreateIndex === 1
|
||||
? { id: 'CCCCCCCCCCCCCCCCCCCCCC', displayKey: 'MHU2-QWER…4321', createdAt: '2026-08-23T12:00:00.000Z' }
|
||||
: { id: 'FFFFFFFFFFFFFFFFFFFFFF', displayKey: 'MHU2-FGHJ…8642', createdAt: '2026-08-27T12:00:00.000Z' };
|
||||
return Promise.resolve({ ok: true, entry });
|
||||
},
|
||||
copyManagedOnlineBackup(id) {
|
||||
managedOnlineBackupProbeCalls.push(['copy', id]);
|
||||
return Promise.resolve({ ok: true });
|
||||
return new Promise(resolve => { pendingManagedOnlineBackupCopy = resolve; });
|
||||
},
|
||||
releaseManagedOnlineBackupCopy() {
|
||||
const resolve = pendingManagedOnlineBackupCopy;
|
||||
pendingManagedOnlineBackupCopy = null;
|
||||
resolve({ ok: true });
|
||||
},
|
||||
deleteManagedOnlineBackup(id) {
|
||||
managedOnlineBackupProbeCalls.push(['delete', id]);
|
||||
@@ -100,8 +126,19 @@ contextBridge.exposeInMainWorld('api', {
|
||||
const fixture = document.createElement('section');
|
||||
fixture.innerHTML = '<div id="managedOnlineBackupList"></div><div id="onlineBackupStatus"></div><button id="createOnlineBackupBtn"></button><input id="onlineBackupKeyInput"><button id="restoreOnlineBackupBtn"></button>';
|
||||
document.body.append(fixture);
|
||||
const waitFor = async predicate => {
|
||||
for (let attempt = 0; attempt < 80; attempt++) {
|
||||
if (await predicate()) return true;
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
}
|
||||
return false;
|
||||
};
|
||||
let confirmation = null;
|
||||
showAppConfirm = async options => { confirmation = options; return true; };
|
||||
let resolveConfirmation = null;
|
||||
showAppConfirm = options => {
|
||||
confirmation = options;
|
||||
return new Promise(resolve => { resolveConfirmation = resolve; });
|
||||
};
|
||||
flushPendingSettingsSaves = async () => {};
|
||||
openOnlineBackupView = () => {};
|
||||
await loadManagedOnlineBackups();
|
||||
@@ -109,26 +146,59 @@ contextBridge.exposeInMainWorld('api', {
|
||||
const maliciousEntry = { id: 'DDDDDDDDDDDDDDDDDDDDDD', displayKey: 'prefix MHU2-' + 'X'.repeat(70) + ' suffix', createdAt: '2026-08-24T12:00:00.000Z' };
|
||||
replaceManagedOnlineBackups([...managedOnlineBackups, maliciousEntry]);
|
||||
const secretRejected = !document.body.textContent.includes(maliciousEntry.displayKey) && managedOnlineBackups.every(entry => entry.displayKey !== maliciousEntry.displayKey);
|
||||
document.querySelector('.online-backup-copy-btn').click();
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
document.querySelector('.online-backup-delete-btn').click();
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
const originalRow = document.querySelector('.online-backup-managed-row');
|
||||
originalRow.querySelector('.online-backup-delete-btn').click();
|
||||
await waitFor(() => typeof resolveConfirmation === 'function');
|
||||
renderManagedOnlineBackups();
|
||||
const pendingDeleteControlsDisabled = [...document.querySelector('.online-backup-managed-row').querySelectorAll('button')].every(button => button.disabled);
|
||||
window.api.releaseManagedOnlineBackupDelete();
|
||||
const rowReplacedDuringConfirmation = !originalRow.isConnected;
|
||||
document.querySelector('.online-backup-copy-btn').click();
|
||||
await waitFor(async () => (await window.api.getManagedOnlineBackupProbeCalls()).some(call => call[0] === 'copy'));
|
||||
const copyPendingControlsDisabled = [...document.querySelector('.online-backup-managed-row').querySelectorAll('button')].every(button => button.disabled);
|
||||
resolveConfirmation(true);
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
const deleteStartedBeforeCopyFinished = (await window.api.getManagedOnlineBackupProbeCalls()).some(call => call[0] === 'delete');
|
||||
window.api.releaseManagedOnlineBackupCopy();
|
||||
const deleteStartedAfterCopy = await waitFor(async () => (await window.api.getManagedOnlineBackupProbeCalls()).filter(call => call[0] === 'delete').length === 1);
|
||||
const deletePendingControlsDisabled = deleteStartedAfterCopy && [...document.querySelector('.online-backup-managed-row').querySelectorAll('button')].every(button => button.disabled);
|
||||
if (deleteStartedAfterCopy) window.api.releaseManagedOnlineBackupDelete();
|
||||
await waitFor(async () => (await window.api.getManagedOnlineBackupProbeCalls()).filter(call => call[0] === 'list').length === 2);
|
||||
const afterDelete = [...document.querySelectorAll('.online-backup-managed-key')].map(element => element.textContent);
|
||||
const deletePostRefreshCount = (await window.api.getManagedOnlineBackupProbeCalls()).filter(call => call[0] === 'list').length - 1;
|
||||
const listCallsBeforeCreate = (await window.api.getManagedOnlineBackupProbeCalls()).filter(call => call[0] === 'list').length;
|
||||
await doOnlineBackupCreate();
|
||||
const afterCreate = [...document.querySelectorAll('.online-backup-managed-key')].map(element => element.textContent);
|
||||
const authoritativeStateFields = [...new Set(managedOnlineBackups.flatMap(entry => Object.keys(entry)))].sort();
|
||||
const callsAfterCreate = await window.api.getManagedOnlineBackupProbeCalls();
|
||||
const createPostRefreshCount = callsAfterCreate.filter(call => call[0] === 'list').length - listCallsBeforeCreate;
|
||||
const listCallsBeforeFailedCreate = callsAfterCreate.filter(call => call[0] === 'list').length;
|
||||
await doOnlineBackupCreate();
|
||||
const failedCreatePostRefreshCount = (await window.api.getManagedOnlineBackupProbeCalls()).filter(call => call[0] === 'list').length - listCallsBeforeFailedCreate;
|
||||
const refreshFailure = {
|
||||
rowCount: document.querySelectorAll('.online-backup-managed-row').length,
|
||||
stateCount: managedOnlineBackups.length,
|
||||
emptyStateVisible: Boolean(document.querySelector('.online-backup-managed-empty')),
|
||||
status: document.getElementById('onlineBackupStatus').textContent,
|
||||
statusState: document.getElementById('onlineBackupStatus').dataset.state
|
||||
};
|
||||
const calls = await window.api.getManagedOnlineBackupProbeCalls();
|
||||
return {
|
||||
initialKeys,
|
||||
secretRejected,
|
||||
pendingDeleteControlsDisabled,
|
||||
rowReplacedDuringConfirmation,
|
||||
copyPendingControlsDisabled,
|
||||
deleteStartedBeforeCopyFinished,
|
||||
deleteStartedAfterCopy,
|
||||
deletePendingControlsDisabled,
|
||||
deleteCallCount: calls.filter(call => call[0] === 'delete').length,
|
||||
afterDelete,
|
||||
deletePostRefreshCount,
|
||||
afterCreate,
|
||||
authoritativeStateFields,
|
||||
createPostRefreshCount,
|
||||
failedCreatePostRefreshCount,
|
||||
refreshFailure,
|
||||
confirmation,
|
||||
calls: await window.api.getManagedOnlineBackupProbeCalls(),
|
||||
stateFields: Object.keys(managedOnlineBackups[0] || {}).sort(),
|
||||
calls,
|
||||
secretInBody: /MHU2-[A-Za-z0-9_-]{70}/.test(document.body.textContent)
|
||||
};
|
||||
})()`;
|
||||
@@ -268,9 +338,25 @@ app.whenReady().then(async () => {
|
||||
assert.equal(result.liveSpeedChart.baselinePresent, false);
|
||||
assert.deepEqual(result.onlineBackupBehavior.initialKeys, ['MHU2-ZYXW…9876', 'MHU2-ABCD…1234']);
|
||||
assert.equal(result.onlineBackupBehavior.secretRejected, true);
|
||||
assert.equal(result.onlineBackupBehavior.pendingDeleteControlsDisabled, true);
|
||||
assert.deepEqual(result.onlineBackupBehavior.afterDelete, ['MHU2-ABCD…1234']);
|
||||
assert.equal(result.onlineBackupBehavior.afterCreate[0], 'MHU2-QWER…4321');
|
||||
assert.equal(result.onlineBackupBehavior.rowReplacedDuringConfirmation, true);
|
||||
assert.equal(result.onlineBackupBehavior.copyPendingControlsDisabled, true);
|
||||
assert.equal(result.onlineBackupBehavior.deleteStartedBeforeCopyFinished, false);
|
||||
assert.equal(result.onlineBackupBehavior.deleteStartedAfterCopy, true);
|
||||
assert.equal(result.onlineBackupBehavior.deletePendingControlsDisabled, true);
|
||||
assert.equal(result.onlineBackupBehavior.deleteCallCount, 1);
|
||||
assert.deepEqual(result.onlineBackupBehavior.afterDelete, ['MHU2-DFGH…2468', 'MHU2-ABCD…1234']);
|
||||
assert.equal(result.onlineBackupBehavior.deletePostRefreshCount, 1);
|
||||
assert.deepEqual(result.onlineBackupBehavior.afterCreate, ['MHU2-ETYU…1357', 'MHU2-DFGH…2468', 'MHU2-QWER…4321']);
|
||||
assert.deepEqual(result.onlineBackupBehavior.authoritativeStateFields, ['createdAt', 'displayKey', 'id']);
|
||||
assert.equal(result.onlineBackupBehavior.createPostRefreshCount, 1);
|
||||
assert.equal(result.onlineBackupBehavior.failedCreatePostRefreshCount, 1);
|
||||
assert.deepEqual(result.onlineBackupBehavior.refreshFailure, {
|
||||
rowCount: 0,
|
||||
stateCount: 0,
|
||||
emptyStateVisible: false,
|
||||
status: 'Online backups could not be loaded',
|
||||
statusState: 'error'
|
||||
});
|
||||
assert.equal(result.onlineBackupBehavior.afterCreate.length, 3);
|
||||
assert.deepEqual(result.onlineBackupBehavior.confirmation, {
|
||||
title: 'Online-Backup löschen',
|
||||
@@ -279,13 +365,15 @@ app.whenReady().then(async () => {
|
||||
danger: true
|
||||
});
|
||||
assert.deepEqual(result.onlineBackupBehavior.calls, [
|
||||
['list'],
|
||||
['list', 1],
|
||||
['copy', 'BBBBBBBBBBBBBBBBBBBBBB'],
|
||||
['delete', 'BBBBBBBBBBBBBBBBBBBBBB'],
|
||||
['list'],
|
||||
['create']
|
||||
['list', 2],
|
||||
['create', 1],
|
||||
['list', 3],
|
||||
['create', 2],
|
||||
['list', 4]
|
||||
]);
|
||||
assert.deepEqual(result.onlineBackupBehavior.stateFields, ['createdAt', 'displayKey', 'id']);
|
||||
assert.equal(result.onlineBackupBehavior.secretInBody, false);
|
||||
assert.ok(Math.abs(result.onlineBackupLayout.german.createRight - result.onlineBackupLayout.german.contentRight) <= 1);
|
||||
assert.ok(Math.abs(result.onlineBackupLayout.english.createRight - result.onlineBackupLayout.english.contentRight) <= 1);
|
||||
|
||||
Reference in New Issue
Block a user