feat(recorder): make it work with [contenteditable] (#19066)

https://github.com/microsoft/playwright/issues/19029
pull/19223/head
Max Schmitt 2 months ago committed by GitHub
parent 1d3feba578
commit 3565d97a36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -283,21 +283,20 @@ class Recorder {
if (this._mode !== 'recording')
return true;
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') {
globalThis.__pw_recorderRecordAction({
name: 'setInputFiles',
selector: this._activeModel!.selector,
signals: [],
files: [...(inputElement.files || [])].map(file => file.name),
});
if (target.nodeName === 'INPUT' && (target as HTMLInputElement).type.toLowerCase() === 'file') {
globalThis.__pw_recorderRecordAction({
name: 'setInputFiles',
selector: this._activeModel!.selector,
signals: [],
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;
}
@ -308,7 +307,7 @@ class Recorder {
name: 'fill',
selector: this._activeModel!.selector,
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');
});
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 }) => {
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');
});
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 }) => {
await page.goto(server.PREFIX + '/input/textarea.html');

Loading…
Cancel
Save