Skip to content

Commit 2b5c10d

Browse files
Lightning00BladeDevtools-frontend LUCI CQ
authored andcommitted
Reduce flakiness and improve error handling
Ensure all onDisconnect handlers are called in the global afterEach hook. When onDisconnect fails, we should associate it with the afterEach of the failing test, not the beforeEach of the next test. Drive-by: Fix error with E2E test of the beforeEach hook failed to set-up the VE logging hook. Bug: none Change-Id: Ie7d92caf82f5262f838599cc2b324bf9f946acb3 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6237084 Commit-Queue: Nikolay Vitkov <[email protected]> Reviewed-by: Simon Zünd <[email protected]> Reviewed-by: Danil Somsikov <[email protected]>
1 parent ed49b09 commit 2b5c10d

File tree

6 files changed

+45
-38
lines changed

6 files changed

+45
-38
lines changed

front_end/testing/DOMHelpers.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,39 @@ function removeChildren(node: Node): void {
5555
node.removeChild(firstChild);
5656
}
5757
}
58+
5859
/**
59-
* Completely cleans out the test DOM to ensure it's empty for the next test run.
60-
* This is run automatically between tests - you should not be manually calling this yourself.
60+
* Sets up the DOM for testing,
61+
* If not clean logs an error and cleans itself
6162
**/
62-
export const resetTestDOM = () => {
63+
export const setupTestDOM = async () => {
6364
const previousContainer = document.getElementById(TEST_CONTAINER_ID);
6465
if (previousContainer) {
65-
removeChildren(previousContainer);
66-
previousContainer.remove();
66+
// This should not be reachable, unless the
67+
// AfterEach hook fails before cleaning the DOM.
68+
// Clean it here and report
69+
console.error('Non clean test state found!');
70+
await cleanTestDOM();
6771
}
68-
6972
const newContainer = document.createElement('div');
7073
newContainer.id = TEST_CONTAINER_ID;
7174

7275
document.body.appendChild(newContainer);
7376
};
7477

78+
/**
79+
* Completely cleans out the test DOM to ensure it's empty for the next test run.
80+
* This is run automatically between tests - you should not be manually calling this yourself.
81+
**/
82+
export const cleanTestDOM = async () => {
83+
const previousContainer = document.getElementById(TEST_CONTAINER_ID);
84+
if (previousContainer) {
85+
removeChildren(previousContainer);
86+
previousContainer.remove();
87+
}
88+
await raf();
89+
};
90+
7591
interface Constructor<T> {
7692
new(...args: unknown[]): T;
7793
}

front_end/testing/MockConnection.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import type * as SDK from '../core/sdk/sdk.js';
77
import type {ProtocolMapping} from '../generated/protocol-mapping.js';
88
import type * as ProtocolProxyApi from '../generated/protocol-proxy-api.js';
99

10-
import {resetTestDOM} from './DOMHelpers.js';
1110
import {deinitializeGlobalVars, initializeGlobalVars} from './EnvironmentHelpers.js';
1211
import {setMockResourceTree} from './ResourceTreeHelpers.js';
1312

@@ -138,7 +137,6 @@ async function disable() {
138137
if (outgoingMessageListenerEntryMap.size > 0) {
139138
throw new Error('MockConnection still has pending listeners. All promises should be awaited.');
140139
}
141-
resetTestDOM();
142140
await deinitializeGlobalVars();
143141
// @ts-ignore Setting back to undefined as a hard reset.
144142
ProtocolClient.InspectorBackend.Connection.setFactory(undefined);

front_end/testing/test_setup.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ import * as Trace from '../models/trace/trace.js';
1212
import * as Timeline from '../panels/timeline/timeline.js';
1313
import * as ThemeSupport from '../ui/legacy/theme_support/theme_support.js';
1414

15-
import {resetTestDOM} from './DOMHelpers.js';
15+
import {cleanTestDOM, setupTestDOM} from './DOMHelpers.js';
1616
import {createFakeSetting} from './EnvironmentHelpers.js';
1717
import {
1818
checkForPendingActivity,
1919
startTrackingAsyncActivity,
2020
stopTrackingAsyncActivity,
2121
} from './TrackAsyncOperations.js';
2222

23-
beforeEach(() => {
24-
resetTestDOM();
23+
beforeEach(async () => {
24+
await setupTestDOM();
2525
// Ensure that no trace data leaks between tests when testing the trace engine.
2626
for (const handler of Object.values(Trace.Handlers.ModelHandlers)) {
2727
handler.reset();
@@ -42,6 +42,7 @@ beforeEach(() => {
4242
});
4343

4444
afterEach(async () => {
45+
await cleanTestDOM();
4546
await checkForPendingActivity();
4647
sinon.restore();
4748
stopTrackingAsyncActivity();

front_end/ui/components/markdown_view/CodeBlock.test.ts

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import * as Host from '../../../core/host/host.js';
66
import {
77
dispatchClickEvent,
88
renderElementIntoDOM,
9-
resetTestDOM,
109
} from '../../../testing/DOMHelpers.js';
1110
import {describeWithEnvironment} from '../../../testing/EnvironmentHelpers.js';
1211

@@ -44,33 +43,24 @@ describeWithEnvironment('CodeBlock', () => {
4443
assert.strictEqual(buttonContainer.textContent?.trim(), '');
4544
} finally {
4645
clock.restore();
47-
resetTestDOM();
4846
}
4947
});
5048

5149
it('renders no legal notice by default', () => {
52-
try {
53-
const component = new MarkdownView.CodeBlock.CodeBlock();
54-
component.code = 'test';
55-
renderElementIntoDOM(component);
56-
const notice = component.shadowRoot!.querySelector('.notice') as HTMLElement;
57-
assert(notice === null, '.notice was found');
58-
} finally {
59-
resetTestDOM();
60-
}
50+
const component = new MarkdownView.CodeBlock.CodeBlock();
51+
component.code = 'test';
52+
renderElementIntoDOM(component);
53+
const notice = component.shadowRoot!.querySelector('.notice') as HTMLElement;
54+
assert(notice === null, '.notice was found');
6155
});
6256

6357
it('renders legal notice if configured', () => {
64-
try {
65-
const component = new MarkdownView.CodeBlock.CodeBlock();
66-
component.code = 'test';
67-
component.displayNotice = true;
68-
renderElementIntoDOM(component);
69-
const notice = component.shadowRoot!.querySelector('.notice') as HTMLElement;
70-
assert.exists(notice);
71-
assert.strictEqual(notice!.innerText, 'Use code snippets with caution');
72-
} finally {
73-
resetTestDOM();
74-
}
58+
const component = new MarkdownView.CodeBlock.CodeBlock();
59+
component.code = 'test';
60+
component.displayNotice = true;
61+
renderElementIntoDOM(component);
62+
const notice = component.shadowRoot!.querySelector('.notice') as HTMLElement;
63+
assert.exists(notice);
64+
assert.strictEqual(notice!.innerText, 'Use code snippets with caution');
7565
});
7666
});

front_end/ui/components/text_editor/TextEditor.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ declare global {
1818
}
1919

2020
export class TextEditor extends HTMLElement {
21-
2221
readonly #shadow = this.attachShadow({mode: 'open'});
2322
#activeEditor: CodeMirror.EditorView|undefined = undefined;
2423
#dynamicSettings: readonly DynamicSetting<unknown>[] = DynamicSetting.none;
@@ -153,15 +152,14 @@ export class TextEditor extends HTMLElement {
153152
}
154153
this.#activeSettingListeners = [];
155154

156-
const settings = Common.Settings.Settings.instance();
157155
for (const dynamicSetting of dynamicSettings) {
158156
const handler = ({data}: {data: unknown}): void => {
159157
const change = dynamicSetting.sync(this.state, data);
160158
if (change && this.#activeEditor) {
161159
this.#activeEditor.dispatch({effects: change});
162160
}
163161
};
164-
const setting = settings.moduleSetting(dynamicSetting.settingName);
162+
const setting = Common.Settings.Settings.instance().moduleSetting(dynamicSetting.settingName);
165163
setting.addChangeListener(handler);
166164
this.#activeSettingListeners.push([setting, handler]);
167165
}

test/conductor/frontend_tab.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,12 @@ export class DevToolsFrontendTab {
8080
// Clear any local storage settings.
8181
await this.page.evaluate(() => {
8282
localStorage.clear();
83-
// @ts-ignore Test logging needs debug event logging, which is controlled via localStorage, hence we need to restart test logging here
84-
globalThis.setVeDebugLoggingEnabled(true, 'Test');
83+
84+
// Test logging needs debug event logging,
85+
// which is controlled via localStorage, hence we need to restart test logging here
86+
// This can be called after a page fails to load DevTools so make it conditional
87+
// @ts-expect-error
88+
globalThis?.setVeDebugLoggingEnabled(true, 'Test');
8589
});
8690
await this.reload();
8791
}

0 commit comments

Comments
 (0)