All checks were successful
CI / build (push) Successful in 16s
Introduces opt-in pending/total task count frontmatter fields and a "count root tasks only" mode. Root tasks are detected structurally by walking the list tree, so tasks nested under non-task bullets (e.g. "- **Action items**:") are correctly classified as root. Also updates the manifest author metadata.
150 lines
4.6 KiB
TypeScript
150 lines
4.6 KiB
TypeScript
import AutoStatusPlugin, { AutoStatusSettingTab } from '../src/main';
|
|
import { App } from 'obsidian';
|
|
|
|
// Mock the Setting class
|
|
jest.mock('obsidian', () => {
|
|
const originalModule = jest.requireActual('./mock_obsidian');
|
|
return {
|
|
...originalModule,
|
|
PluginSettingTab: jest.fn().mockImplementation((app, plugin) => ({
|
|
app,
|
|
plugin,
|
|
containerEl: {
|
|
empty: jest.fn(),
|
|
createEl: jest.fn().mockReturnValue({
|
|
createEl: jest.fn(),
|
|
}),
|
|
},
|
|
display: jest.fn(),
|
|
})),
|
|
Setting: jest.fn().mockImplementation(() => ({
|
|
setName: jest.fn().mockReturnThis(),
|
|
setDesc: jest.fn().mockReturnThis(),
|
|
addText: jest.fn().mockReturnThis(),
|
|
addToggle: jest.fn().mockReturnThis(),
|
|
addTextArea: jest.fn().mockReturnThis(),
|
|
addButton: jest.fn().mockReturnThis(),
|
|
})),
|
|
};
|
|
});
|
|
|
|
describe('Settings UI', () => {
|
|
let plugin: AutoStatusPlugin;
|
|
let mockApp: App;
|
|
|
|
beforeEach(() => {
|
|
mockApp = new App();
|
|
plugin = new AutoStatusPlugin(mockApp, {} as any);
|
|
plugin.settings = {
|
|
propertyName: 'Pending tasks',
|
|
enableAutoProcessing: true,
|
|
excludeFolders: [],
|
|
includeFolders: [],
|
|
debounceDelay: 500,
|
|
enableTaskCounts: false,
|
|
pendingTasksCountProperty: 'Pending tasks count',
|
|
totalTasksCountProperty: 'Total tasks count',
|
|
countRootTasksOnly: false,
|
|
};
|
|
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
test('should create settings tab correctly', () => {
|
|
if (AutoStatusSettingTab) {
|
|
const settingTab = new AutoStatusSettingTab(mockApp as any, plugin as any);
|
|
|
|
expect(settingTab.app).toBe(mockApp);
|
|
expect(settingTab.plugin).toBe(plugin);
|
|
}
|
|
});
|
|
|
|
test('should initialize settings UI elements', () => {
|
|
// Test that settings are properly structured for UI
|
|
expect(plugin.settings).toHaveProperty('propertyName');
|
|
expect(plugin.settings).toHaveProperty('enableAutoProcessing');
|
|
expect(plugin.settings).toHaveProperty('excludeFolders');
|
|
expect(plugin.settings).toHaveProperty('includeFolders');
|
|
expect(plugin.settings).toHaveProperty('debounceDelay');
|
|
|
|
expect(Array.isArray(plugin.settings.excludeFolders)).toBe(true);
|
|
expect(Array.isArray(plugin.settings.includeFolders)).toBe(true);
|
|
expect(typeof plugin.settings.propertyName).toBe('string');
|
|
expect(typeof plugin.settings.enableAutoProcessing).toBe('boolean');
|
|
expect(typeof plugin.settings.debounceDelay).toBe('number');
|
|
});
|
|
|
|
test('should handle folder array conversion correctly', () => {
|
|
// Test folder string to array conversion logic
|
|
const testFolderString = 'Daily Notes, Projects, Work';
|
|
const expectedArray = ['Daily Notes', 'Projects', 'Work'];
|
|
|
|
const result = testFolderString
|
|
.split(',')
|
|
.map(s => s.trim())
|
|
.filter(s => s.length > 0);
|
|
|
|
expect(result).toEqual(expectedArray);
|
|
});
|
|
|
|
test('should handle empty folder string correctly', () => {
|
|
const testFolderString = '';
|
|
const result = testFolderString
|
|
.split(',')
|
|
.map(s => s.trim())
|
|
.filter(s => s.length > 0);
|
|
|
|
expect(result).toEqual([]);
|
|
});
|
|
|
|
test('should handle folder string with extra whitespace', () => {
|
|
const testFolderString = ' Daily Notes , Projects , Work ';
|
|
const expectedArray = ['Daily Notes', 'Projects', 'Work'];
|
|
|
|
const result = testFolderString
|
|
.split(',')
|
|
.map(s => s.trim())
|
|
.filter(s => s.length > 0);
|
|
|
|
expect(result).toEqual(expectedArray);
|
|
});
|
|
|
|
test('should convert array to display string correctly', () => {
|
|
const testArray = ['Daily Notes', 'Projects', 'Work'];
|
|
const expectedString = 'Daily Notes, Projects, Work';
|
|
|
|
const result = testArray.join(', ');
|
|
|
|
expect(result).toBe(expectedString);
|
|
});
|
|
|
|
test('should handle empty array conversion', () => {
|
|
const testArray: string[] = [];
|
|
const expectedString = '';
|
|
|
|
const result = testArray.join(', ');
|
|
|
|
expect(result).toBe(expectedString);
|
|
});
|
|
|
|
test('should validate debounce delay input', () => {
|
|
// Test debounce delay validation logic
|
|
const validValues = ['500', '1000', '100', '0'];
|
|
const invalidValues = ['abc', '-100'];
|
|
|
|
validValues.forEach(value => {
|
|
const numValue = parseInt(value);
|
|
expect(!isNaN(numValue) && numValue >= 0).toBe(true);
|
|
});
|
|
|
|
invalidValues.forEach(value => {
|
|
const numValue = parseInt(value);
|
|
expect(!isNaN(numValue) && numValue >= 0).toBe(false);
|
|
});
|
|
|
|
// Special case: empty string returns NaN from parseInt
|
|
const emptyValue = '';
|
|
const emptyNumValue = parseInt(emptyValue);
|
|
expect(isNaN(emptyNumValue)).toBe(true);
|
|
});
|
|
});
|