mocha#before TypeScript Examples

The following examples show how to use mocha#before. 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: 1.arrange.ts    From attendance-backend with MIT License 6 votes vote down vote up
describe(' => Testing / route', () => {
  before((done) => {
    try {
      serverInstance.listen(3000)
      setTimeout(() => {
        connect(async () => {
          logger.info('Connecting to database to clear old records')
          Seed()
          db.collection('users').deleteMany({})
          logger.info('Cleared collection : users')
          db.collection('events').deleteMany({})
          logger.info('Cleared collection : events')
          db.collection('sessions').deleteMany({})
          logger.info('Cleared collection : sessions')
          done()
        })
      }, 3000)
    } catch (error) {
      done(error)
    }
  })

  //   testing connection
  it('should just pass', (done) => {
    done()
  })
})
Example #2
Source File: parseDocument.test.ts    From vscode-todo-md with MIT License 6 votes vote down vote up
describe(`${headerDelimiter('parse document')}`, () => {
	let tasks: TheTask[] = [];
	before(async () => {
		const parsed = await parseDocument(editor.document);
		tasks = parsed.tasksAsTree;
		return Promise.resolve(undefined);
	});
	it('13 Multiple links', () => {
		const links = tasks[12].links;
		assert.lengthOf(links, 2);
		assert.equal(links[0].value, 'https://www.google.com/');
		assert.equal(links[1].value, 'https://github.com/');
	});
	it('13 Multiple links ranges', () => {
		const links = tasks[12].links;
		assert.deepEqual(links[0].characterRange, [9, 31]);
		assert.deepEqual(links[1].characterRange, [37, 56]);
	});
	it('14 Special tag `{overdue}`', () => {
		assert.equal(tasks[13].due?.isDue, DueState.Overdue);
	});
	it('16,17,18,19 Nested tasks', () => {
		const at16 = tasks[15]!;
		const at17 = tasks[15]!.subtasks[0];
		assert.lengthOf(at16.subtasks, 2);
		assert.lengthOf(at17.subtasks, 1);

		assert.deepEqual(at16.subtasks.map(task => task.title), ['17 Nested 1lvl', '19 Nested 1lvl']);
		assert.deepEqual(at17.subtasks.map(task => task.title), ['18 Nested 2lvl']);
	});
});
Example #3
Source File: _setup-accounts.spec.ts    From mtcute with GNU Lesser General Public License v3.0 6 votes vote down vote up
before(async function () {
    this.timeout(60000)

    const [username1, session1] = await getTestAccount(1)
    const [username2, session2] = await getTestAccount(2)

    process.env.USER_USERNAME = username2
    process.env.USER_SESSION = session2

    process.env.USER_OTHER_DC_USERNAME = username1
    process.env.USER_OTHER_DC_SESSION = session1
})
Example #4
Source File: extension.test.ts    From intersystems-servermanager with MIT License 6 votes vote down vote up
suite("Extension Test Suite", () => {
  suiteSetup(async function () {
    // make sure extension is activated
    const ext = extensions.getExtension(extensionId);
    if (ext) {
      await ext.activate();
    } else {
      assert.fail("Extension not found");
    }
  });

  before(() => {
    window.showInformationMessage("Start all tests.");
  });

  test("Extension started", () => {
    assert.ok("All good");
  });

});
Example #5
Source File: colorization.test.ts    From myst-vs-code with MIT License 6 votes vote down vote up
suite("colorization", () => {
  before(() => {
    // ensure the extension is activated, so the grammar is injected
    commands.executeCommand("myst.Activate").then(
      (_data: any) => {},
      () => {}
    )
  })

  // We place the test files in this lower level FoldingRange, so that when this file is compiled to out/test/,
  // it still finds them
  const extensionColorizeFixturePath = join(
    __dirname,
    "../../../test_static/colorize-fixtures"
  )
  if (fs.existsSync(extensionColorizeFixturePath)) {
    // pause to allow extension to load?
    const fixturesFiles = fs.readdirSync(extensionColorizeFixturePath)
    fixturesFiles.forEach(fixturesFile => {
      // define a test for each fixture
      test(fixturesFile, done => {
        // eslint-disable-next-line @typescript-eslint/no-floating-promises
        assertUnchangedTokens(join(extensionColorizeFixturePath, fixturesFile), done)
      })
    })
  }
})
Example #6
Source File: ChangeWorkspace.test.ts    From dendron with GNU Affero General Public License v3.0 5 votes vote down vote up
// eslint-disable-next-line prefer-arrow-callback
suite("GIVEN ChangeWorkspace command", function () {
  describeMultiWS("WHEN command is gathering inputs", {}, () => {
    let showOpenDialog: sinon.SinonStub;

    beforeEach(async () => {
      const cmd = new ChangeWorkspaceCommand();
      showOpenDialog = sinon.stub(window, "showOpenDialog");
      await cmd.gatherInputs();
    });
    afterEach(() => {
      showOpenDialog.restore();
    });

    test("THEN file picker is opened", (done) => {
      expect(showOpenDialog.calledOnce).toBeTruthy();
      done();
    });
  });

  describeMultiWS("WHEN command is run", {}, (ctx) => {
    describe("AND a code workspace is selected", () => {
      let openWS: sinon.SinonStub;
      let newWSRoot: string;
      before(async () => {
        const { wsRoot: currentWSRoot } = getDWorkspace();
        openWS = sinon.stub(VSCodeUtils, "openWS").resolves();

        const out = await setupLegacyWorkspaceMulti({
          ctx,
          workspaceType: WorkspaceType.CODE,
        });
        newWSRoot = out.wsRoot;
        expect(newWSRoot).toNotEqual(currentWSRoot);

        const cmd = new ChangeWorkspaceCommand();
        sinon.stub(cmd, "gatherInputs").resolves({ rootDirRaw: newWSRoot });
        await cmd.run();
      });
      after(() => {
        openWS.restore();
      });

      test("THEN workspace is opened", (done) => {
        expect(openWS.calledOnce).toBeTruthy();
        expect(openWS.calledOnceWithExactly(newWSRoot));
        done();
      });
    });

    describe("AND a native workspace is selected", () => {
      let openWS: sinon.SinonStub;
      let newWSRoot: string;
      before(async () => {
        const { wsRoot: currentWSRoot } = getDWorkspace();
        openWS = sinon.stub(VSCodeUtils, "openWS").resolves();

        const out = await setupLegacyWorkspaceMulti({
          ctx,
          workspaceType: WorkspaceType.NATIVE,
        });
        newWSRoot = out.wsRoot;
        expect(newWSRoot).toNotEqual(currentWSRoot);

        const cmd = new ChangeWorkspaceCommand();
        sinon.stub(cmd, "gatherInputs").resolves({ rootDirRaw: newWSRoot });
        await cmd.run();
      });
      after(() => {
        openWS.restore();
      });

      test("THEN workspace is opened", (done) => {
        expect(openWS.calledOnce).toBeTruthy();
        expect(openWS.calledOnceWithExactly(newWSRoot));
        done();
      });
    });
  });
});
Example #7
Source File: providers.test.ts    From vscode-recall with MIT License 5 votes vote down vote up
suite('Embedded card provider', () => {

	before(async () => {
    // Get the tasks
    await Utils.embedded.initProvider ();
	});

  test('header cards should be correctly identified', async () => {
    const actual = await Utils.embedded.provider.getFileData(path.resolve(__dirname, '../../../demo/Cards Demo.md'));

    // console.log(JSON.stringify(actual.data, ignoreReverseFor, 2));
    assert.strictEqual(actual.data.length, expectedCardsHeader.length);

    actual.data.forEach((card, i) => {
      // console.log('Matching', t, 'to', i, expected[i]);
      sinon.assert.match(card, expectedCardsHeader[i]);
    });
  });

  test('asterisk cards should be correctly identified', async () => {
    const actual = await Utils.embedded.provider.getFileData(path.resolve(__dirname, '../../../demo/Asterisk Demo.md'));

    // console.log(JSON.stringify(actual.data, ignoreReverseFor, 2));
    assert.strictEqual(actual.data.length, expectedCardsAsterisk.length);

    actual.data.forEach((card, i) => {
      // console.log('Matching', t, 'to', i, expected[i]);
      sinon.assert.match(card, expectedCardsAsterisk[i]);
    });
  });

  test('bullet cards should be correctly identified', async () => {
    const actual = await Utils.embedded.provider.getFileData(path.resolve(__dirname, '../../../demo/Bullets Demo.md'));

    // console.log(JSON.stringify(actual.data, ignoreReverseFor, 2));
    assert.strictEqual(actual.data.length, expectedCardsBullet.length);

    actual.data.forEach((card, i) => {
      // console.log('Matching', t, 'to', i, expected[i]);
      sinon.assert.match(card, expectedCardsBullet[i]);
    });
  });

  test('reverse cards should be correctly identified', async () => {
    const actual = await Utils.embedded.provider.getFileData(path.resolve(__dirname, '../../../demo/Reverse Demo.md'));

    // console.log(JSON.stringify(actual.data, ignoreReverseFor, 2));
    assert.strictEqual(actual.data.length, expectedCardsReverse.length);

    actual.data.forEach((card, i) => {
      // console.log('Matching', t, 'to', i, expected[i]);
      sinon.assert.match(card, expectedCardsReverse[i]);
    });
  });

});
Example #8
Source File: extension.test.ts    From vscode-recall with MIT License 5 votes vote down vote up
// import * as myExtension from '../extension';

suite('Extension Test Suite', () => {
	before(() => {
		vscode.window.showInformationMessage('Start all tests.');
	});
});
Example #9
Source File: testUtilsV3.ts    From dendron with GNU Affero General Public License v3.0 5 votes vote down vote up
/**
 * Use to run tests with a single-vault workspace. Used in the same way as
 * regular `describe`.
 * @param title
 * @param opts
 * @param fn - the test() functions to execute. NOTE: This function CANNOT be
 * async, or else the test may not fail reliably when your expect or assert
 * conditions are not met.
 */
export function describeSingleWS(
  title: string,
  opts: SetupLegacyWorkspaceOpts & {
    /**
     * Custom timeout for test in milleseconds
     * You will need to set this when stepping through mocha tests using breakpoints
     * otherwise the test will timeout during debugging
     * See [[Breakpoints|dendron://dendron.docs/pkg.plugin-core.qa.debug#breakpoints]] for more details
     */
    timeout?: number;
  },
  fn: (ctx: ExtensionContext) => void
) {
  describe(title, function () {
    if (opts.timeout) {
      this.timeout(opts.timeout);
    }
    const ctx = opts.ctx ?? VSCodeUtils.getOrCreateMockContext();
    before(async () => {
      setupWorkspaceStubs({ ...opts, ctx });
      await setupLegacyWorkspace(opts);
      await _activate(ctx);
    });

    const result = fn(ctx);
    assertTestFnNotAsync(result);

    // Release all registered resouces such as commands and providers
    after(() => {
      cleanupWorkspaceStubs(ctx);
    });
  });
}
Example #10
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 #11
Source File: WindowDecorations.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("GIVEN a text document with decorations", function () {
  const CREATED = "1625648278263";
  const UPDATED = "1625758878263";
  const FNAME = "bar";

  describe("AND GIVEN links ", () => {
    function checkTimestampsDecorated({
      decorations,
      document,
    }: {
      decorations: Awaited<ReturnType<typeof updateDecorations>>;
      document: vscode.TextDocument;
    }) {
      const { allDecorations } = decorations;
      const timestampDecorations = getDecorations({
        allDecorations,
        decorationType: EDITOR_DECORATION_TYPES.timestamp,
      });
      expect(timestampDecorations!.length).toEqual(2);
      // check that the decorations are at the right locations
      checkDecoration({
        text: CREATED,
        decorations: timestampDecorations,
        document,
      });
      checkDecoration({
        text: UPDATED,
        decorations: timestampDecorations,
        document,
      });
    }

    describeMultiWS(
      "",
      {
        preSetupHook: async ({ wsRoot, vaults }) => {
          await NoteTestUtilsV4.createNote({
            fname: "withHeader",
            vault: vaults[0],
            wsRoot,
            body: "## ipsam adipisci",
          });
          await NoteTestUtilsV4.createNote({
            fname: "tags.bar",
            vault: vaults[0],
            wsRoot,
          });
          await NoteTestUtilsV4.createNote({
            fname: FNAME,
            body: [
              "Ut incidunt id commodi. ^anchor-1",
              "",
              "* Et repudiandae optio ut suscipit illum hic vel.",
              "* Aut suscipit veniam nobis veniam reiciendis. ^anchor-2",
              "  * Sit sed accusamus saepe voluptatem sint animi quis animi. ^anchor-3",
              "* Dolores suscipit maiores nulla accusamus est.",
              "",
              "#foo",
              "#bar",
              "#foo",
              "[[root]]",
              "",
              "@Hamilton.Margaret",
              "",
              "[[with alias|root]]",
              "",
              "![[root.*#head]]",
              "",
              "[[withHeader#ipsam-adipisci]]",
              "[[withHeader#does-not-exist]]",
              "",
              "[[does.not.exist]]",
              "",
              "[[/test.txt]]",
              "[[/test.txt#L3]]",
            ].join("\n"),
            props: {
              created: _.toInteger(CREATED),
              updated: _.toInteger(UPDATED),
              tags: ["foo", "bar"],
            },
            vault: vaults[0],
            wsRoot,
          });
          await writeFile(
            path.join(wsRoot, "test.txt"),
            "et\nnam\nvelit\nlaboriosam\n"
          );
        },
      },
      () => {
        // TODO: this is currently a regression from refactoring engine
        test.skip("THEN links are decorated", async () => {
          const { editor } = await getNote({ fname: FNAME });
          const document = editor.document;
          const decorations = (await updateDecorations(editor))!;
          const { allDecorations } = decorations;

          checkTimestampsDecorated({ decorations, document });

          const blockAnchorDecorations = allDecorations!.get(
            EDITOR_DECORATION_TYPES.blockAnchor
          );
          expect(blockAnchorDecorations!.length).toEqual(3);
          // check that the decorations are at the right locations
          expect(
            isTextDecorated("^anchor-1", blockAnchorDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated("^anchor-2", blockAnchorDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated("^anchor-3", blockAnchorDecorations!, document)
          ).toBeTruthy();

          const wikilinkDecorations = getDecorations({
            allDecorations,
            decorationType: EDITOR_DECORATION_TYPES.wikiLink,
          });
          expect(wikilinkDecorations!.length).toEqual(8);
          expect(
            isTextDecorated("[[root]]", wikilinkDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated(
              "[[with alias|root]]",
              wikilinkDecorations!,
              document
            )
          ).toBeTruthy();
          expect(
            isTextDecorated("#bar", wikilinkDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated("![[root.*#head]]", wikilinkDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated(
              "[[withHeader#ipsam-adipisci]]",
              wikilinkDecorations!,
              document
            )
          ).toBeTruthy();
          expect(
            isTextDecorated("[[/test.txt]]", wikilinkDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated("[[/test.txt#L3]]", wikilinkDecorations!, document)
          ).toBeTruthy();

          const aliasDecorations = allDecorations!.get(
            EDITOR_DECORATION_TYPES.alias
          );
          expect(aliasDecorations!.length).toEqual(1);
          expect(
            isTextDecorated("with alias", aliasDecorations!, document)
          ).toBeTruthy();

          const brokenWikilinkDecorations = allDecorations!.get(
            EDITOR_DECORATION_TYPES.brokenWikilink
          );
          expect(brokenWikilinkDecorations!.length).toEqual(5);
          expect(
            isTextDecorated(
              "[[does.not.exist]]",
              brokenWikilinkDecorations!,
              document
            )
          ).toBeTruthy();
          expect(
            isTextDecorated(
              "[[withHeader#does-not-exist]]",
              brokenWikilinkDecorations!,
              document
            )
          ).toBeTruthy();
          expect(
            isTextDecorated(
              "@Hamilton.Margaret",
              brokenWikilinkDecorations!,
              document
            )
          ).toBeTruthy();
          expect(
            isTextDecorated("#foo", brokenWikilinkDecorations!, document)
          ).toBeTruthy();
          return;
        });
      }
    );
  });

  describe("AND GIVEN task notes", () => {
    describeMultiWS(
      "",
      {
        preSetupHook: async ({ vaults, wsRoot }) => {
          await NoteTestUtilsV4.createNote({
            fname: "with.all",
            vault: vaults[0],
            wsRoot,
            custom: {
              status: "done",
              owner: "grace",
              priority: "H",
              due: "2021.10.29",
              tags: "foo",
            },
          });
          await NoteTestUtilsV4.createNote({
            fname: "without.status",
            vault: vaults[0],
            wsRoot,
            custom: {
              owner: "grace",
              priority: "high",
              tags: ["foo", "bar"],
            },
          });
          await NoteTestUtilsV4.createNote({
            fname: "without.due",
            vault: vaults[0],
            wsRoot,
            custom: {
              status: "",
              priority: "low",
            },
          });
          await NoteTestUtilsV4.createNote({
            fname: "not.a.task",
            vault: vaults[0],
            wsRoot,
          });
          await NoteTestUtilsV4.createNote({
            fname: FNAME,
            body: [
              "* [[with.all]]",
              "* foo [[without.status]] bar",
              "",
              "[[without.due]]",
              "",
              "[[not.a.task]]",
              "",
            ].join("\n"),
            vault: vaults[0],
            wsRoot,
          });
        },
      },
      () => {
        test("THEN task notes are highlighted", async () => {
          const { editor } = await getNote({ fname: FNAME });
          const document = editor.document;
          const { allDecorations } = (await updateDecorations(editor))!;

          const taskDecorations = allDecorations!.get(
            EDITOR_DECORATION_TYPES.taskNote
          );
          taskDecorations?.sort((decoration) => decoration.range.start.line); // for easier testing
          expect(taskDecorations!.length).toEqual(3);
          // check that the decorations are at the right locations
          expect(
            isTextDecorated("[[with.all]]", taskDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated("[[without.status]]", taskDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated("[[without.due]]", taskDecorations!, document)
          ).toBeTruthy();

          expect(
            taskDecorations![0].renderOptions?.before?.contentText
          ).toEqual("[ ] ");
          expect(taskDecorations![0].renderOptions?.after?.contentText).toEqual(
            " priority:low"
          );
          expect(
            taskDecorations![1].renderOptions?.before?.contentText
          ).toEqual("[x] ");
          expect(taskDecorations![1].renderOptions?.after?.contentText).toEqual(
            " due:2021.10.29 @grace priority:high #foo"
          );
          expect(
            taskDecorations![2].renderOptions?.before?.contentText
          ).toBeFalsy();
          expect(taskDecorations![2].renderOptions?.after?.contentText).toEqual(
            " @grace priority:high #foo #bar"
          );
        });
      }
    );
  });

  describe("AND GIVEN file with wikilinks to itself", () => {
    describeMultiWS(
      "",
      {
        preSetupHook: async ({ vaults, wsRoot }) => {
          await NoteTestUtilsV4.createNote({
            fname: FNAME,
            body: [
              "Ut incidunt id commodi. ^anchor-1",
              "",
              "[[#^anchor-1]]",
              "[[#^anchor-not-exists]]",
              "![[#^anchor-1]]",
              "![[#^anchor-not-exists]]",
              "![[#^anchor-1:#*]]",
              "![[#^anchor-not-exists]]",
            ].join("\n"),
            vault: vaults[0],
            wsRoot,
          });
        },
      },
      () => {
        test("THEN links are highlighted", async () => {
          const { editor } = await getNote({ fname: FNAME });
          const document = editor.document;
          const { allDecorations } = (await updateDecorations(editor))!;

          const wikilinkDecorations = allDecorations!.get(
            EDITOR_DECORATION_TYPES.wikiLink
          );
          expect(wikilinkDecorations!.length).toEqual(3);
          expect(
            isTextDecorated("[[#^anchor-1]]", wikilinkDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated("![[#^anchor-1]]", wikilinkDecorations!, document)
          ).toBeTruthy();
          expect(
            isTextDecorated(
              "![[#^anchor-1:#*]]",
              wikilinkDecorations!,
              document
            )
          ).toBeTruthy();

          const brokenWikilinkDecorations = allDecorations!.get(
            EDITOR_DECORATION_TYPES.brokenWikilink
          );
          expect(brokenWikilinkDecorations!.length).toEqual(3);
          expect(
            isTextDecorated(
              "[[#^anchor-not-exists]]",
              brokenWikilinkDecorations!,
              document
            )
          ).toBeTruthy();
          expect(
            isTextDecorated(
              "![[#^anchor-not-exists]]",
              brokenWikilinkDecorations!,
              document
            )
          ).toBeTruthy();
          expect(
            isTextDecorated(
              "![[#^anchor-not-exists]]",
              brokenWikilinkDecorations!,
              document
            )
          ).toBeTruthy();
        });
      }
    );
  });

  describe("AND given wildcard references", () => {
    describeMultiWS(
      "",
      {
        preSetupHook: async ({ vaults, wsRoot }) => {
          await NoteTestUtilsV4.createNote({
            fname: FNAME,
            body: ["![[foo.bar.*]]"].join("\n"),
            vault: vaults[0],
            wsRoot,
          });
        },
      },
      () => {
        test("THEN links are highlighted", async () => {
          const { editor } = await getNote({ fname: FNAME });
          const document = editor.document;
          const { allDecorations } = (await updateDecorations(editor))!;

          const wikilinkDecorations = allDecorations!.get(
            EDITOR_DECORATION_TYPES.wikiLink
          );
          expect(wikilinkDecorations!.length).toEqual(1);
          expect(
            isTextDecorated("![[foo.bar.*]]", wikilinkDecorations!, document)
          ).toBeTruthy();
        });
      }
    );
  });

  describe("AND for long notes", () => {
    const FNAME = "test.note";
    const repeat = 228;
    describeMultiWS(
      "",
      {
        preSetupHook: async ({ vaults, wsRoot }) => {
          await NoteTestUtilsV4.createNote({
            fname: FNAME,
            body: _.repeat("[[does.not.exist]] #does.not.exist\n", repeat),
            vault: vaults[0],
            wsRoot,
          });
        },
      },
      () => {
        test("THEN only the visible range should be decorated", async () => {
          const { editor } = await getNote({ fname: FNAME });
          const document = editor.document;

          const { allDecorations } = (await updateDecorations(editor))!;

          // This note is really long, so not all links in it will be decorated (there are repeat * 2 many links)
          const brokenWikilinkDecorations = allDecorations!.get(
            EDITOR_DECORATION_TYPES.brokenWikilink
          );
          expect(brokenWikilinkDecorations!.length < repeat * 2).toBeTruthy();
          expect(
            isTextDecorated(
              "[[does.not.exist]]",
              brokenWikilinkDecorations!,
              document
            )
          ).toBeTruthy();
          expect(
            isTextDecorated(
              "#does.not.exist",
              brokenWikilinkDecorations!,
              document
            )
          ).toBeTruthy();
        });
      }
    );
  });

  describe("AND WHEN disabled", () => {
    describeMultiWS(
      "",
      {
        modConfigCb: (config) => {
          config.workspace!.enableEditorDecorations = false;
          return config;
        },
        preSetupHook: async ({ vaults, wsRoot }) => {
          await NoteTestUtilsV4.createNote({
            fname: FNAME,
            body: "[[does.not.exist]] #does.not.exist\n",
            vault: vaults[0],
            wsRoot,
          });
        },
      },
      () => {
        test("THEN decorations are not displayed ", async () => {
          const { editor } = await getNote({ fname: FNAME });

          const { allDecorations, allWarnings } = (await updateDecorations(
            editor
          ))!;

          expect(allDecorations).toBeFalsy();
          expect(allWarnings).toBeFalsy();
        });
      }
    );
  });

  describe("AND GIVEN warnings in document", () => {
    describeMultiWS(
      "AND WHEN missing frontmatter",
      {
        preSetupHook: async ({ vaults, wsRoot }) => {
          const note = await NoteTestUtilsV4.createNote({
            fname: FNAME,
            vault: vaults[0],
            wsRoot,
          });
          // Empty out the note, getting rid of the frontmatter
          const path = NoteUtils.getFullPath({ note, wsRoot });
          await writeFile(path, "foo bar");
        },
      },
      () => {
        test("THEN show frontmatter missing warning", async () => {
          const { editor } = await getNote({ fname: FNAME });
          const { allWarnings } = (await updateDecorations(editor))!;

          expect(allWarnings!.length).toEqual(1);
          expect(
            AssertUtils.assertInString({
              body: allWarnings![0].message,
              match: ["frontmatter", "missing"],
            })
          );
        });
      }
    );

    describeMultiWS(
      "AND WHEN bad note id",
      {
        preSetupHook: async ({ vaults, wsRoot }) => {
          await NoteTestUtilsV4.createNote({
            fname: FNAME,
            vault: vaults[0],
            wsRoot,
            props: {
              id: "-foo",
            },
          });
        },
      },
      () => {
        test("THEN show frontmatter missing warning", async () => {
          const { editor } = await getNote({ fname: FNAME });
          const { allWarnings } = (await updateDecorations(editor))!;
          expect(allWarnings!.length).toEqual(1);
          expect(
            AssertUtils.assertInString({
              body: allWarnings![0].message,
              match: ["id", "bad"],
            })
          );
        });
      }
    );

    describeMultiWS(
      "AND WHEN note id is missing",
      {
        preSetupHook: async ({ vaults, wsRoot }) => {
          const note = await NoteTestUtilsV4.createNote({
            fname: FNAME,
            vault: vaults[0],
            wsRoot,
          });
          // Rewrite the file to have id missing in frontmatter
          const path = NoteUtils.getFullPath({ note, wsRoot });
          await writeFile(
            path,
            ["---", "updated: 234", "created: 123", "---"].join("\n")
          );
        },
      },
      () => {
        test("THEN show frontmatter missing warning", async () => {
          const { editor } = await getNote({ fname: FNAME });
          const { allWarnings } = (await updateDecorations(editor))!;
          expect(allWarnings!.length).toEqual(1);
          expect(
            AssertUtils.assertInString({
              body: allWarnings![0].message,
              match: ["id", "missing"],
            })
          );
        });
      }
    );

    describeMultiWS("AND frontmatter is not visible", {}, () => {
      before(async () => {
        const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
        const note = await NoteTestUtilsV4.createNoteWithEngine({
          fname: "foo",
          vault: vaults[0],
          wsRoot,
          engine,
        });
        // Rewrite the file to have id missing in frontmatter
        const path = NoteUtils.getFullPath({ note, wsRoot });
        await writeFile(
          path,
          ["---", "updated: 234", "created: 123", "---"]
            .join("\n")
            .concat("\n".repeat(200))
        );

        const editor = await WSUtils.openNote(note);
        editor.revealRange(new vscode.Range(200, 0, 200, 0));
      });

      test("THEN still warns for frontmatter issues", async () => {
        const { allWarnings } = (await updateDecorations(
          VSCodeUtils.getActiveTextEditorOrThrow()
        ))!;
        expect(allWarnings!.length).toEqual(1);
        expect(
          AssertUtils.assertInString({
            body: allWarnings![0].message,
            match: ["id", "missing"],
          })
        );
      });

      runTestButSkipForWindows()("", () => {
        test("THEN don't warn for schemas", async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          const engine = ExtensionProvider.getEngine();
          const schema = engine.schemas.root;
          const schemaFile = path.join(
            wsRoot,
            schema.vault.fsPath,
            `${schema.fname}.schema.yml`
          );
          const schemaURI = vscode.Uri.parse(schemaFile);
          const editor = await VSCodeUtils.openFileInEditor(schemaURI);

          const { allDecorations, allWarnings } = (await updateDecorations(
            editor!
          ))!;

          expect(allWarnings).toEqual(undefined);
          expect(allDecorations).toEqual(undefined);
        });
      });
    });
  });
});
Example #12
Source File: currencyPlugin.spec.ts    From edge-currency-plugins with MIT License 4 votes vote down vote up
describe('currencyPlugins.spec', () => {
  for (const fixture of fixtures) {
    const WALLET_TYPE = fixture.WALLET_TYPE
    const WALLET_FORMAT = fixture.WALLET_FORMAT
    const keyName = WALLET_TYPE.split('wallet:')[1].split('-')[0] + 'Key'

    let keys: JsonObject
    let tools: EdgeCurrencyTools

    const fakeIo = makeFakeIo()
    const pluginOpts: EdgeCorePluginOptions = {
      initOptions: {},
      io: {
        ...fakeIo,
        random: () => Uint8Array.from(fixture.key)
      },
      log: testLog,
      nativeIo: {},
      pluginDisklet: fakeIo.disklet
    }
    const factory = edgeCorePlugins[fixture.pluginId]
    if (typeof factory !== 'function') {
      // Skip this test if the plugin is not available
      break
      // throw new TypeError('Bad plugin')
    }
    const corePlugin: EdgeCorePlugin = factory(pluginOpts)
    const plugin = corePlugin as EdgeCurrencyPlugin

    describe(`Info for Wallet type ${WALLET_TYPE}`, function () {
      before('Plugin', async function () {
        assert.equal(
          plugin.currencyInfo.currencyCode,
          fixture['Test Currency code']
        )
        tools = await plugin.makeCurrencyTools()
      })

      it('Test Currency code', function () {
        assert.equal(
          plugin.currencyInfo.currencyCode,
          fixture['Test Currency code']
        )
      })
    })

    describe(`createPrivateKey for Wallet type ${WALLET_TYPE}`, function () {
      it('Test Currency code', function () {
        assert.equal(
          plugin.currencyInfo.currencyCode,
          fixture['Test Currency code']
        )
      })

      it('Create valid key', async function () {
        keys = await tools.createPrivateKey(WALLET_TYPE)
        assert.isOk(keys)
        assert.equal(typeof keys[keyName], 'string')
        const length = keys[keyName].split(' ').length
        assert.equal(length, 24)
      })
    })

    describe(`importPrivateKey for Wallet type ${WALLET_TYPE}`, function () {
      it('Import valid keys', async function () {
        // assert that the function is implemented
        if (tools.importPrivateKey == null) return

        for (const key of fixture.importKey.validKeys) {
          const keys = await tools.importPrivateKey(key)
          assert.isOk(keys)
          assert.equal(keys[keyName], key)
        }
      })
      it('Import invalid keys', async function () {
        // assert that the function is implemented
        if (tools.importPrivateKey == null) return

        for (const key of fixture.importKey.invalidKeys) {
          // Error with a message containing the word 'invalid'
          const promise = tools.importPrivateKey(key)
          await assert.isRejected(promise)
          await promise.catch(err => assert.match(err.message, /invalid/i))
        }
      })
      it('Import unsupported keys', async function () {
        // assert that the function is implemented
        if (tools.importPrivateKey == null) return

        for (const key of fixture.importKey.unsupportedKeys) {
          // Error with a message containing the word 'unsupported'
          const promise = tools.importPrivateKey(key)
          await assert.isRejected(promise)
          await promise.catch(err => assert.match(err.message, /unsupported/i))
        }
      })
    })

    describe(`derivePublicKey for Wallet type ${WALLET_TYPE}`, function () {
      this.timeout(10000)
      it('Valid private key', async function () {
        await tools
          .derivePublicKey({
            type: WALLET_TYPE,
            keys: {
              [keyName]: keys[keyName],
              format: WALLET_FORMAT
            },
            id: '!'
          })
          .then(keys => {
            assert.deepEqual(keys.publicKeys[WALLET_FORMAT], fixture.xpub)
          })
      })

      it('Invalid key name', async function () {
        await tools.derivePublicKey(fixture['invalid key name']).catch(e => {
          assert.equal(e.message, 'Either a public or private key are required')
        })
      })

      it('Invalid wallet type', async function () {
        await tools.derivePublicKey(fixture['invalid wallet type']).catch(e => {
          assert.equal(e.message, 'Either a public or private key are required')
        })
      })
    })

    describe(`parseUri for Wallet type ${WALLET_TYPE}`, function () {
      Object.entries(fixture.parseUri).forEach(([test, [input, output]]) => {
        if (output != null) {
          it(test, async function () {
            const parsedUri = await tools.parseUri(input)
            assert.deepEqual(parsedUri, output)
          })
        } else {
          it(test, async function () {
            await assert.isRejected(tools.parseUri(input))
          })
        }
      })
    })

    describe(`encodeUri for Wallet type ${WALLET_TYPE}`, function () {
      Object.entries(fixture.encodeUri).forEach(([test, [input, output]]) => {
        if (output != null) {
          it(test, async function () {
            const encodedUri = await tools.encodeUri(input)
            assert.equal(encodedUri, output)
          })
        } else {
          it(test, async function () {
            await assert.isRejected(tools.encodeUri(input))
          })
        }
      })
    })

    describe(`getSplittableTypes for Wallet type ${WALLET_TYPE}`, function () {
      const getSplittableTypes = fixture.getSplittableTypes

      if (getSplittableTypes == null) return

      Object.keys(getSplittableTypes).forEach(format => {
        it(`Test for the wallet type ${format}`, function () {
          if (tools.getSplittableTypes == null) {
            throw new Error('No getSplittableTypes')
          }
          const walletTypes = tools.getSplittableTypes({
            type: WALLET_TYPE,
            keys: { format },
            id: '!'
          })
          assert.deepEqual(walletTypes, getSplittableTypes[format])
        })
      })
    })
  }
})
Example #13
Source File: integration.spec.ts    From programmer-fa with MIT License 4 votes vote down vote up
describe('Integration Tests', () => {
  const BASE_DATA_DIR = `${__dirname}/../../src/data`;
  const WORDS_TO_FOLLOW_FILE_PATH = 'words-to-follow.json';
  const WORDS_NOT_TO_FOLLOW_FILE_PATH = 'words-not-to-follow.json';

  let tweets: any[];

  before(async () => {
    await supertest(app)
      .get('/tweets')
      .expect(200)
      .then(async (response) => {
        tweets = JSON.parse(JSON.parse(response.text).tweets);
      });
  });

  it('should return an array including "words to follow", both in plain form and in hashtag form', (done) => {
    const wordsToFollow = loadJSONFileContent(`${BASE_DATA_DIR}/${WORDS_TO_FOLLOW_FILE_PATH}`) as string[];
    let interestingWords: string[] = [];

    interestingWords = fillArrayWithWords(
      interestingWords,
      wordsToFollow as string[],
    );

    const allExpectedValuesAreInInterestedWordsArray = wordsToFollow.every((word: string) => (
      interestingWords.includes(word) && interestingWords.includes(makeHashtag(word))
    ));

    expect(allExpectedValuesAreInInterestedWordsArray).to.be.true;

    done();
  });

  it('should return an array including "words not to follow", both in plain form and in hashtag form', (done) => {
    const wordsNotToFollow = loadJSONFileContent(`${BASE_DATA_DIR}/${WORDS_NOT_TO_FOLLOW_FILE_PATH}`) as string[];
    let blacklistedWords: string[] = [];

    blacklistedWords = fillArrayWithWords(
      blacklistedWords,
      wordsNotToFollow as string[],
    );

    const allExpectedValuesAreInBlackListedWordsArray = wordsNotToFollow.every((word: string) => (
      blacklistedWords.includes(word) && blacklistedWords.includes(makeHashtag(word))
    ));

    expect(allExpectedValuesAreInBlackListedWordsArray).to.be.true;

    done();
  });

  it('should properly count number of hashtags', (done) => {
    const hashtagsGetsCountedProperly = tweets.every(
      // TODO: Make a type for this
      (tweet: { text: string; numberOfHashtags: number }) => (
        tweet.numberOfHashtags
        === getNumberOfHashtags(
          removeRetweetNotation(getTweetFullText(tweet)),
        )
      ),
    );

    expect(hashtagsGetsCountedProperly).to.be.true;

    done();
  });

  it('should return the `id` of valid tweets or "0" for invalid tweets', (done) => {
    tweets.forEach(
      async (tweet: any) => {
        if (tweet.tweetIsValid) {
          expect(await onTweet(tweet))
            .to.be.not.equal('0');
        } else {
          expect(await onTweet(tweet))
            .to.be.equal('0');
        }
      },
    );

    done();
  });
});
Example #14
Source File: CompletionProvider.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("completionProvider", function () {
  describeMultiWS(
    "wikilink",
    {
      preSetupHook: async (opts) => {
        const { wsRoot, vaults } = opts;
        await NoteTestUtilsV4.createNote({
          fname: "test",
          vault: vaults[1],
          wsRoot,
        });
        await ENGINE_HOOKS.setupBasic(opts);
      },
    },
    () => {
      test("THEN provide completions", async () => {
        const { wsRoot, engine, vaults } = ExtensionProvider.getDWorkspace();
        // Open a note, add [[]]
        await WSUtils.openNote(
          NoteUtils.getNoteOrThrow({
            fname: "root",
            vault: vaults[1],
            wsRoot,
            notes: engine.notes,
          })
        );
        const editor = VSCodeUtils.getActiveTextEditorOrThrow();
        await editor.edit((editBuilder) => {
          editBuilder.insert(new Position(8, 0), "[[]]");
        });
        // have the completion provider complete this wikilink
        const items = provideCompletionItems(
          editor.document,
          new Position(8, 2)
        );
        expect(items).toBeTruthy();
        // Suggested all the notes
        expect(items!.length).toEqual(7);
        for (const item of items!) {
          // All suggested items exist
          const found = NoteUtils.getNotesByFnameFromEngine({
            fname: item.label as string,
            engine,
          });
          expect(found.length > 0).toBeTruthy();
        }
        // check that same vault items are sorted before other items
        const sortedItems = _.sortBy(
          items,
          (item) => item.sortText || item.label
        );
        const testIndex = _.findIndex(
          sortedItems,
          (item) => item.label === "test"
        );
        expect(testIndex !== -1 && testIndex < 2).toBeTruthy();
        // Check that xvault links were generated where needed, and only where needed.
        // Using root notes since they are in every vault.
        const rootItems = _.filter(items, (item) => item.label === "root");
        for (const item of rootItems) {
          if (item.detail === VaultUtils.getName(vaults[1])) {
            // don't need an xvault link, should be a regular one
            expect(item.insertText).toEqual(item.label);
            expect(
              (item.insertText as string).startsWith(
                CONSTANTS.DENDRON_DELIMETER
              )
            ).toBeFalsy();
          } else {
            // does need an xvault link
            expect(
              (item.insertText as string).startsWith(
                CONSTANTS.DENDRON_DELIMETER
              )
            ).toBeTruthy();
          }
        }
      });
    }
  );

  describeMultiWS(
    "GIVEN hashtag",
    {
      preSetupHook: async (opts) => {
        const { wsRoot, vaults } = opts;
        await NoteTestUtilsV4.createNote({
          fname: "tags.foo",
          vault: vaults[1],
          wsRoot,
        });
        await NoteTestUtilsV4.createNote({
          fname: "tags.bar",
          vault: vaults[1],
          wsRoot,
        });
      },
    },
    () => {
      test("THEN provide correct completion", async () => {
        const { wsRoot, engine, vaults } = ExtensionProvider.getDWorkspace();
        // Open a note, add [[]]
        await WSUtils.openNote(
          NoteUtils.getNoteOrThrow({
            fname: "root",
            vault: vaults[1],
            wsRoot,
            notes: engine.notes,
          })
        );
        const editor = VSCodeUtils.getActiveTextEditorOrThrow();
        await editor.edit((editBuilder) => {
          editBuilder.insert(new Position(8, 0), "#");
        });
        // have the completion provider complete this wikilink
        const items = provideCompletionItems(
          editor.document,
          new Position(8, 1)
        );
        expect(items).toBeTruthy();
        // Suggested all the notes
        expect(items!.length).toEqual(2);
        for (const item of items!) {
          // All suggested items exist
          const found = NoteUtils.getNotesByFnameFromEngine({
            fname: `${TAGS_HIERARCHY}${item.label}`,
            engine,
          });
          expect(found.length > 0).toBeTruthy();
        }
      });
    }
  );

  describeMultiWS(
    "GIVEN hashtag that's in a sentence",
    {
      preSetupHook: async (opts) => {
        const { wsRoot, vaults } = opts;
        await NoteTestUtilsV4.createNote({
          fname: "tags.foo",
          vault: vaults[1],
          wsRoot,
        });
        await NoteTestUtilsV4.createNote({
          fname: "tags.bar",
          vault: vaults[1],
          wsRoot,
        });
      },
    },
    () => {
      test("THEN provide correct completions", async () => {
        const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
        // Open a note, add [[]]
        await WSUtils.openNote(
          NoteUtils.getNoteOrThrow({
            fname: "root",
            vault: vaults[1],
            wsRoot,
            notes: engine.notes,
          })
        );
        const editor = VSCodeUtils.getActiveTextEditorOrThrow();
        await editor.edit((editBuilder) => {
          editBuilder.insert(new Position(8, 0), "Lorem ipsum #");
        });
        // have the completion provider complete this wikilink
        const items = provideCompletionItems(
          editor.document,
          new Position(8, 13)
        );
        expect(items).toBeTruthy();
        // Suggested all the notes
        expect(items!.length).toEqual(2);
        for (const item of items!) {
          // All suggested items exist
          const found = NoteUtils.getNotesByFnameFromEngine({
            fname: `${TAGS_HIERARCHY}${item.label}`,
            engine,
          });
          expect(found.length > 0).toBeTruthy();
        }
      });
    }
  );

  describeMultiWS(
    "user tag",
    {
      preSetupHook: async (opts) => {
        const { wsRoot, vaults } = opts;
        await NoteTestUtilsV4.createNote({
          fname: "user.foo",
          vault: vaults[1],
          wsRoot,
        });
        await NoteTestUtilsV4.createNote({
          fname: "user.bar",
          vault: vaults[1],
          wsRoot,
        });
      },
    },
    () => {
      test("THEN provide correct completions", async () => {
        const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
        // Open a note, add [[]]
        await WSUtils.openNote(
          NoteUtils.getNoteOrThrow({
            fname: "root",
            vault: vaults[1],
            wsRoot,
            notes: engine.notes,
          })
        );
        const editor = VSCodeUtils.getActiveTextEditorOrThrow();
        await editor.edit((editBuilder) => {
          editBuilder.insert(new Position(8, 0), "@");
        });
        // have the completion provider complete this wikilink
        const items = provideCompletionItems(
          editor.document,
          new Position(8, 1)
        );
        expect(items).toBeTruthy();
        // Suggested all the notes
        expect(items!.length).toEqual(2);
        for (const item of items!) {
          // All suggested items exist
          const found = NoteUtils.getNotesByFnameFromEngine({
            fname: `${USERS_HIERARCHY}${item.label}`,
            engine,
          });
          expect(found.length > 0).toBeTruthy();
        }
      });
    }
  );

  describeMultiWS(
    "WHEN completing a wikilink without closing brackets",
    {},
    () => {
      let items: CompletionItem[] | undefined;
      before(async () => {
        const { vaults, wsRoot, engine } = getDWorkspace();
        await WSUtils.openNote(
          NoteUtils.getNoteOrThrow({
            fname: "root",
            vault: vaults[1],
            wsRoot,
            notes: engine.notes,
          })
        );
        const editor = VSCodeUtils.getActiveTextEditorOrThrow();
        await editor.edit((editBuilder) => {
          editBuilder.insert(new Position(8, 0), "Commodi [[ nam");
        });
        items = provideCompletionItems(editor.document, new Position(8, 10));
      });

      test("THEN it finds completions", () => {
        expect(items?.length).toEqual(3);
      });

      test("THEN it doesn't erase any text following the wikilink", async () => {
        for (const item of items!) {
          const range = item.range! as Range;
          // Since there's no text, start and end of range is at the same place.
          // The end doesn't go over the following text to avoid deleting them, since those are not part of the wikilink.
          expect(range.start.character).toEqual(10);
          expect(range.end.character).toEqual(10);
        }
      });

      test("THEN it adds the closing brackets", async () => {
        for (const item of items!) {
          expect(item.insertText!.toString().endsWith("]]")).toBeTruthy();
        }
      });
    }
  );

  runTestButSkipForWindows()("blocks", () => {
    describeMultiWS(
      "",
      {
        preSetupHook: ENGINE_HOOKS.setupBasic,
      },
      () => {
        test("THEN doesn't provide outside wikilink", async () => {
          const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
          // Open a note, add [[]]
          await WSUtils.openNote(
            NoteUtils.getNoteOrThrow({
              fname: "root",
              vault: vaults[0],
              wsRoot,
              notes: engine.notes,
            })
          );
          const editor = VSCodeUtils.getActiveTextEditorOrThrow();
          await editor.edit((editBuilder) => {
            editBuilder.insert(new Position(8, 0), "^");
          });
          // have the completion provider complete this wikilink
          const items = await provideBlockCompletionItems(
            editor.document,
            new Position(8, 1)
          );
          expect(items).toEqual(undefined);
        });
      }
    );

    describeMultiWS(
      "GIVEN paragraphs",
      {
        preSetupHook: async ({ wsRoot, vaults }) => {
          NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "test",
            body: [
              "Et et quam culpa.",
              "",
              "Cumque molestiae qui deleniti.",
              "Eius odit commodi harum.",
              "",
              "Sequi ut non delectus tempore.",
            ].join("\n"),
          });
        },
      },
      () => {
        test("THEN provide correct completions", async () => {
          const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
          // Open a note, add [[^]]
          await WSUtils.openNote(
            NoteUtils.getNoteOrThrow({
              fname: "test",
              vault: vaults[0],
              wsRoot,
              notes: engine.notes,
            })
          );
          const editor = VSCodeUtils.getActiveTextEditorOrThrow();
          await editor.edit((editBuilder) => {
            editBuilder.insert(new Position(8, 0), "[[^]]");
          });
          // have the completion provider complete this wikilink
          const items = await provideBlockCompletionItems(
            editor.document,
            new Position(8, 3)
          );
          expect(items).toBeTruthy();
          expect(items?.length).toEqual(3);
          // check that the
        });
      }
    );

    describeMultiWS(
      "GIVEN nested list",
      {
        preSetupHook: async ({ wsRoot, vaults }) => {
          NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "test",
            body: [
              "Et et quam culpa.",
              "",
              "* Cumque molestiae qui deleniti.",
              "* Eius odit commodi harum.",
              "  * Sequi ut non delectus tempore.",
              "  * In delectus quam sunt unde.",
              "* Quasi ex debitis aut sed.",
              "",
              "Perferendis officiis ut non.",
            ].join("\n"),
          });
        },
      },
      () => {
        test("THEN provide correct completions", async () => {
          const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
          // Open a note, add [[^]]
          await WSUtils.openNote(
            NoteUtils.getNoteOrThrow({
              fname: "test",
              vault: vaults[0],
              wsRoot,
              notes: engine.notes,
            })
          );
          const editor = VSCodeUtils.getActiveTextEditorOrThrow();
          await editor.edit((editBuilder) => {
            editBuilder.insert(new Position(8, 0), "[[^]]");
          });
          // have the completion provider complete this wikilink
          const items = await provideBlockCompletionItems(
            editor.document,
            new Position(8, 3)
          );
          expect(items).toBeTruthy();
          expect(items?.length).toEqual(8);
        });
      }
    );

    // TODO: flaky
    test.skip("provides headers for other files", (done) => {
      runLegacyMultiWorkspaceTest({
        onInit: async ({ wsRoot, vaults, engine }) => {
          // Open a note, add [[test2#]]
          await WSUtils.openNote(
            NoteUtils.getNoteOrThrow({
              fname: "test",
              vault: vaults[0],
              wsRoot,
              notes: engine.notes,
            })
          );
          const editor = VSCodeUtils.getActiveTextEditorOrThrow();
          await editor.edit((editBuilder) => {
            editBuilder.insert(new Position(7, 0), "[[test2#]]");
          });
          // have the completion provider complete this wikilink
          const items = await provideBlockCompletionItems(
            editor.document,
            new Position(7, 3)
          );
          expect(items).toBeTruthy();
          expect(items?.length).toEqual(2);
          expect(items![0].insertText).toEqual("et-et-quam-culpa");
          expect(items![1].insertText).toEqual("quasi-ex-debitis-aut-sed");
          done();
        },
        preSetupHook: async ({ wsRoot, vaults }) => {
          NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "test2",
            body: [
              "## Et et quam culpa.",
              "",
              "* Cumque molestiae qui deleniti.",
              "* Eius odit commodi harum.",
              "  * Sequi ut non delectus tempore.",
              "  * In delectus quam sunt unde.",
              "",
              "## Quasi ex debitis aut sed.",
              "",
              "Perferendis officiis ut non.",
            ].join("\n"),
          });
          NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "test",
          });
        },
      });
    });

    describeMultiWS(
      "GIVEN other files with block anchors",
      {
        preSetupHook: async ({ wsRoot, vaults }) => {
          NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "test2",
            body: [
              "Et et quam culpa.",
              "",
              "* Cumque molestiae qui deleniti.",
              "* Eius odit commodi harum. ^item-2",
              "  * Sequi ut non delectus tempore.",
              "  * In delectus quam sunt unde. ^item-4",
              "",
              "Quasi ex debitis aut sed.",
              "",
              "Perferendis officiis ut non. ^last-paragraph",
            ].join("\n"),
          });
          NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "test",
          });
        },
      },
      () => {
        test("THEN provide correct completions", async () => {
          const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
          // Open a note, add [[test2#^]]
          await WSUtils.openNote(
            NoteUtils.getNoteOrThrow({
              fname: "test",
              vault: vaults[0],
              wsRoot,
              notes: engine.notes,
            })
          );
          const editor = VSCodeUtils.getActiveTextEditorOrThrow();
          await editor.edit((editBuilder) => {
            editBuilder.insert(new Position(7, 0), "[[test2#^]]");
          });
          // have the completion provider complete this wikilink
          const items = await provideBlockCompletionItems(
            editor.document,
            new Position(7, 3)
          );
          expect(items).toBeTruthy();
          expect(items?.length).toEqual(3);
          expect(items![0].insertText).toEqual("item-2");
          expect(items![1].insertText).toEqual("item-4");
          expect(items![2].insertText).toEqual("last-paragraph");
        });
      }
    );

    function hasNoEditContaining(
      item: CompletionItem,
      newTextSubString: string
    ) {
      expect(
        _.find(
          item.additionalTextEdits,
          (edit) => edit.newText.indexOf(newTextSubString) !== -1
        )
      ).toEqual(undefined);
    }

    describeMultiWS(
      "",
      {
        preSetupHook: async ({ wsRoot, vaults }) => {
          NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "test",
            body: [
              "# Et et quam culpa. ^header",
              "",
              "Ullam vel eius reiciendis. ^paragraph",
              "",
              "* Cumque molestiae qui deleniti. ^item1",
              "* Eius odit commodi harum. ^item2",
              "  * Sequi ut non delectus tempore. ^item3",
              "",
              "^list",
              "",
              "| Sapiente | accusamus |",
              "|----------|-----------|",
              "| Laborum  | libero    |",
              "| Ullam    | optio     | ^table",
            ].join("\n"),
          });
        },
      },
      () => {
        test("THEN provide correct completions", async () => {
          const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
          // Open a note, add [[^]]
          await WSUtils.openNote(
            NoteUtils.getNoteOrThrow({
              fname: "test",
              vault: vaults[0],
              wsRoot,
              notes: engine.notes,
            })
          );
          const editor = VSCodeUtils.getActiveTextEditorOrThrow();
          await editor.edit((editBuilder) => {
            editBuilder.insert(new Position(8, 0), "[[^]]");
          });
          // have the completion provider complete this wikilink
          const items = await provideBlockCompletionItems(
            editor.document,
            new Position(8, 3)
          );
          // Check that the correct anchors were returned
          expect(items).toBeTruthy();
          expect(items!.length).toEqual(7);
          expect(items![0].insertText).toEqual("#et-et-quam-culpa");
          expect(items![1].insertText).toEqual("#^paragraph");
          expect(items![2].insertText).toEqual("#^item1");
          expect(items![3].insertText).toEqual("#^item2");
          expect(items![4].insertText).toEqual("#^item3");
          expect(items![5].insertText).toEqual("#^list");
          expect(items![6].insertText).toEqual("#^table");
          // check that we're not trying to insert unnecessary anchors
          hasNoEditContaining(items![0], "et-et-quam-culpa");
          hasNoEditContaining(items![0], "^");
          hasNoEditContaining(items![1], "^paragraph");
          hasNoEditContaining(items![2], "^item1");
          hasNoEditContaining(items![3], "^item2");
          hasNoEditContaining(items![4], "^item3");
          hasNoEditContaining(items![5], "^list");
          hasNoEditContaining(items![6], "^table");
        });
      }
    );
  });
});
Example #15
Source File: testUtilsV3.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
/**
 * Use to run tests with a multi-vault workspace. Used in the same way as
 * regular `describe`. For example:
 * ```ts
 * describeMultiWS(
 *   "WHEN workspace type is not specified",
 *   {
 *     preSetupHook: ENGINE_HOOKS.setupBasic,
 *   },
 *   () => {
 *     test("THEN initializes correctly", (done) => {
 *       const { engine, _wsRoot, _vaults } = getDWorkspace();
 *       const testNote = engine.notes["foo"];
 *       expect(testNote).toBeTruthy();
 *       done();
 *     });
 *   }
 * );
 * ```
 * @param title
 * @param opts
 * @param fn - the test() functions to execute. NOTE: This function CANNOT be
 * async, or else the test may not fail reliably when your expect or assert
 * conditions are not met. ^eq30h1lt0zat
 */
export function describeMultiWS(
  title: string,
  opts: SetupLegacyWorkspaceMultiOpts & {
    /**
     * Run after we stub vscode mock workspace, but before the workspace is created
     */
    beforeHook?: (opts: { ctx: ExtensionContext }) => Promise<void>;
    /**
     * Run after the workspace is crated, but before dendron is activated
     */
    preActivateHook?: (opts: {
      ctx: ExtensionContext;
      wsRoot: string;
      vaults: DVault[];
    }) => Promise<void>;
    /**
     * @deprecated Please use an `after()` hook instead
     */
    afterHook?: (opts: { ctx: ExtensionContext }) => Promise<void>;
    /**
     * Custom timeout for test in milleseconds
     * You will need to set this when stepping through mocha tests using breakpoints
     * otherwise the test will timeout during debugging
     * See [[Breakpoints|dendron://dendron.docs/pkg.plugin-core.qa.debug#breakpoints]] for more details
     */
    timeout?: number;
    noSetInstallStatus?: boolean;
  },
  fn: (ctx: ExtensionContext) => void
) {
  describe(title, function () {
    if (opts.timeout) {
      this.timeout(opts.timeout);
    }
    const ctx = opts.ctx ?? VSCodeUtils.getOrCreateMockContext();

    before(async () => {
      setupWorkspaceStubs({ ...opts, ctx });
      if (opts.beforeHook) {
        await opts.beforeHook({ ctx });
      }

      const out = await setupLegacyWorkspaceMulti({ ...opts, ctx });

      if (opts.preActivateHook) {
        await opts.preActivateHook({ ctx, ...out });
      }
      await _activate(ctx);
    });

    const result = fn(ctx);
    assertTestFnNotAsync(result);

    // Release all registered resouces such as commands and providers
    after(async () => {
      if (opts.afterHook) {
        await opts.afterHook({ ctx });
      }
      cleanupWorkspaceStubs(ctx);
    });
  });
}
Example #16
Source File: LookupViewModel.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
describe(`GIVEN a LookupV3QuickPick`, () => {
  const qp =
    vscode.window.createQuickPick<DNodePropsQuickInputV2>() as DendronQuickPickerV2;

  qp.buttons = [
    VaultSelectButton.create({ pressed: false }),
    MultiSelectBtn.create({ pressed: false }),
    CopyNoteLinkBtn.create(false),
    DirectChildFilterBtn.create(false),
    SelectionExtractBtn.create(false),
    Selection2LinkBtn.create(false),
    Selection2ItemsBtn.create({
      pressed: false,
    }),
    JournalBtn.create({
      pressed: false,
    }),
    ScratchBtn.create({
      pressed: false,
    }),
    TaskBtn.create(false),
    HorizontalSplitBtn.create(false),
  ];

  const viewModel: ILookupViewModel = {
    selectionState: new TwoWayBinding<LookupSelectionTypeEnum>(
      LookupSelectionTypeEnum.none
    ),
    vaultSelectionMode: new TwoWayBinding<VaultSelectionMode>(
      VaultSelectionMode.auto
    ),
    isMultiSelectEnabled: new TwoWayBinding<boolean>(false),
    isCopyNoteLinkEnabled: new TwoWayBinding<boolean>(false),
    isApplyDirectChildFilter: new TwoWayBinding<boolean>(false),
    nameModifierMode: new TwoWayBinding<LookupNoteTypeEnum>(
      LookupNoteTypeEnum.none
    ),
    isSplitHorizontally: new TwoWayBinding<boolean>(false),
  };

  let viewToTest: LookupV3QuickPickView | undefined;
  before(() => {
    viewToTest = new LookupV3QuickPickView(qp, viewModel);
  });

  after(() => {
    if (viewToTest) {
      viewToTest.dispose();
      viewToTest = undefined;
    }
  });

  describe(`WHEN mode changed to selection2Items`, () => {
    it(`THEN selection2Items button checked and Extract and toLink buttons unchecked`, () => {
      viewModel.selectionState.value = LookupSelectionTypeEnum.selection2Items;
      expect(isButtonPressed("selection2Items", qp.buttons)).toBeTruthy();
      expect(isButtonPressed("selectionExtract", qp.buttons)).toBeFalsy();
      expect(isButtonPressed("selection2link", qp.buttons)).toBeFalsy();
    });
  });

  describe(`WHEN mode changed to selection2Link`, () => {
    it(`THEN selection2Link button checked and Extract and toItems buttons unchecked`, () => {
      viewModel.selectionState.value = LookupSelectionTypeEnum.selection2link;

      expect(isButtonPressed("selection2Items", qp.buttons)).toBeFalsy();
      expect(isButtonPressed("selectionExtract", qp.buttons)).toBeFalsy();
      expect(isButtonPressed("selection2link", qp.buttons)).toBeTruthy();
    });
  });

  describe(`WHEN mode changed to selection2Extract`, () => {
    it(`THEN selection2Extract button checked and toItems and toLink buttons unchecked`, () => {
      viewModel.selectionState.value = LookupSelectionTypeEnum.selectionExtract;

      expect(isButtonPressed("selection2Items", qp.buttons)).toBeFalsy();
      expect(isButtonPressed("selectionExtract", qp.buttons)).toBeTruthy();
      expect(isButtonPressed("selection2link", qp.buttons)).toBeFalsy();
    });
  });

  describe(`WHEN mode changed to None`, () => {
    it(`THEN extract, toItems, toLink buttons all unchecked`, () => {
      viewModel.selectionState.value = LookupSelectionTypeEnum.none;

      expect(isButtonPressed("selection2Items", qp.buttons)).toBeFalsy();
      expect(isButtonPressed("selectionExtract", qp.buttons)).toBeFalsy();
      expect(isButtonPressed("selection2link", qp.buttons)).toBeFalsy();
    });
  });

  describe(`WHEN vaultSelection is alwaysPrompt`, () => {
    it(`THEN vaultSelection button is checked`, () => {
      viewModel.vaultSelectionMode.value = VaultSelectionMode.alwaysPrompt;
      expect(isButtonPressed("selectVault", qp.buttons)).toBeTruthy();
    });
  });

  describe(`WHEN vaultSelection is smart`, () => {
    it(`THEN vaultSelection button is unchecked`, () => {
      viewModel.vaultSelectionMode.value = VaultSelectionMode.smart;
      expect(isButtonPressed("selectVault", qp.buttons)).toBeFalsy();
    });
  });

  describe(`WHEN multiSelect is enabled`, () => {
    it(`THEN multiSelect button is checked`, () => {
      viewModel.isMultiSelectEnabled.value = true;

      expect(isButtonPressed("multiSelect", qp.buttons)).toBeTruthy();
    });
  });

  describe(`WHEN multiSelect is disabled`, () => {
    it(`THEN multiSelect button is unchecked`, () => {
      viewModel.isMultiSelectEnabled.value = false;

      expect(isButtonPressed("multiSelect", qp.buttons)).toBeFalsy();
    });
  });

  // Copy Note Link State
  describe(`WHEN copyNoteLink is enabled`, () => {
    it(`THEN copyNoteLink button is checked`, () => {
      viewModel.isCopyNoteLinkEnabled.value = true;

      expect(isButtonPressed("copyNoteLink", qp.buttons)).toBeTruthy();
    });
  });

  describe(`WHEN copyNoteLink is disabled`, () => {
    it(`THEN copyNoteLink button is unchecked`, () => {
      viewModel.isCopyNoteLinkEnabled.value = false;

      expect(isButtonPressed("copyNoteLink", qp.buttons)).toBeFalsy();
    });
  });

  // Direct Child Only state
  describe(`WHEN directChildOnly is enabled`, () => {
    it(`THEN directChildOnly button is checked`, () => {
      viewModel.isApplyDirectChildFilter.value = true;

      expect(isButtonPressed("directChildOnly", qp.buttons)).toBeTruthy();
    });
  });

  describe(`WHEN directChildOnly is disabled`, () => {
    it(`THEN directChildOnly button is unchecked`, () => {
      viewModel.isApplyDirectChildFilter.value = false;

      expect(isButtonPressed("directChildOnly", qp.buttons)).toBeFalsy();
    });
  });

  // Horizontal Split state
  describe(`WHEN horizontal split is enabled`, () => {
    it(`THEN horizontal button is checked`, () => {
      viewModel.isSplitHorizontally.value = true;

      expect(isButtonPressed("horizontal", qp.buttons)).toBeTruthy();
    });
  });

  describe(`WHEN horizontal split is disabled`, () => {
    it(`THEN horizontal button is unchecked`, () => {
      viewModel.isSplitHorizontally.value = false;

      expect(isButtonPressed("horizontal", qp.buttons)).toBeFalsy();
    });
  });

  // Name Modifier Options (Journal / Scratch / Task):
  describe(`WHEN name modifier mode changed to Journal`, () => {
    it(`THEN journal button checked and scratch and task buttons unchecked`, () => {
      viewModel.nameModifierMode.value = LookupNoteTypeEnum.journal;
      expect(
        isButtonPressed(LookupNoteTypeEnum.journal, qp.buttons)
      ).toBeTruthy();
      expect(
        isButtonPressed(LookupNoteTypeEnum.scratch, qp.buttons)
      ).toBeFalsy();
      expect(isButtonPressed(LookupNoteTypeEnum.task, qp.buttons)).toBeFalsy();
    });
  });

  describe(`WHEN name modifier mode changed to Scratch`, () => {
    it(`THEN scratch button checked and journal and task buttons unchecked`, () => {
      viewModel.nameModifierMode.value = LookupNoteTypeEnum.scratch;
      expect(
        isButtonPressed(LookupNoteTypeEnum.journal, qp.buttons)
      ).toBeFalsy();
      expect(
        isButtonPressed(LookupNoteTypeEnum.scratch, qp.buttons)
      ).toBeTruthy();
      expect(isButtonPressed(LookupNoteTypeEnum.task, qp.buttons)).toBeFalsy();
    });
  });

  describe(`WHEN name modifier mode changed to Task`, () => {
    it(`THEN task button checked and journal and scratch buttons unchecked`, () => {
      viewModel.nameModifierMode.value = LookupNoteTypeEnum.task;
      expect(
        isButtonPressed(LookupNoteTypeEnum.journal, qp.buttons)
      ).toBeFalsy();
      expect(
        isButtonPressed(LookupNoteTypeEnum.scratch, qp.buttons)
      ).toBeFalsy();
      expect(isButtonPressed(LookupNoteTypeEnum.task, qp.buttons)).toBeTruthy();
    });
  });

  describe(`WHEN name modifier mode changed to None`, () => {
    it(`THEN journal, scratch, task buttons all unchecked`, () => {
      viewModel.nameModifierMode.value = LookupNoteTypeEnum.none;
      expect(
        isButtonPressed(LookupNoteTypeEnum.journal, qp.buttons)
      ).toBeFalsy();
      expect(
        isButtonPressed(LookupNoteTypeEnum.scratch, qp.buttons)
      ).toBeFalsy();
      expect(isButtonPressed(LookupNoteTypeEnum.task, qp.buttons)).toBeFalsy();
    });
  });
});
Example #17
Source File: WorkspaceWatcher.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("WorkspaceWatcher", function () {
  let watcher: WorkspaceWatcher;

  describeSingleWS(
    "GIVEN a basic setup on a single vault workspace",
    {
      postSetupHook: setupBasic,
    },
    () => {
      test("WHEN user renames a file outside of dendron rename command, THEN all of its references are also updated", (done) => {
        const { engine, wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        const previewProxy = new MockPreviewProxy();
        const extension = ExtensionProvider.getExtension();

        const windowWatcher = new WindowWatcher({
          extension,
          previewProxy,
        });

        watcher = new WorkspaceWatcher({
          schemaSyncService: ExtensionProvider.getExtension().schemaSyncService,
          extension,
          windowWatcher,
        });
        const oldPath = path.join(wsRoot, vaults[0].fsPath, "oldfile.md");
        const oldUri = vscode.Uri.file(oldPath);
        const newPath = path.join(wsRoot, vaults[0].fsPath, "newfile.md");
        const newUri = vscode.Uri.file(newPath);
        const args: vscode.FileWillRenameEvent = {
          files: [
            {
              oldUri,
              newUri,
            },
          ],
          // eslint-disable-next-line no-undef
          waitUntil: (_args: Thenable<any>) => {
            _args.then(() => {
              const reference = NoteUtils.getNoteOrThrow({
                fname: "foo.one",
                vault: vaults[0],
                wsRoot,
                notes: engine.notes,
              });
              expect(reference.body).toEqual(`[[newfile]]\n`);
              done();
            });
          },
        };

        watcher.onWillRenameFiles(args);
      });
    }
  );

  describeSingleWS(
    "GIVEN a basic setup on a single vault workspace",
    {
      postSetupHook: setupBasic,
    },
    () => {
      test("WHEN user renames a file outside of dendron rename command, THEN the title of fileName is also updated", async () => {
        const { engine, wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        const previewProxy = new MockPreviewProxy();
        const extension = ExtensionProvider.getExtension();

        const windowWatcher = new WindowWatcher({
          extension,
          previewProxy,
        });
        watcher = new WorkspaceWatcher({
          schemaSyncService: ExtensionProvider.getExtension().schemaSyncService,
          extension,
          windowWatcher,
        });
        const oldPath = path.join(wsRoot, vaults[0].fsPath, "oldfile.md");
        const oldUri = vscode.Uri.file(oldPath);
        const newPath = path.join(wsRoot, vaults[0].fsPath, "newfile.md");
        const newUri = vscode.Uri.file(newPath);
        const args: vscode.FileRenameEvent = {
          files: [
            {
              oldUri,
              newUri,
            },
          ],
        };
        const edit = new vscode.WorkspaceEdit();
        edit.renameFile(oldUri, newUri);
        const success = await vscode.workspace.applyEdit(edit);
        if (success) {
          await watcher.onDidRenameFiles(args);
          const newFile = NoteUtils.getNoteOrThrow({
            fname: "newfile",
            vault: vaults[0],
            wsRoot,
            notes: engine.notes,
          });
          expect(newFile.title).toEqual(`Newfile`);
        }
      });
    }
  );

  describeSingleWS(
    "GIVEN a basic setup on a single vault workspace",
    {
      postSetupHook: setupBasic,
    },
    () => {
      test("WHEN user saves a file and content has not changed, THEN updated timestamp in frontmatter is not updated", async () => {
        const engine = ExtensionProvider.getEngine();
        const previewProxy = new MockPreviewProxy();
        const extension = ExtensionProvider.getExtension();

        const windowWatcher = new WindowWatcher({
          extension,
          previewProxy,
        });
        watcher = new WorkspaceWatcher({
          schemaSyncService: ExtensionProvider.getExtension().schemaSyncService,
          extension,
          windowWatcher,
        });
        const fooNote = engine.notes["foo.one"];
        const updatedBefore = fooNote.updated;
        const editor = await ExtensionProvider.getWSUtils().openNote(fooNote);
        const vscodeEvent: vscode.TextDocumentWillSaveEvent = {
          document: editor.document,
        };
        const changes = watcher.onWillSaveTextDocument(vscodeEvent);
        expect(changes).toBeTruthy();
        expect(changes?.changes.length).toEqual(0);
        expect(fooNote.updated).toEqual(updatedBefore);
      });

      test("WHEN user saves a file and content has changed, THEN updated timestamp in frontmatter is updated", (done) => {
        const engine = ExtensionProvider.getEngine();
        const previewProxy = new MockPreviewProxy();
        const extension = ExtensionProvider.getExtension();

        const windowWatcher = new WindowWatcher({
          extension,
          previewProxy,
        });
        watcher = new WorkspaceWatcher({
          schemaSyncService: ExtensionProvider.getExtension().schemaSyncService,
          extension,
          windowWatcher,
        });
        const fooNote = engine.notes["foo.one"];
        const bodyBefore = fooNote.body;
        const updatedBefore = fooNote.updated;
        const textToAppend = "new text here";
        ExtensionProvider.getWSUtils()
          .openNote(fooNote)
          .then((editor) => {
            editor.edit((editBuilder) => {
              const line = editor.document.getText().split("\n").length;
              editBuilder.insert(new vscode.Position(line, 0), textToAppend);
            });
            editor.document.save().then(() => {
              const vscodeEvent: vscode.TextDocumentWillSaveEvent = {
                document: editor.document,
                // eslint-disable-next-line no-undef
                waitUntil: (_args: Thenable<any>) => {
                  _args.then(() => {
                    // Engine note body hasn't been updated yet
                    expect(engine.notes["foo.one"].body).toEqual(bodyBefore);
                    expect(engine.notes["foo.one"].updated).toNotEqual(
                      updatedBefore
                    );
                    done();
                  });
                },
              };
              const changes = watcher.onWillSaveTextDocument(vscodeEvent);
              expect(changes).toBeTruthy();
              expect(changes?.changes.length).toEqual(1);
            });
          });
      });
    }
  );

  describe("GIVEN the user opening a file", () => {
    let ext: IDendronExtension;
    let workspaceWatcher: WorkspaceWatcher;

    beforeEach(async () => {
      ext = ExtensionProvider.getExtension();
      const previewProxy = new MockPreviewProxy();

      const windowWatcher = new WindowWatcher({
        extension: ext,
        previewProxy,
      });

      workspaceWatcher = new WorkspaceWatcher({
        schemaSyncService: ext.schemaSyncService,
        extension: ext,
        windowWatcher,
      });
    });
    afterEach(async () => {
      // imporant since we activate workspace watchers
      await ext.deactivate();
    });
    describeSingleWS(
      "AND WHEN user opens non dendron file for the first time",
      {},
      () => {
        test("THEN do not affect frontmatter", async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          await FileTestUtils.createFiles(wsRoot, [{ path: "sample" }]);
          const notePath = path.join(wsRoot, "sample");
          const editor = await VSCodeUtils.openFileInEditor(
            vscode.Uri.file(notePath)
          );
          const { onFirstOpen } =
            UNSAFE_getWorkspaceWatcherPropsForTesting(workspaceWatcher);
          expect(await onFirstOpen(editor)).toBeFalsy();
        });
      }
    );

    describeSingleWS(
      "AND WHEN user opens non dendron markdown file for the first time",
      {},
      () => {
        test("THEN do not affect frontmatter", async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          await FileTestUtils.createFiles(wsRoot, [{ path: "sample.md" }]);
          const notePath = path.join(wsRoot, "sample.md");
          const editor = await VSCodeUtils.openFileInEditor(
            vscode.Uri.file(notePath)
          );
          const { onFirstOpen } =
            UNSAFE_getWorkspaceWatcherPropsForTesting(workspaceWatcher);
          expect(await onFirstOpen(editor)).toBeFalsy();
        });
      }
    );

    describeSingleWS(
      "WHEN user opens dendron note for the first time",
      {},
      () => {
        let note: NoteProps;
        before(async () => {
          const { engine, vaults, wsRoot } = ExtensionProvider.getDWorkspace();
          note = await NoteTestUtilsV4.createNoteWithEngine({
            engine,
            fname: "test",
            vault: vaults[0],
            wsRoot,
          });
        });
        test("THEN the cursor moves past the frontmatter", async () => {
          const ext = ExtensionProvider.getExtension();
          const wsutils = new WSUtilsV2(ext);
          const editor = await wsutils.openNote(note);
          const { onFirstOpen } =
            UNSAFE_getWorkspaceWatcherPropsForTesting(workspaceWatcher);

          const stubTimeout = sinon.stub(Wrap, "setTimeout");
          expect(await onFirstOpen(editor)).toBeTruthy();
          stubTimeout.callArg(0);
          // the selection should have been moved past the frontmatter
          const { line, character } = editor.selection.active;
          expect(line).toEqual(7);
          expect(character).toEqual(3);
          stubTimeout.restore();
        });
      }
    );

    describeSingleWS(
      "WHEN the user opens the file through the search",
      {},
      () => {
        let note: NoteProps;
        before(async () => {
          const { engine, vaults, wsRoot } = ExtensionProvider.getDWorkspace();
          note = await NoteTestUtilsV4.createNoteWithEngine({
            engine,
            fname: "test",
            vault: vaults[0],
            wsRoot,
          });
        });
        test("THEN the cursor moves past the frontmatter", async () => {
          const stubTimeout = sinon.stub(Wrap, "setTimeout");
          const editor = await WSUtils.openNote(note);
          // pre-move the selection, like what would happen when opening through the serach
          editor.selection = new vscode.Selection(5, 0, 5, 0);
          WorkspaceWatcher.moveCursorPastFrontmatter(editor);
          stubTimeout.callArg(0);
          // the selection didn't move from what it was before
          const { line, character } = editor.selection.active;
          expect(line).toEqual(5);
          expect(character).toEqual(0);
          stubTimeout.restore();
        });
      }
    );
  });
});
Example #18
Source File: DefinitionProvider.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("DefinitionProvider", function () {
  describe("same vault", () => {
    let noteWithLink: NoteProps;
    let noteWithTarget: NoteProps;
    let _wsRoot: string;

    describeMultiWS(
      "",
      {
        preSetupHook: async ({ wsRoot, vaults }) => {
          _wsRoot = wsRoot;
          noteWithTarget = await NOTE_PRESETS_V4.NOTE_WITH_TARGET.create({
            wsRoot,
            vault: vaults[0],
            genRandomId: true,
          });
          noteWithLink = await NOTE_PRESETS_V4.NOTE_WITH_LINK.create({
            wsRoot,
            vault: vaults[0],
          });
        },
      },
      () => {
        test("THEN provide correct definitions", async () => {
          const editor = await WSUtils.openNote(noteWithTarget);
          const location = (await provide(editor)) as vscode.Location;
          expect(location.uri.fsPath.toLowerCase()).toEqual(
            NoteUtils.getFullPath({
              wsRoot: _wsRoot,
              note: noteWithLink,
            }).toLowerCase()
          );
        });
      }
    );

    describeMultiWS(
      "GIVEN vault prefix",
      {
        preSetupHook: async ({ wsRoot, vaults }) => {
          await callSetupHook(SETUP_HOOK_KEYS.WITH_LINKS, {
            workspaceType: "single",
            wsRoot,
            vaults,
            withVaultPrefix: true,
          });
        },
      },
      () => {
        test("THEN provide correct definitions", async () => {
          const { wsRoot, engine } = ExtensionProvider.getDWorkspace();
          const note = engine.notes["alpha"];
          const beta = engine.notes["beta"];
          const editor = await WSUtils.openNote(note);
          const location = (await provide(editor)) as vscode.Location;
          expect(location.uri.fsPath.toLowerCase()).toEqual(
            NoteUtils.getFullPath({ wsRoot, note: beta }).toLowerCase()
          );
        });
      }
    );

    describeMultiWS(
      "GIVEN anchor",
      {
        preSetupHook: async ({ wsRoot, vaults }) => {
          const vault = vaults[0];
          await GOTO_NOTE_PRESETS.ANCHOR.preSetupHook({ wsRoot, vaults });
          await NoteTestUtilsV4.createNote({
            fname: "beta",
            vault,
            body: `[[alpha#h3]]`,
            wsRoot,
          });
        },
      },
      () => {
        test("THEN provide correct definitions", async () => {
          const { engine } = ExtensionProvider.getDWorkspace();
          const note = engine.notes["beta"];
          const editor = await WSUtils.openNote(note);
          const doc = editor?.document as vscode.TextDocument;
          const provider = new DefinitionProvider();
          const pos = LocationTestUtils.getPresetWikiLinkPosition();
          const loc = (await provider.provideDefinition(
            doc,
            pos,
            null as any
          )) as vscode.Location;
          expect(LocationTestUtils.getBasenameFromLocation(loc)).toEqual(
            "alpha.md"
          );
        });
      }
    );

    describeMultiWS(
      "GIVEN alias",
      {
        preSetupHook: async ({ wsRoot, vaults }) => {
          _wsRoot = wsRoot;
          noteWithTarget = await NOTE_PRESETS_V4.NOTE_WITH_TARGET.create({
            wsRoot,
            vault: vaults[0],
          });
          noteWithLink = await NOTE_PRESETS_V4.NOTE_WITH_ALIAS_LINK.create({
            wsRoot,
            vault: vaults[0],
          });
        },
      },
      () => {
        test("THEN provide correct definitions", async () => {
          const editor = await WSUtils.openNote(noteWithLink);
          const location = (await provide(editor)) as vscode.Location;
          expect(location.uri.fsPath.toLowerCase()).toEqual(
            NoteUtils.getFullPath({
              wsRoot: _wsRoot,
              note: noteWithTarget,
            }).toLowerCase()
          );
        });
      }
    );

    const { ANCHOR_WITH_SPECIAL_CHARS } = GOTO_NOTE_PRESETS;
    describeMultiWS(
      "GIVEN anchor with special characters",
      {
        preSetupHook: async ({ wsRoot, vaults }) => {
          const vault = vaults[0];
          const { specialCharsHeader } =
            await ANCHOR_WITH_SPECIAL_CHARS.preSetupHook({
              wsRoot,
              vaults: [vault],
            });
          await NoteTestUtilsV4.createNote({
            fname: "beta",
            vault,
            body: `[[alpha#${getSlugger().slug(specialCharsHeader)}]]`,
            wsRoot,
          });
        },
      },
      () => {
        test("THEN provide correct definitions", async () => {
          const { engine } = ExtensionProvider.getDWorkspace();
          const note = engine.notes["beta"];
          const editor = await WSUtils.openNote(note);
          const doc = editor?.document as vscode.TextDocument;
          const provider = new DefinitionProvider();
          const pos = LocationTestUtils.getPresetWikiLinkPosition();
          const loc = (await provider.provideDefinition(
            doc,
            pos,
            null as any
          )) as vscode.Location;
          expect(LocationTestUtils.getBasenameFromLocation(loc)).toEqual(
            "alpha.md"
          );
        });
      }
    );
  });

  describeMultiWS(
    "GIVEN multi vault",
    {
      preSetupHook: async ({ vaults, wsRoot }) => {
        await ENGINE_HOOKS_MULTI.setupLinksMulti({ wsRoot, vaults });
      },
    },
    () => {
      test("THEN provide correct definitions", async () => {
        const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
        const note = engine.notes["alpha"];
        const editor = await WSUtils.openNote(note);

        const doc = editor?.document as vscode.TextDocument;
        const provider = new DefinitionProvider();
        const locations = (await provider.provideDefinition(
          doc,
          LocationTestUtils.getPresetWikiLinkPosition(),
          null as any
        )) as vscode.Location;
        expect(locations.uri.fsPath.toLowerCase()).toEqual(
          path.join(wsRoot, vaults[1].fsPath, "beta.md").toLowerCase()
        );
      });
    }
  );
  describeMultiWS(
    "GIVEN multi vault with prefix",
    {
      preSetupHook: async ({ vaults, wsRoot }) => {
        await callSetupHook(SETUP_HOOK_KEYS.WITH_LINKS, {
          workspaceType: "multi",
          wsRoot,
          vaults,
          withVaultPrefix: true,
        });
      },
    },
    () => {
      test("THEN provide correct definitions", async () => {
        const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
        const note = engine.notes["alpha"];
        const editor = await WSUtils.openNote(note);

        const doc = editor?.document as vscode.TextDocument;
        const provider = new DefinitionProvider();
        const locations = (await provider.provideDefinition(
          doc,
          LocationTestUtils.getPresetWikiLinkPosition(),
          null as any
        )) as vscode.Location;
        expect(locations.uri.fsPath.toLowerCase()).toEqual(
          path.join(wsRoot, vaults[1].fsPath, "beta.md").toLowerCase()
        );
      });
    }
  );

  let noteTarget1: NoteProps;
  let noteTarget2: NoteProps;
  let noteWithLink: NoteProps;

  describeMultiWS(
    "GIVEN multi vault with same name",
    {
      preSetupHook: async ({ wsRoot, vaults }) => {
        noteTarget1 = await NOTE_PRESETS_V4.NOTE_WITH_TARGET.create({
          wsRoot,
          vault: vaults[0],
          genRandomId: true,
        });
        noteTarget2 = await NOTE_PRESETS_V4.NOTE_WITH_TARGET.create({
          wsRoot,
          vault: vaults[1],
          genRandomId: true,
        });
        noteWithLink = await NOTE_PRESETS_V4.NOTE_WITH_LINK.create({
          wsRoot,
          vault: vaults[0],
        });
      },
    },
    () => {
      test("THEN provide correct definitions", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        const editor = await WSUtils.openNote(noteWithLink);
        const locations = (await provide(editor)) as vscode.Location[];
        expect(locations.length).toEqual(2);
        expect(locations.map((l) => l.uri.fsPath.toLowerCase())).toEqual([
          NoteUtils.getFullPath({
            wsRoot,
            note: noteTarget1,
          }).toLowerCase(),
          NoteUtils.getFullPath({
            wsRoot,
            note: noteTarget2,
          }).toLowerCase(),
        ]);
      });
    }
  );

  describeMultiWS(
    "GIVEN notes with same name",
    {
      preSetupHook: async ({ wsRoot, vaults }) => {
        noteTarget1 = await NOTE_PRESETS_V4.NOTE_WITH_TARGET.create({
          wsRoot,
          vault: vaults[0],
          genRandomId: true,
        });
        noteTarget2 = await NOTE_PRESETS_V4.NOTE_WITH_TARGET.create({
          wsRoot,
          vault: vaults[1],
          genRandomId: true,
        });
        noteWithLink = await NOTE_PRESETS_V4.NOTE_WITH_LINK.create({
          wsRoot,
          vault: vaults[0],
        });
      },
    },
    () => {
      test("THEN provide correct definitions", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        const editor = await WSUtils.openNote(noteWithLink);
        const locations = (await provide(editor)) as vscode.Location[];
        expect(locations.length).toEqual(2);
        expect(locations.map((l) => l.uri.fsPath.toLowerCase())).toEqual([
          NoteUtils.getFullPath({
            wsRoot,
            note: noteTarget1,
          }).toLowerCase(),
          NoteUtils.getFullPath({
            wsRoot,
            note: noteTarget2,
          }).toLowerCase(),
        ]);
      });
    }
  );

  describe("WHEN used on a link to a non-note file", () => {
    describeSingleWS("AND it's a text file", {}, () => {
      before(async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        await fs.writeFile(
          path.join(wsRoot, "test.txt"),
          "Et voluptatem autem sunt."
        );
      });

      test("THEN finds the correct definiton for that file", async () => {
        const { vaults, wsRoot, engine } = ExtensionProvider.getDWorkspace();
        const note = await NoteTestUtilsV4.createNoteWithEngine({
          wsRoot,
          vault: vaults[0],
          fname: "test.note",
          body: "[[/test.txt]]",
          engine,
        });

        await ExtensionProvider.getWSUtils().openNote(note);
        VSCodeUtils.getActiveTextEditorOrThrow().selection =
          new vscode.Selection(7, 1, 7, 1);
        const { document } = VSCodeUtils.getActiveTextEditorOrThrow();
        const pos = LocationTestUtils.getPresetWikiLinkPosition();
        const location = (await new DefinitionProvider().provideDefinition(
          document,
          pos,
          stubCancellationToken()
        )) as vscode.Location;
        // The open file should not have changed
        expect(getActiveEditorBasename()).toEqual("test.note.md");
        // Should have provided the right file as definition
        expect(location).toBeTruthy();
        expect(location.uri.fsPath).toEqual(path.join(wsRoot, "test.txt"));
      });
    });

    describeSingleWS("AND it's a binary file", {}, () => {
      before(async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        await fs.writeFile(
          path.join(wsRoot, "test.txt"),
          "Et voluptatem autem sunt."
        );
        await fs.ensureFile(path.join(wsRoot, "test.png"));
      });

      test("THEN doesn't open the non-note binary file", async () => {
        const openWithDefaultApp = sinon
          .stub(PluginFileUtils, "openWithDefaultApp")
          .resolves();
        const { vaults, wsRoot, engine } = ExtensionProvider.getDWorkspace();
        const note = await NoteTestUtilsV4.createNoteWithEngine({
          wsRoot,
          vault: vaults[0],
          fname: "test.note",
          body: "[[/test.png]]",
          engine,
        });

        await ExtensionProvider.getWSUtils().openNote(note);
        VSCodeUtils.getActiveTextEditorOrThrow().selection =
          new vscode.Selection(7, 1, 7, 1);
        const { document } = VSCodeUtils.getActiveTextEditorOrThrow();
        const pos = LocationTestUtils.getPresetWikiLinkPosition();
        await new DefinitionProvider().provideDefinition(
          document,
          pos,
          stubCancellationToken()
        );
        // The open file should not have changed
        expect(getActiveEditorBasename()).toEqual("test.note.md");
        // The file should not have opened in default app
        expect(openWithDefaultApp.called).toBeFalsy();
        openWithDefaultApp.restore();
      });
    });
  });
});
Example #19
Source File: VaultConvert.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("GIVEN VaultConvert", function () {
  describeMultiWS(
    "WHEN converting a local vault to a remote vault",
    { preSetupHook: ENGINE_HOOKS_MULTI.setupBasicMulti },
    () => {
      let remote: string;
      before(async () => {
        const { vaults } = ExtensionProvider.getDWorkspace();
        const cmd = new VaultConvertCommand();
        sinon.stub(cmd, "gatherType").resolves("remote");
        sinon.stub(cmd, "gatherVault").resolves(vaults[0]);

        // Create a remote repository to be the upstream
        remote = tmpDir().name;
        await GitTestUtils.remoteCreate(remote);
        sinon.stub(cmd, "gatherRemoteURL").resolves(remote);

        await cmd.run();
      });
      after(async () => {
        sinon.restore();
      });

      test("THEN updates .gitignore", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        const contents = await fs.readFile(path.join(wsRoot, ".gitignore"), {
          encoding: "utf-8",
        });
        expect(contents.match(/^vault1$/m)).toBeTruthy();
      });

      test("THEN updates config", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        const config = DConfig.getRaw(wsRoot) as IntermediateDendronConfig;
        expect(ConfigUtils.getVaults(config)[0].remote).toEqual({
          type: "git",
          url: remote,
        });
      });

      test("THEN the folder is a git repository", async () => {
        const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        const git = new Git({ localUrl: path.join(wsRoot, vaults[0].fsPath) });
        expect(await git.getRemote()).toEqual("origin");
        expect(await git.getCurrentBranch()).toBeTruthy();
      });

      describe("AND converting that back to a local vault", () => {
        before(async () => {
          const { vaults } = ExtensionProvider.getDWorkspace();
          const cmd = new VaultConvertCommand();
          sinon.stub(cmd, "gatherType").resolves("local");
          sinon.stub(cmd, "gatherVault").resolves(vaults[0]);

          await cmd.run();
        });
        after(async () => {
          sinon.restore();
        });

        test("THEN updates .gitignore", async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          const contents = await fs.readFile(path.join(wsRoot, ".gitignore"), {
            encoding: "utf-8",
          });
          expect(contents.match(/^vault1$/m)).toBeFalsy();
        });

        test("THEN updates config", async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          const config = DConfig.getRaw(wsRoot) as IntermediateDendronConfig;
          expect(ConfigUtils.getVaults(config)[0].remote).toBeFalsy();
        });

        test("THEN the folder is NOT a git repository", async () => {
          const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
          const git = new Git({
            localUrl: path.join(wsRoot, vaults[0].fsPath),
          });
          expect(await git.getRemote()).toBeFalsy();
        });
      });
    }
  );

  describeMultiWS(
    "WHEN converting a local vault to a remote vault with self contained vaults enabled",
    {
      preSetupHook: ENGINE_HOOKS_MULTI.setupBasicMulti,
      modConfigCb: (config) => {
        config.dev = { enableSelfContainedVaults: true };
        return config;
      },
    },
    () => {
      let remote: string;
      before(async () => {
        const { vaults } = ExtensionProvider.getDWorkspace();
        const cmd = new VaultConvertCommand();
        sinon.stub(cmd, "gatherType").resolves("remote");
        sinon.stub(cmd, "gatherVault").resolves(vaults[0]);
        sinon.stub(cmd, "promptForFolderMove").resolves(true);

        // Create a remote repository to be the upstream
        remote = tmpDir().name;
        await GitTestUtils.remoteCreate(remote);
        sinon.stub(cmd, "gatherRemoteURL").resolves(remote);

        await cmd.run();
      });
      after(async () => {
        sinon.restore();
      });

      test("THEN updates .gitignore", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        const contents = await fs.readFile(path.join(wsRoot, ".gitignore"), {
          encoding: "utf-8",
        });
        // Should have moved under dependencies
        expect(
          contents.match(
            new RegExp(
              `^dependencies${_.escapeRegExp(path.sep)}${path.basename(
                remote
              )}`,
              "m"
            )
          )
        ).toBeTruthy();
      });

      test("THEN updates config", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        const config = DConfig.getRaw(wsRoot) as IntermediateDendronConfig;
        expect(ConfigUtils.getVaults(config)[0].remote).toEqual({
          type: "git",
          url: remote,
        });
      });

      test("THEN the vault is moved to the right folder", async () => {
        const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        const vault = vaults[0];
        expect(
          await fs.pathExists(
            path.join(wsRoot, VaultUtils.getRelPath(vault), "root.md")
          )
        ).toBeTruthy();
        expect(vault.fsPath.startsWith(FOLDERS.DEPENDENCIES)).toBeTruthy();
        expect(vault.fsPath.endsWith(path.basename(remote))).toBeTruthy();
      });

      test("THEN the folder is a git repository", async () => {
        const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
        const git = new Git({ localUrl: path.join(wsRoot, vaults[0].fsPath) });
        expect(await git.getRemote()).toEqual("origin");
        expect(await git.getCurrentBranch()).toBeTruthy();
      });

      describe("AND converting that back to a local vault", () => {
        before(async () => {
          const { vaults } = ExtensionProvider.getDWorkspace();
          const cmd = new VaultConvertCommand();
          sinon.stub(cmd, "gatherType").resolves("local");
          sinon.stub(cmd, "gatherVault").resolves(vaults[0]);
          sinon.stub(cmd, "promptForFolderMove").resolves(true);

          await cmd.run();
        });
        after(async () => {
          sinon.restore();
        });

        test("THEN updates .gitignore", async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          const contents = await fs.readFile(path.join(wsRoot, ".gitignore"), {
            encoding: "utf-8",
          });
          expect(contents.match(/^dependencies/m)).toBeFalsy();
        });

        test("THEN updates config", async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          const config = DConfig.getRaw(wsRoot) as IntermediateDendronConfig;
          expect(ConfigUtils.getVaults(config)[0].remote).toBeFalsy();
        });

        test("THEN the vault is moved to the right folder", async () => {
          const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
          const vault = vaults[0];
          expect(
            await fs.pathExists(
              path.join(wsRoot, VaultUtils.getRelPath(vault), "root.md")
            )
          ).toBeTruthy();
          expect(
            vault.fsPath.startsWith(
              path.join(FOLDERS.DEPENDENCIES, FOLDERS.LOCAL_DEPENDENCY)
            )
          ).toBeTruthy();
        });

        test("THEN the folder is NOT a git repository", async () => {
          const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
          const git = new Git({
            localUrl: path.join(wsRoot, vaults[0].fsPath),
          });
          expect(await git.getRemote()).toBeFalsy();
        });
      });
    }
  );

  describeMultiWS(
    "WHEN given a bad remote URL",
    { preSetupHook: ENGINE_HOOKS_MULTI.setupBasicMulti },
    () => {
      before(async () => {
        const { vaults } = ExtensionProvider.getDWorkspace();
        const cmd = new VaultConvertCommand();
        sinon.stub(cmd, "gatherType").resolves("remote");
        sinon.stub(cmd, "gatherVault").resolves(vaults[0]);

        // Bad remote, not actually a vault
        const remote = tmpDir().name;
        sinon.stub(cmd, "gatherRemoteURL").resolves(remote);

        await cmd.run();
      });
      after(async () => {
        sinon.restore();
      });

      test("THEN conversion fails mid-operation", async () => {
        // config is updated after the remote is fully set up, so if the config has been updated we know that we were able to set up and push to remote
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        const config = DConfig.getRaw(wsRoot) as IntermediateDendronConfig;
        expect(ConfigUtils.getVaults(config)[0].remote).toBeFalsy();
      });

      describe("AND running the conversion command again", () => {
        before(async () => {
          const { vaults } = ExtensionProvider.getDWorkspace();
          const cmd = new VaultConvertCommand();
          sinon.stub(cmd, "gatherType").resolves("remote");
          sinon.stub(cmd, "gatherVault").resolves(vaults[0]);

          // Create a remote repository to be the upstream
          const remote = tmpDir().name;
          await GitTestUtils.remoteCreate(remote);
          sinon.stub(cmd, "gatherRemoteURL").resolves(remote);

          await cmd.run();
        });
        after(async () => {
          sinon.restore();
        });

        test("THEN the conversion completes", async () => {
          // config is updated after the remote is fully set up, so if the config has been updated we know that we were able to set up and push to remote
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          const config = DConfig.getRaw(wsRoot) as IntermediateDendronConfig;
          expect(ConfigUtils.getVaults(config)[0].remote).toBeTruthy();
        });
      });
    }
  );
});
Example #20
Source File: VaultAddCommand.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
describe("GIVEN VaultAddCommand with self contained vaults enabled", function () {
  describeSingleWS(
    "WHEN creating and adding a local vault",
    {
      modConfigCb: enableSelfCOntainedVaults,
    },
    () => {
      const vaultName = "my-self-contained-vault";
      before(async () => {
        sinon.stub(VSCodeUtils, "showQuickPick").resolves({ label: "local" });
        sinon.stub(VSCodeUtils, "showInputBox").resolves(vaultName);
        sinon.stub(vscode.commands, "executeCommand").resolves({}); // stub reload window

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

      test("THEN the vault is under `dependencies/localhost`, and is self contained", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        const vaultPath = path.join(
          wsRoot,
          FOLDERS.DEPENDENCIES,
          FOLDERS.LOCAL_DEPENDENCY,
          vaultName
        );
        expect(await fs.pathExists(vaultPath)).toBeTruthy();
        expect(
          await readYAMLAsync(
            path.join(vaultPath, CONSTANTS.DENDRON_CONFIG_FILE)
          )
        ).toBeTruthy();
        expect(
          await fs.pathExists(path.join(vaultPath, FOLDERS.NOTES))
        ).toBeTruthy();
      });

      test("THEN the vault was added to the workspace config correctly", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        const config = DConfig.getOrCreate(wsRoot);
        const vault = VaultUtils.getVaultByName({
          vaults: ConfigUtils.getVaults(config),
          vname: vaultName,
        });
        expect(vault?.selfContained).toBeTruthy();
        expect(vault?.name).toEqual(vaultName);
        expect(vault?.fsPath).toEqual(
          path.join(FOLDERS.DEPENDENCIES, FOLDERS.LOCAL_DEPENDENCY, vaultName)
        );
      });

      test("THEN the notes in this vault are accessible", async () => {
        // Since we mock the reload window, need to reload index here to pick up the notes in the new vault
        await new ReloadIndexCommand().run();
        const { engine, vaults } = ExtensionProvider.getDWorkspace();
        const vault = VaultUtils.getVaultByName({
          vaults,
          vname: vaultName,
        });
        expect(vault).toBeTruthy();
        const note = NoteUtils.getNoteByFnameFromEngine({
          fname: "root",
          vault: vault!,
          engine,
        });
        expect(note).toBeTruthy();
        expect(note?.vault.name).toEqual(vaultName);
      });
    }
  );

  describeSingleWS(
    "WHEN creating and adding a remote self contained vault",
    { modConfigCb: enableSelfCOntainedVaults },
    () => {
      let vaultName: string;
      let remoteDir: string;
      before(async () => {
        // Create a self contained vault outside the current workspace
        remoteDir = tmpDir().name;
        await createSelfContainedVaultWithGit(remoteDir);
        vaultName = path.basename(remoteDir);

        sinon.stub(VSCodeUtils, "showQuickPick").resolves({ label: "remote" });
        sinon.stub(VSCodeUtils, "showInputBox").resolves(remoteDir);
        sinon.stub(vscode.commands, "executeCommand").resolves({}); // stub reload window

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

      test("THEN the vault is under `dependencies/remote`, and is self contained", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        // It's kinda hard to mock git cloning from a remote here, so the remote
        // we're using is a directory. Because of that, the name of the vault
        // will fall back to the directory name.
        const vaultPath = path.join(wsRoot, FOLDERS.DEPENDENCIES, vaultName);
        expect(await fs.pathExists(vaultPath)).toBeTruthy();
        expect(
          await fs.pathExists(path.join(vaultPath, FOLDERS.NOTES))
        ).toBeTruthy();
        expect(
          await readYAMLAsync(
            path.join(vaultPath, CONSTANTS.DENDRON_CONFIG_FILE)
          )
        ).toBeTruthy();
      });

      test("THEN the vault was added to the workspace config correctly", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        const config = DConfig.getOrCreate(wsRoot);
        const vault = VaultUtils.getVaultByName({
          vaults: ConfigUtils.getVaults(config),
          vname: vaultName,
        });
        expect(vault?.selfContained).toBeTruthy();
        expect(vault?.name).toEqual(vaultName);
        expect(vault?.fsPath).toEqual(
          path.join(FOLDERS.DEPENDENCIES, vaultName)
        );
        expect(vault?.remote?.url).toEqual(remoteDir);
      });

      test("THEN the notes in this vault are accessible", async () => {
        // Since we mock the reload window, need to reload index here to pick up the notes in the new vault
        await new ReloadIndexCommand().run();
        const { engine, vaults } = ExtensionProvider.getDWorkspace();
        const vault = VaultUtils.getVaultByName({
          vaults,
          vname: vaultName,
        });
        expect(vault).toBeTruthy();
        const note = NoteUtils.getNoteByFnameFromEngine({
          fname: "root",
          vault: vault!,
          engine,
        });
        expect(note).toBeTruthy();
        expect(note?.vault.name).toEqual(vaultName);
      });
    }
  );

  runTestButSkipForWindows()("", () => {
    describeSingleWS(
      "WHEN creating and adding a remote vault inside a native workspace",
      {
        modConfigCb: enableSelfCOntainedVaults,
        workspaceType: WorkspaceType.NATIVE,
      },
      () => {
        let remoteDir: string;
        let vaultName: string;
        before(async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          const tmpVaultPath = "tmp";
          remoteDir = path.join(wsRoot, tmpVaultPath);
          await createSelfContainedVaultWithGit(remoteDir);
          vaultName = path.basename(remoteDir);

          sinon
            .stub(VSCodeUtils, "showQuickPick")
            .resolves({ label: "remote" });
          sinon.stub(VSCodeUtils, "showInputBox").resolves(remoteDir);
          sinon.stub(vscode.commands, "executeCommand").resolves({}); // stub reload window

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

        test("THEN the vault is under `dependencies/remote`, and is self contained", async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          // It's kinda hard to mock git cloning from a remote here, so the remote
          // we're using is a directory. That means this looks like
          // `dependencies/tmp-123-foo` which is not "up to spec" but it's a good
          // fallback behavior
          const vaultPath = path.join(wsRoot, FOLDERS.DEPENDENCIES, vaultName);
          expect(await fs.pathExists(vaultPath)).toBeTruthy();
          expect(
            await fs.pathExists(path.join(vaultPath, FOLDERS.NOTES))
          ).toBeTruthy();
          expect(
            await readYAMLAsync(
              path.join(vaultPath, CONSTANTS.DENDRON_CONFIG_FILE)
            )
          ).toBeTruthy();
          // A workspace file hasn't been created
          expect(
            await fs.pathExists(path.join(wsRoot, CONSTANTS.DENDRON_WS_NAME))
          ).toBeFalsy();
        });

        test("THEN the vault was added to the workspace config correctly", async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          const config = DConfig.getOrCreate(wsRoot);
          const vault = VaultUtils.getVaultByName({
            vaults: ConfigUtils.getVaults(config),
            vname: vaultName,
          });
          expect(vault?.selfContained).toBeTruthy();
          expect(vault?.name).toEqual(vaultName);
          expect(vault?.fsPath).toEqual(
            path.join(FOLDERS.DEPENDENCIES, vaultName)
          );
        });

        test("THEN the notes in this vault are accessible", async () => {
          // Since we mock the reload window, need to reload index here to pick up the notes in the new vault
          await new ReloadIndexCommand().run();
          const { engine, vaults } = ExtensionProvider.getDWorkspace();
          const vault = VaultUtils.getVaultByName({
            vaults,
            vname: vaultName,
          });
          expect(vault).toBeTruthy();
          const note = NoteUtils.getNoteByFnameFromEngine({
            fname: "root",
            vault: vault!,
            engine,
          });
          expect(note).toBeTruthy();
          expect(note?.vault.name).toEqual(vaultName);
        });
      }
    );
  });

  describeSingleWS(
    "WHEN creating and adding a remote workspace vault",
    { modConfigCb: enableSelfCOntainedVaults },
    () => {
      let vaultName: string;
      let remoteDir: string;
      before(async () => {
        // Create a self contained vault outside the current workspace
        remoteDir = tmpDir().name;
        await createWorkspaceWithGit(remoteDir);
        vaultName = path.basename(remoteDir);

        sinon.stub(VSCodeUtils, "showQuickPick").resolves({ label: "remote" });
        sinon.stub(VSCodeUtils, "showInputBox").resolves(remoteDir);
        sinon.stub(vscode.commands, "executeCommand").resolves({}); // stub reload window

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

      test("THEN the vault is under `dependencies/remote`, and is a workspace vault", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        // It's kinda hard to mock git cloning from a remote here, so the remote
        // we're using is a directory. Because of that, the name of the vault
        // will fall back to the directory name.
        const vaultPath = path.join(wsRoot, vaultName);
        expect(await fs.pathExists(vaultPath)).toBeTruthy();
        expect(
          await readYAMLAsync(
            path.join(vaultPath, CONSTANTS.DENDRON_CONFIG_FILE)
          )
        ).toBeTruthy();
        expect(
          await fs.pathExists(path.join(vaultPath, FOLDERS.NOTES))
        ).toBeFalsy();
      });

      test("THEN the vault was added to the workspace config correctly", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        const config = DConfig.getOrCreate(wsRoot);
        expect(ConfigUtils.getVaults(config).length).toEqual(2);
        const vault = ConfigUtils.getVaults(config).find(
          (vault) => vault.workspace === vaultName
        );
        expect(vault?.selfContained).toBeFalsy();
        expect(vault?.fsPath).toEqual("vault");
        expect(config.workspace.workspaces).toBeTruthy();
        expect(config.workspace.workspaces![vaultName]).toBeTruthy();
        expect(config.workspace.workspaces![vaultName]?.remote.url).toEqual(
          remoteDir
        );
        expect(vault?.remote?.url).toBeFalsy();
      });

      test("THEN the notes in this vault are accessible", async () => {
        // Since we mock the reload window, need to reload index here to pick up the notes in the new vault
        await new ReloadIndexCommand().run();
        const { engine, wsRoot } = ExtensionProvider.getDWorkspace();
        const config = DConfig.getOrCreate(wsRoot);
        const vault = ConfigUtils.getVaults(config).find(
          (vault) => vault.workspace === vaultName
        );
        expect(vault).toBeTruthy();
        const note = NoteUtils.getNoteByFnameFromEngine({
          fname: "root",
          vault: vault!,
          engine,
        });
        expect(note).toBeTruthy();
        expect(note?.vault.workspace).toEqual(vaultName);
      });
    }
  );

  describeSingleWS(
    "WHEN creating and adding a remote regular (non self contained) vault",
    { modConfigCb: enableSelfCOntainedVaults },
    () => {
      let vaultName: string;
      let remoteDir: string;
      before(async () => {
        // Create a self contained vault outside the current workspace
        remoteDir = tmpDir().name;
        await createVaultWithGit(remoteDir);
        vaultName = path.basename(remoteDir);

        sinon.stub(VSCodeUtils, "showQuickPick").resolves({ label: "remote" });
        sinon.stub(VSCodeUtils, "showInputBox").resolves(remoteDir);
        sinon.stub(vscode.commands, "executeCommand").resolves({}); // stub reload window

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

      test("THEN the vault is under `dependencies/remote`, and is a regular vault", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        // It's kinda hard to mock git cloning from a remote here, so the remote
        // we're using is a directory. Because of that, the name of the vault
        // will fall back to the directory name.
        const vaultPath = path.join(wsRoot, FOLDERS.DEPENDENCIES, vaultName);
        expect(await fs.pathExists(vaultPath)).toBeTruthy();
        expect(
          await fs.pathExists(path.join(vaultPath, FOLDERS.NOTES))
        ).toBeFalsy();
        expect(
          await fs.pathExists(
            path.join(vaultPath, CONSTANTS.DENDRON_CONFIG_FILE)
          )
        ).toBeFalsy();
      });

      test("THEN the vault was added to the workspace config correctly", async () => {
        const { wsRoot } = ExtensionProvider.getDWorkspace();
        const config = DConfig.getOrCreate(wsRoot);
        expect(ConfigUtils.getVaults(config).length).toEqual(2);
        const vault = VaultUtils.getVaultByName({
          vaults: ConfigUtils.getVaults(config),
          vname: vaultName,
        });

        expect(vault?.selfContained).toBeFalsy();
        expect(vault?.fsPath).toEqual(
          path.join(FOLDERS.DEPENDENCIES, vaultName)
        );
        expect(vault?.remote?.url).toEqual(remoteDir);
      });

      test("THEN the notes in this vault are accessible", async () => {
        // Since we mock the reload window, need to reload index here to pick up the notes in the new vault
        await new ReloadIndexCommand().run();
        const { engine, vaults } = ExtensionProvider.getDWorkspace();
        const vault = VaultUtils.getVaultByName({
          vaults,
          vname: vaultName,
        });
        expect(vault).toBeTruthy();
        const note = NoteUtils.getNoteByFnameFromEngine({
          fname: "root",
          vault: vault!,
          engine,
        });
        expect(note).toBeTruthy();
        expect(note?.vault.name).toEqual(vaultName);
      });
    }
  );
});
Example #21
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 #22
Source File: RenameProvider.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("RenameProvider", function () {
  let activeNote: NoteProps;
  let targetNote: NoteProps;
  let editor: vscode.TextEditor;
  let provider: RenameProvider;

  describeMultiWS(
    "GIVEN wikilink",
    {
      preSetupHook: async (opts) => {
        const { wsRoot, vaults } = opts;
        activeNote = await NoteTestUtilsV4.createNote({
          fname: "active",
          vault: vaults[0],
          wsRoot,
          body: [
            "[[target]]", // line 7, char 2 ~ 8
            "[[Target|target]]", // line 8, char 9 ~ 15
            "[[Target|dendron://vault1/target]]", // line 9, char 26 ~ 32
            "[[Target|dendron://vault1/target#foo]]", // line 10, 26 ~ 32
          ].join("\n"),
        });
        targetNote = await NoteTestUtilsV4.createNote({
          fname: "target",
          vault: vaults[0],
          wsRoot,
          body: ["# Foo"].join("\n"),
        });
        await NoteTestUtilsV4.createNote({
          fname: "note-with-link",
          vault: vaults[0],
          wsRoot,
          body: ["[[target]]"].join("\n"),
        });
        await NoteTestUtilsV4.createNote({
          fname: "note-with-link-in-another-vault",
          vault: vaults[1],
          wsRoot,
          body: ["[[dendron://vault1/target]]"].join("\n"),
        });
      },
    },
    () => {
      beforeEach(async () => {
        editor = await WSUtils.openNote(activeNote);
        provider = new RenameProvider();
      });
      test("THEN range is properly provided", async () => {
        const positions = [
          new vscode.Position(7, 0),
          new vscode.Position(8, 0),
          new vscode.Position(9, 0),
          new vscode.Position(10, 0),
        ];
        const actualRanges = await Promise.all(
          positions.map(async (position) => {
            const range = await provider.prepareRename(
              editor.document,
              position
            );
            return range;
          })
        );
        const expectRanges = [
          new vscode.Range(
            new vscode.Position(7, 2),
            new vscode.Position(7, 8)
          ),
          new vscode.Range(
            new vscode.Position(8, 9),
            new vscode.Position(8, 15)
          ),
          new vscode.Range(
            new vscode.Position(9, 26),
            new vscode.Position(9, 32)
          ),
          new vscode.Range(
            new vscode.Position(10, 26),
            new vscode.Position(10, 32)
          ),
        ];
        expect(actualRanges).toEqual(expectRanges);
      });

      describe("WHEN rename is executed", () => {
        let executeOut: { changed: NoteChangeEntry[] } | undefined;
        before(async () => {
          provider.targetNote = targetNote;
          executeOut = await provider.executeRename({ newName: "new-target" });
        });
        test("THEN correctly renamed at symbol position", async () => {
          const { vaults, engine, wsRoot } = getDWorkspace();
          const { notes } = engine;
          const active = NoteUtils.getNoteByFnameV5({
            fname: "active",
            vault: vaults[0],
            notes,
            wsRoot,
          });
          const expectedBody = [
            "[[new-target]]",
            "[[New Target|new-target]]",
            "[[New Target|dendron://vault1/new-target]]",
            "[[New Target|dendron://vault1/new-target#foo]]",
            "",
          ].join("\n");
          expect(active?.body).toEqual(expectedBody);
        });

        test("AND target note is correctly renamed", (done) => {
          const { vaults, engine, wsRoot } = getDWorkspace();
          const { notes } = engine;
          const newTarget = NoteUtils.getNoteByFnameV5({
            fname: "new-target",
            vault: vaults[0],
            notes,
            wsRoot,
          });
          expect(newTarget).toBeTruthy();
          done();
        });
        test("THEN references to target note is correctly updated", (done) => {
          expect(executeOut?.changed.length).toEqual(7);
          const { vaults, engine, wsRoot } = getDWorkspace();
          const { notes } = engine;
          const noteWithLink = NoteUtils.getNoteByFnameV5({
            fname: "note-with-link",
            vault: vaults[0],
            notes,
            wsRoot,
          });
          const noteWithLinkInAnotherVault = NoteUtils.getNoteByFnameV5({
            fname: "note-with-link-in-another-vault",
            vault: vaults[1],
            notes,
            wsRoot,
          });

          expect(noteWithLink?.body).toEqual("[[new-target]]\n");
          expect(noteWithLinkInAnotherVault?.body).toEqual(
            "[[dendron://vault1/new-target]]\n"
          );
          done();
        });
      });
    }
  );

  describeMultiWS(
    "GIVEN note references",
    {
      preSetupHook: async (opts) => {
        const { wsRoot, vaults } = opts;
        activeNote = await NoteTestUtilsV4.createNote({
          fname: "active",
          vault: vaults[0],
          wsRoot,
          body: [
            "![[target]]", // line 7, char 2 ~ 8
            "![[dendron://vault1/target]]", // line 9, char 26 ~ 32
            "![[dendron://vault1/target#foo]]", // line 10, 26 ~ 32
          ].join("\n"),
        });
        targetNote = await NoteTestUtilsV4.createNote({
          fname: "target",
          vault: vaults[0],
          wsRoot,
          body: ["# Foo"].join("\n"),
        });
        await NoteTestUtilsV4.createNote({
          fname: "note-with-link",
          vault: vaults[0],
          wsRoot,
          body: ["![[target]]"].join("\n"),
        });
        await NoteTestUtilsV4.createNote({
          fname: "note-with-link-in-another-vault",
          vault: vaults[1],
          wsRoot,
          body: ["![[dendron://vault1/target]]"].join("\n"),
        });
      },
    },
    () => {
      beforeEach(async () => {
        editor = await WSUtils.openNote(activeNote);
        provider = new RenameProvider();
      });
      test("THEN range is properly provided", async () => {
        const positions = [
          new vscode.Position(7, 1),
          new vscode.Position(8, 1),
          new vscode.Position(9, 1),
        ];
        const actualRanges = await Promise.all(
          positions.map(async (position) => {
            const range = await provider.prepareRename(
              editor.document,
              position
            );
            return range;
          })
        );
        const expectRanges = [
          new vscode.Range(
            new vscode.Position(7, 3),
            new vscode.Position(7, 9)
          ),
          new vscode.Range(
            new vscode.Position(8, 26),
            new vscode.Position(8, 20)
          ),
          new vscode.Range(
            new vscode.Position(9, 26),
            new vscode.Position(9, 20)
          ),
        ];
        expect(actualRanges).toEqual(expectRanges);
      });

      describe("WHEN rename is executed", () => {
        let executeOut: { changed: NoteChangeEntry[] } | undefined;
        before(async () => {
          provider.targetNote = targetNote;
          executeOut = await provider.executeRename({ newName: "new-target" });
        });
        test("THEN correctly renamed at symbol position", async () => {
          const { vaults, engine, wsRoot } = getDWorkspace();
          const { notes } = engine;
          const active = NoteUtils.getNoteByFnameV5({
            fname: "active",
            vault: vaults[0],
            notes,
            wsRoot,
          });
          const expectedBody = [
            "![[new-target]]",
            "![[dendron://vault1/new-target]]",
            "![[dendron://vault1/new-target#foo]]",
            "",
          ].join("\n");
          expect(active?.body).toEqual(expectedBody);
        });

        test("AND target note is correctly renamed", (done) => {
          const { vaults, engine, wsRoot } = getDWorkspace();
          const { notes } = engine;
          const newTarget = NoteUtils.getNoteByFnameV5({
            fname: "new-target",
            vault: vaults[0],
            notes,
            wsRoot,
          });
          expect(newTarget).toBeTruthy();
          done();
        });
        test("THEN references to target note is correctly updated", (done) => {
          expect(executeOut?.changed.length).toEqual(7);
          const { vaults, engine, wsRoot } = getDWorkspace();
          const { notes } = engine;
          const noteWithLink = NoteUtils.getNoteByFnameV5({
            fname: "note-with-link",
            vault: vaults[0],
            notes,
            wsRoot,
          });
          const noteWithLinkInAnotherVault = NoteUtils.getNoteByFnameV5({
            fname: "note-with-link-in-another-vault",
            vault: vaults[1],
            notes,
            wsRoot,
          });

          expect(noteWithLink?.body).toEqual("![[new-target]]\n");
          expect(noteWithLinkInAnotherVault?.body).toEqual(
            "![[dendron://vault1/new-target]]\n"
          );
          done();
        });
      });
    }
  );

  describeMultiWS(
    "GIVEN hashtag",
    {
      preSetupHook: async (opts) => {
        const { wsRoot, vaults } = opts;
        activeNote = await NoteTestUtilsV4.createNote({
          fname: "active",
          vault: vaults[0],
          wsRoot,
          body: [
            "#target", // line 7, char 2 ~ 8
          ].join("\n"),
        });
        targetNote = await NoteTestUtilsV4.createNote({
          fname: "tags.target",
          vault: vaults[0],
          wsRoot,
          body: ["# Foo"].join("\n"),
        });
        await NoteTestUtilsV4.createNote({
          fname: "note-with-link",
          vault: vaults[0],
          wsRoot,
          body: ["#target"].join("\n"),
        });
      },
    },
    () => {
      beforeEach(async () => {
        editor = await WSUtils.openNote(activeNote);
        provider = new RenameProvider();
      });
      test("THEN range is properly provided", async () => {
        const position = new vscode.Position(7, 0);
        const actualRange = await provider.prepareRename(
          editor.document,
          position
        );

        const expectRange = new vscode.Range(
          new vscode.Position(7, 1),
          new vscode.Position(7, 7)
        );
        expect(actualRange).toEqual(expectRange);
      });

      describe("WHEN rename is executed", () => {
        let executeOut: { changed: NoteChangeEntry[] } | undefined;
        before(async () => {
          provider.targetNote = targetNote;
          executeOut = await provider.executeRename({ newName: "new-target" });
        });
        test("THEN correctly renamed at symbol position", async () => {
          const { vaults, engine, wsRoot } = getDWorkspace();
          const { notes } = engine;
          const active = NoteUtils.getNoteByFnameV5({
            fname: "active",
            vault: vaults[0],
            notes,
            wsRoot,
          });
          const expectedBody = "#new-target\n";
          expect(active?.body).toEqual(expectedBody);
        });

        test("AND target note is correctly renamed", (done) => {
          const { vaults, engine, wsRoot } = getDWorkspace();
          const { notes } = engine;
          const newTarget = NoteUtils.getNoteByFnameV5({
            fname: "tags.new-target",
            vault: vaults[0],
            notes,
            wsRoot,
          });
          expect(newTarget).toBeTruthy();
          done();
        });
        test("THEN references to target note is correctly updated", (done) => {
          expect(executeOut?.changed.length).toEqual(8);
          const { vaults, engine, wsRoot } = getDWorkspace();
          const { notes } = engine;
          const noteWithLink = NoteUtils.getNoteByFnameV5({
            fname: "note-with-link",
            vault: vaults[0],
            notes,
            wsRoot,
          });

          expect(noteWithLink?.body).toEqual("#new-target\n");
          done();
        });
      });
    }
  );

  describeMultiWS(
    "GIVEN frontmatter tag",
    {
      preSetupHook: async (opts) => {
        const { wsRoot, vaults } = opts;
        activeNote = await NoteTestUtilsV4.createNote({
          fname: "active",
          vault: vaults[0],
          wsRoot,
          props: { tags: "target" },
        });
        targetNote = await NoteTestUtilsV4.createNote({
          fname: "tags.target",
          vault: vaults[0],
          wsRoot,
          body: ["# Foo"].join("\n"),
        });
        await NoteTestUtilsV4.createNote({
          fname: "note-with-link",
          vault: vaults[0],
          wsRoot,
          props: { tags: "target" },
        });
      },
    },
    () => {
      beforeEach(async () => {
        editor = await WSUtils.openNote(activeNote);
        provider = new RenameProvider();
      });
      test("THEN range is properly provided", async () => {
        const position = new vscode.Position(6, 7);
        const actualRange = await provider.prepareRename(
          editor.document,
          position
        );
        const expectRange = new vscode.Range(
          new vscode.Position(6, 6),
          new vscode.Position(6, 12)
        );
        expect(actualRange).toEqual(expectRange);
      });

      describe("WHEN rename is executed", () => {
        let executeOut: { changed: NoteChangeEntry[] } | undefined;
        before(async () => {
          provider.targetNote = targetNote;
          executeOut = await provider.executeRename({ newName: "new-target" });
        });
        test("THEN correctly renamed at symbol position", async () => {
          const { vaults, engine, wsRoot } = getDWorkspace();
          const { notes } = engine;
          const active = NoteUtils.getNoteByFnameV5({
            fname: "active",
            vault: vaults[0],
            notes,
            wsRoot,
          });
          expect(active?.tags).toEqual("new-target");
        });

        test("AND target note is correctly renamed", (done) => {
          const { vaults, engine, wsRoot } = getDWorkspace();
          const { notes } = engine;
          const newTarget = NoteUtils.getNoteByFnameV5({
            fname: "tags.new-target",
            vault: vaults[0],
            notes,
            wsRoot,
          });
          expect(newTarget).toBeTruthy();
          done();
        });
        test("THEN references to target note is correctly updated", (done) => {
          expect(executeOut?.changed.length).toEqual(8);
          const { vaults, engine, wsRoot } = getDWorkspace();
          const { notes } = engine;
          const noteWithLink = NoteUtils.getNoteByFnameV5({
            fname: "note-with-link",
            vault: vaults[0],
            notes,
            wsRoot,
          });

          expect(noteWithLink?.tags).toEqual("new-target");

          done();
        });
      });
    }
  );

  describeMultiWS(
    "GIVEN usertag",
    {
      preSetupHook: async (opts) => {
        const { wsRoot, vaults } = opts;
        activeNote = await NoteTestUtilsV4.createNote({
          fname: "active",
          vault: vaults[0],
          wsRoot,
          body: [
            "@target", // line 7, char 2 ~ 8
          ].join("\n"),
        });
        targetNote = await NoteTestUtilsV4.createNote({
          fname: "user.target",
          vault: vaults[0],
          wsRoot,
          body: ["# Foo"].join("\n"),
        });
        await NoteTestUtilsV4.createNote({
          fname: "note-with-link",
          vault: vaults[0],
          wsRoot,
          body: ["@target"].join("\n"),
        });
      },
    },
    () => {
      beforeEach(async () => {
        editor = await WSUtils.openNote(activeNote);
        provider = new RenameProvider();
      });
      test("THEN range is properly provided", async () => {
        const position = new vscode.Position(7, 0);
        const actualRange = await provider.prepareRename(
          editor.document,
          position
        );

        const expectRange = new vscode.Range(
          new vscode.Position(7, 1),
          new vscode.Position(7, 7)
        );
        expect(actualRange).toEqual(expectRange);
      });

      describe("WHEN rename is executed", () => {
        let executeOut: { changed: NoteChangeEntry[] } | undefined;
        before(async () => {
          provider.targetNote = targetNote;
          executeOut = await provider.executeRename({ newName: "new-target" });
        });
        test("THEN correctly renamed at symbol position", async () => {
          const { vaults, engine, wsRoot } = getDWorkspace();
          const { notes } = engine;
          const active = NoteUtils.getNoteByFnameV5({
            fname: "active",
            vault: vaults[0],
            notes,
            wsRoot,
          });
          const expectedBody = "@new-target\n";
          expect(active?.body).toEqual(expectedBody);
        });

        test("AND target note is correctly renamed", (done) => {
          const { vaults, engine, wsRoot } = getDWorkspace();
          const { notes } = engine;
          const newTarget = NoteUtils.getNoteByFnameV5({
            fname: "user.new-target",
            vault: vaults[0],
            notes,
            wsRoot,
          });
          expect(newTarget).toBeTruthy();
          done();
        });
        test("THEN references to target note is correctly updated", (done) => {
          expect(executeOut?.changed.length).toEqual(8);
          const { vaults, engine, wsRoot } = getDWorkspace();
          const { notes } = engine;
          const noteWithLink = NoteUtils.getNoteByFnameV5({
            fname: "note-with-link",
            vault: vaults[0],
            notes,
            wsRoot,
          });

          expect(noteWithLink?.body).toEqual("@new-target\n");
          done();
        });
      });
    }
  );
});
Example #23
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 #24
Source File: ReferenceHoverProvider.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("GIVEN ReferenceHoverProvider", function () {
  const ctx = setupBeforeAfter(this);

  describeMultiWS(
    "AND WHEN used in non-dendron file",
    {
      ctx,
      preSetupHook: async (opts) => {
        return FileTestUtils.createFiles(opts.wsRoot, [
          { path: "sample.with-header", body: "## Foo" },
          { path: "sample.empty", body: "" },
        ]);
      },
    },
    () => {
      describe("AND the file is empty", () => {
        test("THEN return null", async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          const notePath = path.join(wsRoot, "sample.empty");
          const editor = await VSCodeUtils.openFileInEditor(
            vscode.Uri.file(notePath)
          );
          const hover = await provideForNonNote(editor!);
          expect(hover).toEqual(null);
        });
      });

      describe("AND file has a header", () => {
        test("THEN return null", async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          const notePath = path.join(wsRoot, "sample.with-header");
          const editor = await VSCodeUtils.openFileInEditor(
            vscode.Uri.file(notePath)
          );
          const hover = await provideForNonNote(editor!);
          expect(hover).toEqual(null);
        });
      });
    }
  );

  describe("has correct hover contents", () => {
    describe("wikilink", () => {
      test("basic", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "target",
              body: [
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ].join("\n"),
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: "[[target]]",
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const provider = new ReferenceHoverProvider();
            const hover = await provider.provideHover(
              editor.document,
              new vscode.Position(7, 4)
            );
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: (hover!.contents[0] as MarkdownString).value,
              match: [
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ],
            });
            done();
          },
        });
      });

      test("missing notes are marked as such", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: "[[target]]", // target doesn't exist
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const hover = await provideForNote(editor);
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: hover!.contents.join(""),
              match: [
                `Note target is missing`,
                `use "Dendron: Go to Note" command`,
              ],
            });
            done();
          },
        });
      });

      test.skip("contains local image", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "target",
              body: [
                "Sint quo sunt maxime.",
                "![](/assets/test/image.png)",
              ].join("\n"),
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: "[[target]]",
            });
          },
          onInit: async ({ wsRoot, vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const hover = await provideForNote(editor);
            expect(hover).toBeTruthy();
            // Local images should get full path to image, because hover preview otherwise can't find the image
            await AssertUtils.assertInString({
              body: (hover!.contents[0] as MarkdownString).value,
              match: [
                "Sint quo sunt maxime.",
                `![](${path.join(
                  wsRoot,
                  VaultUtils.getRelPath(vaults[0]),
                  "assets/test/image.png"
                )})`,
              ],
            });
            done();
          },
        });
      });

      test("contains remote image", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "target",
              body: [
                "Sint quo sunt maxime.",
                "![](https://example.com/image.png)",
              ].join("\n"),
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: "[[target]]",
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const provider = new ReferenceHoverProvider();
            const hover = await provider.provideHover(
              editor.document,
              new vscode.Position(7, 4)
            );
            expect(hover).toBeTruthy();
            // remote images should be unmodified
            await AssertUtils.assertInString({
              body: (hover!.contents[0] as MarkdownString).value,
              match: [
                "Sint quo sunt maxime.",
                "![](https://example.com/image.png)",
              ],
            });
            done();
          },
        });
      });

      test("with alias", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "target",
              body: [
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ].join("\n"),
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: "[[my note alias|target]]",
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const hover = await provideForNote(editor);
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: (hover!.contents[0] as MarkdownString).value,
              match: [
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ],
            });
            done();
          },
        });
      });

      test("with xvault", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            // Creating a note of the same name in multiple vaults to check that it gets the right one
            await NoteTestUtilsV4.createNote({
              vault: vaults[1],
              wsRoot,
              fname: "target",
              body: [
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ].join("\n"),
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "target",
              body: "Voluptatem possimus harum nisi.",
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: `[[dendron://${VaultUtils.getName(vaults[1])}/target]]`,
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const hover = await provideForNote(editor);
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: (hover!.contents[0] as MarkdownString).value,
              match: [
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ],
              nomatch: ["Voluptatem possimus harum nisi."],
            });
            done();
          },
        });
      });

      test("with header", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "target",
              body: [
                "Voluptatem possimus harum nisi.",
                "",
                "# Numquam occaecati",
                "",
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ].join("\n"),
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: `[[target#numquam-occaecati]]`,
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const provider = new ReferenceHoverProvider();
            const hover = await provider.provideHover(
              editor.document,
              new vscode.Position(7, 12)
            );
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: (hover!.contents[0] as MarkdownString).value,
              match: [
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ],
              nomatch: ["Voluptatem possimus harum nisi."],
            });
            done();
          },
        });
      });

      test("with block anchor", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "target",
              body: [
                "Voluptatem possimus harum nisi.",
                "",
                "# Numquam occaecati",
                "",
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima. ^my-anchor",
              ].join("\n"),
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: `[[target#^my-anchor]]`,
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const hover = await provideForNote(editor);
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: (hover!.contents[0] as MarkdownString).value,
              match: [
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ],
              nomatch: ["Voluptatem possimus harum nisi.", "Numquam occaecati"],
            });
            done();
          },
        });
      });

      test("with everything", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await NoteTestUtilsV4.createNote({
              vault: vaults[1],
              wsRoot,
              fname: "target",
              body: [
                "Voluptatem possimus harum nisi.",
                "",
                "# Numquam occaecati",
                "",
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ].join("\n"),
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "target",
              body: "Voluptatem possimus harum nisi.",
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: `[[My note: with an alias|dendron://${VaultUtils.getName(
                vaults[1]
              )}/target#numquam-occaecati]]`,
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const hover = await provideForNote(editor);
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: (hover!.contents[0] as MarkdownString).value,
              match: [
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ],
              nomatch: ["Voluptatem possimus harum nisi."],
            });
            done();
          },
        });
      });

      describe("multiple notes & xvault link", () => {
        test("non-xvault link resolves to same vault", (done) => {
          let note: NoteProps;
          runLegacyMultiWorkspaceTest({
            ctx,
            preSetupHook: async (opts) => {
              note = await ENGINE_HOOKS_MULTI.setupMultiVaultSameFname(opts);
            },
            onInit: async () => {
              const editor = await WSUtils.openNote(note);
              editor.selection = LocationTestUtils.getPresetWikiLinkSelection({
                line: 7,
              });
              const hover = await provideForNote(editor);
              expect(hover).toBeTruthy();
              expect(
                await AssertUtils.assertInString({
                  body: (hover!.contents[0] as MarkdownString).value,
                  match: ["vault 1"],
                  nomatch: ["vault 0", "the test note"],
                })
              ).toBeTruthy();
              done();
            },
          });
        });

        test("xvault link to other vault", (done) => {
          let note: NoteProps;
          runLegacyMultiWorkspaceTest({
            ctx,
            preSetupHook: async (opts) => {
              note = await ENGINE_HOOKS_MULTI.setupMultiVaultSameFname(opts);
            },
            onInit: async () => {
              const editor = await WSUtils.openNote(note);
              const provider = new ReferenceHoverProvider();
              const hover = await provider.provideHover(
                editor.document,
                new vscode.Position(8, 4)
              );
              expect(hover).toBeTruthy();
              expect(
                await AssertUtils.assertInString({
                  body: (hover!.contents[0] as MarkdownString).value,
                  match: ["vault 0"],
                  nomatch: ["vault 1", "the test note"],
                })
              ).toBeTruthy();
              done();
            },
          });
        });

        test("xvault link to same vault", (done) => {
          let note: NoteProps;
          runLegacyMultiWorkspaceTest({
            ctx,
            preSetupHook: async (opts) => {
              note = await ENGINE_HOOKS_MULTI.setupMultiVaultSameFname(opts);
            },
            onInit: async () => {
              const editor = await WSUtils.openNote(note);
              const provider = new ReferenceHoverProvider();
              const hover = await provider.provideHover(
                editor.document,
                new vscode.Position(9, 4)
              );
              expect(hover).toBeTruthy();
              expect(
                await AssertUtils.assertInString({
                  body: (hover!.contents[0] as MarkdownString).value,
                  match: ["vault 1"],
                  nomatch: ["vault 0", "the test note"],
                })
              ).toBeTruthy();
              done();
            },
          });
        });

        test("xvault link to non-existant note", (done) => {
          let note: NoteProps;
          runLegacyMultiWorkspaceTest({
            ctx,
            preSetupHook: async (opts) => {
              note = await ENGINE_HOOKS_MULTI.setupMultiVaultSameFname(opts);
            },
            onInit: async () => {
              const editor = await WSUtils.openNote(note);
              const provider = new ReferenceHoverProvider();
              const hover = await provider.provideHover(
                editor.document,
                new vscode.Position(10, 4)
              );
              expect(hover).toBeTruthy();
              expect(
                await AssertUtils.assertInString({
                  body: hover!.contents.join(""),
                  match: ["eggs", "vaultThree", "missing"],
                  nomatch: [
                    "vault 0",
                    "vault 1",
                    "vault1",
                    "vault2",
                    "the test note",
                  ],
                })
              ).toBeTruthy();
              done();
            },
          });
        });

        test("xvault link to non-existant vault", (done) => {
          let note: NoteProps;
          runLegacyMultiWorkspaceTest({
            ctx,
            preSetupHook: async (opts) => {
              note = await ENGINE_HOOKS_MULTI.setupMultiVaultSameFname(opts);
            },
            onInit: async () => {
              const editor = await WSUtils.openNote(note);
              const provider = new ReferenceHoverProvider();
              const hover = await provider.provideHover(
                editor.document,
                new vscode.Position(11, 4)
              );
              expect(hover).toBeTruthy();
              expect(
                await AssertUtils.assertInString({
                  body: hover!.contents.join(""),
                  match: ["vault3", "does not exist"],
                  nomatch: [
                    "vault 0",
                    "vault 1",
                    "vault1",
                    "vault2",
                    "the test note",
                  ],
                })
              ).toBeTruthy();
              done();
            },
          });
        });
      });
    });

    describe("reference", () => {
      test("basic", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "target",
              body: [
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ].join("\n"),
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: "![[target]]",
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const hover = await provideForNote(editor);
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: (hover!.contents[0] as MarkdownString).value,
              match: [
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ],
            });
            done();
          },
        });
      });

      test("with xvault", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            // Creating a note of the same name in multiple vaults to check that it gets the right one
            await NoteTestUtilsV4.createNote({
              vault: vaults[1],
              wsRoot,
              fname: "target",
              body: [
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ].join("\n"),
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "target",
              body: "Voluptatem possimus harum nisi.",
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: `![[dendron://${VaultUtils.getName(vaults[1])}/target]]`,
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const hover = await provideForNote(editor);
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: (hover!.contents[0] as MarkdownString).value,
              match: [
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ],
              nomatch: ["Voluptatem possimus harum nisi."],
            });
            done();
          },
        });
      });

      test("with header", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "target",
              body: [
                "Voluptatem possimus harum nisi.",
                "",
                "# Numquam occaecati",
                "",
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ].join("\n"),
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: `![[target#numquam-occaecati]]`,
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const hover = await provideForNote(editor);
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: (hover!.contents[0] as MarkdownString).value,
              match: [
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ],
              nomatch: ["Voluptatem possimus harum nisi."],
            });
            done();
          },
        });
      });

      test("with block anchor", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "target",
              body: [
                "Voluptatem possimus harum nisi.",
                "",
                "# Numquam occaecati",
                "",
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima. ^my-anchor",
              ].join("\n"),
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: `![[target#^my-anchor]]`,
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const hover = await provideForNote(editor);
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: (hover!.contents[0] as MarkdownString).value,
              match: [
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ],
              nomatch: ["Voluptatem possimus harum nisi.", "Numquam occaecati"],
            });
            done();
          },
        });
      });

      test("with range", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "target",
              body: [
                "Voluptatem possimus harum nisi.",
                "",
                "# Numquam occaecati",
                "",
                "Sint quo sunt maxime.",
                "",
                "Nisi nam dolorem qui ut minima. ^my-anchor",
                "",
                "Ut quo eius laudantium.",
              ].join("\n"),
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: `![[target#numquam-occaecati:#^my-anchor]]`,
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const hover = await provideForNote(editor);
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: (hover!.contents[0] as MarkdownString).value,
              match: [
                "Numquam occaecati",
                "Sint quo sunt maxime.",
                "Nisi nam dolorem qui ut minima.",
              ],
              nomatch: [
                "Voluptatem possimus harum nisi.",
                "Ut quo eius laudantium.",
              ],
            });
            done();
          },
        });
      });
    });

    test("hashtag", (done) => {
      runLegacyMultiWorkspaceTest({
        ctx,
        preSetupHook: async ({ wsRoot, vaults }) => {
          await NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "tags.foo.test",
            body: "this is tag foo.test",
          });
          await NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "source",
            body: "#foo.test",
          });
        },
        onInit: async ({ vaults }) => {
          const editor = await WSUtils.openNoteByPath({
            vault: vaults[0],
            fname: "source",
          });
          const provider = new ReferenceHoverProvider();
          const hover = await provider.provideHover(
            editor.document,
            new vscode.Position(7, 6)
          );
          expect(hover).toBeTruthy();
          await AssertUtils.assertInString({
            body: (hover!.contents[0] as MarkdownString).value,
            match: ["this is tag foo.test"],
          });
          done();
        },
      });
    });

    test("hashtags are ignored when disabled", (done) => {
      runLegacyMultiWorkspaceTest({
        ctx,
        preSetupHook: async ({ wsRoot, vaults }) => {
          await NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "tags.foo.test",
            body: "this is tag foo.test",
          });
          await NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "source",
            body: "#foo.test",
          });
        },
        modConfigCb: (config) => {
          config.workspace!.enableHashTags = false;
          return config;
        },
        onInit: async ({ vaults }) => {
          const editor = await WSUtils.openNoteByPath({
            vault: vaults[0],
            fname: "source",
          });
          const provider = new ReferenceHoverProvider();
          const hover = await provider.provideHover(
            editor.document,
            new vscode.Position(7, 6)
          );
          expect(hover).toBeFalsy();
          done();
        },
      });
    });

    test("user tag", (done) => {
      runLegacyMultiWorkspaceTest({
        ctx,
        preSetupHook: async ({ wsRoot, vaults }) => {
          await NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "user.test.mctestface",
            body: "this is user test.mctestface",
          });
          await NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "source",
            body: "@test.mctestface",
          });
        },
        onInit: async ({ vaults }) => {
          const editor = await WSUtils.openNoteByPath({
            vault: vaults[0],
            fname: "source",
          });
          const provider = new ReferenceHoverProvider();
          const hover = await provider.provideHover(
            editor.document,
            new vscode.Position(7, 6)
          );
          expect(hover).toBeTruthy();
          await AssertUtils.assertInString({
            body: (hover!.contents[0] as MarkdownString).value,
            match: ["this is user test.mctestface"],
          });
          done();
        },
      });
    });

    test("user tags are ignored when disabled", (done) => {
      runLegacyMultiWorkspaceTest({
        ctx,
        preSetupHook: async ({ wsRoot, vaults }) => {
          await NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "user.test.mctestface",
            body: "this is user test.mctestface",
          });
          await NoteTestUtilsV4.createNote({
            vault: vaults[0],
            wsRoot,
            fname: "source",
            body: "@test.mctestface",
          });
        },
        modConfigCb: (config) => {
          config.workspace!.enableUserTags = false;
          return config;
        },
        onInit: async ({ vaults }) => {
          const editor = await WSUtils.openNoteByPath({
            vault: vaults[0],
            fname: "source",
          });
          const provider = new ReferenceHoverProvider();
          const hover = await provider.provideHover(
            editor.document,
            new vscode.Position(7, 6)
          );
          expect(hover).toBeFalsy();
          done();
        },
      });
    });

    describe("frontmatter tags", () => {
      test("single tag", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "tags.foo.test",
              body: "this is tag foo.test",
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              props: {
                tags: "foo.test",
              },
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const provider = new ReferenceHoverProvider();
            const hover = await provider.provideHover(
              editor.document,
              new vscode.Position(6, 10)
            );
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: (hover!.contents[0] as MarkdownString).value,
              match: ["this is tag foo.test"],
            });
            done();
          },
        });
      });

      test("multiple tags", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "tags.foo.test",
              body: "this is tag foo.test",
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "tags.foo.bar",
              body: "this is the wrong tag",
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "tags.foo.baz",
              body: "this is the wrong tag",
            });
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              props: {
                tags: ["foo.bar", "foo.test", "foo.baz"],
              },
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const provider = new ReferenceHoverProvider();
            const hover = await provider.provideHover(
              editor.document,
              new vscode.Position(8, 4)
            );
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: (hover!.contents[0] as MarkdownString).value,
              match: ["this is tag foo.test"],
            });
            done();
          },
        });
      });
    });

    describe("non-note", () => {
      test("image", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await fs.ensureFile(
              path.join(
                wsRoot,
                VaultUtils.getName(vaults[0]),
                "assets",
                "image.png"
              )
            );
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: "![[/assets/image.png]]",
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const provider = new ReferenceHoverProvider();
            const hover = await provider.provideHover(
              editor.document,
              new vscode.Position(7, 4)
            );
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: hover!.contents.join(""),
              match: ["[", "]", "(", "/assets/image.png", ")"],
            });
            done();
          },
        });
      });

      test("hyperlink", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: "![[http://example.com]]",
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const provider = new ReferenceHoverProvider();
            const hover = await provider.provideHover(
              editor.document,
              new vscode.Position(7, 4)
            );
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: hover!.contents.join(""),
              match: ["[", "]", "(", "http://example.com", ")"],
            });
            done();
          },
        });
      });

      test("email", (done) => {
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: "![[mailto:[email protected]]]",
            });
          },
          onInit: async ({ vaults }) => {
            const editor = await WSUtils.openNoteByPath({
              vault: vaults[0],
              fname: "source",
            });
            const provider = new ReferenceHoverProvider();
            const hover = await provider.provideHover(
              editor.document,
              new vscode.Position(7, 4)
            );
            expect(hover).toBeTruthy();
            await AssertUtils.assertInString({
              body: hover!.contents.join(""),
              match: ["[", "]", "(", "mailto:[email protected]", ")"],
            });
            done();
          },
        });
      });

      describeSingleWS(
        "WHEN used on a link to a non-note file",
        { ctx },
        () => {
          before(async () => {
            const { wsRoot, vaults } = getDWorkspace();
            await fs.writeFile(
              path.join(wsRoot, "test.txt"),
              "Et nam velit laboriosam."
            );
            const note = await NoteTestUtilsV4.createNote({
              vault: vaults[0],
              wsRoot,
              fname: "source",
              body: ["[[test.txt]]", "[[test.txt#L1]]"].join("\n"),
            });
            await WSUtils.openNote(note);
          });

          test("THEN displays message to open it with the default app", async () => {
            const editor = VSCodeUtils.getActiveTextEditorOrThrow();
            const provider = new ReferenceHoverProvider();
            const hover = await provider.provideHover(
              editor.document,
              new vscode.Position(7, 4)
            );
            expect(hover).toBeTruthy();
            expect(
              await AssertUtils.assertInString({
                body: hover!.contents.join(""),
                match: ["test.txt"],
              })
            ).toBeTruthy();
          });

          describe("AND the link has a line anchor", () => {
            test("THEN displays message to open it with the default app", async () => {
              const editor = VSCodeUtils.getActiveTextEditorOrThrow();
              const provider = new ReferenceHoverProvider();
              const hover = await provider.provideHover(
                editor.document,
                new vscode.Position(8, 4)
              );
              expect(hover).toBeTruthy();
              expect(
                await AssertUtils.assertInString({
                  body: hover!.contents.join(""),
                  match: ["test.txt"],
                  nomatch: ["L6"],
                })
              ).toBeTruthy();
            });
          });
        }
      );
    });
  });
});
Example #25
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 #26
Source File: PreviewLinkHandler.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("PreviewLinkHandler", () => {
  const ctx: vscode.ExtensionContext = setupBeforeAfter(this, {
    noSetTimeout: true,
    beforeHook: () => {},
  });

  let testNoteAmbiguous: NoteProps;
  describeMultiWS(
    "GIVEN onLinkClicked",
    {
      ctx,
      preSetupHook: async ({ vaults, wsRoot }) => {
        await NoteTestUtilsV4.createNote({
          fname: "target",
          vault: vaults[0],
          wsRoot,
          body: [
            "Qui dicta nulla at atque qui voluptatem.",
            "Harum qui quasi sint.",
            "",
            "## Nostrum",
            "",
            "Ut recusandae fuga recusandae nihil.",
            "Illum nostrum id animi. ^nihil",
          ].join("\n"),
          props: {
            id: "test-id",
          },
        });
        await NoteTestUtilsV4.createNote({
          fname: "lorem",
          vault: vaults[0],
          wsRoot,
          body: "Est saepe ut et accusamus soluta id",
          props: {
            id: "est",
          },
        });
        testNoteAmbiguous = await NoteTestUtilsV4.createNote({
          fname: "lorem",
          vault: vaults[1],
          wsRoot,
          body: "Reprehenderit dolores pariatur",
          props: {
            id: "reprehenderit",
          },
        });
      },
    },
    () => {
      let note: NoteProps;
      beforeEach(async () => {
        const { engine, vaults } = ExtensionProvider.getDWorkspace();
        note = NoteUtils.getNoteByFnameFromEngine({
          fname: "root",
          engine,
          vault: vaults[0],
        })!;
        expect(note).toBeTruthy();
        await ExtensionProvider.getWSUtils().openNote(note);
      });

      describe("WHEN clicking on an wikilink", () => {
        test("THEN the clicked note is opened", async () => {
          const handler = new PreviewLinkHandler(
            ExtensionProvider.getExtension()
          );
          const out = await handler.onLinkClicked({
            data: {
              href: "vscode-webview://76b3da02-f902-4652-b6a8-746551d032ce/test-id#nostrum",
              id: note.id,
            },
          });
          expect(out).toEqual(LinkType.WIKI);
          expect(
            VSCodeUtils.getActiveTextEditor()?.document.fileName.endsWith(
              "target.md"
            )
          ).toBeTruthy();
        });

        describe("AND the link is to a header", () => {
          test("THEN the note is opened at that header", async () => {
            const handler = new PreviewLinkHandler(
              ExtensionProvider.getExtension()
            );
            const out = await handler.onLinkClicked({
              data: {
                href: "vscode-webview://76b3da02-f902-4652-b6a8-746551d032ce/test-id#nostrum",
                id: note.id,
              },
            });
            expect(out).toEqual(LinkType.WIKI);
            expect(
              VSCodeUtils.getActiveTextEditor()?.document.fileName.endsWith(
                "target.md"
              )
            ).toBeTruthy();
            expect(
              VSCodeUtils.getActiveTextEditor()?.selection.active.line
            ).toEqual(10);
          });
        });

        describe("AND the link is to a block anchor", () => {
          test("THEN the note is opened at that block", async () => {
            const handler = new PreviewLinkHandler(
              ExtensionProvider.getExtension()
            );
            const out = await handler.onLinkClicked({
              data: {
                href: "vscode-webview://76b3da02-f902-4652-b6a8-746551d032ce/test-id#^nihil",
                id: note.id,
              },
            });
            expect(out).toEqual(LinkType.WIKI);
            expect(
              VSCodeUtils.getActiveTextEditor()?.document.fileName.endsWith(
                "target.md"
              )
            ).toBeTruthy();
            expect(
              VSCodeUtils.getActiveTextEditor()?.selection.active.line
            ).toEqual(13);
          });
        });

        describe("AND if the link is to a missing note", () => {
          test("THEN nothing happens", async () => {
            const handler = new PreviewLinkHandler(
              ExtensionProvider.getExtension()
            );
            const openWithDefaultApp = sinon.stub(
              ShowPreviewAssetOpener,
              "openWithDefaultApp"
            );
            const out = await handler.onLinkClicked({
              data: {
                href: "vscode-webview://76b3da02-f902-4652-b6a8-746551d032ce/does-not-exist",
                id: note.id,
              },
            });
            expect(out).toEqual(LinkType.UNKNOWN);
            expect(openWithDefaultApp.called).toBeFalsy();
          });
        });

        describe("AND the link is ambiguous", () => {
          test("THEN it prompts for a note", async () => {
            const showChooseNote = sinon
              .stub(QuickPickUtil, "showChooseNote")
              .returns(Promise.resolve(testNoteAmbiguous));
            const handler = new PreviewLinkHandler(
              ExtensionProvider.getExtension()
            );
            const out = await handler.onLinkClicked({
              data: {
                href: "vscode-webview://76b3da02-f902-4652-b6a8-746551d032ce/lorem",
                id: note.id,
              },
            });
            expect(out).toEqual(LinkType.WIKI);
            expect(
              VSCodeUtils.getActiveTextEditor()
                ?.document.getText()
                .includes("Reprehenderit dolores pariatur")
            ).toBeTruthy();
            expect(showChooseNote.called).toBeTruthy();
          });
        });
      });

      describe("WHEN clicking on a web URL", () => {
        test("THEN opening is left to VSCode", async () => {
          const openWithDefaultApp = sinon.stub(
            ShowPreviewAssetOpener,
            "openWithDefaultApp"
          );
          const handler = new PreviewLinkHandler(
            ExtensionProvider.getExtension()
          );
          const out = await handler.onLinkClicked({
            data: {
              href: "https://wiki.dendron.so/#getting-started",
              id: note.id,
            },
          });
          expect(out).toEqual(LinkType.WEBSITE);
          expect(openWithDefaultApp.called).toBeFalsy();
        });
      });

      describe("WHEN clicking on an asset inside a vault", () => {
        before(async () => {
          const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
          const assetsPath = path.join(
            wsRoot,
            VaultUtils.getRelPath(vaults[0]),
            "assets"
          );
          await fs.mkdir(assetsPath);
          await fs.writeFile(path.join(assetsPath, "test.pdf"), "");
        });

        test("THEN it is opened with the default app", async () => {
          const { wsRoot, vaults } = ExtensionProvider.getDWorkspace();
          const openWithDefaultApp = sinon.stub(
            ShowPreviewAssetOpener,
            "openWithDefaultApp"
          );
          const handler = new PreviewLinkHandler(
            ExtensionProvider.getExtension()
          );
          const out = await handler.onLinkClicked({
            data: {
              href: "vscode-webview://76b3da02-f902-4652-b6a8-746551d032ce/assets/test.pdf",
              id: note.id,
            },
          });
          expect(out).toEqual(LinkType.ASSET);
          expect(openWithDefaultApp.called).toBeTruthy();
          expect(
            openWithDefaultApp.calledWith(
              path.join(
                wsRoot,
                VaultUtils.getRelPath(vaults[0]),
                "assets",
                "test.pdf"
              )
            )
          ).toBeTruthy();
        });
      });

      describe("WHEN clicking on an asset with path relative to wsRoot", () => {
        before(async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          await fs.writeFile(path.join(wsRoot, "test.pdf"), "");
        });

        test("THEN it is opened with the default app", async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          const openWithDefaultApp = sinon.stub(
            ShowPreviewAssetOpener,
            "openWithDefaultApp"
          );
          const handler = new PreviewLinkHandler(
            ExtensionProvider.getExtension()
          );
          const out = await handler.onLinkClicked({
            data: {
              href: "vscode-webview://76b3da02-f902-4652-b6a8-746551d032ce/test.pdf",
              id: note.id,
            },
          });
          expect(out).toEqual(LinkType.ASSET);
          expect(openWithDefaultApp.called).toBeTruthy();
          expect(
            openWithDefaultApp.calledWith(path.join(wsRoot, "test.pdf"))
          ).toBeTruthy();
        });
      });

      describe("WHEN clicking on an asset with an absolute path", () => {
        let testDir: string;
        before(async () => {
          testDir = tmpDir().name;
          await fs.writeFile(path.join(testDir, "test.pdf"), "");
        });

        test("THEN it is opened with the default app", async () => {
          const openWithDefaultApp = sinon.stub(
            ShowPreviewAssetOpener,
            "openWithDefaultApp"
          );
          const handler = new PreviewLinkHandler(
            ExtensionProvider.getExtension()
          );
          const out = await handler.onLinkClicked({
            data: {
              href: `vscode-webview://76b3da02-f902-4652-b6a8-746551d032ce/${path.join(
                testDir,
                "test.pdf"
              )}`,
              id: note.id,
            },
          });
          expect(out).toEqual(LinkType.ASSET);
          expect(openWithDefaultApp.called).toBeTruthy();
          // Added the "toLowerCase"s here because on Windows link handler
          // gets called with C:\ while testDir is c:\
          expect(openWithDefaultApp.args[0][0].toLowerCase()).toEqual(
            path.join(testDir, "test.pdf").toLowerCase()
          );
        });
      });

      describe("WHEN opening a non-note text file", () => {
        before(async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          await fs.writeFile(
            path.join(wsRoot, "test.py"),
            [
              "print('hello world!')",
              "print('hello from a test')",
              "print('hi!') # ^target",
              "print('hey!!!')",
            ].join("\n")
          );
        });

        test("THEN it is opened in the editor", async () => {
          const openWithDefaultApp = sinon.stub(
            ShowPreviewAssetOpener,
            "openWithDefaultApp"
          );
          const handler = new PreviewLinkHandler(
            ExtensionProvider.getExtension()
          );
          const out = await handler.onLinkClicked({
            data: {
              href: "vscode-webview://76b3da02-f902-4652-b6a8-746551d032ce/test.py",
              id: note.id,
            },
          });
          expect(out).toEqual(LinkType.TEXT);
          expect(openWithDefaultApp.called).toBeFalsy();
          expect(
            VSCodeUtils.getActiveTextEditor()?.document.fileName.endsWith(
              "test.py"
            )
          ).toBeTruthy();
        });

        describe("AND the file link is to a line", () => {
          test("THEN it is opened at that line", async () => {
            const openWithDefaultApp = sinon.stub(
              ShowPreviewAssetOpener,
              "openWithDefaultApp"
            );
            const handler = new PreviewLinkHandler(
              ExtensionProvider.getExtension()
            );
            const out = await handler.onLinkClicked({
              data: {
                href: "vscode-webview://76b3da02-f902-4652-b6a8-746551d032ce/test.py#L2",
                id: note.id,
              },
            });
            expect(out).toEqual(LinkType.TEXT);
            expect(openWithDefaultApp.called).toBeFalsy();
            expect(
              VSCodeUtils.getActiveTextEditor()?.document.fileName.endsWith(
                "test.py"
              )
            ).toBeTruthy();
            expect(
              VSCodeUtils.getActiveTextEditor()?.selection.start.line
            ).toEqual(1);
          });
        });

        describe("AND the file link is to an anchor", () => {
          test("THEN it is opened at that anchor", async () => {
            const openWithDefaultApp = sinon.stub(
              ShowPreviewAssetOpener,
              "openWithDefaultApp"
            );
            const handler = new PreviewLinkHandler(
              ExtensionProvider.getExtension()
            );
            const out = await handler.onLinkClicked({
              data: {
                href: "vscode-webview://76b3da02-f902-4652-b6a8-746551d032ce/test.py#^target",
                id: note.id,
              },
            });
            expect(out).toEqual(LinkType.TEXT);
            expect(openWithDefaultApp.called).toBeFalsy();
            expect(
              VSCodeUtils.getActiveTextEditor()?.document.fileName.endsWith(
                "test.py"
              )
            ).toBeTruthy();
            expect(
              VSCodeUtils.getActiveTextEditor()?.selection.start.line
            ).toEqual(2);
          });
        });
      });
    }
  );

  describe(`extractNoteIdFromHref`, () => {
    describe(`WHEN id is present`, () => {
      it("AND with header anchor THEN extract id", () => {
        const linkHandler = new PreviewLinkHandler(
          new MockDendronExtension({})
        );
        const actual = linkHandler.extractNoteIdFromHref({
          id: "id1",
          href: "vscode-webview://4e98b9cf-41d8-49eb-b458-fcfda32c6c01/FSi3bKWQeQXYTjE1PoTB0#heading-2",
        });

        expect(actual).toEqual("FSi3bKWQeQXYTjE1PoTB0");
      });

      it("AND without the header anchor THEN extract id", () => {
        const linkHandler = new PreviewLinkHandler(
          new MockDendronExtension({})
        );

        const actual = linkHandler.extractNoteIdFromHref({
          id: "id1",
          href: "vscode-webview://4e98b9cf-41d8-49eb-b458-fcfda32c6c01/FSi3bKWQeQXYTjE1PoTB0",
        });

        expect(actual).toEqual("FSi3bKWQeQXYTjE1PoTB0");
      });

      it("AND is guid like", () => {
        // This shouldnt typically happen with the way we currently generate ids but we do
        // have some guid like ids in our test workspace right now so to make those
        // notes happy, and in case some older id generation used guid looking identifers.

        const linkHandler = new PreviewLinkHandler(
          new MockDendronExtension({})
        );

        const actual = linkHandler.extractNoteIdFromHref({
          id: "id1",
          href: "vscode-webview://4e98b9cf-41d8-49eb-b458-fcfda32c6c01/56497553-c195-4ec8-bc74-6a76462d9333",
        });

        expect(actual).toEqual("56497553-c195-4ec8-bc74-6a76462d9333");
      });
    });

    it(`WHEN id not present in href THEN default onto passed in id`, () => {
      const linkHandler = new PreviewLinkHandler(new MockDendronExtension({}));

      const actual = linkHandler.extractNoteIdFromHref({
        id: "id1",
        href: "http://localhost:3005/vscode/note-preview.html?ws=WS-VALUE&port=3005#head2",
      });
      expect(actual).toEqual("id1");
    });
  });
});
Example #27
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 #28
Source File: GotoNote.test.ts    From dendron with GNU Affero General Public License v3.0 4 votes vote down vote up
suite("GotoNote", function () {
  describe("new style tests", () => {
    const preSetupHook = ENGINE_HOOKS.setupBasic;

    describeMultiWS(
      "WHEN pass in note",
      {
        preSetupHook,
      },
      () => {
        test("THEN goto note", async () => {
          const { vaults, engine } = ExtensionProvider.getDWorkspace();
          const vault = vaults[0];
          const note = engine.notes["foo"];
          const { note: out } = (await createGoToNoteCmd().run({
            qs: "foo",
            vault,
          })) as { note: NoteProps };
          expect(out).toEqual(note);
          expect(getActiveEditorBasename()).toEqual("foo.md");
        });
      }
    );

    describeMultiWS(
      "WHEN goto stub",
      {
        preSetupHook: async ({ wsRoot, vaults }) => {
          const vault = vaults[0];
          await ENGINE_HOOKS.setupBasic({ wsRoot, vaults });
          const vpath = vault2Path({ vault, wsRoot });
          fs.removeSync(path.join(vpath, "foo.md"));
        },
      },
      () => {
        test("THEN get note", async () => {
          const { vaults, engine } = ExtensionProvider.getDWorkspace();
          const vault = vaults[0];
          const note = NoteUtils.getNoteByFnameV5({
            fname: "foo",
            notes: engine.notes,
            vault,
            wsRoot: getDWorkspace().wsRoot,
          }) as NoteProps;
          expect(_.pick(note, ["fname", "stub"])).toEqual({
            fname: "foo",
            stub: true,
          });

          const { note: out } = (await createGoToNoteCmd().run({
            qs: "foo",
            vault,
          })) as { note: NoteProps };
          expect(_.pick(out, ["fname", "stub", "id"])).toEqual({
            fname: "foo",
            id: note.id,
          });
          expect(getActiveEditorBasename()).toEqual("foo.md");
        });
      }
    );

    describeMultiWS(
      "WHEN goto new note",
      {
        preSetupHook,
      },
      () => {
        test("THEN note created", async () => {
          const { vaults } = ExtensionProvider.getDWorkspace();
          const vault = vaults[0];
          const { note: out } = (await createGoToNoteCmd().run({
            qs: "foo.ch2",
            vault,
          })) as { note: NoteProps };
          expect(_.pick(out, ["fname", "stub"])).toEqual({
            fname: "foo.ch2",
          });
          expect(getActiveEditorBasename()).toEqual("foo.ch2.md");
        });
      }
    );

    describeMultiWS(
      "WHEN goto note with template",
      {
        preSetupHook,
        postSetupHook: async ({ wsRoot, vaults }) => {
          await ENGINE_HOOKS.setupSchemaPreseet({ wsRoot, vaults });
        },
      },
      () => {
        test("THEN apply template", async () => {
          const { vaults } = ExtensionProvider.getDWorkspace();
          const vault = vaults[0];
          await createGoToNoteCmd().run({
            qs: "bar.ch1",
            vault,
          });
          expect(getActiveEditorBasename()).toEqual("bar.ch1.md");
          const content =
            VSCodeUtils.getActiveTextEditor()?.document.getText() as string;
          expect(content.indexOf("ch1 template") >= 0).toBeTruthy();
        });
      }
    );

    describeMultiWS(
      "GIVEN a new note and a template in different vaults",
      {
        preSetupHook: ENGINE_HOOKS_MULTI.setupBasicMulti,
        postSetupHook: async ({ wsRoot, vaults }) => {
          await ENGINE_HOOKS.setupSchemaPreseet({ wsRoot, vaults });
        },
      },
      () => {
        test("THEN new note uses that template", async () => {
          // Template is in vault 1. Note is in vault 2
          const { vaults } = ExtensionProvider.getDWorkspace();
          const vault = vaults[1];
          await createGoToNoteCmd().run({
            qs: "bar.ch1",
            vault,
          });
          expect(getActiveEditorBasename()).toEqual("bar.ch1.md");
          const content =
            VSCodeUtils.getActiveTextEditor()?.document.getText() as string;
          expect(content.indexOf("ch1 template") >= 0).toBeTruthy();
        });
      }
    );

    describeMultiWS(
      "GIVEN a new note and multiple templates in different vaults with the same name",
      {
        preSetupHook: ENGINE_HOOKS_MULTI.setupBasicMulti,
        postSetupHook: async ({ wsRoot, vaults }) => {
          // Template is in vault 1 and 3
          await ENGINE_HOOKS.setupSchemaPreseet({ wsRoot, vaults });
          await NoteTestUtilsV4.createNote({
            wsRoot,
            genRandomId: true,
            body: "food ch2 template in vaultThree",
            fname: "bar.template.ch1",
            vault: vaults[2],
          });
        },
      },
      () => {
        test("THEN new note uses the template that's in the same vault", async () => {
          // Try to create note in vault 3
          const { vaults } = ExtensionProvider.getDWorkspace();
          const vault = vaults[2];
          await createGoToNoteCmd().run({
            qs: "bar.ch1",
            vault,
          });
          expect(getActiveEditorBasename()).toEqual("bar.ch1.md");
          const content =
            VSCodeUtils.getActiveTextEditor()?.document.getText() as string;
          expect(
            content.indexOf("food ch2 template in vaultThree") >= 0
          ).toBeTruthy();
        });
      }
    );

    describeMultiWS(
      "WHEN goto note with anchor",
      {
        preSetupHook: async (opts) => {
          await ANCHOR.preSetupHook(opts);
        },
      },
      () => {
        test("THEN goto anchor", async () => {
          const { vaults } = ExtensionProvider.getDWorkspace();
          const vault = vaults[0];
          await createGoToNoteCmd().run({
            qs: "alpha",
            vault,
            anchor: {
              type: "header",
              value: "H3",
            },
          });
          expect(getActiveEditorBasename()).toEqual("alpha.md");
          const selection = VSCodeUtils.getActiveTextEditor()?.selection;
          expect(selection?.start.line).toEqual(9);
          expect(selection?.start.character).toEqual(0);
        });
      }
    );

    describeMultiWS(
      "WHEN go to note header with wikilink and unicode characters",
      {
        preSetupHook: async ({ vaults, wsRoot }) => {
          await NoteTestUtilsV4.createNote({
            wsRoot,
            vault: vaults[0],
            fname: "target-note",
            body: "\n\n## Lörem [[Foo:Bar?Baz|foo:bar?baz]] Ipsum\n\nlorem ipsum",
          });
        },
      },
      () => {
        test("THEN goto ehader", async () => {
          const { vaults } = ExtensionProvider.getDWorkspace();
          const vault = vaults[0];
          await createGoToNoteCmd().run({
            qs: "target-note",
            vault,
            anchor: {
              type: "header",
              value: "lörem-foo:barbaz-ipsum",
            },
          });
          expect(getActiveEditorBasename()).toEqual("target-note.md");
          const selection = VSCodeUtils.getActiveTextEditor()?.selection;
          expect(selection?.start.line).toEqual(9);
          expect(selection?.start.character).toEqual(0);
        });
      }
    );

    let specialCharsHeader: string;
    describeMultiWS(
      "WHEN anchor with special chars",
      {
        preSetupHook: async (opts) => {
          ({ specialCharsHeader } =
            await ANCHOR_WITH_SPECIAL_CHARS.preSetupHook(opts));
        },
      },
      () => {
        test("THEN goto anchor", async () => {
          const { vaults } = ExtensionProvider.getDWorkspace();
          const vault = vaults[0];
          await createGoToNoteCmd().run({
            qs: "alpha",
            vault,
            anchor: {
              type: "header",
              value: specialCharsHeader,
            },
          });
          expect(getActiveEditorBasename()).toEqual("alpha.md");
          const selection = VSCodeUtils.getActiveTextEditor()?.selection;
          expect(selection?.start.line).toEqual(9);
          expect(selection?.start.character).toEqual(0);
        });
      }
    );
  });

  const ctx = setupBeforeAfter(this, {});
  describe("using args", () => {
    test("block anchor", (done) => {
      runLegacyMultiWorkspaceTest({
        ctx,
        preSetupHook: async ({ wsRoot, vaults }) => {
          await NOTE_PRESETS_V4.NOTE_WITH_BLOCK_ANCHOR_TARGET.create({
            wsRoot,
            vault: vaults[0],
          });
        },
        onInit: async ({ vaults }) => {
          const vault = vaults[0];
          await createGoToNoteCmd().run({
            qs: "anchor-target",
            vault,
            anchor: {
              type: "block",
              value: "block-id",
            },
          });
          expect(getActiveEditorBasename()).toEqual("anchor-target.md");
          const selection = VSCodeUtils.getActiveTextEditor()?.selection;
          expect(selection?.start.line).toEqual(10);
          expect(selection?.start.character).toEqual(0);
          done();
        },
      });
    });

    test("hashtag", (done) => {
      let note: NoteProps;
      runLegacyMultiWorkspaceTest({
        ctx,
        preSetupHook: async ({ wsRoot, vaults }) => {
          // Create a note with a hashtag in it
          note = await NoteTestUtilsV4.createNote({
            wsRoot,
            vault: vaults[0],
            fname: "test.note",
            body: "#my.test-0.tag",
          });
        },
        onInit: async () => {
          // Open the note, select the hashtag, and use the command
          await WSUtils.openNote(note);
          VSCodeUtils.getActiveTextEditorOrThrow().selection =
            new vscode.Selection(
              new vscode.Position(7, 1),
              new vscode.Position(7, 1)
            );
          await createGoToNoteCmd().run();
          // Make sure this took us to the tag note
          expect(getActiveEditorBasename()).toEqual("tags.my.test-0.tag.md");
          done();
        },
      });
    });

    test("user tag", (done) => {
      let note: NoteProps;
      runLegacyMultiWorkspaceTest({
        ctx,
        preSetupHook: async ({ wsRoot, vaults }) => {
          // Create a note with a hashtag in it
          note = await NoteTestUtilsV4.createNote({
            wsRoot,
            vault: vaults[0],
            fname: "test.note",
            body: "@test.mctestface",
          });
        },
        onInit: async () => {
          // Open the note, select the hashtag, and use the command
          await WSUtils.openNote(note);
          VSCodeUtils.getActiveTextEditorOrThrow().selection =
            new vscode.Selection(
              new vscode.Position(7, 1),
              new vscode.Position(7, 1)
            );
          await createGoToNoteCmd().run();
          // Make sure this took us to the tag note
          expect(getActiveEditorBasename()).toEqual("user.test.mctestface.md");
          done();
        },
      });
    });

    describe("frontmatter tags", () => {
      test("single tag", (done) => {
        let note: NoteProps;
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            // Create a note with a hashtag in it
            note = await NoteTestUtilsV4.createNote({
              wsRoot,
              vault: vaults[0],
              fname: "test.note",
              props: {
                tags: "my.test-0.tag",
              },
            });
          },
          onInit: async () => {
            // Open the note, select the hashtag, and use the command
            await WSUtils.openNote(note);
            VSCodeUtils.getActiveTextEditorOrThrow().selection =
              new vscode.Selection(
                new vscode.Position(6, 8),
                new vscode.Position(6, 8)
              );
            await createGoToNoteCmd().run();
            // Make sure this took us to the tag note
            expect(getActiveEditorBasename()).toEqual("tags.my.test-0.tag.md");
            done();
          },
        });
      });

      test("tag containing space", (done) => {
        let note: NoteProps;
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            // Create a note with a hashtag in it
            note = await NoteTestUtilsV4.createNote({
              wsRoot,
              vault: vaults[0],
              fname: "test.note",
              props: {
                tags: "one ",
              },
            });
          },
          onInit: async () => {
            // Open the note, select the hashtag, and use the command
            await WSUtils.openNote(note);
            VSCodeUtils.getActiveTextEditorOrThrow().selection =
              new vscode.Selection(
                new vscode.Position(6, 8),
                new vscode.Position(6, 8)
              );
            await createGoToNoteCmd().run();
            // Make sure this took us to the tag note
            expect(getActiveEditorBasename()).toEqual("tags.one.md");
            done();
          },
        });
      });

      test("multiple tags", (done) => {
        let note: NoteProps;
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async ({ wsRoot, vaults }) => {
            // Create a note with a hashtag in it
            note = await NoteTestUtilsV4.createNote({
              wsRoot,
              vault: vaults[0],
              fname: "test.note",
              props: {
                tags: ["foo", "my.test-0.tag", "bar"],
              },
            });
          },
          onInit: async () => {
            // Open the note, select the hashtag, and use the command
            await WSUtils.openNote(note);
            VSCodeUtils.getActiveTextEditorOrThrow().selection =
              new vscode.Selection(
                new vscode.Position(8, 6),
                new vscode.Position(8, 6)
              );
            await createGoToNoteCmd().run();
            // Make sure this took us to the tag note
            expect(getActiveEditorBasename()).toEqual("tags.my.test-0.tag.md");
            done();
          },
        });
      });
    });
  });

  describe("using selection", () => {
    describeMultiWS(
      "WHEN link in code block",
      {
        preSetupHook: GOTO_NOTE_PRESETS.LINK_IN_CODE_BLOCK.preSetupHook,
      },
      () => {
        test("THEN opens the note", async () => {
          const ext = ExtensionProvider.getExtension();
          await GOTO_NOTE_PRESETS.LINK_IN_CODE_BLOCK.beforeTestResults({ ext });
          await createGoToNoteCmd().run();
          await runMochaHarness(GOTO_NOTE_PRESETS.LINK_IN_CODE_BLOCK.results);
        });
      }
    );

    test("xvault", (done) => {
      runLegacyMultiWorkspaceTest({
        ctx,
        preSetupHook: async (opts) => {
          await ENGINE_HOOKS_MULTI.setupLinksMulti(opts);
        },
        onInit: async ({ engine, vaults }) => {
          const note = engine.notes[NOTE_PRESETS_V4.NOTE_WITH_TARGET.fname];
          const editor = await WSUtils.openNote(note);
          const linkPos = LocationTestUtils.getPresetWikiLinkPosition();
          editor.selection = new vscode.Selection(linkPos, linkPos);
          // foo.ch1.md
          await createGoToNoteCmd().run({});
          const editor2 = VSCodeUtils.getActiveTextEditorOrThrow();
          const suffix =
            path.join(
              vaults[1].fsPath,
              NOTE_PRESETS_V4.NOTE_WITH_ANCHOR_LINK.fname
            ) + ".md";
          expect(editor2.document.uri.fsPath.endsWith(suffix)).toBeTruthy();
          done();
        },
      });
    });

    describe("multiple notes & xvault link", () => {
      test("non-xvault link prompts for vault", (done) => {
        let note: NoteProps;
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async (opts) => {
            note = await ENGINE_HOOKS_MULTI.setupMultiVaultSameFname(opts);
          },
          onInit: async ({ vaults, wsRoot }) => {
            const prompt = sinon
              .stub(PickerUtilsV2, "promptVault")
              .returns(Promise.resolve(vaults[1]));
            try {
              const editor = await WSUtils.openNote(note);
              editor.selection = LocationTestUtils.getPresetWikiLinkSelection({
                line: 7,
              });
              await createGoToNoteCmd().run();
              const openedNote = WSUtils.getNoteFromDocument(
                VSCodeUtils.getActiveTextEditorOrThrow().document
              );
              expect(openedNote?.fname).toEqual("eggs");
              expect(
                VaultUtils.isEqual(openedNote!.vault, vaults[1], wsRoot)
              ).toBeTruthy();
              expect(prompt.calledOnce).toBeTruthy();
              done();
            } finally {
              prompt.restore();
            }
          },
        });
      });

      test("xvault link to other vault", (done) => {
        let note: NoteProps;
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async (opts) => {
            note = await ENGINE_HOOKS_MULTI.setupMultiVaultSameFname(opts);
          },
          onInit: async ({ vaults, wsRoot }) => {
            const editor = await WSUtils.openNote(note);
            editor.selection = LocationTestUtils.getPresetWikiLinkSelection({
              line: 8,
            });
            await createGoToNoteCmd().run();
            const openedNote = WSUtils.getNoteFromDocument(
              VSCodeUtils.getActiveTextEditorOrThrow().document
            );
            expect(openedNote?.fname).toEqual("eggs");
            expect(
              VaultUtils.isEqual(openedNote!.vault, vaults[0], wsRoot)
            ).toBeTruthy();
            done();
          },
        });
      });

      test("xvault link to same vault", (done) => {
        let note: NoteProps;
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async (opts) => {
            note = await ENGINE_HOOKS_MULTI.setupMultiVaultSameFname(opts);
          },
          onInit: async ({ vaults, wsRoot }) => {
            const editor = await WSUtils.openNote(note);
            editor.selection = LocationTestUtils.getPresetWikiLinkSelection({
              line: 9,
            });
            await createGoToNoteCmd().run();
            const openedNote = WSUtils.getNoteFromDocument(
              VSCodeUtils.getActiveTextEditorOrThrow().document
            );
            expect(openedNote?.fname).toEqual("eggs");
            expect(
              VaultUtils.isEqual(openedNote!.vault, vaults[1], wsRoot)
            ).toBeTruthy();
            done();
          },
        });
      });

      test("xvault link to non-existant note", (done) => {
        let note: NoteProps;
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async (opts) => {
            note = await ENGINE_HOOKS_MULTI.setupMultiVaultSameFname(opts);
          },
          onInit: async ({ vaults, wsRoot }) => {
            const editor = await WSUtils.openNote(note);
            editor.selection = LocationTestUtils.getPresetWikiLinkSelection({
              line: 10,
            });
            await createGoToNoteCmd().run();
            const openedNote = WSUtils.getNoteFromDocument(
              VSCodeUtils.getActiveTextEditorOrThrow().document
            );
            // Should have created the note in this vault
            expect(openedNote?.fname).toEqual("eggs");
            expect(
              VaultUtils.isEqual(openedNote!.vault, vaults[2], wsRoot)
            ).toBeTruthy();
            done();
          },
        });
      });

      test("xvault link to non-existant vault", (done) => {
        let note: NoteProps;
        runLegacyMultiWorkspaceTest({
          ctx,
          preSetupHook: async (opts) => {
            note = await ENGINE_HOOKS_MULTI.setupMultiVaultSameFname(opts);
          },
          onInit: async ({ vaults, wsRoot }) => {
            const editor = await WSUtils.openNote(note);
            editor.selection = LocationTestUtils.getPresetWikiLinkSelection({
              line: 11,
            });
            await createGoToNoteCmd().run();
            const openedNote = WSUtils.getNoteFromDocument(
              VSCodeUtils.getActiveTextEditorOrThrow().document
            );
            // Should not have changed notes
            expect(openedNote?.fname).toEqual("test");
            expect(
              VaultUtils.isEqual(openedNote!.vault, vaults[1], wsRoot)
            ).toBeTruthy();
            done();
          },
        });
      });
    });

    test("xvault with multiple matches", (done) => {
      runLegacyMultiWorkspaceTest({
        ctx,
        preSetupHook: async (opts) => {
          await ENGINE_HOOKS_MULTI.setupLinksMulti(opts);
          await NOTE_PRESETS_V4.NOTE_WITH_ANCHOR_LINK.create({
            vault: opts.vaults[2],
            wsRoot: opts.wsRoot,
            genRandomId: true,
          });
        },
        onInit: async ({ engine, vaults }) => {
          sinon
            .stub(PickerUtilsV2, "promptVault")
            .returns(Promise.resolve(vaults[1]));
          const note = engine.notes[NOTE_PRESETS_V4.NOTE_WITH_TARGET.fname];
          const editor = await WSUtils.openNote(note);
          const linkPos = LocationTestUtils.getPresetWikiLinkPosition();
          editor.selection = new vscode.Selection(linkPos, linkPos);
          await createGoToNoteCmd().run({});
          const editor2 = VSCodeUtils.getActiveTextEditorOrThrow();
          const suffix =
            path.join(
              vaults[1].fsPath,
              NOTE_PRESETS_V4.NOTE_WITH_ANCHOR_LINK.fname
            ) + ".md";
          expect(editor2.document.uri.fsPath.endsWith(suffix)).toBeTruthy();
          done();
        },
      });
    });

    test("multi-link in same line", (done) => {
      runLegacyMultiWorkspaceTest({
        ctx,
        preSetupHook: async (opts) => {
          await ENGINE_HOOKS.setupBasic(opts);
          const { wsRoot } = opts;
          const vault = opts.vaults[0];
          await NoteTestUtilsV4.modifyNoteByPath(
            { wsRoot, vault, fname: "foo" },
            (note) => {
              note.body =
                "this is a [[foolink]]. this is another link [[foo.ch1]]";
              return note;
            }
          );
        },
        onInit: async ({ engine, vaults }) => {
          const note = engine.notes["foo"];
          const editor = await WSUtils.openNote(note);
          // put cursor in location on 48
          editor.selection = new vscode.Selection(
            new vscode.Position(7, 48),
            new vscode.Position(7, 48)
          );
          // foo.ch1.md
          await createGoToNoteCmd().run({
            vault: vaults[0],
          });
          expect(getActiveEditorBasename()).toEqual("foo.ch1.md");
          done();
        },
      });
    });

    describe("GIVEN non-note files", () => {
      describeMultiWS("WHEN used on a link to a non-note file", { ctx }, () => {
        before(async () => {
          const { wsRoot, vaults } = getDWorkspace();
          await fs.writeFile(
            path.join(wsRoot, "test.txt"),
            "Et voluptatem autem sunt."
          );
          await fs.ensureDir(
            path.join(wsRoot, VaultUtils.getRelPath(vaults[1]), "assets")
          );
          await fs.writeFile(
            path.join(
              wsRoot,
              VaultUtils.getRelPath(vaults[1]),
              "assets",
              "test.txt"
            ),
            "Et hic est voluptatem eum quia quas pariatur."
          );
        });

        test("THEN opens the non-note file", async () => {
          const { vaults, wsRoot, engine } = getDWorkspace();
          const note = await NoteTestUtilsV4.createNoteWithEngine({
            wsRoot,
            vault: vaults[0],
            fname: "test.note",
            body: "[[/test.txt]]",
            engine,
          });

          await WSUtils.openNote(note);
          VSCodeUtils.getActiveTextEditorOrThrow().selection =
            new vscode.Selection(7, 1, 7, 1);
          await createGoToNoteCmd().run();

          expect(getActiveEditorBasename()).toEqual("test.txt");
          expect(
            VSCodeUtils.getActiveTextEditorOrThrow().document.getText().trim()
          ).toEqual("Et voluptatem autem sunt.");
        });

        describe("AND the link doesn't include a slash", () => {
          before(async () => {
            const { vaults, wsRoot, engine } = getDWorkspace();
            const note = await NoteTestUtilsV4.createNoteWithEngine({
              wsRoot,
              vault: vaults[0],
              fname: "test.note",
              body: "[[test.txt]]",
              engine,
            });

            await WSUtils.openNote(note);
            VSCodeUtils.getActiveTextEditorOrThrow().selection =
              new vscode.Selection(7, 1, 7, 1);
            await createGoToNoteCmd().run();
          });

          test("THEN opens the non-note file", async () => {
            expect(getActiveEditorBasename()).toEqual("test.txt");
            expect(
              VSCodeUtils.getActiveTextEditorOrThrow().document.getText().trim()
            ).toEqual("Et voluptatem autem sunt.");
          });
        });

        describe("AND the link starts with assets", () => {
          before(async () => {
            const { vaults, wsRoot, engine } = getDWorkspace();
            const note = await NoteTestUtilsV4.createNoteWithEngine({
              wsRoot,
              vault: vaults[0],
              fname: "test.note2",
              body: "[[assets/test.txt]]",
              engine,
            });

            await WSUtils.openNote(note);
            VSCodeUtils.getActiveTextEditorOrThrow().selection =
              new vscode.Selection(7, 1, 7, 1);
            await createGoToNoteCmd().run();
          });

          test("THEN opens the non-note file inside assets", async () => {
            expect(getActiveEditorBasename()).toEqual("test.txt");
            expect(
              VSCodeUtils.getActiveTextEditorOrThrow().document.getText().trim()
            ).toEqual("Et hic est voluptatem eum quia quas pariatur.");
          });
        });
      });

      describeMultiWS(
        "WHEN there's a note and non-note file with the same name",
        { ctx },
        () => {
          before(async () => {
            const { wsRoot, vaults, engine } = getDWorkspace();
            const note = await NoteTestUtilsV4.createNoteWithEngine({
              wsRoot,
              vault: vaults[0],
              fname: "test.note",
              body: "[[test.txt]]",
              engine,
            });
            await NoteTestUtilsV4.createNoteWithEngine({
              wsRoot,
              vault: vaults[0],
              fname: "test.txt",
              body: "Accusantium id et sunt cum esse.",
              engine,
            });
            await fs.writeFile(
              path.join(wsRoot, "test.txt"),
              "Et voluptatem autem sunt."
            );

            await WSUtils.openNote(note);
            VSCodeUtils.getActiveTextEditorOrThrow().selection =
              new vscode.Selection(7, 1, 7, 1);
            await createGoToNoteCmd().run();
          });

          test("THEN opens the note", async () => {
            expect(getActiveEditorBasename()).toEqual("test.txt.md");
            expect(
              AssertUtils.assertInString({
                body: VSCodeUtils.getActiveTextEditorOrThrow().document.getText(),
                match: ["Accusantium id et sunt cum esse."],
                nomatch: ["Voluptatibus et totam qui eligendi qui quaerat."],
              })
            ).toBeTruthy();
          });
        }
      );

      describeMultiWS(
        "WHEN linked to a specific line inside of that file",
        { ctx },
        () => {
          before(async () => {
            const { wsRoot, vaults, engine } = getDWorkspace();
            const note = await NoteTestUtilsV4.createNoteWithEngine({
              wsRoot,
              vault: vaults[0],
              fname: "test.note",
              body: "[[test.txt#L3]]",
              engine,
            });
            await fs.writeFile(
              path.join(wsRoot, "test.txt"),
              [
                "Aut fugit eos sint eos explicabo.",
                "Ut dolores fugit qui deserunt.",
                "Animi et recusandae in blanditiis sapiente.",
                "Consequatur est repellat non.",
              ].join("\n")
            );

            await WSUtils.openNote(note);
            VSCodeUtils.getActiveTextEditorOrThrow().selection =
              new vscode.Selection(7, 1, 7, 1);
            await createGoToNoteCmd().run();
          });

          test("THEN opens the file at that line", async () => {
            expect(getActiveEditorBasename()).toEqual("test.txt");
            expect(
              AssertUtils.assertInString({
                body: VSCodeUtils.getActiveTextEditorOrThrow().document.getText(),
                match: ["Animi et recusandae in blanditiis sapiente."],
              })
            ).toBeTruthy();
            expect(
              VSCodeUtils.getActiveTextEditorOrThrow().selection.start.line
              // Link is 1-indexed, while VSCode is 0-indexed
            ).toEqual(2);
          });
        }
      );

      describeMultiWS(
        "WHEN linked to a file starting with a dot",
        { ctx },
        () => {
          before(async () => {
            const { wsRoot, vaults, engine } = getDWorkspace();
            const note = await NoteTestUtilsV4.createNoteWithEngine({
              wsRoot,
              vault: vaults[0],
              fname: "test.note",
              body: "[[.test/file.txt]]",
              engine,
            });
            await fs.ensureDir(path.join(wsRoot, ".test"));
            await fs.writeFile(
              path.join(wsRoot, ".test", "file.txt"),
              ["Et corporis assumenda quia libero illo."].join("\n")
            );

            await WSUtils.openNote(note);
            VSCodeUtils.getActiveTextEditorOrThrow().selection =
              new vscode.Selection(7, 1, 7, 1);
            await createGoToNoteCmd().run();
          });

          test("THEN opens that file", async () => {
            expect(getActiveEditorBasename()).toEqual("file.txt");
            expect(
              AssertUtils.assertInString({
                body: VSCodeUtils.getActiveTextEditorOrThrow().document.getText(),
                match: ["Et corporis assumenda quia libero illo."],
              })
            ).toBeTruthy();
          });
        }
      );

      describeMultiWS("WHEN linked to a binary file", { ctx }, () => {
        const filename = "test.zip";
        const notename = "test.note";
        let openWithDefaultApp: sinon.SinonStub<[string], Promise<void>>;
        before(async () => {
          const { wsRoot, vaults, engine } = ExtensionProvider.getDWorkspace();
          const note = await NoteTestUtilsV4.createNoteWithEngine({
            wsRoot,
            vault: vaults[0],
            fname: notename,
            body: `[[/${filename}]]`,
            engine,
          });
          await fs.writeFile(path.join(wsRoot, filename), "");
          openWithDefaultApp = sinon.stub(
            PluginFileUtils,
            "openWithDefaultApp"
          );

          await ExtensionProvider.getWSUtils().openNote(note);
          VSCodeUtils.getActiveTextEditorOrThrow().selection =
            new vscode.Selection(7, 1, 7, 1);
          await createGoToNoteCmd().run();
        });

        test("THEN opens that file in the default app", async () => {
          const { wsRoot } = ExtensionProvider.getDWorkspace();
          // The open note didn't change
          expect(getActiveEditorBasename().startsWith(notename)).toBeTruthy();
          // Used the stubbed function to open in default app
          expect(
            openWithDefaultApp.calledOnceWith(path.join(wsRoot, filename))
          );
        });
      });

      describeMultiWS(
        "WHEN linked to a file under assets where assets is in root and not a vault",
        { ctx },
        () => {
          before(async () => {
            const { wsRoot, vaults, engine } = getDWorkspace();
            const note = await NoteTestUtilsV4.createNoteWithEngine({
              wsRoot,
              vault: vaults[0],
              fname: "test.note",
              body: "[[assets/file.txt]]",
              engine,
            });
            await fs.ensureDir(path.join(wsRoot, "assets"));
            await fs.writeFile(
              path.join(wsRoot, "assets", "file.txt"),
              ["Dolorum sed earum enim rem expedita nemo."].join("\n")
            );

            await WSUtils.openNote(note);
            VSCodeUtils.getActiveTextEditorOrThrow().selection =
              new vscode.Selection(7, 1, 7, 1);
            await createGoToNoteCmd().run();
          });

          test("THEN opens that file", async () => {
            expect(getActiveEditorBasename()).toEqual("file.txt");
            expect(
              AssertUtils.assertInString({
                body: VSCodeUtils.getActiveTextEditorOrThrow().document.getText(),
                match: ["Dolorum sed earum enim rem expedita nemo."],
              })
            ).toBeTruthy();
          });
        }
      );
    });
  });
});