chore: add stack trace utilities and tests (#2371)

This commit is contained in:
Dmitry Gozman 2020-05-27 14:26:44 -07:00 committed by GitHub
parent 1e2b46437d
commit 609bc4cfb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 104 additions and 29 deletions

View File

@ -16,10 +16,7 @@
import * as fs from 'fs';
import * as util from 'util';
import * as path from 'path';
// NOTE: update this to point to playwright/lib when moving this file.
const PLAYWRIGHT_LIB_PATH = path.normalize(path.join(__dirname, '..'));
import { getCallerFilePath } from './stackTrace';
type Position = {
line: number;
@ -118,28 +115,3 @@ function advancePosition(position: Position, delta: Position) {
column: delta.column + (delta.line ? 0 : position.column),
};
}
function getCallerFilePath(): string | null {
const error = new Error();
const stackFrames = (error.stack || '').split('\n').slice(1);
// Find first stackframe that doesn't point to PLAYWRIGHT_LIB_PATH.
for (let frame of stackFrames) {
frame = frame.trim();
if (!frame.startsWith('at '))
return null;
if (frame.endsWith(')')) {
const from = frame.indexOf('(');
frame = frame.substring(from + 1, frame.length - 1);
} else {
frame = frame.substring('at '.length);
}
const match = frame.match(/^(?:async )?(.*):(\d+):(\d+)$/);
if (!match)
return null;
const filePath = match[1];
if (filePath.startsWith(PLAYWRIGHT_LIB_PATH))
continue;
return filePath;
}
return null;
}

81
src/debug/stackTrace.ts Normal file
View File

@ -0,0 +1,81 @@
/**
* 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 * as path from 'path';
// NOTE: update this to point to playwright/lib when moving this file.
const PLAYWRIGHT_LIB_PATH = path.normalize(path.join(__dirname, '..'));
type ParsedStackFrame = { filePath: string, functionName: string };
function parseStackFrame(frame: string): ParsedStackFrame | null {
frame = frame.trim();
if (!frame.startsWith('at '))
return null;
frame = frame.substring('at '.length);
if (frame.startsWith('async '))
frame = frame.substring('async '.length);
let location: string;
let functionName: string;
if (frame.endsWith(')')) {
const from = frame.indexOf('(');
location = frame.substring(from + 1, frame.length - 1);
functionName = frame.substring(0, from).trim();
} else {
location = frame;
functionName = '';
}
const match = location.match(/^(?:async )?([^(]*):(\d+):(\d+)$/);
if (!match)
return null;
const filePath = match[1];
return { filePath, functionName };
}
export function getCallerFilePath(ignorePrefix = PLAYWRIGHT_LIB_PATH): string | null {
const error = new Error();
const stackFrames = (error.stack || '').split('\n').slice(1);
// Find first stackframe that doesn't point to ignorePrefix.
for (const frame of stackFrames) {
const parsed = parseStackFrame(frame);
if (!parsed)
return null;
if (parsed.filePath.startsWith(ignorePrefix) || parsed.filePath === __filename)
continue;
return parsed.filePath;
}
return null;
}
export function getCurrentApiCall(prefix = PLAYWRIGHT_LIB_PATH): string {
const error = new Error();
const stackFrames = (error.stack || '').split('\n').slice(1);
// Find last stackframe that points to prefix - that should be the api call.
let apiName: string = '';
for (const frame of stackFrames) {
const parsed = parseStackFrame(frame);
if (!parsed || (!parsed.filePath.startsWith(prefix) && parsed.filePath !== __filename))
break;
apiName = parsed.functionName;
}
const parts = apiName.split('.');
if (parts.length && parts[0].length) {
parts[0] = parts[0][0].toLowerCase() + parts[0].substring(1);
if (parts[0] === 'webKit')
parts[0] = 'webkit';
}
return parts.join('.');
}

View File

@ -177,3 +177,20 @@ describe('Fixtures', function() {
});
});
});
describe('StackTrace', () => {
it('caller file path', async state => {
const stackTrace = require(path.join(state.playwrightPath, 'lib', 'debug', 'stackTrace'));
const callme = require('./fixtures/callback');
const filePath = callme(() => {
return stackTrace.getCallerFilePath(path.join(__dirname, 'fixtures') + path.sep);
});
expect(filePath).toBe(__filename);
});
it('api call', async state => {
const stackTrace = require(path.join(state.playwrightPath, 'lib', 'debug', 'stackTrace'));
const callme = require('./fixtures/callback');
const apiCall = callme(stackTrace.getCurrentApiCall.bind(stackTrace, path.join(__dirname, 'fixtures') + path.sep));
expect(apiCall).toBe('callme');
});
});

5
test/fixtures/callback.js vendored Normal file
View File

@ -0,0 +1,5 @@
function callme(cb) {
return cb();
}
module.exports = callme;