mirror of
https://github.com/heyman/heynote.git
synced 2026-02-06 11:27:25 +00:00
Add tests for the main process using Vitest
Add tests for FileLibrary
This commit is contained in:
parent
2e1b7e9a41
commit
6b41281c9f
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@ -16,6 +16,8 @@ jobs:
|
||||
run: npx playwright install --with-deps
|
||||
- name: Run Playwright tests
|
||||
run: npm run test
|
||||
- name: Run Main Process tests
|
||||
run: npm run test:main
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
|
||||
1403
package-lock.json
generated
1403
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -29,6 +29,7 @@
|
||||
"webapp:build": "vite build webapp",
|
||||
"test": "playwright test",
|
||||
"test:ui": "playwright test --ui",
|
||||
"test:main": "vitest",
|
||||
"postinstall": "patch-package"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -80,6 +81,7 @@
|
||||
"vite": "^6.3.5",
|
||||
"vite-plugin-electron": "^0.11.1",
|
||||
"vite-plugin-electron-renderer": "^0.11.4",
|
||||
"vitest": "^2.1.9",
|
||||
"vue": "^3.5.13",
|
||||
"vue-tsc": "^2.2.10",
|
||||
"vuedraggable": "^4.1.0"
|
||||
|
||||
188
tests/main/file-library.test.js
Normal file
188
tests/main/file-library.test.js
Normal file
@ -0,0 +1,188 @@
|
||||
import fs from "node:fs"
|
||||
import os from "node:os"
|
||||
import path from "node:path"
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
|
||||
|
||||
import { SCRATCH_FILE_NAME } from "../../src/common/constants.js"
|
||||
|
||||
vi.mock("electron", () => ({
|
||||
app: { quit: vi.fn(), getPath: vi.fn() },
|
||||
ipcMain: { handle: vi.fn() },
|
||||
dialog: { showOpenDialog: vi.fn() },
|
||||
}))
|
||||
|
||||
vi.mock("../../electron/config.js", () => ({
|
||||
default: { get: vi.fn(() => "") },
|
||||
}))
|
||||
|
||||
const getImgReferencesMock = vi.fn(async () => [])
|
||||
|
||||
const loadFileLibrary = async ({ mockRipgrep = false } = {}) => {
|
||||
vi.resetModules()
|
||||
if (mockRipgrep) {
|
||||
vi.doMock("../../electron/main/ripgrep.js", () => ({
|
||||
searchLibrary: vi.fn(),
|
||||
getImgReferences: (...args) => getImgReferencesMock(...args),
|
||||
}))
|
||||
} else {
|
||||
vi.unmock("../../electron/main/ripgrep.js")
|
||||
}
|
||||
return await import("../../electron/main/file-library.js")
|
||||
}
|
||||
|
||||
const makeTempDir = () =>
|
||||
fs.mkdtempSync(path.join(os.tmpdir(), "heynote-file-library-"))
|
||||
|
||||
describe("FileLibrary", () => {
|
||||
let tmpDir = ""
|
||||
let library = null
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = makeTempDir()
|
||||
getImgReferencesMock.mockClear()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
if (library) {
|
||||
library.close()
|
||||
library = null
|
||||
}
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
it("creates the scratch file if missing", async () => {
|
||||
const { FileLibrary } = await loadFileLibrary()
|
||||
library = new FileLibrary(tmpDir, {})
|
||||
const scratchPath = path.join(tmpDir, SCRATCH_FILE_NAME)
|
||||
expect(fs.existsSync(scratchPath)).toBe(true)
|
||||
})
|
||||
|
||||
it("creates, loads, and saves notes", async () => {
|
||||
const { FileLibrary } = await loadFileLibrary()
|
||||
library = new FileLibrary(tmpDir, {})
|
||||
await library.create("note.txt", "hello")
|
||||
const content = await library.load("note.txt")
|
||||
expect(content).toBe("hello")
|
||||
|
||||
await library.save("note.txt", "updated")
|
||||
const updated = fs.readFileSync(path.join(tmpDir, "note.txt"), "utf8")
|
||||
expect(updated).toBe("updated")
|
||||
})
|
||||
|
||||
it("moves and deletes notes", async () => {
|
||||
const { FileLibrary } = await loadFileLibrary()
|
||||
library = new FileLibrary(tmpDir, {})
|
||||
await library.create("note.txt", "content")
|
||||
await library.move("note.txt", "moved.txt")
|
||||
|
||||
expect(fs.existsSync(path.join(tmpDir, "note.txt"))).toBe(false)
|
||||
expect(fs.existsSync(path.join(tmpDir, "moved.txt"))).toBe(true)
|
||||
|
||||
await library.delete("moved.txt")
|
||||
expect(fs.existsSync(path.join(tmpDir, "moved.txt"))).toBe(false)
|
||||
})
|
||||
|
||||
it("rejects deleting the scratch file", async () => {
|
||||
const { FileLibrary } = await loadFileLibrary()
|
||||
library = new FileLibrary(tmpDir, {})
|
||||
await expect(library.delete(SCRATCH_FILE_NAME)).rejects.toThrow(
|
||||
"Can't delete scratch file"
|
||||
)
|
||||
})
|
||||
|
||||
it("returns metadata from getList", async () => {
|
||||
const { FileLibrary } = await loadFileLibrary()
|
||||
library = new FileLibrary(tmpDir, {})
|
||||
const metadata = { name: "Test Note", tags: ["a", "b"] }
|
||||
const note = `${JSON.stringify(metadata)}\n∞∞∞text\nbody`
|
||||
|
||||
await fs.promises.writeFile(path.join(tmpDir, "meta.txt"), note, "utf8")
|
||||
await fs.promises.writeFile(
|
||||
path.join(tmpDir, "plain.txt"),
|
||||
"no metadata",
|
||||
"utf8"
|
||||
)
|
||||
|
||||
const list = await library.getList()
|
||||
expect(list["meta.txt"]).toEqual(metadata)
|
||||
expect(list["plain.txt"]).toBeNull()
|
||||
})
|
||||
|
||||
it("detects changes with NoteBuffer.loadIfChanged", async () => {
|
||||
const { FileLibrary, NoteBuffer } = await loadFileLibrary()
|
||||
library = new FileLibrary(tmpDir, {})
|
||||
const fullPath = path.join(tmpDir, "change.txt")
|
||||
await fs.promises.writeFile(fullPath, "first", "utf8")
|
||||
|
||||
const buffer = new NoteBuffer({ fullPath, library })
|
||||
await buffer.load()
|
||||
expect(await buffer.loadIfChanged()).toBeNull()
|
||||
|
||||
await fs.promises.writeFile(fullPath, "second", "utf8")
|
||||
expect(await buffer.loadIfChanged()).toBe("second")
|
||||
})
|
||||
|
||||
it("removes unreferenced images older than a day when references exist", async () => {
|
||||
const { FileLibrary } = await loadFileLibrary()
|
||||
const imagesDir = path.join(tmpDir, ".images")
|
||||
await fs.promises.mkdir(imagesDir)
|
||||
|
||||
const referencedImage = "keep.png"
|
||||
const orphanImage = "delete.png"
|
||||
await fs.promises.writeFile(path.join(imagesDir, referencedImage), "")
|
||||
await fs.promises.writeFile(path.join(imagesDir, orphanImage), "")
|
||||
|
||||
const noteContent = `<∞img;id=1;file=heynote-file://image/${referencedImage};w=1;h=1∞>`
|
||||
await fs.promises.writeFile(path.join(tmpDir, "note.txt"), noteContent)
|
||||
|
||||
library = new FileLibrary(tmpDir, {})
|
||||
const oldTime = new Date(Date.now() - 1000 * 60 * 60 * 25)
|
||||
await fs.promises.utimes(
|
||||
path.join(imagesDir, orphanImage),
|
||||
oldTime,
|
||||
oldTime
|
||||
)
|
||||
|
||||
await library.removeUnreferencedImages()
|
||||
|
||||
expect(fs.existsSync(path.join(imagesDir, referencedImage))).toBe(true)
|
||||
expect(fs.existsSync(path.join(imagesDir, orphanImage))).toBe(false)
|
||||
})
|
||||
|
||||
it("keeps unreferenced images when no references exist", async () => {
|
||||
const { FileLibrary } = await loadFileLibrary()
|
||||
const imagesDir = path.join(tmpDir, ".images")
|
||||
await fs.promises.mkdir(imagesDir)
|
||||
|
||||
const orphanImage = "keep.png"
|
||||
await fs.promises.writeFile(path.join(imagesDir, orphanImage), "")
|
||||
const oldTime = new Date(Date.now() - 1000 * 60 * 60 * 25)
|
||||
await fs.promises.utimes(
|
||||
path.join(imagesDir, orphanImage),
|
||||
oldTime,
|
||||
oldTime
|
||||
)
|
||||
|
||||
library = new FileLibrary(tmpDir, {})
|
||||
await library.removeUnreferencedImages()
|
||||
|
||||
expect(fs.existsSync(path.join(imagesDir, orphanImage))).toBe(true)
|
||||
})
|
||||
|
||||
it("keeps images when ripgrep fails", async () => {
|
||||
const { FileLibrary } = await loadFileLibrary({ mockRipgrep: true })
|
||||
const imagesDir = path.join(tmpDir, ".images")
|
||||
await fs.promises.mkdir(imagesDir)
|
||||
|
||||
const orphanImage = "keep.png"
|
||||
await fs.promises.writeFile(path.join(imagesDir, orphanImage), "")
|
||||
|
||||
getImgReferencesMock.mockRejectedValueOnce(new Error("rg failed"))
|
||||
|
||||
library = new FileLibrary(tmpDir, {})
|
||||
await library.removeUnreferencedImages()
|
||||
|
||||
expect(fs.existsSync(path.join(imagesDir, orphanImage))).toBe(true)
|
||||
})
|
||||
})
|
||||
16
vitest.config.mjs
Normal file
16
vitest.config.mjs
Normal file
@ -0,0 +1,16 @@
|
||||
import path from "node:path"
|
||||
import { fileURLToPath } from "node:url"
|
||||
import { defineConfig } from "vitest/config"
|
||||
|
||||
export default defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(path.dirname(fileURLToPath(import.meta.url))),
|
||||
},
|
||||
},
|
||||
test: {
|
||||
environment: "node",
|
||||
include: ["tests/main/**/*.test.js"],
|
||||
exclude: ["tests/**/*.spec.js", "tests/**/*.spec.ts"],
|
||||
},
|
||||
})
|
||||
Loading…
Reference in New Issue
Block a user