fix(editor): ci stability (#14704)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Improved Electron shutdown, diagnostics and tab teardown for more
reliable exits and forced cleanup on stubborn processes.
* **Tests**
* Added polling-based test helpers, stronger scroll/page readiness,
timeout-tolerant page selection, and async cleanup/worker teardown;
updated many tests to wait for UI/model updates and rendering frames.
* **Bug Fixes**
* Reduced flakiness by awaiting paragraph visibility, nested counts,
selection/navigation stability, tab counts, post-action renders, and
safer element interactions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-03-23 16:25:15 +08:00
committed by GitHub
parent 8ba02ed6fb
commit dcf041a3f2
17 changed files with 619 additions and 256 deletions
@@ -391,21 +391,87 @@ export class WebContentViewsManager {
this.closedWorkbenches.push(targetWorkbench);
setTimeout(() => {
globalThis.setTimeout(() => {
const view = this.tabViewsMap.get(id);
this.tabViewsMap.delete(id);
if (this.mainWindow && view) {
this.mainWindow.contentView.removeChildView(view);
view?.webContents.close({
waitForBeforeUnload: true,
});
if (!view) {
return;
}
void this.disposeTabView(id, view).catch(error => {
logger.warn('failed to dispose tab view', {
id,
error: error instanceof Error ? error.message : String(error),
});
});
}, 500); // delay a bit to get rid of the flicker
onTabClose(id);
};
private readonly disposeTabView = async (
id: string,
view: WebContentsView
) => {
const waitForDestroyed = () =>
new Promise<boolean>(resolve => {
if (view.webContents.isDestroyed()) {
resolve(true);
return;
}
const timeout = globalThis.setTimeout(() => {
resolve(false);
}, 1_000);
view.webContents.once('destroyed', () => {
globalThis.clearTimeout(timeout);
resolve(true);
});
});
if (this.mainWindow?.contentView.children.includes(view)) {
this.mainWindow.contentView.removeChildView(view);
}
if (view.webContents.isDestroyed()) {
return;
}
try {
view.webContents.close({
waitForBeforeUnload: true,
});
} catch {
return;
}
if (await waitForDestroyed()) return;
view.webContents.forcefullyCrashRenderer();
try {
view.webContents.close({
waitForBeforeUnload: false,
});
} catch {
return;
}
if (!view.webContents.isDestroyed() && !(await waitForDestroyed())) {
logger.warn('tab webContents is still alive after force close', {
id,
webContentsId: view.webContents.id,
url: view.webContents.getURL(),
});
}
if (this.mainWindow?.contentView.children.includes(view)) {
this.mainWindow.contentView.removeChildView(view);
}
};
undoCloseTab = async () => {
if (this.closedWorkbenches.length === 0) {
return;