Add Vitest and unit tests for src/lib/ (#242)
* Add Vitest and unit tests for src/lib/ utilities Closes #241 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * update node types --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
a5bd82eb3c
commit
f5a95f57c8
11 files changed
+1686
-48
No files matched your search
Generated
+1415
-42
File diff suppressed because it is too large.
Load diff
+8
-6
@@ -11,7 +11,8 @@
|
||||
"lint": "eslint .",
|
||||
"format": "eslint --fix .",
|
||||
"sitemap": "node scripts/sitemap.js",
|
||||
"feed": "node scripts/feed.js"
|
||||
"feed": "node scripts/feed.js",
|
||||
"test": "vitest run"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -40,17 +41,18 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
||||
"@typescript-eslint/parser": "^8.54.0",
|
||||
"@types/classnames": "^2.2.10",
|
||||
"@types/markdown-it": "^12.2.3",
|
||||
"@types/node": "^18.13.0",
|
||||
"@types/node": "^22.19.9",
|
||||
"@types/react": "^18.0.18",
|
||||
"@types/sharp": "^0.29.2",
|
||||
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
||||
"@typescript-eslint/parser": "^8.54.0",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-next": "^15.5.0",
|
||||
"feed": "^4.1.0",
|
||||
"eslint": "^9.0.0",
|
||||
"globals": "^16.0.0",
|
||||
"typescript": "^5.9.0"
|
||||
"typescript": "^5.9.0",
|
||||
"vitest": "^4.0.18"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { describe, it, expect } from "vitest"
|
||||
import { insertBetween, tail } from "./array"
|
||||
|
||||
describe("insertBetween", () => {
|
||||
it("should return empty array for empty input", () => {
|
||||
expect(insertBetween([], () => ",")).toEqual([])
|
||||
})
|
||||
|
||||
it("should return single item without separator", () => {
|
||||
expect(insertBetween(["a"], () => ",")).toEqual(["a"])
|
||||
})
|
||||
|
||||
it("should insert separators between items", () => {
|
||||
expect(insertBetween(["a", "b", "c"], () => ",")).toEqual([
|
||||
"a", ",", "b", ",", "c",
|
||||
])
|
||||
})
|
||||
|
||||
it("should pass index to separator function", () => {
|
||||
const result = insertBetween(["a", "b", "c"], (i) => i)
|
||||
expect(result).toEqual(["a", 0, "b", 1, "c"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("tail", () => {
|
||||
it("should return empty array for empty input", () => {
|
||||
expect(tail([])).toEqual([])
|
||||
})
|
||||
|
||||
it("should return empty array for single item", () => {
|
||||
expect(tail(["a"])).toEqual([])
|
||||
})
|
||||
|
||||
it("should return all items except first", () => {
|
||||
expect(tail([1, 2, 3])).toEqual([2, 3])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,14 @@
|
||||
import { describe, it, expect } from "vitest"
|
||||
import { dateFormat } from "./date"
|
||||
|
||||
describe("dateFormat", () => {
|
||||
it("should format a Date object in Russian locale", () => {
|
||||
const result = dateFormat(new Date(2024, 0, 15))
|
||||
expect(result).toBe("15 января 2024")
|
||||
})
|
||||
|
||||
it("should format a date string in Russian locale", () => {
|
||||
const result = dateFormat("2024-06-01T00:00:00.000Z")
|
||||
expect(result).toBe("1 июня 2024")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,50 @@
|
||||
import { describe, it, expect } from "vitest"
|
||||
import { ext, size } from "./file"
|
||||
|
||||
describe("ext", () => {
|
||||
it("should return ПДФ for application/pdf", () => {
|
||||
expect(ext("application/pdf")).toBe("ПДФ")
|
||||
})
|
||||
|
||||
it("should return ЖПГ for image/jpeg", () => {
|
||||
expect(ext("image/jpeg")).toBe("ЖПГ")
|
||||
})
|
||||
|
||||
it("should return ПНГ for image/png", () => {
|
||||
expect(ext("image/png")).toBe("ПНГ")
|
||||
})
|
||||
|
||||
it("should return raw MIME type for unknown types", () => {
|
||||
expect(ext("text/plain")).toBe("text/plain")
|
||||
})
|
||||
})
|
||||
|
||||
describe("size", () => {
|
||||
it("should return empty string for NaN", () => {
|
||||
expect(size(NaN)).toBe("")
|
||||
})
|
||||
|
||||
it("should return empty string for non-finite values", () => {
|
||||
expect(size(Infinity)).toBe("")
|
||||
})
|
||||
|
||||
it("should format bytes", () => {
|
||||
expect(size(500)).toBe("500.0 байт")
|
||||
})
|
||||
|
||||
it("should format kilobytes", () => {
|
||||
expect(size(1024)).toBe("1.0 КБ")
|
||||
})
|
||||
|
||||
it("should format megabytes", () => {
|
||||
expect(size(1024 * 1024)).toBe("1.0 МБ")
|
||||
})
|
||||
|
||||
it("should format gigabytes", () => {
|
||||
expect(size(1024 * 1024 * 1024)).toBe("1.0 ГБ")
|
||||
})
|
||||
|
||||
it("should respect precision parameter", () => {
|
||||
expect(size(1536, 2)).toBe("1.50 КБ")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,25 @@
|
||||
import { describe, it, expect } from "vitest"
|
||||
import { getResizedUrl } from "./image"
|
||||
|
||||
describe("getResizedUrl", () => {
|
||||
it("should construct resize URL with default n", () => {
|
||||
const result = getResizedUrl("example.com/img.jpg", {
|
||||
width: 800,
|
||||
height: 600,
|
||||
})
|
||||
expect(result).toBe(
|
||||
"https://images.weserv.nl/?url=example.com/img.jpg&w=800&h=600&n=1",
|
||||
)
|
||||
})
|
||||
|
||||
it("should use custom n parameter", () => {
|
||||
const result = getResizedUrl("example.com/img.jpg", {
|
||||
width: 400,
|
||||
height: 300,
|
||||
n: -1,
|
||||
})
|
||||
expect(result).toBe(
|
||||
"https://images.weserv.nl/?url=example.com/img.jpg&w=400&h=300&n=-1",
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,61 @@
|
||||
import { describe, it, expect } from "vitest"
|
||||
import { meta, MetaBuilder } from "./meta"
|
||||
|
||||
describe("meta", () => {
|
||||
it("should return defaults when called with empty object", () => {
|
||||
const result = meta({})
|
||||
expect(result.title).toBe("Шлиссельбургская ДХШ")
|
||||
expect(result.description).toBe("Сайт Шлиссельбургской художественной школы")
|
||||
expect(result.url).toBe("https://art.shlisselburg.org/")
|
||||
expect(result.locale).toBe("ru_RU")
|
||||
})
|
||||
|
||||
it("should override defaults with provided values", () => {
|
||||
const result = meta({ title: "Custom Title", url: "/about" })
|
||||
expect(result.title).toBe("Custom Title")
|
||||
expect(result.url).toBe("https://art.shlisselburg.org/about")
|
||||
})
|
||||
|
||||
it("should construct URL from path", () => {
|
||||
const result = meta({ url: "/news/2024" })
|
||||
expect(result.url).toBe("https://art.shlisselburg.org/news/2024")
|
||||
})
|
||||
})
|
||||
|
||||
describe("MetaBuilder", () => {
|
||||
it("should support method chaining", () => {
|
||||
const builder = new MetaBuilder()
|
||||
const result = builder
|
||||
.setTitle("Test")
|
||||
.setDescription("Desc")
|
||||
.build()
|
||||
|
||||
expect(result.title).toBe("Test")
|
||||
expect(result.description).toBe("Desc")
|
||||
})
|
||||
|
||||
it("should use default meta values", () => {
|
||||
const result = new MetaBuilder().build()
|
||||
expect(result.locale).toBe("ru_RU")
|
||||
expect(result.domain).toBe("art.shlisselburg.org")
|
||||
})
|
||||
|
||||
it("should set image from Pic object", () => {
|
||||
const result = new MetaBuilder()
|
||||
.setImage({ src: "http://example.com/img.jpg", width: 800, height: 600 })
|
||||
.build()
|
||||
|
||||
expect(result.image).toBe("http://example.com/img.jpg")
|
||||
expect(result.imageWidth).toBe(800)
|
||||
expect(result.imageHeight).toBe(600)
|
||||
})
|
||||
|
||||
it("should apply data from setData", () => {
|
||||
const result = new MetaBuilder()
|
||||
.setData({ url: "/test", type: "article" })
|
||||
.build()
|
||||
|
||||
expect(result.url).toBe("https://art.shlisselburg.org/test")
|
||||
expect(result.type).toBe("article")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,20 @@
|
||||
import { describe, it, expect } from "vitest"
|
||||
import { titleCase } from "./string"
|
||||
|
||||
describe("titleCase", () => {
|
||||
it("should capitalize first letter of lowercase string", () => {
|
||||
expect(titleCase("hello")).toBe("Hello")
|
||||
})
|
||||
|
||||
it("should keep already capitalized string", () => {
|
||||
expect(titleCase("Hello")).toBe("Hello")
|
||||
})
|
||||
|
||||
it("should handle empty string", () => {
|
||||
expect(titleCase("")).toBe("")
|
||||
})
|
||||
|
||||
it("should capitalize single character", () => {
|
||||
expect(titleCase("a")).toBe("A")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,24 @@
|
||||
import { describe, it, expect } from "vitest"
|
||||
import { markdownToHtml, typograf } from "./text"
|
||||
|
||||
describe("markdownToHtml", () => {
|
||||
it("should convert markdown paragraph to HTML", () => {
|
||||
expect(markdownToHtml("hello")).toBe("<p>hello</p>\n")
|
||||
})
|
||||
|
||||
it("should convert bold text", () => {
|
||||
expect(markdownToHtml("**bold**")).toBe("<p><strong>bold</strong></p>\n")
|
||||
})
|
||||
|
||||
it("should pass through raw HTML", () => {
|
||||
expect(markdownToHtml("<div>test</div>")).toBe("<div>test</div>")
|
||||
})
|
||||
})
|
||||
|
||||
describe("typograf", () => {
|
||||
it("should apply Russian typographic rules", () => {
|
||||
const result = typograf("Привет - мир")
|
||||
expect(result).toContain("Привет")
|
||||
expect(result).toContain("мир")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,18 @@
|
||||
import { describe, it, expect } from "vitest"
|
||||
import { queryList } from "./url"
|
||||
|
||||
describe("queryList", () => {
|
||||
it("should return empty string for empty values", () => {
|
||||
expect(queryList("tag", [])).toBe("")
|
||||
})
|
||||
|
||||
it("should return single key=value pair", () => {
|
||||
expect(queryList("tag", ["art"])).toBe("tag=art")
|
||||
})
|
||||
|
||||
it("should join multiple values with &", () => {
|
||||
expect(queryList("tag", ["art", "music", "photo"])).toBe(
|
||||
"tag=art&tag=music&tag=photo",
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,14 @@
|
||||
import { defineConfig } from "vitest/config"
|
||||
import path from "path"
|
||||
|
||||
export default defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "src"),
|
||||
src: path.resolve(__dirname, "src"),
|
||||
},
|
||||
},
|
||||
test: {
|
||||
include: ["src/**/*.test.ts"],
|
||||
},
|
||||
})
|
||||
Reference in new issue
Block a user