jan/app/electron/core/plugin-manager/execution/activation-manager.test.js
Louis 20dbc02c03
Refactor Jan into an Electron app (#175)
* hackathon: Refactor Jan into an Electron app

* chore: correct NextJS export output path

* chore: build electron app for all production targets

* fix: correct assetPrefix for production build

* chore: preferences shortcut

* chore: refactor

* chore: refactor into ts

* feature/#52-compile-plugin-with-webpack

* chore: introduce renderer <=> plugins <=> main invocation

* chore: suppress errors - deprecate graphql & next-auth

* chore: data plugin functions

* add llm support

Signed-off-by: James <james@jan.ai>

* chore: update plugin

* chore: introduce data-plugin

* chore: plugin invokes main with args and synchronously

* chore: install db plugin should setup db

* feature: Data Driver Plugin - Load conversations and messages from data plugin

* chore: store text message sent

* chore: shared core services

* feature: inference service

* chore: conversations ordering

* adding model management service

Signed-off-by: James <james@jan.ai>

* chore: strict type

* feature: abstract plugin preferences

* chore: abstract plugin preference

* Revert "chore: strict type"

This reverts commit 9be188d827a0b2e081e9e04b192c323799de5bb5.

* chore: base-plugin styling

* feature: create and delete conversation

* chore: fix plugin search & clean messages

* chore: typing indicator

* chore: refactor useSendChatMessage

* chore: persists inserted id to in-memory messages

* chore: search conversation history

* add delete and download model (#189)

Signed-off-by: James <james@jan.ai>
Co-authored-by: James <james@jan.ai>

* chore: add empty state for conversation list

* chore: prompt missing extension function & fix app crashes

* chore: prompt user to install required plugins

* chore: add launch background

* chore: relaunch app on model downloaded

* Jan app add installation instruction (#191)

Co-authored-by: Hien To <>

* Chore: rename folder web-client to app (#192)

* Chore: rename folder web-client to app
---------

Co-authored-by: Hien To <>

* revert: add pre-install package

* add progress for downloading model

Signed-off-by: James <james@jan.ai>

* feature: production bundle

* add download progress

Signed-off-by: James <james@jan.ai>

* chore: add new chat function

* fix: electron asar unpack modules & dynamic import

* chore: fix unpack

* chore: fix dev pack

* Add instruction to build dmg file to README.md

* init model dynamically

Signed-off-by: James <james@jan.ai>

---------

Signed-off-by: James <james@jan.ai>
Co-authored-by: James <james@jan.ai>
Co-authored-by: NamH <NamNh0122@gmail.com>
Co-authored-by: hiento09 <136591877+hiento09@users.noreply.github.com>
Co-authored-by: Hien To <>
2023-09-24 20:42:58 -07:00

308 lines
6.9 KiB
JavaScript

import { setup } from './index'
import { register, trigger, remove, clear, get } from "./activation-manager";
import { add } from './extension-manager'
let mockPlugins = {}
setup({
importer(plugin) { return mockPlugins[plugin] }
})
afterEach(() => {
clear()
mockPlugins = {}
})
describe('register', () => {
it('should add a new activation point to the register when a new, valid plugin is registered',
() => {
register({
name: 'test',
url: 'testPkg',
activationPoints: ['ap1', 'ap2'],
active: true
})
expect(get()).toEqual([
{
plugin: 'test',
url: 'testPkg',
activationPoint: 'ap1',
activated: false
},
{
plugin: 'test',
url: 'testPkg',
activationPoint: 'ap2',
activated: false
}
])
}
)
it('should not add an activation point to the register when an existing, valid plugin is registered',
() => {
register({
name: 'test',
url: 'testPkg',
activationPoints: ['ap1', 'ap2'],
active: true
})
register({
name: 'test',
url: 'testPkg',
activationPoints: ['ap2', 'ap3'],
active: true
})
expect(get()).toEqual([
{
plugin: 'test',
url: 'testPkg',
activationPoint: 'ap1',
activated: false
},
{
plugin: 'test',
url: 'testPkg',
activationPoint: 'ap2',
activated: false
},
{
plugin: 'test',
url: 'testPkg',
activationPoint: 'ap3',
activated: false
},
])
}
)
it('should throw an error when an invalid plugin is registered',
() => {
const noActivationPoints = () => register({
name: 'test',
url: 'testPkg',
active: true
})
expect(noActivationPoints).toThrow(/does not have any activation points set up in its manifest/)
}
)
})
describe('trigger', () => {
it('should trigger all and only the activations with for the given execution point on triggering an execution, using the defined importer',
async () => {
const triggered = []
mockPlugins.plugin1 = {
ap1() { triggered.push('plugin1-ap1') }
}
mockPlugins.plugin2 = {
ap2() { triggered.push('plugin2-ap2') }
}
mockPlugins.plugin3 = {
ap1() { triggered.push('plugin3-ap1') },
ap2() { triggered.push('plugin3-ap2') }
}
register({
name: 'plugin1',
url: 'plugin1',
activationPoints: ['ap1'],
active: true
})
register({
name: 'plugin2',
url: 'plugin2',
activationPoints: ['ap2'],
active: true
})
register({
name: 'plugin3',
url: 'plugin3',
activationPoints: ['ap1', 'ap2'],
active: true
})
await trigger('ap1')
expect(triggered).toEqual(['plugin1-ap1', 'plugin3-ap1'])
}
)
it('should return an error if an activation point is triggered on a plugin that does not include it',
async () => {
mockPlugins.plugin1 = {
wrongAp() { }
}
register({
name: 'plugin1',
url: 'plugin1',
activationPoints: ['ap1']
})
await expect(() => trigger('ap1')).rejects.toThrow(/was triggered but does not exist on plugin/)
}
)
it('should provide the registered extension points to the triggered activation point if presetEPs is set to true in the setup',
async () => {
setup({
importer(plugin) { return mockPlugins[plugin] },
presetEPs: true,
})
let ap1Res
mockPlugins.plugin1 = {
ap1: eps => ap1Res = eps
}
register({
name: 'plugin1',
url: 'plugin1',
activationPoints: ['ap1']
})
add('ep1')
add('ep2')
await trigger('ap1')
expect(ap1Res.ep1.constructor.name).toEqual('ExtensionPoint')
expect(ap1Res.ep2.constructor.name).toEqual('ExtensionPoint')
}
)
it('should allow registration, execution and serial execution of execution points when an activation point is triggered if presetEPs is set to false in the setup',
async () => {
setup({
importer(plugin) { return mockPlugins[plugin] },
})
let ap1Res
mockPlugins.plugin1 = {
ap1: eps => ap1Res = eps
}
register({
name: 'plugin1',
url: 'plugin1',
activationPoints: ['ap1']
})
await trigger('ap1')
expect(typeof ap1Res.register).toBe('function')
expect(typeof ap1Res.execute).toBe('function')
expect(typeof ap1Res.executeSerial).toBe('function')
}
)
it('should not provide any reference to extension points during activation point triggering if presetEPs is set to null in the setup',
async () => {
setup({
importer(plugin) { return mockPlugins[plugin] },
presetEPs: null,
})
let ap1Res = true
mockPlugins.plugin1 = {
ap1: eps => ap1Res = eps
}
register({
name: 'plugin1',
url: 'plugin1',
activationPoints: ['ap1']
})
await trigger('ap1')
expect(ap1Res).not.toBeDefined()
}
)
})
describe('remove and clear', () => {
beforeEach(() => {
register({
name: 'plugin1',
url: 'plugin1',
activationPoints: ['ap1', 'ap2'],
active: true
})
register({
name: 'plugin2',
url: 'plugin2',
activationPoints: ['ap2', 'ap3'],
active: true
})
})
it('should remove all and only the activations for the given plugin from the register when removing activations',
() => {
remove('plugin1')
expect(get()).toEqual([
{
plugin: 'plugin2',
url: 'plugin2',
activationPoint: 'ap2',
activated: false
},
{
plugin: 'plugin2',
url: 'plugin2',
activationPoint: 'ap3',
activated: false
},
])
}
)
it('should not remove any activations from the register if no plugin name is provided',
() => {
remove()
expect(get()).toEqual([
{
plugin: 'plugin1',
url: 'plugin1',
activationPoint: 'ap1',
activated: false
},
{
plugin: 'plugin1',
url: 'plugin1',
activationPoint: 'ap2',
activated: false
},
{
plugin: 'plugin2',
url: 'plugin2',
activationPoint: 'ap2',
activated: false
},
{
plugin: 'plugin2',
url: 'plugin2',
activationPoint: 'ap3',
activated: false
},
])
}
)
it('should remove all activations from the register when clearing the register',
() => {
clear()
expect(get()).toEqual([])
}
)
})