/**
 * Copyright (c) Microsoft Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { clsx } from '@web/uiUtils';
import { msToString } from '@isomorphic/formatUtils';
import * as React from 'react';
import './callTab.css';
import { CopyToClipboard } from './copyToClipboard';
import { asLocator } from '@isomorphic/locatorGenerators';
import { PlaceholderPanel } from './placeholderPanel';
import { renderTitleForCall } from './actionList';
export const CallTab = ({ action, startTimeOffset, sdkLanguage }) => {
    // We never need the waitForEventInfo (`info`).
    const paramKeys = React.useMemo(() => Object.keys(action?.params ?? {}).filter(name => name !== 'info'), [action]);
    if (!action)
        return <PlaceholderPanel text='No action selected'/>;
    // Calculate execution time relative to the test runner's start time
    const startTimeMillis = action.startTime - startTimeOffset;
    const startTime = msToString(startTimeMillis);
    const { title } = renderTitleForCall(action);
    return (<div className='call-tab'>
      <div className='call-line'>{title}</div>
      <div className='call-section'>Time</div>
      {renderProperty({ name: 'start', type: 'literal', text: startTime })}
      {renderProperty({ name: 'duration', type: 'literal', text: renderDuration(action) })}
      {!!paramKeys.length && <>
          <div className='call-section'>Parameters</div>
          {paramKeys.map(name => renderProperty(propertyToString(action, name, action.params[name], sdkLanguage)))}
        </>}
      {!!action.result && <>
          <div className='call-section'>Return value</div>
          {Object.keys(action.result).map(name => renderProperty(propertyToString(action, name, action.result[name], sdkLanguage)))}
        </>}
    </div>);
};
function renderDuration(action) {
    if (action.endTime)
        return msToString(action.endTime - action.startTime);
    else if (!!action.error)
        return 'Timed Out';
    else
        return 'Running';
}
function renderProperty(property) {
    let text = property.text.replace(/\n/g, '↵');
    if (property.type === 'string')
        text = `"${text}"`;
    return (<div key={property.name} className='call-line'>
      {property.name}:<span className={clsx('call-value', property.type)} title={property.text}>{text}</span>
      {['literal', 'string', 'number', 'object', 'locator'].includes(property.type) &&
            <CopyToClipboard value={property.text}/>}
    </div>);
}
function propertyToString(event, name, value, sdkLanguage) {
    const isEval = event.method.includes('eval') || event.method === 'waitForFunction';
    if (name === 'files')
        return { text: '<files>', type: 'string', name };
    if (name === 'eventInit' || name === 'expectedValue' || (name === 'arg' && isEval))
        value = parseSerializedValue(value.value, new Array(10).fill({ handle: '<handle>' }));
    if (name === 'value' && isEval)
        value = parseSerializedValue(value, new Array(10).fill({ handle: '<handle>' }));
    if (name === 'received' && event.method === 'expect') {
        // Older traces store received as a raw SerializedValue; newer traces wrap it
        // as { value?: SerializedValue, ariaSnapshot?: string }. Support both.
        const wrapped = value && typeof value === 'object' && ('value' in value || 'ariaSnapshot' in value);
        const serialized = wrapped ? value.value : value;
        value = serialized !== undefined
            ? parseSerializedValue(serialized, new Array(10).fill({ handle: '<handle>' }))
            : undefined;
    }
    if (name === 'selector')
        return { text: asLocator(sdkLanguage || 'javascript', event.params.selector), type: 'locator', name: 'locator' };
    const type = typeof value;
    if (type !== 'object' || value === null)
        return { text: String(value), type, name };
    if (value.guid)
        return { text: '<handle>', type: 'handle', name };
    return { text: JSON.stringify(value).slice(0, 1000), type: 'object', name };
}
function parseSerializedValue(value, handles) {
    if (value.n !== undefined)
        return value.n;
    if (value.s !== undefined)
        return value.s;
    if (value.b !== undefined)
        return value.b;
    if (value.v !== undefined) {
        if (value.v === 'undefined')
            return undefined;
        if (value.v === 'null')
            return null;
        if (value.v === 'NaN')
            return NaN;
        if (value.v === 'Infinity')
            return Infinity;
        if (value.v === '-Infinity')
            return -Infinity;
        if (value.v === '-0')
            return -0;
    }
    if (value.d !== undefined)
        return new Date(value.d);
    if (value.r !== undefined)
        return new RegExp(value.r.p, value.r.f);
    if (value.a !== undefined)
        return value.a.map((a) => parseSerializedValue(a, handles));
    if (value.o !== undefined) {
        const result = {};
        for (const { k, v } of value.o)
            result[k] = parseSerializedValue(v, handles);
        return result;
    }
    if (value.h !== undefined) {
        if (handles === undefined)
            return '<object>';
        return handles[value.h];
    }
    return '<object>';
}
//# sourceMappingURL=callTab.js.map