mocha#Test TypeScript Examples

The following examples show how to use mocha#Test. 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: ShowLegacyPreview.test.ts    From dendron with GNU Affero General Public License v3.0 6 votes vote down vote up
suite("ShowLegacyPreview", function () {
  let ctx: ExtensionContext;
  ctx = setupBeforeAfter(this);

  test("ok: show legacy preview when installed ", (done) => {
    runLegacyMultiWorkspaceTest({
      ctx,
      preSetupHook: ENGINE_HOOKS.setupBasic,
      onInit: async ({ engine }) => {
        const note = engine.notes["foo"];
        await WSUtils.openNote(note);
        sinon.stub(MarkdownUtils, "hasLegacyPreview").returns(true);
        const showLegacyPreview = sinon.stub(
          MarkdownUtils,
          "showLegacyPreview"
        );
        await new ShowLegacyPreviewCommand().execute();
        expect(showLegacyPreview.called).toBeTruthy();
        done();
      },
    });
  });
});
Example #2
Source File: migration.test.ts    From dendron with GNU Affero General Public License v3.0 6 votes vote down vote up
suite("MigrationUtils", () => {
  describe("deepCleanObjBy", () => {
    describe("GIVEN _.isNull as predicate", () => {
      describe("WHEN an object has kvp that has null value", () => {
        test("THEN all kvp that has null value are omitted from object", () => {
          const obj = { a: { b: null, c: "foo", d: null } };
          const expected = { a: { c: "foo" } };
          expect(MigrationUtils.deepCleanObjBy(obj, _.isNull)).toEqual(
            expected
          );
        });
      });

      describe("WHEN an object has no kvp that has null value", () => {
        test("THEN nothing is omitted", () => {
          const obj = { a: { b: "foo", c: "bar", d: "egg" } };
          expect(MigrationUtils.deepCleanObjBy(obj, _.isNull)).toEqual(obj);
        });
      });
    });
  });
});
Example #3
Source File: test.ts    From eosio-contract-api with GNU Affero General Public License v3.0 6 votes vote down vote up
export function createTxIt(client: TestClient): any {
    async function runTxTest(fn: () => Promise<void>, self: any): Promise<any> {
        await client.query('BEGIN');

        try {
            await client.init();

            return await fn.call(self, client);
        } finally {
            await client.query('ROLLBACK');
        }
    }

    const result = function txit(title: string, fn: () => Promise<void>): Test {
        return it(title, async function () {
            return await runTxTest(fn, this);
        });
    };

    result.skip = (title: string, func: () => Promise<void>): Test => it.skip(title, func as unknown as AsyncFunc);

    result.only = function (title: string, fn: () => Promise<void>): Test {

        return it.only(title, async () => {
            return await runTxTest(fn, this);
        });
    };

    return result;
}
Example #4
Source File: export-factory.test.ts    From vscode-code-review with MIT License 5 votes vote down vote up
suite('Export Factory', () => {
  suite('compareLocation', () => {
    const cases = [
      {
        lhs: undefined,
        rhs: undefined,
        expected: 0,
      },
      {
        lhs: { lineStart: 1, columnStart: 1, lineEnd: 1, columnEnd: 1 },
        rhs: undefined,
        expected: -1,
      },
      {
        lhs: undefined,
        rhs: { lineStart: 1, columnStart: 1, lineEnd: 1, columnEnd: 1 },
        expected: 1,
      },
      {
        lhs: { lineStart: 1, columnStart: 1, lineEnd: 1, columnEnd: 1 },
        rhs: { lineStart: 1, columnStart: 1, lineEnd: 1, columnEnd: 1 },
        expected: 0,
      },
      {
        lhs: { lineStart: 1, columnStart: 1, lineEnd: 1, columnEnd: 1 },
        rhs: { lineStart: 2, columnStart: 1, lineEnd: 1, columnEnd: 1 },
        expected: -1,
      },
      {
        lhs: { lineStart: 2, columnStart: 1, lineEnd: 1, columnEnd: 1 },
        rhs: { lineStart: 1, columnStart: 1, lineEnd: 1, columnEnd: 1 },
        expected: 1,
      },
      {
        lhs: { lineStart: 1, columnStart: 1, lineEnd: 1, columnEnd: 1 },
        rhs: { lineStart: 1, columnStart: 2, lineEnd: 1, columnEnd: 1 },
        expected: -1,
      },
      {
        lhs: { lineStart: 1, columnStart: 2, lineEnd: 1, columnEnd: 1 },
        rhs: { lineStart: 1, columnStart: 1, lineEnd: 1, columnEnd: 1 },
        expected: 1,
      },
    ];

    cases.forEach((value, idx) => {
      test(`should compare ${value.expected} (#${idx})`, () => {
        const result = compareLocation(value.lhs, value.rhs);
        assert.strictEqual(result, value.expected);
      });
    });
  });
});
Example #5
Source File: analytics.test.ts    From dendron with GNU Affero General Public License v3.0 5 votes vote down vote up
describe("GIVEN AnalyticsUtils", () => {
  describe("WHEN getSessionId called twice", () => {
    test("THEN get same value", () => {
      const val1 = AnalyticsUtils.getSessionId();
      const val2 = AnalyticsUtils.getSessionId();
      expect(val1).toNotEqual(-1);
      expect(val1).toEqual(val2);
    });
  });

  describe("WHEN trackForNextRun is used", () => {
    const event = "TestEventOccurred";
    const numbers = [1, 1, 2, 3, 5, 8];
    const namesYears = {
      "Jeffrey David Ullman": 2020,
      "Jack Dongarra": 2021,
    };

    let homedir: string;
    before(async () => {
      homedir = tmpDir().name;
      sinon.stub(os, "homedir").returns(homedir);

      await AnalyticsUtils.trackForNextRun(event, {
        numbers,
        namesYears,
      });
    });
    after(() => {
      sinon.restore();
    });

    test("THEN the properties are saved to disk", async () => {
      const telemetryDir = path.join(
        homedir,
        FOLDERS.DENDRON_SYSTEM_ROOT,
        FOLDERS.SAVED_TELEMETRY
      );
      const savedFiles = (await fs.readdir(telemetryDir)).filter(
        (filename) => path.extname(filename) === ".json"
      );
      expect(savedFiles.length).toEqual(1);
      const contents = await fs.readFile(
        path.join(telemetryDir, savedFiles[0]),
        { encoding: "utf-8" }
      );
      expect(contents.includes(event)).toBeTruthy();
      expect(contents.includes("5")).toBeTruthy();
      expect(contents.includes("8")).toBeTruthy();
      expect(contents.includes("Jeffrey David Ullman")).toBeTruthy();
      expect(contents.includes("Jack Dongarra")).toBeTruthy();
      expect(contents.includes("timestamp")).toBeTruthy();
    });

    describe("AND when sendSavedAnalytics is used", () => {
      let trackStub: sinon.SinonStub<
        Parameters<typeof SegmentUtils["trackSync"]>
      >;
      before(async () => {
        trackStub = sinon.stub(SegmentUtils, "trackSync");
        await AnalyticsUtils.sendSavedAnalytics();
      });
      after(() => {
        trackStub.restore();
      });

      test("THEN the saved event is sent", async () => {
        expect(trackStub.calledOnce).toBeTruthy();
        const args = trackStub.args[0][0];
        // Should be the right event
        expect(args.event).toEqual(event);
        // All the props should match
        expect(_.isEqual(args.properties?.numbers, numbers)).toBeTruthy();
        expect(_.isEqual(args.properties?.namesYears, namesYears)).toBeTruthy();
        // Timestamp should be serialzed and saved, then parsed on load
        expect(args.timestamp instanceof Date).toBeTruthy();
      });
    });
  });
});
Example #6
Source File: migration.test.ts    From dendron with GNU Affero General Public License v3.0 5 votes vote down vote up
suite("MigrationService", function () {
  const ctx = setupBeforeAfter(this);

  async function ranMigration(
    currentVersion: string,
    migrations: Migrations[]
  ) {
    const { wsRoot, config } = ExtensionProvider.getDWorkspace();
    const wsService = new WorkspaceService({ wsRoot });
    const out = await MigrationService.applyMigrationRules({
      currentVersion,
      previousVersion: "0.62.2",
      migrations,
      dendronConfig: config,
      wsService,
      wsConfig: await ExtensionProvider.getExtension().getWorkspaceSettings(),
      logger: Logger,
    });
    return out.length !== 0;
  }

  describeMultiWS(
    "GIVEN migration of semver 0.63.0",
    {
      ctx,
      preSetupHook: ENGINE_HOOKS.setupBasic,
    },
    () => {
      const dummyFunc: MigrateFunction = async ({
        dendronConfig,
        wsConfig,
      }) => {
        return { data: { dendronConfig, wsConfig } };
      };
      const migrations = [
        {
          version: "0.63.0",
          changes: [
            {
              name: "test",
              func: dummyFunc,
            },
          ],
        },
      ] as Migrations[];
      describe("WHEN current version is smaller than 0.63.0", () => {
        const currentVersion = "0.62.3";
        test("THEN migration should not run", async () => {
          const result = await ranMigration(currentVersion, migrations);
          expect(result).toBeFalsy();
        });
      });

      describe("WHEN current version is 0.63.0", () => {
        const currentVersion = "0.63.0";
        test("THEN migration should run", async () => {
          const result = await ranMigration(currentVersion, migrations);
          expect(result).toBeTruthy();
        });
      });

      describe("WHEN current version is larger than 0.63.0", () => {
        const currentVersion = "0.63.1";
        test("THEN migration should run", async () => {
          const result = await ranMigration(currentVersion, migrations);
          expect(result).toBeTruthy();
        });
      });
    }
  );
});
Example #7
Source File: BacklinksTreeDataProvider.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("BacklinksTreeDataProvider", function () {
  // Set test timeout to 3 seconds
  this.timeout(3000);

  describeSingleWS(
    "GIVEN a single vault workspace with two notes (target, link)",
    {
      postSetupHook: async ({ wsRoot, vaults }) => {
        await NOTE_PRESETS_V4.NOTE_WITH_TARGET.create({
          wsRoot,
          vault: vaults[0],
        });
        await NOTE_PRESETS_V4.NOTE_WITH_LINK.create({
          wsRoot,
          vault: vaults[0],
        });
      },
    },
    () => {
      test("THEN BacklinksTreeDataProvider calculates correct number of backlinks", async () => {
        const { engine, wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        await ExtensionProvider.getWSUtils().openNote(engine.notes["alpha"]);
        const { out } = await getRootChildrenBacklinksAsPlainObject();
        const expectedPath = vscode.Uri.file(
          path.join(wsRoot, vaults[0].fsPath, "beta.md")
        ).path;
        expect(
          out[0].command.arguments[0].path.toLowerCase() as string
        ).toEqual(expectedPath.toLowerCase());
        expect(out.length).toEqual(1);
      });

      test("THEN validate get parent works", async () => {
        const { out: backlinks, provider } = await getRootChildrenBacklinks();
        const parentBacklink = backlinks[0];

        // Our utility method will add the children into out backlink structure.
        // The provider will give just the backlink hence we will remove the
        // children from the structure that will be used to assert equality.
        const { children, ...parentBacklinkForAssert } = parentBacklink;

        // Validate children added by the test setup are able to getParent()
        expect(parentBacklink.children).toBeTruthy();
        parentBacklink.children?.forEach((child) => {
          const foundParent = provider.getParent(child);

          assertAreEqual(foundParent, parentBacklinkForAssert);
        });

        // Validate backlinks created out of refs can getParent()
        expect(parentBacklink.refs).toBeTruthy();
      });

      test("THEN calculating backlinks from cache returns same number of backlinks", async () => {
        // re-initialize engine from cache
        await new ReloadIndexCommand().run();
        const { engine, wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        await ExtensionProvider.getWSUtils().openNote(engine.notes["alpha"]);

        const { out } = await getRootChildrenBacklinksAsPlainObject();
        const expectedPath = vscode.Uri.file(
          path.join(wsRoot, vaults[0].fsPath, "beta.md")
        ).path;
        expect(
          out[0].command.arguments[0].path.toLowerCase() as string
        ).toEqual(expectedPath.toLowerCase());
        expect(out.length).toEqual(1);
      });
    }
  );

  describeMultiWS(
    "WHEN there is one note with the candidate word",
    {
      // NOTE: this test often times out
      timeout: 10e3,
      preSetupHook: async ({ wsRoot, vaults }) => {
        await NOTE_PRESETS_V4.NOTE_WITH_TARGET.create({
          wsRoot,
          vault: vaults[0],
        });
        await NOTE_PRESETS_V4.NOTE_WITH_LINK_CANDIDATE_TARGET.create({
          wsRoot,
          vault: vaults[0],
        });
      },
      modConfigCb: (config) => {
        config.dev = {
          enableLinkCandidates: true,
        };
        return config;
      },
    },
    () => {
      test("THEN finds the backlink candidate for that note", async () => {
        const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
        const isLinkCandidateEnabled = TestConfigUtils.getConfig({ wsRoot }).dev
          ?.enableLinkCandidates;
        expect(isLinkCandidateEnabled).toBeTruthy();

        const noteWithTarget = NoteUtils.getNoteByFnameFromEngine({
          fname: "alpha",
          engine,
          vault: vaults[0],
        });
        await checkNoteBacklinks({ wsRoot, vaults, noteWithTarget });
      });
    }
  );

  describeMultiWS(
    "WHEN there are multiple notes with the candidate word",
    {
      preSetupHook: async ({ wsRoot, vaults }) => {
        // Create 2 notes with the same name
        await NOTE_PRESETS_V4.NOTE_WITH_TARGET.create({
          wsRoot,
          vault: vaults[0],
          genRandomId: true,
        });
        await NOTE_PRESETS_V4.NOTE_WITH_TARGET.create({
          wsRoot,
          vault: vaults[1],
          genRandomId: true,
        });
        await NOTE_PRESETS_V4.NOTE_WITH_LINK_CANDIDATE_TARGET.create({
          wsRoot,
          vault: vaults[0],
        });
      },
      modConfigCb: (config) => {
        config.dev = {
          enableLinkCandidates: true,
        };
        return config;
      },
    },
    () => {
      test("THEN finds the backlink candidate for all notes", async () => {
        const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
        const isLinkCandidateEnabled = TestConfigUtils.getConfig({ wsRoot }).dev
          ?.enableLinkCandidates;
        expect(isLinkCandidateEnabled).toBeTruthy();

        // Check the backlinks for both notes
        await checkNoteBacklinks({
          wsRoot,
          vaults,
          noteWithTarget: NoteUtils.getNoteByFnameFromEngine({
            fname: "alpha",
            engine,
            vault: vaults[0],
          }),
        });
        await checkNoteBacklinks({
          wsRoot,
          vaults,
          noteWithTarget: NoteUtils.getNoteByFnameFromEngine({
            fname: "alpha",
            engine,
            vault: vaults[1],
          }),
        });
      });
    }
  );

  describeMultiWS(
    "GIVEN a multi vault workspace with notes referencing each other across vaults",
    {
      preSetupHook: async ({ wsRoot, vaults }) => {
        await NoteTestUtilsV4.createNote({
          fname: "alpha",
          body: `[[beta]]`,
          vault: vaults[0],
          wsRoot,
        });

        await NoteTestUtilsV4.createNote({
          fname: "beta",
          body: `[[alpha]]`,
          vault: vaults[1],
          wsRoot,
          props: {
            updated: 2,
          },
        });

        await NoteTestUtilsV4.createNote({
          fname: "omega",
          body: `[[alpha]]`,
          vault: vaults[1],
          wsRoot,
          props: {
            updated: 3,
          },
        });
      },
      modConfigCb: (config) => {
        config.dev = {
          enableLinkCandidates: true,
        };
        return config;
      },
    },
    () => {
      test("THEN backlink sort order is correct", async () => {
        const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        function buildVault1Path(fileName: string) {
          return vscode.Uri.file(
            path.join(wsRoot, vaults[1].fsPath, fileName)
          ).path.toLowerCase();
        }

        const notePath = path.join(wsRoot, vaults[0].fsPath, "alpha.md");
        await VSCodeUtils.openFileInEditor(Uri.file(notePath));

        // Test Last Updated sort order
        {
          const { out } = await getRootChildrenBacklinksAsPlainObject(
            BacklinkPanelSortOrder.LastUpdated
          );
          expect(
            out[0].command.arguments[0].path.toLowerCase() as string
          ).toEqual(buildVault1Path("omega.md"));
          expect(out.length).toEqual(2);
        }

        // Test PathNames sort order
        {
          const { out } = await getRootChildrenBacklinksAsPlainObject(
            BacklinkPanelSortOrder.PathNames
          );
          expect(
            out[0].command.arguments[0].path.toLowerCase() as string
          ).toEqual(buildVault1Path("beta.md"));
          expect(out.length).toEqual(2);
        }
      });

      test("THEN BacklinksTreeDataProvider calculates correct number of backlinks", async () => {
        const { engine, wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        await ExtensionProvider.getWSUtils().openNote(engine.notes["alpha"]);
        const { out } = await getRootChildrenBacklinksAsPlainObject();
        const expectedPath = vscode.Uri.file(
          path.join(wsRoot, vaults[1].fsPath, "beta.md")
        ).path;
        expect(
          out[0].command.arguments[0].path.toLowerCase() as string
        ).toEqual(expectedPath.toLowerCase());
        expect(out.length).toEqual(2);
      });
    }
  );

  describeMultiWS(
    "GIVEN a multi vault workspace with two notes in different vaults",
    {
      preSetupHook: async ({ wsRoot, vaults }) => {
        await NoteTestUtilsV4.createNote({
          fname: "alpha",
          body: `gamma`,
          vault: vaults[0],
          wsRoot,
        });
        await NOTE_PRESETS_V4.NOTE_WITH_LINK_CANDIDATE_TARGET.create({
          wsRoot,
          vault: vaults[1],
        });
      },
      modConfigCb: (config) => {
        config.dev = {
          enableLinkCandidates: true,
        };
        return config;
      },
    },
    () => {
      test("THEN link candidates should only work within a vault", async () => {
        const engine = ExtensionProvider.getEngine();

        const alpha = engine.notes["alpha"];
        await ExtensionProvider.getWSUtils().openNote(alpha);
        const alphaOut = (await getRootChildrenBacklinksAsPlainObject()).out;
        expect(alphaOut).toEqual([]);
        expect(alpha.links).toEqual([]);

        const gamma = engine.notes["gamma"];
        await ExtensionProvider.getWSUtils().openNote(gamma);
        const gammaOut = (await getRootChildrenBacklinksAsPlainObject()).out;
        expect(gammaOut).toEqual([]);
        expect(gamma.links).toEqual([]);
      });
    }
  );

  describeSingleWS(
    "GIVEN a single vault workspace with links and feature flag was not enabled",
    {
      postSetupHook: async ({ wsRoot, vaults }) => {
        await NoteTestUtilsV4.createNote({
          fname: "alpha",
          body: "[[beta]] beta",
          vault: vaults[0],
          wsRoot,
        });
        await NoteTestUtilsV4.createNote({
          fname: "beta",
          body: "alpha",
          vault: vaults[0],
          wsRoot,
        });
      },
    },
    () => {
      test("THEN candidate links don't show up", async () => {
        const engine = ExtensionProvider.getEngine();

        await new ReloadIndexCommand().execute();
        const alpha = engine.notes["alpha"];
        await ExtensionProvider.getWSUtils().openNote(alpha);

        const { out: alphaOut } = await getRootChildrenBacklinks();
        const alphaOutObj = backlinksToPlainObject(alphaOut) as any;
        expect(_.isEmpty(alphaOutObj)).toBeTruthy();

        const beta = engine.notes["beta"];
        await ExtensionProvider.getWSUtils().openNote(beta);
        const { out: betaOut } = await getRootChildrenBacklinks();
        const betaOutObj = backlinksToPlainObject(betaOut) as any;
        expect(betaOutObj[0].children.length).toEqual(1);
      });
    }
  );

  describeSingleWS(
    "GIVEN a single vault workspace and a note with many links",
    {
      postSetupHook: async ({ wsRoot, vaults }) => {
        await NoteTestUtilsV4.createNote({
          fname: "alpha",
          body: "this note has many links and candidates to it.",
          vault: vaults[0],
          wsRoot,
        });
        await NoteTestUtilsV4.createNote({
          fname: "beta",
          body: "[[alpha]] alpha alpha [[alpha]] [[alpha]] alpha\nalpha\n\nalpha",
          vault: vaults[0],
          wsRoot,
        });
      },
      modConfigCb: (config) => {
        config.dev = {
          enableLinkCandidates: true,
        };
        return config;
      },
    },
    () => {
      test("THEN multi backlink items are displayed correctly", async () => {
        const engine = ExtensionProvider.getEngine();

        // need this until we move it out of the feature flag.
        await new ReloadIndexCommand().execute();
        const alpha = engine.notes["alpha"];
        await ExtensionProvider.getWSUtils().openNote(alpha);

        const { out } = await getRootChildrenBacklinks();
        const outObj = backlinksToPlainObject(out) as any;

        // source should be beta.md

        const sourceTreeItem = outObj[0];
        expect(sourceTreeItem.label).toEqual("beta");
        // it should have all links and references in a flat list
        expect(sourceTreeItem.children.length).toEqual(8);
      });
    }
  );

  describeMultiWS(
    "GIVEN a multi vault workspace with xvault links",
    {
      preSetupHook: async ({ wsRoot, vaults }) => {
        await NoteTestUtilsV4.createNote({
          fname: "alpha",
          body: `[[beta]]`,
          vault: vaults[0],
          wsRoot,
        });
        await NoteTestUtilsV4.createNote({
          fname: "beta",
          body: `[[dendron://${VaultUtils.getName(vaults[0])}/alpha]]`,
          vault: vaults[1],
          wsRoot,
        });
      },
      modConfigCb: (config) => {
        config.dev = {
          enableLinkCandidates: true,
        };
        return config;
      },
    },
    () => {
      test("THEN BacklinksTreeDataProvider calculates correct number of backlinks", async () => {
        const { engine, wsRoot, vaults } = ExtensionProvider.getDWorkspace();

        const alpha = engine.notes["alpha"];
        await ExtensionProvider.getWSUtils().openNote(alpha);
        const { out } = await getRootChildrenBacklinksAsPlainObject();
        const expectedPath = vscode.Uri.file(
          path.join(wsRoot, vaults[1].fsPath, "beta.md")
        ).path;
        expect(
          out[0].command.arguments[0].path.toLowerCase() as string
        ).toEqual(expectedPath.toLowerCase());
        expect(out.length).toEqual(1);
      });
    }
  );

  describeSingleWS(
    "GIVEN a single vault workspace and anchor notes",
    {
      postSetupHook: async ({ wsRoot, vaults }) => {
        await NOTE_PRESETS_V4.NOTE_WITH_ANCHOR_TARGET.create({
          wsRoot,
          vault: vaults[0],
        });
        await NOTE_PRESETS_V4.NOTE_WITH_ANCHOR_LINK.create({
          wsRoot,
          vault: vaults[0],
        });
      },
    },
    () => {
      test("THEN BacklinksTreeDataProvider calculates correct number of links", async () => {
        const { engine, wsRoot } = ExtensionProvider.getDWorkspace();
        const alpha = engine.notes["alpha"];
        await ExtensionProvider.getWSUtils().openNote(alpha);
        const { out } = await getRootChildrenBacklinksAsPlainObject();
        const expectedPath = vscode.Uri.file(
          NoteUtils.getFullPath({
            note: engine.notes["beta"],
            wsRoot,
          })
        ).path;

        expect(
          out[0].command.arguments[0].path.toLowerCase() as string
        ).toEqual(expectedPath.toLowerCase());
        expect(out.length).toEqual(1);
      });
    }
  );

  describeSingleWS(
    "GIVEN a single vault workspace and alias notes",
    {
      postSetupHook: async ({ wsRoot, vaults }) => {
        await NOTE_PRESETS_V4.NOTE_WITH_TARGET.create({
          wsRoot,
          vault: vaults[0],
        });
        await NOTE_PRESETS_V4.NOTE_WITH_ALIAS_LINK.create({
          wsRoot,
          vault: vaults[0],
        });
      },
    },
    () => {
      test("THEN BacklinksTreeDataProvider calculates correct number of links", async () => {
        const { engine, wsRoot } = ExtensionProvider.getDWorkspace();
        const alpha = engine.notes["alpha"];
        await ExtensionProvider.getWSUtils().openNote(alpha);
        const { out } = await getRootChildrenBacklinksAsPlainObject();
        // assert.strictEqual(
        //   out[0].command.arguments[0].path.toLowerCase() as string,
        //   NoteUtils.getPathV4({ note: noteWithLink, wsRoot })
        // );
        const expectedPath = vscode.Uri.file(
          NoteUtils.getFullPath({
            note: engine.notes["beta"],
            wsRoot,
          })
        ).path;
        expect(
          out[0].command.arguments[0].path.toLowerCase() as string
        ).toEqual(expectedPath.toLowerCase());
        expect(out.length).toEqual(1);
      });
    }
  );

  describeSingleWS(
    "GIVEN a single vault workspace and hashtags",
    {
      postSetupHook: async ({ wsRoot, vaults }) => {
        await NoteTestUtilsV4.createNote({
          wsRoot,
          vault: vaults[0],
          fname: "tags.my.test-0.tag",
        });
        await NoteTestUtilsV4.createNote({
          wsRoot,
          vault: vaults[0],
          fname: "test",
          body: "#my.test-0.tag",
        });
      },
    },
    () => {
      test("THEN BacklinksTreeDataProvider calculates correct number of links", async () => {
        const { engine, wsRoot } = ExtensionProvider.getDWorkspace();
        const alpha = engine.notes["tags.my.test-0.tag"];
        await ExtensionProvider.getWSUtils().openNote(alpha);
        const { out } = await getRootChildrenBacklinksAsPlainObject();
        const expectedPath = vscode.Uri.file(
          NoteUtils.getFullPath({
            note: engine.notes["test"],
            wsRoot,
          })
        ).path;
        expect(
          out[0].command.arguments[0].path.toLowerCase() as string
        ).toEqual(expectedPath.toLowerCase());
        expect(out.length).toEqual(1);
      });
    }
  );

  describeMultiWS(
    "WHEN a basic workspace exists",
    {
      preSetupHook: ENGINE_HOOKS.setupBasic,
    },
    () => {
      let updateSortOrder: sinon.SinonStub;
      let backlinksTreeDataProvider: BacklinksTreeDataProvider;
      let mockEvents: MockEngineEvents;

      beforeEach(() => {
        mockEvents = new MockEngineEvents();
        backlinksTreeDataProvider = new BacklinksTreeDataProvider(
          mockEvents,
          ExtensionProvider.getEngine().config.dev?.enableLinkCandidates
        );

        updateSortOrder = sinon
          .stub(BacklinksTreeDataProvider.prototype, "sortOrder")
          .returns(undefined);
      });
      afterEach(() => {
        updateSortOrder.restore();
        backlinksTreeDataProvider.dispose();
      });

      test("AND a note gets created, THEN the data provider refresh event gets invoked", (done) => {
        const engine = ExtensionProvider.getEngine();
        const testNoteProps = engine.notes["foo"];
        const entry: NoteChangeEntry = {
          note: testNoteProps,
          status: "create",
        };

        backlinksTreeDataProvider.onDidChangeTreeData(() => {
          done();
        });

        mockEvents.testFireOnNoteChanged([entry]);
      });

      test("AND a note gets updated, THEN the data provider refresh event gets invoked", (done) => {
        const engine = ExtensionProvider.getEngine();
        const testNoteProps = engine.notes["foo"];
        const entry: NoteChangeEntry = {
          prevNote: testNoteProps,
          note: testNoteProps,
          status: "update",
        };

        backlinksTreeDataProvider.onDidChangeTreeData(() => {
          done();
        });

        mockEvents.testFireOnNoteChanged([entry]);
      });

      test("AND a note gets deleted, THEN the data provider refresh event gets invoked", (done) => {
        const engine = ExtensionProvider.getEngine();
        const testNoteProps = engine.notes["foo"];
        const entry: NoteChangeEntry = {
          note: testNoteProps,
          status: "delete",
        };

        backlinksTreeDataProvider.onDidChangeTreeData(() => {
          done();
        });

        mockEvents.testFireOnNoteChanged([entry]);
      });
    }
  );
});
Example #8
Source File: CopyNoteUrl.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("GIVEN CopyNoteUrlV2", function () {
  const modConfigCb = setupConfig;
  describe("AND WHEN has selection", () => {
    describeMultiWS(
      "WHEN selection with block anchor",
      {
        modConfigCb,
        postSetupHook: async (opts) => {
          const { vaults, wsRoot } = opts;
          const vault = vaults[0];
          await ENGINE_HOOKS.setupBasic(opts);
          await NOTE_PRESETS_V4.NOTE_WITH_BLOCK_ANCHOR_TARGET.create({
            wsRoot,
            vault,
          });
        },
      },
      () => {
        test("THEN create link with block anchor", async () => {
          const { vaults } = ExtensionProvider.getDWorkspace();
          const vault = vaults[0];
          const fname = NOTE_PRESETS_V4.NOTE_WITH_BLOCK_ANCHOR_TARGET.fname;
          const editor = await WSUtils.openNoteByPath({ vault, fname });
          editor.selection = new vscode.Selection(10, 0, 10, 5);
          const link = await new CopyNoteURLCommand().execute();
          const url = [ROOT_URL, "notes", `${fname}#^block-id`].join("/");
          expect(link).toEqual(url);
        });
      }
    );

    describeMultiWS(
      "WHEN selection with header anchor",
      {
        modConfigCb,
        postSetupHook: async (opts) => {
          const { vaults, wsRoot } = opts;
          const vault = vaults[0];
          await ENGINE_HOOKS.setupBasic(opts);
          await NOTE_PRESETS_V4.NOTE_WITH_ANCHOR_TARGET.create({
            wsRoot,
            vault,
          });
        },
      },
      () => {
        test("THEN create link with header anchor", async () => {
          const { vaults } = ExtensionProvider.getDWorkspace();
          const vault = vaults[0];
          const fname = NOTE_PRESETS_V4.NOTE_WITH_ANCHOR_TARGET.fname;
          const editor = await WSUtils.openNoteByPath({ vault, fname });
          editor.selection = new vscode.Selection(7, 0, 7, 12);
          const link = await new CopyNoteURLCommand().run();
          const url = [ROOT_URL, "notes", `${fname}#h1`].join("/");
          expect(link).toEqual(url);
        });
      }
    );
  });

  describe("AND WHEN regular copy", () => {
    describeMultiWS(
      "",
      {
        modConfigCb,
        postSetupHook: async (opts) => {
          await ENGINE_HOOKS.setupBasic(opts);
        },
      },
      () => {
        test("THEN create regular link", async () => {
          const { vaults } = ExtensionProvider.getDWorkspace();
          const vault = vaults[0];
          const fname = "foo";
          await WSUtils.openNoteByPath({ vault, fname });
          const link = await new CopyNoteURLCommand().execute();
          const url = _.join([ROOT_URL, "notes", `${fname}`], "/");
          expect(link).toEqual(url);
        });
      }
    );
  });

  describe("AND WHEN asset prefix set", () => {
    describeMultiWS(
      "",
      {
        modConfigCb: (config) => {
          config = setupConfig(config);
          config.publishing.assetsPrefix = "/" + ASSET_PREFIX;
          return config;
        },
        postSetupHook: async (opts) => {
          await ENGINE_HOOKS.setupBasic(opts);
        },
      },
      () => {
        test("THEN create link with prefix", async () => {
          const { vaults } = ExtensionProvider.getDWorkspace();
          const vault = vaults[0];
          const fname = "foo";
          await WSUtils.openNoteByPath({ vault, fname });
          const link = await new CopyNoteURLCommand().execute();
          const url = _.join(
            [ROOT_URL, ASSET_PREFIX, "notes", `${fname}`],
            "/"
          );
          expect(link).toEqual(url);
        });
      }
    );
  });
});
Example #9
Source File: CopyNoteUrl.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("CopyNoteUrl with seed", function () {
  const ctx: vscode.ExtensionContext = setupBeforeAfter(this);

  test("with seed site url override", (done) => {
    runLegacyMultiWorkspaceTest({
      ctx,
      preSetupHook: async (opts) => {
        await ENGINE_HOOKS.setupBasic(opts);
      },
      onInit: async ({ wsRoot, engine, vaults }) => {
        await TestSeedUtils.addSeed2WS({
          wsRoot,
          engine,
          modifySeed: (seed) => {
            seed.site = {
              url: "https://foo.com",
            };
            return seed;
          },
        });
        const seedId = TestSeedUtils.defaultSeedId();
        const config = getDWorkspace().config;
        engine.config = config;
        engine.vaults = ConfigUtils.getVaults(engine.config);
        sinon.stub(WSUtils, "getNoteFromDocument").returns(
          await NoteTestUtilsV4.createNote({
            fname: "root",
            vault: vaults[0],
            wsRoot,
          })
        );

        const vault = VaultUtils.getVaultByName({
          vaults: engine.vaults,
          vname: seedId,
        })!;
        await WSUtils.openNoteByPath({ vault, fname: "root" });
        VSCodeUtils.getActiveTextEditorOrThrow().selection =
          new vscode.Selection(0, 0, 0, 0); // Otherwise it has the header selected
        const link = await new CopyNoteURLCommand().run();
        expect(link).toEqual("https://foo.com");
        done();
      },
    });
  });

  test("with seed site url and index override", (done) => {
    runLegacyMultiWorkspaceTest({
      ctx,
      preSetupHook: async (opts) => {
        await ENGINE_HOOKS.setupBasic(opts);
      },
      onInit: async ({ wsRoot, engine, vaults }) => {
        await TestSeedUtils.addSeed2WS({
          wsRoot,
          engine,
          modifySeed: (seed) => {
            seed.site = {
              url: "https://foo.com",
              index: "root",
            };
            return seed;
          },
        });
        const seedId = TestSeedUtils.defaultSeedId();
        engine.config = getDWorkspace().config;
        engine.vaults = ConfigUtils.getVaults(engine.config);
        // TODO: ugly temporary hack. can be removed when [[Unify Runenginetest and Runworkspacetest|scratch.2021.06.17.164102.unify-runenginetest-and-runworkspacetest]] is implemented
        sinon.stub(WSUtils, "getNoteFromDocument").returns(
          await NoteTestUtilsV4.createNote({
            fname: "root",
            vault: vaults[0],
            wsRoot,
          })
        );
        const vault = VaultUtils.getVaultByName({
          vaults: engine.vaults,
          vname: seedId,
        })!;
        await WSUtils.openNoteByPath({ vault, fname: "root" });
        VSCodeUtils.getActiveTextEditorOrThrow().selection =
          new vscode.Selection(0, 0, 0, 0); // Otherwise it has the header selected
        const link = await new CopyNoteURLCommand().run();
        expect(link).toEqual("https://foo.com");
        done();
      },
    });
  });
});
Example #10
Source File: MigrateSelfContainedVault.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("GIVEN the MigrateSelfContainedVault command", () => {
  describeSingleWS(
    "WHEN the vault prompt is cancelled",
    { selfContained: false },
    () => {
      let showErrorMessage: SinonStubbedFn<typeof window["showErrorMessage"]>;
      let reloadWindow: SinonStubbedFn<typeof VSCodeUtils["reloadWindow"]>;
      let showQuickPick: SinonStubbedFn<typeof VSCodeUtils["showQuickPick"]>;

      before(async () => {
        const cmd = new MigrateSelfContainedVaultCommand(
          ExtensionProvider.getExtension()
        );

        showErrorMessage = sinon.stub(window, "showErrorMessage");
        reloadWindow = sinon.stub(VSCodeUtils, "reloadWindow");
        showQuickPick = sinon
          .stub(VSCodeUtils, "showQuickPick")
          .resolves(undefined);

        await cmd.run();
      });
      after(() => {
        [showErrorMessage, reloadWindow, showQuickPick].forEach((stub) =>
          stub.restore()
        );
      });

      test("THEN the workspace did not reload since there was no migration", () => {
        expect(reloadWindow.called).toBeFalsy();
      });

      test("THEN the vault should not have migrated", async () => {
        const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        expect(
          await verifyVaultNotMigrated({ wsRoot, vault: vaults[0] })
        ).toBeTruthy();
      });
    }
  );

  describeSingleWS(
    "WHEN the backup prompt is cancelled",
    { selfContained: false },
    () => {
      let showErrorMessage: SinonStubbedFn<typeof window["showErrorMessage"]>;
      let reloadWindow: SinonStubbedFn<typeof VSCodeUtils["reloadWindow"]>;
      let showQuickPick: SinonStubbedFn<typeof VSCodeUtils["showQuickPick"]>;

      before(async () => {
        const cmd = new MigrateSelfContainedVaultCommand(
          ExtensionProvider.getExtension()
        );

        showErrorMessage = sinon.stub(window, "showErrorMessage");
        reloadWindow = sinon.stub(VSCodeUtils, "reloadWindow");
        const { vaults } = ExtensionProvider.getDWorkspace();
        showQuickPick = stubMigrateQuickPick(
          VaultUtils.getName(vaults[0]),
          MigrateVaultContinueOption.cancel
        );

        await cmd.run();
      });
      after(() => {
        [showErrorMessage, reloadWindow, showQuickPick].forEach((stub) =>
          stub.restore()
        );
      });

      test("THEN the workspace did not reload since there was no migration", () => {
        expect(reloadWindow.called).toBeFalsy();
      });

      test("THEN the vault should not have migrated", async () => {
        const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        expect(
          await verifyVaultNotMigrated({ wsRoot, vault: vaults[0] })
        ).toBeTruthy();
      });
    }
  );

  describeSingleWS(
    "WHEN there's only a single vault, and it's self contained",
    { selfContained: true },
    () => {
      let showErrorMessage: SinonStubbedFn<typeof window["showErrorMessage"]>;
      let reloadWindow: SinonStubbedFn<typeof VSCodeUtils["reloadWindow"]>;
      let showQuickPick: SinonStubbedFn<typeof VSCodeUtils["showQuickPick"]>;

      before(async () => {
        const cmd = new MigrateSelfContainedVaultCommand(
          ExtensionProvider.getExtension()
        );

        showErrorMessage = sinon.stub(window, "showErrorMessage");
        reloadWindow = sinon.stub(VSCodeUtils, "reloadWindow");
        showQuickPick = sinon.stub(VSCodeUtils, "showQuickPick");

        await cmd.run();
      });
      after(() => {
        [showErrorMessage, reloadWindow, showQuickPick].forEach((stub) =>
          stub.restore()
        );
      });

      test("THEN there's an error that there's nothing to migrate", () => {
        expect(showErrorMessage.calledOnce).toBeTruthy();
        expect(showErrorMessage.args[0][0].includes("no vault")).toBeTruthy();
      });

      test("THEN no vault is prompted for", () => {
        expect(showQuickPick.called).toBeFalsy();
      });

      test("THEN the workspace did not reload since there was no migration", () => {
        expect(reloadWindow.called).toBeFalsy();
      });
    }
  );

  describeSingleWS(
    "WHEN there's only a single vault, and it's not self contained",
    {
      selfContained: false,
      modConfigCb: (config) => {
        const vault = ConfigUtils.getVaults(config)[0];
        // Using an asset in the vault as the logo. Migration should update the path.
        ConfigUtils.setPublishProp(
          config,
          "logoPath",
          `${vault.fsPath}/assets/image.png`
        );
        return config;
      },
      postSetupHook: async ({ wsRoot, vaults }) => {
        const vaultPath = pathForVaultRoot({ wsRoot, vault: vaults[0] });
        // Mock git folder & files to see if migration handles them.
        await fs.ensureDir(path.join(vaultPath, ".git"));
        await fs.writeFile(path.join(vaultPath, ".gitignore"), "");
        // Also mock the logo file
        await fs.ensureDir(path.join(vaultPath, "assets"));
        await fs.writeFile(path.join(vaultPath, "assets", "image.png"), "");
      },
    },
    () => {
      let reloadWindow: SinonStubbedFn<typeof VSCodeUtils["reloadWindow"]>;
      let showQuickPick: SinonStubbedFn<typeof VSCodeUtils["showQuickPick"]>;

      before(async () => {
        const { vaults } = ExtensionProvider.getDWorkspace();
        const cmd = new MigrateSelfContainedVaultCommand(
          ExtensionProvider.getExtension()
        );

        reloadWindow = sinon.stub(VSCodeUtils, "reloadWindow");
        showQuickPick = stubMigrateQuickPick(VaultUtils.getName(vaults[0]));

        await cmd.run();
      });
      after(() => {
        [reloadWindow, showQuickPick].forEach((stub) => stub.restore());
      });

      test("THEN it prompts for the vault and confirmation", () => {
        expect(showQuickPick.callCount).toEqual(2);
      });

      test("THEN the workspace reloads to apply the migration", () => {
        expect(reloadWindow.called).toBeTruthy();
      });

      test("THEN the vault is migrated", async () => {
        const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        expect(
          verifyVaultHasMigrated({ wsRoot, vault: vaults[0] })
        ).toBeTruthy();
      });

      test("THEN the logoPath is updated to account for the moved asset", async () => {
        const { wsRoot, config } = ExtensionProvider.getDWorkspace();
        const logoPath = ConfigUtils.getPublishing(config).logoPath;
        expect(logoPath).toBeTruthy();
        // If the logoPath was not updated, then we won't find the asset file there
        expect(
          await fs.pathExists(path.join(wsRoot, path.normalize(logoPath!)))
        ).toBeTruthy();
      });

      test("THEN the git folders/files are handled correctly", async () => {
        const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        expect(
          await fs.pathExists(
            path.join(pathForVaultRoot({ vault: vaults[0], wsRoot }), ".git")
          )
        ).toBeTruthy();
        expect(
          await fs.pathExists(
            path.join(
              pathForVaultRoot({ vault: vaults[0], wsRoot }),
              ".gitignore"
            )
          )
        ).toBeTruthy();
      });
    }
  );

  describeMultiWS(
    "WHEN there are multiple vaults",
    { selfContained: false },
    () => {
      let reloadWindow: SinonStubbedFn<typeof VSCodeUtils["reloadWindow"]>;
      let showQuickPick: SinonStubbedFn<typeof VSCodeUtils["showQuickPick"]>;

      before(async () => {
        const { vaults } = ExtensionProvider.getDWorkspace();
        const cmd = new MigrateSelfContainedVaultCommand(
          ExtensionProvider.getExtension()
        );

        reloadWindow = sinon.stub(VSCodeUtils, "reloadWindow");
        showQuickPick = stubMigrateQuickPick(VaultUtils.getName(vaults[0]));

        await cmd.run();
      });
      after(() => {
        [reloadWindow, showQuickPick].forEach((stub) => stub.restore());
      });

      test("THEN it prompts for the vault and confirmation", () => {
        expect(showQuickPick.callCount).toEqual(2);
      });

      test("THEN the workspace reloads to apply the migration", () => {
        expect(reloadWindow.called).toBeTruthy();
      });

      test("THEN the vault is migrated", async () => {
        const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        expect(
          verifyVaultHasMigrated({ wsRoot, vault: vaults[0] })
        ).toBeTruthy();
      });
    }
  );
});
Example #11
Source File: PreviewPanel.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("GIVEN PreviewPanel", function () {
  describeSingleWS("WHEN opening a note", {}, () => {
    let previewPanel: PreviewPanel;
    before(async () => {
      const { engine, vaults } = ExtensionProvider.getDWorkspace();
      const note = NoteUtils.getNoteByFnameFromEngine({
        fname: "root",
        vault: vaults[0],
        engine,
      });
      expect(note).toBeTruthy();
      await ExtensionProvider.getWSUtils().openNote(note!);
      previewPanel = PreviewPanelFactory.create(
        ExtensionProvider.getExtension()
      ) as PreviewPanel; // overriding the type here to get the function to expose internals
      previewPanel.show(note);
    });

    describe("AND note has block anchor", () => {
      test("Block anchor is not converted to plain text", async () => {
        const note = await makeTestNote({
          previewPanel,
          body: "Lorem ipsum ^anchor",
        });
        expect(
          await AssertUtils.assertInString({
            body: note.body,
            match: ["^anchor"],
          })
        ).toBeTruthy();
      });
    });

    describe("and note has images", () => {
      describe("AND image starts with a forward slash", () => {
        test("THEN URL is correctly rewritten", async () => {
          const { vaults } = ExtensionProvider.getDWorkspace();
          const note = await makeTestNote({
            previewPanel,
            body: "![](/assets/image.png)",
          });
          expect(
            await AssertUtils.assertInString({
              body: note.body,
              match: [
                "https://file",
                "vscode",
                path.posix.join(
                  VaultUtils.getRelPath(vaults[0]),
                  "assets",
                  "image.png"
                ),
              ],
            })
          ).toBeTruthy();
        });
      });

      describe("AND image starts without a forward slash", () => {
        test("THEN URL is correctly rewritten", async () => {
          const { vaults } = ExtensionProvider.getDWorkspace();
          const note = await makeTestNote({
            previewPanel,
            body: "![](assets/image.png)",
          });
          expect(
            await AssertUtils.assertInString({
              body: note.body,
              match: [
                "https://file",
                "vscode",
                path.posix.join(
                  VaultUtils.getRelPath(vaults[0]),
                  "assets",
                  "image.png"
                ),
              ],
            })
          ).toBeTruthy();
        });
      });

      describe("AND image URI is encoded", () => {
        test("THEN URL is correctly rewritten", async () => {
          const { vaults } = ExtensionProvider.getDWorkspace();
          const note = await makeTestNote({
            previewPanel,
            body: "![](assets/Pasted%20image%20%CE%B1.png)",
          });
          expect(
            await AssertUtils.assertInString({
              body: note.body,
              match: [
                "https://file",
                "vscode",
                path.posix.join(
                  VaultUtils.getRelPath(vaults[0]),
                  "assets",
                  // `makeTestNote()` will invoke `rewriteImageUrls()`
                  //  in which `makeImageUrlFullPath()` will expectedly decode "Pasted%20image%20%CE%B1.png"
                  //    to "Pasted image α.png",
                  //  then `panel.webview.asWebviewUri` encodes it back to "Pasted%20image%20%CE%B1.png".
                  "Pasted%20image%20%CE%B1.png"
                ),
              ],
            })
          ).toBeTruthy();
        });
      });

      describe("AND image is an absolute path", () => {
        test("THEN URL is correctly rewritten", async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          const note = await makeTestNote({
            previewPanel,
            body: `![](${path.join(wsRoot, "image.png").normalize()})`,
          });
          expect(
            await AssertUtils.assertInString({
              body: note.body,
              match: ["https://file", "vscode", "image.png"],
            })
          ).toBeTruthy();
        });
      });

      describe("AND image is a URL", () => {
        test("THEN URL is NOT rewritten", async () => {
          const note = await makeTestNote({
            previewPanel,
            body: `![](https://org-dendron-public-assets.s3.amazonaws.com/images/rfc-35-template-1.png)`,
          });
          expect(
            await AssertUtils.assertInString({
              body: note.body,
              match: [
                "https://org-dendron-public-assets.s3.amazonaws.com/images/rfc-35-template-1.png",
              ],
              nomatch: ["vscode", "https://file"],
            })
          ).toBeTruthy();
        });
      });

      describe("AND the note is updated", () => {
        test("THEN the output also updates", async () => {
          const { vaults } = ExtensionProvider.getDWorkspace();
          let note = await makeTestNote({
            previewPanel,
            body: `![](https://org-dendron-public-assets.s3.amazonaws.com/images/rfc-35-template-1.png)`,
            genRandomId: false,
          });
          expect(
            await AssertUtils.assertInString({
              body: note.body,
              match: [
                "https://org-dendron-public-assets.s3.amazonaws.com/images/rfc-35-template-1.png",
              ],
              nomatch: ["vscode", "https://file"],
            })
          ).toBeTruthy();
          // with genRandomId: false, the new note will have the same ID and will update the pervious one
          note = await makeTestNote({
            previewPanel,
            body: `![](/assets/image.png)`,
            genRandomId: false,
          });
          expect(
            await AssertUtils.assertInString({
              body: note.body,
              nomatch: [
                "https://org-dendron-public-assets.s3.amazonaws.com/images/rfc-35-template-1.png",
              ],
              match: [
                "https://file",
                "vscode",
                path.posix.join(
                  VaultUtils.getRelPath(vaults[0]),
                  "assets",
                  "image.png"
                ),
              ],
            })
          ).toBeTruthy();
        });
      });
    });
  });
});
Example #12
Source File: ReloadIndex.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("GIVEN ReloadIndex", function () {
  describeSingleWS("WHEN root files are missing", {}, () => {
    let rootFiles: string[] = [];
    before(async () => {
      const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
      const vaultDir = vault2Path({ vault: vaults[0], wsRoot });
      rootFiles = [
        path.join(vaultDir, "root.md"),
        path.join(vaultDir, "root.schema.yml"),
      ];
      await Promise.all(rootFiles.map((ent) => fs.remove(ent)));

      await new ReloadIndexCommand().run();
    });

    test("THEN the root files are recreated", async () => {
      expect(
        _.every(await Promise.all(rootFiles.map((ent) => fs.pathExists(ent))))
      ).toBeTruthy();
    });
  });

  describeSingleWS("WHEN root files exist", {}, () => {
    let rootFiles: string[] = [];
    before(async () => {
      const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
      const vaultDir = vault2Path({ vault: vaults[0], wsRoot });
      rootFiles = [
        path.join(vaultDir, "root.md"),
        path.join(vaultDir, "root.schema.yml"),
      ];
      await Promise.all([
        fs.appendFile(rootFiles[0], "bond", { encoding: "utf8" }),
        fs.appendFile(rootFiles[1], "# bond", { encoding: "utf8" }),
      ]);

      await new ReloadIndexCommand().run();
    });

    test("THEN the root files are not overwritten", async () => {
      expect(
        _.every(
          await Promise.all(
            rootFiles.map(async (ent) =>
              (await fs.readFile(ent)).includes("bond")
            )
          )
        )
      ).toBeTruthy();
    });
  });

  describeSingleWS("WHEN there are 2 notes with duplicate note IDs", {}, () => {
    const duplicateId = "duplicate";
    const firstNote = "first";
    const secondNote = "second";
    let showMessage: sinon.SinonStub<
      Parameters<typeof VSCodeUtils["showMessage"]>
    >;
    before(async () => {
      const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
      await NoteTestUtilsV4.createNote({
        fname: firstNote,
        vault: vaults[0],
        wsRoot,
        props: {
          id: duplicateId,
        },
      });
      await NoteTestUtilsV4.createNote({
        fname: secondNote,
        vault: vaults[0],
        wsRoot,
        props: {
          id: duplicateId,
        },
      });
      showMessage = sinon.stub(VSCodeUtils, "showMessage").resolves(undefined);

      await new ReloadIndexCommand().run();
    });

    test("THEN warns that there are notes with duplicated IDs", async () => {
      const { vaults } = ExtensionProvider.getDWorkspace();
      expect(showMessage.callCount).toEqual(1);
      expect(showMessage.firstCall.args[1].includes(firstNote)).toBeTruthy();
      expect(showMessage.firstCall.args[1].includes(secondNote)).toBeTruthy();
      expect(
        showMessage.firstCall.args[1].includes(VaultUtils.getName(vaults[0]))
      ).toBeTruthy();
    });
  });

  describeSingleWS(
    "WHEN there are many notes with duplicate note IDs",
    {},
    () => {
      const duplicateId = "duplicate";
      const firstNote = "first";
      const secondNote = "second";
      const thirdNote = "third";
      let showMessage: sinon.SinonStub<
        Parameters<typeof VSCodeUtils["showMessage"]>
      >;
      before(async () => {
        const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        await NoteTestUtilsV4.createNote({
          fname: firstNote,
          vault: vaults[0],
          wsRoot,
          props: {
            id: duplicateId,
          },
        });
        await NoteTestUtilsV4.createNote({
          fname: secondNote,
          vault: vaults[0],
          wsRoot,
          props: {
            id: duplicateId,
          },
        });
        await NoteTestUtilsV4.createNote({
          fname: thirdNote,
          vault: vaults[0],
          wsRoot,
          props: {
            id: duplicateId,
          },
        });
        showMessage = sinon
          .stub(VSCodeUtils, "showMessage")
          .resolves(undefined);

        await new ReloadIndexCommand().run();
      });

      test("THEN warns multiple times that there are notes with duplicated IDs", async () => {
        const { vaults } = ExtensionProvider.getDWorkspace();
        expect(showMessage.callCount).toEqual(2);
        expect(showMessage.getCall(0).args[1].includes(firstNote)).toBeTruthy();
        expect(
          showMessage.getCall(0).args[1].includes(secondNote)
        ).toBeTruthy();
        expect(
          showMessage.getCall(0).args[1].includes(VaultUtils.getName(vaults[0]))
        ).toBeTruthy();
        expect(
          showMessage.getCall(1).args[1].includes(secondNote)
        ).toBeTruthy();
        expect(showMessage.getCall(1).args[1].includes(thirdNote)).toBeTruthy();
        expect(
          showMessage.getCall(1).args[1].includes(VaultUtils.getName(vaults[0]))
        ).toBeTruthy();
      });
    }
  );

  describeMultiWS("WHEN there is a single vault missing", {}, () => {
    before(async () => {
      const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
      const vaultPath = vault2Path({ vault: vaults[0], wsRoot });
      await fs.rmdir(vaultPath, { recursive: true });
    });

    test("THEN other vaults are still loaded", async () => {
      const engine = await new ReloadIndexCommand().run();
      const { vaults } = ExtensionProvider.getDWorkspace();
      expect(engine).toBeTruthy();
      const notes = _.sortBy(
        NoteUtils.getNotesByFnameFromEngine({
          engine: engine!,
          fname: "root",
        }),
        (note) => path.basename(note.vault.fsPath)
      );
      expect(notes.length).toEqual(2);
      expect(VaultUtils.isEqualV2(notes[0].vault, vaults[1])).toBeTruthy();
      expect(VaultUtils.isEqualV2(notes[1].vault, vaults[2])).toBeTruthy();
    });
  });

  describeSingleWS(
    "WHEN a self contained vault is misconfigured",
    {
      selfContained: true,
      postSetupHook: async ({ wsRoot }) => {
        const config = DConfig.getOrCreate(wsRoot);
        expect(config.workspace.vaults.length).toEqual(1);
        delete config.workspace.vaults[0].selfContained;
        await DConfig.writeConfig({ wsRoot, config });
      },
    },
    () => {
      test("THEN it prompts to fix the config", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        sinon
          .stub(window, "showWarningMessage")
          // Cast needed because sinon doesn't get which overload we're stubbing
          .resolves(FIX_CONFIG_SELF_CONTAINED as unknown as MessageItem);
        const reloadWindow = sinon.stub(VSCodeUtils, "reloadWindow");

        await ReloadIndexCommand.checkAndPromptForMisconfiguredSelfContainedVaults(
          { engine: ExtensionProvider.getEngine() }
        );

        // Should reload window after fixing so the plugin picks up new vault config
        expect(reloadWindow.calledOnce).toBeTruthy();
        // The config should be updated to mark the vault as self contained
        const configAfter = DConfig.getOrCreate(wsRoot);
        expect(configAfter.workspace.vaults[0].selfContained).toBeTruthy();
      });
    }
  );
});
Example #13
Source File: ShowPreview.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("GIVEN ShowPreview", function () {
  const ctx = setupBeforeAfter(this);

  describeSingleWS(
    "WHEN opening the preview from the command bar",
    {
      ctx,
    },
    () => {
      let note: NoteProps;
      before(async () => {
        const { engine, wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        note = await NoteTestUtilsV4.createNoteWithEngine({
          engine,
          wsRoot,
          vault: vaults[0],
          fname: "preview-test",
        });
        // Open the note so that's the current note
        await ExtensionProvider.getWSUtils().openNote(note);
      });
      test("THEN the current note is opened", async () => {
        const cmd = new ShowPreviewCommand(
          PreviewPanelFactory.create(ExtensionProvider.getExtension())
        );
        const out = await cmd.run();
        expect(out?.note).toBeTruthy();
        expect(out!.note!.id).toEqual(note.id);
        expect(out!.note!.fname).toEqual(note.fname);
      });
    }
  );

  describeSingleWS(
    "WHEN opening the preview from a context menu AND a note is open",
    {
      ctx,
    },
    () => {
      let note: NoteProps;
      before(async () => {
        const { engine, wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        note = await NoteTestUtilsV4.createNoteWithEngine({
          engine,
          wsRoot,
          vault: vaults[0],
          fname: "preview-test",
        });
        const wrongNote = await NoteTestUtilsV4.createNoteWithEngine({
          engine,
          wsRoot,
          vault: vaults[0],
          fname: "wrong-note",
        });
        // A different note is open this time
        await ExtensionProvider.getWSUtils().openNote(wrongNote);
      });
      test("THEN the selected note is opened", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        const cmd = new ShowPreviewCommand(
          PreviewPanelFactory.create(ExtensionProvider.getExtension())
        );
        // When opened from a menu, the file path will be passed as an argument
        const path = vscode.Uri.file(NoteUtils.getFullPath({ note, wsRoot }));
        const out = await cmd.run(path);
        expect(out?.note).toBeTruthy();
        expect(out!.note!.id).toEqual(note.id);
        expect(out!.note!.fname).toEqual(note.fname);
      });
    }
  );

  describeSingleWS(
    "WHEN opening the preview from a context menu AND no note is open",
    {
      ctx,
    },
    () => {
      let note: NoteProps;
      before(async () => {
        const { engine, wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        note = await NoteTestUtilsV4.createNoteWithEngine({
          engine,
          wsRoot,
          vault: vaults[0],
          fname: "preview-test",
        });
        // Make sure no note is open
        await VSCodeUtils.closeAllEditors();
      });
      test("THEN the selected note is opened", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        const cmd = new ShowPreviewCommand(
          PreviewPanelFactory.create(ExtensionProvider.getExtension())
        );
        // When opened from a menu, the file path will be passed as an argument
        const path = vscode.Uri.file(NoteUtils.getFullPath({ note, wsRoot }));
        const out = await cmd.run(path);
        expect(out?.note).toBeTruthy();
        expect(out!.note!.id).toEqual(note.id);
        expect(out!.note!.fname).toEqual(note.fname);
      });
    }
  );

  describeSingleWS(
    "WHEN opening a non-note file from the content menu",
    {
      ctx,
    },
    () => {
      let fsPath: string;
      before(async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        fsPath = path.join(wsRoot, "foo-bar.md");
        await fs.writeFile(fsPath, "foo bar");
        // Make sure no note is open
        await VSCodeUtils.closeAllEditors();
      });
      test("THEN the selected non-note file is opened", async () => {
        const cmd = new ShowPreviewCommand(
          PreviewPanelFactory.create(ExtensionProvider.getExtension())
        );
        // When opened from a menu, the file path will be passed as an argument
        const path = vscode.Uri.file(fsPath);
        const out = await cmd.run(path);
        expect(out?.fsPath).toEqual(fsPath);
      });
    }
  );

  describeSingleWS(
    "WHEN opening a non-note file from the command bar",
    {
      ctx,
    },
    () => {
      let fsPath: string;
      let uri: vscode.Uri;
      before(async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        fsPath = path.join(wsRoot, "foo-bar.md");
        await fs.writeFile(fsPath, "foo bar");
        uri = vscode.Uri.file(fsPath);
        await VSCodeUtils.openFileInEditor(uri);
      });
      test("THEN the current non-note file is opened", async () => {
        const cmd = new ShowPreviewCommand(
          PreviewPanelFactory.create(ExtensionProvider.getExtension())
        );
        const out = await cmd.run();
        expect(out?.fsPath).toEqual(fsPath);
      });
    }
  );

  describeSingleWS(
    "WHEN preview is open for a note containing link with .md in its name", //[[lorem.ipsum.mdone.first]]
    {
      ctx,
    },
    () => {
      let note: NoteProps;
      before(async () => {
        const { engine, wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        await NoteTestUtilsV4.createNoteWithEngine({
          engine,
          wsRoot,
          vault: vaults[0],
          fname: "lorem.ipsum.mdone.first",
          body: "Lorem ipsum",
        });
        note = await NoteTestUtilsV4.createNoteWithEngine({
          engine,
          wsRoot,
          vault: vaults[0],
          fname: "preview-link-test",
          body: "[[lorem.ipsum.mdone.first]]",
        });
      });
      test("THEN preview must link to the correct note", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        const cmd = new ShowPreviewCommand(
          PreviewPanelFactory.create(ExtensionProvider.getExtension())
        );
        // When opened from a menu, the file path will be passed as an argument
        const path = vscode.Uri.file(NoteUtils.getFullPath({ note, wsRoot }));
        const out = await cmd.run(path);
        expect(out?.note).toBeTruthy();
        expect(out!.note!.fname).toEqual(note.fname);
        const links = out!.note!.links;
        expect(links[0].value).toEqual("lorem.ipsum.mdone.first");
      });
    }
  );
});
Example #14
Source File: migration.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("Migration", function () {
  const ctx = setupBeforeAfter(this);

  describe.skip("runMigration from activate", () => {
    test("global version ahead of workspace version", (done) => {
      runLegacyMultiWorkspaceTest({
        ctx,
        onInit: async () => {
          await ctx.globalState.update(GLOBAL_STATE.VERSION, "0.46.0");
          await ctx.workspaceState.update(WORKSPACE_STATE.VERSION, "0.45.0");
          await _activate(ctx);
          expect(
            sinon
              .spy(VSCodeUtils, "getInstallStatusForExtension")
              .returned(InstallStatus.UPGRADED)
          );
          done();
        },
        modConfigCb: (config) => {
          // @ts-ignore
          delete config["journal"];
          return config;
        },
      });
    });
  });

  describe("runMigration only", () => {
    test("migrate to 46.0", (done) => {
      runLegacyMultiWorkspaceTest({
        ctx,
        onInit: async ({ engine, wsRoot }) => {
          const dendronConfig = engine.config;
          const wsConfig =
            await ExtensionProvider.getExtension().getWorkspaceSettings();
          const wsService = new WorkspaceService({ wsRoot });
          const out = await MigrationService.applyMigrationRules({
            currentVersion: "0.46.0",
            previousVersion: "0.45.0",
            dendronConfig,
            wsConfig,
            wsService,
            logger: Logger,
            migrations: getMigration({ exact: "0.46.0" }),
          });
          const { dendronConfig: newDendronConfig, changeName } = out[0].data;
          expect(changeName).toEqual("update cache");
          const dendronVersion =
            ConfigUtils.getWorkspace(newDendronConfig).dendronVersion;
          expect(dendronVersion).toEqual("0.46.0");
          expect(out.length).toEqual(1);
          done();
        },
      });
    });

    describe("migrate to 47.1", () => {
      test("apply journal config, default settings", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          modConfigCb: (config) => {
            // we are deleting a field that was optional before migraiton, hence the ignore
            // @ts-ignore
            delete config.workspace["journal"];
            return config;
          },
          onInit: async ({ engine, wsRoot }) => {
            const dendronConfig = engine.config;
            const wsConfig =
              await ExtensionProvider.getExtension().getWorkspaceSettings();
            const wsService = new WorkspaceService({ wsRoot });
            const out = await MigrationService.applyMigrationRules({
              currentVersion: "0.47.1",
              previousVersion: "0.46.0",
              dendronConfig,
              wsConfig,
              wsService,
              logger: Logger,
              migrations: getMigration({ from: "0.46.0", to: "0.47.1" }),
            });
            expect(out.length).toEqual(1);
            const config = ExtensionProvider.getDWorkspace().config;
            const journalConfig = ConfigUtils.getJournal(config);
            const defaultJournalConfig = ConfigUtils.getJournal(
              ConfigUtils.genDefaultConfig()
            );
            expect(journalConfig).toEqual(defaultJournalConfig);
            done();
          },
        });
      });

      test("apply journal config, non standard settings", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          modConfigCb: (config) => {
            // we are deleting a field that was optional before migraiton, hence the ignore
            // @ts-ignore
            delete config["journal"];
            return config;
          },
          onInit: async ({ engine, wsRoot }) => {
            const dendronConfig = engine.config;
            const wsConfig =
              await ExtensionProvider.getExtension().getWorkspaceSettings();
            const wsService = new WorkspaceService({ wsRoot });
            const out = await MigrationService.applyMigrationRules({
              currentVersion: "0.47.1",
              previousVersion: "0.46.0",
              dendronConfig,
              wsConfig,
              wsService,
              logger: Logger,
              migrations: getMigration({ from: "0.46.0", to: "0.47.1" }),
            });
            expect(out.length).toEqual(1);
            const config = ExtensionProvider.getDWorkspace().config;
            const journalConfig = ConfigUtils.getJournal(config);
            const expectedJournalConfig = {
              ...ConfigUtils.getJournal(ConfigUtils.genDefaultConfig()),
              name: "foo",
            };
            expect(journalConfig).toEqual(expectedJournalConfig);
            done();
          },
          wsSettingsOverride: {
            settings: {
              [CONFIG.DEFAULT_JOURNAL_NAME.key]: "foo",
            },
          },
          workspaceType: WorkspaceType.CODE,
        });
      });
    });

    test("migrate to 0.52.0, non standard settings", (done) => {
      runLegacyMultiWorkspaceTest({
        ctx,
        onInit: async ({ engine, wsRoot }) => {
          const dendronConfig = engine.config;
          const wsConfig =
            await ExtensionProvider.getExtension().getWorkspaceSettings();
          const wsService = new WorkspaceService({ wsRoot });
          await MigrationService.applyMigrationRules({
            currentVersion: "0.52.0",
            previousVersion: "0.51.0",
            dendronConfig,
            wsConfig,
            wsService,
            logger: Logger,
            migrations: getMigration({ from: "0.51.0", to: "0.52.0" }),
          });
          const config = ExtensionProvider.getDWorkspace().config;
          const scratchConfig = ConfigUtils.getScratch(config);
          const expectedScratchConfig = {
            ...ConfigUtils.getScratch(ConfigUtils.genDefaultConfig()),
            name: "foo",
          };
          expect(scratchConfig).toEqual(expectedScratchConfig);
          done();
        },
        wsSettingsOverride: {
          settings: {
            [CONFIG.DEFAULT_SCRATCH_NAME.key]: "foo",
          },
        },
        workspaceType: WorkspaceType.CODE,
      });
    });

    test("migrate to 0.51.4 (set scratch notes in dendron.yml), non standard settings", (done) => {
      runLegacyMultiWorkspaceTest({
        ctx,
        onInit: async ({ engine, wsRoot }) => {
          const dendronConfig = engine.config;
          const wsConfig =
            await ExtensionProvider.getExtension().getWorkspaceSettings();
          const wsService = new WorkspaceService({ wsRoot });
          await MigrationService.applyMigrationRules({
            currentVersion: "0.51.4",
            previousVersion: "0.51.3",
            dendronConfig,
            wsConfig,
            wsService,
            logger: Logger,
            migrations: getMigration({ from: "0.51.0", to: "0.51.4" }),
          });
          done();
        },
      });
    });

    test("migrate to 0.55.2 (old existing ws config to new dendron config)", (done) => {
      runLegacyMultiWorkspaceTest({
        ctx,
        modConfigCb: (config) => {
          // @ts-ignore
          delete config.commands["lookup"];
          return config;
        },
        onInit: async ({ engine, wsRoot }) => {
          const dendronConfig = engine.config;
          const wsConfig =
            await ExtensionProvider.getExtension().getWorkspaceSettings();
          const wsService = new WorkspaceService({ wsRoot });
          expect(
            wsConfig?.settings[CONFIG.DEFAULT_LOOKUP_CREATE_BEHAVIOR.key]
          ).toEqual(LegacyLookupSelectionType.selection2link);

          // // we explicitly deleted it. don't use ConfigUtils.
          const rawConfig = DConfig.getRaw(wsRoot);
          const lookup = rawConfig.commands?.lookup;
          expect(_.isUndefined(lookup)).toBeTruthy();
          await MigrationService.applyMigrationRules({
            currentVersion: "0.55.2",
            previousVersion: "0.55.1",
            dendronConfig,
            wsConfig,
            wsService,
            logger: Logger,
            migrations: getMigration({ from: "0.55.0", to: "0.55.2" }),
          });
          const config = ExtensionProvider.getDWorkspace().config;
          const lookupConfig = ConfigUtils.getLookup(config);
          expect(lookupConfig.note.selectionMode).toEqual(
            LookupSelectionModeEnum.link
          );
          done();
        },
        wsSettingsOverride: {
          settings: {
            [CONFIG.DEFAULT_LOOKUP_CREATE_BEHAVIOR.key]:
              LegacyLookupSelectionType.selection2link,
          },
        },
        workspaceType: WorkspaceType.CODE,
      });
    });

    test("migrate to 0.55.2 (implicit to new dendron config)", (done) => {
      runLegacyMultiWorkspaceTest({
        ctx,
        modConfigCb: (config) => {
          // @ts-ignore
          delete config.commands["lookup"];
          return config;
        },
        onInit: async ({ engine, wsRoot }) => {
          const dendronConfig = engine.config;
          const wsConfig =
            await ExtensionProvider.getExtension().getWorkspaceSettings();
          const wsService = new WorkspaceService({ wsRoot });
          expect(
            _.isUndefined(
              wsConfig?.settings[CONFIG.DEFAULT_LOOKUP_CREATE_BEHAVIOR.key]
            )
          ).toBeTruthy();

          // testing for explicitly deleted key.
          const rawConfig = DConfig.getRaw(wsRoot);
          const lookup = rawConfig.commands?.lookup;
          expect(_.isUndefined(lookup)).toBeTruthy();
          await MigrationService.applyMigrationRules({
            currentVersion: "0.55.2",
            previousVersion: "0.55.1",
            dendronConfig,
            wsConfig,
            wsService,
            logger: Logger,
            migrations: getMigration({ from: "0.55.0", to: "0.55.2" }),
          });
          const config = ExtensionProvider.getDWorkspace().config;
          const lookupConfig = ConfigUtils.getLookup(config);
          expect(lookupConfig.note.selectionMode).toEqual(
            ConfigUtils.genDefaultConfig().commands!.lookup.note.selectionMode
          );
          done();
        },
        workspaceType: WorkspaceType.CODE,
      });
    });

    describe("Config Namespace Migration", () => {
      describeMultiWS(
        "GIVEN v1 config (pre 0.63)",
        {
          ctx,
          modConfigCb: (config) => {
            // @ts-ignore
            config["version"] = 1;
            config["randomNote"] = {
              include: ["foo", "bar"],
              exclude: ["lorem"],
            } as LegacyRandomNoteConfig;
            config["defaultInsertHierarchy"] = "user.foo";
            config["insertNoteLink"] = {
              aliasMode: "none",
              multiSelect: true,
            } as LegacyInsertNoteLinkConfig;
            config["insertNoteIndex"] = {
              marker: true,
            } as LegacyInsertNoteIndexConfig;
            config["lookup"] = {
              note: {
                selectionType: "none",
                leaveTrace: true,
              },
            } as LegacyLookupConfig;
            config["lookupConfirmVaultOnCreate"] = false;

            // @ts-ignore
            delete config["commands"];

            return config;
          },
        },
        () => {
          DendronExtension.version = () => "0.62.0";
          test("command config correctly migrates to new namespace", async () => {
            const engine = ExtensionProvider.getEngine();
            const wsRoot = ExtensionProvider.getDWorkspace().wsRoot;
            const dendronConfig = DConfig.getRaw(
              wsRoot
            ) as IntermediateDendronConfig;
            const wsConfig =
              await ExtensionProvider.getExtension().getWorkspaceSettings();
            const wsService = new WorkspaceService({ wsRoot });

            const oldKeys = [
              "lookup",
              "randomNote",
              "insertNoteLink",
              "insertNoteIndex",
              "defaultInsertHierarchy",
              "lookupConfirmVaultOnCreate",
            ];

            // @ts-ignore
            dendronConfig["version"] = 1;
            // @ts-ignore
            delete dendronConfig["commands"];
            const originalDeepCopy = _.cloneDeep(dendronConfig);

            // all old configs should exist prior to migration
            const preMigrationCheckItems = [
              _.isUndefined(dendronConfig["commands"]),
              oldKeys.every((value) => {
                return !_.isUndefined(_.get(dendronConfig, value));
              }),
            ];

            preMigrationCheckItems.forEach((item) => {
              expect(item).toBeTruthy();
            });

            await MigrationService.applyMigrationRules({
              currentVersion: "0.83.0",
              previousVersion: "0.62.0",
              dendronConfig,
              wsConfig,
              wsService,
              logger: Logger,
              migrations: [CONFIG_MIGRATIONS],
            });

            // backup of the original should exist.
            const allBackupFiles = fs.readdirSync(
              path.join(wsRoot, ".backup", "config"),
              {
                withFileTypes: true,
              }
            );
            const maybeBackupFileName = allBackupFiles
              .filter((ent) => ent.isFile())
              .filter((fileEnt) =>
                fileEnt.name.includes("migrate-config")
              )[0].name;
            expect(!_.isUndefined(maybeBackupFileName)).toBeTruthy();

            // backup content should be identical to original deep copy.
            const backupContent = readYAML(
              path.join(wsRoot, ".backup", "config", maybeBackupFileName)
            ) as IntermediateDendronConfig;

            // need to omit these because they are set by default during ws init.
            expect(
              _.isEqual(
                _.omit(backupContent, [
                  "version",
                  "publishing",
                  "preview",
                  "workspace",
                  "commands",
                ]),
                _.omit(originalDeepCopy, [
                  "version",
                  "publishing",
                  "preview",
                  "workspace",
                ])
              )
            ).toBeTruthy();

            // post migration, commands namespace should exist
            const postMigrationDendronConfig = (await engine.getConfig()).data!;
            const postMigrationKeys = Object.keys(postMigrationDendronConfig);
            expect(postMigrationKeys.includes("commands")).toBeTruthy();
            // and all old keys should not exist
            expect(
              oldKeys.every((value) => postMigrationKeys.includes(value))
            ).toBeFalsy();

            // and new commands namespace should be correctly mapped
            const expectedCommandConfig: DendronCommandConfig = {
              lookup: {
                note: {
                  selectionMode: LookupSelectionModeEnum.none,
                  confirmVaultOnCreate: false,
                  vaultSelectionModeOnCreate:
                    LookupSelectVaultModeOnCreateEnum.smart,
                  leaveTrace: true,
                  bubbleUpCreateNew: true,
                  fuzzThreshold: 0.2,
                },
              },
              randomNote: {
                include: ["foo", "bar"],
                exclude: ["lorem"],
              },
              insertNote: {
                initialValue: "user.foo",
              },
              insertNoteLink: {
                aliasMode: InsertNoteLinkAliasModeEnum.none,
                enableMultiSelect: true,
              },
              insertNoteIndex: {
                enableMarker: true,
              },
              copyNoteLink: {},
            };

            expect(postMigrationDendronConfig.commands).toEqual(
              expectedCommandConfig
            );
          });
        }
      );

      describeMultiWS(
        "GIVEN v2 config (pre 0.65)",
        {
          ctx,
          modConfigCb: (config) => {
            // @ts-ignore
            config["version"] = 2;
            config["dendronVersion"] = "0.64.0";
            config["vaults"] = [
              {
                fsPath: "vault1",
              },
              {
                fsPath: "vault2",
              },
              {
                fsPath: "vault3",
                name: "vaultThree",
              },
            ];
            config["journal"] = {
              dailyDomain: "foo",
              name: "journal",
              dateFormat: "y.MM.dd",
              addBehavior: LegacyNoteAddBehavior.asOwnDomain,
              firstDayOfWeek: 1,
            };
            config["scratch"] = {
              name: "scratch",
              dateFormat: "y.MM.dd",
              addBehavior: LegacyNoteAddBehavior.asOwnDomain,
            };
            config["graph"] = {
              zoomSpeed: 10,
            };
            config["noTelemetry"] = true;
            config["noAutoCreateOnDefinition"] = true;
            config["noXVaultWikiLink"] = true;
            config["initializeRemoteVaults"] = true;
            config["workspaceVaultSync"] = DVaultSync.SKIP;
            config["autoFoldFrontmatter"] = true;
            config["maxPreviewsCached"] = 100;
            config["maxNoteLength"] = 3000000;
            config["feedback"] = true;
            config["apiEndpoint"] = "foobar.com";

            // @ts-ignore
            delete config["workspace"];

            return config;
          },
        },
        () => {
          DendronExtension.version = () => "0.64.0";
          test("workspace config correctly migrates to new namespace", async () => {
            const engine = ExtensionProvider.getEngine();
            const wsRoot = ExtensionProvider.getDWorkspace().wsRoot;
            const dendronConfig = DConfig.getRaw(
              wsRoot
            ) as IntermediateDendronConfig;
            const wsConfig =
              await ExtensionProvider.getExtension().getWorkspaceSettings();
            const wsService = new WorkspaceService({ wsRoot });

            const oldKeys = [
              "dendronVersion",
              "vaults",
              "journal",
              "scratch",
              "graph",
              "noTelemetry",
              "noAutoCreateOnDefinition",
              "noXVaultWikiLink",
              "initializeRemoteVaults",
              "workspaceVaultSync",
              "autoFoldFrontmatter",
              "maxPreviewsCached",
              "maxNoteLength",
              "feedback",
              "apiEndpoint",
            ];

            // @ts-ignore
            dendronConfig["version"] = 2;
            // @ts-ignore
            delete dendronConfig["workspace"];
            const originalDeepCopy = _.cloneDeep(dendronConfig);

            // all old configs should exist prior to migration
            const preMigrationCheckItems = [
              _.isUndefined(dendronConfig["workspace"]),
              oldKeys.every((value) => {
                return !_.isUndefined(_.get(dendronConfig, value));
              }),
            ];

            preMigrationCheckItems.forEach((item) => {
              expect(item).toBeTruthy();
            });

            await MigrationService.applyMigrationRules({
              currentVersion: "0.83.0",
              previousVersion: "0.64.0",
              dendronConfig,
              wsConfig,
              wsService,
              logger: Logger,
              migrations: [CONFIG_MIGRATIONS],
            });

            // backup of the original should exist.
            const allBackupFiles = fs.readdirSync(
              path.join(wsRoot, ".backup", "config"),
              {
                withFileTypes: true,
              }
            );
            const maybeBackupFileName = allBackupFiles
              .filter((ent) => ent.isFile())
              .filter((fileEnt) =>
                fileEnt.name.includes("migrate-config")
              )[0].name;
            expect(!_.isUndefined(maybeBackupFileName)).toBeTruthy();

            // backup content should be identical to original deep copy.
            const backupContent = readYAML(
              path.join(wsRoot, ".backup", "config", maybeBackupFileName)
            ) as IntermediateDendronConfig;

            // need to omit these because they are set by default during ws init.
            expect(
              _.isEqual(
                _.omit(backupContent, [
                  "version",
                  "publishing",
                  "preview",
                  "workspace",
                ]),
                _.omit(originalDeepCopy, ["version", "publishing", "preview"])
              )
            ).toBeTruthy();

            // post migration, workspace namespace should exist
            const postMigrationDendronConfig = (await engine.getConfig()).data!;
            const postMigrationKeys = Object.keys(postMigrationDendronConfig);
            expect(postMigrationKeys.includes("workspace")).toBeTruthy();
            // and all old keys should not exist
            expect(
              oldKeys.every((value) => postMigrationKeys.includes(value))
            ).toBeFalsy();
          });
        }
      );

      describeMultiWS(
        "GIVEN v3 config (pre 0.70)",
        {
          ctx,
          modConfigCb: (config) => {
            // @ts-ignore we don't have this version defined in the type
            config["version"] = 3;
            config["useFMTitle"] = false;
            config["useNoteTitleForLink"] = false;
            config["mermaid"] = false;
            config["usePrettyRefs"] = false;
            config["useKatex"] = false;
            // @ts-ignore
            delete config.preview;
            return config;
          },
        },
        () => {
          DendronExtension.version = () => "0.69.0";
          test("preview config correctly migrates to new namespace", async () => {
            const engine = ExtensionProvider.getEngine();
            const wsRoot = ExtensionProvider.getDWorkspace().wsRoot;
            const dendronConfig = DConfig.getRaw(
              wsRoot
            ) as IntermediateDendronConfig;
            const wsConfig =
              await ExtensionProvider.getExtension().getWorkspaceSettings();
            const wsService = new WorkspaceService({ wsRoot });

            const oldKeys = [
              "useFMTitle",
              "useNoteTitleForLink",
              "mermaid",
              "usePrettyRefs",
              "useKatex",
            ];

            // @ts-ignore
            dendronConfig["version"] = 3;
            // @ts-ignore
            delete dendronConfig["preview"];
            const originalDeepCopy = _.cloneDeep(dendronConfig);

            // all old configs should exist prior to migration
            const preMigrationCheckItems = [
              _.isUndefined(dendronConfig["preview"]),
              oldKeys.every((value) => {
                return !_.isUndefined(_.get(dendronConfig, value));
              }),
            ];

            preMigrationCheckItems.forEach((item) => {
              expect(item).toBeTruthy();
            });

            await MigrationService.applyMigrationRules({
              currentVersion: "0.83.0",
              previousVersion: "0.69.0",
              dendronConfig,
              wsConfig,
              wsService,
              logger: Logger,
              migrations: [CONFIG_MIGRATIONS],
            });

            // backup of the original should exist.
            const allBackupFiles = fs.readdirSync(
              path.join(wsRoot, ".backup", "config"),
              {
                withFileTypes: true,
              }
            );
            const maybeBackupFileName = allBackupFiles
              .filter((ent) => ent.isFile())
              .filter((fileEnt) =>
                fileEnt.name.includes("migrate-config")
              )[0].name;
            expect(!_.isUndefined(maybeBackupFileName)).toBeTruthy();

            // backup content should be identical to original deep copy.
            const backupContent = readYAML(
              path.join(wsRoot, ".backup", "config", maybeBackupFileName)
            ) as IntermediateDendronConfig;

            // need to omit these because they are set by default during ws init.
            expect(
              _.isEqual(
                _.omit(backupContent, ["version", "publishing", "preview"]),
                _.omit(originalDeepCopy, ["version", "publishing"])
              )
            ).toBeTruthy();

            // post migration, preview namespace should exist
            const postMigrationDendronConfig = (await engine.getConfig()).data!;
            const postMigrationKeys = Object.keys(postMigrationDendronConfig);
            expect(postMigrationKeys.includes("preview")).toBeTruthy();
            // and all old keys should not exist
            expect(
              oldKeys.every((value) => postMigrationKeys.includes(value))
            ).toBeFalsy();

            // and new preview namespace should be correctly mapped
            const expectedPreviewConfig: DendronPreviewConfig = {
              enableFMTitle: false,
              enableNoteTitleForLink: false,
              enableFrontmatterTags: true,
              enableHashesForFMTags: false,
              enableMermaid: false,
              enablePrettyRefs: false,
              enableKatex: false,
              automaticallyShowPreview: false,
            };

            expect(postMigrationDendronConfig.preview).toEqual(
              expectedPreviewConfig
            );
          });
        }
      );

      describeMultiWS(
        "GIVEN v4 config (pre 0.83)",
        {
          ctx,
          modConfigCb: (config) => {
            ConfigUtils.setProp(config, "useFMTitle", true);
            ConfigUtils.setProp(config, "hierarchyDisplay", true);
            ConfigUtils.setProp(config, "hierarchyDisplayTitle", "foo");
            ConfigUtils.setProp(config, "useNoteTitleForLink", true);
            ConfigUtils.setProp(config, "mermaid", true);
            ConfigUtils.setProp(config, "useKatex", true);
            ConfigUtils.setProp(config, "site", {
              usePrettyRefs: true,
              assetsPrefix: "bar",
              copyAssets: true,
              canonicalBaseUrl: "https://example.com",
              customHeaderPath: "header.html",
              ga_tracking: "1234567890",
              logo: "vault/assets/images/logo.png",
              siteFaviconPath: "vault/assets/images/favicon.ico",
              siteIndex: "dendron",
              siteHierarchies: ["dendron", "lorem", "ipsum"],
              siteLastModified: true,
              siteRootDir: "docs",
              siteRepoDir: "https://github.com/dendronhq/dendron-site",
              siteUrl: "https://foo.dev.dendron.so",
              showFrontMatterTags: true,
              useHashesForFMTags: true,
              noRandomlyColoredTags: true,
              config: {
                dendron: {
                  publishByDefault: true,
                },
                lorem: {
                  publishByDefault: {
                    public: true,
                    private: false,
                  },
                  noindexByDefault: true,
                  customFrontmatter: [
                    {
                      key: "foo",
                      value: 1,
                    },
                    {
                      key: "bar",
                      value: 2,
                    },
                  ],
                },
                ipsum: {
                  publishByDefault: false,
                },
              },
              duplicateNoteBehavior: {
                action: LegacyDuplicateNoteAction.USE_VAULT,
                payload: ["vault", "vault2"],
              },
              writeStubs: true,
              title: "Dendron",
              description: "test desc",
              author: "dendronites",
              twitter: "dendronhq",
              image: {
                url: "https://example.com/images/image.png",
                alt: "alt for image",
              },
              githubCname: "foo.dev.dendron.so",
              gh_edit_link: "true",
              gh_edit_link_text: "Edit this page",
              gh_edit_repository:
                "https://github.com/dendronhq/dendron-test-repo",
              gh_edit_branch: "main",
              gh_edit_view_mode: "edit",
              useContainers: true,
              generateChangelog: true,
              segmentKey: "abcdefg",
              cognitoUserPoolId: "qwerty",
              cognitoClientId: "azerty",
              usePrettyLinks: true,
            });
            ConfigUtils.unsetProp(config, "publishing");
            return config;
          },
        },
        () => {
          DendronExtension.version = () => "0.82.0";
          test("publishing config correctly migrates to new namespace", async () => {
            const engine = ExtensionProvider.getEngine();
            const wsRoot = ExtensionProvider.getDWorkspace().wsRoot;
            const dendronConfig = DConfig.getRaw(
              wsRoot
            ) as IntermediateDendronConfig;
            const wsConfig =
              await ExtensionProvider.getExtension().getWorkspaceSettings();
            const wsService = new WorkspaceService({ wsRoot });

            const oldKeys: string[] = [
              "site",
              "useFMTitle",
              "hierarchyDisplay",
              "hierarchyDisplayTitle",
              "useNoteTitleForLink",
              "mermaid",
              "site.usePrettyRefs",
              "useKatex",
              "site.assetsPrefix",
              "site.copyAssets",
              "site.canonicalBaseUrl",
              "site.customHeaderPath",
              "site.ga_tracking",
              "site.logo",
              "site.siteFaviconPath",
              "site.siteIndex",
              "site.siteHierarchies",
              "site.siteLastModified",
              "site.siteRootDir",
              "site.siteRepoDir",
              "site.siteUrl",
              "site.showFrontMatterTags",
              "site.useHashesForFMTags",
              "site.noRandomlyColoredTags",
              "site.config",
              "site.duplicateNoteBehavior",
              "site.writeStubs",
              "site.title",
              "site.description",
              "site.author",
              "site.twitter",
              "site.image",
              "site.githubCname",
              "site.gh_edit_link",
              "site.gh_edit_link_text",
              "site.gh_edit_branch",
              "site.gh_edit_repository",
              "site.useContainers",
              "site.generateChangelog",
              "site.segmentKey",
              "site.cognitoUserPoolId",
              "site.cognitoClientId",
              "site.usePrettyLinks",
            ];

            dendronConfig["version"] = 4;
            delete dendronConfig["publishing"];
            const originalDeepCopy = _.cloneDeep(dendronConfig);

            // all old configs should exist prior to migration
            const preMigrationCheckItems = [
              _.isUndefined(dendronConfig["publishing"]),
              oldKeys.every((value) => {
                return !_.isUndefined(_.get(dendronConfig, value));
              }),
            ];

            preMigrationCheckItems.forEach((item) => {
              expect(item).toBeTruthy();
            });

            await MigrationService.applyMigrationRules({
              currentVersion: "0.83.0",
              previousVersion: "0.82.0",
              dendronConfig,
              wsConfig,
              wsService,
              logger: Logger,
              migrations: [CONFIG_MIGRATIONS],
            });

            // backup of the original should exist.
            const allBackupFiles = fs.readdirSync(
              path.join(wsRoot, ".backup", "config"),
              {
                withFileTypes: true,
              }
            );
            const maybeBackupFileName = allBackupFiles
              .filter((ent) => ent.isFile())
              .filter((fileEnt) =>
                fileEnt.name.includes("migrate-config")
              )[0].name;
            expect(!_.isUndefined(maybeBackupFileName)).toBeTruthy();

            // backup content should be identical to original deep copy.
            const backupContent = readYAML(
              path.join(wsRoot, ".backup", "config", maybeBackupFileName)
            ) as IntermediateDendronConfig;

            // need to omit these because they are set by default during ws init.
            expect(
              _.isEqual(
                _.omit(backupContent, ["version", "publishing"]),
                _.omit(originalDeepCopy, ["version"])
              )
            ).toBeTruthy();

            // post migration, publishing namespace should exist
            const postMigrationDendronConfig = (await engine.getConfig()).data!;
            const postMigrationKeys = Object.keys(postMigrationDendronConfig);
            expect(postMigrationKeys.includes("publishing")).toBeTruthy();
            // and all old keys should not exist
            expect(
              oldKeys.every((value) => postMigrationKeys.includes(value))
            ).toBeFalsy();

            // and new publishing namespace should be correctly mapped from site config
            const expectedPublishingConfig: DendronPublishingConfig = {
              enableFMTitle: true,
              enableKatex: true,
              enableMermaid: true,
              enableNoteTitleForLink: true,
              hierarchyDisplayTitle: "foo",
              enableHierarchyDisplay: true,
              enablePrettyRefs: true,
              assetsPrefix: "bar",
              copyAssets: true,
              canonicalBaseUrl: "https://example.com",
              customHeaderPath: "header.html",
              ga: {
                tracking: "1234567890",
              },
              logoPath: "vault/assets/images/logo.png",
              siteFaviconPath: "vault/assets/images/favicon.ico",
              siteIndex: "dendron",
              siteHierarchies: ["dendron", "lorem", "ipsum"],
              enableSiteLastModified: true,
              siteRootDir: "docs",
              siteUrl: "https://foo.dev.dendron.so",
              enableFrontmatterTags: true,
              enableHashesForFMTags: true,
              enableRandomlyColoredTags: false,
              hierarchy: {
                dendron: {
                  publishByDefault: true,
                },
                lorem: {
                  publishByDefault: {
                    public: true,
                    private: false,
                  },
                  customFrontmatter: [
                    {
                      key: "foo",
                      value: 1,
                    },
                    {
                      key: "bar",
                      value: 2,
                    },
                  ],
                },
                ipsum: {
                  publishByDefault: false,
                },
              },
              duplicateNoteBehavior: {
                action: LegacyDuplicateNoteAction.USE_VAULT,
                payload: ["vault", "vault2"],
              },
              writeStubs: true,
              seo: {
                title: "Dendron",
                description: "test desc",
                author: "dendronites",
                twitter: "dendronhq",
                image: {
                  url: "https://example.com/images/image.png",
                  alt: "alt for image",
                },
              },
              github: {
                cname: "foo.dev.dendron.so",
                enableEditLink: true,
                editLinkText: "Edit this page",
                editRepository:
                  "https://github.com/dendronhq/dendron-test-repo",
                editBranch: "main",
                editViewMode: "edit",
              },
              segmentKey: "abcdefg",
              cognitoUserPoolId: "qwerty",
              cognitoClientId: "azerty",
              enablePrettyLinks: true,
              enableTaskNotes: true,
            };

            expect(postMigrationDendronConfig.publishing).toEqual(
              expectedPublishingConfig
            );
          });
        }
      );
    });
  });
});