elementHandle.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.ElementHandle = void 0;
  6. exports.convertInputFiles = convertInputFiles;
  7. exports.convertSelectOptionValues = convertSelectOptionValues;
  8. exports.determineScreenshotType = determineScreenshotType;
  9. var _frame = require("./frame");
  10. var _jsHandle = require("./jsHandle");
  11. var _fs = _interopRequireDefault(require("fs"));
  12. var _utilsBundle = require("../utilsBundle");
  13. var _path = _interopRequireDefault(require("path"));
  14. var _utils = require("../utils");
  15. var _fileUtils = require("../utils/fileUtils");
  16. var _writableStream = require("./writableStream");
  17. var _stream = require("stream");
  18. var _util = require("util");
  19. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  20. /**
  21. * Copyright (c) Microsoft Corporation.
  22. *
  23. * Licensed under the Apache License, Version 2.0 (the "License");
  24. * you may not use this file except in compliance with the License.
  25. * You may obtain a copy of the License at
  26. *
  27. * http://www.apache.org/licenses/LICENSE-2.0
  28. *
  29. * Unless required by applicable law or agreed to in writing, software
  30. * distributed under the License is distributed on an "AS IS" BASIS,
  31. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  32. * See the License for the specific language governing permissions and
  33. * limitations under the License.
  34. */
  35. const pipelineAsync = (0, _util.promisify)(_stream.pipeline);
  36. class ElementHandle extends _jsHandle.JSHandle {
  37. static from(handle) {
  38. return handle._object;
  39. }
  40. static fromNullable(handle) {
  41. return handle ? ElementHandle.from(handle) : null;
  42. }
  43. constructor(parent, type, guid, initializer) {
  44. super(parent, type, guid, initializer);
  45. this._elementChannel = void 0;
  46. this._elementChannel = this._channel;
  47. }
  48. asElement() {
  49. return this;
  50. }
  51. async ownerFrame() {
  52. return _frame.Frame.fromNullable((await this._elementChannel.ownerFrame()).frame);
  53. }
  54. async contentFrame() {
  55. return _frame.Frame.fromNullable((await this._elementChannel.contentFrame()).frame);
  56. }
  57. async getAttribute(name) {
  58. const value = (await this._elementChannel.getAttribute({
  59. name
  60. })).value;
  61. return value === undefined ? null : value;
  62. }
  63. async inputValue() {
  64. return (await this._elementChannel.inputValue()).value;
  65. }
  66. async textContent() {
  67. const value = (await this._elementChannel.textContent()).value;
  68. return value === undefined ? null : value;
  69. }
  70. async innerText() {
  71. return (await this._elementChannel.innerText()).value;
  72. }
  73. async innerHTML() {
  74. return (await this._elementChannel.innerHTML()).value;
  75. }
  76. async isChecked() {
  77. return (await this._elementChannel.isChecked()).value;
  78. }
  79. async isDisabled() {
  80. return (await this._elementChannel.isDisabled()).value;
  81. }
  82. async isEditable() {
  83. return (await this._elementChannel.isEditable()).value;
  84. }
  85. async isEnabled() {
  86. return (await this._elementChannel.isEnabled()).value;
  87. }
  88. async isHidden() {
  89. return (await this._elementChannel.isHidden()).value;
  90. }
  91. async isVisible() {
  92. return (await this._elementChannel.isVisible()).value;
  93. }
  94. async dispatchEvent(type, eventInit = {}) {
  95. await this._elementChannel.dispatchEvent({
  96. type,
  97. eventInit: (0, _jsHandle.serializeArgument)(eventInit)
  98. });
  99. }
  100. async scrollIntoViewIfNeeded(options = {}) {
  101. await this._elementChannel.scrollIntoViewIfNeeded(options);
  102. }
  103. async hover(options = {}) {
  104. await this._elementChannel.hover(options);
  105. }
  106. async click(options = {}) {
  107. return await this._elementChannel.click(options);
  108. }
  109. async dblclick(options = {}) {
  110. return await this._elementChannel.dblclick(options);
  111. }
  112. async tap(options = {}) {
  113. return await this._elementChannel.tap(options);
  114. }
  115. async selectOption(values, options = {}) {
  116. const result = await this._elementChannel.selectOption({
  117. ...convertSelectOptionValues(values),
  118. ...options
  119. });
  120. return result.values;
  121. }
  122. async fill(value, options = {}) {
  123. return await this._elementChannel.fill({
  124. value,
  125. ...options
  126. });
  127. }
  128. async selectText(options = {}) {
  129. await this._elementChannel.selectText(options);
  130. }
  131. async setInputFiles(files, options = {}) {
  132. const frame = await this.ownerFrame();
  133. if (!frame) throw new Error('Cannot set input files to detached element');
  134. const converted = await convertInputFiles(files, frame.page().context());
  135. await this._elementChannel.setInputFiles({
  136. ...converted,
  137. ...options
  138. });
  139. }
  140. async focus() {
  141. await this._elementChannel.focus();
  142. }
  143. async type(text, options = {}) {
  144. await this._elementChannel.type({
  145. text,
  146. ...options
  147. });
  148. }
  149. async press(key, options = {}) {
  150. await this._elementChannel.press({
  151. key,
  152. ...options
  153. });
  154. }
  155. async check(options = {}) {
  156. return await this._elementChannel.check(options);
  157. }
  158. async uncheck(options = {}) {
  159. return await this._elementChannel.uncheck(options);
  160. }
  161. async setChecked(checked, options) {
  162. if (checked) await this.check(options);else await this.uncheck(options);
  163. }
  164. async boundingBox() {
  165. const value = (await this._elementChannel.boundingBox()).value;
  166. return value === undefined ? null : value;
  167. }
  168. async screenshot(options = {}) {
  169. const copy = {
  170. ...options,
  171. mask: undefined
  172. };
  173. if (!copy.type) copy.type = determineScreenshotType(options);
  174. if (options.mask) {
  175. copy.mask = options.mask.map(locator => ({
  176. frame: locator._frame._channel,
  177. selector: locator._selector
  178. }));
  179. }
  180. const result = await this._elementChannel.screenshot(copy);
  181. if (options.path) {
  182. await (0, _fileUtils.mkdirIfNeeded)(options.path);
  183. await _fs.default.promises.writeFile(options.path, result.binary);
  184. }
  185. return result.binary;
  186. }
  187. async $(selector) {
  188. return ElementHandle.fromNullable((await this._elementChannel.querySelector({
  189. selector
  190. })).element);
  191. }
  192. async $$(selector) {
  193. const result = await this._elementChannel.querySelectorAll({
  194. selector
  195. });
  196. return result.elements.map(h => ElementHandle.from(h));
  197. }
  198. async $eval(selector, pageFunction, arg) {
  199. const result = await this._elementChannel.evalOnSelector({
  200. selector,
  201. expression: String(pageFunction),
  202. isFunction: typeof pageFunction === 'function',
  203. arg: (0, _jsHandle.serializeArgument)(arg)
  204. });
  205. return (0, _jsHandle.parseResult)(result.value);
  206. }
  207. async $$eval(selector, pageFunction, arg) {
  208. const result = await this._elementChannel.evalOnSelectorAll({
  209. selector,
  210. expression: String(pageFunction),
  211. isFunction: typeof pageFunction === 'function',
  212. arg: (0, _jsHandle.serializeArgument)(arg)
  213. });
  214. return (0, _jsHandle.parseResult)(result.value);
  215. }
  216. async waitForElementState(state, options = {}) {
  217. return await this._elementChannel.waitForElementState({
  218. state,
  219. ...options
  220. });
  221. }
  222. async waitForSelector(selector, options = {}) {
  223. const result = await this._elementChannel.waitForSelector({
  224. selector,
  225. ...options
  226. });
  227. return ElementHandle.fromNullable(result.element);
  228. }
  229. }
  230. exports.ElementHandle = ElementHandle;
  231. function convertSelectOptionValues(values) {
  232. if (values === null) return {};
  233. if (!Array.isArray(values)) values = [values];
  234. if (!values.length) return {};
  235. for (let i = 0; i < values.length; i++) (0, _utils.assert)(values[i] !== null, `options[${i}]: expected object, got null`);
  236. if (values[0] instanceof ElementHandle) return {
  237. elements: values.map(v => v._elementChannel)
  238. };
  239. if ((0, _utils.isString)(values[0])) return {
  240. options: values.map(valueOrLabel => ({
  241. valueOrLabel
  242. }))
  243. };
  244. return {
  245. options: values
  246. };
  247. }
  248. function filePayloadExceedsSizeLimit(payloads) {
  249. return payloads.reduce((size, item) => size + (item.buffer ? item.buffer.byteLength : 0), 0) >= _fileUtils.fileUploadSizeLimit;
  250. }
  251. async function convertInputFiles(files, context) {
  252. const items = Array.isArray(files) ? files.slice() : [files];
  253. if (items.some(item => typeof item === 'string')) {
  254. if (!items.every(item => typeof item === 'string')) throw new Error('File paths cannot be mixed with buffers');
  255. if (context._connection.isRemote()) {
  256. const streams = await Promise.all(items.map(async item => {
  257. const lastModifiedMs = (await _fs.default.promises.stat(item)).mtimeMs;
  258. const {
  259. writableStream: stream
  260. } = await context._wrapApiCall(() => context._channel.createTempFile({
  261. name: _path.default.basename(item),
  262. lastModifiedMs
  263. }), true);
  264. const writable = _writableStream.WritableStream.from(stream);
  265. await pipelineAsync(_fs.default.createReadStream(item), writable.stream());
  266. return stream;
  267. }));
  268. return {
  269. streams
  270. };
  271. }
  272. return {
  273. localPaths: items.map(f => _path.default.resolve(f))
  274. };
  275. }
  276. const payloads = items;
  277. if (filePayloadExceedsSizeLimit(payloads)) throw new Error('Cannot set buffer larger than 50Mb, please write it to a file and pass its path instead.');
  278. return {
  279. payloads
  280. };
  281. }
  282. function determineScreenshotType(options) {
  283. if (options.path) {
  284. const mimeType = _utilsBundle.mime.getType(options.path);
  285. if (mimeType === 'image/png') return 'png';else if (mimeType === 'image/jpeg') return 'jpeg';
  286. throw new Error(`path: unsupported mime type "${mimeType}"`);
  287. }
  288. return options.type;
  289. }