Skip to content

Commit

Permalink
feat(kitify): Add clone utilities and tests (#11)
Browse files Browse the repository at this point in the history
Clone
  • Loading branch information
Marinerer authored Dec 25, 2024
2 parents 1272482 + 45d343a commit 7f77053
Show file tree
Hide file tree
Showing 12 changed files with 734 additions and 13 deletions.
11 changes: 7 additions & 4 deletions libs/kitify/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,13 @@ Object related utility functions

对象相关的工具函数。

| Method | Description |
| -------- | -------------------------------- |
| `assign` | Merge objects into a new object. |
| `clone` | Clone an object. |
| Method | Description |
| ----------- | ------------------------------------------------------- |
| `assign` | Merge objects into a new object. |
| `clone` | Deep copy of the value. |
| `cloneDeep` | Deep copy of the value. Supports Map,Set,ArrayBuffer... |
| `cloneLoop` | Loop deep copy of the value. |
| `cloneJSON` | JSON deep copy of the value. |

### Function

Expand Down
18 changes: 18 additions & 0 deletions libs/kitify/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@ export default {
input: 'src/object/assign.ts',
name: 'assign',
},
{
input: 'src/object/clone.ts',
name: 'clone',
},
{
input: 'src/object/cloneDeep.ts',
name: 'cloneDeep',
target: ['es2015'],
},
{
input: 'src/object/cloneLoop.ts',
name: 'cloneLoop',
target: ['es2015'],
},
{
input: 'src/object/cloneJSON.ts',
name: 'cloneJSON',
},
//==> dom
{
input: 'src/dom/detectMouseDirection.ts',
Expand Down
31 changes: 23 additions & 8 deletions libs/kitify/docs/object.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ Object related utility functions
## usage

```ts
import { assign } from 'kitify'
import { assign, clone, cloneDeep } from 'kitify'

import assign from 'kitify/assign'
import clone from 'kitify/clone'
import cloneDeep from 'kitify/cloneDeep'
```

## API
Expand All @@ -24,15 +26,28 @@ Assigns enumerable properties of source objects to the target object.
assign(target: object, ...sources: object[]): object
```

#### arguments

| Name | Type | Description |
| ------- | ---------- | ------------------- |
| target | `object` | The target object. |
| sources | `object[]` | The source objects. |

#### example

```js
assign({ a: 1 }, { b: 2 }, { b: 3 })
```

### clone

Creates a deep copy of the value.

创建一个深拷贝。

```ts
// Deep copy of the value.
clone<T>(value: T): T;

// Deep copy of the value. Supports Map,Set,ArrayBuffer...
cloneDeep<T>(value: T): T;

// Loop deep copy of the value.
cloneLoop<T>(value: T): T;

// JSON deep copy of the value.
cloneJSON<T>(value: T): T;
```
33 changes: 32 additions & 1 deletion libs/kitify/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,36 @@
"module": "./dist/isType.mjs",
"require": "./dist/isType.js"
},
"./assign": {
"types": "./dist/assign.d.ts",
"import": "./dist/assign.mjs",
"module": "./dist/assign.mjs",
"require": "./dist/assign.js"
},
"./clone": {
"types": "./dist/clone.d.ts",
"import": "./dist/clone.mjs",
"module": "./dist/clone.mjs",
"require": "./dist/clone.js"
},
"./cloneDeep": {
"types": "./dist/cloneDeep.d.ts",
"import": "./dist/cloneDeep.mjs",
"module": "./dist/cloneDeep.mjs",
"require": "./dist/cloneDeep.js"
},
"./cloneLoop": {
"types": "./dist/cloneLoop.d.ts",
"import": "./dist/cloneLoop.mjs",
"module": "./dist/cloneLoop.mjs",
"require": "./dist/cloneLoop.js"
},
"./cloneJSON": {
"types": "./dist/cloneJSON.d.ts",
"import": "./dist/cloneJSON.mjs",
"module": "./dist/cloneJSON.mjs",
"require": "./dist/cloneJSON.js"
},
"./detectMouseDirection": {
"types": "./dist/detectMouseDirection.d.ts",
"import": "./dist/detectMouseDirection.mjs",
Expand All @@ -35,7 +65,8 @@
"./*": "./dist/*"
},
"files": [
"dist"
"dist",
"docs"
],
"keywords": [
"kitify",
Expand Down
70 changes: 70 additions & 0 deletions libs/kitify/src/object/__tests__/clone.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//@ts-nocheck
import clone from '../clone'

describe('clone', () => {
it('should return the same value for primitive types', () => {
expect(clone(123)).toBe(123)
expect(clone('abc')).toBe('abc')
expect(clone(true)).toBe(true)
expect(clone(null)).toBe(null)
expect(clone(undefined)).toBe(undefined)

const sym = Symbol('sym')
expect(clone(sym)).toEqual(sym)
})

it('should clone Date objects correctly', () => {
const date = new Date()
const clonedDate = clone(date)
expect(clonedDate).toEqual(date)
expect(clonedDate).not.toBe(date)
})

it('should clone RegExp objects correctly', () => {
const regex = /abc/gi
const clonedRegex = clone(regex)
expect(clonedRegex).toEqual(regex)
expect(clonedRegex).not.toBe(regex)
})

it('should handle circular references', () => {
const obj: any = {}
obj.self = obj
const clonedObj = clone(obj)
expect(clonedObj).not.toBe(obj)
expect(clonedObj.self).toBe(clonedObj)
})

it('should clone arrays correctly', () => {
const arr = [1, 2, 3, [4, 5]]
const clonedArr = clone(arr)
expect(clonedArr).toEqual(arr)
expect(clonedArr).not.toBe(arr)
expect(clonedArr[3]).not.toBe(arr[3])
})

it('should clone objects with nested objects correctly', () => {
const obj = { a: 1, b: { c: 2 } }
const clonedObj = clone(obj)
expect(clonedObj).toEqual(obj)
expect(clonedObj).not.toBe(obj)
expect(clonedObj.a).toBe(obj.a)
expect(clonedObj.b).not.toBe(obj.b)
})

it('should clone objects with prototype chain', () => {
function Person(name) {
this.name = name
}

Person.prototype.sayHello = function () {
return 'Hello, ' + this.name
}

const person = new Person('John')
const clonedPerson = clone(person)
expect(clonedPerson).toEqual(person)
expect(clonedPerson).not.toBe(person)
expect(clonedPerson.sayHello()).toBe('Hello, John')
})
})
113 changes: 113 additions & 0 deletions libs/kitify/src/object/__tests__/cloneDeep.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//@ts-nocheck
import cloneDeep from '../cloneDeep'

describe('cloneDeep', () => {
// 测试原始类型
test('should clone primitive types', () => {
expect(cloneDeep(123)).toBe(123)
expect(cloneDeep('abc')).toBe('abc')
expect(cloneDeep(true)).toBe(true)
expect(cloneDeep(null)).toBe(null)
expect(cloneDeep(undefined)).toBe(undefined)

const sym = Symbol('sym')
expect(cloneDeep(sym)).toEqual(sym)
})

// 测试日期
test('should clone Date', () => {
const date = new Date()
const clonedDate = cloneDeep(date)
expect(clonedDate).toEqual(date)
expect(clonedDate).not.toBe(date)
})

// 测试正则表达式
test('should clone RegExp', () => {
const regex = /abc/gi
const clonedRegex = cloneDeep(regex)
expect(clonedRegex).toEqual(regex)
expect(clonedRegex).not.toBe(regex)
})

// 测试ArrayBuffer
test('should clone ArrayBuffer', () => {
const buffer = new ArrayBuffer(8)
const clonedBuffer = cloneDeep(buffer)
expect(clonedBuffer).toEqual(buffer)
expect(clonedBuffer).not.toBe(buffer)
})

// 测试TypedArray
test('should clone TypedArray', () => {
const typedArray = new Uint8Array([1, 2, 3])
const clonedTypedArray = cloneDeep(typedArray)
expect(clonedTypedArray).toEqual(typedArray)
expect(clonedTypedArray).not.toBe(typedArray)
})

// 测试DataView
test('should clone DataView', () => {
const buffer = new ArrayBuffer(8)
const dataView = new DataView(buffer)
const clonedDataView = cloneDeep(dataView)
expect(clonedDataView).toEqual(dataView)
expect(clonedDataView).not.toBe(dataView)
})

// 测试Map
test('should clone Map', () => {
const map = new Map([
[1, 'a'],
[2, 'b'],
])
const clonedMap = cloneDeep(map)
expect(clonedMap).toEqual(map)
expect(clonedMap).not.toBe(map)
})

// 测试Set
test('should clone Set', () => {
const set = new Set([1, 2, 3])
const clonedSet = cloneDeep(set)
expect(clonedSet).toEqual(set)
expect(clonedSet).not.toBe(set)
})

// 测试数组
test('should clone Array', () => {
const array = [1, 2, 3]
const clonedArray = cloneDeep(array)
expect(clonedArray).toEqual(array)
expect(clonedArray).not.toBe(array)
})

// 测试普通对象
test('should clone Object', () => {
const obj = { a: 1, b: { c: 2 } }
const clonedObj = cloneDeep(obj)
expect(clonedObj).toEqual(obj)
expect(clonedObj).not.toBe(obj)
expect(clonedObj.b).not.toBe(obj.b)
})

// 测试循环引用
test('should handle circular reference', () => {
const obj: any = {}
obj.a = obj
const clonedObj = cloneDeep(obj)
expect(clonedObj).toEqual(obj)
expect(clonedObj).not.toBe(obj)
expect(clonedObj.a).toBe(clonedObj)
})

// 测试Symbol属性
test('should clone Symbol properties', () => {
const sym = Symbol('sym')
const obj = { [sym]: 'value' }
const clonedObj = cloneDeep(obj)
expect(clonedObj).toEqual(obj)
expect(clonedObj).not.toBe(obj)
expect(clonedObj[sym]).toBe(obj[sym])
})
})
40 changes: 40 additions & 0 deletions libs/kitify/src/object/__tests__/cloneJSON.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { afterEach } from 'node:test'
import cloneJSON from '../cloneJSON'

describe('cloneJSON', () => {
afterEach(() => {
jest.resetAllMocks()
})

test('should clone a simple object', () => {
const obj = { name: 'John', age: 30 }
const clonedObj = cloneJSON(obj)
expect(clonedObj).toEqual(obj)
})

test('should handle circular references', () => {
const consoleSpy = jest.spyOn(console, 'error').mockImplementation()

const obj = { name: 'John', age: 30 } as any
obj.self = obj

try {
cloneJSON(obj)
} catch (error) {}

expect(consoleSpy).toHaveBeenCalled()
consoleSpy.mockRestore()
})

test('should handle invalid JSON', () => {
expect(cloneJSON(123)).toEqual(123)
expect(cloneJSON('123')).toEqual('123')
expect(cloneJSON(undefined)).toEqual({})
})

test('should handle empty object', () => {
const obj = {}
const clonedObj = cloneJSON(obj)
expect(clonedObj).toEqual(obj)
})
})
Loading

0 comments on commit 7f77053

Please sign in to comment.