insta-toc
Version:
Simultaneously generate, update, and maintain a table of contents for your notes in real time.
148 lines (125 loc) • 5.88 kB
text/typescript
import type { PluginManifest } from "obsidian";
import { describe, expect, test, vi } from "vitest";
vi.mock("obsidian-dev-utils/obsidian/plugin/plugin-settings-manager-base", () => {
return {
PluginSettingsManagerBase: class PluginSettingsManagerBase {
public app: unknown;
public defaultSettings: Record<string, unknown>;
public plugin: { app: unknown; loadData: () => Promise<Record<string, unknown> | null | undefined>; };
public settingsWrapper: { settings: Record<string, unknown>; };
public constructor(
plugin: { app: unknown; loadData: () => Promise<Record<string, unknown> | null | undefined>; }
) {
this.app = plugin.app;
this.defaultSettings = this.createDefaultSettings();
this.plugin = plugin;
this.settingsWrapper = { settings: { ...this.defaultSettings } };
this.registerValidators();
}
public async loadFromFile(_isInitialLoad: boolean): Promise<void> {
const data = await this.plugin.loadData();
const rawRecord = JSON.parse(JSON.stringify(data ?? {})) as Record<string, unknown>;
await this.onLoadRecord(rawRecord);
this.settingsWrapper = { settings: { ...this.defaultSettings, ...rawRecord } };
}
public async saveToFile(): Promise<void> {}
public registerValidators(): void {}
protected createDefaultSettings(): Record<string, unknown> {
return {};
}
protected async onLoadRecord(_rawRecord: Record<string, unknown>): Promise<void> {}
protected async onSavingRecord(_rawRecord: Record<string, unknown>): Promise<void> {}
protected registerValidator(_key: string, _validator: (value: any) => string | void): void {}
}
};
});
vi.mock("obsidian-dev-utils/obsidian/plugin/plugin-base", () => ({
PluginBase: class PluginBase {
public app: unknown;
public manifest: unknown;
public constructor(app?: unknown, manifest?: unknown) {
this.app = app;
this.manifest = manifest;
}
public registerEvent(): void {}
public registerMarkdownCodeBlockProcessor(): void {}
public addCommand(): void {}
public addRibbonIcon(): void {}
public async onloadImpl(): Promise<void> {}
public async onunloadImpl(): Promise<void> {}
}
}));
vi.mock("obsidian-dev-utils/obsidian/plugin/plugin-context", () => ({ initPluginContext: vi.fn() }));
vi.mock("../src/settings/SettingsTab", () => ({ SettingTab: class SettingTab {} }));
vi.mock(
"../src/svelte",
() => ({
CodeBlockComponent: class CodeBlockComponent {},
LocalSettingsModal: class LocalSettingsModal {
public async open(): Promise<void> {}
},
MarkdownComponentMounter: class MarkdownComponentMounter {}
})
);
import InstaTocPlugin from "../src/Plugin";
import { injectGlobals } from "../src/globals/globalFuncs";
import type { PluginSettingsManager } from "../src/settings/PluginSettingManager";
injectGlobals();
function createPluginWithPersistedData(
data: Record<string, unknown>
): InstaTocPlugin & { loadData: ReturnType<typeof vi.fn>; saveData: ReturnType<typeof vi.fn>; } {
return Object.assign(Object.create(InstaTocPlugin.prototype), {
app: {},
loadData: vi
.fn(async () => JSON.parse(JSON.stringify(data)) as Record<string, unknown>),
manifest: {
author: "Nick C.",
description: "Simultaneously generate, update, and maintain a table of contents for your notes.",
id: "insta-toc",
minAppVersion: "0.15.0",
name: "Insta TOC",
version: "7.0.0"
} satisfies PluginManifest,
saveData: vi.fn(async () => undefined)
});
}
function createSettingsManager(plugin: InstaTocPlugin): PluginSettingsManager {
const createSettingsManager = Reflect.get(InstaTocPlugin.prototype as object, "createSettingsManager") as (
this: InstaTocPlugin
) => PluginSettingsManager;
return createSettingsManager.call(plugin);
}
describe("InstaTocPlugin settings initialization", () => {
test("loads persisted ui state through the settings manager without requiring onloadImpl", async () => {
// Arrange
const persistedData = {
settings: {
excludedChars: [ "*", "_", "#" ],
excludedHeadingLevels: [ 2 ],
excludedHeadingText: [ "Draft" ],
tocTitle: "Contents",
tocTitleCentered: false,
tocTitleLevel: 2,
updateDelay: 1000
},
tocBlockCollapseState: { "folder/note.md": true },
tocFoldState: { "folder/note.md::1": true, "folder/note.md::2": false }
};
const plugin = createPluginWithPersistedData(persistedData);
const settingsManager = createSettingsManager(plugin);
// Act
await settingsManager.loadFromFile(true);
await settingsManager.savePersistedData();
// Assert
expect(plugin.uiStateManager.getPersistedUiState()).toEqual({
tocBlockCollapseState: new Map([ [ "folder/note.md", true ] ]),
tocFoldState: new Map([ [ "folder/note.md::1", true ], [ "folder/note.md::2", false ] ])
});
expect(settingsManager.settingsWrapper.settings.tocTitle).toBe("Contents");
expect(plugin.saveData).toHaveBeenCalledWith({
settings: persistedData.settings,
tocBlockCollapseState: { "folder/note.md": true },
tocFoldState: { "folder/note.md::1": true, "folder/note.md::2": false }
});
});
});