ebely 0.0.2 → 0.0.4
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/Readme.md +34 -0
- package/bin/ebely.mjs +97 -2
- package/clone/tests/ebely/ebely.ts +21 -0
- package/clone/tests/ebely/generate-client.ts +3 -0
- package/clone/tests/ebely/generated.ts +552 -0
- package/clone/tests/ebely/handlers.ts +19 -0
- package/clone/tests/ebely/hooks.ts +28 -0
- package/clone/tests/ebely/userStore.ts +56 -0
- package/clone/tests/ebely/worldStore.ts +26 -0
- package/clone/tests/package.json +19 -0
- package/clone/tests/swagger.json +426 -0
- package/clone/tests/tests/test1.test.ts +62 -0
- package/clone/tests/tsconfig.json +4 -0
- package/package.json +5 -2
package/Readme.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
### Быстрый старт из шаблона
|
|
2
|
+
|
|
3
|
+
Склонировать готовый тест-проект (как `create-react-app`):
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npx ebely create my-tests # в новую/пустую папку
|
|
7
|
+
# или
|
|
8
|
+
npx ebely create # в текущую папку
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Затем:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
cd my-tests
|
|
15
|
+
pnpm install
|
|
16
|
+
# отредактируй ebely/ebely.ts (url, путь к swagger) под свой бэкенд
|
|
17
|
+
pnpm run client:generate
|
|
18
|
+
pnpm test
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Алгоритм действий для пользователя библиотекой
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
#### 1. Установить библиотеку
|
|
25
|
+
`pnpm add -D ebely`
|
|
26
|
+
|
|
27
|
+
#### 2. Один раз разложить скиллы в проект
|
|
28
|
+
`npx ebely skills`
|
|
29
|
+
|
|
30
|
+
#### 3. Открыть проект в Claude Code и вызвать настройку
|
|
31
|
+
`/ebely-setup ` # настроит конфиг, сгенерирует клиент, стор, хуки
|
|
32
|
+
|
|
33
|
+
#### 4. Дальше при написании любых тестов
|
|
34
|
+
`/ebely-write-tests ` # опишет сценарий — тесты напишутся сами
|
package/bin/ebely.mjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { cp, mkdir, readdir,
|
|
2
|
+
import { cp, mkdir, readdir, readFile, writeFile } from 'node:fs/promises'
|
|
3
3
|
import { existsSync } from 'node:fs'
|
|
4
|
-
import { dirname, join, resolve } from 'node:path'
|
|
4
|
+
import { basename, dirname, join, resolve, sep } from 'node:path'
|
|
5
5
|
import { fileURLToPath } from 'node:url'
|
|
6
6
|
|
|
7
7
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
8
8
|
const pkgRoot = resolve(__dirname, '..')
|
|
9
9
|
const skillsSrc = join(pkgRoot, 'skills')
|
|
10
|
+
const templateSrc = join(pkgRoot, 'clone', 'tests')
|
|
10
11
|
|
|
11
12
|
async function listSkills() {
|
|
12
13
|
const entries = await readdir(skillsSrc, { withFileTypes: true })
|
|
@@ -38,10 +39,101 @@ async function installSkills({ toUser }) {
|
|
|
38
39
|
)
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
const STANDALONE_TSCONFIG = `{
|
|
43
|
+
"compilerOptions": {
|
|
44
|
+
"target": "es2022",
|
|
45
|
+
"module": "ESNext",
|
|
46
|
+
"moduleResolution": "Bundler",
|
|
47
|
+
"lib": ["es2022", "dom", "dom.iterable"],
|
|
48
|
+
"types": ["node"],
|
|
49
|
+
"strict": true,
|
|
50
|
+
"noUncheckedIndexedAccess": true,
|
|
51
|
+
"esModuleInterop": true,
|
|
52
|
+
"skipLibCheck": true,
|
|
53
|
+
"verbatimModuleSyntax": true,
|
|
54
|
+
"resolveJsonModule": true,
|
|
55
|
+
"moduleDetection": "force",
|
|
56
|
+
"allowJs": true,
|
|
57
|
+
"noEmit": true
|
|
58
|
+
},
|
|
59
|
+
"include": ["ebely", "tests"]
|
|
60
|
+
}
|
|
61
|
+
`
|
|
62
|
+
|
|
63
|
+
async function ownVersion() {
|
|
64
|
+
const pkg = JSON.parse(await readFile(join(pkgRoot, 'package.json'), 'utf8'))
|
|
65
|
+
return pkg.version
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Имя npm-пакета из имени папки: lowercase, без недопустимых символов. */
|
|
69
|
+
function toPackageName(name) {
|
|
70
|
+
const cleaned = name
|
|
71
|
+
.toLowerCase()
|
|
72
|
+
.replace(/[^a-z0-9._-]+/g, '-')
|
|
73
|
+
.replace(/^[-_.]+|[-_.]+$/g, '')
|
|
74
|
+
return cleaned || 'ebely-tests'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function createProject({ dir }) {
|
|
78
|
+
const target = resolve(process.cwd(), dir ?? '.')
|
|
79
|
+
|
|
80
|
+
if (!existsSync(templateSrc)) {
|
|
81
|
+
console.error(`ebely: шаблон не найден по пути ${templateSrc}`)
|
|
82
|
+
process.exit(1)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Существующая непустая папка — отказ, чтобы не затереть чужие файлы.
|
|
86
|
+
if (existsSync(target)) {
|
|
87
|
+
const entries = await readdir(target)
|
|
88
|
+
if (entries.length > 0) {
|
|
89
|
+
console.error(
|
|
90
|
+
`ebely: папка «${target}» не пуста.\n` +
|
|
91
|
+
`Укажи несуществующую или пустую папку, либо очисти эту.`,
|
|
92
|
+
)
|
|
93
|
+
process.exit(1)
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
await mkdir(target, { recursive: true })
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Копируем шаблон целиком, кроме node_modules.
|
|
100
|
+
await cp(templateSrc, target, {
|
|
101
|
+
recursive: true,
|
|
102
|
+
filter: (src) => !src.split(sep).includes('node_modules'),
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
// package.json: имя по папке + реальная версия ebely вместо workspace:*.
|
|
106
|
+
const version = await ownVersion()
|
|
107
|
+
const pkgPath = join(target, 'package.json')
|
|
108
|
+
const pkg = JSON.parse(await readFile(pkgPath, 'utf8'))
|
|
109
|
+
pkg.name = toPackageName(basename(target))
|
|
110
|
+
pkg.dependencies = { ...pkg.dependencies, ebely: `^${version}` }
|
|
111
|
+
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
|
|
112
|
+
|
|
113
|
+
// Самодостаточный tsconfig (в шаблоне он расширяет конфиг монорепо).
|
|
114
|
+
await writeFile(join(target, 'tsconfig.json'), STANDALONE_TSCONFIG)
|
|
115
|
+
|
|
116
|
+
// Базовый .gitignore.
|
|
117
|
+
await writeFile(join(target, '.gitignore'), 'node_modules\n')
|
|
118
|
+
|
|
119
|
+
const shown = dir && dir !== '.' ? dir : '.'
|
|
120
|
+
console.log(
|
|
121
|
+
`\nГотово. Шаблон ebely создан в «${target}».\n\n` +
|
|
122
|
+
`Дальше:\n` +
|
|
123
|
+
(shown === '.' ? '' : ` cd ${shown}\n`) +
|
|
124
|
+
` pnpm install # установить зависимости\n` +
|
|
125
|
+
` # отредактируй ebely/ebely.ts (url, путь к swagger) под свой бэкенд\n` +
|
|
126
|
+
` pnpm run client:generate # сгенерировать типизированный клиент\n` +
|
|
127
|
+
` pnpm test # запустить пример тестов\n`,
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
41
131
|
function help() {
|
|
42
132
|
console.log(`ebely — CLI
|
|
43
133
|
|
|
44
134
|
Использование:
|
|
135
|
+
npx ebely create [dir] Склонировать шаблон тест-проекта в папку
|
|
136
|
+
(без аргумента или «.» — в текущую папку)
|
|
45
137
|
npx ebely skills Установить скиллы в .claude/skills проекта (рекомендуется)
|
|
46
138
|
npx ebely skills --user Установить скиллы глобально в ~/.claude/skills
|
|
47
139
|
npx ebely help Показать эту справку
|
|
@@ -54,6 +146,9 @@ function help() {
|
|
|
54
146
|
const [cmd, ...rest] = process.argv.slice(2)
|
|
55
147
|
|
|
56
148
|
switch (cmd) {
|
|
149
|
+
case 'create':
|
|
150
|
+
await createProject({ dir: rest.find((a) => !a.startsWith('-')) })
|
|
151
|
+
break
|
|
57
152
|
case 'skills':
|
|
58
153
|
await installSkills({ toUser: rest.includes('--user') })
|
|
59
154
|
break
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { EbelyConfig } from "ebely";
|
|
2
|
+
import { generateClient } from 'ebely'
|
|
3
|
+
import { UserStore } from "./userStore";
|
|
4
|
+
import { WorldStore } from "./worldStore";
|
|
5
|
+
import { hooks } from "./hooks";
|
|
6
|
+
|
|
7
|
+
export const ebely = {
|
|
8
|
+
userStore: UserStore, // внутренние переменные одного пользователя
|
|
9
|
+
worldStore: WorldStore, // внутренние переменные/сценарии всего приложения
|
|
10
|
+
|
|
11
|
+
url: 'http://localhost:3000', // url бекенда, который нужно тестировать
|
|
12
|
+
swagger: { pathToFile: 'swagger.json', }, // путь к swagger-схеме
|
|
13
|
+
generateClientTo: 'ebely/generated.ts', // путь к генерируемому клиенту
|
|
14
|
+
hooks, // before/after-хуки (объявлены в ./hooks.ts, типизированы)
|
|
15
|
+
mode: 'test', // test - не-2xx статусы не бросаются.
|
|
16
|
+
} satisfies EbelyConfig
|
|
17
|
+
|
|
18
|
+
// запустить в другом файле для генерации клиента
|
|
19
|
+
export const generateEbelyClient = async () => {
|
|
20
|
+
await generateClient(ebely)
|
|
21
|
+
}
|