Skip to content

Commit ba18f86

Browse files
committed
feat: inherit tags or metadata from frontmatter
1 parent b3f0640 commit ba18f86

24 files changed

Lines changed: 13812 additions & 15859 deletions

src/__tests__/FileMetadataInheritance.test.ts

Lines changed: 399 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Debug File Metadata Inheritance
3+
*/
4+
5+
import { MarkdownTaskParser } from "../utils/workers/ConfigurableTaskParser";
6+
import { getConfig } from "../common/task-parser-config";
7+
import { createMockPlugin } from "./mockUtils";
8+
import { DEFAULT_SETTINGS } from "../common/setting-definition";
9+
10+
describe("Debug File Metadata Inheritance", () => {
11+
test("should debug inheritance process", () => {
12+
const mockPlugin = createMockPlugin({
13+
...DEFAULT_SETTINGS,
14+
fileMetadataInheritance: {
15+
enabled: true,
16+
inheritFromFrontmatter: true,
17+
inheritFromFrontmatterForSubtasks: false,
18+
},
19+
});
20+
21+
const config = getConfig("tasks", mockPlugin);
22+
console.log("Config fileMetadataInheritance:", config.fileMetadataInheritance);
23+
24+
const parser = new MarkdownTaskParser(config);
25+
26+
const content = "- [ ] Test task";
27+
const fileMetadata = {
28+
priority: "high",
29+
testField: "testValue",
30+
};
31+
32+
const tasks = parser.parseLegacy(content, "test.md", fileMetadata);
33+
34+
// 检查 priority 字段在任务中是否正确继承
35+
const task = tasks[0];
36+
37+
// 使用 throw error 来调试更详细的信息
38+
throw new Error(`Debug detailed info:
39+
Config enabled: ${config.fileMetadataInheritance?.enabled}
40+
File metadata keys: ${Object.keys(fileMetadata).join(', ')}
41+
File metadata values: ${JSON.stringify(fileMetadata)}
42+
Task metadata keys: ${Object.keys(task?.metadata || {}).join(', ')}
43+
Task metadata: ${JSON.stringify(task?.metadata)}
44+
Task priority: ${task?.metadata?.priority} (type: ${typeof task?.metadata?.priority})
45+
Task testField: ${task?.metadata?.testField} (exists: ${'testField' in (task?.metadata || {})})
46+
Priority inherited: ${task?.metadata?.priority === 4}
47+
TestField inherited: ${task?.metadata?.testField === 'testValue'}`);
48+
49+
expect(tasks).toHaveLength(1);
50+
});
51+
});
Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
/**
2+
* File Metadata Inheritance Settings Tests
3+
*
4+
* Tests for settings integration and configuration migration
5+
*/
6+
7+
import { DEFAULT_SETTINGS, FileMetadataInheritanceConfig } from "../common/setting-definition";
8+
import TaskProgressBarPlugin from "../index";
9+
10+
// Mock Obsidian API
11+
const mockPlugin = {
12+
settings: { ...DEFAULT_SETTINGS },
13+
loadData: jest.fn(),
14+
saveData: jest.fn(),
15+
migrateInheritanceSettings: jest.fn(),
16+
} as any;
17+
18+
describe("File Metadata Inheritance Settings", () => {
19+
beforeEach(() => {
20+
jest.clearAllMocks();
21+
mockPlugin.settings = { ...DEFAULT_SETTINGS };
22+
});
23+
24+
describe("Default Configuration", () => {
25+
test("should have correct default values", () => {
26+
const defaultConfig = DEFAULT_SETTINGS.fileMetadataInheritance;
27+
28+
expect(defaultConfig).toBeDefined();
29+
expect(defaultConfig.enabled).toBe(true);
30+
expect(defaultConfig.inheritFromFrontmatter).toBe(true);
31+
expect(defaultConfig.inheritFromFrontmatterForSubtasks).toBe(false);
32+
});
33+
34+
test("should have FileMetadataInheritanceConfig interface", () => {
35+
const config: FileMetadataInheritanceConfig = {
36+
enabled: true,
37+
inheritFromFrontmatter: true,
38+
inheritFromFrontmatterForSubtasks: false,
39+
};
40+
41+
expect(config).toBeDefined();
42+
expect(typeof config.enabled).toBe("boolean");
43+
expect(typeof config.inheritFromFrontmatter).toBe("boolean");
44+
expect(typeof config.inheritFromFrontmatterForSubtasks).toBe("boolean");
45+
});
46+
});
47+
48+
describe("Configuration Migration", () => {
49+
test("should migrate old inheritance settings to new structure", () => {
50+
const savedData = {
51+
projectConfig: {
52+
metadataConfig: {
53+
metadataKey: "project",
54+
inheritFromFrontmatter: true,
55+
inheritFromFrontmatterForSubtasks: true,
56+
enabled: true,
57+
},
58+
},
59+
// 没有新的fileMetadataInheritance配置
60+
};
61+
62+
// 模拟迁移逻辑
63+
const migrateInheritanceSettings = (savedData: any) => {
64+
if (savedData?.projectConfig?.metadataConfig &&
65+
!savedData?.fileMetadataInheritance) {
66+
67+
const oldConfig = savedData.projectConfig.metadataConfig;
68+
69+
return {
70+
enabled: true,
71+
inheritFromFrontmatter: oldConfig.inheritFromFrontmatter ?? true,
72+
inheritFromFrontmatterForSubtasks: oldConfig.inheritFromFrontmatterForSubtasks ?? false
73+
};
74+
}
75+
return null;
76+
};
77+
78+
const migratedConfig = migrateInheritanceSettings(savedData);
79+
80+
expect(migratedConfig).not.toBeNull();
81+
expect(migratedConfig?.enabled).toBe(true);
82+
expect(migratedConfig?.inheritFromFrontmatter).toBe(true);
83+
expect(migratedConfig?.inheritFromFrontmatterForSubtasks).toBe(true);
84+
});
85+
86+
test("should not migrate when new configuration already exists", () => {
87+
const savedData = {
88+
projectConfig: {
89+
metadataConfig: {
90+
metadataKey: "project",
91+
inheritFromFrontmatter: true,
92+
inheritFromFrontmatterForSubtasks: true,
93+
enabled: true,
94+
},
95+
},
96+
fileMetadataInheritance: {
97+
enabled: false,
98+
inheritFromFrontmatter: false,
99+
inheritFromFrontmatterForSubtasks: false,
100+
},
101+
};
102+
103+
// 模拟迁移逻辑
104+
const migrateInheritanceSettings = (savedData: any) => {
105+
if (savedData?.projectConfig?.metadataConfig &&
106+
!savedData?.fileMetadataInheritance) {
107+
108+
const oldConfig = savedData.projectConfig.metadataConfig;
109+
110+
return {
111+
enabled: true,
112+
inheritFromFrontmatter: oldConfig.inheritFromFrontmatter ?? true,
113+
inheritFromFrontmatterForSubtasks: oldConfig.inheritFromFrontmatterForSubtasks ?? false
114+
};
115+
}
116+
return null;
117+
};
118+
119+
const migratedConfig = migrateInheritanceSettings(savedData);
120+
121+
// 应该返回null,表示不需要迁移
122+
expect(migratedConfig).toBeNull();
123+
});
124+
125+
test("should handle missing old configuration gracefully", () => {
126+
const savedData = {
127+
// 没有projectConfig
128+
};
129+
130+
// 模拟迁移逻辑
131+
const migrateInheritanceSettings = (savedData: any) => {
132+
if (savedData?.projectConfig?.metadataConfig &&
133+
!savedData?.fileMetadataInheritance) {
134+
135+
const oldConfig = savedData.projectConfig.metadataConfig;
136+
137+
return {
138+
enabled: true,
139+
inheritFromFrontmatter: oldConfig.inheritFromFrontmatter ?? true,
140+
inheritFromFrontmatterForSubtasks: oldConfig.inheritFromFrontmatterForSubtasks ?? false
141+
};
142+
}
143+
return null;
144+
};
145+
146+
const migratedConfig = migrateInheritanceSettings(savedData);
147+
148+
// 应该返回null,表示没有需要迁移的配置
149+
expect(migratedConfig).toBeNull();
150+
});
151+
});
152+
153+
describe("Settings Validation", () => {
154+
test("should maintain type safety for FileMetadataInheritanceConfig", () => {
155+
const validConfig: FileMetadataInheritanceConfig = {
156+
enabled: true,
157+
inheritFromFrontmatter: true,
158+
inheritFromFrontmatterForSubtasks: false,
159+
};
160+
161+
// TypeScript应该不会报错
162+
expect(validConfig.enabled).toBe(true);
163+
expect(validConfig.inheritFromFrontmatter).toBe(true);
164+
expect(validConfig.inheritFromFrontmatterForSubtasks).toBe(false);
165+
});
166+
167+
test("should handle all boolean combinations", () => {
168+
const combinations = [
169+
{ enabled: true, inheritFromFrontmatter: true, inheritFromFrontmatterForSubtasks: true },
170+
{ enabled: true, inheritFromFrontmatter: true, inheritFromFrontmatterForSubtasks: false },
171+
{ enabled: true, inheritFromFrontmatter: false, inheritFromFrontmatterForSubtasks: true },
172+
{ enabled: true, inheritFromFrontmatter: false, inheritFromFrontmatterForSubtasks: false },
173+
{ enabled: false, inheritFromFrontmatter: true, inheritFromFrontmatterForSubtasks: true },
174+
{ enabled: false, inheritFromFrontmatter: true, inheritFromFrontmatterForSubtasks: false },
175+
{ enabled: false, inheritFromFrontmatter: false, inheritFromFrontmatterForSubtasks: true },
176+
{ enabled: false, inheritFromFrontmatter: false, inheritFromFrontmatterForSubtasks: false },
177+
];
178+
179+
combinations.forEach(config => {
180+
expect(typeof config.enabled).toBe("boolean");
181+
expect(typeof config.inheritFromFrontmatter).toBe("boolean");
182+
expect(typeof config.inheritFromFrontmatterForSubtasks).toBe("boolean");
183+
});
184+
});
185+
});
186+
187+
describe("Integration with Main Settings", () => {
188+
test("should be properly integrated into TaskProgressBarSettings", () => {
189+
const settings = DEFAULT_SETTINGS;
190+
191+
expect(settings.fileMetadataInheritance).toBeDefined();
192+
expect(settings.fileMetadataInheritance.enabled).toBe(true);
193+
expect(settings.fileMetadataInheritance.inheritFromFrontmatter).toBe(true);
194+
expect(settings.fileMetadataInheritance.inheritFromFrontmatterForSubtasks).toBe(false);
195+
});
196+
197+
test("should work independently of project configuration", () => {
198+
const settingsWithoutProject = {
199+
...DEFAULT_SETTINGS,
200+
projectConfig: {
201+
...DEFAULT_SETTINGS.projectConfig,
202+
enableEnhancedProject: false,
203+
},
204+
};
205+
206+
expect(settingsWithoutProject.fileMetadataInheritance).toBeDefined();
207+
expect(settingsWithoutProject.fileMetadataInheritance.enabled).toBe(true);
208+
});
209+
210+
test("should work when project configuration is null", () => {
211+
const settingsWithNullProject = {
212+
...DEFAULT_SETTINGS,
213+
projectConfig: null as any,
214+
};
215+
216+
expect(settingsWithNullProject.fileMetadataInheritance).toBeDefined();
217+
expect(settingsWithNullProject.fileMetadataInheritance.enabled).toBe(true);
218+
});
219+
});
220+
221+
describe("Settings Persistence", () => {
222+
test("should preserve fileMetadataInheritance config during save/load", () => {
223+
const testSettings = {
224+
...DEFAULT_SETTINGS,
225+
fileMetadataInheritance: {
226+
enabled: false,
227+
inheritFromFrontmatter: false,
228+
inheritFromFrontmatterForSubtasks: true,
229+
},
230+
};
231+
232+
// 模拟保存和加载
233+
const savedData = JSON.stringify(testSettings);
234+
const loadedSettings = JSON.parse(savedData);
235+
236+
expect(loadedSettings.fileMetadataInheritance).toBeDefined();
237+
expect(loadedSettings.fileMetadataInheritance.enabled).toBe(false);
238+
expect(loadedSettings.fileMetadataInheritance.inheritFromFrontmatter).toBe(false);
239+
expect(loadedSettings.fileMetadataInheritance.inheritFromFrontmatterForSubtasks).toBe(true);
240+
});
241+
242+
test("should handle partial configuration updates", () => {
243+
const baseSettings = {
244+
...DEFAULT_SETTINGS,
245+
};
246+
247+
// 模拟部分更新
248+
const updatedSettings = {
249+
...baseSettings,
250+
fileMetadataInheritance: {
251+
...baseSettings.fileMetadataInheritance,
252+
enabled: false,
253+
},
254+
};
255+
256+
expect(updatedSettings.fileMetadataInheritance.enabled).toBe(false);
257+
expect(updatedSettings.fileMetadataInheritance.inheritFromFrontmatter).toBe(true);
258+
expect(updatedSettings.fileMetadataInheritance.inheritFromFrontmatterForSubtasks).toBe(false);
259+
});
260+
});
261+
262+
describe("Backward Compatibility", () => {
263+
test("should handle settings without fileMetadataInheritance gracefully", () => {
264+
const oldSettings = {
265+
...DEFAULT_SETTINGS,
266+
};
267+
268+
// 删除新字段模拟旧版本设置
269+
delete (oldSettings as any).fileMetadataInheritance;
270+
271+
// 合并默认设置应该恢复缺失的字段
272+
const mergedSettings = Object.assign({}, DEFAULT_SETTINGS, oldSettings);
273+
274+
expect(mergedSettings.fileMetadataInheritance).toBeDefined();
275+
expect(mergedSettings.fileMetadataInheritance.enabled).toBe(true);
276+
});
277+
278+
test("should maintain project config structure after migration", () => {
279+
const oldProjectConfig = {
280+
enableEnhancedProject: true,
281+
pathMappings: [],
282+
metadataConfig: {
283+
metadataKey: "project",
284+
inheritFromFrontmatter: true,
285+
inheritFromFrontmatterForSubtasks: true,
286+
enabled: true,
287+
},
288+
configFile: {
289+
fileName: "project.md",
290+
searchRecursively: true,
291+
enabled: true,
292+
},
293+
metadataMappings: [],
294+
defaultProjectNaming: {
295+
strategy: "filename" as const,
296+
stripExtension: true,
297+
enabled: true,
298+
},
299+
};
300+
301+
// 迁移后项目配置应该移除继承相关字段
302+
const migratedProjectConfig = {
303+
...oldProjectConfig,
304+
metadataConfig: {
305+
metadataKey: oldProjectConfig.metadataConfig.metadataKey,
306+
enabled: oldProjectConfig.metadataConfig.enabled,
307+
},
308+
};
309+
310+
expect(migratedProjectConfig.metadataConfig.metadataKey).toBe("project");
311+
expect(migratedProjectConfig.metadataConfig.enabled).toBe(true);
312+
expect((migratedProjectConfig.metadataConfig as any).inheritFromFrontmatter).toBeUndefined();
313+
expect((migratedProjectConfig.metadataConfig as any).inheritFromFrontmatterForSubtasks).toBeUndefined();
314+
});
315+
});
316+
});

src/__tests__/ProjectSourceDisplay.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ describe("Project Source Display", () => {
9090
metadataConfig: {
9191
enabled: true,
9292
metadataKey: "projectName",
93-
inheritFromFrontmatter: true,
94-
inheritFromFrontmatterForSubtasks: false,
93+
94+
9595
},
9696
configFile: {
9797
enabled: true,

src/__tests__/ProjectSourceWorkerFix.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,8 @@ describe("Project Source Worker Fix", () => {
265265
metadataConfig: {
266266
enabled: true,
267267
metadataKey: "projectName",
268-
inheritFromFrontmatter: true,
269-
inheritFromFrontmatterForSubtasks: false,
268+
269+
270270
},
271271
configFile: {
272272
enabled: true,

0 commit comments

Comments
 (0)