<?xml version="1.0"?><feed xmlns:media="http://search.yahoo.com/mrss/" xmlns:gr="http://www.google.com/schemas/reader/atom/" xmlns:idx="urn:atom-extension:indexing" xmlns="http://www.w3.org/2005/Atom" idx:index="no" gr:dir="ltr"><!--
Content-type: Preventing XSRF in IE.

--><generator uri="https://bazqux.com">BazQux Reader</generator><id>tag:google.com,2005:reader/feed/http://007unlicensedtotest.blogspot.com/feeds/posts/default</id><title>blogs</title><subtitle type="html">blogs</subtitle><link rel="self" href="https://bazqux.com/feed/d45a6ead98c5f8f9f99f?no_branding"></link><gr:continuation>4690104287673</gr:continuation><updated>2026-05-13T10:44:04Z</updated><entry gr:crawl-timestamp-msec="1778643661000"><id gr:original-id="https://scrolltest.com/docker-compose-testing-setup-playwright-selenium-api/">tag:google.com,2005:reader/item/00000444000001be</id><category term="API Testing"></category><category term="DevOps"></category><category term="Docker"></category><category term="Testing"></category><category term="api testing"></category><category term="API Testing with Playwright"></category><category term="CI/CD"></category><category term="zalenium docker compose"></category><category term="zalenium selenium grid"></category><title type="html">Docker Compose Testing Setup: Playwright, Selenium Grid, and API</title><published>2026-05-13T03:41:01Z</published><updated>2026-05-13T03:41:01Z</updated><link rel="alternate" href="https://scrolltest.com/docker-compose-testing-setup-playwright-selenium-api/" type="text/html"></link><summary type="html">&lt;p&gt;A complete Docker Compose testing setup for Playwright, Selenium Grid, and API tests. One file, consistent environments, zero drift.&lt;/p&gt;
&lt;p&gt;The post &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; rel=&quot;nofollow&quot; href=&quot;https://scrolltest.com/docker-compose-testing-setup-playwright-selenium-api/&quot;&gt;Docker Compose Testing Setup: Playwright, Selenium Grid, and API&lt;/a&gt; appeared first on &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; rel=&quot;nofollow&quot; href=&quot;https://scrolltest.com&quot;&gt;Software Testing &amp;amp; Automation&lt;/a&gt;.&lt;/p&gt;</summary><author><name>Promode</name></author><source gr:stream-id="feed/https://scrolltest.com/feed/"><id>tag:google.com,2005:reader/feed/https://scrolltest.com/feed/</id><title type="html">Software Testing &amp; Automation</title><link rel="alternate" href="https://scrolltest.com" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778630400000"><id gr:original-id="https://qualitywithmillan.github.io/blog/ai/spec-driven-development-and-constitution-md">tag:google.com,2005:reader/item/000008500000004b</id><category term="blog"></category><category term="quality-engineering"></category><category term="ai"></category><category term="qa"></category><category term="ai-testing"></category><category term="spec-driven-development"></category><category term="constitution-md"></category><category term="software-architecture"></category><category term="test-strategy"></category><category term="ai-native"></category><category term="prompt-engineering"></category><category term="human-in-the-loop"></category><title type="html">constitution.md in Spec-Driven Development</title><published>2026-05-13T00:00:00Z</published><updated>2026-05-13T00:00:00Z</updated><link rel="alternate" href="https://qualitywithmillan.github.io/blog/ai/constitution-md-spec-driven-development.html" type="text/html"></link><summary type="html">&lt;p&gt;
 Written by -

    
    
    
        &lt;strong&gt;Millan Kaul&lt;/strong&gt;
    

&lt;/p&gt;

&lt;h2 id=&quot;article-dPdjd4zlWOdOgyX3fxGiKBimeNM-constitutionmd--the-rules-behind-spec-driven-development&quot;&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext language-highlighter-rouge&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://constitution.md&quot;&gt;constitution.md&lt;/a&gt;&lt;/code&gt;&lt;/strong&gt; : The Rules Behind Spec-Driven Development&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext language-highlighter-rouge&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://constitution.md&quot;&gt;constitution.md&lt;/a&gt;&lt;/code&gt; file is the project’s rulebook in Spec-Driven Development. It gives developers, architects, and QA teams a shared set of principles to follow before any feature spec, plan, or implementation begins.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;bqrUnknownImgSize&quot; src=&quot;https://qualitywithmillan.github.io/assets/images/2026/05/sdd_SPECIFY_ASCII.png&quot; alt=&quot;Image 01&quot;&gt;
&lt;/p&gt;

&lt;p&gt;If you are using &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/github/spec-kit&quot;&gt;GitHub Spec Kit&lt;/a&gt;, &lt;code class=&quot;language-plaintext language-highlighter-rouge&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://constitution.md&quot;&gt;constitution.md&lt;/a&gt;&lt;/code&gt; is the document that sets the &lt;strong&gt;rules of the road&lt;/strong&gt; for your project. It is not the feature spec itself. It is the project’s guiding policy: the stable principles that shape how every spec, plan, task, and implementation decision should behave.&lt;/p&gt;

&lt;p&gt;In simple terms, it tells your AI-assisted workflow: &lt;strong&gt;this is how we build here&lt;/strong&gt;. The Spec Kit README describes spec-driven development as a process where the constitution provides the prescriptive guidance that keeps later planning and implementation aligned.&lt;/p&gt;

&lt;h2 id=&quot;article-dPdjd4zlWOdOgyX3fxGiKBimeNM-why-it-matters&quot;&gt;Why it matters&lt;/h2&gt;

&lt;p&gt;For developers and architects, &lt;code class=&quot;language-plaintext language-highlighter-rouge&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://constitution.md&quot;&gt;constitution.md&lt;/a&gt;&lt;/code&gt; :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Acts like an architectural contract.&lt;/li&gt;
  &lt;li&gt;It helps keep decisions consistent across features, teams, and AI-generated output, instead of letting each prompt invent its own style of development.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is especially useful when multiple people or &lt;code class=&quot;language-plaintext language-highlighter-rouge&quot;&gt;agents&lt;/code&gt; contribute to the same codebase.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;For QA and testing, it is equally important because it creates a baseline for what should always be true.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If your constitution says “no silent failures,” “test critical paths first,” or “do not leak user data,” then those become expectations that can be checked during review, planning, and implementation.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;bqrUnknownImgSize&quot; src=&quot;https://qualitywithmillan.github.io/assets/images/2026/05/sdd_SPECIFY_constitution_md.png&quot; alt=&quot;Image 01&quot;&gt;
&lt;/p&gt;

&lt;h2 id=&quot;article-dPdjd4zlWOdOgyX3fxGiKBimeNM-how-to-use-it&quot;&gt;How to use it&lt;/h2&gt;

&lt;p&gt;A good way to think about &lt;code class=&quot;language-plaintext language-highlighter-rouge&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://constitution.md&quot;&gt;constitution.md&lt;/a&gt;&lt;/code&gt; is this: it is the document you write &lt;strong&gt;before&lt;/strong&gt; the feature spec. The feature spec changes often, but the constitution stays steady and guides every later step in the workflow.&lt;/p&gt;

&lt;p&gt;A practical flow looks like this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Define the project principles in &lt;code class=&quot;language-plaintext language-highlighter-rouge&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://constitution.md&quot;&gt;constitution.md&lt;/a&gt;&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Use those principles while creating the feature spec.&lt;/li&gt;
  &lt;li&gt;Let the plan, tasks, and implementation remain consistent with those principles.&lt;/li&gt;
  &lt;li&gt;Review the final result against the constitution, not just the feature requirements.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;Here is a &lt;code class=&quot;language-plaintext language-highlighter-rouge&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://constitution-template.md&quot;&gt;constitution-template.md&lt;/a&gt;&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div&gt;&lt;div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;---&lt;/span&gt;
&lt;span&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://constitution.md&quot;&gt;constitution.md&lt;/a&gt;&lt;/span&gt;
&lt;span&gt;---&lt;/span&gt;

&lt;span&gt;# [PROJECT_NAME] Constitution&lt;/span&gt;
&lt;span&gt;&amp;lt;!-- Example: Spec Constitution, TaskFlow Constitution, etc. --&amp;gt;&lt;/span&gt;

&lt;span&gt;## Core Principles&lt;/span&gt;

&lt;span&gt;### [PRINCIPLE_1_NAME]&lt;/span&gt;
&lt;span&gt;&amp;lt;!-- Example: I. Library-First --&amp;gt;&lt;/span&gt;
[PRINCIPLE_1_DESCRIPTION]
&lt;span&gt;&amp;lt;!-- Example: Every feature starts as a standalone library; Libraries must be self-contained, independently testable, documented; Clear purpose required - no organizational-only libraries --&amp;gt;&lt;/span&gt;

&lt;span&gt;### [PRINCIPLE_2_NAME]&lt;/span&gt;
&lt;span&gt;&amp;lt;!-- Example: II. CLI Interface --&amp;gt;&lt;/span&gt;
[PRINCIPLE_2_DESCRIPTION]
&lt;span&gt;&amp;lt;!-- Example: Every library exposes functionality via CLI; Text in/out protocol: stdin/args → stdout, errors → stderr; Support JSON + human-readable formats --&amp;gt;&lt;/span&gt;

&lt;span&gt;### [PRINCIPLE_3_NAME]&lt;/span&gt;
&lt;span&gt;&amp;lt;!-- Example: III. Test-First (NON-NEGOTIABLE) --&amp;gt;&lt;/span&gt;
[PRINCIPLE_3_DESCRIPTION]
&lt;span&gt;&amp;lt;!-- Example: TDD mandatory: Tests written → User approved → Tests fail → Then implement; Red-Green-Refactor cycle strictly enforced --&amp;gt;&lt;/span&gt;

&lt;span&gt;### [PRINCIPLE_4_NAME]&lt;/span&gt;
&lt;span&gt;&amp;lt;!-- Example: IV. Integration Testing --&amp;gt;&lt;/span&gt;
[PRINCIPLE_4_DESCRIPTION]
&lt;span&gt;&amp;lt;!-- Example: Focus areas requiring integration tests: New library contract tests, Contract changes, Inter-service communication, Shared schemas --&amp;gt;&lt;/span&gt;

&lt;span&gt;### [PRINCIPLE_5_NAME]&lt;/span&gt;
&lt;span&gt;&amp;lt;!-- Example: V. Observability, VI. Versioning &amp;amp; Breaking Changes, VII. Simplicity --&amp;gt;&lt;/span&gt;
[PRINCIPLE_5_DESCRIPTION]
&lt;span&gt;&amp;lt;!-- Example: Text I/O ensures debuggability; Structured logging required; Or: MAJOR.MINOR.BUILD format; Or: Start simple, YAGNI principles --&amp;gt;&lt;/span&gt;

&lt;span&gt;## [SECTION_2_NAME]&lt;/span&gt;
&lt;span&gt;&amp;lt;!-- Example: Additional Constraints, Security Requirements, Performance Standards, etc. --&amp;gt;&lt;/span&gt;

[SECTION_2_CONTENT]
&lt;span&gt;&amp;lt;!-- Example: Technology stack requirements, compliance standards, deployment policies, etc. --&amp;gt;&lt;/span&gt;

&lt;span&gt;## [SECTION_3_NAME]&lt;/span&gt;
&lt;span&gt;&amp;lt;!-- Example: Development Workflow, Review Process, Quality Gates, etc. --&amp;gt;&lt;/span&gt;

[SECTION_3_CONTENT]
&lt;span&gt;&amp;lt;!-- Example: Code review requirements, testing gates, deployment approval process, etc. --&amp;gt;&lt;/span&gt;

&lt;span&gt;## Governance&lt;/span&gt;
&lt;span&gt;&amp;lt;!-- Example: Constitution supersedes all other practices; Amendments require documentation, approval, migration plan --&amp;gt;&lt;/span&gt;

[GOVERNANCE_RULES]
&lt;span&gt;&amp;lt;!-- Example: All PRs/reviews must verify compliance; Complexity must be justified; Use [GUIDANCE_FILE] for runtime development guidance --&amp;gt;&lt;/span&gt;

&lt;span&gt;**Version**&lt;/span&gt;: [CONSTITUTION_VERSION] | &lt;span&gt;**Ratified**&lt;/span&gt;: [RATIFICATION_DATE] | &lt;span&gt;**Last Amended**&lt;/span&gt;: [LAST_AMENDED_DATE]
&lt;span&gt;&amp;lt;!-- Example: Version: 2.1.1 | Ratified: 2025-06-13 | Last Amended: 2025-07-16 --&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;article-dPdjd4zlWOdOgyX3fxGiKBimeNM-what-to-put-in-it&quot;&gt;What to put in it&lt;/h2&gt;

&lt;p&gt;Keep it short, clear, and opinionated. Good constitution rules usually cover architecture, quality, security, testing, data handling, and how AI should behave in your codebase. Examples include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;prefer simple, testable designs.&lt;/li&gt;
  &lt;li&gt;fail loudly instead of hiding errors.&lt;/li&gt;
  &lt;li&gt;protect user data by default.&lt;/li&gt;
  &lt;li&gt;require tests for critical paths.&lt;/li&gt;
  &lt;li&gt;keep generated code aligned with existing architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That style works well because the constitution is meant to be a durable reference, not a long policy manual.&lt;/p&gt;

&lt;h2 id=&quot;article-dPdjd4zlWOdOgyX3fxGiKBimeNM-my-take&quot;&gt;My take&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://constitution.md&quot;&gt;constitution.md&lt;/a&gt;&lt;/strong&gt; is where Spec-Driven Development becomes real. It gives developers a shared architecture mindset, gives QA a testable quality baseline, and gives AI a guardrail before it starts generating anything.&lt;/p&gt;

&lt;p&gt;In other words, it is the document that turns &lt;em&gt;“build it fast”&lt;/em&gt; into &lt;strong&gt;“build it right.”&lt;/strong&gt;&lt;/p&gt;</summary><author><name>Millan Kaul</name></author><source gr:stream-id="feed/https://qualitywithmillan.github.io/feed.xml"><id>tag:google.com,2005:reader/feed/https://qualitywithmillan.github.io/feed.xml</id><title type="html">Quality With Millan</title><link rel="alternate" href="https://qualitywithmillan.github.io/" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778623200000"><id gr:original-id="https://scrolltest.com/?p=7340">tag:google.com,2005:reader/item/00000444000001bd</id><category term="Career Guide"></category><category term="Interview Prep"></category><category term="Testing"></category><title type="html">The 2026 QA Engineer Career Roadmap: Month-by-Month From Manual Testing to Senior SDET</title><published>2026-05-12T22:00:00Z</published><updated>2026-05-12T22:00:00Z</updated><link rel="alternate" href="https://scrolltest.com/qa-engineer-career-roadmap-manual-to-senior-sdet-2026/" type="text/html"></link><summary type="html">&lt;p&gt;The complete 12-month QA career roadmap from manual testing to senior SDET. Month-by-month learning plan with deliverables, interview milestones, and salary expectations.&lt;/p&gt;
&lt;p&gt;The post &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; rel=&quot;nofollow&quot; href=&quot;https://scrolltest.com/qa-engineer-career-roadmap-manual-to-senior-sdet-2026/&quot;&gt;The 2026 QA Engineer Career Roadmap: Month-by-Month From Manual Testing to Senior SDET&lt;/a&gt; appeared first on &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; rel=&quot;nofollow&quot; href=&quot;https://scrolltest.com&quot;&gt;Software Testing &amp;amp; Automation&lt;/a&gt;.&lt;/p&gt;</summary><author><name>Pramod Dutta</name></author><source gr:stream-id="feed/https://scrolltest.com/feed/"><id>tag:google.com,2005:reader/feed/https://scrolltest.com/feed/</id><title type="html">Software Testing &amp; Automation</title><link rel="alternate" href="https://scrolltest.com" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778621154000"><id gr:original-id="http://leadtestinclude.com/?p=2941">tag:google.com,2005:reader/item/0000036d00000039</id><category term="Management"></category><category term="quality"></category><category term="leadership"></category><category term="Quality Engineering"></category><category term="ai"></category><category term="testing"></category><category term="people"></category><category term="technology"></category><category term="artificial-intelligence"></category><title type="html">The IQL Manifesto: Seven Commitments for AI Quality Leadership</title><published>2026-05-12T21:25:54Z</published><updated>2026-05-12T21:25:54Z</updated><link rel="alternate" href="https://leadtestinclude.com/2026/05/12/the-iql-manifesto-seven-commitments-for-ai-quality-leadership/" type="text/html"></link><summary type="html">&lt;p&gt;&lt;em&gt;This is the post where two things I’ve been building separately finally meet.&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;When I started the Intelligent Quality Leadership series earlier this year, I had a model I wanted to stress-test and a set of ideas I needed to get out of my head and onto a page. What I didn’t expect was that writing it would force me to reckon with something important.&lt;/p&gt;



&lt;p&gt;I’ve been building towards quality leadership my entire career. The AI dimension is more recent and is applied, hands-on, and still evolving. What this series has done is show me where those two things intersect. And that intersection is where the real work is.&lt;/p&gt;



&lt;p&gt;This post is where that intersection lands.&lt;/p&gt;



&lt;hr&gt;



&lt;h2&gt;Where This Started&lt;/h2&gt;



&lt;p&gt;I’ve created Quality Engineering principles twice before, at two very different companies.&lt;/p&gt;



&lt;p&gt;The first time was at easyJet, built in collaboration with our QE Architect at the time, Suman Bala. Bold, energetic, built for scale. “Quality as a Culture” sat at the centre of everything, surrounded by principles designed to rally a 120-person distributed organisation around a shared belief. The language was deliberate, “Automate ALL the RIGHT things”, “Testability as a foundation”, because at that size, you need something people can remember, repeat, and actually live by.&lt;/p&gt;



&lt;p&gt;The second time was at Goodnotes. The bones were the same but the expression was different. More refined, more embedded in product thinking. “Sustainable Automation” instead of “automate the right things.” “Collaborative Quality Ownership” instead of a rallying cry. The principles had grown up with me.&lt;/p&gt;



&lt;p&gt;Both sets still hold up. But neither of them accounts for what AI has done to our discipline. And that’s what this is.&lt;/p&gt;



&lt;hr&gt;



&lt;h2&gt;Why a Manifesto&lt;/h2&gt;



&lt;p&gt;I spent a long time thinking about what to call this. Principles felt too passive. Framework felt too corporate. Pillars, charter, ethos, all considered and I chose to set them aside.&lt;/p&gt;



&lt;p&gt;I landed on Manifesto because it’s the right word for what this is. A manifesto isn’t a description of how things are. It’s a stake in the ground about how things should be. It invites adoption, challenge, and conversation. And that’s exactly what I want this to do.&lt;/p&gt;



&lt;p&gt;These aren’t seven things I’ve observed in good teams. They’re seven commitments I believe quality leaders need to make and keep making, if they’re serious about leading in an AI-first engineering world.&lt;/p&gt;



&lt;hr&gt;



&lt;h2&gt;The IQL Manifesto&lt;/h2&gt;



&lt;figure&gt;&lt;img width=&quot;1024&quot; height=&quot;534&quot; data-attachment-id=&quot;2957&quot; data-permalink=&quot;https://leadtestinclude.com/f15ff4f3-542c-4294-8ddc-1f6c6ee2e214-1/&quot; data-orig-file=&quot;https://leadtestinclude.com/wp-content/uploads/2026/05/f15ff4f3-542c-4294-8ddc-1f6c6ee2e214-1.png&quot; data-orig-size=&quot;1736,906&quot; data-comments-opened=&quot;1&quot; data-image-meta=&quot;{&amp;quot;aperture&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;credit&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;camera&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;caption&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;created_timestamp&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;copyright&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;focal_length&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;iso&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;shutter_speed&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;title&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;orientation&amp;quot;:&amp;quot;0&amp;quot;,&amp;quot;alt&amp;quot;:&amp;quot;&amp;quot;}&quot; data-image-title=&quot;f15ff4f3-542c-4294-8ddc-1f6c6ee2e214-1&quot; data-image-description data-image-caption data-large-file=&quot;https://leadtestinclude.com/wp-content/uploads/2026/05/f15ff4f3-542c-4294-8ddc-1f6c6ee2e214-1.png?w=900&quot; alt data-orig-srcset=&quot;https://leadtestinclude.com/wp-content/uploads/2026/05/f15ff4f3-542c-4294-8ddc-1f6c6ee2e214-1.png?w=1024 1024w, https://leadtestinclude.com/wp-content/uploads/2026/05/f15ff4f3-542c-4294-8ddc-1f6c6ee2e214-1.png?w=150 150w, https://leadtestinclude.com/wp-content/uploads/2026/05/f15ff4f3-542c-4294-8ddc-1f6c6ee2e214-1.png?w=300 300w, https://leadtestinclude.com/wp-content/uploads/2026/05/f15ff4f3-542c-4294-8ddc-1f6c6ee2e214-1.png?w=768 768w, https://leadtestinclude.com/wp-content/uploads/2026/05/f15ff4f3-542c-4294-8ddc-1f6c6ee2e214-1.png?w=1440 1440w, https://leadtestinclude.com/wp-content/uploads/2026/05/f15ff4f3-542c-4294-8ddc-1f6c6ee2e214-1.png 1736w&quot; src=&quot;https://leadtestinclude.com/wp-content/uploads/2026/05/f15ff4f3-542c-4294-8ddc-1f6c6ee2e214-1.png&quot;&gt;&lt;/figure&gt;



&lt;h3&gt;01 – Challenge the reasoning, not just the result&lt;/h3&gt;



&lt;p&gt;AI outputs can look right and be wrong. Quality leaders build cultures that interrogate &lt;em&gt;why&lt;/em&gt;, not just &lt;em&gt;what&lt;/em&gt;.&lt;/p&gt;



&lt;p&gt;This is the mindset shift that everything else depends on. Traditional testing has a simple contract: given this input, I expect this output. AI breaks that contract. It’s confident, fluent, and plausible. It can be completely wrong without looking like it.&lt;/p&gt;



&lt;p&gt;The skill quality leaders need to build, in themselves and their teams, isn’t just evaluating what an AI produced. It’s knowing how to interrogate why, and when to trust the answer versus when to push back hard. TRACE, the framework I developed in&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://leadtestinclude.com/2026/04/22/iql-practical-1-dont-just-test-the-output-challenge-the-reasoning/&quot;&gt; IQL Practical #1&lt;/a&gt;, is one way to do this. But the principle is bigger than any framework. It’s a habit of mind.&lt;/p&gt;



&lt;hr&gt;



&lt;h3&gt;02 – Governance doesn’t check quality at the end. It builds the foundation at the start.&lt;/h3&gt;



&lt;p&gt;The earliest conversations about an AI feature are where the quality bar is set, whether you’re in the room or not.&lt;/p&gt;



&lt;p&gt;There’s a version of AI governance that lives in slide decks and quarterly board updates. It’s tidy, well-presented, and almost completely disconnected from the moments that actually matter.&lt;/p&gt;



&lt;p&gt;Real governance happens on a Tuesday morning, ten weeks before release, when a product manager sends a calendar invite for an AI feature scoping kickoff. The quality leader who understands this isn’t waiting for a test plan to think about risk. They’re in that room, asking the questions nobody else thought to ask yet. If you’re not there, the quality bar gets set without you. And it’s very hard to raise it later.&lt;/p&gt;



&lt;hr&gt;



&lt;h3&gt;03 – Test the AI, not just the software containing it.&lt;/h3&gt;



&lt;p&gt;The failures that matter most in AI systems don’t look like bugs. They look like drift, bias, inconsistency, and cost you didn’t see coming.&lt;/p&gt;



&lt;p&gt;Most teams are running their existing playbook against a fundamentally different kind of system. They test the integration layer. They check the UI. Someone runs a few prompts manually and confirms the responses look reasonable. The feature ships.&lt;/p&gt;



&lt;p&gt;Three months later, the model gets updated. Outputs subtly change. A user in a different region gets a culturally inappropriate response. The AI starts hallucinating a statistic that sounds authoritative but is completely fabricated. None of it gets caught because none of it was being tested for.&lt;/p&gt;



&lt;p&gt;Testing AI means covering five distinct dimensions: reliability across runs, behavioural consistency, safety at the edges, explainability, and cost you can sustain. If your AI testing strategy is your existing test strategy with an AI component bolted on, you don’t have an AI testing strategy. You have a gap you haven’t found yet.&lt;/p&gt;



&lt;hr&gt;



&lt;h3&gt;04 – Faster automation isn’t the answer. Smarter AI use is.&lt;/h3&gt;



&lt;p&gt;AI can generate a thousand tests in minutes. The hard question is whether any of them are asking the right thing.&lt;/p&gt;



&lt;p&gt;The rush to use AI for automation is creating a lot of volume and very little value. Teams are generating tests at scale without asking whether they’re generating the right tests. Speed becomes the metric when quality should be.&lt;/p&gt;



&lt;p&gt;The risk isn’t that teams automate too slowly. It’s that they automate thoughtlessly and call it transformation. A quality leader’s job is to make sure AI amplifies good judgement rather than replacing the need for it. That’s a harder thing to lead than it sounds, because the volume looks impressive and the dashboard looks green.&lt;/p&gt;



&lt;hr&gt;



&lt;h3&gt;05 – Collaborative Quality Ownership isn’t optional.&lt;/h3&gt;



&lt;p&gt;In an AI-first team, real ownership has to be deliberate, visible, and led.&lt;/p&gt;



&lt;p&gt;AI has created a democratisation illusion around quality. The optimistic narrative goes: AI puts testing capability in everyone’s hands, so shared ownership should be easier to achieve than ever. I’d ask you to look more carefully at what’s actually happening in your teams.&lt;/p&gt;



&lt;p&gt;When a developer generates a test suite using an AI tool, they’ve accessed a quality capability. They haven’t necessarily taken ownership of quality. Ownership means understanding what you’re testing and why, caring about what the output tells you, and being willing to have the difficult conversation when the picture isn’t clear. Generating a test file and merging it doesn’t require any of that.&lt;/p&gt;



&lt;p&gt;The risk isn’t that AI keeps people away from quality. It’s that AI gives people the feeling of participating in quality without the responsibility that real participation requires. In an AI-first team, collaborative ownership doesn’t emerge naturally. It has to be designed in, made visible, and actively led.&lt;/p&gt;



&lt;hr&gt;



&lt;h3&gt;06 – Human judgement moves to where it still matters most.&lt;/h3&gt;



&lt;p&gt;The decisions AI can’t make, the risks it can’t see, and the moments where someone needs to say no.&lt;/p&gt;



&lt;p&gt;The human in the loop isn’t going anywhere. They’re just moving up.&lt;/p&gt;



&lt;p&gt;As AI takes on more of the execution, writing tests, analysing results, flagging anomalies, it means the human role shifts. Not away from quality, but towards the parts of quality that require something AI genuinely can’t provide: contextual judgement, ethical reasoning, and the willingness to say “I don’t think we should ship this” when the data is inconclusive but the instinct is clear.&lt;/p&gt;



&lt;p&gt;That’s not a limitation of AI maturity. It’s a deliberate quality decision. The teams that understand this will build better products. The ones that don’t will find out the hard way.&lt;/p&gt;



&lt;hr&gt;



&lt;h3&gt;07 – Confidence is the differentiator.&lt;/h3&gt;



&lt;p&gt;In AI-first products, quality isn’t a checkbox. It’s what users feel when the system doesn’t let them down.&lt;/p&gt;



&lt;p&gt;In a world where AI capabilities are increasingly commoditised, the product that wins isn’t necessarily the most powerful one. It’s the one users trust. The one that behaves consistently. The one that doesn’t surprise them in ways that erode confidence.&lt;/p&gt;



&lt;p&gt;Quality engineering in an AI-first world is ultimately about building that confidence, not as a feature or as a compliance requirement, but as a competitive advantage. That’s what users feel when the system doesn’t let them down. And that feeling is what keeps them coming back.&lt;/p&gt;



&lt;hr&gt;



&lt;h2&gt;What I Want You to Do With This&lt;/h2&gt;



&lt;p&gt;I didn’t write this to be read once and filed away. I wrote it to be used.&lt;/p&gt;



&lt;p&gt;If something here resonates, take it into your next team conversation. Use it as a lens for where your quality practice has gaps. Challenge the parts you disagree with! I mean that genuinely, because the best frameworks get sharper through friction, not consensus.&lt;/p&gt;



&lt;p&gt;If your organisation is figuring out what AI-first quality engineering actually looks like in practice, I’d love to talk. These commitments are a starting point, not a finished answer.&lt;/p&gt;



&lt;p&gt;And if this feels like the beginning of something bigger… well. It might be.&lt;/p&gt;



&lt;hr&gt;



&lt;p&gt;&lt;em&gt;The IQL Manifesto is part of the &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://leadtestinclude.com/intelligent-quality-leadership/&quot;&gt;Intelligent Quality Leadership&lt;/a&gt; series on leadtestinclude.com. If you’re new to the series, start at the beginning.&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;&lt;em&gt;Want to talk about any of this? Book time with me at &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://calendly.com/simon-leadtestinclude/30min&quot;&gt;calendly.com/simon-leadtestinclude/30min&lt;/a&gt; or find me on &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://linkedin.com/in/leadtestinclude&quot;&gt;LinkedIn&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</summary><author><name>siprior</name></author><source gr:stream-id="feed/https://priorsworld.wordpress.com/feed/"><id>tag:google.com,2005:reader/feed/https://priorsworld.wordpress.com/feed/</id><title type="html">My World of Testing</title><link rel="alternate" href="https://leadtestinclude.com" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778580527000"><id gr:original-id="https://medium.com/p/5e1b2099c87b">tag:google.com,2005:reader/item/00000fd500000011</id><category term="quality-assurance"></category><category term="testing"></category><category term="qa-automation"></category><category term="software-testing"></category><category term="qa"></category><title type="html">The Automation Island Trap: Why QAs Who Only Automate Stop Growing</title><published>2026-05-12T10:08:47Z</published><updated>2026-05-12T10:08:47Z</updated><link rel="alternate" href="https://medium.com/@marinacruzjordao/the-automation-island-trap-why-qas-who-only-automate-stop-growing-5e1b2099c87b?source=rss-a6f7e93e4f93------2" type="text/html"></link><summary type="html">&lt;p&gt;There’s a pattern I keep seeing across different teams and companies, regardless of size or maturity.&lt;/p&gt;&lt;p&gt;A QA engineer learns automation. At first, it’s a huge step forward. They feel empowered, more technical, more aligned with development. They start contributing faster, building pipelines, integrating tests into CI/CD. It feels like progress and it is. But then something and happens.&lt;/p&gt;&lt;p&gt;They begin to spend less time understanding the product. They stop participating actively in refinement sessions. Exploratory testing becomes rare. Conversations with product managers decrease. Over time, their entire contribution becomes centred around writing and maintaining automated tests. Without realizing it, they isolate themselves.&lt;/p&gt;&lt;p&gt;They move into what I call the automation island, a place where activity is high, but impact is limited.&lt;/p&gt;&lt;p&gt;From the outside, everything looks fine. There are commits, pipelines are running, and dashboards show execution results. But underneath that surface, something critical is missing: real quality ownership.&lt;/p&gt;&lt;h3&gt;&lt;strong&gt;The Illusion of Value&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;Let’s be clear: automation is valuable. Critical, even. Automation gives a strong sense of productivity. You can see tangible outputs: test scripts, reports, pass/fail results. It creates the impression that quality is being actively managed. However, this is where many teams and many QAs fall into a trap. They confuse activity with value.&lt;/p&gt;&lt;p&gt;A large test suite does not automatically mean better quality. High coverage numbers do not guarantee meaningful validation. Fast execution does not imply that the right things are being tested.&lt;/p&gt;&lt;p&gt;When automation is not guided by a deeper understanding of the system, it often ends up validating assumptions instead of challenging them.&lt;/p&gt;&lt;p&gt;You start to see situations where:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Tests consistently pass, but production issues still occur&lt;/li&gt;&lt;li&gt;Failures are treated as “flaky” instead of being investigated&lt;/li&gt;&lt;li&gt;Test results are reviewed mechanically, without insight&lt;/li&gt;&lt;li&gt;The team trusts the automation less over time&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In these cases, automation is not improving quality, it’s creating a false sense of confidence. The root cause is not the automation itself. It’s the lack of critical thinking and product awareness behind it.&lt;/p&gt;&lt;h3&gt;&lt;strong&gt;QA Is a Thinking Role, Not a Toolset&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;At its core, QA is not defined by tools, frameworks, or even test cases. It is defined by how you think about quality. A strong QA engineer constantly builds a mental model of the system:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;How users interact with it&lt;/li&gt;&lt;li&gt;Where complexity exists&lt;/li&gt;&lt;li&gt;What could realistically go wrong&lt;/li&gt;&lt;li&gt;What failures would have the biggest impact&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This kind of thinking cannot be replaced by automation. It requires curiosity, questioning, and continuous learning. When QAs limit themselves to automation, they risk reducing their role to execution. They follow predefined scenarios, validate expected outcomes, and move on to the next task. But real quality work happens in the spaces where things are unclear:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;When requirements are ambiguous&lt;/li&gt;&lt;li&gt;When edge cases are not documented&lt;/li&gt;&lt;li&gt;When user behavior is unpredictable&lt;/li&gt;&lt;li&gt;When systems interact in unexpected ways&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Automation can support this work, but it cannot lead it. Without this broader perspective, automation becomes efficient but shallow.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;The Automation-Only Anti-Pattern&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;When a QA operates only within automation, their workflow tends to become reactive and limited. Typically, they receive a feature after development is complete. Their task is to automate validation based on what has already been defined and implemented. The focus is often on expected flows, the scenarios that developers and product teams already had in mind. This creates a narrow validation scope.&lt;/p&gt;&lt;p&gt;What’s missing is the ability to influence quality earlier in the process. By the time automation begins, many important decisions have already been made, sometimes incorrectly. Without early involvement:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Risks are not identified when they are easiest to address&lt;/li&gt;&lt;li&gt;Gaps in requirements go unnoticed&lt;/li&gt;&lt;li&gt;Edge cases are discovered too late&lt;/li&gt;&lt;li&gt;Design issues become expensive to fix&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In this model, QA becomes a checkpoint at the end, rather than a driver of quality throughout the process. Automation reinforces this if it’s treated as the primary responsibility instead of one part of a broader strategy.&lt;/p&gt;&lt;h3&gt;&lt;strong&gt;Why This Limits Your Growth&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;From a professional development perspective, staying in the automation island creates a very specific kind of limitation. You become highly skilled in a narrow area, writing and maintaining automated tests, but you miss the opportunity to develop the broader capabilities that define senior QA engineers. These include:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Understanding business context and user value&lt;/li&gt;&lt;li&gt;Making risk-based decisions&lt;/li&gt;&lt;li&gt;Communicating effectively with stakeholders&lt;/li&gt;&lt;li&gt;Influencing product and technical direction&lt;/li&gt;&lt;li&gt;Adapting testing strategies to different scenarios&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Over time, this creates dependency on tools and frameworks. Your value becomes tied to specific technologies rather than transferable skills. And this is risky. Tools evolve quickly, frameworks change and AI is now part of day-to-day QA work. New approaches emerge. If your expertise is too tightly coupled to a specific implementation, it becomes harder to adapt.&lt;/p&gt;&lt;p&gt;On the other hand, QAs who develop strong analytical and product-focused skills remain relevant regardless of the tools they use. Because they don’t just execute tests, but they shape how quality is approached.&lt;/p&gt;&lt;h3&gt;&lt;strong&gt;The Real Role of Automation&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;Automation is most powerful when it is used intentionally, as part of a well-defined strategy. Its purpose is not to replace thinking, but to amplify it. When guided correctly, automation allows teams to:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Reduce repetitive manual effort&lt;/li&gt;&lt;li&gt;Increase the speed of feedback&lt;/li&gt;&lt;li&gt;Ensure consistency in validation&lt;/li&gt;&lt;li&gt;Scale testing across environments and configurations&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;But these benefits only materialize when automation is aligned with real needs. That alignment comes from:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Understanding what matters most to the business&lt;/li&gt;&lt;li&gt;Identifying high-risk areas&lt;/li&gt;&lt;li&gt;Prioritizing stability and long-term value&lt;/li&gt;&lt;li&gt;Designing tests that provide meaningful insights&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Without this foundation, automation becomes noise, fast, scalable, but not particularly useful.&lt;/p&gt;&lt;h3&gt;&lt;strong&gt;What High-Impact QAs Do Differently&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;QAs who consistently bring value approach their role very differently. They don’t see automation as their identity. Instead, they see it as one of many tools they use to ensure quality. They actively participate in the entire lifecycle of a feature.&lt;/p&gt;&lt;p&gt;They engage early, asking questions during refinement and planning. This allows them to uncover risks and ambiguities before development even begins.&lt;/p&gt;&lt;p&gt;They think in terms of impact, not just coverage. Instead of trying to automate as much as possible, they focus on what actually matters: the flows and scenarios that could affect users and the business. They combine different testing approaches. Automation is complemented by exploratory testing, data analysis, and collaboration with other team members.&lt;/p&gt;&lt;p&gt;They also take ownership of outcomes. When something fails, whether in tests or in production, they investigate deeply, looking for root causes and systemic improvements. This holistic approach is what differentiates a QA who executes tasks from a QA who drives quality.&lt;/p&gt;&lt;h3&gt;&lt;strong&gt;Breaking Out of the Automation Island&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;Moving away from an automation-only mindset doesn’t require a complete reset. It starts with small, intentional changes in how you approach your work.&lt;/p&gt;&lt;p&gt;Begin by increasing your involvement in the earlier stages of development. Even one meaningful contribution during a refinement session can shift your perspective significantly. Reintroduce exploratory testing into your workflow. Before automating a feature, spend time understanding how it behaves, where it might break, and what feels unclear. Challenge assumptions. Instead of taking requirements at face value, ask questions about edge cases, constraints, and user behavior. Collaborate more closely with developers and product managers. Quality improves when perspectives are shared, not isolated. And most importantly, shift your mindset. Move from: “What tests should I automate?” To: “How do I ensure this feature delivers real value and works reliably in the real world?”&lt;/p&gt;&lt;p&gt;This shift changes everything, including how you use automation.&lt;/p&gt;&lt;h3&gt;&lt;strong&gt;How to Get Out of the Automation Island (Step by Step)&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;Breaking out of the automation island is not about abandoning automation. It’s about repositioning it within a broader, more impactful QA strategy. The shift doesn’t happen overnight, but it can be built deliberately through small, consistent changes in how you work.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;1. Reconnect With the Product&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Start by rebuilding your understanding of the system beyond test cases. Instead of jumping straight into automation when a feature is ready, spend time exploring it like a user would. Navigate through different flows, try unexpected inputs, and observe how the system behaves under less obvious conditions.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt; You receive a new checkout feature to automate.&lt;br&gt; Instead of immediately scripting the happy path:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Try different payment methods&lt;/li&gt;&lt;li&gt;Interrupt the flow midway&lt;/li&gt;&lt;li&gt;Use invalid or borderline data&lt;/li&gt;&lt;li&gt;Switch devices or browsers&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This often reveals gaps that were never documented and would never be covered by predefined automated tests.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2. Shift Left: Get Involved Earlier&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;One of the biggest differences between average and high-impact QAs is &lt;em&gt;when&lt;/em&gt; they engage. Don’t wait for development to finish. Join refinement sessions with a clear purpose: identify risks before they become defects.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt; During a refinement for a new user registration flow, instead of passively listening:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Ask what happens if the email is already registered&lt;/li&gt;&lt;li&gt;Question password constraints and edge cases&lt;/li&gt;&lt;li&gt;Clarify expected behavior for slow network scenarios&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This kind of input can prevent entire categories of bugs before a single line of code is written.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3. Introduce Risk-Based Thinking&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Not everything should be automated. Not everything deserves the same level of attention. Start prioritizing based on impact and probability, not just feasibility.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt; You have limited time in a sprint. Instead of automating 10 low-risk scenarios:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Focus on 2–3 critical user journeys (e.g., login, payment, data persistence)&lt;/li&gt;&lt;li&gt;Identify where failures would hurt the business most&lt;/li&gt;&lt;li&gt;Ensure those paths are deeply validated (automation + exploratory)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This increases the &lt;em&gt;value density&lt;/em&gt; of your work.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;4. Combine Automation With Exploration&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Automation is excellent for consistency, but exploration is where real insights emerge. Before writing automation, explore the feature. After automation, revisit it when changes happen.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt; A test suite for a search feature is fully automated and passing.&lt;br&gt; Instead of trusting it blindly:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Perform exploratory testing with unusual queries&lt;/li&gt;&lt;li&gt;Test performance under load&lt;/li&gt;&lt;li&gt;Try edge cases like special characters or empty inputs&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This often exposes issues that automation alone misses.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;5. Turn Failures Into Learning Opportunities&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;One clear sign of being stuck in automation island is ignoring failures or labelling them as “flaky” without investigation. High-impact QAs treat every failure as a signal.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt; An automated test fails intermittently.&lt;br&gt; Instead of retrying or disabling it:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Investigate logs and system behavior&lt;/li&gt;&lt;li&gt;Identify if it’s a timing issue, environment instability, or real defect&lt;/li&gt;&lt;li&gt;Collaborate with developers to fix the root cause&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This improves both the system and the credibility of your automation.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;6. Increase Collaboration, Reduce Isolation&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Automation-heavy QAs often work in isolation. Breaking out of the island requires intentional collaboration. Quality is a shared responsibility, not a QA-only function.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Pair with developers to review testability of features&lt;/li&gt;&lt;li&gt;Discuss edge cases with product managers&lt;/li&gt;&lt;li&gt;Share insights from exploratory testing with the team&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;These interactions expand your perspective and increase your influence.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;7. Redefine Your Success Metrics&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;If your success is measured only by:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Number of automated tests&lt;/li&gt;&lt;li&gt;Execution speed&lt;/li&gt;&lt;li&gt;Coverage percentage&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;You will naturally stay on the island. Shift your focus toward impact-driven metrics:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Bugs prevented before production&lt;/li&gt;&lt;li&gt;Risks identified early&lt;/li&gt;&lt;li&gt;Critical issues detected before release&lt;/li&gt;&lt;li&gt;Confidence level of the team in the product&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt; Instead of saying:&lt;br&gt; “I automated 20 test cases this sprint”&lt;/p&gt;&lt;p&gt;Aim to say:&lt;br&gt; “I identified a critical edge case in checkout that could have caused revenue loss, and ensured it’s now covered and monitored.”&lt;/p&gt;&lt;p&gt;&lt;strong&gt;8. Use Automation as an Amplifier, Not a Crutch&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Once you shift your mindset, automation becomes significantly more powerful. You’re no longer automating everything. You’re automating the &lt;em&gt;right things&lt;/em&gt;, for the &lt;em&gt;right reasons&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt; After identifying high-risk scenarios through exploration and refinement:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Automate those scenarios for fast regression feedback&lt;/li&gt;&lt;li&gt;Keep tests maintainable and meaningful&lt;/li&gt;&lt;li&gt;Continuously evaluate if they still provide value&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This creates a lean, trustworthy, and high-impact automation suite.&lt;/p&gt;&lt;h3&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;Automation is one of the most powerful capabilities a QA can have. But on its own, it is not enough. Because without context, it becomes mechanical. If there is no involvement, it becomes isolated. And without critical thinking, it becomes superficial.&lt;/p&gt;&lt;p&gt;The most effective QAs don’t define themselves by the tests they write. They define themselves by the impact they have on the product and the process. They move across the entire development lifecycle, combining strategy, exploration, and automation to deliver real quality.&lt;/p&gt;&lt;p&gt;If you want to grow as a QA engineer, don’t aim to be the fastest at writing scripts. Aim to be the one who understands quality the deepest and improve the process and uses automation to amplify that understanding.&lt;/p&gt;&lt;figure&gt;&lt;img class=&quot;bqrUnknownImgSize&quot; alt src=&quot;https://cdn-images-1.medium.com/max/640/1*oKYF-1ITtYxf8RTXA13V8A.jpeg&quot;&gt;&lt;/figure&gt;</summary><author><name>MarinaJordao</name></author><source gr:stream-id="feed/https://medium.com/feed/@marinacruzjordao"><id>tag:google.com,2005:reader/feed/https://medium.com/feed/@marinacruzjordao</id><title type="html">Stories by MarinaJordao on Medium</title><link rel="alternate" href="https://medium.com/@marinacruzjordao?source=rss-a6f7e93e4f93------2" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778569200000"><id gr:original-id="http://emnaayadi.wordpress.com/?p=4564">tag:google.com,2005:reader/item/000006a000000069</id><category term="Blogging"></category><category term="testing"></category><category term="assumptions in testing"></category><category term="human factors in testing"></category><category term="learning from failure"></category><category term="quality engineering"></category><category term="real life bugs"></category><category term="resilience in tech"></category><category term="software testing"></category><category term="systems thinking"></category><category term="Test Management"></category><category term="tester mindset"></category><category term="user experience"></category><title type="html">A tester’s story about assumptions, resilience, and learning from real‑life bugs</title><published>2026-05-12T07:00:00Z</published><updated>2026-05-12T07:00:00Z</updated><link rel="alternate" href="https://emnaayadi.wordpress.com/2026/05/12/tester-story-assumptions-resilience/" type="text/html"></link><summary type="html">&lt;h1&gt;&lt;img class=&quot;bqrUnknownImgSize&quot; src=&quot;https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f686.png&quot; alt=&quot;🚆&quot; style=&quot;height: 1em&quot;&gt; You know that moment when everything looks right… until it’s completely wrong?&lt;/h1&gt;



&lt;p&gt;That happened to me on a German train one morning at 9:47.&lt;/p&gt;



&lt;p&gt;The setup looked perfect.&lt;/p&gt;



&lt;ul&gt;
&lt;li&gt;Ticket bought&lt;/li&gt;



&lt;li&gt;Track 103 clearly marked&lt;/li&gt;



&lt;li&gt;Airport sign displayed&lt;/li&gt;



&lt;li&gt;People with suitcases all around&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Every signal said this was the right way.&lt;/p&gt;



&lt;p&gt;So when the train arrived, I stepped in with confidence.&lt;/p&gt;



&lt;p&gt;Even Google Maps agreed.&lt;br&gt;Even the people inside agreed.&lt;/p&gt;



&lt;p&gt;One minute later, something felt off.&lt;/p&gt;



&lt;p&gt;No airport appeared on the list of upcoming stations.&lt;/p&gt;



&lt;p&gt;That was the moment reality caught up with confidence.&lt;/p&gt;



&lt;p&gt;This was &lt;strong&gt;S1&lt;/strong&gt;, not &lt;strong&gt;S8 or S9&lt;/strong&gt;.&lt;/p&gt;



&lt;p&gt;And suddenly, I wasn’t alone in the mistake.&lt;br&gt;The train was full of people who, just like me, had trusted the same signals and reached the same wrong conclusion.&lt;/p&gt;



&lt;p&gt;Thirty minutes lost fixing it.&lt;br&gt;Going in the wrong direction.&lt;br&gt;Coming back to the starting point.&lt;br&gt;Waiting again for the correct train.&lt;/p&gt;



&lt;p&gt;A small chaos.&lt;/p&gt;



&lt;p&gt;But also a familiar one.&lt;/p&gt;



&lt;p&gt;Because this is exactly what happens in our work as testers.&lt;/p&gt;



&lt;h1&gt;A real‑life bug uncovered in production&lt;/h1&gt;



&lt;p&gt;As testers and test managers, we deal with bugs all the time.&lt;br&gt;But this one didn’t come from code.&lt;/p&gt;



&lt;p&gt;It came from assumptions.&lt;/p&gt;



&lt;p&gt;Everything appeared aligned.&lt;br&gt;The environment looked correct.&lt;br&gt;The signals matched expectations.&lt;/p&gt;



&lt;p&gt;And yet, the conclusion was wrong.&lt;/p&gt;



&lt;p&gt;That’s how many defects escape into production.&lt;br&gt;Not because people are careless.&lt;br&gt;Not because processes don’t exist.&lt;/p&gt;



&lt;p&gt;But because confidence replaces verification when things look familiar.&lt;/p&gt;



&lt;h1&gt;What this train taught me as a tester&lt;/h1&gt;



&lt;p&gt;In testing, we are trained to look for what can go wrong.&lt;br&gt;Still, we are human.&lt;/p&gt;



&lt;p&gt;We trust patterns.&lt;br&gt;We trust systems.&lt;br&gt;We trust crowds.&lt;/p&gt;



&lt;p&gt;This moment reminded me of lessons we all know, but sometimes forget.&lt;/p&gt;



&lt;ul&gt;
&lt;li&gt;Anticipation matters, even when everything seems aligned&lt;/li&gt;



&lt;li&gt;Asking questions matters, but asking the right source matters more&lt;/li&gt;



&lt;li&gt;Plans never equal reality, adaptability is part of quality&lt;/li&gt;



&lt;li&gt;Resilience is essential, things will break and we still have to move forward&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Fixing the mistake wasn’t elegant (lost 30 minutes from my expected arrival time to airport)&lt;br&gt;But it worked.&lt;/p&gt;



&lt;p&gt;And that’s often what resilience looks like in real life and in testing.&lt;/p&gt;



&lt;h1&gt;Bonus reflections from the platform called life&lt;/h1&gt;



&lt;p&gt;Standing on that platform, waiting for the correct train, I kept thinking about testing principles playing out in front of me.&lt;/p&gt;



&lt;ul&gt;
&lt;li&gt;UX matters, when many users fail, the system failed&lt;/li&gt;



&lt;li&gt;Crowd confidence is not validation, everyone can be wrong together&lt;/li&gt;



&lt;li&gt;Edge cases are not rare, they are real life&lt;/li&gt;



&lt;li&gt;Communication equals quality, unclear information creates chaos&lt;/li&gt;



&lt;li&gt;User confusion is not a mistake, it is a signal&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;No one on that train was negligent.&lt;br&gt;They were navigating a system that allowed ambiguity.&lt;/p&gt;



&lt;p&gt;Just like many users do with our products.&lt;/p&gt;



&lt;h1&gt;Resilience is part of quality&lt;/h1&gt;



&lt;p&gt;In testing, resilience isn’t just about handling failures in production.&lt;br&gt;It’s about staying calm when reality diverges from expectations.&lt;/p&gt;



&lt;p&gt;You adapt.&lt;br&gt;You reroute.&lt;br&gt;You keep going.&lt;/p&gt;



&lt;p&gt;That day, the wrong train didn’t ruin the journey.&lt;br&gt;It enriched it.&lt;/p&gt;



&lt;p&gt;It reminded me why testing is not just about tools, frameworks, or processes.&lt;/p&gt;



&lt;p&gt;It’s about thinking.&lt;br&gt;Observing.&lt;br&gt;Questioning.&lt;br&gt;Empathizing.&lt;/p&gt;



&lt;h1&gt;Final thought&lt;/h1&gt;



&lt;blockquote&gt;
&lt;p&gt;Real life is the most honest test environment.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;p&gt;And sometimes, the wrong train is the best reminder of why testers exist.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;#ExploreWithEmna&lt;/strong&gt;&lt;/p&gt;</summary><author><name>Emna Ayadi</name></author><source gr:stream-id="feed/https://emnaayadi.com/feed/"><id>tag:google.com,2005:reader/feed/https://emnaayadi.com/feed/</id><title type="html">Emna Ayadi</title><link rel="alternate" href="https://emnaayadi.wordpress.com" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778557560000"><id gr:original-id="https://medium.com/p/be51d12f5e86">tag:google.com,2005:reader/item/00000a87000000b6</id><category term="software-testing"></category><category term="quality-assurance"></category><category term="software-engineering"></category><category term="automation-testing"></category><category term="test-automation"></category><title type="html">Test Automation Made You Faster. It Also Made You Blind.</title><published>2026-05-12T03:46:00Z</published><updated>2026-05-12T03:46:00Z</updated><link rel="alternate" href="https://manishsaini74.medium.com/test-automation-made-you-faster-it-also-made-you-blind-be51d12f5e86?source=rss-2afcb904d789------2" type="text/html"></link><summary type="html">&lt;div&gt;&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://manishsaini74.medium.com/test-automation-made-you-faster-it-also-made-you-blind-be51d12f5e86?source=rss-2afcb904d789------2&quot;&gt;&lt;img class=&quot;bqrUnknownImgSize&quot; src=&quot;https://cdn-images-1.medium.com/max/1536/1*KHos82D6SYvW1iFsnI2_LQ.png&quot; width=&quot;1536&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Your pipeline is green.&lt;/p&gt;&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://manishsaini74.medium.com/test-automation-made-you-faster-it-also-made-you-blind-be51d12f5e86?source=rss-2afcb904d789------2&quot;&gt;Continue reading on Medium »&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;</summary><author><name>Manish Saini</name></author><source gr:stream-id="feed/https://manishsaini74.medium.com/feed"><id>tag:google.com,2005:reader/feed/https://manishsaini74.medium.com/feed</id><title type="html">Stories by Manish Saini on Medium</title><link rel="alternate" href="https://medium.com/@manishsaini74?source=rss-2afcb904d789------2" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778557202000"><id gr:original-id="https://scrolltest.com/manual-testers-ai-agents-service-companies/">tag:google.com,2005:reader/item/00000444000001bc</id><category term="AI Testing"></category><category term="Career Guide"></category><category term="Testing"></category><category term="AI Agents"></category><category term="API Testing with Playwright"></category><category term="career transition"></category><category term="Crack Any Manual Testing Interview"></category><category term="service companies"></category><title type="html">Manual Testers and AI Agents: Why Service Company QA Jobs Are Disappearing in 2026</title><published>2026-05-12T03:40:02Z</published><updated>2026-05-12T03:40:02Z</updated><link rel="alternate" href="https://scrolltest.com/manual-testers-ai-agents-service-companies/" type="text/html"></link><summary type="html">&lt;p&gt;AI agents are replacing manual testers in Indian IT service companies. Here is the data on salary stagnation, AI tool adoption, and the 90-day escape plan for QA engineers who want to survive.&lt;/p&gt;
&lt;p&gt;The post &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; rel=&quot;nofollow&quot; href=&quot;https://scrolltest.com/manual-testers-ai-agents-service-companies/&quot;&gt;Manual Testers and AI Agents: Why Service Company QA Jobs Are Disappearing in 2026&lt;/a&gt; appeared first on &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; rel=&quot;nofollow&quot; href=&quot;https://scrolltest.com&quot;&gt;Software Testing &amp;amp; Automation&lt;/a&gt;.&lt;/p&gt;</summary><author><name>Promode</name></author><source gr:stream-id="feed/https://scrolltest.com/feed/"><id>tag:google.com,2005:reader/feed/https://scrolltest.com/feed/</id><title type="html">Software Testing &amp; Automation</title><link rel="alternate" href="https://scrolltest.com" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778544000000"><id gr:original-id="https://qualitywithmillan.github.io/blog/ai/spec-driven-development-qas-north-star">tag:google.com,2005:reader/item/000008500000004a</id><category term="blog"></category><category term="quality-engineering"></category><category term="ai"></category><category term="qa"></category><category term="ai-testing"></category><category term="llm-evaluation"></category><category term="guardrails"></category><category term="prompt-engineering"></category><category term="ai-native"></category><category term="spec-driven-development"></category><category term="model-evaluation"></category><category term="human-in-the-loop"></category><title type="html">Spec-Driven Development: QA’s North Star in AI-Native Teams</title><published>2026-05-12T00:00:00Z</published><updated>2026-05-12T00:00:00Z</updated><link rel="alternate" href="https://qualitywithmillan.github.io/blog/ai/spec-driven-development-qas-north-star.html" type="text/html"></link><summary type="html">&lt;p&gt;
 Written by -

    
    
    
        &lt;strong&gt;Millan Kaul&lt;/strong&gt;
    

&lt;/p&gt;

&lt;p&gt;Spec-driven development (SDD) positions QA as the enforcer of executable specs, your north star for AI reliability. In AI-native workflows, QA shifts from reactive bugs to proactive spec validation, catching hallucinations before deploy.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Opinion&lt;/strong&gt;: Without QA owning SDD, AI ships “good enough” disasters; with it, specs become living tests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;article-1-dgnGJTHAhtXX5LaUPfcKVcfm8-qas-pivotal-role&quot;&gt;QA’s Pivotal Role&lt;/h3&gt;

&lt;p&gt;QA authors/validates &lt;code class=&quot;language-plaintext language-highlighter-rouge&quot;&gt;specs as contracts&lt;/code&gt; (inputs, outputs, edges), then verifies AI outputs match.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Tools like GitHub Spec Kit auto-gen tests from specs, &lt;code class=&quot;language-plaintext language-highlighter-rouge&quot;&gt;reducing manual toil by 40%.&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;QA metrics: Schema compliance, &lt;code class=&quot;language-plaintext language-highlighter-rouge&quot;&gt;edge coverage &amp;gt;90%.&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img class=&quot;bqrUnknownImgSize&quot; src=&quot;https://qualitywithmillan.github.io/assets/images/2026/05/sdd_qa_spec.png&quot; alt=&quot;Image 01&quot;&gt;
&lt;/p&gt;

&lt;h3 id=&quot;article-1-dgnGJTHAhtXX5LaUPfcKVcfm8-qa-centric-workflow&quot;&gt;QA-Centric Workflow&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;QA Writes Spec&lt;/strong&gt;: YAML/Markdown with assertions (e.g., “no PII, confidence &amp;gt;0.8”).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;AI Task Breakdown&lt;/strong&gt;: Prompt-engineered plans, QA reviews.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Implement + QA Gate&lt;/strong&gt;: Code/prompts must pass spec-evals (LLM-judge, unit tests).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;QA Iteration&lt;/strong&gt;: Human-in-loop for failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;article-1-dgnGJTHAhtXX5LaUPfcKVcfm8-sample-qa-spec--pii-safe-api&quot;&gt;Sample: QA Spec → PII-Safe API&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;QA spec enforces no PII leak.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div&gt;&lt;div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;# qa_spec.yaml&lt;/span&gt;

&lt;span&gt;spec&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
  &lt;span&gt;endpoint&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;/users&lt;/span&gt;
  &lt;span&gt;input&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;string@format=email&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;string&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;
  &lt;span&gt;output&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;string&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;string&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;  &lt;span&gt;# No email echo!&lt;/span&gt;
  &lt;span&gt;assertions&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
    &lt;span&gt;-&lt;/span&gt; &lt;span&gt;no_pii&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;true&lt;/span&gt;  &lt;span&gt;# QA guardrail hook&lt;/span&gt;
    &lt;span&gt;-&lt;/span&gt; &lt;span&gt;schema_match&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;100%&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;article-1-dgnGJTHAhtXX5LaUPfcKVcfm8-fastapi--qa-guardrail&quot;&gt;FastAPI + QA Guardrail&lt;/h3&gt;

&lt;div&gt;&lt;div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;# &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://test.py&quot;&gt;test.py&lt;/a&gt;
&lt;/span&gt;
&lt;span&gt;from&lt;/span&gt; &lt;span&gt;fastapi&lt;/span&gt; &lt;span&gt;import&lt;/span&gt; &lt;span&gt;FastAPI&lt;/span&gt;
&lt;span&gt;from&lt;/span&gt; &lt;span&gt;pydantic&lt;/span&gt; &lt;span&gt;import&lt;/span&gt; &lt;span&gt;BaseModel&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;EmailStr&lt;/span&gt;
&lt;span&gt;from&lt;/span&gt; &lt;span&gt;nemoguardrails&lt;/span&gt; &lt;span&gt;import&lt;/span&gt; &lt;span&gt;LLMRails&lt;/span&gt;  &lt;span&gt;# QA layer
&lt;/span&gt;
&lt;span&gt;app&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;FastAPI&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;
&lt;span&gt;rails&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;LLMRails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;from_config&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;./qa_config&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;  &lt;span&gt;#Spec + PII rails
&lt;/span&gt;
&lt;span&gt;class&lt;/span&gt; &lt;span&gt;User&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;BaseModel&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;
    &lt;span&gt;email&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;EmailStr&lt;/span&gt;
    &lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;str&lt;/span&gt;

&lt;span&gt;@&lt;/span&gt;&lt;span&gt;app&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;/users&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
&lt;span&gt;async&lt;/span&gt; &lt;span&gt;def&lt;/span&gt; &lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;user&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;
    &lt;span&gt;# QA Pre: Spec input validation
&lt;/span&gt;    &lt;span&gt;if&lt;/span&gt; &lt;span&gt;not&lt;/span&gt; &lt;span&gt;rails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;process_input&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;model_dump&lt;/span&gt;&lt;span&gt;()):&lt;/span&gt;  &lt;span&gt;#PII/block
&lt;/span&gt;        &lt;span&gt;raise&lt;/span&gt; &lt;span&gt;HTTPException&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;400&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;&amp;quot;PII detected&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
    &lt;span&gt;# LLM? Or logic, post-validate output
&lt;/span&gt;    &lt;span&gt;result&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;&lt;span&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;user&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;user&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;  &lt;span&gt;#No email!
&lt;/span&gt;    &lt;span&gt;if&lt;/span&gt; &lt;span&gt;not&lt;/span&gt; &lt;span&gt;rails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;process_output&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;  &lt;span&gt;#Spec schema check
&lt;/span&gt;        &lt;span&gt;raise&lt;/span&gt; &lt;span&gt;HTTPException&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;500&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;&amp;quot;Spec violation&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
    &lt;span&gt;return&lt;/span&gt; &lt;span&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Run QA tests: &lt;code class=&quot;language-plaintext language-highlighter-rouge&quot;&gt;pytest --spec qa_spec.yaml&lt;/code&gt; for 100% alignment.&lt;/p&gt;

&lt;h2 id=&quot;article-1-dgnGJTHAhtXX5LaUPfcKVcfm8-qa-wins&quot;&gt;QA Wins&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Cuts drift 50% via evals.&lt;/li&gt;
  &lt;li&gt;Guardrails as spec extensions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;article-1-dgnGJTHAhtXX5LaUPfcKVcfm8-qa-checklist&quot;&gt;QA Checklist&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Spec coverage: Edges/PII.&lt;/li&gt;
  &lt;li&gt;Auto-eval: &amp;gt;95% pass.&lt;/li&gt;
  &lt;li&gt;CI/CD gate: QA signoff.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;QA makes SDD unbreakable—AI follows specs, not whims.&lt;/p&gt;</summary><author><name>Millan Kaul</name></author><source gr:stream-id="feed/https://qualitywithmillan.github.io/feed.xml"><id>tag:google.com,2005:reader/feed/https://qualitywithmillan.github.io/feed.xml</id><title type="html">Quality With Millan</title><link rel="alternate" href="https://qualitywithmillan.github.io/" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778536800000"><id gr:original-id="https://scrolltest.com/?p=7339">tag:google.com,2005:reader/item/00000444000001bb</id><category term="Career Guide"></category><category term="Testing"></category><title type="html">QA Metrics That Actually Matter: 12 KPIs to Replace Vanity Metrics</title><published>2026-05-11T22:00:00Z</published><updated>2026-05-11T22:00:00Z</updated><link rel="alternate" href="https://scrolltest.com/qa-metrics-that-matter-replace-vanity-kpis/" type="text/html"></link><summary type="html">&lt;p&gt;Stop tracking vanity metrics. 12 QA KPIs that actually indicate quality: escaped defect rate, mutation score, release confidence score, and cost of defects.&lt;/p&gt;
&lt;p&gt;The post &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; rel=&quot;nofollow&quot; href=&quot;https://scrolltest.com/qa-metrics-that-matter-replace-vanity-kpis/&quot;&gt;QA Metrics That Actually Matter: 12 KPIs to Replace Vanity Metrics&lt;/a&gt; appeared first on &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; rel=&quot;nofollow&quot; href=&quot;https://scrolltest.com&quot;&gt;Software Testing &amp;amp; Automation&lt;/a&gt;.&lt;/p&gt;</summary><author><name>Pramod Dutta</name></author><source gr:stream-id="feed/https://scrolltest.com/feed/"><id>tag:google.com,2005:reader/feed/https://scrolltest.com/feed/</id><title type="html">Software Testing &amp; Automation</title><link rel="alternate" href="https://scrolltest.com" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778532742666"><id gr:original-id="tag:blogger.com,1999:blog-3868566217808655382.post-6634369418291478580">tag:google.com,2005:reader/item/000002c500000464</id><category term="Playwright"></category><title type="html">Pramod Dutta discusses 7 Playwright features senior SDETs use daily</title><published>2026-05-11T20:52:22Z</published><updated>2026-05-11T20:52:22Z</updated><link rel="alternate" href="https://www.tjmaher.com/2026/05/pramod-dutta-discusses-7-playwright.html" type="text/html"></link><summary type="html">&lt;div&gt;&lt;b&gt;Pramod Dutta&lt;/b&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://www.linkedin.com/in/pramoddutta/?lipi=urn%3Ali%3Apage%3Ad_flagship3_feed%3BDjFlrRQ1Rtut703aaORrhg%3D%3D&quot;&gt;an SDET from Tekion&lt;/a&gt;, posted on LinkedIn &amp;quot;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://www.linkedin.com/posts/pramoddutta_playwright-testautomation-qa-share-7459464157818466304-MvpP?utm_source=share&amp;amp;utm_medium=member_desktop&amp;amp;rcm=ACoAAAA8wfYBpAyZ0pTDZvsInQZl3dpfWfJ_riU&quot;&gt;7 Playwright features senior SDETs use daily&lt;/a&gt;&amp;quot;. The Playwright features, according to Pramod:&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&amp;quot;→ &lt;b&gt;addLocatorHandler&lt;/b&gt; — auto-dismiss cookie banners, GDPR popups, session modals. One handler. Whole suite cleaner. Stop writing wrapper functions.&lt;br&gt;&amp;quot;→ &lt;b&gt;browser.bind()&lt;/b&gt; — launch one browser. Let your test, Claude Code, and debugger all attach to it simultaneously. The debugging workflow that fixes &amp;quot;works on my machine&amp;quot; forever.&lt;br&gt;&amp;quot;→ &lt;b&gt;URLSearchParams in request.get()&lt;/b&gt; — stop building query strings manually. Eliminates an entire bug class around URL encoding in API tests.&lt;br&gt;&amp;quot;→ &lt;b&gt;expect.toPass() &lt;/b&gt;— polling-based assertions for state that converges over time. The right tool for &amp;apos;this should eventually be true.&amp;apos; Not the same as expect(locator).toBeVisible(). Different problem, different solution.&lt;br&gt;&amp;quot;→ &lt;b&gt;page.requestGC()&lt;/b&gt; — manually trigger garbage collection in the browser. The only Playwright tool that catches memory leaks before production. Used with WeakRef in page.evaluate().&lt;br&gt;&amp;quot;→ &lt;b&gt;--tsconfig flag&lt;/b&gt; — pass a specific tsconfig to Playwright instead of relying on the heuristic. Saves you from &amp;quot;works locally, fails in CI&amp;quot; because of resolved-config differences.&lt;br&gt;&amp;quot;→ &lt;b&gt;webServer.wait regex&lt;/b&gt; — wait until your webserver logs match a pattern, not a fixed port check. The difference between &amp;apos;the server is listening&amp;apos; and &amp;apos;the server is actually ready.&amp;apos;&amp;quot;&lt;br&gt;&lt;br&gt; I scribbled these features down, then turned them over to Claude.ai, my research assistant, to see if he could explain these methods, come up with sample code using them, then add bullet points to the official documentation, source code, and the release notes. &lt;br&gt;&lt;br&gt;... Let&amp;apos;s see how well Claude did explaining them ... &lt;p&gt; &lt;/p&gt;&lt;a name=&quot;article-uQTguXzYXrvAyOKute9SKuDTSjA-more&quot;&gt;&lt;/a&gt;&lt;p&gt;&lt;/p&gt;

&lt;hr&gt;



&lt;h2&gt;
  &lt;code&gt;page.addLocatorHandler()&lt;/code&gt;
  &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/release-notes#version-142&quot; rel=&quot;noopener&quot;&gt;v1.42 — March 2024&lt;/a&gt;
&lt;/h2&gt;

&lt;div&gt;
  Registers a callback that fires whenever a specified locator becomes visible
  and risks blocking a Playwright action. Playwright checks for the locator
  before every actionability check or auto-waiting assertion. When the locator
  is visible, the handler runs first, then the test continues. The handler only
  fires during actions; appearing passively between actions does not trigger it.
  After the handler runs, Playwright waits for the overlay to become hidden
  before proceeding (opt out with &lt;code&gt;noWaitAfter: true&lt;/code&gt;).
  Use the &lt;code&gt;times&lt;/code&gt; option to cap the number of invocations.
  The handler receives the triggering locator as its argument.
&lt;/div&gt;

&lt;h3&gt;Cookie banner (fire-and-forget)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;// Register once, before navigating.&lt;/span&gt;
&lt;span&gt;await&lt;/span&gt; page.&lt;span&gt;addLocatorHandler&lt;/span&gt;(
  page.&lt;span&gt;getByRole&lt;/span&gt;(&lt;span&gt;&amp;apos;heading&amp;apos;&lt;/span&gt;, { name: &lt;span&gt;&amp;apos;You are in control of your cookies.&amp;apos;&lt;/span&gt; }),
  &lt;span&gt;async&lt;/span&gt; () =&amp;gt; {
    &lt;span&gt;await&lt;/span&gt; page.&lt;span&gt;getByRole&lt;/span&gt;(&lt;span&gt;&amp;apos;button&amp;apos;&lt;/span&gt;, { name: &lt;span&gt;&amp;apos;Accept all&amp;apos;&lt;/span&gt; }).&lt;span&gt;click&lt;/span&gt;();
  }
);

&lt;span&gt;// Write the rest of the test normally.&lt;/span&gt;
&lt;span&gt;await&lt;/span&gt; page.&lt;span&gt;goto&lt;/span&gt;(&lt;span&gt;&amp;apos;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://www.example.com/&quot;&gt;https://www.example.com/&lt;/a&gt;&amp;apos;&lt;/span&gt;);
&lt;span&gt;await&lt;/span&gt; page.&lt;span&gt;getByRole&lt;/span&gt;(&lt;span&gt;&amp;apos;link&amp;apos;&lt;/span&gt;, { name: &lt;span&gt;&amp;apos;Shop now&amp;apos;&lt;/span&gt; }).&lt;span&gt;click&lt;/span&gt;();&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Self-removing handler with access to the triggering locator&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;// The handler receives the matched locator; times: 1 auto-removes it.&lt;/span&gt;
&lt;span&gt;const&lt;/span&gt; closeBtn = page.&lt;span&gt;getByLabel&lt;/span&gt;(&lt;span&gt;&amp;apos;Close&amp;apos;&lt;/span&gt;);
&lt;span&gt;await&lt;/span&gt; page.&lt;span&gt;addLocatorHandler&lt;/span&gt;(closeBtn, &lt;span&gt;async&lt;/span&gt; (locator) =&amp;gt; {
  &lt;span&gt;await&lt;/span&gt; locator.&lt;span&gt;click&lt;/span&gt;();
}, { times: &lt;span&gt;1&lt;/span&gt; });

&lt;span&gt;// Remove manually when no longer needed.&lt;/span&gt;
&lt;span&gt;await&lt;/span&gt; page.&lt;span&gt;removeLocatorHandler&lt;/span&gt;(closeBtn);&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Fail fast on an application error overlay&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;// Instead of dismissing, throw immediately so the test does not hang.&lt;/span&gt;
&lt;span&gt;await&lt;/span&gt; page.&lt;span&gt;addLocatorHandler&lt;/span&gt;(
  page.&lt;span&gt;getByRole&lt;/span&gt;(&lt;span&gt;&amp;apos;heading&amp;apos;&lt;/span&gt;, { name: &lt;span&gt;&amp;quot;Sorry, there&amp;apos;s been a problem&amp;quot;&lt;/span&gt; }),
  &lt;span&gt;async&lt;/span&gt; () =&amp;gt; {
    &lt;span&gt;throw new&lt;/span&gt; Error(&lt;span&gt;`Error overlay appeared on &lt;span&gt;${&lt;/span&gt;&lt;span&gt;await&lt;/span&gt; page.&lt;span&gt;url&lt;/span&gt;()&lt;span&gt;}&lt;/span&gt;`&lt;/span&gt;);
  }
);&lt;/code&gt;&lt;/pre&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;th&gt;Resource&lt;/th&gt;&lt;th&gt;Link&lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Official docs&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/api/class-page#page-add-locator-handler&quot; rel=&quot;noopener&quot;&gt;playwright.dev — page.addLocatorHandler()&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Release notes (v1.42)&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/release-notes#version-142&quot; rel=&quot;noopener&quot;&gt;playwright.dev/docs/release-notes#version-142&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GitHub release (v1.42.0)&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/releases/tag/v1.42.0&quot; rel=&quot;noopener&quot;&gt;github.com — Release v1.42.0&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Source code&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/client/page.ts&quot; rel=&quot;noopener&quot;&gt;packages/playwright-core/src/client/page.ts&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr&gt;



&lt;h2&gt;
  &lt;code&gt;browser.bind()&lt;/code&gt;
  &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/release-notes#version-159&quot; rel=&quot;noopener&quot;&gt;v1.59 — April 2026&lt;/a&gt;
&lt;/h2&gt;

&lt;div&gt;
  Binds a launched browser to a named pipe or WebSocket endpoint, making it
  available to &lt;code&gt;playwright-cli&lt;/code&gt;, &lt;code&gt;@playwright/mcp&lt;/code&gt;, and any
  other Playwright client simultaneously. Before v1.59, sharing a browser between
  an MCP agent and a test suite meant two separate processes. Now a single
  &lt;code&gt;browser.bind()&lt;/code&gt; call exposes one browser session to all of them.
  Call &lt;code&gt;browser.unbind()&lt;/code&gt; to stop accepting new connections; existing
  connections keep working. Run &lt;code&gt;playwright-cli show&lt;/code&gt; to open the live
  dashboard listing all bound browsers.
&lt;/div&gt;

&lt;h3&gt;Bind via named pipe (local only)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;import&lt;/span&gt; { chromium } &lt;span&gt;from&lt;/span&gt; &lt;span&gt;&amp;apos;@playwright/test&amp;apos;&lt;/span&gt;;

&lt;span&gt;const&lt;/span&gt; browser = &lt;span&gt;await&lt;/span&gt; chromium.&lt;span&gt;launch&lt;/span&gt;();
&lt;span&gt;const&lt;/span&gt; { endpoint } = &lt;span&gt;await&lt;/span&gt; browser.&lt;span&gt;bind&lt;/span&gt;(&lt;span&gt;&amp;apos;my-session&amp;apos;&lt;/span&gt;, {
  workspaceDir: &lt;span&gt;&amp;apos;/my/project&amp;apos;&lt;/span&gt;,
});
console.&lt;span&gt;log&lt;/span&gt;(&lt;span&gt;&amp;apos;Session bound:&amp;apos;&lt;/span&gt;, endpoint);&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Bind via WebSocket (shareable over a network)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;const&lt;/span&gt; { endpoint: wsEndpoint } = &lt;span&gt;await&lt;/span&gt; browser.&lt;span&gt;bind&lt;/span&gt;(&lt;span&gt;&amp;apos;my-session&amp;apos;&lt;/span&gt;, {
  host: &lt;span&gt;&amp;apos;localhost&amp;apos;&lt;/span&gt;,
  port: &lt;span&gt;0&lt;/span&gt;, &lt;span&gt;// let the OS pick an available port&lt;/span&gt;
});
console.&lt;span&gt;log&lt;/span&gt;(&lt;span&gt;&amp;apos;WebSocket endpoint:&amp;apos;&lt;/span&gt;, wsEndpoint); &lt;span&gt;// ws://localhost:PORT/...&lt;/span&gt;

&lt;span&gt;// A second Playwright client connects to the same browser.&lt;/span&gt;
&lt;span&gt;const&lt;/span&gt; browser2 = &lt;span&gt;await&lt;/span&gt; chromium.&lt;span&gt;connect&lt;/span&gt;(wsEndpoint);
&lt;span&gt;const&lt;/span&gt; page = &lt;span&gt;await&lt;/span&gt; browser2.&lt;span&gt;newPage&lt;/span&gt;();

&lt;span&gt;// Stop accepting new connections when done.&lt;/span&gt;
&lt;span&gt;await&lt;/span&gt; browser.&lt;span&gt;unbind&lt;/span&gt;(&lt;span&gt;&amp;apos;my-session&amp;apos;&lt;/span&gt;);&lt;/code&gt;&lt;/pre&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;th&gt;Resource&lt;/th&gt;&lt;th&gt;Link&lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Official docs&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/api/class-browser#browser-bind&quot; rel=&quot;noopener&quot;&gt;playwright.dev — browser.bind()&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Release notes (v1.59)&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/release-notes#version-159&quot; rel=&quot;noopener&quot;&gt;playwright.dev/docs/release-notes#version-159&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GitHub release (v1.59.0)&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/releases/tag/v1.59.0&quot; rel=&quot;noopener&quot;&gt;github.com — Release v1.59.0&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Source code&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/client/browser.ts&quot; rel=&quot;noopener&quot;&gt;packages/playwright-core/src/client/browser.ts&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr&gt;



&lt;h2&gt;
  &lt;code&gt;URLSearchParams&lt;/code&gt; in &lt;code&gt;request.get()&lt;/code&gt;
  &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/release-notes#version-146&quot; rel=&quot;noopener&quot;&gt;v1.46 — August 2024&lt;/a&gt;
&lt;/h2&gt;

&lt;div&gt;
  The &lt;code&gt;params&lt;/code&gt; option on &lt;code&gt;APIRequestContext&lt;/code&gt; methods
  (&lt;code&gt;get&lt;/code&gt;, &lt;code&gt;post&lt;/code&gt;, &lt;code&gt;put&lt;/code&gt;, &lt;code&gt;patch&lt;/code&gt;,
  &lt;code&gt;delete&lt;/code&gt;) now accepts a native &lt;code&gt;URLSearchParams&lt;/code&gt; object
  or a plain query string in addition to the existing plain-object shorthand.
  This matters when you need to send the same key more than once, which a plain
  object cannot express.
&lt;/div&gt;

&lt;pre&gt;&lt;code&gt;&lt;span&gt;test&lt;/span&gt;(&lt;span&gt;&amp;apos;query params via URLSearchParams&amp;apos;&lt;/span&gt;, &lt;span&gt;async&lt;/span&gt; ({ request }) =&amp;gt; {

  &lt;span&gt;// URLSearchParams lets you append duplicate keys.&lt;/span&gt;
  &lt;span&gt;const&lt;/span&gt; params = &lt;span&gt;new&lt;/span&gt; URLSearchParams();
  params.&lt;span&gt;set&lt;/span&gt;(&lt;span&gt;&amp;apos;userId&amp;apos;&lt;/span&gt;, &lt;span&gt;&amp;apos;1&amp;apos;&lt;/span&gt;);
  params.&lt;span&gt;append&lt;/span&gt;(&lt;span&gt;&amp;apos;tag&amp;apos;&lt;/span&gt;, &lt;span&gt;&amp;apos;automation&amp;apos;&lt;/span&gt;);
  params.&lt;span&gt;append&lt;/span&gt;(&lt;span&gt;&amp;apos;tag&amp;apos;&lt;/span&gt;, &lt;span&gt;&amp;apos;playwright&amp;apos;&lt;/span&gt;); &lt;span&gt;// duplicate key — plain object cannot do this&lt;/span&gt;

  &lt;span&gt;const&lt;/span&gt; response = &lt;span&gt;await&lt;/span&gt; request.&lt;span&gt;get&lt;/span&gt;(
    &lt;span&gt;&amp;apos;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://jsonplaceholder.typicode.com/posts&quot;&gt;https://jsonplaceholder.typicode.com/posts&lt;/a&gt;&amp;apos;&lt;/span&gt;,
    { params }
  );
  &lt;span&gt;expect&lt;/span&gt;(response.&lt;span&gt;status&lt;/span&gt;()).&lt;span&gt;toBe&lt;/span&gt;(&lt;span&gt;200&lt;/span&gt;);

  &lt;span&gt;// Or just pass a pre-built query string.&lt;/span&gt;
  &lt;span&gt;const&lt;/span&gt; response2 = &lt;span&gt;await&lt;/span&gt; request.&lt;span&gt;get&lt;/span&gt;(
    &lt;span&gt;&amp;apos;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://jsonplaceholder.typicode.com/posts&quot;&gt;https://jsonplaceholder.typicode.com/posts&lt;/a&gt;&amp;apos;&lt;/span&gt;,
    { params: &lt;span&gt;&amp;apos;userId=1&amp;amp;tag=automation&amp;apos;&lt;/span&gt; }
  );
  &lt;span&gt;expect&lt;/span&gt;(response2.&lt;span&gt;ok&lt;/span&gt;()).&lt;span&gt;toBeTruthy&lt;/span&gt;();
});&lt;/code&gt;&lt;/pre&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;th&gt;Resource&lt;/th&gt;&lt;th&gt;Link&lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Official docs&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/api/class-apirequestcontext#api-request-context-get&quot; rel=&quot;noopener&quot;&gt;playwright.dev — APIRequestContext.get()&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Release notes (v1.46)&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/release-notes#version-146&quot; rel=&quot;noopener&quot;&gt;playwright.dev/docs/release-notes#version-146&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GitHub release (v1.46.0)&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/releases/tag/v1.46.0&quot; rel=&quot;noopener&quot;&gt;github.com — Release v1.46.0&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Source code&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/client/fetch.ts&quot; rel=&quot;noopener&quot;&gt;packages/playwright-core/src/client/fetch.ts&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr&gt;



&lt;h2&gt;
  &lt;code&gt;expect.toPass()&lt;/code&gt;
  &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/release-notes#version-144&quot; rel=&quot;noopener&quot;&gt;intervals config — v1.44 May 2024&lt;/a&gt;
&lt;/h2&gt;

&lt;div&gt;
  Wraps an &lt;code&gt;async&lt;/code&gt; callback and retries it until every assertion
  inside passes or a timeout is reached. Unlike Playwright&amp;apos;s built-in
  auto-retrying locator assertions, &lt;code&gt;toPass&lt;/code&gt; can contain arbitrary
  logic including API requests, multi-step flows, and non-locator checks.
  By default the timeout is &lt;strong&gt;0&lt;/strong&gt; (no timeout) and probe intervals
  default to &lt;code&gt;[100, 250, 500, 1000]&lt;/code&gt; ms. Both are configurable
  globally in &lt;code&gt;testConfig.expect.toPass&lt;/code&gt; or per-call.
&lt;/div&gt;

&lt;h3&gt;Polling an endpoint until ready&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;test&lt;/span&gt;(&lt;span&gt;&amp;apos;waits for background job&amp;apos;&lt;/span&gt;, &lt;span&gt;async&lt;/span&gt; ({ request }) =&amp;gt; {
  &lt;span&gt;await&lt;/span&gt; &lt;span&gt;expect&lt;/span&gt;(&lt;span&gt;async&lt;/span&gt; () =&amp;gt; {
    &lt;span&gt;const&lt;/span&gt; res = &lt;span&gt;await&lt;/span&gt; request.&lt;span&gt;get&lt;/span&gt;(&lt;span&gt;&amp;apos;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://api.example.com/job/status&quot;&gt;https://api.example.com/job/status&lt;/a&gt;&amp;apos;&lt;/span&gt;);
    &lt;span&gt;expect&lt;/span&gt;(res.&lt;span&gt;status&lt;/span&gt;()).&lt;span&gt;toBe&lt;/span&gt;(&lt;span&gt;200&lt;/span&gt;);
    &lt;span&gt;const&lt;/span&gt; body = &lt;span&gt;await&lt;/span&gt; res.&lt;span&gt;json&lt;/span&gt;();
    &lt;span&gt;expect&lt;/span&gt;(body.status).&lt;span&gt;toBe&lt;/span&gt;(&lt;span&gt;&amp;apos;complete&amp;apos;&lt;/span&gt;);
  }).&lt;span&gt;toPass&lt;/span&gt;({
    intervals: [&lt;span&gt;1_000&lt;/span&gt;, &lt;span&gt;2_000&lt;/span&gt;, &lt;span&gt;10_000&lt;/span&gt;], &lt;span&gt;// probe at 1 s, 2 s, then every 10 s&lt;/span&gt;
    timeout:   &lt;span&gt;60_000&lt;/span&gt;,
  });
});&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Global configuration in &lt;code&gt;playwright.config.ts&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;import&lt;/span&gt; { defineConfig } &lt;span&gt;from&lt;/span&gt; &lt;span&gt;&amp;apos;@playwright/test&amp;apos;&lt;/span&gt;;
&lt;span&gt;export default&lt;/span&gt; &lt;span&gt;defineConfig&lt;/span&gt;({
  expect: {
    toPass: {
      timeout:   &lt;span&gt;30_000&lt;/span&gt;,
      intervals: [&lt;span&gt;500&lt;/span&gt;, &lt;span&gt;1_000&lt;/span&gt;, &lt;span&gt;5_000&lt;/span&gt;],
    },
  },
});&lt;/code&gt;&lt;/pre&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;th&gt;Resource&lt;/th&gt;&lt;th&gt;Link&lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Official docs&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/test-assertions#expecttopass&quot; rel=&quot;noopener&quot;&gt;playwright.dev — Assertions: expect.toPass()&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Release notes (v1.44)&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/release-notes#version-144&quot; rel=&quot;noopener&quot;&gt;playwright.dev/docs/release-notes#version-144&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GitHub release (v1.44.0)&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/releases/tag/v1.44.0&quot; rel=&quot;noopener&quot;&gt;github.com — Release v1.44.0&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Source code&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/blob/main/packages/playwright/src/matchers/toPass.ts&quot; rel=&quot;noopener&quot;&gt;packages/playwright/src/matchers/toPass.ts&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr&gt;



&lt;h2&gt;
  &lt;code&gt;page.requestGC()&lt;/code&gt;
  &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/release-notes#version-146&quot; rel=&quot;noopener&quot;&gt;v1.46 — August 2024&lt;/a&gt;
&lt;/h2&gt;

&lt;div&gt;
  Asks the browser to run garbage collection on the page. There is no guarantee
  that every unreachable object is collected, but in practice it is reliable
  enough to detect leaks in combination with JavaScript
  &lt;code&gt;WeakRef&lt;/code&gt;. The pattern: store a &lt;code&gt;WeakRef&lt;/code&gt; to the object
  under suspicion, call &lt;code&gt;requestGC()&lt;/code&gt;, then assert that
  &lt;code&gt;deref()&lt;/code&gt; returns &lt;code&gt;undefined&lt;/code&gt;.
&lt;/div&gt;

&lt;pre&gt;&lt;code&gt;&lt;span&gt;test&lt;/span&gt;(&lt;span&gt;&amp;apos;no memory leak on the large object&amp;apos;&lt;/span&gt;, &lt;span&gt;async&lt;/span&gt; ({ page }) =&amp;gt; {
  &lt;span&gt;await&lt;/span&gt; page.&lt;span&gt;goto&lt;/span&gt;(&lt;span&gt;&amp;apos;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://your-app.com&quot;&gt;https://your-app.com&lt;/a&gt;&amp;apos;&lt;/span&gt;);

  &lt;span&gt;// 1. Pin a WeakRef to the object under suspicion.&lt;/span&gt;
  &lt;span&gt;await&lt;/span&gt; page.&lt;span&gt;evaluate&lt;/span&gt;(() =&amp;gt; {
    (&lt;span&gt;globalThis as any&lt;/span&gt;).suspectWeakRef = &lt;span&gt;new&lt;/span&gt; WeakRef((&lt;span&gt;window as any&lt;/span&gt;).largeObject);
  });

  &lt;span&gt;// 2. Request GC.&lt;/span&gt;
  &lt;span&gt;await&lt;/span&gt; page.&lt;span&gt;requestGC&lt;/span&gt;();

  &lt;span&gt;// 3. A living WeakRef means a leak; deref() should now be undefined.&lt;/span&gt;
  &lt;span&gt;const&lt;/span&gt; leaked = &lt;span&gt;await&lt;/span&gt; page.&lt;span&gt;evaluate&lt;/span&gt;(
    () =&amp;gt; !!(&lt;span&gt;globalThis as any&lt;/span&gt;).suspectWeakRef.&lt;span&gt;deref&lt;/span&gt;()
  );
  &lt;span&gt;expect&lt;/span&gt;(leaked).&lt;span&gt;toBe&lt;/span&gt;(&lt;span&gt;false&lt;/span&gt;);
});&lt;/code&gt;&lt;/pre&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;th&gt;Resource&lt;/th&gt;&lt;th&gt;Link&lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Official docs&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/api/class-page#page-request-gc&quot; rel=&quot;noopener&quot;&gt;playwright.dev — page.requestGC()&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Release notes (v1.46)&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/release-notes#version-146&quot; rel=&quot;noopener&quot;&gt;playwright.dev/docs/release-notes#version-146&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GitHub release (v1.46.0)&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/releases/tag/v1.46.0&quot; rel=&quot;noopener&quot;&gt;github.com — Release v1.46.0&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Source code&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/client/page.ts&quot; rel=&quot;noopener&quot;&gt;packages/playwright-core/src/client/page.ts&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr&gt;



&lt;h2&gt;
  TypeScript Config: &lt;code&gt;--tsconfig&lt;/code&gt; flag &amp;amp; &lt;code&gt;testConfig.tsconfig&lt;/code&gt;
&lt;/h2&gt;

&lt;div&gt;
  Playwright looks up the nearest &lt;code&gt;tsconfig.json&lt;/code&gt; for each imported
  file by default. Two complementary options override this. The
  &lt;code&gt;--tsconfig&lt;/code&gt; CLI flag
  &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/release-notes#version-146&quot; rel=&quot;noopener&quot;&gt;(v1.46, August 2024)&lt;/a&gt;
  applies one tsconfig to all imported files from the command line. The
  &lt;code&gt;testConfig.tsconfig&lt;/code&gt; property
  &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/release-notes#version-149&quot; rel=&quot;noopener&quot;&gt;(v1.49, October 2024)&lt;/a&gt;
  does the same from the config file. Note that Playwright only honours four
  tsconfig options: &lt;code&gt;allowJs&lt;/code&gt;, &lt;code&gt;baseUrl&lt;/code&gt;,
  &lt;code&gt;paths&lt;/code&gt;, and &lt;code&gt;references&lt;/code&gt;.
&lt;/div&gt;

&lt;h3&gt;CLI flag&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;# Apply one tsconfig to everything Playwright loads.&lt;/span&gt;
npx playwright test --tsconfig tsconfig.test.json&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Config file property&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;// playwright.config.ts&lt;/span&gt;
&lt;span&gt;import&lt;/span&gt; { defineConfig } &lt;span&gt;from&lt;/span&gt; &lt;span&gt;&amp;apos;@playwright/test&amp;apos;&lt;/span&gt;;
&lt;span&gt;export default&lt;/span&gt; &lt;span&gt;defineConfig&lt;/span&gt;({
  tsconfig: &lt;span&gt;&amp;apos;./tsconfig.test.json&amp;apos;&lt;/span&gt;,
});&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Example &lt;code&gt;tests/tsconfig.json&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;{
  &lt;span&gt;&amp;quot;compilerOptions&amp;quot;&lt;/span&gt;: {
    &lt;span&gt;&amp;quot;baseUrl&amp;quot;&lt;/span&gt;: &lt;span&gt;&amp;quot;.&amp;quot;&lt;/span&gt;,
    &lt;span&gt;&amp;quot;allowJs&amp;quot;&lt;/span&gt;: &lt;span&gt;true&lt;/span&gt;,
    &lt;span&gt;&amp;quot;paths&amp;quot;&lt;/span&gt;: {
      &lt;span&gt;&amp;quot;@helpers/*&amp;quot;&lt;/span&gt;: [&lt;span&gt;&amp;quot;../src/helpers/*&amp;quot;&lt;/span&gt;]
    }
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;th&gt;Resource&lt;/th&gt;&lt;th&gt;Link&lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;TypeScript guide&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/test-typescript&quot; rel=&quot;noopener&quot;&gt;playwright.dev — TypeScript&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;testConfig.tsconfig&lt;/code&gt; docs&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/api/class-testconfig#test-config-tsconfig&quot; rel=&quot;noopener&quot;&gt;playwright.dev — TestConfig.tsconfig&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Release notes (&lt;code&gt;--tsconfig&lt;/code&gt;, v1.46)&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/release-notes#version-146&quot; rel=&quot;noopener&quot;&gt;playwright.dev/docs/release-notes#version-146&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Release notes (&lt;code&gt;testConfig.tsconfig&lt;/code&gt;, v1.49)&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/release-notes#version-149&quot; rel=&quot;noopener&quot;&gt;playwright.dev/docs/release-notes#version-149&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GitHub release (v1.46.0)&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/releases/tag/v1.46.0&quot; rel=&quot;noopener&quot;&gt;github.com — Release v1.46.0&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GitHub release (v1.49.0)&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/releases/tag/v1.49.0&quot; rel=&quot;noopener&quot;&gt;github.com — Release v1.49.0&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Source code&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/blob/main/packages/playwright/src/transform/transform.ts&quot; rel=&quot;noopener&quot;&gt;packages/playwright/src/transform/transform.ts&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr&gt;



&lt;h2&gt;
  &lt;code&gt;webServer.wait&lt;/code&gt; Regex
  &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/release-notes#version-159&quot; rel=&quot;noopener&quot;&gt;v1.59 — April 2026&lt;/a&gt;
&lt;/h2&gt;

&lt;div&gt;
  The &lt;code&gt;webServer&lt;/code&gt; block in &lt;code&gt;playwright.config.ts&lt;/code&gt; previously
  waited for a URL to respond with a 2xx–4xx status. The new
  &lt;code&gt;wait.stdout&lt;/code&gt; and &lt;code&gt;wait.stderr&lt;/code&gt; sub-options accept regular
  expressions and watch the server process output instead. This is useful for dev
  servers that print a readiness message rather than exposing an HTTP health
  endpoint. Named capture groups in the regex are automatically exported as
  environment variables, so a varying port can be passed directly to
  &lt;code&gt;test.use({ baseURL })&lt;/code&gt;.
&lt;/div&gt;

&lt;pre&gt;&lt;code&gt;&lt;span&gt;// playwright.config.ts&lt;/span&gt;
&lt;span&gt;import&lt;/span&gt; { defineConfig } &lt;span&gt;from&lt;/span&gt; &lt;span&gt;&amp;apos;@playwright/test&amp;apos;&lt;/span&gt;;
&lt;span&gt;export default&lt;/span&gt; &lt;span&gt;defineConfig&lt;/span&gt;({
  webServer: {
    command: &lt;span&gt;&amp;apos;npm run start&amp;apos;&lt;/span&gt;,
    wait: {
      &lt;span&gt;// Named group &amp;quot;my_server_port&amp;quot; is stored in process.env.MY_SERVER_PORT&lt;/span&gt;
      stdout: &lt;span&gt;/Listening on port (?&amp;lt;my_server_port&amp;gt;\d+)/&lt;/span&gt;,
    },
  },
});&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;&lt;span&gt;// In a test file — consume the captured port.&lt;/span&gt;
&lt;span&gt;import&lt;/span&gt; { test } &lt;span&gt;from&lt;/span&gt; &lt;span&gt;&amp;apos;@playwright/test&amp;apos;&lt;/span&gt;;

test.&lt;span&gt;use&lt;/span&gt;({
  baseURL: &lt;span&gt;`http://localhost:&lt;span&gt;${&lt;/span&gt;process.env.MY_SERVER_PORT ?? &lt;span&gt;3000&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;`&lt;/span&gt;,
});

&lt;span&gt;test&lt;/span&gt;(&lt;span&gt;&amp;apos;homepage loads&amp;apos;&lt;/span&gt;, &lt;span&gt;async&lt;/span&gt; ({ page }) =&amp;gt; {
  &lt;span&gt;await&lt;/span&gt; page.&lt;span&gt;goto&lt;/span&gt;(&lt;span&gt;&amp;apos;/&amp;apos;&lt;/span&gt;);
  &lt;span&gt;await&lt;/span&gt; &lt;span&gt;expect&lt;/span&gt;(page).&lt;span&gt;toHaveTitle&lt;/span&gt;(&lt;span&gt;/My App/&lt;/span&gt;);
});&lt;/code&gt;&lt;/pre&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;th&gt;Resource&lt;/th&gt;&lt;th&gt;Link&lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Web server guide&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/test-webserver&quot; rel=&quot;noopener&quot;&gt;playwright.dev — Web Server&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;testConfig.webServer&lt;/code&gt; docs&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/api/class-testconfig#test-config-web-server&quot; rel=&quot;noopener&quot;&gt;playwright.dev — TestConfig.webServer&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Release notes (v1.59)&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/release-notes#version-159&quot; rel=&quot;noopener&quot;&gt;playwright.dev/docs/release-notes#version-159&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GitHub release (v1.59.0)&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/releases/tag/v1.59.0&quot; rel=&quot;noopener&quot;&gt;github.com — Release v1.59.0&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Source code&lt;/td&gt;
      &lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/blob/main/packages/playwright/src/plugins/webServerPlugin.ts&quot; rel=&quot;noopener&quot;&gt;packages/playwright/src/plugins/webServerPlugin.ts&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr&gt;



&lt;h2&gt;Further Reading&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;div&gt;
      &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://www.martinpoole.cv/blog/playwright-locator-handler&quot; rel=&quot;noopener&quot;&gt;
        The Power of Playwright&amp;apos;s Locator Handler
      &lt;/a&gt;
    &lt;/div&gt;
    &lt;div&gt;
      Martin Poole — February 2026 — Draws a direct comparison to QTP/UFT
      Disaster Recovery patterns and demonstrates a &lt;code&gt;beforeEach&lt;/code&gt; strategy
      that fails tests immediately on unexpected application error overlays.
    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;div&gt;
      &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://testdino.com/blog/playwright-release-guide&quot; rel=&quot;noopener&quot;&gt;
        Playwright 1.59 Release: New Features for Agentic Testing
      &lt;/a&gt;
    &lt;/div&gt;
    &lt;div&gt;
      TestDino — April 2026 — Covers &lt;code&gt;browser.bind()&lt;/code&gt; with
      working multi-client code samples, the &lt;code&gt;playwright-cli&lt;/code&gt; dashboard,
      and the new &lt;code&gt;page.screencast&lt;/code&gt; API.
    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;div&gt;
      &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://www.skptricks.com/2025/05/use-playwrights-addlocatorhandler-to-close-unpredictable-ui-elements.html&quot; rel=&quot;noopener&quot;&gt;
        Use Playwright&amp;apos;s addLocatorHandler to Close Unpredictable UI Elements
      &lt;/a&gt;
    &lt;/div&gt;
    &lt;div&gt;
      SKPTricks — May 2025 — Walks through cookie banners, modal overlays,
      and adding retry logic inside a handler when a click does not immediately dismiss
      the element.
    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;div&gt;
      &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://medium.com/@bhushantbn/playwright-version-1-44-released-bff8323d69c4&quot; rel=&quot;noopener&quot;&gt;
        Playwright Version 1.44 Released
      &lt;/a&gt;
    &lt;/div&gt;
    &lt;div&gt;
      Bhushan Trivedi — May 2024 — Covers the &lt;code&gt;times&lt;/code&gt; and
      &lt;code&gt;noWaitAfter&lt;/code&gt; additions to &lt;code&gt;addLocatorHandler&lt;/code&gt;, and the
      &lt;code&gt;expect.toPass&lt;/code&gt; interval configuration that shipped in the same
      release.
    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;div&gt;
      &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://umairqa.medium.com/configuring-web-server-in-playwright-for-end-to-end-testing-db3dda8415b4&quot; rel=&quot;noopener&quot;&gt;
        Configuring Web Server in Playwright for End-to-End Testing
      &lt;/a&gt;
    &lt;/div&gt;
    &lt;div&gt;
      UmairQA — May 2025 — Covers the full &lt;code&gt;webServer&lt;/code&gt; block
      in depth: URL-based readiness checks, timeouts, multiple servers, and CI
      patterns.
    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;/div&gt;

Happy Testing! &lt;br&gt;&lt;br&gt;
-T.J. Maher&lt;br&gt;
Software Engineer in Test&lt;br&gt;
&lt;br&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://bsky.app/profile/tjmaher1.bsky.social&quot;&gt;BlueSky&lt;/a&gt; | &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://bit.ly/tj_youtube&quot;&gt;YouTube&lt;/a&gt; | &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://www.linkedin.com/in/tjmaher1&quot;&gt;LinkedIn&lt;/a&gt; | &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://www.tjmaher.com/p/media.html&quot;&gt;Articles&lt;/a&gt;</summary><author><name>T.J. Maher</name></author><source gr:stream-id="feed/http://www.tjmaher.com/feeds/posts/default"><id>tag:google.com,2005:reader/feed/http://www.tjmaher.com/feeds/posts/default</id><title type="html">Adventures in Automation</title><link rel="alternate" href="https://www.tjmaher.com/" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778512579000"><id gr:original-id="https://www.kulasangeles.com/?p=2874">tag:google.com,2005:reader/item/00000cef0000000b</id><category term="Blog"></category><title type="html">Finding Rhythm in Music and Tech</title><published>2026-05-11T15:16:19Z</published><updated>2026-05-11T15:16:19Z</updated><link rel="alternate" href="https://www.kulasangeles.com/finding-rhythm-in-music-and-tech/" type="text/html"></link><summary type="html">Last weekend, I picked up my drumsticks and played at a fundraiser gala. 🥁 Outside of my work in AI, quality engineering, speaking, and coaching, music has always been a constant in my life. I started playing at eight years old, and even now, it continues to shape how I think and work. Being back on stage reminded me that the lessons I learned through music never really left they just evolved. Discipline from years…</summary><author><name>Kulas</name></author><source gr:stream-id="feed/https://www.kulasangeles.com/feed/"><id>tag:google.com,2005:reader/feed/https://www.kulasangeles.com/feed/</id><title type="html">https://www.kulasangeles.com/feed/</title><link rel="alternate" href="https://www.kulasangeles.com" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778491986000"><id gr:original-id="http://testwithnishi.com/?p=2963">tag:google.com,2005:reader/item/0000052000000099</id><category term="Conferences &amp; Talks"></category><category term="agile"></category><category term="agile leadership"></category><category term="agile pods"></category><category term="jira"></category><category term="program management"></category><category term="scrum"></category><category term="speaker"></category><category term="sprint planning"></category><category term="startup"></category><category term="talk"></category><title type="html">I am speaking at LeadDev Meetup, Amsterdam</title><published>2026-05-11T09:33:06Z</published><updated>2026-05-11T09:33:06Z</updated><link rel="alternate" href="https://testwithnishi.com/2026/05/11/i-am-speaking-at-leaddev-meetup-amsterdam/" type="text/html"></link><summary type="html">I am super excited to share that I have been invited to speak at the upcoming LeadDev Meetup in Amsterdam – being sponsored by Picnic and CircleCi. The LeadDev group is dedicated to bringing together engineering leaders, managers, and software developers to share best practices in engineering leadership, discuss strategies for promoting leadership accountability, and […]&lt;p style=&quot;clear: both&quot;&gt;&lt;/p&gt;&lt;p data-bqr-info=&quot;attachment&quot;&gt;&lt;img class=&quot;bqrUnknownImgSize&quot; src=&quot;https://testwithnishi.com/wp-content/uploads/2026/05/leaddevmeetup-agenda.png?w=1024&quot;&gt;&lt;/p&gt;</summary><author><name>Nishi Grover Garg</name></author><source gr:stream-id="feed/https://testwithnishi.com/feed/"><id>tag:google.com,2005:reader/feed/https://testwithnishi.com/feed/</id><title type="html">testwithnishi</title><link rel="alternate" href="https://testwithnishi.com" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778471149000"><id gr:original-id="https://scrolltest.com/deepeval-vs-promptfoo-2026-llm-evaluation-framework/">tag:google.com,2005:reader/item/00000444000001ba</id><category term="AI Testing"></category><category term="Testing"></category><category term="AI testing"></category><category term="deepeval"></category><category term="LLM evaluation"></category><category term="promptfoo"></category><category term="qa-tools"></category><title type="html">DeepEval vs PromptFoo 2026: Which LLM Eval Framework Wins?</title><published>2026-05-11T03:45:49Z</published><updated>2026-05-11T03:45:49Z</updated><link rel="alternate" href="https://scrolltest.com/deepeval-vs-promptfoo-2026-llm-evaluation-framework/" type="text/html"></link><summary type="html">&lt;p&gt;I tested DeepEval and PromptFoo on real RAG pipelines. Here are the exact numbers—stars, downloads, metrics, and token costs—so your QA team picks the right LLM evaluation framework in 2026.&lt;/p&gt;
&lt;p&gt;The post &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; rel=&quot;nofollow&quot; href=&quot;https://scrolltest.com/deepeval-vs-promptfoo-2026-llm-evaluation-framework/&quot;&gt;DeepEval vs PromptFoo 2026: Which LLM Eval Framework Wins?&lt;/a&gt; appeared first on &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; rel=&quot;nofollow&quot; href=&quot;https://scrolltest.com&quot;&gt;Software Testing &amp;amp; Automation&lt;/a&gt;.&lt;/p&gt;</summary><author><name>Promode</name></author><source gr:stream-id="feed/https://scrolltest.com/feed/"><id>tag:google.com,2005:reader/feed/https://scrolltest.com/feed/</id><title type="html">Software Testing &amp; Automation</title><link rel="alternate" href="https://scrolltest.com" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778463088196"><id gr:original-id="tag:blogger.com,1999:blog-3868566217808655382.post-8434778038566856910">tag:google.com,2005:reader/item/000002c500000463</id><category term="Playwright"></category><title type="html">Practicing Playwright: Logging in by Storing and Using an Authentication Cookie in Your Automated Tests</title><published>2026-05-11T01:31:28Z</published><updated>2026-05-11T01:31:28Z</updated><link rel="alternate" href="https://www.tjmaher.com/2026/05/practicing-playwright-logging-in-by.html" type="text/html"></link><summary type="html">&lt;div&gt;&lt;div&gt;I absolutely love that LinkedIn offers a free month-long trial period of &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://www.linkedin.com/learning/&quot;&gt;LinkedIn Learning&lt;/a&gt;. &lt;b&gt;Butch Mayhew&lt;/b&gt;&amp;apos;s &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://www.linkedin.com/learning/playwright-essential-training-abstractions-fixtures-and-complex-scenarios&quot;&gt;Playwright Essential Training: Abstractions, Fixtures, and Complex Scenarios&lt;/a&gt; course has been a wonderful resource learning more about Playwright.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;With this blog post, I will be walking through Butch&amp;apos;s code on how to set up an automated test to log into an app without going through the user interface. All it needs is the login cookie. &lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left&quot;&gt;&lt;li&gt;&lt;b&gt;Butch Mayhew&amp;apos;s&lt;/b&gt; &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/LinkedInLearning/playwright-essential-training-abstractions-fixtures-and-complex-scenarios-4278224&quot;&gt;companion GitHub site&lt;/a&gt; on &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/LinkedInLearning&quot;&gt;LinkedIn Learning&amp;apos;s GitHub page&lt;/a&gt;. &lt;/li&gt;&lt;li&gt;&lt;b&gt;Site under test&lt;/b&gt;: &lt;b&gt;Practice Software Testing&lt;/b&gt;: &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://practicesoftwaretesting.com&quot;&gt;https://practicesoftwaretesting.com&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Official Playwright Documentation&lt;/b&gt;: &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs&quot;&gt;https://playwright.dev/docs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Official Playwright GitHub&lt;/b&gt;: &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright&quot;&gt;https://github.com/microsoft/playwright&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;When testing a shopping cart app such as the &lt;b&gt;Practice Software Testing&lt;/b&gt; website, it can get tedious. You need to open a browser, go to the login page, enter a username, enter a password, hit the login button, and verify you have logged in correctly every time you want to test something in the shopping cart. &lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;If you are testing something unrelated to logging in, why have your tests go through the UI to authenticate? Why not have your automated test run that login test once, temporarily save the login cookie once it is produced, then reuse the login cookie, importing it into other tests?&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left&quot;&gt;What is Playwright?&lt;/h2&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;According to &lt;b&gt;Microsoft Playwright&amp;apos;s GitHub site&lt;/b&gt;, Playwright &amp;quot;is a framework for web automation and testing. It drives Chromium, Firefox, and WebKit with a single API — in your tests, in your scripts, and as a tool for AI agents&amp;quot;.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Playwright comes in many features. From &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright&quot;&gt;https://github.com/microsoft/playwright&lt;/a&gt; &lt;/div&gt;&lt;div&gt;&lt;markdown-accessiblity-table data-catalyst style=&quot;background-color: white; box-sizing: border-box; color: #1f2328; display: block; font-family: &amp;quot;Mona Sans VF&amp;quot;, -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, &amp;quot;Noto Sans&amp;quot;, Helvetica, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;; font-size: 16px&quot;&gt;&lt;table style=&quot;border-collapse: collapse; border-spacing: 0px; display: block; font-variant: tabular-nums; margin-bottom: 16px; margin-top: 0px; overflow: auto; width: max-content&quot;&gt;&lt;thead style=&quot;box-sizing: border-box&quot;&gt;&lt;tr style=&quot;border-top: 0.571429px solid rgba(209, 217, 224, 0.7); box-sizing: border-box&quot;&gt;&lt;th style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;&lt;/th&gt;&lt;th style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;Best for&lt;/th&gt;&lt;th style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;Install&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody style=&quot;box-sizing: border-box&quot;&gt;&lt;tr style=&quot;border-top: 0.571429px solid rgba(209, 217, 224, 0.7); box-sizing: border-box&quot;&gt;&lt;td style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 600; margin-bottom: 0px&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright#playwright-test&quot; style=&quot;background-color: rgba(0, 0, 0, 0); box-sizing: border-box; color: #0969da; text-underline-offset: 0.2rem&quot;&gt;Playwright Test&lt;/a&gt;&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;End-to-end testing&lt;/td&gt;&lt;td style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;&lt;code style=&quot;background-color: rgba(129, 139, 152, 0.12); border-radius: 6px; box-sizing: border-box; font-family: &amp;quot;Monaspace Neon&amp;quot;, ui-monospace, SFMono-Regular, &amp;quot;SF Mono&amp;quot;, Menlo, Consolas, &amp;quot;Liberation Mono&amp;quot;, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em; tab-size: 4; white-space: break-spaces&quot;&gt;npm init playwright@latest&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;background-color: #f6f8fa; border-top: 0.571429px solid rgba(209, 217, 224, 0.7); box-sizing: border-box&quot;&gt;&lt;td style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 600; margin-bottom: 0px&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright#playwright-cli&quot; style=&quot;background-color: rgba(0, 0, 0, 0); box-sizing: border-box; color: #0969da; text-underline-offset: 0.2rem&quot;&gt;Playwright CLI&lt;/a&gt;&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;Coding agents (Claude Code, Copilot)&lt;/td&gt;&lt;td style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;&lt;code style=&quot;background-color: rgba(129, 139, 152, 0.12); border-radius: 6px; box-sizing: border-box; font-family: &amp;quot;Monaspace Neon&amp;quot;, ui-monospace, SFMono-Regular, &amp;quot;SF Mono&amp;quot;, Menlo, Consolas, &amp;quot;Liberation Mono&amp;quot;, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em; tab-size: 4; white-space: break-spaces&quot;&gt;npm i -g @playwright/cli@latest&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;border-top: 0.571429px solid rgba(209, 217, 224, 0.7); box-sizing: border-box&quot;&gt;&lt;td style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 600; margin-bottom: 0px&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright#playwright-mcp&quot; style=&quot;background-color: rgba(0, 0, 0, 0); box-sizing: border-box; color: #0969da; text-underline-offset: 0.2rem&quot;&gt;Playwright MCP&lt;/a&gt;&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;AI agents and LLM-driven automation&lt;/td&gt;&lt;td style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;&lt;code style=&quot;background-color: rgba(129, 139, 152, 0.12); border-radius: 6px; box-sizing: border-box; font-family: &amp;quot;Monaspace Neon&amp;quot;, ui-monospace, SFMono-Regular, &amp;quot;SF Mono&amp;quot;, Menlo, Consolas, &amp;quot;Liberation Mono&amp;quot;, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em; tab-size: 4; white-space: break-spaces&quot;&gt;npx @playwright/mcp@latest&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;background-color: #f6f8fa; border-top: 0.571429px solid rgba(209, 217, 224, 0.7); box-sizing: border-box&quot;&gt;&lt;td style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 600; margin-bottom: 0px&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright#playwright-library&quot; style=&quot;background-color: rgba(0, 0, 0, 0); box-sizing: border-box; color: #0969da; text-underline-offset: 0.2rem&quot;&gt;Playwright Library&lt;/a&gt;&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;Browser automation scripts&lt;/td&gt;&lt;td style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;&lt;code style=&quot;background-color: rgba(129, 139, 152, 0.12); border-radius: 6px; box-sizing: border-box; font-family: &amp;quot;Monaspace Neon&amp;quot;, ui-monospace, SFMono-Regular, &amp;quot;SF Mono&amp;quot;, Menlo, Consolas, &amp;quot;Liberation Mono&amp;quot;, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em; tab-size: 4; white-space: break-spaces&quot;&gt;npm i playwright&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;border-top: 0.571429px solid rgba(209, 217, 224, 0.7); box-sizing: border-box&quot;&gt;&lt;td style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 600; margin-bottom: 0px&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright#vs-code-extension&quot; style=&quot;background-color: rgba(0, 0, 0, 0); box-sizing: border-box; color: #0969da; text-underline-offset: 0.2rem&quot;&gt;VS Code Extension&lt;/a&gt;&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;Test authoring and debugging in VS Code&lt;/td&gt;&lt;td style=&quot;border-color: rgb(209, 217, 224); border-image: none 100% / 1 / 0 stretch; border-style: solid; border-width: 0.571429px; box-sizing: border-box; padding: 6px 13px&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright&quot; rel=&quot;nofollow&quot; style=&quot;background-color: rgba(0, 0, 0, 0); box-sizing: border-box; color: #0969da; margin-bottom: 0px; text-underline-offset: 0.2rem&quot;&gt;Install from Marketplace&lt;/a&gt;&lt;br&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/markdown-accessiblity-table&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;... Let&amp;apos;s walk through how Butch uses Playwright grabs the login cookie and uses that in his tests. &lt;/div&gt;&lt;span&gt;&lt;a name=&quot;article-Ft5o2ngojqt3ZB1ptetcB6B5u80-more&quot;&gt;&lt;/a&gt;&lt;/span&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left&quot;&gt;Getting an Authorization Cookie Through the Website&lt;/h2&gt;&lt;div&gt;Before we do anything with our test automation, let&amp;apos;s examine an authorization cookie. &lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left&quot;&gt;&lt;li&gt;Open up Chrome and go to &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://practicesoftwaretesting.com/auth/login&quot;&gt;https://practicesoftwaretesting.com/auth/login&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Right click on the page and select &amp;quot;Inspect&amp;quot; to open Chrome Developer Tools.&lt;/li&gt;&lt;li&gt;Select the &amp;quot;Network&amp;quot; tab.&lt;/li&gt;&lt;li&gt;Enter a username and password of &lt;b&gt;&lt;a href=&quot;mailto:customer@practicesoftwaretesting.com&quot;&gt;customer@practicesoftwaretesting.com&lt;/a&gt;&lt;/b&gt; / &lt;b&gt;welcome01&lt;/b&gt;&lt;/li&gt;&lt;li&gt;Select the &amp;quot;Login&amp;quot; button, then view the Network tab. &lt;/li&gt;&lt;li&gt;On the network tab, click on the &amp;quot;account&amp;quot; entry. &lt;/li&gt;&lt;li&gt;Select the &amp;quot;Cookies&amp;quot; tab in Chrome Developer Tools. &lt;br&gt;&lt;/li&gt;&lt;/ul&gt;Under &lt;b&gt;Request Cookies&lt;/b&gt;, you will see something like this:&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;&lt;code&gt;&amp;quot;cookies&amp;quot;: [
    {
      &amp;quot;name&amp;quot;: &amp;quot;cf_clearance&amp;quot;,
      &amp;quot;value&amp;quot;: &amp;quot;2Rv345ZtjSaGUFRwq6j2QO4Qp3EBG0T.5URv.oenfjg-1778187254-1.2.1.1-90Z63mBartyo.dUm4bwavAggVG9pgrzcYOO0zPS18qBEujmzIq3tnWwjx2b5jQypbZywJMVEIncDFSwJBaAD0ephO3cctJrLkS449MVHNO7ZSuqlAPhzVYGw8vVH4doqYc2R_5WXTY47OHVLRAgnblq7U9glSiaJUvoOKmc6I.Mo6FPl3MrhGmfMxSTVyTkcCBJn8t_C9b1margtoukM7yMuQdNPpkNItvrrZdIQT.TpGQx2sNX7cS_KS8TY2Ghyc0_QLKFZFqC_5ZgZHMad5xgzgvJEm2NZmO3RBLSPKH5bpDFbuynt7g4oIwYfIvWd4pTpTB447hpP5qMPnLOp.Q&amp;quot;,
      &amp;quot;domain&amp;quot;: &amp;quot;.&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://practicesoftwaretesting.com&quot;&gt;practicesoftwaretesting.com&lt;/a&gt;&amp;quot;,
      &amp;quot;path&amp;quot;: &amp;quot;/&amp;quot;,
      &amp;quot;expires&amp;quot;: 1809723253.921407,
      &amp;quot;httpOnly&amp;quot;: true,
      &amp;quot;secure&amp;quot;: true,
      &amp;quot;sameSite&amp;quot;: &amp;quot;None&amp;quot;,
      &amp;quot;partitionKey&amp;quot;: &amp;quot;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://practicesoftwaretesting.com&quot;&gt;https://practicesoftwaretesting.com&lt;/a&gt;&amp;quot;,&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Cross-Indexing this with the &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://developer.chrome.com/docs/devtools/application/cookies&quot;&gt;Chrome DevTools Docs on Cookies&lt;/a&gt;, this means:  &lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;b&gt;Name:&lt;/b&gt; &amp;quot;The cookie&amp;apos;s name&amp;quot;, in this case &amp;quot;&lt;b&gt;cf_clearance&lt;/b&gt;&amp;quot;, is a security token issued by Cloudflare after passing a challenge. [ &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://developers.cloudflare.com/cloudflare-challenges/concepts/clearance/&quot;&gt;See Cloudflare docs&lt;/a&gt; ]&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Value&lt;/b&gt;. Claude AI guessed that the Value of this cookie was this... but was not sure since this value is proprietary. &lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left&quot;&gt;&lt;li&gt;&lt;b&gt;Random token&lt;/b&gt;: 2Rv345ZtjSaGUFRwq6j2QO4Qp3EBG0T.5URv.oenfjg&lt;/li&gt;&lt;li&gt;&lt;b&gt;Unix timestamp&lt;/b&gt;: 1778187254 (Around May 4, 2026)&lt;/li&gt;&lt;li&gt;&lt;b&gt;Version Level&lt;/b&gt;:  1.2.1.1, the Clearance level encoding&lt;/li&gt;&lt;li&gt;&lt;b&gt;Signature payload&lt;/b&gt;: 90Z63mBartyo... HMAC-binding (Hash-Based Message Authentication Codes) to IP, user agent, and session. &lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Domain:&lt;/b&gt; &amp;quot;The hosts that are allowed to receive the cookie&amp;quot;, in this case, practicesoftwaretesting.com.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Path:&lt;/b&gt; &amp;quot;The URL that must exist in the requested URL in order to send the Cookie header&amp;quot;, in this case the root folder. &lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Expires / Max-Age&lt;/b&gt;. &amp;quot;The cookie&amp;apos;s expiration date or maximum age. For session cookies this value is always Session&amp;quot;. According to &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://unixtimestamp.com&quot;&gt;UnixTimeStamp.com&lt;/a&gt; &amp;quot;1809723253.921407&amp;quot; would expire on May 10, 2026 on 3:36 am UTC and 49 seconds.  &lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left&quot;&gt;&lt;li&gt;What is a &lt;b&gt;Unix Time Stamp&lt;/b&gt;? &amp;quot;The unix time stamp is a way to track time as a running total of seconds. This count starts at the Unix Epoch on January 1st, 1970 at UTC. Therefore, the unix time stamp is merely the number of seconds between a particular date and the Unix Epoch. It should also be pointed out (thanks to the comments from visitors to this site) that this point in time technically does not change no matter where you are located on the globe. This is very useful to computer systems for tracking and sorting dated information in dynamic and distributed applications both online and client side&amp;quot; - UnixTimeStamp.com&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;HttpOnly:&lt;/b&gt; &amp;quot;If true, this field indicates that the cookie should only be used over HTTP, and JavaScript modification is not allowed&amp;quot;... and it is set to true. &lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Secure:&lt;/b&gt; &amp;quot;If true, this field indicates that the cookie can only be sent to the server over a secure, HTTPS connection&amp;quot;... and it is set to true. &lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;SameSite&lt;/b&gt;: &amp;quot;Contains Strict or Lax if the cookie is using the experimental SameSite attribute&amp;quot;.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Partition Key:&lt;/b&gt; &amp;quot;For cookies with independent partition state, the partition key is the site of the top-level URL the browser was visiting at the start of the request to the endpoint that set the cookie.&lt;/div&gt;&lt;div&gt;Priority. Contains Low, Medium (default), or High if using deprecated cookie Priority attribute&amp;quot;... and ours is &amp;quot;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://practicesoftwaretesting.com&quot;&gt;https://practicesoftwaretesting.com&lt;/a&gt;&amp;quot;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;By logging into the site, it generates this cookie on the back end, allowing you to keep accessing the site. &lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;If you generate this cooking programmatically before running the tests, and temporally save it, you can then use it in all preceding tests in that test run. &lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left&quot;&gt;Step One: Setup the Setup&lt;/h2&gt;&lt;div&gt;The &lt;b&gt;Playwright Configuration file&lt;/b&gt; has in its projects array two projects: One to set up the, er, setup, and one to set up the Chromium run. &lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;First, Butch set up in his &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/test-configuration&quot;&gt;Playwright Configuration&lt;/a&gt; file, a &lt;b&gt;project dependency&lt;/b&gt;, in a project that he called &amp;quot;setup&amp;quot;. &lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;u&gt;playwright.config.ts:&lt;/u&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;&lt;code&gt;projects: [
    {
      name: &amp;quot;setup&amp;quot;,
      testMatch: /.*\.setup\.ts/,
    },
    {
      name: &amp;quot;chromium&amp;quot;,
      dependencies: [&amp;quot;setup&amp;quot;],
      use: {
        ...devices[&amp;quot;Desktop Chrome&amp;quot;],
        permissions: [&amp;quot;clipboard-read&amp;quot;, &amp;quot;geolocation&amp;quot;],
      },
    },&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;p style=&quot;box-sizing: border-box; margin: 0px 0px 20px&quot;&gt;&lt;br&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; color: #1c1e21; font-family: system-ui, -apple-system, &amp;quot;Segoe UI&amp;quot;, Roboto, Ubuntu, Cantarell, &amp;quot;Noto Sans&amp;quot;, sans-serif, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Helvetica, Arial, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;; font-size: 16px; margin: 0px 0px 20px&quot;&gt;&lt;span style=&quot;color: black; font-family: &amp;quot;Times New Roman&amp;quot;; font-size: medium&quot;&gt;According to &lt;/span&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/next/test-projects#dependencies&quot; style=&quot;font-family: &amp;quot;Times New Roman&amp;quot;; font-size: medium&quot;&gt;Playwright.Dev&lt;/a&gt;&lt;span style=&quot;color: black; font-family: &amp;quot;Times New Roman&amp;quot;; font-size: medium&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: black; font-family: &amp;quot;Times New Roman&amp;quot;; font-size: medium&quot;&gt; &amp;quot;Dependencies are a list of projects that need to run before the tests in another project run. They can be useful for configuring the global setup actions so that one project depends on this running first.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin: 0px 0px 20px&quot;&gt;&amp;quot;When working with tests that have a dependency, the dependency will always run first and once all tests from this project have passed, then the other projects will run in parallel.&lt;br&gt;&lt;br&gt;&amp;quot;Running order: Tests in the &amp;apos;setup&amp;apos; project run. Once all tests from this project have passed, then the tests from the dependent projects will start running.&lt;br&gt;&lt;br&gt;&amp;quot;Tests in the &amp;apos;chromium&amp;apos; [...] projects run together. By default, these projects will &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/next/test-parallel&quot;&gt;run in parallel&lt;/a&gt;, subject to the maximum workers limit&amp;quot;.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;name: &amp;quot;setup&amp;quot;&lt;/b&gt;: A project with the name of  &amp;quot;setup&amp;quot;. &lt;/li&gt;&lt;li&gt;&lt;b&gt;testMatch: /.*setup\.ts&lt;/b&gt;,: This pattern matcher searches on level up for anything with &amp;quot;setup&amp;quot; in the middle of the filename, such as &amp;quot;auth.setup.ts&amp;quot; in the &amp;quot;tests/&amp;quot; directory.&lt;/li&gt;&lt;li&gt;&lt;b&gt;name: &amp;quot;chromium&amp;quot;&lt;/b&gt;: A project that directs the test automation when it runs to use the Desktop Chrome browser devices.&lt;/li&gt;&lt;li&gt;&lt;b&gt;dependencies: [&amp;quot;setup&amp;quot;]: &lt;/b&gt;Before the Chrome browser has kicked off, we tell it we need to run the project called &amp;quot;setup&amp;quot; first. &lt;/li&gt;&lt;li&gt;&lt;b&gt;use: ...devices[&amp;quot;Desktop Chrome&amp;quot;]&lt;/b&gt;: Playwright has an enormous list of devices in its &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/deviceDescriptorsSource.json&quot;&gt;device registry&lt;/a&gt;, such as various models of the Blackberry, Galaxy, iPad, iPhone, Microsoft Lumia, Nexus, Nokia, Pixel, Moto, and Desktop Devices (Chrome, Edge, Firefox, Safari) with many different user agents, viewports, and screen width and heights. &lt;/li&gt;&lt;li&gt;&lt;b&gt;use: permissions:&lt;/b&gt; Playwright&amp;apos;s browser context, &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/api/class-browsercontext#browser-context-grant-permissions&quot;&gt;according to the official Playwright Docs&lt;/a&gt;, has differing sets of permissions in the permission array for each browser emulation, such as emulating the accelerometer, ambient light sensor, background sync, camera, clipboard read and write, geolocation, gyroscope, local fonts, local network access, magnetometer, microphone, midi, notifications, payment-handler, storage-access, and screen-wake-lock. &lt;/li&gt;&lt;li&gt;&lt;b&gt;[clipboard-read]&lt;/b&gt;: We need to give the Chromium browser Playwright spins up permission to read the authentication cookie that is generated the first time we log in. Note, according to notes in Playwright&amp;apos;s source code &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/microsoft/playwright/blob/a26b3614d61b191cb9676c77223d46a6ce57b138/tests/library/permissions.spec.ts#L202&quot;&gt;in the permissions library&lt;/a&gt;, &lt;b&gt;there is no &amp;quot;clipboard-read&amp;quot; in WebKit&amp;apos;s API&lt;/b&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left&quot;&gt;Step Two: Get and Save the Authorization Cookie&lt;/h2&gt;&lt;div&gt;Next, in &lt;b&gt;auth.setup.ts&lt;/b&gt;, Butch has the automated test go to the site, log in, and save the cookie to a file.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;b&gt;&lt;u&gt;
tests/ auth.setup.ts:&lt;/u&gt;&lt;/b&gt; &lt;pre&gt;&lt;code&gt;
import { test as setup, expect } from &amp;quot;@playwright/test&amp;quot;;
import { LoginPage } from &amp;quot;../lib/pages/login/login.page&amp;quot;;

setup(&amp;quot;Create customer 01 auth&amp;quot;, async ({ page, context }) =&amp;gt; {
  const email = &amp;quot;&lt;a href=&quot;mailto:customer@practicesoftwaretesting.com&quot;&gt;customer@practicesoftwaretesting.com&lt;/a&gt;&amp;quot;;
  const password = &amp;quot;welcome01&amp;quot;;
  const customer01AuthFile = &amp;quot;.auth/customer01.json&amp;quot;;

  const loginPage = new LoginPage(page);

  await loginPage.goto();

  await loginPage.login(email, password);

  await expect(page.getByTestId(&amp;quot;nav-menu&amp;quot;)).toContainText(&amp;quot;Jane Doe&amp;quot;);
  await context.storageState({ path: customer01AuthFile });
});&lt;/code&gt;&lt;/pre&gt;&lt;i&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/LinkedInLearning/playwright-essential-training-abstractions-fixtures-and-complex-scenarios-4278224/blob/98d2080e018acb194938157cb7c2fc8993ca21a5/tests/auth.setup.t&quot;&gt;https://github.com/LinkedInLearning/playwright-essential-training-abstractions-fixtures-and-complex-scenarios-4278224/blob/98d2080e018acb194938157cb7c2fc8993ca21a5/tests/auth.setup.t&lt;/a&gt;s&lt;/i&gt;&lt;br&gt;&lt;br&gt;  

What is this code doing?&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left&quot;&gt;&lt;li&gt;&lt;b&gt;Import the test runner playwright/test&lt;/b&gt; into our TypeScript file, setting up an alias for the test runner, calling it &amp;quot;setup&amp;quot; to make the test more readable, and to mark this as performing initialization tasks. To learn more about how aliases work in TypeScript &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://www.freecodecamp.org/news/how-typescript-type-aliases-work/&quot;&gt;see FreeCodeCamp&amp;apos;s explanation&lt;/a&gt;, and read about the &lt;b&gt;test &lt;/b&gt;method &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/api/class-test#test-call&quot;&gt;in the Playwright.dev docs&lt;/a&gt;.  &lt;/li&gt;&lt;li&gt;&lt;b&gt;Import the expect assertion library&lt;/b&gt; into the TypeScript file, so we then can use the expect method, detecting if an element is attached, checkbox is checked, element is disabled, editable, enabled, focused, not visible, visible, contains text, has certain CSS classes, DOM attributes, ids, CSS or JavaScript properties, ARIA roles, screenshots, matches text, or if pages have screenshots, titles, or URLs, failing near immediately if we don&amp;apos;t see that condition. [ &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/test-assertions&quot;&gt;See Playwright.Dev docs about test assertions&lt;/a&gt; ]. We can also match if it meets any instance of a class, matches anything, of an array contains certain elements, if a number is close to equal, a string contains a substring, a string matches a certain substring. All of these expects can be negated chaining a &lt;b&gt;not &lt;/b&gt;to it. &lt;/li&gt;&lt;li&gt;&lt;b&gt;setup(&amp;quot;Create customer 01 auth&amp;quot;&lt;/b&gt; is being used by Butch Mayhew instead of saying &amp;quot;test(&amp;quot;Create customer 01 auth&amp;quot;) to make it more readable. &lt;/li&gt;&lt;li&gt;&lt;b&gt;async &lt;/b&gt;uses the JavaScript keyword &lt;b&gt;async&lt;/b&gt;. &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://www.freecodecamp.org/news/learn-async-programming-in-typescript-promises-asyncawait-and-callbacks/&quot;&gt;FreeCodeCamp&lt;/a&gt; says that, &amp;quot;Async programming is a programming paradigm that allows you to write code that runs asynchronously. In contrast to synchronous programming, which executes code sequentially, async programming allows code to run in the background while the rest of the program continues to execute. This is particularly useful for tasks that may take a long time to complete, such as fetching data from a remote API&amp;quot;.&lt;/li&gt;&lt;li&gt;&lt;b&gt;page, context &lt;/b&gt;are passing these built-in &lt;b&gt;fixtures &lt;/b&gt;into the test.&lt;b&gt; &lt;/b&gt;The Page instance is created for each test, used in isolation from one another. [ &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/test-fixtures&quot;&gt;See Playwright.dev docs on test fixtures&lt;/a&gt;. ] &amp;quot;Test fixtures are used to establish the environment for each test, giving the test everything it needs and nothing else. Test fixtures are isolated between tests. With fixtures, you can group tests based on their meaning, instead of their common setup&amp;quot;. The &amp;quot;page&amp;quot; fixtures uses type &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/api/class-page&quot;&gt;Page&lt;/a&gt;, and &amp;quot;context&amp;quot; fixtures uses type &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/api/class-browsercontext&quot;&gt;BrowserContext&lt;/a&gt; in order to &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/test-configuration&quot;&gt;configure context&lt;/a&gt;. Fixtures can encapsulate setup and teardown, are reusable between test files.&lt;/li&gt;&lt;li&gt;&lt;b&gt;const email, password, customer01AuthFile&lt;/b&gt; is declaring constant variables to store what the username, password, and where the auth cookie should be stored, to make the code more readable. &lt;/li&gt;&lt;li&gt;&lt;b&gt;const loginPage&lt;/b&gt; is instantiating a new login page using the &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/LinkedInLearning/playwright-essential-training-abstractions-fixtures-and-complex-scenarios-4278224/blob/98d2080e018acb194938157cb7c2fc8993ca21a5/lib/pages/login/login.page.ts&quot;&gt;login page object Butch has defined&lt;/a&gt;, not covered in this blog entry. The login page Butch set up contains methods where you can &lt;b&gt;goto &lt;/b&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://practicesoftwaretesting.com/auth/login&quot;&gt;https://practicesoftwaretesting.com/auth/login&lt;/a&gt;, enter the username and password in the &lt;b&gt;login&lt;/b&gt; method using the constants we defined, expecting that we see a greeting to the user. &lt;/li&gt;&lt;/ul&gt;The real magic is the last bit of code: &lt;b&gt;context.storageState({ path: customer01AuthFile });&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left&quot;&gt;&lt;li&gt;Playwright has &lt;b&gt;storage and authentication methods&lt;/b&gt; where you can manage cookies, localStorage and &lt;b&gt;sessionStorage&lt;/b&gt; [ &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/mcp/tools/storage&quot;&gt;See official docs&lt;/a&gt; ]. Session Storage is session scoped, where data is cleared out when the browser tab eventually closes. &lt;/li&gt;&lt;li&gt;According to the &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/auth&quot;&gt;Playwright Docs / Authentication&lt;/a&gt;, when you &amp;quot;[c]reate a new setup project in the config and declare it as a &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://playwright.dev/docs/test-projects#dependencies&quot;&gt;dependency&lt;/a&gt; for all your testing project [...]. This project will always run and authenticate before all the tests. All testing projects should use the authenticated state as storageState&amp;quot;.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left&quot;&gt;The Cookie is Temporarily Saved!&lt;/h2&gt;&lt;br&gt; 
A new folder and file is created in the root folder when this command is run: .auth/customer01.json:  
&lt;pre&gt;&lt;code&gt;
{
  &amp;quot;cookies&amp;quot;: [
    {
      &amp;quot;name&amp;quot;: &amp;quot;cf_clearance&amp;quot;,
      &amp;quot;value&amp;quot;: &amp;quot;2Rv345ZtjSaGUFRwq6j2QO4Qp3EBG0T.5URv.oenfjg-1778187254-1.2.1.1-90Z63mBartyo.dUm4bwavAggVG9pgrzcYOO0zPS18qBEujmzIq3tnWwjx2b5jQypbZywJMVEIncDFSwJBaAD0ephO3cctJrLkS449MVHNO7ZSuqlAPhzVYGw8vVH4doqYc2R_5WXTY47OHVLRAgnblq7U9glSiaJUvoOKmc6I.Mo6FPl3MrhGmfMxSTVyTkcCBJn8t_C9b1margtoukM7yMuQdNPpkNItvrrZdIQT.TpGQx2sNX7cS_KS8TY2Ghyc0_QLKFZFqC_5ZgZHMad5xgzgvJEm2NZmO3RBLSPKH5bpDFbuynt7g4oIwYfIvWd4pTpTB447hpP5qMPnLOp.Q&amp;quot;,
      &amp;quot;domain&amp;quot;: &amp;quot;.&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://practicesoftwaretesting.com&quot;&gt;practicesoftwaretesting.com&lt;/a&gt;&amp;quot;,
      &amp;quot;path&amp;quot;: &amp;quot;/&amp;quot;,
      &amp;quot;expires&amp;quot;: 1809723253.921407,
      &amp;quot;httpOnly&amp;quot;: true,
      &amp;quot;secure&amp;quot;: true,
      &amp;quot;sameSite&amp;quot;: &amp;quot;None&amp;quot;,
      &amp;quot;partitionKey&amp;quot;: &amp;quot;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://practicesoftwaretesting.com&quot;&gt;https://practicesoftwaretesting.com&lt;/a&gt;&amp;quot;,
      &amp;quot;_crHasCrossSiteAncestor&amp;quot;: false
    }
  ],
  &amp;quot;origins&amp;quot;: [
    {
      &amp;quot;origin&amp;quot;: &amp;quot;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://practicesoftwaretesting.com&quot;&gt;https://practicesoftwaretesting.com&lt;/a&gt;&amp;quot;,
      &amp;quot;localStorage&amp;quot;: [
        {
          &amp;quot;name&amp;quot;: &amp;quot;auth-token&amp;quot;,
          &amp;quot;value&amp;quot;: &amp;quot;eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2FwaS5wcmFjdGljZXNvZnR3YXJldGVzdGluZy5jb20vdXNlcnMvbG9naW4iLCJpYXQiOjE3NzgxODcyNTUsImV4cCI6MTc3ODE4NzU1NSwibmJmIjoxNzc4MTg3MjU1LCJqdGkiOiJoeFJ1MmkwNWpDamVrNUdXIiwic3ViIjoiMDFLUjIwRFFYQUpTVEJEWkg2S0Q1R1QwRVQiLCJwcnYiOiIyM2JkNWM4OTQ5ZjYwMGFkYjM5ZTcwMWM0MDA4NzJkYjdhNTk3NmY3Iiwicm9sZSI6InVzZXIifQ.Iqv8TGSweXLUfk7UExbIem_kOoIde9apIrzFZ9cCVpo&amp;quot;
        }
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left&quot;&gt;Step Three: Make Sure The Cookie Is Only Temporarily Saved. Do Not Upload the Cookie To GitHub!!!&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;It is very important that you do &lt;b&gt;NOT &lt;/b&gt;save the .auth cookie, or any other credentials, in GitHub. &lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;If you examine the .&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://github.com/LinkedInLearning/playwright-essential-training-abstractions-fixtures-and-complex-scenarios-4278224/blob/98d2080e018acb194938157cb7c2fc8993ca21a5/.gitignore&quot;&gt;gitignore&lt;/a&gt; file Butch Mayhew has set up, you can see that along with test_results, node_modules, .tmp files, you will not be uploading anything in the &lt;b&gt;.auth/&lt;/b&gt; folder. &lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left&quot;&gt;&lt;li&gt;Read more about .&lt;b&gt;gitignore&lt;/b&gt; on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://git-scm.com/docs/gitignore&quot;&gt;Git-SCM/docs/gitignore page&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;h2 style=&quot;text-align: left&quot;&gt;Step Four: Implement the Auth Cookie for Login&lt;/h2&gt; 


Now that the Setup function generated a new cookie, we then can then use this credential setting up the test to use storageState. &lt;br&gt;&lt;br&gt;&lt;b&gt;&lt;u&gt; 
tests / homepage / home.spec.ts&lt;/u&gt;:  
&lt;/b&gt;&lt;pre&gt;&lt;code&gt;  
test.describe(&amp;quot;Home page customer 01 auth&amp;quot;, () =&amp;gt; {
  test.use({ storageState: &amp;quot;.auth/customer01.json&amp;quot; });
  test.beforeEach(async ({ page }) =&amp;gt; {
    await page.goto(&amp;quot;/&amp;quot;);
  });  
&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&lt;br&gt;&lt;/div&gt;How do we know this is successful? We verify that the sign in navigation is not visible, and instead we are greeted by the username, in this case, &amp;quot;Jane Doe&amp;quot;. &lt;br&gt;   
  
  
&lt;br&gt;&lt;br&gt;&lt;b&gt;&lt;u&gt; 
tests / homepage / home.spec.ts:  
&lt;/u&gt;&lt;/b&gt;&lt;pre&gt;&lt;code&gt;  
  test(&amp;quot;check customer 01 is signed in&amp;quot;, async ({ page }) =&amp;gt; {
    await expect(page.getByTestId(&amp;quot;nav-sign-in&amp;quot;)).not.toBeVisible();
    await expect(page.getByTestId(&amp;quot;nav-menu&amp;quot;)).toContainText(&amp;quot;Jane Doe&amp;quot;);
  }); 
&lt;/code&gt;&lt;/pre&gt;
&lt;br&gt;If we don&amp;apos;t see the sign in page, and instead we see the nav menu for &amp;quot;Jane Doe&amp;quot;, then everything has passed. &lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;So, that&amp;apos;s how we use authentication cookies in tests! &lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Happy Testing!&lt;/div&gt;&lt;br&gt;
-T.J. Maher&lt;br&gt;
Software Engineer in Test&lt;br&gt;
&lt;br&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://bsky.app/profile/tjmaher1.bsky.social&quot;&gt;BlueSky&lt;/a&gt; | &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://bit.ly/tj_youtube&quot;&gt;YouTube&lt;/a&gt; | &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://www.linkedin.com/in/tjmaher1&quot;&gt;LinkedIn&lt;/a&gt; | &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;http://www.tjmaher.com/p/media.html&quot;&gt;Articles&lt;/a&gt;</summary><author><name>T.J. Maher</name></author><source gr:stream-id="feed/http://www.tjmaher.com/feeds/posts/default"><id>tag:google.com,2005:reader/feed/http://www.tjmaher.com/feeds/posts/default</id><title type="html">Adventures in Automation</title><link rel="alternate" href="https://www.tjmaher.com/" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778457600000"><id gr:original-id="https://www.softwaretester.blog/my-review-of-seleniumconf-2026-in-valencia/">tag:google.com,2005:reader/item/000005f300000080</id><category term="conference"></category><category term="selenium"></category><title type="html">My review of SeleniumConf 2026 in Valencia</title><published>2026-05-11T00:00:00Z</published><updated>2026-05-11T00:00:00Z</updated><link rel="alternate" href="https://www.softwaretester.blog/my-review-of-seleniumconf-2026-in-valencia/" type="text/html"></link><summary type="html">&lt;p&gt;I was already a visitor of some Selenium conferences, e.g. in Berlin, Chicago and last year&amp;apos;s Valencia edition. For 2026, the conference yet again returned to Valencia, Spain. Here is my full review.&lt;/p&gt;
        &lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://www.softwaretester.blog/my-review-of-seleniumconf-2026-in-valencia/&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;</summary><author><name></name></author><source gr:stream-id="feed/https://www.softwaretester.blog/blog.rss"><id>tag:google.com,2005:reader/feed/https://www.softwaretester.blog/blog.rss</id><title type="html">Benjamin Bischoff, Test Automation Engineer</title><link rel="alternate" href="https://www.softwaretester.blog/" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778457600000"><id gr:original-id="https://qualitywithmillan.github.io/blog/qa-toolbox-for-ai-testing">tag:google.com,2005:reader/item/0000085000000049</id><category term="blog"></category><category term="quality-engineering"></category><category term="ai"></category><category term="qa"></category><category term="ai-testing"></category><category term="llm-evaluation"></category><category term="guardrails"></category><category term="prompt-engineering"></category><category term="ai-native"></category><category term="spec-driven-development"></category><category term="model-evaluation"></category><category term="human-in-the-loop"></category><title type="html">QA Toolbox: Powering Reliable AI Outputs</title><published>2026-05-11T00:00:00Z</published><updated>2026-05-11T00:00:00Z</updated><link rel="alternate" href="https://qualitywithmillan.github.io/blog/qa-toolbox-for-ai-testing.html" type="text/html"></link><summary type="html">&lt;p&gt;
 Written by -

    
    
    
        &lt;strong&gt;Millan Kaul&lt;/strong&gt;
    

&lt;/p&gt;

&lt;p&gt;QA is not disappearing in the AI era. &lt;strong&gt;It is becoming the layer that makes AI outputs reliable, testable and safe.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;article-ABaCveJWW8mA9gm2e4D2slytLR8-why-qa-still-matters&quot;&gt;Why QA still matters&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;AI-native teams move fast, but speed without quality creates risk.
    &lt;ul&gt;
      &lt;li&gt;Prompts can drift.&lt;/li&gt;
      &lt;li&gt;Outputs hallucinates.&lt;/li&gt;
      &lt;li&gt;Guardrails may get skipped.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;article-ABaCveJWW8mA9gm2e4D2slytLR8-that-is-where-qa-and-quality-engineering-matter-more-than-ever&quot;&gt;That is where QA and quality engineering matter more than ever&lt;/h3&gt;

&lt;p&gt;In AI systems, QA is not just about finding bugs. 
It is about checking behavior, validating outputs and protecting users more than ever.&lt;/p&gt;

&lt;p&gt;But the question is : &lt;strong&gt;Will my old toolbox work ?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is my attempt to answer exactly that..&lt;/p&gt;

&lt;h2 id=&quot;article-ABaCveJWW8mA9gm2e4D2slytLR8-the-qa-toolbox-for-ai&quot;&gt;The QA toolbox for AI&lt;/h2&gt;

&lt;p&gt;Start thinking of QA in AI-native products as a simple toolbox:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Prompts&lt;/strong&gt; define the expected behavior.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Guardrails&lt;/strong&gt; prevent unsafe or off-spec outputs.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Evaluation&lt;/strong&gt; checks whether the model actually did the right thing.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Human-in-the-loop review&lt;/strong&gt; catches the edge cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the new QA stack for AI products.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;bqrUnknownImgSize&quot; src=&quot;https://qualitywithmillan.github.io/assets/images/2026/05/qa-ai-toolbox.png&quot; alt=&quot;Image 01&quot;&gt;
&lt;/p&gt;

&lt;h2 id=&quot;article-ABaCveJWW8mA9gm2e4D2slytLR8-what-about-spec-driven-development-what-are-my-tools-for-that-&quot;&gt;What about Spec-driven development, what are my tools for that ?&lt;/h2&gt;

&lt;p&gt;In spec-driven development, the spec becomes the source of truth.&lt;br&gt;
QA should not treat specs as documentation only. They should be treated as testable contracts.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Define what good output looks like,&lt;/li&gt;
  &lt;li&gt;Define what must never happen, and&lt;/li&gt;
  &lt;li&gt;Test against that every time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;article-ABaCveJWW8mA9gm2e4D2slytLR8-sample-code-pii-guardrails-with-nemo-guardrails&quot;&gt;Sample code: PII guardrails with NeMo Guardrails&lt;/h2&gt;

&lt;p&gt;NeMo Guardrails supports PII detection and masking in input and output flows through its Private AI integration.&lt;/p&gt;

&lt;h3 id=&quot;article-ABaCveJWW8mA9gm2e4D2slytLR8-configyml&quot;&gt;&lt;code class=&quot;language-plaintext language-highlighter-rouge&quot;&gt;config.yml&lt;/code&gt;&lt;/h3&gt;

&lt;div&gt;&lt;div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;models&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
  &lt;span&gt;-&lt;/span&gt; &lt;span&gt;type&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;main&lt;/span&gt;
    &lt;span&gt;engine&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;openai&lt;/span&gt;
    &lt;span&gt;model&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;gpt-4o-mini&lt;/span&gt;

&lt;span&gt;rails&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
  &lt;span&gt;input&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
    &lt;span&gt;flows&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
      &lt;span&gt;-&lt;/span&gt; &lt;span&gt;detect pii on input&lt;/span&gt;
  &lt;span&gt;output&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
    &lt;span&gt;flows&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
      &lt;span&gt;-&lt;/span&gt; &lt;span&gt;detect pii on output&lt;/span&gt;

  &lt;span&gt;config&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
    &lt;span&gt;privateai&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
      &lt;span&gt;server_endpoint&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;http://your-privateai-api-endpoint/process/text&lt;/span&gt;

    &lt;span&gt;input&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
      &lt;span&gt;entities&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
        &lt;span&gt;-&lt;/span&gt; &lt;span&gt;NAME_FAMILY&lt;/span&gt;
        &lt;span&gt;-&lt;/span&gt; &lt;span&gt;LOCATION_ADDRESS_STREET&lt;/span&gt;
        &lt;span&gt;-&lt;/span&gt; &lt;span&gt;EMAIL_ADDRESS&lt;/span&gt;

    &lt;span&gt;output&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
      &lt;span&gt;entities&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;
        &lt;span&gt;-&lt;/span&gt; &lt;span&gt;NAME_FAMILY&lt;/span&gt;
        &lt;span&gt;-&lt;/span&gt; &lt;span&gt;LOCATION_ADDRESS_STREET&lt;/span&gt;
        &lt;span&gt;-&lt;/span&gt; &lt;span&gt;EMAIL_ADDRESS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;article-ABaCveJWW8mA9gm2e4D2slytLR8-python&quot;&gt;Python&lt;/h3&gt;

&lt;div&gt;&lt;div&gt;&lt;pre&gt;&lt;code&gt;&lt;span&gt;from&lt;/span&gt; &lt;span&gt;nemoguardrails&lt;/span&gt; &lt;span&gt;import&lt;/span&gt; &lt;span&gt;RailsConfig&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;LLMRails&lt;/span&gt;

&lt;span&gt;config&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;RailsConfig&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;from_path&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;./config&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
&lt;span&gt;rails&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;LLMRails&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;

&lt;span&gt;messages&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;[&lt;/span&gt;
    &lt;span&gt;{&lt;/span&gt;&lt;span&gt;&amp;quot;role&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;&amp;quot;user&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt; &lt;span&gt;&amp;quot;content&amp;quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt; &lt;span&gt;&amp;quot;Hi, I am John Doe. My email is &lt;a href=&quot;mailto:john.doe@example.com&quot;&gt;john.doe@example.com&lt;/a&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;
&lt;span&gt;]&lt;/span&gt;

&lt;span&gt;response&lt;/span&gt; &lt;span&gt;=&lt;/span&gt; &lt;span&gt;rails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;generate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;messages&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;messages&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
&lt;span&gt;print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;response&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Private AI can detect or mask PII in input, output, and retrieval flows, and the docs note that detection blocks text from passing through if PII is found.&lt;/p&gt;

&lt;h2 id=&quot;article-ABaCveJWW8mA9gm2e4D2slytLR8-what-to-test&quot;&gt;What to test&lt;/h2&gt;

&lt;p&gt;For AI products, QA should always check:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Does the prompt produce the expected structure?&lt;/li&gt;
  &lt;li&gt;Does the model stay within policy?&lt;/li&gt;
  &lt;li&gt;Does the output leak PII?&lt;/li&gt;
  &lt;li&gt;Does the answer remain useful under edge cases?&lt;/li&gt;
  &lt;li&gt;Does human review catch what automation misses?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are few of the ways for QAs to stays relevant in AI-native teams.&lt;/p&gt;

&lt;h2 id=&quot;article-ABaCveJWW8mA9gm2e4D2slytLR8-closing-thought&quot;&gt;Closing thought&lt;/h2&gt;

&lt;p&gt;AI does not replace QA. It makes QA more important. &lt;code class=&quot;language-plaintext language-highlighter-rouge&quot;&gt;[May 2026]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My personal take:&lt;/strong&gt; The teams that win will be the ones that test prompts, guardrails, and evaluation as seriously as code.&lt;/p&gt;</summary><author><name>Millan Kaul</name></author><source gr:stream-id="feed/https://qualitywithmillan.github.io/feed.xml"><id>tag:google.com,2005:reader/feed/https://qualitywithmillan.github.io/feed.xml</id><title type="html">Quality With Millan</title><link rel="alternate" href="https://qualitywithmillan.github.io/" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778457600000"><id gr:original-id="https://www.ontestautomation.com/im-restarting-a-newsletter">tag:google.com,2005:reader/item/000009910000002e</id><category term="General"></category><category term="newsletter"></category><title type="html">I’m (re-)starting a newsletter</title><published>2026-05-11T00:00:00Z</published><updated>2026-05-11T00:00:00Z</updated><link rel="alternate" href="https://www.ontestautomation.com/im-restarting-a-newsletter/" type="text/html"></link><summary type="html">&lt;p&gt;Just a quick update to let those of you who bookmarked this blog or who have subscribed to my &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://www.ontestautomation.com/feed.xml&quot;&gt;RSS feed&lt;/a&gt; know that I have (re-)started a newsletter.&lt;/p&gt;

&lt;h3 id=&quot;article-4l_9yEqVLezZMrZQLs2-BVstPhQ-why-a-newsletter&quot;&gt;Why a newsletter?&lt;/h3&gt;
&lt;p&gt;As you might know (or not), while I’ve been pretty active on &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://www.linkedin.com/in/basdijkstra&quot;&gt;LinkedIn&lt;/a&gt; over the years, I do have a love-hate (or rather an appreciate-hate) relationship with that platform.&lt;/p&gt;

&lt;p&gt;Lately, I’ve been noticing that the pendulum is swinging in the ‘hate’ direction more often, mainly because the ever-changing algorithm used by LinkedIn makes it incredibly hard to predict if people are even going to see what I write.&lt;/p&gt;

&lt;p&gt;I’d rather publish my thoughts, ideas and other ramblings via a platform that I do control, and that platform will be a newsletter.&lt;/p&gt;

&lt;p&gt;I’ve had a newsletter in the past, but that only lived for about three months. This time, I intend to keep writing and publishing a new issue every week. The first edition goes out a few hours after I’m writing this, and a new issue will be sent to subscribers every Monday morning around 11 AM CET.&lt;/p&gt;

&lt;h3 id=&quot;article-4l_9yEqVLezZMrZQLs2-BVstPhQ-but-what-about-the-blog&quot;&gt;But what about the blog?&lt;/h3&gt;
&lt;p&gt;I’ll still publish to the blog, too, but that will be on a much less regular basis. Just like it has been for a while, really. The idea is to post the more ‘technical’ posts, that is, the ones including code, directly to my blog, whereas the ‘text-and-images-only’ posts go through my blog post first.&lt;/p&gt;

&lt;p&gt;My priority is with the newsletter, though.&lt;/p&gt;

&lt;h3 id=&quot;article-4l_9yEqVLezZMrZQLs2-BVstPhQ-how-to-subscribe&quot;&gt;How to subscribe&lt;/h3&gt;
&lt;p&gt;That’s easy, just go to the &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://www.ontestautomation.com/newsletter/&quot;&gt;subscription page&lt;/a&gt;, leave your email address, click the button on the confirmation email and you’re in.&lt;/p&gt;

&lt;p&gt;I promise I won’t use the newsletter or your email to spam or sell to you. Ever.&lt;/p&gt;</summary><author><name>Bas Dijkstra</name></author><source gr:stream-id="feed/https://www.ontestautomation.com/feed.xml"><id>tag:google.com,2005:reader/feed/https://www.ontestautomation.com/feed.xml</id><title type="html">On Test Automation</title><link rel="alternate" href="https://www.ontestautomation.com/" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778450521000"><id gr:original-id="https://www.jamesshore.com/v2/blog/2026/you-need-ai-that-reduces-your-maintenance-costs">tag:google.com,2005:reader/item/00000a7a00000161</id><title type="html">You Need AI That Reduces Maintenance Costs</title><published>2026-05-10T22:02:01Z</published><updated>2026-05-10T22:02:01Z</updated><link rel="alternate" href="https://www.jamesshore.com/v2/blog/2026/you-need-ai-that-reduces-your-maintenance-costs" type="text/html"></link><summary type="html">&lt;p&gt;I’ll get straight to the point: your AI coding agent, the one you use to write code, needs to reduce your maintenance costs. Not by a little bit, either. You write code twice as quick now? Better hope you’ve halved your maintenance costs. Three times as productive? One third the maintenance costs. Otherwise, you’re screwed. You’re trading a temporary speed boost for permanent indenture.&lt;/p&gt;

&lt;p&gt;Oh, you want to know &lt;em&gt;why?&lt;/em&gt; Sure. Let’s go for a drive. On a dark desert highway...&lt;/p&gt;


&lt;h3&gt;Productivity is Determined by Maintenance Costs&lt;/h3&gt;

&lt;p&gt;Every line of code you write has to be maintained: bug fixes, cleanup, dependency upgrades, and so forth. I’m not talking about new features or enhancements. Just maintenance. For every month you spend writing code, you’ll spend some amount of time in the following year maintaining that code, and some in each year after that, forever, as long as that code exists.&lt;/p&gt;

&lt;p&gt;Let’s say you asked a crowd of, say, 50 developers what those maintenance costs were. Using a technique called &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://en.wikipedia.org/wiki/Wisdom_of_the_crowd&quot;&gt;Wisdom of the Crowd&lt;/a&gt;, you could get a reasonably accurate response.&lt;sup&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;1&lt;/sup&gt;You’re welcome to conduct your own wisdom-of-the-crowd survey! But it turns out that the specific numbers don’t matter for the overall point I’m making here.&lt;/p&gt;

&lt;p&gt;Your crowd might tell you that, for each month you spend writing code, you’ll spend...&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;&lt;p&gt;10 days on maintenance in the first year; &lt;em&gt;and&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
	&lt;li&gt;&lt;p&gt;5 days on maintenance each year after that.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you were a particularly obsessive individual, you could spend hours making a spreadsheet modeling how those estimates affect productivity over time. &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://docs.google.com/spreadsheets/d/109XUgvOMClSoknyGr9MtjWvNSfdf6XYiD5fnSvGYTTU/edit&quot;&gt;A spreadsheet like this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;
	&lt;img class=&quot;bqrUnknownImgSize&quot; src=&quot;https://www.jamesshore.com/images/maintenance-costs/effect-of-maintenance-costs.png&quot; width=&quot;100%&quot; alt=&quot;A graph showing the effects of maintenance costs on a project over time. The horizontal axis shows months, from zero to 120, and the vertical axis shows the percent of time spent on value-add work, from zero to 100. A thick blue line on the graph, labelled “normal,” starts at 100% and quickly drops down to about 65% in the first 12 months, then gradually drops to about 12.5% over the remaining 11 years. Two other lines follow a similar trajectory: a dashed yellow line, labelled “half maint,” ends at about 35%. A dashed red line, labelled “double maint,” ends at about 5%. Each line is marked at the point where it crosses 50% with a note that says “Time to &amp;lt;50% productivity.” For the “normal” line, it occurs at 31 months. For “half maint,” it occurs at 68 months. For “double maint,” it occurs at 10 months.&quot;&gt;
&lt;/p&gt;

&lt;p&gt;The first month of a new project is glorious. You spend all your time building fancy new features.&lt;/p&gt;

&lt;p&gt;The next month is slightly less glorious. A fraction of your time—not much, but a smidge—goes to fixing bugs and cleaning up design mistakes from the first month. In the third month, a smidge more. And the fourth month, the fifth, the sixth...&lt;/p&gt;

&lt;p&gt;Eventually, it’s not glorious &lt;em&gt;at all&lt;/em&gt;. According to our crowd’s maintenance estimates, you’ll spend more than half your time on maintenance after 2½ years. After ten years, you can hardly do anything else.&lt;/p&gt;

&lt;p&gt;Halving the crowd’s maintenance estimates gives you three more years before you hit the 50% mark. Doubling them sees you below 50% in less than a year.&lt;/p&gt;

&lt;p&gt;The lesson is clear. If you want a productive team, you have to focus on their maintenance costs.&lt;/p&gt;


&lt;h3&gt;All Models Are Wrong&lt;/h3&gt;

&lt;p&gt;Do these numbers ring true to you? They do to me. In my career as a consultant, I specialized in late-stage startups, and they all had the exact problem shown in the graph above. About 5-9 years in, they’d notice their teams were no longer getting shit done, and then they’d call me.&lt;/p&gt;

&lt;p&gt;Their teams weren’t &lt;em&gt;quite&lt;/em&gt; as bad as the graph shows. Maybe their maintenance costs were lower. Or maybe... and this feels more likely to me... their maintenance costs were &lt;em&gt;exactly&lt;/em&gt; that bad, and they papered over the problem instead. Maybe they:

&lt;/p&gt;&lt;ul&gt;
	&lt;li&gt;&lt;p&gt;Decided not to fix every bug, or upgrade every dependency&lt;/p&gt;&lt;/li&gt;
	&lt;li&gt;&lt;p&gt;Added people when the team got slow... and then kept adding more, because it was never enough&lt;/p&gt;&lt;/li&gt;
	&lt;li&gt;&lt;p&gt;Scrapped it all and started over with a rewrite&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s room to debate the precise maintenance numbers, but overall, the model feels right. If you’ve been around the block, you &lt;em&gt;know&lt;/em&gt; this graph is true. You’ve seen how productivity melts away over time. You have the scars.&lt;/p&gt;


&lt;h3&gt;What Does This Have to Do With AI?&lt;/h3&gt;

&lt;p&gt;Only everything.&lt;/p&gt;

&lt;p&gt;Let’s say your team just started using &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://www.youtube.com/watch?v=n4QSYx4wVQg&quot;&gt;Rock Lobster&lt;/a&gt;, the latest and greatest agentic coding framework, and it Doubles!! your code output! Woohoo! The code’s a bit harder to understand, though, and your team is drowning in pull requests, and you maybe kinda sorta teensy weensy don’t actually read the code before smashing the approve button. Like, at all. I mean, you skimmed it, during boring meetings, sometimes, and that’s gotta be good enough, right? LGTM, let’s get this shit done!&lt;/p&gt;

&lt;p&gt;So now you’re producing two months of work in a month, and let’s say you’ve doubled how much each “month” of output costs to maintain. Next month’s maintenance costs &lt;em&gt;quadruple&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;
	&lt;img class=&quot;bqrUnknownImgSize&quot; src=&quot;https://www.jamesshore.com/images/maintenance-costs/ai-double-costs.png&quot; width=&quot;100%&quot; alt=&quot;The same graph as before, but only showing the thick blue “normal” line. Overlayed on that line is a thin red line labelled “AI Doubles Prod and Maint.” At the 36 month mark, it rockets up to about 85% productivity, to a peak labelled “AI provides massive short term benefit.” Then it rapidly falls below the pre-AI productivity level, with a label that says “Gains erased after 5 months.” Over the next 12 months, it drops to about 10% lower than the blue “normal line” and stays there. A label says “Permanent long-term penalty.”&quot;&gt;
&lt;/p&gt;

&lt;p&gt;Oh.&lt;/p&gt;

&lt;p&gt;About five months after you start using Rock Lobster, your productivity is back down to where you started, and a few months after that, it’s &lt;em&gt;worse&lt;/em&gt; than it would have been had you never touched Rock Lobster in the first place.&lt;/p&gt;

&lt;p&gt;I’m not saying your AI doubles maintenance costs. Or productivity. This is an extreme example. But even if your AI produces code that’s &lt;em&gt;just as easy to maintain&lt;/em&gt; as your human-written code, the productivity gains don’t last.&lt;/p&gt;

&lt;p&gt;
	&lt;img class=&quot;bqrUnknownImgSize&quot; src=&quot;https://www.jamesshore.com/images/maintenance-costs/ai-normal-costs.png&quot; width=&quot;100%&quot; alt=&quot;A new version of the previous graph, with the same thick blue “normal” line. This time, the thin red line is labelled “AI Doubles Prod, Normal Maint.” At 36 months, it rockets up to about 85% like before, but this time it falls more slowly. It falls below the pre-AI productivity level at month 55, with a label that says “Gains erased after 19 months.” It continues to fall a bit more rapidly than the blue line, crossing over at month 86 with a label that says “Net negative after 40 months.” It ends a few percentage points below the blue line.&quot;&gt;
&lt;/p&gt;

&lt;h3&gt;You Can Check Out Any Time You Like&lt;sup&gt;2&lt;/sup&gt;&lt;/h3&gt;

&lt;div&gt;
  &lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/09839DpTctU?autoplay=0&amp;amp;fs=1&amp;amp;rel=0&amp;amp;playsinline=1&amp;amp;modestbranding=1&amp;amp;origin=https:%2f%2fbazqux.com&quot; allow=&quot;autoplay; fullscreen; encrypted-media; clipboard-write; picture-in-picture; web-share&quot; sandbox=&quot;allow-same-origin allow-scripts allow-forms allow-popups allow-presentation&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
  &lt;p&gt;&lt;sup&gt;2&lt;/sup&gt;But you can never leave.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Agents are expensive, and they’re only getting more so. Once your agent’s juice is no longer worth the squeeze, you might decide to save your pennies and go back to coding the old way. Like a caveman. With your &lt;em&gt;fingers&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Ha! Joke’s on you! When you stop using the agent, all the productivity benefit goes away... but the added maintenance costs don’t! As long as that code’s still around, you’re stuck with lower productivity than if you had never touched the agent at all.

&lt;/p&gt;&lt;p&gt;
	&lt;img class=&quot;bqrUnknownImgSize&quot; src=&quot;https://www.jamesshore.com/images/maintenance-costs/ai-double-costs-removed.png&quot; width=&quot;100%&quot; alt=&quot;A repeat of the graph that showed AI doubling productivity and maintenance costs. The thin red line from the previous graph, labelled “AI Doubles Prod and Maint,” is now a dotted red line. A new yellow line is labelled “AI Doubles Prod and Maint, Removed.” The thick blue line is still present and labelled “Normal.” The yellow line follows the trajectory of the red line, with the 36-month jump in productivity labelled “AI introduced.” As before, the line falls rapidly over the next six months. But at month 60, the yellow line diverges from the red line. It falls even more rapidly, losing about 10% more than the red line. This point is labelled “AI removed.” The yellow line recovers a bit, then loses ground more slowly than the red and blue lines, ending up about 5% better than the red line and 5% worse than the blue line.&quot; &quot;&gt;
&lt;/p&gt;


&lt;h3&gt;The Passage Back&lt;/h3&gt;

&lt;p&gt;The math only works if the LLM &lt;em&gt;decreases&lt;/em&gt; your maintenance costs, and by exactly the inverse of the rate it adds code. If you double your output and your cost of maintaining that output, two times two means you’ve quadrupled your maintenance costs. If you double your output and hold your maintenance costs steady, two times one means you’ve &lt;em&gt;still&lt;/em&gt; doubled your maintenance costs.&lt;/p&gt;

&lt;p&gt;Instead, you have to invert your productivity. If you’re producing twice as much code, you need code that costs half as much to maintain. Three times as much code, one third the maintenance.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This&lt;/em&gt; is the secret to success. All the benefits, none of the lock-in.&lt;/p&gt;

&lt;p&gt;
	&lt;img class=&quot;bqrUnknownImgSize&quot; src=&quot;https://www.jamesshore.com/images/maintenance-costs/ai-half-costs-removed.png&quot; width=&quot;100%&quot; alt=&quot;This graph shows the same thick blue “normal” line as the others. This time, though, the thin red line doesn’t fall below the blue line. The red line is labelled “AI Doubles Prod, Halves Maint, Removed.” At the 36 month mark, it jumps up to about 85% productivity, as in the other graphs, at a point that’s labelled “AI introduced.” Then it stays well above the blue line, falling on a similar, but slightly steeper curve. At the 84 month mark, it falls back down to exactly track the blue line at a point that’s labelled “AI removed.”&quot;&gt;
&lt;/p&gt;


&lt;h3&gt;Can We Kill the Beast?&lt;/h3&gt;

&lt;p&gt;I dunno. All my reading of &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://news.ycombinator.com/&quot;&gt;the finest news sources&lt;/a&gt; says that coding agents &lt;em&gt;increase&lt;/em&gt; maintenance costs. Some people do say they help them understand large systems better. But big decreases in costs, of the size we need to see? No. Just the opposite.&lt;/p&gt;

&lt;p&gt;That’s a problem. The model isn’t a perfect representation of reality, but the overall message is right. You need AI that reduces your maintenance costs, and in proportion to the speed boost you get from new code. Without it, you’re screwed. You’re trading a temporary speed boost for permanent indenture.&lt;/p&gt;

&lt;p&gt;So, yeah, go ahead, chase improvements to your coding speed. But spend just as much time chasing improvements to your maintenance costs. Or you, too, will be trapped in Hotel California.&lt;/p&gt;

&lt;p&gt;Such a lovely place.&lt;/p&gt;

&lt;p&gt;Such a lovely face.&lt;/p&gt;



&lt;p&gt;As much as it might seem like it, this isn’t meant to be an anti-AI rant. There’s other levers to pull, such as AI that makes maintenance itself more productive, even if it doesn’t make the &lt;em&gt;code&lt;/em&gt; more maintainable. I encourage you to &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; href=&quot;https://docs.google.com/spreadsheets/d/109XUgvOMClSoknyGr9MtjWvNSfdf6XYiD5fnSvGYTTU/edit&quot;&gt;copy the spreadsheet&lt;/a&gt; and play with all the levers in the model. See what happens when you change the assumptions to match your real-world situation.&lt;/p&gt;</summary><author><name></name></author><source gr:stream-id="feed/http://www.jamesshore.com/index.rss"><id>tag:google.com,2005:reader/feed/http://www.jamesshore.com/index.rss</id><title type="html">James Shore</title><link rel="alternate" href="https://www.jamesshore.com" type="text/html"></link></source></entry><entry gr:crawl-timestamp-msec="1778450400000"><id gr:original-id="https://scrolltest.com/?p=7338">tag:google.com,2005:reader/item/00000444000001b9</id><category term="Performance Testing"></category><category term="Testing"></category><title type="html">k6 Performance Testing From Zero to Production: A JavaScript Developer’s Complete Guide</title><published>2026-05-10T22:00:00Z</published><updated>2026-05-10T22:00:00Z</updated><link rel="alternate" href="https://scrolltest.com/k6-performance-testing-javascript-guide/" type="text/html"></link><summary type="html">&lt;p&gt;Modern load testing with k6: JavaScript-based, CI/CD native, Grafana ready. Complete guide from first test to stress testing and GitHub Actions integration.&lt;/p&gt;
&lt;p&gt;The post &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; rel=&quot;nofollow&quot; href=&quot;https://scrolltest.com/k6-performance-testing-javascript-guide/&quot;&gt;k6 Performance Testing From Zero to Production: A JavaScript Developer’s Complete Guide&lt;/a&gt; appeared first on &lt;a target=&quot;_blank&quot; rel=&quot;noopener&quot; rel=&quot;nofollow&quot; href=&quot;https://scrolltest.com&quot;&gt;Software Testing &amp;amp; Automation&lt;/a&gt;.&lt;/p&gt;</summary><author><name>Pramod Dutta</name></author><source gr:stream-id="feed/https://scrolltest.com/feed/"><id>tag:google.com,2005:reader/feed/https://scrolltest.com/feed/</id><title type="html">Software Testing &amp; Automation</title><link rel="alternate" href="https://scrolltest.com" type="text/html"></link></source></entry></feed>