// Copyright 2024 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 * as Trace from '../../models/trace/trace.js';
import { describeWithEnvironment } from '../../testing/EnvironmentHelpers.js';
import { allThreadEntriesInTrace } from '../../testing/TraceHelpers.js';
import { TraceLoader } from '../../testing/TraceLoader.js';
import * as Timeline from './timeline.js';
describeWithEnvironment('ModificationsManager', () => {
    it('applies modifications when present in a trace file', async function () {
        await TraceLoader.traceEngine(this, 'web-dev-modifications.json.gz');
        const modificationsManager = Timeline.ModificationsManager.ModificationsManager.activeManager();
        if (!modificationsManager) {
            throw new Error('Modifications manager does not exist.');
        }
        const entriesFilter = modificationsManager.getEntriesFilter();
        assert.lengthOf(entriesFilter.expandableEntries(), 1);
        assert.lengthOf(entriesFilter.invisibleEntries(), 108);
        assert.deepEqual(modificationsManager.getTimelineBreadcrumbs().initialBreadcrumb, {
            window: { min: 1020034823047, max: 1020036087961, range: 1264914 },
            child: { window: { min: 1020034823047, max: 1020035228006.5569, range: 404959.5568847656 }, child: null },
        });
        // Make sure the saved Label Annotation is applied
        const labelAnnotation = modificationsManager.getAnnotations()[0];
        const label = (labelAnnotation.type === 'ENTRY_LABEL') ? labelAnnotation.label : '';
        assert.deepEqual(labelAnnotation.type, 'ENTRY_LABEL');
        assert.deepEqual(label, 'Initialize App');
        // Make sure the saved Range Annotation is applied
        const timeRangeAnnotation = modificationsManager.getAnnotations()[1];
        const rangeLabel = (timeRangeAnnotation.type === 'TIME_RANGE') ? timeRangeAnnotation.label : '';
        assert.deepEqual(modificationsManager.getAnnotations()[1].type, 'TIME_RANGE');
        assert.deepEqual(rangeLabel, 'Visibility change 1');
    });
    it('generates a serializable modifications json ', async function () {
        await TraceLoader.traceEngine(this, 'web-dev-modifications.json.gz');
        const modificationsManager = Timeline.ModificationsManager.ModificationsManager.activeManager();
        if (!modificationsManager) {
            throw new Error('Modifications manager does not exist.');
        }
        const entriesFilter = modificationsManager.getEntriesFilter();
        const modifications = modificationsManager.toJSON();
        assert.lengthOf(entriesFilter.expandableEntries(), 1);
        assert.lengthOf(modifications.entriesModifications.expandableEntries, 1);
        assert.lengthOf(modifications.entriesModifications.hiddenEntries, 108);
        assert.deepEqual(modifications.initialBreadcrumb, {
            window: { min: 1020034823047, max: 1020036087961, range: 1264914 },
            child: { window: { min: 1020034823047, max: 1020035228006.5569, range: 404959.5568847656 }, child: null },
        });
        assert.deepEqual(modifications.annotations.entryLabels, [
            { entry: 'p-73704-775-2151-457', label: 'Initialize App' },
        ]);
        assert.deepEqual(modifications.annotations.labelledTimeRanges, [
            {
                bounds: { min: 1020034870460.4769, max: 1020034880507.9258, range: 10047.448852539062 },
                label: 'Visibility change 1',
            },
        ]);
    });
    it('creates annotations and generates correct json for annotations', async function () {
        const parsedTrace = await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz');
        // Get any entres to create a label and a link with.
        const entry = allThreadEntriesInTrace(parsedTrace)[0];
        const entry2 = allThreadEntriesInTrace(parsedTrace)[1];
        const modificationsManager = Timeline.ModificationsManager.ModificationsManager.activeManager();
        assert.isOk(modificationsManager);
        modificationsManager.createAnnotation({
            type: 'ENTRY_LABEL',
            entry,
            label: 'entry label',
        }, { loadedFromFile: false, muteAriaNotifications: false });
        modificationsManager.createAnnotation({
            type: 'ENTRIES_LINK',
            state: "connected" /* Trace.Types.File.EntriesLinkState.CONNECTED */,
            entryFrom: entry,
            entryTo: entry2,
        }, { loadedFromFile: false, muteAriaNotifications: false });
        modificationsManager.createAnnotation({
            type: 'TIME_RANGE',
            bounds: {
                min: Trace.Types.Timing.Micro(0),
                max: Trace.Types.Timing.Micro(10),
                range: Trace.Types.Timing.Micro(10),
            },
            label: 'range label',
        }, { loadedFromFile: false, muteAriaNotifications: false });
        const modifications = modificationsManager.toJSON().annotations;
        assert.deepEqual(modifications, {
            entryLabels: [{
                    entry: 'r-39',
                    label: 'entry label',
                }],
            labelledTimeRanges: [{
                    bounds: {
                        min: Trace.Types.Timing.Micro(0),
                        max: Trace.Types.Timing.Micro(10),
                        range: Trace.Types.Timing.Micro(10),
                    },
                    label: 'range label',
                }],
            linksBetweenEntries: [{
                    entryFrom: 'r-39',
                    entryTo: 'r-42',
                }],
        });
    });
    it('does not add the annotation link between entries into the json saved into metadata if `entryTo` does not exist', async function () {
        const parsedTrace = await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz');
        // Get any entry to create links with.
        const entry = allThreadEntriesInTrace(parsedTrace)[0];
        const entry2 = allThreadEntriesInTrace(parsedTrace)[1];
        const modificationsManager = Timeline.ModificationsManager.ModificationsManager.activeManager();
        assert.isOk(modificationsManager);
        modificationsManager.createAnnotation({
            type: 'ENTRIES_LINK',
            state: "connected" /* Trace.Types.File.EntriesLinkState.CONNECTED */,
            entryFrom: entry,
            entryTo: entry2,
        }, { loadedFromFile: false, muteAriaNotifications: false });
        modificationsManager.createAnnotation({
            type: 'ENTRIES_LINK',
            state: "pending_to_event" /* Trace.Types.File.EntriesLinkState.PENDING_TO_EVENT */,
            entryFrom: entry2,
        }, { loadedFromFile: false, muteAriaNotifications: false });
        // Make sure only the link with both 'to' and 'from' entries in in the generated JSON
        const modifications = modificationsManager.toJSON().annotations;
        assert.deepEqual(modifications, {
            entryLabels: [],
            labelledTimeRanges: [],
            linksBetweenEntries: [{
                    entryFrom: 'r-39',
                    entryTo: 'r-42',
                }],
        });
    });
    it('correctly identifies if a connection between entries already exists', async function () {
        const parsedTrace = await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz');
        // Get any entry to create links with.
        const entry1 = allThreadEntriesInTrace(parsedTrace)[0];
        const entry2 = allThreadEntriesInTrace(parsedTrace)[1];
        const entry3 = allThreadEntriesInTrace(parsedTrace)[2];
        const modificationsManager = Timeline.ModificationsManager.ModificationsManager.activeManager();
        assert.isOk(modificationsManager);
        // Create a connection between entry 1 and entry 2
        modificationsManager.createAnnotation({
            type: 'ENTRIES_LINK',
            state: "connected" /* Trace.Types.File.EntriesLinkState.CONNECTED */,
            entryFrom: entry1,
            entryTo: entry2,
        }, { loadedFromFile: false, muteAriaNotifications: false });
        // Chech if a connection between entries 1 and 3 exists
        const existsBetween1And3 = modificationsManager.linkAnnotationBetweenEntriesExists(entry1, entry3);
        // Make sure the link does not exists
        assert.isFalse(existsBetween1And3);
        // Chech if a connection between entries 1 and 2 exists
        const existsBetween1And2 = modificationsManager.linkAnnotationBetweenEntriesExists(entry1, entry2);
        // Make sure the link exists
        assert.isTrue(existsBetween1And2);
        // Chech if a connection between entries 2 and 1 exists. It should since the order of entries does not matter.
        const existsBetween2And1 = modificationsManager.linkAnnotationBetweenEntriesExists(entry1, entry2);
        // Make sure the link exists
        assert.isTrue(existsBetween2And1);
    });
    it('deletes time ranges with an empty label from the annotations list', async function () {
        await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz');
        const modificationsManager = Timeline.ModificationsManager.ModificationsManager.activeManager();
        assert.isOk(modificationsManager);
        modificationsManager.createAnnotation({
            type: 'TIME_RANGE',
            bounds: {
                min: Trace.Types.Timing.Micro(0),
                max: Trace.Types.Timing.Micro(10),
                range: Trace.Types.Timing.Micro(10),
            },
            label: 'label',
        }, { loadedFromFile: false, muteAriaNotifications: false });
        // Create time range with empty label that shoud be removed
        modificationsManager.createAnnotation({
            type: 'TIME_RANGE',
            bounds: {
                min: Trace.Types.Timing.Micro(3),
                max: Trace.Types.Timing.Micro(10),
                range: Trace.Types.Timing.Micro(7),
            },
            label: '',
        }, { loadedFromFile: false, muteAriaNotifications: false });
        // Create time range with empty label that shoud be removed
        modificationsManager.createAnnotation({
            type: 'TIME_RANGE',
            bounds: {
                min: Trace.Types.Timing.Micro(5),
                max: Trace.Types.Timing.Micro(10),
                range: Trace.Types.Timing.Micro(5),
            },
            label: '',
        }, { loadedFromFile: false, muteAriaNotifications: false });
        modificationsManager.deleteEmptyRangeAnnotations();
        const modifications = modificationsManager.toJSON().annotations;
        // Make sure that the annotations with an empty label were deleted
        assert.deepEqual(modifications.labelledTimeRanges, [{
                bounds: {
                    min: Trace.Types.Timing.Micro(0),
                    max: Trace.Types.Timing.Micro(10),
                    range: Trace.Types.Timing.Micro(10),
                },
                label: 'label',
            }]);
    });
});
//# sourceMappingURL=ModificationsManager.test.js.map