chore: clean .gitignore cache
This commit is contained in:
@@ -1,394 +0,0 @@
|
||||
/**
|
||||
* Backups Page - Restore E2E Tests
|
||||
*
|
||||
* Tests for backup restoration functionality including confirmation dialog,
|
||||
* restore execution, progress tracking, and error handling.
|
||||
* Covers 8 test scenarios as defined in phase5-implementation.md.
|
||||
*
|
||||
* Test Categories:
|
||||
* - Restore Initiation (3 tests): restore button click, confirmation dialog, cancel restore
|
||||
* - Restore Execution (3 tests): successful restore with progress, completion toast, error handling
|
||||
* - Edge Cases (2 tests): reload application state after restore, preserve user session
|
||||
*/
|
||||
|
||||
import { test, expect, loginUser, TEST_PASSWORD } from '../fixtures/auth-fixtures';
|
||||
import { setupBackupsList, completeRestoreFlow, BackupFile } from '../utils/phase5-helpers';
|
||||
import { waitForToast, waitForLoadingComplete, waitForAPIResponse } from '../utils/wait-helpers';
|
||||
|
||||
/**
|
||||
* Mock backup data for testing
|
||||
*/
|
||||
const mockBackups: BackupFile[] = [
|
||||
{ filename: 'backup_2024-01-15_120000.tar.gz', size: 1048576, time: '2024-01-15T12:00:00Z' },
|
||||
{ filename: 'backup_2024-01-14_120000.tar.gz', size: 2097152, time: '2024-01-14T12:00:00Z' },
|
||||
];
|
||||
|
||||
/**
|
||||
* Selectors for the Backups restore functionality
|
||||
*/
|
||||
const SELECTORS = {
|
||||
// Restore buttons and actions
|
||||
restoreBtn: '[data-testid="backup-restore-btn"]',
|
||||
restoreButton: 'button:has-text("Restore")',
|
||||
|
||||
// Confirmation dialog (Dialog component in Backups.tsx)
|
||||
confirmDialog: '[role="dialog"]',
|
||||
dialogTitle: '[role="dialog"] h2, [role="dialog"] [class*="DialogTitle"]',
|
||||
dialogMessage: '[role="dialog"] p',
|
||||
|
||||
// Dialog action buttons - use direct button selector, not nested within dialog selector
|
||||
confirmRestoreButton: 'button:has-text("Restore")',
|
||||
cancelButton: 'button:has-text("Cancel")',
|
||||
|
||||
// Progress indicator
|
||||
progressBar: '[role="progressbar"]',
|
||||
restoreStatus: '[data-testid="restore-status"]',
|
||||
|
||||
// Loading states
|
||||
loadingSkeleton: '[data-testid="loading-skeleton"]',
|
||||
};
|
||||
|
||||
test.describe('Backups Page - Restore', () => {
|
||||
// =========================================================================
|
||||
// Restore Initiation Tests (3 tests)
|
||||
// =========================================================================
|
||||
test.describe('Restore Initiation', () => {
|
||||
test('should show confirmation dialog when clicking restore button', async ({ page, adminUser }) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
await page.route('**/api/v1/backups', async (route) => {
|
||||
if (route.request().method() === 'GET') {
|
||||
await route.fulfill({ status: 200, json: mockBackups });
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/tasks/backups');
|
||||
await waitForLoadingComplete(page);
|
||||
|
||||
// Click restore on first backup
|
||||
const restoreButton = page.locator(SELECTORS.restoreBtn).first();
|
||||
await restoreButton.click();
|
||||
|
||||
// Verify dialog appears
|
||||
const dialog = page.locator(SELECTORS.confirmDialog);
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
// Verify dialog contains restore-related content
|
||||
await expect(dialog).toContainText(/restore/i);
|
||||
});
|
||||
|
||||
test('should display warning message about data replacement in restore dialog', async ({
|
||||
page,
|
||||
adminUser,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
await page.route('**/api/v1/backups', async (route) => {
|
||||
if (route.request().method() === 'GET') {
|
||||
await route.fulfill({ status: 200, json: mockBackups });
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/tasks/backups');
|
||||
await waitForLoadingComplete(page);
|
||||
|
||||
// Click restore on first backup
|
||||
const restoreButton = page.locator(SELECTORS.restoreBtn).first();
|
||||
await restoreButton.click();
|
||||
|
||||
// Verify dialog shows warning message (from translation key backups.restoreConfirmMessage)
|
||||
const dialog = page.locator(SELECTORS.confirmDialog);
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
// The dialog should contain a message about the restore action
|
||||
const dialogMessage = dialog.locator('p');
|
||||
await expect(dialogMessage).toBeVisible();
|
||||
});
|
||||
|
||||
test('should cancel restore when clicking cancel button', async ({ page, adminUser }) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
let restoreRequested = false;
|
||||
|
||||
await page.route('**/api/v1/backups', async (route) => {
|
||||
if (route.request().method() === 'GET') {
|
||||
await route.fulfill({ status: 200, json: mockBackups });
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.route('**/api/v1/backups/*/restore', async (route) => {
|
||||
restoreRequested = true;
|
||||
await route.fulfill({ status: 200, json: { message: 'Restore completed' } });
|
||||
});
|
||||
|
||||
await page.goto('/tasks/backups');
|
||||
await waitForLoadingComplete(page);
|
||||
|
||||
// Click restore on first backup
|
||||
const restoreButton = page.locator(SELECTORS.restoreBtn).first();
|
||||
await restoreButton.click();
|
||||
|
||||
// Wait for dialog
|
||||
const dialog = page.locator(SELECTORS.confirmDialog);
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
// Click cancel button
|
||||
const cancelButton = dialog.locator(SELECTORS.cancelButton);
|
||||
await cancelButton.click();
|
||||
|
||||
// Dialog should close
|
||||
await expect(dialog).not.toBeVisible();
|
||||
|
||||
// Restore API should not have been called
|
||||
expect(restoreRequested).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// =========================================================================
|
||||
// Restore Execution Tests (3 tests)
|
||||
// =========================================================================
|
||||
test.describe('Restore Execution', () => {
|
||||
test('should restore backup successfully after confirmation', async ({ page, adminUser }) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
const filename = 'backup_2024-01-15_120000.tar.gz';
|
||||
let restoreRequested = false;
|
||||
|
||||
await page.route('**/api/v1/backups', async (route) => {
|
||||
if (route.request().method() === 'GET') {
|
||||
await route.fulfill({ status: 200, json: mockBackups });
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.route(`**/api/v1/backups/${filename}/restore`, async (route) => {
|
||||
if (route.request().method() === 'POST') {
|
||||
restoreRequested = true;
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
json: { message: 'Restore completed successfully' },
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/tasks/backups');
|
||||
await waitForLoadingComplete(page);
|
||||
|
||||
// Click restore on first backup
|
||||
const restoreButton = page.locator(SELECTORS.restoreBtn).first();
|
||||
await restoreButton.click();
|
||||
|
||||
// Wait for dialog
|
||||
const dialog = page.locator(SELECTORS.confirmDialog);
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
// Click confirm restore button
|
||||
const confirmButton = dialog.locator(SELECTORS.confirmRestoreButton);
|
||||
await confirmButton.click();
|
||||
|
||||
// Wait for success toast (API response is already fulfilled by mock)
|
||||
await waitForToast(page, /restore|success|completed/i, { type: 'success' });
|
||||
|
||||
// Verify restore was requested
|
||||
expect(restoreRequested).toBe(true);
|
||||
|
||||
// Dialog should close after successful restore
|
||||
await expect(dialog).not.toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test('should show success toast after successful restoration', async ({ page, adminUser }) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
const filename = 'backup_2024-01-15_120000.tar.gz';
|
||||
|
||||
await page.route('**/api/v1/backups', async (route) => {
|
||||
if (route.request().method() === 'GET') {
|
||||
await route.fulfill({ status: 200, json: mockBackups });
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.route(`**/api/v1/backups/${filename}/restore`, async (route) => {
|
||||
if (route.request().method() === 'POST') {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
json: { message: 'Restore completed successfully' },
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/tasks/backups');
|
||||
await waitForLoadingComplete(page);
|
||||
|
||||
// Click restore on first backup
|
||||
const restoreButton = page.locator(SELECTORS.restoreBtn).first();
|
||||
await restoreButton.click();
|
||||
|
||||
// Wait for dialog and confirm
|
||||
const dialog = page.locator(SELECTORS.confirmDialog);
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
const confirmButton = dialog.locator(SELECTORS.confirmRestoreButton);
|
||||
await confirmButton.click();
|
||||
|
||||
// Wait for success toast
|
||||
await waitForToast(page, /success|restored|completed/i, { type: 'success' });
|
||||
});
|
||||
|
||||
test('should handle restore failure gracefully with error toast', async ({ page, adminUser }) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
const filename = 'backup_2024-01-15_120000.tar.gz';
|
||||
|
||||
await page.route('**/api/v1/backups', async (route) => {
|
||||
if (route.request().method() === 'GET') {
|
||||
await route.fulfill({ status: 200, json: mockBackups });
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.route(`**/api/v1/backups/${filename}/restore`, async (route) => {
|
||||
if (route.request().method() === 'POST') {
|
||||
await route.fulfill({
|
||||
status: 500,
|
||||
json: { error: 'Internal server error: backup file corrupted' },
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/tasks/backups');
|
||||
await waitForLoadingComplete(page);
|
||||
|
||||
// Click restore on first backup
|
||||
const restoreButton = page.locator(SELECTORS.restoreBtn).first();
|
||||
await restoreButton.click();
|
||||
|
||||
// Wait for dialog and confirm
|
||||
const dialog = page.locator(SELECTORS.confirmDialog);
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
const confirmButton = dialog.locator(SELECTORS.confirmRestoreButton);
|
||||
await confirmButton.click();
|
||||
|
||||
// Wait for error toast
|
||||
await waitForToast(page, /error|failed/i, { type: 'error' });
|
||||
});
|
||||
});
|
||||
|
||||
// =========================================================================
|
||||
// Edge Cases Tests (2 tests)
|
||||
// =========================================================================
|
||||
test.describe('Edge Cases', () => {
|
||||
test('should disable restore button while restore is in progress', async ({ page, adminUser }) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
const filename = 'backup_2024-01-15_120000.tar.gz';
|
||||
|
||||
await page.route('**/api/v1/backups', async (route) => {
|
||||
if (route.request().method() === 'GET') {
|
||||
await route.fulfill({ status: 200, json: mockBackups });
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.route(`**/api/v1/backups/${filename}/restore`, async (route) => {
|
||||
if (route.request().method() === 'POST') {
|
||||
// Delay response to observe loading state
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
json: { message: 'Restore completed successfully' },
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/tasks/backups');
|
||||
await waitForLoadingComplete(page);
|
||||
|
||||
// Click restore on first backup
|
||||
const restoreButton = page.locator(SELECTORS.restoreBtn).first();
|
||||
await restoreButton.click();
|
||||
|
||||
// Wait for dialog
|
||||
const dialog = page.locator(SELECTORS.confirmDialog);
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
// Click confirm restore button
|
||||
const confirmButton = dialog.locator(SELECTORS.confirmRestoreButton);
|
||||
await confirmButton.click();
|
||||
|
||||
// The confirm button should be in loading state (disabled or showing spinner)
|
||||
// Check if the button shows loading state or is disabled during the request
|
||||
await expect(confirmButton).toBeDisabled({ timeout: 500 }).catch(() => {
|
||||
// Button might use isLoading prop instead of disabled attribute
|
||||
// This is acceptable behavior
|
||||
});
|
||||
|
||||
// Wait for API response
|
||||
await waitForAPIResponse(page, `/api/v1/backups/${filename}/restore`, { status: 200 });
|
||||
});
|
||||
|
||||
test('should handle restore of corrupted backup with appropriate error message', async ({
|
||||
page,
|
||||
adminUser,
|
||||
}) => {
|
||||
await loginUser(page, adminUser);
|
||||
|
||||
const filename = 'backup_2024-01-15_120000.tar.gz';
|
||||
|
||||
await page.route('**/api/v1/backups', async (route) => {
|
||||
if (route.request().method() === 'GET') {
|
||||
await route.fulfill({ status: 200, json: mockBackups });
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.route(`**/api/v1/backups/${filename}/restore`, async (route) => {
|
||||
if (route.request().method() === 'POST') {
|
||||
await route.fulfill({
|
||||
status: 422,
|
||||
json: { error: 'Backup file is corrupted or invalid' },
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/tasks/backups');
|
||||
await waitForLoadingComplete(page);
|
||||
|
||||
// Click restore on first backup
|
||||
const restoreButton = page.locator(SELECTORS.restoreBtn).first();
|
||||
await restoreButton.click();
|
||||
|
||||
// Wait for dialog and confirm
|
||||
const dialog = page.locator(SELECTORS.confirmDialog);
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
const confirmButton = dialog.locator(SELECTORS.confirmRestoreButton);
|
||||
await confirmButton.click();
|
||||
|
||||
// Wait for error toast indicating the backup issue
|
||||
await waitForToast(page, /error|failed|corrupted|invalid/i, { type: 'error' });
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user