@greypan/js-kit 1.0.0 → 1.2.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.
Files changed (49) hide show
  1. package/dist/fetch/index.d.ts +1 -0
  2. package/dist/timer/debounce.js +1 -1
  3. package/dist/url/index.js +1 -2
  4. package/dist/url/index.js.map +1 -1
  5. package/package.json +8 -9
  6. package/.turbo/turbo-build.log +0 -38
  7. package/.turbo/turbo-test.log +0 -87
  8. package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/funnel.js +0 -40
  9. package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/funnel.js.map +0 -1
  10. package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/isNullish.js +0 -8
  11. package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/isNullish.js.map +0 -1
  12. package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/lazyDataLastImpl--3B10z3s.js +0 -12
  13. package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/lazyDataLastImpl--3B10z3s.js.map +0 -1
  14. package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/omitBy.js +0 -14
  15. package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/omitBy.js.map +0 -1
  16. package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/purry.js +0 -12
  17. package/dist/node_modules/.pnpm/remeda@2.39.0/node_modules/remeda/dist/purry.js.map +0 -1
  18. package/src/fetch/__tests__/compose.spec.ts +0 -105
  19. package/src/fetch/compose.ts +0 -23
  20. package/src/index.ts +0 -7
  21. package/src/number/__tests__/number.spec.ts +0 -50
  22. package/src/number/index.ts +0 -22
  23. package/src/paradigm/__tests__/go.spec.ts +0 -63
  24. package/src/paradigm/__tests__/rust.spec.ts +0 -68
  25. package/src/paradigm/go.ts +0 -38
  26. package/src/paradigm/index.ts +0 -2
  27. package/src/paradigm/rust.ts +0 -50
  28. package/src/plugin/__tests__/batch-emitter.spec.ts +0 -48
  29. package/src/plugin/__tests__/core.spec.ts +0 -110
  30. package/src/plugin/__tests__/event-emiiter.spec.ts +0 -41
  31. package/src/plugin/batching-emitter.ts +0 -61
  32. package/src/plugin/core.ts +0 -37
  33. package/src/plugin/event-emitter.ts +0 -25
  34. package/src/plugin/index.ts +0 -2
  35. package/src/random/__tests__/random.spec.ts +0 -77
  36. package/src/random/index.ts +0 -37
  37. package/src/timer/__tests__/controllable-interval.spec.ts +0 -51
  38. package/src/timer/controllable-interval.ts +0 -86
  39. package/src/timer/debounce.ts +0 -92
  40. package/src/timer/index.ts +0 -2
  41. package/src/url/README.md +0 -0
  42. package/src/url/__tests__/url.spec.ts +0 -152
  43. package/src/url/index.ts +0 -44
  44. package/src/utility-types/index.ts +0 -19
  45. package/tsconfig.app.json +0 -11
  46. package/tsconfig.json +0 -8
  47. package/tsconfig.node.json +0 -18
  48. package/tsconfig.vitest.json +0 -10
  49. package/vite.config.ts +0 -32
@@ -1,68 +0,0 @@
1
- import { describe, expect, it } from 'vite-plus/test'
2
-
3
- import { err, isErr, isOk, ok, to, unwrap } from '../rust'
4
-
5
- describe('Result 工具函数(Rust 风格)单元测试', () => {
6
- it('ok() 应当返回一个 Ok<T>', () => {
7
- const result = ok(123)
8
- expect(result).toEqual({ ok: true, value: 123 })
9
- expect(isOk(result)).toBe(true)
10
- expect(isErr(result)).toBe(false)
11
- })
12
-
13
- it('err() 应当返回一个 Err<E>', () => {
14
- const result = err('error')
15
- expect(result).toEqual({ ok: false, error: 'error' })
16
- expect(isErr(result)).toBe(true)
17
- expect(isOk(result)).toBe(false)
18
- })
19
-
20
- it('isOk() 和 isErr() 应当正确区分结果', () => {
21
- const success = ok('success')
22
- const failure = err('fail')
23
-
24
- expect(isOk(success)).toBe(true)
25
- expect(isErr(success)).toBe(false)
26
-
27
- expect(isOk(failure)).toBe(false)
28
- expect(isErr(failure)).toBe(true)
29
- })
30
-
31
- it('to() 应当把成功的 Promise 包装成 Ok', async () => {
32
- const result = await to(Promise.resolve('data'))
33
- expect(result).toEqual({ ok: true, value: 'data' })
34
- expect(isOk(result)).toBe(true)
35
- })
36
-
37
- it('to() 应当把失败的 Promise 包装成 Err', async () => {
38
- const result = await to(Promise.reject('error'))
39
- expect(result).toEqual({ ok: false, error: 'error' })
40
- expect(isErr(result)).toBe(true)
41
- })
42
-
43
- it('unwrap() 在 Ok 时返回值', () => {
44
- const result = ok('data')
45
- expect(unwrap(result)).toBe('data')
46
- })
47
-
48
- it('unwrap() 在 Err 时返回错误', () => {
49
- const result = err('error')
50
- expect(unwrap(result)).toBe('error')
51
- })
52
-
53
- it('Ok<T> 应当只有 ok 和 value 属性,没有 error', () => {
54
- const result = ok('success')
55
- expect(result).toHaveProperty('ok')
56
- expect(result.ok).toBe(true)
57
- expect(result).toHaveProperty('value')
58
- expect(result).not.toHaveProperty('error')
59
- })
60
-
61
- it('Err<E> 应当只有 ok 和 error 属性,没有 value', () => {
62
- const result = err('fail')
63
- expect(result).toHaveProperty('ok')
64
- expect(result.ok).toBe(false)
65
- expect(result).toHaveProperty('error')
66
- expect(result).not.toHaveProperty('value')
67
- })
68
- })
@@ -1,38 +0,0 @@
1
- /**
2
- * @description Go 范式,用于统一处理成功/失败返回值
3
- */
4
-
5
- export type Ok<T> = readonly [null, T]
6
- export type Err<E> = readonly [E, null]
7
- export type Result<T, E> = Ok<T> | Err<E>
8
-
9
- export function ok<T>(value: T): Ok<T> {
10
- return [null, value]
11
- }
12
-
13
- export function err<E>(error: E): Err<E> {
14
- return [error, null]
15
- }
16
-
17
- export function isOk<T, E>(result: Result<T, E>): result is Ok<T> {
18
- return result[0] === null && result[1] !== null
19
- }
20
-
21
- export function isErr<T, E>(result: Result<T, E>): result is Err<E> {
22
- return result[0] !== null && result[1] === null
23
- }
24
-
25
- export async function to<T, E = unknown>(promise: Promise<T>): Promise<Result<Awaited<T>, E>> {
26
- try {
27
- return ok(await promise)
28
- } catch (error) {
29
- return err(error as E)
30
- }
31
- }
32
-
33
- export function unwrap<S extends Result<any, any>>(
34
- result: S
35
- ): S extends Ok<infer T> ? T : S extends Err<infer E> ? E : never {
36
- const [error, value] = result
37
- return isOk(result) ? value : error
38
- }
@@ -1,2 +0,0 @@
1
- export * as go from './go'
2
- export * as rust from './rust'
@@ -1,50 +0,0 @@
1
- /**
2
- * @description Rust 范式,用于统一处理成功/失败返回值
3
- */
4
-
5
- export type Ok<T> = {
6
- readonly ok: true
7
- readonly value: T
8
- }
9
- export type Err<E> = {
10
- readonly ok: false
11
- readonly error: E
12
- }
13
- /**
14
- * 考虑到 T 或 E 其中一个为 never 的情况
15
- * Result<T,never> 等价于 Ok<T>
16
- * Result<never,E> 等价于 Err<E>
17
- * 为什么是 [T] extends [never] ? 而不是 T extends never ?:
18
- * 条件类型是分布式的,防止 T 为联合类型时对每项分别计算再合并结果
19
- */
20
- export type Result<T, E> = Ok<T> | Err<E>
21
-
22
- export function ok<T>(value: T): Ok<T> {
23
- return { ok: true, value }
24
- }
25
-
26
- export function err<E>(error: E): Err<E> {
27
- return { ok: false, error }
28
- }
29
-
30
- export function isOk<T, E>(result: Result<T, E>): result is Ok<T> {
31
- return result.ok
32
- }
33
-
34
- export function isErr<T, E>(result: Result<T, E>): result is Err<E> {
35
- return !result.ok
36
- }
37
-
38
- export async function to<T, E = unknown>(promise: Promise<T>): Promise<Result<Awaited<T>, E>> {
39
- try {
40
- return ok(await promise)
41
- } catch (error) {
42
- return err(error as E)
43
- }
44
- }
45
-
46
- export function unwrap<S extends Result<any, any>>(
47
- result: S
48
- ): S extends Ok<infer T> ? T : S extends Err<infer E> ? E : never {
49
- return result.ok ? result.value : result.error
50
- }
@@ -1,48 +0,0 @@
1
- import { describe, expect, it, vi } from 'vite-plus/test'
2
-
3
- import { defineBatchEmitter } from '..'
4
-
5
- describe('BatchEmitter 单元测试', () => {
6
- it('立即返回单个 id,当 delay <= 0', async () => {
7
- const batchEmitter = defineBatchEmitter().make()
8
- const { batchEmit } = batchEmitter
9
- const result = await batchEmit('a', 0)
10
- expect(result).toEqual(['a'])
11
- })
12
-
13
- it('延迟后批量返回多个 id', async () => {
14
- const batchEmitter = defineBatchEmitter().make()
15
- const { batchEmit } = batchEmitter
16
- const p1 = batchEmit('a', 10)
17
- const p2 = batchEmit('b', 10)
18
- const p3 = batchEmit('c', 10)
19
-
20
- const results = await Promise.all([p1, p2, p3])
21
- // 三个 promise 都应该 resolve 同一个批次
22
- results.forEach(r => expect(r).toEqual(['a', 'b', 'c']))
23
- })
24
-
25
- it('flush 可以同步清空队列并调用 onFlushed', async () => {
26
- const onFlushed = vi.fn()
27
- const batchEmitter = defineBatchEmitter(onFlushed).make()
28
- const { batchEmit, flush } = batchEmitter
29
-
30
- const p1 = batchEmit(1, 100)
31
- const p2 = batchEmit(2, 100)
32
-
33
- await flush()
34
-
35
- const results = await Promise.all([p1, p2])
36
- expect(results[0]).toEqual([1, 2])
37
- expect(results[1]).toEqual([1, 2])
38
- expect(onFlushed).toHaveBeenCalledWith([1, 2])
39
- })
40
-
41
- it('flush 在队列为空时不做任何事', async () => {
42
- const onFlushed = vi.fn()
43
- const batchEmitter = defineBatchEmitter(onFlushed).make()
44
- const { flush } = batchEmitter
45
- await flush()
46
- expect(onFlushed).not.toHaveBeenCalled()
47
- })
48
- })
@@ -1,110 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vite-plus/test'
2
-
3
- import { definePlugin, type PluginMade } from '../core'
4
-
5
- describe('definePlugin 单元测试', () => {
6
- const localStorageMock = (() => {
7
- let store: Record<string, string> = {}
8
- return {
9
- getItem: (key: string) => store[key] || null,
10
- setItem: (key: string, value: string) => {
11
- store[key] = value.toString()
12
- },
13
- clear: () => {
14
- store = {}
15
- }
16
- }
17
- })()
18
- vi.stubGlobal('localStorage', localStorageMock)
19
-
20
- beforeEach(() => {
21
- localStorage.clear()
22
- vi.restoreAllMocks()
23
- })
24
-
25
- describe('make', () => {
26
- it('应当实例化插件,并且能够调用插件的属性方法', () => {
27
- function defineTracker() {
28
- return definePlugin(() => ({
29
- track: (event: string) => console.log(`tracking ${event}`)
30
- }))
31
- }
32
- const tracker = defineTracker().make()
33
- expect(tracker).toHaveProperty('track')
34
- expect(typeof tracker.track).toBe('function')
35
-
36
- const spy = vi.spyOn(console, 'log').mockImplementation(() => {})
37
- tracker.track('test event')
38
- expect(spy).toHaveBeenCalled()
39
- })
40
- })
41
-
42
- describe('use', () => {
43
- const defineStore = (initial: Record<string, number>) =>
44
- definePlugin(() => {
45
- let store: Record<string, number> = initial
46
- return {
47
- setStore(newStore: Record<string, number>) {
48
- store = newStore
49
- },
50
- get(key: string) {
51
- return store[key]
52
- },
53
- set(key: string, val: number) {
54
- store[key] = val
55
- }
56
- }
57
- })
58
-
59
- const defineLogger = () =>
60
- definePlugin((ctx: PluginMade<typeof defineStore>) => ({
61
- set: (key: string, val: number) => {
62
- ctx.set(key, val)
63
- console.log(`set ${key} = ${val}.`)
64
- }
65
- }))
66
-
67
- const definePersist = (persitKey = 'persit-store') =>
68
- definePlugin((ctx: PluginMade<typeof defineStore>) => {
69
- const cache = localStorage.getItem(persitKey)
70
- if (cache) ctx.setStore(JSON.parse(cache))
71
- return {
72
- setStore(newStore: Record<string, number>) {
73
- ctx.setStore(newStore)
74
- localStorage.setItem(persitKey, JSON.stringify(newStore))
75
- },
76
- set(key: string, val: number) {
77
- ctx.set(key, val)
78
- const cache: Record<string, number> = JSON.parse(localStorage.getItem(persitKey) ?? '{}')
79
- cache[key] = val
80
- localStorage.setItem(persitKey, JSON.stringify(cache))
81
- }
82
- }
83
- })
84
-
85
- it('应当正确组合 Store, Logger 和 Persist 逻辑', () => {
86
- const logSpy = vi.spyOn(console, 'log')
87
-
88
- const store = defineStore({ a: 1, b: 2 }).use(defineLogger()).use(definePersist()).make()
89
-
90
- expect(store.get('a')).toBe(1)
91
-
92
- // 调用顺序: Persist.set -> Logger.set -> Store.set
93
- store.set('a', 100)
94
-
95
- expect(store.get('a')).toBe(100)
96
- expect(logSpy).toHaveBeenCalledWith('set a = 100.')
97
-
98
- const cached = JSON.parse(localStorage.getItem('persit-store') || '{}')
99
- expect(cached.a).toBe(100)
100
- })
101
-
102
- it('应当在初始化时从持久化恢复数据', () => {
103
- localStorage.setItem('persit-store', JSON.stringify({ a: 999 }))
104
-
105
- const store = defineStore({ a: 1 }).use(definePersist()).make()
106
-
107
- expect(store.get('a')).toBe(999)
108
- })
109
- })
110
- })
@@ -1,41 +0,0 @@
1
- import { describe, expect, it, vi } from 'vite-plus/test'
2
-
3
- import { defineEventEmitter } from '../event-emitter'
4
-
5
- describe('EventEmitter 单元测试', () => {
6
- it('应当支持注册和触发事件回调', () => {
7
- const logSpy = vi.spyOn(console, 'log')
8
-
9
- const eventEmitter = defineEventEmitter<{
10
- click: [string]
11
- }>().make()
12
- eventEmitter.on('click', e => {
13
- console.log(`${e} click 1`)
14
- })
15
- eventEmitter.on('click', e => {
16
- console.log(`${e} click 2`)
17
- })
18
- eventEmitter.emit('click', 'Tom')
19
-
20
- expect(logSpy).toHaveBeenCalledWith('Tom click 1')
21
- expect(logSpy).toHaveBeenCalledWith('Tom click 2')
22
- })
23
-
24
- it('应当支持传递 rest 参数', () => {
25
- const eventPlugin = defineEventEmitter<{
26
- 'user:update': [number, string, 'admin' | 'user']
27
- 'app:start': []
28
- }>()
29
- const app = eventPlugin.make()
30
- const users: string[] = []
31
-
32
- app.on('user:update', (id, name, role) => {
33
- const user = `ID: ${id}, Name: ${name}, Role: ${role}`
34
- users.push(user)
35
- })
36
-
37
- app.emit('user:update', 1, 'Tom', 'admin')
38
-
39
- expect(users).toEqual(['ID: 1, Name: Tom, Role: admin'])
40
- })
41
- })
@@ -1,61 +0,0 @@
1
- import { definePlugin } from './core'
2
-
3
- type Resolve<S> = (queue: S[]) => void
4
-
5
- export function defineBatchEmitter<S>(onFlushed?: (queue: S[]) => Promise<void>) {
6
- return definePlugin(() => {
7
- let queue: S[] = []
8
- let resolves: Resolve<S>[] = []
9
- let timerId: ReturnType<typeof setTimeout> | null = null
10
-
11
- /**
12
- * 批量发出,延迟执行
13
- */
14
- async function batchEmit(data: S, batchingDelay = 0): Promise<S[]> {
15
- if (batchingDelay <= 0) {
16
- if (onFlushed) await onFlushed([data])
17
- return Promise.resolve([data])
18
- }
19
-
20
- return new Promise((resolve, reject) => {
21
- resolves.push(resolve)
22
-
23
- if (queue.push(data) === 1) {
24
- timerId = setTimeout(() => {
25
- try {
26
- void flush()
27
- } catch (error) {
28
- reject(error)
29
- }
30
- }, batchingDelay)
31
- }
32
- })
33
- }
34
-
35
- /**
36
- * 立即清空并执行当前的队列
37
- */
38
- async function flush() {
39
- if (!queue.length) return
40
-
41
- // 先快照一份,防止后续 push 污染当前批次,消除异步重入(Re-entrancy)
42
- const currentQueue = [...queue]
43
- const currentResolves = [...resolves]
44
- queue = []
45
- resolves = []
46
- if (timerId) {
47
- clearTimeout(timerId)
48
- timerId = null
49
- }
50
-
51
- if (onFlushed) await onFlushed(currentQueue)
52
-
53
- let fn: Resolve<S> | undefined
54
- while ((fn = currentResolves.shift())) {
55
- fn(currentQueue)
56
- }
57
- }
58
-
59
- return { batchEmit, flush }
60
- })
61
- }
@@ -1,37 +0,0 @@
1
- export interface Plugin<C extends object, D extends object> {
2
- readonly use: <U extends object, E extends object>(plugin: Plugin<U, E>) => Plugin<C & U, D & E>
3
- readonly make: <T extends D>(ctx?: T) => T & C
4
- }
5
-
6
- export function definePlugin<C extends object, D extends object>(setup: (ctx: D) => C): Plugin<C, D> {
7
- function use<U extends object, E extends object>(plugin: Plugin<U, E>): Plugin<C & U, D & E> {
8
- return definePlugin<C & U, D & E>(ctx => {
9
- return plugin.make({
10
- ...ctx,
11
- ...setup(ctx)
12
- })
13
- })
14
- }
15
-
16
- function make<T extends D>(ctx = {} as T): T & C {
17
- return {
18
- ...ctx,
19
- ...setup(ctx)
20
- }
21
- }
22
-
23
- return {
24
- use,
25
- make
26
- }
27
- }
28
-
29
- /**
30
- * 提取插件实例化的类型 (C & D)
31
- * T 可以是 Plugin 实例,也可以是返回 Plugin 实例的函数
32
- */
33
- export type PluginMade<T> = T extends (...args: any[]) => Plugin<infer C, infer D>
34
- ? C & D
35
- : T extends Plugin<infer C, infer D>
36
- ? C & D
37
- : never
@@ -1,25 +0,0 @@
1
- import { definePlugin } from './core'
2
-
3
- export function defineEventEmitter<E extends Record<string, unknown[]>>() {
4
- return definePlugin(() => {
5
- const bus = new EventTarget()
6
-
7
- return {
8
- on<K extends keyof E>(type: K & string, handler: (...args: E[K]) => void, options?: AddEventListenerOptions) {
9
- const listener = (e: Event) => {
10
- const args = (e as CustomEvent).detail as E[K]
11
- handler(...args)
12
- }
13
- bus.addEventListener(type, listener, options)
14
- // off fn
15
- return () => bus.removeEventListener(type, listener)
16
- },
17
-
18
- // 支持传入多个参数
19
- emit<K extends keyof E>(type: K & string, ...args: E[K]) {
20
- const event = new CustomEvent(type, { detail: args })
21
- bus.dispatchEvent(event)
22
- }
23
- }
24
- })
25
- }
@@ -1,2 +0,0 @@
1
- export * from './batching-emitter'
2
- export * from './core'
@@ -1,77 +0,0 @@
1
- import { describe, expect, it } from 'vite-plus/test'
2
-
3
- import { randomFloat, randomHex, randomRgb } from '..'
4
-
5
- describe('random 单元测试', () => {
6
- describe('randomFloat', () => {
7
- it('结果应在指定的范围内', () => {
8
- const min = 1.1
9
- const max = 5.5
10
- for (let i = 0; i < 100; i++) {
11
- const result = randomFloat(min, max)
12
- expect(result).toBeGreaterThanOrEqual(min)
13
- // 考虑到 Math.random() 是 [0, 1),结果理论上小于 max
14
- expect(result).toBeLessThanOrEqual(max)
15
- }
16
- })
17
-
18
- it('即便 min 和 max 传反了也应当正常工作', () => {
19
- const min = 10
20
- const max = 1
21
- const result = randomFloat(min, max)
22
- expect(result).toBeGreaterThanOrEqual(1)
23
- expect(result).toBeLessThanOrEqual(10)
24
- })
25
-
26
- it('当 min 等于 max 时应返回该数值', () => {
27
- const result = randomFloat(5, 5)
28
- expect(result).toBe(5)
29
- })
30
- })
31
-
32
- describe('randomRgb', () => {
33
- it('应当返回正确的 rgb(r, g, b) 格式', () => {
34
- // 允许逗号后有或没有空格
35
- const rgbRegex = /^rgb\(\d{1,3},\s?\d{1,3},\s?\d{1,3}\)$/
36
- for (let i = 0; i < 50; i++) {
37
- const result = randomRgb()
38
- expect(result).toMatch(rgbRegex)
39
-
40
- // 进一步校验数值范围
41
- const colors = result.match(/\d+/g)?.map(Number)
42
- colors?.forEach(c => {
43
- expect(c).toBeGreaterThanOrEqual(0)
44
- expect(c).toBeLessThanOrEqual(255)
45
- })
46
- }
47
- })
48
- })
49
-
50
- describe('randomHex', () => {
51
- it('应当返回正确的十六进制格式', () => {
52
- const hexRegex = /^#[0-9a-f]{6}$/
53
- for (let i = 0; i < 50; i++) {
54
- const result = randomHex()
55
- expect(result).toMatch(hexRegex)
56
- }
57
- })
58
-
59
- it('生成的十六进制应当是小写的', () => {
60
- const result = randomHex()
61
- // 检查不含大写字母:如果包含大写字母则测试失败
62
- expect(result).not.toMatch(/[A-Z]/)
63
- })
64
- })
65
-
66
- describe('随机性校验 (Randomness)', () => {
67
- it('连续调用不应产生相同的结果', () => {
68
- const results = new Set()
69
- const count = 100
70
- for (let i = 0; i < count; i++) {
71
- results.add(randomHex())
72
- }
73
- // 16777216 种颜色选 100 次,碰撞概率微乎其微
74
- expect(results.size).toBe(count)
75
- })
76
- })
77
- })
@@ -1,37 +0,0 @@
1
- /**
2
- * 随机整数 [min, max]
3
- * @param min 最小值
4
- * @param max 最大值
5
- */
6
- export function random(min: number, max: number): number {
7
- return Math.floor(Math.random() * (max - min + 1)) + min
8
- }
9
-
10
- /**
11
- * 随机浮点数 [min, max)
12
- * @param min 最小值
13
- * @param max 最大值
14
- * @param precision 保留小数位,不传则返回原始浮点数
15
- */
16
- export function randomFloat(min: number, max: number): number {
17
- const lower = Math.min(min, max)
18
- const upper = Math.max(min, max)
19
- return lower + Math.random() * (upper - lower)
20
- }
21
-
22
- /**
23
- * 生成随机的 RGB 颜色代码
24
- * @returns RGB 颜色代码,例如 rgb(255, 0, 0)
25
- */
26
- export function randomRgb(): string {
27
- return `rgb(${random(0, 255)}, ${random(0, 255)}, ${random(0, 255)})`
28
- }
29
-
30
- /**
31
- * 生成随机的十六进制颜色代码
32
- * @returns 十六进制颜色代码,例如 #ff0000
33
- */
34
- export function randomHex(): string {
35
- const color = Math.floor(Math.random() * 0x1000000).toString(16)
36
- return '#' + color.padStart(6, '0')
37
- }
@@ -1,51 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vite-plus/test'
2
-
3
- import { createControllableInterval } from '..'
4
-
5
- describe('timer 单元测试', () => {
6
- describe('ControllableInterval', () => {
7
- beforeEach(() => {
8
- vi.useFakeTimers()
9
- })
10
-
11
- it('应当能正常循环执行', () => {
12
- const cb = vi.fn()
13
- const timer = createControllableInterval(cb, 1000)
14
- timer.start()
15
-
16
- vi.advanceTimersByTime(3500)
17
- expect(cb).toHaveBeenCalledTimes(3)
18
- timer.stop()
19
- })
20
-
21
- it('暂停后不应触发回调', () => {
22
- const cb = vi.fn()
23
- const timer = createControllableInterval(cb, 1000)
24
- timer.start()
25
-
26
- vi.advanceTimersByTime(500)
27
- timer.pause()
28
- vi.advanceTimersByTime(1000)
29
-
30
- expect(cb).not.toHaveBeenCalled()
31
- timer.stop()
32
- })
33
-
34
- it('恢复后应先补全剩余时间', () => {
35
- const cb = vi.fn()
36
- const timer = createControllableInterval(cb, 1000)
37
- timer.start()
38
-
39
- vi.advanceTimersByTime(800) // 此时还剩 200ms
40
- timer.pause()
41
-
42
- timer.resume()
43
- vi.advanceTimersByTime(150)
44
- expect(cb).not.toHaveBeenCalled() // 还没到 200ms,不触发
45
-
46
- vi.advanceTimersByTime(60) // 超过 200ms 了
47
- expect(cb).toHaveBeenCalledTimes(1)
48
- timer.stop()
49
- })
50
- })
51
- })