Skip to content

Commit 357669d

Browse files
OrKoNDevtools-frontend LUCI CQ
authored andcommitted
Refactor panels/recorder/components/RecordingListView.ts
Fixed: 407940953 Change-Id: I25ee347e62d70c451b737912becc6846dc45b113 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6818234 Reviewed-by: Wolfgang Beyer <[email protected]> Commit-Queue: Alex Rudenko <[email protected]>
1 parent d5e59d9 commit 357669d

File tree

5 files changed

+196
-187
lines changed

5 files changed

+196
-187
lines changed

front_end/panels/recorder/RecorderController.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,18 +1150,20 @@ export class RecorderController extends LitElement {
11501150
const recordings = this.#storage.getRecordings();
11511151
// clang-format off
11521152
return html`
1153-
<devtools-recording-list-view
1154-
.recordings=${recordings.map(recording => ({
1155-
storageName: recording.storageName,
1156-
name: recording.flow.title,
1157-
}))}
1158-
.replayAllowed=${this.#replayAllowed}
1153+
<devtools-widget
1154+
.widgetConfig=${UI.Widget.widgetConfig(Components.RecordingListView.RecordingListView, {
1155+
recordings: recordings.map(recording => ({
1156+
storageName: recording.storageName,
1157+
name: recording.flow.title,
1158+
})),
1159+
replayAllowed: this.#replayAllowed,
1160+
})}
11591161
@createrecording=${this.#onCreateNewRecording}
11601162
@deleterecording=${this.#onDeleteRecording}
11611163
@openrecording=${this.#onRecordingSelected}
11621164
@playrecording=${this.#onPlayRecordingByName}
1163-
>
1164-
</devtools-recording-list-view>
1165+
>
1166+
</devtools-widget>
11651167
`;
11661168
// clang-format on
11671169
}

front_end/panels/recorder/components/RecordingListView.test.ts

Lines changed: 42 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,74 +2,63 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import {dispatchClickEvent, dispatchKeyDownEvent, renderElementIntoDOM} from '../../../testing/DOMHelpers.js';
65
import {
76
describeWithEnvironment,
87
setupActionRegistry,
98
} from '../../../testing/EnvironmentHelpers.js';
10-
import * as RenderCoordinator from '../../../ui/components/render_coordinator/render_coordinator.js';
9+
import {
10+
createViewFunctionStub,
11+
type ViewFunctionStub,
12+
} from '../../../testing/ViewFunctionHelpers.js';
1113

1214
import * as Components from './components.js';
1315

1416
describeWithEnvironment('RecordingListView', () => {
1517
setupActionRegistry();
1618

17-
it('should open a recording on Enter', async () => {
18-
const view = new Components.RecordingListView.RecordingListView();
19-
renderElementIntoDOM(view);
20-
view.recordings = [{storageName: 'storage-test', name: 'test'}];
21-
await RenderCoordinator.done();
22-
const recording = view.shadowRoot?.querySelector('.row') as HTMLDivElement;
23-
assert.isOk(recording);
24-
const eventSent = new Promise<Components.RecordingListView.OpenRecordingEvent>(
25-
resolve => {
26-
view.addEventListener('openrecording', resolve, {once: true});
27-
},
28-
);
29-
dispatchKeyDownEvent(recording, {key: 'Enter'});
30-
const event = await eventSent;
31-
assert.strictEqual(event.storageName, 'storage-test');
19+
const views: Components.RecordingListView.RecordingListView[] = [];
20+
21+
afterEach(() => {
22+
// Unregister global listeners in willHide to prevent leaks.
23+
for (const view of views) {
24+
view.willHide();
25+
}
3226
});
3327

34-
it('should delete a recording', async () => {
35-
const view = new Components.RecordingListView.RecordingListView();
36-
renderElementIntoDOM(view);
37-
view.recordings = [{storageName: 'storage-test', name: 'test'}];
38-
await RenderCoordinator.done();
39-
const deleteButton = view.shadowRoot?.querySelector(
40-
'.delete-recording-button',
41-
) as HTMLButtonElement;
42-
assert.isOk(deleteButton);
43-
const eventSent = new Promise<Components.RecordingListView.DeleteRecordingEvent>(
44-
resolve => {
45-
view.addEventListener('deleterecording', resolve, {once: true});
46-
},
47-
);
48-
dispatchClickEvent(deleteButton);
49-
const event = await eventSent;
50-
assert.strictEqual(event.storageName, 'storage-test');
28+
async function createView(output?: Components.RecordingListView.ViewOutput): Promise<[
29+
ViewFunctionStub<typeof Components.RecordingListView.RecordingListView>,
30+
Components.RecordingListView.RecordingListView
31+
]> {
32+
const view = createViewFunctionStub(Components.RecordingListView.RecordingListView, output);
33+
const component = new Components.RecordingListView.RecordingListView(undefined, view);
34+
component.recordings = [{storageName: 'storage-test', name: 'test'}];
35+
component.replayAllowed = true;
36+
component.wasShown();
37+
views.push(component);
38+
return [view, component];
39+
}
40+
41+
it('should open a recording on Enter', async () => {
42+
const [view, component] = await createView();
43+
const dispatchEventSpy = sinon.spy(component.contentElement, 'dispatchEvent');
44+
45+
view.input.onKeyDown('storage-test', new KeyboardEvent('keydown', {key: 'Enter'}));
46+
47+
sinon.assert.calledOnce(dispatchEventSpy);
48+
const event = dispatchEventSpy.firstCall.args[0];
49+
assert.instanceOf(event, Components.RecordingListView.OpenRecordingEvent);
50+
assert.strictEqual((event as Components.RecordingListView.OpenRecordingEvent).storageName, 'storage-test');
5151
});
5252

53-
it('should not open a recording on Enter on the delete button', async () => {
54-
const view = new Components.RecordingListView.RecordingListView();
55-
renderElementIntoDOM(view);
56-
view.recordings = [{storageName: 'storage-test', name: 'test'}];
57-
await RenderCoordinator.done();
58-
const deleteButton = view.shadowRoot?.querySelector(
59-
'.delete-recording-button',
60-
) as HTMLDivElement;
61-
assert.isOk(deleteButton);
62-
const {resolve: forceResolve, promise: eventSent} =
63-
Promise.withResolvers<Components.RecordingListView.OpenRecordingEvent|void>();
53+
it('should delete a recording', async () => {
54+
const [view, component] = await createView();
55+
const dispatchEventSpy = sinon.spy(component.contentElement, 'dispatchEvent');
6456

65-
view.addEventListener('openrecording', forceResolve, {once: true});
57+
view.input.onDeleteClick('storage-test', new MouseEvent('click'));
6658

67-
dispatchKeyDownEvent(deleteButton, {key: 'Enter', bubbles: true});
68-
const maybeEvent = await Promise.race([
69-
eventSent,
70-
new Promise(resolve => queueMicrotask(() => resolve('timeout'))),
71-
]);
72-
assert.strictEqual(maybeEvent, 'timeout');
73-
forceResolve();
59+
sinon.assert.calledOnce(dispatchEventSpy);
60+
const event = dispatchEventSpy.firstCall.args[0];
61+
assert.instanceOf(event, Components.RecordingListView.DeleteRecordingEvent);
62+
assert.strictEqual((event as Components.RecordingListView.DeleteRecordingEvent).storageName, 'storage-test');
7463
});
7564
});

0 commit comments

Comments
 (0)