Full document, spreadsheet, slideshow, and diagram tooling
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request 'fix: Vite MPA routing, SQLite migration ordering, E2E reliability' (#217) from fix/e2e-mpa-routing-and-migration into main

scott 955df6c4 4d3f8aa6

+46 -19
+4
CHANGELOG.md
··· 108 108 109 109 ### Fixed 110 110 - Fix sheets chat input: keyboard handler no longer captures typing in AI chat sidebar (#233) 111 + - Fix Vite dev server MPA routing: sub-apps (docs, sheets, forms, slides, diagrams) now serve correct HTML (#305) 112 + - Fix SQLite migration ordering: owner column migration now runs before type CHECK expansion (#305) 113 + - Fix E2E test flakiness: replace page reload with addInitScript, add waitForURL before waitForSelector (#305) 111 114 112 115 ### Changed 116 + - Add E2E tests for database views (kanban, gallery, calendar, timeline, pivot) (#302) 113 117 - Add E2E tests for forms builder and submission (#301) 114 118 - Add E2E tests for diagrams whiteboard (#300) 115 119 - Add E2E tests for slides presentations (#299)
+16 -11
e2e/helpers.ts
··· 23 23 * Navigate to landing page and ensure it is ready. 24 24 */ 25 25 export async function goToLanding(page: Page): Promise<void> { 26 + // Inject username into localStorage before any page script runs, 27 + // so the username modal never appears and no reload is needed. 28 + await page.addInitScript(() => { 29 + if (!localStorage.getItem('tools-username')) { 30 + localStorage.setItem('tools-username', 'TestUser'); 31 + } 32 + }); 26 33 await page.goto('/'); 27 - await dismissUsernamePrompt(page); 28 - // Reload after setting username to avoid the modal 29 - await page.reload(); 30 34 await page.waitForSelector('.landing-header'); 31 35 } 32 36 ··· 37 41 export async function createNewDoc(page: Page): Promise<string> { 38 42 await goToLanding(page); 39 43 await page.click('#new-doc'); 40 - // Wait for TipTap editor to mount 44 + await page.waitForURL(/\/docs\//, { timeout: 30000 }); 41 45 await page.waitForSelector('.tiptap', { timeout: 15000 }); 42 46 return page.url(); 43 47 } ··· 49 53 export async function createNewSheet(page: Page): Promise<string> { 50 54 await goToLanding(page); 51 55 await page.click('#new-sheet'); 52 - // Wait for the sheet grid to render rows 56 + await page.waitForURL(/\/sheets\//, { timeout: 30000 }); 53 57 await page.waitForSelector('#sheet-grid tbody tr td[data-id]', { timeout: 15000 }); 54 58 return page.url(); 55 59 } ··· 62 66 await goToLanding(page); 63 67 // Click triggers async createDocument (fetch then window.location.href) 64 68 await page.click('#new-slide'); 65 - // Wait for slide canvas to render after navigation completes 66 - await page.waitForSelector('#slide-canvas', { timeout: 30000 }); 69 + // Wait for navigation to /slides/ path before checking for DOM elements 70 + await page.waitForURL(/\/slides\//, { timeout: 30000 }); 71 + await page.waitForSelector('#slide-canvas', { timeout: 15000 }); 67 72 return page.url(); 68 73 } 69 74 ··· 75 80 await goToLanding(page); 76 81 // Click triggers async createDocument (fetch then window.location.href) 77 82 await page.click('#new-form'); 78 - // Wait for form builder toolbar to render after navigation completes 79 - await page.waitForSelector('#form-toolbar', { timeout: 30000 }); 83 + await page.waitForURL(/\/forms\//, { timeout: 30000 }); 84 + await page.waitForSelector('#form-toolbar', { timeout: 15000 }); 80 85 return page.url(); 81 86 } 82 87 ··· 87 92 export async function createNewDiagram(page: Page): Promise<string> { 88 93 await goToLanding(page); 89 94 await page.click('#new-diagram'); 90 - // Wait for the SVG canvas to render 91 - await page.waitForSelector('#diagram-canvas', { timeout: 30000 }); 95 + await page.waitForURL(/\/diagrams\//, { timeout: 30000 }); 96 + await page.waitForSelector('#diagram-canvas', { timeout: 15000 }); 92 97 return page.url(); 93 98 } 94 99
+10 -8
server/index.ts
··· 193 193 console.log('Migrated: added tags column'); 194 194 } 195 195 196 + // Migration: add owner column to documents (must run before type CHECK migration) 197 + try { 198 + db.prepare("SELECT owner FROM documents LIMIT 1").get(); 199 + } catch { 200 + db.exec("ALTER TABLE documents ADD COLUMN owner TEXT"); 201 + console.log('Migrated: added owner column'); 202 + } 203 + 196 204 // Migration: expand type CHECK constraint to include form, slide, diagram 197 205 try { 198 206 const tableInfo = db.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name='documents'").get() as { sql: string } | undefined; 199 207 if (tableInfo && !tableInfo.sql.includes("'slide'")) { 208 + // Drop leftover temp table from any previous failed migration attempt 209 + db.exec("DROP TABLE IF EXISTS documents_new"); 200 210 db.exec(` 201 211 CREATE TABLE documents_new ( 202 212 id TEXT PRIMARY KEY, ··· 263 273 created_at TEXT DEFAULT (datetime('now')) 264 274 ) 265 275 `); 266 - 267 - // Migration: add owner column to documents 268 - try { 269 - db.prepare("SELECT owner FROM documents LIMIT 1").get(); 270 - } catch { 271 - db.exec("ALTER TABLE documents ADD COLUMN owner TEXT"); 272 - console.log('Migrated: added owner column'); 273 - } 274 276 275 277 const MAX_VERSIONS_PER_DOC = 50; 276 278
+16
vite.config.ts
··· 16 16 transformIndexHtml(html) { 17 17 return html.replace(/%APP_VERSION%/g, pkg.version); 18 18 }, 19 + }, { 20 + // MPA route rewriting: serve the correct index.html for each sub-app in dev mode. 21 + // Without this, Vite's SPA fallback serves src/index.html for all routes. 22 + name: 'mpa-rewrite', 23 + configureServer(server) { 24 + server.middlewares.use((req, _res, next) => { 25 + const url = req.url || ''; 26 + for (const app of ['docs', 'sheets', 'forms', 'slides', 'diagrams']) { 27 + if (url.startsWith(`/${app}/`) && !url.includes('.')) { 28 + req.url = `/${app}/index.html`; 29 + break; 30 + } 31 + } 32 + next(); 33 + }); 34 + }, 19 35 }], 20 36 test: { 21 37 root: '.',