mirror of
https://github.com/lllin000/PaperForge.git
synced 2026-07-22 17:00:23 +00:00
fix(plugin): extract testable functions to src/testable.js, fix vitest imports
- Move resolvePythonExecutable, getPluginVersion, classifyError, ACTIONS, etc. to src/testable.js - main.js now requires from ./src/testable.js instead of inline duplication - Tests import from ../src/testable.js (no obsidian dependency) - Remove vitest obsidian mock setup (no longer needed) - Fixes L3 Plugin Tests that failed because main.js requires 'obsidian'
This commit is contained in:
parent
ca15b25f96
commit
25c3deba2d
7 changed files with 249 additions and 235 deletions
|
|
@ -4,132 +4,19 @@ const fs = require('fs');
|
|||
const path = require('path');
|
||||
const os = require('os');
|
||||
|
||||
// ── Runtime helpers (inlined from src/runtime.js) ──
|
||||
const {
|
||||
resolvePythonExecutable,
|
||||
getPluginVersion,
|
||||
checkRuntimeVersion,
|
||||
classifyError,
|
||||
buildRuntimeInstallCommand,
|
||||
parseRuntimeStatus,
|
||||
ACTIONS,
|
||||
buildCommandArgs,
|
||||
runSubprocess,
|
||||
} = require('./src/testable');
|
||||
|
||||
function resolvePythonExecutable(vaultPath, settings, _fs, _execFileSync) {
|
||||
const f = _fs || fs;
|
||||
const execSync = _execFileSync || require("node:child_process").execFileSync;
|
||||
|
||||
if (settings && settings.python_path && settings.python_path.trim()) {
|
||||
const manualPath = settings.python_path.trim();
|
||||
if (f.existsSync(manualPath)) {
|
||||
return { path: manualPath, source: "manual", extraArgs: [] };
|
||||
}
|
||||
}
|
||||
|
||||
const venvCandidates = [
|
||||
path.join(vaultPath, ".paperforge-test-venv", "Scripts", "python.exe"),
|
||||
path.join(vaultPath, ".venv", "Scripts", "python.exe"),
|
||||
path.join(vaultPath, "venv", "Scripts", "python.exe"),
|
||||
];
|
||||
for (const candidate of venvCandidates) {
|
||||
try {
|
||||
if (f.existsSync(candidate)) {
|
||||
return { path: candidate, source: "auto-detected", extraArgs: [] };
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const systemCandidates = [
|
||||
{ path: "py", extraArgs: ["-3"] },
|
||||
{ path: "python", extraArgs: [] },
|
||||
{ path: "python3", extraArgs: [] },
|
||||
];
|
||||
for (const candidate of systemCandidates) {
|
||||
try {
|
||||
const verOut = execSync(candidate.path, [...candidate.extraArgs, "--version"], {
|
||||
encoding: "utf-8", timeout: 5000, windowsHide: true,
|
||||
});
|
||||
if (verOut && verOut.toLowerCase().includes("python")) {
|
||||
return { path: candidate.path, source: "auto-detected", extraArgs: candidate.extraArgs };
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return { path: "python", source: "auto-detected", extraArgs: [] };
|
||||
}
|
||||
|
||||
function getPluginVersion(app) {
|
||||
try {
|
||||
const manifest = app && app.plugins && app.plugins.plugins &&
|
||||
app.plugins.plugins["paperforge"] && app.plugins.plugins["paperforge"].manifest;
|
||||
return (manifest && manifest.version) || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function checkRuntimeVersion(pythonExe, pluginVersion, cwd, timeout, _execFile) {
|
||||
if (timeout === undefined) timeout = 10000;
|
||||
const exe = _execFile || execFile;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
exe(pythonExe, ["-c", "import paperforge; print(paperforge.__version__)"],
|
||||
{ cwd, timeout },
|
||||
(err, stdout) => {
|
||||
if (err) {
|
||||
resolve({ status: "not-installed", pyVersion: null, pluginVersion, error: err.message });
|
||||
return;
|
||||
}
|
||||
const pyVer = (stdout && stdout.trim()) || null;
|
||||
if (pyVer === pluginVersion) {
|
||||
resolve({ status: "match", pyVersion: pyVer, pluginVersion, error: null });
|
||||
} else {
|
||||
resolve({ status: "mismatch", pyVersion: pyVer, pluginVersion, error: null });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ── Error helpers (inlined from src/errors.js) ──
|
||||
|
||||
function classifyError(errorCode) {
|
||||
const code = String(errorCode);
|
||||
const patterns = {
|
||||
ENOENT: { type: "python_missing", message: "Python executable not found", recoverable: true },
|
||||
"python-missing": { type: "python_missing", message: "Python executable not found", recoverable: true },
|
||||
MODULE_NOT_FOUND: { type: "import_failed", message: "PaperForge package not installed", recoverable: true },
|
||||
"import-failed": { type: "import_failed", message: "PaperForge package not installed", recoverable: true },
|
||||
"version-mismatch": { type: "version_mismatch", message: "Plugin and package versions differ", recoverable: true, action: "sync-runtime" },
|
||||
"pip-failed": { type: "pip_install_failure", message: "pip install command failed", recoverable: true },
|
||||
ETIMEDOUT: { type: "timeout", message: "Subprocess timed out", recoverable: true, action: "retry" },
|
||||
timeout: { type: "timeout", message: "Subprocess timed out", recoverable: true, action: "retry" },
|
||||
};
|
||||
const match = patterns[code];
|
||||
if (match) return { ...match };
|
||||
return { type: "unknown", message: String(errorCode), recoverable: false };
|
||||
}
|
||||
|
||||
function buildRuntimeInstallCommand(pythonExe, version, extraArgs) {
|
||||
if (extraArgs === undefined) extraArgs = [];
|
||||
const url = `git+https://github.com/LLLin000/PaperForge.git@${version}`;
|
||||
const args = [...extraArgs, "-m", "pip", "install", "--upgrade", url];
|
||||
return { cmd: pythonExe, args, url, timeout: 120000 };
|
||||
}
|
||||
|
||||
function parseRuntimeStatus(err, stdout, stderr) {
|
||||
if (!err && stdout) {
|
||||
return { status: "ok", version: stdout.trim() };
|
||||
}
|
||||
if (err && err.code === "ENOENT") {
|
||||
const classified = classifyError("ENOENT");
|
||||
return { status: "error", version: null, ...classified };
|
||||
}
|
||||
if (stderr && stderr.includes("No module named paperforge")) {
|
||||
const classified = classifyError("import-failed");
|
||||
return { status: "error", version: null, ...classified };
|
||||
}
|
||||
if (err && err.killed) {
|
||||
const classified = classifyError("timeout");
|
||||
return { status: "error", version: null, ...classified };
|
||||
}
|
||||
if (stderr && stderr.includes("ModuleNotFoundError")) {
|
||||
const classified = classifyError("import-failed");
|
||||
return { status: "error", version: null, ...classified };
|
||||
}
|
||||
return { status: "error", version: null, type: "unknown",
|
||||
message: err ? err.message : String(stderr), recoverable: false };
|
||||
}
|
||||
|
||||
// ── Cross-platform Python and BBT detection (macOS/Linux) ──
|
||||
|
||||
|
|
@ -277,93 +164,7 @@ function scanBbtUnderProfiles(profilesDir) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// ── Action definitions and subprocess dispatch (inlined from src/commands.js) ──
|
||||
|
||||
const ACTIONS = [
|
||||
{
|
||||
id: "paperforge-sync",
|
||||
title: "Sync Library",
|
||||
desc: "Pull new references from Zotero and generate literature notes",
|
||||
icon: "\u21BB",
|
||||
cmd: "sync",
|
||||
okMsg: "Sync complete",
|
||||
},
|
||||
{
|
||||
id: "paperforge-ocr",
|
||||
title: "Run OCR",
|
||||
desc: "Extract full text and figures from PDFs via PaddleOCR",
|
||||
icon: "\u229E",
|
||||
cmd: "ocr",
|
||||
okMsg: "OCR started",
|
||||
},
|
||||
{
|
||||
id: "paperforge-doctor",
|
||||
title: "Run Doctor",
|
||||
desc: "Verify PaperForge setup \u2014 check configs, Zotero, paths, and index health",
|
||||
icon: "\u2695",
|
||||
cmd: "doctor",
|
||||
okMsg: "Doctor complete",
|
||||
},
|
||||
{
|
||||
id: "paperforge-repair",
|
||||
title: "Repair Issues",
|
||||
desc: "Fix three-way state divergence, path errors, and rebuild index",
|
||||
icon: "\u21BA",
|
||||
cmd: "repair",
|
||||
args: ["--fix", "--fix-paths"],
|
||||
okMsg: "Repair complete",
|
||||
},
|
||||
{
|
||||
id: "paperforge-copy-context",
|
||||
title: "Copy Context",
|
||||
desc: "Copy this paper\u2019s canonical index entry JSON to clipboard for AI use",
|
||||
icon: "\u2139",
|
||||
cmd: "context",
|
||||
needsKey: true,
|
||||
okMsg: "Context copied",
|
||||
},
|
||||
{
|
||||
id: "paperforge-copy-collection-context",
|
||||
title: "Copy Collection Context",
|
||||
desc: "Copy canonical index entries for all visible papers to clipboard",
|
||||
icon: "\u2261",
|
||||
cmd: "context",
|
||||
needsFilter: true,
|
||||
okMsg: "Collection context copied",
|
||||
},
|
||||
];
|
||||
|
||||
function buildCommandArgs(action, key, filter) {
|
||||
const args = Array.isArray(action.args) ? [...action.args] : [];
|
||||
if (action.needsKey && key) args.push(key);
|
||||
if (action.needsFilter || filter) args.push("--all");
|
||||
return args;
|
||||
}
|
||||
|
||||
function runSubprocess(pythonExe, args, cwd, timeout, _spawn) {
|
||||
const sp = _spawn || spawn;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const startTime = Date.now();
|
||||
const child = sp(pythonExe, args, { cwd, timeout, windowsHide: true });
|
||||
const stdoutChunks = [];
|
||||
const stderrChunks = [];
|
||||
|
||||
child.stdout.on("data", (data) => { stdoutChunks.push(data.toString("utf-8")); });
|
||||
child.stderr.on("data", (data) => { stderrChunks.push(data.toString("utf-8")); });
|
||||
|
||||
child.on("close", (code) => {
|
||||
resolve({ stdout: stdoutChunks.join(""), stderr: stderrChunks.join(""),
|
||||
exitCode: code, elapsed: Date.now() - startTime });
|
||||
});
|
||||
|
||||
child.on("error", (err) => {
|
||||
resolve({ stdout: stdoutChunks.join(""),
|
||||
stderr: stderrChunks.join("") + "\n" + err.message,
|
||||
exitCode: -1, elapsed: Date.now() - startTime });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const VIEW_TYPE_PAPERFORGE = 'paperforge-status';
|
||||
const PF_ICON_ID = 'paperforge';
|
||||
|
|
@ -3237,13 +3038,4 @@ module.exports = class PaperForgePlugin extends Plugin {
|
|||
}
|
||||
};
|
||||
|
||||
// ── Named exports for Vitest (functions inlined from src/ after refactor) ──
|
||||
module.exports.resolvePythonExecutable = resolvePythonExecutable;
|
||||
module.exports.getPluginVersion = getPluginVersion;
|
||||
module.exports.checkRuntimeVersion = checkRuntimeVersion;
|
||||
module.exports.classifyError = classifyError;
|
||||
module.exports.buildRuntimeInstallCommand = buildRuntimeInstallCommand;
|
||||
module.exports.parseRuntimeStatus = parseRuntimeStatus;
|
||||
module.exports.ACTIONS = ACTIONS;
|
||||
module.exports.buildCommandArgs = buildCommandArgs;
|
||||
module.exports.runSubprocess = runSubprocess;
|
||||
|
||||
|
|
|
|||
234
paperforge/plugin/src/testable.js
Normal file
234
paperforge/plugin/src/testable.js
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
/**
|
||||
* Testable functions extracted from main.js for Vitest.
|
||||
* No obsidian dependency — safe to import in Node test environment.
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execFile, spawn } = require('node:child_process');
|
||||
|
||||
// ── Runtime helpers ──
|
||||
|
||||
function resolvePythonExecutable(vaultPath, settings, _fs, _execFileSync) {
|
||||
const f = _fs || fs;
|
||||
const execSync = _execFileSync || require("node:child_process").execFileSync;
|
||||
|
||||
if (settings && settings.python_path && settings.python_path.trim()) {
|
||||
const manualPath = settings.python_path.trim();
|
||||
if (f.existsSync(manualPath)) {
|
||||
return { path: manualPath, source: "manual", extraArgs: [] };
|
||||
}
|
||||
}
|
||||
|
||||
const venvCandidates = [
|
||||
path.join(vaultPath, ".paperforge-test-venv", "Scripts", "python.exe"),
|
||||
path.join(vaultPath, ".venv", "Scripts", "python.exe"),
|
||||
path.join(vaultPath, "venv", "Scripts", "python.exe"),
|
||||
];
|
||||
for (const candidate of venvCandidates) {
|
||||
try {
|
||||
if (f.existsSync(candidate)) {
|
||||
return { path: candidate, source: "auto-detected", extraArgs: [] };
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const systemCandidates = [
|
||||
{ path: "py", extraArgs: ["-3"] },
|
||||
{ path: "python", extraArgs: [] },
|
||||
{ path: "python3", extraArgs: [] },
|
||||
];
|
||||
for (const candidate of systemCandidates) {
|
||||
try {
|
||||
const verOut = execSync(candidate.path, [...candidate.extraArgs, "--version"], {
|
||||
encoding: "utf-8", timeout: 5000, windowsHide: true,
|
||||
});
|
||||
if (verOut && verOut.toLowerCase().includes("python")) {
|
||||
return { path: candidate.path, source: "auto-detected", extraArgs: candidate.extraArgs };
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return { path: "python", source: "auto-detected", extraArgs: [] };
|
||||
}
|
||||
|
||||
function getPluginVersion(app) {
|
||||
try {
|
||||
const manifest = app && app.plugins && app.plugins.plugins &&
|
||||
app.plugins.plugins["paperforge"] && app.plugins.plugins["paperforge"].manifest;
|
||||
return (manifest && manifest.version) || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function checkRuntimeVersion(pythonExe, pluginVersion, cwd, timeout, _execFile) {
|
||||
if (timeout === undefined) timeout = 10000;
|
||||
const exe = _execFile || execFile;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
exe(pythonExe, ["-c", "import paperforge; print(paperforge.__version__)"],
|
||||
{ cwd, timeout },
|
||||
(err, stdout) => {
|
||||
if (err) {
|
||||
resolve({ status: "not-installed", pyVersion: null, pluginVersion, error: err.message });
|
||||
return;
|
||||
}
|
||||
const pyVer = (stdout && stdout.trim()) || null;
|
||||
if (pyVer === pluginVersion) {
|
||||
resolve({ status: "match", pyVersion: pyVer, pluginVersion, error: null });
|
||||
} else {
|
||||
resolve({ status: "mismatch", pyVersion: pyVer, pluginVersion, error: null });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ── Error helpers ──
|
||||
|
||||
function classifyError(errorCode) {
|
||||
const code = String(errorCode);
|
||||
const patterns = {
|
||||
ENOENT: { type: "python_missing", message: "Python executable not found", recoverable: true },
|
||||
"python-missing": { type: "python_missing", message: "Python executable not found", recoverable: true },
|
||||
MODULE_NOT_FOUND: { type: "import_failed", message: "PaperForge package not installed", recoverable: true },
|
||||
"import-failed": { type: "import_failed", message: "PaperForge package not installed", recoverable: true },
|
||||
"version-mismatch": { type: "version_mismatch", message: "Plugin and package versions differ", recoverable: true, action: "sync-runtime" },
|
||||
"pip-failed": { type: "pip_install_failure", message: "pip install command failed", recoverable: true },
|
||||
ETIMEDOUT: { type: "timeout", message: "Subprocess timed out", recoverable: true, action: "retry" },
|
||||
timeout: { type: "timeout", message: "Subprocess timed out", recoverable: true, action: "retry" },
|
||||
};
|
||||
const match = patterns[code];
|
||||
if (match) return { ...match };
|
||||
return { type: "unknown", message: String(errorCode), recoverable: false };
|
||||
}
|
||||
|
||||
function buildRuntimeInstallCommand(pythonExe, version, extraArgs) {
|
||||
if (extraArgs === undefined) extraArgs = [];
|
||||
const url = `git+https://github.com/LLLin000/PaperForge.git@${version}`;
|
||||
const args = [...extraArgs, "-m", "pip", "install", "--upgrade", url];
|
||||
return { cmd: pythonExe, args, url, timeout: 120000 };
|
||||
}
|
||||
|
||||
function parseRuntimeStatus(err, stdout, stderr) {
|
||||
if (!err && stdout) {
|
||||
return { status: "ok", version: stdout.trim() };
|
||||
}
|
||||
if (err && err.code === "ENOENT") {
|
||||
const classified = classifyError("ENOENT");
|
||||
return { status: "error", version: null, ...classified };
|
||||
}
|
||||
if (stderr && stderr.includes("No module named paperforge")) {
|
||||
const classified = classifyError("import-failed");
|
||||
return { status: "error", version: null, ...classified };
|
||||
}
|
||||
if (err && err.killed) {
|
||||
const classified = classifyError("timeout");
|
||||
return { status: "error", version: null, ...classified };
|
||||
}
|
||||
if (stderr && stderr.includes("ModuleNotFoundError")) {
|
||||
const classified = classifyError("import-failed");
|
||||
return { status: "error", version: null, ...classified };
|
||||
}
|
||||
return { status: "error", version: null, type: "unknown",
|
||||
message: err ? err.message : String(stderr), recoverable: false };
|
||||
}
|
||||
|
||||
// ── Action definitions ──
|
||||
|
||||
const ACTIONS = [
|
||||
{
|
||||
id: "paperforge-sync",
|
||||
title: "Sync Library",
|
||||
desc: "Pull new references from Zotero and generate literature notes",
|
||||
icon: "\u21BB",
|
||||
cmd: "sync",
|
||||
okMsg: "Sync complete",
|
||||
},
|
||||
{
|
||||
id: "paperforge-ocr",
|
||||
title: "Run OCR",
|
||||
desc: "Extract full text and figures from PDFs via PaddleOCR",
|
||||
icon: "\u229E",
|
||||
cmd: "ocr",
|
||||
okMsg: "OCR started",
|
||||
},
|
||||
{
|
||||
id: "paperforge-doctor",
|
||||
title: "Run Doctor",
|
||||
desc: "Verify PaperForge setup \u2014 check configs, Zotero, paths, and index health",
|
||||
icon: "\u2695",
|
||||
cmd: "doctor",
|
||||
okMsg: "Doctor complete",
|
||||
},
|
||||
{
|
||||
id: "paperforge-repair",
|
||||
title: "Repair Issues",
|
||||
desc: "Fix three-way state divergence, path errors, and rebuild index",
|
||||
icon: "\u21BA",
|
||||
cmd: "repair",
|
||||
args: ["--fix", "--fix-paths"],
|
||||
okMsg: "Repair complete",
|
||||
},
|
||||
{
|
||||
id: "paperforge-copy-context",
|
||||
title: "Copy Context",
|
||||
desc: "Copy this paper\u2019s canonical index entry JSON to clipboard for AI use",
|
||||
icon: "\u2139",
|
||||
cmd: "context",
|
||||
needsKey: true,
|
||||
okMsg: "Context copied",
|
||||
},
|
||||
{
|
||||
id: "paperforge-copy-collection-context",
|
||||
title: "Copy Collection Context",
|
||||
desc: "Copy canonical index entries for all visible papers to clipboard",
|
||||
icon: "\u2261",
|
||||
cmd: "context",
|
||||
needsFilter: true,
|
||||
okMsg: "Collection context copied",
|
||||
},
|
||||
];
|
||||
|
||||
function buildCommandArgs(action, key, filter) {
|
||||
const args = Array.isArray(action.args) ? [...action.args] : [];
|
||||
if (action.needsKey && key) args.push(key);
|
||||
if (action.needsFilter || filter) args.push("--all");
|
||||
return args;
|
||||
}
|
||||
|
||||
function runSubprocess(pythonExe, args, cwd, timeout, _spawn) {
|
||||
const sp = _spawn || spawn;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const startTime = Date.now();
|
||||
const child = sp(pythonExe, args, { cwd, timeout, windowsHide: true });
|
||||
const stdoutChunks = [];
|
||||
const stderrChunks = [];
|
||||
|
||||
child.stdout.on("data", (data) => { stdoutChunks.push(data.toString("utf-8")); });
|
||||
child.stderr.on("data", (data) => { stderrChunks.push(data.toString("utf-8")); });
|
||||
|
||||
child.on("close", (code) => {
|
||||
resolve({ stdout: stdoutChunks.join(""), stderr: stderrChunks.join(""),
|
||||
exitCode: code, elapsed: Date.now() - startTime });
|
||||
});
|
||||
|
||||
child.on("error", (err) => {
|
||||
resolve({ stdout: stdoutChunks.join(""),
|
||||
stderr: stderrChunks.join("") + "\n" + err.message,
|
||||
exitCode: -1, elapsed: Date.now() - startTime });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
resolvePythonExecutable,
|
||||
getPluginVersion,
|
||||
checkRuntimeVersion,
|
||||
classifyError,
|
||||
buildRuntimeInstallCommand,
|
||||
parseRuntimeStatus,
|
||||
ACTIONS,
|
||||
buildCommandArgs,
|
||||
runSubprocess,
|
||||
};
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
const { ACTIONS, buildCommandArgs, runSubprocess } = await import('../main.js');
|
||||
const { ACTIONS, buildCommandArgs, runSubprocess } = await import('../src/testable.js');
|
||||
|
||||
describe('ACTIONS', () => {
|
||||
it('has exactly 6 entries', () => {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const {
|
|||
classifyError,
|
||||
buildRuntimeInstallCommand,
|
||||
parseRuntimeStatus,
|
||||
} = await import('../main.js');
|
||||
} = await import('../src/testable.js');
|
||||
|
||||
describe('classifyError', () => {
|
||||
it('classifies ENOENT as python_missing', () => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const {
|
|||
resolvePythonExecutable,
|
||||
getPluginVersion,
|
||||
checkRuntimeVersion,
|
||||
} = await import('../main.js');
|
||||
} = await import('../src/testable.js');
|
||||
|
||||
describe('resolvePythonExecutable', () => {
|
||||
/** Create injected fs + execFileSync mocks */
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
import { vi } from 'vitest';
|
||||
|
||||
vi.mock('obsidian', () => ({
|
||||
Plugin: class {},
|
||||
Notice: class { setMessage() {} },
|
||||
ItemView: class {},
|
||||
Modal: class { onOpen() {} onClose() {} close() {} },
|
||||
Setting: class { setName() { return this; } setDesc() { return this; } addText() { return this; } addButton() { return this; } addDropdown() { return this; } addToggle() { return this; } },
|
||||
PluginSettingTab: class { display() {} },
|
||||
addIcon: () => {},
|
||||
}));
|
||||
|
|
@ -5,6 +5,5 @@ export default defineConfig({
|
|||
environment: 'jsdom',
|
||||
include: ['tests/**/*.test.mjs'],
|
||||
globals: true,
|
||||
setupFiles: ['./tests/setup.mjs'],
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue