[{"data":1,"prerenderedAt":10137},["ShallowReactive",2],{"blog-posts":3},[4,235,5172,5683,7533,8148,9164],{"id":5,"title":6,"body":7,"created":226,"description":6,"extension":227,"isVisible":228,"meta":229,"navigation":228,"path":230,"seo":231,"stem":232,"tags":233,"__hash__":234},"blog\u002Fblog\u002Fdinosaurs-in-it.md","Dinosaurs in IT",{"type":8,"value":9,"toc":219},"minimark",[10,15,19,22,25,28,39,43,46,49,52,80,103,106,118,125,128,131,146,153,160,172,176,179,203,207,213,216],[11,12,14],"h2",{"id":13},"preface","Preface",[16,17,18],"p",{},"As an IT developer I have seen several monsters at work. These monsters were applications,\ndatabases, projects and sometimes … yes… people!",[16,20,21],{},"Today I am going to describe a kind of people who were actually the \"dinosaurs\" in our\ndevelopment teams.",[16,23,24],{},"The only difference between them and the real ones is that dinosaurs were victims of a\nglobal catastrophe. The IT dinosaurs are a catastrophe themselves.",[16,26,27],{},"Are you a fresh padawan in IT? Just relax and hear some advice from an\nold developer:",[29,30,35],"pre",{"className":31,"code":33,"language":34},[32],"language-text","Never become an IT dinosaur!\n","text",[36,37,33],"code",{"__ignoreMap":38},"",[11,40,42],{"id":41},"beware-of-the-project-tyrannomanagerus","Beware of the project tyrannomanagerus 🦖",[16,44,45],{},"This is a dangerous species of colleagues lurking in many IT companies. These\nkind of dinosaurs do have something more lethal than big teeth and appetite.\nThey have a title they feel proud of: IT project manager.",[16,47,48],{},"Shush! No, not all project managers are dinosaurs are bad. But the \"middle-tier\" IT\nproject manager is definitely a dinosaur of the old ways IT companies used to\nwork.",[16,50,51],{},"How can you distinguish them from the others in five simple steps?",[53,54,55,68,71,74,77],"ul",{},[56,57,58,59,63,64,67],"li",{},"He ",[60,61,62],"em",{},"feels"," and ",[60,65,66],{},"is"," a tyrant in project",[56,69,70],{},"He lives next to waterfall projects 😉",[56,72,73],{},"He considers communication between customers and developers BAD",[56,75,76],{},"His project plan is more important than deliverables",[56,78,79],{},"He finger-points responsibilities if something goes wrong",[16,81,82,83,86,87,90,91,94,95,98,99,102],{},"The key characteristic of this project manager is ",[60,84,85],{},"centralised communication",".\nAnd this is done for two reasons. First, he ",[60,88,89],{},"wants"," to control and considers\nas work ",[60,92,93],{},"controlling"," others. Second, he wants to ",[60,96,97],{},"manipulate"," information for\n",[60,100,101],{},"his benefit",".",[16,104,105],{},"If I am hard with these points, just relax and ask yourself:",[53,107,108,115],{},[56,109,110,111,114],{},"How often were delivery dates decided ",[60,112,113],{},"without"," you being asked?",[56,116,117],{},"How often requirements changed and you hear about it too late?",[16,119,120,121,124],{},"Tyrannosaurus managers ",[60,122,123],{},"know"," they should not decide without asking you.\nBut they do it nevertheless and since they control information they manipulate it and\ntry to trick you.",[16,126,127],{},"A typical case: They want to satisfy customers, promise them something\nand they come back with an impossible delivery date. Typical excuse:\ncustomers decided about delivery date.",[16,129,130],{},"Yes. I have been in this position. A manager comes to you to provide urgently a non-binding estimation about a project you barely heard of. They take this estimation, cut it in half to make it \"more appealing\" to customer and a week later you hear about a delivery date for this project which was already yesterday.",[16,132,133,134,137,138,141,142,145],{},"Another big issue with them is they prioritise project planning above\nproduct value. This is actually totally against customers. Actually they even\nforget there is a ",[60,135,136],{},"product"," behind a ",[60,139,140],{},"project",". I consider this the main\nreason why these kind of people will not even try to understand ",[60,143,144],{},"agile"," ways to\nbuild software.",[16,147,148,149,152],{},"It is even sad when all people on ",[60,150,151],{},"company level"," operate project-based and forget about\nproducts. In the end, all software products will be doomed to bad support,\noutages and the constant feeling \"this is not what I ordered\". I am sorry to tell you\nbut this is the result of bad project management, not development.",[16,154,155,156,159],{},"Of course tyrannosaurus will happily finger-point you if anything goes wrong.\nIs it lack of time that forced you to patch a bug in code with a gum and a coin?\nIs the customer unsatisfied because features are missing you were not aware of? No worries, he will tell everyone\nit is ",[60,157,158],{},"your fault",". Do not forget, he controls communication and does everything to get his big fat bonus.",[16,161,162,163,166,167,171],{},"Another great example is this big scandal about manipulating gas emissions in cars. I can imagine those big managers and CEOs ",[60,164,165],{},"blaming in court"," the development team that they implemented this cheat in code ",[168,169,170],"strong",{},"without their acknowledgement",". Can developers prove them wrong? Managers controlled people and communication and abused them the perfect way.",[11,173,175],{"id":174},"countermeasures","Countermeasures 💪",[16,177,178],{},"If you feel there is nothing you can do about it and you feel too small for turning the wheel in a project, let me give you three simple tips:",[53,180,181,187,193],{},[56,182,183,186],{},[168,184,185],{},"Speak out!"," I spoke about some issues I had with a manager to my team. No comments, just facts. And guess what? All of them had the same experience. In the end with a team decision we threw him out from a project! And people say miracles never happen!",[56,188,189,192],{},[168,190,191],{},"Logs"," As a small test, simply ask your manager to forward you his requests by email. If he is hesitating about it, there is something rotten in this dinosaur.",[56,194,195,198,199,202],{},[168,196,197],{},"Persistence"," He is a project manager and you are developer. ",[168,200,201],{},"So what?"," Do you feel his position as manager is stronger? It is simply not. Companies need months or years to find good developers. HRs are paying lots of money in headhunters and ads. If you do your job right, you actually have the upper hand. If you have a different opinion about something and he plays his title as an argument, throw it back to his face. Don't let him intimidate you.",[11,204,206],{"id":205},"conclusion","Conclusion",[16,208,209,210,212],{},"These dinosaurs are far from extinction. I still find them every day in many big companies, which are trying to find themselves in the modern ",[168,211,144],{}," era. They are afraid of agile working because they think it will be their \"ice age\" and force them to extinction. They usually speak for it and work against it.",[16,214,215],{},"But a new species has evolved from the agile age: the product owners. The amazing thing when I work with them is that we have a common goal. We both want to see a product evolving and becoming better and better. If projects still exist in a company, they work only as a way to finance our common goal.",[16,217,218],{},"Hopefully one day we will only learn about Project Tyrannomanagerus in computer science history class together with COBOL and oracle. Their waterfall projects and stories will live only as storytales and legends to scare the next generation of developers.",{"title":38,"searchDepth":220,"depth":220,"links":221},2,[222,223,224,225],{"id":13,"depth":220,"text":14},{"id":41,"depth":220,"text":42},{"id":174,"depth":220,"text":175},{"id":205,"depth":220,"text":206},1646477615,"md",true,{},"\u002Fblog\u002Fdinosaurs-in-it",{"title":6,"description":6},"blog\u002Fdinosaurs-in-it",[],"A5aPHuAHHVcbifcJjhzyG0i_t3exNhr8BtDBBdyymUI",{"id":236,"title":237,"body":238,"created":5164,"description":5165,"extension":227,"isVisible":228,"meta":5166,"navigation":228,"path":5167,"seo":5168,"stem":5169,"tags":5170,"__hash__":5171},"blog\u002Fblog\u002Fjest-vue-typescript.md","Recipes for unit tests with Jest and Vue.Js",{"type":8,"value":239,"toc":5158},[240,243,260,263,266,270,273,362,365,375,379,393,396,956,959,1620,1623,1654,1667,1675,1679,1688,1691,2477,2480,3776,3779,3813,3817,3820,4292,4295,5092,5099,5107,5110,5113,5130,5133,5154],[16,241,242],{},"In this tutorial we are going to create freshly hand-made unit tests using the\nfollowing ingredients:",[53,244,245,248,251,254,257],{},[56,246,247],{},"Vue3",[56,249,250],{},"eslint",[56,252,253],{},"Jest",[56,255,256],{},"Typescript",[56,258,259],{},"Vuex",[16,261,262],{},"I am writing this guide because it is hard to find good full examples on the\ninternet using all technologies mentioned above together. I will also mention\nall parts where it took me the most time to find a solution.",[16,264,265],{},"I will also use real examples from the code I developed for my own website. I\nomit the styling, since it is not needed for this topic.",[11,267,269],{"id":268},"eslint-configuration","Eslint configuration",[16,271,272],{},"I am usually using the following rulesets in eslint:",[29,274,278],{"className":275,"code":276,"language":277,"meta":38,"style":38},"language-javascript shiki shiki-themes github-dark","module.exports = {\n  \"extends\": [\n    \"eslint:recommended\",\n    \"plugin:@typescript-eslint\u002Feslint-recommended\",\n    \"plugin:@typescript-eslint\u002Frecommended\",\n    \"plugin:vue\u002Frecommended\",\n    '@vue\u002Ftypescript'\n  ],\n}\n","javascript",[36,279,280,302,311,320,328,336,344,350,356],{"__ignoreMap":38},[281,282,285,289,292,295,299],"span",{"class":283,"line":284},"line",1,[281,286,288],{"class":287},"sDLfK","module",[281,290,102],{"class":291},"s95oV",[281,293,294],{"class":287},"exports",[281,296,298],{"class":297},"snl16"," =",[281,300,301],{"class":291}," {\n",[281,303,304,308],{"class":283,"line":220},[281,305,307],{"class":306},"sU2Wk","  \"extends\"",[281,309,310],{"class":291},": [\n",[281,312,314,317],{"class":283,"line":313},3,[281,315,316],{"class":306},"    \"eslint:recommended\"",[281,318,319],{"class":291},",\n",[281,321,323,326],{"class":283,"line":322},4,[281,324,325],{"class":306},"    \"plugin:@typescript-eslint\u002Feslint-recommended\"",[281,327,319],{"class":291},[281,329,331,334],{"class":283,"line":330},5,[281,332,333],{"class":306},"    \"plugin:@typescript-eslint\u002Frecommended\"",[281,335,319],{"class":291},[281,337,339,342],{"class":283,"line":338},6,[281,340,341],{"class":306},"    \"plugin:vue\u002Frecommended\"",[281,343,319],{"class":291},[281,345,347],{"class":283,"line":346},7,[281,348,349],{"class":306},"    '@vue\u002Ftypescript'\n",[281,351,353],{"class":283,"line":352},8,[281,354,355],{"class":291},"  ],\n",[281,357,359],{"class":283,"line":358},9,[281,360,361],{"class":291},"}\n",[16,363,364],{},"I also suggest that everyone to use them from the very beginning especially\nif you are migrating a project into Typescript. The following rules took me\nquite some time to correct in code:",[53,366,367,372],{},[56,368,369],{},[60,370,371],{},"typescript-eslint\u002Fno-explicit-any",[56,373,374],{},"_typescript-eslint\u002Fexplicit-function-return-type",[11,376,378],{"id":377},"recipe-for-stateless-components","Recipe for stateless components",[16,380,381,382,385,386,63,389,392],{},"A ",[60,383,384],{},"stateless"," component is the one that does not include any bindings or states\nfrom Vuex or other resources. Inputs and outputs are only defined as\n",[60,387,388],{},"properties",[60,390,391],{},"events"," respectively.",[16,394,395],{},"Let's assume we have the following small component:",[29,397,401],{"className":398,"code":399,"language":400,"meta":38,"style":38},"language-typescript shiki shiki-themes github-dark","\u003Cscript lang=\"ts\">\nimport {\n  defineComponent,\n} from 'vue';\n\nexport default defineComponent({\n  name: 'IconBase',\n  props: {\n    width: {\n      type: [ String, Number ],\n      default: '16px',\n    },\n    height: {\n      type: [ String, Number ],\n      default: '16px',\n    },\n    iconColor: {\n      type: String,\n      default: 'default',\n    },\n    iconName: {\n      type: String,\n      default: '',\n    },\n    inverted: {\n      type: Boolean,\n      default: false,\n    },\n  },\n  computed: {\n    iconGroupClasses(): string[] {\n      return [\n        'icon-base__group',\n        `icon-base__group--${this.iconColor}`,\n      ];\n    },\n    rootClasses(): string [] {\n      const classes = [ 'icon-base' ];\n      if (this.inverted) {\n        classes.push('icon-base--inverted');\n      }\n\n      return classes;\n    },\n  },\n})\n\u003C\u002Fscript>\n\u003Ctemplate>\n  \u003Csvg\n    xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\"\n    :width=\"width\"\n    :height=\"height\"\n    viewBox=\"0 0 512 512\"\n    role=\"presentation\"\n    :class=\"rootClasses\"\n  >\n    \u003Cg :class=\"iconGroupClasses\">\n      \u003Cslot \u002F>\n    \u003C\u002Fg>\n  \u003C\u002Fsvg>\n\u003C\u002Ftemplate>\n","typescript",[36,402,403,420,427,432,446,451,466,476,481,486,492,503,509,515,520,529,534,540,546,556,561,567,572,582,587,593,599,609,614,620,626,644,653,661,680,686,691,706,726,740,758,764,769,777,782,787,793,804,814,823,834,845,856,867,878,889,895,914,926,936,947],{"__ignoreMap":38},[281,404,405,408,411,414,417],{"class":283,"line":284},[281,406,407],{"class":297},"\u003C",[281,409,410],{"class":291},"script lang",[281,412,413],{"class":297},"=",[281,415,416],{"class":306},"\"ts\"",[281,418,419],{"class":297},">\n",[281,421,422,425],{"class":283,"line":220},[281,423,424],{"class":297},"import",[281,426,301],{"class":291},[281,428,429],{"class":283,"line":313},[281,430,431],{"class":291},"  defineComponent,\n",[281,433,434,437,440,443],{"class":283,"line":322},[281,435,436],{"class":291},"} ",[281,438,439],{"class":297},"from",[281,441,442],{"class":306}," 'vue'",[281,444,445],{"class":291},";\n",[281,447,448],{"class":283,"line":330},[281,449,450],{"emptyLinePlaceholder":228},"\n",[281,452,453,456,459,463],{"class":283,"line":338},[281,454,455],{"class":297},"export",[281,457,458],{"class":297}," default",[281,460,462],{"class":461},"svObZ"," defineComponent",[281,464,465],{"class":291},"({\n",[281,467,468,471,474],{"class":283,"line":346},[281,469,470],{"class":291},"  name: ",[281,472,473],{"class":306},"'IconBase'",[281,475,319],{"class":291},[281,477,478],{"class":283,"line":352},[281,479,480],{"class":291},"  props: {\n",[281,482,483],{"class":283,"line":358},[281,484,485],{"class":291},"    width: {\n",[281,487,489],{"class":283,"line":488},10,[281,490,491],{"class":291},"      type: [ String, Number ],\n",[281,493,495,498,501],{"class":283,"line":494},11,[281,496,497],{"class":291},"      default: ",[281,499,500],{"class":306},"'16px'",[281,502,319],{"class":291},[281,504,506],{"class":283,"line":505},12,[281,507,508],{"class":291},"    },\n",[281,510,512],{"class":283,"line":511},13,[281,513,514],{"class":291},"    height: {\n",[281,516,518],{"class":283,"line":517},14,[281,519,491],{"class":291},[281,521,523,525,527],{"class":283,"line":522},15,[281,524,497],{"class":291},[281,526,500],{"class":306},[281,528,319],{"class":291},[281,530,532],{"class":283,"line":531},16,[281,533,508],{"class":291},[281,535,537],{"class":283,"line":536},17,[281,538,539],{"class":291},"    iconColor: {\n",[281,541,543],{"class":283,"line":542},18,[281,544,545],{"class":291},"      type: String,\n",[281,547,549,551,554],{"class":283,"line":548},19,[281,550,497],{"class":291},[281,552,553],{"class":306},"'default'",[281,555,319],{"class":291},[281,557,559],{"class":283,"line":558},20,[281,560,508],{"class":291},[281,562,564],{"class":283,"line":563},21,[281,565,566],{"class":291},"    iconName: {\n",[281,568,570],{"class":283,"line":569},22,[281,571,545],{"class":291},[281,573,575,577,580],{"class":283,"line":574},23,[281,576,497],{"class":291},[281,578,579],{"class":306},"''",[281,581,319],{"class":291},[281,583,585],{"class":283,"line":584},24,[281,586,508],{"class":291},[281,588,590],{"class":283,"line":589},25,[281,591,592],{"class":291},"    inverted: {\n",[281,594,596],{"class":283,"line":595},26,[281,597,598],{"class":291},"      type: Boolean,\n",[281,600,602,604,607],{"class":283,"line":601},27,[281,603,497],{"class":291},[281,605,606],{"class":287},"false",[281,608,319],{"class":291},[281,610,612],{"class":283,"line":611},28,[281,613,508],{"class":291},[281,615,617],{"class":283,"line":616},29,[281,618,619],{"class":291},"  },\n",[281,621,623],{"class":283,"line":622},30,[281,624,625],{"class":291},"  computed: {\n",[281,627,629,632,635,638,641],{"class":283,"line":628},31,[281,630,631],{"class":461},"    iconGroupClasses",[281,633,634],{"class":291},"()",[281,636,637],{"class":297},":",[281,639,640],{"class":287}," string",[281,642,643],{"class":291},"[] {\n",[281,645,647,650],{"class":283,"line":646},32,[281,648,649],{"class":297},"      return",[281,651,652],{"class":291}," [\n",[281,654,656,659],{"class":283,"line":655},33,[281,657,658],{"class":306},"        'icon-base__group'",[281,660,319],{"class":291},[281,662,664,667,670,672,675,678],{"class":283,"line":663},34,[281,665,666],{"class":306},"        `icon-base__group--${",[281,668,669],{"class":287},"this",[281,671,102],{"class":306},[281,673,674],{"class":291},"iconColor",[281,676,677],{"class":306},"}`",[281,679,319],{"class":291},[281,681,683],{"class":283,"line":682},35,[281,684,685],{"class":291},"      ];\n",[281,687,689],{"class":283,"line":688},36,[281,690,508],{"class":291},[281,692,694,697,699,701,703],{"class":283,"line":693},37,[281,695,696],{"class":461},"    rootClasses",[281,698,634],{"class":291},[281,700,637],{"class":297},[281,702,640],{"class":287},[281,704,705],{"class":291}," [] {\n",[281,707,709,712,715,717,720,723],{"class":283,"line":708},38,[281,710,711],{"class":297},"      const",[281,713,714],{"class":287}," classes",[281,716,298],{"class":297},[281,718,719],{"class":291}," [ ",[281,721,722],{"class":306},"'icon-base'",[281,724,725],{"class":291}," ];\n",[281,727,729,732,735,737],{"class":283,"line":728},39,[281,730,731],{"class":297},"      if",[281,733,734],{"class":291}," (",[281,736,669],{"class":287},[281,738,739],{"class":291},".inverted) {\n",[281,741,743,746,749,752,755],{"class":283,"line":742},40,[281,744,745],{"class":291},"        classes.",[281,747,748],{"class":461},"push",[281,750,751],{"class":291},"(",[281,753,754],{"class":306},"'icon-base--inverted'",[281,756,757],{"class":291},");\n",[281,759,761],{"class":283,"line":760},41,[281,762,763],{"class":291},"      }\n",[281,765,767],{"class":283,"line":766},42,[281,768,450],{"emptyLinePlaceholder":228},[281,770,772,774],{"class":283,"line":771},43,[281,773,649],{"class":297},[281,775,776],{"class":291}," classes;\n",[281,778,780],{"class":283,"line":779},44,[281,781,508],{"class":291},[281,783,785],{"class":283,"line":784},45,[281,786,619],{"class":291},[281,788,790],{"class":283,"line":789},46,[281,791,792],{"class":291},"})\n",[281,794,796,799,802],{"class":283,"line":795},47,[281,797,798],{"class":297},"\u003C\u002F",[281,800,801],{"class":291},"script",[281,803,419],{"class":297},[281,805,807,809,812],{"class":283,"line":806},48,[281,808,407],{"class":291},[281,810,811],{"class":461},"template",[281,813,419],{"class":291},[281,815,817,820],{"class":283,"line":816},49,[281,818,819],{"class":297},"  \u003C",[281,821,822],{"class":291},"svg\n",[281,824,826,829,831],{"class":283,"line":825},50,[281,827,828],{"class":291},"    xmlns",[281,830,413],{"class":297},[281,832,833],{"class":306},"\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\"\n",[281,835,837,840,842],{"class":283,"line":836},51,[281,838,839],{"class":291},"    :width",[281,841,413],{"class":297},[281,843,844],{"class":306},"\"width\"\n",[281,846,848,851,853],{"class":283,"line":847},52,[281,849,850],{"class":291},"    :height",[281,852,413],{"class":297},[281,854,855],{"class":306},"\"height\"\n",[281,857,859,862,864],{"class":283,"line":858},53,[281,860,861],{"class":291},"    viewBox",[281,863,413],{"class":297},[281,865,866],{"class":306},"\"0 0 512 512\"\n",[281,868,870,873,875],{"class":283,"line":869},54,[281,871,872],{"class":291},"    role",[281,874,413],{"class":297},[281,876,877],{"class":306},"\"presentation\"\n",[281,879,881,884,886],{"class":283,"line":880},55,[281,882,883],{"class":291},"    :class",[281,885,413],{"class":297},[281,887,888],{"class":306},"\"rootClasses\"\n",[281,890,892],{"class":283,"line":891},56,[281,893,894],{"class":297},"  >\n",[281,896,898,901,904,907,909,912],{"class":283,"line":897},57,[281,899,900],{"class":297},"    \u003C",[281,902,903],{"class":461},"g",[281,905,906],{"class":291}," :class",[281,908,413],{"class":297},[281,910,911],{"class":306},"\"iconGroupClasses\"",[281,913,419],{"class":297},[281,915,917,920,923],{"class":283,"line":916},58,[281,918,919],{"class":297},"      \u003C",[281,921,922],{"class":291},"slot ",[281,924,925],{"class":297},"\u002F>\n",[281,927,929,932,934],{"class":283,"line":928},59,[281,930,931],{"class":297},"    \u003C\u002F",[281,933,903],{"class":291},[281,935,419],{"class":297},[281,937,939,942,945],{"class":283,"line":938},60,[281,940,941],{"class":297},"  \u003C\u002F",[281,943,944],{"class":291},"svg",[281,946,419],{"class":297},[281,948,950,952,954],{"class":283,"line":949},61,[281,951,798],{"class":297},[281,953,811],{"class":291},[281,955,419],{"class":297},[16,957,958],{},"For the component above I created the following unit tests:",[29,960,962],{"className":398,"code":961,"language":400,"meta":38,"style":38},"import {\n  ComponentPublicInstance,\n} from 'vue';\nimport IconBase from '@\u002Fcomponents\u002Ficons\u002FIconBase.vue';\nimport {\n  shallowMount, VueWrapper,\n} from '@vue\u002Ftest-utils';\n\nconst defaultProps = {\n  iconName: 'testName',\n  width: '20px',\n  inverted: false,\n  iconColor: 'default',\n};\n\ndescribe('IconBase', () => {\n  const createWrapper = (props= defaultProps): VueWrapper\u003CComponentPublicInstance> => shallowMount(IconBase, {\n    props,\n  });\n\n  it('loads successfully', () => {\n    const wrapper = createWrapper();\n\n    expect(\n      wrapper.find('.icon-base').exists(),\n    ).toBe(true);\n  });\n\n  it('renders parameter iconName as aria-labelledby', () => {\n    const wrapper = createWrapper();\n\n    expect(\n      wrapper.find('.icon-base').attributes('aria-labelledby'),\n    ).toEqual('testName');\n  });\n\n  it('renders width parameter as icon width', () => {\n    const wrapper = createWrapper();\n\n    expect(\n      wrapper.find('.icon-base').attributes('width'),\n    ).toEqual('20px');\n  });\n\n  it('renders group color as red', () => {\n    const wrapper = createWrapper({\n      ...defaultProps,\n      iconColor: 'red',\n    });\n\n    expect(\n      wrapper.find('.icon-base__group').classes(),\n    ).toEqual([\n      'icon-base__group',\n      'icon-base__group--red',\n    ]);\n  });\n\n  it('inverts icon when invert=true', () => {\n    const wrapper = createWrapper({\n      ...defaultProps,\n      inverted: true,\n    });\n\n    expect(\n      wrapper.find('.icon-base--inverted').exists(),\n    ).toBe(true);\n  });\n});\n",[36,963,964,970,975,985,999,1005,1010,1021,1025,1037,1047,1057,1066,1075,1080,1084,1101,1143,1148,1153,1157,1173,1188,1192,1200,1222,1237,1241,1245,1260,1272,1276,1282,1305,1318,1322,1326,1341,1353,1357,1363,1384,1396,1400,1404,1419,1431,1439,1449,1454,1458,1464,1482,1491,1498,1505,1510,1514,1518,1533,1545,1551,1561,1566,1571,1578,1596,1609,1614],{"__ignoreMap":38},[281,965,966,968],{"class":283,"line":284},[281,967,424],{"class":297},[281,969,301],{"class":291},[281,971,972],{"class":283,"line":220},[281,973,974],{"class":291},"  ComponentPublicInstance,\n",[281,976,977,979,981,983],{"class":283,"line":313},[281,978,436],{"class":291},[281,980,439],{"class":297},[281,982,442],{"class":306},[281,984,445],{"class":291},[281,986,987,989,992,994,997],{"class":283,"line":322},[281,988,424],{"class":297},[281,990,991],{"class":291}," IconBase ",[281,993,439],{"class":297},[281,995,996],{"class":306}," '@\u002Fcomponents\u002Ficons\u002FIconBase.vue'",[281,998,445],{"class":291},[281,1000,1001,1003],{"class":283,"line":330},[281,1002,424],{"class":297},[281,1004,301],{"class":291},[281,1006,1007],{"class":283,"line":338},[281,1008,1009],{"class":291},"  shallowMount, VueWrapper,\n",[281,1011,1012,1014,1016,1019],{"class":283,"line":346},[281,1013,436],{"class":291},[281,1015,439],{"class":297},[281,1017,1018],{"class":306}," '@vue\u002Ftest-utils'",[281,1020,445],{"class":291},[281,1022,1023],{"class":283,"line":352},[281,1024,450],{"emptyLinePlaceholder":228},[281,1026,1027,1030,1033,1035],{"class":283,"line":358},[281,1028,1029],{"class":297},"const",[281,1031,1032],{"class":287}," defaultProps",[281,1034,298],{"class":297},[281,1036,301],{"class":291},[281,1038,1039,1042,1045],{"class":283,"line":488},[281,1040,1041],{"class":291},"  iconName: ",[281,1043,1044],{"class":306},"'testName'",[281,1046,319],{"class":291},[281,1048,1049,1052,1055],{"class":283,"line":494},[281,1050,1051],{"class":291},"  width: ",[281,1053,1054],{"class":306},"'20px'",[281,1056,319],{"class":291},[281,1058,1059,1062,1064],{"class":283,"line":505},[281,1060,1061],{"class":291},"  inverted: ",[281,1063,606],{"class":287},[281,1065,319],{"class":291},[281,1067,1068,1071,1073],{"class":283,"line":511},[281,1069,1070],{"class":291},"  iconColor: ",[281,1072,553],{"class":306},[281,1074,319],{"class":291},[281,1076,1077],{"class":283,"line":517},[281,1078,1079],{"class":291},"};\n",[281,1081,1082],{"class":283,"line":522},[281,1083,450],{"emptyLinePlaceholder":228},[281,1085,1086,1089,1091,1093,1096,1099],{"class":283,"line":531},[281,1087,1088],{"class":461},"describe",[281,1090,751],{"class":291},[281,1092,473],{"class":306},[281,1094,1095],{"class":291},", () ",[281,1097,1098],{"class":297},"=>",[281,1100,301],{"class":291},[281,1102,1103,1106,1109,1111,1113,1117,1119,1122,1124,1127,1129,1132,1135,1137,1140],{"class":283,"line":536},[281,1104,1105],{"class":297},"  const",[281,1107,1108],{"class":461}," createWrapper",[281,1110,298],{"class":297},[281,1112,734],{"class":291},[281,1114,1116],{"class":1115},"s9osk","props",[281,1118,413],{"class":297},[281,1120,1121],{"class":291}," defaultProps)",[281,1123,637],{"class":297},[281,1125,1126],{"class":461}," VueWrapper",[281,1128,407],{"class":291},[281,1130,1131],{"class":461},"ComponentPublicInstance",[281,1133,1134],{"class":291},"> ",[281,1136,1098],{"class":297},[281,1138,1139],{"class":461}," shallowMount",[281,1141,1142],{"class":291},"(IconBase, {\n",[281,1144,1145],{"class":283,"line":542},[281,1146,1147],{"class":291},"    props,\n",[281,1149,1150],{"class":283,"line":548},[281,1151,1152],{"class":291},"  });\n",[281,1154,1155],{"class":283,"line":558},[281,1156,450],{"emptyLinePlaceholder":228},[281,1158,1159,1162,1164,1167,1169,1171],{"class":283,"line":563},[281,1160,1161],{"class":461},"  it",[281,1163,751],{"class":291},[281,1165,1166],{"class":306},"'loads successfully'",[281,1168,1095],{"class":291},[281,1170,1098],{"class":297},[281,1172,301],{"class":291},[281,1174,1175,1178,1181,1183,1185],{"class":283,"line":569},[281,1176,1177],{"class":297},"    const",[281,1179,1180],{"class":287}," wrapper",[281,1182,298],{"class":297},[281,1184,1108],{"class":461},[281,1186,1187],{"class":291},"();\n",[281,1189,1190],{"class":283,"line":574},[281,1191,450],{"emptyLinePlaceholder":228},[281,1193,1194,1197],{"class":283,"line":584},[281,1195,1196],{"class":461},"    expect",[281,1198,1199],{"class":291},"(\n",[281,1201,1202,1205,1208,1210,1213,1216,1219],{"class":283,"line":589},[281,1203,1204],{"class":291},"      wrapper.",[281,1206,1207],{"class":461},"find",[281,1209,751],{"class":291},[281,1211,1212],{"class":306},"'.icon-base'",[281,1214,1215],{"class":291},").",[281,1217,1218],{"class":461},"exists",[281,1220,1221],{"class":291},"(),\n",[281,1223,1224,1227,1230,1232,1235],{"class":283,"line":595},[281,1225,1226],{"class":291},"    ).",[281,1228,1229],{"class":461},"toBe",[281,1231,751],{"class":291},[281,1233,1234],{"class":287},"true",[281,1236,757],{"class":291},[281,1238,1239],{"class":283,"line":601},[281,1240,1152],{"class":291},[281,1242,1243],{"class":283,"line":611},[281,1244,450],{"emptyLinePlaceholder":228},[281,1246,1247,1249,1251,1254,1256,1258],{"class":283,"line":616},[281,1248,1161],{"class":461},[281,1250,751],{"class":291},[281,1252,1253],{"class":306},"'renders parameter iconName as aria-labelledby'",[281,1255,1095],{"class":291},[281,1257,1098],{"class":297},[281,1259,301],{"class":291},[281,1261,1262,1264,1266,1268,1270],{"class":283,"line":622},[281,1263,1177],{"class":297},[281,1265,1180],{"class":287},[281,1267,298],{"class":297},[281,1269,1108],{"class":461},[281,1271,1187],{"class":291},[281,1273,1274],{"class":283,"line":628},[281,1275,450],{"emptyLinePlaceholder":228},[281,1277,1278,1280],{"class":283,"line":646},[281,1279,1196],{"class":461},[281,1281,1199],{"class":291},[281,1283,1284,1286,1288,1290,1292,1294,1297,1299,1302],{"class":283,"line":655},[281,1285,1204],{"class":291},[281,1287,1207],{"class":461},[281,1289,751],{"class":291},[281,1291,1212],{"class":306},[281,1293,1215],{"class":291},[281,1295,1296],{"class":461},"attributes",[281,1298,751],{"class":291},[281,1300,1301],{"class":306},"'aria-labelledby'",[281,1303,1304],{"class":291},"),\n",[281,1306,1307,1309,1312,1314,1316],{"class":283,"line":663},[281,1308,1226],{"class":291},[281,1310,1311],{"class":461},"toEqual",[281,1313,751],{"class":291},[281,1315,1044],{"class":306},[281,1317,757],{"class":291},[281,1319,1320],{"class":283,"line":682},[281,1321,1152],{"class":291},[281,1323,1324],{"class":283,"line":688},[281,1325,450],{"emptyLinePlaceholder":228},[281,1327,1328,1330,1332,1335,1337,1339],{"class":283,"line":693},[281,1329,1161],{"class":461},[281,1331,751],{"class":291},[281,1333,1334],{"class":306},"'renders width parameter as icon width'",[281,1336,1095],{"class":291},[281,1338,1098],{"class":297},[281,1340,301],{"class":291},[281,1342,1343,1345,1347,1349,1351],{"class":283,"line":708},[281,1344,1177],{"class":297},[281,1346,1180],{"class":287},[281,1348,298],{"class":297},[281,1350,1108],{"class":461},[281,1352,1187],{"class":291},[281,1354,1355],{"class":283,"line":728},[281,1356,450],{"emptyLinePlaceholder":228},[281,1358,1359,1361],{"class":283,"line":742},[281,1360,1196],{"class":461},[281,1362,1199],{"class":291},[281,1364,1365,1367,1369,1371,1373,1375,1377,1379,1382],{"class":283,"line":760},[281,1366,1204],{"class":291},[281,1368,1207],{"class":461},[281,1370,751],{"class":291},[281,1372,1212],{"class":306},[281,1374,1215],{"class":291},[281,1376,1296],{"class":461},[281,1378,751],{"class":291},[281,1380,1381],{"class":306},"'width'",[281,1383,1304],{"class":291},[281,1385,1386,1388,1390,1392,1394],{"class":283,"line":766},[281,1387,1226],{"class":291},[281,1389,1311],{"class":461},[281,1391,751],{"class":291},[281,1393,1054],{"class":306},[281,1395,757],{"class":291},[281,1397,1398],{"class":283,"line":771},[281,1399,1152],{"class":291},[281,1401,1402],{"class":283,"line":779},[281,1403,450],{"emptyLinePlaceholder":228},[281,1405,1406,1408,1410,1413,1415,1417],{"class":283,"line":784},[281,1407,1161],{"class":461},[281,1409,751],{"class":291},[281,1411,1412],{"class":306},"'renders group color as red'",[281,1414,1095],{"class":291},[281,1416,1098],{"class":297},[281,1418,301],{"class":291},[281,1420,1421,1423,1425,1427,1429],{"class":283,"line":789},[281,1422,1177],{"class":297},[281,1424,1180],{"class":287},[281,1426,298],{"class":297},[281,1428,1108],{"class":461},[281,1430,465],{"class":291},[281,1432,1433,1436],{"class":283,"line":795},[281,1434,1435],{"class":297},"      ...",[281,1437,1438],{"class":291},"defaultProps,\n",[281,1440,1441,1444,1447],{"class":283,"line":806},[281,1442,1443],{"class":291},"      iconColor: ",[281,1445,1446],{"class":306},"'red'",[281,1448,319],{"class":291},[281,1450,1451],{"class":283,"line":816},[281,1452,1453],{"class":291},"    });\n",[281,1455,1456],{"class":283,"line":825},[281,1457,450],{"emptyLinePlaceholder":228},[281,1459,1460,1462],{"class":283,"line":836},[281,1461,1196],{"class":461},[281,1463,1199],{"class":291},[281,1465,1466,1468,1470,1472,1475,1477,1480],{"class":283,"line":847},[281,1467,1204],{"class":291},[281,1469,1207],{"class":461},[281,1471,751],{"class":291},[281,1473,1474],{"class":306},"'.icon-base__group'",[281,1476,1215],{"class":291},[281,1478,1479],{"class":461},"classes",[281,1481,1221],{"class":291},[281,1483,1484,1486,1488],{"class":283,"line":858},[281,1485,1226],{"class":291},[281,1487,1311],{"class":461},[281,1489,1490],{"class":291},"([\n",[281,1492,1493,1496],{"class":283,"line":869},[281,1494,1495],{"class":306},"      'icon-base__group'",[281,1497,319],{"class":291},[281,1499,1500,1503],{"class":283,"line":880},[281,1501,1502],{"class":306},"      'icon-base__group--red'",[281,1504,319],{"class":291},[281,1506,1507],{"class":283,"line":891},[281,1508,1509],{"class":291},"    ]);\n",[281,1511,1512],{"class":283,"line":897},[281,1513,1152],{"class":291},[281,1515,1516],{"class":283,"line":916},[281,1517,450],{"emptyLinePlaceholder":228},[281,1519,1520,1522,1524,1527,1529,1531],{"class":283,"line":928},[281,1521,1161],{"class":461},[281,1523,751],{"class":291},[281,1525,1526],{"class":306},"'inverts icon when invert=true'",[281,1528,1095],{"class":291},[281,1530,1098],{"class":297},[281,1532,301],{"class":291},[281,1534,1535,1537,1539,1541,1543],{"class":283,"line":938},[281,1536,1177],{"class":297},[281,1538,1180],{"class":287},[281,1540,298],{"class":297},[281,1542,1108],{"class":461},[281,1544,465],{"class":291},[281,1546,1547,1549],{"class":283,"line":949},[281,1548,1435],{"class":297},[281,1550,1438],{"class":291},[281,1552,1554,1557,1559],{"class":283,"line":1553},62,[281,1555,1556],{"class":291},"      inverted: ",[281,1558,1234],{"class":287},[281,1560,319],{"class":291},[281,1562,1564],{"class":283,"line":1563},63,[281,1565,1453],{"class":291},[281,1567,1569],{"class":283,"line":1568},64,[281,1570,450],{"emptyLinePlaceholder":228},[281,1572,1574,1576],{"class":283,"line":1573},65,[281,1575,1196],{"class":461},[281,1577,1199],{"class":291},[281,1579,1581,1583,1585,1587,1590,1592,1594],{"class":283,"line":1580},66,[281,1582,1204],{"class":291},[281,1584,1207],{"class":461},[281,1586,751],{"class":291},[281,1588,1589],{"class":306},"'.icon-base--inverted'",[281,1591,1215],{"class":291},[281,1593,1218],{"class":461},[281,1595,1221],{"class":291},[281,1597,1599,1601,1603,1605,1607],{"class":283,"line":1598},67,[281,1600,1226],{"class":291},[281,1602,1229],{"class":461},[281,1604,751],{"class":291},[281,1606,1234],{"class":287},[281,1608,757],{"class":291},[281,1610,1612],{"class":283,"line":1611},68,[281,1613,1152],{"class":291},[281,1615,1617],{"class":283,"line":1616},69,[281,1618,1619],{"class":291},"});\n",[16,1621,1622],{},"The most important rules that I used were the following:",[53,1624,1625,1632,1639,1646],{},[56,1626,1627,1628,1631],{},"I am trying to use only once ",[36,1629,1630],{},"expect"," in each function.",[56,1633,1634,1635,1638],{},"I always create the function ",[36,1636,1637],{},"createWrapper"," with all required inputs",[56,1640,1641,1642,1645],{},"I introduce ",[36,1643,1644],{},"defaultProps"," with the default and required values",[56,1647,1648,1649,1651,1652],{},"I use ",[36,1650,1644],{}," as the default parameter for ",[36,1653,1637],{},[16,1655,1656,1657,1660,1661,1663,1664,1666],{},"It took me some time to find ",[36,1658,1659],{},"VueWrapper\u003CComponentPublicInstance>","\nas return trype for ",[36,1662,1637],{},"function. I also introduce the\n",[36,1665,1644],{}," in order to have autocompletion, avoid DRY and type checking.",[16,1668,381,1669,1671,1672,1674],{},[36,1670,1637],{}," function is always invoked by every single test. Our goal is\nto make each test completely ",[60,1673,384],{}," this way. I make sure that all variables\nare initialized for each test, so that tests can be executed concurrently.",[11,1676,1678],{"id":1677},"recipe-for-stateful-components","Recipe for stateful components",[16,1680,381,1681,1684,1685,1687],{},[60,1682,1683],{},"stateful"," component is a component in my applications that uses ",[60,1686,259],{}," as\ninput and output.",[16,1689,1690],{},"Taking the example below:",[29,1692,1694],{"className":398,"code":1693,"language":400,"meta":38,"style":38},"\u003Cscript lang=\"ts\">\nimport {\n  defineComponent,\n} from 'vue';\n\nimport PostSummary from '@\u002Fcomponents\u002FPostSummary.vue';\nimport SpinningLoader from '@\u002Fcomponents\u002FSpinningLoader.vue';\nimport {\n  createNamespacedHelpers,\n} from 'vuex';\n\n\nimport {\n  BlogPost,\n} from '@\u002Fstore\u002Fblog\u002Ftypes';\n\nconst {\n  mapActions,\n  mapState,\n} = createNamespacedHelpers('blog');\n\n\nexport default defineComponent({\n  name: 'BlogView',\n  components: {\n    PostSummary,\n    SpinningLoader,\n  },\n  metaInfo: {\n    title: 'Blog',\n  },\n  data() {\n    return {\n      isLoading: false,\n    };\n  },\n  computed: {\n    ...mapState([\n      'posts',\n    ]),\n    visiblePostsOrderByDateDesc(): ReadonlyArray\u003CBlogPost> {\n      const visiblePosts = this.posts.filter((post: BlogPost) => post.isVisible);\n      visiblePosts.sort((one: BlogPost, another: BlogPost) => another.created - one.created);\n      return visiblePosts;\n    },\n  },\n  async created(): Promise\u003Cvoid> {\n    this.isLoading = true;\n    try {\n      await this.refreshPosts();\n    } finally {\n      this.isLoading = false;\n    }\n  },\n  methods: {\n    ...mapActions([\n      'refreshPosts',\n    ]),\n  },\n});\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv class=\"blog-view\">\n    \u003Ch1 class=\"blog-view__headline\">\n      \u003Ci class=\"icon-heart\" \u002F>\n      Blog\n    \u003C\u002Fh1>\n    \u003Cdiv\n      v-if=\"isLoading\"\n      class=\"blog-view__spinner\"\n    >\n      \u003CSpinningLoader \u002F>\n    \u003C\u002Fdiv>\n    \u003Csection\n      v-else\n      class=\"blog-view__container\"\n    >\n      \u003CPostSummary\n        v-for=\"post in visiblePostsOrderByDateDesc\"\n        :key=\"post.id\"\n        class=\"blog-view__post\"\n        :post=\"post\"\n        :title=\"post.id\"\n      \u002F>\n    \u003C\u002Fsection>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[36,1695,1696,1708,1714,1718,1728,1732,1746,1760,1766,1771,1782,1786,1790,1796,1801,1812,1816,1822,1829,1836,1852,1856,1860,1870,1879,1884,1889,1894,1898,1903,1913,1917,1925,1932,1941,1946,1950,1954,1964,1971,1976,1996,2033,2073,2080,2084,2088,2110,2125,2132,2146,2156,2170,2175,2179,2184,2193,2200,2204,2208,2212,2220,2224,2232,2246,2260,2275,2280,2289,2296,2308,2319,2325,2335,2345,2353,2361,2371,2376,2384,2400,2411,2422,2433,2443,2449,2459,2468],{"__ignoreMap":38},[281,1697,1698,1700,1702,1704,1706],{"class":283,"line":284},[281,1699,407],{"class":297},[281,1701,410],{"class":291},[281,1703,413],{"class":297},[281,1705,416],{"class":306},[281,1707,419],{"class":297},[281,1709,1710,1712],{"class":283,"line":220},[281,1711,424],{"class":297},[281,1713,301],{"class":291},[281,1715,1716],{"class":283,"line":313},[281,1717,431],{"class":291},[281,1719,1720,1722,1724,1726],{"class":283,"line":322},[281,1721,436],{"class":291},[281,1723,439],{"class":297},[281,1725,442],{"class":306},[281,1727,445],{"class":291},[281,1729,1730],{"class":283,"line":330},[281,1731,450],{"emptyLinePlaceholder":228},[281,1733,1734,1736,1739,1741,1744],{"class":283,"line":338},[281,1735,424],{"class":297},[281,1737,1738],{"class":291}," PostSummary ",[281,1740,439],{"class":297},[281,1742,1743],{"class":306}," '@\u002Fcomponents\u002FPostSummary.vue'",[281,1745,445],{"class":291},[281,1747,1748,1750,1753,1755,1758],{"class":283,"line":346},[281,1749,424],{"class":297},[281,1751,1752],{"class":291}," SpinningLoader ",[281,1754,439],{"class":297},[281,1756,1757],{"class":306}," '@\u002Fcomponents\u002FSpinningLoader.vue'",[281,1759,445],{"class":291},[281,1761,1762,1764],{"class":283,"line":352},[281,1763,424],{"class":297},[281,1765,301],{"class":291},[281,1767,1768],{"class":283,"line":358},[281,1769,1770],{"class":291},"  createNamespacedHelpers,\n",[281,1772,1773,1775,1777,1780],{"class":283,"line":488},[281,1774,436],{"class":291},[281,1776,439],{"class":297},[281,1778,1779],{"class":306}," 'vuex'",[281,1781,445],{"class":291},[281,1783,1784],{"class":283,"line":494},[281,1785,450],{"emptyLinePlaceholder":228},[281,1787,1788],{"class":283,"line":505},[281,1789,450],{"emptyLinePlaceholder":228},[281,1791,1792,1794],{"class":283,"line":511},[281,1793,424],{"class":297},[281,1795,301],{"class":291},[281,1797,1798],{"class":283,"line":517},[281,1799,1800],{"class":291},"  BlogPost,\n",[281,1802,1803,1805,1807,1810],{"class":283,"line":522},[281,1804,436],{"class":291},[281,1806,439],{"class":297},[281,1808,1809],{"class":306}," '@\u002Fstore\u002Fblog\u002Ftypes'",[281,1811,445],{"class":291},[281,1813,1814],{"class":283,"line":531},[281,1815,450],{"emptyLinePlaceholder":228},[281,1817,1818,1820],{"class":283,"line":536},[281,1819,1029],{"class":297},[281,1821,301],{"class":291},[281,1823,1824,1827],{"class":283,"line":542},[281,1825,1826],{"class":287},"  mapActions",[281,1828,319],{"class":291},[281,1830,1831,1834],{"class":283,"line":548},[281,1832,1833],{"class":287},"  mapState",[281,1835,319],{"class":291},[281,1837,1838,1840,1842,1845,1847,1850],{"class":283,"line":558},[281,1839,436],{"class":291},[281,1841,413],{"class":297},[281,1843,1844],{"class":461}," createNamespacedHelpers",[281,1846,751],{"class":291},[281,1848,1849],{"class":306},"'blog'",[281,1851,757],{"class":291},[281,1853,1854],{"class":283,"line":563},[281,1855,450],{"emptyLinePlaceholder":228},[281,1857,1858],{"class":283,"line":569},[281,1859,450],{"emptyLinePlaceholder":228},[281,1861,1862,1864,1866,1868],{"class":283,"line":574},[281,1863,455],{"class":297},[281,1865,458],{"class":297},[281,1867,462],{"class":461},[281,1869,465],{"class":291},[281,1871,1872,1874,1877],{"class":283,"line":584},[281,1873,470],{"class":291},[281,1875,1876],{"class":306},"'BlogView'",[281,1878,319],{"class":291},[281,1880,1881],{"class":283,"line":589},[281,1882,1883],{"class":291},"  components: {\n",[281,1885,1886],{"class":283,"line":595},[281,1887,1888],{"class":291},"    PostSummary,\n",[281,1890,1891],{"class":283,"line":601},[281,1892,1893],{"class":291},"    SpinningLoader,\n",[281,1895,1896],{"class":283,"line":611},[281,1897,619],{"class":291},[281,1899,1900],{"class":283,"line":616},[281,1901,1902],{"class":291},"  metaInfo: {\n",[281,1904,1905,1908,1911],{"class":283,"line":622},[281,1906,1907],{"class":291},"    title: ",[281,1909,1910],{"class":306},"'Blog'",[281,1912,319],{"class":291},[281,1914,1915],{"class":283,"line":628},[281,1916,619],{"class":291},[281,1918,1919,1922],{"class":283,"line":646},[281,1920,1921],{"class":461},"  data",[281,1923,1924],{"class":291},"() {\n",[281,1926,1927,1930],{"class":283,"line":655},[281,1928,1929],{"class":297},"    return",[281,1931,301],{"class":291},[281,1933,1934,1937,1939],{"class":283,"line":663},[281,1935,1936],{"class":291},"      isLoading: ",[281,1938,606],{"class":287},[281,1940,319],{"class":291},[281,1942,1943],{"class":283,"line":682},[281,1944,1945],{"class":291},"    };\n",[281,1947,1948],{"class":283,"line":688},[281,1949,619],{"class":291},[281,1951,1952],{"class":283,"line":693},[281,1953,625],{"class":291},[281,1955,1956,1959,1962],{"class":283,"line":708},[281,1957,1958],{"class":297},"    ...",[281,1960,1961],{"class":461},"mapState",[281,1963,1490],{"class":291},[281,1965,1966,1969],{"class":283,"line":728},[281,1967,1968],{"class":306},"      'posts'",[281,1970,319],{"class":291},[281,1972,1973],{"class":283,"line":742},[281,1974,1975],{"class":291},"    ]),\n",[281,1977,1978,1981,1983,1985,1988,1990,1993],{"class":283,"line":760},[281,1979,1980],{"class":461},"    visiblePostsOrderByDateDesc",[281,1982,634],{"class":291},[281,1984,637],{"class":297},[281,1986,1987],{"class":461}," ReadonlyArray",[281,1989,407],{"class":291},[281,1991,1992],{"class":461},"BlogPost",[281,1994,1995],{"class":291},"> {\n",[281,1997,1998,2000,2003,2005,2008,2011,2014,2017,2020,2022,2025,2028,2030],{"class":283,"line":766},[281,1999,711],{"class":297},[281,2001,2002],{"class":287}," visiblePosts",[281,2004,298],{"class":297},[281,2006,2007],{"class":287}," this",[281,2009,2010],{"class":291},".posts.",[281,2012,2013],{"class":461},"filter",[281,2015,2016],{"class":291},"((",[281,2018,2019],{"class":1115},"post",[281,2021,637],{"class":297},[281,2023,2024],{"class":461}," BlogPost",[281,2026,2027],{"class":291},") ",[281,2029,1098],{"class":297},[281,2031,2032],{"class":291}," post.isVisible);\n",[281,2034,2035,2038,2041,2043,2046,2048,2050,2053,2056,2058,2060,2062,2064,2067,2070],{"class":283,"line":771},[281,2036,2037],{"class":291},"      visiblePosts.",[281,2039,2040],{"class":461},"sort",[281,2042,2016],{"class":291},[281,2044,2045],{"class":1115},"one",[281,2047,637],{"class":297},[281,2049,2024],{"class":461},[281,2051,2052],{"class":291},", ",[281,2054,2055],{"class":1115},"another",[281,2057,637],{"class":297},[281,2059,2024],{"class":461},[281,2061,2027],{"class":291},[281,2063,1098],{"class":297},[281,2065,2066],{"class":291}," another.created ",[281,2068,2069],{"class":297},"-",[281,2071,2072],{"class":291}," one.created);\n",[281,2074,2075,2077],{"class":283,"line":779},[281,2076,649],{"class":297},[281,2078,2079],{"class":291}," visiblePosts;\n",[281,2081,2082],{"class":283,"line":784},[281,2083,508],{"class":291},[281,2085,2086],{"class":283,"line":789},[281,2087,619],{"class":291},[281,2089,2090,2093,2096,2098,2100,2103,2105,2108],{"class":283,"line":795},[281,2091,2092],{"class":297},"  async",[281,2094,2095],{"class":461}," created",[281,2097,634],{"class":291},[281,2099,637],{"class":297},[281,2101,2102],{"class":461}," Promise",[281,2104,407],{"class":291},[281,2106,2107],{"class":287},"void",[281,2109,1995],{"class":291},[281,2111,2112,2115,2118,2120,2123],{"class":283,"line":806},[281,2113,2114],{"class":287},"    this",[281,2116,2117],{"class":291},".isLoading ",[281,2119,413],{"class":297},[281,2121,2122],{"class":287}," true",[281,2124,445],{"class":291},[281,2126,2127,2130],{"class":283,"line":816},[281,2128,2129],{"class":297},"    try",[281,2131,301],{"class":291},[281,2133,2134,2137,2139,2141,2144],{"class":283,"line":825},[281,2135,2136],{"class":297},"      await",[281,2138,2007],{"class":287},[281,2140,102],{"class":291},[281,2142,2143],{"class":461},"refreshPosts",[281,2145,1187],{"class":291},[281,2147,2148,2151,2154],{"class":283,"line":836},[281,2149,2150],{"class":291},"    } ",[281,2152,2153],{"class":297},"finally",[281,2155,301],{"class":291},[281,2157,2158,2161,2163,2165,2168],{"class":283,"line":847},[281,2159,2160],{"class":287},"      this",[281,2162,2117],{"class":291},[281,2164,413],{"class":297},[281,2166,2167],{"class":287}," false",[281,2169,445],{"class":291},[281,2171,2172],{"class":283,"line":858},[281,2173,2174],{"class":291},"    }\n",[281,2176,2177],{"class":283,"line":869},[281,2178,619],{"class":291},[281,2180,2181],{"class":283,"line":880},[281,2182,2183],{"class":291},"  methods: {\n",[281,2185,2186,2188,2191],{"class":283,"line":891},[281,2187,1958],{"class":297},[281,2189,2190],{"class":461},"mapActions",[281,2192,1490],{"class":291},[281,2194,2195,2198],{"class":283,"line":897},[281,2196,2197],{"class":306},"      'refreshPosts'",[281,2199,319],{"class":291},[281,2201,2202],{"class":283,"line":916},[281,2203,1975],{"class":291},[281,2205,2206],{"class":283,"line":928},[281,2207,619],{"class":291},[281,2209,2210],{"class":283,"line":938},[281,2211,1619],{"class":291},[281,2213,2214,2216,2218],{"class":283,"line":949},[281,2215,798],{"class":297},[281,2217,801],{"class":291},[281,2219,419],{"class":297},[281,2221,2222],{"class":283,"line":1553},[281,2223,450],{"emptyLinePlaceholder":228},[281,2225,2226,2228,2230],{"class":283,"line":1563},[281,2227,407],{"class":291},[281,2229,811],{"class":461},[281,2231,419],{"class":291},[281,2233,2234,2236,2239,2241,2244],{"class":283,"line":1568},[281,2235,819],{"class":297},[281,2237,2238],{"class":291},"div class",[281,2240,413],{"class":297},[281,2242,2243],{"class":306},"\"blog-view\"",[281,2245,419],{"class":297},[281,2247,2248,2250,2253,2255,2258],{"class":283,"line":1573},[281,2249,900],{"class":297},[281,2251,2252],{"class":291},"h1 class",[281,2254,413],{"class":297},[281,2256,2257],{"class":306},"\"blog-view__headline\"",[281,2259,419],{"class":297},[281,2261,2262,2264,2267,2269,2272],{"class":283,"line":1580},[281,2263,919],{"class":297},[281,2265,2266],{"class":291},"i class",[281,2268,413],{"class":297},[281,2270,2271],{"class":306},"\"icon-heart\"",[281,2273,2274],{"class":297}," \u002F>\n",[281,2276,2277],{"class":283,"line":1598},[281,2278,2279],{"class":291},"      Blog\n",[281,2281,2282,2284,2287],{"class":283,"line":1611},[281,2283,931],{"class":297},[281,2285,2286],{"class":291},"h1",[281,2288,419],{"class":297},[281,2290,2291,2293],{"class":283,"line":1616},[281,2292,900],{"class":297},[281,2294,2295],{"class":291},"div\n",[281,2297,2299,2302,2305],{"class":283,"line":2298},70,[281,2300,2301],{"class":291},"      v",[281,2303,2304],{"class":297},"-if=",[281,2306,2307],{"class":306},"\"isLoading\"\n",[281,2309,2311,2314,2316],{"class":283,"line":2310},71,[281,2312,2313],{"class":291},"      class",[281,2315,413],{"class":297},[281,2317,2318],{"class":306},"\"blog-view__spinner\"\n",[281,2320,2322],{"class":283,"line":2321},72,[281,2323,2324],{"class":297},"    >\n",[281,2326,2328,2330,2333],{"class":283,"line":2327},73,[281,2329,919],{"class":297},[281,2331,2332],{"class":291},"SpinningLoader ",[281,2334,925],{"class":297},[281,2336,2338,2340,2343],{"class":283,"line":2337},74,[281,2339,931],{"class":297},[281,2341,2342],{"class":291},"div",[281,2344,419],{"class":297},[281,2346,2348,2350],{"class":283,"line":2347},75,[281,2349,900],{"class":297},[281,2351,2352],{"class":291},"section\n",[281,2354,2356,2358],{"class":283,"line":2355},76,[281,2357,2301],{"class":291},[281,2359,2360],{"class":297},"-else\n",[281,2362,2364,2366,2368],{"class":283,"line":2363},77,[281,2365,2313],{"class":291},[281,2367,413],{"class":297},[281,2369,2370],{"class":306},"\"blog-view__container\"\n",[281,2372,2374],{"class":283,"line":2373},78,[281,2375,2324],{"class":297},[281,2377,2379,2381],{"class":283,"line":2378},79,[281,2380,919],{"class":297},[281,2382,2383],{"class":291},"PostSummary\n",[281,2385,2387,2390,2392,2395,2397],{"class":283,"line":2386},80,[281,2388,2389],{"class":291},"        v",[281,2391,2069],{"class":297},[281,2393,2394],{"class":291},"for",[281,2396,413],{"class":297},[281,2398,2399],{"class":306},"\"post in visiblePostsOrderByDateDesc\"\n",[281,2401,2403,2406,2408],{"class":283,"line":2402},81,[281,2404,2405],{"class":291},"        :key",[281,2407,413],{"class":297},[281,2409,2410],{"class":306},"\"post.id\"\n",[281,2412,2414,2417,2419],{"class":283,"line":2413},82,[281,2415,2416],{"class":291},"        class",[281,2418,413],{"class":297},[281,2420,2421],{"class":306},"\"blog-view__post\"\n",[281,2423,2425,2428,2430],{"class":283,"line":2424},83,[281,2426,2427],{"class":291},"        :post",[281,2429,413],{"class":297},[281,2431,2432],{"class":306},"\"post\"\n",[281,2434,2436,2439,2441],{"class":283,"line":2435},84,[281,2437,2438],{"class":291},"        :title",[281,2440,413],{"class":297},[281,2442,2410],{"class":306},[281,2444,2446],{"class":283,"line":2445},85,[281,2447,2448],{"class":297},"      \u002F>\n",[281,2450,2452,2454,2457],{"class":283,"line":2451},86,[281,2453,931],{"class":297},[281,2455,2456],{"class":291},"section",[281,2458,419],{"class":297},[281,2460,2462,2464,2466],{"class":283,"line":2461},87,[281,2463,941],{"class":297},[281,2465,2342],{"class":291},[281,2467,419],{"class":297},[281,2469,2471,2473,2475],{"class":283,"line":2470},88,[281,2472,798],{"class":297},[281,2474,811],{"class":291},[281,2476,419],{"class":297},[16,2478,2479],{},"I implemented the following unit test for it:",[29,2481,2483],{"className":398,"code":2482,"language":400,"meta":38,"style":38},"import {\n  shallowMount,\n  createLocalVue,\n  Wrapper,\n} from '@vue\u002Ftest-utils';\nimport Vuex, {\n  Store,\n} from 'vuex';\nimport {\n  RootState,\n} from '@\u002Fstore\u002Ftypes';\nimport {\n  BlogPost,\n  BlogState,\n} from '@\u002Fstore\u002Fblog\u002Ftypes';\n\nimport PostSummary from '@\u002Fcomponents\u002FPostSummary.vue';\nimport SpinningLoader from '@\u002Fcomponents\u002FSpinningLoader.vue';\nimport BlogView from '@\u002Fviews\u002FBlogView.vue';\n\nconst localVue = createLocalVue();\n\nlocalVue.use(Vuex);\n\ndescribe('BlogView', () => {\n  const actions = {\n    refreshPosts: jest.fn(),\n  };\n\n  const createPost = (id = 'test'): BlogPost => ({\n    id,\n    title: `Post ${id}`,\n    created: 1613845690000,\n    isVisible: true,\n    tags: [],\n  });\n\n  const createDefaultState = (): BlogState => ({\n    post: createPost(),\n    postContent: '',\n    posts: [\n      createPost(),\n    ],\n    selectedPostId: '',\n  });\n\n  const createStore = (\n    state: BlogState = createDefaultState(),\n  ): Store\u003CRootState> => new Vuex.Store({\n    modules: {\n      blog: {\n        actions,\n        namespaced: true,\n        state: {\n          ...state,\n        },\n      },\n    },\n  });\n\n  const createWrapper = (\n    store: Store\u003CRootState> = createStore(),\n  ): Wrapper\u003CBlogView> => shallowMount(\n    BlogView,\n    {\n      localVue,\n      store,\n    },\n  );\n\n  beforeEach(() => {\n    actions.refreshPosts.mockClear();\n  });\n\n  it('should refresh posts when initialized', () => {\n    createWrapper();\n\n    expect(\n      actions.refreshPosts,\n    ).toHaveBeenCalled();\n  });\n\n  it('should render spinning loader before loading completed', () => {\n    const wrapper = createWrapper();\n\n    expect(\n      wrapper.findComponent(SpinningLoader).exists(),\n    ).toBe(true);\n  });\n\n  it('should render one visible post after loading completed', async () => {\n    const wrapper = createWrapper();\n\n    await wrapper.vm.$nextTick();\n    await wrapper.vm.$nextTick();\n\n    expect(\n      wrapper.findComponent({\n        name: 'PostSummary',\n      }).exists(),\n    ).toBe(true);\n  });\n\n  it('should render one visible post when another hidden post provided', async () => {\n    const invisiblePost = createPost('another');\n    invisiblePost.isVisible = false;\n\n    const store = createStore({\n      ...createDefaultState(),\n      posts: [\n        createPost(),\n        invisiblePost,\n      ],\n    });\n    const wrapper = createWrapper(store);\n\n    await wrapper.vm.$nextTick();\n    await wrapper.vm.$nextTick();\n\n    expect(\n      wrapper.findAllComponents(PostSummary).length,\n    ).toEqual(1);\n  });\n\n  it('should have the newest post as first', async () => {\n    const post = createPost();\n    const newestPost = createPost('another');\n    newestPost.created = post.created + 60000;\n\n    const store = createStore({\n      ...createDefaultState(),\n      posts: [\n        createPost(),\n        newestPost,\n      ],\n    });\n    const wrapper = createWrapper(store);\n\n    await wrapper.vm.$nextTick();\n    await wrapper.vm.$nextTick();\n\n    expect(\n      wrapper.findComponent(PostSummary).attributes('title'),\n    ).toEqual('another');\n  });\n});\n",[36,2484,2485,2491,2496,2501,2506,2516,2523,2528,2538,2544,2549,2560,2566,2570,2575,2585,2589,2601,2613,2627,2631,2645,2649,2660,2664,2678,2689,2699,2704,2708,2740,2745,2758,2768,2777,2782,2786,2790,2811,2821,2830,2835,2842,2847,2856,2860,2864,2876,2891,2921,2926,2931,2936,2945,2950,2958,2963,2968,2972,2976,2980,2990,3011,3033,3038,3043,3048,3053,3057,3062,3066,3078,3088,3092,3096,3111,3118,3122,3128,3133,3142,3146,3150,3165,3177,3181,3187,3201,3213,3218,3223,3245,3258,3263,3277,3288,3293,3300,3309,3320,3330,3343,3348,3353,3373,3392,3404,3409,3423,3433,3439,3447,3453,3459,3464,3478,3483,3494,3505,3510,3517,3533,3547,3552,3557,3577,3591,3609,3628,3633,3646,3655,3660,3667,3673,3678,3683,3696,3701,3712,3723,3728,3735,3753,3766,3771],{"__ignoreMap":38},[281,2486,2487,2489],{"class":283,"line":284},[281,2488,424],{"class":297},[281,2490,301],{"class":291},[281,2492,2493],{"class":283,"line":220},[281,2494,2495],{"class":291},"  shallowMount,\n",[281,2497,2498],{"class":283,"line":313},[281,2499,2500],{"class":291},"  createLocalVue,\n",[281,2502,2503],{"class":283,"line":322},[281,2504,2505],{"class":291},"  Wrapper,\n",[281,2507,2508,2510,2512,2514],{"class":283,"line":330},[281,2509,436],{"class":291},[281,2511,439],{"class":297},[281,2513,1018],{"class":306},[281,2515,445],{"class":291},[281,2517,2518,2520],{"class":283,"line":338},[281,2519,424],{"class":297},[281,2521,2522],{"class":291}," Vuex, {\n",[281,2524,2525],{"class":283,"line":346},[281,2526,2527],{"class":291},"  Store,\n",[281,2529,2530,2532,2534,2536],{"class":283,"line":352},[281,2531,436],{"class":291},[281,2533,439],{"class":297},[281,2535,1779],{"class":306},[281,2537,445],{"class":291},[281,2539,2540,2542],{"class":283,"line":358},[281,2541,424],{"class":297},[281,2543,301],{"class":291},[281,2545,2546],{"class":283,"line":488},[281,2547,2548],{"class":291},"  RootState,\n",[281,2550,2551,2553,2555,2558],{"class":283,"line":494},[281,2552,436],{"class":291},[281,2554,439],{"class":297},[281,2556,2557],{"class":306}," '@\u002Fstore\u002Ftypes'",[281,2559,445],{"class":291},[281,2561,2562,2564],{"class":283,"line":505},[281,2563,424],{"class":297},[281,2565,301],{"class":291},[281,2567,2568],{"class":283,"line":511},[281,2569,1800],{"class":291},[281,2571,2572],{"class":283,"line":517},[281,2573,2574],{"class":291},"  BlogState,\n",[281,2576,2577,2579,2581,2583],{"class":283,"line":522},[281,2578,436],{"class":291},[281,2580,439],{"class":297},[281,2582,1809],{"class":306},[281,2584,445],{"class":291},[281,2586,2587],{"class":283,"line":531},[281,2588,450],{"emptyLinePlaceholder":228},[281,2590,2591,2593,2595,2597,2599],{"class":283,"line":536},[281,2592,424],{"class":297},[281,2594,1738],{"class":291},[281,2596,439],{"class":297},[281,2598,1743],{"class":306},[281,2600,445],{"class":291},[281,2602,2603,2605,2607,2609,2611],{"class":283,"line":542},[281,2604,424],{"class":297},[281,2606,1752],{"class":291},[281,2608,439],{"class":297},[281,2610,1757],{"class":306},[281,2612,445],{"class":291},[281,2614,2615,2617,2620,2622,2625],{"class":283,"line":548},[281,2616,424],{"class":297},[281,2618,2619],{"class":291}," BlogView ",[281,2621,439],{"class":297},[281,2623,2624],{"class":306}," '@\u002Fviews\u002FBlogView.vue'",[281,2626,445],{"class":291},[281,2628,2629],{"class":283,"line":558},[281,2630,450],{"emptyLinePlaceholder":228},[281,2632,2633,2635,2638,2640,2643],{"class":283,"line":563},[281,2634,1029],{"class":297},[281,2636,2637],{"class":287}," localVue",[281,2639,298],{"class":297},[281,2641,2642],{"class":461}," createLocalVue",[281,2644,1187],{"class":291},[281,2646,2647],{"class":283,"line":569},[281,2648,450],{"emptyLinePlaceholder":228},[281,2650,2651,2654,2657],{"class":283,"line":574},[281,2652,2653],{"class":291},"localVue.",[281,2655,2656],{"class":461},"use",[281,2658,2659],{"class":291},"(Vuex);\n",[281,2661,2662],{"class":283,"line":584},[281,2663,450],{"emptyLinePlaceholder":228},[281,2665,2666,2668,2670,2672,2674,2676],{"class":283,"line":589},[281,2667,1088],{"class":461},[281,2669,751],{"class":291},[281,2671,1876],{"class":306},[281,2673,1095],{"class":291},[281,2675,1098],{"class":297},[281,2677,301],{"class":291},[281,2679,2680,2682,2685,2687],{"class":283,"line":595},[281,2681,1105],{"class":297},[281,2683,2684],{"class":287}," actions",[281,2686,298],{"class":297},[281,2688,301],{"class":291},[281,2690,2691,2694,2697],{"class":283,"line":601},[281,2692,2693],{"class":291},"    refreshPosts: jest.",[281,2695,2696],{"class":461},"fn",[281,2698,1221],{"class":291},[281,2700,2701],{"class":283,"line":611},[281,2702,2703],{"class":291},"  };\n",[281,2705,2706],{"class":283,"line":616},[281,2707,450],{"emptyLinePlaceholder":228},[281,2709,2710,2712,2715,2717,2719,2722,2724,2727,2730,2732,2734,2737],{"class":283,"line":622},[281,2711,1105],{"class":297},[281,2713,2714],{"class":461}," createPost",[281,2716,298],{"class":297},[281,2718,734],{"class":291},[281,2720,2721],{"class":1115},"id",[281,2723,298],{"class":297},[281,2725,2726],{"class":306}," 'test'",[281,2728,2729],{"class":291},")",[281,2731,637],{"class":297},[281,2733,2024],{"class":461},[281,2735,2736],{"class":297}," =>",[281,2738,2739],{"class":291}," ({\n",[281,2741,2742],{"class":283,"line":628},[281,2743,2744],{"class":291},"    id,\n",[281,2746,2747,2749,2752,2754,2756],{"class":283,"line":646},[281,2748,1907],{"class":291},[281,2750,2751],{"class":306},"`Post ${",[281,2753,2721],{"class":291},[281,2755,677],{"class":306},[281,2757,319],{"class":291},[281,2759,2760,2763,2766],{"class":283,"line":655},[281,2761,2762],{"class":291},"    created: ",[281,2764,2765],{"class":287},"1613845690000",[281,2767,319],{"class":291},[281,2769,2770,2773,2775],{"class":283,"line":663},[281,2771,2772],{"class":291},"    isVisible: ",[281,2774,1234],{"class":287},[281,2776,319],{"class":291},[281,2778,2779],{"class":283,"line":682},[281,2780,2781],{"class":291},"    tags: [],\n",[281,2783,2784],{"class":283,"line":688},[281,2785,1152],{"class":291},[281,2787,2788],{"class":283,"line":693},[281,2789,450],{"emptyLinePlaceholder":228},[281,2791,2792,2794,2797,2799,2802,2804,2807,2809],{"class":283,"line":708},[281,2793,1105],{"class":297},[281,2795,2796],{"class":461}," createDefaultState",[281,2798,298],{"class":297},[281,2800,2801],{"class":291}," ()",[281,2803,637],{"class":297},[281,2805,2806],{"class":461}," BlogState",[281,2808,2736],{"class":297},[281,2810,2739],{"class":291},[281,2812,2813,2816,2819],{"class":283,"line":728},[281,2814,2815],{"class":291},"    post: ",[281,2817,2818],{"class":461},"createPost",[281,2820,1221],{"class":291},[281,2822,2823,2826,2828],{"class":283,"line":742},[281,2824,2825],{"class":291},"    postContent: ",[281,2827,579],{"class":306},[281,2829,319],{"class":291},[281,2831,2832],{"class":283,"line":760},[281,2833,2834],{"class":291},"    posts: [\n",[281,2836,2837,2840],{"class":283,"line":766},[281,2838,2839],{"class":461},"      createPost",[281,2841,1221],{"class":291},[281,2843,2844],{"class":283,"line":771},[281,2845,2846],{"class":291},"    ],\n",[281,2848,2849,2852,2854],{"class":283,"line":779},[281,2850,2851],{"class":291},"    selectedPostId: ",[281,2853,579],{"class":306},[281,2855,319],{"class":291},[281,2857,2858],{"class":283,"line":784},[281,2859,1152],{"class":291},[281,2861,2862],{"class":283,"line":789},[281,2863,450],{"emptyLinePlaceholder":228},[281,2865,2866,2868,2871,2873],{"class":283,"line":795},[281,2867,1105],{"class":297},[281,2869,2870],{"class":461}," createStore",[281,2872,298],{"class":297},[281,2874,2875],{"class":291}," (\n",[281,2877,2878,2881,2883,2885,2887,2889],{"class":283,"line":806},[281,2879,2880],{"class":1115},"    state",[281,2882,637],{"class":297},[281,2884,2806],{"class":461},[281,2886,298],{"class":297},[281,2888,2796],{"class":461},[281,2890,1221],{"class":291},[281,2892,2893,2896,2898,2901,2903,2906,2908,2910,2913,2916,2919],{"class":283,"line":816},[281,2894,2895],{"class":291},"  )",[281,2897,637],{"class":297},[281,2899,2900],{"class":461}," Store",[281,2902,407],{"class":291},[281,2904,2905],{"class":461},"RootState",[281,2907,1134],{"class":291},[281,2909,1098],{"class":297},[281,2911,2912],{"class":297}," new",[281,2914,2915],{"class":291}," Vuex.",[281,2917,2918],{"class":461},"Store",[281,2920,465],{"class":291},[281,2922,2923],{"class":283,"line":825},[281,2924,2925],{"class":291},"    modules: {\n",[281,2927,2928],{"class":283,"line":836},[281,2929,2930],{"class":291},"      blog: {\n",[281,2932,2933],{"class":283,"line":847},[281,2934,2935],{"class":291},"        actions,\n",[281,2937,2938,2941,2943],{"class":283,"line":858},[281,2939,2940],{"class":291},"        namespaced: ",[281,2942,1234],{"class":287},[281,2944,319],{"class":291},[281,2946,2947],{"class":283,"line":869},[281,2948,2949],{"class":291},"        state: {\n",[281,2951,2952,2955],{"class":283,"line":880},[281,2953,2954],{"class":297},"          ...",[281,2956,2957],{"class":291},"state,\n",[281,2959,2960],{"class":283,"line":891},[281,2961,2962],{"class":291},"        },\n",[281,2964,2965],{"class":283,"line":897},[281,2966,2967],{"class":291},"      },\n",[281,2969,2970],{"class":283,"line":916},[281,2971,508],{"class":291},[281,2973,2974],{"class":283,"line":928},[281,2975,1152],{"class":291},[281,2977,2978],{"class":283,"line":938},[281,2979,450],{"emptyLinePlaceholder":228},[281,2981,2982,2984,2986,2988],{"class":283,"line":949},[281,2983,1105],{"class":297},[281,2985,1108],{"class":461},[281,2987,298],{"class":297},[281,2989,2875],{"class":291},[281,2991,2992,2995,2997,2999,3001,3003,3005,3007,3009],{"class":283,"line":1553},[281,2993,2994],{"class":1115},"    store",[281,2996,637],{"class":297},[281,2998,2900],{"class":461},[281,3000,407],{"class":291},[281,3002,2905],{"class":461},[281,3004,1134],{"class":291},[281,3006,413],{"class":297},[281,3008,2870],{"class":461},[281,3010,1221],{"class":291},[281,3012,3013,3015,3017,3020,3022,3025,3027,3029,3031],{"class":283,"line":1563},[281,3014,2895],{"class":291},[281,3016,637],{"class":297},[281,3018,3019],{"class":461}," Wrapper",[281,3021,407],{"class":291},[281,3023,3024],{"class":461},"BlogView",[281,3026,1134],{"class":291},[281,3028,1098],{"class":297},[281,3030,1139],{"class":461},[281,3032,1199],{"class":291},[281,3034,3035],{"class":283,"line":1568},[281,3036,3037],{"class":291},"    BlogView,\n",[281,3039,3040],{"class":283,"line":1573},[281,3041,3042],{"class":291},"    {\n",[281,3044,3045],{"class":283,"line":1580},[281,3046,3047],{"class":291},"      localVue,\n",[281,3049,3050],{"class":283,"line":1598},[281,3051,3052],{"class":291},"      store,\n",[281,3054,3055],{"class":283,"line":1611},[281,3056,508],{"class":291},[281,3058,3059],{"class":283,"line":1616},[281,3060,3061],{"class":291},"  );\n",[281,3063,3064],{"class":283,"line":2298},[281,3065,450],{"emptyLinePlaceholder":228},[281,3067,3068,3071,3074,3076],{"class":283,"line":2310},[281,3069,3070],{"class":461},"  beforeEach",[281,3072,3073],{"class":291},"(() ",[281,3075,1098],{"class":297},[281,3077,301],{"class":291},[281,3079,3080,3083,3086],{"class":283,"line":2321},[281,3081,3082],{"class":291},"    actions.refreshPosts.",[281,3084,3085],{"class":461},"mockClear",[281,3087,1187],{"class":291},[281,3089,3090],{"class":283,"line":2327},[281,3091,1152],{"class":291},[281,3093,3094],{"class":283,"line":2337},[281,3095,450],{"emptyLinePlaceholder":228},[281,3097,3098,3100,3102,3105,3107,3109],{"class":283,"line":2347},[281,3099,1161],{"class":461},[281,3101,751],{"class":291},[281,3103,3104],{"class":306},"'should refresh posts when initialized'",[281,3106,1095],{"class":291},[281,3108,1098],{"class":297},[281,3110,301],{"class":291},[281,3112,3113,3116],{"class":283,"line":2355},[281,3114,3115],{"class":461},"    createWrapper",[281,3117,1187],{"class":291},[281,3119,3120],{"class":283,"line":2363},[281,3121,450],{"emptyLinePlaceholder":228},[281,3123,3124,3126],{"class":283,"line":2373},[281,3125,1196],{"class":461},[281,3127,1199],{"class":291},[281,3129,3130],{"class":283,"line":2378},[281,3131,3132],{"class":291},"      actions.refreshPosts,\n",[281,3134,3135,3137,3140],{"class":283,"line":2386},[281,3136,1226],{"class":291},[281,3138,3139],{"class":461},"toHaveBeenCalled",[281,3141,1187],{"class":291},[281,3143,3144],{"class":283,"line":2402},[281,3145,1152],{"class":291},[281,3147,3148],{"class":283,"line":2413},[281,3149,450],{"emptyLinePlaceholder":228},[281,3151,3152,3154,3156,3159,3161,3163],{"class":283,"line":2424},[281,3153,1161],{"class":461},[281,3155,751],{"class":291},[281,3157,3158],{"class":306},"'should render spinning loader before loading completed'",[281,3160,1095],{"class":291},[281,3162,1098],{"class":297},[281,3164,301],{"class":291},[281,3166,3167,3169,3171,3173,3175],{"class":283,"line":2435},[281,3168,1177],{"class":297},[281,3170,1180],{"class":287},[281,3172,298],{"class":297},[281,3174,1108],{"class":461},[281,3176,1187],{"class":291},[281,3178,3179],{"class":283,"line":2445},[281,3180,450],{"emptyLinePlaceholder":228},[281,3182,3183,3185],{"class":283,"line":2451},[281,3184,1196],{"class":461},[281,3186,1199],{"class":291},[281,3188,3189,3191,3194,3197,3199],{"class":283,"line":2461},[281,3190,1204],{"class":291},[281,3192,3193],{"class":461},"findComponent",[281,3195,3196],{"class":291},"(SpinningLoader).",[281,3198,1218],{"class":461},[281,3200,1221],{"class":291},[281,3202,3203,3205,3207,3209,3211],{"class":283,"line":2470},[281,3204,1226],{"class":291},[281,3206,1229],{"class":461},[281,3208,751],{"class":291},[281,3210,1234],{"class":287},[281,3212,757],{"class":291},[281,3214,3216],{"class":283,"line":3215},89,[281,3217,1152],{"class":291},[281,3219,3221],{"class":283,"line":3220},90,[281,3222,450],{"emptyLinePlaceholder":228},[281,3224,3226,3228,3230,3233,3235,3238,3241,3243],{"class":283,"line":3225},91,[281,3227,1161],{"class":461},[281,3229,751],{"class":291},[281,3231,3232],{"class":306},"'should render one visible post after loading completed'",[281,3234,2052],{"class":291},[281,3236,3237],{"class":297},"async",[281,3239,3240],{"class":291}," () ",[281,3242,1098],{"class":297},[281,3244,301],{"class":291},[281,3246,3248,3250,3252,3254,3256],{"class":283,"line":3247},92,[281,3249,1177],{"class":297},[281,3251,1180],{"class":287},[281,3253,298],{"class":297},[281,3255,1108],{"class":461},[281,3257,1187],{"class":291},[281,3259,3261],{"class":283,"line":3260},93,[281,3262,450],{"emptyLinePlaceholder":228},[281,3264,3266,3269,3272,3275],{"class":283,"line":3265},94,[281,3267,3268],{"class":297},"    await",[281,3270,3271],{"class":291}," wrapper.vm.",[281,3273,3274],{"class":461},"$nextTick",[281,3276,1187],{"class":291},[281,3278,3280,3282,3284,3286],{"class":283,"line":3279},95,[281,3281,3268],{"class":297},[281,3283,3271],{"class":291},[281,3285,3274],{"class":461},[281,3287,1187],{"class":291},[281,3289,3291],{"class":283,"line":3290},96,[281,3292,450],{"emptyLinePlaceholder":228},[281,3294,3296,3298],{"class":283,"line":3295},97,[281,3297,1196],{"class":461},[281,3299,1199],{"class":291},[281,3301,3303,3305,3307],{"class":283,"line":3302},98,[281,3304,1204],{"class":291},[281,3306,3193],{"class":461},[281,3308,465],{"class":291},[281,3310,3312,3315,3318],{"class":283,"line":3311},99,[281,3313,3314],{"class":291},"        name: ",[281,3316,3317],{"class":306},"'PostSummary'",[281,3319,319],{"class":291},[281,3321,3323,3326,3328],{"class":283,"line":3322},100,[281,3324,3325],{"class":291},"      }).",[281,3327,1218],{"class":461},[281,3329,1221],{"class":291},[281,3331,3333,3335,3337,3339,3341],{"class":283,"line":3332},101,[281,3334,1226],{"class":291},[281,3336,1229],{"class":461},[281,3338,751],{"class":291},[281,3340,1234],{"class":287},[281,3342,757],{"class":291},[281,3344,3346],{"class":283,"line":3345},102,[281,3347,1152],{"class":291},[281,3349,3351],{"class":283,"line":3350},103,[281,3352,450],{"emptyLinePlaceholder":228},[281,3354,3356,3358,3360,3363,3365,3367,3369,3371],{"class":283,"line":3355},104,[281,3357,1161],{"class":461},[281,3359,751],{"class":291},[281,3361,3362],{"class":306},"'should render one visible post when another hidden post provided'",[281,3364,2052],{"class":291},[281,3366,3237],{"class":297},[281,3368,3240],{"class":291},[281,3370,1098],{"class":297},[281,3372,301],{"class":291},[281,3374,3376,3378,3381,3383,3385,3387,3390],{"class":283,"line":3375},105,[281,3377,1177],{"class":297},[281,3379,3380],{"class":287}," invisiblePost",[281,3382,298],{"class":297},[281,3384,2714],{"class":461},[281,3386,751],{"class":291},[281,3388,3389],{"class":306},"'another'",[281,3391,757],{"class":291},[281,3393,3395,3398,3400,3402],{"class":283,"line":3394},106,[281,3396,3397],{"class":291},"    invisiblePost.isVisible ",[281,3399,413],{"class":297},[281,3401,2167],{"class":287},[281,3403,445],{"class":291},[281,3405,3407],{"class":283,"line":3406},107,[281,3408,450],{"emptyLinePlaceholder":228},[281,3410,3412,3414,3417,3419,3421],{"class":283,"line":3411},108,[281,3413,1177],{"class":297},[281,3415,3416],{"class":287}," store",[281,3418,298],{"class":297},[281,3420,2870],{"class":461},[281,3422,465],{"class":291},[281,3424,3426,3428,3431],{"class":283,"line":3425},109,[281,3427,1435],{"class":297},[281,3429,3430],{"class":461},"createDefaultState",[281,3432,1221],{"class":291},[281,3434,3436],{"class":283,"line":3435},110,[281,3437,3438],{"class":291},"      posts: [\n",[281,3440,3442,3445],{"class":283,"line":3441},111,[281,3443,3444],{"class":461},"        createPost",[281,3446,1221],{"class":291},[281,3448,3450],{"class":283,"line":3449},112,[281,3451,3452],{"class":291},"        invisiblePost,\n",[281,3454,3456],{"class":283,"line":3455},113,[281,3457,3458],{"class":291},"      ],\n",[281,3460,3462],{"class":283,"line":3461},114,[281,3463,1453],{"class":291},[281,3465,3467,3469,3471,3473,3475],{"class":283,"line":3466},115,[281,3468,1177],{"class":297},[281,3470,1180],{"class":287},[281,3472,298],{"class":297},[281,3474,1108],{"class":461},[281,3476,3477],{"class":291},"(store);\n",[281,3479,3481],{"class":283,"line":3480},116,[281,3482,450],{"emptyLinePlaceholder":228},[281,3484,3486,3488,3490,3492],{"class":283,"line":3485},117,[281,3487,3268],{"class":297},[281,3489,3271],{"class":291},[281,3491,3274],{"class":461},[281,3493,1187],{"class":291},[281,3495,3497,3499,3501,3503],{"class":283,"line":3496},118,[281,3498,3268],{"class":297},[281,3500,3271],{"class":291},[281,3502,3274],{"class":461},[281,3504,1187],{"class":291},[281,3506,3508],{"class":283,"line":3507},119,[281,3509,450],{"emptyLinePlaceholder":228},[281,3511,3513,3515],{"class":283,"line":3512},120,[281,3514,1196],{"class":461},[281,3516,1199],{"class":291},[281,3518,3520,3522,3525,3528,3531],{"class":283,"line":3519},121,[281,3521,1204],{"class":291},[281,3523,3524],{"class":461},"findAllComponents",[281,3526,3527],{"class":291},"(PostSummary).",[281,3529,3530],{"class":287},"length",[281,3532,319],{"class":291},[281,3534,3536,3538,3540,3542,3545],{"class":283,"line":3535},122,[281,3537,1226],{"class":291},[281,3539,1311],{"class":461},[281,3541,751],{"class":291},[281,3543,3544],{"class":287},"1",[281,3546,757],{"class":291},[281,3548,3550],{"class":283,"line":3549},123,[281,3551,1152],{"class":291},[281,3553,3555],{"class":283,"line":3554},124,[281,3556,450],{"emptyLinePlaceholder":228},[281,3558,3560,3562,3564,3567,3569,3571,3573,3575],{"class":283,"line":3559},125,[281,3561,1161],{"class":461},[281,3563,751],{"class":291},[281,3565,3566],{"class":306},"'should have the newest post as first'",[281,3568,2052],{"class":291},[281,3570,3237],{"class":297},[281,3572,3240],{"class":291},[281,3574,1098],{"class":297},[281,3576,301],{"class":291},[281,3578,3580,3582,3585,3587,3589],{"class":283,"line":3579},126,[281,3581,1177],{"class":297},[281,3583,3584],{"class":287}," post",[281,3586,298],{"class":297},[281,3588,2714],{"class":461},[281,3590,1187],{"class":291},[281,3592,3594,3596,3599,3601,3603,3605,3607],{"class":283,"line":3593},127,[281,3595,1177],{"class":297},[281,3597,3598],{"class":287}," newestPost",[281,3600,298],{"class":297},[281,3602,2714],{"class":461},[281,3604,751],{"class":291},[281,3606,3389],{"class":306},[281,3608,757],{"class":291},[281,3610,3612,3615,3617,3620,3623,3626],{"class":283,"line":3611},128,[281,3613,3614],{"class":291},"    newestPost.created ",[281,3616,413],{"class":297},[281,3618,3619],{"class":291}," post.created ",[281,3621,3622],{"class":297},"+",[281,3624,3625],{"class":287}," 60000",[281,3627,445],{"class":291},[281,3629,3631],{"class":283,"line":3630},129,[281,3632,450],{"emptyLinePlaceholder":228},[281,3634,3636,3638,3640,3642,3644],{"class":283,"line":3635},130,[281,3637,1177],{"class":297},[281,3639,3416],{"class":287},[281,3641,298],{"class":297},[281,3643,2870],{"class":461},[281,3645,465],{"class":291},[281,3647,3649,3651,3653],{"class":283,"line":3648},131,[281,3650,1435],{"class":297},[281,3652,3430],{"class":461},[281,3654,1221],{"class":291},[281,3656,3658],{"class":283,"line":3657},132,[281,3659,3438],{"class":291},[281,3661,3663,3665],{"class":283,"line":3662},133,[281,3664,3444],{"class":461},[281,3666,1221],{"class":291},[281,3668,3670],{"class":283,"line":3669},134,[281,3671,3672],{"class":291},"        newestPost,\n",[281,3674,3676],{"class":283,"line":3675},135,[281,3677,3458],{"class":291},[281,3679,3681],{"class":283,"line":3680},136,[281,3682,1453],{"class":291},[281,3684,3686,3688,3690,3692,3694],{"class":283,"line":3685},137,[281,3687,1177],{"class":297},[281,3689,1180],{"class":287},[281,3691,298],{"class":297},[281,3693,1108],{"class":461},[281,3695,3477],{"class":291},[281,3697,3699],{"class":283,"line":3698},138,[281,3700,450],{"emptyLinePlaceholder":228},[281,3702,3704,3706,3708,3710],{"class":283,"line":3703},139,[281,3705,3268],{"class":297},[281,3707,3271],{"class":291},[281,3709,3274],{"class":461},[281,3711,1187],{"class":291},[281,3713,3715,3717,3719,3721],{"class":283,"line":3714},140,[281,3716,3268],{"class":297},[281,3718,3271],{"class":291},[281,3720,3274],{"class":461},[281,3722,1187],{"class":291},[281,3724,3726],{"class":283,"line":3725},141,[281,3727,450],{"emptyLinePlaceholder":228},[281,3729,3731,3733],{"class":283,"line":3730},142,[281,3732,1196],{"class":461},[281,3734,1199],{"class":291},[281,3736,3738,3740,3742,3744,3746,3748,3751],{"class":283,"line":3737},143,[281,3739,1204],{"class":291},[281,3741,3193],{"class":461},[281,3743,3527],{"class":291},[281,3745,1296],{"class":461},[281,3747,751],{"class":291},[281,3749,3750],{"class":306},"'title'",[281,3752,1304],{"class":291},[281,3754,3756,3758,3760,3762,3764],{"class":283,"line":3755},144,[281,3757,1226],{"class":291},[281,3759,1311],{"class":461},[281,3761,751],{"class":291},[281,3763,3389],{"class":306},[281,3765,757],{"class":291},[281,3767,3769],{"class":283,"line":3768},145,[281,3770,1152],{"class":291},[281,3772,3774],{"class":283,"line":3773},146,[281,3775,1619],{"class":291},[16,3777,3778],{},"Basic rules for creating this file are the following:",[53,3780,3781,3789,3796,3801,3807],{},[56,3782,3783,3784,3786,3787],{},"I only make ",[60,3785,1683],{}," view components. I prefer any dependencies to them\nto be ",[60,3788,384],{},[56,3790,3791,3792,3795],{},"I instantiate ",[36,3793,3794],{},"wrapper"," in each unit test",[56,3797,3791,3798,3795],{},[36,3799,3800],{},"store",[56,3802,3803,3804],{},"All action methods are mocked using ",[36,3805,3806],{},"jest.fn()",[56,3808,3809,3810],{},"I reset actions before each test using ",[36,3811,3812],{},"actions.refreshPosts.mockReset()",[2286,3814,3816],{"id":3815},"recipe-for-mocking-dependencies","Recipe for mocking dependencies",[16,3818,3819],{},"Another case is the need to mock a service (most usually a http service) that\nwe use inside a vue component. Let's take the example below:",[29,3821,3823],{"className":398,"code":3822,"language":400,"meta":38,"style":38},"import gdprService from '@\u002Fservices\u002FlocalStorage';\nimport {\n  defineComponent,\n} from 'vue';\n\nexport default defineComponent({\n  name: 'CookieNotification',\n  data() {\n    return {\n      isVisible: false,\n      googleAnalytics: null,\n    };\n  },\n  mounted() {\n    this.isVisible = !gdprService.isGdprAccepted();\n  },\n  methods: {\n    accept(): void {\n      gdprService.acceptGdpr();\n      this.isVisible = false;\n    },\n    deny(): void {\n      gdprService.acceptGdpr();\n      this.isVisible = false;\n    },\n  },\n});\n\u003C\u002Fscript>\n\u003Ctemplate>\n  \u003Ctransition name=\"slide-up\">\n    \u003Cdiv\n      v-if=\"isVisible\"\n      class=\"cookie-notification\"\n      qa-ref=\"cookie-notification\"\n    >\n      Hey! This site uses cookies for Google Analytics\n      \u003Cdiv class=\"cookie-notification__actions\">\n        \u003Cbutton\n          class=\"cookie-notification__button cookie-notification__button--ok\"\n          qa-ref=\"cookie-notification-button-yes\"\n          @click=\"accept\"\n        >\n          Got it!\n        \u003C\u002Fbutton>\n        \u003Cbutton\n          class=\"cookie-notification__button cookie-notification__button--no\"\n          qa-ref=\"cookie-notification-button-no\"\n          @click=\"deny\"\n        >\n          No way!\n        \u003C\u002Fbutton>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  \u003C\u002Ftransition>\n\u003C\u002Ftemplate>\n\n",[36,3824,3825,3839,3845,3849,3859,3863,3873,3882,3888,3894,3903,3913,3917,3921,3928,3948,3952,3956,3970,3980,3992,3996,4009,4017,4029,4033,4037,4041,4049,4057,4071,4077,4086,4095,4109,4113,4124,4137,4145,4155,4169,4179,4184,4192,4202,4208,4217,4230,4239,4243,4250,4258,4267,4275,4284],{"__ignoreMap":38},[281,3826,3827,3829,3832,3834,3837],{"class":283,"line":284},[281,3828,424],{"class":297},[281,3830,3831],{"class":291}," gdprService ",[281,3833,439],{"class":297},[281,3835,3836],{"class":306}," '@\u002Fservices\u002FlocalStorage'",[281,3838,445],{"class":291},[281,3840,3841,3843],{"class":283,"line":220},[281,3842,424],{"class":297},[281,3844,301],{"class":291},[281,3846,3847],{"class":283,"line":313},[281,3848,431],{"class":291},[281,3850,3851,3853,3855,3857],{"class":283,"line":322},[281,3852,436],{"class":291},[281,3854,439],{"class":297},[281,3856,442],{"class":306},[281,3858,445],{"class":291},[281,3860,3861],{"class":283,"line":330},[281,3862,450],{"emptyLinePlaceholder":228},[281,3864,3865,3867,3869,3871],{"class":283,"line":338},[281,3866,455],{"class":297},[281,3868,458],{"class":297},[281,3870,462],{"class":461},[281,3872,465],{"class":291},[281,3874,3875,3877,3880],{"class":283,"line":346},[281,3876,470],{"class":291},[281,3878,3879],{"class":306},"'CookieNotification'",[281,3881,319],{"class":291},[281,3883,3884,3886],{"class":283,"line":352},[281,3885,1921],{"class":461},[281,3887,1924],{"class":291},[281,3889,3890,3892],{"class":283,"line":358},[281,3891,1929],{"class":297},[281,3893,301],{"class":291},[281,3895,3896,3899,3901],{"class":283,"line":488},[281,3897,3898],{"class":291},"      isVisible: ",[281,3900,606],{"class":287},[281,3902,319],{"class":291},[281,3904,3905,3908,3911],{"class":283,"line":494},[281,3906,3907],{"class":291},"      googleAnalytics: ",[281,3909,3910],{"class":287},"null",[281,3912,319],{"class":291},[281,3914,3915],{"class":283,"line":505},[281,3916,1945],{"class":291},[281,3918,3919],{"class":283,"line":511},[281,3920,619],{"class":291},[281,3922,3923,3926],{"class":283,"line":517},[281,3924,3925],{"class":461},"  mounted",[281,3927,1924],{"class":291},[281,3929,3930,3932,3935,3937,3940,3943,3946],{"class":283,"line":522},[281,3931,2114],{"class":287},[281,3933,3934],{"class":291},".isVisible ",[281,3936,413],{"class":297},[281,3938,3939],{"class":297}," !",[281,3941,3942],{"class":291},"gdprService.",[281,3944,3945],{"class":461},"isGdprAccepted",[281,3947,1187],{"class":291},[281,3949,3950],{"class":283,"line":531},[281,3951,619],{"class":291},[281,3953,3954],{"class":283,"line":536},[281,3955,2183],{"class":291},[281,3957,3958,3961,3963,3965,3968],{"class":283,"line":542},[281,3959,3960],{"class":461},"    accept",[281,3962,634],{"class":291},[281,3964,637],{"class":297},[281,3966,3967],{"class":287}," void",[281,3969,301],{"class":291},[281,3971,3972,3975,3978],{"class":283,"line":548},[281,3973,3974],{"class":291},"      gdprService.",[281,3976,3977],{"class":461},"acceptGdpr",[281,3979,1187],{"class":291},[281,3981,3982,3984,3986,3988,3990],{"class":283,"line":558},[281,3983,2160],{"class":287},[281,3985,3934],{"class":291},[281,3987,413],{"class":297},[281,3989,2167],{"class":287},[281,3991,445],{"class":291},[281,3993,3994],{"class":283,"line":563},[281,3995,508],{"class":291},[281,3997,3998,4001,4003,4005,4007],{"class":283,"line":569},[281,3999,4000],{"class":461},"    deny",[281,4002,634],{"class":291},[281,4004,637],{"class":297},[281,4006,3967],{"class":287},[281,4008,301],{"class":291},[281,4010,4011,4013,4015],{"class":283,"line":574},[281,4012,3974],{"class":291},[281,4014,3977],{"class":461},[281,4016,1187],{"class":291},[281,4018,4019,4021,4023,4025,4027],{"class":283,"line":584},[281,4020,2160],{"class":287},[281,4022,3934],{"class":291},[281,4024,413],{"class":297},[281,4026,2167],{"class":287},[281,4028,445],{"class":291},[281,4030,4031],{"class":283,"line":589},[281,4032,508],{"class":291},[281,4034,4035],{"class":283,"line":595},[281,4036,619],{"class":291},[281,4038,4039],{"class":283,"line":601},[281,4040,1619],{"class":291},[281,4042,4043,4045,4047],{"class":283,"line":611},[281,4044,798],{"class":297},[281,4046,801],{"class":291},[281,4048,419],{"class":297},[281,4050,4051,4053,4055],{"class":283,"line":616},[281,4052,407],{"class":291},[281,4054,811],{"class":461},[281,4056,419],{"class":291},[281,4058,4059,4061,4064,4066,4069],{"class":283,"line":622},[281,4060,819],{"class":297},[281,4062,4063],{"class":291},"transition name",[281,4065,413],{"class":297},[281,4067,4068],{"class":306},"\"slide-up\"",[281,4070,419],{"class":297},[281,4072,4073,4075],{"class":283,"line":628},[281,4074,900],{"class":297},[281,4076,2295],{"class":291},[281,4078,4079,4081,4083],{"class":283,"line":646},[281,4080,2301],{"class":291},[281,4082,2304],{"class":297},[281,4084,4085],{"class":306},"\"isVisible\"\n",[281,4087,4088,4090,4092],{"class":283,"line":655},[281,4089,2313],{"class":291},[281,4091,413],{"class":297},[281,4093,4094],{"class":306},"\"cookie-notification\"\n",[281,4096,4097,4100,4102,4105,4107],{"class":283,"line":663},[281,4098,4099],{"class":291},"      qa",[281,4101,2069],{"class":297},[281,4103,4104],{"class":291},"ref",[281,4106,413],{"class":297},[281,4108,4094],{"class":306},[281,4110,4111],{"class":283,"line":682},[281,4112,2324],{"class":297},[281,4114,4115,4118,4121],{"class":283,"line":688},[281,4116,4117],{"class":291},"      Hey",[281,4119,4120],{"class":297},"!",[281,4122,4123],{"class":291}," This site uses cookies for Google Analytics\n",[281,4125,4126,4128,4130,4132,4135],{"class":283,"line":693},[281,4127,919],{"class":297},[281,4129,2238],{"class":291},[281,4131,413],{"class":297},[281,4133,4134],{"class":306},"\"cookie-notification__actions\"",[281,4136,419],{"class":297},[281,4138,4139,4142],{"class":283,"line":708},[281,4140,4141],{"class":297},"        \u003C",[281,4143,4144],{"class":291},"button\n",[281,4146,4147,4150,4152],{"class":283,"line":728},[281,4148,4149],{"class":291},"          class",[281,4151,413],{"class":297},[281,4153,4154],{"class":306},"\"cookie-notification__button cookie-notification__button--ok\"\n",[281,4156,4157,4160,4162,4164,4166],{"class":283,"line":742},[281,4158,4159],{"class":291},"          qa",[281,4161,2069],{"class":297},[281,4163,4104],{"class":291},[281,4165,413],{"class":297},[281,4167,4168],{"class":306},"\"cookie-notification-button-yes\"\n",[281,4170,4171,4174,4176],{"class":283,"line":760},[281,4172,4173],{"class":291},"          @click",[281,4175,413],{"class":297},[281,4177,4178],{"class":306},"\"accept\"\n",[281,4180,4181],{"class":283,"line":766},[281,4182,4183],{"class":297},"        >\n",[281,4185,4186,4189],{"class":283,"line":771},[281,4187,4188],{"class":291},"          Got it",[281,4190,4191],{"class":297},"!\n",[281,4193,4194,4197,4200],{"class":283,"line":779},[281,4195,4196],{"class":297},"        \u003C\u002F",[281,4198,4199],{"class":291},"button",[281,4201,419],{"class":297},[281,4203,4204,4206],{"class":283,"line":784},[281,4205,4141],{"class":297},[281,4207,4144],{"class":291},[281,4209,4210,4212,4214],{"class":283,"line":789},[281,4211,4149],{"class":291},[281,4213,413],{"class":297},[281,4215,4216],{"class":306},"\"cookie-notification__button cookie-notification__button--no\"\n",[281,4218,4219,4221,4223,4225,4227],{"class":283,"line":795},[281,4220,4159],{"class":291},[281,4222,2069],{"class":297},[281,4224,4104],{"class":291},[281,4226,413],{"class":297},[281,4228,4229],{"class":306},"\"cookie-notification-button-no\"\n",[281,4231,4232,4234,4236],{"class":283,"line":806},[281,4233,4173],{"class":291},[281,4235,413],{"class":297},[281,4237,4238],{"class":306},"\"deny\"\n",[281,4240,4241],{"class":283,"line":816},[281,4242,4183],{"class":297},[281,4244,4245,4248],{"class":283,"line":825},[281,4246,4247],{"class":291},"          No way",[281,4249,4191],{"class":297},[281,4251,4252,4254,4256],{"class":283,"line":836},[281,4253,4196],{"class":297},[281,4255,4199],{"class":291},[281,4257,419],{"class":297},[281,4259,4260,4263,4265],{"class":283,"line":847},[281,4261,4262],{"class":297},"      \u003C\u002F",[281,4264,2342],{"class":291},[281,4266,419],{"class":297},[281,4268,4269,4271,4273],{"class":283,"line":858},[281,4270,931],{"class":297},[281,4272,2342],{"class":291},[281,4274,419],{"class":297},[281,4276,4277,4279,4282],{"class":283,"line":869},[281,4278,941],{"class":297},[281,4280,4281],{"class":291},"transition",[281,4283,419],{"class":297},[281,4285,4286,4288,4290],{"class":283,"line":880},[281,4287,798],{"class":297},[281,4289,811],{"class":291},[281,4291,419],{"class":297},[16,4293,4294],{},"And this is the unit test I have written for this:",[29,4296,4298],{"className":398,"code":4297,"language":400,"meta":38,"style":38},"import {\n  ComponentPublicInstance,\n} from 'vue';\nimport CookieNotification from '@\u002Fcomponents\u002FCookieNotification.vue';\nimport {\n  shallowMount, VueWrapper,\n} from '@vue\u002Ftest-utils';\nimport {\n  mocked,\n} from 'ts-jest';\n\nimport gdprService from '@\u002Fservices\u002FlocalStorage';\n\njest.mock('@\u002Fservices\u002FlocalStorage');\n\ndescribe('CookieNotification', () => {\n  const $ga = {\n    enable: jest.fn(),\n    page: jest.fn(),\n    disable: jest.fn(),\n  };\n\n  const mockedGdprService = mocked(gdprService);\n\n  const getWrapper = (): VueWrapper\u003CComponentPublicInstance> => shallowMount(CookieNotification, {\n    global: {\n      mocks: {\n        $ga,\n      },\n    },\n  });\n\n  beforeEach(() => {\n    mockedGdprService.acceptGdpr.mockReset();\n    mockedGdprService.isGdprAccepted.mockReset();\n  });\n\n  it('hides the cookie notification by default', () => {\n    mockedGdprService.isGdprAccepted.mockReturnValue(true);\n    const wrapper = getWrapper();\n\n    expect(\n      wrapper.find('.cookie-notification').exists(),\n    ).toBe(false);\n  });\n\n  it('displays the cookie notification after is mounted', async () => {\n    mockedGdprService.isGdprAccepted.mockReturnValue(false);\n    const wrapper = getWrapper();\n    await wrapper.vm.$nextTick();\n    expect(mockedGdprService.isGdprAccepted).toHaveBeenCalled();\n    expect(\n      wrapper.find('.cookie-notification').exists(),\n    ).toBe(true);\n  });\n\n  it('closes the cookie notification after we click OK button', async () => {\n    mockedGdprService.isGdprAccepted.mockReturnValue(false);\n    const wrapper = getWrapper();\n\n    await wrapper.vm.$nextTick();\n\n    const button = wrapper.find('.cookie-notification__button--ok');\n\n    button.trigger('click');\n    await wrapper.vm.$nextTick();\n\n    expect(\n      wrapper.find('.cookie-notification').exists(),\n    ).toBe(false);\n  });\n\n  it('closes the cookie notification after we click NO WAY button', async () => {\n    mockedGdprService.isGdprAccepted.mockReturnValue(false);\n    const wrapper = getWrapper();\n\n    await wrapper.vm.$nextTick();\n\n    const button = wrapper.find('.cookie-notification__button--no');\n    button.trigger('click');\n\n    await wrapper.vm.$nextTick();\n\n    expect(\n      wrapper.find('.cookie-notification').exists(),\n    ).toBe(false);\n  });\n})\n",[36,4299,4300,4306,4310,4320,4334,4340,4344,4354,4360,4365,4376,4380,4392,4396,4411,4415,4429,4440,4449,4458,4467,4471,4475,4490,4494,4522,4527,4532,4537,4541,4545,4549,4553,4563,4573,4582,4586,4590,4605,4618,4630,4634,4640,4657,4669,4673,4677,4696,4708,4720,4730,4741,4747,4763,4775,4779,4783,4802,4814,4826,4830,4840,4844,4865,4869,4884,4894,4898,4904,4920,4932,4936,4940,4959,4971,4983,4987,4997,5001,5020,5032,5036,5046,5050,5056,5072,5084,5088],{"__ignoreMap":38},[281,4301,4302,4304],{"class":283,"line":284},[281,4303,424],{"class":297},[281,4305,301],{"class":291},[281,4307,4308],{"class":283,"line":220},[281,4309,974],{"class":291},[281,4311,4312,4314,4316,4318],{"class":283,"line":313},[281,4313,436],{"class":291},[281,4315,439],{"class":297},[281,4317,442],{"class":306},[281,4319,445],{"class":291},[281,4321,4322,4324,4327,4329,4332],{"class":283,"line":322},[281,4323,424],{"class":297},[281,4325,4326],{"class":291}," CookieNotification ",[281,4328,439],{"class":297},[281,4330,4331],{"class":306}," '@\u002Fcomponents\u002FCookieNotification.vue'",[281,4333,445],{"class":291},[281,4335,4336,4338],{"class":283,"line":330},[281,4337,424],{"class":297},[281,4339,301],{"class":291},[281,4341,4342],{"class":283,"line":338},[281,4343,1009],{"class":291},[281,4345,4346,4348,4350,4352],{"class":283,"line":346},[281,4347,436],{"class":291},[281,4349,439],{"class":297},[281,4351,1018],{"class":306},[281,4353,445],{"class":291},[281,4355,4356,4358],{"class":283,"line":352},[281,4357,424],{"class":297},[281,4359,301],{"class":291},[281,4361,4362],{"class":283,"line":358},[281,4363,4364],{"class":291},"  mocked,\n",[281,4366,4367,4369,4371,4374],{"class":283,"line":488},[281,4368,436],{"class":291},[281,4370,439],{"class":297},[281,4372,4373],{"class":306}," 'ts-jest'",[281,4375,445],{"class":291},[281,4377,4378],{"class":283,"line":494},[281,4379,450],{"emptyLinePlaceholder":228},[281,4381,4382,4384,4386,4388,4390],{"class":283,"line":505},[281,4383,424],{"class":297},[281,4385,3831],{"class":291},[281,4387,439],{"class":297},[281,4389,3836],{"class":306},[281,4391,445],{"class":291},[281,4393,4394],{"class":283,"line":511},[281,4395,450],{"emptyLinePlaceholder":228},[281,4397,4398,4401,4404,4406,4409],{"class":283,"line":517},[281,4399,4400],{"class":291},"jest.",[281,4402,4403],{"class":461},"mock",[281,4405,751],{"class":291},[281,4407,4408],{"class":306},"'@\u002Fservices\u002FlocalStorage'",[281,4410,757],{"class":291},[281,4412,4413],{"class":283,"line":522},[281,4414,450],{"emptyLinePlaceholder":228},[281,4416,4417,4419,4421,4423,4425,4427],{"class":283,"line":531},[281,4418,1088],{"class":461},[281,4420,751],{"class":291},[281,4422,3879],{"class":306},[281,4424,1095],{"class":291},[281,4426,1098],{"class":297},[281,4428,301],{"class":291},[281,4430,4431,4433,4436,4438],{"class":283,"line":536},[281,4432,1105],{"class":297},[281,4434,4435],{"class":287}," $ga",[281,4437,298],{"class":297},[281,4439,301],{"class":291},[281,4441,4442,4445,4447],{"class":283,"line":542},[281,4443,4444],{"class":291},"    enable: jest.",[281,4446,2696],{"class":461},[281,4448,1221],{"class":291},[281,4450,4451,4454,4456],{"class":283,"line":548},[281,4452,4453],{"class":291},"    page: jest.",[281,4455,2696],{"class":461},[281,4457,1221],{"class":291},[281,4459,4460,4463,4465],{"class":283,"line":558},[281,4461,4462],{"class":291},"    disable: jest.",[281,4464,2696],{"class":461},[281,4466,1221],{"class":291},[281,4468,4469],{"class":283,"line":563},[281,4470,2703],{"class":291},[281,4472,4473],{"class":283,"line":569},[281,4474,450],{"emptyLinePlaceholder":228},[281,4476,4477,4479,4482,4484,4487],{"class":283,"line":574},[281,4478,1105],{"class":297},[281,4480,4481],{"class":287}," mockedGdprService",[281,4483,298],{"class":297},[281,4485,4486],{"class":461}," mocked",[281,4488,4489],{"class":291},"(gdprService);\n",[281,4491,4492],{"class":283,"line":584},[281,4493,450],{"emptyLinePlaceholder":228},[281,4495,4496,4498,4501,4503,4505,4507,4509,4511,4513,4515,4517,4519],{"class":283,"line":589},[281,4497,1105],{"class":297},[281,4499,4500],{"class":461}," getWrapper",[281,4502,298],{"class":297},[281,4504,2801],{"class":291},[281,4506,637],{"class":297},[281,4508,1126],{"class":461},[281,4510,407],{"class":291},[281,4512,1131],{"class":461},[281,4514,1134],{"class":291},[281,4516,1098],{"class":297},[281,4518,1139],{"class":461},[281,4520,4521],{"class":291},"(CookieNotification, {\n",[281,4523,4524],{"class":283,"line":595},[281,4525,4526],{"class":291},"    global: {\n",[281,4528,4529],{"class":283,"line":601},[281,4530,4531],{"class":291},"      mocks: {\n",[281,4533,4534],{"class":283,"line":611},[281,4535,4536],{"class":291},"        $ga,\n",[281,4538,4539],{"class":283,"line":616},[281,4540,2967],{"class":291},[281,4542,4543],{"class":283,"line":622},[281,4544,508],{"class":291},[281,4546,4547],{"class":283,"line":628},[281,4548,1152],{"class":291},[281,4550,4551],{"class":283,"line":646},[281,4552,450],{"emptyLinePlaceholder":228},[281,4554,4555,4557,4559,4561],{"class":283,"line":655},[281,4556,3070],{"class":461},[281,4558,3073],{"class":291},[281,4560,1098],{"class":297},[281,4562,301],{"class":291},[281,4564,4565,4568,4571],{"class":283,"line":663},[281,4566,4567],{"class":291},"    mockedGdprService.acceptGdpr.",[281,4569,4570],{"class":461},"mockReset",[281,4572,1187],{"class":291},[281,4574,4575,4578,4580],{"class":283,"line":682},[281,4576,4577],{"class":291},"    mockedGdprService.isGdprAccepted.",[281,4579,4570],{"class":461},[281,4581,1187],{"class":291},[281,4583,4584],{"class":283,"line":688},[281,4585,1152],{"class":291},[281,4587,4588],{"class":283,"line":693},[281,4589,450],{"emptyLinePlaceholder":228},[281,4591,4592,4594,4596,4599,4601,4603],{"class":283,"line":708},[281,4593,1161],{"class":461},[281,4595,751],{"class":291},[281,4597,4598],{"class":306},"'hides the cookie notification by default'",[281,4600,1095],{"class":291},[281,4602,1098],{"class":297},[281,4604,301],{"class":291},[281,4606,4607,4609,4612,4614,4616],{"class":283,"line":728},[281,4608,4577],{"class":291},[281,4610,4611],{"class":461},"mockReturnValue",[281,4613,751],{"class":291},[281,4615,1234],{"class":287},[281,4617,757],{"class":291},[281,4619,4620,4622,4624,4626,4628],{"class":283,"line":742},[281,4621,1177],{"class":297},[281,4623,1180],{"class":287},[281,4625,298],{"class":297},[281,4627,4500],{"class":461},[281,4629,1187],{"class":291},[281,4631,4632],{"class":283,"line":760},[281,4633,450],{"emptyLinePlaceholder":228},[281,4635,4636,4638],{"class":283,"line":766},[281,4637,1196],{"class":461},[281,4639,1199],{"class":291},[281,4641,4642,4644,4646,4648,4651,4653,4655],{"class":283,"line":771},[281,4643,1204],{"class":291},[281,4645,1207],{"class":461},[281,4647,751],{"class":291},[281,4649,4650],{"class":306},"'.cookie-notification'",[281,4652,1215],{"class":291},[281,4654,1218],{"class":461},[281,4656,1221],{"class":291},[281,4658,4659,4661,4663,4665,4667],{"class":283,"line":779},[281,4660,1226],{"class":291},[281,4662,1229],{"class":461},[281,4664,751],{"class":291},[281,4666,606],{"class":287},[281,4668,757],{"class":291},[281,4670,4671],{"class":283,"line":784},[281,4672,1152],{"class":291},[281,4674,4675],{"class":283,"line":789},[281,4676,450],{"emptyLinePlaceholder":228},[281,4678,4679,4681,4683,4686,4688,4690,4692,4694],{"class":283,"line":795},[281,4680,1161],{"class":461},[281,4682,751],{"class":291},[281,4684,4685],{"class":306},"'displays the cookie notification after is mounted'",[281,4687,2052],{"class":291},[281,4689,3237],{"class":297},[281,4691,3240],{"class":291},[281,4693,1098],{"class":297},[281,4695,301],{"class":291},[281,4697,4698,4700,4702,4704,4706],{"class":283,"line":806},[281,4699,4577],{"class":291},[281,4701,4611],{"class":461},[281,4703,751],{"class":291},[281,4705,606],{"class":287},[281,4707,757],{"class":291},[281,4709,4710,4712,4714,4716,4718],{"class":283,"line":816},[281,4711,1177],{"class":297},[281,4713,1180],{"class":287},[281,4715,298],{"class":297},[281,4717,4500],{"class":461},[281,4719,1187],{"class":291},[281,4721,4722,4724,4726,4728],{"class":283,"line":825},[281,4723,3268],{"class":297},[281,4725,3271],{"class":291},[281,4727,3274],{"class":461},[281,4729,1187],{"class":291},[281,4731,4732,4734,4737,4739],{"class":283,"line":836},[281,4733,1196],{"class":461},[281,4735,4736],{"class":291},"(mockedGdprService.isGdprAccepted).",[281,4738,3139],{"class":461},[281,4740,1187],{"class":291},[281,4742,4743,4745],{"class":283,"line":847},[281,4744,1196],{"class":461},[281,4746,1199],{"class":291},[281,4748,4749,4751,4753,4755,4757,4759,4761],{"class":283,"line":858},[281,4750,1204],{"class":291},[281,4752,1207],{"class":461},[281,4754,751],{"class":291},[281,4756,4650],{"class":306},[281,4758,1215],{"class":291},[281,4760,1218],{"class":461},[281,4762,1221],{"class":291},[281,4764,4765,4767,4769,4771,4773],{"class":283,"line":869},[281,4766,1226],{"class":291},[281,4768,1229],{"class":461},[281,4770,751],{"class":291},[281,4772,1234],{"class":287},[281,4774,757],{"class":291},[281,4776,4777],{"class":283,"line":880},[281,4778,1152],{"class":291},[281,4780,4781],{"class":283,"line":891},[281,4782,450],{"emptyLinePlaceholder":228},[281,4784,4785,4787,4789,4792,4794,4796,4798,4800],{"class":283,"line":897},[281,4786,1161],{"class":461},[281,4788,751],{"class":291},[281,4790,4791],{"class":306},"'closes the cookie notification after we click OK button'",[281,4793,2052],{"class":291},[281,4795,3237],{"class":297},[281,4797,3240],{"class":291},[281,4799,1098],{"class":297},[281,4801,301],{"class":291},[281,4803,4804,4806,4808,4810,4812],{"class":283,"line":916},[281,4805,4577],{"class":291},[281,4807,4611],{"class":461},[281,4809,751],{"class":291},[281,4811,606],{"class":287},[281,4813,757],{"class":291},[281,4815,4816,4818,4820,4822,4824],{"class":283,"line":928},[281,4817,1177],{"class":297},[281,4819,1180],{"class":287},[281,4821,298],{"class":297},[281,4823,4500],{"class":461},[281,4825,1187],{"class":291},[281,4827,4828],{"class":283,"line":938},[281,4829,450],{"emptyLinePlaceholder":228},[281,4831,4832,4834,4836,4838],{"class":283,"line":949},[281,4833,3268],{"class":297},[281,4835,3271],{"class":291},[281,4837,3274],{"class":461},[281,4839,1187],{"class":291},[281,4841,4842],{"class":283,"line":1553},[281,4843,450],{"emptyLinePlaceholder":228},[281,4845,4846,4848,4851,4853,4856,4858,4860,4863],{"class":283,"line":1563},[281,4847,1177],{"class":297},[281,4849,4850],{"class":287}," button",[281,4852,298],{"class":297},[281,4854,4855],{"class":291}," wrapper.",[281,4857,1207],{"class":461},[281,4859,751],{"class":291},[281,4861,4862],{"class":306},"'.cookie-notification__button--ok'",[281,4864,757],{"class":291},[281,4866,4867],{"class":283,"line":1568},[281,4868,450],{"emptyLinePlaceholder":228},[281,4870,4871,4874,4877,4879,4882],{"class":283,"line":1573},[281,4872,4873],{"class":291},"    button.",[281,4875,4876],{"class":461},"trigger",[281,4878,751],{"class":291},[281,4880,4881],{"class":306},"'click'",[281,4883,757],{"class":291},[281,4885,4886,4888,4890,4892],{"class":283,"line":1580},[281,4887,3268],{"class":297},[281,4889,3271],{"class":291},[281,4891,3274],{"class":461},[281,4893,1187],{"class":291},[281,4895,4896],{"class":283,"line":1598},[281,4897,450],{"emptyLinePlaceholder":228},[281,4899,4900,4902],{"class":283,"line":1611},[281,4901,1196],{"class":461},[281,4903,1199],{"class":291},[281,4905,4906,4908,4910,4912,4914,4916,4918],{"class":283,"line":1616},[281,4907,1204],{"class":291},[281,4909,1207],{"class":461},[281,4911,751],{"class":291},[281,4913,4650],{"class":306},[281,4915,1215],{"class":291},[281,4917,1218],{"class":461},[281,4919,1221],{"class":291},[281,4921,4922,4924,4926,4928,4930],{"class":283,"line":2298},[281,4923,1226],{"class":291},[281,4925,1229],{"class":461},[281,4927,751],{"class":291},[281,4929,606],{"class":287},[281,4931,757],{"class":291},[281,4933,4934],{"class":283,"line":2310},[281,4935,1152],{"class":291},[281,4937,4938],{"class":283,"line":2321},[281,4939,450],{"emptyLinePlaceholder":228},[281,4941,4942,4944,4946,4949,4951,4953,4955,4957],{"class":283,"line":2327},[281,4943,1161],{"class":461},[281,4945,751],{"class":291},[281,4947,4948],{"class":306},"'closes the cookie notification after we click NO WAY button'",[281,4950,2052],{"class":291},[281,4952,3237],{"class":297},[281,4954,3240],{"class":291},[281,4956,1098],{"class":297},[281,4958,301],{"class":291},[281,4960,4961,4963,4965,4967,4969],{"class":283,"line":2337},[281,4962,4577],{"class":291},[281,4964,4611],{"class":461},[281,4966,751],{"class":291},[281,4968,606],{"class":287},[281,4970,757],{"class":291},[281,4972,4973,4975,4977,4979,4981],{"class":283,"line":2347},[281,4974,1177],{"class":297},[281,4976,1180],{"class":287},[281,4978,298],{"class":297},[281,4980,4500],{"class":461},[281,4982,1187],{"class":291},[281,4984,4985],{"class":283,"line":2355},[281,4986,450],{"emptyLinePlaceholder":228},[281,4988,4989,4991,4993,4995],{"class":283,"line":2363},[281,4990,3268],{"class":297},[281,4992,3271],{"class":291},[281,4994,3274],{"class":461},[281,4996,1187],{"class":291},[281,4998,4999],{"class":283,"line":2373},[281,5000,450],{"emptyLinePlaceholder":228},[281,5002,5003,5005,5007,5009,5011,5013,5015,5018],{"class":283,"line":2378},[281,5004,1177],{"class":297},[281,5006,4850],{"class":287},[281,5008,298],{"class":297},[281,5010,4855],{"class":291},[281,5012,1207],{"class":461},[281,5014,751],{"class":291},[281,5016,5017],{"class":306},"'.cookie-notification__button--no'",[281,5019,757],{"class":291},[281,5021,5022,5024,5026,5028,5030],{"class":283,"line":2386},[281,5023,4873],{"class":291},[281,5025,4876],{"class":461},[281,5027,751],{"class":291},[281,5029,4881],{"class":306},[281,5031,757],{"class":291},[281,5033,5034],{"class":283,"line":2402},[281,5035,450],{"emptyLinePlaceholder":228},[281,5037,5038,5040,5042,5044],{"class":283,"line":2413},[281,5039,3268],{"class":297},[281,5041,3271],{"class":291},[281,5043,3274],{"class":461},[281,5045,1187],{"class":291},[281,5047,5048],{"class":283,"line":2424},[281,5049,450],{"emptyLinePlaceholder":228},[281,5051,5052,5054],{"class":283,"line":2435},[281,5053,1196],{"class":461},[281,5055,1199],{"class":291},[281,5057,5058,5060,5062,5064,5066,5068,5070],{"class":283,"line":2445},[281,5059,1204],{"class":291},[281,5061,1207],{"class":461},[281,5063,751],{"class":291},[281,5065,4650],{"class":306},[281,5067,1215],{"class":291},[281,5069,1218],{"class":461},[281,5071,1221],{"class":291},[281,5073,5074,5076,5078,5080,5082],{"class":283,"line":2451},[281,5075,1226],{"class":291},[281,5077,1229],{"class":461},[281,5079,751],{"class":291},[281,5081,606],{"class":287},[281,5083,757],{"class":291},[281,5085,5086],{"class":283,"line":2461},[281,5087,1152],{"class":291},[281,5089,5090],{"class":283,"line":2470},[281,5091,792],{"class":291},[16,5093,5094,5095,5098],{},"The function ",[36,5096,5097],{},"mocked"," is actually the one that really matters here. It extends\nthe type of the injected dependency and allows you to easily work with it. It\ntook me a lot of time to find this function, but it made my code much cleaner.",[16,5100,5101,5102,63,5104,102],{},"This function is actually the main reason for writing this post, since it is\nalmost necessary for writing unit tests with ",[168,5103,400],{},[168,5105,5106],{},"jest",[11,5108,5109],{"id":5109},"challenges",[16,5111,5112],{},"The challenge I usually find when I write unit tests:",[53,5114,5115,5118,5121],{},[56,5116,5117],{},"Mocking dependency injection",[56,5119,5120],{},"Mocking custom events",[56,5122,5123,5124,5127,5128],{},"Interacting with ",[36,5125,5126],{},"wrapper.vm"," object in ",[168,5129,400],{},[16,5131,5132],{},"My quick tips there:",[53,5134,5135,5138,5145,5151],{},[56,5136,5137],{},"Dependencies shall always return functions, so I can mock them",[56,5139,5140,5141,5144],{},"Create ",[36,5142,5143],{},"mocks.ts"," file containing functions for generating necessary mock\nobjects",[56,5146,5147,5148],{},"When testing component uses other ones, refer to them using ",[36,5149,5150],{},"wrapper.findComponent()",[56,5152,5153],{},"I avoid using wrapper.vm and focus in testing DOM elements instead.",[5155,5156,5157],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":38,"searchDepth":220,"depth":220,"links":5159},[5160,5161,5162,5163],{"id":268,"depth":220,"text":269},{"id":377,"depth":220,"text":378},{"id":1677,"depth":220,"text":1678},{"id":5109,"depth":220,"text":5109},1639294022,"This is a description 5",{},"\u002Fblog\u002Fjest-vue-typescript",{"title":237,"description":5165},"blog\u002Fjest-vue-typescript",[],"NCtszn2jFtbFlXQH5y9C9Tqg4ut7Qt4vDPWEdTYmipA",{"id":5173,"title":5174,"body":5175,"created":5676,"description":5174,"extension":227,"isVisible":228,"meta":5677,"navigation":228,"path":5678,"seo":5679,"stem":5680,"tags":5681,"__hash__":5682},"blog\u002Fblog\u002Fjenkins-pipeline-for-frontend-apps.md","A complete Jenkinsfile example for frontend applications",{"type":8,"value":5176,"toc":5666},[5177,5181,5184,5187,5190,5208,5212,5215,5223,5226,5230,5237,5243,5250,5257,5260,5266,5277,5281,5284,5290,5297,5300,5306,5309,5313,5321,5328,5334,5338,5345,5348,5361,5364,5370,5373,5380,5384,5387,5394,5400,5407,5413,5416,5431,5435,5445,5663],[11,5178,5180],{"id":5179},"about","About",[16,5182,5183],{},"After working on developing the build pipelines for my company, I noticed how modern building tools can help with the automation of important tasks in frontend development.",[16,5185,5186],{},"I also migrated my personal website to use Jenkins for automatically building and deploying it. This guide explains how I was able to do it.",[16,5188,5189],{},"The technology stack I am using for my website is the following",[53,5191,5192,5195,5197,5200,5203,5205],{},[56,5193,5194],{},"Vue.js",[56,5196,256],{},[56,5198,5199],{},"NPM",[56,5201,5202],{},"Eslint",[56,5204,253],{},[56,5206,5207],{},"Cypress",[11,5209,5211],{"id":5210},"why-jenkinsfile","Why Jenkinsfile",[16,5213,5214],{},"I personally prefer to give freedom to developers and let them modify the building process as they need. Each git project also contains the Jenkinsfile in the root folder. We setup a Jenkins project as following:",[53,5216,5217,5220],{},[56,5218,5219],{},"Add a Pipeline option to job",[56,5221,5222],{},"Set \"Pipeline Script from SCM\"",[16,5224,5225],{},"I have worked in the past for projects where the building process and Jenkins were part of the admin team. As frontend developers we had difficulties to introduce new processes and steps in the pipeline and it constrained us to use new technologies we wanted.",[11,5227,5229],{"id":5228},"install-dependencies","Install dependencies",[16,5231,5232,5233,5236],{},"It is a common ",[60,5234,5235],{},"but wrong"," practice to install dependencies in CI pipelines using the following command:",[29,5238,5241],{"className":5239,"code":5240,"language":34},[32],"npm install\n",[36,5242,5240],{"__ignoreMap":38},[16,5244,5245,5246,5249],{},"This is actually wrong, since npm install ",[60,5247,5248],{},"may"," install a newer minor release for either your dependencies and\u002For sub-dependencies.",[16,5251,5252,5253,5256],{},"In a nutshell: This way it is possible that your build works on your developer laptop but it fails to compile in Jenkins, simply because ",[36,5254,5255],{},"npm install"," installed a minor sub-dependency that had a breaking change.",[16,5258,5259],{},"In order to avoid this issue simple use:",[29,5261,5264],{"className":5262,"code":5263,"language":34},[32],"npm ci\n",[36,5265,5263],{"__ignoreMap":38},[16,5267,5268,5269,5272,5273,5276],{},"and add ",[36,5270,5271],{},"package-lock.json"," in GIT. Believe me, this is a life savior! It will ",[60,5274,5275],{},"always"," install the exact package and versions listed in this file.",[11,5278,5280],{"id":5279},"audit-dependencies","Audit dependencies",[16,5282,5283],{},"There is no need for anything else than:",[29,5285,5288],{"className":5286,"code":5287,"language":34},[32],"npm audit --parseable --production\n",[36,5289,5287],{"__ignoreMap":38},[16,5291,5292,5293,5296],{},"This way we are sure that main dependencies have no security issues. I am not checking for any vulnerabilities in dev packages since they are not directly used and it is not possible at the moment to define a ",[60,5294,5295],{},"threshold"," to exclude minor issues.",[16,5298,5299],{},"I am also checking if packages are outdated by executing the command:",[29,5301,5304],{"className":5302,"code":5303,"language":34},[32],"npm outdated || exit 0\n",[36,5305,5303],{"__ignoreMap":38},[16,5307,5308],{},"This way the pipeline won't fail if the packages are outdated. It is also not possible here to define a threshold here.",[11,5310,5312],{"id":5311},"check-linting","Check linting",[16,5314,5315,5317,5318,4120],{},[60,5316,5202],{}," is my favorite option for linting and format checks, since the\nintegration with IDEs and console command is ",[60,5319,5320],{},"almost perfect",[16,5322,5323,5324,5327],{},"If you are using ",[60,5325,5326],{},"vue-cli"," to setup your project, we simply need to execute in Jenkinsfile:",[29,5329,5332],{"className":5330,"code":5331,"language":34},[32],"npm run lint\n",[36,5333,5331],{"__ignoreMap":38},[11,5335,5337],{"id":5336},"unit-testing","Unit testing",[16,5339,5340,5341,5344],{},"A unit testing ",[60,5342,5343],{},"with coverage"," is another safety lock for capturing bugs before entering\nproduction.",[16,5346,5347],{},"The following software has been used for this step:",[53,5349,5350,5352,5355,5358],{},[56,5351,5106],{},[56,5353,5354],{},"@vue\u002Fcli-plugin-unit-jest",[56,5356,5357],{},"jest-junit",[56,5359,5360],{},"Coberture jenkins plugin",[16,5362,5363],{},"Simple execute the following:",[29,5365,5368],{"className":5366,"code":5367,"language":34},[32],"npm run test:unit -- --ci --coverage\n",[36,5369,5367],{"__ignoreMap":38},[16,5371,5372],{},"After successfully integrating Jenkins with Jest you will get a nice overview of the test\nresults directly into Jenkins:",[16,5374,5375],{},[5376,5377],"img",{"alt":5378,"src":5379},"Screenshot 1","https:\u002F\u002Fdev-to-uploads.s3.amazonaws.com\u002Fi\u002Fgpc91qroet7m9r53yjn9.jpg",[11,5381,5383],{"id":5382},"e2e-testing","E2E testing",[16,5385,5386],{},"I used an end-to-end testing as a smoke test for my website. This way I am sure that the website was deployed successfully.",[16,5388,5389,5390,5393],{},"I chose for this case ",[60,5391,5392],{},"nightwatch"," and installed it using vue-cli:",[29,5395,5398],{"className":5396,"code":5397,"language":34},[32],"vue add e2e-nightwatch\n",[36,5399,5397],{"__ignoreMap":38},[16,5401,5402,5403,5406],{},"After building the application, I made the ",[60,5404,5405],{},"dist"," directory as the root directory for my web server.\nOnce it is successfully build, I can execute the smoke test as following:",[29,5408,5411],{"className":5409,"code":5410,"language":34},[32],"npm run test:e2e -- --headless --url https:\u002F\u002Fwww.trelobifteki.com --config video=false\n",[36,5412,5410],{"__ignoreMap":38},[16,5414,5415],{},"Some points there:",[53,5417,5418,5425,5428],{},[56,5419,5420,5421,5424],{},"I used ",[36,5422,5423],{},"--headless"," since this is an automated test (no interaction is required)",[56,5426,5427],{},"I am using the actual URL of my website for end-to-end testing",[56,5429,5430],{},"I deactivated video recording (since I don't need it)",[11,5432,5434],{"id":5433},"complete-example","Complete example",[16,5436,5437,5438,5444],{},"This is the actual pipeline I am using in my ",[5439,5440,140],"a",{"href":5441,"rel":5442},"https:\u002F\u002Fgithub.com\u002Ftrelo-bifteki\u002Fwww.trelobifteki.com\u002F",[5443],"nofollow",". Feel free to check it out and experiment with it!",[29,5446,5450],{"className":5447,"code":5448,"language":5449,"meta":38,"style":38},"language-groovy shiki shiki-themes github-dark","pipeline {\n  agent any\n  stages {\n    stage('Check for vulnerabilities') {\n      steps {\n        sh 'npm audit --parseable --production'\n        sh 'npm outdated || exit 0'\n      }\n    }\n\n    stage('download dependencies') {\n      steps {\n        sh 'npm ci'\n      }\n    }\n\n    stage('Check linting') {\n      steps {\n        sh 'npm run lint'\n      }\n    }\n\n    stage('Check unit:test') {\n      steps {\n        sh 'npm run test:unit -- --ci --coverage'\n      }\n      post {\n        always {\n          junit 'junit.xml'\n          cobertura coberturaReportFile: 'coverage\u002Fcobertura-coverage.xml'\n        }\n      }\n    }\n\n    stage('Build') {\n      steps {\n        sh 'npm run build'\n      }\n    }\n\n    stage('Check unit:e2e') {\n      steps {\n        sh 'npm run test:e2e -- --headless --url https:\u002F\u002Fwww.trelobifteki.com --config video=false'\n      }\n    }\n  }\n}\n","groovy",[36,5451,5452,5457,5462,5467,5472,5477,5482,5487,5491,5495,5499,5504,5508,5513,5517,5521,5525,5530,5534,5539,5543,5547,5551,5556,5560,5565,5569,5574,5579,5584,5589,5594,5598,5602,5606,5611,5615,5620,5624,5628,5632,5637,5641,5646,5650,5654,5659],{"__ignoreMap":38},[281,5453,5454],{"class":283,"line":284},[281,5455,5456],{},"pipeline {\n",[281,5458,5459],{"class":283,"line":220},[281,5460,5461],{},"  agent any\n",[281,5463,5464],{"class":283,"line":313},[281,5465,5466],{},"  stages {\n",[281,5468,5469],{"class":283,"line":322},[281,5470,5471],{},"    stage('Check for vulnerabilities') {\n",[281,5473,5474],{"class":283,"line":330},[281,5475,5476],{},"      steps {\n",[281,5478,5479],{"class":283,"line":338},[281,5480,5481],{},"        sh 'npm audit --parseable --production'\n",[281,5483,5484],{"class":283,"line":346},[281,5485,5486],{},"        sh 'npm outdated || exit 0'\n",[281,5488,5489],{"class":283,"line":352},[281,5490,763],{},[281,5492,5493],{"class":283,"line":358},[281,5494,2174],{},[281,5496,5497],{"class":283,"line":488},[281,5498,450],{"emptyLinePlaceholder":228},[281,5500,5501],{"class":283,"line":494},[281,5502,5503],{},"    stage('download dependencies') {\n",[281,5505,5506],{"class":283,"line":505},[281,5507,5476],{},[281,5509,5510],{"class":283,"line":511},[281,5511,5512],{},"        sh 'npm ci'\n",[281,5514,5515],{"class":283,"line":517},[281,5516,763],{},[281,5518,5519],{"class":283,"line":522},[281,5520,2174],{},[281,5522,5523],{"class":283,"line":531},[281,5524,450],{"emptyLinePlaceholder":228},[281,5526,5527],{"class":283,"line":536},[281,5528,5529],{},"    stage('Check linting') {\n",[281,5531,5532],{"class":283,"line":542},[281,5533,5476],{},[281,5535,5536],{"class":283,"line":548},[281,5537,5538],{},"        sh 'npm run lint'\n",[281,5540,5541],{"class":283,"line":558},[281,5542,763],{},[281,5544,5545],{"class":283,"line":563},[281,5546,2174],{},[281,5548,5549],{"class":283,"line":569},[281,5550,450],{"emptyLinePlaceholder":228},[281,5552,5553],{"class":283,"line":574},[281,5554,5555],{},"    stage('Check unit:test') {\n",[281,5557,5558],{"class":283,"line":584},[281,5559,5476],{},[281,5561,5562],{"class":283,"line":589},[281,5563,5564],{},"        sh 'npm run test:unit -- --ci --coverage'\n",[281,5566,5567],{"class":283,"line":595},[281,5568,763],{},[281,5570,5571],{"class":283,"line":601},[281,5572,5573],{},"      post {\n",[281,5575,5576],{"class":283,"line":611},[281,5577,5578],{},"        always {\n",[281,5580,5581],{"class":283,"line":616},[281,5582,5583],{},"          junit 'junit.xml'\n",[281,5585,5586],{"class":283,"line":622},[281,5587,5588],{},"          cobertura coberturaReportFile: 'coverage\u002Fcobertura-coverage.xml'\n",[281,5590,5591],{"class":283,"line":628},[281,5592,5593],{},"        }\n",[281,5595,5596],{"class":283,"line":646},[281,5597,763],{},[281,5599,5600],{"class":283,"line":655},[281,5601,2174],{},[281,5603,5604],{"class":283,"line":663},[281,5605,450],{"emptyLinePlaceholder":228},[281,5607,5608],{"class":283,"line":682},[281,5609,5610],{},"    stage('Build') {\n",[281,5612,5613],{"class":283,"line":688},[281,5614,5476],{},[281,5616,5617],{"class":283,"line":693},[281,5618,5619],{},"        sh 'npm run build'\n",[281,5621,5622],{"class":283,"line":708},[281,5623,763],{},[281,5625,5626],{"class":283,"line":728},[281,5627,2174],{},[281,5629,5630],{"class":283,"line":742},[281,5631,450],{"emptyLinePlaceholder":228},[281,5633,5634],{"class":283,"line":760},[281,5635,5636],{},"    stage('Check unit:e2e') {\n",[281,5638,5639],{"class":283,"line":766},[281,5640,5476],{},[281,5642,5643],{"class":283,"line":771},[281,5644,5645],{},"        sh 'npm run test:e2e -- --headless --url https:\u002F\u002Fwww.trelobifteki.com --config video=false'\n",[281,5647,5648],{"class":283,"line":779},[281,5649,763],{},[281,5651,5652],{"class":283,"line":784},[281,5653,2174],{},[281,5655,5656],{"class":283,"line":789},[281,5657,5658],{},"  }\n",[281,5660,5661],{"class":283,"line":795},[281,5662,361],{},[5155,5664,5665],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":38,"searchDepth":220,"depth":220,"links":5667},[5668,5669,5670,5671,5672,5673,5674,5675],{"id":5179,"depth":220,"text":5180},{"id":5210,"depth":220,"text":5211},{"id":5228,"depth":220,"text":5229},{"id":5279,"depth":220,"text":5280},{"id":5311,"depth":220,"text":5312},{"id":5336,"depth":220,"text":5337},{"id":5382,"depth":220,"text":5383},{"id":5433,"depth":220,"text":5434},1609502092,{},"\u002Fblog\u002Fjenkins-pipeline-for-frontend-apps",{"title":5174,"description":5174},"blog\u002Fjenkins-pipeline-for-frontend-apps",[],"Jyt5PTpXEPlv0_Wm5TwRXgC2Ja9D5TNRQfeJP160nuM",{"id":5684,"title":5685,"body":5686,"created":7525,"description":7526,"extension":227,"isVisible":228,"meta":7527,"navigation":228,"path":7528,"seo":7529,"stem":7530,"tags":7531,"__hash__":7532},"blog\u002Fblog\u002Fdata-visualization-vuejs-d3js.md","5 simple rules to data visualizations with Vue.js and D3",{"type":8,"value":5687,"toc":7508},[5688,5690,5699,5702,5705,5709,5712,6346,6349,6375,6378,6382,6398,6404,6411,6424,6430,6436,6447,6454,6457,6460,6691,6697,6702,6705,6713,6719,6735,6738,6744,6747,6751,6754,7449,7471,7474,7505],[11,5689,5180],{"id":5179},[16,5691,5692,5695,5696,5698],{},[60,5693,5694],{},"D3.js"," is a visualization library providing all necessary functions and methods\nneeded to create charts and diagrams. ",[60,5697,5194],{}," is a MVC framework for creating\nsingle page applications (SPA).",[16,5700,5701],{},"The real challenge comes where both D3 and Vue.js are able to manipulate\nDOM elements. In this guide, we focus on only using D3 for calculating all\nrendering parts required, but we let Vue.js create and modify the DOM.",[16,5703,5704],{},"This is a walkthrough how I first implemented a graph component in Vue.JS\nusing D3 library and how I improved it afterwards.",[11,5706,5708],{"id":5707},"the-wrong-way","😖 The wrong way",[16,5710,5711],{},"This is actually the first implementation I did inspired by most examples\nfound on the internet:",[29,5713,5715],{"className":398,"code":5714,"language":400,"meta":38,"style":38},"\u003Cscript>\nimport * as d3 from 'd3';\nconst data = [99, 71, 78, 25, 36, 92];\n\nexport default {\n  name: 'WrongLineChart',\n  props: {\n    data: {\n      required: true,\n      type: Array,\n    },\n  },\n  mounted() {\n    const svg = d3.select(this.$el)\n      .append('svg')\n      .attr('width', 500)\n      .attr('height', 270)\n      .append('g')\n      .attr('transform', 'translate(0, 10)');\n    const x = d3.scaleLinear().range([0, 430]);\n    const y = d3.scaleLinear().range([210, 0]);\n    d3.axisLeft().scale(x);\n    d3.axisTop().scale(y);\n    x.domain(d3.extent(data, (d, i) => i));\n    y.domain([0, d3.max(data, d => d)]);\n    const createPath = d3.line()\n      .x((d, i) => x(i))\n      .y(d => y(d));\n    svg.append('path')\n      .attr('d', createPath(data))\n      .attr('fill', 'none')\n      .attr('stroke': '#76BF8A')\n      .attr('stroke-width': '3px')\n  },\n};\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv class=\"demo-chart\" \u002F>\n\u003C\u002Ftemplate>\n\n\u003Cstyle lang=\"sass\">\n.demo-chart\n  svg\n    margin: 25px\n\u003C\u002Fstyle>\n",[36,5716,5717,5725,5745,5788,5792,5800,5809,5813,5818,5827,5832,5836,5840,5846,5868,5884,5902,5920,5933,5951,5985,6013,6029,6043,6075,6102,6118,6142,6160,6174,6193,6211,6230,6248,6252,6256,6264,6268,6276,6289,6297,6301,6315,6325,6330,6338],{"__ignoreMap":38},[281,5718,5719,5721,5723],{"class":283,"line":284},[281,5720,407],{"class":291},[281,5722,801],{"class":461},[281,5724,419],{"class":291},[281,5726,5727,5729,5732,5735,5738,5740,5743],{"class":283,"line":220},[281,5728,424],{"class":297},[281,5730,5731],{"class":287}," *",[281,5733,5734],{"class":297}," as",[281,5736,5737],{"class":291}," d3 ",[281,5739,439],{"class":297},[281,5741,5742],{"class":306}," 'd3'",[281,5744,445],{"class":291},[281,5746,5747,5749,5752,5754,5757,5760,5762,5765,5767,5770,5772,5775,5777,5780,5782,5785],{"class":283,"line":313},[281,5748,1029],{"class":297},[281,5750,5751],{"class":287}," data",[281,5753,298],{"class":297},[281,5755,5756],{"class":291}," [",[281,5758,5759],{"class":287},"99",[281,5761,2052],{"class":291},[281,5763,5764],{"class":287},"71",[281,5766,2052],{"class":291},[281,5768,5769],{"class":287},"78",[281,5771,2052],{"class":291},[281,5773,5774],{"class":287},"25",[281,5776,2052],{"class":291},[281,5778,5779],{"class":287},"36",[281,5781,2052],{"class":291},[281,5783,5784],{"class":287},"92",[281,5786,5787],{"class":291},"];\n",[281,5789,5790],{"class":283,"line":322},[281,5791,450],{"emptyLinePlaceholder":228},[281,5793,5794,5796,5798],{"class":283,"line":330},[281,5795,455],{"class":297},[281,5797,458],{"class":297},[281,5799,301],{"class":291},[281,5801,5802,5804,5807],{"class":283,"line":338},[281,5803,470],{"class":291},[281,5805,5806],{"class":306},"'WrongLineChart'",[281,5808,319],{"class":291},[281,5810,5811],{"class":283,"line":346},[281,5812,480],{"class":291},[281,5814,5815],{"class":283,"line":352},[281,5816,5817],{"class":291},"    data: {\n",[281,5819,5820,5823,5825],{"class":283,"line":358},[281,5821,5822],{"class":291},"      required: ",[281,5824,1234],{"class":287},[281,5826,319],{"class":291},[281,5828,5829],{"class":283,"line":488},[281,5830,5831],{"class":291},"      type: Array,\n",[281,5833,5834],{"class":283,"line":494},[281,5835,508],{"class":291},[281,5837,5838],{"class":283,"line":505},[281,5839,619],{"class":291},[281,5841,5842,5844],{"class":283,"line":511},[281,5843,3925],{"class":461},[281,5845,1924],{"class":291},[281,5847,5848,5850,5853,5855,5858,5861,5863,5865],{"class":283,"line":517},[281,5849,1177],{"class":297},[281,5851,5852],{"class":287}," svg",[281,5854,298],{"class":297},[281,5856,5857],{"class":291}," d3.",[281,5859,5860],{"class":461},"select",[281,5862,751],{"class":291},[281,5864,669],{"class":287},[281,5866,5867],{"class":291},".$el)\n",[281,5869,5870,5873,5876,5878,5881],{"class":283,"line":522},[281,5871,5872],{"class":291},"      .",[281,5874,5875],{"class":461},"append",[281,5877,751],{"class":291},[281,5879,5880],{"class":306},"'svg'",[281,5882,5883],{"class":291},")\n",[281,5885,5886,5888,5891,5893,5895,5897,5900],{"class":283,"line":531},[281,5887,5872],{"class":291},[281,5889,5890],{"class":461},"attr",[281,5892,751],{"class":291},[281,5894,1381],{"class":306},[281,5896,2052],{"class":291},[281,5898,5899],{"class":287},"500",[281,5901,5883],{"class":291},[281,5903,5904,5906,5908,5910,5913,5915,5918],{"class":283,"line":536},[281,5905,5872],{"class":291},[281,5907,5890],{"class":461},[281,5909,751],{"class":291},[281,5911,5912],{"class":306},"'height'",[281,5914,2052],{"class":291},[281,5916,5917],{"class":287},"270",[281,5919,5883],{"class":291},[281,5921,5922,5924,5926,5928,5931],{"class":283,"line":542},[281,5923,5872],{"class":291},[281,5925,5875],{"class":461},[281,5927,751],{"class":291},[281,5929,5930],{"class":306},"'g'",[281,5932,5883],{"class":291},[281,5934,5935,5937,5939,5941,5944,5946,5949],{"class":283,"line":548},[281,5936,5872],{"class":291},[281,5938,5890],{"class":461},[281,5940,751],{"class":291},[281,5942,5943],{"class":306},"'transform'",[281,5945,2052],{"class":291},[281,5947,5948],{"class":306},"'translate(0, 10)'",[281,5950,757],{"class":291},[281,5952,5953,5955,5958,5960,5962,5965,5968,5971,5974,5977,5979,5982],{"class":283,"line":558},[281,5954,1177],{"class":297},[281,5956,5957],{"class":287}," x",[281,5959,298],{"class":297},[281,5961,5857],{"class":291},[281,5963,5964],{"class":461},"scaleLinear",[281,5966,5967],{"class":291},"().",[281,5969,5970],{"class":461},"range",[281,5972,5973],{"class":291},"([",[281,5975,5976],{"class":287},"0",[281,5978,2052],{"class":291},[281,5980,5981],{"class":287},"430",[281,5983,5984],{"class":291},"]);\n",[281,5986,5987,5989,5992,5994,5996,5998,6000,6002,6004,6007,6009,6011],{"class":283,"line":563},[281,5988,1177],{"class":297},[281,5990,5991],{"class":287}," y",[281,5993,298],{"class":297},[281,5995,5857],{"class":291},[281,5997,5964],{"class":461},[281,5999,5967],{"class":291},[281,6001,5970],{"class":461},[281,6003,5973],{"class":291},[281,6005,6006],{"class":287},"210",[281,6008,2052],{"class":291},[281,6010,5976],{"class":287},[281,6012,5984],{"class":291},[281,6014,6015,6018,6021,6023,6026],{"class":283,"line":569},[281,6016,6017],{"class":291},"    d3.",[281,6019,6020],{"class":461},"axisLeft",[281,6022,5967],{"class":291},[281,6024,6025],{"class":461},"scale",[281,6027,6028],{"class":291},"(x);\n",[281,6030,6031,6033,6036,6038,6040],{"class":283,"line":574},[281,6032,6017],{"class":291},[281,6034,6035],{"class":461},"axisTop",[281,6037,5967],{"class":291},[281,6039,6025],{"class":461},[281,6041,6042],{"class":291},"(y);\n",[281,6044,6045,6048,6051,6054,6057,6060,6063,6065,6068,6070,6072],{"class":283,"line":584},[281,6046,6047],{"class":291},"    x.",[281,6049,6050],{"class":461},"domain",[281,6052,6053],{"class":291},"(d3.",[281,6055,6056],{"class":461},"extent",[281,6058,6059],{"class":291},"(data, (",[281,6061,6062],{"class":1115},"d",[281,6064,2052],{"class":291},[281,6066,6067],{"class":1115},"i",[281,6069,2027],{"class":291},[281,6071,1098],{"class":297},[281,6073,6074],{"class":291}," i));\n",[281,6076,6077,6080,6082,6084,6086,6089,6092,6095,6097,6099],{"class":283,"line":589},[281,6078,6079],{"class":291},"    y.",[281,6081,6050],{"class":461},[281,6083,5973],{"class":291},[281,6085,5976],{"class":287},[281,6087,6088],{"class":291},", d3.",[281,6090,6091],{"class":461},"max",[281,6093,6094],{"class":291},"(data, ",[281,6096,6062],{"class":1115},[281,6098,2736],{"class":297},[281,6100,6101],{"class":291}," d)]);\n",[281,6103,6104,6106,6109,6111,6113,6115],{"class":283,"line":595},[281,6105,1177],{"class":297},[281,6107,6108],{"class":287}," createPath",[281,6110,298],{"class":297},[281,6112,5857],{"class":291},[281,6114,283],{"class":461},[281,6116,6117],{"class":291},"()\n",[281,6119,6120,6122,6125,6127,6129,6131,6133,6135,6137,6139],{"class":283,"line":601},[281,6121,5872],{"class":291},[281,6123,6124],{"class":461},"x",[281,6126,2016],{"class":291},[281,6128,6062],{"class":1115},[281,6130,2052],{"class":291},[281,6132,6067],{"class":1115},[281,6134,2027],{"class":291},[281,6136,1098],{"class":297},[281,6138,5957],{"class":461},[281,6140,6141],{"class":291},"(i))\n",[281,6143,6144,6146,6149,6151,6153,6155,6157],{"class":283,"line":611},[281,6145,5872],{"class":291},[281,6147,6148],{"class":461},"y",[281,6150,751],{"class":291},[281,6152,6062],{"class":1115},[281,6154,2736],{"class":297},[281,6156,5991],{"class":461},[281,6158,6159],{"class":291},"(d));\n",[281,6161,6162,6165,6167,6169,6172],{"class":283,"line":616},[281,6163,6164],{"class":291},"    svg.",[281,6166,5875],{"class":461},[281,6168,751],{"class":291},[281,6170,6171],{"class":306},"'path'",[281,6173,5883],{"class":291},[281,6175,6176,6178,6180,6182,6185,6187,6190],{"class":283,"line":622},[281,6177,5872],{"class":291},[281,6179,5890],{"class":461},[281,6181,751],{"class":291},[281,6183,6184],{"class":306},"'d'",[281,6186,2052],{"class":291},[281,6188,6189],{"class":461},"createPath",[281,6191,6192],{"class":291},"(data))\n",[281,6194,6195,6197,6199,6201,6204,6206,6209],{"class":283,"line":628},[281,6196,5872],{"class":291},[281,6198,5890],{"class":461},[281,6200,751],{"class":291},[281,6202,6203],{"class":306},"'fill'",[281,6205,2052],{"class":291},[281,6207,6208],{"class":306},"'none'",[281,6210,5883],{"class":291},[281,6212,6213,6215,6217,6219,6222,6225,6228],{"class":283,"line":646},[281,6214,5872],{"class":291},[281,6216,5890],{"class":461},[281,6218,751],{"class":291},[281,6220,6221],{"class":306},"'stroke'",[281,6223,6224],{"class":291},": ",[281,6226,6227],{"class":306},"'#76BF8A'",[281,6229,5883],{"class":291},[281,6231,6232,6234,6236,6238,6241,6243,6246],{"class":283,"line":655},[281,6233,5872],{"class":291},[281,6235,5890],{"class":461},[281,6237,751],{"class":291},[281,6239,6240],{"class":306},"'stroke-width'",[281,6242,6224],{"class":291},[281,6244,6245],{"class":306},"'3px'",[281,6247,5883],{"class":291},[281,6249,6250],{"class":283,"line":663},[281,6251,619],{"class":291},[281,6253,6254],{"class":283,"line":682},[281,6255,1079],{"class":291},[281,6257,6258,6260,6262],{"class":283,"line":688},[281,6259,798],{"class":297},[281,6261,801],{"class":291},[281,6263,419],{"class":297},[281,6265,6266],{"class":283,"line":693},[281,6267,450],{"emptyLinePlaceholder":228},[281,6269,6270,6272,6274],{"class":283,"line":708},[281,6271,407],{"class":291},[281,6273,811],{"class":461},[281,6275,419],{"class":291},[281,6277,6278,6280,6282,6284,6287],{"class":283,"line":728},[281,6279,819],{"class":297},[281,6281,2238],{"class":291},[281,6283,413],{"class":297},[281,6285,6286],{"class":306},"\"demo-chart\"",[281,6288,2274],{"class":297},[281,6290,6291,6293,6295],{"class":283,"line":742},[281,6292,798],{"class":297},[281,6294,811],{"class":291},[281,6296,419],{"class":297},[281,6298,6299],{"class":283,"line":760},[281,6300,450],{"emptyLinePlaceholder":228},[281,6302,6303,6305,6308,6310,6313],{"class":283,"line":766},[281,6304,407],{"class":297},[281,6306,6307],{"class":291},"style lang",[281,6309,413],{"class":297},[281,6311,6312],{"class":306},"\"sass\"",[281,6314,419],{"class":297},[281,6316,6317,6320,6322],{"class":283,"line":771},[281,6318,6319],{"class":291},".demo",[281,6321,2069],{"class":297},[281,6323,6324],{"class":291},"chart\n",[281,6326,6327],{"class":283,"line":779},[281,6328,6329],{"class":291},"  svg\n",[281,6331,6332,6335],{"class":283,"line":784},[281,6333,6334],{"class":461},"    margin",[281,6336,6337],{"class":291},": 25px\n",[281,6339,6340,6342,6344],{"class":283,"line":789},[281,6341,798],{"class":297},[281,6343,5155],{"class":291},[281,6345,419],{"class":297},[16,6347,6348],{},"This can work quite well in the beginning, but:",[53,6350,6351,6357,6363,6369],{},[56,6352,6353,6354],{},"graph is ",[168,6355,6356],{},"not responsive",[56,6358,6359,6360],{},"data is ",[168,6361,6362],{},"static",[56,6364,6365,6366],{},"it renders the graph ",[168,6367,6368],{},"once",[56,6370,6371,6372],{},"styling is ",[168,6373,6374],{},"hardcoded",[16,6376,6377],{},"In the end, the component is not reusable",[11,6379,6381],{"id":6380},"️-5-simple-rules","🧙‍♂️ 5 simple rules",[6383,6384,6386,6387,6390,6391,6394,6395],"h3",{"id":6385},"rule-1-prefer-viewbox-against-width-height","Rule 1: 📜 Prefer ",[60,6388,6389],{},"viewBox"," against ",[60,6392,6393],{},"width"," & ",[60,6396,6397],{},"height",[16,6399,6400,6401,6403],{},"We want to make our SVG images responsive. Width and height limit our\nimplementation to the dimensions defined. By setting ",[36,6402,6389],{}," instead,\nwe are able to scale the SVG image to fill a container.",[6383,6405,6407,6408,6410],{"id":6406},"rule-2-write-svg-template-in-template-section","Rule 2: 📜 Write SVG template in ",[60,6409,811],{}," section",[16,6412,6413,6414,6416,6417,6420,6421,6423],{},"Most D3js examples on the internet prefer to use ",[36,6415,5875],{}," function and attach\nDOM elements to already existing ones. Since we use Vue.js now for this\nscope it is advisable to ",[168,6418,6419],{},"only"," use ",[36,6422,811],{}," section instead.",[6383,6425,6427,6428,6410],{"id":6426},"rule-3-write-stylings-in-style-section","Rule 3: 📜 Write stylings in ",[60,6429,5155],{},[16,6431,6432,6433,6435],{},"As SVG supports adding classes and styling elements in CSS and it is better to\nwrite styling in ",[36,6434,5155],{}," section than having hardcoded values on the element.",[16,6437,6438,6439,6442,6443,6446],{},"In this rule there is an ",[168,6440,6441],{},"exception",", if you want to introduce a styling as\n",[60,6444,6445],{},"property"," to the component.",[6383,6448,6450,6451,6453],{"id":6449},"rule-4-use-props-to-provide-data","Rule 4: 📜 Use ",[60,6452,1116],{}," to provide data",[16,6455,6456],{},"Of course we want our component to be reusable and dynamically assign data\ninto our graph! As most examples with Vue.js and SVG found on the internet,\nwe set some properties to the component in order to make it dynamic.",[16,6458,6459],{},"Like this example:",[29,6461,6463],{"className":398,"code":6462,"language":400,"meta":38,"style":38},"\u003Cscript>\nexport default {\n  props: {\n    title: {\n      required: true,\n      type: String,\n    },\n  },\n};\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Csvg\n    class=\"svg-demo\"\n    viewBox=\"0 0 240 80\"\n  >\n    \u003Ctext\n      class=\"svg-demo__text\"\n      x=\"20\"\n      y=\"35\"\n    >\n      {{ title }}\n    \u003C\u002Ftext>\n  \u003C\u002Fsvg>\n\u003C\u002Ftemplate>\n\n\u003Cstyle lang=\"scss\">\n.svg-demo {\n  &__text {\n    color: red;\n  }\n}\n\u003C\u002Fstyle>\n",[36,6464,6465,6473,6481,6485,6490,6498,6502,6506,6510,6514,6522,6526,6534,6540,6550,6559,6563,6570,6579,6589,6599,6603,6608,6616,6624,6632,6636,6649,6659,6667,6675,6679,6683],{"__ignoreMap":38},[281,6466,6467,6469,6471],{"class":283,"line":284},[281,6468,407],{"class":291},[281,6470,801],{"class":461},[281,6472,419],{"class":291},[281,6474,6475,6477,6479],{"class":283,"line":220},[281,6476,455],{"class":297},[281,6478,458],{"class":297},[281,6480,301],{"class":291},[281,6482,6483],{"class":283,"line":313},[281,6484,480],{"class":291},[281,6486,6487],{"class":283,"line":322},[281,6488,6489],{"class":291},"    title: {\n",[281,6491,6492,6494,6496],{"class":283,"line":330},[281,6493,5822],{"class":291},[281,6495,1234],{"class":287},[281,6497,319],{"class":291},[281,6499,6500],{"class":283,"line":338},[281,6501,545],{"class":291},[281,6503,6504],{"class":283,"line":346},[281,6505,508],{"class":291},[281,6507,6508],{"class":283,"line":352},[281,6509,619],{"class":291},[281,6511,6512],{"class":283,"line":358},[281,6513,1079],{"class":291},[281,6515,6516,6518,6520],{"class":283,"line":488},[281,6517,798],{"class":297},[281,6519,801],{"class":291},[281,6521,419],{"class":297},[281,6523,6524],{"class":283,"line":494},[281,6525,450],{"emptyLinePlaceholder":228},[281,6527,6528,6530,6532],{"class":283,"line":505},[281,6529,407],{"class":291},[281,6531,811],{"class":461},[281,6533,419],{"class":291},[281,6535,6536,6538],{"class":283,"line":511},[281,6537,819],{"class":297},[281,6539,822],{"class":291},[281,6541,6542,6545,6547],{"class":283,"line":517},[281,6543,6544],{"class":291},"    class",[281,6546,413],{"class":297},[281,6548,6549],{"class":306},"\"svg-demo\"\n",[281,6551,6552,6554,6556],{"class":283,"line":522},[281,6553,861],{"class":291},[281,6555,413],{"class":297},[281,6557,6558],{"class":306},"\"0 0 240 80\"\n",[281,6560,6561],{"class":283,"line":531},[281,6562,894],{"class":297},[281,6564,6565,6567],{"class":283,"line":536},[281,6566,900],{"class":297},[281,6568,6569],{"class":291},"text\n",[281,6571,6572,6574,6576],{"class":283,"line":542},[281,6573,2313],{"class":291},[281,6575,413],{"class":297},[281,6577,6578],{"class":306},"\"svg-demo__text\"\n",[281,6580,6581,6584,6586],{"class":283,"line":548},[281,6582,6583],{"class":291},"      x",[281,6585,413],{"class":297},[281,6587,6588],{"class":306},"\"20\"\n",[281,6590,6591,6594,6596],{"class":283,"line":558},[281,6592,6593],{"class":291},"      y",[281,6595,413],{"class":297},[281,6597,6598],{"class":306},"\"35\"\n",[281,6600,6601],{"class":283,"line":563},[281,6602,2324],{"class":297},[281,6604,6605],{"class":283,"line":569},[281,6606,6607],{"class":291},"      {{ title }}\n",[281,6609,6610,6612,6614],{"class":283,"line":574},[281,6611,931],{"class":297},[281,6613,34],{"class":291},[281,6615,419],{"class":297},[281,6617,6618,6620,6622],{"class":283,"line":584},[281,6619,941],{"class":297},[281,6621,944],{"class":291},[281,6623,419],{"class":297},[281,6625,6626,6628,6630],{"class":283,"line":589},[281,6627,798],{"class":297},[281,6629,811],{"class":291},[281,6631,419],{"class":297},[281,6633,6634],{"class":283,"line":595},[281,6635,450],{"emptyLinePlaceholder":228},[281,6637,6638,6640,6642,6644,6647],{"class":283,"line":601},[281,6639,407],{"class":297},[281,6641,6307],{"class":291},[281,6643,413],{"class":297},[281,6645,6646],{"class":306},"\"scss\"",[281,6648,419],{"class":297},[281,6650,6651,6654,6656],{"class":283,"line":611},[281,6652,6653],{"class":291},".svg",[281,6655,2069],{"class":297},[281,6657,6658],{"class":291},"demo {\n",[281,6660,6661,6664],{"class":283,"line":616},[281,6662,6663],{"class":297},"  &",[281,6665,6666],{"class":291},"__text {\n",[281,6668,6669,6672],{"class":283,"line":622},[281,6670,6671],{"class":461},"    color",[281,6673,6674],{"class":291},": red;\n",[281,6676,6677],{"class":283,"line":628},[281,6678,5658],{"class":291},[281,6680,6681],{"class":283,"line":646},[281,6682,361],{"class":291},[281,6684,6685,6687,6689],{"class":283,"line":655},[281,6686,798],{"class":297},[281,6688,5155],{"class":291},[281,6690,419],{"class":297},[16,6692,6693,6694,6696],{},"Now that we set a parameter in ",[36,6695,1116],{}," area we can also do the following:",[16,6698,6699],{},[36,6700,6701],{},"\u003CSvgDemo title=\"John Snow\">\u003C\u002FSvgDemo>",[16,6703,6704],{},"And we are all set!",[6383,6706,6708,6709,6712],{"id":6707},"rule-5-use-computed-to-provide-d3-calculations","Rule 5: 📜 Use ",[60,6710,6711],{},"computed"," to provide D3 calculations",[16,6714,6715,6716,6718],{},"The biggest challenge here is to render the graphs everytime ",[36,6717,1116],{}," changes.",[16,6720,6721,6722,63,6724,6726,6727,6730,6731,6734],{},"I first tried to use the ",[36,6723,5875],{},[36,6725,5890],{}," methods from D3 library to render\nduring ",[36,6728,6729],{},"mounted",". In order to render after each change I had to introduce\n",[36,6732,6733],{},"watchers",", remove all elements and re-render again.",[16,6736,6737],{},"This solution was already confusing enough for developers and does not work well together with Vue.js. In the end, it is better to perform only the calculations in D3js and bind them with Vue.js instead.",[16,6739,6740,6741,6743],{},"All calculation can be bound directly to DOM using the ",[36,6742,6711],{}," functions. This\nway the calculation will be invoked every time data information changes.",[16,6745,6746],{},"No worries. Vue.js is smart enough to invoke function only when needed 😉.",[11,6748,6750],{"id":6749},"the-right-way","😇 The right way",[16,6752,6753],{},"After applying all rules above the result to the previous example should look\nlike this:",[29,6755,6757],{"className":398,"code":6756,"language":400,"meta":38,"style":38},"\u003Cscript>\nimport * as d3 from 'd3';\n\nexport default {\n  name: 'LineChart',\n  props: {\n    data: {\n      required: true,\n      type: Array,\n    },\n    width: {\n      default: 500,\n      type: Number,\n    },\n    height: {\n      height: 270,\n      type: Number,\n    }\n  },\n  data() {\n    return {\n      padding: 60,\n    };\n  },\n  computed: {\n    rangeX() {\n      const width = this.width - this.padding;\n      return [0, width];\n    },\n    rangeY() {\n      const height = this.height - this.padding;\n      return [0, height];\n    },\n    path() {\n      const x = d3.scaleLinear().range(this.rangeX);\n      const y = d3.scaleLinear().range(this.rangeY);\n      d3.axisLeft().scale(x);\n      d3.axisTop().scale(y);\n      x.domain(d3.extent(this.data, (d, i) => i));\n      y.domain([0, d3.max(this.data, d => d)]);\n      return d3.line()\n        .x((d, i) => x(i))\n        .y(d => y(d));\n    },\n    line() {\n      return this.path(this.data);\n    },\n    viewBox() {\n      return `0 0 ${this.width} ${this.height}`;\n    }\n  },\n};\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Csvg\n    class=\"line-chart\"\n    :viewBox=\"viewBox\"\n  >\n    \u003Cg transform=\"translate(0, 10)\">\n      \u003Cpath\n        class=\"line-chart__line\"\n        :d=\"line\"\n      \u002F>\n    \u003C\u002Fg>\n  \u003C\u002Fsvg>\n\u003C\u002Ftemplate>\n\n\u003Cstyle lang=\"sass\">\n.line-chart\n  margin: 25px\n  &__line\n    fill: none\n    stroke: #76BF8A\n    stroke-width: 3px\n\u003C\u002Fstyle>\n",[36,6758,6759,6767,6783,6787,6795,6804,6808,6812,6820,6824,6828,6832,6840,6845,6849,6853,6862,6866,6870,6874,6880,6886,6896,6900,6904,6908,6915,6936,6947,6951,6958,6978,6989,6993,7000,7023,7046,7059,7071,7101,7129,7139,7162,7178,7182,7189,7207,7211,7217,7243,7247,7251,7255,7263,7267,7275,7281,7290,7300,7304,7318,7325,7334,7344,7348,7356,7364,7372,7376,7388,7397,7404,7411,7419,7430,7441],{"__ignoreMap":38},[281,6760,6761,6763,6765],{"class":283,"line":284},[281,6762,407],{"class":291},[281,6764,801],{"class":461},[281,6766,419],{"class":291},[281,6768,6769,6771,6773,6775,6777,6779,6781],{"class":283,"line":220},[281,6770,424],{"class":297},[281,6772,5731],{"class":287},[281,6774,5734],{"class":297},[281,6776,5737],{"class":291},[281,6778,439],{"class":297},[281,6780,5742],{"class":306},[281,6782,445],{"class":291},[281,6784,6785],{"class":283,"line":313},[281,6786,450],{"emptyLinePlaceholder":228},[281,6788,6789,6791,6793],{"class":283,"line":322},[281,6790,455],{"class":297},[281,6792,458],{"class":297},[281,6794,301],{"class":291},[281,6796,6797,6799,6802],{"class":283,"line":330},[281,6798,470],{"class":291},[281,6800,6801],{"class":306},"'LineChart'",[281,6803,319],{"class":291},[281,6805,6806],{"class":283,"line":338},[281,6807,480],{"class":291},[281,6809,6810],{"class":283,"line":346},[281,6811,5817],{"class":291},[281,6813,6814,6816,6818],{"class":283,"line":352},[281,6815,5822],{"class":291},[281,6817,1234],{"class":287},[281,6819,319],{"class":291},[281,6821,6822],{"class":283,"line":358},[281,6823,5831],{"class":291},[281,6825,6826],{"class":283,"line":488},[281,6827,508],{"class":291},[281,6829,6830],{"class":283,"line":494},[281,6831,485],{"class":291},[281,6833,6834,6836,6838],{"class":283,"line":505},[281,6835,497],{"class":291},[281,6837,5899],{"class":287},[281,6839,319],{"class":291},[281,6841,6842],{"class":283,"line":511},[281,6843,6844],{"class":291},"      type: Number,\n",[281,6846,6847],{"class":283,"line":517},[281,6848,508],{"class":291},[281,6850,6851],{"class":283,"line":522},[281,6852,514],{"class":291},[281,6854,6855,6858,6860],{"class":283,"line":531},[281,6856,6857],{"class":291},"      height: ",[281,6859,5917],{"class":287},[281,6861,319],{"class":291},[281,6863,6864],{"class":283,"line":536},[281,6865,6844],{"class":291},[281,6867,6868],{"class":283,"line":542},[281,6869,2174],{"class":291},[281,6871,6872],{"class":283,"line":548},[281,6873,619],{"class":291},[281,6875,6876,6878],{"class":283,"line":558},[281,6877,1921],{"class":461},[281,6879,1924],{"class":291},[281,6881,6882,6884],{"class":283,"line":563},[281,6883,1929],{"class":297},[281,6885,301],{"class":291},[281,6887,6888,6891,6894],{"class":283,"line":569},[281,6889,6890],{"class":291},"      padding: ",[281,6892,6893],{"class":287},"60",[281,6895,319],{"class":291},[281,6897,6898],{"class":283,"line":574},[281,6899,1945],{"class":291},[281,6901,6902],{"class":283,"line":584},[281,6903,619],{"class":291},[281,6905,6906],{"class":283,"line":589},[281,6907,625],{"class":291},[281,6909,6910,6913],{"class":283,"line":595},[281,6911,6912],{"class":461},"    rangeX",[281,6914,1924],{"class":291},[281,6916,6917,6919,6922,6924,6926,6929,6931,6933],{"class":283,"line":601},[281,6918,711],{"class":297},[281,6920,6921],{"class":287}," width",[281,6923,298],{"class":297},[281,6925,2007],{"class":287},[281,6927,6928],{"class":291},".width ",[281,6930,2069],{"class":297},[281,6932,2007],{"class":287},[281,6934,6935],{"class":291},".padding;\n",[281,6937,6938,6940,6942,6944],{"class":283,"line":611},[281,6939,649],{"class":297},[281,6941,5756],{"class":291},[281,6943,5976],{"class":287},[281,6945,6946],{"class":291},", width];\n",[281,6948,6949],{"class":283,"line":616},[281,6950,508],{"class":291},[281,6952,6953,6956],{"class":283,"line":622},[281,6954,6955],{"class":461},"    rangeY",[281,6957,1924],{"class":291},[281,6959,6960,6962,6965,6967,6969,6972,6974,6976],{"class":283,"line":628},[281,6961,711],{"class":297},[281,6963,6964],{"class":287}," height",[281,6966,298],{"class":297},[281,6968,2007],{"class":287},[281,6970,6971],{"class":291},".height ",[281,6973,2069],{"class":297},[281,6975,2007],{"class":287},[281,6977,6935],{"class":291},[281,6979,6980,6982,6984,6986],{"class":283,"line":646},[281,6981,649],{"class":297},[281,6983,5756],{"class":291},[281,6985,5976],{"class":287},[281,6987,6988],{"class":291},", height];\n",[281,6990,6991],{"class":283,"line":655},[281,6992,508],{"class":291},[281,6994,6995,6998],{"class":283,"line":663},[281,6996,6997],{"class":461},"    path",[281,6999,1924],{"class":291},[281,7001,7002,7004,7006,7008,7010,7012,7014,7016,7018,7020],{"class":283,"line":682},[281,7003,711],{"class":297},[281,7005,5957],{"class":287},[281,7007,298],{"class":297},[281,7009,5857],{"class":291},[281,7011,5964],{"class":461},[281,7013,5967],{"class":291},[281,7015,5970],{"class":461},[281,7017,751],{"class":291},[281,7019,669],{"class":287},[281,7021,7022],{"class":291},".rangeX);\n",[281,7024,7025,7027,7029,7031,7033,7035,7037,7039,7041,7043],{"class":283,"line":688},[281,7026,711],{"class":297},[281,7028,5991],{"class":287},[281,7030,298],{"class":297},[281,7032,5857],{"class":291},[281,7034,5964],{"class":461},[281,7036,5967],{"class":291},[281,7038,5970],{"class":461},[281,7040,751],{"class":291},[281,7042,669],{"class":287},[281,7044,7045],{"class":291},".rangeY);\n",[281,7047,7048,7051,7053,7055,7057],{"class":283,"line":693},[281,7049,7050],{"class":291},"      d3.",[281,7052,6020],{"class":461},[281,7054,5967],{"class":291},[281,7056,6025],{"class":461},[281,7058,6028],{"class":291},[281,7060,7061,7063,7065,7067,7069],{"class":283,"line":708},[281,7062,7050],{"class":291},[281,7064,6035],{"class":461},[281,7066,5967],{"class":291},[281,7068,6025],{"class":461},[281,7070,6042],{"class":291},[281,7072,7073,7076,7078,7080,7082,7084,7086,7089,7091,7093,7095,7097,7099],{"class":283,"line":728},[281,7074,7075],{"class":291},"      x.",[281,7077,6050],{"class":461},[281,7079,6053],{"class":291},[281,7081,6056],{"class":461},[281,7083,751],{"class":291},[281,7085,669],{"class":287},[281,7087,7088],{"class":291},".data, (",[281,7090,6062],{"class":1115},[281,7092,2052],{"class":291},[281,7094,6067],{"class":1115},[281,7096,2027],{"class":291},[281,7098,1098],{"class":297},[281,7100,6074],{"class":291},[281,7102,7103,7106,7108,7110,7112,7114,7116,7118,7120,7123,7125,7127],{"class":283,"line":742},[281,7104,7105],{"class":291},"      y.",[281,7107,6050],{"class":461},[281,7109,5973],{"class":291},[281,7111,5976],{"class":287},[281,7113,6088],{"class":291},[281,7115,6091],{"class":461},[281,7117,751],{"class":291},[281,7119,669],{"class":287},[281,7121,7122],{"class":291},".data, ",[281,7124,6062],{"class":1115},[281,7126,2736],{"class":297},[281,7128,6101],{"class":291},[281,7130,7131,7133,7135,7137],{"class":283,"line":760},[281,7132,649],{"class":297},[281,7134,5857],{"class":291},[281,7136,283],{"class":461},[281,7138,6117],{"class":291},[281,7140,7141,7144,7146,7148,7150,7152,7154,7156,7158,7160],{"class":283,"line":766},[281,7142,7143],{"class":291},"        .",[281,7145,6124],{"class":461},[281,7147,2016],{"class":291},[281,7149,6062],{"class":1115},[281,7151,2052],{"class":291},[281,7153,6067],{"class":1115},[281,7155,2027],{"class":291},[281,7157,1098],{"class":297},[281,7159,5957],{"class":461},[281,7161,6141],{"class":291},[281,7163,7164,7166,7168,7170,7172,7174,7176],{"class":283,"line":771},[281,7165,7143],{"class":291},[281,7167,6148],{"class":461},[281,7169,751],{"class":291},[281,7171,6062],{"class":1115},[281,7173,2736],{"class":297},[281,7175,5991],{"class":461},[281,7177,6159],{"class":291},[281,7179,7180],{"class":283,"line":779},[281,7181,508],{"class":291},[281,7183,7184,7187],{"class":283,"line":784},[281,7185,7186],{"class":461},"    line",[281,7188,1924],{"class":291},[281,7190,7191,7193,7195,7197,7200,7202,7204],{"class":283,"line":789},[281,7192,649],{"class":297},[281,7194,2007],{"class":287},[281,7196,102],{"class":291},[281,7198,7199],{"class":461},"path",[281,7201,751],{"class":291},[281,7203,669],{"class":287},[281,7205,7206],{"class":291},".data);\n",[281,7208,7209],{"class":283,"line":795},[281,7210,508],{"class":291},[281,7212,7213,7215],{"class":283,"line":806},[281,7214,861],{"class":461},[281,7216,1924],{"class":291},[281,7218,7219,7221,7224,7226,7228,7230,7233,7235,7237,7239,7241],{"class":283,"line":816},[281,7220,649],{"class":297},[281,7222,7223],{"class":306}," `0 0 ${",[281,7225,669],{"class":287},[281,7227,102],{"class":306},[281,7229,6393],{"class":291},[281,7231,7232],{"class":306},"} ${",[281,7234,669],{"class":287},[281,7236,102],{"class":306},[281,7238,6397],{"class":291},[281,7240,677],{"class":306},[281,7242,445],{"class":291},[281,7244,7245],{"class":283,"line":825},[281,7246,2174],{"class":291},[281,7248,7249],{"class":283,"line":836},[281,7250,619],{"class":291},[281,7252,7253],{"class":283,"line":847},[281,7254,1079],{"class":291},[281,7256,7257,7259,7261],{"class":283,"line":858},[281,7258,798],{"class":297},[281,7260,801],{"class":291},[281,7262,419],{"class":297},[281,7264,7265],{"class":283,"line":869},[281,7266,450],{"emptyLinePlaceholder":228},[281,7268,7269,7271,7273],{"class":283,"line":880},[281,7270,407],{"class":291},[281,7272,811],{"class":461},[281,7274,419],{"class":291},[281,7276,7277,7279],{"class":283,"line":891},[281,7278,819],{"class":297},[281,7280,822],{"class":291},[281,7282,7283,7285,7287],{"class":283,"line":897},[281,7284,6544],{"class":291},[281,7286,413],{"class":297},[281,7288,7289],{"class":306},"\"line-chart\"\n",[281,7291,7292,7295,7297],{"class":283,"line":916},[281,7293,7294],{"class":291},"    :viewBox",[281,7296,413],{"class":297},[281,7298,7299],{"class":306},"\"viewBox\"\n",[281,7301,7302],{"class":283,"line":928},[281,7303,894],{"class":297},[281,7305,7306,7308,7311,7313,7316],{"class":283,"line":938},[281,7307,900],{"class":297},[281,7309,7310],{"class":291},"g transform",[281,7312,413],{"class":297},[281,7314,7315],{"class":306},"\"translate(0, 10)\"",[281,7317,419],{"class":297},[281,7319,7320,7322],{"class":283,"line":949},[281,7321,919],{"class":297},[281,7323,7324],{"class":291},"path\n",[281,7326,7327,7329,7331],{"class":283,"line":1553},[281,7328,2416],{"class":291},[281,7330,413],{"class":297},[281,7332,7333],{"class":306},"\"line-chart__line\"\n",[281,7335,7336,7339,7341],{"class":283,"line":1563},[281,7337,7338],{"class":291},"        :d",[281,7340,413],{"class":297},[281,7342,7343],{"class":306},"\"line\"\n",[281,7345,7346],{"class":283,"line":1568},[281,7347,2448],{"class":297},[281,7349,7350,7352,7354],{"class":283,"line":1573},[281,7351,931],{"class":297},[281,7353,903],{"class":291},[281,7355,419],{"class":297},[281,7357,7358,7360,7362],{"class":283,"line":1580},[281,7359,941],{"class":297},[281,7361,944],{"class":291},[281,7363,419],{"class":297},[281,7365,7366,7368,7370],{"class":283,"line":1598},[281,7367,798],{"class":297},[281,7369,811],{"class":291},[281,7371,419],{"class":297},[281,7373,7374],{"class":283,"line":1611},[281,7375,450],{"emptyLinePlaceholder":228},[281,7377,7378,7380,7382,7384,7386],{"class":283,"line":1616},[281,7379,407],{"class":297},[281,7381,6307],{"class":291},[281,7383,413],{"class":297},[281,7385,6312],{"class":306},[281,7387,419],{"class":297},[281,7389,7390,7393,7395],{"class":283,"line":2298},[281,7391,7392],{"class":291},".line",[281,7394,2069],{"class":297},[281,7396,6324],{"class":291},[281,7398,7399,7402],{"class":283,"line":2310},[281,7400,7401],{"class":461},"  margin",[281,7403,6337],{"class":291},[281,7405,7406,7408],{"class":283,"line":2321},[281,7407,6663],{"class":297},[281,7409,7410],{"class":291},"__line\n",[281,7412,7413,7416],{"class":283,"line":2327},[281,7414,7415],{"class":461},"    fill",[281,7417,7418],{"class":291},": none\n",[281,7420,7421,7424,7427],{"class":283,"line":2337},[281,7422,7423],{"class":461},"    stroke",[281,7425,7426],{"class":291},": #76",[281,7428,7429],{"class":287},"BF8A\n",[281,7431,7432,7434,7436,7438],{"class":283,"line":2347},[281,7433,7423],{"class":291},[281,7435,2069],{"class":297},[281,7437,6393],{"class":461},[281,7439,7440],{"class":291},": 3px\n",[281,7442,7443,7445,7447],{"class":283,"line":2355},[281,7444,798],{"class":297},[281,7446,5155],{"class":291},[281,7448,419],{"class":297},[16,7450,7451,7452,7454,7455,7457,7458,7460,7461,7463,7464,7467,7468,7470],{},"Check all these functions in ",[36,7453,6711],{}," area! They can be directly used in\n",[36,7456,811],{}," now. The most important one is ",[36,7459,283],{},", which actually draws the chart\nthanks to ",[60,7462,5694],{},". Every time we change ",[36,7465,7466],{},"data",", all other function will be\nreinvoked and a new value for ",[36,7469,283],{}," will be set. The chart will be rerendered\nwith the new values!",[16,7472,7473],{},"Now, in this example:",[53,7475,7476,7482,7492,7499],{},[56,7477,6353,7478,7481],{},[168,7479,7480],{},"responsive"," (like every SVG must be!)",[56,7483,7484,7485,7487,7488,7491],{},"a property ",[60,7486,7466],{}," can be used to ",[168,7489,7490],{},"dynamically load data"," into graph",[56,7493,7494,7495,7498],{},"graph will ",[168,7496,7497],{},"render any changes"," into the data automatically",[56,7500,7501,7504],{},[168,7502,7503],{},"CSS classes"," can be used to override styling (not suggested but possible)",[5155,7506,7507],{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":38,"searchDepth":220,"depth":220,"links":7509},[7510,7511,7512,7524],{"id":5179,"depth":220,"text":5180},{"id":5707,"depth":220,"text":5708},{"id":6380,"depth":220,"text":6381,"children":7513},[7514,7516,7518,7520,7522],{"id":6385,"depth":313,"text":7515},"Rule 1: 📜 Prefer viewBox against width & height",{"id":6406,"depth":313,"text":7517},"Rule 2: 📜 Write SVG template in template section",{"id":6426,"depth":313,"text":7519},"Rule 3: 📜 Write stylings in style section",{"id":6449,"depth":313,"text":7521},"Rule 4: 📜 Use props to provide data",{"id":6707,"depth":313,"text":7523},"Rule 5: 📜 Use computed to provide D3 calculations",{"id":6749,"depth":220,"text":6750},1586292493,"This is a description 4",{},"\u002Fblog\u002Fdata-visualization-vuejs-d3js",{"title":5685,"description":7526},"blog\u002Fdata-visualization-vuejs-d3js",[],"xDUC3ISVUVU2Po_eZytx9v7BXTNzvhtCRTgcvdJEnLw",{"id":7534,"title":7535,"body":7536,"created":8140,"description":8141,"extension":227,"isVisible":228,"meta":8142,"navigation":228,"path":8143,"seo":8144,"stem":8145,"tags":8146,"__hash__":8147},"blog\u002Fblog\u002Fhowto-write-tickets.md","Howto write tickets",{"type":8,"value":7537,"toc":8130},[7538,7541,7545,7548,7552,7562,7569,7572,7577,7622,7626,7743,7747,7754,7758,7888,7892,7910,7914,7917,7920,7923,7927,7934,7938,7941,7944,7946,7949,8127],[16,7539,7540],{},"One of the first (and quite common) issues that developers can face in a project\nis usually the poor or missing ticket description they get from products owners\nor managers.",[11,7542,7544],{"id":7543},"guidelines","Guidelines",[16,7546,7547],{},"Here are some small tips for writing better tickets",[6383,7549,7551],{"id":7550},"problem-description-is-vital-information","Problem description is vital information",[16,7553,7554,7555,7558,7559,102],{},"It is a common problem that a ticket description actually contain \u002F suggests\na ",[168,7556,7557],{},"solution"," but never describes the actual ",[168,7560,7561],{},"problem",[16,7563,7564,7565,7568],{},"It is even more dangerous, if a ticket actually suggests only a ",[168,7566,7567],{},"technical\nsolution"," without describing the actual issue. On top of that, if this ticket\nhas been written by a manager or someone with low technical background, it will be\nrevised most probably by developers.",[16,7570,7571],{},"It is very important to describe the actual problem inside the ticket, since a\nfeature implementation needs to be reasoned. People need to understand the scope\nof this ticket and also get the big picture.",[7573,7574,7576],"h4",{"id":7575},"a-no-go-example","A no go example:",[29,7578,7582],{"className":7579,"code":7580,"language":7581,"meta":38,"style":38},"language-sql shiki shiki-themes github-dark","Description\n-----------\n\nAdd a new column `color` in the table 'FRUITS'\n","sql",[36,7583,7584,7589,7595,7599],{"__ignoreMap":38},[281,7585,7586],{"class":283,"line":284},[281,7587,7588],{"class":297},"Description\n",[281,7590,7591],{"class":283,"line":220},[281,7592,7594],{"class":7593},"sAwPA","-----------\n",[281,7596,7597],{"class":283,"line":313},[281,7598,450],{"emptyLinePlaceholder":228},[281,7600,7601,7604,7607,7610,7613,7616,7619],{"class":283,"line":322},[281,7602,7603],{"class":297},"Add",[281,7605,7606],{"class":291}," a new column ",[281,7608,7609],{"class":306},"`color`",[281,7611,7612],{"class":297}," in",[281,7614,7615],{"class":291}," the ",[281,7617,7618],{"class":297},"table",[281,7620,7621],{"class":306}," 'FRUITS'\n",[7573,7623,7625],{"id":7624},"a-better-example","A better example:",[29,7627,7629],{"className":7579,"code":7628,"language":7581,"meta":38,"style":38},"Description\n-----------\n\nCustomers need to be able to search and choose fruits based on color\n\nAcceptance criteria\n-------------------\n\n1. Allow customers to search fruits by color by the following way:\n.....\n2. Allow customers to store color information for each fruit\n.....\n",[36,7630,7631,7635,7639,7643,7671,7675,7680,7685,7689,7717,7722,7739],{"__ignoreMap":38},[281,7632,7633],{"class":283,"line":284},[281,7634,7588],{"class":297},[281,7636,7637],{"class":283,"line":220},[281,7638,7594],{"class":7593},[281,7640,7641],{"class":283,"line":313},[281,7642,450],{"emptyLinePlaceholder":228},[281,7644,7645,7648,7651,7654,7656,7659,7662,7665,7668],{"class":283,"line":322},[281,7646,7647],{"class":291},"Customers need ",[281,7649,7650],{"class":297},"to",[281,7652,7653],{"class":291}," be able ",[281,7655,7650],{"class":297},[281,7657,7658],{"class":291}," search ",[281,7660,7661],{"class":297},"and",[281,7663,7664],{"class":291}," choose fruits based ",[281,7666,7667],{"class":297},"on",[281,7669,7670],{"class":291}," color\n",[281,7672,7673],{"class":283,"line":330},[281,7674,450],{"emptyLinePlaceholder":228},[281,7676,7677],{"class":283,"line":338},[281,7678,7679],{"class":291},"Acceptance criteria\n",[281,7681,7682],{"class":283,"line":346},[281,7683,7684],{"class":7593},"-------------------\n",[281,7686,7687],{"class":283,"line":352},[281,7688,450],{"emptyLinePlaceholder":228},[281,7690,7691,7693,7696,7698,7701,7704,7707,7709,7711,7714],{"class":283,"line":358},[281,7692,3544],{"class":287},[281,7694,7695],{"class":291},". Allow customers ",[281,7697,7650],{"class":297},[281,7699,7700],{"class":291}," search fruits ",[281,7702,7703],{"class":297},"by",[281,7705,7706],{"class":291}," color ",[281,7708,7703],{"class":297},[281,7710,7615],{"class":291},[281,7712,7713],{"class":297},"following",[281,7715,7716],{"class":291}," way:\n",[281,7718,7719],{"class":283,"line":488},[281,7720,7721],{"class":291},".....\n",[281,7723,7724,7727,7729,7731,7734,7736],{"class":283,"line":494},[281,7725,7726],{"class":287},"2",[281,7728,7695],{"class":291},[281,7730,7650],{"class":297},[281,7732,7733],{"class":291}," store color information ",[281,7735,2394],{"class":297},[281,7737,7738],{"class":291}," each fruit\n",[281,7740,7741],{"class":283,"line":505},[281,7742,7721],{"class":291},[6383,7744,7746],{"id":7745},"is-it-a-bug-write-down-how-to-reproduce-it","Is it a bug?  Write down how to reproduce it",[16,7748,7749,7750,7753],{},"When writing \u002F describing a bug ticket, we need to pay attention to the\ninformation we provide to support team. It is also ",[168,7751,7752],{},"very"," helpful, if we\nwrite down the steps how this problem can be reproduced.",[7573,7755,7757],{"id":7756},"good-example","Good example",[29,7759,7761],{"className":7579,"code":7760,"language":7581,"meta":38,"style":38},"Description:\n------------\nUser is unable to enter device control panel. The following error is shown on\nthe screen:\n\n\"Device cannot be found. Please contact support team\"\n\nThe issue can be reproduced as following:\n1. Enter APP\n2. Click twice on the \"Control panel\" by using the third button (new tab)\n3. The second tab shows an error\"\n",[36,7762,7763,7771,7776,7810,7815,7819,7824,7828,7841,7848,7871],{"__ignoreMap":38},[281,7764,7765,7768],{"class":283,"line":284},[281,7766,7767],{"class":297},"Description",[281,7769,7770],{"class":291},":\n",[281,7772,7773],{"class":283,"line":220},[281,7774,7775],{"class":7593},"------------\n",[281,7777,7778,7781,7783,7786,7788,7791,7794,7797,7799,7802,7804,7807],{"class":283,"line":313},[281,7779,7780],{"class":291},"User ",[281,7782,66],{"class":297},[281,7784,7785],{"class":291}," unable ",[281,7787,7650],{"class":297},[281,7789,7790],{"class":291}," enter device ",[281,7792,7793],{"class":297},"control",[281,7795,7796],{"class":291}," panel. The ",[281,7798,7713],{"class":297},[281,7800,7801],{"class":291}," error ",[281,7803,66],{"class":297},[281,7805,7806],{"class":291}," shown ",[281,7808,7809],{"class":297},"on\n",[281,7811,7812],{"class":283,"line":322},[281,7813,7814],{"class":291},"the screen:\n",[281,7816,7817],{"class":283,"line":330},[281,7818,450],{"emptyLinePlaceholder":228},[281,7820,7821],{"class":283,"line":338},[281,7822,7823],{"class":306},"\"Device cannot be found. Please contact support team\"\n",[281,7825,7826],{"class":283,"line":346},[281,7827,450],{"emptyLinePlaceholder":228},[281,7829,7830,7833,7836,7839],{"class":283,"line":352},[281,7831,7832],{"class":291},"The issue can be reproduced ",[281,7834,7835],{"class":297},"as",[281,7837,7838],{"class":297}," following",[281,7840,7770],{"class":291},[281,7842,7843,7845],{"class":283,"line":358},[281,7844,3544],{"class":287},[281,7846,7847],{"class":291},". Enter APP\n",[281,7849,7850,7852,7855,7857,7859,7862,7865,7868],{"class":283,"line":488},[281,7851,7726],{"class":287},[281,7853,7854],{"class":291},". Click twice ",[281,7856,7667],{"class":297},[281,7858,7615],{"class":291},[281,7860,7861],{"class":306},"\"Control panel\"",[281,7863,7864],{"class":297}," by",[281,7866,7867],{"class":297}," using",[281,7869,7870],{"class":291}," the third button (new tab)\n",[281,7872,7873,7876,7879,7882,7885],{"class":283,"line":494},[281,7874,7875],{"class":287},"3",[281,7877,7878],{"class":291},". The ",[281,7880,7881],{"class":297},"second",[281,7883,7884],{"class":291}," tab shows an error",[281,7886,7887],{"class":306},"\"\n",[7573,7889,7891],{"id":7890},"bad-example","Bad example",[29,7893,7895],{"className":7579,"code":7894,"language":7581,"meta":38,"style":38},"APP is not working.\n",[36,7896,7897],{"__ignoreMap":38},[281,7898,7899,7902,7904,7907],{"class":283,"line":284},[281,7900,7901],{"class":291},"APP ",[281,7903,66],{"class":297},[281,7905,7906],{"class":297}," not",[281,7908,7909],{"class":291}," working.\n",[6383,7911,7913],{"id":7912},"add-test-criteria","Add test criteria",[16,7915,7916],{},"Apart from how to reproduce an issue, steps are very important for QA (quality\nassurance). If you are not resolving a bug, you need to provide a way\nhow to test this new feature you are implementing.",[16,7918,7919],{},"It is also important to also involve the testing team here. They will need\nfurther information from development about how features can be tested.",[16,7921,7922],{},"Sometimes you may also think that several tickets cannot be tested end-to-end.\nActually these cases are very rare and extreme and each feature and change needs\nto be tested. It is better to try to cover with tests as much as possible.",[6383,7924,7926],{"id":7925},"add-implementation-notes","Add implementation notes",[16,7928,7929,7930,7933],{},"Before working on a ticket, it is important to discuss with developers\nand write down, ",[168,7931,7932],{},"how"," this ticket is going to be implemented.\nThese notes can be very useful during code review or later, since you provide\nsome important insights how this solution works. This area can also be useful\nfor the developers themselves, in order to remember what has been discussed.",[6383,7935,7937],{"id":7936},"it-is-all-about-teamwork","It is all about teamwork",[16,7939,7940],{},"It would be excellent, if the product owners or customers could describe and\nformulate all requirements and criteria properly into tickets. But we also\nneed to understand the limits between people and roles into team.",[16,7942,7943],{},"A good ticket description and organisation is in the end a good teamwork where\nall sides contribute to the same goal. As mentioned above, there are several\nparts in a ticket which need to be written by different roles and usually is\na common work between them.",[6383,7945,5434],{"id":5433},[16,7947,7948],{},"This is a complete example that I may use as a template when I write tickets:",[29,7950,7954],{"className":7951,"code":7952,"language":7953,"meta":38,"style":38},"language-markdown shiki shiki-themes github-dark","Description\n-----------\n\nCustomers are unable to check currently in their shopping cart, how many items\nin total it contains. Having this information in their cart is valuable, in\norder to check if they will also rent a car for transporting them or not.\n\nAcceptance criteria:\n--------------------\n\n* Add in shopping cart summary view the total amount of item it contains\n  - label: \"Total items\" (font-size: 13px, color: blue, line-height: 1.5)\n  - value: (font-size: 13px, color: black, line-height: 1.5)\n\n\nQA test criteria:\n--------------\nPlease perform the end-to-end test below: \n\n* Open shopping application\n* Enter cart summary view page: \u002Fapp\u002Fshopping-cart\u002F\n* Check the value marked in element ID: \"shopping-cart-total-items\"\n* Text stored in element shall always contain the total items stored in shopping cart\n\nImplementation notes:\n---------------------\n\n## Backend ##\n\n* Extend ShoppingCart model to also include \"totalItems\" property (long, nonnull)\n* Extend ShoppingCartService to also calculate required field mentioned above\n* Extend OpenAPI documentation\n\n\n## Frontend ##\n\n* Extend ShoppingCartOverview component and add new field\n\n\n","markdown",[36,7955,7956,7960,7964,7968,7973,7978,7983,7987,7992,7997,8001,8006,8011,8016,8020,8024,8029,8034,8039,8043,8048,8053,8058,8063,8067,8072,8077,8081,8086,8090,8095,8100,8105,8109,8113,8118,8122],{"__ignoreMap":38},[281,7957,7958],{"class":283,"line":284},[281,7959,7588],{},[281,7961,7962],{"class":283,"line":220},[281,7963,7594],{},[281,7965,7966],{"class":283,"line":313},[281,7967,450],{"emptyLinePlaceholder":228},[281,7969,7970],{"class":283,"line":322},[281,7971,7972],{},"Customers are unable to check currently in their shopping cart, how many items\n",[281,7974,7975],{"class":283,"line":330},[281,7976,7977],{},"in total it contains. Having this information in their cart is valuable, in\n",[281,7979,7980],{"class":283,"line":338},[281,7981,7982],{},"order to check if they will also rent a car for transporting them or not.\n",[281,7984,7985],{"class":283,"line":346},[281,7986,450],{"emptyLinePlaceholder":228},[281,7988,7989],{"class":283,"line":352},[281,7990,7991],{},"Acceptance criteria:\n",[281,7993,7994],{"class":283,"line":358},[281,7995,7996],{},"--------------------\n",[281,7998,7999],{"class":283,"line":488},[281,8000,450],{"emptyLinePlaceholder":228},[281,8002,8003],{"class":283,"line":494},[281,8004,8005],{},"* Add in shopping cart summary view the total amount of item it contains\n",[281,8007,8008],{"class":283,"line":505},[281,8009,8010],{},"  - label: \"Total items\" (font-size: 13px, color: blue, line-height: 1.5)\n",[281,8012,8013],{"class":283,"line":511},[281,8014,8015],{},"  - value: (font-size: 13px, color: black, line-height: 1.5)\n",[281,8017,8018],{"class":283,"line":517},[281,8019,450],{"emptyLinePlaceholder":228},[281,8021,8022],{"class":283,"line":522},[281,8023,450],{"emptyLinePlaceholder":228},[281,8025,8026],{"class":283,"line":531},[281,8027,8028],{},"QA test criteria:\n",[281,8030,8031],{"class":283,"line":536},[281,8032,8033],{},"--------------\n",[281,8035,8036],{"class":283,"line":542},[281,8037,8038],{},"Please perform the end-to-end test below: \n",[281,8040,8041],{"class":283,"line":548},[281,8042,450],{"emptyLinePlaceholder":228},[281,8044,8045],{"class":283,"line":558},[281,8046,8047],{},"* Open shopping application\n",[281,8049,8050],{"class":283,"line":563},[281,8051,8052],{},"* Enter cart summary view page: \u002Fapp\u002Fshopping-cart\u002F\n",[281,8054,8055],{"class":283,"line":569},[281,8056,8057],{},"* Check the value marked in element ID: \"shopping-cart-total-items\"\n",[281,8059,8060],{"class":283,"line":574},[281,8061,8062],{},"* Text stored in element shall always contain the total items stored in shopping cart\n",[281,8064,8065],{"class":283,"line":584},[281,8066,450],{"emptyLinePlaceholder":228},[281,8068,8069],{"class":283,"line":589},[281,8070,8071],{},"Implementation notes:\n",[281,8073,8074],{"class":283,"line":595},[281,8075,8076],{},"---------------------\n",[281,8078,8079],{"class":283,"line":601},[281,8080,450],{"emptyLinePlaceholder":228},[281,8082,8083],{"class":283,"line":611},[281,8084,8085],{},"## Backend ##\n",[281,8087,8088],{"class":283,"line":616},[281,8089,450],{"emptyLinePlaceholder":228},[281,8091,8092],{"class":283,"line":622},[281,8093,8094],{},"* Extend ShoppingCart model to also include \"totalItems\" property (long, nonnull)\n",[281,8096,8097],{"class":283,"line":628},[281,8098,8099],{},"* Extend ShoppingCartService to also calculate required field mentioned above\n",[281,8101,8102],{"class":283,"line":646},[281,8103,8104],{},"* Extend OpenAPI documentation\n",[281,8106,8107],{"class":283,"line":655},[281,8108,450],{"emptyLinePlaceholder":228},[281,8110,8111],{"class":283,"line":663},[281,8112,450],{"emptyLinePlaceholder":228},[281,8114,8115],{"class":283,"line":682},[281,8116,8117],{},"## Frontend ##\n",[281,8119,8120],{"class":283,"line":688},[281,8121,450],{"emptyLinePlaceholder":228},[281,8123,8124],{"class":283,"line":693},[281,8125,8126],{},"* Extend ShoppingCartOverview component and add new field\n",[5155,8128,8129],{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}",{"title":38,"searchDepth":220,"depth":220,"links":8131},[8132],{"id":7543,"depth":220,"text":7544,"children":8133},[8134,8135,8136,8137,8138,8139],{"id":7550,"depth":313,"text":7551},{"id":7745,"depth":313,"text":7746},{"id":7912,"depth":313,"text":7913},{"id":7925,"depth":313,"text":7926},{"id":7936,"depth":313,"text":7937},{"id":5433,"depth":313,"text":5434},1550866387,"This is a description 2",{},"\u002Fblog\u002Fhowto-write-tickets",{"title":7535,"description":8141},"blog\u002Fhowto-write-tickets",[],"n1__1nRldsWfvt0nujUHpk3-JvjtZAvannJFPQmMA1Q",{"id":8149,"title":8150,"body":8151,"created":9156,"description":9157,"extension":227,"isVisible":228,"meta":9158,"navigation":228,"path":9159,"seo":9160,"stem":9161,"tags":9162,"__hash__":9163},"blog\u002Fblog\u002Fcase-refactoring-actions-pattern.md","Refactoring actions using patterns",{"type":8,"value":8152,"toc":9146},[8153,8155,8158,8161,8172,8175,8178,8375,8378,8381,8385,8389,8392,8423,8426,8489,8493,8496,8591,8598,8824,8827,8830,8834,8841,8844,9113,9117,9120,9143],[11,8154,14],{"id":13},[16,8156,8157],{},"At the company I was working there was a specific application and \"mechanism\" for\nperforming specific decisions \u002F actions. These actions had several conditions in order to\nbe invoked and it was possible that an action had several smaller ones.",[16,8159,8160],{},"The tech team was having before issues with the code related with this feature. They did the\nfollowing refactoring:",[53,8162,8163,8166,8169],{},[56,8164,8165],{},"Moved all actions into the database",[56,8167,8168],{},"Created a UI tool for administrating these action",[56,8170,8171],{},"An action was possible to be assigned to a \"group\" of actions",[11,8173,8174],{"id":7561},"Problem",[16,8176,8177],{},"Adapting and refactoring this solution had a great impact in the database and\nall actions. A single renaming \u002F refactoring on the database schema needed\nto update all queries stored in the tables. This refactoring took me over 3 days to find and\nadapt all queries stored.",[29,8179,8181],{"className":7579,"code":8180,"language":7581,"meta":38,"style":38},"action\n-------------------\nid | action_name  |\n---+--------------+\n 1 | open network |\n---+--------------+\n\naction_sql\n---+------------------------------------------------+\nid | acton_sql                                      |\n---+------------------------------------------------+\n 1 | UPDATE network SET enabled = 1 WHERE ID = :1   |\n 2 | UPDATE operator SET enabled = 1 WHERE ID = :2  |\n---+------------------------------------------------+\n\naction_action_sql\n---+-----------+---------------+\nid | action_id | action_sql_id |\n---+-----------+---------------+\n 1 | 1         | 1             |\n 2 | 1         | 2             |\n---+-----------+---------------+\n",[36,8182,8183,8188,8192,8197,8202,8216,8220,8224,8229,8234,8239,8243,8281,8314,8318,8322,8327,8332,8337,8341,8357,8371],{"__ignoreMap":38},[281,8184,8185],{"class":283,"line":284},[281,8186,8187],{"class":297},"action\n",[281,8189,8190],{"class":283,"line":220},[281,8191,7684],{"class":7593},[281,8193,8194],{"class":283,"line":313},[281,8195,8196],{"class":291},"id | action_name  |\n",[281,8198,8199],{"class":283,"line":322},[281,8200,8201],{"class":7593},"---+--------------+\n",[281,8203,8204,8207,8210,8213],{"class":283,"line":330},[281,8205,8206],{"class":287}," 1",[281,8208,8209],{"class":291}," | ",[281,8211,8212],{"class":297},"open",[281,8214,8215],{"class":291}," network |\n",[281,8217,8218],{"class":283,"line":338},[281,8219,8201],{"class":7593},[281,8221,8222],{"class":283,"line":346},[281,8223,450],{"emptyLinePlaceholder":228},[281,8225,8226],{"class":283,"line":352},[281,8227,8228],{"class":291},"action_sql\n",[281,8230,8231],{"class":283,"line":358},[281,8232,8233],{"class":7593},"---+------------------------------------------------+\n",[281,8235,8236],{"class":283,"line":488},[281,8237,8238],{"class":291},"id | acton_sql                                      |\n",[281,8240,8241],{"class":283,"line":494},[281,8242,8233],{"class":7593},[281,8244,8245,8247,8249,8252,8255,8258,8261,8263,8265,8268,8271,8273,8276,8278],{"class":283,"line":505},[281,8246,8206],{"class":287},[281,8248,8209],{"class":291},[281,8250,8251],{"class":297},"UPDATE",[281,8253,8254],{"class":291}," network ",[281,8256,8257],{"class":297},"SET",[281,8259,8260],{"class":297}," enabled",[281,8262,298],{"class":297},[281,8264,8206],{"class":287},[281,8266,8267],{"class":297}," WHERE",[281,8269,8270],{"class":291}," ID ",[281,8272,413],{"class":297},[281,8274,8275],{"class":291}," :",[281,8277,3544],{"class":287},[281,8279,8280],{"class":291},"   |\n",[281,8282,8283,8286,8288,8290,8293,8295,8297,8299,8301,8303,8305,8307,8309,8311],{"class":283,"line":511},[281,8284,8285],{"class":287}," 2",[281,8287,8209],{"class":291},[281,8289,8251],{"class":297},[281,8291,8292],{"class":291}," operator ",[281,8294,8257],{"class":297},[281,8296,8260],{"class":297},[281,8298,298],{"class":297},[281,8300,8206],{"class":287},[281,8302,8267],{"class":297},[281,8304,8270],{"class":291},[281,8306,413],{"class":297},[281,8308,8275],{"class":291},[281,8310,7726],{"class":287},[281,8312,8313],{"class":291},"  |\n",[281,8315,8316],{"class":283,"line":517},[281,8317,8233],{"class":7593},[281,8319,8320],{"class":283,"line":522},[281,8321,450],{"emptyLinePlaceholder":228},[281,8323,8324],{"class":283,"line":531},[281,8325,8326],{"class":291},"action_action_sql\n",[281,8328,8329],{"class":283,"line":536},[281,8330,8331],{"class":7593},"---+-----------+---------------+\n",[281,8333,8334],{"class":283,"line":542},[281,8335,8336],{"class":291},"id | action_id | action_sql_id |\n",[281,8338,8339],{"class":283,"line":548},[281,8340,8331],{"class":7593},[281,8342,8343,8345,8347,8349,8352,8354],{"class":283,"line":558},[281,8344,8206],{"class":287},[281,8346,8209],{"class":291},[281,8348,3544],{"class":287},[281,8350,8351],{"class":291},"         | ",[281,8353,3544],{"class":287},[281,8355,8356],{"class":291},"             |\n",[281,8358,8359,8361,8363,8365,8367,8369],{"class":283,"line":563},[281,8360,8285],{"class":287},[281,8362,8209],{"class":291},[281,8364,3544],{"class":287},[281,8366,8351],{"class":291},[281,8368,7726],{"class":287},[281,8370,8356],{"class":291},[281,8372,8373],{"class":283,"line":569},[281,8374,8331],{"class":7593},[16,8376,8377],{},"The schema above seems easy at first sight, but after we add 50 actions with over 200 SQL\nqueries and many groups and inter-dependencies, it becomes very hard to maintain since all\nadaptations need to be done in the database.",[16,8379,8380],{},"At the end, developers built a UI in order to maintain this database schema. But still, changes\nin actions took days and maintainability was low.",[11,8382,8384],{"id":8383},"refactoring","Refactoring",[6383,8386,8388],{"id":8387},"_1st-step-define-business-code","1st step - Define business code",[16,8390,8391],{},"I was definitely sure that I needed to find a solution that SQL queries should go back\nin the source code and services bean.  This way the code and backend will translate all\noperations in the database into real business operations. Instead of maintaining",[29,8393,8395],{"className":7579,"code":8394,"language":7581,"meta":38,"style":38},"UPDATE operator SET enabled = false WHERE ID = 1;\n",[36,8396,8397],{"__ignoreMap":38},[281,8398,8399,8401,8403,8405,8407,8409,8412,8415,8417,8419,8421],{"class":283,"line":284},[281,8400,8251],{"class":297},[281,8402,8292],{"class":291},[281,8404,8257],{"class":297},[281,8406,8260],{"class":297},[281,8408,298],{"class":297},[281,8410,8411],{"class":291}," false ",[281,8413,8414],{"class":297},"WHERE",[281,8416,8270],{"class":291},[281,8418,413],{"class":297},[281,8420,8206],{"class":287},[281,8422,445],{"class":291},[16,8424,8425],{},"We should wrap this query into reusable code:",[29,8427,8431],{"className":8428,"code":8429,"language":8430,"meta":38,"style":38},"language-java shiki shiki-themes github-dark","public interface OperatorService {\n\n  \u002F**\n   * Disable a specific operator by ID\n   *\u002F\n  void  disableById(final Long id);\n}\n","java",[36,8432,8433,8446,8450,8455,8460,8465,8485],{"__ignoreMap":38},[281,8434,8435,8438,8441,8444],{"class":283,"line":284},[281,8436,8437],{"class":297},"public",[281,8439,8440],{"class":297}," interface",[281,8442,8443],{"class":461}," OperatorService",[281,8445,301],{"class":291},[281,8447,8448],{"class":283,"line":220},[281,8449,450],{"emptyLinePlaceholder":228},[281,8451,8452],{"class":283,"line":313},[281,8453,8454],{"class":7593},"  \u002F**\n",[281,8456,8457],{"class":283,"line":322},[281,8458,8459],{"class":7593},"   * Disable a specific operator by ID\n",[281,8461,8462],{"class":283,"line":330},[281,8463,8464],{"class":7593},"   *\u002F\n",[281,8466,8467,8470,8473,8475,8478,8481,8483],{"class":283,"line":338},[281,8468,8469],{"class":297},"  void",[281,8471,8472],{"class":461},"  disableById",[281,8474,751],{"class":291},[281,8476,8477],{"class":297},"final",[281,8479,8480],{"class":291}," Long ",[281,8482,2721],{"class":1115},[281,8484,757],{"class":291},[281,8486,8487],{"class":283,"line":346},[281,8488,361],{"class":291},[6383,8490,8492],{"id":8491},"_2nd-step-the-actions-mechanism","2nd step - The actions mechanism",[16,8494,8495],{},"After understanding that actions could also include \"sub-actions\" inside them, I started\nthinking about the command pattern. This will allow me to write composite commands\nand organize code much better.",[29,8497,8499],{"className":8428,"code":8498,"language":8430,"meta":38,"style":38},"public interface NetworkAction {\n\n    @NonNull\n    String getLabel();\n\n    boolean supports(@NonNull final Network network);\n\n    void execute(@NonNull final Network network);\n}\n",[36,8500,8501,8512,8516,8524,8534,8538,8563,8567,8587],{"__ignoreMap":38},[281,8502,8503,8505,8507,8510],{"class":283,"line":284},[281,8504,8437],{"class":297},[281,8506,8440],{"class":297},[281,8508,8509],{"class":461}," NetworkAction",[281,8511,301],{"class":291},[281,8513,8514],{"class":283,"line":220},[281,8515,450],{"emptyLinePlaceholder":228},[281,8517,8518,8521],{"class":283,"line":313},[281,8519,8520],{"class":291},"    @",[281,8522,8523],{"class":297},"NonNull\n",[281,8525,8526,8529,8532],{"class":283,"line":322},[281,8527,8528],{"class":291},"    String ",[281,8530,8531],{"class":461},"getLabel",[281,8533,1187],{"class":291},[281,8535,8536],{"class":283,"line":330},[281,8537,450],{"emptyLinePlaceholder":228},[281,8539,8540,8543,8546,8549,8552,8555,8558,8561],{"class":283,"line":338},[281,8541,8542],{"class":297},"    boolean",[281,8544,8545],{"class":461}," supports",[281,8547,8548],{"class":291},"(@",[281,8550,8551],{"class":297},"NonNull",[281,8553,8554],{"class":297}," final",[281,8556,8557],{"class":291}," Network ",[281,8559,8560],{"class":1115},"network",[281,8562,757],{"class":291},[281,8564,8565],{"class":283,"line":346},[281,8566,450],{"emptyLinePlaceholder":228},[281,8568,8569,8572,8575,8577,8579,8581,8583,8585],{"class":283,"line":352},[281,8570,8571],{"class":297},"    void",[281,8573,8574],{"class":461}," execute",[281,8576,8548],{"class":291},[281,8578,8551],{"class":297},[281,8580,8554],{"class":297},[281,8582,8557],{"class":291},[281,8584,8560],{"class":1115},[281,8586,757],{"class":291},[281,8588,8589],{"class":283,"line":358},[281,8590,361],{"class":291},[16,8592,8593,8594,8597],{},"The ",[60,8595,8596],{},"supports"," method checks if the action is available for the network provided.\nA simple implementation of the interface abve would be the following:",[29,8599,8601],{"className":8428,"code":8600,"language":8430,"meta":38,"style":38},"@Component\npublic class DisableNetworkAction implements NetworkAction {\n\n    private final NetworkService networkService;\n\n    @Override\n    public String getLabel() {\n        return \"Disable network\";\n    }\n\n\n    public DisableNetworkAction(final NetworkService networkService) {\n        this.networkService = networkService;\n    }\n\n    @Override\n    public boolean supports(final Network network) {\n        return !network.isDisabled();\n    }\n\n    @Override\n    public void execute(Network network) {\n        networkService.disableById(network.getId());\n    }\n\n}\n",[36,8602,8603,8611,8628,8632,8642,8646,8653,8665,8675,8679,8683,8687,8706,8719,8723,8727,8733,8752,8766,8770,8774,8780,8795,8812,8816,8820],{"__ignoreMap":38},[281,8604,8605,8608],{"class":283,"line":284},[281,8606,8607],{"class":291},"@",[281,8609,8610],{"class":297},"Component\n",[281,8612,8613,8615,8618,8621,8624,8626],{"class":283,"line":220},[281,8614,8437],{"class":297},[281,8616,8617],{"class":297}," class",[281,8619,8620],{"class":461}," DisableNetworkAction",[281,8622,8623],{"class":297}," implements",[281,8625,8509],{"class":461},[281,8627,301],{"class":291},[281,8629,8630],{"class":283,"line":313},[281,8631,450],{"emptyLinePlaceholder":228},[281,8633,8634,8637,8639],{"class":283,"line":322},[281,8635,8636],{"class":297},"    private",[281,8638,8554],{"class":297},[281,8640,8641],{"class":291}," NetworkService networkService;\n",[281,8643,8644],{"class":283,"line":330},[281,8645,450],{"emptyLinePlaceholder":228},[281,8647,8648,8650],{"class":283,"line":338},[281,8649,8520],{"class":291},[281,8651,8652],{"class":297},"Override\n",[281,8654,8655,8658,8661,8663],{"class":283,"line":346},[281,8656,8657],{"class":297},"    public",[281,8659,8660],{"class":291}," String ",[281,8662,8531],{"class":461},[281,8664,1924],{"class":291},[281,8666,8667,8670,8673],{"class":283,"line":352},[281,8668,8669],{"class":297},"        return",[281,8671,8672],{"class":306}," \"Disable network\"",[281,8674,445],{"class":291},[281,8676,8677],{"class":283,"line":358},[281,8678,2174],{"class":291},[281,8680,8681],{"class":283,"line":488},[281,8682,450],{"emptyLinePlaceholder":228},[281,8684,8685],{"class":283,"line":494},[281,8686,450],{"emptyLinePlaceholder":228},[281,8688,8689,8691,8693,8695,8697,8700,8703],{"class":283,"line":505},[281,8690,8657],{"class":297},[281,8692,8620],{"class":461},[281,8694,751],{"class":291},[281,8696,8477],{"class":297},[281,8698,8699],{"class":291}," NetworkService ",[281,8701,8702],{"class":1115},"networkService",[281,8704,8705],{"class":291},") {\n",[281,8707,8708,8711,8714,8716],{"class":283,"line":511},[281,8709,8710],{"class":287},"        this",[281,8712,8713],{"class":291},".networkService ",[281,8715,413],{"class":297},[281,8717,8718],{"class":291}," networkService;\n",[281,8720,8721],{"class":283,"line":517},[281,8722,2174],{"class":291},[281,8724,8725],{"class":283,"line":522},[281,8726,450],{"emptyLinePlaceholder":228},[281,8728,8729,8731],{"class":283,"line":531},[281,8730,8520],{"class":291},[281,8732,8652],{"class":297},[281,8734,8735,8737,8740,8742,8744,8746,8748,8750],{"class":283,"line":536},[281,8736,8657],{"class":297},[281,8738,8739],{"class":297}," boolean",[281,8741,8545],{"class":461},[281,8743,751],{"class":291},[281,8745,8477],{"class":297},[281,8747,8557],{"class":291},[281,8749,8560],{"class":1115},[281,8751,8705],{"class":291},[281,8753,8754,8756,8758,8761,8764],{"class":283,"line":542},[281,8755,8669],{"class":297},[281,8757,3939],{"class":297},[281,8759,8760],{"class":291},"network.",[281,8762,8763],{"class":461},"isDisabled",[281,8765,1187],{"class":291},[281,8767,8768],{"class":283,"line":548},[281,8769,2174],{"class":291},[281,8771,8772],{"class":283,"line":558},[281,8773,450],{"emptyLinePlaceholder":228},[281,8775,8776,8778],{"class":283,"line":563},[281,8777,8520],{"class":291},[281,8779,8652],{"class":297},[281,8781,8782,8784,8786,8788,8791,8793],{"class":283,"line":569},[281,8783,8657],{"class":297},[281,8785,3967],{"class":297},[281,8787,8574],{"class":461},[281,8789,8790],{"class":291},"(Network ",[281,8792,8560],{"class":1115},[281,8794,8705],{"class":291},[281,8796,8797,8800,8803,8806,8809],{"class":283,"line":574},[281,8798,8799],{"class":291},"        networkService.",[281,8801,8802],{"class":461},"disableById",[281,8804,8805],{"class":291},"(network.",[281,8807,8808],{"class":461},"getId",[281,8810,8811],{"class":291},"());\n",[281,8813,8814],{"class":283,"line":584},[281,8815,2174],{"class":291},[281,8817,8818],{"class":283,"line":589},[281,8819,450],{"emptyLinePlaceholder":228},[281,8821,8822],{"class":283,"line":595},[281,8823,361],{"class":291},[16,8825,8826],{},"This pattern used differs actually from the command pattern, since all actions\nare stateless. The good part is that this way I can use dependency injection\nand organize my beans. The bad part is that rollback cannot be implemented and\nwas removed.",[16,8828,8829],{},"The fact was that rollback was not even used anymore and all actions had a\na way to revert the previous one. This way I was able to convince business to\nremove this feature.",[6383,8831,8833],{"id":8832},"_3rd-step-conditions-builder","3rd step - conditions builder",[16,8835,8836,8837,8840],{},"After building complicated actions I noticed that the conditions were getting\ncomplicated with many sub conditions and over 5 lines of boolean expressions.\nIn order to make this more readable, I tried to utilize a ",[168,8838,8839],{},"builder"," for\nconditions to transform expressions into a domain-specific language.",[16,8842,8843],{},"After refactoring, a complicated action was looking like the example below:",[29,8845,8847],{"className":8428,"code":8846,"language":8430,"meta":38,"style":38},"public class ShutdownNetworkAction implements NetworkAction {\n\n  private final SetStatusClosedNetworkAction setStatusClosedNetworkAction;\n  private final DisableNetworkAction disableNetworkAction;\n\n  @Autowired\n  public ShutdownNetworkAction(final SetStatusClosedNetworkAction setStatusClosedNetworkAction,\n                               final DisableNetworkAction disableNetworkAction) {\n    this.disableNetworkAction = disableNetworkAction;\n    this.setStatusClosedNetworkAction = setStatusClosedNetworkAction;\n  }\n\n  @Override\n  public String getLabel() {\n    return \"Shutdown network\";\n  }\n\n  @Override\n  public boolean supports(Network network) {\n    return ConditionBuilder.create(network)\n      .isValid()\n      .supportAll(disableNetworkAction, setStatusClosedNetworkAction)\n      .build();\n  }\n\n  @Override\n  public void execute(Network network) {\n    setStatusClosedNetworkAction.execute(network);\n    disableNetworkAction.execute(network);\n  }\n}\n",[36,8848,8849,8864,8868,8878,8887,8891,8899,8918,8931,8943,8955,8959,8963,8969,8979,8988,8992,8996,9002,9016,9029,9038,9048,9057,9061,9065,9071,9085,9096,9105,9109],{"__ignoreMap":38},[281,8850,8851,8853,8855,8858,8860,8862],{"class":283,"line":284},[281,8852,8437],{"class":297},[281,8854,8617],{"class":297},[281,8856,8857],{"class":461}," ShutdownNetworkAction",[281,8859,8623],{"class":297},[281,8861,8509],{"class":461},[281,8863,301],{"class":291},[281,8865,8866],{"class":283,"line":220},[281,8867,450],{"emptyLinePlaceholder":228},[281,8869,8870,8873,8875],{"class":283,"line":313},[281,8871,8872],{"class":297},"  private",[281,8874,8554],{"class":297},[281,8876,8877],{"class":291}," SetStatusClosedNetworkAction setStatusClosedNetworkAction;\n",[281,8879,8880,8882,8884],{"class":283,"line":322},[281,8881,8872],{"class":297},[281,8883,8554],{"class":297},[281,8885,8886],{"class":291}," DisableNetworkAction disableNetworkAction;\n",[281,8888,8889],{"class":283,"line":330},[281,8890,450],{"emptyLinePlaceholder":228},[281,8892,8893,8896],{"class":283,"line":338},[281,8894,8895],{"class":291},"  @",[281,8897,8898],{"class":297},"Autowired\n",[281,8900,8901,8904,8906,8908,8910,8913,8916],{"class":283,"line":346},[281,8902,8903],{"class":297},"  public",[281,8905,8857],{"class":461},[281,8907,751],{"class":291},[281,8909,8477],{"class":297},[281,8911,8912],{"class":291}," SetStatusClosedNetworkAction ",[281,8914,8915],{"class":1115},"setStatusClosedNetworkAction",[281,8917,319],{"class":291},[281,8919,8920,8923,8926,8929],{"class":283,"line":352},[281,8921,8922],{"class":297},"                               final",[281,8924,8925],{"class":291}," DisableNetworkAction ",[281,8927,8928],{"class":1115},"disableNetworkAction",[281,8930,8705],{"class":291},[281,8932,8933,8935,8938,8940],{"class":283,"line":358},[281,8934,2114],{"class":287},[281,8936,8937],{"class":291},".disableNetworkAction ",[281,8939,413],{"class":297},[281,8941,8942],{"class":291}," disableNetworkAction;\n",[281,8944,8945,8947,8950,8952],{"class":283,"line":488},[281,8946,2114],{"class":287},[281,8948,8949],{"class":291},".setStatusClosedNetworkAction ",[281,8951,413],{"class":297},[281,8953,8954],{"class":291}," setStatusClosedNetworkAction;\n",[281,8956,8957],{"class":283,"line":494},[281,8958,5658],{"class":291},[281,8960,8961],{"class":283,"line":505},[281,8962,450],{"emptyLinePlaceholder":228},[281,8964,8965,8967],{"class":283,"line":511},[281,8966,8895],{"class":291},[281,8968,8652],{"class":297},[281,8970,8971,8973,8975,8977],{"class":283,"line":517},[281,8972,8903],{"class":297},[281,8974,8660],{"class":291},[281,8976,8531],{"class":461},[281,8978,1924],{"class":291},[281,8980,8981,8983,8986],{"class":283,"line":522},[281,8982,1929],{"class":297},[281,8984,8985],{"class":306}," \"Shutdown network\"",[281,8987,445],{"class":291},[281,8989,8990],{"class":283,"line":531},[281,8991,5658],{"class":291},[281,8993,8994],{"class":283,"line":536},[281,8995,450],{"emptyLinePlaceholder":228},[281,8997,8998,9000],{"class":283,"line":542},[281,8999,8895],{"class":291},[281,9001,8652],{"class":297},[281,9003,9004,9006,9008,9010,9012,9014],{"class":283,"line":548},[281,9005,8903],{"class":297},[281,9007,8739],{"class":297},[281,9009,8545],{"class":461},[281,9011,8790],{"class":291},[281,9013,8560],{"class":1115},[281,9015,8705],{"class":291},[281,9017,9018,9020,9023,9026],{"class":283,"line":558},[281,9019,1929],{"class":297},[281,9021,9022],{"class":291}," ConditionBuilder.",[281,9024,9025],{"class":461},"create",[281,9027,9028],{"class":291},"(network)\n",[281,9030,9031,9033,9036],{"class":283,"line":563},[281,9032,5872],{"class":291},[281,9034,9035],{"class":461},"isValid",[281,9037,6117],{"class":291},[281,9039,9040,9042,9045],{"class":283,"line":569},[281,9041,5872],{"class":291},[281,9043,9044],{"class":461},"supportAll",[281,9046,9047],{"class":291},"(disableNetworkAction, setStatusClosedNetworkAction)\n",[281,9049,9050,9052,9055],{"class":283,"line":574},[281,9051,5872],{"class":291},[281,9053,9054],{"class":461},"build",[281,9056,1187],{"class":291},[281,9058,9059],{"class":283,"line":584},[281,9060,5658],{"class":291},[281,9062,9063],{"class":283,"line":589},[281,9064,450],{"emptyLinePlaceholder":228},[281,9066,9067,9069],{"class":283,"line":595},[281,9068,8895],{"class":291},[281,9070,8652],{"class":297},[281,9072,9073,9075,9077,9079,9081,9083],{"class":283,"line":601},[281,9074,8903],{"class":297},[281,9076,3967],{"class":297},[281,9078,8574],{"class":461},[281,9080,8790],{"class":291},[281,9082,8560],{"class":1115},[281,9084,8705],{"class":291},[281,9086,9087,9090,9093],{"class":283,"line":611},[281,9088,9089],{"class":291},"    setStatusClosedNetworkAction.",[281,9091,9092],{"class":461},"execute",[281,9094,9095],{"class":291},"(network);\n",[281,9097,9098,9101,9103],{"class":283,"line":616},[281,9099,9100],{"class":291},"    disableNetworkAction.",[281,9102,9092],{"class":461},[281,9104,9095],{"class":291},[281,9106,9107],{"class":283,"line":622},[281,9108,5658],{"class":291},[281,9110,9111],{"class":283,"line":628},[281,9112,361],{"class":291},[11,9114,9116],{"id":9115},"summary","Summary",[16,9118,9119],{},"Here are few thoughts from what I learned from this refactoring",[53,9121,9122,9125,9128,9131,9137],{},[56,9123,9124],{},"Business code belongs in services and need to be reusable",[56,9126,9127],{},"No queries shall be stored in a database schema",[56,9129,9130],{},"If data are not modified by end-users, maybe it is not a good idea to\nstore them in database",[56,9132,381,9133,9136],{},[60,9134,9135],{},"command pattern"," can help a lot when actions are composite and consist\nof smaller sub-actions",[56,9138,8593,9139,9142],{},[60,9140,9141],{},"builder pattern"," can help you build your own domain-specific language",[5155,9144,9145],{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}",{"title":38,"searchDepth":220,"depth":220,"links":9147},[9148,9149,9150,9155],{"id":13,"depth":220,"text":14},{"id":7561,"depth":220,"text":8174},{"id":8383,"depth":220,"text":8384,"children":9151},[9152,9153,9154],{"id":8387,"depth":313,"text":8388},{"id":8491,"depth":313,"text":8492},{"id":8832,"depth":313,"text":8833},{"id":9115,"depth":220,"text":9116},1521498900,"This is a description 1",{},"\u002Fblog\u002Fcase-refactoring-actions-pattern",{"title":8150,"description":9157},"blog\u002Fcase-refactoring-actions-pattern",[],"TRGkUfQxVsn6KDWzXSm7DRItPcRiXVFQ6VwqtqgMrMs",{"id":9165,"title":9166,"body":9167,"created":9156,"description":10130,"extension":227,"isVisible":228,"meta":10131,"navigation":228,"path":10132,"seo":10133,"stem":10134,"tags":10135,"__hash__":10136},"blog\u002Fblog\u002Fcase-refactoring-sql-queries.md","Refactoring complicated SQL queries using damn simple query builder",{"type":8,"value":9168,"toc":10123},[9169,9171,9174,9177,9395,9398,9400,9404,9407,9410,9747,9750,9753,9757,9760,9763,9809,9816,9875,9878,10117,10120],[11,9170,8174],{"id":7561},[16,9172,9173],{},"An administrating application included an \"advanced search\" query for finding routes.\nThere were around 10 search criteria that could be combined together.\nAlso this query was using around 10 tables in the database.",[16,9175,9176],{},"Previous code was looking something like this before:",[29,9178,9180],{"className":8428,"code":9179,"language":8430,"meta":38,"style":38},"public class OperatorController {\n\n  @Autowired\n  private OperatorRepository operatorRepository;\n\n  public List\u003COperator> search() {\n    if (!nameInputField.isEmpty() && !classificationField.isEmpty() && !availabilityField.isEmpty()) {\n      return operatorRepository.findByNameAndClassificationAndAvailability(nameInputField,\n        classificationField, availabilityField);\n    } else if (!nameInputField.isEmpty() && !classificationField.isEmpty()) {\n      return operatorRepository.findByNameAndClassification(nameInputField, classificationField);\n    } else if (!nameInputField.isEmpty()) {\n      return operatorRepository.findByName(nameInputField);\n    } else {\n      return Collections.emptyList();\n    }\n  }\n}\n",[36,9181,9182,9193,9197,9203,9210,9214,9231,9273,9286,9291,9321,9333,9351,9363,9371,9383,9387,9391],{"__ignoreMap":38},[281,9183,9184,9186,9188,9191],{"class":283,"line":284},[281,9185,8437],{"class":297},[281,9187,8617],{"class":297},[281,9189,9190],{"class":461}," OperatorController",[281,9192,301],{"class":291},[281,9194,9195],{"class":283,"line":220},[281,9196,450],{"emptyLinePlaceholder":228},[281,9198,9199,9201],{"class":283,"line":313},[281,9200,8895],{"class":291},[281,9202,8898],{"class":297},[281,9204,9205,9207],{"class":283,"line":322},[281,9206,8872],{"class":297},[281,9208,9209],{"class":291}," OperatorRepository operatorRepository;\n",[281,9211,9212],{"class":283,"line":330},[281,9213,450],{"emptyLinePlaceholder":228},[281,9215,9216,9218,9221,9224,9226,9229],{"class":283,"line":338},[281,9217,8903],{"class":297},[281,9219,9220],{"class":291}," List\u003C",[281,9222,9223],{"class":297},"Operator",[281,9225,1134],{"class":291},[281,9227,9228],{"class":461},"search",[281,9230,1924],{"class":291},[281,9232,9233,9236,9238,9240,9243,9246,9249,9252,9254,9257,9259,9261,9263,9265,9268,9270],{"class":283,"line":346},[281,9234,9235],{"class":297},"    if",[281,9237,734],{"class":291},[281,9239,4120],{"class":297},[281,9241,9242],{"class":291},"nameInputField.",[281,9244,9245],{"class":461},"isEmpty",[281,9247,9248],{"class":291},"() ",[281,9250,9251],{"class":297},"&&",[281,9253,3939],{"class":297},[281,9255,9256],{"class":291},"classificationField.",[281,9258,9245],{"class":461},[281,9260,9248],{"class":291},[281,9262,9251],{"class":297},[281,9264,3939],{"class":297},[281,9266,9267],{"class":291},"availabilityField.",[281,9269,9245],{"class":461},[281,9271,9272],{"class":291},"()) {\n",[281,9274,9275,9277,9280,9283],{"class":283,"line":352},[281,9276,649],{"class":297},[281,9278,9279],{"class":291}," operatorRepository.",[281,9281,9282],{"class":461},"findByNameAndClassificationAndAvailability",[281,9284,9285],{"class":291},"(nameInputField,\n",[281,9287,9288],{"class":283,"line":358},[281,9289,9290],{"class":291},"        classificationField, availabilityField);\n",[281,9292,9293,9295,9298,9301,9303,9305,9307,9309,9311,9313,9315,9317,9319],{"class":283,"line":488},[281,9294,2150],{"class":291},[281,9296,9297],{"class":297},"else",[281,9299,9300],{"class":297}," if",[281,9302,734],{"class":291},[281,9304,4120],{"class":297},[281,9306,9242],{"class":291},[281,9308,9245],{"class":461},[281,9310,9248],{"class":291},[281,9312,9251],{"class":297},[281,9314,3939],{"class":297},[281,9316,9256],{"class":291},[281,9318,9245],{"class":461},[281,9320,9272],{"class":291},[281,9322,9323,9325,9327,9330],{"class":283,"line":494},[281,9324,649],{"class":297},[281,9326,9279],{"class":291},[281,9328,9329],{"class":461},"findByNameAndClassification",[281,9331,9332],{"class":291},"(nameInputField, classificationField);\n",[281,9334,9335,9337,9339,9341,9343,9345,9347,9349],{"class":283,"line":505},[281,9336,2150],{"class":291},[281,9338,9297],{"class":297},[281,9340,9300],{"class":297},[281,9342,734],{"class":291},[281,9344,4120],{"class":297},[281,9346,9242],{"class":291},[281,9348,9245],{"class":461},[281,9350,9272],{"class":291},[281,9352,9353,9355,9357,9360],{"class":283,"line":511},[281,9354,649],{"class":297},[281,9356,9279],{"class":291},[281,9358,9359],{"class":461},"findByName",[281,9361,9362],{"class":291},"(nameInputField);\n",[281,9364,9365,9367,9369],{"class":283,"line":517},[281,9366,2150],{"class":291},[281,9368,9297],{"class":297},[281,9370,301],{"class":291},[281,9372,9373,9375,9378,9381],{"class":283,"line":522},[281,9374,649],{"class":297},[281,9376,9377],{"class":291}," Collections.",[281,9379,9380],{"class":461},"emptyList",[281,9382,1187],{"class":291},[281,9384,9385],{"class":283,"line":531},[281,9386,2174],{"class":291},[281,9388,9389],{"class":283,"line":536},[281,9390,5658],{"class":291},[281,9392,9393],{"class":283,"line":542},[281,9394,361],{"class":291},[16,9396,9397],{},"Different combinations were causing writing a unique query and adding a new \"if\"\nclause in code. Removing \u002F Adding search criteria was quite hard and took much\ntime.",[11,9399,8384],{"id":8383},[6383,9401,9403],{"id":9402},"introduce-service-bean","Introduce service bean",[16,9405,9406],{},"The first thing that was clear to me was to separate controller from service logic.\nI tried to ban all repository beans from controllers and introduce a service bean\nbetween them. This way, I could list all operations on Operator domain object into\na single bean.",[16,9408,9409],{},"The method would look now like this:",[29,9411,9413],{"className":8428,"code":9412,"language":8430,"meta":38,"style":38},"public class OperatorService {\n\n  private final OperatorRepository operatorRepository;\n\n  @Autowired\n  public OperatorService(final OperatorRepository operatorRepository) {\n    this.operatorRepository = operatorRepository;\n  }\n\n  public List\u003COperator> search(final Map&lt;String, Serializable&gt; fields) {\n    if (fields.contains(\"name\") && fields.contains(\"classification\") && fields.contains(\"availability\")) {\n      final Classification classification = (Classification) fields.get(\"classification\");\n      final Availability availability = (Availability) fields.get(\"availability\");\n      final String name = fields.get(\"name\").toString();\n      return operatorRepository.findByNameAndClassificationAndAvailability(name,\n        classification, availability);\n    } else if (fields.contains(\"name\") && fields.contains(\"classification\")) {\n      return operatorRepository.findByNameAndClassification(name, classification);\n    } else if (...) {\n      ....\n    } else {\n      ...\n    }\n  }\n\n}\n\n",[36,9414,9415,9425,9429,9437,9441,9447,9465,9477,9481,9485,9534,9579,9601,9621,9645,9656,9661,9691,9702,9713,9718,9726,9731,9735,9739,9743],{"__ignoreMap":38},[281,9416,9417,9419,9421,9423],{"class":283,"line":284},[281,9418,8437],{"class":297},[281,9420,8617],{"class":297},[281,9422,8443],{"class":461},[281,9424,301],{"class":291},[281,9426,9427],{"class":283,"line":220},[281,9428,450],{"emptyLinePlaceholder":228},[281,9430,9431,9433,9435],{"class":283,"line":313},[281,9432,8872],{"class":297},[281,9434,8554],{"class":297},[281,9436,9209],{"class":291},[281,9438,9439],{"class":283,"line":322},[281,9440,450],{"emptyLinePlaceholder":228},[281,9442,9443,9445],{"class":283,"line":330},[281,9444,8895],{"class":291},[281,9446,8898],{"class":297},[281,9448,9449,9451,9453,9455,9457,9460,9463],{"class":283,"line":338},[281,9450,8903],{"class":297},[281,9452,8443],{"class":461},[281,9454,751],{"class":291},[281,9456,8477],{"class":297},[281,9458,9459],{"class":291}," OperatorRepository ",[281,9461,9462],{"class":1115},"operatorRepository",[281,9464,8705],{"class":291},[281,9466,9467,9469,9472,9474],{"class":283,"line":346},[281,9468,2114],{"class":287},[281,9470,9471],{"class":291},".operatorRepository ",[281,9473,413],{"class":297},[281,9475,9476],{"class":291}," operatorRepository;\n",[281,9478,9479],{"class":283,"line":352},[281,9480,5658],{"class":291},[281,9482,9483],{"class":283,"line":358},[281,9484,450],{"emptyLinePlaceholder":228},[281,9486,9487,9489,9491,9493,9495,9497,9499,9501,9504,9507,9510,9513,9516,9518,9521,9523,9526,9529,9532],{"class":283,"line":488},[281,9488,8903],{"class":297},[281,9490,9220],{"class":291},[281,9492,9223],{"class":297},[281,9494,1134],{"class":291},[281,9496,9228],{"class":461},[281,9498,751],{"class":291},[281,9500,8477],{"class":297},[281,9502,9503],{"class":1115}," Map",[281,9505,9506],{"class":291},"&",[281,9508,9509],{"class":1115},"lt",[281,9511,9512],{"class":291},";",[281,9514,9515],{"class":1115},"String",[281,9517,2052],{"class":291},[281,9519,9520],{"class":1115},"Serializable",[281,9522,9506],{"class":291},[281,9524,9525],{"class":1115},"gt",[281,9527,9528],{"class":291},"; ",[281,9530,9531],{"class":1115},"fields",[281,9533,8705],{"class":291},[281,9535,9536,9538,9541,9544,9546,9549,9551,9553,9556,9558,9560,9563,9565,9567,9569,9571,9573,9576],{"class":283,"line":494},[281,9537,9235],{"class":297},[281,9539,9540],{"class":291}," (fields.",[281,9542,9543],{"class":461},"contains",[281,9545,751],{"class":291},[281,9547,9548],{"class":306},"\"name\"",[281,9550,2027],{"class":291},[281,9552,9251],{"class":297},[281,9554,9555],{"class":291}," fields.",[281,9557,9543],{"class":461},[281,9559,751],{"class":291},[281,9561,9562],{"class":306},"\"classification\"",[281,9564,2027],{"class":291},[281,9566,9251],{"class":297},[281,9568,9555],{"class":291},[281,9570,9543],{"class":461},[281,9572,751],{"class":291},[281,9574,9575],{"class":306},"\"availability\"",[281,9577,9578],{"class":291},")) {\n",[281,9580,9581,9584,9587,9589,9592,9595,9597,9599],{"class":283,"line":505},[281,9582,9583],{"class":297},"      final",[281,9585,9586],{"class":291}," Classification classification ",[281,9588,413],{"class":297},[281,9590,9591],{"class":291}," (Classification) fields.",[281,9593,9594],{"class":461},"get",[281,9596,751],{"class":291},[281,9598,9562],{"class":306},[281,9600,757],{"class":291},[281,9602,9603,9605,9608,9610,9613,9615,9617,9619],{"class":283,"line":511},[281,9604,9583],{"class":297},[281,9606,9607],{"class":291}," Availability availability ",[281,9609,413],{"class":297},[281,9611,9612],{"class":291}," (Availability) fields.",[281,9614,9594],{"class":461},[281,9616,751],{"class":291},[281,9618,9575],{"class":306},[281,9620,757],{"class":291},[281,9622,9623,9625,9628,9630,9632,9634,9636,9638,9640,9643],{"class":283,"line":517},[281,9624,9583],{"class":297},[281,9626,9627],{"class":291}," String name ",[281,9629,413],{"class":297},[281,9631,9555],{"class":291},[281,9633,9594],{"class":461},[281,9635,751],{"class":291},[281,9637,9548],{"class":306},[281,9639,1215],{"class":291},[281,9641,9642],{"class":461},"toString",[281,9644,1187],{"class":291},[281,9646,9647,9649,9651,9653],{"class":283,"line":522},[281,9648,649],{"class":297},[281,9650,9279],{"class":291},[281,9652,9282],{"class":461},[281,9654,9655],{"class":291},"(name,\n",[281,9657,9658],{"class":283,"line":531},[281,9659,9660],{"class":291},"        classification, availability);\n",[281,9662,9663,9665,9667,9669,9671,9673,9675,9677,9679,9681,9683,9685,9687,9689],{"class":283,"line":536},[281,9664,2150],{"class":291},[281,9666,9297],{"class":297},[281,9668,9300],{"class":297},[281,9670,9540],{"class":291},[281,9672,9543],{"class":461},[281,9674,751],{"class":291},[281,9676,9548],{"class":306},[281,9678,2027],{"class":291},[281,9680,9251],{"class":297},[281,9682,9555],{"class":291},[281,9684,9543],{"class":461},[281,9686,751],{"class":291},[281,9688,9562],{"class":306},[281,9690,9578],{"class":291},[281,9692,9693,9695,9697,9699],{"class":283,"line":542},[281,9694,649],{"class":297},[281,9696,9279],{"class":291},[281,9698,9329],{"class":461},[281,9700,9701],{"class":291},"(name, classification);\n",[281,9703,9704,9706,9708,9710],{"class":283,"line":548},[281,9705,2150],{"class":291},[281,9707,9297],{"class":297},[281,9709,9300],{"class":297},[281,9711,9712],{"class":291}," (...) {\n",[281,9714,9715],{"class":283,"line":558},[281,9716,9717],{"class":291},"      ....\n",[281,9719,9720,9722,9724],{"class":283,"line":563},[281,9721,2150],{"class":291},[281,9723,9297],{"class":297},[281,9725,301],{"class":291},[281,9727,9728],{"class":283,"line":569},[281,9729,9730],{"class":291},"      ...\n",[281,9732,9733],{"class":283,"line":574},[281,9734,2174],{"class":291},[281,9736,9737],{"class":283,"line":584},[281,9738,5658],{"class":291},[281,9740,9741],{"class":283,"line":589},[281,9742,450],{"emptyLinePlaceholder":228},[281,9744,9745],{"class":283,"line":595},[281,9746,361],{"class":291},[16,9748,9749],{},"Still the main trouble is this control structure that has no sense and feels\nlike a boilerplate code.",[16,9751,9752],{},"In the beginning I moved all fields into a simple map. If clauses were still\ninfecting the service bean, but I separated business logic from the UI at least",[6383,9754,9756],{"id":9755},"extensive-queries","Extensive queries",[16,9758,9759],{},"The main reason that I had to keep the \"if\" clauses were actual boundaries in the way\nwe were writing SQL queries with JPA and @Query annotations. After some research I\ncrossed upon the JPA Metamodel and criteria as a way to programmatically write\nall SQL queries",[16,9761,9762],{},"After some tests and experiments I noticed that JPA criteria were relatively complicated\nand not straight-forward. For fun and profit, I decided to create my own way on top of\nthis solution. My goal was to write something like this:",[29,9764,9766],{"className":8428,"code":9765,"language":8430,"meta":38,"style":38},"select().from(OperatorEntity.class).where(OperatorEntity_.name)\n   .isEqualToIgnoreCase(\"test\")\n   .findOne();\n",[36,9767,9768,9785,9800],{"__ignoreMap":38},[281,9769,9770,9772,9774,9776,9779,9782],{"class":283,"line":284},[281,9771,5860],{"class":461},[281,9773,5967],{"class":291},[281,9775,439],{"class":461},[281,9777,9778],{"class":291},"(OperatorEntity.class).",[281,9780,9781],{"class":461},"where",[281,9783,9784],{"class":291},"(OperatorEntity_.name)\n",[281,9786,9787,9790,9793,9795,9798],{"class":283,"line":220},[281,9788,9789],{"class":291},"   .",[281,9791,9792],{"class":461},"isEqualToIgnoreCase",[281,9794,751],{"class":291},[281,9796,9797],{"class":306},"\"test\"",[281,9799,5883],{"class":291},[281,9801,9802,9804,9807],{"class":283,"line":313},[281,9803,9789],{"class":291},[281,9805,9806],{"class":461},"findOne",[281,9808,1187],{"class":291},[16,9810,9811,9812,9815],{},"On top of this builder, I was able to wrap it with a ",[6067,9813,9814],{},"QueryBuilder"," that checks nulls\nand dynamically builds the query accordingly",[29,9817,9819],{"className":8428,"code":9818,"language":8430,"meta":38,"style":38},"SearchQueryBuilder\n  .fromFields(fields)\n  .resultingEntityClass(OperatorEntity.class)\n  .where(OperatorEntity_.name)\n  .isEqualToIgnoreCase(\"name\")\n  .findOne()\n",[36,9820,9821,9826,9837,9847,9855,9867],{"__ignoreMap":38},[281,9822,9823],{"class":283,"line":284},[281,9824,9825],{"class":291},"SearchQueryBuilder\n",[281,9827,9828,9831,9834],{"class":283,"line":220},[281,9829,9830],{"class":291},"  .",[281,9832,9833],{"class":461},"fromFields",[281,9835,9836],{"class":291},"(fields)\n",[281,9838,9839,9841,9844],{"class":283,"line":313},[281,9840,9830],{"class":291},[281,9842,9843],{"class":461},"resultingEntityClass",[281,9845,9846],{"class":291},"(OperatorEntity.class)\n",[281,9848,9849,9851,9853],{"class":283,"line":322},[281,9850,9830],{"class":291},[281,9852,9781],{"class":461},[281,9854,9784],{"class":291},[281,9856,9857,9859,9861,9863,9865],{"class":283,"line":330},[281,9858,9830],{"class":291},[281,9860,9792],{"class":461},[281,9862,751],{"class":291},[281,9864,9548],{"class":306},[281,9866,5883],{"class":291},[281,9868,9869,9871,9873],{"class":283,"line":338},[281,9870,9830],{"class":291},[281,9872,9806],{"class":461},[281,9874,6117],{"class":291},[16,9876,9877],{},"After this refactoring, the final code was looking something like this:",[29,9879,9881],{"className":8428,"code":9880,"language":8430,"meta":38,"style":38},"  public class OperatorService {\n\n    private final OperatorRepository operatorRepository;\n    private final OperatorEntityToOperatorConverter converter;\n\n    @Autowired\n    public OperatorService(final OperatorRepository operatorRepository,\n      final OperatorEntityToOperatorConverter converter) {\n      this.operatorRepository = operatorRepository;\n      this.converter = converter;\n    }\n\n    public List\u003COperator> search(final Map\u003CString, Serializable> fields) {\n      return SearchQueryBuilder.fromFields(fields)\n        .resultingEntityClass(OperatorEntity.class)\n        .where(OperatorEntity_.name).isEqualToIgnoreCase(\"name\")\n        .and(OperatorEntity_.classification).isEqualByObject(\"classification\")\n        .and(OperatorEntity_.availability).isEqualToObject(\"availability\")\n        .map(OperatorEntityToOperatorConverter::convert)\n        .find();\n    }\n  }\n\n",[36,9882,9883,9893,9897,9905,9914,9918,9924,9940,9952,9962,9974,9978,9982,10013,10024,10032,10049,10067,10085,10101,10109,10113],{"__ignoreMap":38},[281,9884,9885,9887,9889,9891],{"class":283,"line":284},[281,9886,8903],{"class":297},[281,9888,8617],{"class":297},[281,9890,8443],{"class":461},[281,9892,301],{"class":291},[281,9894,9895],{"class":283,"line":220},[281,9896,450],{"emptyLinePlaceholder":228},[281,9898,9899,9901,9903],{"class":283,"line":313},[281,9900,8636],{"class":297},[281,9902,8554],{"class":297},[281,9904,9209],{"class":291},[281,9906,9907,9909,9911],{"class":283,"line":322},[281,9908,8636],{"class":297},[281,9910,8554],{"class":297},[281,9912,9913],{"class":291}," OperatorEntityToOperatorConverter converter;\n",[281,9915,9916],{"class":283,"line":330},[281,9917,450],{"emptyLinePlaceholder":228},[281,9919,9920,9922],{"class":283,"line":338},[281,9921,8520],{"class":291},[281,9923,8898],{"class":297},[281,9925,9926,9928,9930,9932,9934,9936,9938],{"class":283,"line":346},[281,9927,8657],{"class":297},[281,9929,8443],{"class":461},[281,9931,751],{"class":291},[281,9933,8477],{"class":297},[281,9935,9459],{"class":291},[281,9937,9462],{"class":1115},[281,9939,319],{"class":291},[281,9941,9942,9944,9947,9950],{"class":283,"line":352},[281,9943,9583],{"class":297},[281,9945,9946],{"class":291}," OperatorEntityToOperatorConverter ",[281,9948,9949],{"class":1115},"converter",[281,9951,8705],{"class":291},[281,9953,9954,9956,9958,9960],{"class":283,"line":358},[281,9955,2160],{"class":287},[281,9957,9471],{"class":291},[281,9959,413],{"class":297},[281,9961,9476],{"class":291},[281,9963,9964,9966,9969,9971],{"class":283,"line":488},[281,9965,2160],{"class":287},[281,9967,9968],{"class":291},".converter ",[281,9970,413],{"class":297},[281,9972,9973],{"class":291}," converter;\n",[281,9975,9976],{"class":283,"line":494},[281,9977,2174],{"class":291},[281,9979,9980],{"class":283,"line":505},[281,9981,450],{"emptyLinePlaceholder":228},[281,9983,9984,9986,9988,9990,9992,9994,9996,9998,10001,10003,10005,10007,10009,10011],{"class":283,"line":511},[281,9985,8657],{"class":297},[281,9987,9220],{"class":291},[281,9989,9223],{"class":297},[281,9991,1134],{"class":291},[281,9993,9228],{"class":461},[281,9995,751],{"class":291},[281,9997,8477],{"class":297},[281,9999,10000],{"class":291}," Map\u003C",[281,10002,9515],{"class":297},[281,10004,2052],{"class":291},[281,10006,9520],{"class":297},[281,10008,1134],{"class":291},[281,10010,9531],{"class":1115},[281,10012,8705],{"class":291},[281,10014,10015,10017,10020,10022],{"class":283,"line":517},[281,10016,649],{"class":297},[281,10018,10019],{"class":291}," SearchQueryBuilder.",[281,10021,9833],{"class":461},[281,10023,9836],{"class":291},[281,10025,10026,10028,10030],{"class":283,"line":522},[281,10027,7143],{"class":291},[281,10029,9843],{"class":461},[281,10031,9846],{"class":291},[281,10033,10034,10036,10038,10041,10043,10045,10047],{"class":283,"line":531},[281,10035,7143],{"class":291},[281,10037,9781],{"class":461},[281,10039,10040],{"class":291},"(OperatorEntity_.name).",[281,10042,9792],{"class":461},[281,10044,751],{"class":291},[281,10046,9548],{"class":306},[281,10048,5883],{"class":291},[281,10050,10051,10053,10055,10058,10061,10063,10065],{"class":283,"line":536},[281,10052,7143],{"class":291},[281,10054,7661],{"class":461},[281,10056,10057],{"class":291},"(OperatorEntity_.classification).",[281,10059,10060],{"class":461},"isEqualByObject",[281,10062,751],{"class":291},[281,10064,9562],{"class":306},[281,10066,5883],{"class":291},[281,10068,10069,10071,10073,10076,10079,10081,10083],{"class":283,"line":542},[281,10070,7143],{"class":291},[281,10072,7661],{"class":461},[281,10074,10075],{"class":291},"(OperatorEntity_.availability).",[281,10077,10078],{"class":461},"isEqualToObject",[281,10080,751],{"class":291},[281,10082,9575],{"class":306},[281,10084,5883],{"class":291},[281,10086,10087,10089,10092,10095,10098],{"class":283,"line":548},[281,10088,7143],{"class":291},[281,10090,10091],{"class":461},"map",[281,10093,10094],{"class":291},"(OperatorEntityToOperatorConverter",[281,10096,10097],{"class":297},"::",[281,10099,10100],{"class":291},"convert)\n",[281,10102,10103,10105,10107],{"class":283,"line":558},[281,10104,7143],{"class":291},[281,10106,1207],{"class":461},[281,10108,1187],{"class":291},[281,10110,10111],{"class":283,"line":563},[281,10112,2174],{"class":291},[281,10114,10115],{"class":283,"line":569},[281,10116,5658],{"class":291},[16,10118,10119],{},"Much better, right?",[5155,10121,10122],{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}",{"title":38,"searchDepth":220,"depth":220,"links":10124},[10125,10126],{"id":7561,"depth":220,"text":8174},{"id":8383,"depth":220,"text":8384,"children":10127},[10128,10129],{"id":9402,"depth":313,"text":9403},{"id":9755,"depth":313,"text":9756},"This is a description 3",{},"\u002Fblog\u002Fcase-refactoring-sql-queries",{"title":9166,"description":10130},"blog\u002Fcase-refactoring-sql-queries",[],"XDosLUKC-4x06CyDgnyxf3Sdmv9AKLCw8K8K9q4_EDo",1786914024011]