mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
chore: resolve conflict
This commit is contained in:
parent
7e5025f44d
commit
7061c2b58b
9 changed files with 412 additions and 66 deletions
|
|
@ -17,6 +17,10 @@ import {
|
|||
import { Task, CanvasTaskMetadata } from "../types/task";
|
||||
import { createMockPlugin, createMockApp } from "./mockUtils";
|
||||
|
||||
// Mock Date to return consistent date for tests
|
||||
const mockDate = new Date("2025-07-04T12:00:00.000Z");
|
||||
const originalDate = Date;
|
||||
|
||||
// Mock Canvas task updater
|
||||
const mockCanvasTaskUpdater = {
|
||||
deleteCanvasTask: jest.fn(),
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ import { Task } from "../types/task";
|
|||
import { createMockPlugin, createMockApp } from "./mockUtils";
|
||||
import TaskProgressBarPlugin from "../index";
|
||||
|
||||
// Mock Date to return consistent date for tests
|
||||
const mockDate = new Date("2025-07-04T12:00:00.000Z");
|
||||
const originalDate = Date;
|
||||
|
||||
// Mock vault
|
||||
const mockVault = {
|
||||
getAbstractFileByPath: jest.fn(),
|
||||
|
|
@ -44,6 +48,12 @@ describe("ArchiveActionExecutor - Markdown Tasks", () => {
|
|||
mockPlugin = createMockPlugin();
|
||||
mockApp = createMockApp();
|
||||
|
||||
// Mock Date globally
|
||||
global.Date = jest.fn(() => mockDate) as any;
|
||||
global.Date.now = jest.fn(() => mockDate.getTime());
|
||||
global.Date.parse = originalDate.parse;
|
||||
global.Date.UTC = originalDate.UTC;
|
||||
|
||||
// Reset mocks
|
||||
jest.clearAllMocks();
|
||||
|
||||
|
|
@ -69,6 +79,11 @@ describe("ArchiveActionExecutor - Markdown Tasks", () => {
|
|||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Restore original Date
|
||||
global.Date = originalDate;
|
||||
});
|
||||
|
||||
describe("Markdown Task Archiving", () => {
|
||||
it("should successfully archive Markdown task with onCompletion metadata cleanup", async () => {
|
||||
const markdownTask: Task = {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ describe("MoveActionExecutor", () => {
|
|||
tags: [],
|
||||
children: [],
|
||||
},
|
||||
line: 3,
|
||||
line: 1,
|
||||
filePath: "current.md",
|
||||
};
|
||||
|
||||
|
|
@ -123,22 +123,18 @@ describe("MoveActionExecutor", () => {
|
|||
|
||||
it("should move task to existing target file", async () => {
|
||||
const sourceContent = `# Current Tasks
|
||||
|
||||
- [ ] Keep this task
|
||||
- [x] Task to move
|
||||
- [ ] Keep this task too`;
|
||||
|
||||
const targetContent = `# Completed Tasks
|
||||
|
||||
- [x] Previous completed task`;
|
||||
|
||||
const expectedSourceContent = `# Current Tasks
|
||||
|
||||
- [ ] Keep this task
|
||||
- [ ] Keep this task too`;
|
||||
|
||||
const expectedTargetContent = `# Completed Tasks
|
||||
|
||||
- [x] Previous completed task
|
||||
- [x] Task to move`;
|
||||
|
||||
|
|
@ -158,14 +154,16 @@ describe("MoveActionExecutor", () => {
|
|||
"Task moved to archive/completed.md successfully"
|
||||
);
|
||||
|
||||
// Verify source file was updated (task removed)
|
||||
expect(mockVault.modify).toHaveBeenCalledWith(
|
||||
// Verify source file was updated (task removed) - first call
|
||||
expect(mockVault.modify).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
{ path: "current.md" },
|
||||
expectedSourceContent
|
||||
);
|
||||
|
||||
// Verify target file was updated (task added)
|
||||
expect(mockVault.modify).toHaveBeenCalledWith(
|
||||
// Verify target file was updated (task added) - second call
|
||||
expect(mockVault.modify).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
{ path: "archive/completed.md" },
|
||||
expectedTargetContent
|
||||
);
|
||||
|
|
@ -174,7 +172,7 @@ describe("MoveActionExecutor", () => {
|
|||
it("should create target file if it does not exist", async () => {
|
||||
const taskWithCorrectLine = {
|
||||
...mockTask,
|
||||
line: 2, // Correct line for 3-line sourceContent
|
||||
line: 0, // Correct line for single-line content
|
||||
};
|
||||
|
||||
const contextWithCorrectLine = {
|
||||
|
|
@ -182,19 +180,19 @@ describe("MoveActionExecutor", () => {
|
|||
task: taskWithCorrectLine,
|
||||
};
|
||||
|
||||
const sourceContent = `# Current Tasks
|
||||
|
||||
- [x] Task to move`;
|
||||
|
||||
const expectedSourceContent = `# Current Tasks`;
|
||||
const sourceContent = `- [x] Task to move`;
|
||||
const expectedSourceContent = ``;
|
||||
|
||||
// Target file is created empty, then modified with the task
|
||||
const expectedTargetContent = `- [x] Task to move`;
|
||||
|
||||
// Mock source file operations
|
||||
mockVault.getFileByPath
|
||||
.mockReturnValueOnce({ path: "current.md" }) // Source file exists
|
||||
.mockReturnValueOnce(null); // Target file doesn't exist
|
||||
mockVault.read.mockResolvedValueOnce(sourceContent);
|
||||
mockVault.read
|
||||
.mockResolvedValueOnce(sourceContent) // Read source
|
||||
.mockResolvedValueOnce(""); // Read target (empty after creation)
|
||||
mockVault.create.mockResolvedValue({
|
||||
path: "archive/completed.md",
|
||||
});
|
||||
|
|
@ -210,17 +208,25 @@ describe("MoveActionExecutor", () => {
|
|||
"Task moved to archive/completed.md successfully"
|
||||
);
|
||||
|
||||
// Verify target file was created with task
|
||||
// Verify target file was created
|
||||
expect(mockVault.create).toHaveBeenCalledWith(
|
||||
"archive/completed.md",
|
||||
expectedTargetContent
|
||||
""
|
||||
);
|
||||
|
||||
// Verify source file was updated
|
||||
expect(mockVault.modify).toHaveBeenCalledWith(
|
||||
// Verify source file was updated (task removed) - first call
|
||||
expect(mockVault.modify).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
{ path: "current.md" },
|
||||
expectedSourceContent
|
||||
);
|
||||
|
||||
// Verify target file was updated (task added) - second call
|
||||
expect(mockVault.modify).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
{ path: "archive/completed.md" },
|
||||
expectedTargetContent
|
||||
);
|
||||
});
|
||||
|
||||
it("should move task to specific section in target file", async () => {
|
||||
|
|
@ -232,7 +238,7 @@ describe("MoveActionExecutor", () => {
|
|||
|
||||
const taskWithCorrectLine = {
|
||||
...mockTask,
|
||||
line: 0, // Correct line for 1-line sourceContent
|
||||
line: 0, // Correct line for single-line content
|
||||
};
|
||||
|
||||
const contextWithCorrectLine = {
|
||||
|
|
@ -263,6 +269,7 @@ describe("MoveActionExecutor", () => {
|
|||
## Completed Tasks
|
||||
- [x] Previous completed task
|
||||
- [x] Task to move
|
||||
|
||||
## Other Section
|
||||
- [ ] Some other task`;
|
||||
|
||||
|
|
@ -305,7 +312,7 @@ describe("MoveActionExecutor", () => {
|
|||
|
||||
const taskWithCorrectLine = {
|
||||
...mockTask,
|
||||
line: 0, // Correct line for 1-line sourceContent
|
||||
line: 0, // Correct line for single-line content
|
||||
};
|
||||
|
||||
const contextWithCorrectLine = {
|
||||
|
|
@ -361,6 +368,17 @@ describe("MoveActionExecutor", () => {
|
|||
});
|
||||
|
||||
it("should handle task not found in source file", async () => {
|
||||
// Use a line number that's out of bounds
|
||||
const taskWithInvalidLine = {
|
||||
...mockTask,
|
||||
line: 10, // Line doesn't exist in content
|
||||
};
|
||||
|
||||
const contextWithInvalidLine = {
|
||||
...mockContext,
|
||||
task: taskWithInvalidLine,
|
||||
};
|
||||
|
||||
const sourceContent = `# Current Tasks
|
||||
|
||||
- [ ] Different task
|
||||
|
|
@ -371,7 +389,10 @@ describe("MoveActionExecutor", () => {
|
|||
});
|
||||
mockVault.read.mockResolvedValueOnce(sourceContent);
|
||||
|
||||
const result = await executor.execute(mockContext, config);
|
||||
const result = await executor.execute(
|
||||
contextWithInvalidLine,
|
||||
config
|
||||
);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toBe("Task line not found in source file");
|
||||
|
|
@ -389,7 +410,7 @@ describe("MoveActionExecutor", () => {
|
|||
it("should handle target file creation error", async () => {
|
||||
const taskWithCorrectLine = {
|
||||
...mockTask,
|
||||
line: 0, // Correct line for 1-line sourceContent
|
||||
line: 0, // Correct line for single-line content
|
||||
};
|
||||
|
||||
const contextWithCorrectLine = {
|
||||
|
|
@ -420,7 +441,9 @@ describe("MoveActionExecutor", () => {
|
|||
const taskWithMetadata = {
|
||||
...mockTask,
|
||||
content: "Task with metadata #tag @context 📅 2024-01-01",
|
||||
line: 0, // Correct line for 1-line sourceContent
|
||||
originalMarkdown:
|
||||
"- [x] Task with metadata #tag @context 📅 2024-01-01",
|
||||
line: 0, // Correct line for single-line content
|
||||
};
|
||||
|
||||
const contextWithMetadata = {
|
||||
|
|
@ -432,7 +455,6 @@ describe("MoveActionExecutor", () => {
|
|||
const targetContent = `# Archive`;
|
||||
const expectedSourceContent = ``; // Source file should be empty after task removal
|
||||
const expectedTargetContent = `# Archive
|
||||
|
||||
- [x] Task with metadata #tag @context 📅 2024-01-01`;
|
||||
|
||||
mockVault.getFileByPath
|
||||
|
|
@ -510,12 +532,26 @@ describe("MoveActionExecutor", () => {
|
|||
targetFile: "archive.md",
|
||||
};
|
||||
|
||||
// Task line 1 doesn't exist in empty file (only line 0 would be empty string)
|
||||
const taskWithInvalidLine = {
|
||||
...mockTask,
|
||||
line: 1, // Line doesn't exist in empty content
|
||||
};
|
||||
|
||||
const contextWithInvalidLine = {
|
||||
...mockContext,
|
||||
task: taskWithInvalidLine,
|
||||
};
|
||||
|
||||
mockVault.getFileByPath.mockReturnValueOnce({
|
||||
path: "current.md",
|
||||
});
|
||||
mockVault.read.mockResolvedValueOnce("");
|
||||
|
||||
const result = await executor.execute(mockContext, config);
|
||||
const result = await executor.execute(
|
||||
contextWithInvalidLine,
|
||||
config
|
||||
);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toBe("Task line not found in source file");
|
||||
|
|
@ -529,7 +565,7 @@ describe("MoveActionExecutor", () => {
|
|||
|
||||
const taskWithCorrectLine = {
|
||||
...mockTask,
|
||||
line: 0, // Correct line for 1-line sourceContent
|
||||
line: 0, // Correct line for single-line content
|
||||
};
|
||||
|
||||
const contextWithCorrectLine = {
|
||||
|
|
@ -577,21 +613,28 @@ describe("MoveActionExecutor", () => {
|
|||
targetFile: "archive.md",
|
||||
};
|
||||
|
||||
const sourceContent = `# Project
|
||||
const taskWithCorrectLine = {
|
||||
...mockTask,
|
||||
line: 2, // Correct line for the nested task
|
||||
};
|
||||
|
||||
const contextWithCorrectLine = {
|
||||
...mockContext,
|
||||
task: taskWithCorrectLine,
|
||||
};
|
||||
|
||||
const sourceContent = `# Project
|
||||
- [ ] Parent task
|
||||
- [x] Task to move
|
||||
- [ ] Sibling task`;
|
||||
|
||||
const expectedSourceContent = `# Project
|
||||
|
||||
- [ ] Parent task
|
||||
- [ ] Sibling task`;
|
||||
|
||||
const targetContent = `# Archive`;
|
||||
const expectedTargetContent = `# Archive
|
||||
|
||||
- [x] Task to move`;
|
||||
- [x] Task to move`;
|
||||
|
||||
mockVault.getFileByPath
|
||||
.mockReturnValueOnce({ path: "current.md" })
|
||||
|
|
@ -601,14 +644,23 @@ describe("MoveActionExecutor", () => {
|
|||
.mockResolvedValueOnce(targetContent);
|
||||
mockVault.modify.mockResolvedValue(undefined);
|
||||
|
||||
const result = await executor.execute(mockContext, config);
|
||||
const result = await executor.execute(
|
||||
contextWithCorrectLine,
|
||||
config
|
||||
);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(mockVault.modify).toHaveBeenCalledWith(
|
||||
|
||||
// Verify source file was updated (task removed) - first call
|
||||
expect(mockVault.modify).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
{ path: "current.md" },
|
||||
expectedSourceContent
|
||||
);
|
||||
expect(mockVault.modify).toHaveBeenCalledWith(
|
||||
|
||||
// Verify target file was updated (task added) - second call
|
||||
expect(mockVault.modify).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
{ path: "archive.md" },
|
||||
expectedTargetContent
|
||||
);
|
||||
|
|
@ -628,26 +680,15 @@ describe("MoveActionExecutor", () => {
|
|||
tags: [],
|
||||
children: [],
|
||||
},
|
||||
line: 2,
|
||||
line: 0,
|
||||
filePath: "source.md",
|
||||
};
|
||||
|
||||
const sourceContent = `# Tasks
|
||||
|
||||
- [x] Task with onCompletion 🏁 delete
|
||||
- [ ] Other task`;
|
||||
|
||||
const targetContent = `# Archive
|
||||
|
||||
- [x] Previous task`;
|
||||
|
||||
const expectedSourceContent = `# Tasks
|
||||
|
||||
- [ ] Other task`;
|
||||
const sourceContent = `- [x] Task with onCompletion 🏁 delete`;
|
||||
const targetContent = `# Archive`;
|
||||
|
||||
const expectedSourceContent = ``;
|
||||
const expectedTargetContent = `# Archive
|
||||
|
||||
- [x] Previous task
|
||||
- [x] Task with onCompletion`;
|
||||
|
||||
const config: OnCompletionMoveConfig = {
|
||||
|
|
@ -674,14 +715,16 @@ describe("MoveActionExecutor", () => {
|
|||
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
// Verify source file was updated (task removed)
|
||||
expect(mockVault.modify).toHaveBeenCalledWith(
|
||||
// Verify source file was updated (task removed) - first call
|
||||
expect(mockVault.modify).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
{ path: "source.md" },
|
||||
expectedSourceContent
|
||||
);
|
||||
|
||||
// Verify target file was updated (task added without onCompletion)
|
||||
expect(mockVault.modify).toHaveBeenCalledWith(
|
||||
// Verify target file was updated (task added without onCompletion) - second call
|
||||
expect(mockVault.modify).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
{ path: "archive.md" },
|
||||
expectedTargetContent
|
||||
);
|
||||
|
|
@ -734,7 +777,17 @@ describe("MoveActionExecutor", () => {
|
|||
const result = await executor.execute(context, config);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(mockVault.modify).toHaveBeenCalledWith(
|
||||
|
||||
// Verify source file was updated (task removed) - first call
|
||||
expect(mockVault.modify).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
{ path: "source.md" },
|
||||
expectedSourceContent
|
||||
);
|
||||
|
||||
// Verify target file was updated (task added without onCompletion) - second call
|
||||
expect(mockVault.modify).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
{ path: "archive.md" },
|
||||
expectedTargetContent
|
||||
);
|
||||
|
|
@ -789,14 +842,16 @@ describe("MoveActionExecutor", () => {
|
|||
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
// Verify source file was updated (task removed)
|
||||
expect(mockVault.modify).toHaveBeenCalledWith(
|
||||
// Verify source file was updated (task removed) - first call
|
||||
expect(mockVault.modify).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
{ path: "source.md" },
|
||||
expectedSourceContent
|
||||
);
|
||||
|
||||
// Verify target file was updated (task added without onCompletion)
|
||||
expect(mockVault.modify).toHaveBeenCalledWith(
|
||||
// Verify target file was updated (task added without onCompletion) - second call
|
||||
expect(mockVault.modify).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
{ path: "archive.md" },
|
||||
expectedTargetContent
|
||||
);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class MockTFolder {
|
|||
|
||||
class MockVault {
|
||||
private files = new Map<string, MockTFile>();
|
||||
private folders = new Map<string, MockTFolder>();
|
||||
private fileContents = new Map<string, string>();
|
||||
|
||||
addFile(path: string, content: string): MockTFile {
|
||||
|
|
@ -51,13 +52,19 @@ class MockVault {
|
|||
|
||||
addFolder(path: string): MockTFolder {
|
||||
const folderName = path.split("/").pop() || "";
|
||||
return new MockTFolder(path, folderName);
|
||||
const folder = new MockTFolder(path, folderName);
|
||||
this.folders.set(path, folder);
|
||||
return folder;
|
||||
}
|
||||
|
||||
getAbstractFileByPath(path: string): MockTFile | null {
|
||||
return this.files.get(path) || null;
|
||||
}
|
||||
|
||||
getFileByPath(path: string): MockTFile | null {
|
||||
return this.files.get(path) || null;
|
||||
}
|
||||
|
||||
async read(file: MockTFile): Promise<string> {
|
||||
return this.fileContents.get(file.path) || "";
|
||||
}
|
||||
|
|
@ -152,7 +159,6 @@ describe("TaskParsingService Integration", () => {
|
|||
stripExtension: true,
|
||||
enabled: false,
|
||||
},
|
||||
enhancedProjectEnabled: true,
|
||||
metadataConfigEnabled: true,
|
||||
configFileEnabled: true,
|
||||
},
|
||||
|
|
@ -227,7 +233,20 @@ describe("TaskParsingService Integration", () => {
|
|||
|
||||
it("should parse tasks with metadata-based projects", async () => {
|
||||
const parserConfig = createParserConfig();
|
||||
const serviceOptions = createServiceOptions(parserConfig);
|
||||
const serviceOptions = createServiceOptions(parserConfig, {
|
||||
configFileName: "project.md",
|
||||
searchRecursively: true,
|
||||
metadataKey: "project",
|
||||
pathMappings: [],
|
||||
metadataMappings: [],
|
||||
defaultProjectNaming: {
|
||||
strategy: "filename",
|
||||
stripExtension: true,
|
||||
enabled: false,
|
||||
},
|
||||
metadataConfigEnabled: true,
|
||||
configFileEnabled: true,
|
||||
});
|
||||
|
||||
parsingService = new TaskParsingService(serviceOptions);
|
||||
|
||||
|
|
@ -261,7 +280,20 @@ describe("TaskParsingService Integration", () => {
|
|||
|
||||
it("should parse tasks with config file-based projects", async () => {
|
||||
const parserConfig = createParserConfig();
|
||||
const serviceOptions = createServiceOptions(parserConfig);
|
||||
const serviceOptions = createServiceOptions(parserConfig, {
|
||||
configFileName: "project.md",
|
||||
searchRecursively: true,
|
||||
metadataKey: "project",
|
||||
pathMappings: [],
|
||||
metadataMappings: [],
|
||||
defaultProjectNaming: {
|
||||
strategy: "filename",
|
||||
stripExtension: true,
|
||||
enabled: false,
|
||||
},
|
||||
metadataConfigEnabled: true,
|
||||
configFileEnabled: true,
|
||||
});
|
||||
|
||||
parsingService = new TaskParsingService(serviceOptions);
|
||||
|
||||
|
|
@ -575,6 +607,8 @@ describe("TaskParsingService Integration", () => {
|
|||
stripExtension: true,
|
||||
enabled: false,
|
||||
},
|
||||
metadataConfigEnabled: true,
|
||||
configFileEnabled: true,
|
||||
});
|
||||
|
||||
parsingService = new TaskParsingService(serviceOptions);
|
||||
|
|
@ -622,6 +656,8 @@ describe("TaskParsingService Integration", () => {
|
|||
stripExtension: true,
|
||||
enabled: false,
|
||||
},
|
||||
metadataConfigEnabled: true,
|
||||
configFileEnabled: true,
|
||||
});
|
||||
|
||||
parsingService = new TaskParsingService(serviceOptions);
|
||||
|
|
@ -671,7 +707,6 @@ describe("TaskParsingService Integration", () => {
|
|||
stripExtension: true,
|
||||
enabled: false,
|
||||
},
|
||||
enhancedProjectEnabled: true,
|
||||
metadataConfigEnabled: true,
|
||||
configFileEnabled: true,
|
||||
});
|
||||
|
|
@ -754,7 +789,6 @@ describe("TaskParsingService Integration", () => {
|
|||
stripExtension: true,
|
||||
enabled: false,
|
||||
},
|
||||
enhancedProjectEnabled: true,
|
||||
metadataConfigEnabled: true,
|
||||
configFileEnabled: true,
|
||||
});
|
||||
|
|
@ -840,6 +874,8 @@ describe("TaskParsingService Integration", () => {
|
|||
stripExtension: true,
|
||||
enabled: false,
|
||||
},
|
||||
metadataConfigEnabled: true,
|
||||
configFileEnabled: true,
|
||||
});
|
||||
|
||||
parsingService = new TaskParsingService(serviceOptions);
|
||||
|
|
@ -901,6 +937,8 @@ describe("TaskParsingService Integration", () => {
|
|||
stripExtension: true,
|
||||
enabled: false,
|
||||
},
|
||||
metadataConfigEnabled: true,
|
||||
configFileEnabled: true,
|
||||
});
|
||||
|
||||
parsingService = new TaskParsingService(serviceOptions);
|
||||
|
|
@ -952,6 +990,8 @@ describe("TaskParsingService Integration", () => {
|
|||
stripExtension: true,
|
||||
enabled: true,
|
||||
},
|
||||
metadataConfigEnabled: true,
|
||||
configFileEnabled: true,
|
||||
});
|
||||
|
||||
parsingService = new TaskParsingService(serviceOptions);
|
||||
|
|
@ -1013,6 +1053,8 @@ describe("TaskParsingService Integration", () => {
|
|||
stripExtension: true,
|
||||
enabled: false,
|
||||
},
|
||||
metadataConfigEnabled: true,
|
||||
configFileEnabled: true,
|
||||
});
|
||||
|
||||
parsingService = new TaskParsingService(serviceOptions);
|
||||
|
|
@ -1064,7 +1106,6 @@ describe("TaskParsingService Integration", () => {
|
|||
stripExtension: true,
|
||||
enabled: true,
|
||||
},
|
||||
enhancedProjectEnabled: true,
|
||||
metadataConfigEnabled: true,
|
||||
configFileEnabled: true,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
} from "../editor-ext/autoCompleteParent"; // Adjust the import path as necessary
|
||||
import { TaskProgressBarSettings } from "../common/setting-definition";
|
||||
import { EditorView } from "@codemirror/view";
|
||||
import { Task } from "../types/task";
|
||||
|
||||
const mockAnnotationType = {
|
||||
of: jest.fn().mockImplementation((value: string) => ({
|
||||
|
|
@ -658,6 +659,27 @@ export function createMockCanvasTaskUpdater() {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a mock Task object with all required fields
|
||||
*/
|
||||
export function createMockTask(overrides: Partial<Task> = {}): Task {
|
||||
return {
|
||||
id: "test-task-id",
|
||||
content: "Test task content",
|
||||
completed: false,
|
||||
status: " ",
|
||||
metadata: {
|
||||
tags: [],
|
||||
children: [],
|
||||
...overrides.metadata,
|
||||
},
|
||||
filePath: "test.md",
|
||||
line: 1,
|
||||
originalMarkdown: "- [ ] Test task content",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
// createMockText is already exported inline
|
||||
createMockChangeSet, // Export the consolidated function
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ export abstract class BaseTaskBasesView extends Component implements BasesView {
|
|||
protected plugin: TaskProgressBarPlugin;
|
||||
protected tasks: Task[] = [];
|
||||
protected viewMode: ViewMode;
|
||||
protected currentTask: Task | null = null;
|
||||
|
||||
// Details panel properties
|
||||
protected detailsComponent: TaskDetailsComponent;
|
||||
|
|
|
|||
192
src/setting.ts
192
src/setting.ts
|
|
@ -508,7 +508,199 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
|
|||
}
|
||||
|
||||
private displayTimeParsingSettings(containerEl: HTMLElement): void {
|
||||
<<<<<<< HEAD
|
||||
renderTimeParsingSettingsTab(this, containerEl);
|
||||
=======
|
||||
containerEl.createEl("h2", { text: t("Time Parsing Settings") });
|
||||
|
||||
// Enable Time Parsing
|
||||
new Setting(containerEl)
|
||||
.setName(t("Enable Time Parsing"))
|
||||
.setDesc(
|
||||
t(
|
||||
"Automatically parse natural language time expressions in Quick Capture"
|
||||
)
|
||||
)
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.timeParsing?.enabled ?? true)
|
||||
.onChange(async (value) => {
|
||||
if (!this.plugin.settings.timeParsing) {
|
||||
this.plugin.settings.timeParsing = {
|
||||
enabled: value,
|
||||
supportedLanguages: ["en", "zh"],
|
||||
dateKeywords: {
|
||||
start: [
|
||||
"start",
|
||||
"begin",
|
||||
"from",
|
||||
"开始",
|
||||
"从",
|
||||
],
|
||||
due: [
|
||||
"due",
|
||||
"deadline",
|
||||
"by",
|
||||
"until",
|
||||
"截止",
|
||||
"到期",
|
||||
"之前",
|
||||
],
|
||||
scheduled: [
|
||||
"scheduled",
|
||||
"on",
|
||||
"at",
|
||||
"安排",
|
||||
"计划",
|
||||
"在",
|
||||
],
|
||||
},
|
||||
removeOriginalText: true,
|
||||
perLineProcessing: false,
|
||||
realTimeReplacement: false,
|
||||
};
|
||||
} else {
|
||||
this.plugin.settings.timeParsing.enabled = value;
|
||||
}
|
||||
this.applySettingsUpdate();
|
||||
})
|
||||
);
|
||||
|
||||
// Remove Original Text
|
||||
new Setting(containerEl)
|
||||
.setName(t("Remove Original Time Expressions"))
|
||||
.setDesc(t("Remove parsed time expressions from the task text"))
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(
|
||||
this.plugin.settings.timeParsing?.removeOriginalText ??
|
||||
true
|
||||
)
|
||||
.onChange(async (value) => {
|
||||
if (!this.plugin.settings.timeParsing) return;
|
||||
this.plugin.settings.timeParsing.removeOriginalText =
|
||||
value;
|
||||
this.applySettingsUpdate();
|
||||
})
|
||||
);
|
||||
|
||||
// Supported Languages
|
||||
containerEl.createEl("h3", { text: t("Supported Languages") });
|
||||
containerEl.createEl("p", {
|
||||
text: t(
|
||||
"Currently supports English and Chinese time expressions. More languages may be added in future updates."
|
||||
),
|
||||
cls: "setting-item-description",
|
||||
});
|
||||
|
||||
// Date Keywords Configuration
|
||||
containerEl.createEl("h3", { text: t("Date Keywords Configuration") });
|
||||
|
||||
// Start Date Keywords
|
||||
new Setting(containerEl)
|
||||
.setName(t("Start Date Keywords"))
|
||||
.setDesc(t("Keywords that indicate start dates (comma-separated)"))
|
||||
.addTextArea((text) => {
|
||||
const keywords =
|
||||
this.plugin.settings.timeParsing?.dateKeywords?.start || [];
|
||||
text.setValue(keywords.join(", "))
|
||||
.setPlaceholder("start, begin, from, 开始, 从")
|
||||
.onChange(async (value) => {
|
||||
if (!this.plugin.settings.timeParsing) return;
|
||||
this.plugin.settings.timeParsing.dateKeywords.start =
|
||||
value
|
||||
.split(",")
|
||||
.map((k) => k.trim())
|
||||
.filter((k) => k.length > 0);
|
||||
this.applySettingsUpdate();
|
||||
});
|
||||
text.inputEl.rows = 2;
|
||||
});
|
||||
|
||||
// Due Date Keywords
|
||||
new Setting(containerEl)
|
||||
.setName(t("Due Date Keywords"))
|
||||
.setDesc(t("Keywords that indicate due dates (comma-separated)"))
|
||||
.addTextArea((text) => {
|
||||
const keywords =
|
||||
this.plugin.settings.timeParsing?.dateKeywords?.due || [];
|
||||
text.setValue(keywords.join(", "))
|
||||
.setPlaceholder(
|
||||
"due, deadline, by, until, 截止, 到期, 之前"
|
||||
)
|
||||
.onChange(async (value) => {
|
||||
if (!this.plugin.settings.timeParsing) return;
|
||||
this.plugin.settings.timeParsing.dateKeywords.due =
|
||||
value
|
||||
.split(",")
|
||||
.map((k) => k.trim())
|
||||
.filter((k) => k.length > 0);
|
||||
this.applySettingsUpdate();
|
||||
});
|
||||
text.inputEl.rows = 2;
|
||||
});
|
||||
|
||||
// Scheduled Date Keywords
|
||||
new Setting(containerEl)
|
||||
.setName(t("Scheduled Date Keywords"))
|
||||
.setDesc(
|
||||
t("Keywords that indicate scheduled dates (comma-separated)")
|
||||
)
|
||||
.addTextArea((text) => {
|
||||
const keywords =
|
||||
this.plugin.settings.timeParsing?.dateKeywords?.scheduled ||
|
||||
[];
|
||||
text.setValue(keywords.join(", "))
|
||||
.setPlaceholder("scheduled, on, at, 安排, 计划, 在")
|
||||
.onChange(async (value) => {
|
||||
if (!this.plugin.settings.timeParsing) return;
|
||||
this.plugin.settings.timeParsing.dateKeywords.scheduled =
|
||||
value
|
||||
.split(",")
|
||||
.map((k) => k.trim())
|
||||
.filter((k) => k.length > 0);
|
||||
this.applySettingsUpdate();
|
||||
});
|
||||
text.inputEl.rows = 2;
|
||||
});
|
||||
|
||||
// Examples
|
||||
containerEl.createEl("h3", { text: t("Examples") });
|
||||
const examplesEl = containerEl.createEl("div", {
|
||||
cls: "time-parsing-examples",
|
||||
});
|
||||
|
||||
const examples = [
|
||||
{ input: "go to bed tomorrow", output: "go to bed 📅 2025-01-05" },
|
||||
{ input: "meeting next week", output: "meeting 📅 2025-01-11" },
|
||||
{ input: "project due by Friday", output: "project 📅 2025-01-04" },
|
||||
{ input: "明天开会", output: "开会 📅 2025-01-05" },
|
||||
{ input: "3天后完成", output: "完成 📅 2025-01-07" },
|
||||
];
|
||||
|
||||
examples.forEach((example) => {
|
||||
const exampleEl = examplesEl.createEl("div", {
|
||||
cls: "time-parsing-example",
|
||||
});
|
||||
exampleEl.createEl("span", {
|
||||
text: "Input: ",
|
||||
cls: "example-label",
|
||||
});
|
||||
exampleEl.createEl("code", {
|
||||
text: example.input,
|
||||
cls: "example-input",
|
||||
});
|
||||
exampleEl.createEl("br");
|
||||
exampleEl.createEl("span", {
|
||||
text: "Output: ",
|
||||
cls: "example-label",
|
||||
});
|
||||
exampleEl.createEl("code", {
|
||||
text: example.output,
|
||||
cls: "example-output",
|
||||
});
|
||||
});
|
||||
>>>>>>> ead40d2 (fix: tests issue)
|
||||
}
|
||||
|
||||
private displayTimelineSidebarSettings(containerEl: HTMLElement): void {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,15 @@
|
|||
|
||||
import './__mocks__/dom-helpers';
|
||||
|
||||
// Add Jest types
|
||||
declare global {
|
||||
namespace jest {
|
||||
interface Mock<T = any, Y extends any[] = any[]> {
|
||||
(...args: Y): T;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// jsdom is already set up by jest-environment-jsdom
|
||||
// Just need to ensure our DOM helpers are loaded
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,13 @@ export class CompleteActionExecutor extends BaseActionExecutor {
|
|||
return this.createErrorResult("Invalid complete configuration");
|
||||
}
|
||||
|
||||
return this.executeCommon(context, config);
|
||||
}
|
||||
|
||||
private async executeCommon(
|
||||
context: OnCompletionExecutionContext,
|
||||
config: OnCompletionConfig
|
||||
): Promise<OnCompletionExecutionResult> {
|
||||
const completeConfig = config as OnCompletionCompleteConfig;
|
||||
const { plugin } = context;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue