add unittest

This commit is contained in:
vorotamoroz
2026-01-23 05:45:53 +00:00
parent acf4bc3737
commit b97756d0cf
5 changed files with 230 additions and 170 deletions

33
.github/workflows/unit-ci.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
# Run Unit test without Harnesses
name: unit-ci
on:
workflow_dispatch:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24.x'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Install test dependencies (Playwright Chromium)
run: npm run test:install-dependencies
- name: Run unit tests suite
run: npm run test:unit

View File

@@ -26,6 +26,8 @@
"check": "npm run lint && npm run svelte-check",
"unittest": "deno test -A --no-check --coverage=cov_profile --v8-flags=--expose-gc --trace-leaks ./src/",
"test": "vitest run",
"test:unit": "vitest run --config vitest.config.unit.ts",
"test:unit:coverage": "vitest run --config vitest.config.unit.ts --coverage",
"test:install-playwright": "npx playwright install chromium",
"test:install-dependencies": "npm run test:install-playwright",
"test:coverage": "vitest run --coverage",

110
vitest.config.common.ts Normal file
View File

@@ -0,0 +1,110 @@
import { defineConfig } from "vitest/config";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import { sveltePreprocess } from "svelte-preprocess";
import inlineWorkerPlugin from "esbuild-plugin-inline-worker";
import path from "path";
import { fileURLToPath } from "node:url";
import fs from "node:fs";
import { platform } from "node:process";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const manifestJson = JSON.parse(fs.readFileSync("./manifest.json") + "");
const packageJson = JSON.parse(fs.readFileSync("./package.json") + "");
const updateInfo = JSON.stringify(fs.readFileSync("./updates.md") + "");
const prod = false;
const moduleAliasPlugin = {
name: "module-alias",
setup(build: any) {
build.onResolve({ filter: /.(dev)(.ts|)$/ }, (args: any) => {
// console.log(args.path);
if (prod) {
const prodTs = args.path.replace(".dev", ".prod");
const statFile = prodTs.endsWith(".ts") ? prodTs : prodTs + ".ts";
const realPath = path.join(args.resolveDir, statFile);
console.log(`Checking ${statFile}`);
if (fs.existsSync(realPath)) {
console.log(`Replaced ${args.path} with ${prodTs}`);
return {
path: realPath,
namespace: "file",
};
}
}
return null;
});
build.onResolve({ filter: /.(platform)(.ts|)$/ }, (args: any) => {
// console.log(args.path);
if (prod) {
const prodTs = args.path.replace(".platform", ".obsidian");
const statFile = prodTs.endsWith(".ts") ? prodTs : prodTs + ".ts";
const realPath = path.join(args.resolveDir, statFile);
console.log(`Checking ${statFile}`);
if (fs.existsSync(realPath)) {
console.log(`Replaced ${args.path} with ${prodTs}`);
return {
path: realPath,
namespace: "file",
};
}
}
return null;
});
},
};
const externals = [
"obsidian",
"electron",
"crypto",
"@codemirror/autocomplete",
"@codemirror/collab",
"@codemirror/commands",
"@codemirror/language",
"@codemirror/lint",
"@codemirror/search",
"@codemirror/state",
"@codemirror/view",
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
];
const define = {
MANIFEST_VERSION: `"${manifestJson.version}"`,
PACKAGE_VERSION: `"${packageJson.version}"`,
UPDATE_INFO: `${updateInfo}`,
global: "globalThis",
hostPlatform: `"${platform}"`,
};
export default defineConfig({
plugins: [
moduleAliasPlugin,
inlineWorkerPlugin({
external: externals,
treeShaking: true,
}),
svelte({
preprocess: sveltePreprocess(),
compilerOptions: { css: "injected", preserveComments: false },
}),
],
resolve: {
alias: {
obsidian: path.resolve(__dirname, "./test/harness/obsidian-mock.ts"),
"@": path.resolve(__dirname, "./src"),
"@lib": path.resolve(__dirname, "./src/lib/src"),
src: path.resolve(__dirname, "./src"),
},
},
esbuild: {
define: define,
target: "es2018",
platform: "browser",
},
// define,
server: {
headers: {
"Service-Worker-Allowed": "/",
},
port: 5173,
},
});

View File

@@ -1,180 +1,76 @@
import { defineConfig } from "vitest/config";
import { defineConfig, mergeConfig } from "vitest/config";
import { playwright } from "@vitest/browser-playwright";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import { sveltePreprocess } from "svelte-preprocess";
import inlineWorkerPlugin from "esbuild-plugin-inline-worker";
import path from "path";
import { fileURLToPath } from "node:url";
import fs from "node:fs";
import viteConfig from "./vitest.config.common";
import dotenv from "dotenv";
import { platform } from "node:process";
import { acceptWebPeer, closeWebPeer, grantClipboardPermissions, openWebPeer } from "./test/lib/commands.ts";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
import { grantClipboardPermissions, openWebPeer, closeWebPeer, acceptWebPeer } from "./test/lib/commands";
const defEnv = dotenv.config({ path: ".env" }).parsed;
const testEnv = dotenv.config({ path: ".test.env" }).parsed;
const env = Object.assign({}, defEnv, testEnv);
const debuggerEnabled = env?.ENABLE_DEBUGGER === "true";
const enableUI = env?.ENABLE_UI === "true";
// const livesyncLogsEnabled = env?.PRINT_LIVESYNC_LOGS === "true";
const headless = !debuggerEnabled && !enableUI;
const manifestJson = JSON.parse(fs.readFileSync("./manifest.json") + "");
const packageJson = JSON.parse(fs.readFileSync("./package.json") + "");
const updateInfo = JSON.stringify(fs.readFileSync("./updates.md") + "");
const prod = false;
const moduleAliasPlugin = {
name: "module-alias",
setup(build: any) {
build.onResolve({ filter: /.(dev)(.ts|)$/ }, (args: any) => {
// console.log(args.path);
if (prod) {
const prodTs = args.path.replace(".dev", ".prod");
const statFile = prodTs.endsWith(".ts") ? prodTs : prodTs + ".ts";
const realPath = path.join(args.resolveDir, statFile);
console.log(`Checking ${statFile}`);
if (fs.existsSync(realPath)) {
console.log(`Replaced ${args.path} with ${prodTs}`);
return {
path: realPath,
namespace: "file",
};
}
}
return null;
});
build.onResolve({ filter: /.(platform)(.ts|)$/ }, (args: any) => {
// console.log(args.path);
if (prod) {
const prodTs = args.path.replace(".platform", ".obsidian");
const statFile = prodTs.endsWith(".ts") ? prodTs : prodTs + ".ts";
const realPath = path.join(args.resolveDir, statFile);
console.log(`Checking ${statFile}`);
if (fs.existsSync(realPath)) {
console.log(`Replaced ${args.path} with ${prodTs}`);
return {
path: realPath,
namespace: "file",
};
}
}
return null;
});
},
};
const externals = [
"obsidian",
"electron",
"crypto",
"@codemirror/autocomplete",
"@codemirror/collab",
"@codemirror/commands",
"@codemirror/language",
"@codemirror/lint",
"@codemirror/search",
"@codemirror/state",
"@codemirror/view",
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
];
const define = {
MANIFEST_VERSION: `"${manifestJson.version}"`,
PACKAGE_VERSION: `"${packageJson.version}"`,
UPDATE_INFO: `${updateInfo}`,
global: "globalThis",
hostPlatform: `"${platform}"`,
};
export default defineConfig({
plugins: [
moduleAliasPlugin,
inlineWorkerPlugin({
external: externals,
treeShaking: true,
}),
svelte({
preprocess: sveltePreprocess(),
compilerOptions: { css: "injected", preserveComments: false },
}),
],
resolve: {
alias: {
obsidian: path.resolve(__dirname, "./test/harness/obsidian-mock.ts"),
"@": path.resolve(__dirname, "./src"),
"@lib": path.resolve(__dirname, "./src/lib/src"),
src: path.resolve(__dirname, "./src"),
},
},
esbuild: {
define: define,
target: "es2018",
platform: "browser",
},
// define,
server: {
headers: {
"Service-Worker-Allowed": "/",
},
port: 5173,
},
test: {
env: env,
testTimeout: 40000,
hookTimeout: 50000,
fileParallelism: false,
isolate: true,
watch: false,
// environment: "browser",
include: ["test/**/*.test.ts"],
coverage: {
include: ["src/**/*.ts", "src/lib/src/**/*.ts", "src/**/*.svelte"],
exclude: ["**/*.test.ts", "src/lib/**"],
provider: "v8",
reporter: ["text", "json", "html"],
// ignoreEmptyLines: true,
},
browser: {
isolate: true,
commands: {
grantClipboardPermissions,
openWebPeer,
closeWebPeer,
acceptWebPeer,
},
provider: playwright({
launchOptions: {
args: ["--js-flags=--expose-gc"],
// chromiumSandbox: true,
},
}),
enabled: true,
screenshotFailures: false,
instances: [
{
execArgv: ["--js-flags=--expose-gc"],
browser: "chromium",
headless,
isolate: true,
inspector: debuggerEnabled
? {
waitForDebugger: true,
enabled: true,
}
: undefined,
printConsoleTrace: debuggerEnabled,
onUnhandledError(error) {
// Ignore certain errors
const msg = error.message || "";
if (msg.includes("Cannot create so many PeerConnections")) {
return false;
}
},
},
],
headless,
export default mergeConfig(
viteConfig,
defineConfig({
test: {
env: env,
testTimeout: 40000,
hookTimeout: 50000,
fileParallelism: false,
ui: debuggerEnabled || enableUI ? true : false,
isolate: true,
watch: false,
// environment: "browser",
include: ["test/**/*.test.ts"],
coverage: {
include: ["src/**/*.ts", "src/lib/src/**/*.ts", "src/**/*.svelte"],
exclude: ["**/*.test.ts", "src/lib/**"],
provider: "v8",
reporter: ["text", "json", "html"],
// ignoreEmptyLines: true,
},
browser: {
isolate: true,
commands: {
grantClipboardPermissions,
openWebPeer,
closeWebPeer,
acceptWebPeer,
},
provider: playwright({
launchOptions: {
args: ["--js-flags=--expose-gc"],
// chromiumSandbox: true,
},
}),
enabled: true,
screenshotFailures: false,
instances: [
{
execArgv: ["--js-flags=--expose-gc"],
browser: "chromium",
headless,
isolate: true,
inspector: debuggerEnabled
? {
waitForDebugger: true,
enabled: true,
}
: undefined,
printConsoleTrace: debuggerEnabled,
onUnhandledError(error) {
// Ignore certain errors
const msg = error.message || "";
if (msg.includes("Cannot create so many PeerConnections")) {
return false;
}
},
},
],
headless,
fileParallelism: false,
ui: debuggerEnabled || enableUI ? true : false,
},
},
},
});
})
);

19
vitest.config.unit.ts Normal file
View File

@@ -0,0 +1,19 @@
import { defineConfig, mergeConfig } from "vitest/config";
import viteConfig from "./vitest.config.common";
export default mergeConfig(
viteConfig,
defineConfig({
test: {
name: "unit-tests",
include: ["**/*unit.test.ts"],
exclude: ["test/**"],
coverage: {
include: ["src/**/*.ts"],
exclude: ["**/*.test.ts", "src/lib/**/*.test.ts", "**/_*", "src/lib/apps", "src/lib/src/cli"],
provider: "v8",
reporter: ["text", "json", "html"],
},
},
})
);