<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>benchmark - Deployment Every Day</title>
	<atom:link href="http://deploymenteveryday.com/tag/benchmark/feed/" rel="self" type="application/rss+xml" />
	<link>http://deploymenteveryday.com</link>
	<description>JVM (java/kotlin) performance, programming, frequent deployments, devops</description>
	<lastBuildDate>Mon, 13 May 2024 22:11:25 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>
	<item>
		<title>Want to have better accuracy of performance benchmark? Stop using playwright Page.waitForCondition</title>
		<link>http://deploymenteveryday.com/want-to-have-better-accuracy-of-performance-benchmark-stop-using-playwright-page-waitforcondition/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=want-to-have-better-accuracy-of-performance-benchmark-stop-using-playwright-page-waitforcondition</link>
					<comments>http://deploymenteveryday.com/want-to-have-better-accuracy-of-performance-benchmark-stop-using-playwright-page-waitforcondition/#respond</comments>
		
		<dc:creator><![CDATA[Mikolaj Grzaslewicz]]></dc:creator>
		<pubDate>Mon, 13 May 2024 09:21:53 +0000</pubDate>
				<category><![CDATA[performance]]></category>
		<category><![CDATA[benchmark]]></category>
		<category><![CDATA[plawright]]></category>
		<category><![CDATA[polling]]></category>
		<guid isPermaLink="false">https://deploymenteveryday.com/?p=168</guid>

					<description><![CDATA[<p>Let&#8217;s compare accuracy of Page.waitForCondition with custom condition polling For measurements I&#8217;m going to use 2 different approaches to see which one is better for performance benchmarks and has better accuracy. Custom condition polling implementation Website 1: https://www.theathletesfoot.nl/ Measurement ends when button starts working. Starts working means, it&#8217;s reacting on clicks. At this particular website [&#8230;]</p>
<p>The post <a href="http://deploymenteveryday.com/want-to-have-better-accuracy-of-performance-benchmark-stop-using-playwright-page-waitforcondition/">Want to have better accuracy of performance benchmark? Stop using playwright Page.waitForCondition</a> first appeared on <a href="http://deploymenteveryday.com">Deployment Every Day</a>.</p>]]></description>
										<content:encoded><![CDATA[<h1 class="wp-block-heading">Let&#8217;s compare accuracy of <code>Page.waitForCondition</code> with custom condition polling</h1>



<p class="wp-block-paragraph">For measurements I&#8217;m going to use 2 different approaches to see which one is better for performance benchmarks and has better accuracy.</p>



<ul class="wp-block-list">
<li>first using playwright <code>Page.waitForCondition</code></li>



<li>and the second using custom <code>condition polling</code></li>
</ul>



<p class="wp-block-paragraph">Custom <code>condition polling</code> implementation</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#2e3440"><span style="background:#c8d0e0;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#2e3440">Kotlin</span></span><span role="button" tabindex="0" data-code="// part of performance-explainer project I'm developing to measure things

package com.performance.explainer.api.waiter

import com.performance.explainer.spi.waiter.ConditionPolling
import java.time.Duration
import java.time.Instant
import java.util.function.BooleanSupplier


class ConditionPolling : SpiConditionPolling {

    override fun await(
        retryWait: Duration,
        timeout: Duration,
        condition: BooleanSupplier
    ): Boolean {
        val start = Instant.now()
        while (true) {
            val satisfied = condition.asBoolean
            val elapsed = Duration.between(start, Instant.now())
            if (satisfied) {
                return true
            }
            if (elapsed.plus(retryWait) &gt; timeout) {
                return false
            }
            Thread.sleep(retryWait)
        }
    }

}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// part of performance-explainer project I&#39;m developing to measure things</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">package</span><span style="color: #D8DEE9FF"> com.performance.explainer.api.waiter</span></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.performance.explainer.spi.waiter.ConditionPolling</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> java.time.Duration</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> java.time.Instant</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> java.util.function.BooleanSupplier</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span style="color: #81A1C1">class</span><span style="color: #D8DEE9FF"> </span><span style="color: #8FBCBB">ConditionPolling</span><span style="color: #D8DEE9FF"> : SpiConditionPolling {</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">override</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">fun</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">await</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">        retryWait: Duration,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        timeout: Duration,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        condition: BooleanSupplier</span></span>
<span class="line"><span style="color: #D8DEE9FF">    ): Boolean {</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> start </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> Instant.</span><span style="color: #88C0D0">now</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #81A1C1">while</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">true</span><span style="color: #D8DEE9FF">) {</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> satisfied </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> condition.asBoolean</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> elapsed </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> Duration.</span><span style="color: #88C0D0">between</span><span style="color: #D8DEE9FF">(start, Instant.</span><span style="color: #88C0D0">now</span><span style="color: #D8DEE9FF">())</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (satisfied) {</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">true</span></span>
<span class="line"><span style="color: #D8DEE9FF">            }</span></span>
<span class="line"><span style="color: #D8DEE9FF">            </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (elapsed.</span><span style="color: #88C0D0">plus</span><span style="color: #D8DEE9FF">(retryWait) </span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF"> timeout) {</span></span>
<span class="line"><span style="color: #D8DEE9FF">                </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">false</span></span>
<span class="line"><span style="color: #D8DEE9FF">            }</span></span>
<span class="line"><span style="color: #D8DEE9FF">            Thread.</span><span style="color: #88C0D0">sleep</span><span style="color: #D8DEE9FF">(retryWait)</span></span>
<span class="line"><span style="color: #D8DEE9FF">        }</span></span>
<span class="line"><span style="color: #D8DEE9FF">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF">}</span></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-end;background-color:#2e3440ff;color:#c8d0e0;font-size:12px;line-height:1;position:relative">NonBlockingConditionPolling</span></div>



<p class="wp-block-paragraph"></p>



<h2 class="wp-block-heading">Website 1: https://www.theathletesfoot.nl/</h2>



<p class="wp-block-paragraph">Measurement ends when button starts working. Starts working means, it&#8217;s reacting on clicks. At this particular website it means validation error shows up.</p>



<p class="wp-block-paragraph">That unfortunately cannot be checked with locator and <code>.click()</code> as button is not disabled and it&#8217;s clickable before it has event listeners attached. That could be fixed with waiting for <code>loaded</code> event, but that&#8217;s a bad idea for performance benchmark as explained with details in this blog post. So below code is <strong>NOT working</strong> for checking if button is working. Take a look at the explanation in source code</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#2e3440"><span style="background:#c8d0e0;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#2e3440">Kotlin</span></span><span role="button" tabindex="0" data-code="import com.microsoft.playwright.Locator.WaitForOptions
import com.microsoft.playwright.Page
import com.microsoft.playwright.Page.NavigateOptions
import com.microsoft.playwright.TimeoutError
import com.microsoft.playwright.options.WaitUntilState.COMMIT
import java.util.function.Consumer

Consumer&lt;Page&gt; { page -&gt;
    page.navigate(
        &quot;https://www.theathletesfoot.nl/producten/dames/air-max-90-sneakers/iic.nike.fz5163.133.html&quot;,
        NavigateOptions().setWaitUntil(COMMIT)
    )
    val button = page.locator(&quot;#add-to-cart&quot;)
    button.waitFor(WaitForOptions().setTimeout(3000.0))
    // plawright waits for a clickable button.
    // Clickable means it's visible and not disabled
    // This button is visible and not disabled before all resources are loaded.
    // Because of that, it does not actually measure user experience (button is working) 
    button.click()
}
// measured latency is duration of above" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Locator.WaitForOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page.NavigateOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.TimeoutError</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.options.WaitUntilState.COMMIT</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> java.util.function.Consumer</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">Consumer</span><span style="color: #D8DEE9FF">&lt;Page&gt; { page </span><span style="color: #81A1C1">-&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">    page.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #A3BE8C">&quot;https://www.theathletesfoot.nl/producten/dames/air-max-90-sneakers/iic.nike.fz5163.133.html&quot;</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">        </span><span style="color: #88C0D0">NavigateOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setWaitUntil</span><span style="color: #D8DEE9FF">(COMMIT)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    )</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> button </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> page.</span><span style="color: #88C0D0">locator</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;#add-to-cart&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">    button.</span><span style="color: #88C0D0">waitFor</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">WaitForOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setTimeout</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">3000.0</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">// plawright waits for a clickable button.</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">// Clickable means it&#39;s visible and not disabled</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">// This button is visible and not disabled before all resources are loaded.</span></span>
<span class="line"><span style="color: #D8DEE9FF">    </span><span style="color: #616E88">// Because of that, it does not actually measure user experience (button is working) </span></span>
<span class="line"><span style="color: #D8DEE9FF">    button.</span><span style="color: #88C0D0">click</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">}</span></span>
<span class="line"><span style="color: #616E88">// measured latency is duration of above</span></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-end;background-color:#2e3440ff;color:#c8d0e0;font-size:12px;line-height:1;position:relative">Broken measurement</span></div>



<figure class="wp-block-image size-large is-style-zoooom wp-duotone-unset-1"><img fetchpriority="high" decoding="async" width="1024" height="529" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/theathletesfoot-measurement-example-1024x529.png" alt="" class="wp-image-175" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/theathletesfoot-measurement-example-1024x529.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/theathletesfoot-measurement-example-300x155.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/theathletesfoot-measurement-example-768x397.png 768w, http://deploymenteveryday.com/wp-content/uploads/2024/05/theathletesfoot-measurement-example-1536x794.png 1536w, http://deploymenteveryday.com/wp-content/uploads/2024/05/theathletesfoot-measurement-example.png 1567w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-video"><video height="450" style="aspect-ratio: 800 / 450;" width="800" controls src="https://deploymenteveryday.com/wp-content/uploads/2024/05/theathletesfoot-measurement-example.webm"></video><figcaption class="wp-element-caption">Measurement target &#8211; wait for working button adding item to the shop cart</figcaption></figure>



<p class="wp-block-paragraph"></p>



<h3 class="wp-block-heading">baseline 1 &#8211; wait for working button, use custom polling</h3>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#2e3440"><span style="background:#c8d0e0;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#2e3440">Kotlin</span></span><span role="button" tabindex="0" data-code="import com.microsoft.playwright.Locator.WaitForOptions
import com.microsoft.playwright.Page
import com.microsoft.playwright.Page.NavigateOptions
import com.microsoft.playwright.TimeoutError
import com.microsoft.playwright.options.WaitUntilState.COMMIT
import com.performance.explainer.api.waiter.NonBlockingConditionPolling
import java.time.Duration.ofMillis
import java.util.function.Consumer

Consumer&lt;Page&gt; { page -&gt;
   val conditionPolling = NonBlockingConditionPolling.default()
   page.navigate(
       &quot;https://www.theathletesfoot.nl/producten/dames/air-max-90-sneakers/iic.nike.fz5163.133.html&quot;,
       NavigateOptions().setWaitUntil(COMMIT)
   )
   val button = page.locator(&quot;#add-to-cart&quot;)
   button.waitFor(WaitForOptions().setTimeout(3000.0))
   val success = conditionPolling.await(
       retryWait = ofMillis(10),
       timeout = ofMillis(5000)
   ) {
       button.click()
       val formError = page.locator(&quot;.js-select-size-and-color-error&quot;)
       formError.count() == 1
   }
   if (!success) {
       throw TimeoutError(&quot;Waiting for working button timed out&quot;)
   }
}
// measured latency is duration of above" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Locator.WaitForOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page.NavigateOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.TimeoutError</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.options.WaitUntilState.COMMIT</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.performance.explainer.api.waiter.NonBlockingConditionPolling</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> java.time.Duration.ofMillis</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> java.util.function.Consumer</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">Consumer</span><span style="color: #D8DEE9FF">&lt;Page&gt; { page </span><span style="color: #81A1C1">-&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> conditionPolling </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> NonBlockingConditionPolling.</span><span style="color: #88C0D0">default</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">   page.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">       </span><span style="color: #A3BE8C">&quot;https://www.theathletesfoot.nl/producten/dames/air-max-90-sneakers/iic.nike.fz5163.133.html&quot;</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">       </span><span style="color: #88C0D0">NavigateOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setWaitUntil</span><span style="color: #D8DEE9FF">(COMMIT)</span></span>
<span class="line"><span style="color: #D8DEE9FF">   )</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> button </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> page.</span><span style="color: #88C0D0">locator</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;#add-to-cart&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">   button.</span><span style="color: #88C0D0">waitFor</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">WaitForOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setTimeout</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">3000.0</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> success </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> conditionPolling.</span><span style="color: #88C0D0">await</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">       retryWait </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ofMillis</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">10</span><span style="color: #D8DEE9FF">),</span></span>
<span class="line"><span style="color: #D8DEE9FF">       timeout </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ofMillis</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">5000</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">   ) {</span></span>
<span class="line"><span style="color: #D8DEE9FF">       button.</span><span style="color: #88C0D0">click</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">       </span><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> formError </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> page.</span><span style="color: #88C0D0">locator</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;.js-select-size-and-color-error&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">       formError.</span><span style="color: #88C0D0">count</span><span style="color: #D8DEE9FF">() </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span></span>
<span class="line"><span style="color: #D8DEE9FF">   }</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9FF">success) {</span></span>
<span class="line"><span style="color: #D8DEE9FF">       </span><span style="color: #81A1C1">throw</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">TimeoutError</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;Waiting for working button timed out&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">   }</span></span>
<span class="line"><span style="color: #D8DEE9FF">}</span></span>
<span class="line"><span style="color: #616E88">// measured latency is duration of above</span></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-end;background-color:#2e3440ff;color:#c8d0e0;font-size:12px;line-height:1;position:relative">baseline 1 &#8211; wait for working button, use custom polling</span></div>



<p class="wp-block-paragraph"></p>



<h3 class="wp-block-heading">experiment 1 &#8211; wait for working button, use <code>Page.waitForCondition</code></h3>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#2e3440"><span style="background:#c8d0e0;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#2e3440">Kotlin</span></span><span role="button" tabindex="0" data-code="import com.microsoft.playwright.Locator.WaitForOptions
import com.microsoft.playwright.Page
import com.microsoft.playwright.Page.NavigateOptions
import com.microsoft.playwright.options.WaitUntilState.COMMIT
import java.util.function.Consumer

Consumer&lt;Page&gt; { page -&gt;
   page.navigate(
       &quot;https://www.theathletesfoot.nl/producten/dames/air-max-90-sneakers/iic.nike.fz5163.133.html&quot;,
       NavigateOptions().setWaitUntil(COMMIT)
   )
   val button = page.locator(&quot;#add-to-cart&quot;)
   button.waitFor(WaitForOptions().setTimeout(3000.0))
   page.waitForCondition({
       button.click()
       val formError = page.locator(&quot;.js-select-size-and-color-error&quot;)
       formError.count() == 1
   }, Page.WaitForConditionOptions().setTimeout(5000.0))
}
// measured latency is duration of above
" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Locator.WaitForOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page.NavigateOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.options.WaitUntilState.COMMIT</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> java.util.function.Consumer</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">Consumer</span><span style="color: #D8DEE9FF">&lt;Page&gt; { page </span><span style="color: #81A1C1">-&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">   page.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">       </span><span style="color: #A3BE8C">&quot;https://www.theathletesfoot.nl/producten/dames/air-max-90-sneakers/iic.nike.fz5163.133.html&quot;</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">       </span><span style="color: #88C0D0">NavigateOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setWaitUntil</span><span style="color: #D8DEE9FF">(COMMIT)</span></span>
<span class="line"><span style="color: #D8DEE9FF">   )</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> button </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> page.</span><span style="color: #88C0D0">locator</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;#add-to-cart&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">   button.</span><span style="color: #88C0D0">waitFor</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">WaitForOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setTimeout</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">3000.0</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">   page.</span><span style="color: #88C0D0">waitForCondition</span><span style="color: #D8DEE9FF">({</span></span>
<span class="line"><span style="color: #D8DEE9FF">       button.</span><span style="color: #88C0D0">click</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">       </span><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> formError </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> page.</span><span style="color: #88C0D0">locator</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;.js-select-size-and-color-error&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">       formError.</span><span style="color: #88C0D0">count</span><span style="color: #D8DEE9FF">() </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span></span>
<span class="line"><span style="color: #D8DEE9FF">   }, Page.</span><span style="color: #88C0D0">WaitForConditionOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setTimeout</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">5000.0</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">}</span></span>
<span class="line"><span style="color: #616E88">// measured latency is duration of above</span></span>
<span class="line"></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-end;background-color:#2e3440ff;color:#c8d0e0;font-size:12px;line-height:1;position:relative">experiment 1 &#8211; wait for working button, use Page.waitForCondition</span></div>



<p class="wp-block-paragraph"></p>



<h2 class="wp-block-heading">Website 2: https://www.blogmarketingacademy.com/</h2>



<figure class="wp-block-image size-large is-style-zoooom"><img decoding="async" width="1024" height="562" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blogmarketingacademy-measurement-example-1024x562.png" alt="" class="wp-image-177" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blogmarketingacademy-measurement-example-1024x562.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blogmarketingacademy-measurement-example-300x165.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blogmarketingacademy-measurement-example-768x422.png 768w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blogmarketingacademy-measurement-example.png 1457w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Measurement target &#8211; wait for working subscribe button</figcaption></figure>



<figure class="wp-block-video"><video height="450" style="aspect-ratio: 800 / 450;" width="800" controls src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blogmarketingacademy-measurement-example.webm"></video><figcaption class="wp-element-caption">Measurement target &#8211; wait for working subscribe button</figcaption></figure>



<p class="wp-block-paragraph"></p>



<h3 class="wp-block-heading">baseline 2 &#8211; wait for working button, use custom polling</h3>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#2e3440"><span style="background:#c8d0e0;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#2e3440">Kotlin</span></span><span role="button" tabindex="0" data-code="import com.microsoft.playwright.*
import com.microsoft.playwright.Locator.WaitForOptions
import com.microsoft.playwright.Page.NavigateOptions
import com.microsoft.playwright.options.WaitUntilState.COMMIT
import com.performance.explainer.api.waiter.NonBlockingConditionPolling
import java.time.Duration.ofMillis
import java.util.function.Consumer

Consumer&lt;Page&gt; { page -&gt;
   page.navigate(
       &quot;https://www.blogmarketingacademy.com/membership-site-service-credit-system/&quot;,
       NavigateOptions().setWaitUntil(COMMIT)
   )
   val button = page.getByText(&quot;Subscribe Weekly&quot;)
   button.waitFor(WaitForOptions().setTimeout(3000.0))
   val condition = NonBlockingConditionPolling.default()
   val isButtonWorking = condition.await(retryWait = ofMillis(10), timeout = ofMillis(3000)) {
       button.click()
       page.getByText(&quot;This field is required&quot;).count() == 1
   }
   if (!isButtonWorking) {
       throw TimeoutError(&quot;Waiting for working button timed out&quot;)
   }
}
// measured latency is duration of above" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.</span><span style="color: #81A1C1">*</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Locator.WaitForOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page.NavigateOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.options.WaitUntilState.COMMIT</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.performance.explainer.api.waiter.NonBlockingConditionPolling</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> java.time.Duration.ofMillis</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> java.util.function.Consumer</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">Consumer</span><span style="color: #D8DEE9FF">&lt;Page&gt; { page </span><span style="color: #81A1C1">-&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">   page.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">       </span><span style="color: #A3BE8C">&quot;https://www.blogmarketingacademy.com/membership-site-service-credit-system/&quot;</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">       </span><span style="color: #88C0D0">NavigateOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setWaitUntil</span><span style="color: #D8DEE9FF">(COMMIT)</span></span>
<span class="line"><span style="color: #D8DEE9FF">   )</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> button </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> page.</span><span style="color: #88C0D0">getByText</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;Subscribe Weekly&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">   button.</span><span style="color: #88C0D0">waitFor</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">WaitForOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setTimeout</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">3000.0</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> condition </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> NonBlockingConditionPolling.</span><span style="color: #88C0D0">default</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> isButtonWorking </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> condition.</span><span style="color: #88C0D0">await</span><span style="color: #D8DEE9FF">(retryWait </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ofMillis</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">10</span><span style="color: #D8DEE9FF">), timeout </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ofMillis</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">3000</span><span style="color: #D8DEE9FF">)) {</span></span>
<span class="line"><span style="color: #D8DEE9FF">       button.</span><span style="color: #88C0D0">click</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">       page.</span><span style="color: #88C0D0">getByText</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;This field is required&quot;</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">count</span><span style="color: #D8DEE9FF">() </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span></span>
<span class="line"><span style="color: #D8DEE9FF">   }</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9FF">isButtonWorking) {</span></span>
<span class="line"><span style="color: #D8DEE9FF">       </span><span style="color: #81A1C1">throw</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">TimeoutError</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;Waiting for working button timed out&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">   }</span></span>
<span class="line"><span style="color: #D8DEE9FF">}</span></span>
<span class="line"><span style="color: #616E88">// measured latency is duration of above</span></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-end;background-color:#2e3440ff;color:#c8d0e0;font-size:12px;line-height:1;position:relative">baseline 2 &#8211; wait for working button, use custom polling</span></div>



<p class="wp-block-paragraph"></p>



<h3 class="wp-block-heading">experiment 2 &#8211; wait for working button, use <code>Page.waitForCondition</code></h3>



<div class="wp-block-kevinbatdorf-code-block-pro padding-bottom-disabled cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#2e3440"><span style="background:#c8d0e0;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#2e3440">Kotlin</span></span><span role="button" tabindex="0" data-code="import com.microsoft.playwright.Locator.WaitForOptions
import com.microsoft.playwright.Page
import com.microsoft.playwright.Page.NavigateOptions
import com.microsoft.playwright.options.WaitUntilState.COMMIT
import java.util.function.Consumer

Consumer&lt;Page&gt; { page -&gt;
   page.navigate(
       &quot;https://www.blogmarketingacademy.com/membership-site-service-credit-system/&quot;,
       NavigateOptions().setWaitUntil(COMMIT)
   )
   val button = page.getByText(&quot;Subscribe Weekly&quot;)
   button.waitFor(WaitForOptions().setTimeout(3000.0))
   page.waitForCondition({
       button.click()
       page.getByText(&quot;This field is required&quot;).count() == 1
   }, Page.WaitForConditionOptions().setTimeout(3000.0))
}
// measured latency is duration of above" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Locator.WaitForOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page.NavigateOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.options.WaitUntilState.COMMIT</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> java.util.function.Consumer</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">Consumer</span><span style="color: #D8DEE9FF">&lt;Page&gt; { page </span><span style="color: #81A1C1">-&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">   page.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span></span>
<span class="line"><span style="color: #D8DEE9FF">       </span><span style="color: #A3BE8C">&quot;https://www.blogmarketingacademy.com/membership-site-service-credit-system/&quot;</span><span style="color: #D8DEE9FF">,</span></span>
<span class="line"><span style="color: #D8DEE9FF">       </span><span style="color: #88C0D0">NavigateOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setWaitUntil</span><span style="color: #D8DEE9FF">(COMMIT)</span></span>
<span class="line"><span style="color: #D8DEE9FF">   )</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> button </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> page.</span><span style="color: #88C0D0">getByText</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;Subscribe Weekly&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">   button.</span><span style="color: #88C0D0">waitFor</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">WaitForOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setTimeout</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">3000.0</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">   page.</span><span style="color: #88C0D0">waitForCondition</span><span style="color: #D8DEE9FF">({</span></span>
<span class="line"><span style="color: #D8DEE9FF">       button.</span><span style="color: #88C0D0">click</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">       page.</span><span style="color: #88C0D0">getByText</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;This field is required&quot;</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">count</span><span style="color: #D8DEE9FF">() </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">1</span></span>
<span class="line"><span style="color: #D8DEE9FF">   }, Page.</span><span style="color: #88C0D0">WaitForConditionOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setTimeout</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">3000.0</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">}</span></span>
<span class="line"><span style="color: #616E88">// measured latency is duration of above</span></span></code></pre><span style="display:flex;align-items:flex-end;padding:10px;width:100%;justify-content:flex-end;background-color:#2e3440ff;color:#c8d0e0;font-size:12px;line-height:1;position:relative">experiment 2 &#8211; wait for working button, use Page.waitForCondition</span></div>



<p class="wp-block-paragraph"></p>



<h1 class="wp-block-heading">Custom polling VS <code>Page.waitForCondition</code> results</h1>



<p class="wp-block-paragraph">Each baseline and experiment has 200 samples taken. <a href="https://en.wikipedia.org/wiki/Hodges%E2%80%93Lehmann_estimator" target="_blank" rel="noopener" title="">Hodges-Lehmann</a> estimator is used for comparing results</p>



<h2 class="wp-block-heading">baseline 1 VS experiment 1</h2>



<figure class="wp-block-image size-large is-style-zoooom"><img decoding="async" width="1024" height="874" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline1-vs-experiment1-1024x874.png" alt="" class="wp-image-180" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline1-vs-experiment1-1024x874.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline1-vs-experiment1-300x256.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline1-vs-experiment1-768x655.png 768w, http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline1-vs-experiment1.png 1301w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption"><code>Page.waitForCondition</code> 7.958% worse than custom polling</figcaption></figure>



<p class="wp-block-paragraph"></p>



<figure class="wp-block-image size-large is-style-zoooom"><img decoding="async" width="1024" height="898" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline1-vs-experiment1-ttf-excluded-1024x898.png" alt="" class="wp-image-181" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline1-vs-experiment1-ttf-excluded-1024x898.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline1-vs-experiment1-ttf-excluded-300x263.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline1-vs-experiment1-ttf-excluded-768x674.png 768w, http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline1-vs-experiment1-ttf-excluded.png 1302w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption"><code>Page.waitForCondition</code> 14.182% worse than custom condition polling when TTF (time to first byte, irrelevant for experiment) deducted from measured latencies</figcaption></figure>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph"></p>



<h2 class="wp-block-heading">baseline 2 VS experiment 2</h2>



<figure class="wp-block-image size-large is-style-zoooom"><img decoding="async" width="1024" height="873" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline2-vs-experiment2-1024x873.png" alt="" class="wp-image-182" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline2-vs-experiment2-1024x873.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline2-vs-experiment2-300x256.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline2-vs-experiment2-768x655.png 768w, http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline2-vs-experiment2.png 1301w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption"><code>Page.waitForCondition</code> 2.665% worse than custom condition polling. Additionally timeouts went up from 3.5% to 6%</figcaption></figure>



<figure class="wp-block-image size-large is-style-zoooom"><img decoding="async" width="1024" height="898" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline2-vs-experiment2-ttf-excluded-1024x898.png" alt="" class="wp-image-183" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline2-vs-experiment2-ttf-excluded-1024x898.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline2-vs-experiment2-ttf-excluded-300x263.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline2-vs-experiment2-ttf-excluded-768x673.png 768w, http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-baseline2-vs-experiment2-ttf-excluded.png 1300w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption"><code>Page.waitForCondition</code>17.427% worse than custom condition polling when TTF (time to first byte, irrelevant for experiment) deducted from measured latencies</figcaption></figure>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph"></p>



<h1 class="wp-block-heading">Let&#8217;s increase custom condition polling <code>retryWait</code> and compare results again</h1>



<h2 class="wp-block-heading">Results should be closer to <code>Page.waitForCondition</code>, right? Right?</h2>



<p class="wp-block-paragraph">Intuitively with increased wait before another condition poll, results should get less accurate. And this way get closer to less accurate playwright&#8217;s <code>Page.waitForCondition</code>. Right?</p>



<p class="wp-block-paragraph">Nope, in reality increasing retryWait up to 40ms makes no difference.</p>



<h2 class="wp-block-heading">Custom polling with <code>retryWait</code> <code>10 ms</code> VS longer <code>retryWait</code></h2>



<p class="wp-block-paragraph">10 ms VS 20 ms &#8211; no difference</p>



<p class="wp-block-paragraph">10 ms VS 40 ms &#8211; no difference</p>



<p class="wp-block-paragraph">10 ms VS 80 ms &#8211; 2.644% worse</p>



<p class="wp-block-paragraph">So there is difference noticeable in measurements when increasing polling retry wait from 10 ms to 80 ms. Sweet spot is 40 ms</p>



<h1 class="wp-block-heading">Why is there a difference between custom condition polling and <code>Page.waitForCondition</code>?</h1>



<p class="wp-block-paragraph">Let&#8217;s take a look at playwright&#8217;s implementation for <code>Page.waitForCondition</code></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#2e3440"><span style="background:#c8d0e0;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#2e3440">Kotlin</span></span><span role="button" tabindex="0" data-code="// com.microsoft.playwright.Page#waitForCondition(java.util.function.BooleanSupplier)
 @Override
 public void waitForCondition(BooleanSupplier predicate, WaitForConditionOptions options) {
   List&lt;Waitable&lt;Void&gt;&gt; waitables = new ArrayList&lt;&gt;();
   waitables.add(createWaitForCloseHelper());
   waitables.add(createWaitableTimeout(options == null ? null : options.timeout));
   waitables.add(new WaitablePredicate&lt;&gt;(predicate));
   runUntil(() -&gt; {}, new WaitableRace&lt;&gt;(waitables));
 }

 // com.microsoft.playwright.impl.ChannelOwner#runUntil
 &lt;T&gt; T runUntil(Runnable code, Waitable&lt;T&gt; waitable) {
   try {
     code.run();
     while (!waitable.isDone()) {
       connection.processOneMessage();
     }
     return waitable.get();
   } finally {
     waitable.dispose();
   }
 }

 // com.microsoft.playwright.impl.Connection#processOneMessage
 void processOneMessage() {
   JsonObject message = transport.poll(Duration.ofMillis(10));
   if (message == null) {
     return;
   }
   Gson gson = gson();
   Message messageObj = gson.fromJson(message, Message.class);
   dispatch(messageObj);
 }" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// com.microsoft.playwright.Page#waitForCondition(java.util.function.BooleanSupplier)</span></span>
<span class="line"><span style="color: #D8DEE9FF"> @Override</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">public</span><span style="color: #D8DEE9FF"> void </span><span style="color: #88C0D0">waitForCondition</span><span style="color: #D8DEE9FF">(BooleanSupplier predicate, WaitForConditionOptions options) {</span></span>
<span class="line"><span style="color: #D8DEE9FF">   List</span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF">Waitable</span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF">Void</span><span style="color: #81A1C1">&gt;&gt;</span><span style="color: #D8DEE9FF"> waitables </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> new ArrayList</span><span style="color: #81A1C1">&lt;&gt;</span><span style="color: #D8DEE9FF">();</span></span>
<span class="line"><span style="color: #D8DEE9FF">   waitables.</span><span style="color: #88C0D0">add</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">createWaitForCloseHelper</span><span style="color: #D8DEE9FF">());</span></span>
<span class="line"><span style="color: #D8DEE9FF">   waitables.</span><span style="color: #88C0D0">add</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">createWaitableTimeout</span><span style="color: #D8DEE9FF">(options </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">null</span><span style="color: #D8DEE9FF"> ? </span><span style="color: #81A1C1">null</span><span style="color: #D8DEE9FF"> : options.timeout));</span></span>
<span class="line"><span style="color: #D8DEE9FF">   waitables.</span><span style="color: #88C0D0">add</span><span style="color: #D8DEE9FF">(new WaitablePredicate</span><span style="color: #81A1C1">&lt;&gt;</span><span style="color: #D8DEE9FF">(predicate));</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #88C0D0">runUntil</span><span style="color: #D8DEE9FF">(() </span><span style="color: #81A1C1">-&gt;</span><span style="color: #D8DEE9FF"> {}, new WaitableRace</span><span style="color: #81A1C1">&lt;&gt;</span><span style="color: #D8DEE9FF">(waitables));</span></span>
<span class="line"><span style="color: #D8DEE9FF"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// com.microsoft.playwright.impl.ChannelOwner#runUntil</span></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF">T</span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF"> T </span><span style="color: #88C0D0">runUntil</span><span style="color: #D8DEE9FF">(Runnable code, Waitable</span><span style="color: #81A1C1">&lt;</span><span style="color: #D8DEE9FF">T</span><span style="color: #81A1C1">&gt;</span><span style="color: #D8DEE9FF"> waitable) {</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">try</span><span style="color: #D8DEE9FF"> {</span></span>
<span class="line"><span style="color: #D8DEE9FF">     code.</span><span style="color: #88C0D0">run</span><span style="color: #D8DEE9FF">();</span></span>
<span class="line"><span style="color: #D8DEE9FF">     </span><span style="color: #81A1C1">while</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9FF">waitable.</span><span style="color: #88C0D0">isDone</span><span style="color: #D8DEE9FF">()) {</span></span>
<span class="line"><span style="color: #D8DEE9FF">       connection.</span><span style="color: #88C0D0">processOneMessage</span><span style="color: #D8DEE9FF">();</span></span>
<span class="line"><span style="color: #D8DEE9FF">     }</span></span>
<span class="line"><span style="color: #D8DEE9FF">     </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF"> waitable.</span><span style="color: #88C0D0">get</span><span style="color: #D8DEE9FF">();</span></span>
<span class="line"><span style="color: #D8DEE9FF">   } </span><span style="color: #81A1C1">finally</span><span style="color: #D8DEE9FF"> {</span></span>
<span class="line"><span style="color: #D8DEE9FF">     waitable.</span><span style="color: #88C0D0">dispose</span><span style="color: #D8DEE9FF">();</span></span>
<span class="line"><span style="color: #D8DEE9FF">   }</span></span>
<span class="line"><span style="color: #D8DEE9FF"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// com.microsoft.playwright.impl.Connection#processOneMessage</span></span>
<span class="line"><span style="color: #D8DEE9FF"> void </span><span style="color: #88C0D0">processOneMessage</span><span style="color: #D8DEE9FF">() {</span></span>
<span class="line"><span style="color: #D8DEE9FF">   JsonObject message </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> transport.</span><span style="color: #88C0D0">poll</span><span style="color: #D8DEE9FF">(Duration.</span><span style="color: #88C0D0">ofMillis</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">10</span><span style="color: #D8DEE9FF">));</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (message </span><span style="color: #81A1C1">==</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">null</span><span style="color: #D8DEE9FF">) {</span></span>
<span class="line"><span style="color: #D8DEE9FF">     </span><span style="color: #81A1C1">return</span><span style="color: #D8DEE9FF">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">   }</span></span>
<span class="line"><span style="color: #D8DEE9FF">   Gson gson </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">gson</span><span style="color: #D8DEE9FF">();</span></span>
<span class="line"><span style="color: #D8DEE9FF">   Message messageObj </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> gson.</span><span style="color: #88C0D0">fromJson</span><span style="color: #D8DEE9FF">(message, Message.class);</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #88C0D0">dispatch</span><span style="color: #D8DEE9FF">(messageObj);</span></span>
<span class="line"><span style="color: #D8DEE9FF"> }</span></span></code></pre></div>



<p class="wp-block-paragraph">Under the hood in java implementation, playwright uses condition polling every 10 ms. The same interval which was used in custom polling. So why it has worse accuracy than custom condition polling? Source code at this level does not answer the question and that might be a question to playwright dev team.</p>



<h1 class="wp-block-heading">How about <code>Page.locator</code> VS <code>Page.isVisible</code> polling?</h1>



<p class="wp-block-paragraph">After I saw it makes a difference when polling is used for more complex scenarios I thought &#8220;<em>let&#8217;s also checked if it there is difference for plain locators like <code>Page.getByText</code></em>.</p>



<p class="wp-block-paragraph">baseline</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#2e3440"><span style="background:#c8d0e0;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#2e3440">Kotlin</span></span><span role="button" tabindex="0" data-code="import com.microsoft.playwright.Locator.WaitForOptions
import com.microsoft.playwright.Page
import com.microsoft.playwright.Page.NavigateOptions
import com.microsoft.playwright.options.WaitUntilState.COMMIT
import java.util.function.Consumer

Consumer&lt;Page&gt; { page -&gt;
   page.navigate(&quot;https://github.com/&quot;, NavigateOptions().setWaitUntil(COMMIT))
   page.getByText(&quot;Sign up for GitHub&quot;).first().waitFor(WaitForOptions().setTimeout(2000.0))
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Locator.WaitForOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page.NavigateOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.options.WaitUntilState.COMMIT</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> java.util.function.Consumer</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">Consumer</span><span style="color: #D8DEE9FF">&lt;Page&gt; { page </span><span style="color: #81A1C1">-&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">   page.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;https://github.com/&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">NavigateOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setWaitUntil</span><span style="color: #D8DEE9FF">(COMMIT))</span></span>
<span class="line"><span style="color: #D8DEE9FF">   page.</span><span style="color: #88C0D0">getByText</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;Sign up for GitHub&quot;</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">first</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">waitFor</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">WaitForOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setTimeout</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">2000.0</span><span style="color: #D8DEE9FF">))</span></span>
<span class="line"><span style="color: #D8DEE9FF">}</span></span></code></pre></div>



<p class="wp-block-paragraph">experiment</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#2e3440"><span style="background:#c8d0e0;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#2e3440">Kotlin</span></span><span role="button" tabindex="0" data-code="import com.microsoft.playwright.Page
import com.microsoft.playwright.Page.NavigateOptions
import com.microsoft.playwright.TimeoutError
import com.microsoft.playwright.options.WaitUntilState.COMMIT
import com.performance.explainer.api.waiter.NonBlockingConditionPolling
import java.time.Duration.ofMillis
import java.time.Duration.ofSeconds
import java.util.function.Consumer

Consumer&lt;Page&gt; { page -&gt;
   val conditionPolling = NonBlockingConditionPolling.default()
   page.navigate(&quot;https://github.com/&quot;, NavigateOptions().setWaitUntil(COMMIT))
   val isButtonVisible = conditionPolling.await(retryWait = ofMillis(40), timeout = ofSeconds(2)) {
       page.isVisible(&quot;text=Sign up for GitHub&quot;)
   }
   if (!isButtonVisible) {
       throw TimeoutError(&quot;Button not visible&quot;)
   }
}" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page.NavigateOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.TimeoutError</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.options.WaitUntilState.COMMIT</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.performance.explainer.api.waiter.NonBlockingConditionPolling</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> java.time.Duration.ofMillis</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> java.time.Duration.ofSeconds</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> java.util.function.Consumer</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">Consumer</span><span style="color: #D8DEE9FF">&lt;Page&gt; { page </span><span style="color: #81A1C1">-&gt;</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> conditionPolling </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> NonBlockingConditionPolling.</span><span style="color: #88C0D0">default</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">   page.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;https://github.com/&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">NavigateOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setWaitUntil</span><span style="color: #D8DEE9FF">(COMMIT))</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> isButtonVisible </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> conditionPolling.</span><span style="color: #88C0D0">await</span><span style="color: #D8DEE9FF">(retryWait </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ofMillis</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">40</span><span style="color: #D8DEE9FF">), timeout </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">ofSeconds</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">2</span><span style="color: #D8DEE9FF">)) {</span></span>
<span class="line"><span style="color: #D8DEE9FF">       page.</span><span style="color: #88C0D0">isVisible</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;text=Sign up for GitHub&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">   }</span></span>
<span class="line"><span style="color: #D8DEE9FF">   </span><span style="color: #81A1C1">if</span><span style="color: #D8DEE9FF"> (</span><span style="color: #81A1C1">!</span><span style="color: #D8DEE9FF">isButtonVisible) {</span></span>
<span class="line"><span style="color: #D8DEE9FF">       </span><span style="color: #81A1C1">throw</span><span style="color: #D8DEE9FF"> </span><span style="color: #88C0D0">TimeoutError</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;Button not visible&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">   }</span></span>
<span class="line"><span style="color: #D8DEE9FF">}</span></span></code></pre></div>



<figure class="wp-block-image size-large is-style-zoooom"><img decoding="async" width="1024" height="630" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-github-locator-vs-polling-40ms-1024x630.png" alt="" class="wp-image-185" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-github-locator-vs-polling-40ms-1024x630.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-github-locator-vs-polling-40ms-300x185.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-github-locator-vs-polling-40ms-768x473.png 768w, http://deploymenteveryday.com/wp-content/uploads/2024/05/comparison-github-locator-vs-polling-40ms.png 1300w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption"><strong>Uff, no difference</strong> between using <code>Page.getByText</code> vs custom condition polling</figcaption></figure>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph"></p>



<h1 class="wp-block-heading">Summary</h1>



<p class="wp-block-paragraph">I started with checking if playwright <code>Page.waitForCondition</code> replaced by custom condition polling every 10ms makes a difference. And it&#8217;s faster which means you get more accurate results with this approach.</p>



<p class="wp-block-paragraph">Then I also measured 20ms, 40ms, 80ms custom condition polling interval. Up to 40ms there is no difference in measured latencies and it&#8217;s a sweet spot for retries between condition check.</p>



<h1 class="wp-block-heading">Conclusion</h1>



<p class="wp-block-paragraph">If you want higher accuracy of web app performance measurement, use custom condition polling 40ms instead of playwright <code>Page.waitForCondition</code></p><p>The post <a href="http://deploymenteveryday.com/want-to-have-better-accuracy-of-performance-benchmark-stop-using-playwright-page-waitforcondition/">Want to have better accuracy of performance benchmark? Stop using playwright Page.waitForCondition</a> first appeared on <a href="http://deploymenteveryday.com">Deployment Every Day</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>http://deploymenteveryday.com/want-to-have-better-accuracy-of-performance-benchmark-stop-using-playwright-page-waitforcondition/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://deploymenteveryday.com/wp-content/uploads/2024/05/theathletesfoot-measurement-example.webm" length="84722" type="video/webm" />
<enclosure url="https://deploymenteveryday.com/wp-content/uploads/2024/05/blogmarketingacademy-measurement-example.webm" length="134053" type="video/webm" />

			</item>
		<item>
		<title>How to make performance benchmark 530% worse by relying on web app automation defaults?</title>
		<link>http://deploymenteveryday.com/how-to-make-performance-benchmark-530-worse-by-relying-on-web-app-automation-defaults/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=how-to-make-performance-benchmark-530-worse-by-relying-on-web-app-automation-defaults</link>
					<comments>http://deploymenteveryday.com/how-to-make-performance-benchmark-530-worse-by-relying-on-web-app-automation-defaults/#comments</comments>
		
		<dc:creator><![CDATA[Mikolaj Grzaslewicz]]></dc:creator>
		<pubDate>Sun, 05 May 2024 12:26:21 +0000</pubDate>
				<category><![CDATA[performance]]></category>
		<category><![CDATA[benchmark]]></category>
		<category><![CDATA[DOMContentLoaded]]></category>
		<category><![CDATA[end-to-end]]></category>
		<category><![CDATA[playwright]]></category>
		<category><![CDATA[selenium]]></category>
		<guid isPermaLink="false">https://deploymenteveryday.com/?p=112</guid>

					<description><![CDATA[<p>Let&#8217;s start with a necessary definitions: what are DOMContentLoaded and load events in the web browser? DOMContentLoaded According to docs when browser fires this event it means load event When browser fires load event it means DOM content is fully loaded. Browser has finished Both events are NOT about REST calls. And both are not [&#8230;]</p>
<p>The post <a href="http://deploymenteveryday.com/how-to-make-performance-benchmark-530-worse-by-relying-on-web-app-automation-defaults/">How to make performance benchmark 530% worse by relying on web app automation defaults?</a> first appeared on <a href="http://deploymenteveryday.com">Deployment Every Day</a>.</p>]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">Let&#8217;s start with a necessary definitions: what are <code>DOMContentLoaded</code> and <code>load</code> events in the web browser?</h2>



<h3 class="wp-block-heading"><code>DOMContentLoaded</code></h3>



<p class="wp-block-paragraph">According to <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event">docs</a> when browser fires this event it means</p>



<ul class="wp-block-list">
<li>DOM has been completely parsed</li>



<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#defer"><code>&lt;script defer src="…"&gt;</code></a>&nbsp;and&nbsp;<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#module"><code>&lt;script type="module"&gt;</code></a> has been loaded</li>



<li>async script, images, frames might not be loaded yet</li>
</ul>



<h3 class="wp-block-heading"><code>load event</code></h3>



<p class="wp-block-paragraph">When browser fires <code>load event</code> it means DOM content is fully loaded. Browser has finished</p>



<ul class="wp-block-list">
<li>executing all asynchronous resources from the DOM tree</li>



<li>loading images, frames etc.</li>



<li>it comes always after <code>DOMContentLoaded</code></li>
</ul>



<p class="wp-block-paragraph">Both events are NOT about REST calls. And both are not relevant to the user browsing the website.</p>



<h2 class="wp-block-heading">What do web apps automation tools like playwright and selenium do by default?</h2>



<p class="wp-block-paragraph">Take a look at following lines of code measuring navigating to a page and waiting for a button to be visible.</p>



<figure class="wp-block-image size-full"><img decoding="async" width="1359" height="607" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-github-button.png" alt="" class="wp-image-116" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-github-button.png 1359w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-github-button-300x134.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-github-button-1024x457.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-github-button-768x343.png 768w" sizes="(max-width: 1359px) 100vw, 1359px" /></figure>



<p class="wp-block-paragraph">using playwright</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers cbp-blur-enabled cbp-unblur-on-hover" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="val millisBefore = System.currentTimeMillis()
page.navigate(&quot;https://github.com&quot;)
page.getByText(&quot;Try Now&quot;).waitFor()
val latency = System.currentTimeMillis() - millisBefore" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> millisBefore </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> System.</span><span style="color: #88C0D0">currentTimeMillis</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line cbp-no-blur"><span style="color: #D8DEE9FF">page.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;https://github.com&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line cbp-no-blur"><span style="color: #D8DEE9FF">page.</span><span style="color: #88C0D0">getByText</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;Try Now&quot;</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">waitFor</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> latency </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> System.</span><span style="color: #88C0D0">currentTimeMillis</span><span style="color: #D8DEE9FF">() </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> millisBefore</span></span></code></pre></div>



<p class="wp-block-paragraph">or doing the same with selenium</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers cbp-blur-enabled cbp-unblur-on-hover" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="val millisBefore = System.currentTimeMillis()
driver.navigate().to(&quot;https://github.com&quot;)
WebDriverWait(driver, 30, 100).until(ExpectedConditions.visibilityOfElementLocated(By.linkText(&quot;Sign up for GitHub&quot;)))
val latency = System.currentTimeMillis() - millisBefore" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> millisBefore </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> System.</span><span style="color: #88C0D0">currentTimeMillis</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line cbp-no-blur"><span style="color: #D8DEE9FF">driver.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">to</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;https://github.com&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line cbp-no-blur"><span style="color: #88C0D0">WebDriverWait</span><span style="color: #D8DEE9FF">(driver, </span><span style="color: #B48EAD">30</span><span style="color: #D8DEE9FF">, </span><span style="color: #B48EAD">100</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">until</span><span style="color: #D8DEE9FF">(ExpectedConditions.</span><span style="color: #88C0D0">visibilityOfElementLocated</span><span style="color: #D8DEE9FF">(By.</span><span style="color: #88C0D0">linkText</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;Sign up for GitHub&quot;</span><span style="color: #D8DEE9FF">)))</span></span>
<span class="line"><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> latency </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> System.</span><span style="color: #88C0D0">currentTimeMillis</span><span style="color: #D8DEE9FF">() </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> millisBefore</span></span></code></pre></div>



<p class="wp-block-paragraph">When you navigate to a page, by default those automation tools wait for <code>load</code> event. So the line with locator is not executed until then.</p>



<p class="wp-block-paragraph">You might think <em>Pff, so what. It&#8217;s just a technical detail</em></p>



<p class="wp-block-paragraph">It is indeed a technical detail but it impacts performance benchmark <mark>dramatically</mark>.</p>



<h2 class="wp-block-heading">Waiting for <code>DOMContentLoaded</code> VS <code>load</code> VS <code>NOT waiting for any event</code>. Fight!</h2>



<p class="wp-block-paragraph">Let&#8217;s measure how quickly user can see github call to action button.</p>



<h3 class="wp-block-heading">Baseline benchmark &#8211; wait for <code>load</code> event</h3>



<p class="wp-block-paragraph">Take 1000 samples. Wait for <code>load</code> event, then wait for github call to action button</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="// only most important code shown here. Processing measurements is out of the scope
page.navigate(&quot;https://github.com&quot;)
page.getByText(&quot;Sign up for GitHub&quot;).first().waitFor(WaitForOptions().setTimeout(2000.0))" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #616E88">// only most important code shown here. Processing measurements is out of the scope</span></span>
<span class="line"><span style="color: #D8DEE9FF">page.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;https://github.com&quot;</span><span style="color: #D8DEE9FF">)</span></span>
<span class="line"><span style="color: #D8DEE9FF">page.</span><span style="color: #88C0D0">getByText</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;Sign up for GitHub&quot;</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">first</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">waitFor</span><span style="color: #D8DEE9FF">(</span><span style="color: #88C0D0">WaitForOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setTimeout</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">2000.0</span><span style="color: #D8DEE9FF">))</span></span></code></pre></div>



<p class="wp-block-paragraph">It looks like this</p>



<figure class="wp-block-image size-full is-style-zoooom"><img decoding="async" width="1303" height="885" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-load-event-p95.png" alt="" class="wp-image-117" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-load-event-p95.png 1303w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-load-event-p95-300x204.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-load-event-p95-1024x696.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-load-event-p95-768x522.png 768w" sizes="(max-width: 1303px) 100vw, 1303px" /><figcaption class="wp-element-caption">wait for <code>load</code> event` at percentile 95, button measured as visible after 914 ms</figcaption></figure>



<h4 class="wp-block-heading">How to interpret the video of web app measurement?</h4>



<p class="wp-block-paragraph">Playwright video recording is imperfect. Video frame is often not aligned with what was seen in web browser. I confirmed that with modifying the measured page after measurement end.</p>



<p class="wp-block-paragraph">During recording web app, right after measurement end timer is added to the page. This way it doesn&#8217;t interfere with measurement and helps to see when measurement has actually finished on the video.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(2 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="const measuerementEndMillis = ..; // depends on the measuerement
const timerDiv = document.createElement('div');
timerDiv.style.position = 'fixed';
timerDiv.style.top = '0';
timerDiv.style.left = '0';
timerDiv.style.opacity = '0.8';
timerDiv.style.fontSize = '14px';
timerDiv.style.backgroundColor = 'yellow';
timerDiv.style.zIndex = '10000';
document.body.appendChild(timerDiv);

setInterval(function() {
  const now = performance.now();
  timerDiv.textContent = now.toFixed(0) + ' ms, ' + (now - measurementEndMillis).toFixed(0) + ' ms';
}, 20);" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">measuerementEndMillis</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">..</span><span style="color: #81A1C1">;</span><span style="color: #D8DEE9FF"> </span><span style="color: #616E88">// depends on the measuerement</span></span>
<span class="line"><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">timerDiv</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">document</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">createElement</span><span style="color: #D8DEE9FF">(</span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">div</span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">timerDiv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">style</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">position</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">fixed</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">timerDiv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">style</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">top</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">0</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">timerDiv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">style</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">left</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">0</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">timerDiv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">style</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">opacity</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">0.8</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">timerDiv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">style</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">fontSize</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">14px</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">timerDiv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">style</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">backgroundColor</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">yellow</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">timerDiv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">style</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">zIndex</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C">10000</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9">document</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">body</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">appendChild</span><span style="color: #D8DEE9FF">(</span><span style="color: #D8DEE9">timerDiv</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #88C0D0">setInterval</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">function</span><span style="color: #ECEFF4">()</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">{</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #81A1C1">const</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">now</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">performance</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">now</span><span style="color: #D8DEE9FF">()</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #D8DEE9FF">  </span><span style="color: #D8DEE9">timerDiv</span><span style="color: #ECEFF4">.</span><span style="color: #D8DEE9">textContent</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">now</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">toFixed</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C"> ms, </span><span style="color: #ECEFF4">&#39;</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> (</span><span style="color: #D8DEE9">now</span><span style="color: #D8DEE9FF"> </span><span style="color: #81A1C1">-</span><span style="color: #D8DEE9FF"> </span><span style="color: #D8DEE9">measurementEndMillis</span><span style="color: #D8DEE9FF">)</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">toFixed</span><span style="color: #D8DEE9FF">(</span><span style="color: #B48EAD">0</span><span style="color: #D8DEE9FF">) </span><span style="color: #81A1C1">+</span><span style="color: #D8DEE9FF"> </span><span style="color: #ECEFF4">&#39;</span><span style="color: #A3BE8C"> ms</span><span style="color: #ECEFF4">&#39;</span><span style="color: #81A1C1">;</span></span>
<span class="line"><span style="color: #ECEFF4">},</span><span style="color: #D8DEE9FF"> </span><span style="color: #B48EAD">20</span><span style="color: #D8DEE9FF">)</span><span style="color: #81A1C1">;</span></span></code></pre></div>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" width="556" height="140" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-github-timer.png" alt="" class="wp-image-136" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-github-timer.png 556w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-github-timer-300x76.png 300w" sizes="(max-width: 556px) 100vw, 556px" /><figcaption class="wp-element-caption">X, Y<br>X &#8211; milliseconds since time origin (page started loading)<br>Y &#8211; milliseconds since measurement end. Timer is added after locator is satisfied so it doesn&#8217;t disturb the measurement</figcaption></figure>
</div>


<p class="wp-block-paragraph"><strong>When measuring very fast page like github.com landing page, you&#8217;re often going to see a blank page and right after that frames recorded some time after measurement end (signup button visible). Frames are lost. Based on that you should treat every moment of playwright video as possibly inaccurate</strong>.</p>



<p class="wp-block-paragraph">Having said that, let&#8217;s move on.</p>



<figure class="wp-block-video"><video height="450" style="aspect-ratio: 800 / 450;" width="800" controls src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-load-event-p95.webm"></video><figcaption class="wp-element-caption">wait for <code>load</code> event at percentile 95, button measured as visible after 914 ms. Mind the significant part of the video with button visible, yet measurement is not finished</figcaption></figure>



<h3 class="wp-block-heading">Experiment benchmark 1 &#8211; no waiting</h3>



<p class="wp-block-paragraph">Take 1000 samples. Don&#8217;t wait for any event, wait for github call to action button</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers cbp-blur-enabled cbp-unblur-on-hover" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import com.microsoft.playwright.Page.NavigateOptions
import com.microsoft.playwright.options.WaitUntilState.COMMIT

// from playwright docs WaitUntilState.COMMIT:
// consider operation to be finished when network response is received and the document started loading
page.navigate(&quot;https://github.com&quot;, NavigateOptions().setWaitUntil(COMMIT))
page.locator(&quot;#summary-val&quot;).first().waitFor()" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page.NavigateOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.options.WaitUntilState.COMMIT</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// from playwright docs WaitUntilState.COMMIT:</span></span>
<span class="line"><span style="color: #616E88">// consider operation to be finished when network response is received and the document started loading</span></span>
<span class="line cbp-no-blur"><span style="color: #D8DEE9FF">page.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;https://github.com&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">NavigateOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setWaitUntil</span><span style="color: #D8DEE9FF">(COMMIT))</span></span>
<span class="line cbp-no-blur"><span style="color: #D8DEE9FF">page.</span><span style="color: #88C0D0">locator</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;#summary-val&quot;</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">first</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">waitFor</span><span style="color: #D8DEE9FF">()</span></span></code></pre></div>



<p class="wp-block-paragraph">In selenium you have to opt out from waiting for <code>DOMCompleteEvent</code> this way</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="val options = new ChromeOptions()
options.setPageLoadStrategy(PageLoadStrategy.NONE)    
val driver = new ChromeDriver(options)
driver.navigate(..).to()" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> options </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> new </span><span style="color: #88C0D0">ChromeOptions</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">options.</span><span style="color: #88C0D0">setPageLoadStrategy</span><span style="color: #D8DEE9FF">(PageLoadStrategy.NONE)    </span></span>
<span class="line"><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> driver </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> new </span><span style="color: #88C0D0">ChromeDriver</span><span style="color: #D8DEE9FF">(options)</span></span>
<span class="line"><span style="color: #D8DEE9FF">driver.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">..</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">to</span><span style="color: #D8DEE9FF">()</span></span></code></pre></div>



<figure class="wp-block-image size-full is-style-zoooom"><img decoding="async" width="1301" height="887" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-no-wait-p95.png" alt="" class="wp-image-119" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-no-wait-p95.png 1301w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-no-wait-p95-300x205.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-no-wait-p95-1024x698.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-no-wait-p95-768x524.png 768w" sizes="(max-width: 1301px) 100vw, 1301px" /><figcaption class="wp-element-caption">no wait for event at percentile 95, button measured as visible after 156 ms</figcaption></figure>



<figure class="wp-block-video"><video height="450" style="aspect-ratio: 800 / 450;" width="800" controls src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-no-wait-p95.webm"></video><figcaption class="wp-element-caption">no wait for event at percentile 95, button measured as visible after 156 ms</figcaption></figure>



<p class="wp-block-paragraph"></p>



<h3 class="wp-block-heading">Experiment benchmark 2 &#8211; wait for <code>DOMContentLoaded</code> event</h3>



<p class="wp-block-paragraph">Take 1000 samples. Wait for <code>DOMContentLoaded</code>, wait wait for github call to action button</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers cbp-blur-enabled cbp-unblur-on-hover" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="import com.microsoft.playwright.Page.NavigateOptions
import com.microsoft.playwright.options.WaitUntilState.DOMCONTENTLOADED

// from playwright docs WaitUntilState.DOMCONTENTLOADED:
// consider operation to be finished when the DOMContentLoaded event is fired.
page.navigate(&quot;https://github.com&quot;, NavigateOptions().setWaitUntil(DOMCONTENTLOADED))
page.locator(&quot;#summary-val&quot;).first().waitFor()" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.Page.NavigateOptions</span></span>
<span class="line"><span style="color: #81A1C1">import</span><span style="color: #D8DEE9FF"> com.microsoft.playwright.options.WaitUntilState.DOMCONTENTLOADED</span></span>
<span class="line"></span>
<span class="line"><span style="color: #616E88">// from playwright docs WaitUntilState.DOMCONTENTLOADED:</span></span>
<span class="line"><span style="color: #616E88">// consider operation to be finished when the DOMContentLoaded event is fired.</span></span>
<span class="line cbp-no-blur"><span style="color: #D8DEE9FF">page.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;https://github.com&quot;</span><span style="color: #D8DEE9FF">, </span><span style="color: #88C0D0">NavigateOptions</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">setWaitUntil</span><span style="color: #D8DEE9FF">(DOMCONTENTLOADED))</span></span>
<span class="line cbp-no-blur"><span style="color: #D8DEE9FF">page.</span><span style="color: #88C0D0">locator</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">&quot;#summary-val&quot;</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">first</span><span style="color: #D8DEE9FF">().</span><span style="color: #88C0D0">waitFor</span><span style="color: #D8DEE9FF">()</span></span></code></pre></div>



<p class="wp-block-paragraph">In selenium you have to opt out from waiting for <code>DOMContentLoaded</code> event this way</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:14px;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#d8dee9ff;--cbp-line-number-width:calc(1 * 0.6 * 14px);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="val options = new ChromeOptions()
options.setPageLoadStrategy(PageLoadStrategy.EAGER)    
val driver = new ChromeDriver(options)
driver.navigate(..).to()" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> options </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> new </span><span style="color: #88C0D0">ChromeOptions</span><span style="color: #D8DEE9FF">()</span></span>
<span class="line"><span style="color: #D8DEE9FF">options.</span><span style="color: #88C0D0">setPageLoadStrategy</span><span style="color: #D8DEE9FF">(PageLoadStrategy.EAGER)    </span></span>
<span class="line"><span style="color: #81A1C1">val</span><span style="color: #D8DEE9FF"> driver </span><span style="color: #81A1C1">=</span><span style="color: #D8DEE9FF"> new </span><span style="color: #88C0D0">ChromeDriver</span><span style="color: #D8DEE9FF">(options)</span></span>
<span class="line"><span style="color: #D8DEE9FF">driver.</span><span style="color: #88C0D0">navigate</span><span style="color: #D8DEE9FF">(</span><span style="color: #81A1C1">..</span><span style="color: #D8DEE9FF">).</span><span style="color: #88C0D0">to</span><span style="color: #D8DEE9FF">()</span></span></code></pre></div>



<figure class="wp-block-image size-full is-style-zoooom"><img decoding="async" width="1303" height="887" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-DOMContentLoaded-p95.png" alt="" class="wp-image-121" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-DOMContentLoaded-p95.png 1303w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-DOMContentLoaded-p95-300x204.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-DOMContentLoaded-p95-1024x697.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-DOMContentLoaded-p95-768x523.png 768w" sizes="(max-width: 1303px) 100vw, 1303px" /><figcaption class="wp-element-caption">wait for <code>DOMContentLoaded</code> event at percentile 95, button measured as visible after 802 ms</figcaption></figure>



<figure class="wp-block-video"><video height="450" style="aspect-ratio: 800 / 450;" width="800" controls src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-DOMContentLoaded-p95.webm"></video><figcaption class="wp-element-caption">wait for <code>DOMContentLoaded</code> event at percentile 95, button measured as visible after 802 ms. Mind the significant part of the video with button visible, yet measurement is not finished</figcaption></figure>



<p class="wp-block-paragraph"></p>



<h3 class="wp-block-heading">And now let&#8217;s compare the results</h3>



<h4 class="wp-block-heading">P95 comparison</h4>



<figure class="wp-block-table"><table><tbody><tr><td>no waiting</td><td>wait for <code>DOMContentLoaded</code></td><td>wait for <code>load</code> event</td></tr><tr><td>156 ms</td><td>802 ms</td><td>914 ms</td></tr></tbody></table><figcaption class="wp-element-caption">95th percentile  across different type of loading website in playwright/selenium</figcaption></figure>



<p class="wp-block-paragraph">Percentiles do not answer the question <em>how distribution compares</em>? Down the line you&#8217;re going to see <a href="https://en.wikipedia.org/wiki/Hodges%E2%80%93Lehmann_estimator" target="_blank" rel="noopener" title="">Hodges-Lehman estimator</a></p>



<h4 class="wp-block-heading">What hypotheses are we verifying here?</h4>



<ul class="wp-block-list">
<li><code>load</code> event
<ul class="wp-block-list">
<li>(baseline) null hypothesis: waiting for <code>load</code> event has completion has no performance impact</li>



<li>(experiment 1) alternative hypothesis: waiting for <code>load</code>event has impact on measured latencies</li>
</ul>
</li>



<li><code>DOMContentLoaded</code> event
<ul class="wp-block-list">
<li>(baseline) null hypothesis: waiting for <code>DOMContentLoad</code> event has has no impact on measured latencies</li>



<li>(experiment 2) alternative hypothesis: waiting for <code>DOMContentLoad</code>event has impact on measured latencies</li>
</ul>
</li>
</ul>



<h3 class="wp-block-heading">Waiting for <code>load</code> event VS not waiting for any event</h3>



<figure class="wp-block-image size-full is-style-zoooom"><img decoding="async" width="1303" height="429" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-load-vs-no-wait.png" alt="" class="wp-image-124" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-load-vs-no-wait.png 1303w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-load-vs-no-wait-300x99.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-load-vs-no-wait-1024x337.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-load-vs-no-wait-768x253.png 768w" sizes="(max-width: 1303px) 100vw, 1303px" /><figcaption class="wp-element-caption">83.829% improvement when not waiting for any browser event</figcaption></figure>



<p class="wp-block-paragraph">Title of this blog post says there is 530% performance degradation when you rely on browser automation defaults. So where is it? When you reverse baseline VS experiment, there it is</p>



<figure class="wp-block-image size-full is-style-zoooom"><img decoding="async" width="1301" height="279" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load.png" alt="" class="wp-image-125" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load.png 1301w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load-300x64.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load-1024x220.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load-768x165.png 768w" sizes="(max-width: 1301px) 100vw, 1301px" /><figcaption class="wp-element-caption">Null hypothesis is rejected, waiting for <code>load</code> event comes with bigger latencies</figcaption></figure>



<p class="wp-block-paragraph">Null hypothesis is rejected, waiting for <code>load</code> event comes with bigger latencies</p>



<figure class="wp-block-image size-full is-style-zoooom"><img decoding="async" width="1302" height="312" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load-after-ttfb.png" alt="" class="wp-image-126" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load-after-ttfb.png 1302w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load-after-ttfb-300x72.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load-after-ttfb-1024x245.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-no-wait-vs-wait-for-load-after-ttfb-768x184.png 768w" sizes="(max-width: 1302px) 100vw, 1302px" /><figcaption class="wp-element-caption">Time to first byte is irrelevant for hypothesis verification. When you remove it from measured latencies it unfolds more accurate impact on frontend part.</figcaption></figure>



<p class="wp-block-paragraph"></p>



<h3 class="wp-block-heading">Waiting for <code>DOMContentLoaded</code> VS not waiting for any event</h3>



<figure class="wp-block-image size-full is-style-zoooom"><img decoding="async" width="1300" height="279" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-no-wait.png" alt="" class="wp-image-128" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-no-wait.png 1300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-no-wait-300x64.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-no-wait-1024x220.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-no-wait-768x165.png 768w" sizes="(max-width: 1300px) 100vw, 1300px" /><figcaption class="wp-element-caption">79.642% smaller latencies when not waiting for any browser event</figcaption></figure>



<p class="wp-block-paragraph">Null hypothesis is rejected, waiting for <code>DOMContentLoaded </code>increases measured latencies</p>



<h3 class="wp-block-heading">Waiting for <code>DOMContentLoaded</code> VS waiting for <code>load</code> event</h3>



<p class="wp-block-paragraph">Having 2 experiments we can compare them too. Since <code>load</code> event comes always after <code>DOMContentLoaded</code> it&#8217;s expected to see bigger latencies which is reflected in measurements</p>



<figure class="wp-block-image size-full is-style-zoooom"><img decoding="async" width="1300" height="278" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-wait-for-load.png" alt="" class="wp-image-129" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-wait-for-load.png 1300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-wait-for-load-300x64.png 300w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-wait-for-load-1024x219.png 1024w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-comparison-wait-for-DOMContentLoaded-vs-wait-for-load-768x164.png 768w" sizes="(max-width: 1300px) 100vw, 1300px" /></figure>



<p class="wp-block-paragraph"></p>



<h2 class="wp-block-heading">Based on measurements, what are undesired effects of waiting for <code>DOMContentLoad</code> or <code>load</code> event?</h2>



<ul class="wp-block-list">
<li>To understand better following part take a look at difference between accuracy and precision</li>
</ul>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="764" height="794" src="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-Accuracy-vs-Precision.png" alt="" class="wp-image-130" style="width:507px;height:auto" srcset="http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-Accuracy-vs-Precision.png 764w, http://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-Accuracy-vs-Precision-289x300.png 289w" sizes="(max-width: 764px) 100vw, 764px" /></figure>



<ul class="wp-block-list">
<li>The undesired effects</li>



<li>Measuring something neither reflecting user experience nor relevant to user experience. User sees a button and clicks a button regardless of events in the browser
<ul class="wp-block-list">
<li>No user ever thought &#8220;<em>I politely wish this site had dom completion faster</em>&#8220;. User thinks &#8220;<em>Why the f^%@ is this button not working yet?!!??</em>&#8220;</li>
</ul>
</li>



<li>(much) longer benchmarks
<ul class="wp-block-list">
<li>for ones that aim for measurement count, e.g. we want to have 200 samples</li>
</ul>
</li>



<li>Less precise benchmarks
<ul class="wp-block-list">
<li>for ones that aim for duration, e.g. running for 10 minutes</li>



<li>because you will get less samples</li>
</ul>
</li>



<li>Less accurate benchmarks
<ul class="wp-block-list">
<li>because you&#8217;re not measuring what user experiencies</li>



<li>As a result system under benchmark might actually meet some acceptance criteria, while measurements show it&#8217;s not</li>
</ul>
</li>



<li>Disturbs calculating correlations and figuring out frontend latency causation</li>
</ul>



<h2 class="wp-block-heading">Side effects of not waiting for <code>DOMContentLoaded</code> or <code>loaded</code> event</h2>



<p class="wp-block-paragraph">User can click a visible button, that does not react on clicks yet. Which actually is a reality <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p class="wp-block-paragraph">However that complicates measurements. Frameworks do not check if a button has event listeners. It often requires polling for a desired button state. e.g. await condition click and check if expected message is visible</p>



<h2 class="wp-block-heading">Summary</h2>



<p class="wp-block-paragraph">I&#8217;ve taken thousands of measurements of a few different sites, not only github. Systems behave the same. Waiting for browser events makes measured latencies inaccurate. It might be 10% away from real user experience, it might be 530%.</p>



<p class="wp-block-paragraph">It&#8217;s non-zero, reproducible and deterministic. So if you already made an effort to make synthetic performance benchmarks, I&#8217;d recommend putting an effort also in increasing their accuracy.</p>



<h3 class="wp-block-heading">Consider changing default options for browser automation</h3>



<p class="wp-block-paragraph">For playwright and selenium (and whatever browser automation tools there are) you should opt out from waiting for any browser events if you&#8217;re using those tools for performance measurements.</p>



<figure class="wp-block-table"><table><tbody><tr><td>Option</td><td>Selenium</td><td>Playwright</td></tr><tr><td>wait for response received (<strong>best for performance benchmarks</strong>)</td><td>NONE</td><td>COMMIT</td></tr><tr><td>wait for load event (<strong>default</strong>)</td><td>NORMAL</td><td>LOAD</td></tr><tr><td>wait for DOMContentLoaded event (another option)</td><td>EAGER</td><td>DOMCONTENTLOADED</td></tr></tbody></table></figure>



<p class="wp-block-paragraph"></p><p>The post <a href="http://deploymenteveryday.com/how-to-make-performance-benchmark-530-worse-by-relying-on-web-app-automation-defaults/">How to make performance benchmark 530% worse by relying on web app automation defaults?</a> first appeared on <a href="http://deploymenteveryday.com">Deployment Every Day</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>http://deploymenteveryday.com/how-to-make-performance-benchmark-530-worse-by-relying-on-web-app-automation-defaults/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		<enclosure url="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-load-event-p95.webm" length="43031" type="video/webm" />
<enclosure url="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-no-wait-p95.webm" length="41413" type="video/webm" />
<enclosure url="https://deploymenteveryday.com/wp-content/uploads/2024/05/blog_post_should-you-wait-for-DOMContentLoaded-wait-for-DOMContentLoaded-p95.webm" length="41614" type="video/webm" />

			</item>
	</channel>
</rss>
