test: migrate jest to vitest
This commit is contained in:
parent
1c7a20be44
commit
a770e08013
4
.github/workflows/jan-linter-and-test.yml
vendored
4
.github/workflows/jan-linter-and-test.yml
vendored
@ -68,7 +68,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ref-lcov.info
|
||||
path: coverage/merged/lcov.info
|
||||
path: coverage/lcov.info
|
||||
|
||||
test-on-macos:
|
||||
runs-on: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) && 'macos-latest' || 'macos-selfhosted-12-arm64' }}
|
||||
@ -263,7 +263,7 @@ jobs:
|
||||
uses: barecheck/code-coverage-action@v1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
lcov-file: './coverage/merged/lcov.info'
|
||||
lcov-file: './coverage/lcov.info'
|
||||
base-lcov-file: './lcov.info'
|
||||
send-summary-comment: true
|
||||
show-annotations: 'warning'
|
||||
|
||||
@ -108,7 +108,6 @@ jobs:
|
||||
mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json
|
||||
if [ "${{ inputs.channel }}" != "stable" ]; then
|
||||
jq '.bundle.linux.deb.files = {"usr/bin/bun": "resources/bin/bun",
|
||||
"usr/lib/Jan-${{ inputs.channel }}/binaries": "binaries/deps",
|
||||
"usr/lib/Jan-${{ inputs.channel }}/resources/lib/libvulkan.so": "resources/lib/libvulkan.so"}' ./src-tauri/tauri.linux.conf.json > /tmp/tauri.linux.conf.json
|
||||
mv /tmp/tauri.linux.conf.json ./src-tauri/tauri.linux.conf.json
|
||||
fi
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
|
||||
moduleNameMapper: {
|
||||
'@/(.*)': '<rootDir>/src/$1',
|
||||
},
|
||||
runner: './testRunner.js',
|
||||
transform: {
|
||||
'^.+\\.tsx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
diagnostics: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@ -17,27 +17,28 @@
|
||||
"author": "Jan <service@jan.ai>",
|
||||
"scripts": {
|
||||
"lint": "tslint --project tsconfig.json -t codeFrame 'src/**/*.ts' 'test/**/*.ts'",
|
||||
"test": "jest",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"test:ui": "vitest --ui",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"prebuild": "rimraf dist",
|
||||
"build": "tsc -p . && rolldown -c rolldown.config.mjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@npmcli/arborist": "^7.1.0",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^22.10.0",
|
||||
"@vitest/coverage-v8": "^2.1.8",
|
||||
"@vitest/ui": "^2.1.8",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-plugin-jest": "^27.9.0",
|
||||
"jest": "^30.0.3",
|
||||
"jest-junit": "^16.0.0",
|
||||
"jest-runner": "^30.0.3",
|
||||
"happy-dom": "^15.11.6",
|
||||
"pacote": "^21.0.0",
|
||||
"request": "^2.88.2",
|
||||
"request-progress": "^3.0.0",
|
||||
"rimraf": "^6.0.1",
|
||||
"rolldown": "1.0.0-beta.1",
|
||||
"ts-jest": "^29.2.5",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.8.3"
|
||||
"typescript": "^5.8.3",
|
||||
"vitest": "^2.1.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"rxjs": "^7.8.1",
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { openExternalUrl } from './core'
|
||||
import { joinPath } from './core'
|
||||
import { openFileExplorer } from './core'
|
||||
@ -12,7 +10,7 @@ describe('test core apis', () => {
|
||||
const url = 'http://example.com'
|
||||
globalThis.core = {
|
||||
api: {
|
||||
openExternalUrl: jest.fn().mockResolvedValue('opened'),
|
||||
openExternalUrl: vi.fn().mockResolvedValue('opened'),
|
||||
},
|
||||
}
|
||||
const result = await openExternalUrl(url)
|
||||
@ -24,7 +22,7 @@ describe('test core apis', () => {
|
||||
const paths = ['/path/one', '/path/two']
|
||||
globalThis.core = {
|
||||
api: {
|
||||
joinPath: jest.fn().mockResolvedValue('/path/one/path/two'),
|
||||
joinPath: vi.fn().mockResolvedValue('/path/one/path/two'),
|
||||
},
|
||||
}
|
||||
const result = await joinPath(paths)
|
||||
@ -36,7 +34,7 @@ describe('test core apis', () => {
|
||||
const path = '/path/to/open'
|
||||
globalThis.core = {
|
||||
api: {
|
||||
openFileExplorer: jest.fn().mockResolvedValue('opened'),
|
||||
openFileExplorer: vi.fn().mockResolvedValue('opened'),
|
||||
},
|
||||
}
|
||||
const result = await openFileExplorer(path)
|
||||
@ -47,7 +45,7 @@ describe('test core apis', () => {
|
||||
it('should get jan data folder path', async () => {
|
||||
globalThis.core = {
|
||||
api: {
|
||||
getJanDataFolderPath: jest.fn().mockResolvedValue('/path/to/jan/data'),
|
||||
getJanDataFolderPath: vi.fn().mockResolvedValue('/path/to/jan/data'),
|
||||
},
|
||||
}
|
||||
const result = await getJanDataFolderPath()
|
||||
@ -58,7 +56,7 @@ describe('test core apis', () => {
|
||||
|
||||
describe('dirName - just a pass thru api', () => {
|
||||
it('should retrieve the directory name from a file path', async () => {
|
||||
const mockDirName = jest.fn()
|
||||
const mockDirName = vi.fn()
|
||||
globalThis.core = {
|
||||
api: {
|
||||
dirName: mockDirName.mockResolvedValue('/path/to'),
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { it, expect, vi } from 'vitest'
|
||||
import { events } from './events';
|
||||
import { jest } from '@jest/globals';
|
||||
|
||||
it('should emit an event', () => {
|
||||
const mockObject = { key: 'value' };
|
||||
globalThis.core = {
|
||||
events: {
|
||||
emit: jest.fn()
|
||||
emit: vi.fn()
|
||||
}
|
||||
};
|
||||
events.emit('testEvent', mockObject);
|
||||
@ -14,10 +14,10 @@ it('should emit an event', () => {
|
||||
|
||||
|
||||
it('should remove an observer for an event', () => {
|
||||
const mockHandler = jest.fn();
|
||||
const mockHandler = vi.fn();
|
||||
globalThis.core = {
|
||||
events: {
|
||||
off: jest.fn()
|
||||
off: vi.fn()
|
||||
}
|
||||
};
|
||||
events.off('testEvent', mockHandler);
|
||||
@ -26,10 +26,10 @@ it('should remove an observer for an event', () => {
|
||||
|
||||
|
||||
it('should add an observer for an event', () => {
|
||||
const mockHandler = jest.fn();
|
||||
const mockHandler = vi.fn();
|
||||
globalThis.core = {
|
||||
events: {
|
||||
on: jest.fn()
|
||||
on: vi.fn()
|
||||
}
|
||||
};
|
||||
events.on('testEvent', mockHandler);
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
||||
import { BaseExtension } from './extension'
|
||||
import { SettingComponentProps } from '../types'
|
||||
jest.mock('./core')
|
||||
jest.mock('./fs')
|
||||
vi.mock('./core')
|
||||
vi.mock('./fs')
|
||||
|
||||
class TestBaseExtension extends BaseExtension {
|
||||
onLoad(): void {}
|
||||
@ -16,7 +17,7 @@ describe('BaseExtension', () => {
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('should have the correct properties', () => {
|
||||
@ -56,7 +57,7 @@ describe('BaseExtension', () => {
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('should have the correct properties', () => {
|
||||
@ -108,7 +109,7 @@ describe('BaseExtension', () => {
|
||||
Object.defineProperty(global, 'localStorage', {
|
||||
value: localStorageMock,
|
||||
})
|
||||
const mock = jest.spyOn(localStorage, 'setItem')
|
||||
const mock = vi.spyOn(localStorage, 'setItem')
|
||||
await baseExtension.registerSettings(settings)
|
||||
|
||||
expect(mock).toHaveBeenCalledWith(
|
||||
@ -122,7 +123,7 @@ describe('BaseExtension', () => {
|
||||
{ key: 'setting1', controllerProps: { value: 'value1' } } as any,
|
||||
]
|
||||
|
||||
jest.spyOn(baseExtension, 'getSettings').mockResolvedValue(settings)
|
||||
vi.spyOn(baseExtension, 'getSettings').mockResolvedValue(settings)
|
||||
|
||||
const value = await baseExtension.getSetting('setting1', 'defaultValue')
|
||||
expect(value).toBe('value1')
|
||||
@ -136,8 +137,8 @@ describe('BaseExtension', () => {
|
||||
{ key: 'setting1', controllerProps: { value: 'value1' } } as any,
|
||||
]
|
||||
|
||||
jest.spyOn(baseExtension, 'getSettings').mockResolvedValue(settings)
|
||||
const mockSetItem = jest.spyOn(localStorage, 'setItem')
|
||||
vi.spyOn(baseExtension, 'getSettings').mockResolvedValue(settings)
|
||||
const mockSetItem = vi.spyOn(localStorage, 'setItem')
|
||||
|
||||
await baseExtension.updateSettings([
|
||||
{ key: 'setting1', controllerProps: { value: 'newValue' } } as any,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
|
||||
import { it, expect } from 'vitest'
|
||||
import { AssistantExtension } from './assistant';
|
||||
import { ExtensionTypeEnum } from '../extension';
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { describe, it, test, expect, beforeEach } from 'vitest'
|
||||
import { ConversationalExtension } from './conversational'
|
||||
import { ExtensionTypeEnum } from '../extension'
|
||||
import { Thread, ThreadAssistantInfo, ThreadMessage } from '../../types'
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { AIEngine } from './AIEngine'
|
||||
import { events } from '../../events'
|
||||
import { ModelEvent, Model } from '../../../types'
|
||||
|
||||
jest.mock('../../events')
|
||||
jest.mock('./EngineManager')
|
||||
jest.mock('../../fs')
|
||||
vi.mock('../../events')
|
||||
vi.mock('./EngineManager')
|
||||
vi.mock('../../fs')
|
||||
|
||||
class TestAIEngine extends AIEngine {
|
||||
onUnload(): void {}
|
||||
@ -52,7 +53,7 @@ describe('AIEngine', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
engine = new TestAIEngine('', '')
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('should load model successfully', async () => {
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
import { describe, it, test, expect, beforeEach } from 'vitest'
|
||||
import { EngineManager } from './EngineManager'
|
||||
import { AIEngine } from './AIEngine'
|
||||
import { InferenceEngine } from '../../../types'
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest'
|
||||
import { LocalOAIEngine } from './LocalOAIEngine'
|
||||
import { events } from '../../events'
|
||||
import { Model, ModelEvent } from '../../../types'
|
||||
|
||||
jest.mock('../../events')
|
||||
vi.mock('../../events')
|
||||
|
||||
class TestLocalOAIEngine extends LocalOAIEngine {
|
||||
inferenceUrl = 'http://test-local-inference-url'
|
||||
@ -43,12 +41,12 @@ describe('LocalOAIEngine', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
engine = new TestLocalOAIEngine('', '')
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('onLoad', () => {
|
||||
it('should call super.onLoad and subscribe to model events', () => {
|
||||
const superOnLoadSpy = jest.spyOn(Object.getPrototypeOf(Object.getPrototypeOf(engine)), 'onLoad')
|
||||
const superOnLoadSpy = vi.spyOn(Object.getPrototypeOf(Object.getPrototypeOf(engine)), 'onLoad')
|
||||
|
||||
engine.onLoad()
|
||||
|
||||
@ -64,11 +62,11 @@ describe('LocalOAIEngine', () => {
|
||||
})
|
||||
|
||||
it('should load model when OnModelInit event is triggered', () => {
|
||||
const loadModelSpy = jest.spyOn(engine, 'loadModel')
|
||||
const loadModelSpy = vi.spyOn(engine, 'loadModel')
|
||||
engine.onLoad()
|
||||
|
||||
// Get the event handler for OnModelInit
|
||||
const onModelInitCall = (events.on as jest.Mock).mock.calls.find(
|
||||
const onModelInitCall = (events.on as Mock).mock.calls.find(
|
||||
call => call[0] === ModelEvent.OnModelInit
|
||||
)
|
||||
const onModelInitHandler = onModelInitCall[1]
|
||||
@ -80,11 +78,11 @@ describe('LocalOAIEngine', () => {
|
||||
})
|
||||
|
||||
it('should unload model when OnModelStop event is triggered', () => {
|
||||
const unloadModelSpy = jest.spyOn(engine, 'unloadModel')
|
||||
const unloadModelSpy = vi.spyOn(engine, 'unloadModel')
|
||||
engine.onLoad()
|
||||
|
||||
// Get the event handler for OnModelStop
|
||||
const onModelStopCall = (events.on as jest.Mock).mock.calls.find(
|
||||
const onModelStopCall = (events.on as Mock).mock.calls.find(
|
||||
call => call[0] === ModelEvent.OnModelStop
|
||||
)
|
||||
const onModelStopHandler = onModelStopCall[1]
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { OAIEngine } from './OAIEngine'
|
||||
import { events } from '../../events'
|
||||
import {
|
||||
@ -13,7 +11,7 @@ import {
|
||||
ContentType,
|
||||
} from '../../../types'
|
||||
|
||||
jest.mock('../../events')
|
||||
vi.mock('../../events')
|
||||
|
||||
class TestOAIEngine extends OAIEngine {
|
||||
inferenceUrl = 'http://test-inference-url'
|
||||
@ -29,7 +27,7 @@ describe('OAIEngine', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
engine = new TestOAIEngine('', '')
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('should subscribe to events on load', () => {
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
import { describe, test, expect, beforeEach, vi } from 'vitest'
|
||||
import { RemoteOAIEngine } from './'
|
||||
|
||||
class TestRemoteOAIEngine extends RemoteOAIEngine {
|
||||
@ -16,8 +14,8 @@ describe('RemoteOAIEngine', () => {
|
||||
})
|
||||
|
||||
test('should call onLoad and super.onLoad', () => {
|
||||
const onLoadSpy = jest.spyOn(engine, 'onLoad')
|
||||
const superOnLoadSpy = jest.spyOn(Object.getPrototypeOf(RemoteOAIEngine.prototype), 'onLoad')
|
||||
const onLoadSpy = vi.spyOn(engine, 'onLoad')
|
||||
const superOnLoadSpy = vi.spyOn(Object.getPrototypeOf(RemoteOAIEngine.prototype), 'onLoad')
|
||||
engine.onLoad()
|
||||
|
||||
expect(onLoadSpy).toHaveBeenCalled()
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
|
||||
import { expect } from '@jest/globals';
|
||||
import { it, expect } from 'vitest'
|
||||
import * as engines from './index'
|
||||
|
||||
it('should re-export all exports from ./AIEngine', () => {
|
||||
expect(require('./index')).toHaveProperty('AIEngine');
|
||||
});
|
||||
expect(engines).toHaveProperty('AIEngine')
|
||||
})
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { describe, test, expect } from 'vitest'
|
||||
import { ConversationalExtension } from './index';
|
||||
import { InferenceExtension } from './index';
|
||||
import { AssistantExtension } from './index';
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest'
|
||||
import { MessageRequest, ThreadMessage } from '../../types'
|
||||
import { BaseExtension, ExtensionTypeEnum } from '../extension'
|
||||
import { InferenceExtension } from './'
|
||||
|
||||
@ -1,21 +1,22 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { fs } from './fs'
|
||||
|
||||
describe('fs module', () => {
|
||||
beforeEach(() => {
|
||||
globalThis.core = {
|
||||
api: {
|
||||
writeFileSync: jest.fn(),
|
||||
writeBlob: jest.fn(),
|
||||
readFileSync: jest.fn(),
|
||||
existsSync: jest.fn(),
|
||||
readdirSync: jest.fn(),
|
||||
mkdir: jest.fn(),
|
||||
rm: jest.fn(),
|
||||
unlinkSync: jest.fn(),
|
||||
appendFileSync: jest.fn(),
|
||||
copyFile: jest.fn(),
|
||||
getGgufFiles: jest.fn(),
|
||||
fileStat: jest.fn(),
|
||||
writeFileSync: vi.fn(),
|
||||
writeBlob: vi.fn(),
|
||||
readFileSync: vi.fn(),
|
||||
existsSync: vi.fn(),
|
||||
readdirSync: vi.fn(),
|
||||
mkdir: vi.fn(),
|
||||
rm: vi.fn(),
|
||||
unlinkSync: vi.fn(),
|
||||
appendFileSync: vi.fn(),
|
||||
copyFile: vi.fn(),
|
||||
getGgufFiles: vi.fn(),
|
||||
fileStat: vi.fn(),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import * as Core from './core'
|
||||
import * as Events from './events'
|
||||
import * as FileSystem from './fs'
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { ModelManager } from './manager'
|
||||
import { Model, ModelEvent } from '../../types'
|
||||
import { events } from '../events'
|
||||
|
||||
jest.mock('../events', () => ({
|
||||
vi.mock('../events', () => ({
|
||||
events: {
|
||||
emit: jest.fn(),
|
||||
emit: vi.fn(),
|
||||
},
|
||||
}))
|
||||
|
||||
@ -20,7 +21,7 @@ describe('ModelManager', () => {
|
||||
let mockModel: Model
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
;(global.window as any).core = {}
|
||||
modelManager = new ModelManager()
|
||||
mockModel = {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
// web/utils/modelParam.test.ts
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
normalizeValue,
|
||||
validationRules,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
|
||||
import { it, expect } from 'vitest'
|
||||
|
||||
it('should declare global object core when importing the module and then deleting it', () => {
|
||||
import('./index');
|
||||
|
||||
19
core/src/test/setup.ts
Normal file
19
core/src/test/setup.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { vi } from 'vitest'
|
||||
|
||||
// Ensure window exists in test environment
|
||||
if (typeof window === 'undefined') {
|
||||
global.window = {} as any
|
||||
}
|
||||
|
||||
// Mock window.core for browser tests
|
||||
if (!window.core) {
|
||||
Object.defineProperty(window, 'core', {
|
||||
value: {
|
||||
engineManager: undefined
|
||||
},
|
||||
writable: true,
|
||||
configurable: true
|
||||
})
|
||||
}
|
||||
|
||||
// Add any other global mocks needed for core tests
|
||||
@ -1,5 +1,6 @@
|
||||
|
||||
|
||||
import { test, expect } from 'vitest'
|
||||
import { NativeRoute } from '../index';
|
||||
|
||||
test('testNativeRouteEnum', () => {
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import { it, expect } from 'vitest'
|
||||
import { AssistantEvent } from './assistantEvent';
|
||||
|
||||
it('dummy test', () => { expect(true).toBe(true); });
|
||||
|
||||
it('should contain OnAssistantsUpdate event', () => {
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
|
||||
|
||||
import { AppConfigurationEventName } from './appConfigEvent';
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { AppConfigurationEventName } from './appConfigEvent';
|
||||
|
||||
describe('AppConfigurationEventName', () => {
|
||||
describe('AppConfigurationEventName', () => {
|
||||
it('should have the correct value for OnConfigurationUpdate', () => {
|
||||
expect(AppConfigurationEventName.OnConfigurationUpdate).toBe('OnConfigurationUpdate');
|
||||
});
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
|
||||
import { test, expect } from 'vitest'
|
||||
import * as assistant from './assistant';
|
||||
import * as model from './model';
|
||||
import * as thread from './thread';
|
||||
@ -10,7 +11,7 @@ import * as miscellaneous from './miscellaneous';
|
||||
import * as api from './api';
|
||||
import * as setting from './setting';
|
||||
|
||||
test('test_module_exports', () => {
|
||||
test('test_module_exports', () => {
|
||||
expect(assistant).toBeDefined();
|
||||
expect(model).toBeDefined();
|
||||
expect(thread).toBeDefined();
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
|
||||
|
||||
import { ChatCompletionMessage, ChatCompletionRole } from './inferenceEntity';
|
||||
import { test, expect } from 'vitest'
|
||||
import { ChatCompletionMessage, ChatCompletionRole } from './inferenceEntity';
|
||||
|
||||
test('test_chatCompletionMessage_withStringContent_andSystemRole', () => {
|
||||
test('test_chatCompletionMessage_withStringContent_andSystemRole', () => {
|
||||
const message: ChatCompletionMessage = {
|
||||
content: 'Hello, world!',
|
||||
role: ChatCompletionRole.System,
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
|
||||
import { InferenceEvent } from './inferenceEvent';
|
||||
import { test, expect } from 'vitest'
|
||||
import { InferenceEvent } from './inferenceEvent';
|
||||
|
||||
test('testInferenceEventEnumContainsOnInferenceStopped', () => {
|
||||
test('testInferenceEventEnumContainsOnInferenceStopped', () => {
|
||||
expect(InferenceEvent.OnInferenceStopped).toBe('OnInferenceStopped');
|
||||
});
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
|
||||
import { it, expect } from 'vitest'
|
||||
import { MessageStatus } from './messageEntity';
|
||||
|
||||
it('should have correct values', () => {
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
|
||||
import { MessageEvent } from './messageEvent';
|
||||
import { test, expect } from 'vitest'
|
||||
import { MessageEvent } from './messageEvent';
|
||||
|
||||
test('testOnMessageSentValue', () => {
|
||||
test('testOnMessageSentValue', () => {
|
||||
expect(MessageEvent.OnMessageSent).toBe('OnMessageSent');
|
||||
});
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
|
||||
import { MessageRequestType } from './messageRequestType';
|
||||
import { test, expect } from 'vitest'
|
||||
import { MessageRequestType } from './messageRequestType';
|
||||
|
||||
test('testMessageRequestTypeEnumContainsThread', () => {
|
||||
test('testMessageRequestTypeEnumContainsThread', () => {
|
||||
expect(MessageRequestType.Thread).toBe('Thread');
|
||||
});
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
|
||||
import { it, expect } from 'vitest'
|
||||
import { SupportedPlatforms } from './systemResourceInfo';
|
||||
|
||||
it('should contain the correct values', () => {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { test, expect } from 'vitest'
|
||||
import { Model, ModelSettingParams, ModelRuntimeParams } from '../model'
|
||||
import { InferenceEngine } from '../engine'
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
|
||||
|
||||
import { ModelEvent } from './modelEvent';
|
||||
import { test, expect } from 'vitest'
|
||||
import { ModelEvent } from './modelEvent';
|
||||
|
||||
test('testOnModelInit', () => {
|
||||
test('testOnModelInit', () => {
|
||||
expect(ModelEvent.OnModelInit).toBe('OnModelInit');
|
||||
});
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
|
||||
import { it, expect } from 'vitest'
|
||||
import './index'
|
||||
|
||||
it('should not throw any errors', () => {
|
||||
expect(() => require('./index')).not.toThrow();
|
||||
});
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { it, expect } from 'vitest'
|
||||
import * as SettingComponent from './settingComponent'
|
||||
|
||||
it('should not throw any errors when importing settingComponent', () => {
|
||||
expect(() => require('./settingComponent')).not.toThrow()
|
||||
expect(true).toBe(true)
|
||||
})
|
||||
|
||||
it('should export SettingComponentProps type', () => {
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
const jestRunner = require('jest-runner');
|
||||
|
||||
class EmptyTestFileRunner extends jestRunner.default {
|
||||
async runTests(tests, watcher, onStart, onResult, onFailure, options) {
|
||||
const nonEmptyTests = tests.filter(test => test.context.hasteFS.getSize(test.path) > 0);
|
||||
return super.runTests(nonEmptyTests, watcher, onStart, onResult, onFailure, options);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EmptyTestFileRunner;
|
||||
@ -13,7 +13,7 @@
|
||||
"declarationDir": "dist/types",
|
||||
"outDir": "dist",
|
||||
"importHelpers": true,
|
||||
"types": ["jest", "node"]
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["src/**/*.test.ts"]
|
||||
|
||||
22
core/vitest.config.ts
Normal file
22
core/vitest.config.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { defineConfig } from 'vitest/config'
|
||||
import { resolve } from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
globals: true,
|
||||
setupFiles: ['./src/test/setup.ts'],
|
||||
coverage: {
|
||||
reporter: ['text', 'json', 'html', 'lcov'],
|
||||
include: ['src/**/*.{ts,tsx}'],
|
||||
exclude: ['node_modules/', 'dist/', 'src/**/*.test.ts']
|
||||
},
|
||||
include: ['src/**/*.test.ts'],
|
||||
exclude: ['node_modules/', 'dist/']
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, './src')
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -1,5 +0,0 @@
|
||||
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
}
|
||||
@ -7,7 +7,6 @@
|
||||
"author": "Jan <service@jan.ai>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"build": "rolldown -c rolldown.config.mjs",
|
||||
"build:publish": "rimraf *.tgz --glob || true && yarn build && npm pack && cpx *.tgz ../../pre-install"
|
||||
},
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
projects: ['<rootDir>/core'],
|
||||
}
|
||||
19
package.json
19
package.json
@ -12,11 +12,10 @@
|
||||
"lint": "yarn workspace @janhq/web-app lint",
|
||||
"dev": "yarn dev:tauri",
|
||||
"build": "yarn build:web && yarn build:tauri",
|
||||
"test": "jest && yarn workspace @janhq/web-app test",
|
||||
"test:coverage": "yarn test:coverage:jest && yarn test:coverage:vitest && yarn merge:coverage",
|
||||
"test:coverage:jest": "jest --coverage --coverageDirectory=coverage/jest",
|
||||
"test:coverage:vitest": "yarn workspace @janhq/web-app test:coverage",
|
||||
"merge:coverage": "node scripts/merge-coverage.js",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"test:ui": "vitest --ui",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"test:prepare": "yarn build:icon && yarn copy:assets:tauri && yarn build --no-bundle ",
|
||||
"test:e2e:linux": "yarn test:prepare && xvfb-run yarn workspace tests-e2-js test",
|
||||
"test:e2e:win32": "yarn test:prepare && yarn workspace tests-e2-js test",
|
||||
@ -39,21 +38,19 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^2.2.5",
|
||||
"@vitest/coverage-v8": "^3.1.3",
|
||||
"concurrently": "^9.1.0",
|
||||
"cpx": "^1.5.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"happy-dom": "^15.11.6",
|
||||
"husky": "^9.1.5",
|
||||
"istanbul-api": "^3.0.0",
|
||||
"istanbul-lib-coverage": "^3.2.2",
|
||||
"istanbul-lib-report": "^3.0.1",
|
||||
"istanbul-reports": "^3.1.7",
|
||||
"jest": "^30.0.3",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jsdom": "^26.1.0",
|
||||
"nyc": "^17.1.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"run-script-os": "^1.1.6",
|
||||
"tar": "^4.4.19",
|
||||
"unzipper": "^0.12.3",
|
||||
"vitest": "^3.1.3",
|
||||
"wait-on": "^7.0.1"
|
||||
},
|
||||
"version": "0.0.0",
|
||||
|
||||
@ -1,145 +0,0 @@
|
||||
const { createCoverageMap } = require('istanbul-lib-coverage')
|
||||
const { createReporter } = require('istanbul-api')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const coverageDir = path.join(__dirname, '../coverage')
|
||||
const jestCoverage = path.join(coverageDir, 'jest/coverage-final.json')
|
||||
const vitestCoverage = path.join(coverageDir, 'vitest/coverage-final.json')
|
||||
const mergedDir = path.join(coverageDir, 'merged')
|
||||
|
||||
function normalizePath(filePath, workspace) {
|
||||
if (workspace === 'jest') {
|
||||
return `[CORE] ${filePath}`
|
||||
} else if (workspace === 'vitest') {
|
||||
return `[WEB-APP] ${filePath}`
|
||||
}
|
||||
return filePath
|
||||
}
|
||||
|
||||
async function mergeCoverage() {
|
||||
const map = createCoverageMap({})
|
||||
|
||||
console.log('🔍 Checking coverage files...')
|
||||
console.log('Jest coverage path:', jestCoverage)
|
||||
console.log('Vitest coverage path:', vitestCoverage)
|
||||
console.log('Jest file exists:', fs.existsSync(jestCoverage))
|
||||
console.log('Vitest file exists:', fs.existsSync(vitestCoverage))
|
||||
|
||||
// Load Jest coverage (core workspace)
|
||||
if (fs.existsSync(jestCoverage)) {
|
||||
const jestData = JSON.parse(fs.readFileSync(jestCoverage, 'utf8'))
|
||||
console.log('Jest data keys:', Object.keys(jestData).length)
|
||||
map.merge(jestData)
|
||||
console.log('✓ Merged Jest coverage (core workspace)')
|
||||
} else {
|
||||
console.log('❌ Jest coverage file not found')
|
||||
}
|
||||
|
||||
// Load Vitest coverage (web-app workspace)
|
||||
if (fs.existsSync(vitestCoverage)) {
|
||||
const vitestData = JSON.parse(fs.readFileSync(vitestCoverage, 'utf8'))
|
||||
console.log('Vitest data keys:', Object.keys(vitestData).length)
|
||||
map.merge(vitestData)
|
||||
console.log('✓ Merged Vitest coverage (web-app workspace)')
|
||||
} else {
|
||||
console.log('❌ Vitest coverage file not found')
|
||||
}
|
||||
|
||||
console.log('📊 Total files in coverage map:', map.files().length)
|
||||
|
||||
// Create merged directory
|
||||
if (!fs.existsSync(mergedDir)) {
|
||||
fs.mkdirSync(mergedDir, { recursive: true })
|
||||
console.log('✓ Created merged directory')
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('🔄 Generating reports...')
|
||||
|
||||
const context = require('istanbul-lib-report').createContext({
|
||||
dir: mergedDir,
|
||||
coverageMap: map,
|
||||
})
|
||||
|
||||
const htmlReporter = require('istanbul-reports').create('html')
|
||||
const lcovReporter = require('istanbul-reports').create('lcov')
|
||||
const textReporter = require('istanbul-reports').create('text')
|
||||
|
||||
// Generate reports
|
||||
htmlReporter.execute(context)
|
||||
lcovReporter.execute(context)
|
||||
textReporter.execute(context)
|
||||
|
||||
console.log('\n📊 Coverage reports merged successfully!')
|
||||
console.log('📁 HTML report: coverage/merged/index.html')
|
||||
console.log('📁 LCOV report: coverage/merged/lcov.info')
|
||||
|
||||
// Check if files were created
|
||||
if (fs.existsSync(mergedDir)) {
|
||||
const mergedFiles = fs.readdirSync(mergedDir)
|
||||
console.log('📁 Files in merged directory:', mergedFiles)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Error generating reports:', error.message)
|
||||
console.error('Stack trace:', error.stack)
|
||||
throw error
|
||||
}
|
||||
|
||||
// Generate separate reports for each workspace
|
||||
await generateWorkspaceReports()
|
||||
}
|
||||
|
||||
async function generateWorkspaceReports() {
|
||||
// Generate separate core report
|
||||
if (fs.existsSync(jestCoverage)) {
|
||||
const coreMap = createCoverageMap({})
|
||||
const jestData = JSON.parse(fs.readFileSync(jestCoverage, 'utf8'))
|
||||
coreMap.merge(jestData)
|
||||
|
||||
const coreDir = path.join(coverageDir, 'core-only')
|
||||
if (!fs.existsSync(coreDir)) {
|
||||
fs.mkdirSync(coreDir, { recursive: true })
|
||||
}
|
||||
|
||||
const coreContext = require('istanbul-lib-report').createContext({
|
||||
dir: coreDir,
|
||||
coverageMap: coreMap,
|
||||
})
|
||||
|
||||
const htmlReporter = require('istanbul-reports').create('html')
|
||||
const textSummaryReporter =
|
||||
require('istanbul-reports').create('text-summary')
|
||||
|
||||
htmlReporter.execute(coreContext)
|
||||
textSummaryReporter.execute(coreContext)
|
||||
console.log('📁 Core-only report: coverage/core-only/index.html')
|
||||
}
|
||||
|
||||
// Generate separate web-app report
|
||||
if (fs.existsSync(vitestCoverage)) {
|
||||
const webAppMap = createCoverageMap({})
|
||||
const vitestData = JSON.parse(fs.readFileSync(vitestCoverage, 'utf8'))
|
||||
webAppMap.merge(vitestData)
|
||||
|
||||
const webAppDir = path.join(coverageDir, 'web-app-only')
|
||||
if (!fs.existsSync(webAppDir)) {
|
||||
fs.mkdirSync(webAppDir, { recursive: true })
|
||||
}
|
||||
|
||||
const webAppContext = require('istanbul-lib-report').createContext({
|
||||
dir: webAppDir,
|
||||
coverageMap: webAppMap,
|
||||
})
|
||||
|
||||
const htmlReporter = require('istanbul-reports').create('html')
|
||||
const textSummaryReporter =
|
||||
require('istanbul-reports').create('text-summary')
|
||||
|
||||
htmlReporter.execute(webAppContext)
|
||||
textSummaryReporter.execute(webAppContext)
|
||||
console.log('📁 Web-app-only report: coverage/web-app-only/index.html')
|
||||
}
|
||||
}
|
||||
|
||||
mergeCoverage().catch(console.error)
|
||||
@ -20,8 +20,6 @@ fi
|
||||
# bundle additional resources in the AppDir without pulling in their dependencies
|
||||
cp ./src-tauri/resources/bin/bun $APP_DIR/usr/bin/bun
|
||||
mkdir -p $LIB_DIR/engines
|
||||
cp -f ./src-tauri/binaries/deps/*.so* $LIB_DIR/
|
||||
cp -f ./src-tauri/binaries/*.so* $LIB_DIR/
|
||||
|
||||
# remove appimage generated by tauri build
|
||||
APP_IMAGE=./src-tauri/target/release/bundle/appimage/$(ls ./src-tauri/target/release/bundle/appimage/ | grep AppImage | head -1)
|
||||
|
||||
@ -11,7 +11,6 @@
|
||||
"deb": {
|
||||
"files": {
|
||||
"usr/bin/bun": "resources/bin/bun",
|
||||
"usr/lib/Jan/binaries": "binaries/deps",
|
||||
"usr/lib/Jan/resources/lib/libvulkan.so": "resources/lib/libvulkan.so"
|
||||
}
|
||||
}
|
||||
|
||||
13
vitest.config.ts
Normal file
13
vitest.config.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
projects: [
|
||||
// Core package - use its own vitest config
|
||||
'./core',
|
||||
|
||||
// Web-app package - use its own vitest config
|
||||
'./web-app'
|
||||
]
|
||||
}
|
||||
})
|
||||
@ -1,10 +1,25 @@
|
||||
import { expect, afterEach } from 'vitest'
|
||||
import { expect, afterEach, vi } from 'vitest'
|
||||
import { cleanup } from '@testing-library/react'
|
||||
import * as matchers from '@testing-library/jest-dom/matchers'
|
||||
|
||||
// extends Vitest's expect method with methods from react-testing-library
|
||||
expect.extend(matchers)
|
||||
|
||||
// Mock window.matchMedia for useMediaQuery tests
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
value: vi.fn().mockImplementation(query => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: vi.fn(), // deprecated
|
||||
removeListener: vi.fn(), // deprecated
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
})),
|
||||
})
|
||||
|
||||
// runs a cleanup after each test case (e.g. clearing jsdom)
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
|
||||
@ -9,6 +9,11 @@ export default defineConfig({
|
||||
setupFiles: ['./src/test/setup.ts'],
|
||||
globals: true,
|
||||
css: true,
|
||||
coverage: {
|
||||
reporter: ['text', 'json', 'html', 'lcov'],
|
||||
include: ['src/**/*.{ts,tsx}'],
|
||||
exclude: ['node_modules/', 'dist/', 'src/**/*.test.ts', 'src/**/*.test.tsx', 'src/test/**/*']
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user