Skip to content
This repository was archived by the owner on Mar 1, 2026. It is now read-only.

Commit 549a026

Browse files
authored
chore: add tests for common-helpers package (#540)
1 parent f13886b commit 549a026

12 files changed

Lines changed: 708 additions & 1 deletion

packages/common-helpers/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"build": "tsc --noEmit && tsup-node",
88
"watch": "tsup-node --watch",
99
"lint": "eslint src --ext ts",
10+
"test": "vitest run",
1011
"pack": "pnpm pack"
1112
},
1213
"keywords": [],
@@ -29,6 +30,7 @@
2930
},
3031
"devDependencies": {
3132
"@zenstackhq/typescript-config": "workspace:*",
32-
"@zenstackhq/eslint-config": "workspace:*"
33+
"@zenstackhq/eslint-config": "workspace:*",
34+
"@zenstackhq/vitest-config": "workspace:*"
3335
}
3436
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { lowerCaseFirst } from '../src/lower-case-first';
3+
import { upperCaseFirst } from '../src/upper-case-first';
4+
5+
describe('lowerCaseFirst tests', () => {
6+
it('should lowercase the first character', () => {
7+
expect(lowerCaseFirst('Hello')).toBe('hello');
8+
expect(lowerCaseFirst('WORLD')).toBe('wORLD');
9+
expect(lowerCaseFirst('A')).toBe('a');
10+
});
11+
12+
it('should handle already lowercase strings', () => {
13+
expect(lowerCaseFirst('hello')).toBe('hello');
14+
expect(lowerCaseFirst('world')).toBe('world');
15+
});
16+
17+
it('should handle empty string', () => {
18+
expect(lowerCaseFirst('')).toBe('');
19+
});
20+
21+
it('should handle strings with numbers', () => {
22+
expect(lowerCaseFirst('123abc')).toBe('123abc');
23+
});
24+
25+
it('should handle strings with special characters', () => {
26+
expect(lowerCaseFirst('!Hello')).toBe('!Hello');
27+
expect(lowerCaseFirst('@World')).toBe('@World');
28+
});
29+
});
30+
31+
describe('upperCaseFirst tests', () => {
32+
it('should uppercase the first character', () => {
33+
expect(upperCaseFirst('hello')).toBe('Hello');
34+
expect(upperCaseFirst('world')).toBe('World');
35+
expect(upperCaseFirst('a')).toBe('A');
36+
});
37+
38+
it('should handle already uppercase strings', () => {
39+
expect(upperCaseFirst('Hello')).toBe('Hello');
40+
expect(upperCaseFirst('WORLD')).toBe('WORLD');
41+
});
42+
43+
it('should handle empty string', () => {
44+
expect(upperCaseFirst('')).toBe('');
45+
});
46+
47+
it('should handle strings with numbers', () => {
48+
expect(upperCaseFirst('123abc')).toBe('123abc');
49+
});
50+
51+
it('should handle strings with special characters', () => {
52+
expect(upperCaseFirst('!hello')).toBe('!hello');
53+
expect(upperCaseFirst('@world')).toBe('@world');
54+
});
55+
});
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { clone } from '../src/clone';
3+
4+
describe('clone tests', () => {
5+
describe('primitives', () => {
6+
it('should return primitives as-is', () => {
7+
expect(clone(42)).toBe(42);
8+
expect(clone('hello')).toBe('hello');
9+
expect(clone(true)).toBe(true);
10+
expect(clone(false)).toBe(false);
11+
expect(clone(null)).toBe(null);
12+
expect(clone(undefined)).toBe(undefined);
13+
});
14+
});
15+
16+
describe('arrays', () => {
17+
it('should clone simple arrays', () => {
18+
const arr = [1, 2, 3];
19+
const cloned = clone(arr);
20+
expect(cloned).toEqual(arr);
21+
expect(cloned).not.toBe(arr);
22+
});
23+
24+
it('should deep clone nested arrays', () => {
25+
const arr = [1, [2, 3], [4, [5, 6]]];
26+
const cloned = clone(arr);
27+
expect(cloned).toEqual(arr);
28+
expect(cloned).not.toBe(arr);
29+
expect(cloned[1]).not.toBe(arr[1]);
30+
expect(cloned[2]).not.toBe(arr[2]);
31+
});
32+
33+
it('should clone arrays with objects', () => {
34+
const arr = [{ a: 1 }, { b: 2 }];
35+
const cloned = clone(arr);
36+
expect(cloned).toEqual(arr);
37+
expect(cloned).not.toBe(arr);
38+
expect(cloned[0]).not.toBe(arr[0]);
39+
expect(cloned[1]).not.toBe(arr[1]);
40+
});
41+
42+
it('should handle empty arrays', () => {
43+
const arr: number[] = [];
44+
const cloned = clone(arr);
45+
expect(cloned).toEqual([]);
46+
expect(cloned).not.toBe(arr);
47+
});
48+
});
49+
50+
describe('plain objects', () => {
51+
it('should clone simple objects', () => {
52+
const obj = { a: 1, b: 2 };
53+
const cloned = clone(obj);
54+
expect(cloned).toEqual(obj);
55+
expect(cloned).not.toBe(obj);
56+
});
57+
58+
it('should deep clone nested objects', () => {
59+
const obj = { a: 1, b: { c: 2, d: { e: 3 } } };
60+
const cloned = clone(obj);
61+
expect(cloned).toEqual(obj);
62+
expect(cloned).not.toBe(obj);
63+
expect(cloned.b).not.toBe(obj.b);
64+
expect(cloned.b.d).not.toBe(obj.b.d);
65+
});
66+
67+
it('should clone objects with arrays', () => {
68+
const obj = { a: [1, 2], b: { c: [3, 4] } };
69+
const cloned = clone(obj);
70+
expect(cloned).toEqual(obj);
71+
expect(cloned).not.toBe(obj);
72+
expect(cloned.a).not.toBe(obj.a);
73+
expect(cloned.b.c).not.toBe(obj.b.c);
74+
});
75+
76+
it('should handle empty objects', () => {
77+
const obj = {};
78+
const cloned = clone(obj);
79+
expect(cloned).toEqual({});
80+
expect(cloned).not.toBe(obj);
81+
});
82+
83+
it('should handle objects with null prototype', () => {
84+
const obj = Object.create(null);
85+
obj.foo = 'bar';
86+
const cloned = clone(obj);
87+
expect(cloned).toEqual(obj);
88+
expect(cloned).not.toBe(obj);
89+
});
90+
});
91+
92+
describe('non-plain objects', () => {
93+
it('should return Date objects as-is', () => {
94+
const date = new Date();
95+
const cloned = clone(date);
96+
expect(cloned).toBe(date);
97+
});
98+
99+
it('should return RegExp objects as-is', () => {
100+
const regex = /test/gi;
101+
const cloned = clone(regex);
102+
expect(cloned).toBe(regex);
103+
});
104+
105+
it('should return class instances as-is', () => {
106+
class MyClass {
107+
value = 42;
108+
}
109+
const instance = new MyClass();
110+
const cloned = clone(instance);
111+
expect(cloned).toBe(instance);
112+
});
113+
114+
it('should return functions as-is', () => {
115+
const fn = () => 42;
116+
const cloned = clone(fn);
117+
expect(cloned).toBe(fn);
118+
});
119+
});
120+
121+
describe('mixed structures', () => {
122+
it('should handle complex mixed structures', () => {
123+
const complex = {
124+
number: 42,
125+
string: 'hello',
126+
bool: true,
127+
null: null,
128+
array: [1, 2, { nested: 'value' }],
129+
object: {
130+
a: [1, 2, 3],
131+
b: { c: 4 },
132+
},
133+
};
134+
const cloned = clone(complex);
135+
expect(cloned).toEqual(complex);
136+
expect(cloned).not.toBe(complex);
137+
expect(cloned.array).not.toBe(complex.array);
138+
expect(cloned.array[2]).not.toBe(complex.array[2]);
139+
expect(cloned.object).not.toBe(complex.object);
140+
expect(cloned.object.a).not.toBe(complex.object.a);
141+
});
142+
143+
it('should preserve primitive values in nested structures', () => {
144+
const obj = { a: { b: { c: 42 } } };
145+
const cloned = clone(obj);
146+
expect(cloned.a.b.c).toBe(42);
147+
});
148+
});
149+
});
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { enumerate } from '../src/enumerable';
3+
4+
describe('enumerate tests', () => {
5+
it('should return empty array for null', () => {
6+
expect(enumerate(null)).toEqual([]);
7+
});
8+
9+
it('should return empty array for undefined', () => {
10+
expect(enumerate(undefined)).toEqual([]);
11+
});
12+
13+
it('should return array as-is', () => {
14+
const arr = [1, 2, 3];
15+
expect(enumerate(arr)).toBe(arr);
16+
expect(enumerate(arr)).toEqual([1, 2, 3]);
17+
});
18+
19+
it('should wrap scalar values in an array', () => {
20+
expect(enumerate(42)).toEqual([42]);
21+
expect(enumerate('hello')).toEqual(['hello']);
22+
expect(enumerate(true)).toEqual([true]);
23+
expect(enumerate(false)).toEqual([false]);
24+
});
25+
26+
it('should handle empty arrays', () => {
27+
const arr: number[] = [];
28+
expect(enumerate(arr)).toBe(arr);
29+
expect(enumerate(arr)).toEqual([]);
30+
});
31+
32+
it('should handle objects', () => {
33+
const obj = { a: 1 };
34+
expect(enumerate(obj)).toEqual([obj]);
35+
});
36+
37+
it('should handle nested arrays', () => {
38+
const arr = [[1, 2], [3, 4]];
39+
expect(enumerate(arr)).toBe(arr);
40+
expect(enumerate(arr)).toEqual([[1, 2], [3, 4]]);
41+
});
42+
43+
it('should handle functions', () => {
44+
const fn = () => 42;
45+
expect(enumerate(fn)).toEqual([fn]);
46+
});
47+
48+
it('should handle zero', () => {
49+
expect(enumerate(0)).toEqual([0]);
50+
});
51+
52+
it('should handle empty string', () => {
53+
expect(enumerate('')).toEqual(['']);
54+
});
55+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { isPlainObject } from '../src/is-plain-object';
3+
4+
describe('isPlainObject tests', () => {
5+
it('should return true for plain objects', () => {
6+
expect(isPlainObject({})).toBe(true);
7+
expect(isPlainObject({ a: 1 })).toBe(true);
8+
expect(isPlainObject({ a: 1, b: { c: 2 } })).toBe(true);
9+
expect(isPlainObject(Object.create(null))).toBe(true);
10+
});
11+
12+
it('should return false for non-plain objects', () => {
13+
expect(isPlainObject(null)).toBe(false);
14+
expect(isPlainObject(undefined)).toBe(false);
15+
expect(isPlainObject(42)).toBe(false);
16+
expect(isPlainObject('string')).toBe(false);
17+
expect(isPlainObject(true)).toBe(false);
18+
expect(isPlainObject(false)).toBe(false);
19+
});
20+
21+
it('should return false for arrays', () => {
22+
expect(isPlainObject([])).toBe(false);
23+
expect(isPlainObject([1, 2, 3])).toBe(false);
24+
});
25+
26+
it('should return false for class instances', () => {
27+
class MyClass {}
28+
expect(isPlainObject(new MyClass())).toBe(false);
29+
expect(isPlainObject(new Date())).toBe(false);
30+
expect(isPlainObject(new Error())).toBe(false);
31+
expect(isPlainObject(new RegExp(''))).toBe(false);
32+
});
33+
34+
it('should return false for functions', () => {
35+
expect(isPlainObject(() => {})).toBe(false);
36+
expect(isPlainObject(function () {})).toBe(false);
37+
});
38+
39+
it('should return true for objects with custom prototype', () => {
40+
// Objects created with Object.create still have isPrototypeOf on their prototype chain
41+
const obj = Object.create({ custom: 'prototype' });
42+
expect(isPlainObject(obj)).toBe(true);
43+
});
44+
45+
it('should handle objects without hasOwnProperty', () => {
46+
const obj = Object.create(null);
47+
obj.foo = 'bar';
48+
expect(isPlainObject(obj)).toBe(true);
49+
});
50+
});

0 commit comments

Comments
 (0)