203 lines
6.7 KiB
JavaScript
203 lines
6.7 KiB
JavaScript
// Drives Firefox (WebDriver BiDi, through puppeteer-core) to capture the
|
|
// README screenshots. Run by main.go, which serves the app at $URL and
|
|
// passes $OUT (where to write the PNGs), $PROFILE and $FIREFOX.
|
|
import puppeteer from 'puppeteer-core'
|
|
|
|
const { URL: base, OUT, PROFILE, FIREFOX } = process.env
|
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
|
|
|
|
const browser = await puppeteer.launch({ browser: 'firefox', executablePath: FIREFOX, headless: true, userDataDir: PROFILE })
|
|
|
|
/** Opens the app on a script variant, with fresh preferences. */
|
|
async function open(variant = '') {
|
|
const page = await browser.newPage()
|
|
await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 2 })
|
|
page.on('pageerror', (e) => console.error('page error:', e.message))
|
|
const url = base + (variant ? `?v=${variant}` : '')
|
|
await page.goto(url)
|
|
await page.evaluate(() => localStorage.clear())
|
|
await page.goto(url)
|
|
await sleep(1200) // pagination and spelling
|
|
await page.evaluate(() => { document.getElementById('scroller').style.scrollBehavior = 'auto' })
|
|
return page
|
|
}
|
|
|
|
const ctrl = async (page, key, shift = false) => {
|
|
const k = page.keyboard
|
|
await k.down('Control')
|
|
if (shift) await k.down('Shift')
|
|
await k.press(key)
|
|
if (shift) await k.up('Shift')
|
|
await k.up('Control')
|
|
}
|
|
|
|
/** Clicks the first element matching sel whose text contains text. */
|
|
async function clickText(page, sel, text) {
|
|
for (const el of await page.$$(sel)) if ((await el.evaluate((e) => e.textContent)).includes(text)) return el.click()
|
|
throw new Error(`no ${sel} containing "${text}"`)
|
|
}
|
|
|
|
/** Puts the cursor at the end of the first element containing text. */
|
|
async function caretIn(page, text) {
|
|
await page.evaluate((text) => {
|
|
const el = [...document.querySelectorAll('.ProseMirror .el')].find((e) => e.textContent.includes(text))
|
|
const r = document.createRange()
|
|
r.selectNodeContents(el)
|
|
r.collapse(false)
|
|
getSelection().removeAllRanges()
|
|
getSelection().addRange(r)
|
|
document.querySelector('.ProseMirror').focus()
|
|
}, text)
|
|
await sleep(100)
|
|
await page.keyboard.press('End')
|
|
}
|
|
|
|
/** Selects the first occurrence of text in the script. */
|
|
async function select(page, text) {
|
|
await page.evaluate((text) => {
|
|
const walker = document.createTreeWalker(document.querySelector('.ProseMirror'), NodeFilter.SHOW_TEXT)
|
|
for (let n; (n = walker.nextNode()); ) {
|
|
const i = n.textContent.indexOf(text)
|
|
if (i < 0) continue
|
|
const r = document.createRange()
|
|
r.setStart(n, i)
|
|
r.setEnd(n, i + text.length)
|
|
getSelection().removeAllRanges()
|
|
getSelection().addRange(r)
|
|
return
|
|
}
|
|
}, text)
|
|
}
|
|
|
|
const scrollTo = (page, y) => page.evaluate((y) => { document.getElementById('scroller').scrollTop = y }, y)
|
|
|
|
/** Captures the window, the pointer off the header (tooltips). */
|
|
async function shot(page, name, pointer = true) {
|
|
if (pointer) await page.mouse.move(1430, 600)
|
|
await sleep(400)
|
|
await page.screenshot({ path: `${OUT}/${name}.png` })
|
|
console.log('captured', name)
|
|
}
|
|
|
|
let page = await open()
|
|
await caretIn(page, 'It always ends')
|
|
await scrollTo(page, 0)
|
|
await shot(page, 'editor')
|
|
|
|
await ctrl(page, 'F', true)
|
|
await shot(page, 'focus-mode')
|
|
await ctrl(page, 'F', true)
|
|
|
|
await caretIn(page, 'FADE IN:')
|
|
await scrollTo(page, 0)
|
|
await page.evaluate(() => document.getElementById('type-button').dispatchEvent(new MouseEvent('mousedown', { bubbles: true })))
|
|
await shot(page, 'element-menu')
|
|
await page.keyboard.press('Escape')
|
|
|
|
await caretIn(page, 'The film snaps')
|
|
await page.keyboard.press('Enter')
|
|
await page.keyboard.press('Tab')
|
|
await page.keyboard.type('EL')
|
|
await scrollTo(page, 0)
|
|
await shot(page, 'autocomplete')
|
|
await page.keyboard.press('Escape')
|
|
for (let i = 0; i < 4; i++) await ctrl(page, 'z')
|
|
await sleep(1500)
|
|
|
|
await caretIn(page, 'THE END')
|
|
await page.keyboard.press('Enter')
|
|
await page.keyboard.type('INT. ')
|
|
await page.keyboard.type('PROJECTION BOOTH - ')
|
|
await sleep(1600) // autosave: "Saved ยท auto"
|
|
await page.evaluate(() => { const s = document.getElementById('scroller'); s.scrollTop = s.scrollHeight })
|
|
await shot(page, 'scene-autocomplete')
|
|
await page.close()
|
|
|
|
page = await open()
|
|
await caretIn(page, 'FADE IN:')
|
|
await scrollTo(page, 0)
|
|
await ctrl(page, 'h')
|
|
await sleep(100)
|
|
await page.keyboard.type('reel')
|
|
await page.keyboard.press('Enter')
|
|
await page.click('#replace-input')
|
|
await page.keyboard.type('spool')
|
|
await scrollTo(page, 0)
|
|
await shot(page, 'find-replace')
|
|
await page.close()
|
|
|
|
page = await open()
|
|
await caretIn(page, 'It always ends')
|
|
await page.evaluate(() => {
|
|
const s = document.getElementById('scroller')
|
|
s.scrollTop += document.querySelector('.page-break').getBoundingClientRect().top - 390
|
|
})
|
|
await shot(page, 'page-break')
|
|
|
|
await caretIn(page, 'FADE IN:')
|
|
await scrollTo(page, 0)
|
|
await ctrl(page, 'T', true)
|
|
await shot(page, 'title-page')
|
|
await page.keyboard.press('Escape')
|
|
await sleep(100)
|
|
|
|
await caretIn(page, 'FADE IN:')
|
|
await scrollTo(page, 0)
|
|
await ctrl(page, 'p')
|
|
await shot(page, 'export-pdf')
|
|
await page.keyboard.press('Escape')
|
|
await sleep(100)
|
|
|
|
await page.click('.switch') // autosave off, so the change stays unsaved
|
|
await caretIn(page, 'line every shelf.')
|
|
await page.keyboard.type(' Then silence.')
|
|
await scrollTo(page, 0)
|
|
await ctrl(page, 'n')
|
|
await shot(page, 'unsaved-changes')
|
|
await page.close()
|
|
|
|
page = await open('typos')
|
|
await caretIn(page, 'FADE IN:')
|
|
await scrollTo(page, 0)
|
|
await sleep(700)
|
|
const typo = await page.evaluate(() => {
|
|
const r = document.querySelector('.misspelled').getBoundingClientRect()
|
|
return { x: r.left + r.width / 2, y: r.top + r.height / 2 }
|
|
})
|
|
await page.mouse.click(typo.x, typo.y, { button: 'right' })
|
|
await shot(page, 'spelling', false)
|
|
await page.close()
|
|
|
|
page = await open('outline')
|
|
await caretIn(page, 'Warmer light')
|
|
await scrollTo(page, 0)
|
|
await shot(page, 'outline')
|
|
|
|
// Locked pages, a Blue revision, then a Pink one, with the menu open.
|
|
await page.click('[data-action="revisions"]')
|
|
await sleep(150)
|
|
await clickText(page, 'ul.menu.popup li', 'Lock Pages')
|
|
await sleep(500)
|
|
await page.click('[data-action="revisions"]')
|
|
await sleep(150)
|
|
await clickText(page, 'ul.menu.popup li', 'Start Revisions')
|
|
await sleep(300)
|
|
await caretIn(page, 'line every shelf.')
|
|
await page.keyboard.type(' The lamp buzzes.')
|
|
await caretIn(page, 'every single night.')
|
|
await page.keyboard.press('Enter')
|
|
await page.keyboard.type('Then it starts again.')
|
|
await page.click('[data-action="revisions"]')
|
|
await sleep(150)
|
|
await clickText(page, 'ul.menu.popup li', 'New Revision Set')
|
|
await sleep(300)
|
|
await select(page, 'sleeves rolled up')
|
|
await page.keyboard.type('coat still on')
|
|
await sleep(900)
|
|
await scrollTo(page, 0)
|
|
await page.click('[data-action="revisions"]')
|
|
await sleep(250)
|
|
await shot(page, 'revisions')
|
|
|
|
await browser.close()
|