// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { assert } from 'chai';
import sinon from 'sinon';
import * as Common from '../../core/common/common.js';
import * as Platform from '../../core/platform/platform.js';
import * as SDK from '../../core/sdk/sdk.js';
import * as Bindings from '../../models/bindings/bindings.js';
import * as Breakpoints from '../../models/breakpoints/breakpoints.js';
import * as TextUtils from '../../models/text_utils/text_utils.js';
import * as Workspace from '../../models/workspace/workspace.js';
import { assertElements, dispatchClickEvent, dispatchKeyDownEvent, renderElementIntoDOM, } from '../../testing/DOMHelpers.js';
import { createTarget, describeWithEnvironment, } from '../../testing/EnvironmentHelpers.js';
import { describeWithMockConnection } from '../../testing/MockConnection.js';
import { MockProtocolBackend } from '../../testing/MockScopeChain.js';
import { createContentProviderUISourceCode, setupMockedUISourceCode, } from '../../testing/UISourceCodeHelpers.js';
import * as RenderCoordinator from '../../ui/components/render_coordinator/render_coordinator.js';
import * as UI from '../../ui/legacy/legacy.js';
import * as Sources from './sources.js';
const { urlString } = Platform.DevToolsPath;
const DETAILS_SELECTOR = 'details';
const EXPANDED_GROUPS_SELECTOR = 'details[open]';
const COLLAPSED_GROUPS_SELECTOR = 'details:not([open])';
const CODE_SNIPPET_SELECTOR = '.code-snippet';
const GROUP_NAME_SELECTOR = '.group-header-title';
const BREAKPOINT_ITEM_SELECTOR = '.breakpoint-item';
const HIT_BREAKPOINT_SELECTOR = BREAKPOINT_ITEM_SELECTOR + '.hit';
const BREAKPOINT_LOCATION_SELECTOR = '.location';
const REMOVE_FILE_BREAKPOINTS_SELECTOR = '.group-hover-actions > button[data-remove-breakpoint]';
const REMOVE_SINGLE_BREAKPOINT_SELECTOR = '.breakpoint-item-location-or-actions > button[data-remove-breakpoint]';
const EDIT_SINGLE_BREAKPOINT_SELECTOR = 'button[data-edit-breakpoint]';
const PAUSE_ON_UNCAUGHT_EXCEPTIONS_SELECTOR = '.pause-on-uncaught-exceptions';
const PAUSE_ON_CAUGHT_EXCEPTIONS_SELECTOR = '.pause-on-caught-exceptions';
const TABBABLE_SELECTOR = '[tabindex="0"]';
const SUMMARY_SELECTOR = 'summary';
const GROUP_DIFFERENTIATOR_SELECTOR = '.group-header-differentiator';
const HELLO_JS_FILE = 'hello.js';
const TEST_JS_FILE = 'test.js';
function createBreakpointLocations(testData) {
    const breakpointLocations = testData.map(data => {
        const mocked = setupMockedUISourceCode(data.url);
        const mockedContent = Promise.resolve(new TextUtils.ContentData.ContentData(data.content, /* isBase64 */ false, 'text/plain'));
        sinon.stub(mocked.sut, 'requestContentData').returns(mockedContent);
        const uiLocation = new Workspace.UISourceCode.UILocation(mocked.sut, data.lineNumber, data.columnNumber);
        const breakpoint = sinon.createStubInstance(Breakpoints.BreakpointManager.Breakpoint);
        breakpoint.enabled.returns(data.enabled);
        breakpoint.condition.returns(data.condition);
        breakpoint.isLogpoint.returns(data.isLogpoint);
        breakpoint.breakpointStorageId.returns(`${data.url}:${data.lineNumber}:${data.columnNumber}`);
        breakpoint.url.returns(data.url);
        breakpoint.lineNumber.returns(data.lineNumber);
        breakpoint.columnNumber.returns(data.columnNumber);
        breakpoint.getClosestResolvedLocation.returns(uiLocation);
        return new Breakpoints.BreakpointManager.BreakpointLocation(breakpoint, uiLocation);
    });
    return breakpointLocations;
}
function createStubBreakpointManagerAndSettings() {
    const breakpointManager = sinon.createStubInstance(Breakpoints.BreakpointManager.BreakpointManager);
    breakpointManager.supportsConditionalBreakpoints.returns(true);
    const dummyStorage = new Common.Settings.SettingsStorage({});
    const settings = Common.Settings.Settings.instance({
        forceNew: true,
        syncedStorage: dummyStorage,
        globalStorage: dummyStorage,
        localStorage: dummyStorage,
        settingRegistrations: Common.SettingRegistration.getRegisteredSettings(),
    });
    return { breakpointManager, settings };
}
function createStubBreakpointManagerAndSettingsWithMockdata(testData) {
    const { breakpointManager, settings } = createStubBreakpointManagerAndSettings();
    sinon.stub(Breakpoints.BreakpointManager.BreakpointManager, 'instance').returns(breakpointManager);
    const breakpointLocations = createBreakpointLocations(testData);
    breakpointManager.allBreakpointLocations.returns(breakpointLocations);
    return { breakpointManager, settings };
}
function createLocationTestData(url, lineNumber, columnNumber, enabled = true, content = '', condition = Breakpoints.BreakpointManager.EMPTY_BREAKPOINT_CONDITION, isLogpoint = false, hoverText) {
    return {
        url: urlString `${url}`,
        lineNumber,
        columnNumber,
        enabled,
        content,
        condition,
        isLogpoint,
        hoverText,
    };
}
async function setUpTestWithOneBreakpointLocation(params = {
    file: HELLO_JS_FILE,
    lineNumber: 10,
    columnNumber: 3,
    enabled: true,
    snippet: 'const a;',
}) {
    const testData = [
        createLocationTestData(params.file, params.lineNumber, params.columnNumber, params.enabled, params.snippet),
    ];
    const { breakpointManager, settings } = createStubBreakpointManagerAndSettingsWithMockdata(testData);
    const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({ forceNew: true, breakpointManager, settings });
    const data = await controller.getUpdatedBreakpointViewData();
    assert.lengthOf(data.groups, 1);
    assert.lengthOf(data.groups[0].breakpointItems, 1);
    const locations = Breakpoints.BreakpointManager.BreakpointManager.instance().allBreakpointLocations();
    assert.lengthOf(locations, 1);
    return { controller, groups: data.groups, location: locations[0] };
}
class MockRevealer {
    async reveal(_revealable, _omitFocus) {
    }
}
async function createAndInitializeBreakpointsView() {
    // Force creation of a new BreakpointsView singleton so that it gets correctly re-wired with
    // the current controller singleton (to pick up the latest breakpoint state).
    const component = Sources.BreakpointsView.BreakpointsView.instance({ forceNew: true });
    await RenderCoordinator.done(); // Wait until the initial rendering finishes.
    renderElementIntoDOM(component);
    await component.updateComplete;
    return component;
}
async function renderNoBreakpoints({ pauseOnUncaughtExceptions, pauseOnCaughtExceptions }) {
    const component = await createAndInitializeBreakpointsView();
    component.data = {
        breakpointsActive: true,
        pauseOnUncaughtExceptions,
        pauseOnCaughtExceptions,
        groups: [],
    };
    await RenderCoordinator.done();
    await component.updateComplete;
    return component;
}
async function renderSingleBreakpoint(type = "REGULAR_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.REGULAR_BREAKPOINT */, hoverText) {
    // Only provide a hover text if it's not a regular breakpoint.
    assert.isTrue(!hoverText || type !== "REGULAR_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.REGULAR_BREAKPOINT */);
    const component = await createAndInitializeBreakpointsView();
    const data = {
        breakpointsActive: true,
        pauseOnUncaughtExceptions: false,
        pauseOnCaughtExceptions: false,
        groups: [
            {
                name: 'test1.js',
                url: urlString `https://google.com/test1.js`,
                editable: true,
                expanded: true,
                breakpointItems: [
                    {
                        id: '1',
                        location: '1',
                        codeSnippet: 'const a = 0;',
                        isHit: true,
                        status: "ENABLED" /* Sources.BreakpointsView.BreakpointStatus.ENABLED */,
                        type,
                        hoverText,
                    },
                ],
            },
        ],
    };
    component.data = data;
    await RenderCoordinator.done();
    await component.updateComplete;
    return { component, data };
}
async function renderMultipleBreakpoints() {
    const component = await createAndInitializeBreakpointsView();
    const data = {
        breakpointsActive: true,
        pauseOnUncaughtExceptions: false,
        pauseOnCaughtExceptions: false,
        groups: [
            {
                name: 'test1.js',
                url: urlString `https://google.com/test1.js`,
                editable: true,
                expanded: true,
                breakpointItems: [
                    {
                        id: '1',
                        type: "REGULAR_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.REGULAR_BREAKPOINT */,
                        location: '234',
                        codeSnippet: 'const a = x;',
                        isHit: false,
                        status: "ENABLED" /* Sources.BreakpointsView.BreakpointStatus.ENABLED */,
                    },
                    {
                        id: '2',
                        type: "REGULAR_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.REGULAR_BREAKPOINT */,
                        location: '3:3',
                        codeSnippet: 'if (x > a) {',
                        isHit: true,
                        status: "DISABLED" /* Sources.BreakpointsView.BreakpointStatus.DISABLED */,
                    },
                ],
            },
            {
                name: 'test2.js',
                url: urlString `https://google.com/test2.js`,
                editable: false,
                expanded: true,
                breakpointItems: [
                    {
                        id: '3',
                        type: "REGULAR_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.REGULAR_BREAKPOINT */,
                        location: '11',
                        codeSnippet: 'const y;',
                        isHit: false,
                        status: "ENABLED" /* Sources.BreakpointsView.BreakpointStatus.ENABLED */,
                    },
                ],
            },
            {
                name: 'main.js',
                url: urlString `https://test.com/main.js`,
                editable: true,
                expanded: false,
                breakpointItems: [
                    {
                        id: '4',
                        type: "REGULAR_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.REGULAR_BREAKPOINT */,
                        location: '3',
                        codeSnippet: 'if (a == 0) {',
                        isHit: false,
                        status: "ENABLED" /* Sources.BreakpointsView.BreakpointStatus.ENABLED */,
                    },
                ],
            },
        ],
    };
    component.data = data;
    await RenderCoordinator.done();
    await component.updateComplete;
    return { component, data };
}
function extractBreakpointItems(data) {
    const breakpointItems = data.groups.flatMap(group => group.breakpointItems);
    assert.isAbove(breakpointItems.length, 0);
    return breakpointItems;
}
function checkCodeSnippet(renderedBreakpointItem, breakpointItem) {
    const snippetElement = renderedBreakpointItem.querySelector(CODE_SNIPPET_SELECTOR);
    assert.instanceOf(snippetElement, HTMLSpanElement);
    assert.strictEqual(snippetElement.textContent, breakpointItem.codeSnippet);
}
function checkCheckboxState(checkbox, breakpointItem) {
    const checked = checkbox.checked;
    const indeterminate = checkbox.indeterminate;
    if (breakpointItem.status === "INDETERMINATE" /* Sources.BreakpointsView.BreakpointStatus.INDETERMINATE */) {
        assert.isTrue(indeterminate);
    }
    else {
        assert.isFalse(indeterminate);
        assert.strictEqual((breakpointItem.status === "ENABLED" /* Sources.BreakpointsView.BreakpointStatus.ENABLED */), checked);
    }
}
function checkGroupNames(renderedGroupElements, breakpointGroups) {
    assert.lengthOf(renderedGroupElements, breakpointGroups.length);
    for (let i = 0; i < renderedGroupElements.length; ++i) {
        const renderedGroup = renderedGroupElements[i];
        assert.instanceOf(renderedGroup, HTMLDetailsElement);
        const titleElement = renderedGroup.querySelector(GROUP_NAME_SELECTOR);
        assert.instanceOf(titleElement, HTMLSpanElement);
        assert.strictEqual(titleElement.textContent, breakpointGroups[i].name);
    }
}
function hover(component, selector) {
    assert.isNotNull(component.contentElement);
    // Dispatch a mouse over.
    component.contentElement.querySelector(selector)?.dispatchEvent(new Event('mouseover'));
    // Wait until the re-rendering has happened.
    return RenderCoordinator.done();
}
describeWithEnvironment('BreakpointsSidebarController', () => {
    after(() => {
        Sources.BreakpointsView.BreakpointsSidebarController.removeInstance();
    });
    it('can remove a breakpoint', async () => {
        const { groups, location } = await setUpTestWithOneBreakpointLocation();
        const breakpoint = location.breakpoint;
        const breakpointItem = groups[0].breakpointItems[0];
        Sources.BreakpointsView.BreakpointsSidebarController.instance().breakpointsRemoved([breakpointItem]);
        assert.isTrue(breakpoint.remove.calledOnceWith(false));
    });
    it('changes breakpoint state', async () => {
        const { groups, location } = await setUpTestWithOneBreakpointLocation();
        const breakpointItem = groups[0].breakpointItems[0];
        assert.strictEqual(breakpointItem.status, "ENABLED" /* Sources.BreakpointsView.BreakpointStatus.ENABLED */);
        const breakpoint = location.breakpoint;
        Sources.BreakpointsView.BreakpointsSidebarController.instance().breakpointStateChanged(breakpointItem, false);
        sinon.assert.calledWith(breakpoint.setEnabled, false);
    });
    it('correctly reveals source location', async () => {
        const { groups, location: { uiLocation } } = await setUpTestWithOneBreakpointLocation();
        const breakpointItem = groups[0].breakpointItems[0];
        const revealer = sinon.createStubInstance((MockRevealer));
        Common.Revealer.registerRevealer({
            contextTypes() {
                return [Workspace.UISourceCode.UILocation];
            },
            destination: Common.Revealer.RevealerDestination.SOURCES_PANEL,
            async loadRevealer() {
                return revealer;
            },
        });
        await Sources.BreakpointsView.BreakpointsSidebarController.instance().jumpToSource(breakpointItem);
        assert.isTrue(revealer.reveal.calledOnceWith(uiLocation));
    });
    it('correctly reveals breakpoint editor', async () => {
        const { groups, location } = await setUpTestWithOneBreakpointLocation();
        const breakpointItem = groups[0].breakpointItems[0];
        const revealer = sinon.createStubInstance((MockRevealer));
        Common.Revealer.registerRevealer({
            contextTypes() {
                return [Breakpoints.BreakpointManager.BreakpointLocation];
            },
            destination: Common.Revealer.RevealerDestination.SOURCES_PANEL,
            async loadRevealer() {
                return revealer;
            },
        });
        await Sources.BreakpointsView.BreakpointsSidebarController.instance().breakpointEdited(breakpointItem, false /* editButtonClicked */);
        assert.isTrue(revealer.reveal.calledOnceWith(location));
    });
    describe('getUpdatedBreakpointViewData', () => {
        it('extracts breakpoint data', async () => {
            const testData = [
                createLocationTestData(HELLO_JS_FILE, 3, 10),
                createLocationTestData(TEST_JS_FILE, 1, 1),
            ];
            const { breakpointManager, settings } = createStubBreakpointManagerAndSettingsWithMockdata(testData);
            const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({ forceNew: true, breakpointManager, settings });
            const actual = await controller.getUpdatedBreakpointViewData();
            const createExpectedBreakpointGroups = (testData) => {
                const status = testData.enabled ? "ENABLED" /* Sources.BreakpointsView.BreakpointStatus.ENABLED */ :
                    "DISABLED" /* Sources.BreakpointsView.BreakpointStatus.DISABLED */;
                let type = "REGULAR_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.REGULAR_BREAKPOINT */;
                if (testData.condition) {
                    if (testData.isLogpoint) {
                        type = "LOGPOINT" /* SDK.DebuggerModel.BreakpointType.LOGPOINT */;
                    }
                    else {
                        type = "CONDITIONAL_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.CONDITIONAL_BREAKPOINT */;
                    }
                }
                return {
                    name: testData.url,
                    url: testData.url,
                    editable: true,
                    expanded: true,
                    breakpointItems: [
                        {
                            id: `${testData.url}:${testData.lineNumber}:${testData.columnNumber}`,
                            location: `${testData.lineNumber + 1}`,
                            codeSnippet: '',
                            isHit: false,
                            status,
                            type,
                            hoverText: testData.hoverText,
                        },
                    ],
                };
            };
            const expected = {
                breakpointsActive: true,
                pauseOnUncaughtExceptions: false,
                pauseOnCaughtExceptions: false,
                groups: testData.map(createExpectedBreakpointGroups),
            };
            assert.deepEqual(actual, expected);
        });
        it('respects the breakpointsActive setting', async () => {
            const { breakpointManager, settings } = createStubBreakpointManagerAndSettingsWithMockdata([]);
            const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({ forceNew: true, breakpointManager, settings });
            settings.moduleSetting('breakpoints-active').set(true);
            let data = await controller.getUpdatedBreakpointViewData();
            assert.isTrue(data.breakpointsActive);
            settings.moduleSetting('breakpoints-active').set(false);
            data = await controller.getUpdatedBreakpointViewData();
            assert.isFalse(data.breakpointsActive);
        });
        it('marks groups as editable based on conditional breakpoint support', async () => {
            const testData = [
                createLocationTestData(HELLO_JS_FILE, 3, 10),
                createLocationTestData(TEST_JS_FILE, 1, 1),
            ];
            const { breakpointManager, settings } = createStubBreakpointManagerAndSettingsWithMockdata(testData);
            const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({ forceNew: true, breakpointManager, settings });
            breakpointManager.supportsConditionalBreakpoints.returns(false);
            for (const group of (await controller.getUpdatedBreakpointViewData()).groups) {
                assert.isFalse(group.editable);
            }
            breakpointManager.supportsConditionalBreakpoints.returns(true);
            for (const group of (await controller.getUpdatedBreakpointViewData()).groups) {
                assert.isTrue(group.editable);
            }
        });
        it('groups breakpoints that are in the same file', async () => {
            const testData = [
                createLocationTestData(HELLO_JS_FILE, 3, 10),
                createLocationTestData(TEST_JS_FILE, 1, 1),
            ];
            const { breakpointManager, settings } = createStubBreakpointManagerAndSettingsWithMockdata(testData);
            const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({ forceNew: true, breakpointManager, settings });
            const actualViewData = await controller.getUpdatedBreakpointViewData();
            assert.lengthOf(actualViewData.groups, 2);
            assert.lengthOf(actualViewData.groups[0].breakpointItems, 1);
            assert.lengthOf(actualViewData.groups[1].breakpointItems, 1);
        });
        it('correctly sets the name of the group', async () => {
            const { groups } = await setUpTestWithOneBreakpointLocation({ file: HELLO_JS_FILE, lineNumber: 0, columnNumber: 0, enabled: false });
            assert.strictEqual(groups[0].name, HELLO_JS_FILE);
        });
        it('only extracts the line number as location if one breakpoint is on that line', async () => {
            const { groups } = await setUpTestWithOneBreakpointLocation({ file: HELLO_JS_FILE, lineNumber: 4, columnNumber: 0, enabled: false });
            assert.strictEqual(groups[0].breakpointItems[0].location, '5');
        });
        it('extracts the line number and column number as location if more than one breakpoint is on that line', async () => {
            const testData = [
                createLocationTestData(HELLO_JS_FILE, 3, 10),
                createLocationTestData(HELLO_JS_FILE, 3, 15),
            ];
            const { breakpointManager, settings } = createStubBreakpointManagerAndSettingsWithMockdata(testData);
            const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({ forceNew: true, breakpointManager, settings });
            const actualViewData = await controller.getUpdatedBreakpointViewData();
            assert.lengthOf(actualViewData.groups, 1);
            assert.lengthOf(actualViewData.groups[0].breakpointItems, 2);
            assert.strictEqual(actualViewData.groups[0].breakpointItems[0].location, '4:11');
            assert.strictEqual(actualViewData.groups[0].breakpointItems[1].location, '4:16');
        });
        it('orders breakpoints within a file by location', async () => {
            const testData = [
                createLocationTestData(HELLO_JS_FILE, 3, 15),
                createLocationTestData(HELLO_JS_FILE, 3, 10),
            ];
            const { breakpointManager, settings } = createStubBreakpointManagerAndSettingsWithMockdata(testData);
            const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({ forceNew: true, breakpointManager, settings });
            const actualViewData = await controller.getUpdatedBreakpointViewData();
            assert.lengthOf(actualViewData.groups, 1);
            assert.lengthOf(actualViewData.groups[0].breakpointItems, 2);
            assert.strictEqual(actualViewData.groups[0].breakpointItems[0].location, '4:11');
            assert.strictEqual(actualViewData.groups[0].breakpointItems[1].location, '4:16');
        });
        it('orders breakpoints within groups by location', async () => {
            const testData = [
                createLocationTestData(TEST_JS_FILE, 3, 15),
                createLocationTestData(HELLO_JS_FILE, 3, 10),
            ];
            const { breakpointManager, settings } = createStubBreakpointManagerAndSettingsWithMockdata(testData);
            const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({ forceNew: true, breakpointManager, settings });
            const actualViewData = await controller.getUpdatedBreakpointViewData();
            assert.lengthOf(actualViewData.groups, 2);
            const names = actualViewData.groups.map(group => group.name);
            assert.deepEqual(names, [HELLO_JS_FILE, TEST_JS_FILE]);
        });
        it('merges breakpoints mapping to the same location into one', async () => {
            const testData = [
                createLocationTestData(TEST_JS_FILE, 3, 15),
                createLocationTestData(TEST_JS_FILE, 3, 15),
            ];
            const { breakpointManager, settings } = createStubBreakpointManagerAndSettingsWithMockdata(testData);
            const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({ forceNew: true, breakpointManager, settings });
            const actualViewData = await controller.getUpdatedBreakpointViewData();
            assert.lengthOf(actualViewData.groups, 1);
            assert.lengthOf(actualViewData.groups[0].breakpointItems, 1);
        });
        it('correctly extracts the enabled state for true', async () => {
            const { groups } = await setUpTestWithOneBreakpointLocation({ file: '', lineNumber: 0, columnNumber: 0, enabled: true });
            const breakpointItem = groups[0].breakpointItems[0];
            assert.strictEqual(breakpointItem.status, "ENABLED" /* Sources.BreakpointsView.BreakpointStatus.ENABLED */);
        });
        it('correctly extracts the enabled state for false', async () => {
            const { groups } = await setUpTestWithOneBreakpointLocation({ file: '', lineNumber: 0, columnNumber: 0, enabled: false });
            const breakpointItem = groups[0].breakpointItems[0];
            assert.strictEqual(breakpointItem.status, "DISABLED" /* Sources.BreakpointsView.BreakpointStatus.DISABLED */);
        });
        it('correctly extracts the disabled state', async () => {
            const snippet = 'const a = x;';
            const { groups } = await setUpTestWithOneBreakpointLocation({ file: '', lineNumber: 0, columnNumber: 0, enabled: false, snippet });
            assert.strictEqual(groups[0].breakpointItems[0].codeSnippet, snippet);
        });
        it('correctly extracts conditional breakpoints', async () => {
            const condition = 'x < a';
            const testData = [
                createLocationTestData(TEST_JS_FILE, 3, 15, true /* enabled */, '', condition, false /* isLogpoint */, condition),
            ];
            const { breakpointManager, settings } = createStubBreakpointManagerAndSettingsWithMockdata(testData);
            const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({ forceNew: true, breakpointManager, settings });
            const actualViewData = await controller.getUpdatedBreakpointViewData();
            assert.lengthOf(actualViewData.groups, 1);
            assert.lengthOf(actualViewData.groups[0].breakpointItems, 1);
            const breakpointItem = actualViewData.groups[0].breakpointItems[0];
            assert.strictEqual(breakpointItem.type, "CONDITIONAL_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.CONDITIONAL_BREAKPOINT */);
            assert.strictEqual(breakpointItem.hoverText, condition);
        });
        it('correctly extracts logpoints', async () => {
            const logExpression = 'x';
            const testData = [
                createLocationTestData(TEST_JS_FILE, 3, 15, true /* enabled */, '', logExpression, true /* isLogpoint */, logExpression),
            ];
            const { breakpointManager, settings } = createStubBreakpointManagerAndSettingsWithMockdata(testData);
            const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({ forceNew: true, breakpointManager, settings });
            const actualViewData = await controller.getUpdatedBreakpointViewData();
            assert.lengthOf(actualViewData.groups, 1);
            assert.lengthOf(actualViewData.groups[0].breakpointItems, 1);
            const breakpointItem = actualViewData.groups[0].breakpointItems[0];
            assert.strictEqual(breakpointItem.type, "LOGPOINT" /* SDK.DebuggerModel.BreakpointType.LOGPOINT */);
            assert.strictEqual(breakpointItem.hoverText, logExpression);
        });
        describe('breakpoint groups', () => {
            it('are expanded by default', async () => {
                const { controller } = await setUpTestWithOneBreakpointLocation();
                const actualViewData = await controller.getUpdatedBreakpointViewData();
                assert.isTrue(actualViewData.groups[0].expanded);
            });
            it('are collapsed if user collapses it', async () => {
                const { controller, groups } = await setUpTestWithOneBreakpointLocation();
                controller.expandedStateChanged(groups[0].url, false /* expanded */);
                const actualViewData = await controller.getUpdatedBreakpointViewData();
                assert.isFalse(actualViewData.groups[0].expanded);
            });
            it('are expanded if user expands it', async () => {
                const { controller, groups } = await setUpTestWithOneBreakpointLocation();
                controller.expandedStateChanged(groups[0].url, true /* expanded */);
                const actualViewData = await controller.getUpdatedBreakpointViewData();
                assert.isTrue(actualViewData.groups[0].expanded);
            });
            it('remember the collapsed state', async () => {
                {
                    const { controller, groups } = await setUpTestWithOneBreakpointLocation();
                    controller.expandedStateChanged(groups[0].url, false /* expanded */);
                    const actualViewData = await controller.getUpdatedBreakpointViewData();
                    assert.isFalse(actualViewData.groups[0].expanded);
                }
                // A new controller is created and initialized with the expanded settings.
                {
                    const breakpointManager = Breakpoints.BreakpointManager.BreakpointManager.instance();
                    const settings = Common.Settings.Settings.instance();
                    const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({
                        forceNew: true,
                        breakpointManager,
                        settings,
                    });
                    const actualViewData = await controller.getUpdatedBreakpointViewData();
                    assert.isFalse(actualViewData.groups[0].expanded);
                }
            });
            it('remember the expanded state', async () => {
                {
                    const { controller, groups } = await setUpTestWithOneBreakpointLocation();
                    controller.expandedStateChanged(groups[0].url, true /* expanded */);
                    const actualViewData = await controller.getUpdatedBreakpointViewData();
                    assert.isTrue(actualViewData.groups[0].expanded);
                }
                // A new controller is created and initialized with the expanded settings.
                {
                    const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({
                        forceNew: true,
                        breakpointManager: Breakpoints.BreakpointManager.BreakpointManager.instance(),
                        settings: Common.Settings.Settings.instance(),
                    });
                    const actualViewData = await controller.getUpdatedBreakpointViewData();
                    assert.isTrue(actualViewData.groups[0].expanded);
                }
            });
        });
    });
});
describeWithMockConnection('BreakpointsSidebarController', () => {
    beforeEach(() => {
        const workspace = Workspace.Workspace.WorkspaceImpl.instance();
        const targetManager = SDK.TargetManager.TargetManager.instance();
        const resourceMapping = new Bindings.ResourceMapping.ResourceMapping(targetManager, workspace);
        const ignoreListManager = Workspace.IgnoreListManager.IgnoreListManager.instance({ forceNew: true });
        const debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance({
            forceNew: true,
            resourceMapping,
            targetManager,
            ignoreListManager,
            workspace,
        });
        Breakpoints.BreakpointManager.BreakpointManager.instance({
            forceNew: true,
            targetManager,
            workspace,
            debuggerWorkspaceBinding,
            settings: Common.Settings.Settings.instance()
        });
    });
    const DEFAULT_BREAKPOINT = [
        Breakpoints.BreakpointManager.EMPTY_BREAKPOINT_CONDITION,
        true, // enabled
        false, // isLogpoint
        "USER_ACTION" /* Breakpoints.BreakpointManager.BreakpointOrigin.USER_ACTION */,
    ];
    it('auto-expands if a user adds a new  breakpoint', async () => {
        const breakpointManager = Breakpoints.BreakpointManager.BreakpointManager.instance();
        const settings = Common.Settings.Settings.instance();
        const { uiSourceCode, project } = createContentProviderUISourceCode({ url: urlString `test.js`, mimeType: 'text/javascript' });
        const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({ forceNew: true, breakpointManager, settings });
        // Add one breakpoint and collapse the tree.
        const b1 = await breakpointManager.setBreakpoint(uiSourceCode, 0, 0, ...DEFAULT_BREAKPOINT);
        assert.exists(b1);
        {
            controller.expandedStateChanged(uiSourceCode.url(), false /* expanded */);
            const data = await controller.getUpdatedBreakpointViewData();
            assert.lengthOf(data.groups, 1);
            assert.lengthOf(data.groups[0].breakpointItems, 1);
            assert.isFalse(data.groups[0].expanded);
        }
        // Add a new breakpoint and check if it's expanded as expected.
        const b2 = await breakpointManager.setBreakpoint(uiSourceCode, 0, 3, ...DEFAULT_BREAKPOINT);
        assert.exists(b2);
        {
            const data = await controller.getUpdatedBreakpointViewData();
            assert.lengthOf(data.groups, 1);
            assert.lengthOf(data.groups[0].breakpointItems, 2);
            assert.isTrue(data.groups[0].expanded);
        }
        // Clean up.
        await b1.remove(false /* keepInStorage */);
        await b2.remove(false /* keepInStorage */);
        Workspace.Workspace.WorkspaceImpl.instance().removeProject(project);
    });
    it('does not auto-expand if a breakpoint was not triggered by user action', async () => {
        const breakpointManager = Breakpoints.BreakpointManager.BreakpointManager.instance();
        const settings = Common.Settings.Settings.instance();
        const { uiSourceCode, project } = createContentProviderUISourceCode({ url: urlString `test.js`, mimeType: 'text/javascript' });
        const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({ forceNew: true, breakpointManager, settings });
        // Add one breakpoint and collapse the tree.
        const b1 = await breakpointManager.setBreakpoint(uiSourceCode, 0, 0, ...DEFAULT_BREAKPOINT);
        assert.exists(b1);
        {
            controller.expandedStateChanged(uiSourceCode.url(), false /* expanded */);
            const data = await controller.getUpdatedBreakpointViewData();
            assert.lengthOf(data.groups, 1);
            assert.lengthOf(data.groups[0].breakpointItems, 1);
            assert.isFalse(data.groups[0].expanded);
        }
        // Add a new non-user triggered breakpoint and check if it's still collapsed.
        const b2 = await breakpointManager.setBreakpoint(uiSourceCode, 0, 3, Breakpoints.BreakpointManager.EMPTY_BREAKPOINT_CONDITION, true, false, "RESTORED" /* Breakpoints.BreakpointManager.BreakpointOrigin.OTHER */);
        assert.exists(b2);
        {
            const data = await controller.getUpdatedBreakpointViewData();
            assert.lengthOf(data.groups, 1);
            assert.lengthOf(data.groups[0].breakpointItems, 2);
            assert.isFalse(data.groups[0].expanded);
        }
        // Clean up.
        await b1.remove(false /* keepInStorage */);
        await b2.remove(false /* keepInStorage */);
        Workspace.Workspace.WorkspaceImpl.instance().removeProject(project);
    });
    it('auto-expands if a breakpoint was hit', async () => {
        sinon.stub(Common.Revealer.RevealerRegistry.instance(), 'reveal'); // Prevent pending reveal promises after tests are done.
        const backend = new MockProtocolBackend();
        const target = createTarget();
        target.targetManager().setScopeTarget(target);
        const breakpointManager = Breakpoints.BreakpointManager.BreakpointManager.instance();
        const sourceRoot = 'http://example.com';
        const scriptInfo = {
            url: `${sourceRoot}/foo.js`,
            content: 'foo();\n',
        };
        const debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
        const uiSourceCodePromise = debuggerWorkspaceBinding.waitForUISourceCodeAdded(urlString `${`${sourceRoot}/foo.js`}`, target);
        const script = await backend.addScript(target, scriptInfo, null);
        await uiSourceCodePromise;
        const uiSourceCode = debuggerWorkspaceBinding.uiSourceCodeForScript(script);
        assert.exists(uiSourceCode);
        // Set up sdk and ui location, and a mapping between them, such that we can identify that
        // the hit breakpoint is the one we are adding.
        const responderPromise = backend.responderToBreakpointByUrlRequest(uiSourceCode.url(), 0)({
            breakpointId: 'DUMMY_BREAKPOINT',
            locations: [{
                    scriptId: script.scriptId,
                    lineNumber: 0,
                    columnNumber: 0,
                }]
        });
        const b1 = await breakpointManager.setBreakpoint(uiSourceCode, 0, 0, ...DEFAULT_BREAKPOINT);
        assert.exists(b1);
        await responderPromise;
        const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({ forceNew: true, breakpointManager, settings: Common.Settings.Settings.instance() });
        controller.expandedStateChanged(uiSourceCode.url(), false /* expanded */);
        // Double check that the group is collapsed.
        {
            const data = await controller.getUpdatedBreakpointViewData();
            assert.isFalse(data.groups[0].expanded);
        }
        // Simulating a breakpoint hit. Update the DebuggerPausedDetails to contain the info on the hit breakpoint.
        const callFrame = sinon.createStubInstance(SDK.DebuggerModel.CallFrame);
        callFrame.location.returns(new SDK.DebuggerModel.Location(script.debuggerModel, script.scriptId, 0, 0));
        const pausedDetails = sinon.createStubInstance(SDK.DebuggerModel.DebuggerPausedDetails);
        pausedDetails.callFrames = [callFrame];
        // Instead of setting the flavor, directly call `flavorChanged` on the controller and mock what it's set to.
        // Setting the flavor would have other listeners listening to it, and would cause undesirable side effects.
        sinon.stub(UI.Context.Context.instance(), 'flavor')
            .callsFake(flavorType => flavorType === SDK.DebuggerModel.DebuggerPausedDetails ? pausedDetails : null);
        {
            const data = await controller.getUpdatedBreakpointViewData();
            // Assert that the breakpoint is hit and the group is expanded.
            assert.isTrue(data.groups[0].breakpointItems[0].isHit);
            assert.isTrue(data.groups[0].expanded);
        }
        // Clean up.
        await b1.remove(false /* keepInStorage */);
    });
    it('changes pause on exception state', async () => {
        const { breakpointManager, settings } = createStubBreakpointManagerAndSettings();
        breakpointManager.allBreakpointLocations.returns([]);
        const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance({ forceNew: true, breakpointManager, settings });
        for (const pauseOnUncaughtExceptions of [true, false]) {
            for (const pauseOnCaughtExceptions of [true, false]) {
                controller.setPauseOnUncaughtExceptions(pauseOnUncaughtExceptions);
                controller.setPauseOnCaughtExceptions(pauseOnCaughtExceptions);
                const data = await controller.getUpdatedBreakpointViewData();
                assert.strictEqual(data.pauseOnUncaughtExceptions, pauseOnUncaughtExceptions);
                assert.strictEqual(data.pauseOnCaughtExceptions, pauseOnCaughtExceptions);
                assert.strictEqual(settings.moduleSetting('pause-on-uncaught-exception').get(), pauseOnUncaughtExceptions);
                assert.strictEqual(settings.moduleSetting('pause-on-caught-exception').get(), pauseOnCaughtExceptions);
            }
        }
    });
});
describeWithMockConnection('BreakpointsView', () => {
    beforeEach(() => {
        const workspace = Workspace.Workspace.WorkspaceImpl.instance();
        const targetManager = SDK.TargetManager.TargetManager.instance();
        const resourceMapping = new Bindings.ResourceMapping.ResourceMapping(targetManager, workspace);
        const ignoreListManager = Workspace.IgnoreListManager.IgnoreListManager.instance({ forceNew: true });
        const debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance({
            forceNew: true,
            resourceMapping,
            targetManager,
            ignoreListManager,
            workspace,
        });
        Breakpoints.BreakpointManager.BreakpointManager.instance({
            forceNew: true,
            targetManager,
            workspace,
            debuggerWorkspaceBinding,
            settings: Common.Settings.Settings.instance()
        });
    });
    it('correctly expands breakpoint groups', async () => {
        const { component, data } = await renderMultipleBreakpoints();
        assert.isNotNull(component.contentElement);
        const expandedGroups = data.groups.filter(group => group.expanded);
        assert.isAbove(expandedGroups.length, 0);
        const renderedExpandedGroups = Array.from(component.contentElement.querySelectorAll(EXPANDED_GROUPS_SELECTOR));
        assert.lengthOf(renderedExpandedGroups, expandedGroups.length);
        checkGroupNames(renderedExpandedGroups, expandedGroups);
    });
    it('correctly collapses breakpoint groups', async () => {
        const { component, data } = await renderMultipleBreakpoints();
        assert.isNotNull(component.contentElement);
        const collapsedGroups = data.groups.filter(group => !group.expanded);
        assert.isAbove(collapsedGroups.length, 0);
        const renderedCollapsedGroups = Array.from(component.contentElement.querySelectorAll(COLLAPSED_GROUPS_SELECTOR));
        checkGroupNames(renderedCollapsedGroups, collapsedGroups);
    });
    it('renders the group names', async () => {
        const { component, data } = await renderMultipleBreakpoints();
        assert.isNotNull(component.contentElement);
        const renderedGroupNames = component.contentElement.querySelectorAll(GROUP_NAME_SELECTOR);
        assertElements(renderedGroupNames, HTMLSpanElement);
        const expectedNames = data.groups.flatMap(group => group.name);
        const actualNames = [];
        for (const renderedGroupName of renderedGroupNames.values()) {
            actualNames.push(renderedGroupName.textContent);
        }
        assert.deepEqual(actualNames, expectedNames);
    });
    it('renders the breakpoints with their checkboxes', async () => {
        const { component, data } = await renderMultipleBreakpoints();
        assert.isNotNull(component.contentElement);
        const renderedBreakpointItems = Array.from(component.contentElement.querySelectorAll(BREAKPOINT_ITEM_SELECTOR));
        const breakpointItems = extractBreakpointItems(data);
        assert.lengthOf(renderedBreakpointItems, breakpointItems.length);
        for (let i = 0; i < renderedBreakpointItems.length; ++i) {
            const renderedItem = renderedBreakpointItems[i];
            assert.instanceOf(renderedItem, HTMLDivElement);
            const inputElement = renderedItem.querySelector('input');
            assert.instanceOf(inputElement, HTMLInputElement);
            checkCheckboxState(inputElement, breakpointItems[i]);
        }
    });
    it('renders breakpoints with their code snippet', async () => {
        const { component, data } = await renderMultipleBreakpoints();
        assert.isNotNull(component.contentElement);
        const renderedBreakpointItems = Array.from(component.contentElement.querySelectorAll(BREAKPOINT_ITEM_SELECTOR));
        const breakpointItems = extractBreakpointItems(data);
        assert.lengthOf(renderedBreakpointItems, breakpointItems.length);
        for (let i = 0; i < renderedBreakpointItems.length; ++i) {
            const renderedBreakpointItem = renderedBreakpointItems[i];
            assert.instanceOf(renderedBreakpointItem, HTMLDivElement);
            checkCodeSnippet(renderedBreakpointItem, breakpointItems[i]);
        }
    });
    it('renders breakpoint groups with a differentiator if the file names are not unique', async () => {
        const component = await createAndInitializeBreakpointsView();
        const groupTemplate = {
            name: 'index.js',
            url: urlString ``,
            editable: true,
            expanded: true,
            breakpointItems: [
                {
                    id: '1',
                    type: "REGULAR_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.REGULAR_BREAKPOINT */,
                    location: '234',
                    codeSnippet: 'const a = x;',
                    isHit: false,
                    status: "ENABLED" /* Sources.BreakpointsView.BreakpointStatus.ENABLED */,
                },
            ],
        };
        // Create two groups with the same file name, but different url.
        const group1 = { ...groupTemplate };
        group1.url = urlString `https://google.com/lib/index.js`;
        const group2 = { ...groupTemplate };
        group2.url = urlString `https://google.com/src/index.js`;
        const data = {
            breakpointsActive: true,
            pauseOnUncaughtExceptions: false,
            pauseOnCaughtExceptions: false,
            groups: [
                group1,
                group2,
            ],
        };
        component.data = data;
        await RenderCoordinator.done();
        await component.updateComplete;
        assert.isNotNull(component.contentElement);
        const groupSummaries = Array.from(component.contentElement.querySelectorAll(SUMMARY_SELECTOR));
        const differentiatingPath = groupSummaries.map(group => {
            const differentiatorElement = group.querySelector(GROUP_DIFFERENTIATOR_SELECTOR);
            assert.instanceOf(differentiatorElement, HTMLSpanElement);
            return differentiatorElement.textContent;
        });
        assert.deepEqual(differentiatingPath, ['lib/', 'src/']);
    });
    it('renders breakpoints with a differentiating path', async () => {
        const { component, data } = await renderMultipleBreakpoints();
        assert.isNotNull(component.contentElement);
        const renderedBreakpointItems = Array.from(component.contentElement.querySelectorAll(BREAKPOINT_ITEM_SELECTOR));
        const breakpointItems = extractBreakpointItems(data);
        assert.lengthOf(renderedBreakpointItems, breakpointItems.length);
        for (let i = 0; i < renderedBreakpointItems.length; ++i) {
            const renderedBreakpointItem = renderedBreakpointItems[i];
            assert.instanceOf(renderedBreakpointItem, HTMLDivElement);
            const locationElement = renderedBreakpointItem.querySelector(BREAKPOINT_LOCATION_SELECTOR);
            assert.instanceOf(locationElement, HTMLSpanElement);
            const actualLocation = locationElement.textContent;
            const expectedLocation = breakpointItems[i].location;
            assert.strictEqual(actualLocation, expectedLocation);
        }
    });
    it('triggers an event on clicking the checkbox of a breakpoint', async () => {
        const { component, data } = await renderSingleBreakpoint();
        assert.isNotNull(component.contentElement);
        const renderedItem = component.contentElement.querySelector(BREAKPOINT_ITEM_SELECTOR);
        assert.instanceOf(renderedItem, HTMLDivElement);
        const checkbox = renderedItem.querySelector('input');
        assert.instanceOf(checkbox, HTMLInputElement);
        const checked = checkbox.checked;
        const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance();
        const breakpointStateChanged = sinon.stub(controller, 'breakpointStateChanged');
        checkbox.click();
        assert.isTrue(breakpointStateChanged.calledOnceWith(data.groups[0].breakpointItems[0], !checked));
    });
    it('triggers an event on clicking on the snippet text', async () => {
        const { component, data } = await renderSingleBreakpoint();
        assert.isNotNull(component.contentElement);
        const snippet = component.contentElement.querySelector(CODE_SNIPPET_SELECTOR);
        assert.instanceOf(snippet, HTMLSpanElement);
        const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance();
        const jumpToSource = sinon.stub(controller, 'jumpToSource');
        snippet.click();
        assert.isTrue(jumpToSource.calledOnceWith(data.groups[0].breakpointItems[0]));
    });
    it('triggers an event on expanding/unexpanding', async () => {
        const { component, data } = await renderSingleBreakpoint();
        assert.isNotNull(component.contentElement);
        const renderedGroupName = component.contentElement.querySelector(GROUP_NAME_SELECTOR);
        assert.instanceOf(renderedGroupName, HTMLSpanElement);
        const expandedInitialValue = data.groups[0].expanded;
        const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance();
        const expandedStateChanged = sinon.stub(controller, 'expandedStateChanged');
        renderedGroupName.click();
        await new Promise(resolve => setTimeout(resolve, 0));
        const group = data.groups[0];
        assert.isTrue(expandedStateChanged.calledOnceWith(group.url, group.expanded));
        assert.notStrictEqual(group.expanded, expandedInitialValue);
    });
    it('highlights breakpoint if it is set to be hit', async () => {
        const { component } = await renderSingleBreakpoint();
        assert.isNotNull(component.contentElement);
        const renderedBreakpointItem = component.contentElement.querySelector(HIT_BREAKPOINT_SELECTOR);
        assert.instanceOf(renderedBreakpointItem, HTMLDivElement);
    });
    it('triggers an event on removing file breakpoints', async () => {
        const { component, data } = await renderMultipleBreakpoints();
        assert.isNotNull(component.contentElement);
        await hover(component, SUMMARY_SELECTOR);
        const removeFileBreakpointsButton = component.contentElement.querySelector(REMOVE_FILE_BREAKPOINTS_SELECTOR);
        assert.instanceOf(removeFileBreakpointsButton, HTMLButtonElement);
        const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance();
        const breakpointsRemoved = sinon.stub(controller, 'breakpointsRemoved');
        removeFileBreakpointsButton.click();
        // await new Promise(resolve => setTimeout(resolve, 0));
        assert.isTrue(breakpointsRemoved.calledOnceWith(data.groups[0].breakpointItems));
    });
    it('triggers an event on removing one breakpoint', async () => {
        const { component, data } = await renderMultipleBreakpoints();
        assert.isNotNull(component.contentElement);
        await hover(component, BREAKPOINT_ITEM_SELECTOR);
        const removeFileBreakpointsButton = component.contentElement.querySelector(REMOVE_SINGLE_BREAKPOINT_SELECTOR);
        assert.instanceOf(removeFileBreakpointsButton, HTMLButtonElement);
        const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance();
        const breakpointsRemoved = sinon.stub(controller, 'breakpointsRemoved');
        removeFileBreakpointsButton.click();
        // await new Promise(resolve => setTimeout(resolve, 0));
        sinon.assert.calledOnce(breakpointsRemoved);
        assert.deepEqual(breakpointsRemoved.firstCall.firstArg, [data.groups[0].breakpointItems[0]]);
    });
    it('triggers an event on editing one breakpoint', async () => {
        const { component, data } = await renderMultipleBreakpoints();
        assert.isNotNull(component.contentElement);
        await hover(component, BREAKPOINT_ITEM_SELECTOR);
        const editBreakpointButton = component.contentElement.querySelector(EDIT_SINGLE_BREAKPOINT_SELECTOR);
        assert.instanceOf(editBreakpointButton, HTMLButtonElement);
        const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance();
        const breakpointEdited = sinon.stub(controller, 'breakpointEdited');
        editBreakpointButton.click();
        // await new Promise(resolve => setTimeout(resolve, 0));
        assert.isTrue(breakpointEdited.calledOnceWith(data.groups[0].breakpointItems[0], true));
    });
    it('shows a tooltip with edit condition on regular breakpoints', async () => {
        const { component } = await renderSingleBreakpoint("REGULAR_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.REGULAR_BREAKPOINT */);
        assert.isNotNull(component.contentElement);
        await hover(component, BREAKPOINT_ITEM_SELECTOR);
        const editBreakpointButton = component.contentElement.querySelector(EDIT_SINGLE_BREAKPOINT_SELECTOR);
        assert.instanceOf(editBreakpointButton, HTMLButtonElement);
        assert.strictEqual(editBreakpointButton.title, 'Edit condition');
    });
    describe('group checkboxes', () => {
        async function waitForCheckboxToggledEventsWithCheckedUpdate(numBreakpointItems, checked) {
            return await new Promise(resolve => {
                let numCheckboxToggledEvents = 0;
                const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance();
                sinon.stub(controller, 'breakpointStateChanged').callsFake((_, checkedArg) => {
                    assert.strictEqual(checkedArg, checked);
                    ++numCheckboxToggledEvents;
                    if (numCheckboxToggledEvents === numBreakpointItems) {
                        resolve();
                    }
                });
            });
        }
        it('show a checked group checkbox if at least one breakpoint in that group is enabled', async () => {
            const { component, data } = await renderMultipleBreakpoints();
            // Make sure that at least one breakpoint is enabled.
            data.groups[0].breakpointItems[0].status = "ENABLED" /* Sources.BreakpointsView.BreakpointStatus.ENABLED */;
            component.data = data;
            await RenderCoordinator.done();
            await component.updateComplete;
            await hover(component, SUMMARY_SELECTOR);
            assert.isNotNull(component.contentElement);
            const firstGroupSummary = component.contentElement.querySelector(SUMMARY_SELECTOR);
            assert.exists(firstGroupSummary);
            const groupCheckbox = firstGroupSummary.querySelector('input');
            assert.instanceOf(groupCheckbox, HTMLInputElement);
            assert.isTrue(groupCheckbox.checked);
        });
        it('show an unchecked group checkbox if no breakpoint in that group is enabled', async () => {
            const { component, data } = await renderMultipleBreakpoints();
            // Make sure that all breakpoints are disabled.
            const breakpointItems = data.groups[0].breakpointItems;
            for (let i = 0; i < breakpointItems.length; ++i) {
                breakpointItems[i].status = "DISABLED" /* Sources.BreakpointsView.BreakpointStatus.DISABLED */;
            }
            component.data = data;
            await RenderCoordinator.done();
            await component.updateComplete;
            await hover(component, SUMMARY_SELECTOR);
            assert.isNotNull(component.contentElement);
            const firstGroupSummary = component.contentElement.querySelector(SUMMARY_SELECTOR);
            assert.exists(firstGroupSummary);
            const groupCheckbox = firstGroupSummary.querySelector('input');
            assert.instanceOf(groupCheckbox, HTMLInputElement);
            assert.isFalse(groupCheckbox.checked);
        });
        it('disable all breakpoints on unchecking', async () => {
            const { component, data } = await renderMultipleBreakpoints();
            const numBreakpointItems = data.groups[0].breakpointItems.length;
            assert.isTrue(numBreakpointItems > 1);
            // Make sure that all breakpoints are enabled.
            for (let i = 0; i < numBreakpointItems; ++i) {
                data.groups[0].breakpointItems[i].status = "ENABLED" /* Sources.BreakpointsView.BreakpointStatus.ENABLED */;
            }
            component.data = data;
            await RenderCoordinator.done();
            await component.updateComplete;
            await hover(component, SUMMARY_SELECTOR);
            // Uncheck the group checkbox.
            assert.isNotNull(component.contentElement);
            const firstGroupSummary = component.contentElement.querySelector(SUMMARY_SELECTOR);
            assert.exists(firstGroupSummary);
            const groupCheckbox = firstGroupSummary.querySelector('input');
            assert.instanceOf(groupCheckbox, HTMLInputElement);
            // Wait until we receive all events fired that notify us of disabled breakpoints.
            const waitForEventPromise = waitForCheckboxToggledEventsWithCheckedUpdate(numBreakpointItems, false);
            groupCheckbox.click();
            await waitForEventPromise;
        });
        it('enable all breakpoints on unchecking', async () => {
            const { component, data } = await renderMultipleBreakpoints();
            const numBreakpointItems = data.groups[0].breakpointItems.length;
            assert.isTrue(numBreakpointItems > 1);
            // Make sure that all breakpoints are disabled.
            for (let i = 0; i < numBreakpointItems; ++i) {
                data.groups[0].breakpointItems[i].status = "DISABLED" /* Sources.BreakpointsView.BreakpointStatus.DISABLED */;
            }
            component.data = data;
            await RenderCoordinator.done();
            await component.updateComplete;
            await hover(component, SUMMARY_SELECTOR);
            // Uncheck the group checkbox.
            assert.isNotNull(component.contentElement);
            const firstGroupSummary = component.contentElement.querySelector(SUMMARY_SELECTOR);
            assert.exists(firstGroupSummary);
            const groupCheckbox = firstGroupSummary.querySelector('input');
            assert.instanceOf(groupCheckbox, HTMLInputElement);
            // Wait until we receive all events fired that notify us of enabled breakpoints.
            const waitForEventPromise = waitForCheckboxToggledEventsWithCheckedUpdate(numBreakpointItems, true);
            groupCheckbox.click();
            await waitForEventPromise;
        });
    });
    it('only renders edit button for breakpoints in editable groups', async () => {
        const component = await createAndInitializeBreakpointsView();
        const data = {
            breakpointsActive: true,
            pauseOnUncaughtExceptions: false,
            pauseOnCaughtExceptions: false,
            groups: [
                {
                    name: 'test1.js',
                    url: urlString `https://google.com/test1.js`,
                    editable: false,
                    expanded: true,
                    breakpointItems: [
                        {
                            id: '1',
                            location: '1',
                            codeSnippet: 'const a = 0;',
                            isHit: true,
                            status: "ENABLED" /* Sources.BreakpointsView.BreakpointStatus.ENABLED */,
                            type: "REGULAR_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.REGULAR_BREAKPOINT */,
                        },
                    ],
                },
            ],
        };
        component.data = data;
        await RenderCoordinator.done();
        assert.isNotNull(component.contentElement);
        await hover(component, BREAKPOINT_ITEM_SELECTOR);
        const editBreakpointButton = component.contentElement.querySelector(EDIT_SINGLE_BREAKPOINT_SELECTOR);
        assert.isNull(editBreakpointButton);
    });
    it('initializes data from the controller on construction', async () => {
        await setUpTestWithOneBreakpointLocation();
        const component = await createAndInitializeBreakpointsView();
        const renderedGroupName = component.contentElement?.querySelector(GROUP_NAME_SELECTOR);
        assert.strictEqual(renderedGroupName?.textContent, HELLO_JS_FILE);
    });
    describe('conditional breakpoints', () => {
        const breakpointDetails = 'x < a';
        it('are rendered', async () => {
            const { component } = await renderSingleBreakpoint("CONDITIONAL_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.CONDITIONAL_BREAKPOINT */, breakpointDetails);
            const breakpointItem = component.contentElement?.querySelector(BREAKPOINT_ITEM_SELECTOR);
            assert.exists(breakpointItem);
            assert.instanceOf(breakpointItem, HTMLDivElement);
            assert.isTrue(breakpointItem.classList.contains('conditional-breakpoint'));
        });
        it('show a tooltip', async () => {
            const { component } = await renderSingleBreakpoint("CONDITIONAL_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.CONDITIONAL_BREAKPOINT */, breakpointDetails);
            const codeSnippet = component.contentElement?.querySelector(CODE_SNIPPET_SELECTOR);
            assert.exists(codeSnippet);
            assert.instanceOf(codeSnippet, HTMLSpanElement);
            assert.strictEqual(codeSnippet.title, `Condition: ${breakpointDetails}`);
        });
        it('show a tooltip on editing the condition', async () => {
            const { component } = await renderSingleBreakpoint("CONDITIONAL_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.CONDITIONAL_BREAKPOINT */, breakpointDetails);
            assert.isNotNull(component.contentElement);
            await hover(component, BREAKPOINT_ITEM_SELECTOR);
            const editBreakpointButton = component.contentElement.querySelector(EDIT_SINGLE_BREAKPOINT_SELECTOR);
            assert.instanceOf(editBreakpointButton, HTMLButtonElement);
            assert.strictEqual(editBreakpointButton.title, 'Edit condition');
        });
    });
    describe('logpoints', () => {
        const breakpointDetails = 'x, a';
        it('are rendered', async () => {
            const { component } = await renderSingleBreakpoint("LOGPOINT" /* SDK.DebuggerModel.BreakpointType.LOGPOINT */, breakpointDetails);
            const breakpointItem = component.contentElement?.querySelector(BREAKPOINT_ITEM_SELECTOR);
            assert.exists(breakpointItem);
            assert.instanceOf(breakpointItem, HTMLDivElement);
            assert.isTrue(breakpointItem.classList.contains('logpoint'));
        });
        it('show a tooltip', async () => {
            const { component } = await renderSingleBreakpoint("LOGPOINT" /* SDK.DebuggerModel.BreakpointType.LOGPOINT */, breakpointDetails);
            const codeSnippet = component.contentElement?.querySelector(CODE_SNIPPET_SELECTOR);
            assert.exists(codeSnippet);
            assert.instanceOf(codeSnippet, HTMLSpanElement);
            assert.strictEqual(codeSnippet.title, `Logpoint: ${breakpointDetails}`);
        });
        it('show a tooltip on editing the logpoint', async () => {
            const { component } = await renderSingleBreakpoint("LOGPOINT" /* SDK.DebuggerModel.BreakpointType.LOGPOINT */, breakpointDetails);
            assert.isNotNull(component.contentElement);
            await hover(component, BREAKPOINT_ITEM_SELECTOR);
            const editBreakpointButton = component.contentElement.querySelector(EDIT_SINGLE_BREAKPOINT_SELECTOR);
            assert.instanceOf(editBreakpointButton, HTMLButtonElement);
            assert.strictEqual(editBreakpointButton.title, 'Edit logpoint');
        });
    });
    describe('pause on exceptions', () => {
        it('state is rendered correctly when disabled', async () => {
            const component = await renderNoBreakpoints({ pauseOnUncaughtExceptions: false, pauseOnCaughtExceptions: false });
            assert.isNotNull(component.contentElement);
            const pauseOnUncaughtExceptionsItem = component.contentElement.querySelector(PAUSE_ON_UNCAUGHT_EXCEPTIONS_SELECTOR);
            assert.exists(pauseOnUncaughtExceptionsItem);
            const pauseOnUncaughtExceptionsCheckbox = pauseOnUncaughtExceptionsItem.querySelector('input');
            assert.instanceOf(pauseOnUncaughtExceptionsCheckbox, HTMLInputElement);
            assert.isFalse(pauseOnUncaughtExceptionsCheckbox.checked);
            const pauseOnCaughtExceptionsItem = component.contentElement?.querySelector(PAUSE_ON_CAUGHT_EXCEPTIONS_SELECTOR);
            assert.exists(pauseOnCaughtExceptionsItem);
            const pauseOnCaughtExceptionsCheckbox = pauseOnUncaughtExceptionsItem.querySelector('input');
            assert.instanceOf(pauseOnCaughtExceptionsCheckbox, HTMLInputElement);
            assert.isFalse(pauseOnCaughtExceptionsCheckbox.checked);
        });
        it('state is rendered correctly when pausing on uncaught exceptions', async () => {
            const component = await renderNoBreakpoints({ pauseOnUncaughtExceptions: true, pauseOnCaughtExceptions: false });
            assert.isNotNull(component.contentElement);
            const pauseOnUncaughtExceptionsItem = component.contentElement.querySelector(PAUSE_ON_UNCAUGHT_EXCEPTIONS_SELECTOR);
            assert.exists(pauseOnUncaughtExceptionsItem);
            const pauseOnUncaughtExceptionsCheckbox = pauseOnUncaughtExceptionsItem.querySelector('input');
            assert.exists(pauseOnUncaughtExceptionsCheckbox);
            assert.instanceOf(pauseOnUncaughtExceptionsCheckbox, HTMLInputElement);
            assert.isTrue(pauseOnUncaughtExceptionsCheckbox.checked);
            const pauseOnCaughtExceptionsItem = component.contentElement?.querySelector(PAUSE_ON_CAUGHT_EXCEPTIONS_SELECTOR);
            assert.exists(pauseOnCaughtExceptionsItem);
            const pauseOnCaughtExceptionsCheckbox = pauseOnCaughtExceptionsItem.querySelector('input');
            assert.exists(pauseOnCaughtExceptionsCheckbox);
            assert.instanceOf(pauseOnCaughtExceptionsCheckbox, HTMLInputElement);
            assert.isFalse(pauseOnCaughtExceptionsCheckbox.checked);
        });
        it('state is rendered correctly when pausing on all exceptions', async () => {
            const component = await renderNoBreakpoints({ pauseOnUncaughtExceptions: true, pauseOnCaughtExceptions: true });
            assert.isNotNull(component.contentElement);
            const pauseOnUncaughtExceptionsItem = component.contentElement.querySelector(PAUSE_ON_UNCAUGHT_EXCEPTIONS_SELECTOR);
            assert.exists(pauseOnUncaughtExceptionsItem);
            const pauseOnUncaughtExceptionsCheckbox = pauseOnUncaughtExceptionsItem.querySelector('input');
            assert.exists(pauseOnUncaughtExceptionsCheckbox);
            assert.instanceOf(pauseOnUncaughtExceptionsCheckbox, HTMLInputElement);
            assert.isTrue(pauseOnUncaughtExceptionsCheckbox.checked);
            const pauseOnCaughtExceptionsItem = component.contentElement?.querySelector(PAUSE_ON_CAUGHT_EXCEPTIONS_SELECTOR);
            assert.exists(pauseOnCaughtExceptionsItem);
            const pauseOnCaughtExceptionsCheckbox = pauseOnCaughtExceptionsItem.querySelector('input');
            assert.exists(pauseOnCaughtExceptionsCheckbox);
            assert.instanceOf(pauseOnCaughtExceptionsCheckbox, HTMLInputElement);
            assert.isTrue(pauseOnCaughtExceptionsCheckbox.checked);
        });
        it('triggers an event when disabling pausing on all exceptions', async () => {
            const component = await renderNoBreakpoints({ pauseOnUncaughtExceptions: true, pauseOnCaughtExceptions: false });
            assert.isNotNull(component.contentElement);
            const item = component.contentElement.querySelector(PAUSE_ON_UNCAUGHT_EXCEPTIONS_SELECTOR);
            assert.exists(item);
            const checkbox = item.querySelector('input');
            assert.instanceOf(checkbox, HTMLInputElement);
            const { checked } = checkbox;
            const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance();
            const setPauseOnUncaughtExceptions = sinon.stub(controller, 'setPauseOnUncaughtExceptions');
            checkbox.click();
            assert.isTrue(setPauseOnUncaughtExceptions.calledOnceWith(!checked));
        });
        it('triggers an event when enabling pausing on caught exceptions', async () => {
            const component = await renderNoBreakpoints({ pauseOnUncaughtExceptions: true, pauseOnCaughtExceptions: false });
            assert.isNotNull(component.contentElement);
            const item = component.contentElement.querySelector(PAUSE_ON_CAUGHT_EXCEPTIONS_SELECTOR);
            assert.exists(item);
            const checkbox = item.querySelector('input');
            assert.instanceOf(checkbox, HTMLInputElement);
            const { checked } = checkbox;
            const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance();
            const setPauseOnCaughtExceptions = sinon.stub(controller, 'setPauseOnCaughtExceptions');
            checkbox.click();
            assert.isTrue(setPauseOnCaughtExceptions.calledOnceWith(!checked));
        });
        it('triggers an event when enabling pausing on uncaught exceptions', async () => {
            const component = await renderNoBreakpoints({ pauseOnUncaughtExceptions: false, pauseOnCaughtExceptions: true });
            assert.isNotNull(component.contentElement);
            const item = component.contentElement.querySelector(PAUSE_ON_UNCAUGHT_EXCEPTIONS_SELECTOR);
            assert.exists(item);
            const checkbox = item.querySelector('input');
            assert.instanceOf(checkbox, HTMLInputElement);
            const { checked } = checkbox;
            const controller = Sources.BreakpointsView.BreakpointsSidebarController.instance();
            const setPauseOnUncaughtExceptions = sinon.stub(controller, 'setPauseOnUncaughtExceptions');
            checkbox.click();
            assert.isTrue(setPauseOnUncaughtExceptions.calledOnceWith(!checked));
        });
    });
    describe('navigating with keyboard', () => {
        /** One expanded group with 2 breakpoints, and one collapsed with 2 breakpoints. **/
        async function renderBreakpointsForKeyboardNavigation() {
            const component = await createAndInitializeBreakpointsView();
            const data = {
                breakpointsActive: true,
                pauseOnUncaughtExceptions: false,
                pauseOnCaughtExceptions: false,
                groups: [
                    {
                        name: 'test1.js',
                        url: urlString `https://google.com/test1.js`,
                        editable: false,
                        expanded: true,
                        breakpointItems: [
                            {
                                id: '1',
                                type: "REGULAR_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.REGULAR_BREAKPOINT */,
                                location: '234',
                                codeSnippet: 'const a = x;',
                                isHit: false,
                                status: "ENABLED" /* Sources.BreakpointsView.BreakpointStatus.ENABLED */,
                            },
                            {
                                id: '2',
                                type: "REGULAR_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.REGULAR_BREAKPOINT */,
                                location: '3:3',
                                codeSnippet: 'if (x > a) {',
                                isHit: true,
                                status: "DISABLED" /* Sources.BreakpointsView.BreakpointStatus.DISABLED */,
                            },
                        ],
                    },
                    {
                        name: 'test2.js',
                        url: urlString `https://google.com/test2.js`,
                        editable: false,
                        expanded: false,
                        breakpointItems: [
                            {
                                id: '3',
                                type: "REGULAR_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.REGULAR_BREAKPOINT */,
                                location: '11',
                                codeSnippet: 'const y;',
                                isHit: false,
                                status: "ENABLED" /* Sources.BreakpointsView.BreakpointStatus.ENABLED */,
                            },
                            {
                                id: '4',
                                type: "REGULAR_BREAKPOINT" /* SDK.DebuggerModel.BreakpointType.REGULAR_BREAKPOINT */,
                                location: '12',
                                codeSnippet: 'const y;',
                                isHit: false,
                                status: "ENABLED" /* Sources.BreakpointsView.BreakpointStatus.ENABLED */,
                            },
                        ],
                    },
                ],
            };
            component.data = data;
            await RenderCoordinator.done();
            await component.updateComplete;
            return { component, data };
        }
        it('pause on exceptions is tabbable', async () => {
            const component = await renderNoBreakpoints({ pauseOnUncaughtExceptions: true, pauseOnCaughtExceptions: false });
            assert.isNotNull(component.contentElement);
            const focusableElements = component.contentElement.querySelectorAll(TABBABLE_SELECTOR);
            assert.lengthOf(focusableElements, 1);
            const pauseOnUncaughtExceptions = component.contentElement.querySelector(PAUSE_ON_UNCAUGHT_EXCEPTIONS_SELECTOR);
            assert.deepEqual(focusableElements[0], pauseOnUncaughtExceptions);
        });
        describe('pressing the HOME key', () => {
            it('takes the user to the pause-on-exceptions line', async () => {
                const { component } = await renderBreakpointsForKeyboardNavigation();
                assert.isNotNull(component.contentElement);
                const secondGroupsSummary = component.contentElement.querySelector(`${DETAILS_SELECTOR}:nth-of-type(2) > ${SUMMARY_SELECTOR}`);
                assert.instanceOf(secondGroupsSummary, HTMLElement);
                // Focus on second group by clicking on it, then press Home button.
                dispatchClickEvent(secondGroupsSummary);
                dispatchKeyDownEvent(secondGroupsSummary, { key: 'Home', bubbles: true });
                await RenderCoordinator.done();
                const selected = component.contentElement.querySelector(TABBABLE_SELECTOR);
                assert.instanceOf(selected, HTMLElement);
                const pauseOnUncaughtExceptions = component.contentElement.querySelector(PAUSE_ON_UNCAUGHT_EXCEPTIONS_SELECTOR);
                assert.instanceOf(pauseOnUncaughtExceptions, HTMLElement);
                assert.strictEqual(selected, pauseOnUncaughtExceptions);
            });
        });
        describe('pressing the END key', () => {
            it('takes the user to the summary node of the last group (if last group is collapsed)', async () => {
                const { component } = await renderBreakpointsForKeyboardNavigation();
                assert.isNotNull(component.contentElement);
                const pauseOnUncaughtExceptions = component.contentElement.querySelector(PAUSE_ON_UNCAUGHT_EXCEPTIONS_SELECTOR);
                assert.instanceOf(pauseOnUncaughtExceptions, HTMLElement);
                // Focus on the pause-on-exceptions line by clicking on it, then press End key.
                dispatchClickEvent(pauseOnUncaughtExceptions);
                dispatchKeyDownEvent(pauseOnUncaughtExceptions, { key: 'End', bubbles: true });
                await RenderCoordinator.done();
                const selected = component.contentElement.querySelector(TABBABLE_SELECTOR);
                assert.instanceOf(selected, HTMLElement);
                const lastGroupSummary = component.contentElement.querySelector(`${DETAILS_SELECTOR}:nth-of-type(2) > ${SUMMARY_SELECTOR}`);
                assert.instanceOf(lastGroupSummary, HTMLElement);
                assert.strictEqual(selected, lastGroupSummary);
            });
            it('takes the user to the last breakpoint item (if last group is expanded))', async () => {
                const { component, data } = await renderBreakpointsForKeyboardNavigation();
                // Expand the last group.
                data.groups[1].expanded = true;
                component.data = data;
                await RenderCoordinator.done();
                assert.isNotNull(component.contentElement);
                const firstGroupSummary = component.contentElement.querySelector(SUMMARY_SELECTOR);
                assert.instanceOf(firstGroupSummary, HTMLElement);
                // First focus on the first group by clicking on it, then press the End button.
                dispatchClickEvent(firstGroupSummary);
                dispatchKeyDownEvent(firstGroupSummary, { key: 'End', bubbles: true });
                await RenderCoordinator.done();
                const selected = component.contentElement.querySelector(TABBABLE_SELECTOR);
                assert.instanceOf(selected, HTMLElement);
                const breakpointItems = component.contentElement.querySelectorAll(BREAKPOINT_ITEM_SELECTOR);
                assertElements(breakpointItems, HTMLDivElement);
                const lastBreakpointItem = breakpointItems.item(breakpointItems.length - 1);
                assert.strictEqual(selected, lastBreakpointItem);
            });
        });
        describe('pressing the ArrowDown key', () => {
            it('on the pause-on-uncaught-exception takes the user to the summary node of the top most details element', async () => {
                const { component } = await renderBreakpointsForKeyboardNavigation();
                assert.isNotNull(component.contentElement);
                const pauseOnCaughtException = component.contentElement.querySelector(PAUSE_ON_CAUGHT_EXCEPTIONS_SELECTOR);
                assert.instanceOf(pauseOnCaughtException, HTMLElement);
                // Focus on the pause on exception, and navigate one down.
                dispatchClickEvent(pauseOnCaughtException);
                dispatchKeyDownEvent(pauseOnCaughtException, { key: 'ArrowDown', bubbles: true });
                await RenderCoordinator.done();
                const selected = component.contentElement.querySelector(TABBABLE_SELECTOR);
                const firstSummary = component.contentElement.querySelector(`${DETAILS_SELECTOR} > ${SUMMARY_SELECTOR}`);
                assert.instanceOf(firstSummary, HTMLElement);
                assert.strictEqual(selected, firstSummary);
            });
            it('on the summary node of an expanded group takes the user to the top most breakpoint item of that group', async () => {
                const { component } = await renderBreakpointsForKeyboardNavigation();
                assert.isNotNull(component.contentElement);
                const collapsedDetailsElement = component.contentElement.querySelector(COLLAPSED_GROUPS_SELECTOR);
                assert.instanceOf(collapsedDetailsElement, HTMLDetailsElement);
                const collapsedGroupSummary = collapsedDetailsElement.querySelector(SUMMARY_SELECTOR);
                assert.instanceOf(collapsedGroupSummary, HTMLElement);
                // Focus on the collapsed group and collapse it by clicking on it. Then navigate down.
                dispatchClickEvent(collapsedGroupSummary);
                dispatchKeyDownEvent(collapsedGroupSummary, { key: 'ArrowDown', bubbles: true });
                await RenderCoordinator.done();
                const selected = component.contentElement.querySelector(TABBABLE_SELECTOR);
                assert.instanceOf(selected, HTMLElement);
                const firstBreakpointItem = collapsedDetailsElement.querySelector(BREAKPOINT_ITEM_SELECTOR);
                assert.instanceOf(firstBreakpointItem, HTMLDivElement);
                assert.strictEqual(selected, firstBreakpointItem);
            });
            it('on the summary node of a collapsed group takes the user to the summary node of the next group', async () => {
                const { component } = await renderBreakpointsForKeyboardNavigation();
                assert.isNotNull(component.contentElement);
                const firstGroupSummary = component.contentElement.querySelector(`${DETAILS_SELECTOR}:nth-of-type(1) > ${SUMMARY_SELECTOR}`);
                assert.instanceOf(firstGroupSummary, HTMLElement);
                // Focus on the expanded group and collapse it by clicking on it. Then navigate down.
                dispatchClickEvent(firstGroupSummary);
                dispatchKeyDownEvent(firstGroupSummary, { key: 'ArrowDown', bubbles: true });
                await RenderCoordinator.done();
                const selected = component.contentElement.querySelector(TABBABLE_SELECTOR);
                assert.instanceOf(selected, HTMLElement);
                const secondGroupSummary = component.contentElement.querySelector(`${DETAILS_SELECTOR}:nth-of-type(2) > ${SUMMARY_SELECTOR}`);
                assert.instanceOf(secondGroupSummary, HTMLElement);
                assert.strictEqual(selected, secondGroupSummary);
            });
            it('on a breakpoint item takes the user to the next breakpoint item', async () => {
                const { component } = await renderBreakpointsForKeyboardNavigation();
                assert.isNotNull(component.contentElement);
                const firstDetailsElement = component.contentElement.querySelector('details');
                assert.instanceOf(firstDetailsElement, HTMLDetailsElement);
                const firstBreakpointItem = firstDetailsElement.querySelector(BREAKPOINT_ITEM_SELECTOR);
                assert.instanceOf(firstBreakpointItem, HTMLDivElement);
                // Focus on the first breakpoint item. Then navigate up.
                dispatchClickEvent(firstBreakpointItem);
                dispatchKeyDownEvent(firstBreakpointItem, { key: 'ArrowDown', bubbles: true });
                await RenderCoordinator.done();
                const selected = component.contentElement.querySelector(TABBABLE_SELECTOR);
                assert.instanceOf(selected, HTMLElement);
                const secondBreakpointItem = firstDetailsElement.querySelector(`${BREAKPOINT_ITEM_SELECTOR}:nth-of-type(2)`);
                assert.instanceOf(secondBreakpointItem, HTMLDivElement);
                assert.strictEqual(selected, secondBreakpointItem);
            });
        });
        describe('pressing the ArrowUp key', () => {
            it('on the first summary takes a user to the pause on exceptions', async () => {
                const { component } = await renderBreakpointsForKeyboardNavigation();
                assert.isNotNull(component.contentElement);
                const firstSummary = component.contentElement.querySelector(`${DETAILS_SELECTOR} > ${SUMMARY_SELECTOR}`);
                assert.instanceOf(firstSummary, HTMLElement);
                // Focus on the summary element.
                dispatchClickEvent(firstSummary);
                dispatchKeyDownEvent(firstSummary, { key: 'ArrowUp', bubbles: true });
                await RenderCoordinator.done();
                const selected = component.contentElement.querySelector(TABBABLE_SELECTOR);
                const pauseOnUncaughtExceptions = component.contentElement.querySelector(PAUSE_ON_CAUGHT_EXCEPTIONS_SELECTOR);
                assert.instanceOf(pauseOnUncaughtExceptions, HTMLDivElement);
                assert.strictEqual(selected, pauseOnUncaughtExceptions);
            });
            it('on the first breakpoint item in an expanded group takes the user to the summary node', async () => {
                const { component } = await renderBreakpointsForKeyboardNavigation();
                assert.isNotNull(component.contentElement);
                const expandedDetails = component.contentElement.querySelector(EXPANDED_GROUPS_SELECTOR);
                assert.instanceOf(expandedDetails, HTMLDetailsElement);
                const firstBreakpointItem = expandedDetails.querySelector(BREAKPOINT_ITEM_SELECTOR);
                assert.instanceOf(firstBreakpointItem, HTMLDivElement);
                // Focus on first breakpoint item. Then navigate up.
                dispatchClickEvent(firstBreakpointItem);
                dispatchKeyDownEvent(firstBreakpointItem, { key: 'ArrowUp', bubbles: true });
                await RenderCoordinator.done();
                const selected = component.contentElement.querySelector(TABBABLE_SELECTOR);
                assert.instanceOf(selected, HTMLElement);
                const summary = expandedDetails.querySelector(SUMMARY_SELECTOR);
                assert.instanceOf(summary, HTMLElement);
                assert.strictEqual(selected, summary);
            });
            it('on a breakpoint item in an expanded group takes the user to the previous breakpoint item', async () => {
                const { component } = await renderBreakpointsForKeyboardNavigation();
                assert.isNotNull(component.contentElement);
                const expandedDetails = component.contentElement.querySelector(EXPANDED_GROUPS_SELECTOR);
                assert.instanceOf(expandedDetails, HTMLDetailsElement);
                const breakpointItems = expandedDetails.querySelectorAll(BREAKPOINT_ITEM_SELECTOR);
                assert.isAbove(breakpointItems.length, 1);
                const lastBreakpointItem = breakpointItems.item(breakpointItems.length - 1);
                // Focus on last breakpoint item. Then navigate up.
                dispatchClickEvent(lastBreakpointItem);
                dispatchKeyDownEvent(lastBreakpointItem, { key: 'ArrowUp', bubbles: true });
                await RenderCoordinator.done();
                const selected = component.contentElement.querySelector(TABBABLE_SELECTOR);
                assert.instanceOf(selected, HTMLElement);
                const nextToLastBreakpointItem = breakpointItems.item(breakpointItems.length - 2);
                assert.instanceOf(nextToLastBreakpointItem, HTMLDivElement);
                assert.strictEqual(selected, nextToLastBreakpointItem);
            });
            it('on a summary node takes the user to the last breakpoint item of the previous group', async () => {
                const { component } = await renderBreakpointsForKeyboardNavigation();
                assert.isNotNull(component.contentElement);
                const secondGroupSummary = component.contentElement.querySelector(`${DETAILS_SELECTOR}:nth-of-type(2) > ${SUMMARY_SELECTOR}`);
                assert.instanceOf(secondGroupSummary, HTMLElement);
                // Focus on the group. Then navigate up.
                dispatchClickEvent(secondGroupSummary);
                dispatchKeyDownEvent(secondGroupSummary, { key: 'ArrowUp', bubbles: true });
                await RenderCoordinator.done();
                const selected = component.contentElement.querySelector(TABBABLE_SELECTOR);
                assert.instanceOf(selected, HTMLElement);
                const firstDetailsElement = component.contentElement.querySelector(DETAILS_SELECTOR);
                assert.exists(firstDetailsElement);
                const lastBreakpointItem = firstDetailsElement.querySelector(`${BREAKPOINT_ITEM_SELECTOR}:last-child`);
                assert.instanceOf(lastBreakpointItem, HTMLDivElement);
                assert.strictEqual(selected, lastBreakpointItem);
            });
        });
    });
});
//# sourceMappingURL=BreakpointsView.test.js.map