feat: add tests
This commit is contained in:
parent
6fd2bab661
commit
4f410a5974
|
|
@ -35,7 +35,7 @@
|
|||
"unist-util-visit": "^5.0.0",
|
||||
"unist-util-visit-parents": "^6.0.1",
|
||||
"yamljs": "^0.3.0",
|
||||
"zotero-plugin-scaffold": "^0.1.8-beta.1",
|
||||
"zotero-plugin-scaffold": "^0.1.8-beta.3",
|
||||
"zotero-plugin-toolkit": "^4.0.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -12259,9 +12259,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/zotero-plugin-scaffold": {
|
||||
"version": "0.1.8-beta.1",
|
||||
"resolved": "https://registry.npmjs.org/zotero-plugin-scaffold/-/zotero-plugin-scaffold-0.1.8-beta.1.tgz",
|
||||
"integrity": "sha512-cZCtsR8S8GyV5IGmN+m3w4GOcA3okiV0cl55+FLvmUK4nNImLSU1p3FXKXXoMG9e5M4drBDRfiG17klfim+6NQ==",
|
||||
"version": "0.1.8-beta.3",
|
||||
"resolved": "https://registry.npmjs.org/zotero-plugin-scaffold/-/zotero-plugin-scaffold-0.1.8-beta.3.tgz",
|
||||
"integrity": "sha512-i6Uu8EA+XXhwznhyB35+dfn9PyttKGNk5/AvGc18COSubW/wihOjJiWBLytrhl8JDsdch9pDh4qtUPfw/Il8iQ==",
|
||||
"dependencies": {
|
||||
"@commander-js/extra-typings": "^12.1.0",
|
||||
"@inquirer/prompts": "^7.0.0",
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@
|
|||
"unist-util-visit": "^5.0.0",
|
||||
"unist-util-visit-parents": "^6.0.1",
|
||||
"yamljs": "^0.3.0",
|
||||
"zotero-plugin-scaffold": "^0.1.8-beta.1",
|
||||
"zotero-plugin-scaffold": "^0.1.8-beta.3",
|
||||
"zotero-plugin-toolkit": "^4.0.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ class Addon {
|
|||
uid: string;
|
||||
alive: boolean;
|
||||
env: "development" | "production" | "test";
|
||||
initialized?: boolean;
|
||||
ztoolkit: ZToolkit;
|
||||
// ztoolkit: ZoteroToolkit;
|
||||
locale?: {
|
||||
|
|
|
|||
|
|
@ -80,6 +80,9 @@ async function onStartup() {
|
|||
setSyncing();
|
||||
|
||||
await onMainWindowLoad(Zotero.getMainWindow());
|
||||
|
||||
// For testing
|
||||
addon.data.initialized = true;
|
||||
}
|
||||
|
||||
async function onMainWindowLoad(win: _ZoteroTypes.MainWindow): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
describe("Array", function () {
|
||||
it("should return -1 when the value is not present", function () {
|
||||
expect([1, 2, 3].indexOf(4)).to.equal(-1);
|
||||
});
|
||||
|
||||
it("should return the index when the value is present", function () {
|
||||
expect([1, 2, 3].indexOf(2)).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { config } from "../../package.json";
|
||||
|
||||
describe("Startup", function () {
|
||||
it("should have plugin instance defined", function () {
|
||||
assert.isNotEmpty(Zotero[config.addonRef]);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import { BasicTool } from "zotero-plugin-toolkit";
|
||||
import { waitForNoteWindow, waitForTabSelectEvent } from "../utils/wait";
|
||||
import { resetAll } from "../utils/status";
|
||||
|
||||
describe("Workspace", function () {
|
||||
const tool = new BasicTool();
|
||||
|
||||
this.beforeAll(async function () {
|
||||
await resetAll();
|
||||
});
|
||||
|
||||
this.afterEach(async function () {
|
||||
await resetAll();
|
||||
});
|
||||
|
||||
it("should open note in tab", async function () {
|
||||
const note = new Zotero.Item("note");
|
||||
await note.saveTx();
|
||||
|
||||
const promise = waitForTabSelectEvent();
|
||||
|
||||
// An example of how to debug the test
|
||||
debug("Calling viewItems");
|
||||
|
||||
tool.getGlobal("ZoteroPane").viewItems([note]);
|
||||
await promise;
|
||||
|
||||
const selectedID = tool.getGlobal("Zotero_Tabs").selectedID;
|
||||
const selectedTab = tool.getGlobal("Zotero_Tabs")._getTab(selectedID);
|
||||
|
||||
expect(selectedTab.tab.data.itemID).to.be.equal(note.id);
|
||||
});
|
||||
|
||||
it("should open note in window if shift key is pressed", async function () {
|
||||
const note = new Zotero.Item("note");
|
||||
await note.saveTx();
|
||||
|
||||
const promise = waitForNoteWindow();
|
||||
|
||||
tool.getGlobal("ZoteroPane").viewItems([note], { shiftKey: true });
|
||||
const win = await promise;
|
||||
|
||||
expect(win).to.be.not.null;
|
||||
|
||||
const editor = win!.document.querySelector(
|
||||
"#zotero-note-editor",
|
||||
) as EditorElement;
|
||||
|
||||
expect(editor).to.be.not.null;
|
||||
|
||||
expect(editor.item?.id).to.be.equal(note.id);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
describe("Zotero", function () {
|
||||
it("should have Zotero defined", function () {
|
||||
expect(Zotero).to.not.be.undefined;
|
||||
});
|
||||
});
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"include": ["./**/*.spec.ts", "typings", "../node_modules/zotero-types"],
|
||||
"include": ["./**/*.ts", "typings", "../node_modules/zotero-types"],
|
||||
"exclude": []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
declare interface EditorCore {
|
||||
debouncedUpdate: Function;
|
||||
disableDrag: boolean;
|
||||
docChanged: boolean;
|
||||
isAttachmentNote: false;
|
||||
metadata: {
|
||||
_citationItems: { itemData: { [k: string]: any } }[];
|
||||
uris: string[];
|
||||
};
|
||||
nodeViews: any[];
|
||||
onUpdateState: Function;
|
||||
options: {
|
||||
isAttachmentNote: false;
|
||||
onImportImages: Function;
|
||||
onInsertObject: Function;
|
||||
onOpenAnnotation: Function;
|
||||
onOpenCitationPage: Function;
|
||||
onOpenCitationPopup: Function;
|
||||
onOpenContextMenu: Function;
|
||||
onOpenURL: Function;
|
||||
onShowCitationItem: Function;
|
||||
onSubscribe: Function;
|
||||
onUnsubscribe: Function;
|
||||
onUpdate: Function;
|
||||
onUpdateCitationItemsList: Function;
|
||||
placeholder: boolean;
|
||||
readOnly: boolean;
|
||||
reloaded: boolean;
|
||||
smartQuotes: boolean;
|
||||
unsaved: boolean;
|
||||
value: string;
|
||||
};
|
||||
pluginState: { [k: string]: any };
|
||||
provider: import("react").Provider;
|
||||
readOnly: boolean;
|
||||
reloaded: boolean;
|
||||
view: import("prosemirror-view").EditorView & {
|
||||
docView: NodeViewDesc;
|
||||
};
|
||||
}
|
||||
|
||||
declare type EditorAPI =
|
||||
typeof import("../src/extras/editorScript").BetterNotesEditorAPI;
|
||||
|
||||
declare interface EditorElement extends XULBoxElement {
|
||||
_iframe: HTMLIFrameElement;
|
||||
_editorInstance: Zotero.EditorInstance;
|
||||
_initialized?: boolean;
|
||||
mode?: "edit" | "view";
|
||||
viewMode?: string;
|
||||
parent?: Zotero.Item;
|
||||
item?: Zotero.Item;
|
||||
getCurrentInstance(): Zotero.EditorInstance;
|
||||
initEditor(): Promise<void>;
|
||||
}
|
||||
|
|
@ -3,4 +3,5 @@ import type * as chai from "chai";
|
|||
declare global {
|
||||
const expect: typeof chai.expect;
|
||||
const assert: typeof chai.assert;
|
||||
const debug: (...data: any[]) => void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
export async function resetData() {
|
||||
// Delete collections, items, tags
|
||||
const collections = await Zotero.Collections.getAllIDs(
|
||||
Zotero.Libraries.userLibraryID,
|
||||
);
|
||||
await Zotero.Collections.erase(collections);
|
||||
|
||||
const items = await Zotero.Items.getAllIDs(Zotero.Libraries.userLibraryID);
|
||||
await Zotero.Items.erase(items);
|
||||
}
|
||||
|
||||
export async function resetTabs() {
|
||||
const win = Zotero.getMainWindow();
|
||||
const Zotero_Tabs = win.Zotero_Tabs;
|
||||
Zotero_Tabs.closeAll();
|
||||
}
|
||||
|
||||
export async function resetAll() {
|
||||
await resetTabs();
|
||||
await resetData();
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
export async function waitNoMoreThan<T>(
|
||||
promise: Promise<T>,
|
||||
timeout: number = 3000,
|
||||
message: string = "Timeout",
|
||||
) {
|
||||
let resolved = false;
|
||||
|
||||
return Promise.any([
|
||||
promise.then((result) => {
|
||||
resolved = true;
|
||||
return result;
|
||||
}),
|
||||
Zotero.Promise.delay(timeout).then(() => {
|
||||
if (resolved) return;
|
||||
throw new Error(message);
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
export async function waitForNotifierEvent(
|
||||
event: _ZoteroTypes.Notifier.Event,
|
||||
type: _ZoteroTypes.Notifier.Type,
|
||||
timeout: number = 3000,
|
||||
) {
|
||||
if (!event) throw new Error("event not provided");
|
||||
let resolved = false;
|
||||
|
||||
return waitNoMoreThan(
|
||||
new Promise((resolve, reject) => {
|
||||
const notifierID = Zotero.Notifier.registerObserver(
|
||||
{
|
||||
notify: function (ev, type, ids, extraData) {
|
||||
if (ev == event) {
|
||||
Zotero.Notifier.unregisterObserver(notifierID);
|
||||
resolved = true;
|
||||
|
||||
resolve({
|
||||
ids: ids,
|
||||
extraData: extraData,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
[type],
|
||||
"test",
|
||||
101,
|
||||
);
|
||||
}),
|
||||
timeout,
|
||||
);
|
||||
}
|
||||
|
||||
export function waitForTabSelectEvent(timeout: number = 3000) {
|
||||
return waitForNotifierEvent("select", "tab", timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a window with a specific URL to open. Returns a promise for the window, and
|
||||
* optionally passes the window to a callback immediately for use with modal dialogs,
|
||||
* which prevent async code from continuing
|
||||
*/
|
||||
export async function waitForWindow(uri: string, timeout: number = 3000) {
|
||||
return waitNoMoreThan(
|
||||
new Promise<Window>((resolve, reject) => {
|
||||
const loadObserver = function (ev: Event) {
|
||||
ev.originalTarget?.removeEventListener("load", loadObserver, false);
|
||||
const href = (ev.target as Window)?.location.href;
|
||||
Zotero.debug("Window opened: " + href);
|
||||
|
||||
if (href != uri) {
|
||||
Zotero.debug(`Ignoring window ${href} in waitForWindow()`);
|
||||
return;
|
||||
}
|
||||
|
||||
Services.ww.unregisterNotification(winObserver);
|
||||
const win = ev.target?.ownerGlobal;
|
||||
// Give window code time to run on load
|
||||
win?.setTimeout(function () {
|
||||
resolve(win);
|
||||
});
|
||||
};
|
||||
const winObserver = {
|
||||
observe: function (subject: Window, topic: string, data: any) {
|
||||
if (topic != "domwindowopened") return;
|
||||
subject.addEventListener("load", loadObserver, false);
|
||||
},
|
||||
} as nsIObserver;
|
||||
Services.ww.registerNotification(winObserver);
|
||||
}),
|
||||
timeout,
|
||||
);
|
||||
}
|
||||
|
||||
export async function waitForNoteWindow() {
|
||||
return await waitForWindow("chrome://zotero/content/note.xhtml");
|
||||
}
|
||||
|
|
@ -2,6 +2,10 @@ import pkg from "./package.json";
|
|||
import { defineConfig } from "zotero-plugin-scaffold";
|
||||
import { replaceInFile } from "replace-in-file";
|
||||
|
||||
const TEST_PREFS = {};
|
||||
// Disable user guide, keep in sync with src/modules/userGuide.ts
|
||||
TEST_PREFS[`${pkg.config.prefsPrefix}.latestTourVersion`] = 1;
|
||||
|
||||
export default defineConfig({
|
||||
source: ["src", "addon"],
|
||||
dist: "build",
|
||||
|
|
@ -66,10 +70,11 @@ export default defineConfig({
|
|||
},
|
||||
test: {
|
||||
entries: ["test/"],
|
||||
prefs: {},
|
||||
prefs: TEST_PREFS,
|
||||
abortOnFail: true,
|
||||
exitOnFinish: false,
|
||||
hooks: {},
|
||||
waitForPlugin: `() => Zotero.${pkg.config.addonRef}.data.initialized`,
|
||||
},
|
||||
|
||||
// If you need to see a more detailed build log, uncomment the following line:
|
||||
|
|
|
|||
Loading…
Reference in New Issue