chore: make asLocator() always safe (#28207)

This commit is contained in:
Dmitry Gozman 2023-11-16 16:31:34 -08:00 committed by GitHub
parent 738155d85d
commit 5488c03d7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 15 additions and 20 deletions

View File

@ -355,7 +355,7 @@ export class Locator implements api.Locator {
}
toString() {
return asLocator('javascript', this._selector, undefined, true);
return asLocator('javascript', this._selector);
}
}

View File

@ -35,20 +35,16 @@ export interface LocatorFactory {
chainLocators(locators: string[]): string;
}
export function asLocator(lang: Language, selector: string, isFrameLocator: boolean = false, playSafe: boolean = false): string {
return asLocators(lang, selector, isFrameLocator, playSafe)[0];
export function asLocator(lang: Language, selector: string, isFrameLocator: boolean = false): string {
return asLocators(lang, selector, isFrameLocator)[0];
}
export function asLocators(lang: Language, selector: string, isFrameLocator: boolean = false, playSafe: boolean = false, maxOutputSize = 20, preferredQuote?: Quote): string[] {
if (playSafe) {
try {
return innerAsLocators(new generators[lang](preferredQuote), parseSelector(selector), isFrameLocator, maxOutputSize);
} catch (e) {
// Tolerate invalid input.
return [selector];
}
} else {
export function asLocators(lang: Language, selector: string, isFrameLocator: boolean = false, maxOutputSize = 20, preferredQuote?: Quote): string[] {
try {
return innerAsLocators(new generators[lang](preferredQuote), parseSelector(selector), isFrameLocator, maxOutputSize);
} catch (e) {
// Tolerate invalid input.
return [selector];
}
}

View File

@ -219,7 +219,7 @@ export function locatorOrSelectorAsSelector(language: Language, locator: string,
}
try {
const { selector, preferredQuote } = parseLocator(locator, testIdAttributeName);
const locators = asLocators(language, selector, undefined, undefined, undefined, preferredQuote);
const locators = asLocators(language, selector, undefined, undefined, preferredQuote);
const digest = digestForComparison(locator);
if (locators.some(candidate => digestForComparison(candidate) === digest))
return selector;

View File

@ -729,7 +729,7 @@ function renderApiCall(apiName: string, params: any) {
continue;
let value;
if (name === 'selector' && isString(params[name]) && params[name].startsWith('internal:')) {
const getter = asLocator('javascript', params[name], false, true);
const getter = asLocator('javascript', params[name]);
apiName = apiName.replace(/^locator\./, 'locator.' + getter + '.');
apiName = apiName.replace(/^page\./, 'page.' + getter + '.');
apiName = apiName.replace(/^frame\./, 'frame.' + getter + '.');

View File

@ -40,8 +40,7 @@ export const CallLogView: React.FC<CallLogProps> = ({
{log.map(callLog => {
const expandOverride = expandOverrides.get(callLog.id);
const isExpanded = typeof expandOverride === 'boolean' ? expandOverride : callLog.status !== 'done';
const locator = callLog.params.selector ? asLocator(language, callLog.params.selector, false) : null;
const locatorCall = `page.${locator}`;
const locator = callLog.params.selector ? asLocator(language, callLog.params.selector) : null;
let titlePrefix = callLog.title;
let titleSuffix = '';
if (callLog.title.startsWith('expect.to') || callLog.title.startsWith('expect.not.to')) {
@ -63,7 +62,7 @@ export const CallLogView: React.FC<CallLogProps> = ({
}}></span>
{ titlePrefix }
{ callLog.params.url ? <span className='call-log-details'><span className='call-log-url' title={callLog.params.url}>{callLog.params.url}</span></span> : undefined }
{ locator ? <span className='call-log-details'><span className='call-log-selector' title={locatorCall}>{locatorCall}</span></span> : undefined }
{ locator ? <span className='call-log-details'><span className='call-log-selector' title={`page.${locator}`}>{`page.${locator}`}</span></span> : undefined }
{ titleSuffix }
<span className={'codicon ' + iconClass(callLog)}></span>
{ typeof callLog.duration === 'number' ? <span className='call-log-time'> {msToString(callLog.duration)}</span> : undefined}

View File

@ -88,7 +88,7 @@ export const renderAction = (
}) => {
const { sdkLanguage, revealConsole, isLive, showDuration, showBadges } = options;
const { errors, warnings } = modelUtil.stats(action);
const locator = action.params.selector ? asLocator(sdkLanguage || 'javascript', action.params.selector, false /* isFrameLocator */, true /* playSafe */) : undefined;
const locator = action.params.selector ? asLocator(sdkLanguage || 'javascript', action.params.selector) : undefined;
let time: string = '';
if (action.endTime)

View File

@ -86,7 +86,7 @@ function propertyToString(event: ActionTraceEvent, name: string, value: any, sdk
if ((name === 'value' && isEval) || (name === 'received' && event.method === 'expect'))
value = parseSerializedValue(value, new Array(10).fill({ handle: '<handle>' }));
if (name === 'selector')
return { text: asLocator(sdkLanguage || 'javascript', event.params.selector, false /* isFrameLocator */, true /* playSafe */), type: 'locator', name: 'locator' };
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 };

View File

@ -245,7 +245,7 @@ export const InspectModeController: React.FunctionComponent<{
overlay: { offsetX: 0 },
}, {
async setSelector(selector: string) {
setHighlightedLocator(asLocator(sdkLanguage, frameSelector + selector, false /* isFrameLocator */, true /* playSafe */));
setHighlightedLocator(asLocator(sdkLanguage, frameSelector + selector));
},
highlightUpdated() {
for (const r of recorders) {