test: add core modules test cases (#3498)
* chore: add core module test cases * chore: fix tests * chore: add code coverage report * chore: split coverage step * chore: split coverage step * Update jan-electron-linter-and-test.yml * Update jan-electron-linter-and-test.yml * Update jan-electron-linter-and-test.yml * chore: update tests * chore: add web utils test cases * chore: add restful and helper tests * chore: add tests
This commit is contained in:
parent
f759fae55f
commit
846efb3126
@ -37,6 +37,30 @@ on:
|
||||
- '!README.md'
|
||||
|
||||
jobs:
|
||||
|
||||
base_branch_cov:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.base_ref }}
|
||||
- name: Use Node.js v20.9.0
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: v20.9.0
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Run test coverage
|
||||
run: yarn test:coverage
|
||||
|
||||
- name: Upload code coverage for ref branch
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ref-lcov.info
|
||||
path: ./coverage/lcov.info
|
||||
|
||||
test-on-macos:
|
||||
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
||||
runs-on: [self-hosted, macOS, macos-desktop]
|
||||
@ -292,6 +316,56 @@ jobs:
|
||||
TURBO_TEAM: 'linux'
|
||||
TURBO_TOKEN: '${{ secrets.TURBO_TOKEN }}'
|
||||
|
||||
coverage-check:
|
||||
runs-on: [self-hosted, Linux, ubuntu-desktop]
|
||||
needs: base_branch_cov
|
||||
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
||||
steps:
|
||||
- name: Getting the repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Installing node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: 'Cleanup cache'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
rm -rf ~/jan
|
||||
make clean
|
||||
|
||||
- name: Download code coverage report from base branch
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ref-lcov.info
|
||||
|
||||
- name: Linter and test coverage
|
||||
run: |
|
||||
export DISPLAY=$(w -h | awk 'NR==1 {print $2}')
|
||||
echo -e "Display ID: $DISPLAY"
|
||||
npm config set registry ${{ secrets.NPM_PROXY }} --global
|
||||
yarn config set registry ${{ secrets.NPM_PROXY }} --global
|
||||
make lint
|
||||
yarn build:test
|
||||
yarn test:coverage
|
||||
env:
|
||||
TURBO_API: '${{ secrets.TURBO_API }}'
|
||||
TURBO_TEAM: 'linux'
|
||||
TURBO_TOKEN: '${{ secrets.TURBO_TOKEN }}'
|
||||
|
||||
- name: Generate Code Coverage report
|
||||
id: code-coverage
|
||||
uses: barecheck/code-coverage-action@v1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
lcov-file: "./coverage/lcov.info"
|
||||
base-lcov-file: "./lcov.info"
|
||||
send-summary-comment: true
|
||||
show-annotations: "warning"
|
||||
|
||||
test-on-ubuntu-pr-target:
|
||||
runs-on: [self-hosted, Linux, ubuntu-desktop]
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -41,3 +41,5 @@ extensions/*-extension/bin/vulkaninfo
|
||||
.turbo
|
||||
electron/test-data
|
||||
electron/test-results
|
||||
core/test_results.html
|
||||
coverage
|
||||
|
||||
2
Makefile
2
Makefile
@ -104,7 +104,7 @@ endif
|
||||
# Testing
|
||||
test: lint
|
||||
yarn build:test
|
||||
yarn test:unit
|
||||
yarn test:coverage
|
||||
yarn test
|
||||
|
||||
# Builds and publishes the app
|
||||
|
||||
@ -4,4 +4,5 @@ module.exports = {
|
||||
moduleNameMapper: {
|
||||
'@/(.*)': '<rootDir>/src/$1',
|
||||
},
|
||||
runner: './testRunner.js',
|
||||
}
|
||||
|
||||
@ -46,6 +46,8 @@
|
||||
"eslint": "8.57.0",
|
||||
"eslint-plugin-jest": "^27.9.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-junit": "^16.0.0",
|
||||
"jest-runner": "^29.7.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "^2.38.5",
|
||||
"rollup-plugin-commonjs": "^9.1.8",
|
||||
@ -53,7 +55,7 @@
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-sourcemaps": "^0.6.3",
|
||||
"rollup-plugin-typescript2": "^0.36.0",
|
||||
"ts-jest": "^29.1.2",
|
||||
"ts-jest": "^29.2.5",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
|
||||
98
core/src/browser/core.test.ts
Normal file
98
core/src/browser/core.test.ts
Normal file
@ -0,0 +1,98 @@
|
||||
import { openExternalUrl } from './core';
|
||||
import { joinPath } from './core';
|
||||
import { openFileExplorer } from './core';
|
||||
import { getJanDataFolderPath } from './core';
|
||||
import { abortDownload } from './core';
|
||||
import { getFileSize } from './core';
|
||||
import { executeOnMain } from './core';
|
||||
|
||||
it('should open external url', async () => {
|
||||
const url = 'http://example.com';
|
||||
globalThis.core = {
|
||||
api: {
|
||||
openExternalUrl: jest.fn().mockResolvedValue('opened')
|
||||
}
|
||||
};
|
||||
const result = await openExternalUrl(url);
|
||||
expect(globalThis.core.api.openExternalUrl).toHaveBeenCalledWith(url);
|
||||
expect(result).toBe('opened');
|
||||
});
|
||||
|
||||
|
||||
it('should join paths', async () => {
|
||||
const paths = ['/path/one', '/path/two'];
|
||||
globalThis.core = {
|
||||
api: {
|
||||
joinPath: jest.fn().mockResolvedValue('/path/one/path/two')
|
||||
}
|
||||
};
|
||||
const result = await joinPath(paths);
|
||||
expect(globalThis.core.api.joinPath).toHaveBeenCalledWith(paths);
|
||||
expect(result).toBe('/path/one/path/two');
|
||||
});
|
||||
|
||||
|
||||
it('should open file explorer', async () => {
|
||||
const path = '/path/to/open';
|
||||
globalThis.core = {
|
||||
api: {
|
||||
openFileExplorer: jest.fn().mockResolvedValue('opened')
|
||||
}
|
||||
};
|
||||
const result = await openFileExplorer(path);
|
||||
expect(globalThis.core.api.openFileExplorer).toHaveBeenCalledWith(path);
|
||||
expect(result).toBe('opened');
|
||||
});
|
||||
|
||||
|
||||
it('should get jan data folder path', async () => {
|
||||
globalThis.core = {
|
||||
api: {
|
||||
getJanDataFolderPath: jest.fn().mockResolvedValue('/path/to/jan/data')
|
||||
}
|
||||
};
|
||||
const result = await getJanDataFolderPath();
|
||||
expect(globalThis.core.api.getJanDataFolderPath).toHaveBeenCalled();
|
||||
expect(result).toBe('/path/to/jan/data');
|
||||
});
|
||||
|
||||
|
||||
it('should abort download', async () => {
|
||||
const fileName = 'testFile';
|
||||
globalThis.core = {
|
||||
api: {
|
||||
abortDownload: jest.fn().mockResolvedValue('aborted')
|
||||
}
|
||||
};
|
||||
const result = await abortDownload(fileName);
|
||||
expect(globalThis.core.api.abortDownload).toHaveBeenCalledWith(fileName);
|
||||
expect(result).toBe('aborted');
|
||||
});
|
||||
|
||||
|
||||
it('should get file size', async () => {
|
||||
const url = 'http://example.com/file';
|
||||
globalThis.core = {
|
||||
api: {
|
||||
getFileSize: jest.fn().mockResolvedValue(1024)
|
||||
}
|
||||
};
|
||||
const result = await getFileSize(url);
|
||||
expect(globalThis.core.api.getFileSize).toHaveBeenCalledWith(url);
|
||||
expect(result).toBe(1024);
|
||||
});
|
||||
|
||||
|
||||
it('should execute function on main process', async () => {
|
||||
const extension = 'testExtension';
|
||||
const method = 'testMethod';
|
||||
const args = ['arg1', 'arg2'];
|
||||
globalThis.core = {
|
||||
api: {
|
||||
invokeExtensionFunc: jest.fn().mockResolvedValue('result')
|
||||
}
|
||||
};
|
||||
const result = await executeOnMain(extension, method, ...args);
|
||||
expect(globalThis.core.api.invokeExtensionFunc).toHaveBeenCalledWith(extension, method, ...args);
|
||||
expect(result).toBe('result');
|
||||
});
|
||||
37
core/src/browser/events.test.ts
Normal file
37
core/src/browser/events.test.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { events } from './events';
|
||||
import { jest } from '@jest/globals';
|
||||
|
||||
it('should emit an event', () => {
|
||||
const mockObject = { key: 'value' };
|
||||
globalThis.core = {
|
||||
events: {
|
||||
emit: jest.fn()
|
||||
}
|
||||
};
|
||||
events.emit('testEvent', mockObject);
|
||||
expect(globalThis.core.events.emit).toHaveBeenCalledWith('testEvent', mockObject);
|
||||
});
|
||||
|
||||
|
||||
it('should remove an observer for an event', () => {
|
||||
const mockHandler = jest.fn();
|
||||
globalThis.core = {
|
||||
events: {
|
||||
off: jest.fn()
|
||||
}
|
||||
};
|
||||
events.off('testEvent', mockHandler);
|
||||
expect(globalThis.core.events.off).toHaveBeenCalledWith('testEvent', mockHandler);
|
||||
});
|
||||
|
||||
|
||||
it('should add an observer for an event', () => {
|
||||
const mockHandler = jest.fn();
|
||||
globalThis.core = {
|
||||
events: {
|
||||
on: jest.fn()
|
||||
}
|
||||
};
|
||||
events.on('testEvent', mockHandler);
|
||||
expect(globalThis.core.events.on).toHaveBeenCalledWith('testEvent', mockHandler);
|
||||
});
|
||||
46
core/src/browser/extension.test.ts
Normal file
46
core/src/browser/extension.test.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { BaseExtension } from './extension'
|
||||
|
||||
class TestBaseExtension extends BaseExtension {
|
||||
onLoad(): void {}
|
||||
onUnload(): void {}
|
||||
}
|
||||
|
||||
describe('BaseExtension', () => {
|
||||
let baseExtension: TestBaseExtension
|
||||
|
||||
beforeEach(() => {
|
||||
baseExtension = new TestBaseExtension('https://example.com', 'TestExtension')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks()
|
||||
})
|
||||
|
||||
it('should have the correct properties', () => {
|
||||
expect(baseExtension.name).toBe('TestExtension')
|
||||
expect(baseExtension.productName).toBeUndefined()
|
||||
expect(baseExtension.url).toBe('https://example.com')
|
||||
expect(baseExtension.active).toBeUndefined()
|
||||
expect(baseExtension.description).toBeUndefined()
|
||||
expect(baseExtension.version).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should return undefined for type()', () => {
|
||||
expect(baseExtension.type()).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should have abstract methods onLoad() and onUnload()', () => {
|
||||
expect(baseExtension.onLoad).toBeDefined()
|
||||
expect(baseExtension.onUnload).toBeDefined()
|
||||
})
|
||||
|
||||
it('should have installationState() return "NotRequired"', async () => {
|
||||
const installationState = await baseExtension.installationState()
|
||||
expect(installationState).toBe('NotRequired')
|
||||
})
|
||||
|
||||
it('should install the extension', async () => {
|
||||
await baseExtension.install()
|
||||
// Add your assertions here
|
||||
})
|
||||
})
|
||||
60
core/src/browser/extensions/engines/helpers/sse.test.ts
Normal file
60
core/src/browser/extensions/engines/helpers/sse.test.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { lastValueFrom, Observable } from 'rxjs'
|
||||
import { requestInference } from './sse'
|
||||
|
||||
describe('requestInference', () => {
|
||||
it('should send a request to the inference server and return an Observable', () => {
|
||||
// Mock the fetch function
|
||||
const mockFetch: any = jest.fn(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
json: () => Promise.resolve({ choices: [{ message: { content: 'Generated response' } }] }),
|
||||
headers: new Headers(),
|
||||
redirected: false,
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
// Add other required properties here
|
||||
})
|
||||
)
|
||||
jest.spyOn(global, 'fetch').mockImplementation(mockFetch)
|
||||
|
||||
// Define the test inputs
|
||||
const inferenceUrl = 'https://inference-server.com'
|
||||
const requestBody = { message: 'Hello' }
|
||||
const model = { id: 'model-id', parameters: { stream: false } }
|
||||
|
||||
// Call the function
|
||||
const result = requestInference(inferenceUrl, requestBody, model)
|
||||
|
||||
// Assert the expected behavior
|
||||
expect(result).toBeInstanceOf(Observable)
|
||||
expect(lastValueFrom(result)).resolves.toEqual('Generated response')
|
||||
})
|
||||
|
||||
it('returns 401 error', () => {
|
||||
// Mock the fetch function
|
||||
const mockFetch: any = jest.fn(() =>
|
||||
Promise.resolve({
|
||||
ok: false,
|
||||
json: () => Promise.resolve({ error: { message: 'Wrong API Key', code: 'invalid_api_key' } }),
|
||||
headers: new Headers(),
|
||||
redirected: false,
|
||||
status: 401,
|
||||
statusText: 'invalid_api_key',
|
||||
// Add other required properties here
|
||||
})
|
||||
)
|
||||
jest.spyOn(global, 'fetch').mockImplementation(mockFetch)
|
||||
|
||||
// Define the test inputs
|
||||
const inferenceUrl = 'https://inference-server.com'
|
||||
const requestBody = { message: 'Hello' }
|
||||
const model = { id: 'model-id', parameters: { stream: false } }
|
||||
|
||||
// Call the function
|
||||
const result = requestInference(inferenceUrl, requestBody, model)
|
||||
|
||||
// Assert the expected behavior
|
||||
expect(result).toBeInstanceOf(Observable)
|
||||
expect(lastValueFrom(result)).rejects.toEqual({ message: 'Wrong API Key', code: 'invalid_api_key' })
|
||||
})
|
||||
})
|
||||
32
core/src/browser/index.test.ts
Normal file
32
core/src/browser/index.test.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import * as Core from './core';
|
||||
import * as Events from './events';
|
||||
import * as FileSystem from './fs';
|
||||
import * as Extension from './extension';
|
||||
import * as Extensions from './extensions';
|
||||
import * as Tools from './tools';
|
||||
|
||||
describe('Module Tests', () => {
|
||||
it('should export Core module', () => {
|
||||
expect(Core).toBeDefined();
|
||||
});
|
||||
|
||||
it('should export Event module', () => {
|
||||
expect(Events).toBeDefined();
|
||||
});
|
||||
|
||||
it('should export Filesystem module', () => {
|
||||
expect(FileSystem).toBeDefined();
|
||||
});
|
||||
|
||||
it('should export Extension module', () => {
|
||||
expect(Extension).toBeDefined();
|
||||
});
|
||||
|
||||
it('should export all base extensions', () => {
|
||||
expect(Extensions).toBeDefined();
|
||||
});
|
||||
|
||||
it('should export all base tools', () => {
|
||||
expect(Tools).toBeDefined();
|
||||
});
|
||||
});
|
||||
10
core/src/node/api/common/adapter.test.ts
Normal file
10
core/src/node/api/common/adapter.test.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { RequestAdapter } from './adapter';
|
||||
|
||||
it('should return undefined for unknown route', () => {
|
||||
const adapter = new RequestAdapter();
|
||||
const route = 'unknownRoute';
|
||||
|
||||
const result = adapter.process(route, 'arg1', 'arg2');
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
25
core/src/node/api/common/handler.test.ts
Normal file
25
core/src/node/api/common/handler.test.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { CoreRoutes } from '../../../types/api';
|
||||
import { RequestHandler } from './handler';
|
||||
import { RequestAdapter } from './adapter';
|
||||
|
||||
it('should not call handler if CoreRoutes is empty', () => {
|
||||
const mockHandler = jest.fn();
|
||||
const mockObserver = jest.fn();
|
||||
const requestHandler = new RequestHandler(mockHandler, mockObserver);
|
||||
|
||||
CoreRoutes.length = 0; // Ensure CoreRoutes is empty
|
||||
|
||||
requestHandler.handle();
|
||||
|
||||
expect(mockHandler).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should initialize handler and adapter correctly', () => {
|
||||
const mockHandler = jest.fn();
|
||||
const mockObserver = jest.fn();
|
||||
const requestHandler = new RequestHandler(mockHandler, mockObserver);
|
||||
|
||||
expect(requestHandler.handler).toBe(mockHandler);
|
||||
expect(requestHandler.adapter).toBeInstanceOf(RequestAdapter);
|
||||
});
|
||||
40
core/src/node/api/processors/app.test.ts
Normal file
40
core/src/node/api/processors/app.test.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { App } from './app';
|
||||
|
||||
it('should call stopServer', () => {
|
||||
const app = new App();
|
||||
const stopServerMock = jest.fn().mockResolvedValue('Server stopped');
|
||||
jest.mock('@janhq/server', () => ({
|
||||
stopServer: stopServerMock
|
||||
}));
|
||||
const result = app.stopServer();
|
||||
expect(stopServerMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should correctly retrieve basename', () => {
|
||||
const app = new App();
|
||||
const result = app.baseName('/path/to/file.txt');
|
||||
expect(result).toBe('file.txt');
|
||||
});
|
||||
|
||||
it('should correctly identify subdirectories', () => {
|
||||
const app = new App();
|
||||
const basePath = process.platform === 'win32' ? 'C:\\path\\to' : '/path/to';
|
||||
const subPath = process.platform === 'win32' ? 'C:\\path\\to\\subdir' : '/path/to/subdir';
|
||||
const result = app.isSubdirectory(basePath, subPath);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should correctly join multiple paths', () => {
|
||||
const app = new App();
|
||||
const result = app.joinPath(['path', 'to', 'file']);
|
||||
const expectedPath = process.platform === 'win32' ? 'path\\to\\file' : 'path/to/file';
|
||||
expect(result).toBe(expectedPath);
|
||||
});
|
||||
|
||||
it('should call correct function with provided arguments using process method', () => {
|
||||
const app = new App();
|
||||
const mockFunc = jest.fn();
|
||||
app.joinPath = mockFunc;
|
||||
app.process('joinPath', ['path1', 'path2']);
|
||||
expect(mockFunc).toHaveBeenCalledWith(['path1', 'path2']);
|
||||
});
|
||||
59
core/src/node/api/processors/download.test.ts
Normal file
59
core/src/node/api/processors/download.test.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import { Downloader } from './download';
|
||||
import { DownloadEvent } from '../../../types/api';
|
||||
import { DownloadManager } from '../../helper/download';
|
||||
|
||||
it('should handle getFileSize errors correctly', async () => {
|
||||
const observer = jest.fn();
|
||||
const url = 'http://example.com/file';
|
||||
|
||||
const downloader = new Downloader(observer);
|
||||
const requestMock = jest.fn((options, callback) => {
|
||||
callback(new Error('Test error'), null);
|
||||
});
|
||||
jest.mock('request', () => requestMock);
|
||||
|
||||
await expect(downloader.getFileSize(observer, url)).rejects.toThrow('Test error');
|
||||
});
|
||||
|
||||
|
||||
it('should pause download correctly', () => {
|
||||
const observer = jest.fn();
|
||||
const fileName = process.platform === 'win32' ? 'C:\\path\\to\\file' : 'path/to/file';
|
||||
|
||||
const downloader = new Downloader(observer);
|
||||
const pauseMock = jest.fn();
|
||||
DownloadManager.instance.networkRequests[fileName] = { pause: pauseMock };
|
||||
|
||||
downloader.pauseDownload(observer, fileName);
|
||||
|
||||
expect(pauseMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should resume download correctly', () => {
|
||||
const observer = jest.fn();
|
||||
const fileName = process.platform === 'win32' ? 'C:\\path\\to\\file' : 'path/to/file';
|
||||
|
||||
const downloader = new Downloader(observer);
|
||||
const resumeMock = jest.fn();
|
||||
DownloadManager.instance.networkRequests[fileName] = { resume: resumeMock };
|
||||
|
||||
downloader.resumeDownload(observer, fileName);
|
||||
|
||||
expect(resumeMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle aborting a download correctly', () => {
|
||||
const observer = jest.fn();
|
||||
const fileName = process.platform === 'win32' ? 'C:\\path\\to\\file' : 'path/to/file';
|
||||
|
||||
const downloader = new Downloader(observer);
|
||||
const abortMock = jest.fn();
|
||||
DownloadManager.instance.networkRequests[fileName] = { abort: abortMock };
|
||||
|
||||
downloader.abortDownload(observer, fileName);
|
||||
|
||||
expect(abortMock).toHaveBeenCalled();
|
||||
expect(observer).toHaveBeenCalledWith(DownloadEvent.onFileDownloadError, expect.objectContaining({
|
||||
error: 'aborted'
|
||||
}));
|
||||
});
|
||||
9
core/src/node/api/processors/extension.test.ts
Normal file
9
core/src/node/api/processors/extension.test.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Extension } from './extension';
|
||||
|
||||
it('should call function associated with key in process method', () => {
|
||||
const mockFunc = jest.fn();
|
||||
const extension = new Extension();
|
||||
(extension as any).testKey = mockFunc;
|
||||
extension.process('testKey', 'arg1', 'arg2');
|
||||
expect(mockFunc).toHaveBeenCalledWith('arg1', 'arg2');
|
||||
});
|
||||
18
core/src/node/api/processors/fs.test.ts
Normal file
18
core/src/node/api/processors/fs.test.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { FileSystem } from './fs';
|
||||
|
||||
it('should throw an error when the route does not exist in process', async () => {
|
||||
const fileSystem = new FileSystem();
|
||||
await expect(fileSystem.process('nonExistentRoute', 'arg1')).rejects.toThrow();
|
||||
});
|
||||
|
||||
|
||||
it('should throw an error for invalid argument in mkdir', async () => {
|
||||
const fileSystem = new FileSystem();
|
||||
expect(() => fileSystem.mkdir(123)).toThrow('mkdir error: Invalid argument [123]');
|
||||
});
|
||||
|
||||
|
||||
it('should throw an error for invalid argument in rm', async () => {
|
||||
const fileSystem = new FileSystem();
|
||||
expect(() => fileSystem.rm(123)).toThrow('rm error: Invalid argument [123]');
|
||||
});
|
||||
34
core/src/node/api/processors/fsExt.test.ts
Normal file
34
core/src/node/api/processors/fsExt.test.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { FSExt } from './fsExt';
|
||||
import { defaultAppConfig } from '../../helper';
|
||||
|
||||
it('should handle errors in writeBlob', () => {
|
||||
const fsExt = new FSExt();
|
||||
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
fsExt.writeBlob('invalid-path', 'data');
|
||||
expect(consoleSpy).toHaveBeenCalled();
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should call correct function in process method', () => {
|
||||
const fsExt = new FSExt();
|
||||
const mockFunction = jest.fn();
|
||||
(fsExt as any).mockFunction = mockFunction;
|
||||
fsExt.process('mockFunction', 'arg1', 'arg2');
|
||||
expect(mockFunction).toHaveBeenCalledWith('arg1', 'arg2');
|
||||
});
|
||||
|
||||
|
||||
it('should return correct user home path', () => {
|
||||
const fsExt = new FSExt();
|
||||
const userHomePath = fsExt.getUserHomePath();
|
||||
expect(userHomePath).toBe(defaultAppConfig().data_folder);
|
||||
});
|
||||
|
||||
|
||||
|
||||
it('should return empty array when no files are provided', async () => {
|
||||
const fsExt = new FSExt();
|
||||
const result = await fsExt.getGgufFiles([]);
|
||||
expect(result.supportedFiles).toEqual([]);
|
||||
expect(result.unsupportedFiles).toEqual([]);
|
||||
});
|
||||
0
core/src/node/api/processors/processor.test.ts
Normal file
0
core/src/node/api/processors/processor.test.ts
Normal file
62
core/src/node/api/restful/app/download.test.ts
Normal file
62
core/src/node/api/restful/app/download.test.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { HttpServer } from '../../HttpServer'
|
||||
import { DownloadManager } from '../../../helper/download'
|
||||
|
||||
describe('downloadRouter', () => {
|
||||
let app: HttpServer
|
||||
|
||||
beforeEach(() => {
|
||||
app = {
|
||||
register: jest.fn(),
|
||||
post: jest.fn(),
|
||||
get: jest.fn(),
|
||||
patch: jest.fn(),
|
||||
put: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
it('should return download progress for a given modelId', async () => {
|
||||
const modelId = '123'
|
||||
const downloadProgress = { progress: 50 }
|
||||
|
||||
DownloadManager.instance.downloadProgressMap[modelId] = downloadProgress as any
|
||||
|
||||
const req = { params: { modelId } }
|
||||
const res = {
|
||||
status: jest.fn(),
|
||||
send: jest.fn(),
|
||||
}
|
||||
|
||||
jest.spyOn(app, 'get').mockImplementation((path, handler) => {
|
||||
if (path === `/download/getDownloadProgress/${modelId}`) {
|
||||
res.status(200)
|
||||
res.send(downloadProgress)
|
||||
}
|
||||
})
|
||||
|
||||
app.get(`/download/getDownloadProgress/${modelId}`, req as any)
|
||||
expect(res.status).toHaveBeenCalledWith(200)
|
||||
expect(res.send).toHaveBeenCalledWith(downloadProgress)
|
||||
})
|
||||
|
||||
it('should return 404 if download progress is not found', async () => {
|
||||
const modelId = '123'
|
||||
|
||||
const req = { params: { modelId } }
|
||||
const res = {
|
||||
status: jest.fn(),
|
||||
send: jest.fn(),
|
||||
}
|
||||
|
||||
|
||||
jest.spyOn(app, 'get').mockImplementation((path, handler) => {
|
||||
if (path === `/download/getDownloadProgress/${modelId}`) {
|
||||
res.status(404)
|
||||
res.send({ message: 'Download progress not found' })
|
||||
}
|
||||
})
|
||||
app.get(`/download/getDownloadProgress/${modelId}`, req as any)
|
||||
expect(res.status).toHaveBeenCalledWith(404)
|
||||
expect(res.send).toHaveBeenCalledWith({ message: 'Download progress not found' })
|
||||
})
|
||||
})
|
||||
16
core/src/node/api/restful/app/handlers.test.ts
Normal file
16
core/src/node/api/restful/app/handlers.test.ts
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
import { jest } from '@jest/globals';
|
||||
|
||||
import { HttpServer } from '../../HttpServer';
|
||||
import { handleRequests } from './handlers';
|
||||
import { Handler, RequestHandler } from '../../common/handler';
|
||||
|
||||
it('should initialize RequestHandler and call handle', () => {
|
||||
const mockHandle = jest.fn();
|
||||
jest.spyOn(RequestHandler.prototype, 'handle').mockImplementation(mockHandle);
|
||||
|
||||
const mockApp = { post: jest.fn() };
|
||||
handleRequests(mockApp as unknown as HttpServer);
|
||||
|
||||
expect(mockHandle).toHaveBeenCalled();
|
||||
});
|
||||
21
core/src/node/api/restful/common.test.ts
Normal file
21
core/src/node/api/restful/common.test.ts
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
import { commonRouter } from './common';
|
||||
import { JanApiRouteConfiguration } from './helper/configuration';
|
||||
|
||||
test('commonRouter sets up routes for each key in JanApiRouteConfiguration', async () => {
|
||||
const mockHttpServer = {
|
||||
get: jest.fn(),
|
||||
post: jest.fn(),
|
||||
patch: jest.fn(),
|
||||
put: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
};
|
||||
await commonRouter(mockHttpServer as any);
|
||||
|
||||
const expectedRoutes = Object.keys(JanApiRouteConfiguration);
|
||||
expectedRoutes.forEach((key) => {
|
||||
expect(mockHttpServer.get).toHaveBeenCalledWith(`/${key}`, expect.any(Function));
|
||||
expect(mockHttpServer.get).toHaveBeenCalledWith(`/${key}/:id`, expect.any(Function));
|
||||
expect(mockHttpServer.delete).toHaveBeenCalledWith(`/${key}/:id`, expect.any(Function));
|
||||
});
|
||||
});
|
||||
24
core/src/node/api/restful/helper/configuration.test.ts
Normal file
24
core/src/node/api/restful/helper/configuration.test.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { JanApiRouteConfiguration } from './configuration'
|
||||
|
||||
describe('JanApiRouteConfiguration', () => {
|
||||
it('should have the correct models configuration', () => {
|
||||
const modelsConfig = JanApiRouteConfiguration.models;
|
||||
expect(modelsConfig.dirName).toBe('models');
|
||||
expect(modelsConfig.metadataFileName).toBe('model.json');
|
||||
expect(modelsConfig.delete.object).toBe('model');
|
||||
});
|
||||
|
||||
it('should have the correct assistants configuration', () => {
|
||||
const assistantsConfig = JanApiRouteConfiguration.assistants;
|
||||
expect(assistantsConfig.dirName).toBe('assistants');
|
||||
expect(assistantsConfig.metadataFileName).toBe('assistant.json');
|
||||
expect(assistantsConfig.delete.object).toBe('assistant');
|
||||
});
|
||||
|
||||
it('should have the correct threads configuration', () => {
|
||||
const threadsConfig = JanApiRouteConfiguration.threads;
|
||||
expect(threadsConfig.dirName).toBe('threads');
|
||||
expect(threadsConfig.metadataFileName).toBe('thread.json');
|
||||
expect(threadsConfig.delete.object).toBe('thread');
|
||||
});
|
||||
});
|
||||
16
core/src/node/api/restful/v1.test.ts
Normal file
16
core/src/node/api/restful/v1.test.ts
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
import { v1Router } from './v1';
|
||||
import { commonRouter } from './common';
|
||||
|
||||
test('should define v1Router function', () => {
|
||||
expect(v1Router).toBeDefined();
|
||||
});
|
||||
|
||||
test('should register commonRouter', () => {
|
||||
const mockApp = {
|
||||
register: jest.fn(),
|
||||
};
|
||||
v1Router(mockApp as any);
|
||||
expect(mockApp.register).toHaveBeenCalledWith(commonRouter);
|
||||
});
|
||||
|
||||
122
core/src/node/extension/extension.test.ts
Normal file
122
core/src/node/extension/extension.test.ts
Normal file
@ -0,0 +1,122 @@
|
||||
import Extension from './extension';
|
||||
import { join } from 'path';
|
||||
import 'pacote';
|
||||
|
||||
it('should set active and call emitUpdate', () => {
|
||||
const extension = new Extension();
|
||||
extension.emitUpdate = jest.fn();
|
||||
|
||||
extension.setActive(true);
|
||||
|
||||
expect(extension._active).toBe(true);
|
||||
expect(extension.emitUpdate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should return correct specifier', () => {
|
||||
const origin = 'test-origin';
|
||||
const options = { version: '1.0.0' };
|
||||
const extension = new Extension(origin, options);
|
||||
|
||||
expect(extension.specifier).toBe('test-origin@1.0.0');
|
||||
});
|
||||
|
||||
|
||||
it('should set origin and installOptions in constructor', () => {
|
||||
const origin = 'test-origin';
|
||||
const options = { someOption: true };
|
||||
const extension = new Extension(origin, options);
|
||||
|
||||
expect(extension.origin).toBe(origin);
|
||||
expect(extension.installOptions.someOption).toBe(true);
|
||||
expect(extension.installOptions.fullMetadata).toBe(true); // default option
|
||||
});
|
||||
|
||||
it('should install extension and set url', async () => {
|
||||
const origin = 'test-origin';
|
||||
const options = {};
|
||||
const extension = new Extension(origin, options);
|
||||
|
||||
const mockManifest = {
|
||||
name: 'test-name',
|
||||
productName: 'Test Product',
|
||||
version: '1.0.0',
|
||||
main: 'index.js',
|
||||
description: 'Test description'
|
||||
};
|
||||
|
||||
jest.mock('pacote', () => ({
|
||||
manifest: jest.fn().mockResolvedValue(mockManifest),
|
||||
extract: jest.fn().mockResolvedValue(null)
|
||||
}));
|
||||
|
||||
extension.emitUpdate = jest.fn();
|
||||
await extension._install();
|
||||
|
||||
expect(extension.url).toBe('extension://test-name/index.js');
|
||||
expect(extension.emitUpdate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should call all listeners in emitUpdate', () => {
|
||||
const extension = new Extension();
|
||||
const callback1 = jest.fn();
|
||||
const callback2 = jest.fn();
|
||||
|
||||
extension.subscribe('listener1', callback1);
|
||||
extension.subscribe('listener2', callback2);
|
||||
|
||||
extension.emitUpdate();
|
||||
|
||||
expect(callback1).toHaveBeenCalledWith(extension);
|
||||
expect(callback2).toHaveBeenCalledWith(extension);
|
||||
});
|
||||
|
||||
|
||||
it('should remove listener in unsubscribe', () => {
|
||||
const extension = new Extension();
|
||||
const callback = jest.fn();
|
||||
|
||||
extension.subscribe('testListener', callback);
|
||||
extension.unsubscribe('testListener');
|
||||
|
||||
expect(extension.listeners['testListener']).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
it('should add listener in subscribe', () => {
|
||||
const extension = new Extension();
|
||||
const callback = jest.fn();
|
||||
|
||||
extension.subscribe('testListener', callback);
|
||||
|
||||
expect(extension.listeners['testListener']).toBe(callback);
|
||||
});
|
||||
|
||||
|
||||
it('should set properties from manifest', async () => {
|
||||
const origin = 'test-origin';
|
||||
const options = {};
|
||||
const extension = new Extension(origin, options);
|
||||
|
||||
const mockManifest = {
|
||||
name: 'test-name',
|
||||
productName: 'Test Product',
|
||||
version: '1.0.0',
|
||||
main: 'index.js',
|
||||
description: 'Test description'
|
||||
};
|
||||
|
||||
jest.mock('pacote', () => ({
|
||||
manifest: jest.fn().mockResolvedValue(mockManifest)
|
||||
}));
|
||||
|
||||
await extension.getManifest();
|
||||
|
||||
expect(extension.name).toBe('test-name');
|
||||
expect(extension.productName).toBe('Test Product');
|
||||
expect(extension.version).toBe('1.0.0');
|
||||
expect(extension.main).toBe('index.js');
|
||||
expect(extension.description).toBe('Test description');
|
||||
});
|
||||
|
||||
28
core/src/node/extension/manager.test.ts
Normal file
28
core/src/node/extension/manager.test.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import * as fs from 'fs';
|
||||
import { join } from 'path';
|
||||
import { ExtensionManager } from './manager';
|
||||
|
||||
it('should throw an error when an invalid path is provided', () => {
|
||||
const manager = new ExtensionManager();
|
||||
jest.spyOn(fs, 'existsSync').mockReturnValue(false);
|
||||
expect(() => manager.setExtensionsPath('')).toThrow('Invalid path provided to the extensions folder');
|
||||
});
|
||||
|
||||
|
||||
it('should return an empty string when extensionsPath is not set', () => {
|
||||
const manager = new ExtensionManager();
|
||||
expect(manager.getExtensionsFile()).toBe(join('', 'extensions.json'));
|
||||
});
|
||||
|
||||
|
||||
it('should return undefined if no path is set', () => {
|
||||
const manager = new ExtensionManager();
|
||||
expect(manager.getExtensionsPath()).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
it('should return the singleton instance', () => {
|
||||
const instance1 = new ExtensionManager();
|
||||
const instance2 = new ExtensionManager();
|
||||
expect(instance1).toBe(instance2);
|
||||
});
|
||||
43
core/src/node/extension/store.test.ts
Normal file
43
core/src/node/extension/store.test.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { getAllExtensions } from './store';
|
||||
import { getActiveExtensions } from './store';
|
||||
import { getExtension } from './store';
|
||||
|
||||
test('should return empty array when no extensions added', () => {
|
||||
expect(getAllExtensions()).toEqual([]);
|
||||
});
|
||||
|
||||
|
||||
test('should throw error when extension does not exist', () => {
|
||||
expect(() => getExtension('nonExistentExtension')).toThrow('Extension nonExistentExtension does not exist');
|
||||
});
|
||||
|
||||
import { addExtension } from './store';
|
||||
import Extension from './extension';
|
||||
|
||||
test('should return all extensions when multiple extensions added', () => {
|
||||
const ext1 = new Extension('ext1');
|
||||
ext1.name = 'ext1';
|
||||
const ext2 = new Extension('ext2');
|
||||
ext2.name = 'ext2';
|
||||
|
||||
addExtension(ext1, false);
|
||||
addExtension(ext2, false);
|
||||
|
||||
expect(getAllExtensions()).toEqual([ext1, ext2]);
|
||||
});
|
||||
|
||||
|
||||
|
||||
test('should return only active extensions', () => {
|
||||
const ext1 = new Extension('ext1');
|
||||
ext1.name = 'ext1';
|
||||
ext1.setActive(true);
|
||||
const ext2 = new Extension('ext2');
|
||||
ext2.name = 'ext2';
|
||||
ext2.setActive(false);
|
||||
|
||||
addExtension(ext1, false);
|
||||
addExtension(ext2, false);
|
||||
|
||||
expect(getActiveExtensions()).toEqual([ext1]);
|
||||
});
|
||||
14
core/src/node/helper/config.test.ts
Normal file
14
core/src/node/helper/config.test.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { getEngineConfiguration } from './config';
|
||||
import { getAppConfigurations, defaultAppConfig } from './config';
|
||||
|
||||
it('should return undefined for invalid engine ID', async () => {
|
||||
const config = await getEngineConfiguration('invalid_engine');
|
||||
expect(config).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
it('should return default config when CI is e2e', () => {
|
||||
process.env.CI = 'e2e';
|
||||
const config = getAppConfigurations();
|
||||
expect(config).toEqual(defaultAppConfig());
|
||||
});
|
||||
11
core/src/node/helper/download.test.ts
Normal file
11
core/src/node/helper/download.test.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { DownloadManager } from './download';
|
||||
|
||||
it('should set a network request for a specific file', () => {
|
||||
const downloadManager = new DownloadManager();
|
||||
const fileName = 'testFile';
|
||||
const request = { url: 'http://example.com' };
|
||||
|
||||
downloadManager.setRequest(fileName, request);
|
||||
|
||||
expect(downloadManager.networkRequests[fileName]).toEqual(request);
|
||||
});
|
||||
47
core/src/node/helper/logger.test.ts
Normal file
47
core/src/node/helper/logger.test.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { Logger, LoggerManager } from './logger';
|
||||
|
||||
it('should flush queued logs to registered loggers', () => {
|
||||
class TestLogger extends Logger {
|
||||
name = 'testLogger';
|
||||
log(args: any): void {
|
||||
console.log(args);
|
||||
}
|
||||
}
|
||||
const loggerManager = new LoggerManager();
|
||||
const testLogger = new TestLogger();
|
||||
loggerManager.register(testLogger);
|
||||
const logSpy = jest.spyOn(testLogger, 'log');
|
||||
loggerManager.log('test log');
|
||||
expect(logSpy).toHaveBeenCalledWith('test log');
|
||||
});
|
||||
|
||||
|
||||
it('should unregister a logger', () => {
|
||||
class TestLogger extends Logger {
|
||||
name = 'testLogger';
|
||||
log(args: any): void {
|
||||
console.log(args);
|
||||
}
|
||||
}
|
||||
const loggerManager = new LoggerManager();
|
||||
const testLogger = new TestLogger();
|
||||
loggerManager.register(testLogger);
|
||||
loggerManager.unregister('testLogger');
|
||||
const retrievedLogger = loggerManager.get('testLogger');
|
||||
expect(retrievedLogger).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
it('should register and retrieve a logger', () => {
|
||||
class TestLogger extends Logger {
|
||||
name = 'testLogger';
|
||||
log(args: any): void {
|
||||
console.log(args);
|
||||
}
|
||||
}
|
||||
const loggerManager = new LoggerManager();
|
||||
const testLogger = new TestLogger();
|
||||
loggerManager.register(testLogger);
|
||||
const retrievedLogger = loggerManager.get('testLogger');
|
||||
expect(retrievedLogger).toBe(testLogger);
|
||||
});
|
||||
23
core/src/node/helper/module.test.ts
Normal file
23
core/src/node/helper/module.test.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ModuleManager } from './module';
|
||||
|
||||
it('should clear all imported modules', () => {
|
||||
const moduleManager = new ModuleManager();
|
||||
moduleManager.setModule('module1', { key: 'value1' });
|
||||
moduleManager.setModule('module2', { key: 'value2' });
|
||||
moduleManager.clearImportedModules();
|
||||
expect(moduleManager.requiredModules).toEqual({});
|
||||
});
|
||||
|
||||
|
||||
it('should set a module correctly', () => {
|
||||
const moduleManager = new ModuleManager();
|
||||
moduleManager.setModule('testModule', { key: 'value' });
|
||||
expect(moduleManager.requiredModules['testModule']).toEqual({ key: 'value' });
|
||||
});
|
||||
|
||||
|
||||
it('should return the singleton instance', () => {
|
||||
const instance1 = new ModuleManager();
|
||||
const instance2 = new ModuleManager();
|
||||
expect(instance1).toBe(instance2);
|
||||
});
|
||||
29
core/src/node/helper/path.test.ts
Normal file
29
core/src/node/helper/path.test.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { normalizeFilePath } from './path'
|
||||
|
||||
import { jest } from '@jest/globals'
|
||||
describe('Test file normalize', () => {
|
||||
test('returns no file protocol prefix on Unix', async () => {
|
||||
expect(normalizeFilePath('file://test.txt')).toBe('test.txt')
|
||||
expect(normalizeFilePath('file:/test.txt')).toBe('test.txt')
|
||||
})
|
||||
test('returns no file protocol prefix on Windows', async () => {
|
||||
expect(normalizeFilePath('file:\\\\test.txt')).toBe('test.txt')
|
||||
expect(normalizeFilePath('file:\\test.txt')).toBe('test.txt')
|
||||
})
|
||||
|
||||
test('returns correct path when Electron is available and app is not packaged', () => {
|
||||
const electronMock = {
|
||||
app: {
|
||||
getAppPath: jest.fn().mockReturnValue('/mocked/path'),
|
||||
isPackaged: false,
|
||||
},
|
||||
protocol: {},
|
||||
}
|
||||
jest.mock('electron', () => electronMock)
|
||||
|
||||
const { appResourcePath } = require('./path')
|
||||
|
||||
const expectedPath = process.platform === 'win32' ? '\\mocked\\path' : '/mocked/path'
|
||||
expect(appResourcePath()).toBe(expectedPath)
|
||||
})
|
||||
})
|
||||
15
core/src/node/helper/resource.test.ts
Normal file
15
core/src/node/helper/resource.test.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { getSystemResourceInfo } from './resource';
|
||||
|
||||
it('should return the correct system resource information with a valid CPU count', async () => {
|
||||
const mockCpuCount = 4;
|
||||
jest.spyOn(require('./config'), 'physicalCpuCount').mockResolvedValue(mockCpuCount);
|
||||
const logSpy = jest.spyOn(require('./logger'), 'log').mockImplementation(() => {});
|
||||
|
||||
const result = await getSystemResourceInfo();
|
||||
|
||||
expect(result).toEqual({
|
||||
numCpuPhysicalCore: mockCpuCount,
|
||||
memAvailable: 0,
|
||||
});
|
||||
expect(logSpy).toHaveBeenCalledWith(`[CORTEX]::CPU information - ${mockCpuCount}`);
|
||||
});
|
||||
7
core/src/types/assistant/assistantEvent.test.ts
Normal file
7
core/src/types/assistant/assistantEvent.test.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { AssistantEvent } from './assistantEvent';
|
||||
it('dummy test', () => { expect(true).toBe(true); });
|
||||
|
||||
it('should contain OnAssistantsUpdate event', () => {
|
||||
expect(AssistantEvent.OnAssistantsUpdate).toBe('OnAssistantsUpdate');
|
||||
});
|
||||
|
||||
@ -16,7 +16,7 @@ export type DownloadState = {
|
||||
|
||||
error?: string
|
||||
extensionId?: string
|
||||
downloadType?: DownloadType
|
||||
downloadType?: DownloadType | string
|
||||
localPath?: string
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ export type DownloadRequest = {
|
||||
*/
|
||||
extensionId?: string
|
||||
|
||||
downloadType?: DownloadType
|
||||
downloadType?: DownloadType | string
|
||||
}
|
||||
|
||||
type DownloadTime = {
|
||||
|
||||
10
core/testRunner.js
Normal file
10
core/testRunner.js
Normal file
@ -0,0 +1,10 @@
|
||||
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;
|
||||
@ -1,12 +0,0 @@
|
||||
import { normalizeFilePath } from "../../src/node/helper/path";
|
||||
|
||||
describe("Test file normalize", () => {
|
||||
test("returns no file protocol prefix on Unix", async () => {
|
||||
expect(normalizeFilePath("file://test.txt")).toBe("test.txt");
|
||||
expect(normalizeFilePath("file:/test.txt")).toBe("test.txt");
|
||||
});
|
||||
test("returns no file protocol prefix on Windows", async () => {
|
||||
expect(normalizeFilePath("file:\\\\test.txt")).toBe("test.txt");
|
||||
expect(normalizeFilePath("file:\\test.txt")).toBe("test.txt");
|
||||
});
|
||||
});
|
||||
@ -16,4 +16,5 @@
|
||||
"types": ["@types/jest"],
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["**/*.test.ts"]
|
||||
}
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
module.exports = {
|
||||
projects: ['<rootDir>/core', '<rootDir>/web'],
|
||||
projects: ['<rootDir>/core', '<rootDir>/web', '<rootDir>/joi'],
|
||||
}
|
||||
|
||||
35
web/utils/Stack.test.ts
Normal file
35
web/utils/Stack.test.ts
Normal file
@ -0,0 +1,35 @@
|
||||
|
||||
import { Stack } from './Stack';
|
||||
|
||||
it('should return elements in reverse order', () => {
|
||||
const stack = new Stack<number>();
|
||||
stack.push(1);
|
||||
stack.push(2);
|
||||
stack.push(3);
|
||||
const reversedOutput = stack.reverseOutput();
|
||||
expect(reversedOutput).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
|
||||
it('should pop an element from the stack', () => {
|
||||
const stack = new Stack<number>();
|
||||
stack.push(1);
|
||||
const poppedElement = stack.pop();
|
||||
expect(poppedElement).toBe(1);
|
||||
expect(stack.isEmpty()).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
it('should push an element to the stack', () => {
|
||||
const stack = new Stack<number>();
|
||||
stack.push(1);
|
||||
expect(stack.isEmpty()).toBe(false);
|
||||
expect(stack.size()).toBe(1);
|
||||
expect(stack.peek()).toBe(1);
|
||||
});
|
||||
|
||||
|
||||
it('should initialize as empty', () => {
|
||||
const stack = new Stack<number>();
|
||||
expect(stack.isEmpty()).toBe(true);
|
||||
});
|
||||
22
web/utils/componentSettings.test.ts
Normal file
22
web/utils/componentSettings.test.ts
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
import { getConfigurationsData } from './componentSettings';
|
||||
|
||||
it('should process checkbox setting', () => {
|
||||
const settings = { embedding: true };
|
||||
const result = getConfigurationsData(settings);
|
||||
expect(result[0].controllerProps.value).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
it('should process input setting and handle array input', () => {
|
||||
const settings = { prompt_template: ['Hello', 'World', ''] };
|
||||
const result = getConfigurationsData(settings);
|
||||
expect(result[0].controllerProps.value).toBe('Hello World ');
|
||||
});
|
||||
|
||||
|
||||
it('should return an empty array when settings object is empty', () => {
|
||||
const settings = {};
|
||||
const result = getConfigurationsData(settings);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
27
web/utils/datetime.test.ts
Normal file
27
web/utils/datetime.test.ts
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
import { displayDate } from './datetime';
|
||||
import { isToday } from './datetime';
|
||||
|
||||
test('should return only time for today\'s timestamp', () => {
|
||||
const today = new Date();
|
||||
const timestamp = today.getTime();
|
||||
const expectedTime = today.toLocaleTimeString(undefined, {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: true,
|
||||
});
|
||||
expect(displayDate(timestamp)).toBe(expectedTime);
|
||||
});
|
||||
|
||||
|
||||
test('should return N/A for undefined timestamp', () => {
|
||||
expect(displayDate()).toBe('N/A');
|
||||
});
|
||||
|
||||
|
||||
test('should return true for today\'s timestamp', () => {
|
||||
const today = new Date();
|
||||
const timestamp = today.setHours(0, 0, 0, 0);
|
||||
expect(isToday(timestamp)).toBe(true);
|
||||
});
|
||||
17
web/utils/jsonToCssVariables.test.ts
Normal file
17
web/utils/jsonToCssVariables.test.ts
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
import cssVars from './jsonToCssVariables';
|
||||
|
||||
test('should convert nested JSON object to CSS variables', () => {
|
||||
const input = { theme: { color: 'blue', font: { size: '14px', weight: 'bold' } } };
|
||||
const expectedOutput = '--theme-color: blue;--theme-font-size: 14px;--theme-font-weight: bold;';
|
||||
const result = cssVars(input);
|
||||
expect(result).toBe(expectedOutput);
|
||||
});
|
||||
|
||||
|
||||
test('should convert simple JSON object to CSS variables', () => {
|
||||
const input = { color: 'red', fontSize: '16px' };
|
||||
const expectedOutput = '--color: red;--fontSize: 16px;';
|
||||
const result = cssVars(input);
|
||||
expect(result).toBe(expectedOutput);
|
||||
});
|
||||
@ -1,5 +1,3 @@
|
||||
// @auto-generated
|
||||
|
||||
import { utilizedMemory } from './memory'
|
||||
|
||||
test('test_utilizedMemory_arbitraryValues', () => {
|
||||
|
||||
18
web/utils/predefinedComponent.test.ts
Normal file
18
web/utils/predefinedComponent.test.ts
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
import { presetConfiguration } from './predefinedComponent';
|
||||
|
||||
it('should have correct configuration for prompt_template', () => {
|
||||
const config = presetConfiguration['prompt_template'];
|
||||
expect(config).toEqual({
|
||||
key: 'prompt_template',
|
||||
title: 'Prompt template',
|
||||
description: `A predefined text or framework that guides the AI model's response generation. It includes placeholders or instructions for the model to fill in or expand upon.`,
|
||||
controllerType: 'input',
|
||||
controllerProps: {
|
||||
placeholder: 'Prompt template',
|
||||
value: '',
|
||||
},
|
||||
requireModelReload: true,
|
||||
configType: 'setting',
|
||||
});
|
||||
});
|
||||
9
web/utils/thread.test.ts
Normal file
9
web/utils/thread.test.ts
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
import { generateThreadId } from './thread';
|
||||
|
||||
test('shouldGenerateThreadIdWithCorrectFormat', () => {
|
||||
const assistantId = 'assistant123';
|
||||
const threadId = generateThreadId(assistantId);
|
||||
const regex = /^assistant123_\d{10}$/;
|
||||
expect(threadId).toMatch(regex);
|
||||
});
|
||||
25
web/utils/titleUtils.test.ts
Normal file
25
web/utils/titleUtils.test.ts
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
import { openFileTitle } from './titleUtils';
|
||||
|
||||
test('should return "Open Containing Folder" when neither isMac nor isWindows is true', () => {
|
||||
(global as any).isMac = false;
|
||||
(global as any).isWindows = false;
|
||||
const result = openFileTitle();
|
||||
expect(result).toBe('Open Containing Folder');
|
||||
});
|
||||
|
||||
|
||||
test('should return "Show in File Explorer" when isWindows is true', () => {
|
||||
(global as any).isMac = false;
|
||||
(global as any).isWindows = true;
|
||||
const result = openFileTitle();
|
||||
expect(result).toBe('Show in File Explorer');
|
||||
});
|
||||
|
||||
|
||||
test('should return "Show in Finder" when isMac is true', () => {
|
||||
(global as any).isMac = true;
|
||||
(global as any).isWindows = false;
|
||||
const result = openFileTitle();
|
||||
expect(result).toBe('Show in Finder');
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user