Flash Message Request Lab — Trace a small categorized flash message through a request, redirect and the next consuming render, then check that a later request has no…

HTML for this example

Select the code and use your copy shortcut. Changes in this preview are temporary.

<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Flash Message Request Lab</title><style>:root{--bg:#f4f5f7;--ink:#253043;--accent:#465b89;--line:#ccd3df;--wash:#e9edf6;}*{box-sizing:border-box}body{margin:0;font:16px/1.5 Arial,Helvetica,sans-serif;color:var(--ink);background:var(--bg)}main{max-width:1120px;padding:24px;margin:auto}h1,h2,h3,p{margin:0}h1{font-size:clamp(26px,4vw,38px);line-height:1.15;letter-spacing:-.03em}h2{font-size:23px}h3{font-size:18px}header{margin-bottom:20px}header p{margin-top:8px}.eyebrow{text-transform:uppercase;letter-spacing:.13em;font-size:12px;font-weight:bold;color:var(--accent)}button,input,select,textarea{font:inherit}button{cursor:pointer;min-height:44px;padding:9px 15px;border:1px solid var(--accent);background:var(--accent);color:white;border-radius:7px;font-weight:600}button.secondary{background:white;color:var(--accent)}button:disabled{opacity:.45;cursor:default}input,select,textarea{width:100%;min-height:44px;border:1px solid #a2aaba;border-radius:5px;background:white;color:var(--ink);padding:9px}textarea{resize:vertical;min-height:95px}label{display:block;font-size:14px;font-weight:bold;margin:12px 0 5px}input[type=checkbox]{width:20px;min-height:20px;vertical-align:middle;margin-right:8px}:focus-visible{outline:3px solid #df7a16;outline-offset:3px}.actions{display:flex;gap:9px;flex-wrap:wrap;margin:14px 0}.panel{padding:22px;background:white;border:1px solid var(--line);border-radius:12px;min-width:0}.columns{display:grid;grid-template-columns:1.6fr 1fr;gap:20px}.two{display:grid;grid-template-columns:1fr 1fr;gap:18px}.stats{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:15px}.stat{flex:1;min-width:92px;background:var(--wash);padding:10px 14px;border-radius:8px}.stat small{display:block}.stat strong{font-size:25px}.muted{color:#526178;font-size:14px}.notice{padding:14px 16px;background:var(--wash);border-left:4px solid var(--accent);margin:14px 0;overflow-wrap:anywhere}.error{color:#a12e25;font-weight:bold}.below{margin-top:28px;padding-top:22px;border-top:1px solid var(--line)}.below p{margin-top:9px}svg.board{width:100%;display:block;border-radius:10px;border:1px solid var(--line)}table{border-collapse:collapse;width:100%;font-size:14px}td,th{text-align:left;padding:10px;border-bottom:1px solid var(--line);vertical-align:top;overflow-wrap:anywhere}th{font-size:12px;text-transform:uppercase;letter-spacing:.04em}pre{white-space:pre-wrap;overflow-wrap:anywhere;font:14px/1.6 ui-monospace,monospace}.scroll{overflow-x:auto}.tag{font-size:12px;padding:4px 8px;background:var(--wash);border-radius:4px}.big{font-size:clamp(36px,6vw,62px);font-weight:bold;line-height:1.15}.foot{font-size:14px;color:#526178;margin-top:25px}details{margin:18px 0}summary{cursor:pointer;font-weight:bold;min-height:44px;padding:10px 0}[hidden]{display:none!important}@media(max-width:800px){.columns{grid-template-columns:1fr}.two{grid-template-columns:1fr}}@media(max-width:480px){main{padding:18px 14px}.panel{padding:16px}.stat{min-width:78px;padding:8px}.actions button{flex-grow:1}td,th{padding:7px}.big{font-size:38px}}.request-rail{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin:18px 0}.request-card{padding:18px;border:1px solid var(--line);border-top:4px solid var(--accent);background:white;border-radius:7px}.request-card strong{font:600 23px ui-monospace,monospace}.request-card p{font-size:14px;margin-top:8px}.render-window{border:1px solid #bec9da;border-radius:8px;overflow:hidden;background:white}.window-bar{background:#e4e9f1;padding:10px 15px;font:13px ui-monospace,monospace}.render-body{min-height:170px;padding:24px}.render-body h3{margin-bottom:16px}#notice-text{white-space:pre-wrap;overflow-wrap:anywhere;font-size:21px}#raw-output{font:14px/1.7 ui-monospace,monospace;min-height:120px}#request-log{max-height:330px;overflow:auto}.request-line{padding:14px;border-bottom:1px solid var(--line);white-space:pre-wrap;font:13px/1.6 ui-monospace,monospace}.fixed-label{display:inline-block;font-size:12px;font-weight:bold;background:#e4eaf6;padding:4px 8px;margin-bottom:8px;border-radius:4px}#plan{height:250px;font:13px/1.6 ui-monospace,monospace}@media(max-width:600px){.request-rail{grid-template-columns:1fr}.request-card{padding:13px}.request-card p{margin-top:3px}}</style></head><body><main><header><p class="eyebrow">Local model · native Flask evidence stays separate</p><h1>Follow the notice across requests</h1><p>Stage, retrieve once for display, then inspect a separate later request.</p></header><div class="request-rail"><article class="request-card"><strong>Q1 → 302</strong><p>Stage fixture and redirect to /demo/read. Redirect alone does not consume it.</p></article><article class="request-card"><strong>Q2 → one notice</strong><p>Retrieve with categories. Repeated retrieval in Q2 returns the same collection.</p></article><article class="request-card"><strong>Q3 → no old notice</strong><p>A separate later reading request sees no old fixture after consumption.</p></article></div><div class="columns"><section><div class="render-window"><div class="window-bar" id="request-title">Modeled Q2 · /demo/read</div><div class="render-body"><h3>Reading route preview</h3><span class="fixed-label" id="presentation">Notice</span><p id="notice-text">Demo review queued</p></div></div><div class="actions"><button type="button" id="stage">Stage a new model sequence</button><button type="button" id="read" class="secondary">New reading request</button><button type="button" id="repeat" class="secondary">Retrieve again in same request</button><button type="button" id="worked" class="secondary">Load worked trace</button></div><p id="status" class="notice" role="status">Worked trace: Q2 renders one notice; Q3 retrieves none.</p><h3>Escaped HTML representation · local model</h3><pre class="panel" id="raw-output"></pre></section><aside class="panel"><h2>Fixture controls</h2><label for="message">Message text</label><input id="message" value="Demo review queued"><label for="category">Category text</label><input id="category" value="notice"><label><input type="checkbox" id="consume" checked>Reading route retrieves messages</label><p class="muted">Disable retrieval to see why an empty-looking render alone is not proof of consumption.</p><div class="actions"><button type="button" id="escaping" class="secondary">Use literal-tag fixture</button><button type="button" id="reset" class="secondary">Reset model</button></div><p>Fixed presentation labels: notice → Notice, message → Message, anything else → Feedback. Category input never becomes a class or style.</p></aside></div><section class="below"><h2>Modeled request ledger</h2><div id="request-log" class="panel"></div><h2 style="margin-top:24px">Native acceptance plan · manual copy</h2><label for="plan">Expected native evidence and blank actual fields</label><textarea id="plan" readonly></textarea><div class="actions"><button type="button" id="select-plan" class="secondary">Select acceptance plan</button></div><p id="copy-status" class="muted" role="status"></p><p class="foot">This JavaScript companion models the authored sequence; it does not run Flask, Jinja, Python, session cookies or a job queue. “Demo review queued” is fictional feedback. Actual native status/location, installed version, context/session setup, escaping and test-client behavior remain Not tested. A model reset does not reset a server session. Native app/template/test evidence remains authoritative; no credentials, authentication, database, network or persistence.</p></section></main><script>'use strict';const $=id=>document.getElementById(id);let queue=[],cache=null,requests=[],requestNo=0,current=null;const escapeHtml=v=>String(v).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');const label=c=>c==='notice'?'Notice':c==='message'?'Message':'Feedback';
function stage(){if(!$('message').value.trim()){$('status').textContent='Enter nonblank fixture text before staging.';return;}queue=[[$('category').value,$('message').value]];cache=null;requests=[];requestNo=1;current={id:'Q1',route:'/demo/stage',status:302,location:'/demo/read',tuples:[],display:[],note:'Fixture staged; redirect does not consume.'};requests.push(current);render();$('status').textContent='Q1 modeled: 302 → /demo/read. One fixture remains queued.';}
function read(){if(!current){$('status').textContent='Stage a model sequence before reading.';return;}requestNo++;cache=null;const consume=$('consume').checked;if(consume){cache=queue.slice();queue=[];}current={id:'Q'+requestNo,route:'/demo/read',status:200,location:'—',tuples:consume?cache.slice():[],display:consume?cache.slice():[],note:consume?'Retrieved with categories; render captured collection once.':'Retrieval disabled; queued fixture is not consumed.'};requests.push(current);render();$('status').textContent=current.id+' modeled: '+(consume?(cache.length+' retrieved; '+cache.length+' rendered. Teacher/backend evidence is not implied.'):'no retrieval; '+queue.length+' fixture remains queued.');}
function repeat(){if(!current||current.route!=='/demo/read'||!$('consume').checked){$('status').textContent='A consuming reading request is required for same-request retrieval.';return;}if(cache===null){cache=queue.slice();queue=[];}current.note='Same-request retrieval reused '+cache.length+' tuple(s); display still uses one captured collection.';render();$('status').textContent=current.id+' same-request retrieval: '+cache.length+' tuple(s), not a new request. Display is not duplicated.';}
function render(){$('request-title').textContent=current?'Modeled '+current.id+' · '+current.route:'No modeled request';const display=current?.display||[];$('presentation').hidden=!display.length;$('presentation').textContent=display.length?label(display[0][0]):'Feedback';$('notice-text').textContent=display.length?display[0][1]:'No notice displayed.';$('raw-output').textContent=display.length?'<p class="feedback">'+escapeHtml(display[0][1])+'</p>':'<!-- No notice rendered -->';$('request-log').textContent='';for(const r of requests){const row=document.createElement('div');row.className='request-line';row.textContent=r.id+' · modeled status '+r.status+' · '+r.route+'\nLocation: '+r.location+'\nTuples: '+JSON.stringify(r.tuples)+'\nRendered count: '+r.display.length+'\n'+r.note;$('request-log').append(row);}if(!requests.length)$('request-log').textContent='No modeled requests yet.';}
function worked(){$('message').value='Demo review queued';$('category').value='notice';$('consume').checked=true;stage();read();read();const q2=requests[1];current=q2;cache=q2.tuples.slice();render();$('status').textContent='Worked trace: Q2 renders one notice; Q3 retrieves none. Preview is Q2.';}
function reset(){queue=[];cache=null;requests=[];requestNo=0;current=null;$('message').value='Demo review queued';$('category').value='notice';$('consume').checked=true;render();$('status').textContent='Model reset. No server session was changed.';}
$('stage').addEventListener('click',stage);$('read').addEventListener('click',read);$('repeat').addEventListener('click',repeat);$('worked').addEventListener('click',worked);$('reset').addEventListener('click',reset);$('escaping').addEventListener('click',()=>{$('message').value='<em>Demo</em>';$('category').value='notice';stage();read();});for(const id of ['message','category'])$(id).addEventListener('input',()=>{queue=[];cache=null;requests=[];requestNo=0;current=null;render();$('status').textContent='Fixture edited — stage a new sequence; old model evidence cleared.';});$('consume').addEventListener('change',()=>{$('status').textContent='Retrieval setting changed. Stage a fresh sequence to compare the full lifecycle.';});$('plan').value='NATIVE FLASK/JINJA ACCEPTANCE PLAN — REVISION 1\nInstalled Flask version: Not tested (reference: 3.1.x docs)\nClient/session identity and cookie setup: Not tested\nIsolated test configuration/secret source: Not tested; never publish credentials\nRoute/template revision: Not tested\n\nQ1 /demo/stage: flash (notice, Demo review queued); expected redirect 302 Location /demo/read (chosen model contract). Actual status/location: Not tested\nQ2 /demo/read same client: get_flashed_messages(with_categories=True); expected one tuple and one rendered notice. Actual tuple/render: Not tested\nQ2 repeated helper call: expected same cached collection, not an empty later request. Actual: Not tested\nQ3 separate /demo/read without restaging: expected no old fixture after Q2 retrieval. Actual: Not tested\n\nSeparate fixture: <em>Demo</em> must appear literally under HTML autoescaping. Verify escaped raw HTML and absence of an em node. Actual: Not tested\nCategory mapping: notice / message / fallback only; untrusted category never supplies raw class/style. Actual: Not tested\nMissing context/session errors: retain actual error; do not manufacture success. Actual: Not tested\n\nCapture retrieved messages once for a single presentation block. Redirect alone is not consumption. Do not use safe/Markup/autoescape-off. No browser storage stands in for Flask transport.';$('select-plan').addEventListener('click',()=>{$('plan').focus();$('plan').select();$('copy-status').textContent='Acceptance plan selected. Use your device’s copy command and paste manually.';});globalThis.app={stage,read,repeat,worked,reset,label,escapeHtml,get:()=>({queue,cache,requests,current,requestNo})};worked();</script></body></html>