// 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 Platform from '../../core/platform/platform.js';
import * as SDK from '../../core/sdk/sdk.js';
import { createTarget, describeWithEnvironment } from '../../testing/EnvironmentHelpers.js';
import { MockDebuggerBackend } from '../../testing/MockScopeChain.js';
import { setMockResourceTree } from '../../testing/ResourceTreeHelpers.js';
import { loadBasicSourceMapExample } from '../../testing/SourceMapHelpers.js';
import { protocolCallFrame, stringifyStackTrace } from '../../testing/StackTraceHelpers.js';
import * as Workspace from '../workspace/workspace.js';
import * as Bindings from './bindings.js';
const { urlString } = Platform.DevToolsPath;
describeWithEnvironment('DebuggerWorkspaceBinding', () => {
    let debuggerWorkspaceBinding;
    beforeEach(() => {
        const targetManager = SDK.TargetManager.TargetManager.instance();
        const workspace = Workspace.Workspace.WorkspaceImpl.instance();
        const resourceMapping = new Bindings.ResourceMapping.ResourceMapping(targetManager, workspace);
        const ignoreListManager = Workspace.IgnoreListManager.IgnoreListManager.instance({ forceNew: true });
        debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance({
            forceNew: true,
            resourceMapping,
            targetManager,
            ignoreListManager,
            workspace,
        });
        setMockResourceTree(false);
    });
    it('can wait for a uiSourceCode if it is not yet available', async () => {
        const backend = new MockDebuggerBackend();
        debuggerWorkspaceBinding = backend.universe.debuggerWorkspaceBinding;
        const target = backend.createTarget({ id: 'main', name: 'main', type: SDK.Target.Type.FRAME });
        SDK.TargetManager.TargetManager.instance().setScopeTarget(target);
        const scriptUrl = urlString `http://script-host/script.js`;
        const scriptInfo = { url: scriptUrl, content: 'console.log(1);', startLine: 0, startColumn: 0, hasSourceURL: false };
        // Create a second target.
        const workerTarget = backend.createTarget({
            id: 'worker',
            name: 'worker',
            type: SDK.Target.Type.ServiceWorker,
            parentTarget: target,
        });
        // Before any script is registered, there shouldn't be any uiSourceCodes.
        assert.isNull(backend.universe.workspace.uiSourceCodeForURL(scriptUrl));
        // Create promise to await the uiSourceCode given the url and its target.
        const uiSourceCodePromise = debuggerWorkspaceBinding.waitForUISourceCodeAdded(scriptUrl, target);
        // Register the script, which will kick off creating the uiSourceCode.
        await backend.addScript(target, scriptInfo, null);
        await backend.addScript(workerTarget, scriptInfo, null);
        // Await the promise to retrieve the uiSourceCode.
        const uiSourceCode = await uiSourceCodePromise;
        // Check if the uiSourceCode is the expected one (from the main target, and having the correct sourceURL).
        assert.strictEqual(uiSourceCode.url(), scriptUrl);
        assert.deepEqual(Bindings.NetworkProject.NetworkProject.targetForUISourceCode(uiSourceCode), target);
    });
    it('augments sourcemap with scopes via DebuggerWorkspaceBindings.setFunctionRanges', async () => {
        const target = createTarget({ id: 'main', name: 'main', type: SDK.Target.Type.FRAME });
        const validFunctionRanges = [{ start: { line: 0, column: 0 }, end: { line: 10, column: 1 }, name: 'foo' }];
        const debuggerModel = target.model(SDK.DebuggerModel.DebuggerModel);
        assert.exists(debuggerModel);
        const script = (await loadBasicSourceMapExample(target)).script;
        const sourceMap = await debuggerModel.sourceMapManager().sourceMapForClientPromise(script);
        assert.exists(sourceMap);
        await sourceMap.waitForScopeInfo();
        assert.strictEqual(sourceMap.url(), 'file://gen.js.map/');
        const uiSourceCodeForSourceMap = Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(sourceMap.sourceURLs()[0]);
        assert.exists(uiSourceCodeForSourceMap);
        Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().setFunctionRanges(uiSourceCodeForSourceMap, validFunctionRanges);
        assert.isTrue(sourceMap.hasScopeInfo());
        assert.strictEqual(sourceMap.findOriginalFunctionName({ line: 0, column: 110 }), 'foo');
    });
    describe('createStackTraceFromProtocolRuntime', () => {
        it('identity translates frames by default', async () => {
            const target = createTarget({ id: 'main', name: 'main', type: SDK.Target.Type.FRAME });
            const stackTrace = await debuggerWorkspaceBinding.createStackTraceFromProtocolRuntime({
                callFrames: [
                    'foo.js:1:foo:1:10',
                    'bar.js:2:bar:2:20',
                    'baz.js:3:baz:3:30',
                ].map(protocolCallFrame),
            }, target);
            assert.strictEqual(stringifyStackTrace(stackTrace), [
                'at foo (foo.js:1:10)',
                'at bar (bar.js:2:20)',
                'at baz (baz.js:3:30)',
            ].join('\n'));
        });
        it('identity translates frames for disposed targets (no ModelData instance)', async () => {
            const target = createTarget({ id: 'main', name: 'main', type: SDK.Target.Type.FRAME });
            target.dispose('disposed for testing');
            const stackTrace = await debuggerWorkspaceBinding.createStackTraceFromProtocolRuntime({
                callFrames: [
                    'foo.js:1:foo:1:10',
                    'bar.js:2:bar:2:20',
                    'baz.js:3:baz:3:30',
                ].map(protocolCallFrame),
            }, target);
            assert.strictEqual(stringifyStackTrace(stackTrace), [
                'at foo (foo.js:1:10)',
                'at bar (bar.js:2:20)',
                'at baz (baz.js:3:30)',
            ].join('\n'));
        });
        it('calls the debugger language plugin', async () => {
            const target = createTarget({ id: 'main', name: 'main', type: SDK.Target.Type.FRAME });
            const spy = sinon.spy(debuggerWorkspaceBinding.pluginManager, 'translateRawFramesStep');
            await debuggerWorkspaceBinding.createStackTraceFromProtocolRuntime({
                callFrames: [
                    'foo.js:1:foo:1:10',
                    'bar.js:2:bar:2:20',
                    'baz.js:3:baz:3:30',
                ].map(protocolCallFrame),
            }, target);
            sinon.assert.calledThrice(spy);
        });
        it('translates source location via the fallback script mapping', async () => {
            const backend = new MockDebuggerBackend();
            debuggerWorkspaceBinding = backend.universe.debuggerWorkspaceBinding;
            const target = backend.createTarget({ id: 'main', name: 'main', type: SDK.Target.Type.FRAME });
            const script = await backend.addScript(target, {
                url: Platform.DevToolsPath.urlString `http://example.com/foo.js`,
                content: '// content omitted as its not required',
            }, null);
            const uiSourceCode = debuggerWorkspaceBinding.uiSourceCodeForScript(script);
            assert.exists(uiSourceCode);
            const stackTrace = await debuggerWorkspaceBinding.createStackTraceFromProtocolRuntime({
                callFrames: [
                    `${script.contentURL()}:${script.scriptId}:foo:1:10`,
                    `${script.contentURL()}:${script.scriptId}:bar:2:20`,
                    `${script.contentURL()}:${script.scriptId}:baz:3:30`,
                ].map(protocolCallFrame),
            }, target);
            assert.strictEqual(stringifyStackTrace(stackTrace), [
                'at foo (foo.js:1:10)',
                'at bar (foo.js:2:20)',
                'at baz (foo.js:3:30)',
            ].join('\n'));
            assert.strictEqual(stackTrace.syncFragment.frames[0].uiSourceCode, uiSourceCode);
            assert.strictEqual(stackTrace.syncFragment.frames[1].uiSourceCode, uiSourceCode);
            assert.strictEqual(stackTrace.syncFragment.frames[2].uiSourceCode, uiSourceCode);
        });
    });
});
//# sourceMappingURL=DebuggerWorkspaceBinding.test.js.map