File size: 7,620 Bytes
85920d3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import asyncio, json, pathlib, csv
from playwright.async_api import async_playwright

ROOT=pathlib.Path(__file__).parent
async def main():
    async with async_playwright() as p:
        browser=await p.chromium.launch(headless=True,executable_path='/usr/bin/chromium',args=['--no-sandbox'])
        page=await browser.new_page(viewport={'width':1512,'height':1060},accept_downloads=True)
        errors=[]
        page.on('pageerror', lambda exc: errors.append(str(exc)))
        source=(ROOT/'index.html').read_text()
        # Set content, then add source scripts, intercept JSON responses using a fetch shim.
        await page.set_content(source, wait_until='domcontentloaded')
        await page.add_style_tag(content=(ROOT/'styles.css').read_text())
        for script in (ROOT/'engine.js',): await page.add_script_tag(content=script.read_text())
        source_json=(ROOT/'evaluation_snapshots.json').read_text()
        manifest=(ROOT/'benchmark_manifest.json').read_text()
        await page.evaluate('''({data,manifest}) => { const f = window.fetch; window.fetch = async (url) => {
           if(String(url).includes('evaluation_snapshots.json')) return {ok:true,json:async()=>JSON.parse(data)};
           if(String(url).includes('benchmark_manifest.json')) return {ok:true,json:async()=>JSON.parse(manifest)};
           return f(url);
        }}''',{'data':source_json,'manifest':manifest})
        await page.add_script_tag(content=(ROOT/'app.js').read_text())
        await page.locator('.metric').first.wait_for(timeout=10000)
        checks=[]
        async def ck(label,cond):
            if not cond: raise AssertionError(label)
            checks.append(label)
            print('PASS',label)
        await ck('initial overview page', await page.locator('h2').first.inner_text()=='Evaluation overview')
        await ck('736 prediction records', '736' in await page.locator('#heroN').inner_text())
        await ck('initial gate warning visible', 'release check' in (await page.locator('#panel').inner_text()).lower())
        await ck('hybrid initial metrics 77.8', '77.8%' in await page.locator('#panel').inner_text())
        await page.locator('#methodSelect').select_option('nb')
        await ck('model selector updates to naive bayes', '74.1%' in await page.locator('#panel').inner_text())
        await page.locator('#splitSelect').select_option('iid')
        await ck('split selector 100 percent baseline', '100.0%' in await page.locator('#panel').inner_text())
        await page.locator('[data-view="drift"]').click()
        await ck('drift section shows PSI and JSD', 'Text-length PSI' in await page.locator('#panel').inner_text() and 'Language JSD' in await page.locator('#panel').inner_text())
        await page.locator('[data-view="slices"]').click()
        await ck('slice view has language controls', await page.locator('#sliceBy').count()==1)
        await page.locator('#sliceBy').select_option('truth')
        await ck('slice view label option', 'Six rhetorical classes' not in await page.locator('#panel').inner_text() and 'background' in await page.locator('#panel').inner_text())
        await page.locator('[data-view="errors"]').click()
        await ck('confusion matrix visible', 'CONFUSION MATRIX' in await page.locator('#panel').inner_text())
        await page.locator('#splitSelect').select_option('challenge')
        await ck('challenge examples show errors', await page.locator('span.tag.bad').count()>0)
        await page.locator('#errorBy').select_option('limitation')
        await ck('error filter changes view', 'Misclassifications' in await page.locator('#panel').inner_text())
        await page.locator('[data-view="regression"]').click()
        await ck('paired model compare n54', 'Matched example IDs' in await page.locator('#panel').inner_text())
        await page.locator('#compareMethod').select_option('word')
        await ck('paired method updates', 'Word TF-IDF' in await page.locator('#panel').inner_text())
        await page.locator('[data-view="gates"]').click()
        await ck('default release gates fail', 'CONFIGURED CHECKS FAIL' in await page.locator('#panel').inner_text())
        await page.locator('#tPSI').fill('1.0')
        await page.locator('#tGap').fill('30')
        await page.locator('#tAccuracy').fill('70')
        await page.locator('#applyGates').click()
        await ck('thresholds change gate view without modifying data', 'CONFIGURED CHECKS PASS' in await page.locator('#panel').inner_text())
        await page.locator('#resetGates').click()
        await ck('reset restores research defaults', 'CONFIGURED CHECKS FAIL' in await page.locator('#panel').inner_text())
        await page.locator('[data-view="registry"]').click()
        await ck('registry lists SHA256', 'evaluationSha256' in await page.locator('#panel').inner_text())
        await ck('registry shows lineage', 'Academic Rhetorical Intent Mini' in await page.locator('#panel').inner_text())
        await page.locator('[data-view="export"]').click()
        async with page.expect_download() as d:
            await page.locator('[data-download="rows"]').click()
        dl=await d.value
        await ck('CSV export generated',dl.suggested_filename.endswith('.csv'))
        async with page.expect_download() as d:
            await page.locator('[data-download="session"]').click()
        dl=await d.value
        await ck('full JSON session downloaded',dl.suggested_filename.endswith('.json'))
        async with page.expect_download() as d:
            await page.locator('[data-download="report"]').click()
        dl=await d.value
        await ck('Markdown audit report downloaded',dl.suggested_filename.endswith('.md'))
        async with page.expect_download() as d:
            await page.locator('[data-download="template"]').click()
        dl=await d.value
        await ck('CSV import template downloaded', 'template' in dl.suggested_filename)
        await page.locator('#inputFile').set_input_files(files={'name':'custom.json','mimeType':'application/json','buffer':json.dumps({'rows':[dict(id='custom1',split='challenge',method='nb',language='en',domain='test',truth='background',pred='background',tokenCount=4)]}).encode()})
        await page.locator('#importBtn').click()
        await page.wait_for_timeout(150);await ck('user import applied', '1' in await page.locator('#heroN').inner_text() and 'Imported 1 rows' in await page.locator('#notice').inner_text())
        await page.locator('[data-view="export"]').click()
        await page.locator('#restoreBtn').click()
        await ck('restores original dataset', '736' in await page.locator('#heroN').inner_text())
        await page.locator('#methodSelect').select_option('hybrid')
        await page.locator('#notice').evaluate('(el)=>el.hidden=true')
        await page.set_viewport_size({'width':390,'height':844})
        await page.screenshot(path='/mnt/data/ModelGuard_Observatory_MOBILE_PREVIEW.png',full_page=True)
        await ck('mobile viewport does not overflow', await page.evaluate('document.documentElement.scrollWidth <= window.innerWidth + 3'))
        await ck('navigation persists on mobile', await page.locator('[data-view="drift"]').is_visible())
        await page.set_viewport_size({'width':1512,'height':1050})
        await page.locator('[data-view="overview"]').click()
        await page.screenshot(path='/mnt/data/ModelGuard_Observatory_DESKTOP_PREVIEW.png',full_page=True)
        await ck('no browser JavaScript errors', not errors)
        print('BROWSER TESTS',len(checks),'PASS')
        await browser.close()
asyncio.run(main())