feat(recorder): make it work with [contenteditable] (#19066)
https://github.com/microsoft/playwright/issues/19029
This commit is contained in:
parent
1d3feba578
commit
3565d97a36
|
|
@ -283,21 +283,20 @@ class Recorder {
|
||||||
if (this._mode !== 'recording')
|
if (this._mode !== 'recording')
|
||||||
return true;
|
return true;
|
||||||
const target = this._deepEventTarget(event);
|
const target = this._deepEventTarget(event);
|
||||||
if (['INPUT', 'TEXTAREA'].includes(target.nodeName)) {
|
|
||||||
const inputElement = target as HTMLInputElement;
|
|
||||||
const elementType = (inputElement.type || '').toLowerCase();
|
|
||||||
if (['checkbox', 'radio'].includes(elementType)) {
|
|
||||||
// Checkbox is handled in click, we can't let input trigger on checkbox - that would mean we dispatched click events while recording.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elementType === 'file') {
|
if (target.nodeName === 'INPUT' && (target as HTMLInputElement).type.toLowerCase() === 'file') {
|
||||||
globalThis.__pw_recorderRecordAction({
|
globalThis.__pw_recorderRecordAction({
|
||||||
name: 'setInputFiles',
|
name: 'setInputFiles',
|
||||||
selector: this._activeModel!.selector,
|
selector: this._activeModel!.selector,
|
||||||
signals: [],
|
signals: [],
|
||||||
files: [...(inputElement.files || [])].map(file => file.name),
|
files: [...((target as HTMLInputElement).files || [])].map(file => file.name),
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['INPUT', 'TEXTAREA'].includes(target.nodeName) || target.isContentEditable) {
|
||||||
|
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.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -308,7 +307,7 @@ class Recorder {
|
||||||
name: 'fill',
|
name: 'fill',
|
||||||
selector: this._activeModel!.selector,
|
selector: this._activeModel!.selector,
|
||||||
signals: [],
|
signals: [],
|
||||||
text: inputElement.value,
|
text: target.isContentEditable ? target.innerText : (target as HTMLInputElement).value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -306,6 +306,23 @@ test.describe('cli codegen', () => {
|
||||||
expect(message.text()).toBe('John');
|
expect(message.text()).toBe('John');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should fill [contentEditable]', async ({ page, openRecorder }) => {
|
||||||
|
const recorder = await openRecorder();
|
||||||
|
|
||||||
|
await recorder.setContentAndWait(`<div id="content" contenteditable="" oninput="console.log(content.innerText)"/>`);
|
||||||
|
const locator = await recorder.focusElement('div');
|
||||||
|
expect(locator).toBe(`locator('#content')`);
|
||||||
|
|
||||||
|
const [message, sources] = await Promise.all([
|
||||||
|
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
||||||
|
recorder.waitForOutput('JavaScript', 'fill'),
|
||||||
|
page.fill('div', 'John Doe')
|
||||||
|
]);
|
||||||
|
expect(sources.get('JavaScript').text).toContain(`
|
||||||
|
await page.locator('#content').fill('John Doe');`);
|
||||||
|
expect(message.text()).toBe('John Doe');
|
||||||
|
});
|
||||||
|
|
||||||
test('should press', async ({ page, openRecorder }) => {
|
test('should press', async ({ page, openRecorder }) => {
|
||||||
const recorder = await openRecorder();
|
const recorder = await openRecorder();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,15 @@ it('should fill contenteditable', async ({ page, server }) => {
|
||||||
expect(await page.$eval('div[contenteditable]', div => div.textContent)).toBe('some value');
|
expect(await page.$eval('div[contenteditable]', div => div.textContent)).toBe('some value');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should fill contenteditable with new lines', async ({ page, server, browserName }) => {
|
||||||
|
it.fixme(browserName === 'firefox', 'Firefox does not handle new lines in contenteditable');
|
||||||
|
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
await page.setContent(`<div contenteditable="true"></div>`);
|
||||||
|
await page.locator('div[contenteditable]').fill('John\nDoe');
|
||||||
|
expect(await page.locator('div[contenteditable]').innerText()).toBe('John\nDoe');
|
||||||
|
});
|
||||||
|
|
||||||
it('should fill elements with existing value and selection', async ({ page, server }) => {
|
it('should fill elements with existing value and selection', async ({ page, server }) => {
|
||||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue