Merge pull request #163 from nathonius/more-tests

More tests
This commit is contained in:
Nathan 2025-02-26 22:09:45 -06:00 committed by GitHub
commit fef1eb84a0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 203 additions and 106 deletions

View file

@ -3,6 +3,7 @@ import { PluginMock } from "./obsidian/Plugin";
import { ModalMock } from "./obsidian/Modal";
import { PluginSettingTabMock } from "./obsidian/PluginSettingTab";
import { NoticeMock } from "./obsidian/Notice";
import { setIconMock } from "./obsidian/setIcon";
module.exports = {
App: AppMock,
@ -10,4 +11,5 @@ module.exports = {
Modal: ModalMock,
PluginSettingTab: PluginSettingTabMock,
Notice: NoticeMock,
setIcon: setIconMock,
};

View file

@ -0,0 +1,5 @@
export function setIconMock(parent: HTMLElement, iconId: string): void {
const icon = document.createElement("span");
icon.innerText = iconId;
parent.appendChild(icon);
}

45
src/inline/inline.spec.ts Normal file
View file

@ -0,0 +1,45 @@
import { test, describe, expect, beforeAll } from "@jest/globals";
import { elementTestSetup } from "../../test/element";
import { createTag } from "./inline";
const selectors = {
org: ".github-link-inline-org",
repo: ".github-link-inline-repo",
issueTitle: ".github-link-inline-issue-title",
prTitle: ".github-link-inline-pr-title",
};
describe("createTag", () => {
beforeAll(() => {
elementTestSetup();
});
test.each([
{ url: "https://github.com/nathonius", user: "nathonius" },
{ url: "https://github.com/nathonius/", user: "nathonius" },
{ url: "https://github.com/sam.lake", user: "sam.lake" },
{ url: "https://github.com/abraham_lincoln", user: "abraham_lincoln" },
])("user only $user", ({ url, user }) => {
const tag = createTag(url);
expect(tag).toBeTruthy();
const org = tag?.querySelector(selectors.org) as HTMLSpanElement;
expect(org.innerText).toEqual(user);
[selectors.repo, selectors.issueTitle, selectors.prTitle].forEach((s) => {
expect(tag?.querySelector(s)).toBeFalsy();
});
});
test.each([
{ url: "https://github.com/nathonius/obsidian-github-link", user: "nathonius", repo: "obsidian-github-link" },
{ url: "https://github.com/nathonius/obsidian.github.link", user: "nathonius", repo: "obsidian.github.link" },
])("repo $repo", ({ url, user, repo }) => {
const tag = createTag(url);
expect(tag).toBeTruthy();
const org = tag?.querySelector(selectors.org) as HTMLSpanElement;
const repoEl = tag?.querySelector(selectors.repo) as HTMLSpanElement;
expect(org.innerText).toEqual(user);
expect(repoEl.innerText).toEqual(repo);
[selectors.issueTitle, selectors.prTitle].forEach((s) => {
expect(tag?.querySelector(s)).toBeFalsy();
});
});
});

View file

@ -1,123 +1,102 @@
import { expect, test, describe } from "@jest/globals";
import { matchRegexp } from './view-plugin';
import { matchRegexp } from "./view-plugin";
// Some potential URLs:
const GITHUB_URLS = [
// USER/ORG
'https://github.com/nathonius',
// REPO
'https://github.com/nathonius/obsidian-github-link',
'https://github.com/nathonius/obsidian-github-link/tree/main',
'https://github.com/joshleaves/obsidian-github-link/tree/fix/156-linking-to-file',
'https://github.com/nathonius/obsidian-github-link/blob/main/src/github/url-parse.ts',
'https://github.com/nathonius/obsidian-github-link/blob/main/src/inline/view-plugin.ts#L21',
'https://github.com/nathonius/obsidian-github-link/blob/main/src/inline/view-plugin.ts#L13-32',
// ISSUES
'https://github.com/nathonius/obsidian-github-link/issues',
'https://github.com/nathonius/obsidian-github-link/issues/156',
// PULLS
'https://github.com/nathonius/obsidian-github-link/pulls',
'https://github.com/nathonius/obsidian-github-link/pull/157',
'https://github.com/nathonius/obsidian-github-link/pull/157/commits',
'https://github.com/nathonius/obsidian-github-link/pull/157/commits/42d35e4c7070d2ec9f3bacf7f4a0561d9d7346bb',
'https://github.com/nathonius/obsidian-github-link/pull/157/files',
// MORE EDGE CASES
'https://github.com/kwsch/pk3DS/blob/e40d3ce5548d75821f31785dc88cd465610530a6/pk3DS.Core/CTR/Images/BCLIM.cs',
'https://github.com/kwsch/png2bclim/blob/master/png2bclim/BCLIM.cs',
'https://github.com/kwsch/png2bclim',
'https://github.com/ihaveamac/3DS-rom-tools/wiki/Extract-a-game-or-application-in-.3ds-or-.cci-format'
]
// USER/ORG
"https://github.com/nathonius",
// REPO
"https://github.com/nathonius/obsidian-github-link",
"https://github.com/nathonius/obsidian-github-link/tree/main",
"https://github.com/joshleaves/obsidian-github-link/tree/fix/156-linking-to-file",
"https://github.com/nathonius/obsidian-github-link/blob/main/src/github/url-parse.ts",
"https://github.com/nathonius/obsidian-github-link/blob/main/src/inline/view-plugin.ts#L21",
"https://github.com/nathonius/obsidian-github-link/blob/main/src/inline/view-plugin.ts#L13-32",
// ISSUES
"https://github.com/nathonius/obsidian-github-link/issues",
"https://github.com/nathonius/obsidian-github-link/issues/156",
// PULLS
"https://github.com/nathonius/obsidian-github-link/pulls",
"https://github.com/nathonius/obsidian-github-link/pull/157",
"https://github.com/nathonius/obsidian-github-link/pull/157/commits",
"https://github.com/nathonius/obsidian-github-link/pull/157/commits/42d35e4c7070d2ec9f3bacf7f4a0561d9d7346bb",
"https://github.com/nathonius/obsidian-github-link/pull/157/files",
// MORE EDGE CASES
"https://github.com/kwsch/pk3DS/blob/e40d3ce5548d75821f31785dc88cd465610530a6/pk3DS.Core/CTR/Images/BCLIM.cs",
"https://github.com/kwsch/png2bclim/blob/master/png2bclim/BCLIM.cs",
"https://github.com/kwsch/png2bclim",
"https://github.com/ihaveamac/3DS-rom-tools/wiki/Extract-a-game-or-application-in-.3ds-or-.cci-format",
];
describe('matchRegexp', () => {
test('matches GitHub URLs on their own', () => {
GITHUB_URLS.forEach(url => {
const text = `${url}` // (...)
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
});
});
describe("url regex match", () => {
test.each(GITHUB_URLS)("matches GitHub URLs on their own", (url) => {
const text = `${url}`; // (...)
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
});
test('does not match URLs inside markdown links', () => {
GITHUB_URLS.forEach(url => {
const text = `[A link to GitHub](${url})`;
const match = text.match(matchRegexp);
expect(match).toBeNull();
});
});
test.each(GITHUB_URLS)("does not match URLs inside markdown links", (url) => {
const text = `[A link to GitHub](${url})`;
const match = text.match(matchRegexp);
expect(match).toBeNull();
});
test('matches URLs ending a sentence', () => {
GITHUB_URLS.forEach(url => {
const text = `That's the end of the line for ${url}.`;
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
});
});
test.each(GITHUB_URLS)("matches URLs ending a sentence", (url) => {
const text = `That's the end of the line for ${url}.`;
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
});
test('matches URLs followed by punctuation', () => {
GITHUB_URLS.forEach(url => {
const text = `First ${url}, then some text.`;
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
});
});
test.each(GITHUB_URLS)("matches URLs followed by punctuation", (url) => {
const text = `First ${url}, then some text.`;
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
});
test('matches URLs ending a sentence with an interrogative', () => {
GITHUB_URLS.forEach(url => {
const text = `You heard about ${url}?`;
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
});
});
test.each(GITHUB_URLS)("matches URLs ending a sentence with an interrogative", (url) => {
const text = `You heard about ${url}?`;
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
});
test('matches URLs followed by multiple punctuation', () => {
GITHUB_URLS.forEach(url => {
const text = `A test for ${url}...`;
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
});
});
test.each(GITHUB_URLS)("matches URLs followed by multiple punctuation", (url) => {
const text = `A test for ${url}...`;
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
});
test.each(GITHUB_URLS)("matches URLs ending a sentence that segues to another", (url) => {
const text = `That's the end of the line for ${url}. But not for this variable!`;
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
});
test('matches URLs ending a sentence that segues to another', () => {
GITHUB_URLS.forEach(url => {
const text = `That's the end of the line for ${url}. But not for this variable!`;
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
});
});
test('matches URLs in text with multiple lines', () => {
GITHUB_URLS.forEach(url => {
const text = `Some text
test.each(GITHUB_URLS)("matches URLs in text with multiple lines", (url) => {
const text = `Some text
${url}
more text.`;
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
});
});
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
});
test('matches URLs in text followed by another text', () => {
GITHUB_URLS.forEach(url => {
const text = `${url} is a cool plugin`;
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
})
});
test.each(GITHUB_URLS)("matches URLs in text followed by another text", (url) => {
const text = `${url} is a cool plugin`;
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
});
test('matches URLs quoted', () => {
GITHUB_URLS.forEach(url => {
const text = `Obama chuckled: you mean "${url}" ?`;
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
})
});
test.each(GITHUB_URLS)("matches URLs quoted", (url) => {
const text = `Obama chuckled: you mean "${url}" ?`;
const match = text.match(matchRegexp);
expect(match).not.toBeNull();
expect(match![0]).toBe(url);
});
});

66
test/element.ts Normal file
View file

@ -0,0 +1,66 @@
/**
* Local version of obsidian's 'createEl' function, copied from
* https://ryotaushio.github.io/the-hobbyist-dev/obsidian/api-&-internals/createel.html
*/
function createEl<K extends keyof HTMLElementTagNameMap>(
tag: K,
options?: string | DomElementInfo,
callback?: (el: HTMLElementTagNameMap[K]) => void,
): HTMLElementTagNameMap[K] {
const el = document.createElement(tag);
if (typeof options === "string") options = { cls: options };
options = options || {};
if (options.cls) Array.isArray(options.cls) ? (el.className = options.cls.join(" ")) : (el.className = options.cls);
if (options.text) el.setText(options.text);
if (options.attr) {
for (const [k, v] of Object.entries(options.attr)) {
el.setAttribute(k, String(v));
}
}
if (options.title !== undefined) el.title = options.title;
if (
options.value !== undefined &&
(el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLOptionElement)
) {
el.value = options.value;
}
if (options.type && el instanceof HTMLInputElement) el.type = options.type;
if (options.type && el instanceof HTMLStyleElement) el.setAttribute("type", options.type);
if (options.placeholder && el instanceof HTMLInputElement) el.placeholder = options.placeholder;
if (
options.href &&
(el instanceof HTMLAnchorElement || el instanceof HTMLLinkElement || el instanceof HTMLBaseElement)
)
el.href = options.href;
callback?.(el);
if (options.parent) {
if (options.prepend) options.parent.insertBefore(el, options.parent.firstChild);
else options.parent.appendChild(el);
}
// add event listeners (e.g. { onclick: () => {} })
for (const key in options) {
if (Object.prototype.hasOwnProperty.call(options, key) && key.startsWith("on")) {
const value = options[key as keyof DomElementInfo];
if (typeof value === "function") el.addEventListener(key.substring(2), value);
}
}
return el;
}
function createSpan(options?: string | DomElementInfo, callback?: (el: HTMLSpanElement) => void) {
return createEl("span", options, callback);
}
function createDiv(options?: string | DomElementInfo, callback?: (el: HTMLDivElement) => void) {
return createEl("div", options, callback);
}
export function elementTestSetup() {
HTMLElement.prototype.setText = function (text: string) {
this.innerText = text;
};
window.createEl = createEl;
window.createSpan = createSpan;
window.createDiv = createDiv;
}