mirror of
https://github.com/microsoft/playwright.git
synced 2024-10-26 21:33:38 +03:00
feat(codegen): slider support (#29087)
This commit is contained in:
parent
020a39860d
commit
b88a008a87
@ -225,6 +225,10 @@ class RecordActionTool implements RecorderTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onClick(event: MouseEvent) {
|
onClick(event: MouseEvent) {
|
||||||
|
// in webkit, sliding a range element may trigger a click event with a different target if the mouse is released outside the element bounding box.
|
||||||
|
// So we check the hovered element instead, and if it is a range input, we skip click handling
|
||||||
|
if (isRangeInput(this._hoveredElement))
|
||||||
|
return;
|
||||||
if (this._shouldIgnoreMouseEvent(event))
|
if (this._shouldIgnoreMouseEvent(event))
|
||||||
return;
|
return;
|
||||||
if (this._actionInProgress(event))
|
if (this._actionInProgress(event))
|
||||||
@ -317,6 +321,17 @@ class RecordActionTool implements RecorderTool {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isRangeInput(target)) {
|
||||||
|
this._recorder.delegate.recordAction?.({
|
||||||
|
name: 'fill',
|
||||||
|
// must use hoveredModel instead of activeModel for it to work in webkit
|
||||||
|
selector: this._hoveredModel!.selector,
|
||||||
|
signals: [],
|
||||||
|
text: target.value,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (['INPUT', 'TEXTAREA'].includes(target.nodeName) || target.isContentEditable) {
|
if (['INPUT', 'TEXTAREA'].includes(target.nodeName) || target.isContentEditable) {
|
||||||
if (target.nodeName === 'INPUT' && ['checkbox', 'radio'].includes((target as HTMLInputElement).type.toLowerCase())) {
|
if (target.nodeName === 'INPUT' && ['checkbox', 'radio'].includes((target as HTMLInputElement).type.toLowerCase())) {
|
||||||
// Checkbox is handled in click, we can't let input trigger on checkbox - that would mean we dispatched click events while recording.
|
// Checkbox is handled in click, we can't let input trigger on checkbox - that would mean we dispatched click events while recording.
|
||||||
@ -414,7 +429,7 @@ class RecordActionTool implements RecorderTool {
|
|||||||
const nodeName = target.nodeName;
|
const nodeName = target.nodeName;
|
||||||
if (nodeName === 'SELECT' || nodeName === 'OPTION')
|
if (nodeName === 'SELECT' || nodeName === 'OPTION')
|
||||||
return true;
|
return true;
|
||||||
if (nodeName === 'INPUT' && ['date'].includes((target as HTMLInputElement).type))
|
if (nodeName === 'INPUT' && ['date', 'range'].includes((target as HTMLInputElement).type))
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1226,6 +1241,13 @@ function asCheckbox(node: Node | null): HTMLInputElement | null {
|
|||||||
return ['checkbox', 'radio'].includes(inputElement.type) ? inputElement : null;
|
return ['checkbox', 'radio'].includes(inputElement.type) ? inputElement : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isRangeInput(node: Node | null): node is HTMLInputElement {
|
||||||
|
if (!node || node.nodeName !== 'INPUT')
|
||||||
|
return false;
|
||||||
|
const inputElement = node as HTMLInputElement;
|
||||||
|
return inputElement.type.toLowerCase() === 'range';
|
||||||
|
}
|
||||||
|
|
||||||
function addEventListener(target: EventTarget, eventName: string, listener: EventListener, useCapture?: boolean): () => void {
|
function addEventListener(target: EventTarget, eventName: string, listener: EventListener, useCapture?: boolean): () => void {
|
||||||
target.addEventListener(eventName, listener, useCapture);
|
target.addEventListener(eventName, listener, useCapture);
|
||||||
const remove = () => {
|
const remove = () => {
|
||||||
|
@ -746,4 +746,75 @@ await page.GetByText("Click me").ClickAsync(new LocatorClickOptions
|
|||||||
Button = MouseButton.Middle,
|
Button = MouseButton.Middle,
|
||||||
});`);
|
});`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should record slider', async ({ page, openRecorder }) => {
|
||||||
|
const recorder = await openRecorder();
|
||||||
|
|
||||||
|
await recorder.setContentAndWait(`<input type="range" min="0" max="10" value="5">`);
|
||||||
|
|
||||||
|
const dragSlider = async () => {
|
||||||
|
const { x, y, width, height } = await page.locator('input').boundingBox();
|
||||||
|
await page.mouse.move(x + width / 2, y + height / 2);
|
||||||
|
await page.mouse.down();
|
||||||
|
await page.mouse.move(x + width, y + height / 2);
|
||||||
|
await page.mouse.up();
|
||||||
|
};
|
||||||
|
|
||||||
|
const [sources] = await Promise.all([
|
||||||
|
recorder.waitForOutput('JavaScript', 'fill'),
|
||||||
|
dragSlider(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await expect(page.locator('input')).toHaveValue('10');
|
||||||
|
|
||||||
|
expect(sources.get('JavaScript')!.text).not.toContain(`
|
||||||
|
await page.getByRole('slider').click();`);
|
||||||
|
|
||||||
|
expect(sources.get('JavaScript')!.text).toContain(`
|
||||||
|
await page.getByRole('slider').fill('10');`);
|
||||||
|
|
||||||
|
expect.soft(sources.get('Python')!.text).toContain(`
|
||||||
|
page.get_by_role("slider").fill("10")`);
|
||||||
|
|
||||||
|
expect.soft(sources.get('Python Async')!.text).toContain(`
|
||||||
|
await page.get_by_role("slider").fill("10")`);
|
||||||
|
|
||||||
|
expect.soft(sources.get('Java')!.text).toContain(`
|
||||||
|
page.getByRole(AriaRole.SLIDER).fill("10")`);
|
||||||
|
|
||||||
|
expect.soft(sources.get('C#')!.text).toContain(`
|
||||||
|
await page.GetByRole(AriaRole.Slider).FillAsync("10");`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should click button with nested div', async ({ page, openRecorder }) => {
|
||||||
|
test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/29067' });
|
||||||
|
|
||||||
|
const recorder = await openRecorder();
|
||||||
|
|
||||||
|
await recorder.setContentAndWait(`<button><div role="none">Submit</div></button>`);
|
||||||
|
|
||||||
|
// we hover the nested div, but it must record the button
|
||||||
|
const locator = await recorder.hoverOverElement('div');
|
||||||
|
expect(locator).toBe(`getByRole('button', { name: 'Submit' })`);
|
||||||
|
|
||||||
|
const [sources] = await Promise.all([
|
||||||
|
recorder.waitForOutput('JavaScript', 'Submit'),
|
||||||
|
recorder.trustedClick(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect.soft(sources.get('JavaScript')!.text).toContain(`
|
||||||
|
await page.getByRole('button', { name: 'Submit' }).click();`);
|
||||||
|
|
||||||
|
expect.soft(sources.get('Python')!.text).toContain(`
|
||||||
|
page.get_by_role("button", name="Submit").click()`);
|
||||||
|
|
||||||
|
expect.soft(sources.get('Python Async')!.text).toContain(`
|
||||||
|
await page.get_by_role("button", name="Submit").click()`);
|
||||||
|
|
||||||
|
expect.soft(sources.get('Java')!.text).toContain(`
|
||||||
|
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Submit")).click()`);
|
||||||
|
|
||||||
|
expect.soft(sources.get('C#')!.text).toContain(`
|
||||||
|
await page.GetByRole(AriaRole.Button, new() { Name = "Submit" }).ClickAsync();`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user