// Copyright 2023 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 { assertScreenshot, renderElementIntoDOM } from '../../testing/DOMHelpers.js';
import { describeWithEnvironment } from '../../testing/EnvironmentHelpers.js';
import * as ObjectUI from '../../ui/legacy/components/object_ui/object_ui.js';
import * as UI from '../../ui/legacy/legacy.js';
import { render } from '../../ui/lit/lit.js';
import * as Network from './network.js';
const { urlString } = Platform.DevToolsPath;
describeWithEnvironment('RequestPayloadView', () => {
    it('decodes headers', async () => {
        const encoded = 'Test+%21%40%23%24%25%5E%26*%28%29_%2B+parameters.';
        const parameterElement = document.createDocumentFragment();
        render(Network.RequestPayloadView.RequestPayloadView.formatParameter(encoded, '', true), parameterElement);
        assert.strictEqual(parameterElement.textContent?.trim(), 'Test !@#$%^&*()_+ parameters.');
    });
    it('does not decode headers if decodeParameters is false', async () => {
        const encoded = 'Test+%21%40%23%24%25%5E%26*%28%29_%2B+parameters.';
        const parameterElement = document.createDocumentFragment();
        render(Network.RequestPayloadView.RequestPayloadView.formatParameter(encoded, '', false), parameterElement);
        assert.strictEqual(parameterElement.textContent?.trim(), encoded);
    });
    it('adds the class name to the element', async () => {
        const parameterElement = document.createDocumentFragment();
        render(Network.RequestPayloadView.RequestPayloadView.formatParameter('test', 'test-class', true), parameterElement);
        const div = parameterElement.firstElementChild;
        assert.isNotNull(div);
        assert.isTrue(div?.classList.contains('test-class'));
    });
    it('adds the empty-value class when value is empty', async () => {
        const parameterElement = document.createDocumentFragment();
        render(Network.RequestPayloadView.RequestPayloadView.formatParameter('', '', true), parameterElement);
        const div = parameterElement.firstElementChild;
        assert.isNotNull(div);
        assert.isTrue(div?.classList.contains('empty-value'));
    });
    it('shows error message when decoding fails', async () => {
        const invalidEncoded = '%E0%A4%A'; // Invalid URI sequence
        const parameterElement = document.createDocumentFragment();
        render(Network.RequestPayloadView.RequestPayloadView.formatParameter(invalidEncoded, '', true), parameterElement);
        const errorSpan = parameterElement.querySelector('.payload-decode-error');
        assert.isNotNull(errorSpan);
        assert.strictEqual(errorSpan?.textContent, '(unable to decode value)');
    });
    it('displays query string parameters', async () => {
        const request = SDK.NetworkRequest.NetworkRequest.create('requestId', urlString `https://example.com/api?foo=bar&baz=qux`, urlString ``, null, null, null);
        const view = new Network.RequestPayloadView.RequestPayloadView();
        view.request = request;
        renderElementIntoDOM(view, { includeCommonStyles: true });
        view.wasShown();
        await assertScreenshot('network/request-payload-query-params.png');
    });
    it('displays form data parameters', async () => {
        const request = SDK.NetworkRequest.NetworkRequest.create('requestId', urlString `https://example.com/api`, urlString ``, null, null, null);
        request.setRequestHeaders([{ name: 'Content-Type', value: 'application/x-www-form-urlencoded' }]);
        // Mock requestFormData to return URL-encoded form data.
        sinon.stub(request, 'requestFormData').resolves('foo=bar&baz=qux');
        const view = new Network.RequestPayloadView.RequestPayloadView();
        view.request = request;
        renderElementIntoDOM(view, { includeCommonStyles: true });
        view.wasShown();
        await view.updateComplete;
        await assertScreenshot('network/request-payload-data-params.png');
    });
    it('toggles URL decoding', async () => {
        const request = SDK.NetworkRequest.NetworkRequest.create('requestId', urlString `https://example.com/api?foo=bar%20baz`, urlString ``, null, null, null);
        const view = new Network.RequestPayloadView.RequestPayloadView();
        view.request = request;
        renderElementIntoDOM(view, { includeCommonStyles: true });
        view.wasShown();
        await view.updateComplete;
        const treeOutline = view.element.querySelector('.request-payload-tree');
        assert.isNotNull(treeOutline);
        const shadowRoot = treeOutline.shadowRoot;
        assert.isNotNull(shadowRoot);
        const getPayloadValues = () => {
            return Array.from(shadowRoot.querySelectorAll('.payload-value')).map(el => el.textContent).join(' ');
        };
        // Initial state: Decoded
        assert.include(getPayloadValues(), 'bar baz');
        const toggleButton = shadowRoot.querySelectorAll('.payload-toggle').item(1);
        assert.exists(toggleButton);
        toggleButton.click();
        await view.updateComplete;
        // Take the screenshot before checking contents, this forces the widget to render.
        await assertScreenshot('network/request-payload-url-decoding.png');
        // Toggled state: Encoded
        assert.include(getPayloadValues(), 'bar%20baz');
    });
    it('toggles between parsed and source view', async () => {
        const request = SDK.NetworkRequest.NetworkRequest.create('requestId', urlString `https://example.com/api?foo=bar`, urlString ``, null, null, null);
        const view = new Network.RequestPayloadView.RequestPayloadView();
        view.request = request;
        renderElementIntoDOM(view, { includeCommonStyles: true });
        view.wasShown();
        await view.updateComplete;
        const treeOutline = view.element.querySelector('.request-payload-tree');
        const shadowRoot = treeOutline?.shadowRoot;
        assert.exists(shadowRoot);
        const getTextContent = () => {
            const names = Array.from(shadowRoot.querySelectorAll('.payload-name')).map(el => el.textContent);
            const values = Array.from(shadowRoot.querySelectorAll('.payload-value')).map(el => el.textContent);
            const widgets = Array.from(shadowRoot.querySelectorAll('devtools-widget')).map(el => el.textContent);
            return [...names, ...values, ...widgets].join(' ');
        };
        // Initial state: Parsed (foo: bar)
        const initialText = getTextContent();
        assert.include(initialText, 'foo');
        assert.include(initialText, 'bar');
        // Find "View source" button.
        const buttons = shadowRoot.querySelectorAll('.payload-toggle');
        const viewSourceButton = Array.from(buttons).find(b => b.textContent?.includes('View source'));
        assert.exists(viewSourceButton);
        viewSourceButton.click();
        await view.updateComplete;
        await assertScreenshot('network/request-payload-url-source-view.png');
        // Source state: "foo=bar"
        const sourceText = getTextContent();
        assert.include(sourceText, 'foo=bar');
        // Toggle back
        const viewParsedButton = Array.from(shadowRoot.querySelectorAll('.payload-toggle'))
            .find(b => b.textContent?.includes('View parsed'));
        assert.exists(viewParsedButton);
        viewParsedButton.click();
        await view.updateComplete;
        const finalText = getTextContent();
        assert.include(finalText, 'foo');
        assert.include(finalText, 'bar');
    });
    it('truncates long source text and in a ShowMore widget', async () => {
        const text = 'A'.repeat(3010);
        const request = SDK.NetworkRequest.NetworkRequest.create('requestId', urlString `https://example.com/api?foo=${text}`, urlString ``, null, null, null);
        const view = new Network.RequestPayloadView.RequestPayloadView();
        view.request = request;
        renderElementIntoDOM(view, { includeCommonStyles: true });
        view.wasShown();
        await view.updateComplete;
        const treeOutline = view.element.querySelector('.request-payload-tree');
        assert.exists(treeOutline);
        const shadowRoot = treeOutline?.shadowRoot;
        assert.exists(shadowRoot);
        // Switch to View Source
        const buttons = shadowRoot.querySelectorAll('.payload-toggle');
        const viewSourceButton = Array.from(buttons).find(b => b.textContent?.includes('View source'));
        assert.exists(viewSourceButton);
        viewSourceButton.click();
        await view.updateComplete;
        const payloadValue = shadowRoot.querySelector('devtools-widget');
        assert.exists(payloadValue);
        const payloadValueWidget = UI.Widget.Widget.get(payloadValue);
        assert.instanceOf(payloadValueWidget, Network.ShowMoreDetailsWidget.ShowMoreDetailsWidget);
        assert.strictEqual(payloadValueWidget.text, `foo=${text}`);
        await assertScreenshot('network/request-payload-show-more.png');
    });
    it('displays JSON payload and toggles between parsed and source view', async () => {
        const request = SDK.NetworkRequest.NetworkRequest.create('requestId', urlString `https://example.com/api`, urlString ``, null, null, null);
        request.setRequestHeaders([{ name: 'Content-Type', value: 'application/json' }]);
        sinon.stub(request, 'requestFormData').resolves('{"foo": "bar"}');
        const view = new Network.RequestPayloadView.RequestPayloadView();
        view.request = request;
        renderElementIntoDOM(view, { includeCommonStyles: true });
        view.wasShown();
        await view.updateComplete;
        const treeOutline = view.element.querySelector('.request-payload-tree');
        assert.exists(treeOutline);
        const shadowRoot = treeOutline.shadowRoot;
        assert.exists(shadowRoot);
        const getButton = (text) => {
            const buttons = shadowRoot.querySelectorAll('li:not(.hidden) .payload-toggle');
            return Array.from(buttons).find(b => b.textContent?.includes(text));
        };
        // Initial state: Parsed.
        // Check that "View source" button exists
        const viewSourceButton = getButton('View source');
        assert.exists(viewSourceButton);
        await assertScreenshot('network/request-payload-json.png');
        // Toggle to source
        viewSourceButton?.click();
        await view.updateComplete;
        // Check for source text
        const payloadValue = shadowRoot.querySelector('devtools-widget');
        assert.exists(payloadValue);
        const payloadValueWidget = UI.Widget.Widget.get(payloadValue);
        assert.instanceOf(payloadValueWidget, Network.ShowMoreDetailsWidget.ShowMoreDetailsWidget);
        // Check that "View parsed" button exists
        const viewParsedButton = getButton('View parsed');
        assert.exists(viewParsedButton);
        await assertScreenshot('network/request-payload-json-source.png');
        // Click "View parsed"
        viewParsedButton?.click();
        await view.updateComplete;
        // Check that "View source" button exists again
        assert.exists(getButton('View source'));
        // And check that source text is gone
        assert.isNull(shadowRoot.querySelector('.payload-value'));
    });
    it('renders read-only object properties for payload', async () => {
        const request = SDK.NetworkRequest.NetworkRequest.create('requestId', urlString `https://example.com/api`, urlString ``, null, null, null);
        request.setRequestHeaders([{ name: 'Content-Type', value: 'application/json' }]);
        sinon.stub(request, 'requestFormData').resolves('{"foo": "bar"}');
        const view = new Network.RequestPayloadView.RequestPayloadView();
        view.request = request;
        renderElementIntoDOM(view, { includeCommonStyles: true });
        view.wasShown();
        await view.updateComplete;
        const treeOutline = view.element.querySelector('.request-payload-tree');
        assert.exists(treeOutline);
        const shadowRoot = treeOutline.shadowRoot;
        assert.exists(shadowRoot);
        const firstChildNode = shadowRoot.querySelector('li.object-properties-section-root-element');
        assert.exists(firstChildNode);
        const rootElement = UI.TreeOutline.TreeElement.getTreeElementBylistItemNode(firstChildNode);
        assert.exists(rootElement);
        // Ensure children are populated
        await rootElement.onpopulate();
        const firstProperty = rootElement.childAt(0);
        assert.instanceOf(firstProperty, ObjectUI.ObjectPropertiesSection.ObjectPropertyTreeElement);
        assert.isFalse(firstProperty.editable);
    });
});
//# sourceMappingURL=RequestPayloadView.test.js.map