Implement Basic input API
This patch implements Basic Input api: - Page.focus(selector) - focuses element with selector - Page.click(selector) - clicks element with selector - Page.type(text) - types text into a focused element Fixed #43.
This commit is contained in:
parent
3d90ea38a9
commit
d5a91650ae
89
lib/Page.js
89
lib/Page.js
@ -59,6 +59,8 @@ class Page extends EventEmitter {
|
|||||||
this._inPageCallbacks = new Map();
|
this._inPageCallbacks = new Map();
|
||||||
/** @type {?function(!Request)} */
|
/** @type {?function(!Request)} */
|
||||||
this._requestInterceptor = null;
|
this._requestInterceptor = null;
|
||||||
|
/** @type {?Promise<number>} */
|
||||||
|
this._rootNodeIdPromise = null;
|
||||||
|
|
||||||
this._screenshotTaskChain = Promise.resolve();
|
this._screenshotTaskChain = Promise.resolve();
|
||||||
|
|
||||||
@ -73,6 +75,7 @@ class Page extends EventEmitter {
|
|||||||
client.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event));
|
client.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event));
|
||||||
client.on('Page.javascriptDialogOpening', event => this._onDialog(event));
|
client.on('Page.javascriptDialogOpening', event => this._onDialog(event));
|
||||||
client.on('Runtime.exceptionThrown', exception => this._handleException(exception.exceptionDetails));
|
client.on('Runtime.exceptionThrown', exception => this._handleException(exception.exceptionDetails));
|
||||||
|
client.on('DOM.documentUpdated', event => this._rootNodeIdPromise = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -479,6 +482,92 @@ class Page extends EventEmitter {
|
|||||||
async close() {
|
async close() {
|
||||||
await this._client.dispose();
|
await this._client.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {!Promise<number>}
|
||||||
|
*/
|
||||||
|
_rootNodeId() {
|
||||||
|
if (!this._rootNodeIdPromise) {
|
||||||
|
this._rootNodeIdPromise = this._client.send('DOM.getDocument', {
|
||||||
|
depth: 0
|
||||||
|
}).then(obj => obj.root.nodeId);
|
||||||
|
}
|
||||||
|
return this._rootNodeIdPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} selector
|
||||||
|
* @param {!Promise<number>}
|
||||||
|
*/
|
||||||
|
async _querySelector(selector) {
|
||||||
|
return (await this._client.send('DOM.querySelector', {
|
||||||
|
nodeId: await this._rootNodeId(),
|
||||||
|
selector
|
||||||
|
})).nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} selector
|
||||||
|
* @param {!Promise}
|
||||||
|
*/
|
||||||
|
async click(selector) {
|
||||||
|
let boxModel = (await this._client.send('DOM.getBoxModel', {
|
||||||
|
nodeId: await this._querySelector(selector)
|
||||||
|
})).model.content;
|
||||||
|
let x = Math.round((boxModel[0] + boxModel[4]) / (2 * this._screenDPI));
|
||||||
|
let y = Math.round((boxModel[1] + boxModel[5]) / (2 * this._screenDPI));
|
||||||
|
|
||||||
|
this._client.send('Input.dispatchMouseEvent', {
|
||||||
|
type: 'mouseMoved',
|
||||||
|
x, y
|
||||||
|
});
|
||||||
|
this._client.send('Input.dispatchMouseEvent', {
|
||||||
|
type: 'mousePressed',
|
||||||
|
button: 'left',
|
||||||
|
x, y,
|
||||||
|
clickCount: 1
|
||||||
|
});
|
||||||
|
await this._client.send('Input.dispatchMouseEvent', {
|
||||||
|
type: 'mouseReleased',
|
||||||
|
button: 'left',
|
||||||
|
x, y,
|
||||||
|
clickCount: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} selector
|
||||||
|
* @param {!Promise}
|
||||||
|
*/
|
||||||
|
async focus(selector) {
|
||||||
|
await this._client.send('DOM.focus', {
|
||||||
|
nodeId: await this._querySelector(selector)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} text
|
||||||
|
* @param {!Promise}
|
||||||
|
*/
|
||||||
|
async type(text) {
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
let char = text.charAt(i);
|
||||||
|
this._client.send('Input.dispatchKeyEvent', {
|
||||||
|
type: 'keyDown',
|
||||||
|
key: char
|
||||||
|
});
|
||||||
|
this._client.send('Input.dispatchKeyEvent', {
|
||||||
|
type: 'char',
|
||||||
|
text: char,
|
||||||
|
key: char,
|
||||||
|
unmodifiedText: char
|
||||||
|
});
|
||||||
|
await this._client.send('Input.dispatchKeyEvent', {
|
||||||
|
type: 'keyUp',
|
||||||
|
key: char
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
|
15
test/assets/input/button.html
Normal file
15
test/assets/input/button.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Button test</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<button onclick="clicked();">Click target</button>
|
||||||
|
<script>
|
||||||
|
window.result = 'Was not clicked';
|
||||||
|
function clicked() {
|
||||||
|
result = 'Clicked';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
14
test/assets/input/textarea.html
Normal file
14
test/assets/input/textarea.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Textarea test</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<textarea></textarea>
|
||||||
|
<script>
|
||||||
|
window.result = '';
|
||||||
|
let textarea = document.querySelector('textarea');
|
||||||
|
textarea.addEventListener('input', () => result = textarea.value, false);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
21
test/test.js
21
test/test.js
@ -339,6 +339,27 @@ describe('Puppeteer', function() {
|
|||||||
expect(navigatedFrames.length).toBe(1);
|
expect(navigatedFrames.length).toBe(1);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('input', function() {
|
||||||
|
it('should click the button', SX(async function() {
|
||||||
|
await page.navigate(STATIC_PREFIX + '/input/button.html');
|
||||||
|
await page.click('button');
|
||||||
|
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||||
|
}));
|
||||||
|
it('should type into the textarea', SX(async function() {
|
||||||
|
await page.navigate(STATIC_PREFIX + '/input/textarea.html');
|
||||||
|
await page.focus('textarea');
|
||||||
|
await page.type('Type in this text!');
|
||||||
|
expect(await page.evaluate(() => result)).toBe('Type in this text!');
|
||||||
|
}));
|
||||||
|
it('should click the button after navigation ', SX(async function() {
|
||||||
|
await page.navigate(STATIC_PREFIX + '/input/button.html');
|
||||||
|
await page.click('button');
|
||||||
|
await page.navigate(STATIC_PREFIX + '/input/button.html');
|
||||||
|
await page.click('button');
|
||||||
|
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Since Jasmine doesn't like async functions, they should be wrapped
|
// Since Jasmine doesn't like async functions, they should be wrapped
|
||||||
|
Loading…
Reference in New Issue
Block a user