mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(trace): allow typing in selector w/ frames (#26919)
This commit is contained in:
parent
ea4974ce36
commit
d65da74b8f
@ -53,7 +53,7 @@ export class Recorder {
|
||||
console.error('Recorder script ready for test'); // eslint-disable-line no-console
|
||||
}
|
||||
|
||||
refreshListenersIfNeeded() {
|
||||
installListeners() {
|
||||
// Ensure we are attached to the current document, and we are on top (last element);
|
||||
if (this._highlight.isInstalled())
|
||||
return;
|
||||
@ -74,19 +74,24 @@ export class Recorder {
|
||||
return;
|
||||
this._hoveredModel = null;
|
||||
this._highlight.hideActionPoint();
|
||||
this._updateHighlight();
|
||||
this._updateHighlight(false);
|
||||
}, true),
|
||||
];
|
||||
this._highlight.install();
|
||||
}
|
||||
|
||||
uninstallListeners() {
|
||||
removeEventListeners(this._listeners);
|
||||
this._highlight.uninstall();
|
||||
}
|
||||
|
||||
setUIState(state: UIState, delegate: RecorderDelegate) {
|
||||
this._delegate = delegate;
|
||||
|
||||
if (state.mode !== 'none' || state.actionSelector)
|
||||
this.refreshListenersIfNeeded();
|
||||
this.installListeners();
|
||||
else
|
||||
removeEventListeners(this._listeners);
|
||||
this.uninstallListeners();
|
||||
|
||||
const { mode, actionPoint, actionSelector, language, testIdAttributeName } = state;
|
||||
this._testIdAttributeName = testIdAttributeName;
|
||||
@ -113,7 +118,7 @@ export class Recorder {
|
||||
|
||||
if (actionSelector !== this._actionSelector) {
|
||||
this._hoveredModel = actionSelector ? querySelector(this._injectedScript, actionSelector, this.document) : null;
|
||||
this._updateHighlight();
|
||||
this._updateHighlight(false);
|
||||
this._actionSelector = actionSelector;
|
||||
}
|
||||
}
|
||||
@ -121,7 +126,7 @@ export class Recorder {
|
||||
clearHighlight() {
|
||||
this._hoveredModel = null;
|
||||
this._activeModel = null;
|
||||
this._updateHighlight();
|
||||
this._updateHighlight(false);
|
||||
}
|
||||
|
||||
private _actionInProgress(event: Event): boolean {
|
||||
@ -257,7 +262,7 @@ export class Recorder {
|
||||
if (!this._hoveredElement || !this._hoveredElement.isConnected) {
|
||||
this._hoveredModel = null;
|
||||
this._hoveredElement = null;
|
||||
this._updateHighlight();
|
||||
this._updateHighlight(true);
|
||||
return;
|
||||
}
|
||||
const hoveredElement = this._hoveredElement;
|
||||
@ -265,14 +270,14 @@ export class Recorder {
|
||||
if ((this._hoveredModel && this._hoveredModel.selector === selector))
|
||||
return;
|
||||
this._hoveredModel = selector ? { selector, elements } : null;
|
||||
this._updateHighlight();
|
||||
this._updateHighlight(true);
|
||||
}
|
||||
|
||||
private _updateHighlight() {
|
||||
private _updateHighlight(userGesture: boolean) {
|
||||
const elements = this._hoveredModel ? this._hoveredModel.elements : [];
|
||||
const selector = this._hoveredModel ? this._hoveredModel.selector : '';
|
||||
this._highlight.updateHighlight(elements, selector, this._mode === 'recording');
|
||||
if (this._hoveredModel)
|
||||
if (userGesture)
|
||||
this._delegate.highlightUpdated?.();
|
||||
}
|
||||
|
||||
@ -522,7 +527,7 @@ export class PollingRecorder implements RecorderDelegate {
|
||||
this._recorder = new Recorder(injectedScript);
|
||||
this._embedder = injectedScript.window as any;
|
||||
|
||||
injectedScript.onGlobalListenersRemoved.add(() => this._recorder.refreshListenersIfNeeded());
|
||||
injectedScript.onGlobalListenersRemoved.add(() => this._recorder.installListeners());
|
||||
|
||||
const refreshOverlay = () => {
|
||||
this._pollRecorderMode().catch(e => console.log(e)); // eslint-disable-line no-console
|
||||
|
||||
@ -228,8 +228,9 @@ export const InspectModeController: React.FunctionComponent<{
|
||||
}> = ({ iframe, isInspecting, sdkLanguage, testIdAttributeName, highlightedLocator, setHighlightedLocator, iteration }) => {
|
||||
React.useEffect(() => {
|
||||
const recorders: { recorder: Recorder, frameSelector: string }[] = [];
|
||||
const isUnderTest = new URLSearchParams(window.location.search).get('isUnderTest') === 'true';
|
||||
try {
|
||||
createRecorders(recorders, sdkLanguage, testIdAttributeName, '', iframe?.contentWindow);
|
||||
createRecorders(recorders, sdkLanguage, testIdAttributeName, isUnderTest, '', iframe?.contentWindow);
|
||||
} catch {
|
||||
// Potential cross-origin exceptions.
|
||||
}
|
||||
@ -238,7 +239,7 @@ export const InspectModeController: React.FunctionComponent<{
|
||||
const actionSelector = locatorOrSelectorAsSelector(sdkLanguage, highlightedLocator, testIdAttributeName);
|
||||
recorder.setUIState({
|
||||
mode: isInspecting ? 'inspecting' : 'none',
|
||||
actionSelector,
|
||||
actionSelector: actionSelector.startsWith(frameSelector) ? actionSelector.substring(frameSelector.length).trim() : undefined,
|
||||
language: sdkLanguage,
|
||||
testIdAttributeName,
|
||||
}, {
|
||||
@ -257,12 +258,12 @@ export const InspectModeController: React.FunctionComponent<{
|
||||
return <></>;
|
||||
};
|
||||
|
||||
function createRecorders(recorders: { recorder: Recorder, frameSelector: string }[], sdkLanguage: Language, testIdAttributeName: string, parentFrameSelector: string, frameWindow: Window | null | undefined) {
|
||||
function createRecorders(recorders: { recorder: Recorder, frameSelector: string }[], sdkLanguage: Language, testIdAttributeName: string, isUnderTest: boolean, parentFrameSelector: string, frameWindow: Window | null | undefined) {
|
||||
if (!frameWindow)
|
||||
return;
|
||||
const win = frameWindow as any;
|
||||
if (!win._recorder) {
|
||||
const injectedScript = new InjectedScript(frameWindow as any, false, sdkLanguage, testIdAttributeName, 1, 'chromium', []);
|
||||
const injectedScript = new InjectedScript(frameWindow as any, isUnderTest, sdkLanguage, testIdAttributeName, 1, 'chromium', []);
|
||||
const recorder = new Recorder(injectedScript);
|
||||
win._injectedScript = injectedScript;
|
||||
win._recorder = { recorder, frameSelector: parentFrameSelector };
|
||||
@ -272,7 +273,7 @@ function createRecorders(recorders: { recorder: Recorder, frameSelector: string
|
||||
for (let i = 0; i < frameWindow.frames.length; ++i) {
|
||||
const childFrame = frameWindow.frames[i];
|
||||
const frameSelector = childFrame.frameElement ? win._injectedScript.generateSelector(childFrame.frameElement, { omitInternalEngines: true, testIdAttributeName }) + ' >> internal:control=enter-frame >> ' : '';
|
||||
createRecorders(recorders, sdkLanguage, testIdAttributeName, parentFrameSelector + frameSelector, childFrame);
|
||||
createRecorders(recorders, sdkLanguage, testIdAttributeName, isUnderTest, parentFrameSelector + frameSelector, childFrame);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1034,3 +1034,58 @@ test('should pick locator in iframe', async ({ page, runAndTrace, server }) => {
|
||||
await snapshot.frameLocator('#frame1').frameLocator('iframe').frameLocator('[name=two]').getByText('HelloNameTwo').click();
|
||||
await expect.soft(cmWrapper).toContainText(`frameLocator('#frame1').frameLocator('iframe').frameLocator('iframe[name="two"]').getByText('HelloNameTwo')`, { timeout: 0 });
|
||||
});
|
||||
|
||||
test('should highlight locator in iframe while typing', async ({ page, runAndTrace, server, platform }) => {
|
||||
/*
|
||||
iframe[id=frame1]
|
||||
div Hello1
|
||||
iframe
|
||||
div Hello2
|
||||
iframe[name=one]
|
||||
div HelloNameOne
|
||||
iframe[name=two]
|
||||
dev HelloNameTwo
|
||||
*/
|
||||
const traceViewer = await runAndTrace(async () => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<iframe id=frame1 srcdoc="<div>Hello1</div><iframe srcdoc='<div>Hello2</div><iframe name=one></iframe><iframe name=two></iframe><iframe></iframe>'>">`);
|
||||
const frameOne = page.frame({ name: 'one' });
|
||||
await frameOne.setContent(`<div>HelloNameOne</div>`);
|
||||
const frameTwo = page.frame({ name: 'two' });
|
||||
await frameTwo.setContent(`<div>HelloNameTwo</div>`);
|
||||
await page.evaluate('2+2');
|
||||
});
|
||||
|
||||
const snapshot = await traceViewer.snapshotFrame('page.evaluate');
|
||||
await traceViewer.page.getByText('Locator').click();
|
||||
await traceViewer.page.locator('.CodeMirror').click();
|
||||
|
||||
const locators = [{
|
||||
text: `frameLocator('#frame1').getByText('Hello1')`,
|
||||
element: snapshot.frameLocator('#frame1').locator('div', { hasText: 'Hello1' }),
|
||||
highlight: snapshot.frameLocator('#frame1').locator('x-pw-highlight'),
|
||||
}, {
|
||||
text: `frameLocator('#frame1').frameLocator('iframe').getByText('Hello2')`,
|
||||
element: snapshot.frameLocator('#frame1').frameLocator('iframe').locator('div', { hasText: 'Hello2' }),
|
||||
highlight: snapshot.frameLocator('#frame1').frameLocator('iframe').locator('x-pw-highlight'),
|
||||
}, {
|
||||
text: `frameLocator('#frame1').frameLocator('iframe').frameLocator('iframe[name="one"]').getByText('HelloNameOne')`,
|
||||
element: snapshot.frameLocator('#frame1').frameLocator('iframe').frameLocator('iframe[name="one"]').locator('div', { hasText: 'HelloNameOne' }),
|
||||
highlight: snapshot.frameLocator('#frame1').frameLocator('iframe').frameLocator('iframe[name="one"]').locator('x-pw-highlight'),
|
||||
}];
|
||||
|
||||
for (const locator of locators) {
|
||||
if (platform === 'darwin')
|
||||
await traceViewer.page.keyboard.press('Meta+a');
|
||||
else
|
||||
await traceViewer.page.keyboard.press('Control+a');
|
||||
await traceViewer.page.keyboard.press('Backspace');
|
||||
await traceViewer.page.keyboard.type(locator.text);
|
||||
const elementBox = await locator.element.boundingBox();
|
||||
const highlightBox = await locator.highlight.boundingBox();
|
||||
expect(Math.abs(elementBox.width - highlightBox.width)).toBeLessThan(5);
|
||||
expect(Math.abs(elementBox.height - highlightBox.height)).toBeLessThan(5);
|
||||
expect(Math.abs(elementBox.x - highlightBox.x)).toBeLessThan(5);
|
||||
expect(Math.abs(elementBox.y - highlightBox.y)).toBeLessThan(5);
|
||||
}
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user