{"@attributes":{"version":"2.0"},"channel":{"title":"DEV Community: Shi Ling","description":"The latest articles on DEV Community by Shi Ling (@shiling).","link":"https:\/\/dev.to\/shiling","image":{"url":"https:\/\/media2.dev.to\/dynamic\/image\/width=90,height=90,fit=cover,gravity=auto,format=auto\/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F11766%2F4a059374-4b4f-4777-bf0a-8a0301476298.png","title":"DEV Community: Shi Ling","link":"https:\/\/dev.to\/shiling"},"language":"en","item":[{"title":"Got frustrated with the docs, so I made a Playwright Cheatsheet","pubDate":"Thu, 11 Sep 2025 23:05:25 +0000","link":"https:\/\/dev.to\/shiling\/got-frustrated-with-the-docs-so-i-made-a-playwright-cheatsheet-1aaf","guid":"https:\/\/dev.to\/shiling\/got-frustrated-with-the-docs-so-i-made-a-playwright-cheatsheet-1aaf","description":"<p>Every time I need to look up the <strong>Playwright<\/strong> docs, I open about 10+ tabs just to piece together what I need to know to solve my problem. And the doc pages... they are so long! Search is meh - I usually have to open about 3 results to find the right page. \ud83e\uddd0 <\/p>\n\n<p>So, I made a <a href=\"https:\/\/uilicious.com\/playwright-cheatsheet\" rel=\"noopener noreferrer\"><strong>Playwright Cheatsheet<\/strong><\/a>: \ud83d\ude42<\/p>\n\n<ul>\n<li><strong>all the most common commands and usage<\/strong><\/li>\n<li><strong>tiny but useful code snippets to copy<\/strong><\/li>\n<li><strong>and SEARCH!<\/strong><\/li>\n<\/ul>\n\n<p>Here you go! Please enjoy. Bookmark it if you like. Print it as a PDF or whatever. <\/p>\n\n<p><a href=\"https:\/\/media2.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs5zroif6bajqg5iu6s0x.gif\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media2.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs5zroif6bajqg5iu6s0x.gif\" alt=\"Search commonly used commands using Playwright Cheatsheet\" width=\"640\" height=\"480\"><\/a><\/p>\n\n<p>Let me know if there's any incorrectness and feel free to suggest feedback to improve.<\/p>\n\n<p><em>Disclaimer: I do work for a test automation company! But this is not a promo - just wanted to share something I made because I got really frustrated with the docs.<\/em><\/p>\n\n","category":["playwright","browser","automation","javascript"]},{"title":"Featherless - running any llama model serverless","pubDate":"Sun, 23 Jun 2024 07:27:40 +0000","link":"https:\/\/dev.to\/shiling\/featherless-running-any-llama-model-serverless-4jcn","guid":"https:\/\/dev.to\/shiling\/featherless-running-any-llama-model-serverless-4jcn","description":"<p>There's a lot of interesting AI models out there on hugging face, and sometimes I want to try them them. But I procrastinate, because honestly, it is a bit of a hassle to download and deploy the AI models. <\/p>\n\n<p>Well, looks like some folks thought the same, and made a service that lets you run any model from huggingface serverless: <a href=\"https:\/\/featherless.ai\/\" rel=\"noopener noreferrer\">Featherless<\/a>. <\/p>\n\n<p>There's like 492 LLAMA models right now, but looks like they plan to add more open source models every week. <\/p>\n\n<p>I think it's really convenient that they let you chat with the model and preview how the AI would respond. It's hard to pick which ones you want when there's so many to pick, and I don't really want to download and deploy every single one of them to evaluate. Huggingface really should have had this feature.<\/p>\n\n<p>Right, now to test some of these interesting roleplaying models, and form my DnD party. :D<\/p>\n\n<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhmi9w2n8bwt30tx0qom3.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhmi9w2n8bwt30tx0qom3.png\" alt=\"Chatting with roleplay-llama-3\"><\/a><\/p>\n\n","category":["ai","serverless","llama"]},{"title":"Tip : Write better error messages with ChatGPT","pubDate":"Mon, 16 Jan 2023 11:21:26 +0000","link":"https:\/\/dev.to\/shiling\/tip-write-better-error-messages-with-chatgpt-53k","guid":"https:\/\/dev.to\/shiling\/tip-write-better-error-messages-with-chatgpt-53k","description":"<p>A friend of mine showed me a great use of ChatGPT to write human-friendly error messages. I thought it's brilliant and everyone needs to know this.<\/p>\n\n<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr33vhnoykfpdkla7xivf.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr33vhnoykfpdkla7xivf.png\" alt=\"Use ChatGPT to write better error messages\"><\/a><\/p>\n\n<p>Original Tweet: <a href=\"https:\/\/twitter.com\/douglascayers\/status\/1613714294910160897\" rel=\"noopener noreferrer\">https:\/\/twitter.com\/douglascayers\/status\/1613714294910160897<\/a><\/p>\n\n","category":["a11y","ux","productivity","webdev"]},{"title":"\ud83e\udd14Pop Quiz! Which of these is an infinite loop?","pubDate":"Wed, 23 Oct 2019 06:14:40 +0000","link":"https:\/\/dev.to\/uilicious\/pop-quiz-which-of-these-is-an-infinite-loop-p6d","guid":"https:\/\/dev.to\/uilicious\/pop-quiz-which-of-these-is-an-infinite-loop-p6d","description":"<h1>\n  \n  \n  Pop Quiz!\n<\/h1>\n\n<p>Which of these is an infinite loop?<\/p>\n\n<p>And guess many times console.log will be printed.<\/p>\n\n<p>A: <code>let<\/code> 5x3 Loop<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">let<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">5<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span>\n  <span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">let<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">3<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span>\n    <span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>B: <code>var<\/code> 5x3 Loop<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">var<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">5<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span>\n  <span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">var<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">3<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span>\n    <span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>C: <code>var<\/code> 5x5 Loop<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">var<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">5<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span>\n  <span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">var<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">5<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span> \n    <span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>D: <code>let<\/code> 5x5 Loop<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">let<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">5<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span>\n  <span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">let<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">5<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span>\n    <span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h1>\n  \n  \n  Answer\n<\/h1>\n\n<p><strong>Which of these is an infinite loop?<\/strong><br>\nB: <code>var<\/code> 5x3<\/p>\n\n<p><strong>Guess many times console.log will be printed.<\/strong><br>\nA: <code>let<\/code> 5x3 - 15 times<br>\nB: <code>var<\/code> 5x3 - Infinite times<br>\nC: <code>var<\/code> 5x5 - 5 times<br>\nD: <code>let<\/code> 5x5 - 25 times<\/p>\n\n<p>Did any of the answers surprise you? I was! <\/p>\n<h1>\n  \n  \n  \ud83e\uddd0 What, but why? The difference between <code>let<\/code> and <code>var<\/code>.\n<\/h1>\n\n<p>&lt; flashback &gt;<\/p>\n\n\n\n<p>A junior dev came up to me for help with a bug in his Javascript code that's causing a memory error, my intuition told me that there was an infinite loop somewhere. <\/p>\n\n<p>One of the nested loops stood out as a red flag to me:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">let<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">5<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span>\n  <span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">let<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"mi\">5<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span>\n    <span class=\"c1\">\/\/ some code<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>I said - Hey, you're using the same variable name for outer and inner loop, this is going to cause an infinite loop.<\/p>\n\n<p><strong>JD<\/strong>: Huh? How come?<\/p>\n\n<p><strong>Me<\/strong>:  Because the inner loop is reseting <code>i<\/code> to 0, causing the outer loop to never exit. Come let's try this in the console, it's faster to just see it. <\/p>\n\n<p>To my surprise, there wasn't an infinite loop, we got this:<\/p>\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--0dz_hV8d--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/2ltf53ans6trtk5lvpsp.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--0dz_hV8d--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/2ltf53ans6trtk5lvpsp.png\" alt=\"let 5x5 loop\" width=\"409\" height=\"149\"><\/a><\/p>\n\n<p><code>console.log<\/code> was printed only 25 times.<\/p>\n\n<p><strong>Me<\/strong>: Hm...? That's odd. <em>(Then I realised that the code uses <code>let<\/code> instead of <code>var<\/code>.)<\/em> Maybe... the infinite loop only happens if you use <code>var<\/code> instead of <code>let<\/code>. <\/p>\n\n<p>We changed <code>let<\/code> to <code>var<\/code>, and ran it again:<\/p>\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--O0Y2svvE--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/ymhlykebg8ckpr7m1j6m.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--O0Y2svvE--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/ymhlykebg8ckpr7m1j6m.png\" alt=\"var 5x5 loop\" width=\"404\" height=\"152\"><\/a><\/p>\n\n<p><code>console.log<\/code> was printed only 5 times.<\/p>\n\n<p><strong>Us<\/strong>: Wait whuut? Why it is printing only 5 times?<\/p>\n\n<p><strong>Me<\/strong>: ... Ahhh, that's because since this example uses <code>var<\/code> instead of <code>let<\/code>. <strong><code>let<\/code> allows you to safely declare scoped variables with the same name. But when you use <code>var<\/code> to declare variables with the same name in different parent-child scopes, they all reference the same thing.<\/strong> So here, when you use <code>var<\/code>, the inner loop is sharing the same <code>i<\/code> as the outer loop. Meaning, when the inner loop counts <code>i<\/code> up to 5 and exits, the outer loop exits immediately because it's <code>i<\/code> counter is set to 5 by the inner loop.<\/p>\n\n\n\n\n<p><strong>JD<\/strong>: Ohh... okay, then what if we set the inner loop to exit when <code>i<\/code> is 3? I guess that produces an infinite loop?<\/p>\n\n<p><strong>Me<\/strong>: Let's find out.<\/p>\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--ogGi_Mfp--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/7gbf2rygqn2jfplxr2nf.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--ogGi_Mfp--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/7gbf2rygqn2jfplxr2nf.png\" alt=\"var 5x3 loop\" width=\"418\" height=\"154\"><\/a><\/p>\n\n<p><code>console.log<\/code> was printed way beyond 15 times, and crashed the browser.<\/p>\n\n<p><strong>Us<\/strong>: \ud83d\ude4c We got an infinite loop!<\/p>\n\n\n\n\n<p>And what if we change <code>var<\/code> to <code>let<\/code> now?<\/p>\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--lX31ayHp--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/f27y9vjv38ih3sidx6gj.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--lX31ayHp--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/f27y9vjv38ih3sidx6gj.png\" alt=\"let 5x3 loop\" width=\"430\" height=\"162\"><\/a><\/p>\n\n<p><code>console.log<\/code> was printed only 15 times.<\/p>\n\n\n\n\n<p><strong>JD<\/strong>: Ok, cool. But what's the use case of <code>var<\/code> then?<\/p>\n\n<p><strong>Me<\/strong>: Ah that's a good question. \ud83e\udd14 ...none actually. There isn't a good use case for <code>var<\/code> now that ES6 introduced <code>let<\/code>. <code>var<\/code> is just how we used to declare variables - but then there's this problem with variables leaking out of their scope - hence <code>let<\/code> was proposed. Don't ask me why they decided to name the keyword <code>let<\/code>. And this is why our eslint is configured to enforce <code>let<\/code> over <code>var<\/code>. :)<\/p>\n\n<p><strong>JD<\/strong>: Oh! Ok... so if this loop isn't causing the memory error, so what's causing it? <em>(That turned out to be something else altogether.)<\/em><\/p>\n\n<p>&lt; \/flashback &gt;<\/p>\n\n<p>Ah thinking back, we have it good now with ES6.<\/p>\n\n","category":["javascript","quiz"]},{"title":"I dreamt that I was coding","pubDate":"Fri, 19 Jul 2019 15:45:04 +0000","link":"https:\/\/dev.to\/shiling\/i-dreamt-that-i-was-coding-38b","guid":"https:\/\/dev.to\/shiling\/i-dreamt-that-i-was-coding-38b","description":"<p>So one fine morning, I had this conversation with <a class=\"mentioned-user\" href=\"https:\/\/dev.to\/picocreator\">@picocreator<\/a>:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>Him: I dreamt that I was coding this morning.\nMe:  Wow! That's awesome, you are productive in your sleep!\nHim: No! It totally sucks!\nMe:  Why?\nHim: Coz' I didn't get to save my work! Q_Q\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fkm8qbho3c82o6ye4g2y6.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fkm8qbho3c82o6ye4g2y6.png\" alt=\"Noooooo\"><\/a><\/p>\nWhen the power goes out (?) and you realised you didn't save your work. \n\n\n\n","category":"jokes"},{"title":"vue-online-prop - Am I online? A tiny VueJS plugin (<1kb).","pubDate":"Thu, 20 Jun 2019 03:52:53 +0000","link":"https:\/\/dev.to\/uilicious\/vue-online-prop-am-i-online-a-tiny-vuejs-plugin-1kb-61p","guid":"https:\/\/dev.to\/uilicious\/vue-online-prop-am-i-online-a-tiny-vuejs-plugin-1kb-61p","description":"<p>I just wanted one thing only and only one thing. <\/p>\n\n<p>I just want a reactive property <code>$online<\/code> in all my Vue components to tell me if the user is connected to the internet or not. <\/p>\n\n<p>I know I know, there's already a bunch of existing <code>vue-online<\/code> packages, but... No thanks, I don't need the extra UI components bundled in...<\/p>\n\n<p>So I made <code>vue-online-prop<\/code> (<a href=\"https:\/\/www.npmjs.com\/package\/vue-online-prop\" rel=\"noopener noreferrer\">npm<\/a>, <a href=\"https:\/\/github.com\/shiling\/vue-online-prop\" rel=\"noopener noreferrer\">github<\/a>)<\/p>\n\n<p>Setup:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"k\">import<\/span> <span class=\"nx\">VueOnlineProp<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">vue-online-prop<\/span><span class=\"dl\">\"<\/span>\n<span class=\"nx\">Vue<\/span><span class=\"p\">.<\/span><span class=\"nf\">use<\/span><span class=\"p\">(<\/span><span class=\"nx\">VueOnlineProp<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Usage:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight html\"><code><span class=\"nt\">&lt;div<\/span> <span class=\"na\">v-if=<\/span><span class=\"s\">\"!$online\"<\/span><span class=\"nt\">&gt;<\/span> \n    You are currently offline!\n<span class=\"nt\">&lt;\/div&gt;<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Tada.<\/p>\n\n<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F53del7kraqs4ct2u4qnw.gif\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F53del7kraqs4ct2u4qnw.gif\" alt=\"I added a offline indicator!\"><\/a><\/p>\n\n<p>That's all!<\/p>\n\n\n\n\n<h3>\n  \n  \n  (Extras) How it is made\n<\/h3>\n\n<h5>\n  \n  \n  How to check if I am online with Javascript\n<\/h5>\n\n<p>You can query the internet connectivity through Javascript using <code>navigator.onLine<\/code>, and listen to changes to the connectivity using the <code>online<\/code> and <code>offline<\/code> events on the window.<\/p>\n\n<p>\ud83d\udc49<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/NavigatorOnLine\/onLine\" rel=\"noopener noreferrer\">MDN docs on <code>navigator.onLine<\/code><\/a><\/p>\n\n<h5>\n  \n  \n  Creating the VueJS plugin\n<\/h5>\n\n<p>This plugin simply listens to the <code>online<\/code> and <code>offline<\/code> events on the window, and sets the value of <code>navigator.onLine<\/code> to a reactive property <code>status<\/code> managed by the plugin. When the plugin is installed using <code>Vue.use(VueOnlineProp)<\/code>, it adds a <code>beforeCreate<\/code> mixin, which will to bind the reactive property <code>status<\/code> to the <code>$online<\/code> property in every component. (\ud83d\udc49<a href=\"https:\/\/github.com\/shiling\/vue-online-prop\/blob\/master\/index.js\" rel=\"noopener noreferrer\">Here's the code<\/a>)<\/p>\n\n","category":["vue","ux","showdev","javascript"]},{"title":"Javascript Array.push is 945x faster than Array.concat \ud83e\udd2f\ud83e\udd14","pubDate":"Thu, 02 May 2019 08:34:18 +0000","link":"https:\/\/dev.to\/uilicious\/javascript-array-push-is-945x-faster-than-array-concat-1oki","guid":"https:\/\/dev.to\/uilicious\/javascript-array-push-is-945x-faster-than-array-concat-1oki","description":"<h1>\n  \n  \n  TDLR\n<\/h1>\n\n<p>If you are merging arrays with thousands of elements across, you can shave off seconds from the process by using <code>arr1.push(...arr2)<\/code> instead of <code>arr1 = arr1.concat(arr2)<\/code>. If you really to go faster, you might even want to write your own implementation to merge arrays.<\/p>\n\n<h1>\n  \n  \n  Wait a minute... how long does it take to merge 15,000 arrays with <code>.concat<\/code>...\n<\/h1>\n\n<p>Recently, we had a user complaining of a major slowdown in the execution of their UI tests on <a href=\"https:\/\/uilicious.com\" rel=\"noopener noreferrer\">UI-licious<\/a>. Each <code>I.click<\/code> <code>I.fill<\/code> <code>I.see<\/code> command which usually takes ~1 second to complete (post-processing e.g. taking screenshots) now took over 40 seconds to complete , so test suites that usually completed under 20 minutes took hours instead and was severely limiting their deployment process. <\/p>\n\n<p>It didn't take long for me to set up timers to narrow down out which part of the code was causing the slowdown, but I was pretty surprised when I found the culprit:<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code>\n\n<span class=\"nx\">arr1<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">arr1<\/span><span class=\"p\">.<\/span><span class=\"nf\">concat<\/span><span class=\"p\">(<\/span><span class=\"nx\">arr2<\/span><span class=\"p\">)<\/span>\n\n\n<\/code><\/pre>\n\n<\/div>\n\n<p>Array's <code>.concat<\/code> method.<\/p>\n\n<p>In order to allow tests to be written using simple commands like <code>I.click(\"Login\")<\/code> instead of CSS or XPATH selectors <code>I.click(\"#login-btn\")<\/code>, UI-licious works using dynamic code analysis to analyse the DOM tree to determine what and how to test your website based on semantics, accessibility attributes, and popular but non-standard patterns. The <code>.concat<\/code> operations was being used to flatten the DOM tree for analysis, but worked very poorly when the DOM tree was very large and very deep, which happened when our user recently pushed an update to their application that caused their pages to bloat significantly (that's another performance issue on their side, but it's another topic).<\/p>\n\n<p><strong>It took 6 seconds to merge 15,000 arrays that each had an average size of 5 elements with <code>.concat<\/code>.<\/strong><\/p>\n\n<p><strong>What?<\/strong><\/p>\n\n<p>6 seconds...<\/p>\n\n<p>For 15,000 arrays with the average size of 5 elements?<\/p>\n\n<p><strong>That's not a lot data.<\/strong><\/p>\n\n<p>Why is it so slow? Are there faster ways to merge arrays?<\/p>\n\n\n\n\n<h1>\n  \n  \n  Benchmark comparisons\n<\/h1>\n\n<h2>\n  \n  \n  .push vs. .concat for 10000 arrays with 10 elements each\n<\/h2>\n\n<p>So I started researching (by that, I mean googling) benchmarks for <code>.concat<\/code> compared to other methods to merge arrays in Javascript. <\/p>\n\n<p>It turns out the fastest method to merge arrays is to use <code>.push<\/code> which accepts n arguments:<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code>\n\n<span class=\"c1\">\/\/ Push contents of arr2 to arr1<\/span>\n<span class=\"nx\">arr1<\/span><span class=\"p\">.<\/span><span class=\"nf\">push<\/span><span class=\"p\">(<\/span><span class=\"nx\">arr2<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">],<\/span> <span class=\"nx\">arr2<\/span><span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">],<\/span> <span class=\"nx\">arr2<\/span><span class=\"p\">[<\/span><span class=\"mi\">3<\/span><span class=\"p\">],<\/span> <span class=\"p\">...,<\/span> <span class=\"nx\">arr2<\/span><span class=\"p\">[<\/span><span class=\"nx\">n<\/span><span class=\"p\">])<\/span>\n\n<span class=\"c1\">\/\/ Since my arrays are not fixed in size, I used `apply` instead<\/span>\n<span class=\"nb\">Array<\/span><span class=\"p\">.<\/span><span class=\"nx\">prototype<\/span><span class=\"p\">.<\/span><span class=\"nx\">push<\/span><span class=\"p\">.<\/span><span class=\"nf\">apply<\/span><span class=\"p\">(<\/span><span class=\"nx\">arr1<\/span><span class=\"p\">,<\/span> <span class=\"nx\">arr2<\/span><span class=\"p\">)<\/span>\n\n\n<\/code><\/pre>\n\n<\/div>\n\n<p>And it is faster by leaps in comparison.<\/p>\n\n<p>How fast?<\/p>\n\n<p>I ran a few performance benchmarks on my own to see for myself. Lo and behold, here's the difference on Chrome:<\/p>\n\n<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnfag86cvq73ev0tvnqd9.PNG\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnfag86cvq73ev0tvnqd9.PNG\" alt=\"JsPerf - .push vs. .concat 10000 size-10 arrays (Chrome)\"><\/a><\/p>\n\n<p>\ud83d\udc49 <a href=\"https:\/\/jsperf.com\/javascript-array-concat-vs-push\/100\" rel=\"noopener noreferrer\">Link to the test on JsPerf<\/a><\/p>\n\n<p>To merge arrays of size 10 for 10,000 times, <code>.concat<\/code> performs at 0.40 ops\/sec, while <code>.push<\/code> performs at 378 ops\/sec. <code>push<\/code> is 945x faster than <code>concat<\/code>! This difference might not be linear, but it is already is already significant at this small scale. <\/p>\n\n<p>And on Firefox, here's the results:<\/p>\n\n<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0x80umojjrmof4fs9tfv.PNG\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0x80umojjrmof4fs9tfv.PNG\" alt=\"JsPerf - .push vs. .concat 10000 size-10 arrays (Firefox)\"><\/a><\/p>\n\n<p>Firefox's SpiderMonkey Javascript engine is generally slower compared to Chrome's V8 engine, but <code>.push<\/code> still comes out top, at 2260x faster.<\/p>\n\n<p>This one change to our code fixed the entire slowdown problem. <\/p>\n\n<h2>\n  \n  \n  .push vs. .concat for 2 arrays with 50,000 elements each\n<\/h2>\n\n<p>But ok, what if you are not merging 10,000 size-10 arrays, but 2 giant arrays with 50000 elements each instead? <\/p>\n\n<p>Here's the the results on Chrome along with results:<\/p>\n\n<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9i6w6l8l4ft0umg59jrq.PNG\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9i6w6l8l4ft0umg59jrq.PNG\" alt=\"JsPerf - .push vs. .concat 2 size-50000 arrays (chrome)\"><\/a><\/p>\n\n<p>\ud83d\udc49 <a href=\"https:\/\/jsperf.com\/javascript-array-concat-vs-push\/170\" rel=\"noopener noreferrer\">Link to the test on JsPerf<\/a><\/p>\n\n<p><code>.push<\/code> is still faster than <code>.concat<\/code>, but a factor of 9.<\/p>\n\n<p>Not as dramatic as 945x slower, but still dang slow.<\/p>\n\n\n\n\n<h2>\n  \n  \n  Prettier syntax with rest spread\n<\/h2>\n\n<p>If you find <code>Array.prototype.push.apply(arr1, arr2)<\/code> verbose, you can use a simple variant using the rest spread ES6 syntax:<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code>\n\n<span class=\"nx\">arr1<\/span><span class=\"p\">.<\/span><span class=\"nf\">push<\/span><span class=\"p\">(...<\/span><span class=\"nx\">arr2<\/span><span class=\"p\">)<\/span>\n\n\n<\/code><\/pre>\n\n<\/div>\n\n<p>The performance difference between <code>Array.prototype.push.apply(arr1, arr2)<\/code> and <code>arr1.push(...arr2)<\/code> is negligable.<\/p>\n\n\n\n\n<h1>\n  \n  \n  But why is <code>Array.concat<\/code> so slow?\n<\/h1>\n\n<p>It lot of it has to do with the Javascript engine, but I don't know the exact answer, so I asked my buddy <a class=\"mentioned-user\" href=\"https:\/\/dev.to\/picocreator\">@picocreator<\/a>, the co-creator of <a href=\"http:\/\/gpu.rocks\/\" rel=\"noopener noreferrer\">GPU.js<\/a>, as he had spent a fair bit of time digging around the V8 source code before. <a class=\"mentioned-user\" href=\"https:\/\/dev.to\/picocreator\">@picocreator<\/a>'s also lent me his sweet gaming PC which he used to benchmark GPU.js to run the JsPerf tests because my MacBook didn't have the memory to even perform <code>.concat<\/code> with two size-50000 arrays.<\/p>\n\n<p>Apparently the answer has a lot to do with the fact that <code>.concat<\/code> creates a new array while <code>.push<\/code> modifies the first array. The additional work <code>.concat<\/code> does to add the elements from the first array to the returned array is the main reason for the slowdown.<\/p>\n\n<blockquote>\n<p>Me: \"What? Really? That's it? But by that much? No way!\"<br>\n<a class=\"mentioned-user\" href=\"https:\/\/dev.to\/picocreator\">@picocreator<\/a>: \"Serious, just try writing some naive implementations of .concat vs .push then!\"<\/p>\n<\/blockquote>\n\n<p>So I tried writing some naive implementations of <code>.concat<\/code> and <code>.push<\/code>. Several in fact, plus a comparison with <a href=\"https:\/\/lodash.com\/\" rel=\"noopener noreferrer\">lodash<\/a>'s <code>_.concat<\/code>:<\/p>\n\n<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvoipcak9p1rzsk01pfou.PNG\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvoipcak9p1rzsk01pfou.PNG\" alt=\"JsPerf - Various ways to merge arrays (Chrome)\"><\/a><\/p>\n\n<p>\ud83d\udc49 <a href=\"https:\/\/jsperf.com\/merge-array-implementations\/1\" rel=\"noopener noreferrer\">Link to the test on JsPerf<\/a><\/p>\n\n<h2>\n  \n  \n  Naive implementation 1\n<\/h2>\n\n<p>Let's talk about the first set of naive implementation:<\/p>\n\n<h5>\n  \n  \n  Naive implementation of <code>.concat<\/code>\n<\/h5>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code>\n\n<span class=\"c1\">\/\/ Create result array<\/span>\n<span class=\"kd\">var<\/span> <span class=\"nx\">arr3<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[]<\/span>\n\n<span class=\"c1\">\/\/ Add Array 1<\/span>\n<span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">var<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"nx\">arr1Length<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span>\n  <span class=\"nx\">arr3<\/span><span class=\"p\">[<\/span><span class=\"nx\">i<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">arr1<\/span><span class=\"p\">[<\/span><span class=\"nx\">i<\/span><span class=\"p\">]<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"c1\">\/\/ Add Array 2<\/span>\n<span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">var<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"nx\">arr2Length<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span>\n  <span class=\"nx\">arr3<\/span><span class=\"p\">[<\/span><span class=\"nx\">arr1Length<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">i<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">arr2<\/span><span class=\"p\">[<\/span><span class=\"nx\">i<\/span><span class=\"p\">]<\/span>\n<span class=\"p\">}<\/span>\n\n\n<\/code><\/pre>\n\n<\/div>\n<h5>\n  \n  \n  Naive implementation of <code>.push<\/code>\n<\/h5>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code>\n\n<span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">var<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"nx\">arr2Length<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span>\n  <span class=\"nx\">arr1<\/span><span class=\"p\">[<\/span><span class=\"nx\">arr1Length<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">i<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">arr2<\/span><span class=\"p\">[<\/span><span class=\"nx\">i<\/span><span class=\"p\">]<\/span>\n<span class=\"p\">}<\/span>\n\n\n<\/code><\/pre>\n\n<\/div>\n\n<p>As you can see, the only difference between the two is that the <code>.push<\/code> implementation modifies the first array directly.<\/p>\n\n<h5>\n  \n  \n  Results of vanilla methods:\n<\/h5>\n\n<ul>\n<li>\n<code>.concat<\/code> : 75 ops\/sec<\/li>\n<li>\n<code>.push<\/code>: 793 ops\/sec (10x faster)<\/li>\n<\/ul>\n\n<h5>\n  \n  \n  Results of naive implementation 1\n<\/h5>\n\n<ul>\n<li>\n<code>.concat<\/code> : 536 ops\/sec<\/li>\n<li>\n<code>.push<\/code> : 11,104 ops\/sec (20x faster)<\/li>\n<\/ul>\n\n<p>It turns that my DIY <code>concat<\/code> and <code>push<\/code> is faster than the vanilla implementations... But here we can see that simply creating a new result array and copying the content of the first array over slows down the process significantly.<\/p>\n\n<h2>\n  \n  \n  Naive implementation 2 (Preallocate size of the final array)\n<\/h2>\n\n<p>We can further improve the naive implementations by preallocating the size of the array before adding the elements, and this makes a huge difference.<\/p>\n\n<h5>\n  \n  \n  Naive implementation of <code>.concat<\/code> with pre-allocation\n<\/h5>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code>\n\n<span class=\"c1\">\/\/ Create result array with preallocated size<\/span>\n<span class=\"kd\">var<\/span> <span class=\"nx\">arr3<\/span> <span class=\"o\">=<\/span> <span class=\"nc\">Array<\/span><span class=\"p\">(<\/span><span class=\"nx\">arr1Length<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">arr2Length<\/span><span class=\"p\">)<\/span>\n\n<span class=\"c1\">\/\/ Add Array 1<\/span>\n<span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">var<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"nx\">arr1Length<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span>\n  <span class=\"nx\">arr3<\/span><span class=\"p\">[<\/span><span class=\"nx\">i<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">arr1<\/span><span class=\"p\">[<\/span><span class=\"nx\">i<\/span><span class=\"p\">]<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"c1\">\/\/ Add Array 2<\/span>\n<span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">var<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"nx\">arr2Length<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span>\n  <span class=\"nx\">arr3<\/span><span class=\"p\">[<\/span><span class=\"nx\">arr1Length<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">i<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">arr2<\/span><span class=\"p\">[<\/span><span class=\"nx\">i<\/span><span class=\"p\">]<\/span>\n<span class=\"p\">}<\/span>\n\n\n<\/code><\/pre>\n\n<\/div>\n<h5>\n  \n  \n  Naive implementation of <code>.push<\/code> with pre-allocation\n<\/h5>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code>\n\n<span class=\"c1\">\/\/ Pre allocate size<\/span>\n<span class=\"nx\">arr1<\/span><span class=\"p\">.<\/span><span class=\"nx\">length<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">arr1Length<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">arr2Length<\/span>\n\n<span class=\"c1\">\/\/ Add arr2 items to arr1<\/span>\n<span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">var<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"nx\">arr2Length<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span>\n  <span class=\"nx\">arr1<\/span><span class=\"p\">[<\/span><span class=\"nx\">arr1Length<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">i<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">arr2<\/span><span class=\"p\">[<\/span><span class=\"nx\">i<\/span><span class=\"p\">]<\/span>\n<span class=\"p\">}<\/span>\n\n\n<\/code><\/pre>\n\n<\/div>\n<h5>\n  \n  \n  Results of naive implementation 1\n<\/h5>\n\n<ul>\n<li>\n<code>.concat<\/code> : 536 ops\/sec<\/li>\n<li>\n<code>.push<\/code> : 11,104 ops\/sec (20x faster)<\/li>\n<\/ul>\n<h5>\n  \n  \n  Results of naive implementation 2\n<\/h5>\n\n<ul>\n<li>\n<code>.concat<\/code> : 1,578 ops\/sec<\/li>\n<li>\n<code>.push<\/code> : 18,996 ops\/sec (12x faster)<\/li>\n<\/ul>\n\n<p>Preallocating the size of the final array improves the performance by 2-3 times for each method.<\/p>\n<h2>\n  \n  \n  <code>.push<\/code> array vs. <code>.push<\/code> elements individually\n<\/h2>\n\n<p>Ok, what if we just .push elements individually? Is that faster than <code>Array.prototype.push.apply(arr1, arr2)<\/code><\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code>\n\n<span class=\"k\">for<\/span><span class=\"p\">(<\/span><span class=\"kd\">var<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span> <span class=\"o\">&lt;<\/span> <span class=\"nx\">arr2Length<\/span><span class=\"p\">;<\/span> <span class=\"nx\">i<\/span><span class=\"o\">++<\/span><span class=\"p\">){<\/span>\n  <span class=\"nx\">arr1<\/span><span class=\"p\">.<\/span><span class=\"nf\">push<\/span><span class=\"p\">(<\/span><span class=\"nx\">arr2<\/span><span class=\"p\">[<\/span><span class=\"nx\">i<\/span><span class=\"p\">])<\/span>\n<span class=\"p\">}<\/span>\n\n\n<\/code><\/pre>\n\n<\/div>\n<h5>\n  \n  \n  Results\n<\/h5>\n\n<ul>\n<li>\n<code>.push<\/code> entire array: 793 ops\/sec<\/li>\n<li>\n<code>.push<\/code> elements individually: 735 ops\/sec (slower)<\/li>\n<\/ul>\n\n<p>So doing <code>.push<\/code> on individual elements is slower than doing <code>.push<\/code> on the entire array. Makes sense.<\/p>\n<h1>\n  \n  \n  Conclusion: Why <code>.push<\/code> is faster <code>.concat<\/code>\n<\/h1>\n\n<p>In conclusion, it is true that the main reason why <code>concat<\/code> is so much slower than <code>.push<\/code> is simply that it creates a new array and does the additional work to copy the first array over.<\/p>\n\n<p>That said, now there's another mystery to me... <\/p>\n<h1>\n  \n  \n  Another mystery\n<\/h1>\n\n<p>Why are the vanilla implementations so much slower than the naive implementations?\ud83e\udd14I asked for <a class=\"mentioned-user\" href=\"https:\/\/dev.to\/picocreator\">@picocreator<\/a>'s help again. <\/p>\n\n<p>We took a look at lodash's <code>_.concat<\/code> implementation for some hints as to what else is vanilla <code>.concat<\/code> doing under the hood, as it is comparable in performance (lodash's is slightly faster).<\/p>\n\n<p>It turns out that because according to the vanilla's <code>.concat<\/code>'s specs, the method is overloaded, and supports two signatures: <\/p>\n\n<ol>\n<li>Values to append as n number of arguments, e.g. <code>[1,2].concat(3,4,5)<\/code>\n<\/li>\n<li>The array to append itself, e.g. <code>[1,2].concat([3,4,5])<\/code>\n<\/li>\n<\/ol>\n\n<p>You can even do both like this: <code>[1,2].concat(3,4,[5,6])<\/code><\/p>\n\n<p>Lodash also handles both overloaded signatures, and to do so, lodash puts all the arguments into an array, and flattens it. It make sense if you are passing in several arrays as arguments. But when passed an array to append, it doesn't just use the array as it is, it copies that into another array, and then flattens it. <\/p>\n\n<p>... ok...<\/p>\n\n<p>Definitely could be more optimised. And this is why you might want to DIY your own merge array implementation.<\/p>\n\n<p>Also, it's just my and <a class=\"mentioned-user\" href=\"https:\/\/dev.to\/picocreator\">@picocreator<\/a>'s theory of how vanilla <code>.concat<\/code> works under the hood based on Lodash's source code and his slightly outdated knowledge of the V8 source code.<\/p>\n\n<p>You can read the lodash's source code at your leisure <a href=\"https:\/\/github.com\/lodash\/lodash\/blob\/4.17.11\/lodash.js#L6913\" rel=\"noopener noreferrer\">here<\/a>.<\/p>\n\n\n<h2>\n  \n  \n  Additional Notes\n<\/h2>\n\n<ol>\n<li><p>The tests are done with Arrays that only contain Integers. Javascript engines are known to perform faster with Typed Arrays. The results are expected to be slower if you have objects in the arrays.<\/p><\/li>\n<li><p>Here are the specs for the PC used to run the benchmarks:<br>\n<a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe86nvx39b26n4k1gukqd.PNG\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe86nvx39b26n4k1gukqd.PNG\" alt=\"PC specs for the performance tests\"><\/a><\/p><\/li>\n<\/ol>\n\n\n<h1>\n  \n  \n  Why are we doing such large array operations during UI-licious tests anyway?\n<\/h1>\n\n<p><a href=\"https:\/\/snippet.uilicious.com\/test\/public\/1cUHCW368zsHrByzHCkzLE\" rel=\"noopener noreferrer\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnreaiqlqnokx20xn8zym.gif\" alt=\"Uilicious Snippet dev.to test\"><\/a><\/p>\n\n<p>Under the hood, the UI-licious test engine scans the DOM tree of the target application, evaluating the semantics, accessible attributes and other common patterns to determine what is the target element and how to test it. <\/p>\n\n<p>This is so that we can make sure tests can be written as simple as this:<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code>\n\n<span class=\"c1\">\/\/ Lets go to dev.to<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nf\">goTo<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">https:\/\/dev.to<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n\n<span class=\"c1\">\/\/ Fill up search<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nf\">fill<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">Search<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">uilicious<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nf\">pressEnter<\/span><span class=\"p\">()<\/span>\n\n<span class=\"c1\">\/\/ I should see myself or my co-founder<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nf\">see<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">Shi Ling<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nf\">see<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">Eugene Cheah<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n\n\n<\/code><\/pre>\n\n<\/div>\n\n<p>Without the use of CSS or XPATH selectors, so that the tests can be more readable, less sensitive to changes in the UI, and easier to maintain.<\/p>\n\n<h3>\n  \n  \n  ATTENTION: Public service announcement - Please keep your DOM count low!\n<\/h3>\n\n<p>Unfortunately, there's a trend of DOM trees growing excessively large these days because people are building more and more complex and dynamic applications with modern front-end frameworks. It's a double-edge sword, frameworks allow us to develop faster, folks often forget how much bloat frameworks add. I sometimes cringe at the number of elements that are just there to wrap other elements when inspecting the source code of various websites. <\/p>\n\n<p>If you want to find out whether your website has too many DOM nodes, you can run a <a href=\"https:\/\/developers.google.com\/web\/tools\/lighthouse\/\" rel=\"noopener noreferrer\">Lighthouse<\/a> audit. <\/p>\n\n<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdevelopers.google.com%2Fweb%2Fprogressive-web-apps%2Fimages%2Fpwa-lighthouse.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fdevelopers.google.com%2Fweb%2Fprogressive-web-apps%2Fimages%2Fpwa-lighthouse.png\" alt=\"Google Lighthouse\"><\/a><\/p>\n\n<p>According to Google, the optimal DOM tree is:<\/p>\n\n<ul>\n<li>Less than 1500 nodes<\/li>\n<li>Depth size of less than 32 levels<\/li>\n<li>A parent node has less than 60 children<\/li>\n<\/ul>\n\n<p>A quick audit on the Dev.to feed shows that the DOM tree size is pretty good:<\/p>\n\n<ul>\n<li>Total count of 941 nodes<\/li>\n<li>Max. depth of 14<\/li>\n<li>Max number of child elements at 49<\/li>\n<\/ul>\n\n<p>Not bad!<\/p>\n\n","category":["showdev","javascript","webperf"]},{"title":"Testing - Start with the code that scares you most","pubDate":"Fri, 19 Apr 2019 03:04:42 +0000","link":"https:\/\/dev.to\/uilicious\/testing-start-with-the-code-that-scares-you-most-5dpn","guid":"https:\/\/dev.to\/uilicious\/testing-start-with-the-code-that-scares-you-most-5dpn","description":"<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fiaxak1j8uyvjl96htde6.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fiaxak1j8uyvjl96htde6.png\" alt=\"Let software architecture decisions be simpler than Brexit\"><\/a><\/p>\nLet software architecture decisions be simpler than Brexit\n\n\n\n<h1>\n  \n  \n  A paradox of choices\n<\/h1>\n\n<p>At one of the first jobs as a software engineers, I had a privilege (or misfortune - depends on how you see it) of working at a startup where the mantra was in Move Fast, Break Things. Engineering was focused on shipping features out as quickly as possible, and testing was done on production. Naturally, with a crew of mostly freshly minted software engineers, we racked up technical debt pretty fast. Much of the code was held together with duct tape (metaphorically) at every layer in rush to push things fast. <\/p>\n\n<p>Needless to say, bugs were frequent, and so were the customer complains. The business team were getting frustrated at the engineers, saying \"Don't you guys test?\".<\/p>\n\n<p>Ouch, but they are right.<\/p>\n\n<p>We weren't testing enough, and most of us also agreed that we probably sucked at testing because the work is so dry and developers have a bit of tunnel vision when it comes to their own work.<\/p>\n\n<p>Automating tests sounds like a good idea.<\/p>\n\n<p>Ok, so... <strong>where do we start, how should we do it, what tools should we use?<\/strong><\/p>\n\n<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw14io90n2300ss9wkoql.jpeg\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw14io90n2300ss9wkoql.jpeg\" alt=\"Here's comes the ideology wards\"><\/a><\/p>\nEngineers can get very intense with their ideology wars\n\n\n\n<p>Some argued the best way is to start with Unit Tests, because then we can quickly isolate failures to individual components. Others argued that would take too long to get coverage for most of the code base, we should start with Integration tests instead and work our way down to Unit Tests later.<\/p>\n\n<p>Some argued Test Driven Development (TDD) is the right way. Others argued that TDD is too time-consuming, and is a waste of time because requirements frequently changed for new features. <\/p>\n\n<p>And then there were the tools. <\/p>\n\n<p><strong>The team was stuck with decision paralysis<\/strong> and decided to leave the discussion for a later time (which is usually when hell breaks loose again).<\/p>\n\n<h1>\n  \n  \n  Where to start? Just start with the Scariest code.\n<\/h1>\n\n<p>Good thing, working a startup, one has a lot of autonomy and opportunities to experiment. (Or it could be a bad thing, when you have too many junior developers falling for <strong>The Shiny Object<\/strong> trap...). I went ahead and started writing tests and hooking them up to our build processes anyway. <\/p>\n\n<p>Automating tests sounds like a tremendous undertaking when you have so much backlog, but all journeys start with a single step. <\/p>\n\n<p>I found a good rule of thumb for deciding on where to start.<\/p>\n\n<p><strong>Identify the code that you are most scared of touching.<\/strong> <\/p>\n\n<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdm7i57klknna5m3wjdo7.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdm7i57klknna5m3wjdo7.png\" alt=\"Complex Code\"><\/a><\/p>\n\n<p>Why? <\/p>\n\n<p>Chances are, you are scared of touching this code because:<\/p>\n\n<ul>\n<li>\n<strong>It's complicated<\/strong>, because there's a lot of conditionals, and it's probably not well documented.<\/li>\n<li>\n<strong>It's critical<\/strong> to the business.<\/li>\n<\/ul>\n\n<p>I started with writing tests for our billing module. The calculations for computing how much a customer should be billed was complex. Unfortunately, the management team frequently wanted to change the billing model too. If the calculations were wrong, well, we'd lose a lot of revenue and trust from the customers. And I really don't want to get myself fired and possibly being liable as well for the financial loss...<\/p>\n\n<p>Once you figured out what to test, it's actually pretty easy from there to figure out how and what tools to use.<\/p>\n\n<p>For the billing module, I set up unit tests with JUnit since we had a Java codebase and it's pretty simple to hook the tests up into our Maven builds so these tests are ran every time a developer try to compile the application. API tests technically could cover the same breadth of tests, but I think getting immediate feedback on critical functionality was more important. <\/p>\n\n<p>Once I had tests up and running, I wasn't afraid anymore to refactor the code to improve readability and maintainability. And it wasn't overwhelming for me to handover my work to another developer when I moved on to other projects and eventually to another job. I could simply point to the unit tests, and say here's your documentation, this is how the code works. Looking back, it made me realise that how true the saying <strong>\"legacy code is code that is not tested\"<\/strong> is. <\/p>\n\n<p>So if you are thinking of automating tests, but it sounds overwhelming for you, try starting with the scariest code. Once you eat that frog, it gets easier.<\/p>\n\n","category":["testing","refactoring","devtips"]},{"title":"Trains are for boys, dolls are for girls. Nevertheless, Shi Ling coded.","pubDate":"Fri, 08 Mar 2019 18:23:50 +0000","link":"https:\/\/dev.to\/shiling\/nevertheless-shi-ling-coded-232c","guid":"https:\/\/dev.to\/shiling\/nevertheless-shi-ling-coded-232c","description":"<p><strong>As a kid, I loved exploring things.<\/strong> I wanted to be an archeologist after watching Indiana Jones explore tombs and procure precious artefacts and studying them. I love solving mysteries, and I still keep the Nancy Drew yellow hardbacks in my shelf.<\/p>\n\n<p><strong>I loved tinkering.<\/strong> I'd purposely take apart things that ain't broke, like ballpoint pens, and toasters, just to find out how the pieces work together, and fix them back together. <em>Kids keep asking me to fix broken things for them -dang, should have charged a fee back then...<\/em><\/p>\n\n<p><strong>I loved asking questions.<\/strong> I annoyed or entertained everyone I knew with never-ending \"what if's\" and \"I wonder...\". <\/p>\n\n<p><strong>I loved maths.<\/strong> I could work on a tricky math problem for hours, and even overnight. And once I solved the problem, I'd be beaming all day. <\/p>\n\n<p><strong>I loved science<\/strong> <em>(except chemistry)<\/em>. Because the universe is fascinating.<\/p>\n\n<p><strong>I loved video games.<\/strong> This is where everything started. I started learning HTML because I wanted to create a profile page for my Neopet and make scrollbars that sparkle. Then I learnt to script macros to automate my mouse and keyboard to beat video games. HA. <\/p>\n\n<h1>\n  \n  \n  Then I grew up, and being a nerd is \"un-cute\", especially for a girl\n<\/h1>\n\n<p>As I grew into my teens, people around me became more concerned about what others thought of them. Some wanted to be the coolest kids on the block, buying the most fashionable bags and latest phones with their parents' money. Some obsessed about their body image, makeup, and their relationship status. <\/p>\n\n<p>Suddenly, being a nerd is un-cute, especially for a girl. <\/p>\n\n<p>Science girl? Meh.<br>\nComputer girl? Un-cute. <br>\nVideo game girl? Heavens forbid.<\/p>\n\n<p>Even adults were saying, \"Hey, stop talking about smart stuff all the time, guys don't like that, you're not going to get a boyfriend, and you're going grow old alone.\" <\/p>\n\n<p>\ud83d\ude44<\/p>\n\n<p>Good thing I was stubborn as hell and I don't listen, because I was a teen.<\/p>\n\n<h1>\n  \n  \n  Nevertheless, I coded.\n<\/h1>\n\n<p>I fell in love with programming, it makes me feel like wizard doing magic, and I'm absolutely addicted. I get buzzed and feel high writing code <em>(though it might just be the coffee)<\/em>.<\/p>\n\n<p>But I'm sad that I had less and less female classmates as I progressed through my academic years. <\/p>\n\n<p>I'm sad that I frequently get complimented for being a fantastic programmer (and wait for it)... <em>even though I'm a girl<\/em>.<\/p>\n\n<p>I'm sad that when I won a speed coding tournament, people say \"I can't believe a girl won!\", and my female professor hugged me afterwards saying \"finally, we have a woman who won!\".<\/p>\n\n<p>I'm sad that I get asked to be at a panel because they need women representation in tech.<\/p>\n\n<p>I'm sad when I go to meetups and find myself the only women there.<\/p>\n\n<p>I'm sad, but not offended ok. Just sad. <\/p>\n\n<p><strong>Because it's lonely being a woman who code.<\/strong><\/p>\n\n<h1>\n  \n  \n  Where are the other women?\n<\/h1>\n\n<p>What the hell happened as we grew up?<\/p>\n\n<p>The smartest girls I knew that loved science ended up doing business administration, marketing, accounting, economics, sociology, psychology. For some reason they can't imagine doing science and engineering anymore because they think they are not smart enough. <em>(Girl, you got straight As, and you telling me you're not smart enough? B*tch.)<\/em><\/p>\n\n<blockquote>\n<p>Trains are for boys, dolls are for girls<\/p>\n<\/blockquote>\n\n<p>Perhaps it's that myth that society still perpetuate that <strong>trains are for boys, dolls are for girls<\/strong>, and it's not cool \/ cute the other way around. <em>(I choose neither and picked the mini golf set when I was a kid at the toy store. It was pink.)<\/em><\/p>\n\n<p>I don't know all the reasons why girls lose interest in STEM careers. But at least for me and most women I know, we have been told by folks around us that science is for boys, therefore studying science makes you a tomboy (unless you are studying medicine because doctors are hot), and nobody wants to date a tomboy, and therefore you'll die alone. <\/p>\n\n<p>It's like the only thing that matters in life is whether you'd get married or grow old dying alone.<\/p>\n\n<p>We need to stop telling impressionable kids that false dichotomy and exaggerated narrative.<\/p>\n\n<p>I guess I was lucky in someway that money was tight in my family when I grew up, so I inherited my brother's clothes and toys. Growing up, I just thought the constraints people were telling me about my gender were just absurd.<\/p>\n\n<h1>\n  \n  \n  \ud83d\udc83 I'm happy and proud of who I am\n<\/h1>\n\n<p>Yes, I'm a nerd, I love science, I love Neil Degrasse Tyson and David Attenborough, and quoting xkcd. <\/p>\n\n<p>And maybe I'm a tomboy, I don't like makeup, I know kung-fu. Oh and I really love video games - I build my own PCs just because I want the best hardware to render Civilizations and Divinity in it's full glory.<\/p>\n\n<p>Or maybe I'm just a girl, I love wearing dresses instead of jeans because it's hot in Singapore and I want to look cute on dates - yes - I go on dates!<\/p>\n\n<p>And I'm proud of my work as a programmer, because I'm damn good at it, and create things that make people happy. And I'm proud of starting a company, <a href=\"https:\/\/uilicious.com\">UI-licious<\/a>, with <a class=\"mentioned-user\" href=\"https:\/\/dev.to\/picocreator\">@picocreator<\/a> to help developers testing UIs painlessly.<\/p>\n\n<p>So I'm glad I coded, nevertheless.<\/p>\n\n<h1>\n  \n  \n  To all the women out there\n<\/h1>\n\n<p>Makeup or no makeup. <br>\nSkirts or jeans. <br>\nSkinny or curvy. <br>\nLeft-brained or right-brained. <br>\n<strong>You are beautiful<\/strong>, no matter what people say.<\/p>\n\n<p>Happy International Women's Day!<\/p>\n\n","category":"wecoded"},{"title":"\ud83d\udd04 Swap out test data in UI-licious with Datasets!","pubDate":"Thu, 14 Feb 2019 04:15:16 +0000","link":"https:\/\/dev.to\/uilicious\/datasets-lets-you-swap-out-test-data-in-your-ui-licious-tests-53cf","guid":"https:\/\/dev.to\/uilicious\/datasets-lets-you-swap-out-test-data-in-your-ui-licious-tests-53cf","description":"<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Frgefcjppjw8if31e4k3l.gif\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Frgefcjppjw8if31e4k3l.gif\" alt=\"run-with-dataset.gif\"><\/a><\/p>\nSwap out test data with Datasets!\n\n\n\n<p>Happy Valentines, everyone!<\/p>\n\n<p>This is our first release note of the new year, and we're happy to announce the release of Datasets for <a href=\"https:\/\/uilicious.com\/\" rel=\"noopener noreferrer\">UI-licious Studio<\/a>!<\/p>\n\n<p>Datasets lets you configure different sets of test data to run a test with.<\/p>\n\n<p>It's useful when you want to:<\/p>\n\n<ul>\n<li>Setup tests for different <strong>environments<\/strong> e.g. staging, production<\/li>\n<li>Test the same user journey but for different <strong>roles<\/strong>\n<\/li>\n<li>Store <strong>sensitive<\/strong> test data like passwords that cannot be written in plain text in the test scripts, and also mask them in test reports.<\/li>\n<\/ul>\n\n<p><em>(Note: Datasets is only available on the professional edition of UI-licious Studio. You can <a href=\"https:\/\/snippet.uilicious.com\/test\/public\/1VBEfXDTJYacreZWGcahGV\" rel=\"noopener noreferrer\">run public tests for free<\/a> on UI-licious Snippets.)<\/em><\/p>\n\n\n\n\n<h2>\n  \n  \n  Setting up and using Datasets\n<\/h2>\n\n<p>Datasets work by populating in the <code>DATA<\/code> object in your tests.<\/p>\n\n<p>Let's use this test for logging into Salesforce for example:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"c1\">\/\/ Login to Salesforce<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nf\">goTo<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">https:\/\/login.saleforce.com\/<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nf\">fill<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">Username<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"nx\">DATA<\/span><span class=\"p\">.<\/span><span class=\"nx\">username<\/span><span class=\"p\">)<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nf\">fill<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">Password<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"nx\">DATA<\/span><span class=\"p\">.<\/span><span class=\"nx\">password<\/span><span class=\"p\">)<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nf\">click<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">Log in<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n\n<span class=\"c1\">\/\/ Verify that user is logged into the correct instance of Salesforce<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nf\">amAt<\/span><span class=\"p\">(<\/span><span class=\"nx\">DATA<\/span><span class=\"p\">.<\/span><span class=\"nx\">url<\/span><span class=\"p\">)<\/span>\n\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>We'll set the <code>username<\/code> and <code>password<\/code> to log with, and <code>url<\/code> for the instance that user is expected to be directed to for each test run using a dataset.<\/p>\n\n<p>Let's add a dataset (right beside the \"Run\" button):<\/p>\n\n<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fr86hyye3c9z5ejvwwctr.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fr86hyye3c9z5ejvwwctr.png\" alt=\"Screen Shot 2019-02-12 at 4.07.53 PM.png\"><\/a><\/p>\n\n<p>Give your dataset a name \"John Doe\", and add the <code>username<\/code> property and value as below:<\/p>\n\n<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F8l2h1a3kgluqz95plp9x.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F8l2h1a3kgluqz95plp9x.png\" alt=\"2\\. add-dataset-username.png\"><\/a><\/p>\n\n<p>Add the <code>password<\/code> property and its value as below, but remember to check mark it as a <strong>secret<\/strong> if you want to mask its value in the test reports.<\/p>\n\n<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdnofknslvht1lv6na2el.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdnofknslvht1lv6na2el.png\" alt=\"3\\. add-dataset-password.png\"><\/a><\/p>\n\n<p>Finally, we'll add the <code>url<\/code> property and it's value.<\/p>\n\n<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fileptg5otbykm92ga33q.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fileptg5otbykm92ga33q.png\" alt=\"4\\. add-dataset-url.png\"><\/a><\/p>\n\n<p>Now, let's run the test with the \"John Doe\" dataset, and voila!<\/p>\n\n<p><a href=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmoi3i43py2uqpk10d0pw.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/media.dev.to\/dynamic\/image\/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto\/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmoi3i43py2uqpk10d0pw.png\" alt=\"5. run-dataset.png\"><\/a><br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>1. I go to \"https:\/\/login.salesforce.com\/\" ------ 2.3s SUCCESS\n2. I fill \"Username\": \"johndoe@uilicious.com\" --- 5.9s SUCCESS\n3. I fill \"Password\": \"[truncated]\" ------------- 1.8s SUCCESS\n4. I click \"Log in\" ----------------------------- 6.1s SUCCESS\n5. I am at \"https:\/\/ap4.lightning.force.com\/\" ---      SUCCESS\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>The value of <code>password<\/code> has been masked in the result because it is set as a secret property in this dataset.<\/p>\n\n\n\n\n<h1>\n  \n  \n  What's else is new and improved?\n<\/h1>\n\n<p>For the past months, we've also been focusing on making performance improvement, and developing new features for the test engine so that you can create more elaborate and readable tests.<\/p>\n\n<h2>\n  \n  \n  \ud83d\ude80 Test engine\n<\/h2>\n\n<ul>\n<li>Drag and drop commands:\n\n<ul>\n<li>\n<code>I.dragTo<\/code> - Drag an element to another target element.<\/li>\n<li>\n<code>I.dragBy<\/code> - Drag an element in a direction using (x,y) pixel offset.<\/li>\n<li>\n<code>I.dragUp<\/code>, <code>I.dragDown<\/code>, <code>I.dragLeft<\/code>, <code>I.dragRight<\/code> - Shortcuts for I.dragBy<\/li>\n<\/ul>\n\n\n<\/li>\n\n<li>\n<code>I.getValue<\/code> - Reads and return the value of an input field. This allows you to extract the value of an input field and set it to a variable.<\/li>\n\n<li>New scroll commands for convenience - <code>I.scrollUp<\/code>, <code>I.scrollDown<\/code>, <code>I.scrollLeft<\/code>, <code>I.scrollRight<\/code>.<\/li>\n\n<li>Error suppression - For all I and UI commands, you can suppress the error with the <code>$<\/code> variant of the command - e.g. <code>I.see$(\"Flying pigs\")<\/code>. This may be useful when you want so suppress the errors for commands used as conditional expressions in if\/else blocks.<\/li>\n\n<li>Error and log suppression - For all I and UI commands, you can suppress the error and logs with the <code>$$<\/code> variant of the command - e.g. <code>UI.execute$$(...)<\/code>.<\/li>\n\n<li>\n<code>UI.context<\/code> can now be used to explicitly switch the context to an iframe. This is useful in cases where security policies on the web application or on specific browsers prevents UI-licious from normally being able to access and evaluate the contents of iframes and test it.<\/li>\n\n<\/ul>\n\n<p>See the <a href=\"https:\/\/docs.uilicious.com\/scripting\/list_of_commands.html\" rel=\"noopener noreferrer\">list of commands<\/a>.<\/p>\n\n<h2>\n  \n  \n  \ud83d\udcc8 Reports\n<\/h2>\n\n<ul>\n<li>Better logs to indicate errors in the test script itself.<\/li>\n<li>Improved the performance of retrieving the status of the jobs on the monitoring dashboard.<\/li>\n<li>\n<code>UI.execute<\/code> now prints out a snippet of the script to execute to make the test reports and flow easier to understand. And you may also override the log message for UI.execute.<\/li>\n<li>Added duration to test run reports sent through webhooks<\/li>\n<\/ul>\n\n\n\n\n<h2>\n  \n  \n  Thank you!\n<\/h2>\n\n<p>A great big thank you to users who have kindly lent themselves to beta test this new feature and trying to break it and giving us comments on improve the user experience!<\/p>\n\n<h2>\n  \n  \n  Feedback?\n<\/h2>\n\n<p>We always love hearing feedback from users, and finding out the sorts of creative things people are doing with UI-licious, so feel free to drop us a note at <a href=\"mailto:support@uilicious.com\">support@uilicious.com<\/a>!<\/p>\n\n","category":["uilicious","releasenotes","testing","webdev"]},{"title":"Taking the horror out of UI testing \ud83d\ude31","pubDate":"Sat, 27 Oct 2018 14:37:50 +0000","link":"https:\/\/dev.to\/uilicious\/taking-the-horror-out-of-ui-testing--1fi6","guid":"https:\/\/dev.to\/uilicious\/taking-the-horror-out-of-ui-testing--1fi6","description":"<blockquote>\n<p>The problem isn't the broken tests. We just have very broken tools for UI testing.<\/p>\n<\/blockquote>\n\n<p>UI testing sucks. It really does. <\/p>\n\n<p>If you aren't already familiar with automating end-to-end tests yet, there's a few well-known free and open-source frameworks out there, in the order of Github stars: <a href=\"https:\/\/github.com\/segmentio\/nightmare\">NightmareJS (16K)<\/a>, <a href=\"https:\/\/github.com\/SeleniumHQ\/selenium\">Selenium (12K)<\/a>, <a href=\"https:\/\/github.com\/webdriverio\/webdriverio\">WebDriverIO (4K)<\/a>, <a href=\"https:\/\/github.com\/Codeception\/CodeceptJS\">CodeceptJS (1K)<\/a>.<\/p>\n\n<p>Tests typically look more or less like this - take a minute to figure out what this \"Hello World\" example from NightmareJS does \ud83e\udd14:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"kd\">const<\/span> <span class=\"nx\">Nightmare<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">require<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">nightmare<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span>\n<span class=\"kd\">const<\/span> <span class=\"nx\">nightmare<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">Nightmare<\/span><span class=\"p\">({<\/span> <span class=\"na\">show<\/span><span class=\"p\">:<\/span> <span class=\"kc\">true<\/span> <span class=\"p\">})<\/span>\n\n<span class=\"nx\">nightmare<\/span>\n  <span class=\"p\">.<\/span><span class=\"nx\">goto<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">https:\/\/duckduckgo.com<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">.<\/span><span class=\"nx\">type<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">#search_form_input_homepage<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">github nightmare<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">.<\/span><span class=\"nx\">click<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">#search_button_homepage<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">.<\/span><span class=\"nx\">wait<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">#r1-0 a.result__a<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">.<\/span><span class=\"nx\">evaluate<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nb\">document<\/span><span class=\"p\">.<\/span><span class=\"nx\">querySelector<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">#r1-0 a.result__a<\/span><span class=\"dl\">'<\/span><span class=\"p\">).<\/span><span class=\"nx\">href<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">.<\/span><span class=\"nx\">end<\/span><span class=\"p\">()<\/span>\n  <span class=\"p\">.<\/span><span class=\"nx\">then<\/span><span class=\"p\">(<\/span><span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">.<\/span><span class=\"k\">catch<\/span><span class=\"p\">(<\/span><span class=\"nx\">error<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\n    <span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">error<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">Search failed:<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"nx\">error<\/span><span class=\"p\">)<\/span>\n  <span class=\"p\">})<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n<p>Have you figured it out? <\/p>\n\n<p>What this does is to go to DuckDuckGo, enter \"github nightmare\" into the search box, press the search button, wait for the first result to show up, and print the link address of the first result. <\/p>\n\n<p>Come on people, I thought we already know that hard-coding stuff and using magic waits is a no-no. Test code is still code, and this code smells \ud83d\udca9. This makes things hard to read, and <strong>harder to maintain<\/strong>. What if product changes the design or front-end decides to do just a bit of spring cleaning? Damn, the tests broke. Ain't nobody got time to fix those hundred and one bloody CSS selectors!<\/p>\n\n<p>And, <strong>what are we really trying to test anyway?<\/strong><br>\nThe <strong>user journey<\/strong>, or <em>the HTML<\/em>?<\/p>\n\n<p>How about writing tests like this?<br>\n<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">goTo<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">https:\/\/duckduckgo.com<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">fill<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">Search<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">Github nightmare<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">pressEnter<\/span><span class=\"p\">()<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">see<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">Github - segmentio\/nightmare<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">click<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">Github - segmentio\/nightmare<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n<p>Concise. Readable. <strong>Maintainable<\/strong>.<br>\nAnd front-end agnostic. VueJS, ReactJS, Angular... does it matter?<\/p>\n\n<p><a class=\"mentioned-user\" href=\"https:\/\/dev.to\/picocreator\">@picocreator<\/a> and I have been working building web applications since pre-jQuery times, we both have accumulated our own 2AM horror stories in trying to making sure stuff gets tested and shipped <strong>on time<\/strong>, or having things blow up in our face by testing in production \ud83d\udca3\ud83d\udca3\ud83d\udca3\ud83d\ude31\ud83d\ude31\ud83d\ude31. We tell junior devs these horror stories every year on halloween night. Ok, getting a little side-tracked, anyway...<\/p>\n\n<p>We disagree a lot on software architecture and often debate what maintainable code looks like, but one thing we agree is that the problem isn't the broken tests. <strong>We just have very broken tools for UI testing<\/strong>. Someone needs to fix it. And this is what we've dedicated our past two years working on:<\/p>\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--r63YN5E3--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/ggw3s02vif0pk2z7qg30.gif\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--r63YN5E3--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/ggw3s02vif0pk2z7qg30.gif\" alt=\"Testing DuckDuckGo\" width=\"880\" height=\"1077\"><\/a><\/p>\n\n<p>Piece of cake. <\/p>\n\n<p>But this test is too simple. You are probably thinking, yeah that's nice, but what if things get more complicated, like when there's 50 \"Add to cart\" buttons, or what about icon buttons?<\/p>\n\n<p><strong>Let's have some fun, shall we?<\/strong> \ud83d\ude0e<\/p>\n\n<p><em>Oh wait, and before we start, just so you know, this absolutely isn't a black box algorithm Powered by AI\u2122, but more on that later.<\/em><\/p>\n<h1>\n  \n  \n  Testing Dev.To\n<\/h1>\n\n<p>Let's start with the basics, and make sure that the one of the most critical feature - Search - works.<br>\n<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">goTo<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">https:\/\/dev.to\/<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">fill<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">Search<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">dev.to<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">pressEnter<\/span><span class=\"p\">()<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">click<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">thepracticaldev<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">see<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">The hardworking team behind dev.to <\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span> <span class=\"c1\">\/\/ mmhm, very hardworking indeed.<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n<p>The nice thing about tests being decoupled from the implementation of the UI is that we can easily reuse the same test for testing responsive designs. Let's see if search works as expected on desktop and on mobile view.<\/p>\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--Bcli3NTo--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/95u22qzfzduklrn99zsb.gif\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--Bcli3NTo--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/95u22qzfzduklrn99zsb.gif\" alt=\"Testing search on dev.to\" width=\"880\" height=\"546\"><\/a><\/p>\n\n<p>Now, let's try clicking on DEV.to's logo to get back home. UI-licious scans for accessibility attributes and tooltips set with <code>title<\/code> and other similar attributes used by various popular frameworks. Does our home logo have something we can use?<br>\n<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight html\"><code><span class=\"nt\">&lt;a<\/span> <span class=\"na\">href=<\/span><span class=\"s\">\"\/\"<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"logo-link\"<\/span> <span class=\"na\">id=<\/span><span class=\"s\">\"logo-link\"<\/span> <span class=\"na\">aria-label=<\/span><span class=\"s\">\"DEV Home\"<\/span><span class=\"nt\">&gt;&lt;svg<\/span> <span class=\"err\">...<\/span> <span class=\"nt\">\/&gt;&lt;\/a&gt;<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n<p>Oh look, this is what DEV.to's logo looks like under the hood. There's an <code>aria-label<\/code>, <strong>fantastic<\/strong>! Let's click on \"Dev Home\".<br>\n<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">click<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">DEV Home<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span> <span class=\"c1\">\/\/ We love aria-labels<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">amAt<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">https:\/\/dev.to\/<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n<p>There we go:<\/p>\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--_ZStAJgi--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/i37xe7c6ljv7h62g5j2x.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--_ZStAJgi--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/i37xe7c6ljv7h62g5j2x.png\" alt=\"Clicking on logo using ARIA-labels\" width=\"880\" height=\"466\"><\/a><\/p>\n\n<p>Ok, let's get creative and do a bit of shopping at the Dev Shop. I'm just gonna grab a hundred of these Sticker Pack and Dev totes.<br>\n<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">click<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">DEV Shop<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">amAt<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">https:\/\/shop.dev.to\/<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n\n<span class=\"kd\">let<\/span> <span class=\"nx\">shopping_list<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span>\n  <span class=\"dl\">\"<\/span><span class=\"s2\">Dev tote<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\n  <span class=\"dl\">\"<\/span><span class=\"s2\">Sticker Pack<\/span><span class=\"dl\">\"<\/span>\n<span class=\"p\">]<\/span>\n<span class=\"nx\">shopping_list<\/span><span class=\"p\">.<\/span><span class=\"nx\">forEach<\/span><span class=\"p\">((<\/span><span class=\"nx\">item<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>   \n  <span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">click<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">The DEV shop<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n  <span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">click<\/span><span class=\"p\">(<\/span><span class=\"nx\">item<\/span><span class=\"p\">)<\/span>\n  <span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">fill<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">Quantity<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"mi\">100<\/span><span class=\"p\">)<\/span> <span class=\"c1\">\/\/ lets' get a hundred of each<\/span>\n  <span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">click<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">Add to cart<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">})<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--RDYFos8N--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/fun5vv0au3k19zaccgol.gif\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--RDYFos8N--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/fun5vv0au3k19zaccgol.gif\" alt=\"Shopping on DEV.to\" width=\"880\" height=\"546\"><\/a><\/p>\n\n<p>Ok... almost done. No wait, let's just grab a few more totes. Hmm... there's a few rows of items in the cart, we need to pick the correct quantity box to update. No sweat, I just need to be a little specific and tell UI-licious what <code>I.see<\/code> before updating the quantity.<br>\n<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">amAt<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">\/cart<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">see<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">Dev tote<\/span><span class=\"dl\">\"<\/span><span class=\"p\">)<\/span> \n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">fill<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">Quantity<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span> <span class=\"mi\">120<\/span><span class=\"p\">)<\/span> <span class=\"c1\">\/\/ UI-licious will pick the quantity box for \"Dev Tote\" to fill<\/span>\n<span class=\"nx\">I<\/span><span class=\"p\">.<\/span><span class=\"nx\">pressEnter<\/span><span class=\"p\">()<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--2Q0z2jOK--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/51khr52csal5cpkojp1m.gif\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--2Q0z2jOK--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/51khr52csal5cpkojp1m.gif\" alt=\"Resolving similarities\" width=\"880\" height=\"481\"><\/a><\/p>\n\n<p>And to top it off, let's just do some test-ception, just to make sure UI-licious itself works. <\/p>\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--0HN6q09f--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/c6f4abcajsoojek7tnbo.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--0HN6q09f--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/c6f4abcajsoojek7tnbo.png\" alt=\"Eating our own dogfood\" width=\"880\" height=\"514\"><\/a><\/p>\n\n<p>Yea baby. Piece of cake. \ud83d\ude0e<\/p>\n<h1>\n  \n  \n  Under the hood\n<\/h1>\n\n<p>No, it's not Powered by AI\u2122. Not in the modern sense at least. <\/p>\n\n<p>Warning, opinion\u2122 ahead! Tests should be deterministic, meaning it should always produce the same result given the same input. Random unpredictable behaviour isn't exactly desirable in tests, and fixing defects in an AI-driven test engine involves... throwing more \"correct\" sample data at it to make it more accurate. <\/p>\n\n<p>UI-licious works by methodologically reverse engineering intent (What you mean by <code>I.click(\"Sign in\")<\/code> from your HTML) and what the previous steps were. It works best on accessible websites. Your code <em>doesn't have to be perfect<\/em> to be testable, but it certainly helps to use <strong>semantic HTML and ARIA attributes<\/strong>. <\/p>\n\n<p><em>(And by the way, the UI-licious IDE is completely built using VueJS. \\o\/)<\/em><\/p>\n<h1>\n  \n  \n  Making testing great... for the person fixing the bug.\n<\/h1>\n\n<p>I think the most annoying part of getting bug reports is when they are incomplete and I need to chase after the reporter for steps to replicate the bug. While at the same time, to be honest, I get lazy reporting bugs too. That's why we try to make bug replication reports as complete and actionable (and pretty!) as possible. \ud83d\udc47<\/p>\n\n<p><a href=\"https:\/\/snippet.uilicious.com\/test\/public\/1f9qgLPBa5EC4ryZbnnAoB\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--eyrnXkHJ--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/lbs70o62kaztpa04cmxx.gif\" alt=\"Awesome bug replication reports\" width=\"880\" height=\"580\"><\/a><\/p>\n<h1>\n  \n  \n  When should you automate UI testing?\n<\/h1>\n\n<p>A good guideline is: When you are testing <em>that<\/em> login flow for <em>that<\/em> user role for the <em>n-th<\/em> time. <\/p>\n\n<p>And also \ud83d\udc47<\/p>\n\n\n<div class=\"ltag__link\">\n  <a href=\"\/klamping\" class=\"ltag__link__link\">\n    <div class=\"ltag__link__pic\">\n      <img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--Rwd8DW8l--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--nBTURO0x--\/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150\/https:\/\/dev-to-uploads.s3.amazonaws.com\/uploads\/user\/profile_image\/32007\/e8d792cc-921f-4e2a-979c-e2060360c382.jpeg\" alt=\"klamping\">\n    <\/div>\n  <\/a>\n  <a href=\"\/klamping\/stop-dont-test-that-21hh\" class=\"ltag__link__link\">\n    <div class=\"ltag__link__content\">\n      <h2>Stop! Don't test that!<\/h2>\n      <h3>Kevin Lamping \u30fb Oct 11 '18 \u30fb 3 min read<\/h3>\n      <div class=\"ltag__link__taglist\">\n        <span class=\"ltag__link__tag\">#testing<\/span>\n        <span class=\"ltag__link__tag\">#javascript<\/span>\n      <\/div>\n    <\/div>\n  <\/a>\n<\/div>\n\n\n\n<p>Should you automate unit tests or integration tests or end-to-end test first? Doesn't matter, just start somewhere. I usually recommend starting with unit tests for anything requiring complex conditionals and math, and end-to-end tests for critical user flows because these can also help catch errors downstream too.<\/p>\n\n<h1>\n  \n  \n  Pros and cons?\n<\/h1>\n\n<p>Pro: It starts at $0. And it's one less thing for devops to worry about.<br>\nCon: It's not open-source, yet. (... until money falls from the sky)<\/p>\n<h1>\n  \n  \n  Is the cake real?\n<\/h1>\n\n<p>Yes. It's not a lie, we always have cake where ever we go.<\/p>\n\n\n<blockquote class=\"ltag__twitter-tweet\">\n      <div class=\"ltag__twitter-tweet__media\">\n        <img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--iF3IZNc1--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/pbs.twimg.com\/media\/DZBneALVAAAFRYT.jpg\" alt=\"unknown tweet media content\">\n      <\/div>\n\n  <div class=\"ltag__twitter-tweet__main\">\n    <div class=\"ltag__twitter-tweet__header\">\n      <img class=\"ltag__twitter-tweet__profile-image\" src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s---P4msmh_--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/pbs.twimg.com\/profile_images\/918757448273444864\/UR00Vrw0_normal.jpg\" alt=\"UI-licious profile image\">\n      <div class=\"ltag__twitter-tweet__full-name\">\n        UI-licious\n      <\/div>\n      <div class=\"ltag__twitter-tweet__username\">\n        @ui_licious\n      <\/div>\n      <div class=\"ltag__twitter-tweet__twitter-logo\">\n        <img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--ir1kO05j--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev.to\/assets\/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg\" alt=\"twitter logo\">\n      <\/div>\n    <\/div>\n    <div class=\"ltag__twitter-tweet__body\">\n      Break time <a href=\"https:\/\/twitter.com\/fossasia\">@fossasia<\/a>! Have some \ud83c\udf70 and \u2615 at the exhibition hall. \n    <\/div>\n    <div class=\"ltag__twitter-tweet__date\">\n      04:01 AM - 24 Mar 2018\n    <\/div>\n\n\n    <div class=\"ltag__twitter-tweet__actions\">\n      <a href=\"https:\/\/twitter.com\/intent\/tweet?in_reply_to=977394981714325504\" class=\"ltag__twitter-tweet__actions__button\">\n        <img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--fFnoeFxk--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev.to\/assets\/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg\" alt=\"Twitter reply action\">\n      <\/a>\n      <a href=\"https:\/\/twitter.com\/intent\/retweet?tweet_id=977394981714325504\" class=\"ltag__twitter-tweet__actions__button\">\n        <img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--k6dcrOn8--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev.to\/assets\/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg\" alt=\"Twitter retweet action\">\n      <\/a>\n      <a href=\"https:\/\/twitter.com\/intent\/like?tweet_id=977394981714325504\" class=\"ltag__twitter-tweet__actions__button\">\n        <img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--SRQc9lOp--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/dev.to\/assets\/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg\" alt=\"Twitter like action\">\n      <\/a>\n    <\/div>\n  <\/div>\n<\/blockquote>\n\n\n<p><strong>And Happy Halloween folks!<\/strong><br>\n\ud83d\udc7b\ud83d\udc7b\ud83d\udc7b<\/p>\n\n<h1>\n  \n  \n  Credits\n<\/h1>\n\n<p>We have a very small but dedicated team of senior and junior devs -&gt; <a class=\"mentioned-user\" href=\"https:\/\/dev.to\/picocreator\">@picocreator<\/a>, <a class=\"mentioned-user\" href=\"https:\/\/dev.to\/jmtiong\">@jmtiong<\/a>, <a class=\"mentioned-user\" href=\"https:\/\/dev.to\/sopnopriyo\">@sopnopriyo<\/a>, Wesley Chang, and myself.<\/p>\n\n<p>Cover Photo by NeONBRAND on Unsplash<\/p>\n\n","category":["testing","showdev","webdev","javascript"]},{"title":"\ud83d\ude80 What's new in UI-licious? IE11, highlighted interactions, user agents and more.","pubDate":"Thu, 11 Oct 2018 16:33:22 +0000","link":"https:\/\/dev.to\/uilicious\/whats-new-in-ui-licious-ie11-highlighted-interactions-user-agents-and-more-1a2","guid":"https:\/\/dev.to\/uilicious\/whats-new-in-ui-licious-ie11-highlighted-interactions-user-agents-and-more-1a2","description":"<p>Over here in <a href=\"https:\/\/uilicious.com\">UI-licious<\/a>, we're hell bent on a mission to get everyone to automate UI testing and build a better web for everyone.<\/p>\n\n<p>If you haven't heard of UI-licious, we're just a neat little tool for you to automatically test user journeys on your web app - that just works.<\/p>\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--wMtMJFCH--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/12ad7sjuaotvawcyy2vg.gif\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--wMtMJFCH--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/12ad7sjuaotvawcyy2vg.gif\" alt=\"Testing is a piece of cake\" width=\"880\" height=\"519\"><\/a><\/p>\nTesting is a piece of cake.\n\n\n\n<p>So what's new in UI-licious?<\/p>\n\n<h1>\n  \n  \n  Run tests on IE11\n<\/h1>\n\n<p>If you don't need to support IE11, good for you!<\/p>\n\n<p>But if you are stuck with having to develop for IE11, there's finally good news!<\/p>\n\n<p>After two years of building UI-licious, we now support testing on IE11. \ud83c\udf89 <\/p>\n\n<p>We're super happy that we've finally gotten here. One of the core philosophies that <a class=\"mentioned-user\" href=\"https:\/\/dev.to\/picocreator\">@picocreator<\/a> and I decided when we first started building UI-licious was that <strong>tests must be independent<\/strong> from how the UI would be implemented and the environment (i.e. OS &amp; browser) where the UI is served. The reason why we felt this was important was that <strong>it makes tests more reusable and maintainable<\/strong>. However, each browser had its own quirks in rendering and its own quirks in the implementation of the WebDriver protocol for browser automation, so many test engineers resort to writing browser-specific hacks in their test scripts to work around these inconsistencies. I don't blame the browsers for not getting test automation right using the WebDriver protocol standards, the specs aren't exactly the clearest, and THEY KEEP CHANGING, BLOODY HELL.<\/p>\n\n<p>On the bright side, we will never have to worry about the specs changing for IE11\u200a-\u200aever.<\/p>\n\n<h1>\n  \n  \n  Highlights for interaction commands\n<\/h1>\n\n<p>One of the goals of UI-licious is to make bug replication reports as <strong>clear, complete and unambiguous<\/strong> as possible. As a software engineer, I used to get terribly annoyed when bug reports are filed with incomplete or unclear information and I\u2019d have chase after the reporter for clarification.<\/p>\n\n<p>So we added highlights to show you where <code>I.click<\/code> and <code>I.fill<\/code> commands are performed on the screen. We\u2019ll slowly be rolling this feature out for other commands, just wait!<\/p>\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--_TL-2pxs--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/4r4hv6phwhwyu67a88sv.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--_TL-2pxs--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/4r4hv6phwhwyu67a88sv.png\" alt=\"Highlights for interaction commands\" width=\"555\" height=\"619\"><\/a> <\/p>\nNow you can see better! \ud83e\udd13\n\n\n\n<h1>\n  \n  \n  Full page screenshots\n<\/h1>\n\n<p>We\u2019ve added new command <code>TEST.takeFullScreenshot<\/code> for you to take full page screenshots of the current page. <em>This is currently supported only for Safari and IE11 tests.<\/em> But, use it when you really need to! Taking full page screenshots all the time will slow down your tests.<\/p>\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s---kvxjEnF--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/5x93t3llzwgkck8cdqre.gif\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s---kvxjEnF--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/5x93t3llzwgkck8cdqre.gif\" alt=\"Taking full page screenshots\" width=\"812\" height=\"852\"><\/a> <\/p>\nHm\u2026 let\u2019s do a bit of window shopping today\u2026\n\n\n\n<h1>\n  \n  \n  Set browser user agent\n<\/h1>\n\n<p>If your web application relies on browser user agent detection, you can set the user agent for the test run. <em>This is currently supported only for Chrome tests.<\/em> By default, the user agent string will be automatically determined by the OS and the browser. You can check the user agent of your test with <code>I.goTo(\"https:\/\/www.whatismybrowser.com\/detect\/what-is-my-user-agent\")<\/code><\/p>\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--LWVYHwZO--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/gqsugjqchncnkcsks7yy.gif\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--LWVYHwZO--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/gqsugjqchncnkcsks7yy.gif\" alt=\"How to set the browser user agent\" width=\"880\" height=\"485\"><\/a> <\/p>\nHow to set the user agent for a test run\n\n\n\n<h1>\n  \n  \n  Write custom reporting logs\n<\/h1>\n\n<p>While we aim to make UI-licious as simple as possible to write tests, we hate to compromise on the flexibility of writing custom functions and reporting logs for your requirements. To support writing custom reports, we\u2019ve added three logging commands for you to use - <code>TEST.log.pass<\/code>, <code>TEST.log.fail<\/code>, <code>TEST.log.info<\/code>.<\/p>\n\n<p>Here\u2019s how to use them:<\/p>\n\n\n<div class=\"ltag_gist-liquid-tag\">\n  \n<\/div>\nThis script has a custom function to compare two strings and pass the test if strings are different and fails the test otherwise.\n\n\n\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--yme22QBC--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/sxzpffthycpzizopr02x.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--yme22QBC--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/sxzpffthycpzizopr02x.png\" alt=\"Write custom reporting logs\" width=\"880\" height=\"483\"><\/a><\/p>\nJust making sure this random string generator is legit.\n\n\n\n<h1>\n  \n  \n  Other enhancements\n<\/h1>\n\n<p>There\u2019s a bunch more stuff that we\u2019ve rolled out to improve your experience of writing and running tests using UI-licious.<\/p>\n\n<ul>\n<li>Added prompts to warn you when you have unsaved work to make sure that you don\u2019t accidentally lose your changes when you navigate away while editing a test! There\u2019s also a switch to disable the autosave functionality for those of us who have cats that decide that the keyboard is the warmest place to take a nap\u2026<\/li>\n<\/ul>\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--V_ZiwcLb--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/s9lhmlisq3zlrjeny656.jpeg\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--V_ZiwcLb--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/s9lhmlisq3zlrjeny656.jpeg\" alt=\"Cat on keyboard\" width=\"500\" height=\"281\"><\/a><\/p>\nSigh, not again\u2026\n\n\n\n<ul>\n<li><p>\u201cView test\u201d button to open the test script for a step.<\/p><\/li>\n<li><p>Download test reports as JSON or CSV.<\/p><\/li>\n<li><p>Pagination on the test run history page for your jobs, so you can go back in time.<\/p><\/li>\n<\/ul>\n\n<h1>\n  \n  \n  Coming up next...\n<\/h1>\n\n<p>The next few features we are working on is <strong>enhancements to your job reports<\/strong> (we need more stats and fancy charts!), and multi-user support so you can <strong>share and collaborate<\/strong> with members of your team. You will be able to set and configure test data sets to use in test runs, so that you can quickly <strong>swap out the test data for different environments<\/strong> from the UI. We\u2019re also rolling out <strong>embeddable versions of test run reports<\/strong>, so that you can just stick them in your issue trackers.<\/p>\n\n<p><a href=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--yuAm8nvf--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/67rc1ds9id64p0q6vwql.png\" class=\"article-body-image-wrapper\"><img src=\"https:\/\/res.cloudinary.com\/practicaldev\/image\/fetch\/s--yuAm8nvf--\/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880\/https:\/\/thepracticaldev.s3.amazonaws.com\/i\/67rc1ds9id64p0q6vwql.png\" alt=\"Sneak peak of embedded reports\" width=\"880\" height=\"416\"><\/a><\/p>\nA sneak peak of embedded reports!\n\n\n\n<p>On the other note, we know that there\u2019s areas where the performance could be better\u200a\u2014\u200awe\u2019re working on speeding up test startup times and shaving off a few more milliseconds in retrieving your reports and screenshots. <\/p>\n\n<h1>\n  \n  \n  Feedback, suggestions?\n<\/h1>\n\n<p>As always, feedbacks and suggestions are always welcomed. <\/p>\n\n<p>Leave your thoughts in the comments.<\/p>\n\n<p>And check out our roadmap on <a href=\"https:\/\/trello.com\/b\/G80eoZU3\/ui-licious-development-roadmap\">Trello<\/a> here.<\/p>\n\n<p>Cheers!<\/p>\n\n<p>Let\u2019s build a better web!<\/p>\n\n<p><em>This article's <a href=\"https:\/\/blog.uilicious.com\/whats-new-in-ui-licious-oct-2018-9f64339d7b81\">a re-post<\/a> from our blog, where we talk about testing and automating the shit out of our entire dev stack.<\/em> <\/p>\n\n","category":["testing","frontend"]}]}}