mirror of
https://github.com/callumalpass/tasknotes.git
synced 2026-07-22 12:50:26 +00:00
Commit Description: • Update Typescript Interfaces: - In src/types.ts, extend TaskCreationData to include an optional "folder" property for specifying the folder path during task file creation. - In TimeEntry, add a "duration" property (number, in minutes) to support both calculated and manually entered time durations. • Improve Utility Mocks: - Modify the debounce mock in tests/__mocks__/utils.ts to use a more precise function signature (...args: any[]) => any, increasing type-safety. • Enhance Integration Helpers: - In tests/helpers/integration-helpers.ts, add a getMockApp() method for easier access to the mock app instance. - Update the filename generation logic to handle cases where the task title is undefined, defaulting to "untitled". - Refine method signatures in spies (e.g. generateTaskFilename and generateTaskBodyFromTemplate) with explicit any types for taskData and settings. - Change the getTaskByPath method to a private getTaskByPathFromFileSystem to clarify its purpose and usage. • Strengthen Mock Factories: - In tests/helpers/mock-factories.ts, add explicit type annotations to mapping functions (contexts, tags, due) and define type for due (string | null). - Specify the statusMap as a Record<string, string> for clarity. • Update Test Setup: - In tests/test-setup.ts, cast the global window and document objects using (global as any) to improve compatibility with mocked environments. - Enhance the createMockElement function by updating method signatures (createEl, createDiv, createSpan, setText, addClass, removeClass, toggleClass) with appropriate types. - Extend mocks for global objects such as IntersectionObserver and URL to include additional methods (takeRecords, createObjectURL, revokeObjectURL). • Revise Modal Unit Tests: - In BaseTaskModal and TaskCreationModal tests, introduce a helper (createMockApp) to safely cast the mock App to the expected Obsidan App type, and update initialization accordingly. - Address TypeScript warnings with proper @ts-ignore comments and ensure modal initialization and method calls (including initializeFormData) adhere to expected types. • Update Helpers Unit Tests: - In tests/unit/utils/helpers.test.ts, remove redundant mapping definitions from the mockFieldMapper and refine the mapToFrontmatter and toUserField function signatures, ensuring explicit type annotations. This refactor enhances type safety, improves test reliability, and ensures consistency in the way tasks and utility mocks are handled across the codebase.
220 lines
No EOL
5.9 KiB
TypeScript
220 lines
No EOL
5.9 KiB
TypeScript
/**
|
|
* Global test setup for TaskNotes plugin tests
|
|
* This file is run once before all tests
|
|
*/
|
|
|
|
// Mock global objects and APIs that would normally be provided by Obsidian
|
|
(global as any).window = global.window || {};
|
|
(global as any).document = global.document || {};
|
|
|
|
// Store the original createElement to avoid recursion
|
|
const originalCreateElement = document.createElement;
|
|
|
|
// Enhanced DOM mocking for Obsidian's HTMLElement extensions
|
|
function createMockElement(tagName: string = 'div'): any {
|
|
const element = originalCreateElement.call(document, tagName) as any;
|
|
|
|
// Mock Obsidian's createEl method
|
|
element.createEl = function(this: any, tagName: string, options: any = {}, callback?: (el: any) => void): any {
|
|
const child = createMockElement(tagName);
|
|
|
|
// Handle options object
|
|
if (options) {
|
|
// Handle class names
|
|
if (options.cls) {
|
|
if (typeof options.cls === 'string') {
|
|
child.className = options.cls;
|
|
} else if (Array.isArray(options.cls)) {
|
|
child.className = options.cls.join(' ');
|
|
}
|
|
}
|
|
|
|
// Handle text content
|
|
if (options.text !== undefined) {
|
|
child.textContent = options.text;
|
|
}
|
|
|
|
// Handle HTML content
|
|
if (options.html !== undefined) {
|
|
child.innerHTML = options.html;
|
|
}
|
|
|
|
// Handle attributes
|
|
if (options.attr) {
|
|
Object.entries(options.attr).forEach(([key, value]) => {
|
|
child.setAttribute(key, String(value));
|
|
});
|
|
}
|
|
|
|
// Handle other properties (like type for input elements)
|
|
Object.keys(options).forEach(key => {
|
|
if (!['cls', 'text', 'html', 'attr'].includes(key)) {
|
|
(child as any)[key] = options[key];
|
|
}
|
|
});
|
|
}
|
|
|
|
// Append to parent
|
|
this.appendChild(child);
|
|
|
|
// Call callback if provided
|
|
if (callback) {
|
|
callback(child);
|
|
}
|
|
|
|
return child;
|
|
};
|
|
|
|
// Mock createDiv as a convenience method
|
|
element.createDiv = function(this: any, options: any = {}, callback?: (el: any) => void): any {
|
|
return this.createEl('div', options, callback);
|
|
};
|
|
|
|
// Mock createSpan as a convenience method
|
|
element.createSpan = function(this: any, options: any = {}, callback?: (el: any) => void): any {
|
|
return this.createEl('span', options, callback);
|
|
};
|
|
|
|
// Mock setText method
|
|
element.setText = function(this: any, text: string): any {
|
|
this.textContent = text;
|
|
return this;
|
|
};
|
|
|
|
// Mock addClass method
|
|
element.addClass = function(this: any, className: string): any {
|
|
this.classList.add(className);
|
|
return this;
|
|
};
|
|
|
|
// Mock removeClass method
|
|
element.removeClass = function(this: any, className: string): any {
|
|
this.classList.remove(className);
|
|
return this;
|
|
};
|
|
|
|
// Mock toggleClass method
|
|
element.toggleClass = function(this: any, className: string, add?: boolean): any {
|
|
if (add !== undefined) {
|
|
this.classList.toggle(className, add);
|
|
} else {
|
|
this.classList.toggle(className);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
return element;
|
|
}
|
|
|
|
// Override document.createElement to return enhanced mock elements
|
|
document.createElement = function(tagName: string, options?: ElementCreationOptions): HTMLElement {
|
|
return createMockElement(tagName);
|
|
} as any;
|
|
|
|
// Mock requestAnimationFrame for animations and timers
|
|
if (!global.requestAnimationFrame) {
|
|
global.requestAnimationFrame = (callback: FrameRequestCallback) => {
|
|
return setTimeout(callback, 16);
|
|
};
|
|
}
|
|
|
|
if (!global.cancelAnimationFrame) {
|
|
global.cancelAnimationFrame = (handle: number) => {
|
|
clearTimeout(handle);
|
|
};
|
|
}
|
|
|
|
// Mock ResizeObserver for DOM resize detection
|
|
if (!global.ResizeObserver) {
|
|
global.ResizeObserver = class ResizeObserver {
|
|
observe() {}
|
|
unobserve() {}
|
|
disconnect() {}
|
|
};
|
|
}
|
|
|
|
// Mock IntersectionObserver for visibility detection
|
|
if (!global.IntersectionObserver) {
|
|
global.IntersectionObserver = class IntersectionObserver {
|
|
root = null;
|
|
rootMargin = '';
|
|
thresholds = [];
|
|
|
|
constructor() {}
|
|
observe() {}
|
|
unobserve() {}
|
|
disconnect() {}
|
|
takeRecords() { return []; }
|
|
} as any;
|
|
}
|
|
|
|
// Mock URL constructor for URL handling
|
|
if (!global.URL) {
|
|
global.URL = class URL {
|
|
constructor(public href: string, public base?: string) {}
|
|
toString() { return this.href; }
|
|
|
|
static createObjectURL() { return ''; }
|
|
static revokeObjectURL() {}
|
|
} as any;
|
|
}
|
|
|
|
// Mock fetch for HTTP requests
|
|
if (!global.fetch) {
|
|
global.fetch = jest.fn(() =>
|
|
Promise.resolve({
|
|
ok: true,
|
|
status: 200,
|
|
json: () => Promise.resolve({}),
|
|
text: () => Promise.resolve(''),
|
|
} as Response)
|
|
);
|
|
}
|
|
|
|
// Make Notice globally available for tests
|
|
(global as any).Notice = jest.fn().mockImplementation((message: string, timeout?: number) => {
|
|
return {};
|
|
});
|
|
|
|
// Set up date mocking utilities
|
|
jest.mock('date-fns', () => ({
|
|
...jest.requireActual('date-fns'),
|
|
// Override specific date functions for consistent testing
|
|
}));
|
|
|
|
// Configure Jest globals
|
|
jest.setTimeout(10000);
|
|
|
|
// Add custom matchers if needed in the future
|
|
// expect.extend({
|
|
// toBeValidTaskInfo(received) {
|
|
// // Custom matcher for TaskInfo validation
|
|
// }
|
|
// });
|
|
|
|
// Global test utilities that can be used across all tests
|
|
export const TestUtils = {
|
|
// Wait for next tick
|
|
nextTick: () => new Promise(resolve => setTimeout(resolve, 0)),
|
|
|
|
// Wait for specific duration
|
|
wait: (ms: number) => new Promise(resolve => setTimeout(resolve, ms)),
|
|
|
|
// Create a mock date that's consistent across tests
|
|
createMockDate: (dateString: string) => new Date(dateString),
|
|
|
|
// Mock current date for consistent testing
|
|
mockCurrentDate: (dateString: string) => {
|
|
const mockDate = new Date(dateString);
|
|
jest.spyOn(global.Date, 'now').mockReturnValue(mockDate.getTime());
|
|
return mockDate;
|
|
},
|
|
|
|
// Restore date mocking
|
|
restoreDate: () => {
|
|
jest.restoreAllMocks();
|
|
}
|
|
};
|
|
|
|
// Export for use in individual test files
|
|
export default TestUtils; |