@lytjs/test-utils 4.2.0 → 5.0.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @lytjs/test-utils
2
2
 
3
- Lyt.js 测试工具库 - 提供测试支持的工具函数和组件。
3
+ Lyt.js 统一测试框架 -- 轻量级测试运行器和断言库,纯原生实现,零外部依赖。
4
4
 
5
5
  ## 安装
6
6
 
@@ -13,188 +13,286 @@ pnpm add @lytjs/test-utils -D
13
13
 
14
14
  ## 特性
15
15
 
16
- - 📦 组件挂载工具
17
- - 🔍 元素查询
18
- - 🎯 事件触发
19
- - 💾 状态模拟
20
- - 🚀 零运行时依赖
16
+ - 轻量级测试运行器(describe/it/test/skip)
17
+ - 链式断言库(expect + .not 取反)
18
+ - 生命周期钩子(beforeEach/afterEach)
19
+ - 异步测试支持
20
+ - 彩色终端输出
21
+ - 失败日志自动保存
22
+ - 零外部依赖
21
23
 
22
24
  ## 快速开始
23
25
 
24
- ```javascript
25
- import { mount } from '@lytjs/test-utils';
26
- import { defineComponent, ref } from '@lytjs/core';
27
-
28
- const Counter = defineComponent({
29
- setup() {
30
- const count = ref(0);
31
- const increment = () => count.value++;
32
- return { count, increment };
33
- },
34
- template: `
35
- <div>
36
- <p>Count: {{ count }}</p>
37
- <button @click="increment">Increment</button>
38
- </div>
39
- `
40
- });
41
-
42
- test('increments counter', () => {
43
- const wrapper = mount(Counter);
44
- const button = wrapper.find('button');
45
- const p = wrapper.find('p');
46
-
47
- expect(p.text()).toBe('Count: 0');
48
- button.trigger('click');
49
- expect(p.text()).toBe('Count: 1');
50
- });
26
+ ```typescript
27
+ import { describe, it, expect, runAll } from '@lytjs/test-utils'
28
+
29
+ describe('我的模块', () => {
30
+ it('应该正常工作', () => {
31
+ expect(1 + 1).toBe(2)
32
+ })
33
+
34
+ it('应该支持深度比较', () => {
35
+ expect({ a: 1, b: 2 }).toEqual({ a: 1, b: 2 })
36
+ })
37
+
38
+ it('应该支持取反断言', () => {
39
+ expect(null).not.toBeTruthy()
40
+ })
41
+ })
42
+
43
+ runAll()
51
44
  ```
52
45
 
53
46
  ## API 参考
54
47
 
55
- ### mount
48
+ ### describe(name, fn)
56
49
 
57
- 挂载组件进行测试
50
+ 定义测试套件。
58
51
 
59
- ```javascript
60
- import { mount } from '@lytjs/test-utils';
52
+ ```typescript
53
+ import { describe } from '@lytjs/test-utils'
61
54
 
62
- const wrapper = mount(Component, {
63
- props: {},
64
- slots: {},
65
- attachTo: document.body
66
- });
55
+ describe('数学运算', () => {
56
+ // 在此注册测试用例
57
+ })
67
58
  ```
68
59
 
69
- ### shallowMount
60
+ ### it(name, fn) / test(name, fn)
61
+
62
+ 注册测试用例。`test` 是 `it` 的别名。
70
63
 
71
- 浅层挂载组件,不渲染子组件
64
+ ```typescript
65
+ import { it, test } from '@lytjs/test-utils'
72
66
 
73
- ```javascript
74
- import { shallowMount } from '@lytjs/test-utils';
67
+ it('加法运算', () => {
68
+ expect(1 + 1).toBe(2)
69
+ })
75
70
 
76
- const wrapper = shallowMount(Component);
71
+ // test it 的别名
72
+ test('减法运算', () => {
73
+ expect(3 - 1).toBe(2)
74
+ })
77
75
  ```
78
76
 
79
- ### createTestingApp
77
+ ### skip(name, fn)
78
+
79
+ 跳过测试用例(不会执行)。
80
+
81
+ ```typescript
82
+ import { skip } from '@lytjs/test-utils'
83
+
84
+ skip('待实现的测试', () => {
85
+ expect(true).toBe(false)
86
+ })
87
+ ```
88
+
89
+ ### beforeEach(fn) / afterEach(fn)
90
+
91
+ 注册前置/后置钩子,在每个测试用例执行前/后调用。
80
92
 
81
- 创建测试用的应用实例
93
+ ```typescript
94
+ import { describe, it, beforeEach, afterEach, expect } from '@lytjs/test-utils'
82
95
 
83
- ```javascript
84
- import { createTestingApp } from '@lytjs/test-utils';
96
+ let counter = 0
85
97
 
86
- const app = createTestingApp(Component);
98
+ describe('计数器', () => {
99
+ beforeEach(() => {
100
+ counter = 0
101
+ })
102
+
103
+ afterEach(() => {
104
+ counter = -1
105
+ })
106
+
107
+ it('初始值为 0', () => {
108
+ expect(counter).toBe(0)
109
+ })
110
+ })
87
111
  ```
88
112
 
89
- ### Wrapper API
113
+ ### expect(value)
114
+
115
+ 创建断言对象,支持链式调用。
116
+
117
+ #### 断言方法
90
118
 
91
119
  | 方法 | 说明 |
92
120
  |------|------|
93
- | `find(selector)` | 查找元素 |
94
- | `findAll(selector)` | 查找所有元素 |
95
- | `trigger(event)` | 触发事件 |
96
- | `text()` | 获取文本内容 |
97
- | `html()` | 获取 HTML |
98
- | `isVisible()` | 检查是否可见 |
99
- | `isEnabled()` | 检查是否启用 |
100
- | `attributes()` | 获取属性 |
101
- | `classes()` | 获取类名 |
102
- | `setProps(props)` | 设置组件属性 |
103
- | `vm` | 访问组件实例 |
121
+ | `toBe(expected)` | 严格相等 (`===`) |
122
+ | `toEqual(expected)` | 深度相等 |
123
+ | `toBeTruthy()` | 断言为真值 |
124
+ | `toBeFalsy()` | 断言为假值 |
125
+ | `toBeNull()` | 断言为 null |
126
+ | `toBeUndefined()` | 断言为 undefined |
127
+ | `toBeDefined()` | 断言已定义(非 undefined) |
128
+ | `toThrow(message?)` | 断言函数抛出异常 |
129
+ | `toContain(item)` | 断言数组/字符串包含指定项 |
130
+ | `toBeGreaterThan(n)` | 断言数值大于 n |
131
+ | `toBeLessThan(n)` | 断言数值小于 n |
132
+ | `toBeGreaterThanOrEqual(n)` | 断言数值大于等于 n |
133
+ | `toBeLessThanOrEqual(n)` | 断言数值小于等于 n |
134
+ | `toHaveLength(n)` | 断言数组/字符串长度为 n |
135
+
136
+ #### 取反断言
137
+
138
+ 所有断言方法均可通过 `.not` 取反:
139
+
140
+ ```typescript
141
+ expect(1).not.toBe(2)
142
+ expect([1, 2]).not.toContain(3)
143
+ ```
144
+
145
+ ### runAll()
146
+
147
+ 运行所有已注册的测试套件,输出格式化报告。
148
+
149
+ ```typescript
150
+ import { runAll } from '@lytjs/test-utils'
151
+
152
+ const result = await runAll()
153
+ // result: { total, passed, failed, skipped, results }
154
+ ```
155
+
156
+ ### waitFor(ms)
157
+
158
+ 等待指定毫秒数,用于异步测试。
159
+
160
+ ```typescript
161
+ import { waitFor } from '@lytjs/test-utils'
162
+
163
+ it('异步操作', async () => {
164
+ await waitFor(100)
165
+ expect(true).toBe(true)
166
+ })
167
+ ```
168
+
169
+ ### deepEqual(a, b)
170
+
171
+ 深度比较两个值是否相等。
172
+
173
+ ```typescript
174
+ import { deepEqual } from '@lytjs/test-utils'
175
+
176
+ const result = deepEqual({ a: [1, 2] }, { a: [1, 2] })
177
+ // result: true
178
+ ```
104
179
 
105
180
  ## 示例
106
181
 
107
- ### 测试组件属性
182
+ ### 基础测试
183
+
184
+ ```typescript
185
+ import { describe, it, expect, runAll } from '@lytjs/test-utils'
186
+
187
+ describe('字符串操作', () => {
188
+ it('应该正确拼接字符串', () => {
189
+ const result = 'hello' + ' ' + 'world'
190
+ expect(result).toBe('hello world')
191
+ })
108
192
 
109
- ```javascript
110
- import { mount } from '@lytjs/test-utils';
111
- import { defineComponent } from '@lytjs/core';
193
+ it('应该正确获取长度', () => {
194
+ expect('hello').toHaveLength(5)
195
+ })
112
196
 
113
- const Greeting = defineComponent({
114
- props: ['name'],
115
- template: '<p>Hello, {{ name }}!</p>'
116
- });
197
+ it('应该包含子串', () => {
198
+ expect('hello world').toContain('world')
199
+ })
200
+ })
117
201
 
118
- test('renders with name', () => {
119
- const wrapper = mount(Greeting, {
120
- props: { name: 'Alice' }
121
- });
122
- expect(wrapper.text()).toBe('Hello, Alice!');
123
- });
202
+ runAll()
124
203
  ```
125
204
 
126
- ### 测试事件
205
+ ### 异步测试
127
206
 
128
- ```javascript
129
- import { mount } from '@lytjs/test-utils';
130
- import { defineComponent } from '@lytjs/core';
207
+ ```typescript
208
+ import { describe, it, expect, waitFor, runAll } from '@lytjs/test-utils'
131
209
 
132
- const Button = defineComponent({
133
- emits: ['click'],
134
- template: '<button @click="$emit('click')">Click</button>'
135
- });
210
+ describe('异步操作', () => {
211
+ it('应该支持 async/await', async () => {
212
+ await waitFor(50)
213
+ expect(true).toBe(true)
214
+ })
136
215
 
137
- test('emits click event', () => {
138
- const wrapper = mount(Button);
139
- const button = wrapper.find('button');
140
- button.trigger('click');
141
- expect(wrapper.emitted('click')).toBeTruthy();
142
- });
216
+ it('应该处理 Promise', async () => {
217
+ const promise = Promise.resolve(42)
218
+ const result = await promise
219
+ expect(result).toBe(42)
220
+ })
221
+ })
222
+
223
+ runAll()
143
224
  ```
144
225
 
145
- ### 测试插槽
226
+ ### 错误处理测试
227
+
228
+ ```typescript
229
+ import { describe, it, expect, runAll } from '@lytjs/test-utils'
146
230
 
147
- ```javascript
148
- import { mount } from '@lytjs/test-utils';
149
- import { defineComponent } from '@lytjs/core';
231
+ describe('错误处理', () => {
232
+ it('应该抛出异常', () => {
233
+ expect(() => {
234
+ throw new Error('test error')
235
+ }).toThrow('test error')
236
+ })
150
237
 
151
- const Card = defineComponent({
152
- template: '<div><slot /></div>'
153
- });
238
+ it('应该断言不抛出异常', () => {
239
+ expect(() => {
240
+ // 不抛出异常
241
+ }).not.toThrow()
242
+ })
243
+ })
154
244
 
155
- test('renders slot content', () => {
156
- const wrapper = mount(Card, {
157
- slots: {
158
- default: '<p>Hello</p>'
159
- }
160
- });
161
- expect(wrapper.text()).toBe('Hello');
162
- });
245
+ runAll()
163
246
  ```
164
247
 
165
- ### 测试响应式数据
248
+ ### 数值比较
249
+
250
+ ```typescript
251
+ import { describe, it, expect, runAll } from '@lytjs/test-utils'
166
252
 
167
- ```javascript
168
- import { mount } from '@lytjs/test-utils';
169
- import { defineComponent, ref } from '@lytjs/core';
253
+ describe('数值比较', () => {
254
+ it('应该支持大于比较', () => {
255
+ expect(10).toBeGreaterThan(5)
256
+ })
170
257
 
171
- const Form = defineComponent({
172
- setup() {
173
- const input = ref('');
174
- return { input };
175
- },
176
- template: '<input v-model="input" />'
177
- });
258
+ it('应该支持小于比较', () => {
259
+ expect(3).toBeLessThan(8)
260
+ })
178
261
 
179
- test('updates input value', async () => {
180
- const wrapper = mount(Form);
181
- const input = wrapper.find('input');
182
- input.setValue('Hello');
183
- expect(wrapper.vm.input).toBe('Hello');
184
- });
262
+ it('应该支持大于等于', () => {
263
+ expect(5).toBeGreaterThanOrEqual(5)
264
+ })
265
+
266
+ it('应该支持小于等于', () => {
267
+ expect(5).toBeLessThanOrEqual(5)
268
+ })
269
+ })
270
+
271
+ runAll()
185
272
  ```
186
273
 
187
- ## 性能
274
+ ## 运行输出
275
+
276
+ 运行 `runAll()` 后,终端会输出彩色格式化的测试报告:
188
277
 
189
- - 轻量级测试工具
190
- - 零运行时依赖
191
- - 快速的组件挂载
192
- - 高效的测试执行
278
+ ```
279
+ === Lyt.js 测试运行器 ===
280
+
281
+ 我的模块
282
+ [PASS] 应该正常工作
283
+ [PASS] 应该支持深度比较
284
+
285
+ === 测试结果 ===
286
+ 总计: 2
287
+ 通过: 2
288
+
289
+ 所有测试通过!
290
+ ```
193
291
 
194
292
  ## 兼容性
195
293
 
196
294
  - Node.js >= 18.0.0
197
- - 支持主流测试框架(Jest、Vitest)
295
+ - TypeScript 5.0+
198
296
 
199
297
  ## License
200
298
 
package/dist/index.cjs CHANGED
@@ -1,7 +1 @@
1
- var j=Object.create;var $=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var R=Object.getPrototypeOf,x=Object.prototype.hasOwnProperty;var L=(s,t)=>{for(var e in t)$(s,e,{get:t[e],enumerable:!0})},_=(s,t,e,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of B(t))!x.call(s,i)&&i!==e&&$(s,i,{get:()=>t[i],enumerable:!(r=A(t,i))||r.enumerable});return s};var T=(s,t,e)=>(e=s!=null?j(R(s)):{},_(t||!s||!s.__esModule?$(e,"default",{value:s,enumerable:!0}):e,s)),C=s=>_($({},"__esModule",{value:!0}),s);var D={};L(D,{Assertion:()=>p,afterEach:()=>G,beforeEach:()=>U,deepEqual:()=>d,describe:()=>N,expect:()=>J,it:()=>k,runAll:()=>W,skip:()=>I,test:()=>q,waitFor:()=>K});module.exports=C(D);var S=T(require("fs")),F=T(require("path")),w=[];var a=null;function N(s,t){let e={name:s,tests:[],beforeEachFn:[],afterEachFn:[]};w.push(e);let r=a;a=e;try{t()}finally{a=r}}function k(s,t){if(!a)throw new Error(`it() \u5FC5\u987B\u5728 describe() \u5185\u90E8\u8C03\u7528: "${s}"`);a.tests.push({name:s,fn:t})}function q(s,t){k(s,t)}function I(s,t){if(!a)throw new Error(`skip() \u5FC5\u987B\u5728 describe() \u5185\u90E8\u8C03\u7528: "${s}"`);a.tests.push({name:s,fn:()=>{throw new g(s)}})}var g=class extends Error{constructor(t){super(`SKIP: ${t}`),this.name="SkipError"}};function U(s){if(!a)throw new Error("beforeEach() \u5FC5\u987B\u5728 describe() \u5185\u90E8\u8C03\u7528");a.beforeEachFn.push(s)}function G(s){if(!a)throw new Error("afterEach() \u5FC5\u987B\u5728 describe() \u5185\u90E8\u8C03\u7528");a.afterEachFn.push(s)}var p=class{constructor(t){this.negated=!1;this.actual=t}get not(){return this.negated=!this.negated,this}toBe(t){let e=this.negated?!Object.is(this.actual,t):Object.is(this.actual,t);this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u7B49\u4E8E ${this._fmt(t)}`)}toEqual(t){let e=this.negated?!d(this.actual,t):d(this.actual,t);this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u6DF1\u5EA6\u7B49\u4E8E ${this._fmt(t)}`)}toBeTruthy(){let t=this.negated?!this.actual:!!this.actual;this._assert(t,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u4E3A\u771F\u503C`)}toBeFalsy(){let t=this.negated?!!this.actual:!this.actual;this._assert(t,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u4E3A\u5047\u503C`)}toBeNull(){let t=this.negated?this.actual!==null:this.actual===null;this._assert(t,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u4E3A null`)}toBeUndefined(){let t=this.negated?this.actual!==void 0:this.actual===void 0;this._assert(t,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u4E3A undefined`)}toBeDefined(){let t=this.negated?this.actual===void 0:this.actual!==void 0;this._assert(t,`\u671F\u671B\u503C ${this.negated?"\u4E0D":""}\u5DF2\u5B9A\u4E49`)}toThrow(t){let e=!1,r="";try{if(typeof this.actual=="function")this.actual();else throw new Error("toThrow() \u7684\u5B9E\u9645\u503C\u5FC5\u987B\u662F\u51FD\u6570")}catch(u){e=!0,r=u.message||String(u)}let i=this.negated?!e:e;i&&e&&t&&!this.negated&&(i=r.includes(t)),this._assert(i,`\u671F\u671B\u51FD\u6570${this.negated?"\u4E0D":""}\u629B\u51FA\u5F02\u5E38${t?` (\u5305\u542B "${t}")`:""}`)}toContain(t){let e;typeof this.actual=="string"?e=this.actual.includes(t):Array.isArray(this.actual)?e=this.actual.some(r=>d(r,t)):e=!1,this.negated&&(e=!e),this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5305\u542B ${this._fmt(t)}`)}toBeGreaterThan(t){let e=this.negated?!(this.actual>t):this.actual>t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5927\u4E8E ${t}`)}toBeLessThan(t){let e=this.negated?!(this.actual<t):this.actual<t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5C0F\u4E8E ${t}`)}toBeGreaterThanOrEqual(t){let e=this.negated?!(this.actual>=t):this.actual>=t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5927\u4E8E\u7B49\u4E8E ${t}`)}toBeLessThanOrEqual(t){let e=this.negated?!(this.actual<=t):this.actual<=t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5C0F\u4E8E\u7B49\u4E8E ${t}`)}toHaveLength(t){var i;let e=(i=this.actual)==null?void 0:i.length,r=this.negated?e!==t:e===t;this._assert(r,`\u671F\u671B\u957F\u5EA6\u4E3A ${t}\uFF0C\u5B9E\u9645\u4E3A ${e}`)}_assert(t,e){if(!t)throw new v(e)}_fmt(t){if(t===null)return"null";if(t===void 0)return"undefined";if(typeof t=="string")return`"${t}"`;if(typeof t=="function")return"[Function]";if(Array.isArray(t))try{return JSON.stringify(t)}catch(e){return"[Array]"}if(typeof t=="object")try{return JSON.stringify(t)}catch(e){return"[Object]"}return String(t)}},v=class extends Error{constructor(t){super(t),this.name="AssertionError"}};function J(s){return new p(s)}function d(s,t){if(Object.is(s,t))return!0;if(s===null||t===null||typeof s!=typeof t)return!1;if(typeof s=="object"){let e=Object.keys(s),r=Object.keys(t);if(e.length!==r.length)return!1;for(let i of e)if(!Object.prototype.hasOwnProperty.call(t,i)||!d(s[i],t[i]))return!1;return!0}return!1}function K(s){return new Promise(t=>setTimeout(t,s))}var n={reset:"\x1B[0m",green:"\x1B[32m",red:"\x1B[31m",yellow:"\x1B[33m",cyan:"\x1B[36m",gray:"\x1B[90m",bold:"\x1B[1m"};async function W(){let s=[],t=0,e=0,r=0,i=0,u=[];console.log(`
2
- ${n.bold}${n.cyan}=== Lyt.js \u6D4B\u8BD5\u8FD0\u884C\u5668 ===${n.reset}
3
- `);for(let l of w){console.log(`${n.bold}${l.name}${n.reset}`);for(let h of l.tests){t++;let O=performance.now();if(l.beforeEachFn)for(let o of l.beforeEachFn)try{o()}catch(b){console.warn(` ${n.yellow}[WARN] beforeEach \u51FA\u9519: ${b.message}${n.reset}`)}let f="passed",c;try{let o=h.fn();o instanceof Promise&&await o}catch(o){o instanceof g?(f="skipped",i++):(f="failed",r++,c=o,u.push(`Suite: ${l.name}, Test: ${h.name}, Error: ${(c==null?void 0:c.message)||"Unknown error"}`))}let m=performance.now()-O;if(l.afterEachFn)for(let o of l.afterEachFn)try{o()}catch(b){console.warn(` ${n.yellow}[WARN] afterEach \u51FA\u9519: ${b.message}${n.reset}`)}f==="passed"&&e++;let P={name:h.name,suite:l.name,status:f,error:c,duration:m};s.push(P);let y=m<1?"":` ${n.gray}(${m.toFixed(1)}ms)${n.reset}`;f==="passed"?console.log(` ${n.green}[PASS]${n.reset} ${h.name}${y}`):f==="skipped"?console.log(` ${n.yellow}[SKIP]${n.reset} ${h.name}${y}`):(console.log(` ${n.red}[FAIL]${n.reset} ${h.name}${y}`),c&&console.log(` ${n.red}${c.message}${n.reset}`))}console.log("")}let E=F.join(process.cwd(),"failed-tests.log");return S.writeFileSync(E,u.join(`
4
- `),"utf8"),console.log(`\u5931\u8D25\u7684\u6D4B\u8BD5\u5DF2\u4FDD\u5B58\u5230 ${E}, \u5171 ${u.length} \u4E2A\u5931\u8D25
5
- `),console.log(`${n.bold}=== \u6D4B\u8BD5\u7ED3\u679C ===${n.reset}`),console.log(` \u603B\u8BA1: ${t}`),console.log(` ${n.green}\u901A\u8FC7: ${e}${n.reset}`),r>0&&console.log(` ${n.red}\u5931\u8D25: ${r}${n.reset}`),i>0&&console.log(` ${n.yellow}\u8DF3\u8FC7: ${i}${n.reset}`),console.log(""),console.log(r===0?`${n.green}${n.bold}\u6240\u6709\u6D4B\u8BD5\u901A\u8FC7!${n.reset}
6
- `:`${n.red}${n.bold}\u5B58\u5728\u5931\u8D25\u7684\u6D4B\u8BD5!${n.reset}
7
- `),w.length=0,{total:t,passed:e,failed:r,skipped:i,results:s}}0&&(module.exports={Assertion,afterEach,beforeEach,deepEqual,describe,expect,it,runAll,skip,test,waitFor});
1
+ "use strict";var d=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var y=Object.prototype.hasOwnProperty;var b=(i,t)=>{for(var e in t)d(i,e,{get:t[e],enumerable:!0})},_=(i,t,e,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of $(t))!y.call(i,s)&&s!==e&&d(i,s,{get:()=>t[s],enumerable:!(a=m(t,s))||a.enumerable});return i};var v=i=>_(d({},"__esModule",{value:!0}),i);var B={};b(B,{Assertion:()=>l,afterEach:()=>T,beforeEach:()=>k,deepEqual:()=>u,describe:()=>E,expect:()=>O,it:()=>g,runAll:()=>j,skip:()=>x,test:()=>w,waitFor:()=>F});module.exports=v(B);var p=[],o=null;function E(i,t){let e={name:i,tests:[],beforeEachFn:[],afterEachFn:[]},a=o;o=e,t(),o=a,p.push(e)}function g(i,t){if(!o)throw new Error("it() must be called inside describe()");o.tests.push({name:i,fn:t})}var w=g;function x(i,t){if(!o)throw new Error("skip() must be called inside describe()");o.tests.push({name:i,fn:t,skipped:!0})}function k(i){var t;(t=o==null?void 0:o.beforeEachFn)==null||t.push(i)}function T(i){var t;(t=o==null?void 0:o.afterEachFn)==null||t.push(i)}function O(i){return new l(i)}function u(i,t){if(Object.is(i,t))return!0;if(i===null||t===null||typeof i!=typeof t)return!1;if(typeof i=="object"){let e=Object.keys(i),a=Object.keys(t);if(e.length!==a.length)return!1;for(let s of e)if(!Object.prototype.hasOwnProperty.call(t,s)||!u(i[s],t[s]))return!1;return!0}return!1}function F(i){return new Promise(t=>setTimeout(t,i))}async function j(){let i=[],t=0,e=0,a=0;for(let s of p)for(let n of s.tests){if(n.skipped){a++,i.push({name:n.name,suite:s.name,status:"skipped",duration:0}),console.log(` \x1B[33m\u2298 SKIP\x1B[0m ${s.name} > ${n.name}`);continue}let r=Date.now();try{if(s.beforeEachFn)for(let f of s.beforeEachFn)f();let h=n.fn();if(h instanceof Promise&&await h,s.afterEachFn)for(let f of s.afterEachFn)f();let c=Date.now()-r;t++,i.push({name:n.name,suite:s.name,status:"passed",duration:c}),console.log(` \x1B[32m\u2713 PASS\x1B[0m ${s.name} > ${n.name} (${c}ms)`)}catch(h){let c=Date.now()-r;e++,i.push({name:n.name,suite:s.name,status:"failed",error:h,duration:c}),console.log(` \x1B[31m\u2717 FAIL\x1B[0m ${s.name} > ${n.name}`),console.log(` \x1B[31m${h.message}\x1B[0m`)}}return p.length=0,console.log(""),console.log(`--- \u6D4B\u8BD5\u7ED3\u679C: ${t} \u901A\u8FC7, ${e} \u5931\u8D25, ${a} \u8DF3\u8FC7, \u5171 ${t+e+a} \u4E2A ---`),console.log(""),{total:t+e+a,passed:t,failed:e,skipped:a,results:i}}var l=class{constructor(t){this.negated=!1;this.actual=t}get not(){return this.negated=!this.negated,this}toBe(t){let e=this.negated?!Object.is(this.actual,t):Object.is(this.actual,t);this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u7B49\u4E8E ${this._fmt(t)}`)}toEqual(t){let e=this.negated?!u(this.actual,t):u(this.actual,t);this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u6DF1\u5EA6\u7B49\u4E8E ${this._fmt(t)}`)}toBeTruthy(){let t=this.negated?!this.actual:!!this.actual;this._assert(t,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u4E3A\u771F\u503C`)}toBeFalsy(){let t=this.negated?!!this.actual:!this.actual;this._assert(t,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u4E3A\u5047\u503C`)}toBeNull(){let t=this.negated?this.actual!==null:this.actual===null;this._assert(t,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u4E3A null`)}toBeUndefined(){let t=this.negated?this.actual!==void 0:this.actual===void 0;this._assert(t,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u4E3A undefined`)}toBeDefined(){let t=this.negated?this.actual===void 0:this.actual!==void 0;this._assert(t,`\u671F\u671B\u503C ${this.negated?"\u4E0D":""}\u5DF2\u5B9A\u4E49`)}toThrow(t){let e=!1,a="";try{if(typeof this.actual=="function")this.actual();else throw new Error("toThrow() \u7684\u5B9E\u9645\u503C\u5FC5\u987B\u662F\u51FD\u6570")}catch(n){e=!0,a=n.message||String(n)}let s=this.negated?!e:e;s&&e&&t&&!this.negated&&(s=a.includes(t)),this._assert(s,`\u671F\u671B\u51FD\u6570${this.negated?"\u4E0D":""}\u629B\u51FA\u5F02\u5E38${t?` (\u5305\u542B "${t}")`:""}`)}toContain(t){let e;typeof this.actual=="string"?e=this.actual.includes(t):Array.isArray(this.actual)?e=this.actual.some(a=>u(a,t)):e=!1,this.negated&&(e=!e),this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5305\u542B ${this._fmt(t)}`)}toBeGreaterThan(t){let e=this.negated?!(this.actual>t):this.actual>t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5927\u4E8E ${t}`)}toBeLessThan(t){let e=this.negated?!(this.actual<t):this.actual<t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5C0F\u4E8E ${t}`)}toBeGreaterThanOrEqual(t){let e=this.negated?!(this.actual>=t):this.actual>=t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5927\u4E8E\u7B49\u4E8E ${t}`)}toBeLessThanOrEqual(t){let e=this.negated?!(this.actual<=t):this.actual<=t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5C0F\u4E8E\u7B49\u4E8E ${t}`)}toHaveLength(t){var s;let e=(s=this.actual)==null?void 0:s.length,a=this.negated?e!==t:e===t;this._assert(a,`\u671F\u671B\u957F\u5EA6\u4E3A ${t}\uFF0C\u5B9E\u9645\u4E3A ${e}`)}toBeInstanceOf(t){let e=this.negated?!(this.actual instanceof t):this.actual instanceof t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u662F ${t.name} \u7684\u5B9E\u4F8B`)}toHaveProperty(t,e){let a=t.split("."),s=this.actual,n=!0;for(let r of a){if(s==null||typeof s!="object"){n=!1;break}s=s[r]}if(e!==void 0){let r=this.negated?!(n&&Object.is(s,e)):n&&Object.is(s,e);this._assert(r,`\u671F\u671B\u5BF9\u8C61${this.negated?"\u4E0D":""}\u5305\u542B\u5C5E\u6027 "${t}" \u503C\u4E3A ${this._fmt(e)}`)}else{let r=this.negated?!n:n;this._assert(r,`\u671F\u671B\u5BF9\u8C61${this.negated?"\u4E0D":""}\u5305\u542B\u5C5E\u6027 "${t}"`)}}_assert(t,e){if(!t)throw new Error(e)}_fmt(t){if(t===null)return"null";if(t===void 0)return"undefined";if(typeof t=="string")return`"${t}"`;if(typeof t=="function")return"[Function]";if(Array.isArray(t))try{return JSON.stringify(t)}catch(e){return"[Array]"}if(typeof t=="object")try{return JSON.stringify(t)}catch(e){return"[Object]"}return String(t)}};0&&(module.exports={Assertion,afterEach,beforeEach,deepEqual,describe,expect,it,runAll,skip,test,waitFor});
package/dist/index.mjs CHANGED
@@ -1,7 +1 @@
1
- import*as E from"fs";import*as _ from"path";var y=[];var a=null;function k(n,t){let e={name:n,tests:[],beforeEachFn:[],afterEachFn:[]};y.push(e);let i=a;a=e;try{t()}finally{a=i}}function F(n,t){if(!a)throw new Error(`it() \u5FC5\u987B\u5728 describe() \u5185\u90E8\u8C03\u7528: "${n}"`);a.tests.push({name:n,fn:t})}function O(n,t){F(n,t)}function P(n,t){if(!a)throw new Error(`skip() \u5FC5\u987B\u5728 describe() \u5185\u90E8\u8C03\u7528: "${n}"`);a.tests.push({name:n,fn:()=>{throw new $(n)}})}var $=class extends Error{constructor(t){super(`SKIP: ${t}`),this.name="SkipError"}};function j(n){if(!a)throw new Error("beforeEach() \u5FC5\u987B\u5728 describe() \u5185\u90E8\u8C03\u7528");a.beforeEachFn.push(n)}function A(n){if(!a)throw new Error("afterEach() \u5FC5\u987B\u5728 describe() \u5185\u90E8\u8C03\u7528");a.afterEachFn.push(n)}var b=class{constructor(t){this.negated=!1;this.actual=t}get not(){return this.negated=!this.negated,this}toBe(t){let e=this.negated?!Object.is(this.actual,t):Object.is(this.actual,t);this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u7B49\u4E8E ${this._fmt(t)}`)}toEqual(t){let e=this.negated?!d(this.actual,t):d(this.actual,t);this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u6DF1\u5EA6\u7B49\u4E8E ${this._fmt(t)}`)}toBeTruthy(){let t=this.negated?!this.actual:!!this.actual;this._assert(t,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u4E3A\u771F\u503C`)}toBeFalsy(){let t=this.negated?!!this.actual:!this.actual;this._assert(t,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u4E3A\u5047\u503C`)}toBeNull(){let t=this.negated?this.actual!==null:this.actual===null;this._assert(t,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u4E3A null`)}toBeUndefined(){let t=this.negated?this.actual!==void 0:this.actual===void 0;this._assert(t,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u4E3A undefined`)}toBeDefined(){let t=this.negated?this.actual===void 0:this.actual!==void 0;this._assert(t,`\u671F\u671B\u503C ${this.negated?"\u4E0D":""}\u5DF2\u5B9A\u4E49`)}toThrow(t){let e=!1,i="";try{if(typeof this.actual=="function")this.actual();else throw new Error("toThrow() \u7684\u5B9E\u9645\u503C\u5FC5\u987B\u662F\u51FD\u6570")}catch(u){e=!0,i=u.message||String(u)}let r=this.negated?!e:e;r&&e&&t&&!this.negated&&(r=i.includes(t)),this._assert(r,`\u671F\u671B\u51FD\u6570${this.negated?"\u4E0D":""}\u629B\u51FA\u5F02\u5E38${t?` (\u5305\u542B "${t}")`:""}`)}toContain(t){let e;typeof this.actual=="string"?e=this.actual.includes(t):Array.isArray(this.actual)?e=this.actual.some(i=>d(i,t)):e=!1,this.negated&&(e=!e),this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5305\u542B ${this._fmt(t)}`)}toBeGreaterThan(t){let e=this.negated?!(this.actual>t):this.actual>t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5927\u4E8E ${t}`)}toBeLessThan(t){let e=this.negated?!(this.actual<t):this.actual<t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5C0F\u4E8E ${t}`)}toBeGreaterThanOrEqual(t){let e=this.negated?!(this.actual>=t):this.actual>=t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5927\u4E8E\u7B49\u4E8E ${t}`)}toBeLessThanOrEqual(t){let e=this.negated?!(this.actual<=t):this.actual<=t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5C0F\u4E8E\u7B49\u4E8E ${t}`)}toHaveLength(t){var r;let e=(r=this.actual)==null?void 0:r.length,i=this.negated?e!==t:e===t;this._assert(i,`\u671F\u671B\u957F\u5EA6\u4E3A ${t}\uFF0C\u5B9E\u9645\u4E3A ${e}`)}_assert(t,e){if(!t)throw new w(e)}_fmt(t){if(t===null)return"null";if(t===void 0)return"undefined";if(typeof t=="string")return`"${t}"`;if(typeof t=="function")return"[Function]";if(Array.isArray(t))try{return JSON.stringify(t)}catch(e){return"[Array]"}if(typeof t=="object")try{return JSON.stringify(t)}catch(e){return"[Object]"}return String(t)}},w=class extends Error{constructor(t){super(t),this.name="AssertionError"}};function B(n){return new b(n)}function d(n,t){if(Object.is(n,t))return!0;if(n===null||t===null||typeof n!=typeof t)return!1;if(typeof n=="object"){let e=Object.keys(n),i=Object.keys(t);if(e.length!==i.length)return!1;for(let r of e)if(!Object.prototype.hasOwnProperty.call(t,r)||!d(n[r],t[r]))return!1;return!0}return!1}function R(n){return new Promise(t=>setTimeout(t,n))}var s={reset:"\x1B[0m",green:"\x1B[32m",red:"\x1B[31m",yellow:"\x1B[33m",cyan:"\x1B[36m",gray:"\x1B[90m",bold:"\x1B[1m"};async function x(){let n=[],t=0,e=0,i=0,r=0,u=[];console.log(`
2
- ${s.bold}${s.cyan}=== Lyt.js \u6D4B\u8BD5\u8FD0\u884C\u5668 ===${s.reset}
3
- `);for(let l of y){console.log(`${s.bold}${l.name}${s.reset}`);for(let h of l.tests){t++;let T=performance.now();if(l.beforeEachFn)for(let o of l.beforeEachFn)try{o()}catch(m){console.warn(` ${s.yellow}[WARN] beforeEach \u51FA\u9519: ${m.message}${s.reset}`)}let f="passed",c;try{let o=h.fn();o instanceof Promise&&await o}catch(o){o instanceof $?(f="skipped",r++):(f="failed",i++,c=o,u.push(`Suite: ${l.name}, Test: ${h.name}, Error: ${(c==null?void 0:c.message)||"Unknown error"}`))}let g=performance.now()-T;if(l.afterEachFn)for(let o of l.afterEachFn)try{o()}catch(m){console.warn(` ${s.yellow}[WARN] afterEach \u51FA\u9519: ${m.message}${s.reset}`)}f==="passed"&&e++;let S={name:h.name,suite:l.name,status:f,error:c,duration:g};n.push(S);let p=g<1?"":` ${s.gray}(${g.toFixed(1)}ms)${s.reset}`;f==="passed"?console.log(` ${s.green}[PASS]${s.reset} ${h.name}${p}`):f==="skipped"?console.log(` ${s.yellow}[SKIP]${s.reset} ${h.name}${p}`):(console.log(` ${s.red}[FAIL]${s.reset} ${h.name}${p}`),c&&console.log(` ${s.red}${c.message}${s.reset}`))}console.log("")}let v=_.join(process.cwd(),"failed-tests.log");return E.writeFileSync(v,u.join(`
4
- `),"utf8"),console.log(`\u5931\u8D25\u7684\u6D4B\u8BD5\u5DF2\u4FDD\u5B58\u5230 ${v}, \u5171 ${u.length} \u4E2A\u5931\u8D25
5
- `),console.log(`${s.bold}=== \u6D4B\u8BD5\u7ED3\u679C ===${s.reset}`),console.log(` \u603B\u8BA1: ${t}`),console.log(` ${s.green}\u901A\u8FC7: ${e}${s.reset}`),i>0&&console.log(` ${s.red}\u5931\u8D25: ${i}${s.reset}`),r>0&&console.log(` ${s.yellow}\u8DF3\u8FC7: ${r}${s.reset}`),console.log(""),console.log(i===0?`${s.green}${s.bold}\u6240\u6709\u6D4B\u8BD5\u901A\u8FC7!${s.reset}
6
- `:`${s.red}${s.bold}\u5B58\u5728\u5931\u8D25\u7684\u6D4B\u8BD5!${s.reset}
7
- `),y.length=0,{total:t,passed:e,failed:i,skipped:r,results:n}}export{b as Assertion,A as afterEach,j as beforeEach,d as deepEqual,k as describe,B as expect,F as it,x as runAll,P as skip,O as test,R as waitFor};
1
+ var f=[],o=null;function g(i,t){let e={name:i,tests:[],beforeEachFn:[],afterEachFn:[]},a=o;o=e,t(),o=a,f.push(e)}function p(i,t){if(!o)throw new Error("it() must be called inside describe()");o.tests.push({name:i,fn:t})}var m=p;function $(i,t){if(!o)throw new Error("skip() must be called inside describe()");o.tests.push({name:i,fn:t,skipped:!0})}function y(i){var t;(t=o==null?void 0:o.beforeEachFn)==null||t.push(i)}function b(i){var t;(t=o==null?void 0:o.afterEachFn)==null||t.push(i)}function _(i){return new d(i)}function c(i,t){if(Object.is(i,t))return!0;if(i===null||t===null||typeof i!=typeof t)return!1;if(typeof i=="object"){let e=Object.keys(i),a=Object.keys(t);if(e.length!==a.length)return!1;for(let s of e)if(!Object.prototype.hasOwnProperty.call(t,s)||!c(i[s],t[s]))return!1;return!0}return!1}function v(i){return new Promise(t=>setTimeout(t,i))}async function E(){let i=[],t=0,e=0,a=0;for(let s of f)for(let n of s.tests){if(n.skipped){a++,i.push({name:n.name,suite:s.name,status:"skipped",duration:0}),console.log(` \x1B[33m\u2298 SKIP\x1B[0m ${s.name} > ${n.name}`);continue}let r=Date.now();try{if(s.beforeEachFn)for(let l of s.beforeEachFn)l();let h=n.fn();if(h instanceof Promise&&await h,s.afterEachFn)for(let l of s.afterEachFn)l();let u=Date.now()-r;t++,i.push({name:n.name,suite:s.name,status:"passed",duration:u}),console.log(` \x1B[32m\u2713 PASS\x1B[0m ${s.name} > ${n.name} (${u}ms)`)}catch(h){let u=Date.now()-r;e++,i.push({name:n.name,suite:s.name,status:"failed",error:h,duration:u}),console.log(` \x1B[31m\u2717 FAIL\x1B[0m ${s.name} > ${n.name}`),console.log(` \x1B[31m${h.message}\x1B[0m`)}}return f.length=0,console.log(""),console.log(`--- \u6D4B\u8BD5\u7ED3\u679C: ${t} \u901A\u8FC7, ${e} \u5931\u8D25, ${a} \u8DF3\u8FC7, \u5171 ${t+e+a} \u4E2A ---`),console.log(""),{total:t+e+a,passed:t,failed:e,skipped:a,results:i}}var d=class{constructor(t){this.negated=!1;this.actual=t}get not(){return this.negated=!this.negated,this}toBe(t){let e=this.negated?!Object.is(this.actual,t):Object.is(this.actual,t);this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u7B49\u4E8E ${this._fmt(t)}`)}toEqual(t){let e=this.negated?!c(this.actual,t):c(this.actual,t);this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u6DF1\u5EA6\u7B49\u4E8E ${this._fmt(t)}`)}toBeTruthy(){let t=this.negated?!this.actual:!!this.actual;this._assert(t,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u4E3A\u771F\u503C`)}toBeFalsy(){let t=this.negated?!!this.actual:!this.actual;this._assert(t,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u4E3A\u5047\u503C`)}toBeNull(){let t=this.negated?this.actual!==null:this.actual===null;this._assert(t,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u4E3A null`)}toBeUndefined(){let t=this.negated?this.actual!==void 0:this.actual===void 0;this._assert(t,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u4E3A undefined`)}toBeDefined(){let t=this.negated?this.actual===void 0:this.actual!==void 0;this._assert(t,`\u671F\u671B\u503C ${this.negated?"\u4E0D":""}\u5DF2\u5B9A\u4E49`)}toThrow(t){let e=!1,a="";try{if(typeof this.actual=="function")this.actual();else throw new Error("toThrow() \u7684\u5B9E\u9645\u503C\u5FC5\u987B\u662F\u51FD\u6570")}catch(n){e=!0,a=n.message||String(n)}let s=this.negated?!e:e;s&&e&&t&&!this.negated&&(s=a.includes(t)),this._assert(s,`\u671F\u671B\u51FD\u6570${this.negated?"\u4E0D":""}\u629B\u51FA\u5F02\u5E38${t?` (\u5305\u542B "${t}")`:""}`)}toContain(t){let e;typeof this.actual=="string"?e=this.actual.includes(t):Array.isArray(this.actual)?e=this.actual.some(a=>c(a,t)):e=!1,this.negated&&(e=!e),this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5305\u542B ${this._fmt(t)}`)}toBeGreaterThan(t){let e=this.negated?!(this.actual>t):this.actual>t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5927\u4E8E ${t}`)}toBeLessThan(t){let e=this.negated?!(this.actual<t):this.actual<t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5C0F\u4E8E ${t}`)}toBeGreaterThanOrEqual(t){let e=this.negated?!(this.actual>=t):this.actual>=t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5927\u4E8E\u7B49\u4E8E ${t}`)}toBeLessThanOrEqual(t){let e=this.negated?!(this.actual<=t):this.actual<=t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u5C0F\u4E8E\u7B49\u4E8E ${t}`)}toHaveLength(t){var s;let e=(s=this.actual)==null?void 0:s.length,a=this.negated?e!==t:e===t;this._assert(a,`\u671F\u671B\u957F\u5EA6\u4E3A ${t}\uFF0C\u5B9E\u9645\u4E3A ${e}`)}toBeInstanceOf(t){let e=this.negated?!(this.actual instanceof t):this.actual instanceof t;this._assert(e,`\u671F\u671B ${this._fmt(this.actual)} ${this.negated?"\u4E0D":""}\u662F ${t.name} \u7684\u5B9E\u4F8B`)}toHaveProperty(t,e){let a=t.split("."),s=this.actual,n=!0;for(let r of a){if(s==null||typeof s!="object"){n=!1;break}s=s[r]}if(e!==void 0){let r=this.negated?!(n&&Object.is(s,e)):n&&Object.is(s,e);this._assert(r,`\u671F\u671B\u5BF9\u8C61${this.negated?"\u4E0D":""}\u5305\u542B\u5C5E\u6027 "${t}" \u503C\u4E3A ${this._fmt(e)}`)}else{let r=this.negated?!n:n;this._assert(r,`\u671F\u671B\u5BF9\u8C61${this.negated?"\u4E0D":""}\u5305\u542B\u5C5E\u6027 "${t}"`)}}_assert(t,e){if(!t)throw new Error(e)}_fmt(t){if(t===null)return"null";if(t===void 0)return"undefined";if(typeof t=="string")return`"${t}"`;if(typeof t=="function")return"[Function]";if(Array.isArray(t))try{return JSON.stringify(t)}catch(e){return"[Array]"}if(typeof t=="object")try{return JSON.stringify(t)}catch(e){return"[Object]"}return String(t)}};export{d as Assertion,b as afterEach,y as beforeEach,c as deepEqual,g as describe,_ as expect,p as it,E as runAll,$ as skip,m as test,v as waitFor};
@@ -1,106 +1,66 @@
1
- interface TestCase {
1
+ /**
2
+ * Lyt.js 统一测试框架
3
+ *
4
+ * 自定义轻量级测试框架,用于 Node.js 环境下运行测试。
5
+ * 由 test-runner.ts 统一调度,通过 globalThis 挂载到全局。
6
+ *
7
+ * 使用方式:
8
+ * ```ts
9
+ * import { describe, it, expect } from '@lytjs/test-utils'
10
+ *
11
+ * describe('我的模块', () => {
12
+ * it('应该正常工作', () => {
13
+ * expect(1 + 1).toBe(2)
14
+ * })
15
+ * })
16
+ * ```
17
+ */
18
+ export interface TestCase {
2
19
  name: string;
3
20
  fn: () => void | Promise<void>;
21
+ skipped?: boolean;
4
22
  }
5
- interface TestResult {
23
+ export interface TestResult {
6
24
  name: string;
7
25
  suite: string;
8
26
  status: 'passed' | 'failed' | 'skipped';
9
27
  error?: Error;
10
28
  duration: number;
11
29
  }
12
- interface TestSuite {
30
+ export interface TestSuite {
13
31
  name: string;
14
32
  tests: TestCase[];
15
33
  beforeEachFn?: (() => void)[];
16
34
  afterEachFn?: (() => void)[];
17
35
  }
18
36
  /**
19
- * 定义测试套件
20
- *
21
- * @param name 测试套件名称
22
- * @param fn 定义函数,内部使用 it/test 注册测试用例
37
+ * 注册一个测试套件
23
38
  */
24
- declare function describe(name: string, fn: () => void): void;
39
+ export declare function describe(name: string, fn: () => void): void;
25
40
  /**
26
- * 注册测试用例
27
- *
28
- * @param name 测试名称
29
- * @param fn 测试函数
41
+ * 注册一个测试用例
30
42
  */
31
- declare function it(name: string, fn: () => void | Promise<void>): void;
32
- /** it 的别名 */
33
- declare function test(name: string, fn: () => void | Promise<void>): void;
43
+ export declare function it(name: string, fn: () => void | Promise<void>): void;
34
44
  /**
35
- * 跳过测试用例
36
- *
37
- * @param name 测试名称
38
- * @param fn 测试函数(不会执行)
45
+ * test 是 it 的别名
39
46
  */
40
- declare function skip(name: string, fn: () => void | Promise<void>): void;
47
+ export declare const test: typeof it;
41
48
  /**
42
- * 注册前置钩子
43
- *
44
- * @param fn 每个测试用例执行前调用的函数
49
+ * 跳过一个测试用例
45
50
  */
46
- declare function beforeEach(fn: () => void): void;
51
+ export declare function skip(name: string, fn: () => void | Promise<void>): void;
47
52
  /**
48
- * 注册后置钩子
49
- *
50
- * @param fn 每个测试用例执行后调用的函数
53
+ * 注册 beforeEach 钩子
51
54
  */
52
- declare function afterEach(fn: () => void): void;
55
+ export declare function beforeEach(fn: () => void): void;
53
56
  /**
54
- * Assertion 断言类
55
- *
56
- * 支持链式调用和 .not 取反。
57
+ * 注册 afterEach 钩子
57
58
  */
58
- declare class Assertion {
59
- private actual;
60
- private negated;
61
- constructor(actual: any);
62
- /** 取反后续断言 */
63
- get not(): Assertion;
64
- /** 断言严格相等 (===) */
65
- toBe(expected: any): void;
66
- /** 断言深度相等 */
67
- toEqual(expected: any): void;
68
- /** 断言为真值 */
69
- toBeTruthy(): void;
70
- /** 断言为假值 */
71
- toBeFalsy(): void;
72
- /** 断言为 null */
73
- toBeNull(): void;
74
- /** 断言为 undefined */
75
- toBeUndefined(): void;
76
- /** 断言不为 undefined(即已定义) */
77
- toBeDefined(): void;
78
- /** 断言函数抛出异常 */
79
- toThrow(message?: string): void;
80
- /** 断言数组/字符串包含指定项 */
81
- toContain(item: any): void;
82
- /** 断言数值大于 n */
83
- toBeGreaterThan(n: number): void;
84
- /** 断言数值小于 n */
85
- toBeLessThan(n: number): void;
86
- /** 断言数值大于等于 n */
87
- toBeGreaterThanOrEqual(n: number): void;
88
- /** 断言数值小于等于 n */
89
- toBeLessThanOrEqual(n: number): void;
90
- /** 断言数组/字符串长度为 n */
91
- toHaveLength(n: number): void;
92
- /** 内部断言方法 */
93
- private _assert;
94
- /** 格式化值用于错误消息 */
95
- private _fmt;
96
- }
59
+ export declare function afterEach(fn: () => void): void;
97
60
  /**
98
- * 创建断言
99
- *
100
- * @param value 要断言的值
101
- * @returns Assertion 实例
61
+ * 创建一个断言对象
102
62
  */
103
- declare function expect(value: any): Assertion;
63
+ export declare function expect(actual: any): Assertion;
104
64
  /**
105
65
  * 深度比较两个值
106
66
  *
@@ -108,28 +68,46 @@ declare function expect(value: any): Assertion;
108
68
  * @param b 第二个值
109
69
  * @returns 是否深度相等
110
70
  */
111
- declare function deepEqual(a: any, b: any): boolean;
71
+ export declare function deepEqual(a: any, b: any): boolean;
112
72
  /**
113
73
  * 等待指定毫秒数
114
74
  *
115
75
  * @param ms 等待时间(毫秒)
116
76
  * @returns Promise
117
77
  */
118
- declare function waitFor(ms: number): Promise<void>;
78
+ export declare function waitFor(ms: number): Promise<void>;
119
79
  /**
120
80
  * 运行所有已注册的测试套件
121
- *
122
- * 按注册顺序执行所有 describe 中的测试用例,
123
- * 收集结果并输出格式化报告。
124
- *
125
- * @returns 测试结果汇总
126
81
  */
127
- declare function runAll(): Promise<{
82
+ export declare function runAll(): Promise<{
128
83
  total: number;
129
84
  passed: number;
130
85
  failed: number;
131
86
  skipped: number;
132
87
  results: TestResult[];
133
88
  }>;
134
- export { describe, it, test, skip, beforeEach, afterEach, expect, Assertion, deepEqual, waitFor, runAll, type TestCase, type TestResult, type TestSuite, };
89
+ export declare class Assertion {
90
+ private actual;
91
+ private negated;
92
+ constructor(actual: any);
93
+ get not(): Assertion;
94
+ toBe(expected: any): void;
95
+ toEqual(expected: any): void;
96
+ toBeTruthy(): void;
97
+ toBeFalsy(): void;
98
+ toBeNull(): void;
99
+ toBeUndefined(): void;
100
+ toBeDefined(): void;
101
+ toThrow(message?: string): void;
102
+ toContain(item: any): void;
103
+ toBeGreaterThan(n: number): void;
104
+ toBeLessThan(n: number): void;
105
+ toBeGreaterThanOrEqual(n: number): void;
106
+ toBeLessThanOrEqual(n: number): void;
107
+ toHaveLength(n: number): void;
108
+ toBeInstanceOf(cls: any): void;
109
+ toHaveProperty(path: string, value?: any): void;
110
+ private _assert;
111
+ private _fmt;
112
+ }
135
113
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@lytjs/test-utils",
3
- "version": "4.2.0",
3
+ "version": "5.0.1",
4
4
  "description": "Lyt.js 测试工具库 - 提供组件挂载、状态模拟和断言辅助等测试工具",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
7
7
  "exports": {
8
8
  ".": {
9
+ "types": "./dist/types/index.d.ts",
9
10
  "import": "./dist/index.mjs",
10
11
  "require": "./dist/index.cjs",
11
12
  "default": "./dist/index.mjs"