mirror of
https://github.com/callumalpass/tasknotes.git
synced 2026-07-22 12:50:26 +00:00
test(#1425): Add skipped tests for calendar auto-refresh on time entry/pomodoro changes
Documents expected behavior for Issue #1425 feature request: - Calendar should refresh immediately when time entries are saved - Calendar should refresh immediately when Pomodoro session completes - Added prerequisite test for time entry toggle visibility Tests are marked as .fixme pending implementation of the feature.
This commit is contained in:
parent
6f917b1eef
commit
13a91f5bca
1 changed files with 333 additions and 0 deletions
|
|
@ -8316,3 +8316,336 @@ test.describe('Issue #1423 - Project cards refresh when subtasks are removed', (
|
|||
}, parentTitle);
|
||||
});
|
||||
});
|
||||
|
||||
// Issue #1425: [FR] Auto-Refresh Calendar Upon Edit to Time Entries
|
||||
// https://github.com/anthropics/tasknotes/issues/1425
|
||||
//
|
||||
// Feature Request:
|
||||
// When (a) users select `Save` after editing the Time Entries for a task, and
|
||||
// (b) upon completing a Pomodoro Session, the Advanced Calendar should automatically
|
||||
// update if it is presently open.
|
||||
//
|
||||
// Current behavior:
|
||||
// - Time entry edits trigger EVENT_TASK_UPDATED but calendar uses 5-second debounce
|
||||
// - Pomodoro completion triggers EVENT_TASK_UPDATED via stopTimeTracking() but
|
||||
// the calendar does not listen to EVENT_POMODORO_COMPLETE specifically
|
||||
// - User-initiated actions should use expectImmediateUpdate() but currently don't
|
||||
//
|
||||
// Expected behavior:
|
||||
// - Calendar should refresh immediately (not after 5-second debounce) when:
|
||||
// 1. User saves time entries via Time Entry Editor modal
|
||||
// 2. A Pomodoro session completes (work session ends, creating a time entry)
|
||||
test.describe('Issue #1425 - Calendar auto-refresh on time entry and pomodoro updates', () => {
|
||||
// Helper to open calendar view
|
||||
async function openCalendarView(page: Page): Promise<void> {
|
||||
await ensureSidebarExpanded(page);
|
||||
|
||||
// First expand TaskNotes folder if collapsed
|
||||
const tasknotesFolder = page.locator('.nav-folder-title').filter({ hasText: /^TaskNotes$/ }).first();
|
||||
if (await tasknotesFolder.isVisible({ timeout: 5000 }).catch(() => false)) {
|
||||
const parentFolder = tasknotesFolder.locator('xpath=ancestor::div[contains(@class, "nav-folder")][1]');
|
||||
const isTasknotesCollapsed = await parentFolder.evaluate(el => el.classList.contains('is-collapsed')).catch(() => true);
|
||||
if (isTasknotesCollapsed) {
|
||||
await tasknotesFolder.click();
|
||||
await page.waitForTimeout(500);
|
||||
}
|
||||
}
|
||||
|
||||
// Then expand Views folder if collapsed
|
||||
const viewsFolder = page.locator('.nav-folder-title').filter({ hasText: /^Views$/ });
|
||||
if (await viewsFolder.isVisible({ timeout: 5000 }).catch(() => false)) {
|
||||
const parentFolder = viewsFolder.locator('xpath=ancestor::div[contains(@class, "nav-folder")][1]');
|
||||
const isViewsCollapsed = await parentFolder.evaluate(el => el.classList.contains('is-collapsed')).catch(() => true);
|
||||
if (isViewsCollapsed) {
|
||||
await viewsFolder.click();
|
||||
await page.waitForTimeout(500);
|
||||
}
|
||||
}
|
||||
|
||||
// Click on calendar-default.base file to open it
|
||||
const calendarFile = page.locator('.nav-file-title').filter({ hasText: /calendar-default/ }).first();
|
||||
await calendarFile.click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Wait for calendar to be rendered
|
||||
await expect(page.locator('.fc-view-harness')).toBeVisible({ timeout: 10000 });
|
||||
}
|
||||
|
||||
test.fixme('calendar should refresh immediately when time entries are saved', async () => {
|
||||
// Issue #1425: Time entry edits should trigger immediate calendar refresh
|
||||
//
|
||||
// Steps to reproduce:
|
||||
// 1. Create a task with no time entries
|
||||
// 2. Open Advanced Calendar view
|
||||
// 3. Open Time Entry Editor for the task
|
||||
// 4. Add a time entry for today
|
||||
// 5. Save the time entries
|
||||
//
|
||||
// Expected: Calendar should show the new time entry event immediately
|
||||
// Actual: Calendar waits up to 5 seconds (debounce) before showing the entry
|
||||
const page = getPage();
|
||||
|
||||
// Create a test task
|
||||
const taskTitle = 'Issue1425 Time Entry Test';
|
||||
const now = new Date();
|
||||
|
||||
await page.evaluate(async (taskTitle) => {
|
||||
// @ts-ignore - Obsidian global
|
||||
const app = (window as any).app;
|
||||
const plugin = app?.plugins?.plugins?.['tasknotes'];
|
||||
if (!plugin?.taskService) return;
|
||||
|
||||
await plugin.taskService.createTask({
|
||||
title: taskTitle,
|
||||
status: 'todo',
|
||||
priority: 'normal',
|
||||
timeEntries: [] // No time entries initially
|
||||
});
|
||||
}, taskTitle);
|
||||
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Open calendar view
|
||||
await openCalendarView(page);
|
||||
|
||||
// The calendar should NOT show any time entry events for this task yet
|
||||
let timeEntryEvent = page.locator('.fc-event').filter({ hasText: taskTitle });
|
||||
await expect(timeEntryEvent).not.toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Add a time entry via the Time Entry Editor
|
||||
// (In practice, we'd open the context menu and click "Edit Time Entries")
|
||||
// For this test, we'll add the time entry directly via the API
|
||||
await page.evaluate(async ({ taskTitle, now }) => {
|
||||
// @ts-ignore - Obsidian global
|
||||
const app = (window as any).app;
|
||||
const plugin = app?.plugins?.plugins?.['tasknotes'];
|
||||
if (!plugin?.taskService) return;
|
||||
|
||||
const allTasks = plugin.taskService.getAllTasks() || [];
|
||||
const task = allTasks.find((t: any) => t.title === taskTitle);
|
||||
if (!task) return;
|
||||
|
||||
// Simulate what TimeEntryEditorModal does when saving
|
||||
const startTime = new Date(now);
|
||||
startTime.setHours(startTime.getHours() - 1);
|
||||
const endTime = new Date(now);
|
||||
|
||||
const timeEntry = {
|
||||
startTime: startTime.toISOString(),
|
||||
endTime: endTime.toISOString(),
|
||||
duration: 60, // 1 hour
|
||||
description: 'Test time entry'
|
||||
};
|
||||
|
||||
await plugin.taskService.updateTask(task, {
|
||||
timeEntries: [timeEntry]
|
||||
});
|
||||
|
||||
// The code in main.ts also triggers EVENT_DATA_CHANGED after updateTask
|
||||
// This should cause calendar to refresh
|
||||
plugin.emitter.trigger('data-changed');
|
||||
}, { taskTitle, now: now.toISOString() });
|
||||
|
||||
// Take screenshot before waiting for debounce
|
||||
await page.screenshot({ path: 'test-results/screenshots/issue-1425-before-refresh.png' });
|
||||
|
||||
// BUG: The calendar should refresh IMMEDIATELY (within 1-2 seconds)
|
||||
// but due to the 5-second debounce, we have to wait longer
|
||||
// This test documents the bug - it should pass once fixed
|
||||
timeEntryEvent = page.locator('.fc-event').filter({ hasText: taskTitle });
|
||||
|
||||
// The fix would call expectImmediateUpdate() before saving time entries
|
||||
// so the calendar bypasses the 5-second debounce
|
||||
await expect(timeEntryEvent).toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Cleanup
|
||||
await page.evaluate(async (taskTitle) => {
|
||||
// @ts-ignore - Obsidian global
|
||||
const app = (window as any).app;
|
||||
const plugin = app?.plugins?.plugins?.['tasknotes'];
|
||||
if (!plugin?.taskService) return;
|
||||
|
||||
const allTasks = plugin.taskService.getAllTasks() || [];
|
||||
const task = allTasks.find((t: any) => t.title === taskTitle);
|
||||
if (task) await plugin.taskService.deleteTask(task.path);
|
||||
}, taskTitle);
|
||||
});
|
||||
|
||||
test.fixme('calendar should refresh immediately when pomodoro session completes', async () => {
|
||||
// Issue #1425: Pomodoro completion should trigger immediate calendar refresh
|
||||
//
|
||||
// Steps to reproduce:
|
||||
// 1. Create a task
|
||||
// 2. Open Advanced Calendar view
|
||||
// 3. Start a Pomodoro session for the task
|
||||
// 4. Complete the Pomodoro session (or let timer run out)
|
||||
//
|
||||
// Expected: Calendar should show the new time entry from the Pomodoro immediately
|
||||
// Actual: Calendar may take up to 5 seconds to refresh, or may not refresh at all
|
||||
// if the calendar doesn't listen to EVENT_POMODORO_COMPLETE
|
||||
const page = getPage();
|
||||
|
||||
// Create a test task
|
||||
const taskTitle = 'Issue1425 Pomodoro Test';
|
||||
|
||||
await page.evaluate(async (taskTitle) => {
|
||||
// @ts-ignore - Obsidian global
|
||||
const app = (window as any).app;
|
||||
const plugin = app?.plugins?.plugins?.['tasknotes'];
|
||||
if (!plugin?.taskService) return;
|
||||
|
||||
await plugin.taskService.createTask({
|
||||
title: taskTitle,
|
||||
status: 'todo',
|
||||
priority: 'normal'
|
||||
});
|
||||
}, taskTitle);
|
||||
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Open calendar view
|
||||
await openCalendarView(page);
|
||||
|
||||
// No time entry events should exist for this task yet
|
||||
let timeEntryEvent = page.locator('.fc-event').filter({ hasText: taskTitle });
|
||||
await expect(timeEntryEvent).not.toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Simulate a completed Pomodoro session
|
||||
// In practice, this would happen after 25 minutes, but we can simulate the
|
||||
// end result which is a time entry being added to the task
|
||||
await page.evaluate(async (taskTitle) => {
|
||||
// @ts-ignore - Obsidian global
|
||||
const app = (window as any).app;
|
||||
const plugin = app?.plugins?.plugins?.['tasknotes'];
|
||||
if (!plugin?.taskService || !plugin?.pomodoroService) return;
|
||||
|
||||
const allTasks = plugin.taskService.getAllTasks() || [];
|
||||
const task = allTasks.find((t: any) => t.title === taskTitle);
|
||||
if (!task) return;
|
||||
|
||||
// Simulate what PomodoroService.completePomodoro() does:
|
||||
// 1. It calls stopTimeTracking which adds a time entry
|
||||
// 2. It triggers EVENT_POMODORO_COMPLETE
|
||||
const now = new Date();
|
||||
const startTime = new Date(now.getTime() - 25 * 60 * 1000); // 25 min ago
|
||||
|
||||
const timeEntry = {
|
||||
startTime: startTime.toISOString(),
|
||||
endTime: now.toISOString(),
|
||||
duration: 25,
|
||||
description: 'Pomodoro session'
|
||||
};
|
||||
|
||||
await plugin.taskService.updateTask(task, {
|
||||
timeEntries: [timeEntry]
|
||||
});
|
||||
|
||||
// Trigger the pomodoro complete event (which calendar doesn't currently listen to)
|
||||
plugin.emitter.trigger('pomodoro-complete', {
|
||||
session: {
|
||||
type: 'work',
|
||||
taskPath: task.path,
|
||||
completed: true,
|
||||
startTime: startTime.toISOString(),
|
||||
endTime: now.toISOString()
|
||||
},
|
||||
nextType: 'short-break'
|
||||
});
|
||||
}, taskTitle);
|
||||
|
||||
// Take screenshot
|
||||
await page.screenshot({ path: 'test-results/screenshots/issue-1425-pomodoro-complete.png' });
|
||||
|
||||
// BUG: The calendar should refresh IMMEDIATELY after pomodoro completion
|
||||
// Currently it either waits for debounce or doesn't respond to EVENT_POMODORO_COMPLETE
|
||||
timeEntryEvent = page.locator('.fc-event').filter({ hasText: taskTitle });
|
||||
|
||||
// The fix would be to:
|
||||
// 1. Have CalendarView listen to EVENT_POMODORO_COMPLETE
|
||||
// 2. Call expectImmediateUpdate() when receiving that event
|
||||
// OR
|
||||
// 1. Have PomodoroService trigger EVENT_DATA_CHANGED after stopTimeTracking
|
||||
// 2. Have the calendar recognize this as a user-initiated action
|
||||
await expect(timeEntryEvent).toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Cleanup
|
||||
await page.evaluate(async (taskTitle) => {
|
||||
// @ts-ignore - Obsidian global
|
||||
const app = (window as any).app;
|
||||
const plugin = app?.plugins?.plugins?.['tasknotes'];
|
||||
if (!plugin?.taskService) return;
|
||||
|
||||
const allTasks = plugin.taskService.getAllTasks() || [];
|
||||
const task = allTasks.find((t: any) => t.title === taskTitle);
|
||||
if (task) await plugin.taskService.deleteTask(task.path);
|
||||
}, taskTitle);
|
||||
});
|
||||
|
||||
test.fixme('calendar should show time entry toggle enabled to see time entry events', async () => {
|
||||
// Issue #1425: Prerequisite - verify time entries are visible in calendar
|
||||
//
|
||||
// This test verifies that the calendar's "Show Time Entries" toggle is working
|
||||
// and time entry events can be displayed. This is a prerequisite for the
|
||||
// auto-refresh feature.
|
||||
const page = getPage();
|
||||
|
||||
// Create a test task with a time entry
|
||||
const taskTitle = 'Issue1425 Toggle Test';
|
||||
const now = new Date();
|
||||
|
||||
await page.evaluate(async ({ taskTitle, now }) => {
|
||||
// @ts-ignore - Obsidian global
|
||||
const app = (window as any).app;
|
||||
const plugin = app?.plugins?.plugins?.['tasknotes'];
|
||||
if (!plugin?.taskService) return;
|
||||
|
||||
const startTime = new Date(now);
|
||||
startTime.setHours(startTime.getHours() - 1);
|
||||
const endTime = new Date(now);
|
||||
|
||||
await plugin.taskService.createTask({
|
||||
title: taskTitle,
|
||||
status: 'todo',
|
||||
priority: 'normal',
|
||||
timeEntries: [{
|
||||
startTime: startTime.toISOString(),
|
||||
endTime: endTime.toISOString(),
|
||||
duration: 60,
|
||||
description: 'Existing time entry'
|
||||
}]
|
||||
});
|
||||
}, { taskTitle, now: now.toISOString() });
|
||||
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Open calendar view
|
||||
await openCalendarView(page);
|
||||
|
||||
// Wait for calendar to fully load
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Check if "Show Time Entries" toggle exists and is enabled
|
||||
// The calendar config should have showTimeEntries option
|
||||
const timeEntryEvent = page.locator('.fc-event').filter({ hasText: taskTitle });
|
||||
|
||||
// If time entries toggle is enabled, the event should be visible
|
||||
// (after accounting for the current date view)
|
||||
await page.screenshot({ path: 'test-results/screenshots/issue-1425-toggle-test.png' });
|
||||
|
||||
// Note: This may fail if the calendar is not showing today's date by default
|
||||
// or if showTimeEntries is disabled in the calendar config
|
||||
await expect(timeEntryEvent).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// Cleanup
|
||||
await page.evaluate(async (taskTitle) => {
|
||||
// @ts-ignore - Obsidian global
|
||||
const app = (window as any).app;
|
||||
const plugin = app?.plugins?.plugins?.['tasknotes'];
|
||||
if (!plugin?.taskService) return;
|
||||
|
||||
const allTasks = plugin.taskService.getAllTasks() || [];
|
||||
const task = allTasks.find((t: any) => t.title === taskTitle);
|
||||
if (task) await plugin.taskService.deleteTask(task.path);
|
||||
}, taskTitle);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue