initial commit
Some checks failed
ci / go (push) Has been cancelled
ci / admin-ui (push) Has been cancelled
ci / runtime-sdk (push) Has been cancelled
ci / helm (push) Has been cancelled
ci / openapi (push) Has been cancelled
ci / container (push) Has been cancelled
ci / release-config (push) Has been cancelled
ci / shared styling (push) Has been cancelled
ci / security (push) Has been cancelled
ci / all green (push) Has been cancelled
Some checks failed
ci / go (push) Has been cancelled
ci / admin-ui (push) Has been cancelled
ci / runtime-sdk (push) Has been cancelled
ci / helm (push) Has been cancelled
ci / openapi (push) Has been cancelled
ci / container (push) Has been cancelled
ci / release-config (push) Has been cancelled
ci / shared styling (push) Has been cancelled
ci / security (push) Has been cancelled
ci / all green (push) Has been cancelled
This commit is contained in:
commit
951dd8d26b
175 changed files with 50041 additions and 0 deletions
167
hack/pilot-go-tell.js
Normal file
167
hack/pilot-go-tell.js
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/**
|
||||
* The go-tell pilot check.
|
||||
*
|
||||
* Injected into a *copy* of go-tell's shell; go-tell's own source is untouched.
|
||||
*
|
||||
* go-tell is the hard case among the applications: before this it had no CSS custom
|
||||
* properties at all, 340 hardcoded hex values and 153 per-palette override rules. Its
|
||||
* stylesheet now reads from the shared contract, so the question this answers is
|
||||
* whether that rewrite actually works, and whether the two shipped controls can drive
|
||||
* an application whose mechanism is `html[data-theme="…"]` rather than a class or a
|
||||
* `--theme` variable.
|
||||
*/
|
||||
import { getContractState } from '/assets/components.js'
|
||||
|
||||
const results = []
|
||||
const check = (name, pass, detail = '') => results.push({ name, pass: !!pass, detail: String(detail) })
|
||||
|
||||
const root = document.documentElement
|
||||
const state = getContractState()
|
||||
|
||||
/** The computed background of the page, which is what a palette actually looks like. */
|
||||
const pageBg = () => getComputedStyle(document.body).backgroundColor
|
||||
/** The computed colour of a heading, which proves the text role resolved. */
|
||||
const headingColour = () => {
|
||||
const el = document.querySelector('.brand, h1, .header')
|
||||
return el ? getComputedStyle(el).color : ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits past this application's own CSS transitions.
|
||||
*
|
||||
* go-tell transitions `background` over 300ms, so reading a computed background sooner
|
||||
* than that returns a value mid-animation and makes a working palette switch look
|
||||
* broken. The wait is deliberately longer than the longest transition in the
|
||||
* stylesheet rather than merely "long enough today".
|
||||
*/
|
||||
async function settle() {
|
||||
await new Promise((r) => setTimeout(r, 450))
|
||||
}
|
||||
|
||||
async function run() {
|
||||
await new Promise((r) => setTimeout(r, 600)) // the selector's fetch
|
||||
|
||||
const selector = document.querySelector('bananas-theme-selector')
|
||||
const toggle = document.querySelector('bananas-appearance-toggle')
|
||||
const shadow = (el) => /** @type {ShadowRoot} */ (el.shadowRoot)
|
||||
|
||||
check('the controls upgraded', !!selector && !!toggle && !!shadow(selector) && !!shadow(toggle))
|
||||
if (!selector || !toggle) return finish()
|
||||
|
||||
const buttons = [...shadow(selector).querySelectorAll('button')]
|
||||
const labels = buttons.map((b) => b.textContent.trim())
|
||||
check('palettes are listed from the service', buttons.length > 1, labels.join(', '))
|
||||
|
||||
// go-tell's own four palettes must all be offered, because those are the slugs its
|
||||
// stylesheet and its rules key on.
|
||||
for (const name of ['Cyberpunk', 'Nordic Moss', 'Deep Space Obsidian', 'Terminal Retro']) {
|
||||
check(`go-tell's "${name}" is offered`, labels.some((l) => l.includes(name)))
|
||||
}
|
||||
|
||||
// The decisive one: the contract must reach go-tell's page, which no longer
|
||||
// defines a single custom property of its own.
|
||||
const surface = getComputedStyle(document.body).getPropertyValue('--bn-surface').trim()
|
||||
|
||||
// Diagnostics, because "the contract did not reach the page" has three very
|
||||
// different causes and the symptom alone cannot tell them apart.
|
||||
const link = document.querySelector('link[data-theme-contract]')
|
||||
const linkHref = link ? link.href : '(no link element)'
|
||||
let linkLoaded = 'unknown'
|
||||
if (link && link.sheet) {
|
||||
try {
|
||||
linkLoaded = String(link.sheet.cssRules.length) + ' rules'
|
||||
} catch {
|
||||
// A cross-origin sheet is unreadable by design. Reaching this branch means the
|
||||
// stylesheet loaded and the browser is refusing to expose its rules, which is
|
||||
// the healthy case for a cross-origin link.
|
||||
linkLoaded = 'loaded (cross-origin, rules not readable)'
|
||||
}
|
||||
} else if (link) {
|
||||
linkLoaded = 'not loaded'
|
||||
}
|
||||
|
||||
check('the contract link is present', !!link, linkHref)
|
||||
check('the contract stylesheet loaded', linkLoaded !== 'not loaded', linkLoaded)
|
||||
check('the contract reaches go-tell', surface !== '', 'href=' + linkHref + ' loaded=' + linkLoaded + ' data-theme=' + root.getAttribute('data-theme') + ' data-mode=' + root.getAttribute('data-mode'))
|
||||
check('--bn-surface has a real value', surface !== '' && surface !== 'rgba(0, 0, 0, 0)', surface)
|
||||
|
||||
// Switching a palette must move the page, through go-tell's own rules.
|
||||
let startBg = pageBg()
|
||||
const startText = headingColour()
|
||||
|
||||
for (const name of ['Nordic Moss', 'Deep Space Obsidian', 'Terminal Retro', 'Cyberpunk']) {
|
||||
const button = buttons.find((b) => b.textContent.trim().includes(name))
|
||||
if (!button) {
|
||||
check(`selecting ${name}`, false, 'button not found')
|
||||
continue
|
||||
}
|
||||
|
||||
// The loop returns to Cyberpunk at the end, which is where it started. Selecting
|
||||
// the palette that is already showing cannot change anything, so the "did the
|
||||
// page move" assertion does not apply to that step.
|
||||
const alreadyShowing = buttons.find((b) => b.getAttribute('aria-checked') === 'true')
|
||||
const wasThis = alreadyShowing && alreadyShowing.textContent.trim().includes(name)
|
||||
|
||||
button.click()
|
||||
await settle()
|
||||
|
||||
const slug = root.getAttribute('data-theme')
|
||||
const contractBg = getComputedStyle(document.body).getPropertyValue('--bn-bg').trim()
|
||||
check(`${name} writes data-theme`, !!slug, slug)
|
||||
if (wasThis) {
|
||||
check(`${name} is a no-op when already showing`, pageBg() === startBg, pageBg())
|
||||
} else {
|
||||
check(`${name} changes the page`, pageBg() !== startBg || headingColour() !== startText,
|
||||
`slug=${slug} mode=${root.getAttribute('data-mode')} bodyBg=${pageBg()} --bn-bg=${contractBg} text=${headingColour()}`)
|
||||
}
|
||||
startBg = pageBg()
|
||||
}
|
||||
|
||||
// The terminal palette forces a monospace face with !important on every element.
|
||||
// Shadow DOM is supposed to make that impossible to reach the controls in; if the
|
||||
// controls inherit the terminal face here, the isolation has failed.
|
||||
const terminal = buttons.find((b) => b.textContent.trim().includes('Terminal'))
|
||||
if (terminal) {
|
||||
terminal.click()
|
||||
await settle()
|
||||
const font = getComputedStyle(terminal).fontFamily
|
||||
check('the controls are not restyled by the terminal palette', !font.includes('Courier'), font)
|
||||
check('the app itself did adopt the monospace face',
|
||||
getComputedStyle(document.body).fontFamily.includes('Courier') ||
|
||||
getComputedStyle(document.body).fontFamily.includes('monospace'),
|
||||
getComputedStyle(document.body).fontFamily)
|
||||
}
|
||||
|
||||
// The appearance axis: go-tell had no light mode before this. The control writing
|
||||
// data-mode is what makes one possible.
|
||||
const toggleButtons = [...shadow(toggle).querySelectorAll('button')]
|
||||
check('the appearance toggle has three states', toggleButtons.length === 3,
|
||||
toggleButtons.map((b) => b.getAttribute('aria-label')).join(', '))
|
||||
|
||||
const light = toggleButtons.find((b) => b.getAttribute('aria-label') === 'Light')
|
||||
if (light) {
|
||||
light.click()
|
||||
await settle()
|
||||
check('light writes data-mode', root.getAttribute('data-mode') === 'light', root.getAttribute('data-mode'))
|
||||
}
|
||||
|
||||
check('the contract store is reachable', state && typeof state.selectTheme === 'function')
|
||||
finish()
|
||||
}
|
||||
|
||||
function finish() {
|
||||
const failed = results.filter((r) => !r.pass)
|
||||
const out = document.createElement('pre')
|
||||
out.id = 'pilot'
|
||||
out.textContent = failed.length
|
||||
? 'PILOT FAIL ' + failed.length + '/' + results.length + ' :: ' + failed.map((f) => f.name + ' [' + f.detail + ']').join(' | ')
|
||||
: 'PILOT PASS ' + results.length + ' :: ' + results.map((f) => f.name).join(' | ')
|
||||
document.body.append(out)
|
||||
}
|
||||
|
||||
run().catch((error) => {
|
||||
const out = document.createElement('pre')
|
||||
out.id = 'pilot'
|
||||
out.textContent = 'PILOT FAIL the check threw: ' + (error instanceof Error ? error.message : String(error))
|
||||
document.body.append(out)
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue