/** * Copyright 2017 Google Inc. All rights reserved. * Modifications copyright (c) Microsoft Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { test as it, expect } from './pageTest'; async function giveItAChanceToFill(page) { for (let i = 0; i < 5; i++) await page.evaluate(() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)))); } it('should fill textarea @smoke', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/textarea.html'); await page.fill('textarea', 'some value'); expect(await page.evaluate(() => window['result'])).toBe('some value'); }); it('should fill input', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/textarea.html'); await page.fill('input', 'some value'); expect(await page.evaluate(() => window['result'])).toBe('some value'); }); it('should throw on unsupported inputs', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/textarea.html'); for (const type of ['button', 'checkbox', 'file', 'image', 'radio', 'reset', 'submit']) { await page.$eval('input', (input, type) => input.setAttribute('type', type), type); let error = null; await page.fill('input', '').catch(e => error = e); expect(error.message).toContain(`Input of type "${type}" cannot be filled`); } }); it('should fill different input types', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/textarea.html'); for (const type of ['password', 'search', 'tel', 'text', 'url', 'invalid-type']) { await page.$eval('input', (input, type) => input.setAttribute('type', type), type); await page.fill('input', 'text ' + type); expect(await page.evaluate(() => window['result'])).toBe('text ' + type); } }); it('should fill range input', async ({ page }) => { await page.setContent(''); await page.fill('input', '42'); expect(await page.$eval('input', input => input.value)).toBe('42'); }); it('should throw on incorrect range value', async ({ page }) => { await page.setContent(''); const error1 = await page.fill('input', 'foo').catch(e => e); expect(error1.message).toContain('Malformed value'); const error2 = await page.fill('input', '200').catch(e => e); expect(error2.message).toContain('Malformed value'); const error3 = await page.fill('input', '15.43').catch(e => e); expect(error3.message).toContain('Malformed value'); }); it('should fill color input', async ({ page }) => { await page.setContent(''); await page.fill('input', '#aaaaaa'); expect(await page.$eval('input', input => input.value)).toBe('#aaaaaa'); }); it('should throw on incorrect color value', async ({ page, browserName, isWindows }) => { it.skip(browserName === 'webkit' && isWindows, 'WebKit win does not support color inputs'); await page.setContent(''); const error1 = await page.fill('input', 'badvalue').catch(e => e); expect(error1.message).toContain('Malformed value'); }); it('should fill date input after clicking', async ({ page, server }) => { await page.setContent(''); await page.click('input'); await page.fill('input', '2020-03-02'); expect(await page.$eval('input', input => input.value)).toBe('2020-03-02'); }); for (const [type, value] of Object.entries({ 'color': '#aaaaaa', 'date': '2020-03-02', 'time': '13:15', 'datetime-local': '2020-03-02T13:15:30', 'month': '2020-03', 'range': '42', 'week': '2020-W50' })) { it(`input event.composed should be true and cross shadow dom boundary - ${type}`, async ({ page, server, browserName, isWindows }) => { it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/28726' }); it.skip(browserName !== 'chromium' && ['month', 'week'].includes(type), 'Some browser/platforms do not implement certain input types'); it.skip(browserName === 'webkit' && isWindows && ['color', 'date', 'time', 'datetime-local'].includes(type), 'Some browser/platforms do not implement certain input types'); await page.goto(server.EMPTY_PAGE); await page.setContent(``); await page.locator('body').evaluate(select => { (window as any).firedBodyEvents = []; for (const event of ['input', 'change']) { select.addEventListener(event, e => { (window as any).firedBodyEvents.push(e.type + ':' + e.composed); }, false); } }); await page.locator('input').evaluate(select => { (window as any).firedEvents = []; for (const event of ['input', 'change']) { select.addEventListener(event, e => { (window as any).firedEvents.push(e.type + ':' + e.composed); }, false); } }); await page.locator('input').fill(value); expect(await page.evaluate(() => window['firedEvents'])).toEqual(['input:true', 'change:false']); expect(await page.evaluate(() => window['firedBodyEvents'])).toEqual(['input:true']); }); } it('should throw on incorrect date', async ({ page, browserName }) => { it.skip(browserName === 'webkit', 'WebKit does not support date inputs'); await page.setContent(''); const error = await page.fill('input', '2020-13-05').catch(e => e); expect(error.message).toContain('Malformed value'); }); it('should fill time input', async ({ page }) => { await page.setContent(''); await page.fill('input', '13:15'); expect(await page.$eval('input', input => input.value)).toBe('13:15'); }); it('should fill month input', async ({ page }) => { await page.setContent(''); await page.fill('input', '2020-07'); expect(await page.$eval('input', input => input.value)).toBe('2020-07'); }); it('should throw on incorrect month', async ({ page, browserName }) => { it.skip(browserName !== 'chromium', 'Only Chromium supports month inputs'); await page.setContent(''); const error = await page.fill('input', '2020-13').catch(e => e); expect(error.message).toContain('Malformed value'); }); it('should fill week input', async ({ page }) => { await page.setContent(''); await page.fill('input', '2020-W50'); expect(await page.$eval('input', input => input.value)).toBe('2020-W50'); }); it('should throw on incorrect week', async ({ page, browserName }) => { it.skip(browserName !== 'chromium', 'Only Chromium supports week inputs'); await page.setContent(''); const error = await page.fill('input', '2020-123').catch(e => e); expect(error.message).toContain('Malformed value'); }); it('should throw on incorrect time', async ({ page, browserName }) => { it.skip(browserName === 'webkit', 'WebKit does not support time inputs'); await page.setContent(''); const error = await page.fill('input', '25:05').catch(e => e); expect(error.message).toContain('Malformed value'); }); it('should fill datetime-local input', async ({ page, server }) => { await page.setContent(''); await page.fill('input', '2020-03-02T05:15'); expect(await page.$eval('input', input => input.value)).toBe('2020-03-02T05:15'); }); it('should throw on incorrect datetime-local', async ({ page, server, browserName }) => { it.skip(browserName !== 'chromium', 'Only Chromium supports datetime-local inputs'); await page.setContent(''); const error = await page.fill('input', 'abc').catch(e => e); expect(error.message).toContain('Malformed value'); }); it('should fill contenteditable', async ({ page, server }) => { await page.goto(server.PREFIX + '/input/textarea.html'); await page.fill('div[contenteditable]', '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(`
`); 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'); await page.$eval('input', input => input.value = 'value one'); await page.fill('input', 'another value'); expect(await page.evaluate(() => window['result'])).toBe('another value'); await page.$eval('input', input => { input.selectionStart = 1; input.selectionEnd = 2; }); await page.fill('input', 'maybe this one'); expect(await page.evaluate(() => window['result'])).toBe('maybe this one'); await page.$eval('div[contenteditable]', div => { div.innerHTML = 'some text some more text and even more text'; const range = document.createRange(); range.selectNodeContents(div.querySelector('span')); const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); }); await page.fill('div[contenteditable]', 'replace with this'); expect(await page.$eval('div[contenteditable]', div => div.textContent)).toBe('replace with this'); }); it('should throw nice error without injected script stack when element is not an ', async ({ page, server }) => { let error = null; await page.goto(server.PREFIX + '/input/textarea.html'); await page.fill('body', '').catch(e => error = e); expect(error.message).toContain('page.fill: Error: Element is not an ,