TidGi-Desktop/docs/Testing.md
lin onetwo ce332374bc
Fix/misc bug2 (#698)
* fix: hide non-wiki workspaces from menu and disable remove for them (#694)

* fix(workspace): make workspace settings save transactional to prevent data loss

- Move disk write before memory update in Workspace.set()
- Remove error swallowing in writeTidgiConfig() to let errors propagate
- Add error handling in useForm.ts to catch and log save failures
- Add UI error display in EditWorkspace/index.tsx
- Only update Observable after successful persistence

This fixes the issue where save button disappears but changes aren't
persisted to tidgi.config.json, causing data loss when reopening settings.

* feat(i18n): add error messages for workspace save failures

Add SaveError and SaveErrorPrefix translations in English and Chinese
to display error messages when workspace settings fail to save.

* test(e2e): add test for tagNames persistence and missing step definitions

- Add @edit-workspace-save-tagnames scenario to verify tagNames persist
  after save to tidgi.config.json
- Add 'I clear and type' step definition for clearing input before typing
- Add 'I close current window' step definition for closing windows
- This test covers the regression where save button disappears but
  changes aren't persisted

* use 5.4.0

* fix: spaced file in git op

* fix: menu register race condition

* Update pnpm-lock.yaml

* Update wiki

* fix: regenerate lockfile with pnpm 10.33.0 to fix checksum format

* fix: remove unused import and useless constructor

* fix: address Copilot review comments

- Use new path for rename/copy in git operations
- Ensure transactional workspace save (persist before cache update)
- Normalize null label to undefined in menu

* Remove close-window step; simplify UI typing

Remove the Cucumber step that closed the current window and simplify a UI step by calling locator.fill(...) inline (also replace {tmpDir} in the input). Clean up minor whitespace in gitOperations and fix indentation/extra brace around startWiki error handling in the wiki service to correct control flow and prevent accidental scope issues.

* Improve workspace form, git diff, and UI tests

Refactor EditWorkspace form and UI behavior, make git diff/status handling more robust, and update E2E tests.

- Add hasConfigChanges and related effects in useForm to correctly detect config-only changes and control restart requests; fix save button visibility in EditWorkspace and pass currentWorkspace to restart snackbar. Rename workspace section test id from 'workspace-section-search' to 'preference-section-search'.
- Enhance gitOperations.getFileDiff to use porcelain -z and a helper to parse per-path status (getPorcelainStatusForPath) for reliable untracked/deleted detection.
- Add clickBrowserViewElementWithRetry helper with backoff and text-aware selector handling; replace repetitive click logic in browser view step definitions and remove some redundant browser background assertions and a deprecated clear-and-type step.
- Update feature files (gitLog, editWorkspace, vectorSearch) to reflect selector/id/name changes and i18n fallbacks for tab/button text.

* doc

* Use localized draft selector and show e2e window

Update feature tests to target the localized draft tiddler title (data-tiddler-title$='的草稿') instead of the English prefix selector. Applied the change across features/hibernation.feature and features/tiddler.feature to ensure selectors match localized UI. Also enable SHOW_E2E_WINDOW=1 in the test:manual-e2e script in package.json so manual end-to-end runs open a visible window for debugging.
2026-04-21 22:14:43 +08:00

6.8 KiB

Testing Guide

Testing guide for TidGi-Desktop using Vitest + React Testing Library for unit tests and Playwright + Cucumber for E2E tests.

Quick Start

# Run all tests
pnpm test

# Run unit tests only
pnpm test:unit

# Run E2E tests (requires prepare packaged app, but only when you modify code in ./src) Don't need to run this if you only modify .feature file or step definition ts files.
pnpm run test:prepare-e2e
# (When only modify tests in ./features folder, and you have packaged app before, only need to run this.)
pnpm test:e2e
# Or run a specific e2e test by using same `@xxx` as in the `.feature` file.
# Not `-- --tags` , and not `tag`
# Don't directly concat filename after pnpm test:e2e, only unit test can do that, e2e test can't.
pnpm test:e2e --tags="@smoke"
# Or run a single e2e test by `--name`
pnpm test:e2e --name "Wiki-search tool usage" # Not `-- --name` , and not `name`, is is just `--name` and have "" around the value, not omitting `--name`
# Don't directly concat filename after pnpm test:e2e, only unit test can do that, e2e test can't.

# Run with coverage
pnpm test:unit -- --coverage

# Run a single test file to reduce execution time when fixing an issue.
pnpm test:unit src/services/agentDefinition/__tests__/responsePatternUtility.test.ts
# Don't directly concat filename after pnpm test:e2e, only unit test can do that, e2e test can't.

# Start packed e2e electron app manually to see what's going on as a human (AI agent is not allowed to run this, can only run commands above)
cross-env NODE_ENV=test pnpm dlx tsx ./scripts/start-e2e-app.ts

Except for above parameters, AI agent can't use other parameters, otherwise complex shell command usage or parameters will require human approval and may not passed.

Long running script

prepare and test may run for a long time. Don't execute any shell command like echo "waiting" or Start-Sleep -Seconds 5;, they are useless, and only will they interrupt the command. You need to check active terminal output in a loop until you see it is truly done.

Project Setup

Test Configuration: TypeScript-first with vitest.config.ts

  • Unit tests: Vitest + React Testing Library + jsdom
  • E2E tests: Playwright + Cucumber
  • Coverage: HTML reports in coverage/

Related file structure:

src/
├── __tests__/           # Global test setup & utilities
├── components/*/
│   └── __tests__/       # Component tests
└── services/*/
    └── __tests__/       # Service tests

features/                # E2E tests
├── *.feature           # Gherkin scenarios
├── stepDefinitions/    # Playwright implementations
└── supports/           # Test utilities

out/                    # `test:prepare-e2e` Bundled production app to test
test-artifacts/xxx-scenario-name/userData-test/           # User setting folder created during `test:e2e`
userData-dev/           # User setting folder created during `start:dev`
wiki-test/           # containing wiki folders created during `test:e2e`
wiki-dev/           # containing wiki folders created during `start:dev`

Writing Unit Tests

See docs/TestingUnit.md

Writing E2E Tests

See docs/TestingE2E.md

Key E2E Testing Patterns

  1. Window Management: Use getWindow() with retry logic for reliable window switching
  2. Generic Steps: Reusable steps for common UI interactions with descriptive selectors
  3. Domain Steps: Specific steps for complex workflows (like agent conversations)
  4. Mock Services: Use tagged cleanup for feature-specific resources
  5. Streaming Support: Special handling for real-time updates in chat interfaces
  6. Don't think about adding new step definitions or change timeout duration, unless human ask you to do. You should always reuse existing steps, and debug the fundamental reason that causes timeout. Timeout usually because of expected element not percent.
  7. If you forget to run pnpm run test:prepare-e2e after modify code in ./src folder, you may find expected elements missing.
  8. Usually don't need to add wait time, because most check already will wait for a while. Should use exact test-id to wait internal steps, and test-id should contribute larger than 2 second waiting, otherwise it is useless.

Testing Library Best Practices

Important Testing Rules:

  • Do NOT simplify tests - write comprehensive, professional unit tests
  • Can add test-ids when accessibility queries aren't practical
  • Do NOT be lazy - fix ALL tests until pnpm test:unit passes completely
  • Do NOT summarize until ALL unit tests pass
  • Focus on professional, fix all seemly complex unit tests before moving to E2E

Viewing e2e tests

We check isTest when xxxWindow.show(), so it won't popup while testing. You can clear the desktop windows so you can see it.

Log

When AI is fixing issues, you can let it add more logs for troubleshooting, and then show the latest test log files (in test-artifacts/xxx-some-scenario-name/userData-test/ ) or dev log files to the AI. Of course, it's best to run tests using pnpm test:unit, as it's fast and can be automated by AI without manual intervention. The logs should also be visible in the test, just change the mock of logger to use console log, and run a single test to get minimal logs.

If you want to send frontend log to the log file, you can't directly use import { logger } from '@services/libs/log'; you need to use void window.service.native.log('error', 'Renderer: xxx', { ...additionalMetadata });. Otherwise you will get Can't resolve 'os' error

Only use VSCode tool to read file. Don't ever use shell command to read file. Use shell command to read file may be immediately refused by user, because he don't want to manually approve shell commands.

User profile

When running tests — especially E2E or other tests that start an Electron instance — the test runner will set Electron's userData to test-artifacts/xxx-scenario-name/userData-test/. This ensures the test process uses a separate configuration and data directory from any development or production TidGi instance, and prevents accidental triggering of Electron's single-instance lock.

  • src/constants/appPaths.ts: in test mode we call app.setPath('userData', path.resolve(sourcePath, '..', 'test-artifacts/xxx-scenario-name/userData-test/')) to redirect settings and cache.
  • src/helpers/singleInstance.ts: the main process uses app.requestSingleInstanceLock() to enforce single-instance behavior; without a separate userData directory, a running local TidGi could conflict with test instances and cause one of them to exit.

For this reason, test workflows in this project (for example when running pnpm test:e2e or CI integration tests) need to do with cross-env NODE_ENV=test so it creates isolate state in userData-test.