Chore: Add baseline unit tests (#2417)

* Fix: Better Windows path support

* Move to 'chai' for test running

* Fix: Let's start with what we know

* Chore: Add 'root' level file path test, remove less useful tests

* Chore: Add 'root' level file path test, remove less useful tests

---------

Co-authored-by: Dennis Bartlett <bartlett.dc.1@gmail.com>
This commit is contained in:
Mark Percival 2025-04-03 23:57:07 -04:00 committed by GitHub
parent cac0309579
commit 21e95ab67e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 536 additions and 7 deletions

View File

@ -0,0 +1,6 @@
---
"claude-dev": patch
---
Chore: Add tests to context-mentions, run tests with mocha and ts-node - `npm run test:unit`

View File

@ -73,6 +73,9 @@ jobs:
- name: Build Extension
run: npm run compile
- name: Unit Tests
run: npm run test:unit
# Run extension tests with coverage
- name: Extension Tests with Coverage
id: extension_coverage

6
.mocharc.json Normal file
View File

@ -0,0 +1,6 @@
{
"extension": ["ts"],
"spec": "src/**/__tests__/*.ts",
"require": ["ts-node/register", "source-map-support/register"],
"recursive": true
}

170
package-lock.json generated
View File

@ -82,6 +82,7 @@
"prettier": "^3.3.3",
"should": "^13.2.3",
"sinon": "^19.0.2",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
},
"engines": {
@ -4286,6 +4287,30 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "0.3.9"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
@ -8569,6 +8594,34 @@
"integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
"license": "MIT"
},
"node_modules/@tsconfig/node10": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
"dev": true,
"license": "MIT"
},
"node_modules/@tsconfig/node12": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
"dev": true,
"license": "MIT"
},
"node_modules/@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
"dev": true,
"license": "MIT"
},
"node_modules/@tsconfig/node16": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/chai": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.0.1.tgz",
@ -9112,6 +9165,19 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/acorn-walk": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"acorn": "^8.11.0"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/agent-base": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
@ -9215,6 +9281,13 @@
"integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
"license": "ISC"
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true,
"license": "MIT"
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@ -10173,6 +10246,13 @@
"node": ">= 0.10"
}
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true,
"license": "MIT"
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@ -10863,9 +10943,9 @@
}
},
"node_modules/escalade": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"license": "MIT",
"engines": {
"node": ">=6"
@ -13446,6 +13526,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true,
"license": "ISC"
},
"node_modules/mammoth": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/mammoth/-/mammoth-1.8.0.tgz",
@ -15608,9 +15695,9 @@
"license": "MIT"
},
"node_modules/semver": {
"version": "7.6.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"version": "7.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@ -16608,6 +16695,60 @@
"typescript": ">=4.2.0"
}
},
"node_modules/ts-node": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-cwd": "dist/bin-cwd.js",
"ts-node-esm": "dist/bin-esm.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"peerDependencies": {
"@swc/core": ">=1.2.50",
"@swc/wasm": ">=1.2.50",
"@types/node": "*",
"typescript": ">=2.7"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
},
"@swc/wasm": {
"optional": true
}
}
},
"node_modules/ts-node/node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
@ -16932,6 +17073,13 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"dev": true,
"license": "MIT"
},
"node_modules/v8-to-istanbul": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz",
@ -17486,6 +17634,16 @@
"fd-slicer": "~1.1.0"
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",

View File

@ -303,6 +303,7 @@
"format:fix": "prettier . --write",
"test": "vscode-test",
"test:ci": "node scripts/test-ci.js",
"test:unit": "TS_NODE_PROJECT='./tsconfig.unit-test.json' mocha",
"test:coverage": "vscode-test --coverage",
"install:all": "npm install && cd webview-ui && npm install",
"dev:webview": "cd webview-ui && npm run dev",
@ -339,6 +340,7 @@
"prettier": "^3.3.3",
"should": "^13.2.3",
"sinon": "^19.0.2",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
},
"dependencies": {

View File

@ -0,0 +1,155 @@
import { ContextManager } from "../ContextManager"
import { Anthropic } from "@anthropic-ai/sdk"
import { expect } from "chai"
describe("ContextManager", () => {
function createMessages(count: number): Anthropic.Messages.MessageParam[] {
const messages: Anthropic.Messages.MessageParam[] = []
messages.push({
role: "user",
content: "Initial task message",
})
let role: "user" | "assistant" = "assistant"
for (let i = 1; i < count; i++) {
messages.push({
role,
content: `Message ${i}`,
})
role = role === "user" ? "assistant" : "user"
}
return messages
}
describe("getNextTruncationRange", () => {
let contextManager: ContextManager
beforeEach(() => {
contextManager = new ContextManager()
})
it("first truncation with half keep", () => {
const messages = createMessages(11)
const result = contextManager.getNextTruncationRange(messages, undefined, "half")
expect(result).to.deep.equal([1, 4])
})
it("first truncation with quarter keep", () => {
const messages = createMessages(11)
const result = contextManager.getNextTruncationRange(messages, undefined, "quarter")
expect(result).to.deep.equal([1, 6])
})
it("sequential truncation with half keep", () => {
const messages = createMessages(21)
const firstRange = contextManager.getNextTruncationRange(messages, undefined, "half")
expect(firstRange).to.deep.equal([1, 10])
// Pass the previous range for sequential truncation
const secondRange = contextManager.getNextTruncationRange(messages, firstRange, "half")
expect(secondRange).to.deep.equal([1, 14])
})
it("sequential truncation with quarter keep", () => {
const messages = createMessages(41)
const firstRange = contextManager.getNextTruncationRange(messages, undefined, "quarter")
const secondRange = contextManager.getNextTruncationRange(messages, firstRange, "quarter")
expect(secondRange[0]).to.equal(1)
expect(secondRange[1]).to.be.greaterThan(firstRange[1])
})
it("ensures the last message in range is a user message", () => {
const messages = createMessages(14)
const result = contextManager.getNextTruncationRange(messages, undefined, "half")
// Check if the message at the end of range is a user message
const lastRemovedMessage = messages[result[1]]
expect(lastRemovedMessage.role).to.equal("user")
// Check if the next message after the range is an assistant message
const nextMessage = messages[result[1] + 1]
expect(nextMessage.role).to.equal("assistant")
})
it("handles small message arrays", () => {
const messages = createMessages(3)
const result = contextManager.getNextTruncationRange(messages, undefined, "half")
expect(result).to.deep.equal([1, 0])
})
it("preserves the message structure when truncating", () => {
const messages = createMessages(20)
const result = contextManager.getNextTruncationRange(messages, undefined, "half")
// Get messages after removing the range
const effectiveMessages = [...messages.slice(0, result[0]), ...messages.slice(result[1] + 1)]
// Check first message and alternating pattern
expect(effectiveMessages[0].role).to.equal("user")
for (let i = 1; i < effectiveMessages.length; i++) {
const expectedRole = i % 2 === 1 ? "assistant" : "user"
expect(effectiveMessages[i].role).to.equal(expectedRole)
}
})
})
describe("getTruncatedMessages", () => {
let contextManager: ContextManager
beforeEach(() => {
contextManager = new ContextManager()
})
it("returns original messages when no range is provided", () => {
const messages = createMessages(3)
const result = contextManager.getTruncatedMessages(messages, undefined)
expect(result).to.deep.equal(messages)
})
it("correctly removes messages in the specified range", () => {
const messages = createMessages(5)
const range: [number, number] = [1, 3]
const result = contextManager.getTruncatedMessages(messages, range)
expect(result).to.have.lengthOf(2)
expect(result[0]).to.deep.equal(messages[0])
expect(result[1]).to.deep.equal(messages[4])
})
it("works with a range that starts at the first message after task", () => {
const messages = createMessages(4)
const range: [number, number] = [1, 2]
const result = contextManager.getTruncatedMessages(messages, range)
expect(result).to.have.lengthOf(2)
expect(result[0]).to.deep.equal(messages[0])
expect(result[1]).to.deep.equal(messages[3])
})
it("correctly handles removing a range while preserving alternation pattern", () => {
const messages = createMessages(5)
const range: [number, number] = [1, 2]
const result = contextManager.getTruncatedMessages(messages, range)
expect(result).to.have.lengthOf(3)
expect(result[0]).to.deep.equal(messages[0])
expect(result[1]).to.deep.equal(messages[3])
expect(result[2]).to.deep.equal(messages[4])
expect(result[0].role).to.equal("user")
expect(result[1].role).to.equal("assistant")
expect(result[2].role).to.equal("user")
})
})
})

View File

@ -0,0 +1,191 @@
import { expect } from "chai"
import { mentionRegex, mentionRegexGlobal } from "../context-mentions"
interface TestResult {
actual: string | null
expected: string | null
}
function testMention(input: string, expected: string | null): TestResult {
const match = mentionRegex.exec(input)
return {
actual: match ? match[0] : null,
expected,
}
}
function assertMatch(result: TestResult) {
expect(result.actual).eq(result.expected)
return true
}
describe("Mention Regex", () => {
describe("Windows Path Support", () => {
it("matches simple Windows paths", () => {
const cases: Array<[string, string]> = [
["@/C:\\folder\\file.txt", "@/C:\\folder\\file.txt"],
["@/C:\\file.txt", "@/C:\\file.txt"],
]
cases.forEach(([input, expected]) => {
const result = testMention(input, expected)
assertMatch(result)
})
})
})
describe("Edge Cases", () => {
it("handles edge cases correctly", () => {
const cases: Array<[string, string]> = [
["@/C:\\Users\\name\\path\\to\\文件夹\\file.txt", "@/C:\\Users\\name\\path\\to\\文件夹\\file.txt"],
["@/path123/file-name_2.0.txt", "@/path123/file-name_2.0.txt"],
]
cases.forEach(([input, expected]) => {
const result = testMention(input, expected)
assertMatch(result)
})
})
})
describe("Existing Functionality", () => {
it("matches Unix paths", () => {
const cases: Array<[string, string]> = [
["@/usr/local/bin/file", "@/usr/local/bin/file"],
["@/path/to/file.txt", "@/path/to/file.txt"],
["@//etc/host", "@//etc/host"],
]
cases.forEach(([input, expected]) => {
const result = testMention(input, expected)
assertMatch(result)
})
})
it("matches URLs", () => {
const cases: Array<[string, string]> = [
["@http://example.com", "@http://example.com"],
["@https://example.com/path/to/file.html", "@https://example.com/path/to/file.html"],
["@ftp://server.example.com/file.zip", "@ftp://server.example.com/file.zip"],
]
cases.forEach(([input, expected]) => {
const result = testMention(input, expected)
assertMatch(result)
})
})
it("matches git hashes", () => {
const cases: Array<[string, string]> = [
["@abcdef1234567890abcdef1234567890abcdef12", "@abcdef1234567890abcdef1234567890abcdef12"],
]
cases.forEach(([input, expected]) => {
const result = testMention(input, expected)
assertMatch(result)
})
})
it("matches special keywords", () => {
const cases: Array<[string, string]> = [
["@problems", "@problems"],
["@git-changes", "@git-changes"],
["@terminal", "@terminal"],
]
cases.forEach(([input, expected]) => {
const result = testMention(input, expected)
assertMatch(result)
})
})
})
describe("Invalid Patterns", () => {
it("rejects invalid patterns", () => {
const cases: Array<[string, null]> = [
["C:\\folder\\file.txt", null],
["@", null],
["@ C:\\file.txt", null],
]
cases.forEach(([input, expected]) => {
const result = testMention(input, expected)
assertMatch(result)
})
})
it("matches only until invalid characters", () => {
const result = testMention("@/C:\\folder\\file.txt invalid suffix", "@/C:\\folder\\file.txt")
assertMatch(result)
})
})
describe("In Context", () => {
it("matches mentions within text", () => {
const cases: Array<[string, string]> = [
["Check the file at @/C:\\folder\\file.txt for details.", "@/C:\\folder\\file.txt"],
["Review @problems and @git-changes.", "@problems"],
["Multiple: @/file1.txt and @/C:\\file2.txt and @terminal", "@/file1.txt"],
]
cases.forEach(([input, expected]) => {
const result = testMention(input, expected)
assertMatch(result)
})
})
})
describe("Multiple Mentions", () => {
it("finds all mentions in a string using global regex", () => {
const text = "Check @/path/file1.txt and @/C:\\folder\\file2.txt and report any @problems to @git-changes"
const matches = text.match(mentionRegexGlobal)
expect(matches).deep.eq(["@/path/file1.txt", "@/C:\\folder\\file2.txt", "@problems", "@git-changes"])
})
})
describe("Special Characters in Paths", () => {
it("handles special characters in file paths", () => {
const cases: Array<[string, string]> = [
["@/path/with-dash/file_underscore.txt", "@/path/with-dash/file_underscore.txt"],
["@/C:\\folder+plus\\file(parens)[]brackets.txt", "@/C:\\folder+plus\\file(parens)[]brackets.txt"],
["@/path/with/file#hash%percent.txt", "@/path/with/file#hash%percent.txt"],
["@/path/with/file@symbol$dollar.txt", "@/path/with/file@symbol$dollar.txt"],
]
cases.forEach(([input, expected]) => {
const result = testMention(input, expected)
assertMatch(result)
})
})
})
describe("Mixed Path Types in Single String", () => {
it("correctly identifies the first path in a string with multiple path types", () => {
const text = "Check both @/unix/path and @/C:\\windows\\path for details."
const result = mentionRegex.exec(text) || []
expect(result[0]).eq("@/unix/path")
// Test starting from after the first match
const secondSearchStart = text.indexOf("@/C:")
const secondResult = mentionRegex.exec(text.substring(secondSearchStart)) || []
expect(secondResult[0]).eq("@/C:\\windows\\path")
})
})
describe("Non-Latin Character Support", () => {
it("handles international characters in paths", () => {
const cases: Array<[string, string]> = [
["@/path/to/你好/file.txt", "@/path/to/你好/file.txt"],
["@/C:\\用户\\документы\\файл.txt", "@/C:\\用户\\документы\\файл.txt"],
["@/путь/к/файлу.txt", "@/путь/к/файлу.txt"],
["@/C:\\folder\\file_äöü.txt", "@/C:\\folder\\file_äöü.txt"],
]
cases.forEach(([input, expected]) => {
const result = testMention(input, expected)
assertMatch(result)
})
})
})
})

View File

@ -14,5 +14,5 @@
"rootDir": "src"
},
"include": ["src/**/*.test.ts"],
"exclude": ["src/test/**/*.js"]
"exclude": ["src/test/**/*.js", "src/**/__tests__/*"]
}

8
tsconfig.unit-test.json Normal file
View File

@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs"
},
"include": ["test/**/*.ts"],
"exclude": ["node_modules"]
}