Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import math 

2import time 

3import urllib.parse 

4import yaml 

5from selenium.webdriver.support.ui import WebDriverWait 

6from selenium.webdriver.support import expected_conditions 

7from selenium.webdriver.common.action_chains import ActionChains 

8from selenium.webdriver.common.alert import Alert 

9from selenium.webdriver.support.select import Select 

10 

11 

12open_schema = yaml.safe_load(""" 

13oneOf: 

14 - type: string 

15 - type: object 

16 properties: 

17 url: {type: string} 

18 query: {type: object} 

19 required: [url] 

20""") 

21 

22 

23def Base_open(self, param): 

24 """ 

25 - name: open google 

26 open: https://www.google.com 

27 - name: open google 

28 open: 

29 url: https://www.google.com/search 

30 query: 

31 q: keyword1 

32 """ 

33 self.log.debug("open %s", param) 

34 if isinstance(param, str): 

35 self.driver.get(param) 

36 elif isinstance(param, dict): 

37 url = param.get("url", None) 

38 if url is None: 

39 raise Exception("cannot find open.url: %s" % (param)) 

40 query = param.get("query", {}) 

41 qstr = urllib.parse.urlencode(query) 

42 if qstr != "": 

43 url += "?" 

44 url += qstr 

45 self.driver.get(url) 

46 return self.driver.current_url 

47 

48 

49screenshot_schema = yaml.safe_load(""" 

50oneOf: 

51 - type: string 

52 - allOf: 

53 - type: object 

54 properties: 

55 output: {type: string} 

56 optimize: {type: boolean} 

57 archive: {type: string} 

58 crop: 

59 oneOf: 

60 - type: string 

61 enum: [auto] 

62 - type: array 

63 items: {type: integer} 

64 resize: 

65 type: array 

66 items: {type: integer} 

67 - "$ref": "#/definitions/common/locator" 

68""") 

69 

70 

71def Base_screenshot(self, param): 

72 """ 

73 - name: take screenshot 1 

74 screenshot: shot1.png 

75 - name: take screenshot 2 

76 screenshot: 

77 output: shot2.png 

78 optimize: true 

79 archive: images.tar 

80 crop: auto 

81 resize: [800, 600] 

82 """ 

83 self.log.debug("screenshot %s", param) 

84 if isinstance(param, str): 

85 self.saveshot(param) 

86 return param 

87 elif isinstance(param, dict): 

88 output = param.get("output") 

89 if output is None: 

90 # generate filename 

91 ts = time.time() 

92 msec = math.modf(ts)[0] * 1000 

93 output = param.get("prefix", "") 

94 output += time.strftime("%Y%m%d_%H%M%S", time.localtime(ts)) 

95 output += "_%03d.png" % (msec) 

96 self.log.debug("filename generated %s", output) 

97 self.saveshot(output) 

98 elem = self.findmany2one(param) 

99 if elem is not None: 

100 x1 = elem.location['x'] 

101 y1 = elem.location['y'] 

102 x2 = x1 + elem.size['width'] 

103 y2 = y1 + elem.size['height'] 

104 nparam = { 

105 "input": output, 

106 "size": [x1, y1, x2, y2], 

107 } 

108 self.do_image_crop(nparam) 

109 if param.get("crop", None) is not None: 

110 nparam = { 

111 "input": output, 

112 "size": param.get("crop"), 

113 } 

114 self.do_image_crop(nparam) 

115 if param.get("resize", None) is not None: 

116 nparam = { 

117 "input": output, 

118 "size": param.get("resize"), 

119 } 

120 self.do_image_resize(nparam) 

121 if param.get("optimize", False): 

122 nparam = { 

123 "input": output, 

124 } 

125 self.do_image_optimize(nparam) 

126 if param.get("archive", False): 

127 nparam = { 

128 "output": param.get("archive"), 

129 "input": output, 

130 "delete": True, 

131 } 

132 self.do_image_archive(nparam) 

133 return output 

134 

135 

136click_schema = {"$ref": "#/definitions/common/locator"} 

137 

138 

139def Base_click(self, param): 

140 """ 

141 - name: click1 

142 click: 

143 id: elementid1 

144 - name: click2 

145 click: 

146 xpath: //div[1] 

147 """ 

148 self.findmany2one(param).click() 

149 

150 

151submit_schema = click_schema 

152 

153 

154def Base_submit(self, param): 

155 """ 

156 - name: submit element 

157 submit: 

158 id: elementid1 

159 """ 

160 self.findmany2one(param).submit() 

161 

162 

163def Base_waitfor(self, param): 

164 waiter = WebDriverWait(self.driver, param.get("timeout", 10)) 

165 simple_fn = [ 

166 "title_is", "title_contains", "url_changes", "url_contains", "url_matches", 

167 "url_to_be", "number_of_windows_to_be", 

168 ] 

169 if "alert_is_present" in param: 

170 return waiter.until(expected_conditions.alert_is_present()) 

171 for f in simple_fn: 

172 if f in param: 

173 return waiter.until(getattr(expected_conditions, f)(param.get(f))) 

174 locator_fn = [ 

175 "element_located_to_be_selected", "element_to_be_clickable", 

176 "frame_to_be_available_and_switch_to_it", 

177 "invisibility_of_element_located", "presence_of_all_elements_located", 

178 "presence_of_element_located", "visibility_of_all_elements_located", 

179 "visibility_of_any_elements_located", "visibility_of_element_located" 

180 ] 

181 for f in locator_fn: 

182 if f in param: 

183 loc = self.getlocator(param) 

184 if len(loc) != 2 or loc[0] is None: 

185 raise Exception("locator not set: %s" % (param)) 

186 return waiter.until(getattr(expected_conditions, f)(loc)) 

187 locator_and_fn = { 

188 "text_to_be_present_in_element": None, 

189 "text_to_be_present_in_element_value": None, 

190 "element_located_selection_state_to_be": "selected", 

191 } 

192 for f, v in locator_and_fn.items(): 

193 if f in param: 

194 if v is None: 

195 arg = self.getvalue(param) 

196 else: 

197 arg = param.get(v) 

198 if arg is None: 

199 raise Exception("missing argument %s: param=%s" % (v, param)) 

200 loc = self.getlocator(param) 

201 if len(loc) != 2 or loc[0] is None: 

202 raise Exception("locator not set: %s" % (param)) 

203 return waiter.until(getattr(expected_conditions, f)(loc, arg)) 

204 # other conditions: 

205 # element_selection_state_to_be, element_to_be_selected, 

206 # new_window_is_opened, staleness_of, visibility_of 

207 raise Exception("not implemented: param=%s" % (param)) 

208 

209 

210script_schema = yaml.safe_load(""" 

211oneOf: 

212 - type: string 

213 - type: array 

214 items: {type: string} 

215 - type: object 

216 properties: 

217 file: {type: string} 

218 required: [file] 

219""") 

220 

221 

222def Base_script(self, param): 

223 """ 

224 - name: execute js 

225 script: 'alert("hello")' 

226 """ 

227 if isinstance(param, (list, tuple)): 

228 for s in param: 

229 self.driver.execute_script(s) 

230 elif isinstance(param, str): 

231 self.driver.execute_script(param) 

232 elif isinstance(param, dict): 

233 fname = param.get("file", None) 

234 if fname is None: 

235 raise Exception("no file") 

236 with open(fname) as f: 

237 self.driver.execute_script(f.read()) 

238 else: 

239 raise Exception("parameter error: %s" % (param)) 

240 

241 

242history_schema = yaml.safe_load(""" 

243oneOf: 

244 - type: string 

245 enum: [forward, fwd, f, backward, back, b] 

246 - type: array 

247 items: 

248 type: string 

249 enum: [forward, fwd, f, backward, back, b] 

250""") 

251 

252 

253def Base_history(self, param): 

254 """ 

255 - name: back 

256 history: [back, fwd, back, refresh] 

257 """ 

258 fwd = ("forward", "fwd", "f") 

259 back = ("backward", "back", "b") 

260 refresh = ("refresh", "reload", "r") 

261 if isinstance(param, (tuple, list)): 

262 for d in param: 

263 if d in fwd: 

264 self.driver.forward() 

265 elif d in back: 

266 self.driver.back() 

267 elif d in refresh: 

268 self.driver.refresh() 

269 else: 

270 raise Exception("no such direction: %s" % (d)) 

271 elif isinstance(param, str): 

272 if param in fwd: 

273 self.driver.forward() 

274 elif param in back: 

275 self.driver.back() 

276 elif param in refresh: 

277 self.driver.refresh() 

278 else: 

279 raise Exception("no such direction: %s" % (param)) 

280 else: 

281 raise Exception("history: not supported direction: %s" % (param)) 

282 

283 

284sendKeys_schema = yaml.safe_load(""" 

285allOf: 

286 - "$ref": "#/definitions/common/locator" 

287 - "$ref": "#/definitions/common/textvalue" 

288 - type: object 

289 properties: 

290 clear: {type: boolean} 

291""") 

292 

293 

294def Base_sendKeys(self, param): 

295 """ 

296 - name: input username 

297 sendKeys: 

298 text: user1 

299 id: elementid1 

300 - name: input password 

301 sendKeys: 

302 password: site/password 

303 # get text from $(pass site/password) 

304 id: elementid2 

305 """ 

306 clear = param.get("clear", False) 

307 txt = self.getvalue(param) 

308 if txt is None: 

309 raise Exception("text not set: param=%s" % (param)) 

310 elem = self.findmany2one(param) 

311 if clear: 

312 elem.clear() 

313 elem.send_keys(txt) 

314 return self.return_element(param, elem) 

315 

316 

317setTextValue_schema = yaml.safe_load(""" 

318allOf: 

319 - "$ref": "#/definitions/common/locator" 

320 - "$ref": "#/definitions/common/textvalue" 

321""") 

322 

323 

324def Base_setTextValue(self, param): 

325 """ 

326 - name: input username 

327 setTextValue: 

328 text: | 

329 multi line text1 

330 multi line text2 

331 id: elementid1 

332 """ 

333 txt = self.getvalue(param) 

334 if txt is None: 

335 raise Exception("text not set: param=%s" % (param)) 

336 elem = self.findmany2one(param) 

337 self.driver.execute_script("arguments[0].value = arguments[1];", elem, txt) 

338 return self.return_element(param, elem) 

339 

340 

341save_schema = yaml.safe_load(""" 

342allOf: 

343 - type: object 

344 properties: 

345 mode: 

346 type: string 

347 enum: ["source", "source_outer", "text", "title"] 

348 output: {type: string} 

349 - "$ref": "#/definitions/common/locator" 

350""") 

351 

352 

353def Base_save(self, param): 

354 """ 

355 - name: save page title 

356 save: 

357 mode: title 

358 output: title.txt 

359 - name: save page content 

360 save: 

361 mode: source 

362 id: element1 

363 - name: copy page content to variable 

364 save: 

365 mode: text 

366 id: element2 

367 register: title1 

368 """ 

369 mode = param.get("mode", "source") 

370 locator = self.getlocator(param) 

371 if mode == "source": 

372 if locator[0] is None: 

373 txt = [self.driver.page_source] 

374 else: 

375 txt = [] 

376 for p in self.findmany(param): 

377 txt.append(p.get_attribute("innerHTML")) 

378 elif mode == "source_outer": 

379 if locator[0] is None: 

380 txt = [self.driver.page_source] 

381 else: 

382 txt = [] 

383 for p in self.findmany(param): 

384 txt.append(p.get_attribute("outerHTML")) 

385 elif mode == "title": 

386 txt = [self.driver.title] 

387 elif mode == "text": 

388 if locator[0] is None: 

389 txt = [self.driver.find_element_by_xpath("/html").text] 

390 else: 

391 txt = [] 

392 for p in self.findmany(param): 

393 txt.append(p.text) 

394 output = param.get("output", None) 

395 if output is not None: 

396 with open(output, "w") as f: 

397 f.write("\n".join(txt)) 

398 return txt 

399 

400 

401dragdrop_schema = yaml.safe_load(""" 

402type: object 

403properties: 

404 src: {"$ref": "#/definitions/common/locator"} 

405 dst: {"$ref": "#/definitions/common/locator"} 

406""") 

407 

408 

409def Base_dragdrop(self, param): 

410 """ 

411 - name: drag and drop 

412 dragdrop: 

413 src: 

414 xpath: //div[1] 

415 dst: 

416 select: "$.x.y.z" 

417 """ 

418 src = self.findmany2one(param.get("src")) 

419 dst = self.findmany2one(param.get("dst")) 

420 ActionChains(self.driver).drag_and_drop(src, dst).perform() 

421 

422 

423switch_schema = yaml.safe_load(""" 

424oneOf: 

425 - type: string 

426 enum: [default] 

427 - type: boolean 

428 - type: "null" 

429 - type: object 

430 properties: 

431 window: {type: string} 

432 frame: {type: string} 

433""") 

434 

435 

436def Base_switch(self, param): 

437 """ 

438 - name: switch window 

439 switch: 

440 window: win1 

441 - name: switch frame 

442 switch: 

443 frame: frm1 

444 - name: switch to default window 

445 switch: default 

446 """ 

447 if param in ("default", None, {}, True): 

448 self.driver.switch_to_default_content() 

449 elif "window" in param: 

450 self.driver.switch_to_window(param.get("window")) 

451 elif "frame" in param: 

452 self.driver.switch_to_frame(param.get("frame")) 

453 

454 

455def Base_dropfile(self, param): 

456 """ 

457 - name: drop file 

458 dropfile: 

459 filename: /path/to/file 

460 id: element1 

461 """ 

462 raise Exception("not implemented yet") 

463 

464 

465deletecookie_schema = yaml.safe_load(""" 

466oneOf: 

467 - type: array 

468 items: {type: string} 

469 - type: string 

470""") 

471 

472 

473def Base_deletecookie(self, param): 

474 """ 

475 - name: delete all cookie 

476 deletecookie: all 

477 - name: clear cookie a, b, c 

478 deletecookie: [a, b, c] 

479 """ 

480 if param == "all": 

481 self.driver.delete_all_cookies() 

482 elif isinstance(param, (tuple, list)): 

483 for c in param: 

484 self.driver.delete_cookie(c) 

485 elif isinstance(param, str): 

486 self.driver.delete_cookie(param) 

487 else: 

488 raise Exception("invalid argument") 

489 

490 

491alertOK_schema = {"type": "boolean"} 

492 

493 

494def Base_alertOK(self, param): 

495 """ 

496 - name: accept alert 

497 alertOK: true 

498 - name: cancel alert 

499 alertOK: false 

500 """ 

501 if isinstance(param, bool): 

502 if param: 

503 Alert(self.driver).accept() 

504 else: 

505 Alert(self.driver).dismiss() 

506 

507 

508auth_schema = yaml.safe_load(""" 

509type: object 

510properties: 

511 username: {type: string} 

512 password: {type: string} 

513""") 

514 

515 

516def Base_auth(self, param): 

517 """ 

518 - name: basic/digest auth 

519 auth: 

520 username: user1 

521 password: password1 

522 """ 

523 user = param.get("username", "") 

524 passwd = param.get("password", "") 

525 Alert(self.driver).authenticate(user, passwd) 

526 

527 

528select_schema = yaml.safe_load(""" 

529allOf: 

530 - "$ref": "#/definitions/common/locator" 

531 - type: object 

532 properties: 

533 by_index: {type: integer} 

534 by_value: {type: string} 

535 by_text: {type: string} 

536 all: {type: boolean} 

537 return: 

538 type: string 

539 enum: [selected, first, all] 

540""") 

541 

542 

543def Base_select(self, param): 

544 """ 

545 - name: select 1st 

546 select: 

547 id: element1 

548 by_index: 1 

549 - name: select by value 

550 select: 

551 id: element1 

552 by_value: value1 

553 - name: select by visible text 

554 select: 

555 id: element1 

556 by_text: "text 1" 

557 """ 

558 elem = self.findone(param) 

559 if elem is None: 

560 raise Exception("element not found: %s" % (param)) 

561 flag = param.get("deselect", False) 

562 sel = Select(elem) 

563 if "by_index" in param: 

564 if flag: 

565 sel.deselect_by_index(param.get("by_index")) 

566 else: 

567 sel.select_by_index(param.get("by_index")) 

568 elif "by_value" in param: 

569 if flag: 

570 sel.deselect_by_value(param.get("by_value")) 

571 else: 

572 sel.select_by_value(param.get("by_value")) 

573 elif "by_text" in param: 

574 if flag: 

575 sel.deselect_by_visible_text(param.get("by_text")) 

576 else: 

577 sel.select_by_visible_text(param.get("by_text")) 

578 elif param.get("all", False): 

579 if flag: 

580 sel.deselect_all() 

581 else: 

582 sel.select_all() 

583 retp = param.get("return", "selected") 

584 if retp == "selected": 

585 res = sel.all_selected_options 

586 elif retp == "first": 

587 res = sel.first_selected_option 

588 elif retp == "all": 

589 res = sel.options 

590 else: 

591 return 

592 return self.return_element(param, res) 

593 

594 

595scroll_schema = yaml.safe_load(""" 

596anyOf: 

597 - "$ref": "#/definitions/common/locator" 

598 - type: object 

599 properties: 

600 relative: 

601 type: array 

602 items: {type: integer} 

603 absolute: 

604 type: array 

605 items: {type: integer} 

606 percent: 

607 type: array 

608 items: {type: number} 

609 position: 

610 type: string 

611 enum: [top, bottom, right, left, topright, topleft, bottomright, bottomleft] 

612""") 

613 

614 

615def scrollto(fn, x, y): 

616 return "window.%s(%s,%s)" % (fn, x, y) 

617 

618 

619def Base_scroll(self, param): 

620 """ 

621 - name: scroll down 100 pixel 

622 scroll: 

623 relative: [0, 100] 

624 - name: scroll to pixel 

625 scroll: 

626 absolute: [0, 100] 

627 - name: scroll to percent 

628 scroll: 

629 percent: [0, 50] 

630 - name: scroll to position 

631 scroll: 

632 position: bottom 

633 - name: scroll to element 

634 scroll: 

635 id: element1 

636 """ 

637 xmax, ymax = "document.body.scrollWidth", "document.body.scrollHeight" 

638 

639 relative = param.get("relative") 

640 if relative is not None and isinstance(relative, (tuple, list)) and len(relative) == 2: 

641 self.driver.execute_script(scrollto("scrollBy", relative[0], relative[1])) 

642 absolute = param.get("absolute") 

643 if absolute is not None and isinstance(absolute, (tuple, list)) and len(absolute) == 2: 

644 self.driver.execute_script(scrollto("scrollTo", absolute[0], absolute[1])) 

645 percent = param.get("percent") 

646 if percent is not None and isinstance(percent, (tuple, list)) and len(percent) == 2: 

647 self.driver.execute_script(scrollto("scrollTo", 

648 "%s*%f" % (xmax, percent[0] / 100.0), 

649 "%s*%f" % (ymax, percent[1] / 100.0))) 

650 pos = param.get("position") 

651 if pos in ("bottom", "bottomleft"): 

652 self.driver.execute_script(scrollto("scrollTo", 0, ymax)) 

653 elif pos in ("right", "topright"): 

654 self.driver.execute_script(scrollto("scrollTo", xmax, 0)) 

655 elif pos in ("bottomright",): 

656 self.driver.execute_script(scrollto("scrollTo", xmax, ymax)) 

657 elif pos in ("top", "topleft", "left"): 

658 self.driver.execute_script(scrollto("scrollTo", 0, 0)) 

659 locator = self.getlocator(param) 

660 if locator[0] is not None: 

661 elem = self.findmany2one(param) 

662 if elem is not None: 

663 self.driver.execute_script("arguments[0].scrollIntoView();", elem) 

664 

665 

666def Base_shutdown(self, params): 

667 """ 

668 - name: shutdown webdriver 

669 shutdown: null 

670 """ 

671 self.shutdown_driver() 

672 

673 

674def Base_browser_setting(self, params): 

675 restart = params.pop("restart", False) 

676 copt = params.pop("options", {}) 

677 self.browser_args.update(params) 

678 if len(copt) != 0: 

679 opt = self.get_options() 

680 for k, v in copt.items(): 

681 if hasattr(opt, k) and callable(getattr(opt, k)): 

682 getattr(opt, k)(v) 

683 else: 

684 self.log.error("no such option: %s(%s): %s", k, v, dir(opt)) 

685 raise Exception("no such option: %s" % (k)) 

686 self.browser_args["options"] = opt 

687 if restart: 

688 self.do_shutdown({})