mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-23 02:36:05 +00:00
fix(e2e): fix flaky WebDAV sync tests by verifying selection took effect
The WebDAV E2E tests were failing on first run due to a race condition where clicking the WebDAV option didn't always update the Formly form model before waiting for the form fields to appear. Changes: - Verify mat-select displays "WebDAV" after clicking the option - Add stabilization wait for Formly hideExpression to evaluate - Increase timeouts (5s→8s in loop, 10s→15s final check) - Add dialog-level retry as fallback when all attempts fail
This commit is contained in:
parent
4a3155b887
commit
530f341171
1 changed files with 138 additions and 103 deletions
|
|
@ -31,131 +31,166 @@ export class SyncPage extends BasePage {
|
|||
password: string;
|
||||
syncFolderPath: string;
|
||||
}): Promise<void> {
|
||||
// Dismiss any visible snackbars/toasts that might block clicks
|
||||
const snackBar = this.page.locator('.mat-mdc-snack-bar-container');
|
||||
if (await snackBar.isVisible({ timeout: 500 }).catch(() => false)) {
|
||||
const dismissBtn = snackBar.locator('button');
|
||||
if (await dismissBtn.isVisible({ timeout: 500 }).catch(() => false)) {
|
||||
await dismissBtn.click().catch(() => {});
|
||||
// Try entire setup flow up to 2 times (dialog-level retry)
|
||||
for (let dialogAttempt = 0; dialogAttempt < 2; dialogAttempt++) {
|
||||
if (dialogAttempt > 0) {
|
||||
console.log(`[setupWebdavSync] Dialog-level retry attempt ${dialogAttempt + 1}`);
|
||||
}
|
||||
await this.page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
// Ensure sync button is visible and clickable
|
||||
await this.syncBtn.waitFor({ state: 'visible', timeout: 10000 });
|
||||
|
||||
// Click sync button to open settings dialog - use force click if needed
|
||||
await this.syncBtn.click({ timeout: 5000 });
|
||||
|
||||
// Wait for dialog to appear
|
||||
const dialog = this.page.locator('mat-dialog-container, .mat-mdc-dialog-container');
|
||||
const dialogVisible = await dialog
|
||||
.waitFor({ state: 'visible', timeout: 5000 })
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
// If dialog didn't open, try clicking again
|
||||
if (!dialogVisible) {
|
||||
await this.page.waitForTimeout(500);
|
||||
await this.syncBtn.click({ force: true });
|
||||
await dialog.waitFor({ state: 'visible', timeout: 5000 });
|
||||
}
|
||||
|
||||
// Wait for dialog to be fully loaded
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
await this.providerSelect.waitFor({ state: 'visible', timeout: 10000 });
|
||||
|
||||
// Wait a moment for Angular animations
|
||||
await this.page.waitForTimeout(500);
|
||||
|
||||
// Click on provider select to open dropdown with retry
|
||||
const webdavOption = this.page.locator('mat-option').filter({ hasText: 'WebDAV' });
|
||||
|
||||
// Use the existing providerSelect locator which targets the mat-select directly
|
||||
// Use the providerSelect locator that was already validated above
|
||||
const selectElement = this.providerSelect;
|
||||
|
||||
for (let attempt = 0; attempt < 5; attempt++) {
|
||||
// Ensure the select is in view
|
||||
await selectElement.scrollIntoViewIfNeeded({ timeout: 5000 }).catch(async () => {
|
||||
// If scrollIntoViewIfNeeded fails, try scrolling the dialog content
|
||||
const dialogContent = this.page.locator('mat-dialog-content');
|
||||
if (await dialogContent.isVisible()) {
|
||||
await dialogContent.evaluate((el) => el.scrollTo(0, 0));
|
||||
// Dismiss any visible snackbars/toasts that might block clicks
|
||||
const snackBar = this.page.locator('.mat-mdc-snack-bar-container');
|
||||
if (await snackBar.isVisible({ timeout: 500 }).catch(() => false)) {
|
||||
const dismissBtn = snackBar.locator('button');
|
||||
if (await dismissBtn.isVisible({ timeout: 500 }).catch(() => false)) {
|
||||
await dismissBtn.click().catch(() => {});
|
||||
}
|
||||
});
|
||||
await this.page.waitForTimeout(300);
|
||||
|
||||
// Focus and click the select element
|
||||
await selectElement.focus().catch(() => {});
|
||||
await this.page.waitForTimeout(200);
|
||||
|
||||
// Try multiple ways to open the dropdown
|
||||
if (attempt === 0) {
|
||||
// First attempt: regular click
|
||||
await selectElement.click().catch(() => {});
|
||||
} else if (attempt === 1) {
|
||||
// Second attempt: use Space key to open
|
||||
await this.page.keyboard.press('Space');
|
||||
} else if (attempt === 2) {
|
||||
// Third attempt: use ArrowDown to open
|
||||
await this.page.keyboard.press('ArrowDown');
|
||||
} else {
|
||||
// Later attempts: force click
|
||||
await selectElement.click({ force: true }).catch(() => {});
|
||||
await this.page.waitForTimeout(500);
|
||||
}
|
||||
await this.page.waitForTimeout(500);
|
||||
|
||||
// Wait for any mat-option to appear (dropdown opened)
|
||||
const anyOption = this.page.locator('mat-option').first();
|
||||
const anyOptionVisible = await anyOption
|
||||
.waitFor({ state: 'visible', timeout: 3000 })
|
||||
// Ensure sync button is visible and clickable
|
||||
await this.syncBtn.waitFor({ state: 'visible', timeout: 10000 });
|
||||
|
||||
// Click sync button to open settings dialog - use force click if needed
|
||||
await this.syncBtn.click({ timeout: 5000 });
|
||||
|
||||
// Wait for dialog to appear
|
||||
const dialog = this.page.locator('mat-dialog-container, .mat-mdc-dialog-container');
|
||||
const dialogVisible = await dialog
|
||||
.waitFor({ state: 'visible', timeout: 5000 })
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
if (anyOptionVisible) {
|
||||
// Now wait for WebDAV option specifically
|
||||
const webdavVisible = await webdavOption
|
||||
// If dialog didn't open, try clicking again
|
||||
if (!dialogVisible) {
|
||||
await this.page.waitForTimeout(500);
|
||||
await this.syncBtn.click({ force: true });
|
||||
await dialog.waitFor({ state: 'visible', timeout: 5000 });
|
||||
}
|
||||
|
||||
// Wait for dialog to be fully loaded
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
await this.providerSelect.waitFor({ state: 'visible', timeout: 10000 });
|
||||
|
||||
// Wait a moment for Angular animations
|
||||
await this.page.waitForTimeout(500);
|
||||
|
||||
// Click on provider select to open dropdown with retry
|
||||
const webdavOption = this.page.locator('mat-option').filter({ hasText: 'WebDAV' });
|
||||
const selectElement = this.providerSelect;
|
||||
const selectValueText = this.page.locator(
|
||||
'formly-field-mat-select .mat-mdc-select-value-text',
|
||||
);
|
||||
|
||||
let selectionSucceeded = false;
|
||||
|
||||
for (let attempt = 0; attempt < 5; attempt++) {
|
||||
// Ensure the select is in view
|
||||
await selectElement.scrollIntoViewIfNeeded({ timeout: 5000 }).catch(async () => {
|
||||
// If scrollIntoViewIfNeeded fails, try scrolling the dialog content
|
||||
const dialogContent = this.page.locator('mat-dialog-content');
|
||||
if (await dialogContent.isVisible()) {
|
||||
await dialogContent.evaluate((el) => el.scrollTo(0, 0));
|
||||
}
|
||||
});
|
||||
await this.page.waitForTimeout(300);
|
||||
|
||||
// Focus and click the select element
|
||||
await selectElement.focus().catch(() => {});
|
||||
await this.page.waitForTimeout(200);
|
||||
|
||||
// Try multiple ways to open the dropdown
|
||||
if (attempt === 0) {
|
||||
await selectElement.click().catch(() => {});
|
||||
} else if (attempt === 1) {
|
||||
await this.page.keyboard.press('Space');
|
||||
} else if (attempt === 2) {
|
||||
await this.page.keyboard.press('ArrowDown');
|
||||
} else {
|
||||
await selectElement.click({ force: true }).catch(() => {});
|
||||
}
|
||||
await this.page.waitForTimeout(500);
|
||||
|
||||
// Wait for any mat-option to appear (dropdown opened)
|
||||
const anyOption = this.page.locator('mat-option').first();
|
||||
const anyOptionVisible = await anyOption
|
||||
.waitFor({ state: 'visible', timeout: 3000 })
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
if (webdavVisible) {
|
||||
await webdavOption.click();
|
||||
|
||||
// Wait for Formly to re-render the WebDAV form fields
|
||||
// (hideExpression needs time to evaluate and show the fieldGroup)
|
||||
const fieldVisible = await this.baseUrlInput
|
||||
.waitFor({ state: 'visible', timeout: 5000 })
|
||||
if (anyOptionVisible) {
|
||||
const webdavVisible = await webdavOption
|
||||
.waitFor({ state: 'visible', timeout: 3000 })
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
if (fieldVisible) {
|
||||
break; // Form fields are visible, we can proceed
|
||||
if (webdavVisible) {
|
||||
await webdavOption.click();
|
||||
|
||||
// KEY FIX: Verify selection actually took effect by checking mat-select value
|
||||
const selectionVerified = await selectValueText
|
||||
.waitFor({ state: 'visible', timeout: 3000 })
|
||||
.then(async () => {
|
||||
const text = await selectValueText.textContent();
|
||||
return text?.includes('WebDAV') ?? false;
|
||||
})
|
||||
.catch(() => false);
|
||||
|
||||
if (selectionVerified) {
|
||||
// Selection confirmed - now wait for Formly to process the model change
|
||||
await this.page.waitForTimeout(500); // Allow Formly hideExpression to evaluate
|
||||
|
||||
// Wait for WebDAV form fields with increased timeout
|
||||
const fieldVisible = await this.baseUrlInput
|
||||
.waitFor({ state: 'visible', timeout: 8000 })
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
if (fieldVisible) {
|
||||
selectionSucceeded = true;
|
||||
break; // Form fields are visible, we can proceed
|
||||
}
|
||||
}
|
||||
}
|
||||
// If fields didn't appear, close any open dropdown and retry
|
||||
}
|
||||
|
||||
// Close dropdown and retry
|
||||
await this.page.keyboard.press('Escape');
|
||||
await this.page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
// Close dropdown and retry
|
||||
if (selectionSucceeded) {
|
||||
// Final verification with increased timeout
|
||||
await this.baseUrlInput.waitFor({ state: 'visible', timeout: 15000 });
|
||||
|
||||
// Fill in the configuration
|
||||
await this.baseUrlInput.fill(config.baseUrl);
|
||||
await this.userNameInput.fill(config.username);
|
||||
await this.passwordInput.fill(config.password);
|
||||
await this.syncFolderInput.fill(config.syncFolderPath);
|
||||
|
||||
// Save the configuration
|
||||
await this.saveBtn.click();
|
||||
|
||||
// Wait for dialog to close
|
||||
await this.page.waitForTimeout(500);
|
||||
return; // Success - exit the method
|
||||
}
|
||||
|
||||
// Selection failed after all attempts - close dialog and retry from scratch
|
||||
console.log(
|
||||
'[setupWebdavSync] All dropdown attempts failed, closing dialog to retry',
|
||||
);
|
||||
await this.page.keyboard.press('Escape');
|
||||
await this.page.waitForTimeout(500);
|
||||
await this.page.waitForTimeout(1000);
|
||||
|
||||
// Ensure dialog is closed before retrying
|
||||
await dialog.waitFor({ state: 'hidden', timeout: 5000 }).catch(() => {});
|
||||
}
|
||||
|
||||
// Final check - form fields should be visible now
|
||||
await this.baseUrlInput.waitFor({ state: 'visible', timeout: 10000 });
|
||||
|
||||
// Fill in the configuration
|
||||
await this.baseUrlInput.fill(config.baseUrl);
|
||||
await this.userNameInput.fill(config.username);
|
||||
await this.passwordInput.fill(config.password);
|
||||
await this.syncFolderInput.fill(config.syncFolderPath);
|
||||
|
||||
// Save the configuration
|
||||
await this.saveBtn.click();
|
||||
|
||||
// Wait for dialog to close
|
||||
await this.page.waitForTimeout(500);
|
||||
// If we get here, both dialog-level attempts failed
|
||||
throw new Error(
|
||||
'[setupWebdavSync] Failed to setup WebDAV sync after multiple dialog-level retries',
|
||||
);
|
||||
}
|
||||
|
||||
async triggerSync(): Promise<void> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue