From 18f525f7f2520627f14b75f88b044c836437fe21 Mon Sep 17 00:00:00 2001 From: Artem Denysov Date: Fri, 2 Mar 2018 20:45:33 +0200 Subject: [PATCH] init --- .gitignore | 3 + LICENSE | 21 ++ README.md | 43 +++ extension/background.js | 107 ++++++++ extension/images/icon-120x120.png | Bin 0 -> 8091 bytes extension/images/icon-16x16.png | Bin 0 -> 1242 bytes extension/manifest.json | 21 ++ extension/popup.html | 105 ++++++++ extension/popup.js | 112 ++++++++ package-lock.json | 433 ++++++++++++++++++++++++++++++ package.json | 14 + test/fixtures/background.js | 1 + test/fixtures/key.pem | 28 ++ test/fixtures/manifest.json | 18 ++ test/fixtures/popup.html | 1 + test/fixtures/popup.js | 1 + test/test.js | 200 ++++++++++++++ 17 files changed, 1108 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 extension/background.js create mode 100644 extension/images/icon-120x120.png create mode 100644 extension/images/icon-16x16.png create mode 100644 extension/manifest.json create mode 100644 extension/popup.html create mode 100644 extension/popup.js create mode 100644 package-lock.json create mode 100644 package.json create mode 120000 test/fixtures/background.js create mode 100644 test/fixtures/key.pem create mode 100644 test/fixtures/manifest.json create mode 120000 test/fixtures/popup.html create mode 120000 test/fixtures/popup.js create mode 100755 test/test.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c727bb7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +.idea +.DS_Store diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3312f1f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6ff4a31 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# Sloth + +`Sloth` - an extension to make you suffer, browsing the web. + +Do you think that users have fast device/connection as you as developer has? + +No they are not. Their devices/connection are slow! Very slooow! + +![](https://media.giphy.com/media/6olNeyYPutJjq/giphy.gif) + +--- + +So enabling network and cpu throttling to have the same user experience. + +--- + +## Conditions: + +- CPU: `4x` throttling + +- Network connection: `1.6Mbps` - download, `750Kbps` - upload + +## Testing + +Extension is tested using [puppeteer](https://github.com/GoogleChrome/puppeteer). +Token was generated to rich tested extension page. It's value stored in fixtures the same as fixture for manifest.json. +All other files (background.js, popup.html, popup.js) are symlinks (./extension -> ./test/fixtures) + +## Development + +After adding new permissions commands below has to be run. + +``` +# Create private key called key.pem +2>/dev/null openssl genrsa 2048 | openssl pkcs8 -topk8 -nocrypt -out key.pem + +# Generate string to be used as "key" in manifest.json (outputs to stdout) +2>/dev/null openssl rsa -in key.pem -pubout -outform DER | openssl base64 -A + +# Calculate extension ID (outputs to stdout). Should be added to URL to path to extention page, aka chrome-extension://new_generate_key/popup.html +2>/dev/null openssl rsa -in key.pem -pubout -outform DER | shasum -a 256 | head -c32 | tr 0-9a-f a-p + +``` diff --git a/extension/background.js b/extension/background.js new file mode 100644 index 0000000..60ec82b --- /dev/null +++ b/extension/background.js @@ -0,0 +1,107 @@ +class Storage { + constructor() { + this.storage = chrome.storage; + + this.schema = { + throttlingEnabled: 'throttlingEnabled', + applyToAllTabs: 'applyToAllTabs' + } + } + + set(key, value) { + const data = {}; + data[key] = value; + return new Promise((resolve, reject) => { + this.storage.sync.set(data, () => { + if (chrome.runtime.error) { + reject('Value was not set'); + } + + resolve(); + }); + }) + } + + get(key) { + return new Promise((resolve, reject) => { + this.storage.sync.get(key, value => { + if (chrome.runtime.error) { + reject('Value can\'t be get'); + } + resolve(value); + }); + }); + } + + onChanged(cb) { + this.storage.onChanged.addListener(cb); + } +} + +class Debugger { + constructor() { + this.debugger = chrome.debugger; + } + + sendCommand(target, method, commandParams = null) { + return new Promise((resolve, reject) => { + this.debugger.sendCommand(target, method, commandParams, () => { + if (chrome.runtime.error) { + reject(chrome.runtime.lastError); + } + + resolve(); + }); + }); + } + + attach(target, requiredVersion) { + return new Promise((resolve,reject) => { + this.debugger.attach(target, requiredVersion, () => { + if (chrome.runtime.error) { + reject(chrome.runtime.lastError); + } + + resolve(); + }); + }); + } +} + + +class Tabs { + constructor() { + this.tabs = chrome.tabs; + } + + getOpenedTabs() { + return new Promise(resolve => { + this.tabs.query({}, resolve); + }); + } + + getCurrentTab() { + const queryInfo = { + active: true, + currentWindow: true + }; + + return new Promise(resolve => { + chrome.tabs.query(queryInfo, (tabs) => { + resolve(tabs[0]); + }); + }); + } + + onCreated(cb) { + this.tabs.onCreated.addListener(cb); + } +} + +const storage = window.storage = new Storage(); +const chromeDebugger = window.chromeDebugger = new Debugger(); +const chromeTabs = window.chromeTabs = new Tabs(); + +chrome.debugger.onDetach.addListener(() => { + storage.set(storage.schema.throttlingEnabled, false); +}); diff --git a/extension/images/icon-120x120.png b/extension/images/icon-120x120.png new file mode 100644 index 0000000000000000000000000000000000000000..48c98938ea92af962314b1b3243f21c8d7c565f9 GIT binary patch literal 8091 zcmZ{J1ymeO*XH2v8iHGJml+6|;7*WWg9UdR+$F$^1Og#IumC{xoVJ!S5k4J0001CTRZ-AIY5#u(7aNs=Y$T>p z3e#RzQx*WI{Yda&iG?bodFd+40jkF74^RbWYZYBh03eVP00@r)0Pav#;rjr9j}QQG z_znP&`T_vZc;vL`yhJr%Td6B60RH}a<+puLMb+SWs2F;Wf}*xldF0(v6(Zod~1HQN)?yDTFo#njsQ8K$OR{_{OEHAWV< zgE$!P58FCVdWQBDpaNzZ`0E_o2}kQ3#+!juF8Dq%>J_wcDy$%%hxvp*O$gqHND0uX zTs{V@KU-fs5WO7vNpo5?vVDPZT2JzMJydi#j`<3N^Ps;z5WxgUV%X1?@i)S5$9*td z5A$Jli+P2!8q9>-g{(477nGr7+zV&ISS4spGpueTPQX6bU!UpR+xMh+!&m)&ayO15 z_+)seJ;aO9pX6M$(?Ut9UC$+-=;7J9>Uup_dTr{904vn2{HkqlrT~D^AJEUWdL?o< zpGwgt&^xe#(e@#H8LQ%%5%cYM?bqNZ5HKsgOb^jV&>aHUU zZpX+l{6C888-?l9<(O;Op$vayitw1=?!Gt1$pKOGc{sz${Aq?{jMxGgBrFPR3^z(_ z&K_%xSmqePn157zyTE><+uGK#u0@*zy#g&MiYn6=jAh`OF!iIiN2Gg~k3o}#qEj?+vtCq8e(slGO zoOA65@@eo#z7jKGq5MExmYcIWvX|hOTv+|&1Hzu))n`x1VNwbw0-ltXo0!&wh|U;l zd{L8Io%d_HWBPOr2nS@>?~n(L@I0amQ8YK&^_D79w~tXLn19?m!5HR|8)5+tM2#M% zUDeF6gY!X6J=;&_8ncI!Ws(Q8CJMO%oLah-t#6*j15>3nG#iaD;#5^Oqs-yz@Wt1T z(Y_21Ld<+Ncm$vam)Nalu1aFPdIp7Sae_D8Ms&KtaJIQsKC8mVnXCIQd2W|alde7v z`c4hynt{0jsyQB|c*Y?`6W*pcf`o&gp*ACmkuI^&oSxZ3>T|sHt=Ga5 zC&u$?U=gnNbb=!?TMX&s?WDO`n!yQX=%4xLHG~>@gPQnBjXN0f8 z5S;M73_t!91fBB+Gh}(QZXg9k_E@Gb3I23lug=GQ-Dm_Pu_n3D zqe7$6^fmlc*7-^}Dz#%066P5D%}sexj0xw7=$wK+KUULfe>QtI1L2_m{0sZSU(Jtz zMbE+XUzAA`q|{Imd@#^xog_RAoo}VO%=at|=0kND(g_E>h8Fr}EVfb6@#=pve|YD3 z7%QYIXI-oYN3eqpzxhLn!4&kfTa?{n_k7oqWI+J~sf{dit3S~@Fj^1ZMOivj>6fjV z6)d?heI`w!logP73{6_rIADg|KJR>kVX>l`@KeU;-Rwhs=X2t33T;}w!-;S{i;l}5 z95ne$F7?5W#JrNH(?<$8zrRQ_o7&<$P#w)H)ll`Ep2Uh;Z_ZWexz`1UL@aI&^EOs9 zU~#IJ@>kWMDzw@HNIy>E8oK|KrrpWLZN0v-rQ{)LWg61^N?TDYX28SO=ACT-^CT6# zdtIpST5~n+O2?b#lW~CQiS6GZw^J?CW}G1DT#BQzd7G-?(PW)i+}JSuYs4>hF?UG) zj4HBp#qxGXdN$qQg=U=`Fo<=>gCQGF3pb?!ajPn`Kqv3x84WW@5K((#RGv;ShQm~R zJh7Zd+1+JG9{h(lq!4&o()&oZI4H$IP@?eLv&x#r8Wr+4Lnmx;)(C3qvQ?8VjU<~y zkDI%$E(CK5zCRB6;2OB1xVE8x@q|O;;gCrq5uf5k#ftg2+6D>LYNRtMF%piHjkJ&G zAT}|%xR^98*Bn~G2>F!l3{zd1mIvmX{FTEn4Z1UwJuVyIy!hmBvCCP#uedQ+USED>uzvh2{I8 z{gJiN{XL&dcFB_qxuiPLd*#iwKb!TI#_)x3-E{<$2FMJ#e%YCQ6SQYgtQF#^`Dvwu z77qbAOmJ@C`K`KqI#!fQa;5)F7aUU&A}kS@8#}y`Rt9PyVfWP7IUQzyK-fnNRGBw_ z=>HZjSvRq~+wYX-R_HS;64+O`*l&O*&pcU)y&m8ISSL31H8$#vl+-(sTrNiB2phIRQ~#?Iuy50HPO5O#Xw7RWa$i* ze8`X1)b~`%I~Wy;h+JL#fvKjddUH+|gJIiBjwFgrAZL7$DFt_G zWa(VVj6+HuZ(`vPqenm1xG=UN!e|l3$(gCqG;(Pwk{Ql??n!Ttj3bcqm80zQhLfRq zMGBx-8$h}VFFbVAoK7tozwqG=Uh9FVe2Q|D{I+sBo&EZFi9N=*Qq`>lsKr|l?j=yi zxjm>7D7<3lye3NgS=UAct=W_-6&@3gz^_ys5zPz-YVG?5q3Av7t^1Ef8Vs7MlIbv4 zOQ>zmZkXqO5z4Mlx%fK#gx}BD5vc*SH<(K2G6|1%!pv{Dt z8vYh1^KdTw39pNn87>m}NQ%lN)~Ulb^h1ag}SMOS~+7LXF(xLfGU{7aEapT(j(TI<-YFXnHE ztw&qz)CuyDyfH^D2k#t3cJ{$+*<<;z5_9*I51F!Ay?D7w*&1a+<2wZ-v}_L6ggS6( z-^*hlao@M(AX05L2+qI-9(q_vV^~M?@x(rOU#D3&c}NATYObGsVfdKDdKAd7E+o~Z zCPK#U3zF(dDnyFk)OgU^oW=+d)=(e*-lZLbahwis`!ek7D9=RL*$^4Xc?jCQg{J9l zLo?+$Ze(R?u~9i;o-EeWKF7nE!8EbS50;jFZM50!Y+%7uyg{38`8@a|^Xsgm7Z2B*8VkoGkZi7sNRB z#T7$RMuaz@vZt6VDXHScWF39#_78%JBxqkL=RZeMm|Zd1V@2ET9`&-)BE8rK+3Aoa z^5>JopMFz$1z^#(hm@;IcmQ=)4ZHUmUwMKV6^h?guA01%jJK?`b@b)$k38a1d&Xr; znWpq{uNoO}CHIwLClSqn)W2bAeDc*%`?4~7z?`B)5FH)0O>`3r({8|N%Qb}HC-$H? zHnns5Tsm&agsfzvw>40@P@BEAb>52eY_qxNW{AhEnol_0+~t5HhMj>{3bFyBo6rYB zD)XfIHhZCu=0Um3()#MKHcyO%;)OJW10!=C=f2>_!1v06KYqtAlGqW-dxYt?3mOYwZ=nm~Qs3Ir>)A>C-2 z4XpX;@}~?*S-6f%Da?u|kugn4qdt2w;V0>_6Es2|bf!BVL|HG;UVbK>T#NX`zWHQY zuE=N5V#J$e;=JU#qe93h$!E;;DI=din#`mYc*%7?2 zPNHBjEb&Bf(RPLi`<1epc#7MgSIN|hV$I`)G|@KP9jvO?%=1$~^RSHe0@WlH@jt|M zvc1VApZYH7>5(jPf$%EecbJl6U$>e)MRN!(99}=Oc%?Ey6*183squa0BPJ-H{s%)} zw^N3iD0KwUe9;nSf4_WXwARF=@ll;=0@t4D_$c;gIRXh5)#=}YINMI6cdU)C>mo;IkYnd6DZ!!0Y{$FxNL&m{r|VB9Gl(5=aYYmN=p`yY7{&VnmAU;-kt6Ea!m4nveH4!VT3U<*q7ijo5f$}O zn^ud*CodSsjk(LbtBlTm#2M~XG2ycNF-_p~-_s~Hw%zA~lBHrd=i1!Sd3Z(Y^hDw{ zymZDl9-izh4$*llD-x9rd`Lqph_r(nQ#;0YwJa+oF)9V#I>ynA*}ygZah|xK;#dl! zQaZP)XHqgj%x;=k-ht`a&N0zH3YhL1RYr722ldpS^<6}wZi+?r#fFV4#p(+(dG69i zD|6H1sr^+8@iaACnLxQ-)|z8TX&IR&2LMd7`R6o=yFQn3xhlS_-mq5WRSrKdMCO?W z2w$YV+iz!4w-lJZR@&BvNzD_p6Z6FYC$YD)EsH}qM$|TOPle;hA3GP+3P9K?#hcJC&V~XV9~u_QJM+-P zKDc!>UH2%3Tf6IL^b61G$iYZuZoPN7FL|D=6@y!>A5+KnJclK9i{hs_xEFaa8EEbnBo-iGK^-yE|h3$^Y4f4 zW-wol+vs@*K`=2(6H(p9d#cNW+xrzWc$Q0ZI8?&bQ$LR2O zFL6iGH`7fmK}@;NiC-38UOrq zehQM3&A)XAk5O?4Ou(i2)PRF~Rd;!a_~)HSzf^6vlGwbJWy2^&(=T&5d2^$7Q~?$# zB$QsDzG&mIK4VRmc@tg^vUlF7MbHpLda}tCPY$x5ebQyIJv`Znnx@-@#v>W^as)>1 zgOWbzK-;a_i<*JoC9@5wA6!)2N}7=BVGK>R$SU3-<>U`#g}=Q9C48j~xCO6KZ zuGqBTJJ3uQ_4X2U9e=4jxSH-!N_D$HZr)UWt<>p7Tfg_|We`bpLt|$iv(EQ>RqYgC)Yh;CAc~BS+&bHjVl^j2${^ zHCO+pU$Tc~dQ0x~yY(Vw-hRtyPwYE(hR3QNif74i(IWgRM-`;7XE5m&aYyd zDs5KiPJAMEH^!xkC>Jv5$M2axk^22HkNZdl1Fx`qA_isY>!p5uVHTB8aJZ#S_wWb) zDi(IFxPBS50vxpE%9E0qDxvE=ZW1a7=^=o1cn4KujfFdOVHrHfaOk+(HqW+k(&8yo z9%aLk_A8$c%oz(uZwZ1|A@`d#LAWF*tcGen5fe2szNTnTA0jx}Bu&2g2it8nkA7uS z^{)@;Q|qZid`jNVS8~6&K8fq{YAm7fnem=ai?Nlc2I?;^%rr7_6>~A>u}H!l-|4@x zg)BhtSkE&*Clm;YbvY(BW51L+f>iFVM+OX+qtaaO`l} zyP7-H%lOtZhJ8J(y1hDK@0Zb9(H~gqe)yC85hv&<>$QKx>*fAMc^-QdH2PI0PL+HS zHgrm}Eqxwh&mbin?B~cj`Y-ZX3r%Vuf*FM0#tgENQ3nIK#F?gPsbEYR=+grEOBEc|=K z$h$CkS0LPE(7^vM?z0_^PzjssfqxwTJ74x4s?nErK;% zNsmM+;%~m!F_m$#`F%ouZLZMCQG_;2xN7skL;k$Jl3J%pA!<~sbb6nLF)ymh!v1ut z+2fba?(WpF(hd{B4_omZGZ{Y^lMTn~nur=T11dIt#&L`UV?9XyS&N805#b=9?|~j& zyZ{3~S^HciDOx6%_%EH#{Q7GaegEvpWDdq3XG=m@9>lNJig{BKYN{#YpX`klrkL3PH%*Hx|lZd z?t{|NnQ~)HnBVu+(W>QE$qShWO7d>Fo_O$O(vEK*X|Z1D_4I!G(X!u$xAA4aUc4TM za380po6(@K-zmE#3sNp?`OUl0yl=2)w(DZ}#BOhX!TmP2Rku26C>DDEJX*5cTn-3+ z4fS`m)DcHIQJBQktl1Cm)43T^A`SU&4#}N95G^gJVKAX~GGlGi9MlGykO18eW>KDDoSA`owxVYEQZnv z!q%~#_DsP|tFAvQYH`-+&!X5ytl@W81JjvDsNb$FOD=h*vnJr>irLkfA7ZPk&VJ+U zo^>5Q&x!Gpjugx}O$XC2Er!z)@`wl)z{JqZrQE<7Bev85!M1yktnEsFW^$^IU&jNg z-@Ysfwhqd_c?l_9H47-J)x9myYP)6gr0nRU^NJTXEC#>!PDA*au03AXB;U)0Ry~+H zhMq=ev_Jg5J5V-%zXfkS$$i7%^Nfiw^1cS~Ywqu@ppl8uCIk3#=5XQf`BGoM(}}p} z>^)1xqmGSowHH0Zf|-p~CsOA{Tt)1^fFr^ky+rbRgi!;=U6D#ya_4 z?-N?LGLr;_Vb>#v&dM234kZ z{9vr6S4Tl<@vx|7!s1)dc>96i#bkEqb*bV{A`dVp{@a{le)#b*HS0!W`<{SOcP>MI1m?VRY@D|A&FAyN#n=!2jQ11VZ!;WuWx02QPO!Z$C?@EkM=P(%x3n(%#Y9nOQnQ5)}0F^Am9V7mX4WTEHD@zZ`RdA_UO>%lOv8 z$JI(oO2OUQ$JN%&TS`hB1@!_5fBy>?{2!SA_9-RhVC!h_;0+K4iI4!G(I^bffAIV- zjG?2Ax5K}XnFD?tx8dvKkB*8kwqe2YQ>gvCXKP=gW| z1@3>@LdCHGB>%yq=NM=U5R#Mo{O#2-s>$F#n)G#50d|fqwy5C?y1IG0xZ4ZbS^7F! zySsS_{_lwqv=$W>1p%$a#DO*v)*@D-LN>M%l2&3iwsyi+AW45bDP46hOx7_4S6Fo+k-*%fF5)TbZd z6XFU~aCT?=*&Xd4KkfMc|Nn-bBoG$|I=8C>j}%CDSNo|g&1ZLYoZHn7 z|Npo4FDhBzmAJY&YH5Ac&RKb@TVhr>MXha#f*1(Ya%6S&fhA?9x3%tDSORndP{FFE zsMFh8c1+J%)fl;VLD7cpq%GZvyC$V=?M+_a9=B^^+Of5@Yg%JAb|tLq$=EeD=g6GA zgEMn?Ps`cRo3W`Y;n=#mHLbBTtK$;FBRUIX*L0>%tx8Oah?rFqzp^8#DmA<^HN2-F zq9`%EJ3pc#C4Be%;yG23%j%;hmqcdAh6CL>yD}2!;AIU_wdoNtAz>4WB7x!aVf&j* zV328*1o?sD0tlF-oe~R`yKj8^XL(-Xc=O+jU;lpSdBM8GqVXZ4$$}?;Sk*%E8$u>~ z0hKZ)dAqx?^k>`*1adeFJR*x382Ao@Fyrz36)8Z$hn_BuArhB!&mT=Xh;O0hyZ`_BFZh}ll$BR9-dor+QG1G4>zs)X;}?0Yz4%Iqb8gt;Fh!m&B&6d9hwKiB&p$+0?AH zaqCUBww$?n;9kzw{rjx%<}JT2zhzcg0gwCRlO<;J&oAqKn(=~RdG^uu^%af}>dpV0 zddjjrefMqomdAk>bIwKgOJ8{XIey*t-+#;Y{-}*(nP_o<&H3SRWf`tbj2mB+Do=D< zw6VfQtk*+T{@(luE47a`J}ZTRo>eVzjVMV;EJ?LWE=mPb3`Pcq7Px5}Yt$jwj5OsmAL;dB)5BA^C0kPXH8X(i=}MX3y)xrynizKQ9X z$=M2Kdggi-x|Rw+D@}9_Omz*76ao#+6f#Om3as??%gf94GC`_=7^qq=zbO4q+gqT4 z3=$v{Lo!NpldP;<@{>z*Q}ar!tO9^aix~`;|KF{TrUq=Ml~qP+W_m^mgQ2AnKSNO~ zP>nc}IZ!p>nJFb1ASIXAC~^RmNFpim&CE?LsVqok0J}=RATQm1Q8@mC9h? literal 0 HcmV?d00001 diff --git a/extension/manifest.json b/extension/manifest.json new file mode 100644 index 0000000..27b999d --- /dev/null +++ b/extension/manifest.json @@ -0,0 +1,21 @@ +{ + "name": "Sloth", + "description": "Brings connection like your users have to you ;)", + "version": "0.1", + "icons": { + "16": "images/icon-16x16.png", + "120": "images/icon-120x120.png" + }, + "permissions": [ + "debugger", + "storage" + ], + "background": { + "scripts": ["background.js"] + }, + "browser_action": { + "default_title": "Sloth - slooow connection by default", + "default_popup": "popup.html" + }, + "manifest_version": 2 +} diff --git a/extension/popup.html b/extension/popup.html new file mode 100644 index 0000000..66c73a2 --- /dev/null +++ b/extension/popup.html @@ -0,0 +1,105 @@ + + + + + Enable throttling + + + +
+
+ + +
+
+ + +
+ +
+ + + \ No newline at end of file diff --git a/extension/popup.js b/extension/popup.js new file mode 100644 index 0000000..9a44841 --- /dev/null +++ b/extension/popup.js @@ -0,0 +1,112 @@ +'use strict'; + +// Huge shot out to Lighthouse for const values +const LATENCY_FACTOR = 3.75; +const THROUGHPUT_FACTOR = 0.9; + +const TARGET_LATENCY = 150; // 150ms +const TARGET_DOWNLOAD_THROUGHPUT = Math.floor(1.6 * 1024 * 1024 / 8); // 1.6Mbps +const TARGET_UPLOAD_THROUGHPUT = Math.floor(750 * 1024 / 8); // 750Kbps +const TARGET_CPU_RATE = 2; + +const TYPICAL_MOBILE_THROTTLING_METRICS = { + targetLatency: TARGET_LATENCY, + latency: TARGET_LATENCY * LATENCY_FACTOR, + targetDownloadThroughput: TARGET_DOWNLOAD_THROUGHPUT, + downloadThroughput: TARGET_DOWNLOAD_THROUGHPUT * THROUGHPUT_FACTOR, + targetUploadThroughput: TARGET_UPLOAD_THROUGHPUT, + uploadThroughput: TARGET_UPLOAD_THROUGHPUT * THROUGHPUT_FACTOR, + offline: false, +}; + +const getBackgroundPage = () => { + return new Promise((resolve, reject) => { + if (chrome.runtime.error) { + reject('Background can\'t be get'); + } + + chrome.runtime.getBackgroundPage(resolve); + }); +}; + +(async() => { + + const background = await getBackgroundPage(); + + const storage = background.storage; + const chromeDebugger = background.chromeDebugger; + const chromeTabs = background.chromeTabs; + + const enableThrottlingBtn = document.querySelector('.js-enable-throttling'); + const applyToAllTabsCheckbox = document.querySelector('.js-apply-to-all-tabs'); + + const toggleApplyThrottlingBtn = state => { + enableThrottlingBtn.disabled = state; + }; + + storage.get(storage.schema.applyToAllTabs).then(value => { + if (value && typeof value.applyToAllTabs !== 'undefined') { + applyToAllTabsCheckbox.checked = value.applyToAllTabs; + } + }); + + storage.get(storage.schema.throttlingEnabled).then(value => { + if (value && typeof value.throttlingEnabled !== 'undefined') { + toggleApplyThrottlingBtn(value.throttlingEnabled); + } + }); + + storage.onChanged(changes => { + const storageChange = changes[storage.schema.throttlingEnabled]; + if (storageChange) { + toggleApplyThrottlingBtn(storageChange.newValue); + } + }); + + enableThrottlingBtn.addEventListener('click', enableThrottling); + + + // @todo if throttling enabled to all tabs make sure new opened tab will also have it + // @todo use storage - https://developer.chrome.com/apps/storage + // chrome.tabs.onCreated.addListener(enableThrottling); + + async function enableThrottling() { + try { + const enabled = document.querySelector('.js-apply-to-tab').checked; + const enabledToAll = applyToAllTabsCheckbox.checked; + + if (enabledToAll) { + const tabs = await chromeTabs.getOpenedTabs(); + for (const tab of tabs) { + attachDebugger(tab); + storage.set(storage.schema.applyToAllTabs, enabledToAll); + } + } else if (enabled) { + // @todo make this as feature, store applied tabs by URL(domain name) + const currentTab = await chromeTabs.getCurrentTab(); + attachDebugger(currentTab); + } else { + console.log(new Error('Throttling was not applied')); + } + } catch (e) { + console.log(e.message); + } + } + + async function attachDebugger(tab) { + // try/catch to not fail on empty tabs + try { + const tabId = tab.id; + const target = { tabId: tabId }; + await chromeDebugger.attach(target, '1.1'); + + await chromeDebugger.sendCommand(target, 'Network.enable'); + await chromeDebugger.sendCommand(target, 'Network.emulateNetworkConditions', TYPICAL_MOBILE_THROTTLING_METRICS); + await chromeDebugger.sendCommand(target, 'Emulation.setCPUThrottlingRate', { rate: TARGET_CPU_RATE }); + + storage.set(storage.schema.throttlingEnabled, true); + } catch(e) { + console.log(e.message); + } + } +})(); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..f5e2568 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,433 @@ +{ + "name": "Sloth", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "agent-base": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.0.tgz", + "integrity": "sha512-c+R/U5X+2zz2+UCrCFv6odQzJdoqI+YecuhnAJLa1zYaMc13zPfwMwZrr91Pd1DYNo/yPRbiM4WVf9whgwFsIg==", + "dev": true, + "requires": { + "es6-promisify": "5.0.0" + } + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "dev": true + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.4", + "typedarray": "0.0.6" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "diff": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "dev": true + }, + "es6-promise": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", + "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "4.2.4" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "extract-zip": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.6.tgz", + "integrity": "sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw=", + "dev": true, + "requires": { + "concat-stream": "1.6.0", + "debug": "2.6.9", + "mkdirp": "0.5.0", + "yauzl": "2.4.1" + } + }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "dev": true, + "requires": { + "pend": "1.2.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "dev": true + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "https-proxy-agent": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.1.1.tgz", + "integrity": "sha512-LK6tQUR/VOkTI6ygAfWUKKP95I+e6M1h7N3PncGu1CATHCnex+CAv9ttR0lbHu1Uk2PXm/WoAHFo6JCGwMjVMw==", + "dev": true, + "requires": { + "agent-base": "4.2.0", + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.1.tgz", + "integrity": "sha512-SpwyojlnE/WRBNGtvJSNfllfm5PqEDFxcWluSIgLeSBJtXG4DmoX2NNAeEA7rP5kK+79VgtVq8nG6HskaL1ykg==", + "dev": true, + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.3.1", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "progress": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + }, + "proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", + "dev": true + }, + "puppeteer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.1.1.tgz", + "integrity": "sha1-rb8l5J9e8DRDwQq44JqVTKDHv+4=", + "dev": true, + "requires": { + "debug": "2.6.9", + "extract-zip": "1.6.6", + "https-proxy-agent": "2.1.1", + "mime": "1.6.0", + "progress": "2.0.0", + "proxy-from-env": "1.0.0", + "rimraf": "2.6.2", + "ws": "3.3.3" + } + }, + "readable-stream": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz", + "integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "requires": { + "async-limiter": "1.0.0", + "safe-buffer": "5.1.1", + "ultron": "1.1.1" + } + }, + "yauzl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "dev": true, + "requires": { + "fd-slicer": "1.0.1" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..560d8f6 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "Sloth", + "version": "0.1.0", + "description": "Brings connection like your users have to you ;)", + "scripts": { + "test": "mocha ./test/test.js" + }, + "author": "", + "license": "MIT", + "devDependencies": { + "mocha": "^5.0.1", + "puppeteer": "^1.1.1" + } +} diff --git a/test/fixtures/background.js b/test/fixtures/background.js new file mode 120000 index 0000000..e8ca680 --- /dev/null +++ b/test/fixtures/background.js @@ -0,0 +1 @@ +../../extension/background.js \ No newline at end of file diff --git a/test/fixtures/key.pem b/test/fixtures/key.pem new file mode 100644 index 0000000..24afa25 --- /dev/null +++ b/test/fixtures/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDSRLp0YHkchHjq +7AEu3AEYG13g1GEpNAZunKp2qU5QMJs1umain0NwJnhghcUYoOi5i5K/g3hA1ZxS +NvojLeOBVM9P5CHXPQAT74MskQxVJYXXZEOWVWdHtjqSr6oKLgR8TXUrqRFgPzSm +wEYcoKF3cRr2QjQKxImfNGeShrwDkyZeUOImZkb8BjpjuEBCIAlQpd1MkQm1ATx0 +R1wvnt7zukCEM59n76zEl5XfHkzlt5WdNJTVleeTFY9i/yxS52GH7E/cywlb1wfk +OiGQbPqxnGBQymwwcUCyAbLk+p/fg2QwmTSn4aD8I8DB95/e/qQnj15ocjvvc7r6 +gBPfELOzAgMBAAECggEASzOXd+3VAvUCBgsYNTksDwIipjlu8nyP3Fmdwci1oIpx +yFp9QKYzSVYI8YSGRFOwSEP53RLZHF8JhIHzHkfYOTYq2wjdbYx8jaS4xmSuA3nj +D3Dll0u+H3C2LCY1a2Sf6BEP2eiitUiFvSnBKZXtibljCjYNRNib29uzOz2C2sfM +WUFMBE7i8PUpyczTm6HXs01+AaRpPnsZh/KbW6DP9k30qhexn3oQusGsajf0XW70 +pUcppseGG4YwPC6yyhXDQSrWR156pvnJ/+tbPvcpufVrZ6Ji8aCxb8FI0yD4G79Y +VRuK7lmuh87QVwagOu8vcWndS2/S3l5iGVQWIHwQQQKBgQD/IbV6YkYRXrDdHX+N +qBP8VEzbT0U5VFXqp3G3LFAXoCkAR6R4+uixP+xNQ2unAIZLpWb3nEZVqdmDCHU0 +ig/jRXu18NvF397MGGylmQ/j2lKOgKOmdI3vTs6hKui3eLj+x379aN+dOQy6f3Sb +DkAIzqPiz2p9HvcXsIhF9KVpEwKBgQDS++5YApFY7VBanBgc7A4iGKfDQI9bTmSc +Ty0BjVwK2EJfcKn6QW4glt8g2nkoynO59ZPC4imbP7NXMqcTRD44tY5vaTvstxXg +RfBr++F/5eZ9wc1hdY3wsL+eIphuXNBb0O2Q/jvhbdGBJ9Gh4MuZj35ai2uRngC2 +S9r/hyh+4QKBgQCcG2ATdjYxQqMtaHM3lfREnMA6IJLWtUeswK/PyIlpK1JrHWsB +q2Tdr6NC01H3aVbCyn3qW/CdUcQJbQ9qCmAWS9maRbnzpTuJ3hf8a19Rp7CECSoZ +G5ANMUc1Ti/+sxteqWqGW7kXVmT3aC1NArcjrGySxx79WAkQsa9wPnVHiQKBgCHr +Lp4ni4ZoYIqwnY/Iw1zn6dCSCHn9WF9ouyuMtkdoDQOa8TiyXCoRFz4I4YGVSpWS +zXZ/NYEn9IUIN6P5TKeBSKzQljmwFWxA0i4KCG6vXybIiWQpqfAP+j5KAYd8lGKu +bALrXGUZap0A8cq8gP7m+sFDuL9oT2T/PO3npLMBAoGAP/ulr/a+2W5fKVrb89T8 +oKtMFJvxKpyfrhF+ivppVnsm+GpDAy7Ua7drvR98wzSv42x4ryeSGKHj46UVde+p +OQeNTYr0XpTnfF9L2wAksM8mwr5oDXR7GfJe14odgE3VWZFIPFMVoBURZQAlZDso +aDH1SYEqgENJcD8THHKu/0g= +-----END PRIVATE KEY----- diff --git a/test/fixtures/manifest.json b/test/fixtures/manifest.json new file mode 100644 index 0000000..cba1b0e --- /dev/null +++ b/test/fixtures/manifest.json @@ -0,0 +1,18 @@ +{ + "name": "Sloooow connection by default for testing purposes", + "description": "Brings connection like your users have to you ;)", + "version": "0.1", + "permissions": [ + "debugger", + "storage" + ], + "background": { + "scripts": ["background.js"] + }, + "browser_action": { + "default_title": "Slooow connection by default", + "default_popup": "popup.html" + }, + "manifest_version": 2, + "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0kS6dGB5HIR46uwBLtwBGBtd4NRhKTQGbpyqdqlOUDCbNbpmop9DcCZ4YIXFGKDouYuSv4N4QNWcUjb6Iy3jgVTPT+Qh1z0AE++DLJEMVSWF12RDllVnR7Y6kq+qCi4EfE11K6kRYD80psBGHKChd3Ea9kI0CsSJnzRnkoa8A5MmXlDiJmZG/AY6Y7hAQiAJUKXdTJEJtQE8dEdcL57e87pAhDOfZ++sxJeV3x5M5beVnTSU1ZXnkxWPYv8sUudhh+xP3MsJW9cH5DohkGz6sZxgUMpsMHFAsgGy5Pqf34NkMJk0p+Gg/CPAwfef3v6kJ49eaHI773O6+oAT3xCzswIDAQAB" +} diff --git a/test/fixtures/popup.html b/test/fixtures/popup.html new file mode 120000 index 0000000..55c3394 --- /dev/null +++ b/test/fixtures/popup.html @@ -0,0 +1 @@ +../../extension/popup.html \ No newline at end of file diff --git a/test/fixtures/popup.js b/test/fixtures/popup.js new file mode 120000 index 0000000..a7abd23 --- /dev/null +++ b/test/fixtures/popup.js @@ -0,0 +1 @@ +../../extension/popup.js \ No newline at end of file diff --git a/test/test.js b/test/test.js new file mode 100755 index 0000000..52608cc --- /dev/null +++ b/test/test.js @@ -0,0 +1,200 @@ +/** + * Note: Tests are applied ONLY for functionality when throttling applied to all tabs + * @todo find the way to fix it + */ +const path = require('path'); +const puppeteer = require('puppeteer'); +const assert = require('assert'); + +const CRX_PATH = path.join(__dirname, '/fixtures'); +const URL = 'https://www.nytimes.com/'; +const POPUP_URL = 'chrome-extension://daclkijhjpmgpmjnlppibebgficnlfop/popup.html'; +const TEST_TIMEOUT = 10 * 30000; + +const measure = async page => { + const performanceTiming = JSON.parse( + await page.evaluate(() => JSON.stringify(window.performance.timing)) + ); + + return extractDataFromPerformanceTiming( + performanceTiming, + 'responseEnd', + 'domInteractive', + 'domContentLoadedEventEnd', + 'loadEventEnd' + ); +}; + +const extractDataFromPerformanceTiming = (timing, ...dataNames) => { + const navigationStart = timing.navigationStart; + + const extractedData = {}; + dataNames.forEach(name => { + extractedData[name] = timing[name] - navigationStart; + }); + + return extractedData; +}; + +const getDefaultTimings = async () => { + try { + const browser = await puppeteer.launch({ + headless: false, + args: [ + '--user-agent=PuppeteerAgentFast' + ] + }); + const page = await browser.newPage(); + await page.goto(URL); + const result = await measure(page, 'Fast folk'); + await browser.close(); + + return result; + } catch(e) { + throw e; + } +}; + +const getThrottledTimings = async () => { + try { + const browser = await launchBrowserWithExtension(); + + const page = await browser.newPage(); + await page.goto(URL); + + const extensionPage = await browser.newPage(); + await openPopUp(extensionPage); + await enableThrottlingForAllTabs(extensionPage); + await page.reload(); + + const result = await measure(page, 'Slow folk'); + await browser.close(); + + return result; + } catch (e) { + throw e; + } +}; + +const launchBrowserWithExtension = async () => { + return await puppeteer.launch({ + headless: false, + args: [ + `--disable-extensions-except=${CRX_PATH}`, + `--load-extension=${CRX_PATH}`, + '--user-agent=PuppeteerAgentSlow' + ] + }); +}; + +const enableThrottlingForAllTabs = async page => { + await page.waitFor(1000); + await page.evaluate(() => { + document.querySelector('.js-apply-to-all-tabs').checked = true; + }); + await applyThrottling(page); +}; + +const applyThrottling = async page => { + page.click('.js-enable-throttling'); + // await to apply extension changes + await page.waitFor(2000); +}; + +const openPopUp = async page => { + await page.goto(POPUP_URL); +}; + +describe('Throttling extension', function () { + describe('Throttled results', function () { + it('should be slower', async function () { + this.timeout(TEST_TIMEOUT); + + const defaultTimings = await getDefaultTimings(); + const throttlingTimings = await getThrottledTimings(); + + assert(throttlingTimings.responseEnd > defaultTimings.responseEnd); + assert(throttlingTimings.domInteractive > defaultTimings.domInteractive); + assert(throttlingTimings.domContentLoadedEventEnd > defaultTimings.domContentLoadedEventEnd); + assert(throttlingTimings.loadEventEnd > defaultTimings.loadEventEnd); + }); + + // @todo add test when throttling applied but new tab was opened + }); + + describe('Popup state', function () { + let browser; + + beforeEach(async function () { + browser = await launchBrowserWithExtension(); + }); + + afterEach(async function () { + await browser.close(); + }); + + it('should not have enabled throttling for all tabs', async function () { + this.timeout(TEST_TIMEOUT); + + const page = await browser.newPage(); + await openPopUp(page); + await page.waitFor(1000); + + const checkboxChecked = await page.evaluate(() => document.querySelector('.js-apply-to-all-tabs').checked); + assert(!checkboxChecked); + }); + + it('should save stored popup state', async function () { + this.timeout(TEST_TIMEOUT); + + let page = await browser.newPage(); + await openPopUp(page); + await enableThrottlingForAllTabs(page); + await page.close(); + + page = await browser.newPage(); + await openPopUp(page); + await page.waitFor(1000); + + const checkboxChecked = await page.evaluate(() => document.querySelector('.js-apply-to-all-tabs').checked); + assert(checkboxChecked); + }); + }); + + describe('Apply throttling', function () { + let browser; + + beforeEach(async function () { + browser = await launchBrowserWithExtension(); + }); + + afterEach(async function () { + await browser.close(); + }); + + it('should be enabled', async function () { + this.timeout(TEST_TIMEOUT); + + let page = await browser.newPage(); + await openPopUp(page); + + const disabledButton = await page.evaluate(() => document.querySelector('.js-enable-throttling').disabled); + assert(!disabledButton); + }); + + it('should be disabled if throttling applied', async function () { + this.timeout(TEST_TIMEOUT); + + let page = await browser.newPage(); + await openPopUp(page); + await enableThrottlingForAllTabs(page); + await page.close(); + + page = await browser.newPage(); + await openPopUp(page); + + const disabledButton = await page.evaluate(() => document.querySelector('.js-enable-throttling').disabled); + assert(disabledButton); + }); + }); +});