// Exercises actual PeerFiles, dashboard wrapper, shared styles and fade directive. // Synthetic catalog only; no live app, wallet, payment or node requests. const { chromium, expect } = require('../../neode-ui/node_modules/@playwright/test'); const fs = require('node:fs'); const path = require('node:path'); const base = process.env.PEER_SCROLL_TEST_URL || 'http://127.0.0.1:5198'; const output = process.env.PEER_SCROLL_EVIDENCE || '/tmp/archy-peer-scroll'; fs.mkdirSync(output, {recursive:true,mode:0o700}); const peer = 'a'.repeat(56) + '.onion'; (async()=>{ // Vite resolves dependency URLs for this exact checkout. const source = await fetch(base+'/src/stores/resources.ts').then(r=>r.text()); const vue = source.match(/from "([^"]*\/vue\.js[^" ]*)"/)[1]; const peerSource = await fetch(base+'/src/views/PeerFiles.vue').then(r=>r.text()); const routerUrl = peerSource.match(/from "([^"]*vue-router\.js[^" ]*)"/)[1]; const pinia = source.match(/from "([^"]*pinia[^" ]*\.js[^" ]*)"/)[1]; const browser = await chromium.launch({headless:true}); const results=[]; try { for (const width of [390,844,1440]) { const page = await browser.newPage({viewport:{width,height:900},hasTouch:width<1000}); const errors=[];page.on('pageerror',e=>{errors.push(e.message);console.error('PAGE',e.message)}); page.on('console',m=>{if(m.type()==='error')console.error('CONSOLE',m.text())}); await page.route('**/peer-scroll-fixture',route=>route.fulfill({contentType:'text/html',body:`
`.replace('width<768',`${width}<768`)})); // Every HTTP request stays on the fixture Vite origin. await page.route('**/*',route=>{ if(new URL(route.request().url()).origin!==base)return route.abort(); return route.fallback(); }); await page.goto(base+'/peer-scroll-fixture'); await page.waitForSelector('.peer-files-scroll .glass-card',{timeout:45000}); const scroll=page.locator('.peer-files-scroll'); const header=page.locator('.peer-files-identity'); const back=page.getByRole('button',{name:'Back to Cloud',exact:true}).filter({visible:true}); await expect(back).toBeVisible(); const before=await header.boundingBox();const beforeBack=await back.boundingBox(); const initial=await scroll.evaluate(el=>({height:el.clientHeight,content:el.scrollHeight,mask:el.style.maskImage,outer:el.closest('.dashboard-scroll-panel').scrollTop})); if(initial.height<160||initial.content<=initial.height)throw Error('Missing constrained scrolling viewport '+JSON.stringify(initial)); if(!initial.mask.includes('black 0px')||!initial.mask.includes('18px'))throw Error('Initial bottom fade absent'); await scroll.evaluate(el=>{el.scrollTop=350;el.dispatchEvent(new Event('scroll'))}); await page.waitForTimeout(100); const after=await header.boundingBox();const afterBack=await back.boundingBox(); for(const key of ['x','y','width','height']){ if(Math.abs(before[key]-after[key])>1)throw Error('Header moved: '+key); if(Math.abs(beforeBack[key]-afterBack[key])>1)throw Error('Back button moved: '+key); } const mid=await scroll.evaluate(el=>({top:el.scrollTop,mask:el.style.maskImage,outer:el.closest('.dashboard-scroll-panel').scrollTop,rect:el.getBoundingClientRect().toJSON()})); if(mid.top<300||mid.outer!==initial.outer||!mid.mask.includes('black 18px'))throw Error('Wrong scroll ownership or top fade'); await page.screenshot({path:path.join(output,`peer-scroll-${width}.png`)}); await scroll.evaluate(el=>{el.scrollTop=el.scrollHeight;el.dispatchEvent(new Event('scroll'))}); const last=await scroll.evaluate(el=>({mask:el.style.maskImage,remaining:el.scrollHeight-el.clientHeight-el.scrollTop})); if(last.remaining>1||!/100% [+-] 0px/.test(last.mask))throw Error('Bottom fade not cleared at end '+JSON.stringify(last)); const isolation=await page.evaluate(()=>{const panel=document.createElement('div');panel.className='dashboard-scroll-panel';panel.style.overflowY='auto';panel.innerHTML='';document.body.append(panel);const value=getComputedStyle(panel).overflowY;panel.remove();return value}); if(isolation!=='auto')throw Error('Unrelated page scroll changed'); const horizontal=await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth); if(horizontal)throw Error('Horizontal overflow'); if(errors.length)throw Error(errors.join('\n')); results.push({width,height:900,passed:true,initial,mid,last,header:after,back:afterBack}); await page.close(); } } finally {await browser.close();fs.writeFileSync(path.join(output,'result.json'),JSON.stringify(results,null,2));} console.log(JSON.stringify({passed:true,viewports:results.map(r=>r.width),evidence:output})); })().catch(e=>{console.error(e);process.exitCode=1});