<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Johannes Dienst</title>
    <description>The latest articles on DEV Community by Johannes Dienst (@johannesdienst).</description>
    <link>https://dev.to/johannesdienst</link>
    <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%2F919231%2F4f51f573-9e3c-49e1-9534-0afce74fd0e0.png</url>
      <title>DEV Community: Johannes Dienst</title>
      <link>https://dev.to/johannesdienst</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/johannesdienst"/>
    <language>en</language>
    <item>
      <title>Use Playwright and AskUI Together</title>
      <dc:creator>Johannes Dienst</dc:creator>
      <pubDate>Wed, 25 Sep 2024 08:42:53 +0000</pubDate>
      <link>https://dev.to/askui/use-playwright-and-askui-together-1oam</link>
      <guid>https://dev.to/askui/use-playwright-and-askui-together-1oam</guid>
      <description>&lt;h1&gt;
  
  
  Use Playwright and AskUI Together
&lt;/h1&gt;

&lt;p&gt;Playwright is the de facto standard when it comes to reliably testing browser applications across browsers and operating systems.&lt;/p&gt;

&lt;p&gt;But what if your workflow under test leaves your browser and requires you to automate on a desktop application?&lt;/p&gt;

&lt;p&gt;AskUI is not confined to a specific application because it uses visual selectors and real &lt;strong&gt;human user-like&lt;/strong&gt; input with keypresses and natural mouse movement. This makes it an ideal tool to accompany Playwright.&lt;/p&gt;

&lt;p&gt;In this post you will learn to setup Playwright together with AskUI to define workflows that can leave the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AskUI installed and configured on your system (&lt;a href="https://docs.askui.com/docs/general/Getting%20Started/getting-started" rel="noopener noreferrer"&gt;Windows&lt;/a&gt;, &lt;a href="https://docs.askui.com/docs/general/Getting%20Started/getting-started-linux" rel="noopener noreferrer"&gt;Linux&lt;/a&gt;, &lt;a href="https://docs.askui.com/docs/general/Getting%20Started/getting-started-macos" rel="noopener noreferrer"&gt;macOS&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install Playwright
&lt;/h2&gt;

&lt;p&gt;Playwright for JavaScript/TypeScript consists of two components.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The playwright node package&lt;/li&gt;
&lt;li&gt;A browser installed through Playwright library&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First you have to install &lt;code&gt;playwright&lt;/code&gt; as a dev-dependency. Execute the following command in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; playwright
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this blog you will use Chromium as a browser. The installation through &lt;code&gt;playwright&lt;/code&gt; is done like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx playwright &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--with-deps&lt;/span&gt; chromium
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure Playwright
&lt;/h2&gt;

&lt;p&gt;You will configure Playwright and the Chromium browser in the file &lt;code&gt;helpers/askui-helper.ts&lt;/code&gt;, so it will work correctly together with Jest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UiControlClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UiController&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;askui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AskUIAllureStepReporter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@askui/askui-reporters&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Server for controlling the operating system&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;uiController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UiController&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Client is necessary to use the askui API&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UiControlClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Playwright setup&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Browser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BrowserContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;playwright&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;pwBrowser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Browser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;pwContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BrowserContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Playwright setup&lt;/span&gt;
  &lt;span class="nx"&gt;pwBrowser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;slowMo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;pwContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pwBrowser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Desktop Chrome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="nx"&gt;uiController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UiController&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="cm"&gt;/**
      * Select the display you want to run your tests on, display 0 is your main display;
      * ignore if you have only one display
      */&lt;/span&gt;
    &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;uiController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;aui&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;UiControlClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;workspaceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;your-workspace-id&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;your-access-token&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;reporter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AskUIAllureStepReporter&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="nf"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Playwright setup&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pwContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pwBrowser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;uiController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// 4. Playwright setup&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pwContext&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Import the necessary classes from &lt;code&gt;playwright&lt;/code&gt; and define the variables&lt;/li&gt;
&lt;li&gt;Configure chromium to be &lt;strong&gt;not&lt;/strong&gt; headless and also slow down its execution. Also we want a &lt;code&gt;Desktop Chrome&lt;/code&gt; as we are on desktop.&lt;/li&gt;
&lt;li&gt;Close the &lt;code&gt;BrowserContext&lt;/code&gt; and the &lt;code&gt;Browser&lt;/code&gt; after the execution finished.&lt;/li&gt;
&lt;li&gt;Export the Context so you can use it in your workflow&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Write a Workflow
&lt;/h2&gt;

&lt;p&gt;With the &lt;code&gt;BrowserContext&lt;/code&gt; set up, hop over to the file &lt;code&gt;my-first-askui-test-seuite.test.ts&lt;/code&gt; and import it there. Add this to the start of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pwContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./helpers/askui-helper&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can implement a simple workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a new tab&lt;/li&gt;
&lt;li&gt;Navigate to &lt;a href="https://google.com/" rel="noopener noreferrer"&gt;https://google.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click away the cookie banner&lt;/li&gt;
&lt;li&gt;Fill the search field with &lt;strong&gt;Playwright framework&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Press &lt;strong&gt;Enter&lt;/strong&gt; on keyboard&lt;/li&gt;
&lt;li&gt;Switch to image search by clicking on the Button &lt;strong&gt;architecture&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Move the mouse cursor to the element that has an image with a red and a green mask in it (Playwright logo description)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jest with askui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should open askui.com in Chrome with playwright&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pwContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://google.com/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Reject all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;//textarea[@name="q"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Playwright framework&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keyboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;press&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// AskUI workflow&lt;/span&gt;
    &lt;span class="c1"&gt;// Make sure you have the focus on the browser&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mouseLeftClick&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;button&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;withText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Images&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;architecture&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;moveMouseTo&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;element&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;matching&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image with a red and a green mask in it&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testrun
&lt;/h2&gt;

&lt;p&gt;To run this you need to run this command in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;// Linux + macOS
npm run askui

// Windows
AskUI-RunProject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following gif shows the execution of the whole workflow:&lt;/p&gt;

&lt;p&gt;&lt;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%2Fecupuvo91zgy1mtvktud.gif" class="article-body-image-wrapper"&gt;&lt;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%2Fecupuvo91zgy1mtvktud.gif" alt="Gif showing the Playwright and AskUI workflow in action (2 times speedup)."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Combining AskUI with Playwright enhances automation capabilities to extend beyond web browsers. While Playwright is a solid choice for browser automation, AskUI takes the reins when dealing with desktop apps and operating systems.&lt;/p&gt;

&lt;p&gt;This integration offers a practical toolkit for workflows across various applications for efficient automation.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Run AskUI Workflows with Pipedream for Smoke Testing</title>
      <dc:creator>Johannes Dienst</dc:creator>
      <pubDate>Wed, 07 Aug 2024 14:18:25 +0000</pubDate>
      <link>https://dev.to/askui/run-askui-workflows-with-pipedream-for-smoke-testing-d82</link>
      <guid>https://dev.to/askui/run-askui-workflows-with-pipedream-for-smoke-testing-d82</guid>
      <description>&lt;p&gt;Dylan Pierce shared an interesting approach to smoke testing at our meetup: Fill out a contact form with the help of Computer Vision &amp;amp; Large Language Models. Especially the vision part was impressive but it lacked one specific feature: Interaction with the form. How can you be sure your form works if you do not try to fill it out and send it?&lt;/p&gt;

&lt;p&gt;This is where I thought an AskUI integration into the Pipedream workflow could be useful. AskUI uses visual selectors instead of code selectors and can interact like a human with a form like that. Giving us a &lt;em&gt;real&lt;/em&gt; smoke test!&lt;/p&gt;

&lt;p&gt;In this blog I will describe how I integrated AskUI into a Pipedream workflow, so we get the benefit of visual selection and user interaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AskUI Controller installed and configured on a remote accessible system like Gitpod or a cloud machine. You can use our &lt;a href="https://github.com/askui/askui-try-out" rel="noopener noreferrer"&gt;Try-Out-Repository&lt;/a&gt; or install on your own system (&lt;a href="https://docs.askui.com/docs/general/Getting%20Started/getting-started" rel="noopener noreferrer"&gt;Windows&lt;/a&gt;, &lt;a href="https://docs.askui.com/docs/general/Getting%20Started/getting-started-linux" rel="noopener noreferrer"&gt;Linux&lt;/a&gt;, &lt;a href="https://docs.askui.com/docs/general/Getting%20Started/getting-started-macos" rel="noopener noreferrer"&gt;macOS&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Pipedream account and a workflow. Follow their &lt;a href="https://pipedream.com/docs" rel="noopener noreferrer"&gt;Introduction&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Will We Build? Aka the Use Case
&lt;/h2&gt;

&lt;p&gt;Dylan Pierce showed an awesome use case with Pipedream and Puppeteer, where he implemented a smoke test with the help of AI without writing any selectors by himself. I highly recommend to watch the recording: &lt;a href="https://youtu.be/o6hej9Ip2vs" rel="noopener noreferrer"&gt;https://youtu.be/o6hej9Ip2vs&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Our Use Case: Smoke Test with Visual Selectors
&lt;/h2&gt;

&lt;p&gt;Dylans use case involved querying a Large Language/Multimodal Model to implement the smoke test. We will modify this a little bit to use the visual selectors from AskUI, which do not rely on the specific UI-technology but identify elements through their appearance with an AI vision model.&lt;/p&gt;

&lt;p&gt;Here are the steps we will implement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trigger the smoke test once per day&lt;/li&gt;
&lt;li&gt;Perform the smoke test with AskUI on a remote system&lt;/li&gt;
&lt;li&gt;Send an email if the smoke test was successful or not&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Add a Trigger
&lt;/h2&gt;

&lt;p&gt;The first thing Pipedream wants us to add is a trigger. We add a &lt;strong&gt;Schedule&lt;/strong&gt; trigger that will run our Workflow everyday at 9:00 AM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv78qv6iavkass6s8azh5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv78qv6iavkass6s8azh5.png" alt="Pipeship trigger configuration." width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Prepare a Custom Code Action
&lt;/h2&gt;

&lt;p&gt;AskUI does not have an action in Pipedream. So we will make use of the AskUI node package and run a custom code action. For this we a a _&lt;em&gt;Code&lt;/em&gt; action. In this action we will fill out the simple authentication from &lt;a href="https://authenticationtest.com/simpleFormAuth/" rel="noopener noreferrer"&gt;https://authenticationtest.com/simpleFormAuth/&lt;/a&gt;. If we see the success page we will send &lt;/p&gt;

&lt;p&gt;We have to do the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a property &lt;em&gt;uiControllerUrl&lt;/em&gt;, so we do not have to hardcode it into the code&lt;/li&gt;
&lt;li&gt;Store our credentials in &lt;a href="https://pipedream.com/settings/env-vars" rel="noopener noreferrer"&gt;environment variables&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Import &lt;code&gt;UiControlClient&lt;/code&gt; from the &lt;code&gt;askui&lt;/code&gt; node package&lt;/li&gt;
&lt;li&gt;Configure the AskUI &lt;em&gt;UiControlClient&lt;/em&gt; to use the credentials and the &lt;em&gt;uiControllerUrl&lt;/em&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first thing we want to add to our custom code is the &lt;strong&gt;uiControllerUrl&lt;/strong&gt;. Click &lt;strong&gt;Refresh fields&lt;/strong&gt; so the &lt;strong&gt;Configure&lt;/strong&gt; tab shows up at the start of the action. Here is the relevant code snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineComponent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;

  &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;uiControllerUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then head over to &lt;a href="https://pipedream.com/settings/env-vars" rel="noopener noreferrer"&gt;environment variables&lt;/a&gt; and add your &lt;strong&gt;workspaceId&lt;/strong&gt; and &lt;strong&gt;accessToken&lt;/strong&gt; there. You obtained those by following the prerequisites instructions above. You can now setup the connection to the AskUI Controller over the &lt;strong&gt;UiControlClient&lt;/strong&gt; like this. Notice how easy it is to use arbitrary node-packages in Pipedream? I only needed to import &lt;strong&gt;UiControlClient&lt;/strong&gt; and it just worked 🥳.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: The following code also contains the teardown.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UiControlClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;askui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;UiControlClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;workspaceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspaceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;uiControllerUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uiControllerUrl&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// AskUI Workflow will be added here&lt;/span&gt;

    &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Write the AskUI Workflow
&lt;/h2&gt;

&lt;p&gt;When you look at our example form, we want to fill out, you notice that we have to do the following things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write &lt;code&gt;simpleForm@authenticationtest.com&lt;/code&gt; into the textfield &lt;em&gt;E-Mail Address&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Write &lt;code&gt;pa$$w0rd&lt;/code&gt; into the next textfield&lt;/li&gt;
&lt;li&gt;Click the button &lt;code&gt;Log In&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Validate the success&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The less inference we invoke the faster the AskUI workflow will execute. Everything that prompts AskUI to search for an element on the screen invokes inference. So let us try to invoke the inference only once by finding the first textfield for the E-Mail Address. Then we will use keypresses to navigate the form. This is the code to achieve this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This line only works with the Gitpod setup shown in the next section&lt;/span&gt;
&lt;span class="c1"&gt;// Select the browser url textfield for your use case with the appropriate instruction!&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;typeIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://authenticationtest.com/simpleFormAuth/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;textfield&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;containsText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;docs.askui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;enter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Fill out the form&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;typeIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;simpleForm@authenticationtest.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;textfield&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;E-Mail Address&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pa$$w0rd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;enter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Check if the the login succeeded: Login Success is shown on the page&lt;/span&gt;
&lt;span class="c1"&gt;// Pass result to next step&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Login Success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Smoke Test successful!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Smoke Test failed!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Send an Email
&lt;/h2&gt;

&lt;p&gt;Doing a smoke test without reporting about its success state would not help us. So we will just send us an email with the &lt;strong&gt;Send Yourself an Email&lt;/strong&gt; action. &lt;/p&gt;

&lt;p&gt;As subject we choose &lt;strong&gt;Smoke Test State&lt;/strong&gt; and for the text we reference our success variable we returned in our action above with &lt;code&gt;{{steps.code.$return_value.success}}&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gitpod As Remote Machine
&lt;/h2&gt;

&lt;p&gt;If you do not have a remote machine ready-to-go you can use a service like Gitpod. We have a prepared &lt;a href="https://github.com/askui/askui-try-out" rel="noopener noreferrer"&gt;Try-Out-Repository&lt;/a&gt; which comes with AskUI already setup and a VNC to observe the AskUI workflow. Start the repository in Gitpod over the &lt;strong&gt;Open in Gitpod&lt;/strong&gt;-button and let it finish the predefined AskUI workflow. When it reached the AskUI Docs (docs.askui.com) maximize the browser window in the &lt;strong&gt;Simple Browser&lt;/strong&gt; tab.&lt;/p&gt;

&lt;p&gt;Switch to the &lt;strong&gt;TERMINAL&lt;/strong&gt; tab and start the AskUI-Controller with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./node_modules/askui/dist/release/latest/linux/askui-ui-controller.AppImage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also make sure that you expose the port to the AskUI Controller (open lock icon). Head to the &lt;strong&gt;PORTS&lt;/strong&gt; tab and make the port &lt;code&gt;6769&lt;/code&gt; public. Then copy the URL and add it as the property &lt;strong&gt;uiControllerUrl&lt;/strong&gt; in the Pipedream action. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fov9ehjoxsc92wblzgngm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fov9ehjoxsc92wblzgngm.png" alt="Expose the AskUI Controller on port 6769" width="800" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a smoke test with Pipedream and AskUI was a practical use case to see how both tools integrate. The simplicity of Pipedream and its ability to integrate JavaScript code and Node packages was helpful. With that AskUI could be setup seamlessly inside an action and connected to an external AskUI Controller.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Use AskUI and Cucumber Together</title>
      <dc:creator>Johannes Dienst</dc:creator>
      <pubDate>Wed, 24 Jul 2024 07:27:45 +0000</pubDate>
      <link>https://dev.to/askui/use-askui-and-cucumber-together-2803</link>
      <guid>https://dev.to/askui/use-askui-and-cucumber-together-2803</guid>
      <description>&lt;h1&gt;
  
  
  Use AskUI and Cucumber Together
&lt;/h1&gt;

&lt;p&gt;By defining the behavior of a system in a structured format like Gherkin, Behavior-Driven Development (BDD) enables teams to bridge the gap between stakeholders, testers, and developers, avoiding misunderstandings and reducing rework. As a collaborative approach, BDD encourages all parties to work together from the outset, ensuring that everyone is on&lt;br&gt;
the same page and that requirements are accurately captured.&lt;/p&gt;

&lt;p&gt;In this process, Cucumber is a popular tool used to implement BDD, enabling teams to write clear, executable tests that ensure the system behaves as expected.&lt;/p&gt;

&lt;p&gt;In this blog post, we'll show you how to set up Cucumber in conjunction with AskUI to define AskUI workflows using BDD principles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fknb6otmcg5ggtlkuwf3v.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fknb6otmcg5ggtlkuwf3v.gif" alt="Gif showing the whole workflow. Open new tab in Google Chrome browser, typing in the AskUI Practice Page URL and pressing enter. Then the practice page is opened" width="720" height="395"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;AskUI installed and configured on your system (&lt;a href="https://docs.askui.com/docs/general/Getting%20Started/getting-started" rel="noopener noreferrer"&gt;Windows&lt;/a&gt;, &lt;a href="https://docs.askui.com/docs/general/Getting%20Started/getting-started-linux" rel="noopener noreferrer"&gt;Linux&lt;/a&gt;, &lt;a href="https://docs.askui.com/docs/general/Getting%20Started/getting-started-macos" rel="noopener noreferrer"&gt;macOS&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delete &lt;code&gt;askui_example/my-first-askui-test-suite.test.ts&lt;/code&gt; after initialization&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Prepare Setup
&lt;/h2&gt;

&lt;p&gt;Cucumber does not play nice with AskUI's default setup yet (Version 0.20.3). For AskUI to play nice with Cucumber you need to do two small preparations as AskUI uses Jest as its runner.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Change Jest's &lt;code&gt;testEnvironmentOptions&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;In the file &lt;code&gt;askui_example/helpers/jest.config.ts&lt;/code&gt; you have to disable that code is included in the run report. You achieve this by adding a &lt;code&gt;testEnvironmentOptions&lt;/code&gt; property with the &lt;code&gt;addCodeInReport&lt;/code&gt; property set to &lt;code&gt;false&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InitialOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;testEnvironment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@askui/jest-allure-circus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;testEnvironmentOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;addCodeInReport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Tell Jest Where to Find the Implementation for The Step Definitions
&lt;/h3&gt;

&lt;p&gt;Also in &lt;code&gt;askui_example/helpers/jest.config.ts&lt;/code&gt; you need to expand the default &lt;code&gt;testMatch&lt;/code&gt; property. It must include files ending in &lt;code&gt;step.ts&lt;/code&gt; because we will store the implementation there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InitialOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;testEnvironment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@askui/jest-allure-circus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;testEnvironmentOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;addCodeInReport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;testMatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;**/__tests__/**/*.[jt]s?(x)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;**/?(*.)+(spec|test|step).[jt]s?(x)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install &lt;code&gt;jest-cucumber&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The easiest way to use Jest together with Cucumber is the npm-package &lt;a href="https://www.npmjs.com/package/jest-cucumber" rel="noopener noreferrer"&gt;jest-cucumber&lt;/a&gt;. Let's install it with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; jest-cucumber
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a Basic Features File
&lt;/h2&gt;

&lt;p&gt;Create a folder &lt;code&gt;features&lt;/code&gt; and in there a &lt;em&gt;Feature&lt;/em&gt; file &lt;code&gt;NavigateToWebsite.feature&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;project_root/
├─ askui_example/
├─ features/
  ├─ NavigateToWebsite.feature
├─ node_modules/
├─ ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Write the following basic &lt;em&gt;Feature&lt;/em&gt; into this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Feature: Navigate to a website

Scenario: Entering the correct URL into the browser address bar
  Given I am on the Google search page
  When I &lt;span class="nb"&gt;type &lt;/span&gt;&lt;span class="k"&gt;in &lt;/span&gt;the URL &lt;span class="k"&gt;for &lt;/span&gt;AskUI practice page
  Then I will land on the webpage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create the Step Definitions Implementations
&lt;/h2&gt;

&lt;p&gt;Create the step definition file &lt;code&gt;askui_example/navigate-to-url.step.ts&lt;/code&gt; where each &lt;code&gt;test&lt;/code&gt; maps to a specific scenario.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineFeature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loadFeature&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jest-cucumber&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./helpers/askui-helper&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Load the feature file&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;feature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;loadFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;features/NavigateToWebsite.feature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;defineFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// Maps to 'Scenario' in your feature file&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Entering the correct URL into the browser address bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;given&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;when&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;then&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nf"&gt;given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;I am on the Google search page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;moveMouse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mouseLeftClick&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressTwoKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;I type in the URL for AskUI practice page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;typeIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://askui.github.io/askui-practice-page/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;textfield&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;enter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;I will land on the webpage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Welcome to the AskUI Practice Page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run The Workflow
&lt;/h2&gt;

&lt;p&gt;Open your browser in full screen and start the workflow with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run askui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see that the workflow run will open a new tab and navigate to AskUI's practice page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Combining AskUI with Cucumber enables you to write AskUI workflows in BDD style. Executing your tests like a real human-user will make them more realistic for every stakeholder.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Common Terms in Computer Vision</title>
      <dc:creator>Johannes Dienst</dc:creator>
      <pubDate>Tue, 09 Jul 2024 10:14:42 +0000</pubDate>
      <link>https://dev.to/askui/common-terms-in-computer-vision-36o8</link>
      <guid>https://dev.to/askui/common-terms-in-computer-vision-36o8</guid>
      <description>&lt;p&gt;When you read about Artificial Intelligence, Machine Learning and Computer Vision you often come across terms that seem to be common.&lt;/p&gt;

&lt;p&gt;But do you know all of them and what they actually mean?&lt;/p&gt;

&lt;p&gt;In this blog we will list the most common terms that we use in our day-to-day work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bounding Box
&lt;/h2&gt;

&lt;p&gt;A &lt;em&gt;Bounding Box&lt;/em&gt; is a rectangle around a detected element or region in a picture. In code they are represented with their coordinates. There are different formats used. Some examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pascal_voc&lt;/code&gt;: [xmin, ymin, ymax, ymax]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;coco&lt;/code&gt;: [xmin, ymin, width, height]&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;yolo&lt;/code&gt;: normalized [xcenter, ycenter, width, height]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example in JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"xmin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1128.2720982142857"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ymin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"160.21332310267857"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"xmax"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1178.8204241071428"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ymax"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"180.83512834821428"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Annotation
&lt;/h2&gt;

&lt;p&gt;An annotation can be the visual representation of a bounding box or just a keypoint. But also a textual description is possible.&lt;/p&gt;

&lt;p&gt;Usually bounding boxes are used in &lt;em&gt;grounding&lt;/em&gt; together with &lt;em&gt;labels&lt;/em&gt; and to establish a ground-truth or reference for trainingdata and testdata for training computer vision models.&lt;/p&gt;

&lt;p&gt;Annotations differ from labels in that they can include any additional metadata.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx166zi669gk41km52ai9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx166zi669gk41km52ai9.png" alt="Annotation with labels on a website with a lot of text." width="700" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Label
&lt;/h2&gt;

&lt;p&gt;A label is given to a bounding box or a keypoint and identifies which class or category an object or area the model assigned to it. In the above example the &lt;code&gt;person&lt;/code&gt;-text above the bounding box is a label.&lt;/p&gt;

&lt;p&gt;Labels are also used to train models where the training set consists of correctly labeled images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Image Classification
&lt;/h2&gt;

&lt;p&gt;Image classification is used to put a predefined label on an image.&lt;/p&gt;

&lt;p&gt;For an image of a cat would get the label &lt;em&gt;cat&lt;/em&gt;, while the image of a sunset would get the label &lt;em&gt;sunset&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This seems like a trivial thing to do but it actually has a lot of practical use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recognition of objects or entities depicted in images&lt;/li&gt;
&lt;li&gt;Classifying medical image for detecting abnormalities&lt;/li&gt;
&lt;li&gt;Image retrieval based on categories&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Image Segmentation
&lt;/h2&gt;

&lt;p&gt;The goal of image segmentation is to divide an image into areas that belong together. The areas are visually distinctive from each other.&lt;/p&gt;

&lt;p&gt;The output of the process is a set of segmentation masks that can be used to process the image further.&lt;/p&gt;

&lt;p&gt;Take this segmentation of an avatar image where the masks define where a person is and where the background is. A practical application would be to remove the background based on these masks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu69t2rqb697re7clml6w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu69t2rqb697re7clml6w.png" alt="Colored segmentation areas for an avatar image." width="790" height="700"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Object Detection
&lt;/h2&gt;

&lt;p&gt;Object detection is used to identify objects in images and video. It is a crucial task in a lot of domains such as Surveillance and Security, Environmental Monitoring and Augmented Reality.&lt;/p&gt;

&lt;p&gt;It takes visual input and determines the objects that are present. The objects are described with a bounding box and also get a class-label and sometimes a confidence score.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdklzwt3ju9ptalhi0rsq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdklzwt3ju9ptalhi0rsq.png" alt="Detected objects are person and cell phone in a screenshot of a person sitting in a chair holding up a cell phone." width="529" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature
&lt;/h2&gt;

&lt;p&gt;A distinctive feature in an area or keypoint of an image. Usually you attribute features to a specific class you want to detect.&lt;/p&gt;

&lt;p&gt;For a &lt;strong&gt;mouse&lt;/strong&gt; those could be the shape, surroundings or fur pattern. Those are represented as numerical vectors, called descriptors, that capture the visual properties of an area or keypoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Vector
&lt;/h2&gt;

&lt;p&gt;Feature vectors are not limited to Computer Vision, but used there for all kind of tasks.&lt;/p&gt;

&lt;p&gt;A feature vector is generated from input data such as an image and contains one numerical entry for every feature. Feature vectors are typically high-dimensional.&lt;/p&gt;

&lt;p&gt;In practice feature vectors get generated by a &lt;em&gt;Backbone&lt;/em&gt; (see next heading) which are by themselves models trained to extract useful features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grounding
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Grounding&lt;/em&gt; is the process of connecting high-level concepts, for example &lt;strong&gt;Mouse&lt;/strong&gt;, with visual features, for example &lt;strong&gt;tail&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now that you have high-level concepts and their representation in the model you can also put them into context and relation with each-other.&lt;/p&gt;

&lt;p&gt;Grounding can also be done by humans for training data. There you annotate an input with bounding boxes and labels, so the model has a &lt;em&gt;Ground Truth&lt;/em&gt; to learn from.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referring Expression
&lt;/h2&gt;

&lt;p&gt;Referring Expression in Computer Vision are used to describe objects or areas in images. They can contain attributes like color or relations to other objects.&lt;/p&gt;

&lt;p&gt;Take this example: "The bike next to the child." You do not point to the bike directly but give a reference point &lt;em&gt;child&lt;/em&gt;. Or you could give a description: "The red building next to the blue car.".&lt;/p&gt;

&lt;p&gt;Referring Expressions are typically used in tasks like object localization, object detection or image retrieval.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zero-Shot
&lt;/h2&gt;

&lt;p&gt;In general &lt;em&gt;zero-shot&lt;/em&gt; is when models can do tasks they were not specifically trained on.&lt;/p&gt;

&lt;p&gt;To some extend, and most of them rather poorly, every model can perform tasks without specific training as the goal of training is to get a higher-level understanding of concepts.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Zero-shot&lt;/em&gt; learning in Computer Vision also includes more information than just labeled examples. The focus is on data that describes relationships between categories, so the model can generalize better to new categories.&lt;/p&gt;

&lt;p&gt;This also requires large scale training on a lot of data, so the model is exposed to billions of examples and can generalize without specific training to new tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That are all, but sure there are more. Do you know of any we should include?&lt;/p&gt;

&lt;p&gt;Let us know on &lt;a href="https://www.linkedin.com/company/askyourui" rel="noopener noreferrer"&gt;Social Media&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Tutorial: Automate Highlighting in WYSIWIG Editors</title>
      <dc:creator>Johannes Dienst</dc:creator>
      <pubDate>Tue, 26 Sep 2023 22:00:00 +0000</pubDate>
      <link>https://dev.to/askui/tutorial-automate-highlighting-in-wysiwig-editors-2c6e</link>
      <guid>https://dev.to/askui/tutorial-automate-highlighting-in-wysiwig-editors-2c6e</guid>
      <description>&lt;h1&gt;
  
  
  Automate Highlighting in Wysiwyg Editors
&lt;/h1&gt;

&lt;p&gt;Working in What-You-See-Is-What-You-Get editors (WYSIWYG) can be very tiring and also taxing on your wrists. Things like highlighting the same thing over and over again can often not be done with search-and-replace, because there is just no button for it 😤&lt;/p&gt;

&lt;p&gt;I recently had to put a transcript of a podcast into such an editor and had to &lt;strong&gt;bold&lt;/strong&gt; the speaker hints, so that you clearly know who was speaking in that part of the podcast. It looks like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;JD: Welcome to J^2 Talk AI&lt;/p&gt;

&lt;p&gt;JH: I think first of all we should understand...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is always alternating between &lt;strong&gt;JD&lt;/strong&gt; and &lt;strong&gt;JH&lt;/strong&gt; as that is the nature of a conversation 😋.&lt;/p&gt;

&lt;p&gt;Bolden the beginning speaker hints would need me to double-left-click the abbreviations and then hit the shortcut &lt;code&gt;cmd&lt;/code&gt;+&lt;code&gt;b&lt;/code&gt; (I am on macOS) in this particular editor.&lt;/p&gt;

&lt;p&gt;I do not want to do this by myself as that would mean to do roughly &lt;code&gt;20*2 = 40&lt;/code&gt; actions on average &lt;strong&gt;per episode&lt;/strong&gt;. And I had 5 episodes... Extremely straining on your wrists and arms and it is dull work also!&lt;/p&gt;

&lt;p&gt;In this tutorial I will show you how I automated this task with AskUI and also how to work around some kinks of WYSIWIG-Editors like microanimations and strange behaviour.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AskUI installed: &lt;a href="https://docs.askui.com/docs/general/Getting%20Started/Installing%20AskUI/getting-started" rel="noopener noreferrer"&gt;Windows&lt;/a&gt;, &lt;a href="https://docs.askui.com/docs/general/Getting%20Started/Installing%20AskUI/getting-started-linux" rel="noopener noreferrer"&gt;Linux&lt;/a&gt;, &lt;a href="https://docs.askui.com/docs/general/Getting%20Started/Installing%20AskUI/getting-started-macos" rel="noopener noreferrer"&gt;macOS&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Open your favorite WYSIWIG-Editor&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;So what do we need to do?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Find all the &lt;strong&gt;JD&lt;/strong&gt; and &lt;strong&gt;JH&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Double-left-click each one&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;cmd&lt;/code&gt;+&lt;code&gt;b&lt;/code&gt; (macOS) or (&lt;code&gt;ctrl&lt;/code&gt;+&lt;code&gt;b&lt;/code&gt; Linux and Windows)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how it looks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Br1p8Edn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://assets-global.website-files.com/62ee4472fd829cb6569660cc/6509b65027723cff0cfabd7c_automate_highlighting_wysiwyg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Br1p8Edn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://assets-global.website-files.com/62ee4472fd829cb6569660cc/6509b65027723cff0cfabd7c_automate_highlighting_wysiwyg.gif" width="500" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let Us Develop It Step-By-Step
&lt;/h2&gt;

&lt;p&gt;So the first thing that needs to be done is finding all the &lt;strong&gt;JD&lt;/strong&gt; and &lt;strong&gt;JH&lt;/strong&gt;. This can be done with the &lt;code&gt;get()&lt;/code&gt; command. As you can see in the demo above there are possibly multiple pages, so we have to scroll somehow. The easiest way to do this is to press the &lt;code&gt;pagedown&lt;/code&gt;-key. So let us be conservative and press that key three times.&lt;/p&gt;

&lt;p&gt;Every time we press it there is a scroll animation that might mess up the inference. So let's wait two seconds with &lt;code&gt;waitFor(2000)&lt;/code&gt;. That should be enough to finish the animation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;containsText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JD:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;containsText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JH:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pagedown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This already looks like it would be a good idea to refactor it and put the actual highlighting code into a function &lt;code&gt;highlight()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pagedown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;containsText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JD:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;containsText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JH:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, now we need to &lt;code&gt;mouse-double-left-click&lt;/code&gt; the text &lt;strong&gt;JD&lt;/strong&gt; and &lt;strong&gt;JH&lt;/strong&gt; one by one with a &lt;code&gt;for&lt;/code&gt;-loop. But when we try it with &lt;code&gt;await aui.moveMouseTo(jds[0]).exec()&lt;/code&gt; it will move the cursor to the middle of the line where &lt;strong&gt;JD&lt;/strong&gt;/&lt;strong&gt;JH&lt;/strong&gt; is at the beginning. We have to use the bounding box &lt;code&gt;x-min&lt;/code&gt; and &lt;code&gt;y-min&lt;/code&gt; values to get to the beginning of the line. Also we have to add like 10 pixels to the values to actually move the cursor over the &lt;strong&gt;JD&lt;/strong&gt;/&lt;strong&gt;JH&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;containsText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JD:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;jds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;bndbox&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;moveMouse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xmin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ymin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can do the highlighting by &lt;code&gt;mouse-double-left-click&lt;/code&gt; and press the bold shortcut.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;containsText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JD:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;jds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;bndbox&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;moveMouse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xmin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ymin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mouseDoubleLeftClick&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressTwoKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Use CTRL+b on Linux/Windows&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That should highlight all the &lt;strong&gt;JD&lt;/strong&gt;. Let's do the same with &lt;strong&gt;JH&lt;/strong&gt;. Also we need to add a keypress of &lt;code&gt;escape&lt;/code&gt; here because the mini-menu that pops up when double-left-clicking the last &lt;strong&gt;JD&lt;/strong&gt;  might cover a &lt;strong&gt;JH&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;containsText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JD:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;jds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;bndbox&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;moveMouse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xmin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ymin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mouseDoubleLeftClick&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressTwoKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;escape&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;containsText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JH:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;jhs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jhs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;bndbox&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;moveMouse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xmin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ymin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mouseDoubleLeftClick&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressTwoKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Two More Tweaks - Complete Code
&lt;/h2&gt;

&lt;p&gt;Another problem you might run into is missing focus on the editor. This is why adding the first two lines to your code is beneficial. It moves the mouse cursor into the editor and with the left-click gets the focus.&lt;/p&gt;

&lt;p&gt;Also extracting a function &lt;code&gt;boldenText(bbox)&lt;/code&gt; that does the highlighting given a bounding box &lt;code&gt;bbox&lt;/code&gt; avoids duplicate code.&lt;/p&gt;

&lt;p&gt;Here it is, the complete code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;highlights all the JD and JH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;moveMouse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mouseLeftClick&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pagedown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;containsText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JD:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;jds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;boldenText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;bndbox&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;escape&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;containsText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JH:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;jhs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;boldenText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jhs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;bndbox&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;boldenText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;moveMouse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xmin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ymin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mouseLeftClick&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mouseDoubleLeftClick&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressTwoKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial you learned how to automate tedious tasks like highlighting some recurring text in a WYSIWYG-Editor.&lt;/p&gt;

&lt;p&gt;You also learned how to overcome some of the challenges user interfaces throw at you, like mini-popups and micro-animations.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@pankajpatel?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Pankaj Patel&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/Ylk5n_nd9dA?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>A Comprehensive DevRel Metrics Guide (Part 4): Company Stages and Reporting</title>
      <dc:creator>Johannes Dienst</dc:creator>
      <pubDate>Mon, 25 Sep 2023 06:00:00 +0000</pubDate>
      <link>https://dev.to/johannesdienst/a-comprehensive-devrel-metrics-guide-part-4-company-stages-and-reporting-2j9e</link>
      <guid>https://dev.to/johannesdienst/a-comprehensive-devrel-metrics-guide-part-4-company-stages-and-reporting-2j9e</guid>
      <description>&lt;p&gt;Imagine you are working at an early stage startup and you are tasked with tracking meaningful metrics. And then imagine you are working at an enterprise company with the same task.&lt;/p&gt;

&lt;p&gt;Would you track the same metrics?&lt;/p&gt;

&lt;p&gt;I have been scratching my head over this question because it does not make sense to me that the metrics should be the same for both companies 🤔.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Company Stages Are There?
&lt;/h2&gt;

&lt;p&gt;I found an &lt;a href="https://www.youtube.com/watch?v=KXGXfbQfrrI"&gt;inspiring talk from Vera Tiago&lt;/a&gt; that resonated with me. There is also &lt;a href="https://vera-tiago.medium.com/northstar-metrics-and-okrs-to-show-devrel-value-and-get-focus-92fb058f3e1a"&gt;a written form of it&lt;/a&gt;, if that is more your thing 🥰.&lt;/p&gt;

&lt;p&gt;In there she talks about North Star metrics and then comes to the topic of DevRel-Focus areas. Her Developer Relations focus areas are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Awareness&lt;/li&gt;
&lt;li&gt;Adoption&lt;/li&gt;
&lt;li&gt;Education&lt;/li&gt;
&lt;li&gt;Retention&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And then she makes an interesting distinction between two areas you have to look at as a company:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maturity of your product&lt;/li&gt;
&lt;li&gt;Maturity of your community&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if you are an early stage startup you probably have an &lt;strong&gt;unmature&lt;/strong&gt; product and a just &lt;strong&gt;forming&lt;/strong&gt; community. Thus working towards retention may not be your highest priority as your product has to many kinks and your community is too small.&lt;/p&gt;

&lt;p&gt;It should be better to work towards acquisition and adoption to get more users.&lt;/p&gt;

&lt;p&gt;By doing this you get more feedback and exposure to achieve product market fit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BsbFiMT---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v6ud20zhjx2i6bi1gi7f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BsbFiMT---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v6ud20zhjx2i6bi1gi7f.png" alt="Reach, Awareness, Engagement and DevRel Qualified Leads in this order in a horizontal pyramid. Decreasing breadth from left to right and increasing depth from left to right." width="800" height="358"&gt;&lt;/a&gt;&lt;br&gt;Vera Tiagos four pillars of DevRel and company stage (Source &lt;a href="(https://vera-tiago.medium.com/northstar-metrics-and-okrs-to-show-devrel-value-and-get-focus-92fb058f3e1a)"&gt;Vera Tiagos Talk above in written form&lt;/a&gt;).
&lt;/p&gt;

&lt;p&gt;Evaluating and validating your company’s stage and your focus with management is extremely important as it again will determine the metrics you are prioritizing.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Do Reporting?
&lt;/h2&gt;

&lt;p&gt;Many companies have a monthly All-Hands meeting. Some call it a Jour-Fix. Whatever you call it, entering your collected data into a nice presentation template gives you the possibility to always be ready to give a presentation.&lt;/p&gt;

&lt;p&gt;I do not get into all the details in the presentation deck but focus on the things that matter now. Even when I track more than that as outlined in the previous blog post. There is a dedicated slide at the end where I link to the raw metrics if anyone is interested in them.&lt;/p&gt;

&lt;p&gt;With the slide deck I am good to go to present 5-10 Minutes and give a concise update about how we are doing 🎉&lt;/p&gt;

&lt;p&gt;Here is &lt;a href="https://docs.google.com/presentation/d/1rdYEUbZWYPi7tVBbx5YRF78n1GdJDHHP8tCQ3piydL4/edit?usp=sharing"&gt;my template as a Google-Slide Deck&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;And this marks the end of this series. We discussed why metrics are important, where to even start and my personal view on what metrics to track.&lt;/p&gt;

&lt;p&gt;In this blog post I gave an introduction on what might be important and expected from DevRel at different company stages.&lt;/p&gt;

&lt;p&gt;As always: Do not blindly follow advice but take it as a food for thought and an idea to test in your circumstance! I feel this is especially important in DevRel as no company is the same 🙃.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@andretaissin?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Andre Taissin&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/hOwcob_3dpc?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devrel</category>
      <category>community</category>
    </item>
    <item>
      <title>Visual Regression Testing with AskUI and Jest</title>
      <dc:creator>Johannes Dienst</dc:creator>
      <pubDate>Mon, 07 Aug 2023 07:07:00 +0000</pubDate>
      <link>https://dev.to/askui/visual-regression-testing-with-askui-and-jest-2i5e</link>
      <guid>https://dev.to/askui/visual-regression-testing-with-askui-and-jest-2i5e</guid>
      <description>&lt;p&gt;Visual Regression Testing (VRT) is a useful method to guard yourself against unwanted layout and visual changes that can occur with new version of a Graphical User Interface (GUI).&lt;/p&gt;

&lt;p&gt;AskUI is not a testing tool in the original sense but an automation tool for all GUIs that is platform-independent. Wouldn't it be nice to combine VRT with AskUI, so you do not have to maintain two different projects.&lt;/p&gt;

&lt;p&gt;Luckily you can do this because Jest - AskUIs favourite runner - can be extended to provide VRT, while AskUI delivers the screenshots to test on.&lt;/p&gt;

&lt;p&gt;In this blog you will learn how to use AskUI in combination with Jest and the package &lt;code&gt;jest-image-snapshot&lt;/code&gt; to include a visual regression test in your AskUI runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AskUI installed: &lt;a href="https://docs.askui.com/docs/general/Getting%20Started/Installing%20AskUI/getting-started" rel="noopener noreferrer"&gt;Windows&lt;/a&gt;, &lt;a href="https://docs.askui.com/docs/general/Getting%20Started/Installing%20AskUI/getting-started-linux" rel="noopener noreferrer"&gt;Linux&lt;/a&gt;, &lt;a href="https://docs.askui.com/docs/general/Getting%20Started/Installing%20AskUI/getting-started-macos" rel="noopener noreferrer"&gt;macOS&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup &lt;code&gt;jest-image-snapshot&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;First you need to install the package that provides the VRT capabilities: &lt;a href="https://github.com/americanexpress/jest-image-snapshot" rel="noopener noreferrer"&gt;jest-image-snapshot&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;Install it as a dev dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; jest-image-snapshot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Integration
&lt;/h3&gt;

&lt;p&gt;The package provides a function &lt;code&gt;toMatchImageSnapshot&lt;/code&gt; which implements Jest's &lt;code&gt;Matchers&amp;lt;R&amp;gt;&lt;/code&gt; interface making it a Jest matcher that can be used with Jest's &lt;code&gt;expect()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We have to add this matcher to Jest with &lt;code&gt;expect.extend&lt;/code&gt; like this in your workflow file (&lt;a href="https://jestjs.io/docs/expect#expectextendmatchers" rel="noopener noreferrer"&gt;See the docs&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;toMatchImageSnapshot&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jest-image-snapshot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;toMatchImageSnapshot&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the only thing you have to provide for a regression test in your workflows is an image to the function &lt;code&gt;expect()&lt;/code&gt; as &lt;code&gt;Buffer&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageBuffer&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toMatchImageSnapshot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use AskUI to get the screenshot as a &lt;code&gt;base64&lt;/code&gt; encoded &lt;code&gt;string&lt;/code&gt;. The string is in the format of a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs" rel="noopener noreferrer"&gt;data URL&lt;/a&gt;. So you have to strip the scheme &lt;code&gt;data:[&amp;lt;mediatype&amp;gt;][;base64],&lt;/code&gt; away because it is not a valid image with the scheme. Here is the full code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;annotate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageBase64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Strip away this: data:image/png;base64,&lt;/span&gt;
&lt;span class="c1"&gt;// Not valid png -&amp;gt; Only if used as a data URL&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;imageBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;imageBase64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageBuffer&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toMatchImageSnapshot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How to Get it to work with TypeScript
&lt;/h3&gt;

&lt;p&gt;When you run this you will get an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;FAIL  &lt;span class="nb"&gt;test&lt;/span&gt;/my-first-askui-test-suite.test.ts
  ● Test suite failed to run

  &lt;span class="nb"&gt;test&lt;/span&gt;/my-first-askui-test-suite.test.ts:16:25 - error TS2551: Property &lt;span class="s1"&gt;'toMatchImageSnapshot'&lt;/span&gt; does not exist on &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;'JestMatchers&amp;lt;Buffer&amp;gt;'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; Did you mean &lt;span class="s1"&gt;'toMatchSnapshot'&lt;/span&gt;?

  16     expect&lt;span class="o"&gt;(&lt;/span&gt;imageBuffer&lt;span class="o"&gt;)&lt;/span&gt;.toMatchImageSnapshot&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                            ~~~~~~~~~~~~~~~~~~~~

    node_modules/@types/jest/index.d.ts:1111:9
    1111         toMatchSnapshot&amp;lt;U extends &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;P &lt;span class="k"&gt;in &lt;/span&gt;keyof T]: any &lt;span class="o"&gt;}&amp;gt;(&lt;/span&gt;propertyMatchers: Partial&amp;lt;U&amp;gt;, snapshotName?: string&lt;span class="o"&gt;)&lt;/span&gt;: R&lt;span class="p"&gt;;&lt;/span&gt;
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      &lt;span class="s1"&gt;'toMatchSnapshot'&lt;/span&gt; is declared here.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because Jest's typings in &lt;code&gt;jest.d.ts&lt;/code&gt; do not include &lt;code&gt;toMatchImageSnapshot&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Luckily TypeScript has a mechanism called &lt;a href="https://www.typescriptlang.org/docs/handbook/declaration-merging.html" rel="noopener noreferrer"&gt;Declaration Merging&lt;/a&gt;. When we create a file &lt;code&gt;jest.d.ts&lt;/code&gt; in our project and declare our matcher there, TypeScript will pick it up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Matchers&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;toMatchImageSnapshot&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;CustomMatcherResult&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you rerun it you will get no error 🥳.&lt;/p&gt;

&lt;h3&gt;
  
  
  How &lt;code&gt;jest-image-snapshot&lt;/code&gt; Works
&lt;/h3&gt;

&lt;p&gt;From the &lt;a href="https://github.com/americanexpress/jest-image-snapshot" rel="noopener noreferrer"&gt;repository&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Given an image (Buffer instance with PNG image data) the toMatchImageSnapshot() matcher will create a &lt;strong&gt;image_snapshots&lt;/strong&gt; directory in the directory the test is in and will store the baseline snapshot image there on the first run. Note that if the customSnapshotsDir option is given then it will store baseline snapshot there instead.&lt;/p&gt;

&lt;p&gt;On subsequent test runs the matcher will compare the image being passed against the stored snapshot.&lt;/p&gt;

&lt;p&gt;To update the stored image snapshot run Jest with --updateSnapshot or -u argument. All this works the same way as Jest snapshots.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You also want to check out the &lt;a href="https://github.com/americanexpress/jest-image-snapshot#%EF%B8%8F-api" rel="noopener noreferrer"&gt;options you can provide&lt;/a&gt; to &lt;code&gt;toMatchImageSnapshot()&lt;/code&gt; to fine-tune the behaviour. For example you might want to set a threshold for a mismatch so that minimal differences do not &lt;em&gt;fail&lt;/em&gt; a run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toMatchImageSnapshot&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;failureThreshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;failureThresholdType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;percent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You can use AskUI in combination with a Visual Regression Testing library that integrates with Jest like &lt;a href="https://github.com/americanexpress/jest-image-snapshot" rel="noopener noreferrer"&gt;jest-image-snapshot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With this you can validate your GUI's final state and ensure it is the one you expected 🥳.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Integrating Test Data in AskUI Workflows from APIs, CSV Files, and Databases</title>
      <dc:creator>Johannes Dienst</dc:creator>
      <pubDate>Tue, 11 Jul 2023 09:39:43 +0000</pubDate>
      <link>https://dev.to/johannesdienst/integrating-test-data-in-askui-workflows-from-apis-csv-files-and-databases-5f8</link>
      <guid>https://dev.to/johannesdienst/integrating-test-data-in-askui-workflows-from-apis-csv-files-and-databases-5f8</guid>
      <description>&lt;p&gt;Are you struggling to integrate test data into your AskUI workflows effectively? Do you find yourself juggling APIs, CSV files, and databases without a clear approach?&lt;/p&gt;

&lt;p&gt;Integrating test data from various sources can be a daunting task.&lt;/p&gt;

&lt;p&gt;By the end of this post, you'll have a foundation for integrating test data into your AskUI workflows using APIs, CSV files, and databases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrate API
&lt;/h2&gt;

&lt;p&gt;To use an API for fetching test data, you'll need a library like &lt;code&gt;axios&lt;/code&gt;. After installing it, import it into your AskUI workflow file and use it to call an API, extract data, and insert it into a text field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Top of your workflow file&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://fruityvice.com/api/fruit/all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;typeIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textfield&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;withText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;E-Mail Address&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reading from CSV Files
&lt;/h2&gt;

&lt;p&gt;For integrating CSV files and databases, let's dive into a combined example. Begin by installing the required libraries and adding the necessary imports to your AskUIworkflow file. First, read the CSV file into your code and parse it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Top of your workflow file&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;csv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;async-csv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;csvData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./data.csv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;csvData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our CSV data looks like this. Save it as &lt;code&gt;data.csv&lt;/code&gt; in the root-folder of your AskUI project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;year_month,month_of_release,passenger_type,direction,sex,age,estimate,standard_error,status
2001-01,2020-09,Long-term migrant,Arrivals,Female,0-4 years,344,0,Final
2001-01,2020-09,Long-term migrant,Arrivals,Male,0-4 years,341,0,Final
2001-01,2020-09,Long-term migrant,Arrivals,Female,10-14 years,459,0,Final
2001-01,2020-09,Long-term migrant,Arrivals,Male,10-14 years,510,0,Final
2001-01,2020-09,Long-term migrant,Arrivals,Female,15-19 years,899,0,Final
2001-01,2020-09,Long-term migrant,Arrivals,Male,15-19 years,904,0,Final
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create and Initialize Database
&lt;/h2&gt;

&lt;p&gt;Next, create a database on your file system, within the same project as your Ascii workflow file. Set up a schema by opening the database and creating a table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Top of your workflow file&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;aadb&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aa-sqlite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Open a new database&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filepath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./data.db&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aadb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Create a table&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aadb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
  CREATE TABLE migration (
    year_month VARCHAR(10),
    month_of_release VARCHAR(10),
    passenger_type VARCHAR(50),
    direction VARCHAR(20),
    sex VARCHAR(10),
    age VARCHAR(50),
    estimate INT
  )
`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once completed, you'll have successfully integrated test data into your AskUI workflow using both CSV files and databases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Insert Data into Database
&lt;/h2&gt;

&lt;p&gt;Now that we have our database and table set up, we'll insert the parsed CSV data into the database. To do this, iterate over each row of the CSV data and use a SQL statement to insert it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aadb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    INSERT INTO migration (
      year_month, month_of_release, passenger_type,
      direction, sex, age, estimate
    ) VALUES (?, ?, ?, ?, ?, ?, ?)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;year_month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;month_of_release&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passenger_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;estimate&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't have a CSV file, you can follow a similar approach to the previous example, where you read data directly from the database and integrate it into your code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Get all the data&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dataAsync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aadb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;select * from migration;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataAsync&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We provided examples of incorporating test data into your AskUI workflow using APIs, CSV files, or databases. While these examples are not comprehensive, they serve as a solid starting point for integrating test data into your projects.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Being a Developer Advocate: Half a Year Review</title>
      <dc:creator>Johannes Dienst</dc:creator>
      <pubDate>Fri, 30 Jun 2023 14:14:28 +0000</pubDate>
      <link>https://dev.to/johannesdienst/being-a-developer-advocate-half-a-year-review-2ab6</link>
      <guid>https://dev.to/johannesdienst/being-a-developer-advocate-half-a-year-review-2ab6</guid>
      <description>&lt;p&gt;I blinked and six months are already gone since I plunged head first into Developer Relations as a single Developer Advocate at a small startup.&lt;/p&gt;

&lt;p&gt;In hindsight it was clear that it would have been a lot easier with a whole team already in place to learn from 😋&lt;/p&gt;

&lt;p&gt;But at that time it was exactly what I needed: The freedom to learn in every direction possible and expand my knowledge in all of the ways in an area I deeply care about!&lt;/p&gt;

&lt;p&gt;I got to do so many things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building Developer Docs from the ground up using Docs-As-Code&lt;/li&gt;
&lt;li&gt;Giving a ton of presentations at conferences and on company workshops&lt;/li&gt;
&lt;li&gt;Hosting events&lt;/li&gt;
&lt;li&gt;Sketchnoting our Meetup&lt;/li&gt;
&lt;li&gt;Hosting a podcast and learning about the distribution of it&lt;/li&gt;
&lt;li&gt;Creating a 15 Episodes strong YouTube short-form series about the companies product by doing everything from scripting to thumbnails to postproduction and promotion&lt;/li&gt;
&lt;li&gt;Writing a ton of technical blog posts&lt;/li&gt;
&lt;li&gt;Passionately advocating internally for our users&lt;/li&gt;
&lt;li&gt;Improving the developer experience as much as I possibly could&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I most enjoyed?
&lt;/h2&gt;

&lt;p&gt;Helping humans to succeed is the best possible feeling you can get in my opinion 🥰&lt;/p&gt;

&lt;p&gt;Doing it while also being allowed to work with code and docs is the perfect combination! Nothing beats being able to geek out about machine-learning stuff on a podcast, then getting feedback from users on how to improve your product afterwards. Finding a quick fix for it you can document quickly in the docs and then communicate the overall problem to product/engineering for improvement.&lt;/p&gt;

&lt;p&gt;It is this mix of cross-functional skills and impact you can have when you do it well that feels like a superpower.&lt;/p&gt;

&lt;p&gt;At the same time it can make you feel helpless because all you can do is &lt;strong&gt;try&lt;/strong&gt; to influence without the assurance it will actually achieve your desired outcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I hope to achieve in the next 6 months?
&lt;/h2&gt;

&lt;p&gt;So now that I know what I enjoy and hopefully also excel at, I will work hard to professionalize it.&lt;/p&gt;

&lt;p&gt;At the moment a lot of things are not automated and time consuming. Publishing blog posts for example means publishing them manually on every platform. Then doing the social media posts manually.&lt;/p&gt;

&lt;p&gt;I also hope to build more. By learning all the basics building time with our tool and also coding in general was not enough. Not losing touch with coding is a major challenge I want to solve by actually building stuff consistently.&lt;/p&gt;

&lt;p&gt;Also a goal will be to do a talk at a meetup or if even possible at a conference about DevRel. I really enjoy geeking out about docs and metrics and I believe this is something to explore further and talk about.&lt;/p&gt;

&lt;p&gt;So if you want to geek out about DevRel, docs, metrics or anything else: Hit me up and I am happy to join!&lt;/p&gt;

</description>
      <category>devrel</category>
      <category>learning</category>
      <category>learninpublic</category>
    </item>
    <item>
      <title>Use the Power Of TypeScript in AskUI</title>
      <dc:creator>Johannes Dienst</dc:creator>
      <pubDate>Fri, 30 Jun 2023 13:28:28 +0000</pubDate>
      <link>https://dev.to/askui/use-the-power-of-typescript-in-askui-4ik2</link>
      <guid>https://dev.to/askui/use-the-power-of-typescript-in-askui-4ik2</guid>
      <description>&lt;h2&gt;
  
  
  Optimizing UI Automation with Functions in TypeScript
&lt;/h2&gt;

&lt;p&gt;In the past, we've discussed using if and else statements, try and catch blocks, and for loops in TypeScript for building UI automations. &lt;/p&gt;

&lt;p&gt;However, to avoid repeating code and add real UI interactivity to your AskUI workflows, it's essential to harness the power of functions. Functions can help create reusable and efficient code for checking if elements are present and reacting accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;When you want to check if an element is present on your UI you might do it like this and do something else when the element is not found:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;Your text here!&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Do something else&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is totally fine, but once you create more sophisticated workflows you will notice that you need a variation of this code 1,2,3 ... x times.&lt;/p&gt;

&lt;p&gt;You can do better if you extract this into a function!&lt;/p&gt;

&lt;h2&gt;
  
  
  Extract into a function
&lt;/h2&gt;

&lt;p&gt;To get started, create a &lt;code&gt;util.ts&lt;/code&gt; file in the same folder as your automation file. First, import the &lt;code&gt;UiControlClient&lt;/code&gt; into this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./helper/jest.setup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can create a function in this file that takes a parameter &lt;code&gt;text&lt;/code&gt; and returns &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; depending on if the element is present or not. This function is asynchronous and checks for the presence of a specific element on the screen. It returns a boolean value to indicate whether the element is present or not.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;withText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; not found!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;export&lt;/code&gt; keyword makes sure we can use it in our actual AskUI-workflow file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Do this in your actual AskUI workflow file at the start&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;checkSuccess&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./utils&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Usage&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;checkSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// React to text not being there&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Implementing it like this makes it possible to reuse the this functionality for all text you need. It also improves the readability of your workflow file 🥳&lt;/p&gt;

&lt;h2&gt;
  
  
  Endless Possibilities
&lt;/h2&gt;

&lt;p&gt;You can wrap (nearly) any functionality you need in a handy function.&lt;/p&gt;

&lt;p&gt;Here's an example of a &lt;code&gt;waitUntil()&lt;/code&gt; function that you can use to wait for an element or an app to appear on the screen, even if it takes longer than a few seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Retry the command 5 times with a&lt;/span&gt;
&lt;span class="c1"&gt;// wait time of 2 seconds between each try&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;askuiCommand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxTry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;askuiCommand&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxTry&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Retry predicting command, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;maxTry&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; tries left`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;askuiCommand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxTry&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Wait for the text 'Github' to be displayed&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;aui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;withText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Github&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Leveraging functions in TypeScript significantly streamlines your UI automation efforts and provides numerous opportunities for optimization.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>A Comprehensive DevRel Metrics Guide (Part 3): A Framework to Get Started!</title>
      <dc:creator>Johannes Dienst</dc:creator>
      <pubDate>Fri, 30 Jun 2023 13:05:56 +0000</pubDate>
      <link>https://dev.to/johannesdienst/a-comprehensive-devrel-metrics-guide-part-3-a-framework-to-get-started-2b43</link>
      <guid>https://dev.to/johannesdienst/a-comprehensive-devrel-metrics-guide-part-3-a-framework-to-get-started-2b43</guid>
      <description>&lt;p&gt;In part two of the series we discussed what DevRel north star metrics commonly get used across the industry. We also saw that it can be quite tricky to find the right metrics to measure because they are plenty, sometimes lag behind or the relation to your North Star is not direct but only delivered over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So what should we do?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I will answer this question in this article for people who are just starting out and looking for a way to have a solid foundation to build on. Because that is what I found myself in when I started my Developer Advocate journey 😊.&lt;/p&gt;

&lt;p&gt;But also if you have something in place already. This article will help you make sense of metrics and give you inspiration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solid Framework: Kim Maidas Keystone Framework
&lt;/h2&gt;

&lt;p&gt;Just starting out with DevRel-Metrics? Look no further than Kim Maidas &lt;strong&gt;DevRel Metrics Keystone Framework&lt;/strong&gt;. Over the course of her career leading in Developer Relations she says she found a way to holistically measure DevRel in a way the C-Suite understands. And I could not agree more!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mXRBTWck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fiveandahalfstars.ninja/images/blog/2023/kim-maida-keystone-devrel-metrics-horizontal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mXRBTWck--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fiveandahalfstars.ninja/images/blog/2023/kim-maida-keystone-devrel-metrics-horizontal.png" alt="Reach, Awareness, Engagement and DevRel Qualified Leads in this order in a horizontal pyramid. Decreasing breadth from left to right and increasing depth from left to right." width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;
Kim Maidas Keystone DevRel Metrics



&lt;p&gt;Her idea is to define one Keystone (North Star) as your guidance. Her recommended keystone is &lt;strong&gt;1 signup/registration&lt;/strong&gt;. Ideally it is your companies north star.&lt;/p&gt;

&lt;p&gt;Once you have your keystone you can come up with a price tag for it. Yes, real money!&lt;/p&gt;

&lt;p&gt;Then with a price tag you can attach a percentage of this keystone value to each metric in the following categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://maida.kim/how-to-measure-the-value-of-developer-relations/#ib-toc-anchor-9"&gt;Reach&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://maida.kim/how-to-measure-the-value-of-developer-relations/#ib-toc-anchor-12"&gt;Awareness&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://maida.kim/how-to-measure-the-value-of-developer-relations/#ib-toc-anchor-15"&gt;Engagement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://maida.kim/how-to-measure-the-value-of-developer-relations/#ib-toc-anchor-18"&gt;DevRel Qualified Leads&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is an order to it also. Reach metrics are at the top of the funnel and thus improving metrics from that category is not as valuable as further down the funnel. Engagement and finally DevRel Qualified Leads are worth so much more and you can add more value to each metric in those categories.&lt;/p&gt;

&lt;p&gt;What you basically do is attaching a value tag on every metric and are thus able to calculate the value DevRel brings to the table 🥳&lt;/p&gt;

&lt;p&gt;I do not want to copy &lt;a href="https://maida.kim/how-to-measure-the-value-of-developer-relations"&gt;Kim Maidas article&lt;/a&gt;, so for the sake of brevity, I encourage you to read it and use her method to give every metric a meaning tied to company metrics.&lt;/p&gt;

&lt;p&gt;There is also much more in her article that makes the read worthwile!&lt;/p&gt;

&lt;h2&gt;
  
  
  My Personal Additions
&lt;/h2&gt;

&lt;p&gt;I personally found two additions helpful in showing impact on product/engineering:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Internal Impact&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct Impact&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first one captures the feedback you bring from the community into the company. The second one measures your direct impact you have by doing developer experience audits and creating content.&lt;/p&gt;

&lt;h3&gt;
  
  
  Internal Impact
&lt;/h3&gt;

&lt;p&gt;In my daily work a lot of things I do are related to product development. Be it providing feedback for new features, betatesting new features, writing docs, gathering feedback from the community and relaying it back to development. The above areas do not cover them completely.&lt;/p&gt;

&lt;p&gt;Luckily I found two articles that cover these areas well (See &lt;a href="https://thefalc.com/2020/12/devrel-metrics-and-why-they-matter/"&gt;DevRel Metrics and Why they Matter by Sean Falconer&lt;/a&gt; and &lt;a href="https://medium.com/google-developers/the-core-competencies-of-developer-relations-f3e1c04c0f5b"&gt;The Core competencies of Developer Relations by Reto Meier&lt;/a&gt;). They talk about the developer relations cycle where they introduce &lt;strong&gt;Developer Feedback&lt;/strong&gt; and &lt;strong&gt;Developer Sentiment&lt;/strong&gt; that Developer Relations collects and brings into product development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QePdHTGZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fiveandahalfstars.ninja/images/blog/2023/reto-meier-core-competencies-of-devrel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QePdHTGZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://fiveandahalfstars.ninja/images/blog/2023/reto-meier-core-competencies-of-devrel.png" alt="Developer Relations Cycle: Platform &amp;amp; Engineering putting out features for your product and receiving developer feedback. Developer Relations as middlehuman to spread awareness and put out resources for the developer community. Also receiving developer feedback and sentiment." width="720" height="372"&gt;&lt;/a&gt;&lt;/p&gt;
The Developer Relations ongoing interface cycle (Source The Core competencies of Developer Relations by Reto Meier)



&lt;p&gt;As you are acting as a middlehuman for these two, I call this &lt;strong&gt;internal impact&lt;/strong&gt; as you affect your product through the feedback delivered.&lt;/p&gt;

&lt;p&gt;What I like to measure here is the number of feedback and sentiment brought to the department in your company where it is best seated. If anything comes out of this feedback that is also noted down.&lt;/p&gt;

&lt;h3&gt;
  
  
  Direct Impact
&lt;/h3&gt;

&lt;p&gt;As you have noticed, most metrics are loosely tied to company goals. But there are ways to demonstrate a direct impact. Things I found useful are the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Developer Experience Audits: Evaluating your product as a user would and providing detailed feedback and improvement suggestions to product management. You should also track what is finally implemented and if possible if any metric was impacted by the change (Less bug-tickets, specific user complaints decreasing).&lt;/li&gt;
&lt;li&gt;Tracking your content's impact: With sufficient tracking in place you can track if your company’s North Star metric is affected by your content like docs, blogs or videos. In fact this is what I do and I could demonstrate that videos on YouTube drive Sign Ups.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion Part 3
&lt;/h2&gt;

&lt;p&gt;With a framework like the one from Kim Maida you can lay a solid foundation for all your metric needs in a structured way. It also makes reporting easier by directly tying individual metrics to your companies north star.&lt;/p&gt;

&lt;p&gt;In the last part of the series we will dive into the wonderful world of company development stages and their individual needs from DevRel. Early stage startups are different than enterprise organizations. And there is a lot of space in between.&lt;/p&gt;

&lt;p&gt;Thus we should shift our focus and the metrics we take into focus accordingly.&lt;/p&gt;

&lt;p&gt;I will also share my template I use to report DevRel metrics every month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Photo by &lt;a href="https://www.pexels.com/photo/scales-with-berries-and-organic-ingredients-for-recipe-3743169/"&gt;Leonardo Vazquez&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>devrel</category>
      <category>community</category>
    </item>
    <item>
      <title>A Comprehensive DevRel Metrics Guide (Part 2): DevRel Metrics to Track?</title>
      <dc:creator>Johannes Dienst</dc:creator>
      <pubDate>Thu, 15 Jun 2023 12:47:34 +0000</pubDate>
      <link>https://dev.to/johannesdienst/a-comprehensive-devrel-metrics-guide-part-2-devrel-metrics-to-track-2i7k</link>
      <guid>https://dev.to/johannesdienst/a-comprehensive-devrel-metrics-guide-part-2-devrel-metrics-to-track-2i7k</guid>
      <description>&lt;p&gt;Now that we got the &lt;strong&gt;Why?&lt;/strong&gt; out of the way. Let's face the difficult challenge on what DevRel-Metrics we should collect in the first place.&lt;/p&gt;

&lt;p&gt;Of course I will stand on the shoulders of giants here again as a lot of smart DevRel-practitioners have already written about this.&lt;/p&gt;

&lt;p&gt;The goal of this post is to get to a common understanding what others usually track. I also want to give you my opinion on metrics as I think there is so much confusion about metrics. I believe that a pragmatic approach is reasonable to be practical for each individual situation.&lt;/p&gt;

&lt;p&gt;We will discuss the following things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;DevRel-North Star metrics: Why they are useful but not sufficient&lt;/li&gt;
&lt;li&gt;Common metrics that are collected in the industry&lt;/li&gt;
&lt;li&gt;My critique and opinion about this&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  DevRel-North Star Metrics
&lt;/h2&gt;

&lt;p&gt;Swyx claims nearly all companies settle on one specific metric: &lt;strong&gt;Monthly Active Developers&lt;/strong&gt; (Source: &lt;a href="https://www.swyx.io/measuring-devrel"&gt;Measuring DevRel by swyx&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Also popular:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Registered Developer Accounts (Source: &lt;a href="https://dev.to/slashdatahq/*%20developer-marketing-kpis-are-different-from-devrel-kpis-3i8l"&gt;Slashdata Report 2020&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Developer Satisfaction score (Source: &lt;a href="https://dev.to/slashdatahq/developer-marketing-kpis-are-different-from-devrel-kpis-3i8l"&gt;Slashdata Report 2020&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Time To Hello World (TTHW) (Source: &lt;a href="https://thefalc.com/2020/12/devrel-metrics-and-why-they-matter/"&gt;Sean Falconers - DevRel Metrics And Why They Matter&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Time to First WOW! Exclamation (TTFWE) (My own invention: The first time a user experiences and realizes the value of your product.)&lt;/li&gt;
&lt;li&gt;Number of signups / registrations (Source: &lt;a href="https://maida.kim/how-to-measure-the-value-of-developer-relations"&gt;Kim Maidas Keystone Framework&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, have a look at &lt;a href="https://devrel-kpis.com/"&gt;DevRel-KPIs.com&lt;/a&gt; for more inspiration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Metrics in the Industry
&lt;/h2&gt;

&lt;p&gt;So this &lt;strong&gt;may&lt;/strong&gt; be useful when you have to report one or two numbers to management, but not sufficient for what DevRel is actually doing in their day-to-day work.&lt;/p&gt;

&lt;p&gt;Many more metrics have been collected and I will just link them here for you to explore and get an idea:&lt;/p&gt;

&lt;p&gt;I really like the DevRel-Metrics &lt;em&gt;swyx&lt;/em&gt; compiled for his North Start-metric &lt;strong&gt;Monthly Active Developers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5tMsCJ93--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5tep9cvh4cmlpnqjn5gm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5tMsCJ93--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5tep9cvh4cmlpnqjn5gm.png" alt="Metrics feeding into Monthly Active Developers. Community: Number of Members, Topics, Contributions, Orbit Level 1, Events, Attendees, Superusers. Content: Newsletter subs, YouTube subs, Twitter follows, Workshops complete, Confs/Hackathons, Meetups, Traffic/ SEO Authority. Product: Launch users, Launch mentions, Prioritized issues, Integration/tooling, Sean Ellis Question, ???. Bad Metrics: GitHub Stars, GA UTM Tag, Badges Scanned, NPS" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;
Metrics that feed into Monthly Active Developers North Star Metric.(Source: [Measuring DevRel by swyx](https://www.swyx.io/measuring-devrel))



&lt;p&gt;From &lt;a href="https://maida.kim/how-to-measure-the-value-of-developer-relations"&gt;Kim Maidas Keystone Framework&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://maida.kim/how-to-measure-the-value-of-developer-relations/#ib-toc-anchor-9"&gt;Reach&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://maida.kim/how-to-measure-the-value-of-developer-relations/#ib-toc-anchor-12"&gt;Awareness&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://maida.kim/how-to-measure-the-value-of-developer-relations/#ib-toc-anchor-15"&gt;Engagement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://maida.kim/how-to-measure-the-value-of-developer-relations/#ib-toc-anchor-18"&gt;DevRel Qualified Leads&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also again &lt;a href="https://devrel-kpis.com/"&gt;DevRel-KPIs.com&lt;/a&gt; is a great resource to get you going.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are There Bad Metrics
&lt;/h3&gt;

&lt;p&gt;I would say &lt;strong&gt;It depends&lt;/strong&gt; :-P&lt;/p&gt;

&lt;p&gt;Usually you do not want to be measured with a marketing metric. We are dealing with relations here, remember?&lt;br&gt;
But if we produce content for example that is for creating awareness or inspiration. What metrics can we actually collect to measure its success? In my opinion this would be something like views or likes or Github-stars.&lt;/p&gt;

&lt;p&gt;So I suggest the following: Be pragmatic with what you track. There is no harm in collecting a marketing metric if it fits your current situation. If management cares about these sometimes so called &lt;em&gt;Vanity Metrics&lt;/em&gt;, even better. Easy to collect and report on.&lt;/p&gt;

&lt;p&gt;But do not get caught up in them! An audience is not a community and you want to foster real relationships :-)&lt;/p&gt;

&lt;h2&gt;
  
  
  My Critique and Opinion About This
&lt;/h2&gt;

&lt;p&gt;It always felt wrong as a software developer to reduce a complex topic into a single number. It feels even more so with DevRel. I get that management would like to have a single number to gauge how DevRel is doing. But DevRel performs a cross-functional function in a company. The metrics are influenced by a lot of departments and also ultimately by the state your product is in.&lt;/p&gt;

&lt;p&gt;A metric like &lt;strong&gt;Monthly Active Developers&lt;/strong&gt; is a great metric if the management trusts that you influence it positively. The problem is, that it is usually influenced by a lot of departments like product, support, technical writing, marketing and so forth. If your product has sharp edges you can not work around even with the best onboarding and with the best feedback for engineering you can possibly provide: Good luck with improving your TTHW!&lt;/p&gt;

&lt;p&gt;Also tieing your activities to a North Star metric may sometimes be hard as it can lag significantly behind as not every relationship or piece of content has impact immediately. Often it is a compounding effect of positive encounters that tips the scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion Part 2
&lt;/h2&gt;

&lt;p&gt;In this post we discussed popular DevRel-North Star metrics like Monthly Active Developers used in the industry. They are useful when reporting to management, but not sufficient to measure the complex day-to-day work or specific project/campaigns DevRel is focusing on in a specific company.&lt;/p&gt;

&lt;p&gt;We gave a lot of pointers on what you can also collect with the general recommmendation to be pragmatic and, if possible, to focus on metrics that cover relationships.&lt;/p&gt;

&lt;p&gt;In the next part of this series we will cover Kim Maidas Keystone Framework. It is a great framework if you are just starting out! I use it with a few extensions. It is useful in my current situation and it includes the Relations part of a DevRel-Practitioner!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Photo by Igor Mashkov: &lt;a href="https://www.pexels.com/photo/radio-telescope-against-sky-with-stars-6325001/"&gt;https://www.pexels.com/photo/radio-telescope-against-sky-with-stars-6325001/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>devrel</category>
      <category>community</category>
    </item>
  </channel>
</rss>
