TidGi-Desktop/features/gitLog.feature
lin onetwo 4c5e1d16c7
Fix/misc bug (#691)
* Use git service for backups and dynamic AI menus

Switch backup actions to call gitService.commitAndSync(commitOnly) so local backups work without remote auth and AI commit generation is triggered by omitting commitMessage. Make AI-related menu items always registered but use dynamic visibility/enabled checks (isAIEnabled) so they appear/disappear at runtime. Update menu item types/imports accordingly. Optimize workspace persistence to write only the single updated workspace to settings.json (stripping syncable fields when tidgi.config exists) instead of saving all workspaces; remove the old saveWorkspacesToSettings method. Add warnings/logging: warn if git worker observable is undefined, log/notify when cloud sync is skipped due to missing auth/gitUrl. Misc: remove a redundant debug log in tidgiConfig, remove native process monitoring startup call, include commitMessage for CommitDetailsPanel sync, and drop entriesFingerprint/debug noise from git log data.

* fix: avoid rewriting unchanged workspace config

* fix: separate plain and ai backup flows

* feat: add searchable settings views

* fix: narrow sync adaptor revision type

* chore: release tidgi-shared 0.1.3

* Preferences: unify Scheduled/Background tasks and fix skeleton nav

- Remove legacy background task UI/dialogs (use ScheduledTask unified system)
- Attach invisible anchors to skeleton placeholders so sidebar scrollIntoView works while loading
- Add English/zh translation keys for AddAlarm/AddHeartbeat
- Add requestIdleCallback polyfill (tests) and speed up to 0ms for tests
- Search: match English translations (txEn) in SearchResultsView

* Fix view zero-size on minimize; realign on restore/show

Guard view bounds from 0x0 content size to prevent BrowserView disappearing when window is minimized; fall back to safe offscreen size. Add window 'restore' and 'show' handlers to realign views. Add getMemoryUsage() in wiki worker and expose RSS/heap via getWorkersInfo; show worker memory in Developer Tools diagnostics.

* Fix background/quit behavior: synchronous close handler; platform-specific runOnBackground default; add getWindowMetaSync; add forced-exit timeout for before-quit cleanup

* Refactor preferences: add definitions and registry

Add a new structured preferences system: introduce definition schemas, typed item/section types, explicit section files, a registry (allSections/sectionById), side effects, action handlers, and helper builders (zodPreferencesSchema). Add custom preference UI items and registration (customItems, registerCustomSections) and tests validating schemas. Replace previous zod/settings schema files with the new definitions and make IPreferences an explicit TypeScript interface. Small UI updates: use LanguageSelectorItem and WikiUserNameItem in Guide and Help pages. Also remove net.isOnline() pre-checks from Git.commitAndSync and Git.forcePull to avoid false-negative network detections.

* Fix blank wiki after hide+reopen: re-attach orphaned views on realign

Root cause: three compounding issues when the main window is hidden and then re-shown via a second-instance shortcut click.

1. getView() now auto-removes stale entries whose webContents.isDestroyed() == true.
   This allows addView() / showWorkspaceView() to recreate destroyed views instead of
   silently skipping them (which left the new window blank).

2. realignView() now calls browserWindow.contentView.addChildView(view) before setBounds().
   If a view survived window destruction but became orphaned (detached from its parent
   BrowserWindow), re-attaching it makes it visible again.  addChildView is idempotent
   so normal re-entrant calls are safe.

3. window.open() existing-window branch now calls addViewForAllBrowserViews(activeWorkspace)
   before realignActiveWorkspace(). This ensures that any destroyed/missing view is
   recreated BEFORE the realign attempts to reposition it.  The call is a no-op when
   views already exist and are healthy.

* Fix blank WebContentsView after restoring hidden window (Windows bug)

Electron on Windows sometimes fails to repaint a WebContentsView that remains attached to a window that is hidden and then shown again. By unconditionally calling \
emoveChildView\ followed by \ddChildView\ during \
ealignView\ and \showView\, we force the Chromium compositor to reparent and paint the view correctly, ensuring the Wiki becomes visible as soon as the user restores the app from the background.

* Fix blank WebView on window restore: show view before realign, fix same-workspace click

Two root causes identified and fixed:

1. openWorkspaceTiddler silently skipped setActiveWorkspaceView when the user
   clicked the already-active workspace icon (guard: oldId !== newId).  The view
   was blank and clicking its icon did nothing.  Guard removed  setActiveWorkspaceView
   is now always called; it is safe with the same ID (hibernation guard is already
   correct for that case).

2. The 'show' event handler and window.open() existing-window path were calling
   realignActiveWorkspace() which only calls setBounds.  On Windows, when a window
   transitions from hidden/background to visible the Chromium compositor may not
   repaint a WebContentsView whose bounds have not changed.  Both paths now call
   refreshActiveWorkspaceView()  a new lightweight helper that calls showView()
   (removeChildView + addChildView + setBounds + webContents.focus) before realigning.
   This forces a compositor repaint and makes the wiki page visible immediately.

* Refactor view restore chain: clean up redundant repaint/realign calls

Full chain analysis identified 4 structural problems:

1. refreshActiveWorkspaceView() called TWICE concurrently on window restore:
   window.open() called existedWindow.show() which fired the 'show' event
    refreshActiveWorkspaceView() AND THEN immediately called refreshActiveWorkspaceView()
   again explicitly, creating a race condition on removeChildView+addChildView.

2. realignView() contained removeChildView+addChildView, which ran immediately after
   showView() already did removeChildView+addChildView+setBounds+focus.  The second
   remove+add clobbered the focus state set by showView, breaking keyboard focus.

3. setActiveWorkspaceView() called showWorkspaceView + realignActiveWorkspace, meaning
   the view was remove+add+setBounds+focused by showView, then immediately remove+add+
   setBounds-without-focus again by realignView.  Double bounds, lost focus.

4. Same pattern in refreshActiveWorkspaceView: showWorkspaceView + realignActiveWorkspace.

Clean design after refactor:
- showView()        = force-repaint path: remove+add+setBounds+focus (unchanged)
- realignView()     = bounds-only:        setBounds ONLY, no remove+add
- showWorkspaceView = calls showView for main+mini windows
- realignActiveWorkspace = calls realignView (now just setBounds) + buildMenu;
                     used for fullscreen/sidebar/resize events
- setActiveWorkspaceView = showWorkspaceView + buildMenu (not +realignActiveWorkspace)
- refreshActiveWorkspaceView = showWorkspaceView + buildMenu (not +realignActiveWorkspace);
                     called from 'show' window event (fire-and-forget: no rethrow)
- window.open() existing-window = show() only; 'show' event handler calls
                     refreshActiveWorkspaceView automatically, no duplicate call

* chore: bump electron-ipc-cat to 2.4.0

Rolling Observable timeout (120s initial, 60s idle) fixes git-upload-pack
timeout for large repos (100+ MB) during mobile sync.

* style: unify layout between Preferences and EditWorkspace

Use PageRoot and PageInner from PreferenceComponents to eliminate subtle padding/background differences. Resize EditWorkspace window to match Preferences. Clean up lint errors.

* Add E2E test for window-restore blank-view bug + log markers

Two changes:

1. Log markers added to aid diagnosis and enable E2E verification:
   - [test-id-VIEW_SHOWN]             in ViewService.showView()
   - [test-id-REFRESH_ACTIVE_VIEW_START/DONE] in WorkspaceView.refreshActiveWorkspaceView()

2. New E2E feature: features/windowRestore.feature
   Scenario 1: 'Wiki WebContentsView is visible immediately after restoring hidden window'
     - hides main window (same path as close+runOnBackground)
     - triggers second-instance via app.emit('second-instance')
     - asserts [test-id-REFRESH_ACTIVE_VIEW_DONE] and [test-id-VIEW_SHOWN] log markers
     - asserts browser view is within visible window bounds
     - asserts wiki content is readable
   Scenario 2: 'Clicking already-active workspace icon re-shows the WebContentsView'
     - verifies the removed oldId !== newId guard: clicking current workspace must
       now call setActiveWorkspaceView which fires showView

   Two step definitions added to features/stepDefinitions/application.ts:
   - 'I hide the main window as if closing with runOnBackground'
     calls BrowserWindow.hide() directly in main process
   - 'I reopen the main window as second instance would'
     emits app 'second-instance' event in main process

* Fix E2E test: correct second-instance emit args, add wiki-ready wait in Background

Three issues found and fixed by running the tests:

1. app.emit('second-instance') argument order wrong
   DeepLinkService listener: (_event, commandLine) => commandLine.pop()
   Our emit: app.emit('second-instance', [], process.cwd(), {})
   This made 'process.cwd()' land in commandLine, .pop() failed on a string.
   Fix: app.emit('second-instance', {}, [], '', {})  fake Event first,
   then empty argv array, then workingDirectory.

2. In test mode, window.open() skips existedWindow.show() to avoid UI popups.
   The 'show' event never fired so refreshActiveWorkspaceView was never called
   and the window stayed hidden from Playwright's perspective.
   Fix: explicitly call mainWindow.show() via app.evaluate() after emitting
   second-instance, replicating what production window.open() does.

3. Background used 'the browser view should be loaded and visible' which has
   a 21-second timeout and fails before TiddlyWiki finishes initializing in
   the test environment (pre-existing issue in defaultWiki.feature too).
   Fix: replaced with deterministic log marker waits:
     [test-id-WIKI_WORKER_STARTED] + [test-id-VIEW_LOADED]
   plus 'I confirm the main window browser view is positioned within visible
   window bounds' for a structural check without content dependency.

Result: both @window-restore scenarios pass (31/31 steps green, ~48s).

* Fix reopened main window restore after recreation and rebind view resize

Root cause on Windows was not the hide/show path, but the close+recreate path when tidgi mini window keeps the app alive while runOnBackground is false.

What was actually happening:
1. The user closed the main window.
2. The app stayed alive because tidgi mini window still existed.
3. A second-instance launch recreated a new main BrowserWindow.
4. The old workspace WebContentsView still existed in ViewService.
5. But the new main window missed the automatic restore because the BrowserWindow 'show' event fired inside handleCreateBasicWindow() before registerBrowserViewWindowListeners() attached the 'show' listener.
6. If the user then clicked the workspace icon, showView() reattached the old view manually, but its resize listener was still bound to the old destroyed BrowserWindow, so resizing the new window no longer resized the view.

Fix:
- ViewService now rebinds the debounced resize handler every time showView() attaches an existing view to a BrowserWindow.
- Window.open() now detects the recreate-main-window case for BrowserView windows and immediately calls refreshActiveWorkspaceView() if the active workspace already has an existing view instance.
  This restores the view without waiting for a workspace icon click.

Why old E2E missed it:
- It simulated hide/show (runOnBackground=true) instead of the real user path (main window close + app kept alive by tidgi mini window).
- It only checked that the view was within visible bounds; it did not resize the window and assert the view filled the content area after the reopen.

New E2E coverage:
- Configures tidgiMiniWindow=true and runOnBackground=false before launch.
- Closes the main window, reopens it via second-instance, verifies refresh/view-shown markers, verifies bounds, resizes the recreated main window, and asserts the BrowserView fills the content area after the debounced resize handler runs.
- Scenario passes locally: 1 scenario, 20 steps, all green.

* Update pnpm-lock.yaml

* fix: address Copilot PR review issues

- Restore workspaceID from window.meta() in EditWorkspace (was hard-coded debug value)
- Add missing React/type imports to customComponentRegistry.ts, workspaceCustomComponentRegistry.ts, registerCustomSections.tsx, registerWorkspaceCustomSections.tsx, useSections.ts
- Fix HighlightText regex: use index parity (odd index = match) instead of stateful regex.test() with global flag
- Fix actionHandlers native.pickDirectory to read current preference value instead of passing the key string as a path
- Move PreferenceComponents import before registerCustomSections() call to fix import ordering

* fix: fix import ordering to satisfy dprint/eslint format rules

* fix: stabilize e2e selectors and EditWorkspace loading fallback

- align workspace section testids in e2e features
- migrate background-task e2e to scheduled-task selectors
- add edit workspace fallback loading when metadata/observable is late
- add deterministic switch testid for schema boolean items
- make sync snackbar assertion resilient to progress text changes
- clear draft-check timeout handle in sync service

* fix: add apiKey to test provider config so isAIAvailable() returns true

The AI commit message e2e test expects both commit-now-button and
commit-now-ai-button to appear. The AI button only renders when
isAIGenerateBackupTitleEnabled() returns true, which internally calls
externalAPIService.isAIAvailable(). That method requires a non-empty
apiKey for openAICompatible providers, but the test's
createProviderConfig() never set one, causing isAIAvailable() to
return false and the AI button to never render.

* feat(gitServer): add generateFullArchive for fast mobile clone

- Add generateFullArchive() to IGitServerService interface
- Implement tar archive generation: git archive + system tar append
- Archives working tree + minimal .git metadata (HEAD, refs, objects)
- Cache by HEAD commit hash, auto-cleanup old archives
- Bump tidgi-shared to 0.1.5

* fix(e2e): resolve workspace by runtime name/folder in step defs

* fix(ci): satisfy lint rules in gitServer archive generation
2026-04-01 15:45:26 +08:00

242 lines
17 KiB
Gherkin

Feature: Git Log Window
As a user
I want to view git commit history in a dedicated window
So that I can track changes to my wiki
Background:
Given I cleanup test wiki so it could create a new one on start
And I launch the TidGi application
And I wait for the page to load completely
Then I should see a "default wiki workspace" element with selector "div[data-testid^='workspace-']:has-text('wiki')"
# Enable file system watch for testing (default is false in production)
When I update workspace "wiki" settings:
| property | value |
| enableFileSystemWatch | true |
When I click on a "default wiki workspace button" element with selector "div[data-testid^='workspace-']:has-text('wiki')"
Then the browser view should be loaded and visible
And I wait for SSE and watch-fs to be ready
And I wait for "git initialization" log marker "[test-id-git-init-complete]"
@git
Scenario: View git commit in Git Log window
# Create a new tiddler file to trigger a git commit
When I create file "{tmpDir}/wiki/tiddlers/GitLogTestTiddler.tid" with content:
"""
created: 20250226070000000
modified: 20250226070000000
title: GitLogTestTiddler
tags: TestTag
This is a test tiddler for git log feature.
"""
Then I wait for tiddler "GitLogTestTiddler" to be added by watch-fs
# Use menu to commit the file - this will use default message (no AI configured)
When I click menu " > Git"
# wait for git operation to complete
Then I wait for "git commit completed" log marker "[test-id-git-commit-complete]"
# Open Git Log through menu
When I click menu " > "
And I switch to "gitHistory" window
And I wait for the page to load completely
# Wait for git log to query history and render UI
Then I wait for "git log UI refreshed" log marker "[test-id-git-log-refreshed]"
# Verify the git log window shows commits
Then I should see "git log list and commit with default message" elements with selectors:
| element description | selector |
| git log list | [data-testid='git-log-list'] |
| commit with default message | p.MuiTypography-body2:has-text('使') |
# Click on the commit row containing GitLogTestTiddler file
When I click on a "commit row with GitLogTestTiddler" element with selector "[data-testid^='commit-row-']:has-text('GitLogTestTiddler')"
# Verify the filename appears in the details panel (may include path like tiddlers/GitLogTestTiddler.tid)
Then I should see a "GitLogTestTiddler.tid file in details" element with selector "li:has-text('GitLogTestTiddler')"
@git
Scenario: Git Log window shows uncommitted changes and commit now button works
# Modify the existing Index.tid file to create uncommitted changes
And I modify file "{tmpDir}/wiki/tiddlers/Index.tid" to contain "Modified Index content - testing realtime update!"
Then I wait for tiddler "Index" to be updated by watch-fs
# Open Git Log window
When I click menu " > "
And I should see "Modified Index content" in the browser view content
And I switch to "gitHistory" window
And I wait for the page to load completely
# Verify uncommitted changes is auto-selected by checking if the file list is visible
Then I should see "uncommitted changes row and Index.tid file in uncommitted list" elements with selectors:
| element description | selector |
| uncommitted changes row | [data-testid='uncommitted-changes-row']|
| Index.tid file in uncommitted list | li:has-text('Index.tid') |
# Switch to Actions tab to access commit button
When I click on a "actions tab" element with selector "button[role='tab']:has-text('')"
# Verify the commit now button is visible
Then I should see a "commit now button" element with selector "button[data-testid='commit-now-button']"
# In uncommitted state, sync-to-remote button must NOT appear (it only appears for unpushed commits)
Then I should not see a "sync-to-remote button" element with selector "button[data-testid='sync-to-remote-button']"
# Clear old git-log-refreshed markers BEFORE clicking commit button
When I clear log lines containing "[test-id-git-log-refreshed]"
# Click the commit now button
When I click on a "commit now button" element with selector "button[data-testid='commit-now-button']"
# Wait for git commit to complete and UI to refresh
Then I wait for log markers:
| description | marker |
| git commit completed | [test-id-git-commit-complete] |
| git log refreshed after commit | [test-id-git-log-refreshed] |
# Verify that uncommitted changes row is gone (commit was successful)
Then I should not see a "uncommitted changes row" element with selector "[data-testid='uncommitted-changes-row']"
# Verify the correct commit is selected and we're on the latest commit (should show amend button)
Then I should see "selected commit row and commit message and amend button and revert button" elements with selectors:
| element description | selector |
| selected commit row | [data-testid^='commit-row-'][data-selected='true']:has-text('使') |
| commit message | p.MuiTypography-body2:has-text('使') |
| amend button | button:has-text('') |
| revert button | button:has-text('') |
# Clear the git-log-refreshed marker BEFORE clicking revert button
When I clear log lines containing "[test-id-git-log-refreshed]"
# Click revert button
When I click on a "revert button" element with selector "button:has-text('')"
# Wait for git revert operation to complete and UI to refresh
Then I wait for log markers:
| description | marker |
| git revert completed | [test-id-git-revert-complete] |
| git log refreshed after revert | [test-id-git-log-refreshed] |
# Verify new revert commit is selected and revert button is visible
Then I should see "selected revert commit row and revert button for the new revert commit" elements with selectors:
| element description | selector |
| selected revert commit row | [data-testid^='commit-row-'][data-selected='true']:has-text('退') |
| revert button for the new revert commit| button:has-text('') |
# Switch back to main window to verify the revert
When I switch to "main" window
# Wait for tiddler to be updated by watch-fs after git revert
Then I wait for tiddler "Index" to be updated by watch-fs
# The modified content should be reverted, and make sure file won't be deleted
Then I should not see a "missing tiddler indicator" element in browser view with selector "[data-tiddler-title='Index']:has-text('')"
Then I should not see a "modified content in Index tiddler" element in browser view with selector "[data-tiddler-title='Index']:has-text('Modified Index content')"
@git
Scenario: Discard uncommitted changes for a single file
# Modify the existing Index.tid file to create uncommitted changes
And I modify file "{tmpDir}/wiki/tiddlers/Index.tid" to contain "Discard test content - should be reverted!"
Then I wait for tiddler "Index" to be updated by watch-fs
# Open Git Log window
When I click menu " > "
And I should see "Discard test content" in the browser view content
And I switch to "gitHistory" window
And I wait for the page to load completely
Then I should see a "uncommitted changes row" element with selector "[data-testid='uncommitted-changes-row']"
# Click on the uncommitted changes row
When I click on a "uncommitted changes row" element with selector "[data-testid='uncommitted-changes-row']"
# Verify we can see the modified Index.tid file
Then I should see a "Index.tid file in uncommitted list" element with selector "li:has-text('Index.tid')"
# Click on the Index.tid file to select it
When I click on a "Index.tid file in list" element with selector "li:has-text('Index.tid')"
# Verify the file diff panel has loaded by checking for the file name header
Then I should see a "file name header in diff panel" element with selector "h6:has-text('Index.tid')"
# Click the Actions tab in the file diff panel (the one that has the file name above it)
# We need to find the Actions tab that is a sibling of the h6 containing "Index.tid"
When I click on a "actions tab in file diff panel" element with selector "h6:has-text('Index.tid') ~ div button[role='tab']:has-text('')"
# Verify the discard changes button exists (only shows for uncommitted changes)
Then I should see a "discard changes button" element with selector "button:has-text('')"
When I click on a "discard changes button" element with selector "button:has-text('')"
# Verify the file is no longer in the uncommitted list (should go back to showing no selection)
Then I should not see a "Index.tid file still selected" element with selector "li:has-text('Index.tid')[class*='selected']"
# Switch back to main window to verify the discard
When I switch to "main" window
# The modified content should be discarded
Then I should not see a "modified content in Index tiddler" element in browser view with selector "[data-tiddler-title='Index']:has-text('Discard test content')"
@git
Scenario: Git Log window auto-refreshes when files change (only when window is open)
# Open Git Log window FIRST
When I click menu " > "
And I switch to "gitHistory" window
And I wait for the page to load completely
# Should see initial commits
Then I should see a "commit history list" element with selector "[data-testid='git-log-list']"
# Now modify a file WHILE window is open - this should trigger auto-refresh
When I switch to "main" window
And I modify file "{tmpDir}/wiki/tiddlers/Index.tid" to contain "Modified with window open"
Then I wait for tiddler "Index" to be updated by watch-fs
# Wait for git log to auto-refresh after detecting file changes
And I wait for "git log auto-refreshed after file change" log marker "[test-id-git-log-refreshed]"
# Switch back to git log window
When I switch to "gitHistory" window
# Should see uncommitted changes row appear or update
Then I should see a "uncommitted changes row" element with selector "[data-testid='uncommitted-changes-row']"
# Click on uncommitted changes to verify the modified file is there
When I click on a "uncommitted changes row" element with selector "[data-testid='uncommitted-changes-row']"
# Should see Index.tid in the uncommitted list
Then I should see a "Index.tid in uncommitted list" element with selector "li:has-text('Index.tid')"
# Now create a NEW file while window is still open
When I switch to "main" window
And I create file "{tmpDir}/wiki/tiddlers/AutoRefreshTest.tid" with content:
"""
created: 20250227070000000
modified: 20250227070000000
title: AutoRefreshTest
tags: TestTag
This file is created to test auto-refresh when git log window is open.
"""
Then I wait for tiddler "AutoRefreshTest" to be added by watch-fs
# Wait for git log to auto-refresh after detecting new file
And I wait for "git log auto-refreshed after new file" log marker "[test-id-git-log-refreshed]"
# Switch back to git log window
When I switch to "gitHistory" window
# The uncommitted changes row should still be visible
Then I should see a "uncommitted changes row" element with selector "[data-testid='uncommitted-changes-row']"
# Click on uncommitted changes again to see both files
When I click on a "uncommitted changes row" element with selector "[data-testid='uncommitted-changes-row']"
# Both Index.tid and AutoRefreshTest.tid should be in the uncommitted list
Then I should see "Index.tid and AutoRefreshTest.tid in uncommitted list" elements with selectors:
| element description | selector |
| Index.tid in uncommitted list | li:has-text('Index.tid') |
| AutoRefreshTest.tid in uncommitted list | li:has-text('AutoRefreshTest.tid') |
@git @sync
Scenario: GitLog sync-to-remote button pushes unpushed commits to remote
# Configure a bare remote repository so this workspace becomes a cloud workspace
When I create a bare git repository at "{tmpDir}/remote-gitlog-sync.git"
When I open edit workspace window for workspace with name "wiki"
And I switch to "editWorkspace" window
And I wait for the page to load completely
When I click on "saveAndSyncOptions accordion and syncToCloud toggle" elements with selectors:
| element description | selector |
| saveAndSyncOptions accordion| [data-testid='preference-section-saveAndSync'] |
| syncToCloud toggle | [data-testid='synced-local-workspace-switch'] |
When I type in "git url input and github username input and github email input and github token input" elements with selectors:
| text | selector |
| {tmpDir}/remote-gitlog-sync.git | label:has-text('Git线') + * input, label:has-text('Git Repo URL') + * input, input[aria-label='Git线'], input[aria-label='Git Repo URL'] |
| testuser | [data-testid='github-userName-input'] input |
| testuser@example.com | [data-testid='github-email-input'] input |
| test-token-12345 | [data-testid='github-token-input'] input |
When I click on a "save workspace button" element with selector "[data-testid='edit-workspace-save-button']"
Then I should not see a "save workspace button" element with selector "[data-testid='edit-workspace-save-button']"
When I switch to "main" window
# Create a tiddler file to produce a change
When I create file "{tmpDir}/wiki/tiddlers/SyncButtonTest.tid" with content:
"""
created: 20250226100000000
modified: 20250226100000000
title: SyncButtonTest
tags: SyncTest
This tiddler tests the GitLog sync-to-remote button.
"""
Then I wait for tiddler "SyncButtonTest" to be added by watch-fs
# Do a local-only backup first (creates an unpushed commit)
When I click menu " > Git"
Then I wait for "git commit completed" log marker "[test-id-git-commit-complete]"
# Open GitLog window and verify the sync-to-remote button appears on the unpushed commit
When I click menu " > "
And I switch to "gitHistory" window
And I wait for the page to load completely
Then I wait for "git log UI refreshed" log marker "[test-id-git-log-refreshed]"
# Switch to Actions tab
When I click on a "actions tab" element with selector "button[role='tab']:has-text(''), button[role='tab']:has-text('Actions')"
# Verify sync-to-remote button is visible (this is the button that was previously broken)
Then I should see a "sync-to-remote button" element with selector "button[data-testid='sync-to-remote-button']"
When I clear test-id markers from logs
# Click the sync-to-remote button
When I click on a "sync-to-remote button" element with selector "button[data-testid='sync-to-remote-button']"
Then I wait for "git sync completed" log marker "[test-id-git-sync-complete]"
# Verify the file was pushed to the remote repository
And the remote repository "{tmpDir}/remote-gitlog-sync.git" should contain file "tiddlers/SyncButtonTest.tid"