<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Laurent</title>
    <description>The latest articles on DEV Community by Laurent (@booyakasha).</description>
    <link>https://dev.to/booyakasha</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2940379%2Fd4f8bf9e-486c-433d-8343-7d7a2afec732.png</url>
      <title>DEV Community: Laurent</title>
      <link>https://dev.to/booyakasha</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/booyakasha"/>
    <language>en</language>
    <item>
      <title>Flowbook: versioned YAML playbooks for the product journeys your tests and API docs both miss</title>
      <dc:creator>Laurent</dc:creator>
      <pubDate>Wed, 07 Oct 2026 02:01:41 +0000</pubDate>
      <link>https://dev.to/booyakasha/flowbook-versioned-yaml-playbooks-for-the-product-journeys-your-tests-and-api-docs-both-miss-44bc</link>
      <guid>https://dev.to/booyakasha/flowbook-versioned-yaml-playbooks-for-the-product-journeys-your-tests-and-api-docs-both-miss-44bc</guid>
      <description>&lt;p&gt;Most web apps have a handful of journeys that actually pay the bills: a user signs up, verifies their email, pays, and gets access to the premium thing. If one of those breaks, you hear about it from customers.&lt;/p&gt;

&lt;p&gt;Yet when I look for where those journeys are &lt;em&gt;written down&lt;/em&gt;, I usually find two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;E2E test dumps&lt;/strong&gt; (Cypress, Playwright, recorded scripts) — they exercise the journey, but they're long, selector-heavy, and hard for anyone outside the test author to read or review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API specs&lt;/strong&gt; (OpenAPI) — precise about each endpoint, but silent about the &lt;em&gt;order&lt;/em&gt; things happen in and what "success" means for the business.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither one answers the simple question: &lt;em&gt;"What is our signup-to-paid flow, step by step, and does it still work?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That's the gap &lt;strong&gt;Flowbook&lt;/strong&gt; tries to fill.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Flowbook is
&lt;/h2&gt;

&lt;p&gt;Flowbook is a small open-source CLI that runs &lt;strong&gt;versioned YAML playbooks of business journeys&lt;/strong&gt;. A playbook is a short, reviewable file in your repo that describes a flow as a sequence of steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;http&lt;/code&gt; — make a request, check the status, capture values from the response&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;assert&lt;/code&gt; — check status / JSON / captured variables&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser&lt;/code&gt; — drive Chromium via Playwright (&lt;code&gt;goto&lt;/code&gt;, &lt;code&gt;fill&lt;/code&gt;, &lt;code&gt;click&lt;/code&gt;, &lt;code&gt;waitFor&lt;/code&gt;, &lt;code&gt;capture&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea is that the playbook is the shared artifact: product, backend and QA can all read it in a PR, and CI can run it.&lt;/p&gt;

&lt;p&gt;The MVP focuses on one journey that almost every SaaS has: &lt;strong&gt;signup → email verify → checkout (Stripe-style test payment) → assert premium access&lt;/strong&gt;, either via the API or through the UI.&lt;/p&gt;

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

&lt;p&gt;The npm package is &lt;code&gt;flowbook-cli&lt;/code&gt;; the binary it installs is &lt;code&gt;flowbook&lt;/code&gt;. Node 18+.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# one-shot, no global install&lt;/span&gt;
npx flowbook-cli validate path/to/playbook.yaml
npx flowbook-cli run path/to/playbook.yaml

&lt;span class="c"&gt;# or install it — the command is `flowbook`&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; flowbook-cli
flowbook run path/to/playbook.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use &lt;code&gt;browser&lt;/code&gt; steps, install Chromium once (it's not downloaded on install, to keep the package light):&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  A playbook, end to end (HTTP)
&lt;/h2&gt;

&lt;p&gt;Here's the HTTP version of the MVP journey, trimmed slightly from the repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;auth-signup-checkout&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Signup → email verify → mock Stripe checkout → assert premium access&lt;/span&gt;
&lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${BASE_URL:-http://127.0.0.1:3847}&lt;/span&gt;
&lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo+${TIMESTAMP}@example.com&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;secret123&lt;/span&gt;
&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;signup&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POST&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/signup&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${email}&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${password}&lt;/span&gt;
    &lt;span class="na"&gt;expect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;201&lt;/span&gt;
    &lt;span class="na"&gt;capture&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;verifyToken&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$.verifyToken&lt;/span&gt;
      &lt;span class="na"&gt;sessionToken&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$.sessionToken&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;verify&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POST&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/verify&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${verifyToken}&lt;/span&gt;
    &lt;span class="na"&gt;expect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;checkout&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POST&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/checkout&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bearer ${sessionToken}&lt;/span&gt;
    &lt;span class="na"&gt;expect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;assert-premium&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;assert&lt;/span&gt;
    &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/me&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bearer ${sessionToken}&lt;/span&gt;
    &lt;span class="na"&gt;expect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;
      &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;plan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;premium&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things worth noting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;${var}&lt;/code&gt; pulls from &lt;code&gt;vars&lt;/code&gt;, earlier &lt;code&gt;capture&lt;/code&gt;s, or environment variables; &lt;code&gt;${VAR:-default}&lt;/code&gt; gives a fallback.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;${TIMESTAMP}&lt;/code&gt; and &lt;code&gt;${UUID}&lt;/code&gt; are built in, so every run gets a fresh user.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;capture&lt;/code&gt; uses simple JSONPath (&lt;code&gt;$.field&lt;/code&gt;, &lt;code&gt;$.a.b[0]&lt;/code&gt;) to carry tokens forward.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can point the same file at another environment without editing it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://staging.example.com npx flowbook-cli run playbooks/auth-signup-checkout.yaml
&lt;span class="c"&gt;# or&lt;/span&gt;
npx flowbook-cli run playbooks/auth-signup-checkout.yaml &lt;span class="nt"&gt;--base-url&lt;/span&gt; https://staging.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The same journey through the UI
&lt;/h2&gt;

&lt;p&gt;Some flows can't be trusted from the API alone — the button might be broken even if the endpoint works. A &lt;code&gt;browser&lt;/code&gt; step runs the journey in headless Chromium:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ui-journey&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;browser&lt;/span&gt;
    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;goto&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#email'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#password'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#signup-btn'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;waitFor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#verify-token'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;capture&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;verifyToken&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#verify-token'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;attr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;data-verify-token&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;goto&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/verify&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#token'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;verifyToken&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#verify-btn'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;waitFor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/checkout'&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#checkout-btn'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;waitFor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/account'&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;capture&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;plan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#account-plan'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;attr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;data-plan&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;assert-premium-plan&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;assert&lt;/span&gt;
    &lt;span class="na"&gt;expect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;plan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;premium&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set &lt;code&gt;FLOWBOOK_HEADED=1&lt;/code&gt; (or &lt;code&gt;headed: true&lt;/code&gt;) to watch it run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it locally in two minutes
&lt;/h2&gt;

&lt;p&gt;The repo ships a tiny Express demo app (in-memory, no database, no real Stripe keys — checkout is mocked) so you can see a full run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/WhoIsLoH/flowbook &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;flowbook
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npx playwright &lt;span class="nb"&gt;install &lt;/span&gt;chromium
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--prefix&lt;/span&gt; examples/demo-app
npm run demo            &lt;span class="c"&gt;# starts the demo app on :3847&lt;/span&gt;

&lt;span class="c"&gt;# in another terminal&lt;/span&gt;
npm run flowbook:http
npm run flowbook:browser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both playbooks are validated and run in GitHub Actions on every push, and CI is green.&lt;/p&gt;

&lt;p&gt;If you'd rather not install anything yet, there's an interactive demo in the browser: &lt;a href="https://whoisloh.github.io/flowbook/demo.html" rel="noopener noreferrer"&gt;https://whoisloh.github.io/flowbook/demo.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it fits (and where it doesn't)
&lt;/h2&gt;

&lt;p&gt;Flowbook is &lt;strong&gt;not&lt;/strong&gt; a replacement for your unit tests or your full E2E suite. Think of it as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;living spec&lt;/strong&gt; for the 3–10 journeys that matter most to the business,&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;smoke check&lt;/strong&gt; you can run against local, staging, or a preview deploy,&lt;/li&gt;
&lt;li&gt;a place to encode &lt;strong&gt;payment test flows&lt;/strong&gt; (e.g. a Stripe test-mode checkout) as steps someone can actually read in review.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's an early &lt;code&gt;0.1.0&lt;/code&gt;. Right now that means sequential steps, &lt;code&gt;http&lt;/code&gt; / &lt;code&gt;assert&lt;/code&gt; / &lt;code&gt;browser&lt;/code&gt; step types, and one reference journey. I'd love feedback on what the next step types or journeys should be — real Stripe test-mode examples, magic-link email verification, team invites, plan downgrades…&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/WhoIsLoH/flowbook" rel="noopener noreferrer"&gt;https://github.com/WhoIsLoH/flowbook&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/flowbook-cli" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/flowbook-cli&lt;/a&gt; (&lt;code&gt;npx flowbook-cli&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Landing page: &lt;a href="https://whoisloh.github.io/flowbook/" rel="noopener noreferrer"&gt;https://whoisloh.github.io/flowbook/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Interactive demo: &lt;a href="https://whoisloh.github.io/flowbook/demo.html" rel="noopener noreferrer"&gt;https://whoisloh.github.io/flowbook/demo.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Issues and PRs welcome. If you have a journey that keeps breaking in your app, open an issue describing it — that's exactly the kind of playbook I want to support.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;🇫🇷 Petite note en français :&lt;/strong&gt; Flowbook décrit vos parcours métier clés (inscription → vérification email → paiement → accès premium) en playbooks YAML versionnés, exécutables en HTTP ou dans un vrai navigateur (Playwright). Essayez &lt;code&gt;npx flowbook-cli&lt;/code&gt; — retours bienvenus sur GitHub.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>playwright</category>
      <category>devops</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
