← back to Commercialrealestate
5x night item 1: map.html — aerial-img onerror graceful fallback + strong map-assert.js gate (markers/toggles/popup-decode/empty-data); map verified 6/6 + strong PASS
f98b8afb018211228c5f734420dbe2cb1ef4ce79 · 2026-06-28 23:01:12 -0700 · Steve
Files touched
A 5x/map-assert.jsA 5x/map-sweep-1.rawM public/map.html
Diff
commit f98b8afb018211228c5f734420dbe2cb1ef4ce79
Author: Steve <steve@designerwallcoverings.com>
Date: Sun Jun 28 23:01:12 2026 -0700
5x night item 1: map.html — aerial-img onerror graceful fallback + strong map-assert.js gate (markers/toggles/popup-decode/empty-data); map verified 6/6 + strong PASS
---
5x/map-assert.js | 37 +++++++++++++++++++++++++++++++++++++
5x/map-sweep-1.raw | 15 +++++++++++++++
public/map.html | 2 +-
3 files changed, 53 insertions(+), 1 deletion(-)
diff --git a/5x/map-assert.js b/5x/map-assert.js
new file mode 100644
index 0000000..7dbfe9d
--- /dev/null
+++ b/5x/map-assert.js
@@ -0,0 +1,37 @@
+// Strong map.html gate: markers drew per type, legend toggles add/remove layers, popup aerial decodes to
+// real pixels, and empty-data degrades (no markers, no JS error). What /3x's --selector cannot assert.
+const pw=require('/Users/stevestudio2/.claude/skills/browserbase/node_modules/playwright-core');
+const EXEC='/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
+async function run(label, mockEmpty){
+ const errors=[]; const b=await pw.chromium.launch({executablePath:EXEC,headless:true});
+ const pg=await b.newPage({viewport:{width:1280,height:820}});
+ pg.on('pageerror',e=>errors.push('PAGEERR: '+e.message));
+ pg.on('console',m=>{if(m.type()==='error'&&!/Failed to load resource/.test(m.text()))errors.push('CONSOLE: '+m.text());});
+ if(mockEmpty) await pg.route('**/data/map-points.json',r=>r.fulfill({status:200,contentType:'application/json',body:'{"points":[]}'}));
+ await pg.goto('http://127.0.0.1:9911/map.html',{waitUntil:'networkidle'}); await pg.waitForTimeout(2500);
+ const base=await pg.evaluate(()=>({legend:document.querySelectorAll('#legend label').length,
+ layerTypes:Object.keys(layers||{}).length,
+ markers:Object.values(layers||{}).reduce((n,l)=>n+l.getLayers().length,0)}));
+ let toggleOk=true, popup={};
+ if(!mockEmpty){
+ const labs=await pg.$$('#legend label');
+ for(const lab of labs){ const t=await lab.evaluate(e=>e.querySelector('input').dataset.t);
+ await lab.click(); await pg.waitForTimeout(80); const off=await pg.evaluate(t=>map.hasLayer(layers[t]),t);
+ await lab.click(); await pg.waitForTimeout(80); const on=await pg.evaluate(t=>map.hasLayer(layers[t]),t);
+ if(off||!on) toggleOk=false; }
+ popup=await pg.evaluate(async()=>{let tgt=null;for(const t in layers){layers[t].eachLayer(l=>{if(!tgt)tgt=l});if(tgt)break;}
+ if(!tgt)return{open:false}; tgt.openPopup(); await new Promise(r=>setTimeout(r,700));
+ const img=document.querySelector('.leaflet-popup img'); return{open:!!document.querySelector('.leaflet-popup'),hasImg:!!img};});
+ if(popup.hasImg){ await pg.waitForTimeout(1500);
+ popup.decoded=await pg.evaluate(()=>{const i=document.querySelector('.leaflet-popup img');return i.complete&&i.naturalWidth>0;}); }
+ }
+ await b.close();
+ return {label, ...base, toggleOk, popup, jsErrors:errors};
+}
+(async()=>{
+ const normal=await run('NORMAL',false); const empty=await run('EMPTY',true);
+ const passNormal = normal.markers>1000 && normal.legend>=4 && normal.toggleOk && normal.popup.open && normal.popup.decoded && normal.jsErrors.length===0;
+ const passEmpty = empty.markers===0 && empty.jsErrors.length===0;
+ console.log(JSON.stringify({normal,empty,passNormal,passEmpty,VERDICT:(passNormal&&passEmpty)?'PASS':'FAIL'},null,2));
+ process.exit((passNormal&&passEmpty)?0:1);
+})().catch(e=>{console.error('FAIL',e.message);process.exit(1);});
diff --git a/5x/map-sweep-1.raw b/5x/map-sweep-1.raw
new file mode 100644
index 0000000..f9ad8fb
--- /dev/null
+++ b/5x/map-sweep-1.raw
@@ -0,0 +1,15 @@
+
+/3x — verifying http://127.0.0.1:9911/map.html
+
+Phase A — 3 unique methods:
+ [PASS] M1 HTTP contract request layer — HTTP 200 text/html
+ [PASS] M2 headless render render layer — screenshot 2201KB → /var/folders/2n/jmw9_4v56_x4m3539tr4v0lr0000gn/T/3x-B6UBTO/render.png
+ [PASS] M3 automation E2E interaction layer — 5×"#legend label", 0 JS errors
+
+Phase B — 3 browsers (cross-browser):
+ [PASS] B4 Google Chrome cross-browser — automated render OK
+ [PASS] B5 Safari cross-browser — automated render OK
+ [PASS] B6 Firefox cross-browser — automated render OK
+
+ RESULT → 6/6 passed
+ artifacts: /var/folders/2n/jmw9_4v56_x4m3539tr4v0lr0000gn/T/3x-B6UBTO
diff --git a/public/map.html b/public/map.html
index 715541a..aaa5f20 100644
--- a/public/map.html
+++ b/public/map.html
@@ -72,7 +72,7 @@ function popupHtml(p){
return `<div class="pop"><h3>${p.address||'(address n/a)'}</h3><div class="ad">${p.city||''} ${p.zip||''}</div>`+
`<div class="row"><span class="tag" style="border-color:${COLORS[p.type]||'#888'}">${p.type}</span>`+
(p.price?`<span class="tag price">${money(p.price)}</span>`:'')+`</div>`+
- `<img loading="lazy" src="${aerialUrl(p.lat,p.lng)}" alt="aerial view">`+
+ `<img loading="lazy" src="${aerialUrl(p.lat,p.lng)}" alt="aerial view" onerror="this.style.display='none';this.nextElementSibling.textContent='Aerial imagery unavailable for this point';">`+
`<div class="aerialcap">Overhead imagery © Esri World Imagery (free)</div>`+
`<div class="links">`+
(p.source?`<a href="${p.source}" target="_blank" rel="noopener">View listing ↗</a>`:'')+
← 14b36ac 5x: overnight refinement night-log + queue
·
back to Commercialrealestate
·
5x night item 3: wire nonqm segment agent count (union of no b6a5bdd →