Automerge backed Markdown editor for the Atmosphere
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252import assert from 'node:assert/strict'import { readFile, mkdir } from 'node:fs/promises'import { createHash } from 'node:crypto'import { chromium } from 'playwright'
if (!process.env.PLAYWRIGHT_CHROMIUM_BIN) throw new Error('Run in the Nix shell with PLAYWRIGHT_CHROMIUM_BIN')const url = process.env.REAMS_CATALOG_TEST_URL || 'http://localhost:7051'const candidate = process.env.CANDIDATE_PLUGINconst artifacts = '.invariant-artifacts/plugin-catalog'await mkdir(artifacts, { recursive: true })const browser = await chromium.launch({ executablePath: process.env.PLAYWRIGHT_CHROMIUM_BIN, headless: true, args: ['--no-sandbox'] })const context = await browser.newContext()const page = await context.newPage()const errors = []const consoleErrors = []page.on('pageerror', error => errors.push(error.message))page.on('response', response => { if (response.status() >= 400) console.error('Failed response:', response.status(), response.url()) })page.on('console', message => { if (message.type() === 'error' && !message.text().includes('net::ERR_FAILED') && !message.location().url.endsWith('/favicon.ico')) { consoleErrors.push(message.text()) console.error('Browser console:', message.text(), message.location()) }})const original = JSON.parse(await readFile('public/plugin-catalog/catalog.json', 'utf8'))if (candidate) { const directory = `public/test-plugins/${candidate}` const manifest = JSON.parse(await readFile(`${directory}/manifest.json`, 'utf8')) const files = {} for (const file of ['main.js', 'styles.css']) { try { files[file] = createHash('sha256').update(await readFile(`${directory}/${file}`)).digest('hex') } catch (error) { if (file !== 'styles.css' || error.code !== 'ENOENT') throw error } } const bundlePath = `bundles/${manifest.id}/${manifest.version}/` original.plugins = original.plugins.filter(entry => entry.manifest.id !== manifest.id) original.plugins.push({ manifest, files, bundlePath, waitForMetadata: candidate === 'datacore', notes: 'Candidate under validation', testedAt: '2026-10-10', repository: `https://github.com/${candidate === 'datacore' ? 'blacksmithgu/datacore' : 'argenos/nldates-obsidian'}` }) await page.route(`**/${bundlePath}*`, async route => { const file = new URL(route.request().url()).pathname.split('/').pop() await route.fulfill({ body: await readFile(`${directory}/${file}`), contentType: file.endsWith('.css') ? 'text/css' : 'text/javascript' }) })}if (candidate) await page.route('**/plugin-catalog/catalog.json', route => route.fulfill({ json: original }))const entry = candidate ? original.plugins.at(-1) : original.plugins.find(entry => entry.manifest.id === (process.env.PLUGIN_ID || 'emoji-shortcodes'))assert.ok(entry, 'Requested plugin exists in the catalog')const name = entry.manifest.nameconst id = entry.manifest.idasync function openSettings() { await page.getByRole('button', { name: 'Open settings', exact: true }).click() await page.getByRole('tab', { name: 'Obsidian Plugins', exact: true }).click() await page.getByRole('switch', { name: `Toggle ${name}`, exact: true }).waitFor({ state: 'attached' })}async function active() { await page.waitForFunction(id => window.__reamsApp?._pluginLoader.getPluginState(id) === 'active', id, { timeout: 15000 })}async function setSourceMode(enabled) { await page.getByRole('button', { name: 'Open settings', exact: true }).click() await page.getByRole('tab', { name: 'Editor', exact: true }).click() const toggle = page.getByRole('switch', { name: 'Source mode', exact: true }) if (await toggle.isChecked() !== enabled) await toggle.locator('..').click() await page.getByRole('button', { name: 'Close settings', exact: true }).click()}async function replaceNote(content) { const editor = page.locator('.cm-content[contenteditable=true]').first() await editor.click() await page.keyboard.press('Control+a') await page.keyboard.insertText(content) await page.keyboard.press('Control+End')}async function createNote(name, content) { await page.getByRole('button', { name: 'New file', exact: true }).click() await page.getByPlaceholder('my-note or folder/my-note').fill(name) const template = page.getByRole('combobox', { name: 'Template', exact: true }) if (await template.count()) await template.selectOption('') await page.locator('.create-item-modal button[type=submit]').click() await page.getByRole('tab', { name, exact: true }).waitFor() await replaceNote(content)}async function openPlaygroundNote(path) { await page.locator(`.cm-wikilink-chip[data-target="Datacore Playground/${path}"]`).click() await page.waitForFunction(path => window.__reamsApp.workspace.getActiveFile()?.path === `Datacore Playground/${path}.md`, path)}try { await page.goto(url) await page.getByRole('button', { name: 'Open settings', exact: true }).waitFor({ timeout: 60000 }) await openSettings() assert.equal(await page.getByRole('note').textContent().then(text => text.includes('Request more plugins in the feedback UI.')), true) assert.equal(await page.getByRole('switch', { name: 'Toggle Periodic Notes', exact: true }).count(), 0) assert.equal(await page.getByRole('switch', { name: 'Toggle Recent Files', exact: true }).count(), 0) assert.equal(await page.getByText('Not installed', { exact: true }).count(), 0) assert.equal(await page.getByRole('button', { name: 'Add test corpus', exact: true }).count(), 0) const gaps = await page.locator('.settings-catalog-card').first().evaluate(card => { const items = [...card.querySelectorAll('.settings-plugin-name, .settings-plugin-description, .settings-plugin-version, .settings-catalog-notes')] return items.slice(1).map((item, i) => item.getBoundingClientRect().top - items[i].getBoundingClientRect().bottom) }) assert.deepEqual(gaps, [4, 4, 4], 'Card copy has consistent vertical gaps') await page.getByRole('switch', { name: `Toggle ${name}`, exact: true }).locator('..').click() await active() if (id === 'datacore') await page.waitForFunction(() => window.datacore?.core.initialized) await page.screenshot({ path: `${artifacts}/${id}-installed.png` }) assert.equal(await page.getByRole('switch', { name: `Toggle ${name}`, exact: true }).isChecked(), true) assert.equal(await page.getByRole('button', { name: 'Add test corpus', exact: true }).count(), 0) await page.getByRole('switch', { name: `Toggle ${name}`, exact: true }).locator('..').click() await page.waitForFunction(id => window.__reamsApp?._pluginLoader.getPluginState(id) === 'disabled', id) assert.equal(await page.evaluate(id => !!document.getElementById(`reams-plugin-styles-${id}`), id), false) await page.getByRole('switch', { name: `Toggle ${name}`, exact: true }).locator('..').click() await active() await page.getByRole('tab', { name, exact: true }).click() assert.ok(await page.locator('[role=tabpanel] input, [role=tabpanel] select').count(), 'Plugin settings render') await page.getByRole('button', { name: 'Close settings', exact: true }).click() if (id === 'nldates-obsidian') { const expected = await page.evaluate(() => window.moment().add(1, 'day').format('YYYY-MM-DD')) assert.equal(await page.evaluate(() => window.__reamsApp.plugins.getPlugin('nldates-obsidian').parseDate('tomorrow').formattedString), expected) await createNote('catalog-date-test', '') const editor = page.locator('.cm-content[contenteditable=true]').first() await editor.click() await page.keyboard.type('@tomorrow', { delay: 60 }) await page.locator('.cm-editor-suggest-dropdown').waitFor() await page.screenshot({ path: `${artifacts}/${id}-suggestion.png` }) await page.keyboard.press('Enter') await page.waitForFunction(expected => document.querySelector('.cm-content')?.textContent.includes(expected), expected) const note = await page.evaluate(async () => window.__reamsApp.vault.read(window.__reamsApp.vault.getAbstractFileByPath('catalog-date-test.md'))) assert.ok(note.includes(expected), 'Date insertion writes the actual note') } if (id === 'datacore') { await page.waitForFunction(() => window.datacore?.query('@page').length > 0, null, { timeout: 15000 }) await openSettings() await page.getByRole('switch', { name: 'Toggle Dataview', exact: true }).locator('..').click() await page.waitForFunction(() => !!window.DataviewAPI) await page.getByRole('button', { name: 'Close settings', exact: true }).click() await setSourceMode(true) await createNote('datacore-data', '---\nstatus: active\ntags: [catalog-probe]\n---\n\nTest data\n\n- [ ] Ship the query\n') const query = '# Query comparison\n\n```datacorejsx\nreturn function View() { const rows = dc.useQuery("@page and #catalog-probe"); const columns = [{id:"File", value:r=>r.$link},{id:"Status",value:r=>r.field("status")?.value}]; return <div data-testid="datacore-probe"><dc.Table rows={rows} columns={columns} paging={false}/></div>; }\n```\n\n```dataview\nTABLE status AS Status\nFROM #catalog-probe\n```\n\nEnd of query\n' await createNote('datacore-query', query) await page.waitForFunction(() => window.datacore.query('@page and #catalog-probe').some(row => row.$path === 'datacore-data.md')) await setSourceMode(false) await page.locator('[data-testid=datacore-probe]').waitFor() await page.getByRole('cell', { name: 'active', exact: true }).first().waitFor() await page.locator('table.dataview').waitFor() const styles = await page.evaluate(() => { const inspect = table => { const header = getComputedStyle(table.querySelector('th')) const cell = getComputedStyle(table.querySelector('td')) const surface = getComputedStyle(table.closest('.cm-fenced-code-block-processor')) return { headerRule: header.borderBottom, weight: header.fontWeight, headerSize: header.fontSize, cellPadding: cell.padding, rowRule: cell.borderBottom, backdrop: surface.backgroundColor, overflow: surface.overflowX } } return { datacore: inspect(document.querySelector('table.datacore-table')), dataview: inspect(document.querySelector('table.dataview')) } }) assert.deepEqual(styles.datacore, styles.dataview, 'Datacore and Dataview share table styling') console.log('Table styling:', styles) await page.screenshot({ path: `${artifacts}/${id}-query.png` }) await page.setViewportSize({ width: 390, height: 844 }) const hideSidebar = page.getByRole('button', { name: 'Hide sidebar', exact: true }) if (await hideSidebar.count()) await hideSidebar.click() const hidePanel = page.getByRole('button', { name: 'Hide right panel', exact: true }) if (await hidePanel.count()) await hidePanel.click() await page.locator('table.datacore-table').waitFor({ state: 'visible' }) await page.screenshot({ path: `${artifacts}/${id}-query-mobile.png`, animations: 'disabled' }) assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > innerWidth), false) await page.setViewportSize({ width: 1280, height: 720 }) const showRibbon = page.getByRole('button', { name: 'Show ribbon', exact: true }) if (await showRibbon.count()) await showRibbon.click() const showSidebar = page.getByRole('button', { name: 'Show sidebar', exact: true }) if (await showSidebar.count()) await showSidebar.click() await page.getByRole('tab', { name: 'datacore-data', exact: true }).click() await replaceNote('Removed the query tag') await page.getByRole('tab', { name: 'datacore-query', exact: true }).click() await page.waitForFunction(() => document.querySelector('[data-testid=datacore-probe] table tbody')?.children.length === 0) await page.getByRole('tab', { name: 'datacore-data', exact: true }).click() await replaceNote('---\nstatus: restored\ntags: [catalog-probe]\n---\n\nRestored tag') await page.getByRole('tab', { name: 'datacore-query', exact: true }).click() await page.locator('table.datacore-table').getByRole('cell', { name: 'restored', exact: true }).waitFor() await page.getByRole('button', { name: 'Open command palette', exact: true }).click() await page.getByPlaceholder('Type a command...').fill('Reindex entire vault') await page.getByRole('option', { name: 'Reindex entire vault', exact: true }).click() await page.waitForFunction(() => window.datacore.core.initialized) await page.locator('table.datacore-table').getByRole('cell', { name: 'restored', exact: true }).waitFor()
const queryUrl = page.url() const fixtureUrl = new URL(url) fixtureUrl.searchParams.set('invariantFixture', 'datacore') fixtureUrl.hash = '' await page.goto(fixtureUrl.href) await page.getByRole('button', { name: 'Open settings', exact: true }).waitFor({ timeout: 60000 }) await active() await page.getByRole('tab', { name: 'Start here', exact: true }).waitFor() await openPlaygroundNote('Dashboards/Projects') await page.waitForFunction(() => document.querySelector('table.datacore-table tbody')?.children.length === 3) await page.locator('table.datacore-table').getByRole('cell', { name: '1200', exact: true }).waitFor() await page.screenshot({ path: `${artifacts}/${id}-playground-projects.png` }) await openPlaygroundNote('Start here') await openPlaygroundNote('Dashboards/Active projects') await page.waitForFunction(() => document.querySelector('table.datacore-table tbody')?.children.length === 1) await openPlaygroundNote('Start here') await openPlaygroundNote('Dashboards/People') await page.waitForFunction(() => document.querySelector('table.datacore-table tbody')?.children.length === 2) await page.locator('table.datacore-table').getByRole('cell', { name: 'Designer', exact: true }).waitFor() await openPlaygroundNote('Start here') await openPlaygroundNote('Dashboards/Open tasks') await page.waitForFunction(() => document.querySelector('table.datacore-table tbody')?.children.length === 4) await page.screenshot({ path: `${artifacts}/${id}-playground-tasks.png` }) await page.locator('table.datacore-table').getByRole('link', { name: 'Aurora', exact: true }).first().click() await page.getByRole('tab', { name: 'Aurora', exact: true }).waitFor() const source = await page.evaluate(async () => window.__reamsApp.vault.read(window.__reamsApp.workspace.getActiveFile())) await setSourceMode(true) await replaceNote(source.replace('- [ ] Draft the welcome flow', '- [x] Draft the welcome flow')) await page.getByRole('tab', { name: 'Open tasks', exact: true }).click() await setSourceMode(false) await page.waitForFunction(() => document.querySelector('table.datacore-table tbody')?.children.length === 3) await page.goto(fixtureUrl.href) await page.getByRole('button', { name: 'Open settings', exact: true }).waitFor({ timeout: 60000 }) await active() await openPlaygroundNote('Dashboards/Open tasks') await page.waitForFunction(() => document.querySelector('table.datacore-table tbody')?.children.length === 3) assert.equal(await page.evaluate(() => window.__reamsApp.vault.getMarkdownFiles().filter(file => file.path.startsWith('Datacore Playground/')).length), 11) await page.goto(queryUrl) await page.getByRole('button', { name: 'Open settings', exact: true }).waitFor({ timeout: 60000 }) await active() await page.locator('table.datacore-table').getByRole('cell', { name: 'restored', exact: true }).waitFor() } await page.reload() await page.getByRole('button', { name: 'Open settings', exact: true }).waitFor({ timeout: 60000 }) await active() // Block the catalog and bundles to prove installed activation is device-local. await page.route('**/plugin-catalog/**', route => route.abort()) await page.reload() await page.getByRole('button', { name: 'Open settings', exact: true }).waitFor({ timeout: 60000 }) await active() if (id === 'datacore') await page.locator('table.datacore-table').getByRole('cell', { name: 'restored', exact: true }).waitFor() await openSettings() await page.getByRole('button', { name: `Uninstall ${name}`, exact: true }).click() await page.waitForFunction(id => window.__reamsApp?._pluginLoader.getPluginState(id) === null, id) await page.setViewportSize({ width: 390, height: 844 }) await page.screenshot({ path: `${artifacts}/${id}-mobile.png` }) assert.equal(await page.evaluate(() => document.documentElement.scrollWidth > innerWidth), false, 'No mobile horizontal overflow') assert.deepEqual(errors, [], 'No uncaught browser errors') assert.deepEqual(consoleErrors, [], 'No application or plugin console errors') console.log(`PASS ${name}: card install, disable/re-enable, settings, reload, offline bundle restore, uninstall, mobile layout${candidate ? ', candidate functionality' : ''}`)} catch (error) { await page.screenshot({ path: `${artifacts}/${id}-failure.png` }) console.error('Plugin card:', await page.locator('.settings-catalog-card').filter({ hasText: name }).textContent().catch(() => 'not visible')) console.error('Browser errors:', errors) if (id === 'datacore') console.error('Query diagnostics:', await page.evaluate(async () => { const app = window.__reamsApp const file = app.vault.getAbstractFileByPath('datacore-data.md') const dv = window.DataviewAPI?.page('datacore-data.md') return { content: file ? await app.vault.read(file) : null, metadata: file ? app.metadataCache.getFileCache(file) : null, dataview: dv ? { status: dv.status, tags: dv.file.tags?.array?.() ?? dv.file.tags } : null, datacore: window.datacore?.page('datacore-data.md')?.$tags } }).catch(error => error.message)) throw error} finally { await context.close(); await browser.close() }