<?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>plawright - Deployment Every Day</title>
	<atom:link href="http://deploymenteveryday.com/tag/plawright/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>
	</channel>
</rss>
