From ed984ac47983ae40cbb2e532b58f3a9c9ef1afdc Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Thu, 21 Feb 2019 13:10:15 -0800 Subject: [PATCH] chore(firefox): kill original puppeteer-firefox tests (#4047) When we started working on Puppeteer-Firefox, we forked original Puppeteer testsuite. This patch concludes the effort to merge testsuites back together. Fixes #3889 --- .../puppeteer-firefox/completeness.sh | 7 - experimental/puppeteer-firefox/package.json | 25 -- .../test/assets/consolelog.html | 11 - .../puppeteer-firefox/test/assets/csp.html | 1 - .../test/assets/detect-touch.html | 12 - .../test/assets/digits/0.png | Bin 434 -> 0 bytes .../test/assets/digits/1.png | Bin 346 -> 0 bytes .../test/assets/digits/2.png | Bin 413 -> 0 bytes .../test/assets/digits/3.png | Bin 434 -> 0 bytes .../test/assets/digits/4.png | Bin 403 -> 0 bytes .../test/assets/digits/5.png | Bin 422 -> 0 bytes .../test/assets/digits/6.png | Bin 445 -> 0 bytes .../test/assets/digits/7.png | Bin 387 -> 0 bytes .../test/assets/digits/8.png | Bin 447 -> 0 bytes .../test/assets/digits/9.png | Bin 437 -> 0 bytes .../puppeteer-firefox/test/assets/empty.html | 0 .../puppeteer-firefox/test/assets/empty2.html | 0 .../puppeteer-firefox/test/assets/error.html | 15 - .../test/assets/es6/es6import.js | 2 - .../test/assets/es6/es6module.js | 1 - .../test/assets/es6/es6pathimport.js | 2 - .../test/assets/frames/frame.html | 8 - .../test/assets/frames/frameset.html | 8 - .../test/assets/frames/nested-frames.html | 25 -- .../test/assets/frames/one-frame.html | 1 - .../test/assets/frames/script.js | 1 - .../test/assets/frames/style.css | 3 - .../test/assets/frames/two-frames.html | 13 - .../test/assets/global-var.html | 3 - .../puppeteer-firefox/test/assets/grid.html | 52 --- .../test/assets/injectedfile.js | 2 - .../test/assets/injectedstyle.css | 3 - .../test/assets/input/button.html | 16 - .../test/assets/input/checkbox.html | 42 -- .../test/assets/input/keyboard.html | 44 -- .../test/assets/input/scrollable.html | 23 - .../test/assets/input/select.html | 69 --- .../test/assets/input/textarea.html | 15 - .../puppeteer-firefox/test/assets/mobile.html | 1 - .../test/assets/modernizr.js | 3 - .../test/assets/offscreenbuttons.html | 36 -- .../test/assets/one-style.css | 3 - .../test/assets/one-style.html | 2 - .../test/assets/tamperable.html | 3 - .../puppeteer-firefox/test/assets/title.html | 1 - .../test/assets/wrappedlink.html | 32 -- .../puppeteer-firefox/test/browser.spec.js | 30 -- .../test/browsercontext.spec.js | 160 ------- .../test/chromiumonly.spec.js | 24 - .../puppeteer-firefox/test/click.spec.js | 211 --------- .../puppeteer-firefox/test/dialog.spec.js | 39 -- .../test/elementhandle.spec.js | 103 ----- .../puppeteer-firefox/test/emulation.spec.js | 86 ---- .../puppeteer-firefox/test/evaluation.spec.js | 181 -------- .../test/firefoxonly.spec.js | 24 - .../puppeteer-firefox/test/frame.spec.js | 137 ------ .../test/golden-chromium/grid-cell-0.png | Bin 462 -> 0 bytes .../test/golden-chromium/grid-cell-1.png | Bin 288 -> 0 bytes .../screenshot-clip-odd-size.png | Bin 81 -> 0 bytes .../golden-chromium/screenshot-clip-rect.png | Bin 1972 -> 0 bytes .../screenshot-element-bounding-box.png | Bin 461 -> 0 bytes ...creenshot-element-larger-than-viewport.png | Bin 2807 -> 0 bytes .../screenshot-element-padding-border.png | Bin 168 -> 0 bytes .../screenshot-element-rotate.png | Bin 2342 -> 0 bytes .../screenshot-element-scrolled-into-view.png | Bin 168 -> 0 bytes .../screenshot-grid-fullpage.png | Bin 75053 -> 0 bytes .../screenshot-offscreen-clip.png | Bin 1524 -> 0 bytes .../golden-chromium/screenshot-sanity.png | Bin 36283 -> 0 bytes .../test/golden-chromium/transparent.png | Bin 119 -> 0 bytes .../test/golden-chromium/white.jpg | Bin 357 -> 0 bytes .../test/golden-firefox/grid-cell-0.png | Bin 331 -> 0 bytes .../test/golden-firefox/grid-cell-1.png | Bin 201 -> 0 bytes .../screenshot-clip-odd-size.png | Bin 75 -> 0 bytes .../golden-firefox/screenshot-clip-rect.png | Bin 1371 -> 0 bytes .../screenshot-element-bounding-box.png | Bin 311 -> 0 bytes ...creenshot-element-larger-than-viewport.png | Bin 2797 -> 0 bytes .../screenshot-element-padding-border.png | Bin 153 -> 0 bytes .../screenshot-element-rotate.png | Bin 1800 -> 0 bytes .../screenshot-element-scrolled-into-view.png | Bin 153 -> 0 bytes .../screenshot-grid-fullpage.png | Bin 55662 -> 0 bytes .../screenshot-offscreen-clip.png | Bin 1121 -> 0 bytes .../test/golden-firefox/screenshot-sanity.png | Bin 26146 -> 0 bytes .../puppeteer-firefox/test/golden-utils.js | 149 ------ .../puppeteer-firefox/test/hover.spec.js | 37 -- .../test/ignorehttpserrors.spec.js | 57 --- .../puppeteer-firefox/test/jshandle.spec.js | 157 ------- .../puppeteer-firefox/test/keyboard.spec.js | 193 -------- .../puppeteer-firefox/test/launcher.spec.js | 56 --- .../puppeteer-firefox/test/mouse.spec.js | 108 ----- .../puppeteer-firefox/test/navigation.spec.js | 285 ------------ .../puppeteer-firefox/test/page.spec.js | 403 ----------------- .../puppeteer-firefox/test/puppeteer.spec.js | 104 ----- .../test/queryselector.spec.js | 194 -------- .../test/run_static_server.js | 30 -- .../puppeteer-firefox/test/screenshot.spec.js | 232 ---------- .../puppeteer-firefox/test/target.spec.js | 100 ----- experimental/puppeteer-firefox/test/test.js | 80 ---- .../puppeteer-firefox/test/type.spec.js | 84 ---- experimental/puppeteer-firefox/test/utils.js | 98 ---- .../puppeteer-firefox/test/waittask.spec.js | 423 ------------------ test/click.spec.js | 7 +- test/navigation.spec.js | 8 + 102 files changed, 10 insertions(+), 4275 deletions(-) delete mode 100755 experimental/puppeteer-firefox/completeness.sh delete mode 100644 experimental/puppeteer-firefox/test/assets/consolelog.html delete mode 100644 experimental/puppeteer-firefox/test/assets/csp.html delete mode 100644 experimental/puppeteer-firefox/test/assets/detect-touch.html delete mode 100644 experimental/puppeteer-firefox/test/assets/digits/0.png delete mode 100644 experimental/puppeteer-firefox/test/assets/digits/1.png delete mode 100644 experimental/puppeteer-firefox/test/assets/digits/2.png delete mode 100644 experimental/puppeteer-firefox/test/assets/digits/3.png delete mode 100644 experimental/puppeteer-firefox/test/assets/digits/4.png delete mode 100644 experimental/puppeteer-firefox/test/assets/digits/5.png delete mode 100644 experimental/puppeteer-firefox/test/assets/digits/6.png delete mode 100644 experimental/puppeteer-firefox/test/assets/digits/7.png delete mode 100644 experimental/puppeteer-firefox/test/assets/digits/8.png delete mode 100644 experimental/puppeteer-firefox/test/assets/digits/9.png delete mode 100644 experimental/puppeteer-firefox/test/assets/empty.html delete mode 100644 experimental/puppeteer-firefox/test/assets/empty2.html delete mode 100644 experimental/puppeteer-firefox/test/assets/error.html delete mode 100644 experimental/puppeteer-firefox/test/assets/es6/es6import.js delete mode 100644 experimental/puppeteer-firefox/test/assets/es6/es6module.js delete mode 100644 experimental/puppeteer-firefox/test/assets/es6/es6pathimport.js delete mode 100644 experimental/puppeteer-firefox/test/assets/frames/frame.html delete mode 100644 experimental/puppeteer-firefox/test/assets/frames/frameset.html delete mode 100644 experimental/puppeteer-firefox/test/assets/frames/nested-frames.html delete mode 100644 experimental/puppeteer-firefox/test/assets/frames/one-frame.html delete mode 100644 experimental/puppeteer-firefox/test/assets/frames/script.js delete mode 100644 experimental/puppeteer-firefox/test/assets/frames/style.css delete mode 100644 experimental/puppeteer-firefox/test/assets/frames/two-frames.html delete mode 100644 experimental/puppeteer-firefox/test/assets/global-var.html delete mode 100644 experimental/puppeteer-firefox/test/assets/grid.html delete mode 100644 experimental/puppeteer-firefox/test/assets/injectedfile.js delete mode 100644 experimental/puppeteer-firefox/test/assets/injectedstyle.css delete mode 100644 experimental/puppeteer-firefox/test/assets/input/button.html delete mode 100644 experimental/puppeteer-firefox/test/assets/input/checkbox.html delete mode 100644 experimental/puppeteer-firefox/test/assets/input/keyboard.html delete mode 100644 experimental/puppeteer-firefox/test/assets/input/scrollable.html delete mode 100644 experimental/puppeteer-firefox/test/assets/input/select.html delete mode 100644 experimental/puppeteer-firefox/test/assets/input/textarea.html delete mode 100644 experimental/puppeteer-firefox/test/assets/mobile.html delete mode 100644 experimental/puppeteer-firefox/test/assets/modernizr.js delete mode 100644 experimental/puppeteer-firefox/test/assets/offscreenbuttons.html delete mode 100644 experimental/puppeteer-firefox/test/assets/one-style.css delete mode 100644 experimental/puppeteer-firefox/test/assets/one-style.html delete mode 100644 experimental/puppeteer-firefox/test/assets/tamperable.html delete mode 100644 experimental/puppeteer-firefox/test/assets/title.html delete mode 100644 experimental/puppeteer-firefox/test/assets/wrappedlink.html delete mode 100644 experimental/puppeteer-firefox/test/browser.spec.js delete mode 100644 experimental/puppeteer-firefox/test/browsercontext.spec.js delete mode 100644 experimental/puppeteer-firefox/test/chromiumonly.spec.js delete mode 100644 experimental/puppeteer-firefox/test/click.spec.js delete mode 100644 experimental/puppeteer-firefox/test/dialog.spec.js delete mode 100644 experimental/puppeteer-firefox/test/elementhandle.spec.js delete mode 100644 experimental/puppeteer-firefox/test/emulation.spec.js delete mode 100644 experimental/puppeteer-firefox/test/evaluation.spec.js delete mode 100644 experimental/puppeteer-firefox/test/firefoxonly.spec.js delete mode 100644 experimental/puppeteer-firefox/test/frame.spec.js delete mode 100644 experimental/puppeteer-firefox/test/golden-chromium/grid-cell-0.png delete mode 100644 experimental/puppeteer-firefox/test/golden-chromium/grid-cell-1.png delete mode 100644 experimental/puppeteer-firefox/test/golden-chromium/screenshot-clip-odd-size.png delete mode 100644 experimental/puppeteer-firefox/test/golden-chromium/screenshot-clip-rect.png delete mode 100644 experimental/puppeteer-firefox/test/golden-chromium/screenshot-element-bounding-box.png delete mode 100644 experimental/puppeteer-firefox/test/golden-chromium/screenshot-element-larger-than-viewport.png delete mode 100644 experimental/puppeteer-firefox/test/golden-chromium/screenshot-element-padding-border.png delete mode 100644 experimental/puppeteer-firefox/test/golden-chromium/screenshot-element-rotate.png delete mode 100644 experimental/puppeteer-firefox/test/golden-chromium/screenshot-element-scrolled-into-view.png delete mode 100644 experimental/puppeteer-firefox/test/golden-chromium/screenshot-grid-fullpage.png delete mode 100644 experimental/puppeteer-firefox/test/golden-chromium/screenshot-offscreen-clip.png delete mode 100644 experimental/puppeteer-firefox/test/golden-chromium/screenshot-sanity.png delete mode 100644 experimental/puppeteer-firefox/test/golden-chromium/transparent.png delete mode 100644 experimental/puppeteer-firefox/test/golden-chromium/white.jpg delete mode 100644 experimental/puppeteer-firefox/test/golden-firefox/grid-cell-0.png delete mode 100644 experimental/puppeteer-firefox/test/golden-firefox/grid-cell-1.png delete mode 100644 experimental/puppeteer-firefox/test/golden-firefox/screenshot-clip-odd-size.png delete mode 100644 experimental/puppeteer-firefox/test/golden-firefox/screenshot-clip-rect.png delete mode 100644 experimental/puppeteer-firefox/test/golden-firefox/screenshot-element-bounding-box.png delete mode 100644 experimental/puppeteer-firefox/test/golden-firefox/screenshot-element-larger-than-viewport.png delete mode 100644 experimental/puppeteer-firefox/test/golden-firefox/screenshot-element-padding-border.png delete mode 100644 experimental/puppeteer-firefox/test/golden-firefox/screenshot-element-rotate.png delete mode 100644 experimental/puppeteer-firefox/test/golden-firefox/screenshot-element-scrolled-into-view.png delete mode 100644 experimental/puppeteer-firefox/test/golden-firefox/screenshot-grid-fullpage.png delete mode 100644 experimental/puppeteer-firefox/test/golden-firefox/screenshot-offscreen-clip.png delete mode 100644 experimental/puppeteer-firefox/test/golden-firefox/screenshot-sanity.png delete mode 100644 experimental/puppeteer-firefox/test/golden-utils.js delete mode 100644 experimental/puppeteer-firefox/test/hover.spec.js delete mode 100644 experimental/puppeteer-firefox/test/ignorehttpserrors.spec.js delete mode 100644 experimental/puppeteer-firefox/test/jshandle.spec.js delete mode 100644 experimental/puppeteer-firefox/test/keyboard.spec.js delete mode 100644 experimental/puppeteer-firefox/test/launcher.spec.js delete mode 100644 experimental/puppeteer-firefox/test/mouse.spec.js delete mode 100644 experimental/puppeteer-firefox/test/navigation.spec.js delete mode 100644 experimental/puppeteer-firefox/test/page.spec.js delete mode 100644 experimental/puppeteer-firefox/test/puppeteer.spec.js delete mode 100644 experimental/puppeteer-firefox/test/queryselector.spec.js delete mode 100755 experimental/puppeteer-firefox/test/run_static_server.js delete mode 100644 experimental/puppeteer-firefox/test/screenshot.spec.js delete mode 100644 experimental/puppeteer-firefox/test/target.spec.js delete mode 100644 experimental/puppeteer-firefox/test/test.js delete mode 100644 experimental/puppeteer-firefox/test/type.spec.js delete mode 100644 experimental/puppeteer-firefox/test/utils.js delete mode 100644 experimental/puppeteer-firefox/test/waittask.spec.js diff --git a/experimental/puppeteer-firefox/completeness.sh b/experimental/puppeteer-firefox/completeness.sh deleted file mode 100755 index 1a3a59cb25529..0000000000000 --- a/experimental/puppeteer-firefox/completeness.sh +++ /dev/null @@ -1,7 +0,0 @@ -set -e - -total=`git grep ' \* \[' README.md| wc -l` -complete=`git grep ' \* \[x' README.md | wc -l` -ratio=`echo "$complete / $total * 100" | bc -l` -printf "%.2f%%\n" $ratio - diff --git a/experimental/puppeteer-firefox/package.json b/experimental/puppeteer-firefox/package.json index 0dd0e9749b124..035dcc69f97c4 100644 --- a/experimental/puppeteer-firefox/package.json +++ b/experimental/puppeteer-firefox/package.json @@ -13,9 +13,6 @@ }, "scripts": { "install": "node install.js", - "unit": "node test/test.js", - "funit": "cross-env DUMPIO=1 PRODUCT=firefox node test/test.js", - "cunit": "cross-env PRODUCT=chromium node test/test.js", "tsc": "tsc -p ." }, "author": "The Chromium Authors", @@ -28,27 +25,5 @@ "progress": "^2.0.1", "proxy-from-env": "^1.0.0", "rimraf": "^2.6.1" - }, - "devDependencies": { - "puppeteer": "^1.11.0", - "@pptr/testrunner": "^0.5.0", - "@pptr/testserver": "^0.5.0", - "@types/debug": "0.0.31", - "@types/extract-zip": "^1.6.2", - "@types/mime": "^2.0.0", - "@types/node": "^8.10.34", - "@types/rimraf": "^2.0.2", - "@types/ws": "^6.0.1", - "commonmark": "^0.28.1", - "cross-env": "^5.0.5", - "eslint": "^5.9.0", - "esprima": "^4.0.0", - "jpeg-js": "^0.3.4", - "minimist": "^1.2.0", - "ncp": "^2.0.0", - "pixelmatch": "^4.0.2", - "pngjs": "^3.3.3", - "text-diff": "^1.0.1", - "typescript": "3.1.6" } } diff --git a/experimental/puppeteer-firefox/test/assets/consolelog.html b/experimental/puppeteer-firefox/test/assets/consolelog.html deleted file mode 100644 index 7fa1b211a4df0..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/consolelog.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - console.log test - - - - - diff --git a/experimental/puppeteer-firefox/test/assets/csp.html b/experimental/puppeteer-firefox/test/assets/csp.html deleted file mode 100644 index 34fc1fc1a5c41..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/csp.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/experimental/puppeteer-firefox/test/assets/detect-touch.html b/experimental/puppeteer-firefox/test/assets/detect-touch.html deleted file mode 100644 index 80a4123fbd970..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/detect-touch.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - Detect Touch Test - - - - - - diff --git a/experimental/puppeteer-firefox/test/assets/digits/0.png b/experimental/puppeteer-firefox/test/assets/digits/0.png deleted file mode 100644 index ac3c4768edfbe7bd47c436b1451938fa83483a0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 434 zcmeAS@N?(olHy`uVBq!ia0vp^Ahr+(3y?gqul)d!Vo7)Ob!1@J*w6hZkrl{SNcITw zWnidMV_;}#VPN<`%l_%+p4TGxUM+vVxv7$R|b>IB0qra%Ul`~MX*b+&Ovv2B|q(!_F5JEB7P zed0v5sZ%E?Yh1hbjAP{`Rxy#BDWWlty*FwfV(a#?G%)Vx{1iU1`q`mv2gSp$&p2~* z-}_~cq6GO$WM?Q#u2eqHG5>*&;LICE?MK)yaHod0^GXWrZw)prU@KA-JFv3vi--cJ zxiY6feZzXKeM^rjA81T>7dtU=-Ac=*J)Oz+GF_*I;`H`Ro8h-tLFm*jTUKAWRU1F- zfBotg>*8DSc8V=VKyNdsmbgZgq$HN4S|t~y0x1R~14DCN12bJivk*g5D-%O2V@q8F mb1MUbn=hryP&DM`r(~v8;?}TY%i>c&4Gf;HelF{r5}E+JGmpjq diff --git a/experimental/puppeteer-firefox/test/assets/digits/1.png b/experimental/puppeteer-firefox/test/assets/digits/1.png deleted file mode 100644 index 6768222729b7a487d338fdd7691e44ca4604b216..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 346 zcmeAS@N?(olHy`uVBq!ia0vp^Ahr+(3y?gqul)d!Vo7)Ob!1@J*w6hZkrl{SNcITw zWnidMV_;}#VPN<`%l_%+p4TGxUMOY(Ga43W5;{O5cF=M0GtoN~YQ|NXb^`8@xj{o(qyZXTZh zNB`|_{xm~E;-A5pf9w0RzSOs614Wzvrz@>Y`EmY3PvY_a|9PY*vP4Qqs7R_vN=noT zzEz3h*rdQ|G)c~jg_Tj2N2{sHz90fFVdQ&MBb@0M_4YL;wH) diff --git a/experimental/puppeteer-firefox/test/assets/digits/2.png b/experimental/puppeteer-firefox/test/assets/digits/2.png deleted file mode 100644 index b1daa4735d8a8c8dcce6be0fc2db02beb265860b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 413 zcmeAS@N?(olHy`uVBq!ia0vp^Ahr+(3y?gqul)d!Vo7)Ob!1@J*w6hZkrl{SNcITw zWnidMV_;}#VPN<`%l_%$`QSZ$aB6k6)(;us=vdFljxE~Y{OxBK}UC0Lr@F!n?rT`(_E{}JO6 z<{7z73T*rf7DVR9aWl3E8iRZQbg$2f$a-VDO8#vl&KeD#DWG<1jqwYkJ(8lLS zB4RWT1dM<5ebWu_3N}u7Cea|(Ea3RP_Gw+vkLTX@hooODEC@-v@oNsy!Kx*$5hW>! zC8<`)MX5lF!N|bST-U%%*U&7)(A3Jr(8|O>*TCG$z`$)w_#PAux%nxXX_dG&n7@1v QRL{WR>FVdQ&MBb@0Eyv<`%l_%$`QSZ$aB6x!wK;us=vdFq6XS%(-TTK3zgoV~U1u8i>$)?1-gCGWI< z@aS2U3)ZwcEm*ubc;$ti-mV3rF6`=#s{>A4SDeUI_Oe&LoV{Fey4Mfsycbe!vf}Dh z3k0h9j%Z)+Fc6$CIUO_QmvAUQh^kMk%6JPu7R1Zp;?Hbsg;SLm5G6_ nfw`4|f!mhwJt!J-^HVa@DsgKtfB7D$o`J#B)z4*}Q$iB}nZcB~ diff --git a/experimental/puppeteer-firefox/test/assets/digits/4.png b/experimental/puppeteer-firefox/test/assets/digits/4.png deleted file mode 100644 index a721071e2cc4f4d3aeb9a939fb353bee19a655b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 403 zcmeAS@N?(olHy`uVBq!ia0vp^Ahr+(3y?gqul)d!Vo7)Ob!1@J*w6hZkrl{SNcITw zWnidMV_;}#VPN<`%l_%$^T+cq!(g=TxYIEF}Eo;tyhk1Daqt63f# z{2;4qbecI;$g&|gJoK~I{dMMIqQ-q^Z?t=`WG!sjc4tSKLf;*&Y1X25Kle?TG^sF6 z>D=BYXVM%qnde@pu;0vgB#G~T{mbP)S=PRcF1)|-gago-swJ)wB`Jv|saDBFsX&Us z$iUEC*T78I&@9Bz)XKyVh;$9itqcqv9FE~Y(U6;;l9^VCTf@Y8PqqLxFnGH9xvX<`%l_%$^T+cq!(h1PkxIEF}E?wz22-my@kZT1K|fI9blJ2l{+r3^|)} z*8RfYYV*z8_c%_x;UgqEd!^9Poh`*TW`=*TdCT>^*4BT*|H;1(oM7&m8^G|u$3Da% zDeBX$-lmw9w+s(E_C1K%nffs5$fStkT(0({CkN(r>}Jt>Q{|STYPMH+)2ATwXX;N9 zUY^;$J4woIp1{qfw?{sQ>->K1(PN|HYUInB{Z4ScYq_3-VJ}bo{&&hX4DrV0Symw> zhk!0uEpd$~Nl7e8wMs5Z1yT$~28QOk24=d3W+8^ARwjl(q-$VqWnl2&a0~~EhTQy= Z%(P0}8Ya$rvIVGt!PC{xWt~$(69C)6lY0OF diff --git a/experimental/puppeteer-firefox/test/assets/digits/6.png b/experimental/puppeteer-firefox/test/assets/digits/6.png deleted file mode 100644 index 639f38439d94e856e41a6750952b6e06c418cf3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 445 zcmeAS@N?(olHy`uVBq!ia0vp^Ahr+(3y?gqul)d!Vo7)Ob!1@J*w6hZkrl{SNcITw zWnidMV_;}#VPN<`%l_%*enOfK#K3LW)yaSV~TJavMh*Wm;PxBK(YEu72bwVdZ}kbt`XgDKWG zHhQxkJbRS+g$`@}15O#2T~i(@MY241a<;z7)g=_)Zn4zjOK<*{ z^|H0Og+AWm-uT?SDW)^nHSdUci~p}UEv0yy-ENEcy{zt3Kiarmu2tfJsEduUXmP#3 zy#q&>em;$vqY=pZB*h^?VgbV<=ODgW9}Ojw!x+xA)XDx=DXab9=X3YU$4?nbk5(4D zIQzm0Xkxq!^4049#^7%ybRS xLJUo5~f0{rI44$rjF6*2UngAmjmB9c2 diff --git a/experimental/puppeteer-firefox/test/assets/digits/7.png b/experimental/puppeteer-firefox/test/assets/digits/7.png deleted file mode 100644 index 5c1150b005a9fe3c2b970617dcb8801d98408fe7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 387 zcmeAS@N?(olHy`uVBq!ia0vp^Ahr+(3y?gqul)d!Vo7)Ob!1@J*w6hZkrl{SNcITw zWnidMV_;}#VPN<`%l_%*enOfK#K3UzzBIEF}E?!B;(m&sAU?cw&>o3As9E-=s#a$@F?F}=VL zyP)8Tqo9h03QMC?e9=i(rpg_=V;%1F{Qf6sEc|5qBl*P@8;rI@9-ZW>@#KW&%vnq~ zzOl}^kHZ9KZ=-wR{MdtU&MofI@cqgkb+dDrc!nov}_HpAKo1^uZt{iyr``c~- z=k|r)r~i7)zL771W73?7Q-RJ>Epd$~Nl7e8wMs5Z1yT$~28QOk24=d3W+8^ARwjm4 pCPumj=2iv<7q#6`HRR@}WTsW(*07-LpC-@-22WQ%mvv4FO#tK!e`o*z diff --git a/experimental/puppeteer-firefox/test/assets/digits/8.png b/experimental/puppeteer-firefox/test/assets/digits/8.png deleted file mode 100644 index abb8b48b0b1e5ac8aabb667265d9469d48b7fa24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 447 zcmeAS@N?(olHy`uVBq!ia0vp^Ahr+(3y?gqul)d!Vo7)Ob!1@J*w6hZkrl{SNcITw zWnidMV_;}#VPN<`%l_%*e4jUVa(g^qi=IEF}Eo;pF-zbR3qZT<755iFO44ID4;ZXsr|G6Z-3AQ~YMF#9yB;s?AMz zKS@rU5cTWfbfKwA)i>7f%FSiSf0jRY%Pf;;KrgA5xJHzuB$lLFB^RXvDF!10LvvjN zGhIWo5JOWd6GJN#V_gGtD+7b_7+*yc4Y~O#nQ4`{HLx*$oeR{!;OXk;vd$@?2>`+@ BpQr!; diff --git a/experimental/puppeteer-firefox/test/assets/digits/9.png b/experimental/puppeteer-firefox/test/assets/digits/9.png deleted file mode 100644 index 6a40a21c6f58545cab61ea346f1b6bb8b9300c6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 437 zcmeAS@N?(olHy`uVBq!ia0vp^Ahr+(3y?gqul)d!Vo7)Ob!1@J*w6hZkrl{SNcITw zWnidMV_;}#VPN<`%l_%*e4jUVa(h4y;7IEF}Eo;pD>k10`t_4)k-!5MN3m>MJ651DFC-ODU@ zpZg1!gh!athOH(nj7nKY-017|7X(lneSP5`X2ea_zN%F?Q`q4 z_N=>MvQe>5^dWanr4!!~yIt-xI*MIYJ|5&1TI#UWB`1>;t5@F(qUrTrX|{*Mm$Cl}5-eYz`MaCM!?KE?X)(klhI z7Jr!kGOw6X)u&2!`$KOxphr|oTq8 -a(); - -function a() { - b(); -} - -function b() { - c(); -} - -function c() { - throw new Error('Fancy error!'); -} - diff --git a/experimental/puppeteer-firefox/test/assets/es6/es6import.js b/experimental/puppeteer-firefox/test/assets/es6/es6import.js deleted file mode 100644 index 9a0a1095d1542..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/es6/es6import.js +++ /dev/null @@ -1,2 +0,0 @@ -import num from './es6module.js'; -window.__es6injected = num; \ No newline at end of file diff --git a/experimental/puppeteer-firefox/test/assets/es6/es6module.js b/experimental/puppeteer-firefox/test/assets/es6/es6module.js deleted file mode 100644 index a4012bff06c1b..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/es6/es6module.js +++ /dev/null @@ -1 +0,0 @@ -export default 42; \ No newline at end of file diff --git a/experimental/puppeteer-firefox/test/assets/es6/es6pathimport.js b/experimental/puppeteer-firefox/test/assets/es6/es6pathimport.js deleted file mode 100644 index 99919621a82d4..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/es6/es6pathimport.js +++ /dev/null @@ -1,2 +0,0 @@ -import num from './es6/es6module.js'; -window.__es6injected = num; \ No newline at end of file diff --git a/experimental/puppeteer-firefox/test/assets/frames/frame.html b/experimental/puppeteer-firefox/test/assets/frames/frame.html deleted file mode 100644 index 8f20d2da9fb10..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/frames/frame.html +++ /dev/null @@ -1,8 +0,0 @@ - - - -
Hi, I'm frame
diff --git a/experimental/puppeteer-firefox/test/assets/frames/frameset.html b/experimental/puppeteer-firefox/test/assets/frames/frameset.html deleted file mode 100644 index 4d56f88839065..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/frames/frameset.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/experimental/puppeteer-firefox/test/assets/frames/nested-frames.html b/experimental/puppeteer-firefox/test/assets/frames/nested-frames.html deleted file mode 100644 index de1987586ff3d..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/frames/nested-frames.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - diff --git a/experimental/puppeteer-firefox/test/assets/frames/one-frame.html b/experimental/puppeteer-firefox/test/assets/frames/one-frame.html deleted file mode 100644 index e941d795a2777..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/frames/one-frame.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/experimental/puppeteer-firefox/test/assets/frames/script.js b/experimental/puppeteer-firefox/test/assets/frames/script.js deleted file mode 100644 index be22256d16b33..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/frames/script.js +++ /dev/null @@ -1 +0,0 @@ -console.log('Cheers!'); diff --git a/experimental/puppeteer-firefox/test/assets/frames/style.css b/experimental/puppeteer-firefox/test/assets/frames/style.css deleted file mode 100644 index 5b5436e8740e7..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/frames/style.css +++ /dev/null @@ -1,3 +0,0 @@ -div { - color: blue; -} diff --git a/experimental/puppeteer-firefox/test/assets/frames/two-frames.html b/experimental/puppeteer-firefox/test/assets/frames/two-frames.html deleted file mode 100644 index b2ee853edac52..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/frames/two-frames.html +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/experimental/puppeteer-firefox/test/assets/global-var.html b/experimental/puppeteer-firefox/test/assets/global-var.html deleted file mode 100644 index b6be975038f06..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/global-var.html +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/experimental/puppeteer-firefox/test/assets/grid.html b/experimental/puppeteer-firefox/test/assets/grid.html deleted file mode 100644 index 0bdbb1220e5a1..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/grid.html +++ /dev/null @@ -1,52 +0,0 @@ - - - diff --git a/experimental/puppeteer-firefox/test/assets/injectedfile.js b/experimental/puppeteer-firefox/test/assets/injectedfile.js deleted file mode 100644 index 6cb04f1bba081..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/injectedfile.js +++ /dev/null @@ -1,2 +0,0 @@ -window.__injected = 42; -window.__injectedError = new Error('hi'); \ No newline at end of file diff --git a/experimental/puppeteer-firefox/test/assets/injectedstyle.css b/experimental/puppeteer-firefox/test/assets/injectedstyle.css deleted file mode 100644 index aa1634c255034..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/injectedstyle.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - background-color: red; -} diff --git a/experimental/puppeteer-firefox/test/assets/input/button.html b/experimental/puppeteer-firefox/test/assets/input/button.html deleted file mode 100644 index d4c6e13fd2801..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/input/button.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - Button test - - - - - - - \ No newline at end of file diff --git a/experimental/puppeteer-firefox/test/assets/input/checkbox.html b/experimental/puppeteer-firefox/test/assets/input/checkbox.html deleted file mode 100644 index ca56762e2b3ab..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/input/checkbox.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - Selection Test - - - - - - - diff --git a/experimental/puppeteer-firefox/test/assets/input/keyboard.html b/experimental/puppeteer-firefox/test/assets/input/keyboard.html deleted file mode 100644 index 9f1d48152d911..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/input/keyboard.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - Keyboard test - - - - - - \ No newline at end of file diff --git a/experimental/puppeteer-firefox/test/assets/input/scrollable.html b/experimental/puppeteer-firefox/test/assets/input/scrollable.html deleted file mode 100644 index 885d3739d5f01..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/input/scrollable.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - Scrollable test - - - - - - \ No newline at end of file diff --git a/experimental/puppeteer-firefox/test/assets/input/select.html b/experimental/puppeteer-firefox/test/assets/input/select.html deleted file mode 100644 index 879a537a766fe..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/input/select.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - Selection Test - - - - - - diff --git a/experimental/puppeteer-firefox/test/assets/input/textarea.html b/experimental/puppeteer-firefox/test/assets/input/textarea.html deleted file mode 100644 index 6d5be760740f0..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/input/textarea.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - Textarea test - - - - - - - \ No newline at end of file diff --git a/experimental/puppeteer-firefox/test/assets/mobile.html b/experimental/puppeteer-firefox/test/assets/mobile.html deleted file mode 100644 index 8e94b2fe2917e..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/mobile.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/experimental/puppeteer-firefox/test/assets/modernizr.js b/experimental/puppeteer-firefox/test/assets/modernizr.js deleted file mode 100644 index 7991a4ec4025e..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/modernizr.js +++ /dev/null @@ -1,3 +0,0 @@ -/*! modernizr 3.5.0 (Custom Build) | MIT * -* https://modernizr.com/download/?-touchevents-setclasses !*/ -!function(e,n,t){function o(e,n){return typeof e===n}function s(){var e,n,t,s,a,i,r;for(var l in c)if(c.hasOwnProperty(l)){if(e=[],n=c[l],n.name&&(e.push(n.name.toLowerCase()),n.options&&n.options.aliases&&n.options.aliases.length))for(t=0;t - button { - position: absolute; - width: 100px; - height: 20px; - } - - #btn0 { right: 0px; top: 0; } - #btn1 { right: -10px; top: 25px; } - #btn2 { right: -20px; top: 50px; } - #btn3 { right: -30px; top: 75px; } - #btn4 { right: -40px; top: 100px; } - #btn5 { right: -50px; top: 125px; } - #btn6 { right: -60px; top: 150px; } - #btn7 { right: -70px; top: 175px; } - #btn8 { right: -80px; top: 200px; } - #btn9 { right: -90px; top: 225px; } - #btn10 { right: -100px; top: 250px; } - - - - - - - - - - - - - diff --git a/experimental/puppeteer-firefox/test/assets/one-style.css b/experimental/puppeteer-firefox/test/assets/one-style.css deleted file mode 100644 index 7b26410d8a15e..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/one-style.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - background-color: pink; -} diff --git a/experimental/puppeteer-firefox/test/assets/one-style.html b/experimental/puppeteer-firefox/test/assets/one-style.html deleted file mode 100644 index 4760f2b9f7e37..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/one-style.html +++ /dev/null @@ -1,2 +0,0 @@ - -
hello, world!
diff --git a/experimental/puppeteer-firefox/test/assets/tamperable.html b/experimental/puppeteer-firefox/test/assets/tamperable.html deleted file mode 100644 index d027e97038573..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/tamperable.html +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/experimental/puppeteer-firefox/test/assets/title.html b/experimental/puppeteer-firefox/test/assets/title.html deleted file mode 100644 index 88a86ce412b07..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/title.html +++ /dev/null @@ -1 +0,0 @@ -Woof-Woof diff --git a/experimental/puppeteer-firefox/test/assets/wrappedlink.html b/experimental/puppeteer-firefox/test/assets/wrappedlink.html deleted file mode 100644 index 429b6e915671b..0000000000000 --- a/experimental/puppeteer-firefox/test/assets/wrappedlink.html +++ /dev/null @@ -1,32 +0,0 @@ - -
- diff --git a/experimental/puppeteer-firefox/test/browser.spec.js b/experimental/puppeteer-firefox/test/browser.spec.js deleted file mode 100644 index d2eb98de316a0..0000000000000 --- a/experimental/puppeteer-firefox/test/browser.spec.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - describe('Browser.process', function() { - it('should return child_process instance', async function({browser}) { - const process = await browser.process(); - expect(process.pid).toBeGreaterThan(0); - }); - }); -}; - - diff --git a/experimental/puppeteer-firefox/test/browsercontext.spec.js b/experimental/puppeteer-firefox/test/browsercontext.spec.js deleted file mode 100644 index 11cf3c0ca9111..0000000000000 --- a/experimental/puppeteer-firefox/test/browsercontext.spec.js +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ - -const utils = require('./utils'); -const {TimeoutError} = require('../Errors'); - -module.exports.addTests = function({testRunner, expect, puppeteer, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('BrowserContext', function() { - it('should have default context', async function({browser, server}) { - expect(browser.browserContexts().length).toBe(1); - const defaultContext = browser.browserContexts()[0]; - expect(defaultContext.isIncognito()).toBe(false); - let error = null; - await defaultContext.close().catch(e => error = e); - expect(browser.defaultBrowserContext()).toBe(defaultContext); - expect(error.message).toContain('cannot be closed'); - }); - it('should create new incognito context', async function({browser, server}) { - expect(browser.browserContexts().length).toBe(1); - const context = await browser.createIncognitoBrowserContext(); - expect(context.isIncognito()).toBe(true); - expect(browser.browserContexts().length).toBe(2); - expect(browser.browserContexts().indexOf(context) !== -1).toBe(true); - await context.close(); - expect(browser.browserContexts().length).toBe(1); - }); - it('should close all belonging targets once closing context', async function({browser, server}) { - expect((await browser.pages()).length).toBe(1); - - const context = await browser.createIncognitoBrowserContext(); - await context.newPage(); - expect((await browser.pages()).length).toBe(2); - expect((await context.pages()).length).toBe(1); - - await context.close(); - expect((await browser.pages()).length).toBe(1); - }); - it('window.open should use parent tab context', async function({browser, server}) { - const context = await browser.createIncognitoBrowserContext(); - const page = await context.newPage(); - await page.goto(server.EMPTY_PAGE); - const [popupTarget] = await Promise.all([ - utils.waitEvent(browser, 'targetcreated'), - page.evaluate(url => window.open(url), server.EMPTY_PAGE) - ]); - expect(popupTarget.browserContext()).toBe(context); - await context.close(); - }); - it('should fire target events', async function({browser, server}) { - const context = await browser.createIncognitoBrowserContext(); - const events = []; - context.on('targetcreated', target => events.push('CREATED: ' + target.url())); - context.on('targetchanged', target => events.push('CHANGED: ' + target.url())); - context.on('targetdestroyed', target => events.push('DESTROYED: ' + target.url())); - const page = await context.newPage(); - await page.goto(server.EMPTY_PAGE); - await page.close(); - expect(events).toEqual([ - 'CREATED: about:blank', - `CHANGED: ${server.EMPTY_PAGE}`, - `DESTROYED: ${server.EMPTY_PAGE}` - ]); - await context.close(); - }); - it('should wait for a target', async function({browser, server}) { - const context = await browser.createIncognitoBrowserContext(); - let resolved = false; - const targetPromise = context.waitForTarget(target => target.url() === server.EMPTY_PAGE); - targetPromise.then(() => resolved = true); - const page = await context.newPage(); - expect(resolved).toBe(false); - await page.goto(server.EMPTY_PAGE); - const target = await targetPromise; - expect(await target.page()).toBe(page); - await context.close(); - }); - it('should timeout waiting for a non-existent target', async function({browser, server}) { - const context = await browser.createIncognitoBrowserContext(); - const error = await context.waitForTarget(target => target.url() === server.EMPTY_PAGE, {timeout: 1}).catch(e => e); - expect(error).toBeInstanceOf(TimeoutError); - await context.close(); - }); - it('should isolate localStorage and cookies', async function({browser, server}) { - // Create two incognito contexts. - const context1 = await browser.createIncognitoBrowserContext(); - const context2 = await browser.createIncognitoBrowserContext(); - expect(context1.targets().length).toBe(0); - expect(context2.targets().length).toBe(0); - - // Create a page in first incognito context. - const page1 = await context1.newPage(); - await page1.goto(server.EMPTY_PAGE); - await page1.evaluate(() => { - localStorage.setItem('name', 'page1'); - document.cookie = 'name=page1'; - }); - - expect(context1.targets().length).toBe(1); - expect(context2.targets().length).toBe(0); - - // Create a page in second incognito context. - const page2 = await context2.newPage(); - await page2.goto(server.EMPTY_PAGE); - await page2.evaluate(() => { - localStorage.setItem('name', 'page2'); - document.cookie = 'name=page2'; - }); - - expect(context1.targets().length).toBe(1); - expect(context1.targets()[0]).toBe(page1.target()); - expect(context2.targets().length).toBe(1); - expect(context2.targets()[0]).toBe(page2.target()); - - // Make sure pages don't share localstorage or cookies. - expect(await page1.evaluate(() => localStorage.getItem('name'))).toBe('page1'); - expect(await page1.evaluate(() => document.cookie)).toBe('name=page1'); - expect(await page2.evaluate(() => localStorage.getItem('name'))).toBe('page2'); - expect(await page2.evaluate(() => document.cookie)).toBe('name=page2'); - - // Cleanup contexts. - await Promise.all([ - context1.close(), - context2.close() - ]); - expect(browser.browserContexts().length).toBe(1); - }); - (FFOX ? xit : it)('should work across sessions', async function({browser, server}) { - expect(browser.browserContexts().length).toBe(1); - const context = await browser.createIncognitoBrowserContext(); - expect(browser.browserContexts().length).toBe(2); - const remoteBrowser = await puppeteer.connect({ - browserWSEndpoint: browser.wsEndpoint() - }); - const contexts = remoteBrowser.browserContexts(); - expect(contexts.length).toBe(2); - await remoteBrowser.disconnect(); - await context.close(); - }); - }); -}; diff --git a/experimental/puppeteer-firefox/test/chromiumonly.spec.js b/experimental/puppeteer-firefox/test/chromiumonly.spec.js deleted file mode 100644 index 00b21c716ae06..0000000000000 --- a/experimental/puppeteer-firefox/test/chromiumonly.spec.js +++ /dev/null @@ -1,24 +0,0 @@ -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - describe('Chromium-specific tests', function() { - describe('Browser.version', function() { - xit('should return whether we are in headless', async({browser}) => { - const version = await browser.version(); - expect(version.length).toBeGreaterThan(0); - expect(version.startsWith('Headless')).toBe(headless); - }); - }); - - describe('Browser.userAgent', function() { - it('should include WebKit', async({browser}) => { - const userAgent = await browser.userAgent(); - expect(userAgent.length).toBeGreaterThan(0); - expect(userAgent).toContain('WebKit'); - }); - }); - }); -}; - diff --git a/experimental/puppeteer-firefox/test/click.spec.js b/experimental/puppeteer-firefox/test/click.spec.js deleted file mode 100644 index 1384660a5a728..0000000000000 --- a/experimental/puppeteer-firefox/test/click.spec.js +++ /dev/null @@ -1,211 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ -const utils = require('./utils'); -const os = require('os'); -const DeviceDescriptors = require('puppeteer/DeviceDescriptors'); -const iPhone = DeviceDescriptors['iPhone 6']; - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('Page.click', () => { - it('should click the button', async({page, server}) => { - await page.goto(server.PREFIX + '/input/button.html'); - await page.click('button'); - expect(await page.evaluate(() => result)).toBe('Clicked'); - }); - it('should click offscreen buttons', async({page, server}) => { - await page.goto(server.PREFIX + '/offscreenbuttons.html'); - const messages = []; - page.on('console', msg => messages.push(msg.text())); - for (let i = 0; i < 11; ++i) { - // We might've scrolled to click a button - reset to (0, 0). - await page.evaluate(() => window.scrollTo(0, 0)); - await page.click(`#btn${i}`); - } - expect(messages).toEqual([ - 'button #0 clicked', - 'button #1 clicked', - 'button #2 clicked', - 'button #3 clicked', - 'button #4 clicked', - 'button #5 clicked', - 'button #6 clicked', - 'button #7 clicked', - 'button #8 clicked', - 'button #9 clicked', - 'button #10 clicked' - ]); - }); - it('should click wrapped links', async({page, server}) => { - await page.goto(server.PREFIX + '/wrappedlink.html'); - await page.click('a'); - expect(await page.evaluate(() => window.__clicked)).toBe(true); - }); - it('should click on checkbox input and toggle', async({page, server}) => { - await page.goto(server.PREFIX + '/input/checkbox.html'); - expect(await page.evaluate(() => result.check)).toBe(null); - await page.click('input#agree'); - expect(await page.evaluate(() => result.check)).toBe(true); - expect(await page.evaluate(() => result.events)).toEqual([ - 'mouseover', - 'mouseenter', - 'mousemove', - 'mousedown', - 'mouseup', - 'click', - 'input', - 'change', - ]); - await page.click('input#agree'); - expect(await page.evaluate(() => result.check)).toBe(false); - }); - it('should click on checkbox label and toggle', async({page, server}) => { - await page.goto(server.PREFIX + '/input/checkbox.html'); - expect(await page.evaluate(() => result.check)).toBe(null); - await page.click('label[for="agree"]'); - expect(await page.evaluate(() => result.check)).toBe(true); - expect(await page.evaluate(() => result.events)).toEqual([ - 'click', - 'input', - 'change', - ]); - await page.click('label[for="agree"]'); - expect(await page.evaluate(() => result.check)).toBe(false); - }); - it('should fail to click a missing button', async({page, server}) => { - await page.goto(server.PREFIX + '/input/button.html'); - let error = null; - await page.click('button.does-not-exist').catch(e => error = e); - expect(error.message).toBe('No node found for selector: button.does-not-exist'); - }); - // @see https://github.com/GoogleChrome/puppeteer/issues/161 - it('should not hang with touch-enabled viewports', async({page, server}) => { - await page.setViewport(iPhone.viewport); - await page.mouse.down(); - await page.mouse.move(100, 10); - await page.mouse.up(); - }); - it('should click the button after navigation ', async({page, server}) => { - await page.goto(server.PREFIX + '/input/button.html'); - await page.click('button'); - await page.goto(server.PREFIX + '/input/button.html'); - await page.click('button'); - expect(await page.evaluate(() => result)).toBe('Clicked'); - }); - it('should scroll and click the button', async({page, server}) => { - await page.goto(server.PREFIX + '/input/scrollable.html'); - await page.click('#button-5'); - expect(await page.evaluate(() => document.querySelector('#button-5').textContent)).toBe('clicked'); - await page.click('#button-80'); - expect(await page.evaluate(() => document.querySelector('#button-80').textContent)).toBe('clicked'); - }); - it('should double click the button', async({page, server}) => { - await page.goto(server.PREFIX + '/input/button.html'); - await page.evaluate(() => { - window.double = false; - const button = document.querySelector('button'); - button.addEventListener('dblclick', event => { - window.double = true; - }); - }); - const button = await page.$('button'); - await button.click({ clickCount: 2 }); - expect(await page.evaluate('double')).toBe(true); - expect(await page.evaluate('result')).toBe('Clicked'); - }); - it('should click a partially obscured button', async({page, server}) => { - await page.goto(server.PREFIX + '/input/button.html'); - await page.evaluate(() => { - const button = document.querySelector('button'); - button.textContent = 'Some really long text that will go offscreen'; - button.style.position = 'absolute'; - button.style.left = '368px'; - }); - await page.click('button'); - expect(await page.evaluate(() => window.result)).toBe('Clicked'); - }); - it('should select the text by triple clicking', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.focus('textarea'); - const text = 'This is the text that we are going to try to select. Let\'s see how it goes.'; - await page.keyboard.type(text); - await page.click('textarea'); - await page.click('textarea', {clickCount: 2}); - await page.click('textarea', {clickCount: 3}); - expect(await page.evaluate(() => { - const textarea = document.querySelector('textarea'); - return textarea.value.substring(textarea.selectionStart, textarea.selectionEnd); - })).toBe(text); - }); - it('should fire contextmenu event on right click', async({page, server}) => { - await page.goto(server.PREFIX + '/input/scrollable.html'); - await page.click('#button-8', {button: 'right'}); - expect(await page.evaluate(() => document.querySelector('#button-8').textContent)).toBe('context menu'); - }); - it('should set modifier keys on click', async({page, server}) => { - await page.goto(server.PREFIX + '/input/scrollable.html'); - await page.evaluate(() => document.querySelector('#button-3').addEventListener('mousedown', e => window.lastEvent = e, true)); - const modifiers = {'Shift': 'shiftKey', 'Control': 'ctrlKey', 'Alt': 'altKey', 'Meta': 'metaKey'}; - // In Firefox, the Meta modifier only exists on Mac - if (FFOX && os.platform() !== 'darwin') - delete modifiers['Meta']; - for (const modifier in modifiers) { - await page.keyboard.down(modifier); - await page.click('#button-3'); - if (!(await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier]))) - throw new Error(modifiers[modifier] + ' should be true'); - await page.keyboard.up(modifier); - } - await page.click('#button-3'); - for (const modifier in modifiers) { - if ((await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier]))) - throw new Error(modifiers[modifier] + ' should be false'); - } - }); - // @see https://github.com/GoogleChrome/puppeteer/issues/206 - it('should click links which cause navigation', async({page, server}) => { - await page.setContent(`empty.html`); - // This await should not hang. - await page.click('a'); - }); - it('should click the button inside an iframe', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.setContent('
spacer
'); - await utils.attachFrame(page, 'button-test', server.PREFIX + '/input/button.html'); - const frame = page.frames()[1]; - const button = await frame.$('button'); - await button.click(); - expect(await frame.evaluate(() => window.result)).toBe('Clicked'); - }); - it('should click the button with deviceScaleFactor set', async({page, server}) => { - await page.setViewport({width: 400, height: 400, deviceScaleFactor: 5}); - expect(await page.evaluate(() => window.devicePixelRatio)).toBe(5); - await page.setContent('
spacer
'); - await utils.attachFrame(page, 'button-test', server.PREFIX + '/input/button.html'); - const frame = page.frames()[1]; - const button = await frame.$('button'); - await button.click(); - expect(await frame.evaluate(() => window.result)).toBe('Clicked'); - }); - }); -}; - diff --git a/experimental/puppeteer-firefox/test/dialog.spec.js b/experimental/puppeteer-firefox/test/dialog.spec.js deleted file mode 100644 index ac4211b1dab81..0000000000000 --- a/experimental/puppeteer-firefox/test/dialog.spec.js +++ /dev/null @@ -1,39 +0,0 @@ -const utils = require('./utils'); - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('Page.Events.Dialog', function() { - it('should fire', async({page, server}) => { - page.on('dialog', dialog => { - expect(dialog.type()).toBe('alert'); - expect(dialog.defaultValue()).toBe(''); - expect(dialog.message()).toBe('yo'); - dialog.accept(); - }); - await page.evaluate(() => alert('yo')); - }); - it('should allow accepting prompts', async({page, server}) => { - page.on('dialog', dialog => { - expect(dialog.type()).toBe('prompt'); - expect(dialog.defaultValue()).toBe('yes.'); - expect(dialog.message()).toBe('question?'); - dialog.accept('answer!'); - }); - const result = await page.evaluate(() => prompt('question?', 'yes.')); - expect(result).toBe('answer!'); - }); - it('should dismiss the prompt', async({page, server}) => { - page.on('dialog', dialog => { - dialog.dismiss(); - }); - const result = await page.evaluate(() => prompt('question?')); - expect(result).toBe(null); - }); - }); -}; diff --git a/experimental/puppeteer-firefox/test/elementhandle.spec.js b/experimental/puppeteer-firefox/test/elementhandle.spec.js deleted file mode 100644 index 7d8909f4084de..0000000000000 --- a/experimental/puppeteer-firefox/test/elementhandle.spec.js +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ -const utils = require('./utils'); - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('JSHandle.asElement', function() { - it('should work', async({page, server}) => { - await page.setContent('
test
'); - const handle = await page.evaluateHandle(() => document.querySelector('section')); - const element = handle.asElement(); - expect(element).not.toBe(null); - }); - it('should work with nullified Node', async({page, server}) => { - await page.setContent('
test
'); - await page.evaluate(() => delete Node); - const handle = await page.evaluateHandle(() => document.querySelector('section')); - const element = handle.asElement(); - expect(element).not.toBe(null); - }); - it('should return null for non-elements', async({page, server}) => { - const handle = await page.evaluateHandle(() => ({foo: 'bar'})); - expect(handle.asElement()).toBe(null); - }); - }); - - describe('ElementHandle.isIntersectingViewport', function() { - it('should work', async({page, server}) => { - await page.goto(server.PREFIX + '/offscreenbuttons.html'); - for (let i = 0; i < 11; ++i) { - const button = await page.$('#btn' + i); - // All but last button are visible. - const visible = i < 10; - expect(await button.isIntersectingViewport()).toBe(visible); - } - }); - }); - - describe('ElementHandle.boundingBox', function() { - it('should work', async({page, server}) => { - await page.setViewport({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - const elementHandle = await page.$('.box:nth-of-type(13)'); - const box = await elementHandle.boundingBox(); - expect(box).toEqual({ x: 100, y: 50, width: 50, height: 50 }); - }); - xit('should handle nested frames', async({page, server}) => { - await page.setViewport({width: 500, height: 500}); - await page.goto(server.PREFIX + '/frames/nested-frames.html'); - const nestedFrame = page.frames()[1].childFrames()[1]; - const elementHandle = await nestedFrame.$('div'); - const box = await elementHandle.boundingBox(); - await new Promise(() => {}); - expect(box).toEqual({ x: 28, y: 260, width: 264, height: 18 }); - }); - it('should return null for invisible elements', async({page, server}) => { - await page.setContent('
hi
'); - const element = await page.$('div'); - expect(await element.boundingBox()).toBe(null); - }); - it('should force a layout', async({page, server}) => { - await page.setViewport({ width: 500, height: 500 }); - await page.setContent('
hello
'); - const elementHandle = await page.$('div'); - await page.evaluate(element => element.style.height = '200px', elementHandle); - const box = await elementHandle.boundingBox(); - expect(box).toEqual({ x: 8, y: 8, width: 100, height: 200 }); - }); - it('should work with SVG nodes', async({page, server}) => { - await page.setContent(` - - - - `); - const element = await page.$('#therect'); - const pptrBoundingBox = await element.boundingBox(); - const webBoundingBox = await page.evaluate(e => { - const rect = e.getBoundingClientRect(); - return {x: rect.x, y: rect.y, width: rect.width, height: rect.height}; - }, element); - expect(pptrBoundingBox).toEqual(webBoundingBox); - }); - }); -}; diff --git a/experimental/puppeteer-firefox/test/emulation.spec.js b/experimental/puppeteer-firefox/test/emulation.spec.js deleted file mode 100644 index 61acb7cf3d8b5..0000000000000 --- a/experimental/puppeteer-firefox/test/emulation.spec.js +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ -const DeviceDescriptors = require('puppeteer/DeviceDescriptors'); -const iPhone = DeviceDescriptors['iPhone 6']; -const iPhoneLandscape = DeviceDescriptors['iPhone 6 landscape']; - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('Page.viewport', function() { - it('should get the proper viewport size', async({page, server}) => { - expect(page.viewport()).toEqual({width: 800, height: 600}); - await page.setViewport({width: 123, height: 456}); - expect(page.viewport()).toEqual({width: 123, height: 456}); - }); - it('should support mobile emulation', async({page, server}) => { - await page.goto(server.PREFIX + '/mobile.html'); - expect(await page.evaluate(() => window.innerWidth)).toBe(800); - await page.setViewport(iPhone.viewport); - expect(await page.evaluate(() => window.innerWidth)).toBe(375); - await page.setViewport({width: 400, height: 300}); - expect(await page.evaluate(() => window.innerWidth)).toBe(400); - }); - it('should support touch emulation', async({page, server}) => { - await page.goto(server.PREFIX + '/mobile.html'); - expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false); - await page.setViewport(iPhone.viewport); - expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true); - expect(await page.evaluate(dispatchTouch)).toBe('Received touch'); - await page.setViewport({width: 100, height: 100}); - expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false); - - function dispatchTouch() { - let fulfill; - const promise = new Promise(x => fulfill = x); - window.ontouchstart = function(e) { - fulfill('Received touch'); - }; - window.dispatchEvent(new Event('touchstart')); - - fulfill('Did not receive touch'); - - return promise; - } - }); - it('should be detectable by Modernizr', async({page, server}) => { - await page.goto(server.PREFIX + '/detect-touch.html'); - expect(await page.evaluate(() => document.body.textContent.trim())).toBe('NO'); - await page.setViewport(iPhone.viewport); - await page.goto(server.PREFIX + '/detect-touch.html'); - expect(await page.evaluate(() => document.body.textContent.trim())).toBe('YES'); - }); - it('should detect touch when applying viewport with touches', async({page, server}) => { - await page.setViewport({ width: 800, height: 600, hasTouch: true }); - await page.addScriptTag({url: server.PREFIX + '/modernizr.js'}); - expect(await page.evaluate(() => Modernizr.touchevents)).toBe(true); - }); - xit('should support landscape emulation', async({page, server}) => { - await page.goto(server.PREFIX + '/mobile.html'); - expect(await page.evaluate(() => screen.orientation.type)).toBe('portrait-primary'); - await page.setViewport(iPhoneLandscape.viewport); - expect(await page.evaluate(() => screen.orientation.type)).toBe('landscape-primary'); - await page.setViewport({width: 100, height: 100}); - expect(await page.evaluate(() => screen.orientation.type)).toBe('portrait-primary'); - }); - }); -}; - diff --git a/experimental/puppeteer-firefox/test/evaluation.spec.js b/experimental/puppeteer-firefox/test/evaluation.spec.js deleted file mode 100644 index 0a726397d9519..0000000000000 --- a/experimental/puppeteer-firefox/test/evaluation.spec.js +++ /dev/null @@ -1,181 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ -const utils = require('./utils'); - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('Page.evaluate', function() { - it('should work', async({page, server}) => { - const result = await page.evaluate(() => 7 * 3); - expect(result).toBe(21); - }); - it('should throw when evaluation triggers reload', async({page, server}) => { - let error = null; - await page.evaluate(() => { - location.reload(); - return new Promise(resolve => { - setTimeout(() => resolve(1), 0); - }); - }).catch(e => error = e); - expect(error.message).toContain('Protocol error'); - }); - it('should await promise', async({page, server}) => { - const result = await page.evaluate(() => Promise.resolve(8 * 7)); - expect(result).toBe(56); - }); - it('should reject promise with exception', async({page, server}) => { - let error = null; - await page.evaluate(() => not.existing.object.property).catch(e => error = e); - expect(error).toBeTruthy(); - expect(error.message).toContain('not is not defined'); - }); - it('should support thrown strings as error messages', async({page, server}) => { - let error = null; - await page.evaluate(() => { throw 'qwerty'; }).catch(e => error = e); - expect(error).toBeTruthy(); - expect(error.message).toContain('qwerty'); - }); - it('should support thrown numbers as error messages', async({page, server}) => { - let error = null; - await page.evaluate(() => { throw 100500; }).catch(e => error = e); - expect(error).toBeTruthy(); - expect(error.message).toContain('100500'); - }); - it('should return complex objects', async({page, server}) => { - const object = {foo: 'bar!'}; - const result = await page.evaluate(a => a, object); - expect(result).not.toBe(object); - expect(result).toEqual(object); - }); - it('should transfer NaN', async({page, server}) => { - const result = await page.evaluate(a => a, NaN); - expect(Object.is(result, NaN)).toBe(true); - }); - it('should transfer -0', async({page, server}) => { - const result = await page.evaluate(a => a, -0); - expect(Object.is(result, -0)).toBe(true); - }); - it('should transfer Infinity', async({page, server}) => { - const result = await page.evaluate(a => a, Infinity); - expect(Object.is(result, Infinity)).toBe(true); - }); - it('should transfer -Infinity', async({page, server}) => { - const result = await page.evaluate(a => a, -Infinity); - expect(Object.is(result, -Infinity)).toBe(true); - }); - it('should transfer arrays', async({page, server}) => { - const result = await page.evaluate(a => a, [1, 2, 3]); - expect(result).toEqual([1,2,3]); - }); - it('should transfer arrays as arrays, not objects', async({page, server}) => { - const result = await page.evaluate(a => Array.isArray(a), [1, 2, 3]); - expect(result).toBe(true); - }); - it('should accept "undefined" as one of multiple parameters', async({page, server}) => { - const result = await page.evaluate((a, b) => Object.is(a, undefined) && Object.is(b, 'foo'), undefined, 'foo'); - expect(result).toBe(true); - }); - it('should properly serialize null fields', async({page}) => { - expect(await page.evaluate(() => ({a: undefined}))).toEqual({}); - }); - it('should return undefined for non-serializable objects', async({page, server}) => { - expect(await page.evaluate(() => window)).toBe(undefined); - }); - xit('should return undefined for objects with symbols', async({page, server}) => { - expect(await page.evaluate(() => [Symbol('foo4')])).toBe(undefined); - }); - it('should fail for circular object', async({page, server}) => { - const result = await page.evaluate(() => { - const a = {}; - const b = {a}; - a.b = b; - return a; - }); - expect(result).toBe(undefined); - }); - it('should accept a string', async({page, server}) => { - const result = await page.evaluate('1 + 2'); - expect(result).toBe(3); - }); - it('should accept a string with semi colons', async({page, server}) => { - const result = await page.evaluate('1 + 5;'); - expect(result).toBe(6); - }); - it('should accept a string with comments', async({page, server}) => { - const result = await page.evaluate('2 + 5;\n// do some math!'); - expect(result).toBe(7); - }); - it('should simulate a user gesture', async({page, server}) => { - const result = await page.evaluate(() => document.execCommand('copy')); - expect(result).toBe(true); - }); - it('should evaluate in the page context', async({page, server}) => { - await page.goto(server.PREFIX + '/global-var.html'); - expect(await page.evaluate('globalVar')).toBe(123); - }); - it('should modify global environment', async({page}) => { - await page.evaluate(() => window.globalVar = 123); - expect(await page.evaluate('globalVar')).toBe(123); - }); - }); - - describe('Frame.evaluate', function() { - it('should have different execution contexts', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE2); - expect(page.frames().length).toBe(2); - await page.frames()[0].evaluate(() => window.FOO = 'foo'); - await page.frames()[1].evaluate(() => window.FOO = 'bar'); - expect(await page.frames()[0].evaluate(() => window.FOO)).toBe('foo'); - expect(await page.frames()[1].evaluate(() => window.FOO)).toBe('bar'); - }); - it('should have correct execution contexts', async({page, server}) => { - await page.goto(server.PREFIX + '/frames/one-frame.html'); - expect(page.frames().length).toBe(2); - expect(await page.frames()[0].evaluate(() => document.body.textContent.trim())).toBe(''); - expect(await page.frames()[1].evaluate(() => document.body.textContent.trim())).toBe(`Hi, I'm frame`); - }); - }); - - describe('Page.evaluateOnNewDocument', function() { - it('should evaluate before anything else on the page', async({page, server}) => { - await page.evaluateOnNewDocument(function(){ - window.injected = 123; - }); - await page.goto(server.PREFIX + '/tamperable.html'); - expect(await page.evaluate(() => window.result)).toBe(123); - }); - it('should work with CSP', async({page, server}) => { - server.setCSP('/empty.html', 'script-src ' + server.PREFIX); - await page.evaluateOnNewDocument(function(){ - window.injected = 123; - }); - await page.goto(server.PREFIX + '/empty.html'); - expect(await page.evaluate(() => window.injected)).toBe(123); - - // Make sure CSP works. - await page.addScriptTag({content: 'window.e = 10;'}).catch(e => void e); - expect(await page.evaluate(() => window.e)).toBe(undefined); - }); - }); -}; - diff --git a/experimental/puppeteer-firefox/test/firefoxonly.spec.js b/experimental/puppeteer-firefox/test/firefoxonly.spec.js deleted file mode 100644 index 5babfda2824a6..0000000000000 --- a/experimental/puppeteer-firefox/test/firefoxonly.spec.js +++ /dev/null @@ -1,24 +0,0 @@ -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - describe('Firefox-specific tests', function() { - describe('Browser.version', function() { - it('should return whether we are in headless', async({browser}) => { - const version = await browser.version(); - expect(version.length).toBeGreaterThan(0); - expect(version.startsWith('Firefox/')).toBe(true); - }); - }); - - describe('Browser.userAgent', function() { - it('should include WebKit', async({browser}) => { - const userAgent = await browser.userAgent(); - expect(userAgent.length).toBeGreaterThan(0); - expect(userAgent).toContain('Gecko'); - }); - }); - }); -}; - diff --git a/experimental/puppeteer-firefox/test/frame.spec.js b/experimental/puppeteer-firefox/test/frame.spec.js deleted file mode 100644 index d386aa333ceec..0000000000000 --- a/experimental/puppeteer-firefox/test/frame.spec.js +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ -const utils = require('./utils'); - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('Frame Management', function() { - it('should handle nested frames', async({page, server}) => { - await page.goto(server.PREFIX + '/frames/nested-frames.html'); - expect(utils.dumpFrames(page.mainFrame())).toEqual([ - 'http://localhost:/frames/nested-frames.html', - ' http://localhost:/frames/two-frames.html (2frames)', - ' http://localhost:/frames/frame.html (uno)', - ' http://localhost:/frames/frame.html (dos)', - ' http://localhost:/frames/frame.html (aframe)' - ]); - }); - it('should send events when frames are manipulated dynamically', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - // validate frameattached events - const attachedFrames = []; - page.on('frameattached', frame => attachedFrames.push(frame)); - await utils.attachFrame(page, 'frame1', '/frames/frame.html'); - expect(attachedFrames.length).toBe(1); - expect(attachedFrames[0].url()).toContain('/frames/frame.html'); - - // validate framenavigated events - const navigatedFrames = []; - page.on('framenavigated', frame => navigatedFrames.push(frame)); - await utils.navigateFrame(page, 'frame1', server.PREFIX + '/empty2.html'); - expect(navigatedFrames.length).toBe(1); - expect(navigatedFrames[0].url()).toBe(server.PREFIX + '/empty2.html'); - - // validate framedetached events - const detachedFrames = []; - page.on('framedetached', frame => detachedFrames.push(frame)); - await utils.detachFrame(page, 'frame1'); - expect(detachedFrames.length).toBe(1); - expect(detachedFrames[0].isDetached()).toBe(true); - }); - it('should send "framenavigated" when navigating on anchor URLs', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await Promise.all([ - page.goto(server.EMPTY_PAGE + '#foo'), - utils.waitEvent(page, 'framenavigated') - ]); - expect(page.url()).toBe(server.EMPTY_PAGE + '#foo'); - }); - it('should not send attach/detach events for main frame', async({page, server}) => { - let hasEvents = false; - page.on('frameattached', frame => hasEvents = true); - page.on('framedetached', frame => hasEvents = true); - await page.goto(server.EMPTY_PAGE); - expect(hasEvents).toBe(false); - }); - it('should detach child frames on navigation', async({page, server}) => { - let attachedFrames = []; - let detachedFrames = []; - let navigatedFrames = []; - page.on('frameattached', frame => attachedFrames.push(frame)); - page.on('framedetached', frame => detachedFrames.push(frame)); - page.on('framenavigated', frame => navigatedFrames.push(frame)); - await page.goto(server.PREFIX + '/frames/nested-frames.html'); - expect(attachedFrames.length).toBe(4); - expect(detachedFrames.length).toBe(0); - expect(navigatedFrames.length).toBe(5); - - attachedFrames = []; - detachedFrames = []; - navigatedFrames = []; - await page.goto(server.EMPTY_PAGE); - expect(attachedFrames.length).toBe(0); - expect(detachedFrames.length).toBe(4); - expect(navigatedFrames.length).toBe(1); - }); - it('should support framesets', async({page, server}) => { - let attachedFrames = []; - let detachedFrames = []; - let navigatedFrames = []; - page.on('frameattached', frame => attachedFrames.push(frame)); - page.on('framedetached', frame => detachedFrames.push(frame)); - page.on('framenavigated', frame => navigatedFrames.push(frame)); - await page.goto(server.PREFIX + '/frames/frameset.html'); - expect(attachedFrames.length).toBe(4); - expect(detachedFrames.length).toBe(0); - expect(navigatedFrames.length).toBe(5); - - attachedFrames = []; - detachedFrames = []; - navigatedFrames = []; - await page.goto(server.EMPTY_PAGE); - expect(attachedFrames.length).toBe(0); - expect(detachedFrames.length).toBe(4); - expect(navigatedFrames.length).toBe(1); - }); - it('should report frame.name()', async({page, server}) => { - await utils.attachFrame(page, 'theFrameId', server.EMPTY_PAGE2); - await page.evaluate(url => { - const frame = document.createElement('iframe'); - frame.name = 'theFrameName'; - frame.src = url; - document.body.appendChild(frame); - return new Promise(x => frame.onload = x); - }, server.EMPTY_PAGE); - expect(page.frames()[0].name()).toBe(''); - expect(page.frames()[1].name()).toBe('theFrameId'); - expect(page.frames()[2].name()).toBe('theFrameName'); - }); - it('should report frame.parent()', async({page, server}) => { - await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE); - expect(page.frames()[0].parentFrame()).toBe(null); - expect(page.frames()[1].parentFrame()).toBe(page.mainFrame()); - expect(page.frames()[2].parentFrame()).toBe(page.mainFrame()); - }); - }); - -}; diff --git a/experimental/puppeteer-firefox/test/golden-chromium/grid-cell-0.png b/experimental/puppeteer-firefox/test/golden-chromium/grid-cell-0.png deleted file mode 100644 index 248fdb340f298b9556b8385469792774404c2d37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2o;fFi!P!aSX|5d^<}qgDFs=VZZdY z$S|2T98oS39L5qU1~XhEHt6s#yMF9cPm)Ol&!e2Us|GBuPhBdRu}=EPvP8DY>N$zi zk`EnvcYOQD`nP+Vf8P_Yvye+K-F=t;AERQ!>x!ED&wq&SJ=xM+E3c$sa>N7+dZPYo zv*hugHept0XXbwX^G|OnQ;QQ&sMV=4!^CU(<;0yalTJUah+VH*(h%UWHA;2z$q(Ot zoH+YZb8=jNgpS+Vu*nw~ofOx-4$={;tzH_`c`-v}Au~(*6SJwG)(UsBeEu2ZC0=vl zbQbG@3!iI#&pyl6@Ui{O+u#R^_skM>EpF`QuzB+71xLf@!-wadzdTKJi;%#aiOXMJ z*&e4O=AtC1IoHR-Xy%bZnfr0O@2bi0dB0#-C~$k5BoEsiOFrlG%JC~7?9%IXb9Q3S zd0*H3%x?D4)z>wrpEjB8%Mi6zEL>e($jU*3<-x;8P3<2eUVXiPVSW7N3j){?$HD9W d{p#oXdreKT_B!!uJ}{mbJYD@<);T3K0RT-!(nSCO diff --git a/experimental/puppeteer-firefox/test/golden-chromium/grid-cell-1.png b/experimental/puppeteer-firefox/test/golden-chromium/grid-cell-1.png deleted file mode 100644 index b0483e92c2292027b2c095d6b184246af0adb9c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 288 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nET=bkQ(AsLNtZ*Al})v}4mIS|+{4MZ)?^hOv!xN6? z^=H^@wEEW@=`rv5iA_ANff2qY7OR_AuX37oBx3G44#kMGduHsNXwu2ZP#PQUYx3e( z>S6&iuFTVxo2B<1v`I00@pw~Y^!XQ;7lDKm(TK+l&;R@Ff5*6Gxq{si3q4_=ml-@= L{an^LB{Ts5TpMzG diff --git a/experimental/puppeteer-firefox/test/golden-chromium/screenshot-clip-odd-size.png b/experimental/puppeteer-firefox/test/golden-chromium/screenshot-clip-odd-size.png deleted file mode 100644 index b010d1f87f0cdc2193383fdcf193ca77ee3ae570..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqh5}q!OAsQ2t|NQ^|zn)ErQFtSl eyC{FqQHF!BS%f5Jy;b*203es{f;#V=TKRP=H zHS&zgGq%+6UL5`W4uX0o-|FP4f4*48u`f9W?@I}Je02E90eHT&SB^uOQbEnZtxLL1 zW}}Tm|0)hfoPo@E&p;2|w_6{0nr>B9)NZo^kG#6P1icW%vqrq+C+j6^=)e?;ZaLGf z0i0jZ7a^=octRuvc)*#2N>hTj`x-1kLnP&kw2|JmZN{C&tk)NwR0!BPLElOP>u8dUg&$3O*)89^68P|`I&LJXHq+OFZ)s`yNf{Ip6(w)` zNO2c!LKVR^idf6|R>6~=#M4@Ni^zW)x|vN^+}ZYd5O>a06_uX;bIS5$zK6jbCE`9e z4zl$1L*vmx=Cy@EEo(>}+CiXQxzAH|b#*-oy8F87rjhUfRXzUaGUc@fU35&7#wNn# z&(4!b?*Zgo3!YILY~zD4u{Z3^c*Gza8j4ch*<4}qBA1yCMJDK*H|;*JS@t{Swz5NM zxb5w2gVU$8R~YKACmG57;V1LES$3Q{zZ{}E;$3H53I*vC-cxg<{}e@FG+;U{nj2|K z-;5WvK22uF0jB2WPw*-MO#jd1e8DtK{g<5~3c94a2&K*4T=5y@a3mHAGvSFBe?jiHS1{J0YWOP(H_Rd!tB zqH8{K`sN55nzl}8X={7@{CN`FAv`EZlZqiz@d^RV#mPjawNx0eI?D6Cw*75x+SUh$ z`zL39oJ%%#+wf0{buXqc*^=UD^<@m4tVp0CgT8(z-*yTfdxquGBNZbCs)-n~g_cU; zr&qTR`bRWl>kdPWMH@>b63I-HMx!0PCo-`zao;Jq6yQy`V3E*vA)JwbmTjtQIYcB9 z0idk!Zg-yI%m#Kmrh+M-*R7M`b#L+&LPqP&t^bj-zW|hlh7?O@iPh5pkF(5uK^9x2 zb%#t%B@rS>onF?gv^`T9myrLK-oNbn7mPs`Uf1WUx2we`8^kV(_-vEf{w^?0PHGAl z)zLh7jRMCgEOmk*Sx4r%xnnuE`oaUEtUs#U3bwk6 zDTZ3C@%tfxF}$ru_?+nG&e(4#fmz*|IC}%-Kk3)spXdVsyMQFNd(W|=Ba|LV*%bm5 z@6y?rs<~236@55%kv#^c$wZLOx{KxXdr2iIHXoM`tfU|7Or8?;$5FlRsmnq?aeuk|ij<#Pd z+{J0Fw2`r>izk14J%mn5?q0N0Ce&7w4dP4?zn3>Xr3Z{q`(qF5%`UiOajU7;3yX`xY2$u#-*^-w2hvW#QsQ@EbaF~cC^M|@ zxpmD$yHvq3kF5-`p8R{}jZ9W=Vtd-lD}gVPdzUXqVyC5(49zE1VK9`tyP`F+t!?v8 zs5r(FJPoHwCR@!U>{{|ts>oLKPhN1=FCgSqHkhhOJo0fs@)QndDM}EecXJRcct7rF z1y}$f2>mR|8I|EV(K+4fJ_(n8r1<>+$nN;@a}LFrD#D03#jRi(usYNOaCCbUDY|GX zmmhWyzRTM?}^h_zdhT}pNUJM*KKk2oYPOM)*VeUbl^R* xL1(qH{YFzSghv{Y3|>mU^ExdB^RWeeN$`2#PGmPUZ{j?bP0l+XkKPxw46 diff --git a/experimental/puppeteer-firefox/test/golden-chromium/screenshot-element-rotate.png b/experimental/puppeteer-firefox/test/golden-chromium/screenshot-element-rotate.png deleted file mode 100644 index 52e2a0f6d3c66bd455961250051d416897790ffd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2342 zcmYLLc|6ox8$Tn<*qS!Gq)5u3RfNXP%$3R-YC;oP%aYeNikZn$DHSEMG+c?y*zQO# zSq5p*$l4%op{|RuFJl>I++%t_pZAa7@Av(k=lMS8`#k47=X2t1tu17wl%)UwvX&>Y z_V7zte~{wv{r0#U3jpMVCH6N5V&=p*!WkDwHHqn|GZKx~eg5AVn;7Mn{P`G|q9+{rIi^KOcH zx=zk_{g6^Ry&{Zll`Ew;nud~bBnD0Vv>*$#6JFjtZ!S}{FKlller65_(gnS- zNhKDOyyaQlAwxl3P@_grrXf*e$oZ38b#cXBR9(-w;zs^ZTOM<=_QurPCf?&a7C(r7 zt(TGN>Vu*0Y$CuLiK>}TZ8~Cn>I(Tkm}APuZR{^`F&I9;pOY;14ux%tgGJUG(T*H)QQpAkdMS(McCj*H%@YJ5#*=A>#-Xwgfg<29rI z_Ux54>o!xkZgIUbtLi6pyN~f-;u`MR5rK!IZ>ldE-;HB{X8qRZyoc=b6=mx4)bxoX zhU{xnu2!x-OZb?!s*qeXXgaecUl4SP^DK#m{E=jsaOWn+pKU+2pyP?~v3mWZsI}id z=m-q=VA5Dh`(71A`^?@lIPM8O|d1r|ufvBeH zj>ka6=X;+FoXUf5>8Iac%(HZ{MAe;EAT-c-)a;u(6|nDw9FbR_?}n8z;oi&NBz4OH z({!i+F<{5%;fA_SH7dLB&kwJoOEG%$`>FuIr4e&0W5Tf zh&r9em3B?R=Zcx(pwJf2ln;w0#XO_2lhTw=oNk-(h{2Gbz65mHNl8i;jewbXd+=T| zSSp9CCoHtRNVfSeR!Xn@AgiQdL$)(_Y|p~%*y#Jth7^3PgxN3*g4HcU%9gPtrWY2P~l3Flc(6jsx(p++-3i?O2x;DWhc`K%#Yq_xbxs8kDKZT zy+zE}B}^k|6s6Dzre7ut;}Ul-Q5_!Mxh&~D!JSy$keF_5H&qgU-aLT}9;IncX~cte znsz!ZV@bS@fzGUnOc_qXCKl)EfdLiDAMram{3Eb*ZS6e>N-*$Q>U`TCWnrgOELwK} z%(3Q1m>sQShP!MFcfMY@9DsnoHZ@G~22ZzF+XI|eo;m{`UblJQpn;!G|LcVVsh}C% zJkYVUTg_r~{rG7A``sYwdDNGctJz`Rx)&B+7p@rTA+kpaD5w-E+7+mDVgrJtE6T?BjF+HWD1d(kotGJz#pPp7*n_b@nS-TpTX)^DJdxA z`bS4T10q@Sb=c!Gg=~GDDPYT`Ka9MEasDn;GsxI)t!k3ASkh_?4)e#{$~dV{&cVfD zThw+kbxYR0Z%5JyP8Wq~E;}#_tu{a{$EDH){u$zw<15_6KrZW(5@0dfj`j}o?6A}m zw=vEwK*mbOEi9;uT@ME!zXi0Vl1;bS^BD~B^{gtC1b+DSNNQ3=B>ex8$}j6Pfxi%$ zRuqu%5ap4DC|sBy5zY+}oO|Fk&$CE+z2wu!R=;+wTTO|OF(zV05O4=xK&y~3ttjxj zD$MsHozQgwjIfw$)lSSp^=T$2+{gV+-yc}ji!fg#MZn3IRD*RwLb z?(Rf)MfnPHOprb05c8OZ2M}e5*7?qMX~Mm~WmL3-kRN9f?V+G2eVMn|>C%VEx7sj} zysQ;NbwIUvv|QpWBqvHFagRNJHsUL{c(goP@F4N>yzKn_g+clhb4w7ey^7aIKhcZX zVE9?DB`5F|Dn3Lc%PK1Ghh(js)UEy8s=WdCvMRNuDV^nHg1HUt_CIlJTVWSPZUz}* z(mQNO3D{#dgHu}v`*zei@-=s3otS6>OmX>xXLazzcFhjS>Xmm@=LJD{?&I7|u8^n# z@+)5$X&$nCs-bm!xqA;hw3A0TsV{IK^F33=B9Nv4C3FuL(@7h{bLfPWFvK-p2i@jUIotmc(896Wb&Kt7rl&bETeCcO%py9 d&BB%;BgmKzm3Ksu%kcgKEOFM@;uDvm{{`<2gQEZd diff --git a/experimental/puppeteer-firefox/test/golden-chromium/screenshot-element-scrolled-into-view.png b/experimental/puppeteer-firefox/test/golden-chromium/screenshot-element-scrolled-into-view.png deleted file mode 100644 index 917dd48188d45ea1b07c1fa460a691a9e4a2d050..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^W+2SL1|)l2v+e?^d`}n0kc`H+w+y)&3bP0l+XkKPxw46 diff --git a/experimental/puppeteer-firefox/test/golden-chromium/screenshot-grid-fullpage.png b/experimental/puppeteer-firefox/test/golden-chromium/screenshot-grid-fullpage.png deleted file mode 100644 index db627293a815531a0728eea9f62793aa68ce8698..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75053 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 diff --git a/experimental/puppeteer-firefox/test/golden-chromium/transparent.png b/experimental/puppeteer-firefox/test/golden-chromium/transparent.png deleted file mode 100644 index 1cf45d8688fc68c85c4d250d14eaf6fdf153ec82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 119 zcmeAS@N?(olHy`uVBq!ia0vp^DImYvN4 fzW~T20yHdVoU??1aarlqK9FipS3j3^P64pCDOIKH5=01Lltqk1N(!ldMxTLG=%91Z0tOs89O(;;-%;TcYIGc$?0u}Y aZ}qX3f7?3*4l-994qG_w`ZND`MBiSf?k6e$ diff --git a/experimental/puppeteer-firefox/test/golden-firefox/grid-cell-0.png b/experimental/puppeteer-firefox/test/golden-firefox/grid-cell-0.png deleted file mode 100644 index 4677bdbc4f84999748dc38c3c2038b729acfae17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 331 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2o;fFba9PIEG|2zP+N@*J>cr`fzj8 zx&>m*4ih%8@pvaHa`14uHmy}y!YRbu*7fdXPnCXR+7pAFwL2e~A2*4=BzDfS&Z*$s zL_@W?PD_>wWNNxZaZQ|tU|w4+@ZPr9kmbN4jUDf+tAHHF4LaWEKKo?rGJe>#Zi_^iMl6^sIFFR;$i1 z@A*u#mXF_8e{K5nHL~CNTIt^0#}>=J-`K44`{UJTm44Z|Q4Cq9DL>U~R*r zJFKRG2U%G$6Zp78%KeJP zqGyv&+?&00${#_7oyu!XnNG+v9(cVgBKxdtWcI}KXD!p;oml<-U59d?#Uu@*V;0Jv kSV5YOp_}tZS^WoN`?ktMuij+c1GYfz4YTLM*0l$nZm*nTVzx|}Nzgt&ybl+@P7%FD}Vf6mX(pQXY%CnD80?Br3Q)o*Hich>FiSG{)Y)~v1$Ia%44 z877xhJVRW*{QI}>lcf@qdRT1#`zJ}Aq2+ew*I$1HN<4Yua{k=8uKk=X5#{COT1r*L z#hVqPXD#FYdHVF}N1{zC({3N0uHk!mY4OG*MdzQNp0+IWguIQogMGqym|5OpWT#*Ylr7Y_oo7NZTkGP*m}OTG+#TnrQpSj86xp( zv?i%&ie{V57B!ch6cHNHAFy+Ng|^P&bK%o(-ntbeco4`*PEK}qb(ueJo|f_T>(@Ud z?X-0Qy8G$8^>04Def##&W6$;14~t|M7YDn#oIig)yZ8k{h2HmHzgF$uy&LG6s;Vy4 zu{gvty>FGz zv{dfR2NMLIXtygQsd_G&>zBUrxk6W}t<%$_&eZ3tPxl;ueCd4b-Z=ip$4+fpQ&B1- zCzrMVzP^Q>U0!d5ukEz6z?gVh^Kb37mn9`Oa<4ysF8+8lt3$dtbt4}iUzd_yYP`9` z<(DE}YnI8KewNgEwZzUo-)Lsa<(DO?E1UL;DjleIN`}WdRT2)@Mh;Hiqe}l-X8Nf8 zTKPFFIn@rBCf6L_eJ+}pk^~8JoU6gcKfZH)D{pV;Qk!=Fv9{&ygf5k7_mi*2h-_SC zR(j3$x%SNYVyjoR&tDCcQ3wm|fB$Sz$J4x5U(ze*=|B0)yIKPneSae7JwJNpZo(^2 zA}iT=NA91_`Oiy>1M}BBEQqL5?sZ#yY3J@lQP(y7_O^d!e%&xXw|V9K&AiJt9^nDCNc;4r)|p>FJb%Y_d4f{A0EGL) zPp>QD-7>%OyxCT_m4LZvZZv;+nzWDe{XWGbMb$fsUvJFOd(|7dedT7|q?@`SR}b&K z`pRs^F|pTs7alJ!x_IQ*l~2BNOF?0)a?Rb9|NF;fAm8i_{}Q1p?z`s3hxr%q6dAA> jBB9*rcG7Wj`YbKZ>AzxsJFf|_&}8s*^>bP0l+XkK?WceC diff --git a/experimental/puppeteer-firefox/test/golden-firefox/screenshot-element-bounding-box.png b/experimental/puppeteer-firefox/test/golden-firefox/screenshot-element-bounding-box.png deleted file mode 100644 index f4e059c300ccd27816427fb0dc2c466edc640418..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 311 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nETf1WOmAsLNtuPUx%ItmnP*&); zA!ETTwe{^!ML*d)Un~9E^?^%J&@J$Z>;q9Fp2M}$zLz(wTD5!M`@@Y_N^70%xc~j; z{gf$rM1k$%bjK~J0=bed(QFr|ftj}apIZJgoR3e=`RDR`Ixr*{JYD@<);T3K0RRV7 Bh8_R_ diff --git a/experimental/puppeteer-firefox/test/golden-firefox/screenshot-element-larger-than-viewport.png b/experimental/puppeteer-firefox/test/golden-firefox/screenshot-element-larger-than-viewport.png deleted file mode 100644 index 6d28cddcea336612c0e4ad4df15d34c91b7d0418..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2797 zcmeAS@N?(olHy`uVBq!ia0y~yV2T1^4mP03_vn|e7#O&=c)B=-RLpsM&5)PDfak!5 zPwIDToiEgE6XBaVGuCsGis)G;hJrI0j0`S4Vhj#PMj4}lFq#rZbHQjX7|jKvxnMLG pjOK#TTtL5E@Gk?{B7M1!{a6Rx71&BPt}pZa{4t3!wSE@zDe9k_lqIWn}K@IaJs`|v;M&Qa#kfldWDx`1?>5cAfu z5Zy)Kq=q`DTl}Ny5AKO-274kgBk|nCmy;Wb5}!@MAg_+Uyy=$FBP|TSha<1A(`$1{ zMzXEkVr`n|`Iy%}0B|7cIgNEM`DQ4)vG7-)--^Hb2K$z837t78y_k_z31Cf6?*lPx zHu$|{qUhc6r3d+mgp!Xb+3hAjw<&v3r8Zki*Jx;McgJQgD!ujW_FK704EU(aW3+^r z#`%MA+PnS=KL4?FYkj8VV`*GYXNsQR@N<5FW|}0<+qT>YIb|9S(%Q_FzhAZ4R2+!j zd}BBBDlJ&sQl5RLsn}84|HLQ(ZnFN~FnWE!TFDY8Hj9;4lK%f!SO)lDyf#(O+6G!Ss~F!~U?4dG^2SVGxpaO` zK!x4Il`;n9`erKLV$n>$c$Dc6UM8k-FZZz;j_hjcU}gN0|O({i(Y1J;(f5w>x8kdH8RR;C)Rp=EUfi zahff{41ey&Mtp@)oTGLORiiF+({p_xbRi96j`dC@o(7)7m5A~K8esf-wYAuJq2|U>MDs4KK#NQ91%Vq680yX zD~5@ga$;OPnnM9Gb zFXG`N%hCIH5M6^gJ6+0<=OCRC4;@9UV(*V$Z_4o2egZ9dY0Y zai6t{)rRDf=eJL;>=!_m2(-6llDz{ak;2Qh+#00q&P!0Z9UR(oNX05c7Id;d0Be(T zB{BSjUy)*8QEj+xGIH~z6afXH5cUwV3I-Hi-*(MH6daE8uqXEy_5SlR1U`WVdUEvO zU68tHq1{M;@fSj(4g^7e9;Vn;Q6%JneMD7vir%xR1o-Jw;s+mAMakOma547t5x3X( zQ`&V}e0Nf$4-af0RCT)`4g#KL;SAC+$w8Zg42QLAJ0-YaKsH8(YLR#?Zv1{6(WZ-c z*`Me#^EC@}WdD|{xG~7)8ThD1!x2y_>(6Uuvk+UwYUa%yKAZN4?z;e`96`=gpDwwE zIQU-IED&^n@c~fns)E;+Gj%7Snf^{UE5#rBjfJ=d-{3arBI8uuy1v0RAHa@q7=Tj^ zb`^7jx0e$4L;gpHTLUEw6lv>Dgpj(GZF_R5k|z7pR)b003P^y`_fW>)tl2wz#eiv; zIl)GxJA_qo!jQ2_ox%fMH>RCUnhfL@{2ada)QVg{vNy3dARVfiTiD+7Y!02)%9O04 z@{bnFFp&HPclL-ji^Rvj{Q4v9vrtE*SU(^Ds%c?>1MkI(T`?-wtqi^XmF&LbQw|g9 z!>z#7d$GK}Yc%=ftsV8qmrgtnuEC*519L^&@;dJ9nZw{i<_V$_ZqtFIL_6^CoECQW zA$>Fan)(MWKWo(E5{hIwpr&yg=T*mj90MYN)OZhK?J3DB?I1{oBU-3fu}Xcj0F@Yi z7d*ps0bhMeCe{W@Ry}Dzs^}IzGH)to&XWo2M*Ow>9GgX3&Ue6+s|;g5BJ{GP)T`8m zDveVmI?2sq4n3k`*Ig@su@vlJb3ud7sw*{j2aaI)x0CFv`!w_+d_7S|KR|-Khy6%Ew1n{C(mN@ diff --git a/experimental/puppeteer-firefox/test/golden-firefox/screenshot-element-scrolled-into-view.png b/experimental/puppeteer-firefox/test/golden-firefox/screenshot-element-scrolled-into-view.png deleted file mode 100644 index 2b72c7528b256daf8afa4ec296e50429eaa8d307..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 153 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 diff --git a/experimental/puppeteer-firefox/test/golden-firefox/screenshot-offscreen-clip.png b/experimental/puppeteer-firefox/test/golden-firefox/screenshot-offscreen-clip.png deleted file mode 100644 index 791496e5da72ddc20aabbbbca900eeafab0e144b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1121 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 siv2j|fBXqN|ICKzN 0 ? { diff: PNG.sync.write(diff) } : null; -} - -/** - * @param {?Object} actual - * @param {!Buffer} expectedBuffer - * @return {?{diff: (!Object:undefined), errorMessage: (string|undefined)}} - */ -function compareText(actual, expectedBuffer) { - if (typeof actual !== 'string') - return { errorMessage: 'Actual result should be string' }; - const expected = expectedBuffer.toString('utf-8'); - if (expected === actual) - return null; - const diff = new Diff(); - const result = diff.main(expected, actual); - diff.cleanupSemantic(result); - let html = diff.prettyHtml(result); - const diffStylePath = path.join(__dirname, 'diffstyle.css'); - html = `` + html; - return { - diff: html, - diffExtension: '.html' - }; -} - -/** - * @param {?Object} actual - * @param {string} goldenName - * @return {!{pass: boolean, message: (undefined|string)}} - */ -function compare(goldenPath, outputPath, actual, goldenName) { - goldenPath = path.normalize(goldenPath); - outputPath = path.normalize(outputPath); - const expectedPath = path.join(goldenPath, goldenName); - const actualPath = path.join(outputPath, goldenName); - - const messageSuffix = 'Output is saved in "' + path.basename(outputPath + '" directory'); - - if (!fs.existsSync(expectedPath)) { - ensureOutputDir(); - fs.writeFileSync(actualPath, actual); - return { - pass: false, - message: goldenName + ' is missing in golden results. ' + messageSuffix - }; - } - const expected = fs.readFileSync(expectedPath); - const mimeType = mime.getType(goldenName); - const comparator = GoldenComparators[mimeType]; - if (!comparator) { - return { - pass: false, - message: 'Failed to find comparator with type ' + mimeType + ': ' + goldenName - }; - } - const result = comparator(actual, expected, mimeType); - if (!result) - return { pass: true }; - ensureOutputDir(); - if (goldenPath === outputPath) { - fs.writeFileSync(addSuffix(actualPath, '-actual'), actual); - } else { - fs.writeFileSync(actualPath, actual); - // Copy expected to the output/ folder for convenience. - fs.writeFileSync(addSuffix(actualPath, '-expected'), expected); - } - if (result.diff) { - const diffPath = addSuffix(actualPath, '-diff', result.diffExtension); - fs.writeFileSync(diffPath, result.diff); - } - - let message = goldenName + ' mismatch!'; - if (result.errorMessage) - message += ' ' + result.errorMessage; - return { - pass: false, - message: message + ' ' + messageSuffix - }; - - function ensureOutputDir() { - if (!fs.existsSync(outputPath)) - fs.mkdirSync(outputPath); - } -} - -/** - * @param {string} filePath - * @param {string} suffix - * @param {string=} customExtension - * @return {string} - */ -function addSuffix(filePath, suffix, customExtension) { - const dirname = path.dirname(filePath); - const ext = path.extname(filePath); - const name = path.basename(filePath, ext); - return path.join(dirname, name + suffix + (customExtension || ext)); -} diff --git a/experimental/puppeteer-firefox/test/hover.spec.js b/experimental/puppeteer-firefox/test/hover.spec.js deleted file mode 100644 index 485ab1e225ee3..0000000000000 --- a/experimental/puppeteer-firefox/test/hover.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('Hover', function() { - it('should trigger hover state', async({page, server}) => { - await page.goto(server.PREFIX + '/input/scrollable.html'); - await page.hover('#button-6'); - expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6'); - await page.hover('#button-2'); - expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-2'); - await page.hover('#button-91'); - expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-91'); - }); - }); -}; - diff --git a/experimental/puppeteer-firefox/test/ignorehttpserrors.spec.js b/experimental/puppeteer-firefox/test/ignorehttpserrors.spec.js deleted file mode 100644 index 64e6e61bcbb20..0000000000000 --- a/experimental/puppeteer-firefox/test/ignorehttpserrors.spec.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ - -module.exports.addTests = function({testRunner, expect, product, puppeteer, defaultBrowserOptions}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('ignoreHTTPSErrors', function() { - beforeAll(async state => { - const options = Object.assign({ignoreHTTPSErrors: true}, defaultBrowserOptions); - state.browser = await puppeteer.launch(options); - }); - afterAll(async state => { - await state.browser.close(); - delete state.browser; - }); - beforeEach(async state => { - state.page = await state.browser.newPage(); - }); - afterEach(async state => { - await state.page.close(); - delete state.page; - }); - it('should work', async({page, httpsServer}) => { - let error = null; - await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e); - expect(error).toBe(null); - }); - it('should work with mixed content', async({page, server, httpsServer}) => { - httpsServer.setRoute('/mixedcontent.html', (req, res) => { - res.end(``); - }); - await page.goto(httpsServer.PREFIX + '/mixedcontent.html', {waitUntil: 'load'}); - expect(page.frames().length).toBe(2); - // Make sure blocked iframe has functional execution context - // @see https://github.com/GoogleChrome/puppeteer/issues/2709 - expect(await page.frames()[0].evaluate('1 + 2')).toBe(3); - expect(await page.frames()[1].evaluate('2 + 3')).toBe(5); - }); - }); -}; diff --git a/experimental/puppeteer-firefox/test/jshandle.spec.js b/experimental/puppeteer-firefox/test/jshandle.spec.js deleted file mode 100644 index c4845bb3a22d3..0000000000000 --- a/experimental/puppeteer-firefox/test/jshandle.spec.js +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ -const utils = require('./utils'); - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('Page.evaluateHandle', function() { - it('should work', async({page, server}) => { - const windowHandle = await page.evaluateHandle(() => window); - expect(windowHandle).toBeTruthy(); - }); - it('should accept object handle as an argument', async({page, server}) => { - const navigatorHandle = await page.evaluateHandle(() => navigator); - const text = await page.evaluate(e => e.userAgent, navigatorHandle); - expect(text).toContain('Mozilla'); - }); - it('should accept object handle to primitive types', async({page, server}) => { - const aHandle = await page.evaluateHandle(() => 5); - const isFive = await page.evaluate(e => Object.is(e, 5), aHandle); - expect(isFive).toBeTruthy(); - }); - it('should accept object handle to unserializable value', async({page, server}) => { - const aHandle = await page.evaluateHandle(() => Infinity); - expect(await page.evaluate(e => Object.is(e, Infinity), aHandle)).toBe(true); - }); - it('should use the same JS wrappers', async({page, server}) => { - const aHandle = await page.evaluateHandle(() => { - window.FOO = 123; - return window; - }); - expect(await page.evaluate(e => e.FOO, aHandle)).toBe(123); - }); - it('should work with primitives', async({page, server}) => { - const aHandle = await page.evaluateHandle(() => { - window.FOO = 123; - return window; - }); - expect(await page.evaluate(e => e.FOO, aHandle)).toBe(123); - }); - }); - - describe('JSHandle.jsonValue', function() { - it('should work', async({page, server}) => { - const aHandle = await page.evaluateHandle(() => ({foo: 'bar'})); - const json = await aHandle.jsonValue(); - expect(json).toEqual({foo: 'bar'}); - }); - // This relies on Chrome's internal serialization. We can either repeat it - // in FFOX, or migrate to JSON.stringify() logic altogether. - (FFOX ? xit : it)('should not work with dates', async({page, server}) => { - const dateHandle = await page.evaluateHandle(() => new Date('2017-09-26T00:00:00.000Z')); - const json = await dateHandle.jsonValue(); - expect(json).toEqual({}); - }); - it('should throw for circular objects', async({page, server}) => { - const windowHandle = await page.evaluateHandle('window'); - let error = null; - await windowHandle.jsonValue().catch(e => error = e); - expect(error).not.toBe(null); - }); - }); - - describe('JSHandle.toString', function() { - it('should work for primitives', async({page, server}) => { - const numberHandle = await page.evaluateHandle(() => 2); - expect(numberHandle.toString()).toBe('JSHandle:2'); - const stringHandle = await page.evaluateHandle(() => 'a'); - expect(stringHandle.toString()).toBe('JSHandle:a'); - }); - it('should work for complicated objects', async({page, server}) => { - const aHandle = await page.evaluateHandle(() => window); - expect(aHandle.toString()).toBe('JSHandle@object'); - }); - it('should work with different subtypes', async({page, server}) => { - expect((await page.evaluateHandle('(function(){})')).toString()).toBe('JSHandle@function'); - expect((await page.evaluateHandle('12')).toString()).toBe('JSHandle:12'); - expect((await page.evaluateHandle('true')).toString()).toBe('JSHandle:true'); - expect((await page.evaluateHandle('undefined')).toString()).toBe('JSHandle:undefined'); - expect((await page.evaluateHandle('"foo"')).toString()).toBe('JSHandle:foo'); - expect((await page.evaluateHandle('Symbol()')).toString()).toBe('JSHandle@symbol'); - expect((await page.evaluateHandle('new Map()')).toString()).toBe('JSHandle@map'); - expect((await page.evaluateHandle('new Set()')).toString()).toBe('JSHandle@set'); - expect((await page.evaluateHandle('[]')).toString()).toBe('JSHandle@array'); - expect((await page.evaluateHandle('null')).toString()).toBe('JSHandle:null'); - expect((await page.evaluateHandle('/foo/')).toString()).toBe('JSHandle@regexp'); - expect((await page.evaluateHandle('document.body')).toString()).toBe('JSHandle@node'); - expect((await page.evaluateHandle('new Date()')).toString()).toBe('JSHandle@date'); - expect((await page.evaluateHandle('new WeakMap()')).toString()).toBe('JSHandle@weakmap'); - expect((await page.evaluateHandle('new WeakSet()')).toString()).toBe('JSHandle@weakset'); - expect((await page.evaluateHandle('new Error()')).toString()).toBe('JSHandle@error'); - expect((await page.evaluateHandle('new Int32Array()')).toString()).toBe('JSHandle@typedarray'); - expect((await page.evaluateHandle('new Proxy({}, {})')).toString()).toBe('JSHandle@proxy'); - }); - }); - - describe('JSHandle.getProperties', function() { - it('should work', async({page, server}) => { - const aHandle = await page.evaluateHandle(() => ({ - foo: 'bar' - })); - const properties = await aHandle.getProperties(); - const foo = properties.get('foo'); - expect(foo).toBeTruthy(); - expect(await foo.jsonValue()).toBe('bar'); - }); - it('should return even non-own properties', async({page, server}) => { - const aHandle = await page.evaluateHandle(() => { - class A { - constructor() { - this.a = '1'; - } - } - class B extends A { - constructor() { - super(); - this.b = '2'; - } - } - return new B(); - }); - const properties = await aHandle.getProperties(); - expect(await properties.get('a').jsonValue()).toBe('1'); - expect(await properties.get('b').jsonValue()).toBe('2'); - }); - }); - - describe('JSHandle.getProperty', function() { - it('should work', async({page, server}) => { - const aHandle = await page.evaluateHandle(() => ({ - one: 1, - two: 2, - three: 3 - })); - const twoHandle = await aHandle.getProperty('two'); - expect(await twoHandle.jsonValue()).toEqual(2); - }); - }); -}; diff --git a/experimental/puppeteer-firefox/test/keyboard.spec.js b/experimental/puppeteer-firefox/test/keyboard.spec.js deleted file mode 100644 index 66e054f6d6244..0000000000000 --- a/experimental/puppeteer-firefox/test/keyboard.spec.js +++ /dev/null @@ -1,193 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ -const utils = require('./utils'); -const os = require('os'); - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('Keyboard', function() { - it('should type into a textarea', async({page, server}) => { - await page.evaluate(() => { - const textarea = document.createElement('textarea'); - document.body.appendChild(textarea); - textarea.focus(); - }); - const text = 'Hello world. I am the text that was typed!'; - await page.keyboard.type(text); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe(text); - }); - it('should type emoji', async({page, server}) => { - await page.evaluate(() => { - const textarea = document.createElement('textarea'); - document.body.appendChild(textarea); - textarea.focus(); - }); - await page.keyboard.type('👹 Tokyo street Japan 🇯🇵'); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('👹 Tokyo street Japan 🇯🇵'); - }); - it('should type emoji', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.type('textarea', '👹 Tokyo street Japan 🇯🇵'); - expect(await page.$eval('textarea', textarea => textarea.value)).toBe('👹 Tokyo street Japan 🇯🇵'); - }); - it('should type emoji into an iframe', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await utils.attachFrame(page, 'emoji-test', server.PREFIX + '/input/textarea.html'); - const frame = page.frames()[1]; - const textarea = await frame.$('textarea'); - await textarea.type('👹 Tokyo street Japan 🇯🇵'); - expect(await frame.$eval('textarea', textarea => textarea.value)).toBe('👹 Tokyo street Japan 🇯🇵'); - }); - it('should send a character with sendCharacter', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.focus('textarea'); - await page.keyboard.sendCharacter('嗨'); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨'); - await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true)); - await page.keyboard.sendCharacter('a'); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨a'); - }); - it('should press the metaKey', async({page}) => { - await page.evaluate(() => { - window.keyPromise = new Promise(resolve => document.addEventListener('keydown', event => resolve(event.key))); - }); - await page.keyboard.press('Meta'); - expect(await page.evaluate('keyPromise')).toBe(FFOX && os.platform() !== 'darwin' ? 'OS' : 'Meta'); - }); - xit('should report shiftKey', async({page, server}) => { - await page.goto(server.PREFIX + '/input/keyboard.html'); - const keyboard = page.keyboard; - const codeForKey = {'Shift': 16, 'Alt': 18, 'Meta': 91, 'Control': 17}; - // Firefox has no Meta modifier on Windows/Linux - if (FFOX && os.platform() !== 'darwin') - delete codeForKey['Meta']; - for (const modifierKey in codeForKey) { - await keyboard.down(modifierKey); - expect(await page.evaluate(() => getResult())).toBe('Keydown: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' [' + modifierKey + ']'); - await keyboard.down('!'); - // Shift+! will generate a keypress - if (modifierKey === 'Shift') - expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']\nKeypress: ! Digit1 33 33 [' + modifierKey + ']'); - else - expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']'); - - await keyboard.up('!'); - expect(await page.evaluate(() => getResult())).toBe('Keyup: ! Digit1 49 [' + modifierKey + ']'); - await keyboard.up(modifierKey); - expect(await page.evaluate(() => getResult())).toBe('Keyup: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' []'); - } - }); - xit('should report multiple modifiers', async({page, server}) => { - await page.goto(server.PREFIX + '/input/keyboard.html'); - const keyboard = page.keyboard; - await keyboard.down('Control'); - expect(await page.evaluate(() => getResult())).toBe('Keydown: Control ControlLeft 17 [Control]'); - await keyboard.down('Alt'); - expect(await page.evaluate(() => getResult())).toBe('Keydown: Alt AltLeft 18 [Alt Control]'); - await keyboard.down(';'); - expect(await page.evaluate(() => getResult())).toBe('Keydown: ; Semicolon 186 [Alt Control]'); - await keyboard.up(';'); - expect(await page.evaluate(() => getResult())).toBe('Keyup: ; Semicolon 186 [Alt Control]'); - await keyboard.up('Control'); - expect(await page.evaluate(() => getResult())).toBe('Keyup: Control ControlLeft 17 [Alt]'); - await keyboard.up('Alt'); - expect(await page.evaluate(() => getResult())).toBe('Keyup: Alt AltLeft 18 []'); - }); - it('should send proper codes while typing', async({page, server}) => { - await page.goto(server.PREFIX + '/input/keyboard.html'); - await page.keyboard.type('!'); - expect(await page.evaluate(() => getResult())).toBe( - [ 'Keydown: ! Digit1 49 []', - 'Keypress: ! Digit1 33 33 []', - 'Keyup: ! Digit1 49 []'].join('\n')); - await page.keyboard.type('^'); - expect(await page.evaluate(() => getResult())).toBe( - [ 'Keydown: ^ Digit6 54 []', - 'Keypress: ^ Digit6 94 94 []', - 'Keyup: ^ Digit6 54 []'].join('\n')); - }); - it('should send proper codes while typing with shift', async({page, server}) => { - await page.goto(server.PREFIX + '/input/keyboard.html'); - const keyboard = page.keyboard; - await keyboard.down('Shift'); - await page.keyboard.type('~'); - expect(await page.evaluate(() => getResult())).toBe( - [ 'Keydown: Shift ShiftLeft 16 [Shift]', - 'Keydown: ~ Backquote 192 [Shift]', // 192 is ` keyCode - 'Keypress: ~ Backquote 126 126 [Shift]', // 126 is ~ charCode - 'Keyup: ~ Backquote 192 [Shift]'].join('\n')); - await keyboard.up('Shift'); - }); - it('should not type canceled events', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.focus('textarea'); - await page.evaluate(() => { - window.addEventListener('keydown', event => { - event.stopPropagation(); - event.stopImmediatePropagation(); - if (event.key === 'l') - event.preventDefault(); - if (event.key === 'o') - event.preventDefault(); - }, false); - }); - await page.keyboard.type('Hello World!'); - expect(await page.evaluate(() => textarea.value)).toBe('He Wrd!'); - }); - it('should specify repeat property', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.focus('textarea'); - await page.evaluate(() => document.querySelector('textarea').addEventListener('keydown', e => window.lastEvent = e, true)); - await page.keyboard.down('a'); - expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false); - await page.keyboard.press('a'); - expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true); - - await page.keyboard.down('b'); - expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false); - await page.keyboard.down('b'); - expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true); - - await page.keyboard.up('a'); - await page.keyboard.down('a'); - expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false); - }); - it('should type all kinds of characters', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.focus('textarea'); - const text = 'This text goes onto two lines.\nThis character is 嗨.'; - await page.keyboard.type(text); - expect(await page.evaluate('result')).toBe(text); - }); - it('should throw on unknown keys', async({page, server}) => { - let error = await page.keyboard.press('NotARealKey').catch(e => e); - expect(error.message).toBe('Unknown key: "NotARealKey"'); - - error = await page.keyboard.press('ё').catch(e => e); - expect(error && error.message).toBe('Unknown key: "ё"'); - - error = await page.keyboard.press('😊').catch(e => e); - expect(error && error.message).toBe('Unknown key: "😊"'); - }); - }); -}; - diff --git a/experimental/puppeteer-firefox/test/launcher.spec.js b/experimental/puppeteer-firefox/test/launcher.spec.js deleted file mode 100644 index da874933d12d4..0000000000000 --- a/experimental/puppeteer-firefox/test/launcher.spec.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ -const fs = require('fs'); - -module.exports.addTests = function({testRunner, expect, product, puppeteer, defaultBrowserOptions}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - describe('Launcher.executablePath', function() { - it('should work', async({server}) => { - const executablePath = puppeteer.executablePath(); - expect(fs.existsSync(executablePath)).toBe(true); - }); - }); - - describe('Launcher.launch', () => { - it('should set the default viewport', async() => { - const options = Object.assign({}, defaultBrowserOptions, { - defaultViewport: { - width: 456, - height: 789 - } - }); - const browser = await puppeteer.launch(options); - const page = await browser.newPage(); - expect(await page.evaluate('window.innerWidth')).toBe(456); - expect(await page.evaluate('window.innerHeight')).toBe(789); - await browser.close(); - }); - it('should disable the default viewport', async() => { - const options = Object.assign({}, defaultBrowserOptions, { - defaultViewport: null - }); - const browser = await puppeteer.launch(options); - const page = await browser.newPage(); - expect(page.viewport()).toBe(null); - await browser.close(); - }); - }); -} diff --git a/experimental/puppeteer-firefox/test/mouse.spec.js b/experimental/puppeteer-firefox/test/mouse.spec.js deleted file mode 100644 index 5d0312fd41e62..0000000000000 --- a/experimental/puppeteer-firefox/test/mouse.spec.js +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('Mouse', function() { - it('should click the document', async({page, server}) => { - await page.evaluate(() => { - window.clickPromise = new Promise(resolve => { - document.addEventListener('click', event => { - resolve({ - type: event.type, - detail: event.detail, - clientX: event.clientX, - clientY: event.clientY, - isTrusted: event.isTrusted, - button: event.button - }); - }); - }); - }); - await page.mouse.click(50, 60); - const event = await page.evaluate(() => window.clickPromise); - expect(event.type).toBe('click'); - expect(event.detail).toBe(1); - expect(event.clientX).toBe(50); - expect(event.clientY).toBe(60); - expect(event.isTrusted).toBe(true); - expect(event.button).toBe(0); - }); - it('should resize the textarea', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - const {x, y, width, height} = await page.evaluate(dimensions); - const mouse = page.mouse; - await mouse.move(x + width - 4, y + height - 4); - await mouse.down(); - await mouse.move(x + width + 100, y + height + 100); - await mouse.up(); - const newDimensions = await page.evaluate(dimensions); - expect(newDimensions.width).toBe(Math.round(width + 104)); - expect(newDimensions.height).toBe(Math.round(height + 104)); - }); - it('should select the text with mouse', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.focus('textarea'); - const text = 'This is the text that we are going to try to select. Let\'s see how it goes.'; - await page.keyboard.type(text); - // Firefox needs an extra frame here after typing or it will fail to set the scrollTop - await page.evaluate(() => new Promise(requestAnimationFrame)); - await page.evaluate(() => document.querySelector('textarea').scrollTop = 0); - const {x, y} = await page.evaluate(dimensions); - await page.mouse.move(x + 2,y + 2); - await page.mouse.down(); - await page.mouse.move(200,100); - await page.mouse.up(); - expect(await page.evaluate(() => { - const textarea = document.querySelector('textarea'); - return textarea.value.substring(textarea.selectionStart, textarea.selectionEnd); - })).toBe(text); - }); - it('should tween mouse movement', async({page, server}) => { - await page.mouse.move(100, 100); - await page.evaluate(() => { - window.result = []; - document.addEventListener('mousemove', event => { - window.result.push([event.clientX, event.clientY]); - }); - }); - await page.mouse.move(200, 300, {steps: 5}); - expect(await page.evaluate('result')).toEqual([ - [120, 140], - [140, 180], - [160, 220], - [180, 260], - [200, 300] - ]); - }); - }); -}; - -function dimensions() { - const rect = document.querySelector('textarea').getBoundingClientRect(); - return { - x: rect.left, - y: rect.top, - width: rect.width, - height: rect.height - }; -} diff --git a/experimental/puppeteer-firefox/test/navigation.spec.js b/experimental/puppeteer-firefox/test/navigation.spec.js deleted file mode 100644 index 2029e5c5ab39f..0000000000000 --- a/experimental/puppeteer-firefox/test/navigation.spec.js +++ /dev/null @@ -1,285 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ - -const utils = require('./utils'); - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('Page.goto', function () { - it('should work', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - expect(page.url()).toBe(server.EMPTY_PAGE); - }); - it('should work with anchor navigation', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - expect(page.url()).toBe(server.EMPTY_PAGE); - await page.goto(server.EMPTY_PAGE + '#foo'); - expect(page.url()).toBe(server.EMPTY_PAGE + '#foo'); - await page.goto(server.EMPTY_PAGE + '#bar'); - expect(page.url()).toBe(server.EMPTY_PAGE + '#bar'); - }); - it('should navigate to about:blank', async({page, server}) => { - await page.goto('about:blank'); - expect(page.url()).toBe('about:blank'); - }); - it('should work with redirects', async({page, server}) => { - server.setRedirect('/redirect/1.html', '/redirect/2.html'); - server.setRedirect('/redirect/2.html', '/empty.html'); - await page.goto(server.PREFIX + '/redirect/1.html'); - expect(page.url()).toBe(server.EMPTY_PAGE); - }); - it('should work with subframes return 204', async({page, server}) => { - server.setRoute('/frames/frame.html', (req, res) => { - res.statusCode = 204; - res.end(); - }); - await page.goto(server.PREFIX + '/frames/one-frame.html'); - }); - it('should fail when server returns 204', async({page, server}) => { - server.setRoute('/empty.html', (req, res) => { - res.statusCode = 204; - res.end(); - }); - let error = null; - await page.goto(server.EMPTY_PAGE).catch(e => error = e); - expect(error).not.toBe(null); - expect(error.message).toContain('ABORTED'); - }); - it('should work when page calls history API in beforeunload', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.evaluate(() => { - window.addEventListener('beforeunload', () => history.replaceState(null, 'initial', window.location.href), false); - }); - await page.goto(server.PREFIX + '/grid.html'); - }); - it('should fail when navigating to bad url', async({page, server}) => { - let error = null; - await page.goto('asdfasdf').catch(e => error = e); - expect(error).not.toBe(null); - }); - it('should fail when navigating to bad url', async({page, server}) => { - let error = null; - await page.goto('asdf').catch(e => error = e); - expect(error).not.toBe(null); - }); - it('should fail when navigating to bad SSL', async({page, httpsServer}) => { - let error = null; - await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e); - expect(error).not.toBe(null); - }); - it('should fail when navigating to bad SSL after redirects', async({page, server, httpsServer}) => { - httpsServer.setRedirect('/redirect/1.html', '/redirect/2.html'); - httpsServer.setRedirect('/redirect/2.html', '/empty.html'); - let error = null; - await page.goto(httpsServer.PREFIX + '/redirect/1.html').catch(e => error = e); - expect(error).not.toBe(null); - }); - it('should fail when main resources failed to load', async({page, server}) => { - let error = null; - await page.goto('http://localhost:44123/non-existing-url').catch(e => error = e); - expect(error).not.toBe(null); - }); - it('should fail when exceeding maximum navigation timeout', async({page, server}) => { - // Hang for request to the empty.html - server.setRoute('/empty.html', (req, res) => { }); - let error = null; - await page.goto(server.PREFIX + '/empty.html', {timeout: 1}).catch(e => error = e); - expect(error.message).toContain('Navigation Timeout Exceeded: 1ms'); - }); - it('should disable timeout when its set to 0', async({page, server}) => { - await page.goto(server.PREFIX + '/grid.html', {timeout: 0}); - }); - it('should work when navigating to data url', async({page, server}) => { - await page.goto('data:text/html,hello'); - }); - it('should work when navigating to 404', async({page, server}) => { - await page.goto(server.PREFIX + '/not-found'); - }); - it('should not leak listeners during navigation', async({page, server}) => { - let warning = null; - const warningHandler = w => warning = w; - process.on('warning', warningHandler); - for (let i = 0; i < 20; ++i) - await page.goto(server.EMPTY_PAGE); - process.removeListener('warning', warningHandler); - expect(warning).toBe(null); - }); - it('should fail when navigating and show the url at the error message', async function({page, server, httpsServer}) { - const url = httpsServer.PREFIX + '/redirect/1.html'; - let error = null; - try { - await page.goto(url); - } catch (e) { - error = e; - } - expect(error.message).toContain(url); - }); - }); - - describe('Page.waitForNavigation', function() { - it('should work', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const [response] = await Promise.all([ - page.waitForNavigation(), - page.evaluate(url => window.location.href = url, server.PREFIX + '/grid.html') - ]); - expect(page.url()).toBe(server.PREFIX + '/grid.html'); - }); - it('should work with both domcontentloaded and load', async({page, server}) => { - let response = null; - server.setRoute('/one-style.css', (req, res) => response = res); - const navigationPromise = page.goto(server.PREFIX + '/one-style.html'); - const domContentLoadedPromise = page.waitForNavigation({ - waitUntil: 'domcontentloaded' - }); - - let bothFired = false; - const bothFiredPromise = page.waitForNavigation({ - waitUntil: ['load', 'domcontentloaded'] - }).then(() => bothFired = true); - - await server.waitForRequest('/one-style.css'); - await domContentLoadedPromise; - expect(bothFired).toBe(false); - response.end(); - await bothFiredPromise; - await navigationPromise; - }); - it('should work with clicking on anchor links', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.setContent(`foobar`); - await Promise.all([ - page.waitForNavigation(), - page.click('a'), - ]); - expect(page.url()).toBe(server.EMPTY_PAGE + '#foobar'); - }); - it('should work with history.pushState()', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.setContent(` - SPA - - `); - await Promise.all([ - page.waitForNavigation(), - page.click('a'), - ]); - expect(page.url()).toBe(server.PREFIX + '/wow.html'); - }); - it('should work with history.replaceState()', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.setContent(` - SPA - - `); - await Promise.all([ - page.waitForNavigation(), - page.click('a'), - ]); - expect(page.url()).toBe(server.PREFIX + '/replaced.html'); - }); - xit('should work with DOM history.back()/history.forward()', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.setContent(` - back - forward - - `); - expect(page.url()).toBe(server.PREFIX + '/second.html'); - await Promise.all([ - page.waitForNavigation(), - page.click('a#back'), - ]); - expect(page.url()).toBe(server.PREFIX + '/first.html'); - await Promise.all([ - page.waitForNavigation(), - page.click('a#forward'), - ]); - expect(page.url()).toBe(server.PREFIX + '/second.html'); - }); - it('should work when subframe issues window.stop()', async({page, server}) => { - server.setRoute('/frames/style.css', (req, res) => {}); - const navigationPromise = page.goto(server.PREFIX + '/frames/one-frame.html'); - const frame = await utils.waitEvent(page, 'frameattached'); - await new Promise(fulfill => { - page.on('framenavigated', f => { - if (f === frame) - fulfill(); - }); - }); - await Promise.all([ - frame.evaluate(() => window.stop()), - navigationPromise - ]); - }); - }); - - describe('Page.goBack + Page.goForward', function() { - it('should work', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.goto(server.PREFIX + '/grid.html'); - - await page.goBack(); - expect(page.url()).toBe(server.EMPTY_PAGE); - - await page.goForward(); - expect(page.url()).toContain('/grid.html'); - - await page.goForward(); - expect(page.url()).toContain('/grid.html'); - }); - it('should work with HistoryAPI', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.evaluate(() => { - history.pushState({}, '', '/first.html'); - history.pushState({}, '', '/second.html'); - }); - expect(page.url()).toBe(server.PREFIX + '/second.html'); - - await page.goBack(); - expect(page.url()).toBe(server.PREFIX + '/first.html'); - await page.goBack(); - expect(page.url()).toBe(server.EMPTY_PAGE); - await page.goForward(); - expect(page.url()).toBe(server.PREFIX + '/first.html'); - }); - }); - - describe('Page.reload', function() { - it('should work', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.evaluate(() => window._foo = 10); - await page.reload(); - expect(await page.evaluate(() => window._foo)).toBe(undefined); - }); - }); -}; - diff --git a/experimental/puppeteer-firefox/test/page.spec.js b/experimental/puppeteer-firefox/test/page.spec.js deleted file mode 100644 index 3a26a9b71356f..0000000000000 --- a/experimental/puppeteer-firefox/test/page.spec.js +++ /dev/null @@ -1,403 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ -const utils = require('./utils'); -const path = require('path'); - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('Page.Events.DOMContentLoaded', function() { - it('should fire when expected', async({page, server}) => { - await Promise.all([ - page.goto('about:blank'), - utils.waitEvent(page, 'domcontentloaded'), - ]); - }); - }); - describe('Page.Events.Load', function() { - it('should fire when expected', async({page, server}) => { - await Promise.all([ - page.goto('about:blank'), - utils.waitEvent(page, 'load'), - ]); - }); - }); - - describe('Page.Events.PageError', function() { - it('should fire', async({page, server}) => { - let error = null; - page.once('pageerror', e => error = e); - await Promise.all([ - page.goto(server.PREFIX + '/error.html'), - utils.waitEvent(page, 'pageerror') - ]); - expect(error.message).toContain('Fancy'); - }); - }); - - - describe('Page.close', function() { - it('should reject all promises when page is closed', async({browser}) => { - const newPage = await browser.newPage(); - const neverResolves = newPage.evaluate(() => new Promise(r => {})); - newPage.close(); - let error = null; - await neverResolves.catch(e => error = e); - expect(error).not.toBe(null); - }); - it('should not be visible in browser.pages', async({browser}) => { - const newPage = await browser.newPage(); - expect(await browser.pages()).toContain(newPage); - await newPage.close(); - expect(await browser.pages()).not.toContain(newPage); - }); - it('should set the page close state', async({browser}) => { - const newPage = await browser.newPage(); - expect(newPage.isClosed()).toBe(false); - await newPage.close(); - expect(newPage.isClosed()).toBe(true); - }); - it('should work with page.close', async function({ page, context, server }) { - const newPage = await context.newPage(); - const closedPromise = new Promise(x => newPage.on('close', x)); - await newPage.close(); - await closedPromise; - }); - }); - - describe('Page.Events.Console', function() { - it('should work', async({page, server}) => { - let message = null; - page.once('console', m => message = m); - await Promise.all([ - page.evaluate(() => console.log('hello', 5, {foo: 'bar'})), - utils.waitEvent(page, 'console') - ]); - expect(message.text()).toEqual('hello 5 JSHandle@object'); - expect(message.type()).toEqual('log'); - expect(await message.args()[0].jsonValue()).toEqual('hello'); - expect(await message.args()[1].jsonValue()).toEqual(5); - expect(await message.args()[2].jsonValue()).toEqual({foo: 'bar'}); - }); - it('should work for different console API calls', async({page, server}) => { - const messages = []; - page.on('console', msg => messages.push(msg)); - // All console events will be reported before `page.evaluate` is finished. - await page.evaluate(() => { - // A pair of time/timeEnd generates only one Console API call. - console.time('calling console.time'); - console.timeEnd('calling console.time'); - console.trace('calling console.trace'); - console.dir('calling console.dir'); - console.warn('calling console.warn'); - console.error('calling console.error'); - console.log(Promise.resolve('should not wait until resolved!')); - }); - expect(messages.map(msg => msg.type())).toEqual([ - 'timeEnd', 'trace', 'dir', 'warning', 'error', 'log' - ]); - expect(messages[0].text()).toContain('calling console.time'); - expect(messages.slice(1).map(msg => msg.text())).toEqual([ - 'calling console.trace', - 'calling console.dir', - 'calling console.warn', - 'calling console.error', - 'JSHandle@promise', - ]); - }); - it('should not fail for window object', async({page, server}) => { - let message = null; - page.once('console', msg => message = msg); - await Promise.all([ - page.evaluate(() => console.error(window)), - utils.waitEvent(page, 'console') - ]); - expect(message.text()).toBe('JSHandle@object'); - }); - }); - - describe('Page.content / Page.setContent', function() { - const expectedOutput = '
hello
'; - it('should work', async({page, server}) => { - await page.setContent('
hello
'); - const result = await page.content(); - expect(result).toBe(expectedOutput); - }); - it('should work with doctype', async({page, server}) => { - const doctype = ''; - await page.setContent(`${doctype}
hello
`); - const result = await page.content(); - expect(result).toBe(`${doctype}${expectedOutput}`); - }); - it('should work with HTML 4 doctype', async({page, server}) => { - const doctype = ''; - await page.setContent(`${doctype}
hello
`); - const result = await page.content(); - expect(result).toBe(`${doctype}${expectedOutput}`); - }); - }); - - describe('Page.addScriptTag', function() { - it('should throw an error if no options are provided', async({page, server}) => { - let error = null; - try { - await page.addScriptTag('/injectedfile.js'); - } catch (e) { - error = e; - } - expect(error.message).toBe('Provide an object with a `url`, `path` or `content` property'); - }); - - it('should work with a url', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const scriptHandle = await page.addScriptTag({ url: '/injectedfile.js' }); - expect(scriptHandle.asElement()).not.toBeNull(); - expect(await page.evaluate(() => __injected)).toBe(42); - }); - - it('should work with a url and type=module', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.addScriptTag({ url: '/es6/es6import.js', type: 'module' }); - expect(await page.evaluate(() => __es6injected)).toBe(42); - }); - - it('should work with a path and type=module', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.addScriptTag({ path: path.join(__dirname, 'assets/es6/es6pathimport.js'), type: 'module' }); - await page.waitForFunction('window.__es6injected'); - expect(await page.evaluate(() => __es6injected)).toBe(42); - }); - - it('should work with a content and type=module', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.addScriptTag({ content: `import num from '/es6/es6module.js';window.__es6injected = num;`, type: 'module' }); - await page.waitForFunction('window.__es6injected'); - expect(await page.evaluate(() => __es6injected)).toBe(42); - }); - - it('should throw an error if loading from url fail', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - let error = null; - try { - await page.addScriptTag({ url: '/nonexistfile.js' }); - } catch (e) { - error = e; - } - expect(error.message).toBe('Loading script from /nonexistfile.js failed'); - }); - - it('should work with a path', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const scriptHandle = await page.addScriptTag({ path: path.join(__dirname, 'assets/injectedfile.js') }); - expect(scriptHandle.asElement()).not.toBeNull(); - expect(await page.evaluate(() => __injected)).toBe(42); - }); - - it('should include sourcemap when path is provided', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.addScriptTag({ path: path.join(__dirname, 'assets/injectedfile.js') }); - const result = await page.evaluate(() => __injectedError.stack); - expect(result).toContain(path.join('assets', 'injectedfile.js')); - }); - - it('should work with content', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const scriptHandle = await page.addScriptTag({ content: 'window.__injected = 35;' }); - expect(scriptHandle.asElement()).not.toBeNull(); - expect(await page.evaluate(() => __injected)).toBe(35); - }); - - xit('should throw when added with content to the CSP page', async({page, server}) => { - await page.goto(server.PREFIX + '/csp.html'); - let error = null; - await page.addScriptTag({ content: 'window.__injected = 35;' }).catch(e => error = e); - expect(error).toBeTruthy(); - }); - - it('should throw when added with URL to the CSP page', async({page, server}) => { - await page.goto(server.PREFIX + '/csp.html'); - let error = null; - await page.addScriptTag({ url: server.CROSS_PROCESS_PREFIX + '/injectedfile.js' }).catch(e => error = e); - expect(error).toBeTruthy(); - }); - }); - - describe('Page.addStyleTag', function() { - it('should throw an error if no options are provided', async({page, server}) => { - let error = null; - try { - await page.addStyleTag('/injectedstyle.css'); - } catch (e) { - error = e; - } - expect(error.message).toBe('Provide an object with a `url`, `path` or `content` property'); - }); - - it('should work with a url', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const styleHandle = await page.addStyleTag({ url: '/injectedstyle.css' }); - expect(styleHandle.asElement()).not.toBeNull(); - expect(await page.evaluate(`window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')`)).toBe('rgb(255, 0, 0)'); - }); - - it('should throw an error if loading from url fail', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - let error = null; - try { - await page.addStyleTag({ url: '/nonexistfile.js' }); - } catch (e) { - error = e; - } - expect(error.message).toBe('Loading style from /nonexistfile.js failed'); - }); - - it('should work with a path', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const styleHandle = await page.addStyleTag({ path: path.join(__dirname, 'assets/injectedstyle.css') }); - expect(styleHandle.asElement()).not.toBeNull(); - expect(await page.evaluate(`window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')`)).toBe('rgb(255, 0, 0)'); - }); - - it('should include sourcemap when path is provided', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.addStyleTag({ path: path.join(__dirname, 'assets/injectedstyle.css') }); - const styleHandle = await page.$('style'); - const styleContent = await page.evaluate(style => style.innerHTML, styleHandle); - expect(styleContent).toContain(path.join('assets', 'injectedstyle.css')); - }); - - it('should work with content', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const styleHandle = await page.addStyleTag({ content: 'body { background-color: green; }' }); - expect(styleHandle.asElement()).not.toBeNull(); - expect(await page.evaluate(`window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')`)).toBe('rgb(0, 128, 0)'); - }); - - xit('should throw when added with content to the CSP page', async({page, server}) => { - await page.goto(server.PREFIX + '/csp.html'); - let error = null; - await page.addStyleTag({ content: 'body { background-color: green; }' }).catch(e => error = e); - expect(error).toBeTruthy(); - }); - - it('should throw when added with URL to the CSP page', async({page, server}) => { - await page.goto(server.PREFIX + '/csp.html'); - let error = null; - await page.addStyleTag({ url: server.CROSS_PROCESS_PREFIX + '/injectedstyle.css' }).catch(e => error = e); - expect(error).toBeTruthy(); - }); - }); - - describe('Page.title', function() { - it('should return the page title', async({page, server}) => { - await page.goto(server.PREFIX + '/title.html'); - expect(await page.title()).toBe('Woof-Woof'); - }); - }); - - describe('Page.select', function() { - it('should select single option', async({page, server}) => { - await page.goto(server.PREFIX + '/input/select.html'); - await page.select('select', 'blue'); - expect(await page.evaluate(() => result.onInput)).toEqual(['blue']); - expect(await page.evaluate(() => result.onChange)).toEqual(['blue']); - }); - it('should select only first option', async({page, server}) => { - await page.goto(server.PREFIX + '/input/select.html'); - await page.select('select', 'blue', 'green', 'red'); - expect(await page.evaluate(() => result.onInput)).toEqual(['blue']); - expect(await page.evaluate(() => result.onChange)).toEqual(['blue']); - }); - it('should select multiple options', async({page, server}) => { - await page.goto(server.PREFIX + '/input/select.html'); - await page.evaluate(() => makeMultiple()); - await page.select('select', 'blue', 'green', 'red'); - expect(await page.evaluate(() => result.onInput)).toEqual(['blue', 'green', 'red']); - expect(await page.evaluate(() => result.onChange)).toEqual(['blue', 'green', 'red']); - }); - it('should respect event bubbling', async({page, server}) => { - await page.goto(server.PREFIX + '/input/select.html'); - await page.select('select', 'blue'); - expect(await page.evaluate(() => result.onBubblingInput)).toEqual(['blue']); - expect(await page.evaluate(() => result.onBubblingChange)).toEqual(['blue']); - }); - it('should throw when element is not a element.'); - }); - it('should return [] on no matched values', async({page, server}) => { - await page.goto(server.PREFIX + '/input/select.html'); - const result = await page.select('select','42','abc'); - expect(result).toEqual([]); - }); - it('should return an array of matched values', async({page, server}) => { - await page.goto(server.PREFIX + '/input/select.html'); - await page.evaluate(() => makeMultiple()); - const result = await page.select('select','blue','black','magenta'); - expect(result.reduce((accumulator,current) => ['blue', 'black', 'magenta'].includes(current) && accumulator, true)).toEqual(true); - }); - it('should return an array of one element when multiple is not set', async({page, server}) => { - await page.goto(server.PREFIX + '/input/select.html'); - const result = await page.select('select','42','blue','black','magenta'); - expect(result.length).toEqual(1); - }); - it('should return [] on no values',async({page, server}) => { - await page.goto(server.PREFIX + '/input/select.html'); - const result = await page.select('select'); - expect(result).toEqual([]); - }); - it('should deselect all options when passed no values for a multiple select',async({page, server}) => { - await page.goto(server.PREFIX + '/input/select.html'); - await page.evaluate(() => makeMultiple()); - await page.select('select','blue','black','magenta'); - await page.select('select'); - expect(await page.$eval('select', select => Array.from(select.options).every(option => !option.selected))).toEqual(true); - }); - it('should deselect all options when passed no values for a select without multiple',async({page, server}) => { - await page.goto(server.PREFIX + '/input/select.html'); - await page.select('select','blue','black','magenta'); - await page.select('select'); - expect(await page.$eval('select', select => Array.from(select.options).every(option => !option.selected))).toEqual(true); - }); - it('should throw if passed in non-strings', async({page, server}) => { - await page.setContent(''); - let error = null; - try { - await page.select('select', 12); - } catch (e) { - error = e; - } - expect(error.message).toContain('Values must be strings'); - }); - // @see https://github.com/GoogleChrome/puppeteer/issues/3327 - xit('should work when re-defining top-level Event class', async({page, server}) => { - await page.goto(server.PREFIX + '/input/select.html'); - await page.evaluate(() => window.Event = null); - await page.select('select', 'blue'); - expect(await page.evaluate(() => result.onInput)).toEqual(['blue']); - expect(await page.evaluate(() => result.onChange)).toEqual(['blue']); - }); - }); -}; - diff --git a/experimental/puppeteer-firefox/test/puppeteer.spec.js b/experimental/puppeteer-firefox/test/puppeteer.spec.js deleted file mode 100644 index 7f1159a4a05a2..0000000000000 --- a/experimental/puppeteer-firefox/test/puppeteer.spec.js +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ -const path = require('path'); -const GoldenUtils = require('./golden-utils'); -const {Matchers} = require('@pptr/testrunner'); - -module.exports.addTests = ({testRunner, product, puppeteer}) => testRunner.describe(product, () => { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const GOLDEN_DIR = path.join(__dirname, 'golden-' + product); - const OUTPUT_DIR = path.join(__dirname, 'output-' + product); - const {expect} = new Matchers({ - toBeGolden: GoldenUtils.compare.bind(null, GOLDEN_DIR, OUTPUT_DIR) - }); - - const defaultBrowserOptions = { - handleSIGINT: false, - executablePath: product === 'chromium' ? process.env.CHROME : process.env.FFOX, - dumpio: !!process.env.DUMPIO, - args: product === 'chromium' ? ['--no-sandbox'] : [], - }; - - const testOptions = {testRunner, expect, product, puppeteer, defaultBrowserOptions}; - - if (product === 'firefox' && defaultBrowserOptions.executablePath) { - beforeAll(async () => { - await require('../misc/install-preferences')(defaultBrowserOptions.executablePath); - console.log('RUNNING CUSTOM FIREFOX: ' + defaultBrowserOptions.executablePath); - }); - } - - require('./launcher.spec.js').addTests(testOptions); - require('./ignorehttpserrors.spec.js').addTests(testOptions); - - describe('Browser', () => { - beforeAll(async state => { - state.browser = await puppeteer.launch(defaultBrowserOptions); - }); - - afterAll(async state => { - await state.browser.close(); - state.browser = null; - }); - - require('./browser.spec.js').addTests(testOptions); - require('./browsercontext.spec.js').addTests(testOptions); - - describe('Page', () => { - beforeEach(async state => { - state.context = await state.browser.createIncognitoBrowserContext(); - state.page = await state.context.newPage(); - }); - - afterEach(async state => { - await state.context.close(); - state.context = null; - state.page = null; - }); - - require('./page.spec.js').addTests(testOptions); - require('./evaluation.spec.js').addTests(testOptions); - require('./navigation.spec.js').addTests(testOptions); - require('./dialog.spec.js').addTests(testOptions); - require('./frame.spec.js').addTests(testOptions); - require('./jshandle.spec.js').addTests(testOptions); - require('./elementhandle.spec.js').addTests(testOptions); - require('./target.spec.js').addTests(testOptions); - require('./waittask.spec.js').addTests(testOptions); - require('./queryselector.spec.js').addTests(testOptions); - require('./emulation.spec.js').addTests(testOptions); - require('./screenshot.spec.js').addTests(testOptions); - - // Input tests - require('./keyboard.spec.js').addTests(testOptions); - require('./mouse.spec.js').addTests(testOptions); - require('./click.spec.js').addTests(testOptions); - require('./type.spec.js').addTests(testOptions); - require('./hover.spec.js').addTests(testOptions); - - // Browser-specific page tests - if (product === 'firefox') - require('./firefoxonly.spec.js').addTests(testOptions); - else - require('./chromiumonly.spec.js').addTests(testOptions); - }); - }); -}); - - diff --git a/experimental/puppeteer-firefox/test/queryselector.spec.js b/experimental/puppeteer-firefox/test/queryselector.spec.js deleted file mode 100644 index 36ed6969130af..0000000000000 --- a/experimental/puppeteer-firefox/test/queryselector.spec.js +++ /dev/null @@ -1,194 +0,0 @@ -const utils = require('./utils'); - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('Page.$', function() { - it('should query existing element', async({page, server}) => { - await page.setContent('
test
'); - const element = await page.$('section'); - expect(element).toBeTruthy(); - }); - it('should return null for non-existing element', async({page, server}) => { - const element = await page.$('non-existing-element'); - expect(element).toBe(null); - }); - }); - - describe('Page.$$', function() { - it('should query existing elements', async({page, server}) => { - await page.setContent('
A

B
'); - const elements = await page.$$('div'); - expect(elements.length).toBe(2); - const promises = elements.map(element => page.evaluate(e => e.textContent, element)); - expect(await Promise.all(promises)).toEqual(['A', 'B']); - }); - it('should return empty array if nothing is found', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const elements = await page.$$('div'); - expect(elements.length).toBe(0); - }); - }); - - describe('Page.$eval', function() { - it('should work', async({page, server}) => { - await page.setContent('
43543
'); - const idAttribute = await page.$eval('section', e => e.id); - expect(idAttribute).toBe('testAttribute'); - }); - it('should accept arguments', async({page, server}) => { - await page.setContent('
hello
'); - const text = await page.$eval('section', (e, suffix) => e.textContent + suffix, ' world!'); - expect(text).toBe('hello world!'); - }); - it('should accept ElementHandles as arguments', async({page, server}) => { - await page.setContent('
hello
world
'); - const divHandle = await page.$('div'); - const text = await page.$eval('section', (e, div) => e.textContent + div.textContent, divHandle); - expect(text).toBe('hello world'); - }); - it('should throw error if no element is found', async({page, server}) => { - let error = null; - await page.$eval('section', e => e.id).catch(e => error = e); - expect(error.message).toContain('failed to find element matching selector "section"'); - }); - }); - - describe('Page.$$eval', function() { - it('should work', async({page, server}) => { - await page.setContent('
hello
beautiful
world!
'); - const divsCount = await page.$$eval('div', divs => divs.length); - expect(divsCount).toBe(3); - }); - }); - - describe('Page.$x', function() { - it('should query existing element', async({page, server}) => { - await page.setContent('
test
'); - const elements = await page.$x('/html/body/section'); - expect(elements[0]).toBeTruthy(); - expect(elements.length).toBe(1); - }); - it('should return empty array for non-existing element', async({page, server}) => { - const element = await page.$x('/html/body/non-existing-element'); - expect(element).toEqual([]); - }); - it('should return multiple elements', async({page, sever}) => { - await page.setContent('
'); - const elements = await page.$x('/html/body/div'); - expect(elements.length).toBe(2); - }); - }); - - describe('ElementHandle.$', function() { - it('should query existing element', async({page, server}) => { - await page.goto(server.PREFIX + '/playground.html'); - await page.setContent('
A
'); - const html = await page.$('html'); - const second = await html.$('.second'); - const inner = await second.$('.inner'); - const content = await page.evaluate(e => e.textContent, inner); - expect(content).toBe('A'); - }); - - it('should return null for non-existing element', async({page, server}) => { - await page.setContent('
B
'); - const html = await page.$('html'); - const second = await html.$('.third'); - expect(second).toBe(null); - }); - }); - - describe('ElementHandle.$$', function() { - it('should query existing elements', async({page, server}) => { - await page.setContent('
A

B
'); - const html = await page.$('html'); - const elements = await html.$$('div'); - expect(elements.length).toBe(2); - const promises = elements.map(element => page.evaluate(e => e.textContent, element)); - expect(await Promise.all(promises)).toEqual(['A', 'B']); - }); - - it('should return empty array for non-existing elements', async({page, server}) => { - await page.setContent('A
B'); - const html = await page.$('html'); - const elements = await html.$$('div'); - expect(elements.length).toBe(0); - }); - }); - - describe('ElementHandle.$eval', function() { - it('should work', async({page, server}) => { - await page.setContent('
10
'); - const tweet = await page.$('.tweet'); - const content = await tweet.$eval('.like', node => node.innerText); - expect(content).toBe('100'); - }); - - it('should retrieve content from subtree', async({page, server}) => { - const htmlContent = '
not-a-child-div
a-child-div
'; - await page.setContent(htmlContent); - const elementHandle = await page.$('#myId'); - const content = await elementHandle.$eval('.a', node => node.innerText); - expect(content).toBe('a-child-div'); - }); - - it('should throw in case of missing selector', async({page, server}) => { - const htmlContent = '
not-a-child-div
'; - await page.setContent(htmlContent); - const elementHandle = await page.$('#myId'); - const errorMessage = await elementHandle.$eval('.a', node => node.innerText).catch(error => error.message); - expect(errorMessage).toBe(`Error: failed to find element matching selector ".a"`); - }); - }); - - describe('ElementHandle.$$eval', function() { - it('should work', async({page, server}) => { - await page.setContent('
'); - const tweet = await page.$('.tweet'); - const content = await tweet.$$eval('.like', nodes => nodes.map(n => n.innerText)); - expect(content).toEqual(['100', '10']); - }); - - it('should retrieve content from subtree', async({page, server}) => { - const htmlContent = '
not-a-child-div
a1-child-div
a2-child-div
'; - await page.setContent(htmlContent); - const elementHandle = await page.$('#myId'); - const content = await elementHandle.$$eval('.a', nodes => nodes.map(n => n.innerText)); - expect(content).toEqual(['a1-child-div', 'a2-child-div']); - }); - - it('should not throw in case of missing selector', async({page, server}) => { - const htmlContent = '
not-a-child-div
'; - await page.setContent(htmlContent); - const elementHandle = await page.$('#myId'); - const nodesLength = await elementHandle.$$eval('.a', nodes => nodes.length); - expect(nodesLength).toBe(0); - }); - - }); - - describe('ElementHandle.$x', function() { - it('should query existing element', async({page, server}) => { - await page.goto(server.PREFIX + '/playground.html'); - await page.setContent('
A
'); - const html = await page.$('html'); - const second = await html.$x(`./body/div[contains(@class, 'second')]`); - const inner = await second[0].$x(`./div[contains(@class, 'inner')]`); - const content = await page.evaluate(e => e.textContent, inner[0]); - expect(content).toBe('A'); - }); - - it('should return null for non-existing element', async({page, server}) => { - await page.setContent('
B
'); - const html = await page.$('html'); - const second = await html.$x(`/div[contains(@class, 'third')]`); - expect(second).toEqual([]); - }); - }); -}; diff --git a/experimental/puppeteer-firefox/test/run_static_server.js b/experimental/puppeteer-firefox/test/run_static_server.js deleted file mode 100755 index 9c6b766efc69a..0000000000000 --- a/experimental/puppeteer-firefox/test/run_static_server.js +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env node -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * 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. - */ -const path = require('path'); -const {TestServer} = require('@pptr/testserver'); - -const port = 8907; -const httpsPort = 8908; -const assetsPath = path.join(__dirname, 'assets'); - -Promise.all([ - TestServer.create(assetsPath, port), - TestServer.createHTTPS(assetsPath, httpsPort) -]).then(([server, httpsServer]) => { - console.log(`HTTP: server is running on http://localhost:${port}`); - console.log(`HTTPS: server is running on https://localhost:${httpsPort}`); -}); diff --git a/experimental/puppeteer-firefox/test/screenshot.spec.js b/experimental/puppeteer-firefox/test/screenshot.spec.js deleted file mode 100644 index b13ebd0fdf499..0000000000000 --- a/experimental/puppeteer-firefox/test/screenshot.spec.js +++ /dev/null @@ -1,232 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('Page.screenshot', function() { - it('should work', async({page, server}) => { - await page.setViewport({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - const screenshot = await page.screenshot(); - expect(screenshot).toBeGolden('screenshot-sanity.png'); - }); - it('should clip rect', async({page, server}) => { - await page.setViewport({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - const screenshot = await page.screenshot({ - clip: { - x: 50, - y: 100, - width: 150, - height: 100 - } - }); - expect(screenshot).toBeGolden('screenshot-clip-rect.png'); - }); - it('should work for offscreen clip', async({page, server}) => { - await page.setViewport({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - const screenshot = await page.screenshot({ - clip: { - x: 50, - y: 600, - width: 100, - height: 100 - } - }); - expect(screenshot).toBeGolden('screenshot-offscreen-clip.png'); - }); - it('should run in parallel', async({page, server}) => { - await page.setViewport({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - const promises = []; - for (let i = 0; i < 3; ++i) { - promises.push(page.screenshot({ - clip: { - x: 50 * i, - y: 0, - width: 50, - height: 50 - } - })); - } - const screenshots = await Promise.all(promises); - expect(screenshots[1]).toBeGolden('grid-cell-1.png'); - }); - it('should take fullPage screenshots', async({page, server}) => { - await page.setViewport({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - const screenshot = await page.screenshot({ - fullPage: true - }); - expect(screenshot).toBeGolden('screenshot-grid-fullpage.png'); - }); - it('should run in parallel in multiple pages', async({page, server, browser}) => { - const N = 2; - const pages = await Promise.all(Array(N).fill(0).map(async() => { - const page = await browser.newPage(); - await page.goto(server.PREFIX + '/grid.html'); - return page; - })); - const promises = []; - for (let i = 0; i < N; ++i) - promises.push(pages[i].screenshot({ clip: { x: 50 * i, y: 0, width: 50, height: 50 } })); - const screenshots = await Promise.all(promises); - for (let i = 0; i < N; ++i) - expect(screenshots[i]).toBeGolden(`grid-cell-${i}.png`); - await Promise.all(pages.map(page => page.close())); - }); - (FFOX ? xit : it)('should allow transparency', async({page, server}) => { - await page.setViewport({ width: 100, height: 100 }); - await page.goto(server.EMPTY_PAGE); - const screenshot = await page.screenshot({omitBackground: true}); - expect(screenshot).toBeGolden('transparent.png'); - }); - (FFOX ? xit : it)('should render white background on jpeg file', async({page, server}) => { - await page.setViewport({ width: 100, height: 100 }); - await page.goto(server.EMPTY_PAGE); - const screenshot = await page.screenshot({omitBackground: true, type: 'jpeg'}); - expect(screenshot).toBeGolden('white.jpg'); - }); - it('should work with odd clip size on Retina displays', async({page, server}) => { - const screenshot = await page.screenshot({ - clip: { - x: 0, - y: 0, - width: 11, - height: 11, - } - }); - expect(screenshot).toBeGolden('screenshot-clip-odd-size.png'); - }); - it('should return base64', async({page, server}) => { - await page.setViewport({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - const screenshot = await page.screenshot({ - encoding: 'base64' - }); - expect(Buffer.isBuffer(screenshot)).toBe(false); - expect(Buffer.from(screenshot, 'base64')).toBeGolden('screenshot-sanity.png'); - }); - }); - - describe('ElementHandle.screenshot', function() { - it('should work', async({page, server}) => { - await page.setViewport({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - await page.evaluate(() => window.scrollBy(50, 100)); - const elementHandle = await page.$('.box:nth-of-type(3)'); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toBeGolden('screenshot-element-bounding-box.png'); - }); - it('should take into account padding and border', async({page, server}) => { - await page.setViewport({width: 500, height: 500}); - await page.setContent(` - something above - -
- `); - const elementHandle = await page.$('div'); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toBeGolden('screenshot-element-padding-border.png'); - }); - it('should capture full element when larger than viewport', async({page, server}) => { - await page.setViewport({width: 500, height: 500}); - - await page.setContent(` - something above - -
- `); - const elementHandle = await page.$('div.to-screenshot'); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toBeGolden('screenshot-element-larger-than-viewport.png'); - - expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 }); - }); - it('should scroll element into view', async({page, server}) => { - await page.setViewport({width: 500, height: 500}); - await page.setContent(` - something above - -
-
- `); - const elementHandle = await page.$('div.to-screenshot'); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png'); - }); - it('should work with a rotated element', async({page, server}) => { - await page.setViewport({width: 500, height: 500}); - await page.setContent(`
 
`); - const elementHandle = await page.$('div'); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toBeGolden('screenshot-element-rotate.png'); - }); - it('should fail to screenshot a detached element', async({page, server}) => { - await page.setContent('

remove this

'); - const elementHandle = await page.$('h1'); - await page.evaluate(element => element.remove(), elementHandle); - const screenshotError = await elementHandle.screenshot().catch(error => error); - expect(screenshotError.message).toBe('Node is either not visible or not an HTMLElement'); - }); - (CHROME ? xit : it)('should not hang with zero width/height element', async({page, server}) => { - await page.setContent('
'); - const div = await page.$('div'); - await div.screenshot(); - }); - }); -}; - diff --git a/experimental/puppeteer-firefox/test/target.spec.js b/experimental/puppeteer-firefox/test/target.spec.js deleted file mode 100644 index 35b9fe7d59f59..0000000000000 --- a/experimental/puppeteer-firefox/test/target.spec.js +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ - -const utils = require('./utils'); -const {waitEvent} = utils; -const {TimeoutError} = require('../Errors'); - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - describe('Target', function() { - it('Browser.targets should return all of the targets', async({page, server, browser}) => { - // The pages will be the testing page and the original newtab page - const targets = browser.targets(); - expect(targets.some(target => target.type() === 'page' && - target.url() === 'about:blank')).toBeTruthy('Missing blank page'); - }); - it('Browser.pages should return all of the pages', async({page, server, browser}) => { - // The pages will be the testing page - const allPages = await browser.pages(); - expect(allPages.length).toBe(2); - expect(allPages).toContain(page); - expect(allPages[0]).not.toBe(allPages[1]); - }); - it('should be able to use the default page in the browser', async({page, server, browser}) => { - // The pages will be the testing page and the original newtab page - const allPages = await browser.pages(); - const originalPage = allPages.find(p => p !== page); - expect(await originalPage.evaluate(() => ['Hello', 'world'].join(' '))).toBe('Hello world'); - expect(await originalPage.$('body')).toBeTruthy(); - }); - it('should report when a new page is created and closed', async({page, server, browser}) => { - const [otherPage] = await Promise.all([ - browser.waitForTarget(target => target.url() === server.EMPTY_PAGE2).then(target => target.page()), - page.evaluate(url => window.open(url), server.EMPTY_PAGE2), - ]); - - expect(await otherPage.evaluate(() => ['Hello', 'world'].join(' '))).toBe('Hello world'); - expect(await otherPage.$('body')).toBeTruthy(); - - let allPages = await browser.pages(); - expect(allPages).toContain(page); - expect(allPages).toContain(otherPage); - - const closePagePromise = new Promise(fulfill => browser.once('targetdestroyed', target => fulfill(target.page()))); - await otherPage.close(); - expect(await closePagePromise).toBe(otherPage); - - allPages = await Promise.all(browser.targets().map(target => target.page())); - expect(allPages).toContain(page); - expect(allPages).not.toContain(otherPage); - }); - it('should report when a target url changes', async({page, server, browser}) => { - await page.goto(server.EMPTY_PAGE); - let changedTarget = new Promise(fulfill => browser.once('targetchanged', target => fulfill(target))); - await page.goto(server.EMPTY_PAGE2); - expect((await changedTarget).url()).toBe(server.EMPTY_PAGE2); - - changedTarget = new Promise(fulfill => browser.once('targetchanged', target => fulfill(target))); - await page.goto(server.EMPTY_PAGE); - expect((await changedTarget).url()).toBe(server.EMPTY_PAGE); - }); - }); - - describe('Browser.waitForTarget', () => { - it('should wait for a target', async function({browser, server}) { - let resolved = false; - const targetPromise = browser.waitForTarget(target => target.url() === server.EMPTY_PAGE); - targetPromise.then(() => resolved = true); - const page = await browser.newPage(); - expect(resolved).toBe(false); - await page.goto(server.EMPTY_PAGE); - const target = await targetPromise; - expect(await target.page()).toBe(page); - await page.close(); - }); - it('should timeout waiting for a non-existent target', async function({browser, server}) { - let error = null; - await browser.waitForTarget(target => target.url() === server.EMPTY_PAGE, { - timeout: 1 - }).catch(e => error = e); - expect(error).toBeInstanceOf(TimeoutError); - }); - }); -}; diff --git a/experimental/puppeteer-firefox/test/test.js b/experimental/puppeteer-firefox/test/test.js deleted file mode 100644 index 3f9471fd0c58f..0000000000000 --- a/experimental/puppeteer-firefox/test/test.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ -const path = require('path'); -const {TestServer} = require('@pptr/testserver'); -const {TestRunner, Reporter} = require('@pptr/testrunner'); - -let parallel = 1; -const parallelArgIndex = process.argv.indexOf('-j'); -if (parallelArgIndex !== -1) - parallel = parseInt(process.argv[parallelArgIndex + 1], 10); -require('events').defaultMaxListeners *= parallel; - -let timeout = 10000; -if (!isNaN(process.env.TIMEOUT)) - timeout = parseInt(process.env.TIMEOUT, 10); -const testRunner = new TestRunner({timeout, parallel}); - -console.log('Testing on Node', process.version); - -testRunner.beforeAll(async state => { - const assetsPath = path.join(__dirname, 'assets'); - - const port = 8907 + state.parallelIndex * 2; - state.server = await TestServer.create(assetsPath, port); - state.server.PORT = port; - state.server.PREFIX = `http://localhost:${port}`; - state.server.EMPTY_PAGE = `http://localhost:${port}/empty.html`; - state.server.EMPTY_PAGE2 = `http://localhost:${port}/empty2.html`; - - const httpsPort = port + 1; - state.httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort); - state.httpsServer.PORT = httpsPort; - state.httpsServer.PREFIX = `https://localhost:${httpsPort}`; - state.httpsServer.EMPTY_PAGE = `https://localhost:${httpsPort}/empty.html`; - state.httpsServer.EMPTY_PAGE2 = `https://localhost:${httpsPort}/empty2.html`; -}); - -testRunner.afterAll(async({server, httpsServer}) => { - await Promise.all([ - server.stop(), - httpsServer.stop(), - ]); -}); - -testRunner.beforeEach(async({server, httpsServer}) => { - server.reset(); - httpsServer.reset(); -}); - -const product = process.env['PRODUCT']; -const pptrFirefox = require('..'); -const pptrChromium = require('puppeteer'); -if (product) { - console.log(`WARNING: Running with ${product} only product because of PRODUCT env`); - require('./puppeteer.spec.js').addTests({testRunner, product, puppeteer: product === 'chromium' ? pptrChromium : pptrFirefox}); -} else { - require('./puppeteer.spec.js').addTests({testRunner, product: 'chromium', puppeteer: pptrChromium}); - require('./puppeteer.spec.js').addTests({testRunner, product: 'firefox', puppeteer: pptrFirefox}); -} - -if (process.env.CI && testRunner.hasFocusedTestsOrSuites()) { - console.error('ERROR: "focused" tests/suites are prohibitted on bots. Remove any "fit"/"fdescribe" declarations.'); - process.exit(1); -} - -new Reporter(testRunner, path.join(__dirname, '..')); -testRunner.run(); diff --git a/experimental/puppeteer-firefox/test/type.spec.js b/experimental/puppeteer-firefox/test/type.spec.js deleted file mode 100644 index fea75ad3e5d90..0000000000000 --- a/experimental/puppeteer-firefox/test/type.spec.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('Page.type', () => { - it('should type into the textarea', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - - const textarea = await page.$('textarea'); - await textarea.type('Type in this text!'); - expect(await page.evaluate(() => result)).toBe('Type in this text!'); - }); - it('should move with the arrow keys', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.type('textarea', 'Hello World!'); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!'); - for (let i = 0; i < 'World!'.length; i++) - page.keyboard.press('ArrowLeft'); - await page.keyboard.type('inserted '); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello inserted World!'); - page.keyboard.down('Shift'); - for (let i = 0; i < 'inserted '.length; i++) - page.keyboard.press('ArrowLeft'); - page.keyboard.up('Shift'); - await page.keyboard.press('Backspace'); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!'); - }); - }); - - describe('ElementHandle.press', () => { - it('should send a character with ElementHandle.press', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - const textarea = await page.$('textarea'); - await textarea.press('a'); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('a'); - - await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true)); - - await textarea.press('a'); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('a'); - }); - - it('should specify location', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.evaluate(() => { - window.addEventListener('keydown', event => window.keyLocation = event.location, true); - }); - const textarea = await page.$('textarea'); - - await textarea.press('Digit5'); - expect(await page.evaluate('keyLocation')).toBe(0); - - await textarea.press('ControlLeft'); - expect(await page.evaluate('keyLocation')).toBe(1); - - await textarea.press('ControlRight'); - expect(await page.evaluate('keyLocation')).toBe(2); - - await textarea.press('NumpadSubtract'); - expect(await page.evaluate('keyLocation')).toBe(3); - }); - }); -}; - diff --git a/experimental/puppeteer-firefox/test/utils.js b/experimental/puppeteer-firefox/test/utils.js deleted file mode 100644 index 872c9767d52dd..0000000000000 --- a/experimental/puppeteer-firefox/test/utils.js +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright 2017 Google Inc. All rights reserved. - * - * 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. - */ - -const fs = require('fs'); -const path = require('path'); - -const utils = module.exports = { - /** - * @param {!Page} page - * @param {string} frameId - * @param {string} url - * @return {!Puppeteer.Frame} - */ - attachFrame: async function(page, frameId, url) { - await page.evaluate(attachFrame, frameId, url); - - async function attachFrame(frameId, url) { - const frame = document.createElement('iframe'); - frame.src = url; - frame.id = frameId; - document.body.appendChild(frame); - await new Promise(x => frame.onload = x); - } - }, - - /** - * @param {!Page} page - * @param {string} frameId - */ - detachFrame: async function(page, frameId) { - await page.evaluate(detachFrame, frameId); - - function detachFrame(frameId) { - const frame = document.getElementById(frameId); - frame.remove(); - } - }, - - /** - * @param {!Page} page - * @param {string} frameId - * @param {string} url - */ - navigateFrame: async function(page, frameId, url) { - await page.evaluate(navigateFrame, frameId, url); - - function navigateFrame(frameId, url) { - const frame = document.getElementById(frameId); - frame.src = url; - return new Promise(x => frame.onload = x); - } - }, - - /** - * @param {!Frame} frame - * @param {string=} indentation - * @return {!Array} - */ - dumpFrames: function(frame, indentation) { - indentation = indentation || ''; - let description = frame.url().replace(/:\d{4}\//, ':/'); - if (frame.name()) - description += ' (' + frame.name() + ')'; - let result = [indentation + description]; - for (const child of frame.childFrames()) - result.push(...utils.dumpFrames(child, ' ' + indentation)); - return result; - }, - - /** - * @param {!EventEmitter} emitter - * @param {string} eventName - * @return {!Promise} - */ - waitEvent: function(emitter, eventName, predicate = () => true) { - return new Promise(fulfill => { - emitter.on(eventName, function listener(event) { - if (!predicate(event)) - return; - emitter.removeListener(eventName, listener); - fulfill(event); - }); - }); - }, -}; diff --git a/experimental/puppeteer-firefox/test/waittask.spec.js b/experimental/puppeteer-firefox/test/waittask.spec.js deleted file mode 100644 index f3b3c5da7c0a8..0000000000000 --- a/experimental/puppeteer-firefox/test/waittask.spec.js +++ /dev/null @@ -1,423 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * - * 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. - */ -const utils = require('./utils'); -const {TimeoutError} = require('../Errors'); - -module.exports.addTests = function({testRunner, expect, product}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const FFOX = product === 'firefox'; - const CHROME = product === 'chromium'; - - describe('Page.waitForFunction', function() { - it('should accept a string', async({page, server}) => { - const watchdog = page.waitForFunction('window.__FOO === 1'); - await page.evaluate(() => window.__FOO = 1); - await watchdog; - }); - it('should work when resolved right before execution context disposal', async({page, server}) => { - await page.evaluateOnNewDocument(() => window.__RELOADED = true); - await page.waitForFunction(() => { - if (!window.__RELOADED) - window.location.reload(); - return true; - }); - }); - it('should poll on interval', async({page, server}) => { - let success = false; - const startTime = Date.now(); - const polling = 100; - const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling}) - .then(() => success = true); - await page.evaluate(() => window.__FOO = 'hit'); - expect(success).toBe(false); - await page.evaluate(() => document.body.appendChild(document.createElement('div'))); - await watchdog; - expect(Date.now() - startTime).not.toBeLessThan(polling / 2); - }); - it('should poll on mutation', async({page, server}) => { - let success = false; - const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'mutation'}) - .then(() => success = true); - await page.evaluate(() => window.__FOO = 'hit'); - expect(success).toBe(false); - await page.evaluate(() => document.body.appendChild(document.createElement('div'))); - await watchdog; - }); - it('should poll on raf', async({page, server}) => { - const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'raf'}); - await page.evaluate(() => window.__FOO = 'hit'); - await watchdog; - }); - xit('should work with strict CSP policy', async({page, server}) => { - server.setCSP('/empty.html', 'script-src ' + server.PREFIX); - await page.goto(server.EMPTY_PAGE); - const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'raf'}); - await page.evaluate(() => window.__FOO = 'hit'); - await watchdog; - }); - it('should throw on bad polling value', async({page, server}) => { - let error = null; - try { - await page.waitForFunction(() => !!document.body, {polling: 'unknown'}); - } catch (e) { - error = e; - } - expect(error).toBeTruthy(); - expect(error.message).toContain('polling'); - }); - it('should throw negative polling interval', async({page, server}) => { - let error = null; - try { - await page.waitForFunction(() => !!document.body, {polling: -10}); - } catch (e) { - error = e; - } - expect(error).toBeTruthy(); - expect(error.message).toContain('Cannot poll with non-positive interval'); - }); - it('should return the success value as a JSHandle', async({page}) => { - expect(await (await page.waitForFunction(() => 5)).jsonValue()).toBe(5); - }); - it('should return the window as a success value', async({ page }) => { - expect(await page.waitForFunction(() => window)).toBeTruthy(); - }); - it('should accept ElementHandle arguments', async({page}) => { - await page.setContent('
'); - const div = await page.$('div'); - let resolved = false; - const waitForFunction = page.waitForFunction(element => !element.parentElement, {}, div).then(() => resolved = true); - expect(resolved).toBe(false); - await page.evaluate(element => element.remove(), div); - await waitForFunction; - }); - it('should respect timeout', async({page}) => { - let error = null; - await page.waitForFunction('false', {timeout: 10}).catch(e => error = e); - expect(error).toBeTruthy(); - expect(error.message).toContain('waiting for function failed: timeout'); - expect(error).toBeInstanceOf(TimeoutError); - }); - it('should disable timeout when its set to 0', async({page}) => { - const watchdog = page.waitForFunction(() => { - window.__counter = (window.__counter || 0) + 1; - return window.__injected; - }, {timeout: 0, polling: 10}); - await page.waitForFunction(() => window.__counter > 10); - await page.evaluate(() => window.__injected = true); - await watchdog; - }); - it('should survive navigations', async({page, server}) => { - const watchdog = page.waitForFunction(() => window.__done); - await page.goto(server.EMPTY_PAGE); - await page.goto(server.PREFIX + '/consolelog.html'); - await page.evaluate(() => window.__done = true); - await watchdog; - }); - }); - - describe('Page.waitForSelector', function() { - const addElement = tag => document.body.appendChild(document.createElement(tag)); - - it('should immediately resolve promise if node exists', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const frame = page.mainFrame(); - await frame.waitForSelector('*'); - await frame.evaluate(addElement, 'div'); - await frame.waitForSelector('div'); - }); - - it('should resolve promise when node is added', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const frame = page.mainFrame(); - const watchdog = frame.waitForSelector('div'); - await frame.evaluate(addElement, 'br'); - await frame.evaluate(addElement, 'div'); - const eHandle = await watchdog; - const tagName = await eHandle.getProperty('tagName').then(e => e.jsonValue()); - expect(tagName).toBe('DIV'); - }); - - it('should work when node is added through innerHTML', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - const watchdog = page.waitForSelector('h3 div'); - await page.evaluate(addElement, 'span'); - await page.evaluate(() => document.querySelector('span').innerHTML = '

'); - await watchdog; - }); - - it('Page.waitForSelector is shortcut for main frame', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE2); - const otherFrame = page.frames()[1]; - const watchdog = page.waitForSelector('div'); - await otherFrame.evaluate(addElement, 'div'); - await page.evaluate(addElement, 'div'); - const eHandle = await watchdog; - expect(await page.mainFrame().evaluate(e => !!e, eHandle)).toBe(true); - }); - - it('should run in specified frame', async({page, server}) => { - await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE); - const frame1 = page.frames()[1]; - const frame2 = page.frames()[2]; - const waitForSelectorPromise = frame2.waitForSelector('div'); - await frame1.evaluate(addElement, 'div'); - await frame2.evaluate(addElement, 'div'); - const eHandle = await waitForSelectorPromise; - expect(await frame2.evaluate(e => !!e, eHandle)).toBe(true); - }); - - it('should throw if evaluation failed', async({page, server}) => { - await page.evaluateOnNewDocument(function() { - document.querySelector = null; - }); - await page.goto(server.EMPTY_PAGE); - let error = null; - await page.waitForSelector('*').catch(e => error = e); - expect(error.message).toContain('document.querySelector is not a function'); - }); - it('should throw when frame is detached', async({page, server}) => { - await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - const frame = page.frames()[1]; - let waitError = null; - const waitPromise = frame.waitForSelector('.box').catch(e => waitError = e); - await utils.detachFrame(page, 'frame1'); - await waitPromise; - expect(waitError).toBeTruthy(); - expect(waitError.message).toContain('waitForFunction failed: frame got detached.'); - }); - xit('should survive cross-process navigation', async({page, server}) => { - let boxFound = false; - const waitForSelector = page.waitForSelector('.box').then(() => boxFound = true); - await page.goto(server.EMPTY_PAGE); - expect(boxFound).toBe(false); - await page.reload(); - expect(boxFound).toBe(false); - await page.goto(server.CROSS_PROCESS_PREFIX + '/grid.html'); - await waitForSelector; - expect(boxFound).toBe(true); - }); - it('should wait for visible', async({page, server}) => { - let divFound = false; - const waitForSelector = page.waitForSelector('div', {visible: true}).then(() => divFound = true); - await page.setContent(`
1
`); - expect(divFound).toBe(false); - await page.evaluate(() => document.querySelector('div').style.removeProperty('display')); - expect(divFound).toBe(false); - await page.evaluate(() => document.querySelector('div').style.removeProperty('visibility')); - expect(await waitForSelector).toBe(true); - expect(divFound).toBe(true); - }); - it('should wait for visible recursively', async({page, server}) => { - let divVisible = false; - const waitForSelector = page.waitForSelector('div#inner', {visible: true}).then(() => divVisible = true); - await page.setContent(`
hi
`); - expect(divVisible).toBe(false); - await page.evaluate(() => document.querySelector('div').style.removeProperty('display')); - expect(divVisible).toBe(false); - await page.evaluate(() => document.querySelector('div').style.removeProperty('visibility')); - expect(await waitForSelector).toBe(true); - expect(divVisible).toBe(true); - }); - it('hidden should wait for visibility: hidden', async({page, server}) => { - let divHidden = false; - await page.setContent(`
`); - const waitForSelector = page.waitForSelector('div', {hidden: true}).then(() => divHidden = true); - await page.waitForSelector('div'); // do a round trip - expect(divHidden).toBe(false); - await page.evaluate(() => document.querySelector('div').style.setProperty('visibility', 'hidden')); - expect(await waitForSelector).toBe(true); - expect(divHidden).toBe(true); - }); - it('hidden should wait for display: none', async({page, server}) => { - let divHidden = false; - await page.setContent(`
`); - const waitForSelector = page.waitForSelector('div', {hidden: true}).then(() => divHidden = true); - await page.waitForSelector('div'); // do a round trip - expect(divHidden).toBe(false); - await page.evaluate(() => document.querySelector('div').style.setProperty('display', 'none')); - expect(await waitForSelector).toBe(true); - expect(divHidden).toBe(true); - }); - it('hidden should wait for removal', async({page, server}) => { - await page.setContent(`
`); - let divRemoved = false; - const waitForSelector = page.waitForSelector('div', {hidden: true}).then(() => divRemoved = true); - await page.waitForSelector('div'); // do a round trip - expect(divRemoved).toBe(false); - await page.evaluate(() => document.querySelector('div').remove()); - expect(await waitForSelector).toBe(true); - expect(divRemoved).toBe(true); - }); - it('should respect timeout', async({page, server}) => { - let error = null; - await page.waitForSelector('div', {timeout: 10}).catch(e => error = e); - expect(error).toBeTruthy(); - expect(error.message).toContain('waiting for selector "div" failed: timeout'); - expect(error).toBeInstanceOf(TimeoutError); - }); - it('should have an error message specifically for awaiting an element to be hidden', async({page, server}) => { - await page.setContent(`
`); - let error = null; - await page.waitForSelector('div', {hidden: true, timeout: 10}).catch(e => error = e); - expect(error).toBeTruthy(); - expect(error.message).toContain('waiting for selector "div" to be hidden failed: timeout'); - }); - - it('should respond to node attribute mutation', async({page, server}) => { - let divFound = false; - const waitForSelector = page.waitForSelector('.zombo').then(() => divFound = true); - await page.setContent(`
`); - expect(divFound).toBe(false); - await page.evaluate(() => document.querySelector('div').className = 'zombo'); - expect(await waitForSelector).toBe(true); - }); - it('should return the element handle', async({page, server}) => { - const waitForSelector = page.waitForSelector('.zombo'); - await page.setContent(`
anything
`); - expect(await page.evaluate(x => x.textContent, await waitForSelector)).toBe('anything'); - }); - it('should have correct stack trace for timeout', async({page, server}) => { - let error; - await page.waitForSelector('.zombo', {timeout: 10}).catch(e => error = e); - expect(error.stack).toContain('waittask.spec.js'); - }); - }); - - describe('Frame.waitForXPath', function() { - const addElement = tag => document.body.appendChild(document.createElement(tag)); - - it('should support some fancy xpath', async({page, server}) => { - await page.setContent(`

red herring

hello world

`); - const waitForXPath = page.waitForXPath('//p[normalize-space(.)="hello world"]'); - expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('hello world '); - }); - it('should respect timeout', async({page}) => { - let error = null; - await page.waitForXPath('//div', {timeout: 10}).catch(e => error = e); - expect(error).toBeTruthy(); - expect(error.message).toContain('waiting for XPath "//div" failed: timeout'); - expect(error).toBeInstanceOf(TimeoutError); - }); - it('should run in specified frame', async({page, server}) => { - await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE); - const frame1 = page.frames()[1]; - const frame2 = page.frames()[2]; - const waitForXPathPromise = frame2.waitForXPath('//div'); - await frame1.evaluate(addElement, 'div'); - await frame2.evaluate(addElement, 'div'); - const eHandle = await waitForXPathPromise; - expect(await frame2.evaluate(e => !!e, eHandle)).toBe(true); - }); - it('should throw if evaluation failed', async({page, server}) => { - await page.evaluateOnNewDocument(function() { - document.evaluate = null; - }); - await page.goto(server.EMPTY_PAGE); - let error = null; - await page.waitForXPath('*').catch(e => error = e); - expect(error.message).toContain('document.evaluate is not a function'); - }); - it('should throw when frame is detached', async({page, server}) => { - await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); - const frame = page.frames()[1]; - let waitError = null; - const waitPromise = frame.waitForXPath('//*[@class="box"]').catch(e => waitError = e); - await utils.detachFrame(page, 'frame1'); - await waitPromise; - expect(waitError).toBeTruthy(); - expect(waitError.message).toContain('waitForFunction failed: frame got detached.'); - }); - it('hidden should wait for display: none', async({page, server}) => { - let divHidden = false; - await page.setContent(`
`); - const waitForXPath = page.waitForXPath('//div', {hidden: true}).then(() => divHidden = true); - await page.waitForXPath('//div'); // do a round trip - expect(divHidden).toBe(false); - await page.evaluate(() => document.querySelector('div').style.setProperty('display', 'none')); - expect(await waitForXPath).toBe(true); - expect(divHidden).toBe(true); - }); - it('should return the element handle', async({page, server}) => { - const waitForXPath = page.waitForXPath('//*[@class="zombo"]'); - await page.setContent(`
anything
`); - expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('anything'); - }); - it('should allow you to select a text node', async({page, server}) => { - await page.setContent(`
some text
`); - const text = await page.waitForXPath('//div/text()'); - expect(await (await text.getProperty('nodeType')).jsonValue()).toBe(3 /* Node.TEXT_NODE */); - }); - it('should allow you to select an element with single slash', async({page, server}) => { - await page.setContent(`
some text
`); - const waitForXPath = page.waitForXPath('/html/body/div'); - expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('some text'); - }); - }); - - describe('Page.waitFor', function() { - it('should wait for selector', async({page, server}) => { - let found = false; - const waitFor = page.waitFor('div').then(() => found = true); - await page.goto(server.EMPTY_PAGE); - expect(found).toBe(false); - await page.goto(server.PREFIX + '/grid.html'); - await waitFor; - expect(found).toBe(true); - }); - it('should wait for an xpath', async({page, server}) => { - let found = false; - const waitFor = page.waitFor('//div').then(() => found = true); - await page.goto(server.EMPTY_PAGE); - expect(found).toBe(false); - await page.goto(server.PREFIX + '/grid.html'); - await waitFor; - expect(found).toBe(true); - }); - it('should not allow you to select an element with single slash xpath', async({page, server}) => { - await page.setContent(`
some text
`); - let error = null; - await page.waitFor('/html/body/div').catch(e => error = e); - expect(error).toBeTruthy(); - }); - it('should timeout', async({page, server}) => { - const startTime = Date.now(); - const timeout = 42; - await page.waitFor(timeout); - expect(Date.now() - startTime).not.toBeLessThan(timeout / 2); - }); - it('should wait for predicate', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await Promise.all([ - page.waitFor(() => window.location.href.includes('woof')), - page.goto(server.EMPTY_PAGE + '#woof'), - ]); - }); - it('should throw when unknown type', async({page, server}) => { - let error = null; - await page.waitFor({foo: 'bar'}).catch(e => error = e); - expect(error.message).toContain('Unsupported target type'); - }); - it('should wait for predicate with arguments', async({page, server}) => { - await page.waitFor((arg1, arg2) => arg1 !== arg2, {}, 1, 2); - }); - }); -}; diff --git a/test/click.spec.js b/test/click.spec.js index d9995a7d3b7a7..117a9db777385 100644 --- a/test/click.spec.js +++ b/test/click.spec.js @@ -89,11 +89,8 @@ module.exports.addTests = function({testRunner, expect}) { it('should click wrapped links', async({page, server}) => { await page.goto(server.PREFIX + '/wrappedlink.html'); - await Promise.all([ - page.click('a'), - page.waitForNavigation() - ]); - expect(page.url()).toBe(server.PREFIX + '/wrappedlink.html#clicked'); + await page.click('a'); + expect(await page.evaluate(() => window.__clicked)).toBe(true); }); it('should click on checkbox input and toggle', async({page, server}) => { diff --git a/test/navigation.spec.js b/test/navigation.spec.js index a69a9140b4ac9..66905a486b79f 100644 --- a/test/navigation.spec.js +++ b/test/navigation.spec.js @@ -27,6 +27,14 @@ module.exports.addTests = function({testRunner, expect, Errors, CHROME}) { await page.goto(server.EMPTY_PAGE); expect(page.url()).toBe(server.EMPTY_PAGE); }); + it('should work with anchor navigation', async({page, server}) => { + await page.goto(server.EMPTY_PAGE); + expect(page.url()).toBe(server.EMPTY_PAGE); + await page.goto(server.EMPTY_PAGE + '#foo'); + expect(page.url()).toBe(server.EMPTY_PAGE + '#foo'); + await page.goto(server.EMPTY_PAGE + '#bar'); + expect(page.url()).toBe(server.EMPTY_PAGE + '#bar'); + }); it('should work with redirects', async({page, server}) => { server.setRedirect('/redirect/1.html', '/redirect/2.html'); server.setRedirect('/redirect/2.html', '/empty.html');