diff --git a/features/filesystemPlugin.feature b/features/filesystemPlugin.feature index d78af55c..9a503bfd 100644 --- a/features/filesystemPlugin.feature +++ b/features/filesystemPlugin.feature @@ -27,22 +27,27 @@ Feature: Filesystem Plugin And I click on a "create sub workspace button" element with selector "button.MuiButton-colorSecondary" And I switch to "main" window Then I should see a "SubWiki workspace" element with selector "div[data-testid^='workspace-']:has-text('SubWiki')" - # After create subwiki, webview will auto refresh here, wait for wiki to restart - And I wait for SSE and watch-fs to be ready - # Click on SubWiki workspace icon to open the TestTag tiddler + # Wait for main wiki to restart after sub-wiki creation + Then I wait for main wiki to restart after sub-wiki creation + Then I wait for view to finish loading + # Click SubWiki workspace again to ensure TestTag tiddler is displayed + And I wait for 1 seconds When I click on a "SubWiki workspace button" element with selector "div[data-testid^='workspace-']:has-text('SubWiki')" - And I wait for 0.2 seconds - # Subwiki tiddler should now be visible + And I wait for 1 seconds + # Verify TestTag tiddler is visible And I should see "TestTag" in the browser view content - # create tiddler with tag + # Create tiddler with tag to test file system plugin And I click on "add tiddler button" element in browser view with selector "button[aria-label='添加条目']" # Focus on title input, clear it, and type new title in the draft tiddler And I click on "title input" element in browser view with selector "div[data-tiddler-title^='Draft of'] input.tc-titlebar.tc-edit-texteditor" + # Wait for tiddler state to settle, otherwise And I wait for 0.1 seconds And I press "Control+a" in browser view + And I wait for 0.1 seconds And I press "Delete" in browser view And I wait for 0.1 seconds And I type "TestTiddlerTitle" in "title input" element in browser view with selector "div[data-tiddler-title^='Draft of'] input.tc-titlebar.tc-edit-texteditor" + And I wait for 0.1 seconds # Input tag by typing in the tag input field - use precise selector to target the tag input specifically And I click on "tag input" element in browser view with selector "div[data-tiddler-title^='Draft of'] div.tc-edit-add-tag-ui input.tc-edit-texteditor[placeholder='标签名称']" And I press "Control+a" in browser view @@ -52,10 +57,9 @@ Feature: Filesystem Plugin # Click the add tag button to confirm the tag (not just typing) And I wait for 0.2 seconds And I click on "add tag button" element in browser view with selector "div[data-tiddler-title^='Draft of'] span.tc-add-tag-button button" - # Wait more time for ipc and filesystem to process + # Wait for file system plugin to save the draft tiddler to SubWiki folder And I wait for 3 seconds # Verify the DRAFT tiddler has been routed to sub-wiki immediately after adding the tag - # The draft file should now be in SubWiki folder with the tag applied Then file "Draft of '新条目'.tid" should exist in "{tmpDir}/SubWiki" # Click confirm button to save the tiddler And I click on "confirm button" element in browser view with selector "div[data-tiddler-title^='Draft of'] button[aria-label='确定对此条目的更改']" diff --git a/features/stepDefinitions/wiki.ts b/features/stepDefinitions/wiki.ts index a762a47e..8fb0b297 100644 --- a/features/stepDefinitions/wiki.ts +++ b/features/stepDefinitions/wiki.ts @@ -16,16 +16,16 @@ const BACKOFF_OPTIONS = { /** * Generic function to wait for a log marker to appear in wiki log files. */ -async function waitForLogMarker(searchString: string, errorMessage: string, maxWaitMs = 10000): Promise { +async function waitForLogMarker(searchString: string, errorMessage: string, maxWaitMs = 10000, logFilePattern = 'wiki-'): Promise { const logPath = path.join(process.cwd(), 'userData-test', 'logs'); await backOff( async () => { try { const files = await fs.readdir(logPath); - const wikiLogFiles = files.filter(f => f.startsWith('wiki-') && f.endsWith('.log')); + const logFiles = files.filter(f => f.startsWith(logFilePattern) && f.endsWith('.log')); - for (const file of wikiLogFiles) { + for (const file of logFiles) { const content = await fs.readFile(path.join(logPath, file), 'utf-8'); if (content.includes(searchString)) { return; @@ -216,6 +216,17 @@ Then('I wait for SSE and watch-fs to be ready', async function(this: Application await waitForLogMarker('[test-id-SSE_READY]', 'SSE backend did not become ready within timeout', 15000); }); +Then('I wait for main wiki to restart after sub-wiki creation', async function(this: ApplicationWorld) { + await waitForLogMarker('[test-id-MAIN_WIKI_RESTARTED_AFTER_SUBWIKI]', 'Main wiki did not restart after sub-wiki creation within timeout', 20000, 'TidGi-'); + // Also wait for SSE and watch-fs to be ready after restart + await waitForLogMarker('[test-id-WATCH_FS_STABILIZED]', 'watch-fs did not become ready after restart within timeout', 15000); + await waitForLogMarker('[test-id-SSE_READY]', 'SSE backend did not become ready after restart within timeout', 15000); +}); + +Then('I wait for view to finish loading', async function(this: ApplicationWorld) { + await waitForLogMarker('[test-id-VIEW_LOADED]', 'Browser view did not finish loading within timeout', 10000, 'wiki-'); +}); + Then('I wait for tiddler {string} to be added by watch-fs', async function(this: ApplicationWorld, tiddlerTitle: string) { await waitForLogMarker( `[test-id-WATCH_FS_TIDDLER_ADDED] ${tiddlerTitle}`, diff --git a/src/preload/view.ts b/src/preload/view.ts index 2bb10b2d..9367ca21 100644 --- a/src/preload/view.ts +++ b/src/preload/view.ts @@ -4,6 +4,7 @@ import type { IPossibleWindowMeta, WindowMeta } from '@services/windows/WindowPr import { WindowNames } from '@services/windows/WindowProperties'; import { browserViewMetaData, windowName } from './common/browserViewMetaData'; import { consoleLogToLogFile } from './fixer/consoleLogToLogFile'; +import { native } from './common/services'; let handled = false; const handleLoaded = (event: string): void => { @@ -20,9 +21,13 @@ const handleLoaded = (event: string): void => { async function executeJavaScriptInBrowserView(): Promise { const viewMetaData = browserViewMetaData as IPossibleWindowMeta; - await consoleLogToLogFile(viewMetaData.workspace?.name); + const workspaceName = viewMetaData.workspace?.name ?? 'unknown'; + await consoleLogToLogFile(workspaceName); const workspaceID = viewMetaData.workspace?.id; + // Log when view is fully loaded for E2E tests + void native.logFor(workspaceName, 'info', `[test-id-VIEW_LOADED] Browser view preload script executed and ready for workspace: ${workspaceName}`); + try { await webFrame.executeJavaScript(` (function() { diff --git a/src/services/wiki/index.ts b/src/services/wiki/index.ts index b738e320..9a650b18 100644 --- a/src/services/wiki/index.ts +++ b/src/services/wiki/index.ts @@ -702,6 +702,8 @@ export class Wiki implements IWikiService { // Use restartWorkspaceViewService to restart wiki worker and reload frontend view const workspaceViewService = container.get(serviceIdentifier.WorkspaceView); await workspaceViewService.restartWorkspaceViewService(mainWikiID); + // Log that main wiki restart is complete after creating sub-wiki + logger.info('[test-id-MAIN_WIKI_RESTARTED_AFTER_SUBWIKI] Main wiki restarted after sub-wiki creation', { mainWikiID, subWikiID: id }); } } else { try { diff --git a/src/services/wiki/wikiWorker/ipcServerRoutes.ts b/src/services/wiki/wikiWorker/ipcServerRoutes.ts index c18dee71..34258dbd 100644 --- a/src/services/wiki/wikiWorker/ipcServerRoutes.ts +++ b/src/services/wiki/wikiWorker/ipcServerRoutes.ts @@ -227,7 +227,10 @@ export class IpcServerRoutes { this.wikiInstance.wiki.addEventListener('change', (changes) => { observer.next(changes); }); - console.log('[test-id-SSE_READY] Wiki change observer registered and ready'); + // Log SSE ready every time a new observer subscribes (including after worker restart) + // Include timestamp to make each log entry unique for test detection + const timestamp = new Date().toISOString(); + console.log(`[test-id-SSE_READY] Wiki change observer registered and ready at ${timestamp}`); }; void getWikiChangeObserverInWorkerIIFE(); });