From 84742caddafb51ef19bf6da0f9428b64b83a5652 Mon Sep 17 00:00:00 2001 From: Zeke Lu Date: Thu, 17 Jun 2021 16:03:25 +0800 Subject: [PATCH] screenshot: replace the unit test for Screenshot This unit test is copied from puppeteer. The Screenshot action is updated to handle fractional dimensions properly too. --- screenshot.go | 8 ++ screenshot_test.go | 87 +++++++++++------- testdata/screenshot.html | 73 +++++++++++++++ .../screenshots/element-fractional-offset.png | Bin 0 -> 138 bytes testdata/screenshots/element-fractional.png | Bin 0 -> 138 bytes .../element-larger-than-viewport.png | Bin 0 -> 2807 bytes .../screenshots/element-padding-border.png | Bin 0 -> 168 bytes testdata/screenshots/element-rotate.png | Bin 0 -> 2342 bytes .../element-scrolled-into-view.png | Bin 0 -> 168 bytes 9 files changed, 136 insertions(+), 32 deletions(-) create mode 100644 testdata/screenshot.html create mode 100644 testdata/screenshots/element-fractional-offset.png create mode 100644 testdata/screenshots/element-fractional.png create mode 100644 testdata/screenshots/element-larger-than-viewport.png create mode 100644 testdata/screenshots/element-padding-border.png create mode 100644 testdata/screenshots/element-rotate.png create mode 100644 testdata/screenshots/element-scrolled-into-view.png diff --git a/screenshot.go b/screenshot.go index 8cbd4390..367c1f48 100644 --- a/screenshot.go +++ b/screenshot.go @@ -3,6 +3,7 @@ package chromedp import ( "context" "fmt" + "math" "github.com/chromedp/cdproto/cdp" "github.com/chromedp/cdproto/page" @@ -43,6 +44,13 @@ func Screenshot(sel interface{}, picbuf *[]byte, opts ...QueryOption) QueryActio return err } + // The "Capture node screenshot" command does not handle fractional dimensions properly. + // Let's align with puppeteer: + // https://github.com/puppeteer/puppeteer/blob/bba3f41286908ced8f03faf98242d4c3359a5efc/src/common/Page.ts#L2002-L2011 + x, y := math.Round(clip.X), math.Round(clip.Y) + clip.Width, clip.Height = math.Round(clip.Width+clip.X-x), math.Round(clip.Height+clip.Y-y) + clip.X, clip.Y = x, y + // The next comment is copied from the original code. // This seems to be necessary? Seems to do the right thing regardless of DPI. clip.Scale = 1 diff --git a/screenshot_test.go b/screenshot_test.go index 34ec83cd..a2e1dbb3 100644 --- a/screenshot_test.go +++ b/screenshot_test.go @@ -5,7 +5,7 @@ import ( "fmt" "image" _ "image/jpeg" - "image/png" + _ "image/png" "os" "path" "testing" @@ -16,43 +16,66 @@ import ( func TestScreenshot(t *testing.T) { t.Parallel() - ctx, cancel := testAllocate(t, "image2.html") - defer cancel() - tests := []struct { + name string sel string - by QueryOption - size int + want string }{ - {`/html/body/img`, BySearch, 239}, - {`img`, ByQueryAll, 239}, - {`#icon-github`, ByID, 120}, - {`document.querySelector('#imagething').shadowRoot.querySelector('.container')`, ByJSPath, 190}, + { + name: "padding border", + sel: "#padding-border", + want: "element-padding-border.png", + }, + { + name: "larger than viewport", + sel: "#larger-than-viewport", + want: "element-larger-than-viewport.png", + }, + { + name: "outside viewport", + sel: "#outside-viewport", + want: "element-scrolled-into-view.png", + }, + { + name: "rotate element", + sel: "#rotated", + want: "element-rotate.png", + }, + { + name: "fractional dimensions", + sel: "#fractional-dimensions", + want: "element-fractional.png", + }, + { + name: "fractional offset", + sel: "#fractional-offset", + want: "element-fractional-offset.png", + }, } - // a smaller viewport speeds up this test - if err := Run(ctx, EmulateViewport(600, 400)); err != nil { - t.Fatal(err) - } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + ctx, cancel := testAllocate(t, "screenshot.html") + defer cancel() - for i, test := range tests { - var buf []byte - if err := Run(ctx, Screenshot(test.sel, &buf, test.by)); err != nil { - t.Fatalf("test %d got error: %v", i, err) - } - - if len(buf) == 0 { - t.Fatalf("test %d failed to capture screenshot", i) - } - img, err := png.Decode(bytes.NewReader(buf)) - if err != nil { - t.Fatal(err) - } - size := img.Bounds().Size() - if size.X != test.size || size.Y != test.size { - t.Errorf("expected dimensions to be %d*%d, got %d*%d", - test.size, test.size, size.X, size.Y) - } + var buf []byte + if err := Run(ctx, + EmulateViewport(500, 500), + EvaluateAsDevTools("document.documentElement.scrollTo(20, 30)", nil), + Screenshot(test.sel, &buf, ByQuery), + ); err != nil { + t.Fatal(err) + } + diff, err := matchPixel(buf, test.want) + if err != nil { + t.Fatal(err) + } + if diff != 0 { + t.Fatalf("screenshot does not match. diff: %v", diff) + } + }) } } diff --git a/testdata/screenshot.html b/testdata/screenshot.html new file mode 100644 index 00000000..b9ddacad --- /dev/null +++ b/testdata/screenshot.html @@ -0,0 +1,73 @@ + + + + + screenshot + + + +
+
+
+
+
+
+
+ + diff --git a/testdata/screenshots/element-fractional-offset.png b/testdata/screenshots/element-fractional-offset.png new file mode 100644 index 0000000000000000000000000000000000000000..cc8669d598be11889adf0f5d5ac1453e54c3efee GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^ra&yl!3HD~?p=8Sq!^2X+?^QKos)S9D6u`Nc+rW&s8 l$xQXpPL7hitfgaR5|>%Ts?Gg~>mblP22WQ%mvv4FO#tsSEqDL` literal 0 HcmV?d00001 diff --git a/testdata/screenshots/element-larger-than-viewport.png b/testdata/screenshots/element-larger-than-viewport.png new file mode 100644 index 0000000000000000000000000000000000000000..5fcdb923555dcdff72047ca5a53974015cb17b6e GIT binary patch literal 2807 zcmeAS@N?(olHy`uVBq!ia0y~yV2T1^4mP03_vn|e7#O(rdAc};RLpsM?I7m?1Azk@ zykGv)UhaK`rOBjmew=YNKLf-5>hv{Y3|>mU^ExdB^RWeeN$`2#PGmPUZ{j?bP0l+XkKPxw46 literal 0 HcmV?d00001 diff --git a/testdata/screenshots/element-rotate.png b/testdata/screenshots/element-rotate.png new file mode 100644 index 0000000000000000000000000000000000000000..52e2a0f6d3c66bd455961250051d416897790ffd GIT binary patch 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 literal 0 HcmV?d00001 diff --git a/testdata/screenshots/element-scrolled-into-view.png b/testdata/screenshots/element-scrolled-into-view.png new file mode 100644 index 0000000000000000000000000000000000000000..917dd48188d45ea1b07c1fa460a691a9e4a2d050 GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^W+2SL1|)l2v+e?^d`}n0kc`H+w+y)&3bP0l+XkKPxw46 literal 0 HcmV?d00001