mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-30 19:33:45 +08:00
c2921c1a8f
Co-authored-by: AngusBell97 <124716116+AngusBell97@users.noreply.github.com>
124 lines
9.0 KiB
JavaScript
124 lines
9.0 KiB
JavaScript
// Local-only real DOM smoke. Requires Playwright + its Chromium; no live API.
|
|
// PLAYWRIGHT_MODULE can point at an existing isolated Playwright installation.
|
|
const assert = require('node:assert/strict')
|
|
const fs = require('node:fs')
|
|
const path = require('node:path')
|
|
const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'playwright')
|
|
const repo = path.resolve(__dirname, '../../../..')
|
|
const assets = path.join(repo, 'starpilot/system/the_galaxy/assets')
|
|
// All API responses below are synthetic. Layout/assets come from this checkout.
|
|
const layoutFixture = JSON.parse(fs.readFileSync(path.join(repo, 'starpilot/common/assets/device_settings_layout.json'), 'utf8'))
|
|
const fixture = `
|
|
const layoutFixture = ${JSON.stringify(layoutFixture)};
|
|
const paramsFixture = Object.fromEntries(layoutFixture.flatMap(section => section.params || []).map(param =>
|
|
[param.key, param.data_type === 'bool' ? false : (param.default ?? param.min ?? 0)]));
|
|
const jsonResponse = value => new Response(JSON.stringify(value), {status:200});
|
|
window.showSnackbar=()=>{};
|
|
window.fetch=async (input, init={}) => {
|
|
const url=new URL(input,location.href);
|
|
const method=init.method || 'GET';
|
|
if(method !== 'GET') throw new Error('Unexpected fixture write: '+url.pathname);
|
|
if(url.pathname==='/assets/components/tools/device_settings_layout.json') return jsonResponse(layoutFixture);
|
|
if(url.pathname==='/api/params/defaults' || url.pathname==='/api/flm/workspace') return jsonResponse({});
|
|
if(url.pathname==='/api/params/all') return jsonResponse(paramsFixture);
|
|
if(url.pathname==='/api/favorites/slots') return jsonResponse({options:[],slots:[null,null,null],values:{}});
|
|
if(url.pathname==='/api/favorites/values') return jsonResponse({values:{}});
|
|
if(url.pathname==='/api/params') return new Response(String(paramsFixture[url.searchParams.get('key')] ?? false));
|
|
throw new Error('Unmocked request: '+method+' '+url.pathname);
|
|
};
|
|
paramsFixture.IsOnroad = ${process.env.GALAXY_DOM_ONROAD === '1'};
|
|
paramsFixture.GalaxyDeveloperMode = false;
|
|
let modeState = {mode:'conditional_experimental', values:{ExperimentalMode:true,ConditionalExperimental:true,ConditionalChill:false},locked:false,reason:'',experimental_confirmed:false};
|
|
window.modeWrites=[]; window.failModeWrite=false; window.failModeRead=false;
|
|
window.holdModeWrite=false;
|
|
const fixtureFetch=window.fetch;
|
|
window.fetch=async (input, init={}) => {
|
|
const url=new URL(input,location.href);
|
|
if(url.pathname!='/api/longitudinal_mode') return fixtureFetch(input,init);
|
|
if(init.method==='PUT') {
|
|
const body=JSON.parse(init.body); window.modeWrites.push(body);
|
|
if(window.holdModeWrite) await new Promise(resolve => {window.releaseModeWrite=resolve});
|
|
if(window.failModeWrite) return new Response(JSON.stringify({error:'Injected failure'}),{status:500});
|
|
modeState={...modeState,mode:body.mode,values:{ExperimentalMode:body.mode==='experimental',ConditionalExperimental:body.mode==='conditional_experimental',ConditionalChill:body.mode==='conditional_chill'}};
|
|
}
|
|
if(window.failModeRead) return new Response('{}',{status:503});
|
|
return new Response(JSON.stringify(modeState),{status:200});
|
|
};
|
|
window.lockMode=()=>{modeState={...modeState,locked:true,reason:'Safe Mode locked'};};
|
|
import {DeviceSettings} from '/assets/components/tools/device_settings.js';
|
|
DeviceSettings({params:{section:'longitudinal-speed-following'}})(document.querySelector('#app'));
|
|
`
|
|
;(async () => {
|
|
const browser = await chromium.launch({ headless: true, executablePath: process.env.CHROMIUM_EXECUTABLE, args: ['--no-sandbox'] })
|
|
try {
|
|
const page = await browser.newPage({ viewport: { width: 1100, height: 900 } })
|
|
const errors = []
|
|
const dialogs = []
|
|
page.on('dialog', dialog => { dialogs.push(dialog.message()); dialog.dismiss() })
|
|
page.on('pageerror', error => errors.push(error.message))
|
|
await page.route('**/*', async route => {
|
|
const url = new URL(route.request().url())
|
|
if (url.hostname !== 'offline.invalid') throw new Error('External access blocked')
|
|
if (url.pathname === '/device_settings') return route.fulfill({contentType:'text/html',body:'<html><head><link rel="stylesheet" href="/assets/components/main.css"><link rel="stylesheet" href="/assets/components/settings.css"><link rel="stylesheet" href="/assets/components/tools/device_settings.css"></head><body><main id="app"></main><script type="module" src="/setup.js"></script></body></html>'})
|
|
if (url.pathname === '/setup.js') return route.fulfill({contentType:'text/javascript',body:fixture})
|
|
if (url.pathname.startsWith('/assets/')) {
|
|
const file = path.join(assets, url.pathname.slice('/assets/'.length))
|
|
if (fs.existsSync(file) && fs.statSync(file).isFile()) return route.fulfill({path:file})
|
|
}
|
|
return route.fulfill({status:404,body:'not found'})
|
|
})
|
|
await page.goto('http://offline.invalid/device_settings')
|
|
await page.getByRole('button', {name:'Longitudinal (Speed & Following)',exact:true}).click()
|
|
const select = page.locator('#ds-LongitudinalControlMode')
|
|
await select.waitFor()
|
|
await page.waitForFunction(() => document.querySelector('#ds-LongitudinalControlMode')?.value === 'conditional_experimental')
|
|
assert.equal(await select.isEnabled(), true)
|
|
assert.deepEqual(await select.locator('option').allTextContents(), ['Chill','Experimental','Conditional Experimental','Conditional Chill'])
|
|
assert.equal(await page.locator('#ds-ConditionalExperimental, #ds-ConditionalChill').count(), 0)
|
|
assert.equal(await page.locator('#ds-manual-CESpeed').count(), 0)
|
|
const manage = page.locator('[aria-controls="ds-LongitudinalControlMode-children"]')
|
|
assert.equal(await manage.getAttribute('aria-expanded'), 'false')
|
|
if(process.env.GALAXY_DOM_SCREENSHOT) await page.screenshot({path:process.env.GALAXY_DOM_SCREENSHOT.replace('.png','-collapsed.png'),fullPage:true})
|
|
await manage.click()
|
|
assert.equal(await page.locator('#ds-manual-CESpeed').isVisible(), true)
|
|
await manage.click()
|
|
assert.equal(await page.locator('#ds-manual-CESpeed').count(), 0)
|
|
assert.equal(await select.isVisible(), true)
|
|
await manage.click()
|
|
assert.equal(await page.locator('#ds-manual-CCMSpeed').count(), 0)
|
|
assert.equal(await page.evaluate(() => window.modeWrites.length), 0)
|
|
for (const target of ['conditional_chill','chill']) {
|
|
await select.selectOption(target)
|
|
await page.waitForFunction(target => document.querySelector('#ds-LongitudinalControlMode')?.value === target && !document.querySelector('#ds-LongitudinalControlMode')?.disabled, target)
|
|
assert.equal(await page.locator('#ds-manual-CESpeed').count(), 0)
|
|
assert.equal(await manage.count(), target === 'chill' ? 0 : 1)
|
|
assert.equal(await page.locator('#ds-manual-CCMSpeed').count(), target === 'conditional_chill' ? 1 : 0)
|
|
}
|
|
const beforeExperimental = await page.evaluate(() => window.modeWrites.length)
|
|
await page.evaluate(() => { window.holdModeWrite=true })
|
|
await select.selectOption('experimental')
|
|
await page.waitForFunction(() => !!window.releaseModeWrite && document.querySelector('#ds-LongitudinalControlMode')?.disabled)
|
|
await page.evaluate(() => { window.holdModeWrite=false; window.releaseModeWrite() })
|
|
await page.waitForFunction(() => document.querySelector('#ds-LongitudinalControlMode')?.value === 'experimental' && !document.querySelector('#ds-LongitudinalControlMode')?.disabled)
|
|
assert.equal(await page.evaluate(() => window.modeWrites.at(-1).acknowledged), true)
|
|
assert.equal(await page.evaluate(() => window.modeWrites.length), beforeExperimental + 1)
|
|
assert.deepEqual(dialogs, [])
|
|
assert.equal(await manage.count(), 0)
|
|
assert.equal(await page.locator('#ds-manual-CESpeed, #ds-manual-CCMSpeed').count(), 0)
|
|
await page.evaluate(() => { window.failModeWrite = true })
|
|
await select.selectOption('conditional_chill')
|
|
await page.waitForFunction(() => document.querySelector('#ds-LongitudinalControlMode')?.value === 'experimental' && !document.querySelector('#ds-LongitudinalControlMode')?.disabled)
|
|
await page.evaluate(() => { window.failModeWrite = false })
|
|
await select.selectOption('conditional_experimental')
|
|
await page.waitForFunction(() => document.querySelector('#ds-manual-CESpeed'))
|
|
await page.screenshot({path:process.env.GALAXY_DOM_SCREENSHOT || '/tmp/galaxy-longitudinal-mode.png',fullPage:true})
|
|
await page.evaluate(() => window.lockMode())
|
|
await page.waitForFunction(() => document.querySelector('#ds-LongitudinalControlMode')?.disabled)
|
|
await page.evaluate(() => { window.failModeRead=true })
|
|
await page.waitForFunction(() => document.querySelector('#ds-LongitudinalControlMode')?.value === '')
|
|
assert.equal(await select.isDisabled(), true)
|
|
assert.deepEqual(errors, [])
|
|
console.log('PASS: real DOM initial CEM precedence/no writes; collapsed Manage disclosure/visible dropdown; all four choices; mode-specific children; no experimental dialog; request acknowledgement; failed-write readback; Safe Mode and missing-state locks; zero page errors')
|
|
} finally { await browser.close() }
|
|
})().catch(error => { console.error(error); process.exitCode=1 })
|