Skip to content

Commit bb3a9a5

Browse files
authored
Merge pull request #783 from callumalpass/issue-776
Fix screens failing to load with non-string tags
2 parents 9908573 + 3819921 commit bb3a9a5

File tree

3 files changed

+127
-1
lines changed

3 files changed

+127
-1
lines changed

docs/releases/unreleased.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,10 @@ Example:
2525
2626
-->
2727

28+
## Fixed
29+
30+
- (#776) Fixed screens failing to load with "toLowerCase is not a function" error
31+
- Issue occurred when frontmatter tags array contained non-string values (numbers, booleans, etc.)
32+
- Added type validation to filter out non-string values before processing tags
33+
- Valid string tags continue to work normally while invalid types are safely skipped
34+
- Thanks to @kmf and @Andre-Ioda for help debugging this issue

src/utils/MinimalNativeCache.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export class MinimalNativeCache extends Events {
123123
// Use exact matching (no substring fallback) for task identification
124124
if (!Array.isArray(frontmatter.tags)) return false;
125125
return frontmatter.tags.some((tag: string) =>
126-
FilterUtils.matchesHierarchicalTagExact(tag, this.taskTag)
126+
typeof tag === 'string' && FilterUtils.matchesHierarchicalTagExact(tag, this.taskTag)
127127
);
128128
}
129129
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { MinimalNativeCache } from '../../src/utils/MinimalNativeCache';
2+
import { TaskNotesSettings } from '../../src/types/settings';
3+
4+
// Mock FilterUtils
5+
jest.mock('../../src/utils/FilterUtils', () => ({
6+
FilterUtils: {
7+
matchesHierarchicalTagExact: jest.fn((tag: string, taskTag: string) => {
8+
return tag.toLowerCase() === taskTag.toLowerCase();
9+
}),
10+
},
11+
}));
12+
13+
describe('MinimalNativeCache - isTaskFile with non-string tags', () => {
14+
let cache: MinimalNativeCache;
15+
let mockApp: any;
16+
let settings: TaskNotesSettings;
17+
18+
beforeEach(() => {
19+
mockApp = {
20+
metadataCache: {
21+
on: jest.fn(),
22+
},
23+
vault: {
24+
on: jest.fn(),
25+
},
26+
};
27+
28+
settings = {
29+
taskTag: 'task',
30+
taskIdentificationMethod: 'tag',
31+
excludedFolders: '',
32+
disableNoteIndexing: false,
33+
storeTitleInFilename: false,
34+
} as TaskNotesSettings;
35+
36+
cache = new MinimalNativeCache(mockApp, settings);
37+
});
38+
39+
test('handles frontmatter with valid string tags', () => {
40+
const frontmatter = {
41+
tags: ['task', 'project', 'important'],
42+
};
43+
44+
const result = (cache as any).isTaskFile(frontmatter);
45+
expect(result).toBe(true);
46+
});
47+
48+
test('handles frontmatter with number in tags array', () => {
49+
const frontmatter = {
50+
tags: ['task', 123, 'project'],
51+
};
52+
53+
const result = (cache as any).isTaskFile(frontmatter);
54+
expect(result).toBe(true);
55+
});
56+
57+
test('handles frontmatter with boolean in tags array', () => {
58+
const frontmatter = {
59+
tags: [true, 'task', false],
60+
};
61+
62+
const result = (cache as any).isTaskFile(frontmatter);
63+
expect(result).toBe(true);
64+
});
65+
66+
test('handles frontmatter with mixed non-string types', () => {
67+
const frontmatter = {
68+
tags: [123, true, 'task', null, undefined, 'project'],
69+
};
70+
71+
const result = (cache as any).isTaskFile(frontmatter);
72+
expect(result).toBe(true);
73+
});
74+
75+
test('returns false when all tags are non-strings', () => {
76+
const frontmatter = {
77+
tags: [123, true, null, undefined, 456],
78+
};
79+
80+
const result = (cache as any).isTaskFile(frontmatter);
81+
expect(result).toBe(false);
82+
});
83+
84+
test('handles frontmatter with object in tags array', () => {
85+
const frontmatter = {
86+
tags: [{ nested: 'value' }, 'task'],
87+
};
88+
89+
const result = (cache as any).isTaskFile(frontmatter);
90+
expect(result).toBe(true);
91+
});
92+
93+
test('handles frontmatter with array in tags array', () => {
94+
const frontmatter = {
95+
tags: [['nested', 'array'], 'task'],
96+
};
97+
98+
const result = (cache as any).isTaskFile(frontmatter);
99+
expect(result).toBe(true);
100+
});
101+
102+
test('handles empty tags array', () => {
103+
const frontmatter = {
104+
tags: [],
105+
};
106+
107+
const result = (cache as any).isTaskFile(frontmatter);
108+
expect(result).toBe(false);
109+
});
110+
111+
test('handles nested tag hierarchy with non-strings', () => {
112+
const frontmatter = {
113+
tags: ['task', 123, 'other/tag'],
114+
};
115+
116+
const result = (cache as any).isTaskFile(frontmatter);
117+
expect(result).toBe(true);
118+
});
119+
});

0 commit comments

Comments
 (0)