obsidian#QuickSwitcherPluginInstance TypeScript Examples

The following examples show how to use obsidian#QuickSwitcherPluginInstance. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts    From obsidian-switcher-plus with GNU General Public License v3.0 6 votes vote down vote up
export function getSystemSwitcherInstance(app: App): QuickSwitcherPluginInstance {
  const plugin = getInternalPluginById(app, 'switcher');
  return plugin?.instance as QuickSwitcherPluginInstance;
}
Example #2
Source File: switcherPlusSettings.test.ts    From obsidian-switcher-plus with GNU General Public License v3.0 4 votes vote down vote up
describe('SwitcherPlusSettings', () => {
  let mockApp: MockProxy<App>;
  let mockPlugin: MockProxy<SwitcherPlusPlugin>;
  let sut: SwitcherPlusSettings;

  beforeAll(() => {
    mockApp = mock<App>({ internalPlugins: mock<InternalPlugins>() });
    mockPlugin = mock<SwitcherPlusPlugin>({ app: mockApp });
  });

  beforeEach(() => {
    sut = new SwitcherPlusSettings(mockPlugin);
  });

  it('should return default settings', () => {
    // extract enabledSymbolTypes to handle separately, because it's not exposed
    // on SwitcherPlusSettings directly
    const { enabledSymbolTypes, ...defaults } = transientSettingsData(true);

    expect(sut).toEqual(expect.objectContaining(defaults));
    expect(sut.editorListPlaceholderText).toBe(defaults.editorListCommand);
    expect(sut.symbolListPlaceholderText).toBe(defaults.symbolListCommand);
    expect(sut.workspaceListPlaceholderText).toBe(defaults.workspaceListCommand);
    expect(sut.headingsListPlaceholderText).toBe(defaults.headingsListCommand);
    expect(sut.starredListPlaceholderText).toBe(defaults.starredListCommand);
    expect(sut.commandListPlaceholderText).toBe(defaults.commandListCommand);
    expect(sut.relatedItemsListPlaceholderText).toBe(defaults.relatedItemsListCommand);
    expect(sut.includeSidePanelViewTypesPlaceholder).toBe(
      defaults.includeSidePanelViewTypes.join('\n'),
    );

    expect(sut.isSymbolTypeEnabled(SymbolType.Embed)).toBe(
      enabledSymbolTypes[SymbolType.Embed],
    );
    expect(sut.isSymbolTypeEnabled(SymbolType.Heading)).toBe(
      enabledSymbolTypes[SymbolType.Heading],
    );
    expect(sut.isSymbolTypeEnabled(SymbolType.Link)).toBe(
      enabledSymbolTypes[SymbolType.Link],
    );
    expect(sut.isSymbolTypeEnabled(SymbolType.Tag)).toBe(
      enabledSymbolTypes[SymbolType.Tag],
    );
  });

  it('should save modified settings', async () => {
    const settings = transientSettingsData(false);

    sut.onOpenPreferNewPane = settings.onOpenPreferNewPane;
    sut.alwaysNewPaneForSymbols = settings.alwaysNewPaneForSymbols;
    sut.useActivePaneForSymbolsOnMobile = settings.useActivePaneForSymbolsOnMobile;
    sut.symbolsInLineOrder = settings.symbolsInLineOrder;
    sut.editorListCommand = settings.editorListCommand;
    sut.symbolListCommand = settings.symbolListCommand;
    sut.workspaceListCommand = settings.workspaceListCommand;
    sut.headingsListCommand = settings.headingsListCommand;
    sut.starredListCommand = settings.starredListCommand;
    sut.commandListCommand = settings.commandListCommand;
    sut.relatedItemsListCommand = settings.relatedItemsListCommand;
    sut.strictHeadingsOnly = settings.strictHeadingsOnly;
    sut.searchAllHeadings = settings.searchAllHeadings;
    sut.includeSidePanelViewTypes = settings.includeSidePanelViewTypes;
    sut.limit = settings.limit;
    sut.selectNearestHeading = settings.selectNearestHeading;
    sut.excludeFolders = settings.excludeFolders;
    sut.excludeLinkSubTypes = settings.excludeLinkSubTypes;
    sut.excludeRelatedFolders = settings.excludeRelatedFolders;
    sut.excludeOpenRelatedFiles = settings.excludeOpenRelatedFiles;

    sut.setSymbolTypeEnabled(
      SymbolType.Heading,
      settings.enabledSymbolTypes[SymbolType.Heading],
    );

    sut.setSymbolTypeEnabled(
      SymbolType.Link,
      settings.enabledSymbolTypes[SymbolType.Link],
    );

    sut.setSymbolTypeEnabled(SymbolType.Tag, settings.enabledSymbolTypes[SymbolType.Tag]);

    sut.setSymbolTypeEnabled(
      SymbolType.Embed,
      settings.enabledSymbolTypes[SymbolType.Embed],
    );

    let savedSettings: SettingsData;
    mockPlugin.saveData.mockImplementationOnce((data: SettingsData) => {
      savedSettings = data;
      return Promise.resolve();
    });

    await sut.saveSettings();

    expect(savedSettings).toEqual(expect.objectContaining(settings));
    expect(mockPlugin.saveData).toHaveBeenCalled();

    mockPlugin.saveData.mockReset();
  });

  it('should load saved settings', async () => {
    const settings = transientSettingsData(false);
    const { enabledSymbolTypes, ...prunedSettings } = settings;
    mockPlugin.loadData.mockResolvedValueOnce(settings);

    await sut.loadSettings();

    expect(sut).toEqual(expect.objectContaining(prunedSettings));
    expect(sut.isSymbolTypeEnabled(SymbolType.Heading)).toBe(
      enabledSymbolTypes[SymbolType.Heading],
    );
    expect(sut.isSymbolTypeEnabled(SymbolType.Link)).toBe(
      enabledSymbolTypes[SymbolType.Link],
    );
    expect(sut.isSymbolTypeEnabled(SymbolType.Tag)).toBe(
      enabledSymbolTypes[SymbolType.Tag],
    );
    expect(sut.isSymbolTypeEnabled(SymbolType.Embed)).toBe(
      enabledSymbolTypes[SymbolType.Embed],
    );

    expect(mockPlugin.loadData).toHaveBeenCalled();

    mockPlugin.loadData.mockReset();
  });

  it('should load saved settings, even with missing data keys', async () => {
    const defaults = transientSettingsData(true);
    const settings = transientSettingsData(false);

    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const { enabledSymbolTypes, ...prunedSettings } = settings;
    mockPlugin.loadData.mockResolvedValueOnce(prunedSettings);

    await sut.loadSettings();

    expect(sut).toEqual(expect.objectContaining(prunedSettings));
    expect(sut.isSymbolTypeEnabled(SymbolType.Heading)).toBe(
      defaults.enabledSymbolTypes[SymbolType.Heading],
    );
    expect(sut.isSymbolTypeEnabled(SymbolType.Link)).toBe(
      defaults.enabledSymbolTypes[SymbolType.Link],
    );
    expect(sut.isSymbolTypeEnabled(SymbolType.Tag)).toBe(
      defaults.enabledSymbolTypes[SymbolType.Tag],
    );
    expect(sut.isSymbolTypeEnabled(SymbolType.Embed)).toBe(
      defaults.enabledSymbolTypes[SymbolType.Embed],
    );

    expect(mockPlugin.loadData).toHaveBeenCalled();

    mockPlugin.loadData.mockReset();
  });

  it('should use default data if settings cannot be loaded', async () => {
    const { enabledSymbolTypes, ...defaults } = transientSettingsData(true);
    mockPlugin.loadData.mockResolvedValueOnce(null);

    await sut.loadSettings();

    expect(sut).toEqual(expect.objectContaining(defaults));
    expect(sut.editorListPlaceholderText).toBe(defaults.editorListCommand);
    expect(sut.symbolListPlaceholderText).toBe(defaults.symbolListCommand);
    expect(sut.workspaceListPlaceholderText).toBe(defaults.workspaceListCommand);
    expect(sut.headingsListPlaceholderText).toBe(defaults.headingsListCommand);
    expect(sut.includeSidePanelViewTypesPlaceholder).toBe(
      defaults.includeSidePanelViewTypes.join('\n'),
    );

    expect(sut.isSymbolTypeEnabled(SymbolType.Embed)).toBe(
      enabledSymbolTypes[SymbolType.Embed],
    );
    expect(sut.isSymbolTypeEnabled(SymbolType.Heading)).toBe(
      enabledSymbolTypes[SymbolType.Heading],
    );
    expect(sut.isSymbolTypeEnabled(SymbolType.Link)).toBe(
      enabledSymbolTypes[SymbolType.Link],
    );
    expect(sut.isSymbolTypeEnabled(SymbolType.Tag)).toBe(
      enabledSymbolTypes[SymbolType.Tag],
    );

    expect(mockPlugin.loadData).toHaveBeenCalled();
  });

  it('should load built-in system switcher settings', () => {
    const builtInOptions = mock<QuickSwitcherOptions>({
      showAllFileTypes: chance.bool(),
      showAttachments: chance.bool(),
      showExistingOnly: chance.bool(),
    });

    const pluginInstance = mock<QuickSwitcherPluginInstance>({
      id: 'switcher',
      options: builtInOptions,
      QuickSwitcherModal: null,
    });

    const builtInSwitcherPlugin = mock<InstalledPlugin>({
      enabled: true,
      instance: pluginInstance,
    });

    const mockInternalPlugins = mockApp.internalPlugins as MockProxy<InternalPlugins>;
    mockInternalPlugins.getPluginById.mockReturnValue(builtInSwitcherPlugin);

    expect(sut.builtInSystemOptions).toMatchObject(builtInOptions);
    expect(sut.showAllFileTypes).toBe(builtInOptions.showAllFileTypes);
    expect(sut.showAttachments).toBe(builtInOptions.showAttachments);
    expect(sut.showExistingOnly).toBe(builtInOptions.showExistingOnly);
    expect(mockInternalPlugins.getPluginById).toHaveBeenCalled();

    mockInternalPlugins.getPluginById.mockReset();
  });

  it('should log errors to console on fire and forget save operation', () => {
    // Promise used to trigger the error condition
    const saveDataPromise = Promise.resolve();

    mockPlugin.saveData.mockImplementationOnce((_data: SettingsData) => {
      // throw to simulate saveData() failing. This happens first
      return saveDataPromise.then(() => {
        throw new Error('saveData() unit test mock error');
      });
    });

    // Promise used to track the call to console.log
    let consoleLogPromiseResolveFn: (value: void | PromiseLike<void>) => void;
    const consoleLogPromise = new Promise<void>((resolve, _reject) => {
      consoleLogPromiseResolveFn = resolve;
    });

    const consoleLogSpy = jest
      .spyOn(console, 'log')
      .mockImplementation((message: string) => {
        if (message.startsWith('Switcher++: error saving changes to settings')) {
          // resolve the consoleLogPromise. This happens second and will allow
          // allPromises to resolve itself
          consoleLogPromiseResolveFn();
        }
      });

    // wait for the other promises to resolve before this promise can resolve
    const allPromises = Promise.all([saveDataPromise, consoleLogPromise]);

    sut.save();

    // when all the promises are resolved check expectations and clean up
    return allPromises.finally(() => {
      expect(mockPlugin.saveData).toHaveBeenCalled();
      expect(consoleLogSpy).toHaveBeenCalled();

      consoleLogSpy.mockRestore();
    });
  });
});
Example #3
Source File: switcherPlus.test.ts    From obsidian-switcher-plus with GNU General Public License v3.0 4 votes vote down vote up
describe('switcherPlus', () => {
  let mockApp: MockProxy<App>;
  let mockPlugin: MockProxy<SwitcherPlusPlugin>;

  // mock version of the built in system Switcher plugin instance
  // QuickSwitcherModal is the Class that SwitcherPlus inherits from, so set
  // that to the mock class from above
  const mockSystemSwitcherPluginInstance = mock<QuickSwitcherPluginInstance>({
    QuickSwitcherModal: MockSystemSwitcherModal,
  });

  // mock of utils function that retrieves the builtin switcher plugin instance
  // defaults to returning the mocked version of the plugin instance
  const mockGetSystemSwitcherInstance = jest
    .mocked(getSystemSwitcherInstance)
    .mockReturnValue(mockSystemSwitcherPluginInstance);

  beforeAll(() => {
    mockApp = mock<App>();
    mockPlugin = mock<SwitcherPlusPlugin>({ app: mockApp });
  });

  describe('createSwitcherPlus', () => {
    it('should log error to the console if the builtin QuickSwitcherModal is not accessible', () => {
      let wasLogged = false;
      const consoleLogSpy = jest
        .spyOn(console, 'log')
        .mockImplementation((message: string) => {
          if (message.startsWith('Switcher++: unable to extend system switcher.')) {
            wasLogged = true;
          }
        });

      mockGetSystemSwitcherInstance.mockReturnValueOnce(null);

      const result = createSwitcherPlus(mockApp, mockPlugin);

      expect(result).toBeNull();
      expect(mockGetSystemSwitcherInstance).toHaveBeenCalledWith(mockApp);
      expect(consoleLogSpy).toHaveBeenCalled();
      expect(wasLogged).toBe(true);

      consoleLogSpy.mockRestore();
    });

    it('should return an instance of a class that implements SwitcherPlus', () => {
      const result = createSwitcherPlus(mockApp, mockPlugin);

      // todo: more thorough checking needed here
      expect(result).not.toBeFalsy();
      expect(mockGetSystemSwitcherInstance).toHaveBeenLastCalledWith(mockApp);
    });
  });

  describe('SwitcherPlusModal', () => {
    let sut: SwitcherPlus;

    beforeAll(() => {
      sut = createSwitcherPlus(mockApp, mockPlugin);
    });

    test('openInMode() should forward to ModeHandler and  call super.Open()', () => {
      const setSessionOpenModeSpy = jest.spyOn(
        ModeHandler.prototype,
        'setSessionOpenMode',
      );

      const superOpenSpy = jest
        .spyOn(MockSystemSwitcherModal.prototype, 'open')
        .mockImplementation(() => {
          /* noop */
        });

      const mode = Mode.EditorList;
      sut.openInMode(mode);

      expect(setSessionOpenModeSpy).toHaveBeenCalledWith(mode, mockChooser);
      expect(superOpenSpy).toHaveBeenCalled();

      setSessionOpenModeSpy.mockReset();
      superOpenSpy.mockRestore();
    });

    test('onOpen() should forward to ModeHandler and call super.onOpen()', () => {
      const mhOnOpenSpy = jest.spyOn(ModeHandler.prototype, 'onOpen');

      const superOnOpenSpy = jest.spyOn(MockSystemSwitcherModal.prototype, 'onOpen');

      sut.onOpen();

      expect(mhOnOpenSpy).toHaveBeenCalled();
      expect(superOnOpenSpy).toHaveBeenCalled();

      mhOnOpenSpy.mockReset();
      superOnOpenSpy.mockRestore();
    });

    test('onClose() should forward to ModeHandler and call super.onClose()', () => {
      const mhOnCloseSpy = jest.spyOn(ModeHandler.prototype, 'onClose');

      const superOnCloseSpy = jest.spyOn(MockSystemSwitcherModal.prototype, 'onClose');

      sut.onClose();

      expect(mhOnCloseSpy).toHaveBeenCalled();
      expect(superOnCloseSpy).toHaveBeenCalled();

      mhOnCloseSpy.mockReset();
      superOnCloseSpy.mockRestore();
    });

    it('should should forward to ModeHandler to get suggestions', () => {
      const insertCmdStringSpy = jest.spyOn(
        ModeHandler.prototype,
        'insertSessionOpenModeCommandString',
      );

      const mhUpdateSuggestionsSpy = jest
        .spyOn(ModeHandler.prototype, 'updateSuggestions')
        .mockReturnValue(true); // true to signify that ModeHandler handled it

      const superUpdateSuggestionsSpy = jest.spyOn(
        MockSystemSwitcherModal.prototype,
        'updateSuggestions',
      );

      const inputText = 'foo';
      const mockInputEl = mock<HTMLInputElement>({ value: inputText });
      sut.inputEl = mockInputEl;

      // internally calls updateSuggestions()
      sut.open();

      expect(insertCmdStringSpy).toHaveBeenCalledWith(mockInputEl);
      expect(mhUpdateSuggestionsSpy).toHaveBeenCalledWith(inputText, mockChooser);

      // expect to not get called because ModeHandler should have handled it
      expect(superUpdateSuggestionsSpy).not.toHaveBeenCalled();

      insertCmdStringSpy.mockReset();
      mhUpdateSuggestionsSpy.mockReset();
      superUpdateSuggestionsSpy.mockRestore();
      mockClear(sut.inputEl);
    });

    it('should forward to builtin system switcher if not handled by Modehandler', () => {
      const insertCmdStringSpy = jest.spyOn(
        ModeHandler.prototype,
        'insertSessionOpenModeCommandString',
      );

      const mhUpdateSuggestionsSpy = jest
        .spyOn(ModeHandler.prototype, 'updateSuggestions')
        .mockReturnValue(false); // false to signify that ModeHandler did not handled it

      const superUpdateSuggestionsSpy = jest.spyOn(
        MockSystemSwitcherModal.prototype,
        'updateSuggestions',
      );

      const inputText = 'foo';
      const mockInputEl = mock<HTMLInputElement>({ value: inputText });
      sut.inputEl = mockInputEl;

      // internally calls updateSuggestions()
      sut.open();

      expect(insertCmdStringSpy).toHaveBeenCalledWith(mockInputEl);
      expect(mhUpdateSuggestionsSpy).toHaveBeenCalledWith(inputText, mockChooser);

      // expect to get called because ModeHandler did not handle it
      expect(superUpdateSuggestionsSpy).toHaveBeenCalled();

      insertCmdStringSpy.mockReset();
      mhUpdateSuggestionsSpy.mockReset();
      superUpdateSuggestionsSpy.mockRestore();
      mockClear(sut.inputEl);
    });

    test('onChooseSuggestion() should forward to ModeHandler', () => {
      const mhOnChooseSuggestionSpy = jest
        .spyOn(ModeHandler.prototype, 'onChooseSuggestion')
        .mockReturnValue(true); // true to signify that ModeHandler handled it

      const superOnChooseSuggestionSpy = jest.spyOn(
        MockSystemSwitcherModal.prototype,
        'onChooseSuggestion',
      );

      const mockSugg = mock<EditorSuggestion>();
      const mockEvt = mock<MouseEvent>();

      sut.onChooseSuggestion(mockSugg, mockEvt);

      expect(mhOnChooseSuggestionSpy).toHaveBeenCalledWith(mockSugg, mockEvt);

      // expect to not get called because ModeHandler should have handled it
      expect(superOnChooseSuggestionSpy).not.toHaveBeenCalled();

      mhOnChooseSuggestionSpy.mockReset();
      superOnChooseSuggestionSpy.mockRestore();
    });

    test('onChooseSuggestion() should forward to builtin system switcher if not handled by ModeHandler', () => {
      const mhOnChooseSuggestionSpy = jest
        .spyOn(ModeHandler.prototype, 'onChooseSuggestion')
        .mockReturnValue(false); // false to signify that ModeHandler did not handled it

      const superOnChooseSuggestionSpy = jest.spyOn(
        MockSystemSwitcherModal.prototype,
        'onChooseSuggestion',
      );

      const mockSugg = mock<EditorSuggestion>();
      const mockEvt = mock<MouseEvent>();

      sut.onChooseSuggestion(mockSugg, mockEvt);

      expect(mhOnChooseSuggestionSpy).toHaveBeenCalledWith(mockSugg, mockEvt);

      // expect to get called because ModeHandler did not handle it
      expect(superOnChooseSuggestionSpy).toHaveBeenCalledWith(mockSugg, mockEvt);

      mhOnChooseSuggestionSpy.mockReset();
      superOnChooseSuggestionSpy.mockRestore();
    });

    test('renderSuggestion() should forward to ModeHandler', () => {
      const mhRenderSuggestionSpy = jest
        .spyOn(ModeHandler.prototype, 'renderSuggestion')
        .mockReturnValue(true); // true to signify that ModeHandler handled it

      const superRenderSuggestionSpy = jest.spyOn(
        MockSystemSwitcherModal.prototype,
        'renderSuggestion',
      );

      const mockSugg = mock<EditorSuggestion>();
      const mockEl = mock<HTMLElement>();

      sut.renderSuggestion(mockSugg, mockEl);

      expect(mhRenderSuggestionSpy).toHaveBeenCalledWith(mockSugg, mockEl);

      // expect to not get called because ModeHandler should have handled it
      expect(superRenderSuggestionSpy).not.toHaveBeenCalled();

      mhRenderSuggestionSpy.mockReset();
      superRenderSuggestionSpy.mockRestore();
    });

    test('renderSuggestion() should forward to builtin system switcher if not handled by ModeHandler', () => {
      const mhRenderSuggestionSpy = jest
        .spyOn(ModeHandler.prototype, 'renderSuggestion')
        .mockReturnValue(false); // false to signify that ModeHandler did not handled it

      const superRenderSuggestionSpy = jest.spyOn(
        MockSystemSwitcherModal.prototype,
        'renderSuggestion',
      );

      const mockSugg = mock<EditorSuggestion>();
      const mockEl = mock<HTMLElement>();

      sut.renderSuggestion(mockSugg, mockEl);

      expect(mhRenderSuggestionSpy).toHaveBeenCalledWith(mockSugg, mockEl);

      // expect to get called because ModeHandler did not handle it
      expect(superRenderSuggestionSpy).toHaveBeenCalledWith(mockSugg, mockEl);

      mhRenderSuggestionSpy.mockReset();
      superRenderSuggestionSpy.mockRestore();
    });
  });
});