mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2025-12-06 10:41:02 -08:00
* feat: Skip restart if file system watch is enabled - the watcher will handle file changes automatically * fix: sometimes change sync interval not working fixes #310 * fix: Return false on sync failure - no successful changes were made fixes #558 * fix: step that is wrong * feat: monitoring subwiki * AI added waitForSSEReady * Revert "AI added waitForSSEReady" This reverts commit983b1c623c. * fix: error on frontend loading worker thread * fix * Update wiki.ts * auto reload view and click subwiki icon * Refactor sync echo prevention and improve logging Removed frontend-side echo prevention logic in ipcSyncAdaptor, relying solely on backend file exclusion for echo prevention. Improved console log wrappers to preserve native behavior and added a log statement to setupSSE. Updated test steps and file modification logic to better simulate external edits without modifying timestamps. Added internal documentation on sync architecture. * feat: deboucne and prevent data race when write file * Update watch-filesystem-adaptor.ts * rename camelcase * Update filesystemPlugin.feature * Fix sync interval timezone handling and add tests Refactored syncDebounceInterval logic in Sync.tsx to be timezone-independent, ensuring correct interval storage and display across all timezones. Added comprehensive tests in Sync.timezone.test.ts to verify correct behavior and document previous timezone-related bugs. fixes #310 * i18n for notification * Update index.tsx * fix: potential symlinks problem of subwiki * Update Sync.timezone.test.ts * lint * Implement backoff for file existence check Refactor file existence check to use backoff strategy and add directory tree retrieval for error reporting. * Update BACKOFF_OPTIONS with new configuration * Update wiki.ts * remove log * Update wiki.ts * fix: draft not move to sub * Update filesystemPlugin.feature * fix: routing tw logger to file * Update filesystemPlugin.feature * test: use id to check view load and sse load * Optimize test steps and screenshot logic Removed unnecessary short waits in filesystemPlugin.feature and increased wait time for tiddler state to settle. Updated application.ts to skip screenshots for wait steps, reducing redundant screenshots during test execution. * Check if the WebContents is actually loaded and remove fake webContentsViewHelper.new.ts created by AI * Update view.ts * fix: prevent echo by exclude title * test: Then file "Draft of '新条目'.tid" should not exist in "{tmpDir}/wiki/tiddlers" * Revert "fix: prevent echo by exclude title" This reverts commit86aa838d24. * fix: when move file to subwiki, delete old file * fix: prevent ipc echo change back to frontend * test: view might take longer to load * fix: minor issues * test: fix cleanup timeout * Update cleanup.ts * feat: capture webview screenshot * Update filesystemPlugin.feature * Update SyncArchitecture.md * rename * test: add some time to easy failed steps * Separate logs by test scenario for easier debugging * Update selectors for add and confirm buttons in tests Changed the CSS selectors for the add tiddler and confirm buttons in the filesystem plugin feature tests to use :has() with icon classes. This improves selector robustness and aligns with UI changes. * Ensure window has focus and is ready * Update window.ts * fix: webview screenshot capture prevent mini window to close * fix: Failed to take screenshot: Error: ENAMETOOLONG: name too long, open '/home/runner/work/TidGi-Desktop/TidGi-Desktop/userData-test/logs/screenshots/Agent workflow - Create notes- update embeddings- then search/2025-10-30T11-46-28-891Z-I type -在 wiki 工作区创建一个名为 AI Agent Guide 的笔记-内容是-智能体是一种可以执行任务的AI系统-它可以使用工具-搜索信息并与用户交互- in -chat input- element with selec-PASSED-page.png' * Update window.ts * feat: remove deprecated symlink subwiki approach * Update wiki.ts * fix: remove AI buggy bring window to front cause mini window test to fail * lint * Adjust wait time for draft saving in filesystemPlugin Increased wait time for file system plugin to save draft. * Adjust wait time for tiddler state stabilization Increased wait time to ensure tiddler state settles properly. * Refactor release workflow to simplify dependency installation Removed installation steps for x64 and arm64 dependencies, and adjusted the build process for plugins and native modules. * Enhance wait for IPC in filesystemPlugin feature Added a wait time to improve reliability of content update verification in CI.
193 lines
6.3 KiB
TypeScript
193 lines
6.3 KiB
TypeScript
import { Then, When } from '@cucumber/cucumber';
|
|
import { backOff } from 'exponential-backoff';
|
|
import { clickElement, clickElementWithText, elementExists, getDOMContent, getTextContent, isLoaded, pressKey, typeText } from '../supports/webContentsViewHelper';
|
|
import type { ApplicationWorld } from './application';
|
|
|
|
// Backoff configuration for retries
|
|
const BACKOFF_OPTIONS = {
|
|
numOfAttempts: 10,
|
|
startingDelay: 100,
|
|
timeMultiple: 2,
|
|
};
|
|
|
|
Then('I should see {string} in the browser view content', async function(this: ApplicationWorld, expectedText: string) {
|
|
if (!this.app) {
|
|
throw new Error('Application not launched');
|
|
}
|
|
|
|
if (!this.currentWindow) {
|
|
throw new Error('No current window available');
|
|
}
|
|
|
|
await backOff(
|
|
async () => {
|
|
const content = await getTextContent(this.app!);
|
|
if (!content || !content.includes(expectedText)) {
|
|
throw new Error(`Expected text "${expectedText}" not found`);
|
|
}
|
|
},
|
|
BACKOFF_OPTIONS,
|
|
).catch(async () => {
|
|
const finalContent = await getTextContent(this.app!);
|
|
throw new Error(
|
|
`Expected text "${expectedText}" not found in browser view content. Actual content: ${finalContent ? finalContent.substring(0, 200) + '...' : 'null'}`,
|
|
);
|
|
});
|
|
});
|
|
|
|
Then('I should see {string} in the browser view DOM', async function(this: ApplicationWorld, expectedText: string) {
|
|
if (!this.app) {
|
|
throw new Error('Application not launched');
|
|
}
|
|
|
|
if (!this.currentWindow) {
|
|
throw new Error('No current window available');
|
|
}
|
|
|
|
await backOff(
|
|
async () => {
|
|
const domContent = await getDOMContent(this.app!);
|
|
if (!domContent || !domContent.includes(expectedText)) {
|
|
throw new Error(`Expected text "${expectedText}" not found in DOM`);
|
|
}
|
|
},
|
|
BACKOFF_OPTIONS,
|
|
).catch(async () => {
|
|
const finalDomContent = await getDOMContent(this.app!);
|
|
throw new Error(
|
|
`Expected text "${expectedText}" not found in browser view DOM. Actual DOM: ${finalDomContent ? finalDomContent.substring(0, 200) + '...' : 'null'}`,
|
|
);
|
|
});
|
|
});
|
|
|
|
Then('the browser view should be loaded and visible', { timeout: 15000 }, async function(this: ApplicationWorld) {
|
|
if (!this.app) {
|
|
throw new Error('Application not launched');
|
|
}
|
|
|
|
if (!this.currentWindow) {
|
|
throw new Error('No current window available');
|
|
}
|
|
|
|
await backOff(
|
|
async () => {
|
|
const isLoadedResult = await isLoaded(this.app!);
|
|
if (!isLoadedResult) {
|
|
throw new Error('Browser view not loaded');
|
|
}
|
|
},
|
|
{ ...BACKOFF_OPTIONS, numOfAttempts: 15 },
|
|
).catch(() => {
|
|
throw new Error('Browser view is not loaded or visible after multiple attempts');
|
|
});
|
|
});
|
|
|
|
When('I click on {string} element in browser view with selector {string}', async function(this: ApplicationWorld, elementComment: string, selector: string) {
|
|
if (!this.app) {
|
|
throw new Error('Application not launched');
|
|
}
|
|
|
|
try {
|
|
// Check if selector contains :has-text() pseudo-selector
|
|
const hasTextMatch = selector.match(/^(.+):has-text\(['"](.+)['"]\)$/);
|
|
|
|
if (hasTextMatch) {
|
|
// Extract base selector and text content
|
|
const baseSelector = hasTextMatch[1];
|
|
const textContent = hasTextMatch[2];
|
|
await clickElementWithText(this.app, baseSelector, textContent);
|
|
} else {
|
|
// Use regular selector
|
|
await clickElement(this.app, selector);
|
|
}
|
|
} catch (error) {
|
|
throw new Error(`Failed to click ${elementComment} element with selector "${selector}" in browser view: ${error as Error}`);
|
|
}
|
|
});
|
|
|
|
When('I type {string} in {string} element in browser view with selector {string}', async function(this: ApplicationWorld, text: string, elementComment: string, selector: string) {
|
|
if (!this.app) {
|
|
throw new Error('Application not launched');
|
|
}
|
|
|
|
try {
|
|
await typeText(this.app, selector, text);
|
|
} catch (error) {
|
|
throw new Error(`Failed to type in ${elementComment} element with selector "${selector}" in browser view: ${error as Error}`);
|
|
}
|
|
});
|
|
|
|
When('I press {string} in browser view', async function(this: ApplicationWorld, key: string) {
|
|
if (!this.app) {
|
|
throw new Error('Application not launched');
|
|
}
|
|
|
|
try {
|
|
await pressKey(this.app, key);
|
|
} catch (error) {
|
|
throw new Error(`Failed to press key "${key}" in browser view: ${error as Error}`);
|
|
}
|
|
});
|
|
|
|
Then('I should not see {string} in the browser view content', async function(this: ApplicationWorld, unexpectedText: string) {
|
|
if (!this.app) {
|
|
throw new Error('Application not launched');
|
|
}
|
|
|
|
if (!this.currentWindow) {
|
|
throw new Error('No current window available');
|
|
}
|
|
|
|
// Wait a bit for UI to update
|
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
|
|
// Check that text does not exist in content
|
|
const content = await getTextContent(this.app);
|
|
if (content && content.includes(unexpectedText)) {
|
|
throw new Error(`Unexpected text "${unexpectedText}" found in browser view content`);
|
|
}
|
|
});
|
|
|
|
Then('I should not see a(n) {string} element in browser view with selector {string}', async function(this: ApplicationWorld, elementComment: string, selector: string) {
|
|
if (!this.app) {
|
|
throw new Error('Application not launched');
|
|
}
|
|
|
|
if (!this.currentWindow) {
|
|
throw new Error('No current window available');
|
|
}
|
|
|
|
await backOff(
|
|
async () => {
|
|
const exists: boolean = await elementExists(this.app!, selector);
|
|
if (exists) {
|
|
throw new Error('Element still exists');
|
|
}
|
|
},
|
|
BACKOFF_OPTIONS,
|
|
).catch(() => {
|
|
throw new Error(`Element "${elementComment}" with selector "${selector}" was found in browser view after multiple attempts, but should not be visible`);
|
|
});
|
|
});
|
|
|
|
Then('I should see a(n) {string} element in browser view with selector {string}', async function(this: ApplicationWorld, elementComment: string, selector: string) {
|
|
if (!this.app) {
|
|
throw new Error('Application not launched');
|
|
}
|
|
|
|
if (!this.currentWindow) {
|
|
throw new Error('No current window available');
|
|
}
|
|
|
|
await backOff(
|
|
async () => {
|
|
const exists: boolean = await elementExists(this.app!, selector);
|
|
if (!exists) {
|
|
throw new Error('Element does not exist yet');
|
|
}
|
|
},
|
|
BACKOFF_OPTIONS,
|
|
).catch(() => {
|
|
throw new Error(`Element "${elementComment}" with selector "${selector}" not found in browser view after multiple attempts`);
|
|
});
|
|
});
|