Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(inspector): render params and durations in log #5489

Merged
merged 1 commit into from
Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/dispatchers/dispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ export class DispatcherConnection {
rewriteErrorMessage(e, e.message + formatLogRecording(callMetadata.log) + kLoggingNote);
this.onmessage({ id, error: serializeError(e) });
} finally {
callMetadata.endTime = monotonicTime();
if (sdkObject)
await sdkObject.instrumentation.onAfterCall(sdkObject, callMetadata);
}
Expand Down
2 changes: 2 additions & 0 deletions src/server/instrumentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export type CallMetadata = {
id: number;
startTime: number;
endTime: number;
pauseStartTime?: number;
pauseEndTime?: number;
type: string;
method: string;
params: any;
Expand Down
1 change: 0 additions & 1 deletion src/server/progress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ export class ProgressController {
throw e;
} finally {
clearTimeout(timer);
this.metadata.endTime = monotonicTime();
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/server/supplements/inspectorController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,15 @@ export class InspectorController implements InstrumentationListener {
const info = metadata.params.info;
switch (info.phase) {
case 'before':
metadata.endTime = 0;
// Fall through.
case 'log':
return;
case 'after':
metadata = this._waitOperations.get(info.waitId)!;
const originalMetadata = this._waitOperations.get(info.waitId)!;
originalMetadata.endTime = metadata.endTime;
this._waitOperations.delete(info.waitId);
metadata = originalMetadata;
break;
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/server/supplements/recorder/recorderTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export type CallLog = {
status: 'in-progress' | 'done' | 'error' | 'paused';
error?: string;
reveal?: boolean;
duration?: number;
params: {
url?: string,
selector?: string,
};
};

export type SourceHighlight = {
Expand Down
24 changes: 21 additions & 3 deletions src/server/supplements/recorderSupplement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { RecorderApp } from './recorder/recorderApp';
import { CallMetadata, internalCallMetadata, SdkObject } from '../instrumentation';
import { Point } from '../../common/types';
import { CallLog, EventData, Mode, Source, UIState } from './recorder/recorderTypes';
import { isUnderTest } from '../../utils/utils';
import { isUnderTest, monotonicTime } from '../../utils/utils';

type BindingSource = { frame: Frame, page: Page };

Expand Down Expand Up @@ -210,6 +210,7 @@ export class RecorderSupplement {
this._pausedCallsMetadata.set(metadata, f);
});
this._recorderApp!.setPaused(true);
metadata.pauseStartTime = monotonicTime();
this._updateUserSources();
this.updateCallLog([metadata]);
return result;
Expand All @@ -219,8 +220,11 @@ export class RecorderSupplement {
this._pauseOnNextStatement = step;
this._recorderApp?.setPaused(false);

for (const callback of this._pausedCallsMetadata.values())
const endTime = monotonicTime();
for (const [metadata, callback] of this._pausedCallsMetadata) {
metadata.pauseEndTime = endTime;
callback();
}
this._pausedCallsMetadata.clear();

this._updateUserSources();
Expand Down Expand Up @@ -418,7 +422,21 @@ export class RecorderSupplement {
status = 'paused';
if (metadata.error)
status = 'error';
logs.push({ id: metadata.id, messages: metadata.log, title, status, error: metadata.error });
const params = {
url: metadata.params?.url,
selector: metadata.params?.selector,
};
let duration = metadata.endTime ? metadata.endTime - metadata.startTime : undefined;
if (duration && metadata.pauseStartTime && metadata.pauseEndTime)
duration -= (metadata.pauseEndTime - metadata.pauseStartTime);
logs.push({
id: metadata.id,
messages: metadata.log,
title, status,
error: metadata.error,
params,
duration
});
}
this._recorderApp?.updateCallLogs(logs);
}
Expand Down
13 changes: 13 additions & 0 deletions src/web/recorder/callLog.css
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,16 @@
.call-log .codicon-error {
color: red;
}

.call-log-url {
color: var(--blue);
}

.call-log-selector {
color: var(--orange);
}

.call-log-time {
margin-left: 4px;
color: var(--gray);
}
19 changes: 15 additions & 4 deletions src/web/recorder/callLog.example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,21 @@ export function exampleCallLog(): CallLog[] {
'id': 3,
'messages': [],
'title': 'newPage',
'status': 'done'
'status': 'done',
'duration': 100,
'params': {}
},
{
'id': 4,
'messages': [
'navigating to "https://github.com/microsoft", waiting until "load"',
],
'title': 'goto',
'status': 'done'
'status': 'done',
'params': {
'url': 'https://github.com/microsoft'
},
'duration': 1100,
},
{
'id': 5,
Expand All @@ -47,7 +53,10 @@ export function exampleCallLog(): CallLog[] {
' performing click action'
],
'title': 'click',
'status': 'paused'
'status': 'paused',
'params': {
'selector': 'input[aria-label="Find a repository…"]'
},
},
{
'id': 6,
Expand All @@ -56,7 +65,9 @@ export function exampleCallLog(): CallLog[] {
],
'error': 'Error occured',
'title': 'error',
'status': 'error'
'status': 'error',
'params': {
},
},
];
}
4 changes: 4 additions & 0 deletions src/web/recorder/callLog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import './callLog.css';
import * as React from 'react';
import type { CallLog } from '../../server/supplements/recorder/recorderTypes';
import { msToString } from '../uiUtils';

export interface CallLogProps {
log: CallLog[]
Expand Down Expand Up @@ -46,7 +47,10 @@ export const CallLogView: React.FC<CallLogProps> = ({
setExpandOverrides(newOverrides);
}}></span>
{ callLog.title }
{ callLog.params.url ? <span>(<span className='call-log-url'>{callLog.params.url}</span>)</span> : undefined }
{ callLog.params.selector ? <span>(<span className='call-log-selector'>{callLog.params.selector}</span>)</span> : undefined }
<span className={'codicon ' + iconClass(callLog)}></span>
{ typeof callLog.duration === 'number' ? <span className='call-log-time'>— {msToString(callLog.duration)}</span> : undefined}
</div>
{ (isExpanded ? callLog.messages : []).map((message, i) => {
return <div className='call-log-message' key={i}>
Expand Down
26 changes: 0 additions & 26 deletions src/web/traceViewer/ui/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,29 +72,3 @@ export const Expandable: React.FunctionComponent<{
{ expanded && <div style={{ display: 'flex', flex: 'auto', margin: '5px 0 5px 20px' }}>{body}</div> }
</div>;
};

export function msToString(ms: number): string {
if (!isFinite(ms))
return '-';

if (ms === 0)
return '0';

if (ms < 1000)
return ms.toFixed(0) + 'ms';

const seconds = ms / 1000;
if (seconds < 60)
return seconds.toFixed(1) + 's';

const minutes = seconds / 60;
if (minutes < 60)
return minutes.toFixed(1) + 'm';

const hours = minutes / 60;
if (hours < 24)
return hours.toFixed(1) + 'h';

const days = hours / 24;
return days.toFixed(1) + 'd';
}
3 changes: 2 additions & 1 deletion src/web/traceViewer/ui/snapshotTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import { ActionEntry } from '../../../cli/traceViewer/traceModel';
import { Boundaries, Size } from '../geometry';
import './snapshotTab.css';
import * as React from 'react';
import { msToString, useMeasure } from './helpers';
import { useMeasure } from './helpers';
import { msToString } from '../../uiUtils';

export const SnapshotTab: React.FunctionComponent<{
actionEntry: ActionEntry | undefined,
Expand Down
3 changes: 2 additions & 1 deletion src/web/traceViewer/ui/timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import { ContextEntry, InterestingPageEvent, ActionEntry, trace } from '../../..
import './timeline.css';
import { Boundaries } from '../geometry';
import * as React from 'react';
import { msToString, useMeasure } from './helpers';
import { useMeasure } from './helpers';
import { msToString } from '../../uiUtils';

type TimelineBar = {
entry?: ActionEntry;
Expand Down
41 changes: 41 additions & 0 deletions src/web/uiUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
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.
*/

export function msToString(ms: number): string {
if (!isFinite(ms))
return '-';

if (ms === 0)
return '0';

if (ms < 1000)
return ms.toFixed(0) + 'ms';

const seconds = ms / 1000;
if (seconds < 60)
return seconds.toFixed(1) + 's';

const minutes = seconds / 60;
if (minutes < 60)
return minutes.toFixed(1) + 'm';

const hours = minutes / 60;
if (hours < 24)
return hours.toFixed(1) + 'h';

const days = hours / 24;
return days.toFixed(1) + 'd';
}
33 changes: 19 additions & 14 deletions test/pause.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ describe('pause', (suite, { mode }) => {
await recorderPage.click('[title="Resume"]');
await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")');
expect(await sanitizeLog(recorderPage)).toEqual([
'pause',
'click',
'pause- XXms',
'click(button)- XXms',
'pause',
]);
await recorderPage.click('[title="Resume"]');
Expand All @@ -168,9 +168,9 @@ describe('pause', (suite, { mode }) => {
await recorderPage.click('[title="Resume"]');
await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")');
expect(await sanitizeLog(recorderPage)).toEqual([
'pause',
'waitForEvent()',
'click',
'pause- XXms',
'waitForEvent(console)- XXms',
'click(button)- XXms',
'pause',
]);
await recorderPage.click('[title="Resume"]');
Expand All @@ -187,10 +187,10 @@ describe('pause', (suite, { mode }) => {
await recorderPage.click('[title="Resume"]');
await recorderPage.waitForSelector('.source-line-error');
expect(await sanitizeLog(recorderPage)).toEqual([
'pause',
'isChecked',
'pause- XXms',
'isChecked(button)- XXms',
'checking \"checked\" state of \"button\"',
'selector resolved to <button onclick=\"console.log()\">Submit</button>',
'selector resolved to <button onclick=\"console.log(1)\">Submit</button>',
'Not a checkbox or radio button',
]);
const error = await scriptPromise;
Expand All @@ -199,10 +199,15 @@ describe('pause', (suite, { mode }) => {
});

async function sanitizeLog(recorderPage: Page): Promise<string[]> {
const text = await recorderPage.innerText('.call-log');
return text.split('\n').filter(l => {
return l !== 'element is not stable - waiting...';
}).map(l => {
return l.replace(/\(.*\)/, '()');
});
const results = [];
for (const entry of await recorderPage.$$('.call-log-call')) {
const header = await (await (await entry.$('.call-log-call-header')).textContent()).replace(/— \d+(ms|s)/, '- XXms');
results.push(header);
results.push(...await entry.$$eval('.call-log-message', ee => ee.map(e => e.textContent)));
const errorElement = await entry.$('.call-log-error');
const error = errorElement ? await errorElement.textContent() : undefined;
if (error)
results.push(error);
}
return results;
}