bajo 2.23.0 → 2.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsdoc.conf.json +2 -1
- package/class/app.js +220 -107
- package/class/bajo.js +246 -174
- package/class/base.js +85 -24
- package/class/cache.js +8 -16
- package/class/err.js +31 -10
- package/class/log.js +46 -38
- package/class/plugin.js +53 -30
- package/class/print.js +18 -15
- package/class/tools.js +6 -5
- package/docs/App.html +51 -1
- package/docs/Bajo.html +1 -1
- package/docs/Base.html +34 -1
- package/docs/Cache.html +1 -1
- package/docs/Err.html +2 -2
- package/docs/Log.html +2 -2
- package/docs/Main.html +3 -0
- package/docs/Plugin.html +1 -1
- package/docs/Print.html +1 -1
- package/docs/Tools.html +1 -1
- package/docs/class_app.js.html +222 -109
- package/docs/class_bajo.js.html +260 -179
- package/docs/class_base.js.html +87 -26
- package/docs/class_cache.js.html +10 -18
- package/docs/class_err.js.html +33 -12
- package/docs/class_log.js.html +48 -40
- package/docs/class_plugin.js.html +55 -32
- package/docs/class_print.js.html +20 -17
- package/docs/class_tools.js.html +8 -7
- package/docs/data/search.json +1 -1
- package/docs/extend_bajo_hook.js.html +289 -0
- package/docs/global.html +22 -1
- package/docs/index.html +1 -1
- package/docs/index.js.html +53 -16
- package/docs/lib_applet-hook.js.html +328 -0
- package/docs/lib_config.js.html +167 -0
- package/docs/lib_helper.js.html +186 -192
- package/docs/module-Applet.html +3 -0
- package/docs/module-Helper-Main.html +3 -0
- package/docs/module-Helper.html +1 -8
- package/docs/module-Hook.html +1 -1
- package/docs/tutorial-01_WELCOME.html +3 -0
- package/docs/tutorial-02_GETTING_STARTED.html +125 -0
- package/docs/tutorial-03_USER_GUIDE.html +54 -0
- package/docs/tutorial-04_DEV_GUIDE.html +178 -0
- package/docs/tutorial-05_ECOSYSTEM.html +3 -0
- package/extend/bajo/intl/en-US.json +2 -1
- package/extend/bajo/intl/id.json +2 -1
- package/index.js +51 -14
- package/lib/applet-hook.js +325 -0
- package/lib/config.js +164 -0
- package/lib/helper.js +184 -190
- package/package.json +6 -2
- package/test/00-global-hooks.test.js +44 -0
- package/test/e2e/_run.js +19 -0
- package/test/e2e/e2e-applet-process.test.js +66 -0
- package/test/e2e/e2e-boot-process.test.js +62 -0
- package/test/e2e/e2e-hooks-process.test.js +68 -0
- package/test/integration/_setup.js +55 -0
- package/test/integration/aspect-01-boot-instance.test.js +24 -0
- package/test/integration/aspect-02-plugin-loading.test.js +24 -0
- package/test/integration/aspect-03-config-merge.test.js +27 -0
- package/test/integration/aspect-04-intl-loading.test.js +29 -0
- package/test/integration/aspect-05-cache-roundtrip.test.js +26 -0
- package/test/integration/aspect-06-log-file-save.test.js +28 -0
- package/test/integration/aspect-07-plugin-data-dir.test.js +25 -0
- package/test/integration/aspect-08-plugin-lookup.test.js +25 -0
- package/test/integration/aspect-09-config-formats.test.js +26 -0
- package/test/integration/aspect-10-hooks.test.js +38 -0
- package/test/unit/app.test.js +115 -0
- package/test/unit/applet-hook.test.js +10 -0
- package/test/unit/bajo.test.js +151 -0
- package/test/unit/base.test.js +90 -0
- package/test/unit/cache.test.js +89 -0
- package/test/unit/config.test.js +14 -0
- package/test/unit/err.test.js +57 -0
- package/test/unit/helper.test.js +116 -0
- package/test/unit/index.test.js +40 -0
- package/test/unit/log.test.js +84 -0
- package/test/unit/plugin.test.js +95 -0
- package/test/unit/print.test.js +72 -0
- package/test/unit/tools.test.js +29 -0
- package/wiki/CHANGES.md +4 -0
- package/wiki/tutorials/01_WELCOME.md +18 -0
- package/wiki/tutorials/02_GETTING_STARTED.md +366 -0
- package/wiki/tutorials/03_USER_GUIDE.md +262 -0
- package/wiki/tutorials/04_DEV_GUIDE.md +386 -0
- package/wiki/tutorials/05_ECOSYSTEM.md +95 -0
- package/wiki/tutorials/tutorials.json +17 -0
- package/lib/hook.js +0 -228
- package/test/app.test.js +0 -183
- package/test/bajo.test.js +0 -124
- package/test/base.test.js +0 -75
- package/test/cache.test.js +0 -94
- package/test/e2e.test.js +0 -137
- package/test/err.test.js +0 -73
- package/test/helper.test.js +0 -39
- package/test/import-module.test.js +0 -138
- package/test/integration.test.js +0 -218
- package/test/log.test.js +0 -119
- package/test/plugin.test.js +0 -116
- package/test/print.test.js +0 -100
- package/test/tools.test.js +0 -38
- package/wiki/CONFIG.md +0 -36
- package/wiki/DEV_GUIDE.md +0 -3
- package/wiki/ECOSYSTEM.md +0 -93
- package/wiki/GETTING-STARTED.md +0 -356
- package/wiki/USER-GUIDE.md +0 -256
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/* global beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
const state = {
|
|
4
|
+
originalSetInterval: null,
|
|
5
|
+
createdIntervals: [],
|
|
6
|
+
listenerCounts: null
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
state.createdIntervals = []
|
|
11
|
+
state.listenerCounts = {
|
|
12
|
+
SIGINT: process.listeners('SIGINT').length,
|
|
13
|
+
SIGTERM: process.listeners('SIGTERM').length,
|
|
14
|
+
beforeExit: process.listeners('beforeExit').length,
|
|
15
|
+
uncaughtException: process.listeners('uncaughtException').length,
|
|
16
|
+
unhandledRejection: process.listeners('unhandledRejection').length,
|
|
17
|
+
warning: process.listeners('warning').length
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!state.originalSetInterval) state.originalSetInterval = global.setInterval
|
|
21
|
+
global.setInterval = (fn, delay, ...args) => {
|
|
22
|
+
const id = state.originalSetInterval(fn, delay, ...args)
|
|
23
|
+
state.createdIntervals.push(id)
|
|
24
|
+
return id
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
if (state.originalSetInterval) {
|
|
30
|
+
global.setInterval = state.originalSetInterval
|
|
31
|
+
}
|
|
32
|
+
for (const id of state.createdIntervals) {
|
|
33
|
+
clearInterval(id)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for (const event of Object.keys(state.listenerCounts)) {
|
|
37
|
+
const listeners = process.listeners(event)
|
|
38
|
+
const keep = state.listenerCounts[event]
|
|
39
|
+
if (listeners.length <= keep) continue
|
|
40
|
+
for (const listener of listeners.slice(keep)) {
|
|
41
|
+
process.removeListener(event, listener)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
})
|
package/test/e2e/_run.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process'
|
|
2
|
+
|
|
3
|
+
export const runNode = (cwd, file, timeoutMs = 15000) => {
|
|
4
|
+
return new Promise((resolve) => {
|
|
5
|
+
const child = spawn(process.execPath, [file], { cwd, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
6
|
+
let stdout = ''
|
|
7
|
+
let stderr = ''
|
|
8
|
+
child.stdout.on('data', c => { stdout += c.toString() })
|
|
9
|
+
child.stderr.on('data', c => { stderr += c.toString() })
|
|
10
|
+
const timer = setTimeout(() => {
|
|
11
|
+
child.kill('SIGTERM')
|
|
12
|
+
resolve({ code: null, timedOut: true, stdout, stderr })
|
|
13
|
+
}, timeoutMs)
|
|
14
|
+
child.on('close', code => {
|
|
15
|
+
clearTimeout(timer)
|
|
16
|
+
resolve({ code, timedOut: false, stdout, stderr })
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/* global describe, it, beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { pathToFileURL } from 'node:url'
|
|
5
|
+
import fs from 'fs-extra'
|
|
6
|
+
import { expect } from 'chai'
|
|
7
|
+
import { runNode } from './_run.js'
|
|
8
|
+
|
|
9
|
+
describe('e2e applet process', () => {
|
|
10
|
+
let root
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
root = fs.mkdtempSync(path.join('/tmp', 'bajo-e2e-applet-'))
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
if (root) fs.rmSync(root, { recursive: true, force: true })
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('runs applet path in separate process with real plugin', async function () {
|
|
21
|
+
this.timeout(25000)
|
|
22
|
+
|
|
23
|
+
const entry = pathToFileURL(path.join('/mnt/d/Projects/Bajo/bajo', 'index.js')).href
|
|
24
|
+
const pluginName = 'bajo-cli'
|
|
25
|
+
const pdir = path.join(root, 'node_modules', pluginName)
|
|
26
|
+
|
|
27
|
+
await fs.ensureDir(pdir)
|
|
28
|
+
await fs.writeJson(path.join(root, 'package.json'), { name: 'e2e-app', type: 'module', bajo: { plugins: [pluginName] } })
|
|
29
|
+
await fs.writeJson(path.join(pdir, 'package.json'), { name: pluginName, version: '1.0.0', type: 'module', main: 'index.js' })
|
|
30
|
+
await fs.writeFile(path.join(pdir, 'index.js'), `
|
|
31
|
+
async function factory (pkgName) {
|
|
32
|
+
const me = this
|
|
33
|
+
return class BajoCli extends this.app.baseClass.Base {
|
|
34
|
+
constructor () {
|
|
35
|
+
super(pkgName, me.app)
|
|
36
|
+
this.config = {}
|
|
37
|
+
}
|
|
38
|
+
runApplet = async () => {
|
|
39
|
+
await this.app.lib.fs.writeFile(this.app.dir + '/e2e-applet.txt', 'ran', 'utf8')
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export default factory
|
|
44
|
+
`, 'utf8')
|
|
45
|
+
|
|
46
|
+
await fs.ensureDir(path.join(pdir, 'extend', 'bajoCli'))
|
|
47
|
+
await fs.writeFile(path.join(pdir, 'extend', 'bajoCli', 'applet.js'), 'export default function () { return { name: "noop" } }', 'utf8')
|
|
48
|
+
|
|
49
|
+
await fs.ensureDir(path.join(root, 'data', 'config'))
|
|
50
|
+
await fs.writeJson(path.join(root, 'data', 'config', 'bajo.json'), { log: { level: 'silent', save: false }, exitHandler: false, cache: { purgeIntvDur: '1h' } })
|
|
51
|
+
|
|
52
|
+
await fs.writeFile(path.join(root, 'run-app.mjs'), `
|
|
53
|
+
process.argv = ['node', 'index.js', '--applet=bajoCli:noop']
|
|
54
|
+
import boot from ${JSON.stringify(entry)}
|
|
55
|
+
await boot({ cwd: process.cwd() })
|
|
56
|
+
console.log('APPLET_OK')
|
|
57
|
+
process.exit(0)
|
|
58
|
+
`, 'utf8')
|
|
59
|
+
|
|
60
|
+
const res = await runNode(root, 'run-app.mjs')
|
|
61
|
+
expect(res.timedOut).to.equal(false)
|
|
62
|
+
expect(res.code).to.equal(0)
|
|
63
|
+
expect(res.stdout).to.include('APPLET_OK')
|
|
64
|
+
expect(fs.existsSync(path.join(root, 'e2e-applet.txt'))).to.equal(true)
|
|
65
|
+
})
|
|
66
|
+
})
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/* global describe, it, beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { pathToFileURL } from 'node:url'
|
|
5
|
+
import fs from 'fs-extra'
|
|
6
|
+
import { expect } from 'chai'
|
|
7
|
+
import { runNode } from './_run.js'
|
|
8
|
+
|
|
9
|
+
describe('e2e boot process', () => {
|
|
10
|
+
let root
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
root = fs.mkdtempSync(path.join('/tmp', 'bajo-e2e-boot-'))
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
if (root) fs.rmSync(root, { recursive: true, force: true })
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('boots real app in separate process and runs plugin start', async function () {
|
|
21
|
+
this.timeout(20000)
|
|
22
|
+
|
|
23
|
+
const entry = pathToFileURL(path.join('/mnt/d/Projects/Bajo/bajo', 'index.js')).href
|
|
24
|
+
const pluginName = 'e2e-plugin-boot'
|
|
25
|
+
const pdir = path.join(root, 'node_modules', pluginName)
|
|
26
|
+
|
|
27
|
+
await fs.ensureDir(pdir)
|
|
28
|
+
await fs.writeJson(path.join(root, 'package.json'), { name: 'e2e-app', type: 'module', bajo: { plugins: [pluginName] } })
|
|
29
|
+
await fs.writeJson(path.join(pdir, 'package.json'), { name: pluginName, version: '1.0.0', type: 'module', main: 'index.js' })
|
|
30
|
+
await fs.writeFile(path.join(pdir, 'index.js'), `
|
|
31
|
+
async function factory (pkgName) {
|
|
32
|
+
const me = this
|
|
33
|
+
return class E2EPluginBoot extends this.app.baseClass.Base {
|
|
34
|
+
constructor () {
|
|
35
|
+
super(pkgName, me.app)
|
|
36
|
+
this.config = {}
|
|
37
|
+
this.start = async () => {
|
|
38
|
+
await this.app.lib.fs.writeFile(this.app.dir + '/e2e-boot.txt', 'started', 'utf8')
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export default factory
|
|
44
|
+
`, 'utf8')
|
|
45
|
+
|
|
46
|
+
await fs.ensureDir(path.join(root, 'data', 'config'))
|
|
47
|
+
await fs.writeJson(path.join(root, 'data', 'config', 'bajo.json'), { log: { level: 'silent', save: false }, exitHandler: false, cache: { purgeIntvDur: '1h' } })
|
|
48
|
+
|
|
49
|
+
await fs.writeFile(path.join(root, 'run-app.mjs'), `
|
|
50
|
+
import boot from ${JSON.stringify(entry)}
|
|
51
|
+
const app = await boot({ cwd: process.cwd() })
|
|
52
|
+
console.log('BOOT_OK:' + !!app.getPlugin(${JSON.stringify(pluginName)}, true))
|
|
53
|
+
process.exit(0)
|
|
54
|
+
`, 'utf8')
|
|
55
|
+
|
|
56
|
+
const res = await runNode(root, 'run-app.mjs')
|
|
57
|
+
expect(res.timedOut).to.equal(false)
|
|
58
|
+
expect(res.code).to.equal(0)
|
|
59
|
+
expect(res.stdout).to.include('BOOT_OK:true')
|
|
60
|
+
expect(fs.existsSync(path.join(root, 'e2e-boot.txt'))).to.equal(true)
|
|
61
|
+
})
|
|
62
|
+
})
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* global describe, it, beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { pathToFileURL } from 'node:url'
|
|
5
|
+
import fs from 'fs-extra'
|
|
6
|
+
import { expect } from 'chai'
|
|
7
|
+
import { runNode } from './_run.js'
|
|
8
|
+
|
|
9
|
+
describe('e2e hooks process', () => {
|
|
10
|
+
let root
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
root = fs.mkdtempSync(path.join('/tmp', 'bajo-e2e-hooks-'))
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
if (root) fs.rmSync(root, { recursive: true, force: true })
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('executes real plugin hooks in separate process', async function () {
|
|
21
|
+
this.timeout(20000)
|
|
22
|
+
|
|
23
|
+
const entry = pathToFileURL(path.join('/mnt/d/Projects/Bajo/bajo', 'index.js')).href
|
|
24
|
+
const pluginName = 'e2e-plugin-hooks'
|
|
25
|
+
const pdir = path.join(root, 'node_modules', pluginName)
|
|
26
|
+
|
|
27
|
+
await fs.ensureDir(path.join(pdir, 'extend', 'bajo', 'hook'))
|
|
28
|
+
await fs.writeJson(path.join(root, 'package.json'), { name: 'e2e-app', type: 'module', bajo: { plugins: [pluginName] } })
|
|
29
|
+
await fs.writeJson(path.join(pdir, 'package.json'), { name: pluginName, version: '1.0.0', type: 'module', main: 'index.js' })
|
|
30
|
+
await fs.writeFile(path.join(pdir, 'index.js'), `
|
|
31
|
+
async function factory (pkgName) {
|
|
32
|
+
const me = this
|
|
33
|
+
return class E2EPluginHooks extends this.app.baseClass.Base {
|
|
34
|
+
constructor () {
|
|
35
|
+
super(pkgName, me.app)
|
|
36
|
+
this.config = {}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export default factory
|
|
41
|
+
`, 'utf8')
|
|
42
|
+
|
|
43
|
+
await fs.writeFile(path.join(pdir, 'extend', 'bajo', 'hook', 'bajo@after-boot.js'), `
|
|
44
|
+
export default {
|
|
45
|
+
level: 1,
|
|
46
|
+
handler: async function () {
|
|
47
|
+
await this.app.lib.fs.writeFile(this.app.dir + '/e2e-hook.txt', 'done', 'utf8')
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
`, 'utf8')
|
|
51
|
+
|
|
52
|
+
await fs.ensureDir(path.join(root, 'data', 'config'))
|
|
53
|
+
await fs.writeJson(path.join(root, 'data', 'config', 'bajo.json'), { log: { level: 'silent', save: false }, exitHandler: false, cache: { purgeIntvDur: '1h' } })
|
|
54
|
+
|
|
55
|
+
await fs.writeFile(path.join(root, 'run-app.mjs'), `
|
|
56
|
+
import boot from ${JSON.stringify(entry)}
|
|
57
|
+
await boot({ cwd: process.cwd() })
|
|
58
|
+
console.log('HOOK_OK')
|
|
59
|
+
process.exit(0)
|
|
60
|
+
`, 'utf8')
|
|
61
|
+
|
|
62
|
+
const res = await runNode(root, 'run-app.mjs')
|
|
63
|
+
expect(res.timedOut).to.equal(false)
|
|
64
|
+
expect(res.code).to.equal(0)
|
|
65
|
+
expect(res.stdout).to.include('HOOK_OK')
|
|
66
|
+
expect(fs.existsSync(path.join(root, 'e2e-hook.txt'))).to.equal(true)
|
|
67
|
+
})
|
|
68
|
+
})
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import fs from 'fs-extra'
|
|
3
|
+
|
|
4
|
+
export const makeRoot = (prefix = 'bajo-int-') => fs.mkdtempSync(path.join('/tmp', prefix))
|
|
5
|
+
|
|
6
|
+
export const cleanupRoot = (root) => {
|
|
7
|
+
if (root) fs.rmSync(root, { recursive: true, force: true })
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const writeBaseApp = async (root, pluginName, extraBajoConfig = {}) => {
|
|
11
|
+
await fs.writeJson(path.join(root, 'package.json'), {
|
|
12
|
+
name: 'bajo-int-app',
|
|
13
|
+
type: 'module',
|
|
14
|
+
bajo: { plugins: [pluginName] }
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
await fs.ensureDir(path.join(root, 'data', 'config'))
|
|
18
|
+
await fs.writeJson(path.join(root, 'data', 'config', 'bajo.json'), {
|
|
19
|
+
env: 'dev',
|
|
20
|
+
lang: 'en-US',
|
|
21
|
+
exitHandler: false,
|
|
22
|
+
log: { level: 'silent', save: false },
|
|
23
|
+
cache: { purgeIntvDur: '1h' },
|
|
24
|
+
...extraBajoConfig
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const writePlugin = async (root, pluginName, className = 'IntegrationPlugin', startBody = '') => {
|
|
29
|
+
const pluginDir = path.join(root, 'node_modules', pluginName)
|
|
30
|
+
await fs.ensureDir(pluginDir)
|
|
31
|
+
await fs.writeJson(path.join(pluginDir, 'package.json'), {
|
|
32
|
+
name: pluginName,
|
|
33
|
+
version: '1.0.0',
|
|
34
|
+
type: 'module',
|
|
35
|
+
main: 'index.js'
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const code = `
|
|
39
|
+
async function factory (pkgName) {
|
|
40
|
+
const me = this
|
|
41
|
+
return class ${className} extends this.app.baseClass.Base {
|
|
42
|
+
constructor () {
|
|
43
|
+
super(pkgName, me.app)
|
|
44
|
+
this.config = { marker: 'ok', feature: true }
|
|
45
|
+
this.start = async () => {
|
|
46
|
+
${startBody}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export default factory
|
|
52
|
+
`
|
|
53
|
+
await fs.writeFile(path.join(pluginDir, 'index.js'), code, 'utf8')
|
|
54
|
+
return pluginDir
|
|
55
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/* global describe, it, beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai'
|
|
4
|
+
import boot from '../../index.js'
|
|
5
|
+
import { makeRoot, cleanupRoot, writeBaseApp, writePlugin } from './_setup.js'
|
|
6
|
+
|
|
7
|
+
describe('integration aspect 01 - boot instance', () => {
|
|
8
|
+
let root
|
|
9
|
+
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
root = makeRoot('bajo-int-a01-')
|
|
12
|
+
await writeBaseApp(root, 'int-a01-plugin')
|
|
13
|
+
await writePlugin(root, 'int-a01-plugin', 'IntA01', "await this.app.lib.fs.writeFile(this.app.dir + '/a01.txt', 'ok', 'utf8')")
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
afterEach(() => cleanupRoot(root))
|
|
17
|
+
|
|
18
|
+
it('boots app instance successfully', async function () {
|
|
19
|
+
this.timeout(12000)
|
|
20
|
+
const app = await boot({ cwd: root })
|
|
21
|
+
expect(app).to.be.an('object')
|
|
22
|
+
expect(app.bajo).to.be.an('object')
|
|
23
|
+
})
|
|
24
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/* global describe, it, beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai'
|
|
4
|
+
import boot from '../../index.js'
|
|
5
|
+
import { makeRoot, cleanupRoot, writeBaseApp, writePlugin } from './_setup.js'
|
|
6
|
+
|
|
7
|
+
describe('integration aspect 02 - plugin loading', () => {
|
|
8
|
+
let root
|
|
9
|
+
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
root = makeRoot('bajo-int-a02-')
|
|
12
|
+
await writeBaseApp(root, 'int-a02-plugin')
|
|
13
|
+
await writePlugin(root, 'int-a02-plugin', 'IntA02', "this.state.started = true")
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
afterEach(() => cleanupRoot(root))
|
|
17
|
+
|
|
18
|
+
it('loads plugin and keeps namespace mapping', async function () {
|
|
19
|
+
this.timeout(12000)
|
|
20
|
+
const app = await boot({ cwd: root })
|
|
21
|
+
expect(app.intA02Plugin).to.be.an('object')
|
|
22
|
+
expect(app.getAllNs()).to.include('intA02Plugin')
|
|
23
|
+
})
|
|
24
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/* global describe, it, beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import fs from 'fs-extra'
|
|
5
|
+
import { expect } from 'chai'
|
|
6
|
+
import boot from '../../index.js'
|
|
7
|
+
import { makeRoot, cleanupRoot, writeBaseApp, writePlugin } from './_setup.js'
|
|
8
|
+
|
|
9
|
+
describe('integration aspect 03 - config merge', () => {
|
|
10
|
+
let root
|
|
11
|
+
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
root = makeRoot('bajo-int-a03-')
|
|
14
|
+
await writeBaseApp(root, 'int-a03-plugin')
|
|
15
|
+
await writePlugin(root, 'int-a03-plugin', 'IntA03', '')
|
|
16
|
+
await fs.writeJson(path.join(root, 'data', 'config', 'intA03Plugin.json'), { marker: 'override', feature: false })
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
afterEach(() => cleanupRoot(root))
|
|
20
|
+
|
|
21
|
+
it('merges plugin runtime config from data/config', async function () {
|
|
22
|
+
this.timeout(12000)
|
|
23
|
+
const app = await boot({ cwd: root })
|
|
24
|
+
expect(app.intA03Plugin.getConfig('marker')).to.equal('override')
|
|
25
|
+
expect(app.intA03Plugin.getConfig('feature')).to.equal(false)
|
|
26
|
+
})
|
|
27
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* global describe, it, beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import fs from 'fs-extra'
|
|
5
|
+
import { expect } from 'chai'
|
|
6
|
+
import boot from '../../index.js'
|
|
7
|
+
import { makeRoot, cleanupRoot, writeBaseApp, writePlugin } from './_setup.js'
|
|
8
|
+
|
|
9
|
+
describe('integration aspect 04 - intl loading', () => {
|
|
10
|
+
let root
|
|
11
|
+
let pluginDir
|
|
12
|
+
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
root = makeRoot('bajo-int-a04-')
|
|
15
|
+
await writeBaseApp(root, 'int-a04-plugin')
|
|
16
|
+
pluginDir = await writePlugin(root, 'int-a04-plugin', 'IntA04', '')
|
|
17
|
+
await fs.ensureDir(path.join(pluginDir, 'extend', 'bajo', 'intl'))
|
|
18
|
+
await fs.writeJson(path.join(pluginDir, 'extend', 'bajo', 'intl', 'en-US.json'), { hello: 'Hello %s' })
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
afterEach(() => cleanupRoot(root))
|
|
22
|
+
|
|
23
|
+
it('loads plugin translation file and translates text', async function () {
|
|
24
|
+
this.timeout(12000)
|
|
25
|
+
const app = await boot({ cwd: root })
|
|
26
|
+
expect(app.intA04Plugin.intl['en-US'].hello).to.equal('Hello %s')
|
|
27
|
+
expect(app.t('intA04Plugin', 'hello', 'Joe')).to.equal('Hello Joe')
|
|
28
|
+
})
|
|
29
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/* global describe, it, beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai'
|
|
4
|
+
import boot from '../../index.js'
|
|
5
|
+
import { makeRoot, cleanupRoot, writeBaseApp, writePlugin } from './_setup.js'
|
|
6
|
+
|
|
7
|
+
describe('integration aspect 05 - cache roundtrip', () => {
|
|
8
|
+
let root
|
|
9
|
+
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
root = makeRoot('bajo-int-a05-')
|
|
12
|
+
await writeBaseApp(root, 'int-a05-plugin')
|
|
13
|
+
await writePlugin(root, 'int-a05-plugin', 'IntA05', '')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
afterEach(() => cleanupRoot(root))
|
|
17
|
+
|
|
18
|
+
it('persists and reads cache values through app.cache', async function () {
|
|
19
|
+
this.timeout(12000)
|
|
20
|
+
const app = await boot({ cwd: root })
|
|
21
|
+
const payload = { ok: true, ids: [1, 2, 3] }
|
|
22
|
+
await app.cache.save('bajo.demo:path/item', payload, 60000)
|
|
23
|
+
const loaded = await app.cache.load('bajo.demo:path/item', 60000)
|
|
24
|
+
expect(loaded).to.deep.equal(payload)
|
|
25
|
+
})
|
|
26
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/* global describe, it, beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import fs from 'fs-extra'
|
|
5
|
+
import { expect } from 'chai'
|
|
6
|
+
import boot from '../../index.js'
|
|
7
|
+
import { makeRoot, cleanupRoot, writeBaseApp, writePlugin } from './_setup.js'
|
|
8
|
+
|
|
9
|
+
describe('integration aspect 06 - log save', () => {
|
|
10
|
+
let root
|
|
11
|
+
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
root = makeRoot('bajo-int-a06-')
|
|
14
|
+
await writeBaseApp(root, 'int-a06-plugin', { log: { level: 'trace', save: true, pretty: false, useUtc: false, timeTaken: false, dateFormat: 'YYYY-MM-DD', rotation: { cycle: 'none', byPlugin: false } } })
|
|
15
|
+
await writePlugin(root, 'int-a06-plugin', 'IntA06', '')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
afterEach(() => cleanupRoot(root))
|
|
19
|
+
|
|
20
|
+
it('writes log file when save is enabled', async function () {
|
|
21
|
+
this.timeout(12000)
|
|
22
|
+
const app = await boot({ cwd: root })
|
|
23
|
+
app.log.info('demo', 'saved%s', 'ok')
|
|
24
|
+
const logFile = path.join(root, 'data', 'log', 'bajo.log')
|
|
25
|
+
expect(fs.existsSync(logFile)).to.equal(true)
|
|
26
|
+
expect(fs.readFileSync(logFile, 'utf8')).to.include('saved')
|
|
27
|
+
})
|
|
28
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* global describe, it, beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
import fs from 'fs-extra'
|
|
4
|
+
import { expect } from 'chai'
|
|
5
|
+
import boot from '../../index.js'
|
|
6
|
+
import { makeRoot, cleanupRoot, writeBaseApp, writePlugin } from './_setup.js'
|
|
7
|
+
|
|
8
|
+
describe('integration aspect 07 - plugin data dir', () => {
|
|
9
|
+
let root
|
|
10
|
+
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
root = makeRoot('bajo-int-a07-')
|
|
13
|
+
await writeBaseApp(root, 'int-a07-plugin')
|
|
14
|
+
await writePlugin(root, 'int-a07-plugin', 'IntA07', "const d = this.app.getPluginDataDir(this.ns); this.app.lib.fs.writeFileSync(d + '/ready.txt', '1', 'utf8')")
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
afterEach(() => cleanupRoot(root))
|
|
18
|
+
|
|
19
|
+
it('creates plugin data dir and allows writes during plugin start', async function () {
|
|
20
|
+
this.timeout(12000)
|
|
21
|
+
const app = await boot({ cwd: root })
|
|
22
|
+
const d = app.getPluginDataDir('intA07Plugin')
|
|
23
|
+
expect(fs.existsSync(d + '/ready.txt')).to.equal(true)
|
|
24
|
+
})
|
|
25
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* global describe, it, beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai'
|
|
4
|
+
import boot from '../../index.js'
|
|
5
|
+
import { makeRoot, cleanupRoot, writeBaseApp, writePlugin } from './_setup.js'
|
|
6
|
+
|
|
7
|
+
describe('integration aspect 08 - plugin lookup', () => {
|
|
8
|
+
let root
|
|
9
|
+
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
root = makeRoot('bajo-int-a08-')
|
|
12
|
+
await writeBaseApp(root, 'int-a08-plugin')
|
|
13
|
+
await writePlugin(root, 'int-a08-plugin', 'IntA08', '')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
afterEach(() => cleanupRoot(root))
|
|
17
|
+
|
|
18
|
+
it('resolves plugin by namespace and package name', async function () {
|
|
19
|
+
this.timeout(12000)
|
|
20
|
+
const app = await boot({ cwd: root })
|
|
21
|
+
const byNs = app.getPlugin('intA08Plugin')
|
|
22
|
+
const byPkg = app.getPlugin('int-a08-plugin')
|
|
23
|
+
expect(byNs).to.equal(byPkg)
|
|
24
|
+
})
|
|
25
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/* global describe, it, beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
import { expect } from 'chai'
|
|
4
|
+
import boot from '../../index.js'
|
|
5
|
+
import { makeRoot, cleanupRoot, writeBaseApp, writePlugin } from './_setup.js'
|
|
6
|
+
|
|
7
|
+
describe('integration aspect 09 - config formats', () => {
|
|
8
|
+
let root
|
|
9
|
+
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
root = makeRoot('bajo-int-a09-')
|
|
12
|
+
await writeBaseApp(root, 'int-a09-plugin')
|
|
13
|
+
await writePlugin(root, 'int-a09-plugin', 'IntA09', '')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
afterEach(() => cleanupRoot(root))
|
|
17
|
+
|
|
18
|
+
it('provides config format registry from runtime handlers', async function () {
|
|
19
|
+
this.timeout(12000)
|
|
20
|
+
const app = await boot({ cwd: root })
|
|
21
|
+
const exts = app.getConfigFormats()
|
|
22
|
+
expect(exts).to.include('.json')
|
|
23
|
+
expect(exts).to.include('.yml')
|
|
24
|
+
expect(app.getConfigFormats(true)).to.include('json')
|
|
25
|
+
})
|
|
26
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* global describe, it, beforeEach, afterEach */
|
|
2
|
+
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import fs from 'fs-extra'
|
|
5
|
+
import { expect } from 'chai'
|
|
6
|
+
import boot from '../../index.js'
|
|
7
|
+
import { makeRoot, cleanupRoot, writeBaseApp, writePlugin } from './_setup.js'
|
|
8
|
+
|
|
9
|
+
describe('integration aspect 10 - hook execution', () => {
|
|
10
|
+
let root
|
|
11
|
+
let pluginDir
|
|
12
|
+
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
root = makeRoot('bajo-int-a10-')
|
|
15
|
+
await writeBaseApp(root, 'int-a10-plugin')
|
|
16
|
+
pluginDir = await writePlugin(root, 'int-a10-plugin', 'IntA10', "await this.app.lib.fs.writeFile(this.app.dir + '/plugin-start.txt', 'started', 'utf8')")
|
|
17
|
+
|
|
18
|
+
const hookDir = path.join(pluginDir, 'extend', 'bajo', 'hook')
|
|
19
|
+
await fs.ensureDir(hookDir)
|
|
20
|
+
await fs.writeFile(path.join(hookDir, 'bajo@after-boot.js'), `
|
|
21
|
+
export default {
|
|
22
|
+
level: 100,
|
|
23
|
+
handler: async function () {
|
|
24
|
+
await this.app.lib.fs.writeFile(this.app.dir + '/after-boot.txt', 'ok', 'utf8')
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
`, 'utf8')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
afterEach(() => cleanupRoot(root))
|
|
31
|
+
|
|
32
|
+
it('collects and runs plugin hook during boot lifecycle', async function () {
|
|
33
|
+
this.timeout(12000)
|
|
34
|
+
await boot({ cwd: root })
|
|
35
|
+
expect(fs.existsSync(path.join(root, 'plugin-start.txt'))).to.equal(true)
|
|
36
|
+
expect(fs.existsSync(path.join(root, 'after-boot.txt'))).to.equal(true)
|
|
37
|
+
})
|
|
38
|
+
})
|