fix: c
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / CI OK (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
Close stale issues / stale (push) Has been cancelled
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / CI OK (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
Close stale issues / stale (push) Has been cancelled
This commit is contained in:
@@ -1,130 +0,0 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { StorageManager } from '../storage-manager';
|
||||
|
||||
describe('storageManager', () => {
|
||||
let storageManager: StorageManager;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
localStorage.clear();
|
||||
storageManager = new StorageManager({
|
||||
prefix: 'test_',
|
||||
});
|
||||
});
|
||||
|
||||
it('should set and get an item', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' });
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toEqual({ age: 30, name: 'John Doe' });
|
||||
});
|
||||
|
||||
it('should return default value if item does not exist', () => {
|
||||
const user = storageManager.getItem('nonexistent', {
|
||||
age: 0,
|
||||
name: 'Default User',
|
||||
});
|
||||
expect(user).toEqual({ age: 0, name: 'Default User' });
|
||||
});
|
||||
|
||||
it('should remove an item', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' });
|
||||
storageManager.removeItem('user');
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toBeNull();
|
||||
});
|
||||
|
||||
it('should clear all items with the prefix', () => {
|
||||
storageManager.setItem('user1', { age: 30, name: 'John Doe' });
|
||||
storageManager.setItem('user2', { age: 25, name: 'Jane Doe' });
|
||||
storageManager.clear();
|
||||
expect(storageManager.getItem('user1')).toBeNull();
|
||||
expect(storageManager.getItem('user2')).toBeNull();
|
||||
});
|
||||
|
||||
it('should clear expired items', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' }, 1000); // 1秒过期
|
||||
vi.advanceTimersByTime(1001); // 快进时间
|
||||
storageManager.clearExpiredItems();
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toBeNull();
|
||||
});
|
||||
|
||||
it('should not clear non-expired items', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' }, 10_000); // 10秒过期
|
||||
vi.advanceTimersByTime(5000); // 快进时间
|
||||
storageManager.clearExpiredItems();
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toEqual({ age: 30, name: 'John Doe' });
|
||||
});
|
||||
|
||||
it('should handle JSON parse errors gracefully', () => {
|
||||
localStorage.setItem('test_user', '{ invalid JSON }');
|
||||
const user = storageManager.getItem('user', {
|
||||
age: 0,
|
||||
name: 'Default User',
|
||||
});
|
||||
expect(user).toEqual({ age: 0, name: 'Default User' });
|
||||
});
|
||||
it('should return null for non-existent items without default value', () => {
|
||||
const user = storageManager.getItem('nonexistent');
|
||||
expect(user).toBeNull();
|
||||
});
|
||||
|
||||
it('should overwrite existing items', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' });
|
||||
storageManager.setItem('user', { age: 25, name: 'Jane Doe' });
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toEqual({ age: 25, name: 'Jane Doe' });
|
||||
});
|
||||
|
||||
it('should handle items without expiry correctly', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' });
|
||||
vi.advanceTimersByTime(5000);
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toEqual({ age: 30, name: 'John Doe' });
|
||||
});
|
||||
|
||||
it('should remove expired items when accessed', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' }, 1000); // 1秒过期
|
||||
vi.advanceTimersByTime(1001); // 快进时间
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toBeNull();
|
||||
});
|
||||
|
||||
it('should not remove non-expired items when accessed', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' }, 10_000); // 10秒过期
|
||||
vi.advanceTimersByTime(5000); // 快进时间
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toEqual({ age: 30, name: 'John Doe' });
|
||||
});
|
||||
|
||||
it('should handle multiple items with different expiry times', () => {
|
||||
storageManager.setItem('user1', { age: 30, name: 'John Doe' }, 1000); // 1秒过期
|
||||
storageManager.setItem('user2', { age: 25, name: 'Jane Doe' }, 2000); // 2秒过期
|
||||
vi.advanceTimersByTime(1500); // 快进时间
|
||||
storageManager.clearExpiredItems();
|
||||
const user1 = storageManager.getItem('user1');
|
||||
const user2 = storageManager.getItem('user2');
|
||||
expect(user1).toBeNull();
|
||||
expect(user2).toEqual({ age: 25, name: 'Jane Doe' });
|
||||
});
|
||||
|
||||
it('should handle items with no expiry', () => {
|
||||
storageManager.setItem('user', { age: 30, name: 'John Doe' });
|
||||
vi.advanceTimersByTime(10_000); // 快进时间
|
||||
storageManager.clearExpiredItems();
|
||||
const user = storageManager.getItem('user');
|
||||
expect(user).toEqual({ age: 30, name: 'John Doe' });
|
||||
});
|
||||
|
||||
it('should clear all items correctly', () => {
|
||||
storageManager.setItem('user1', { age: 30, name: 'John Doe' });
|
||||
storageManager.setItem('user2', { age: 25, name: 'Jane Doe' });
|
||||
storageManager.clear();
|
||||
const user1 = storageManager.getItem('user1');
|
||||
const user2 = storageManager.getItem('user2');
|
||||
expect(user1).toBeNull();
|
||||
expect(user2).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -1,58 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
convertToHsl,
|
||||
convertToHslCssVar,
|
||||
convertToRgb,
|
||||
isValidColor,
|
||||
} from '../convert';
|
||||
|
||||
describe('color conversion functions', () => {
|
||||
it('should correctly convert color to HSL format', () => {
|
||||
const color = '#ff0000';
|
||||
const expectedHsl = 'hsl(0 100% 50%)';
|
||||
expect(convertToHsl(color)).toEqual(expectedHsl);
|
||||
});
|
||||
|
||||
it('should correctly convert color with alpha to HSL format', () => {
|
||||
const color = 'rgba(255, 0, 0, 0.5)';
|
||||
const expectedHsl = 'hsl(0 100% 50%) 0.5';
|
||||
expect(convertToHsl(color)).toEqual(expectedHsl);
|
||||
});
|
||||
|
||||
it('should correctly convert color to HSL CSS variable format', () => {
|
||||
const color = '#ff0000';
|
||||
const expectedHsl = '0 100% 50%';
|
||||
expect(convertToHslCssVar(color)).toEqual(expectedHsl);
|
||||
});
|
||||
|
||||
it('should correctly convert color with alpha to HSL CSS variable format', () => {
|
||||
const color = 'rgba(255, 0, 0, 0.5)';
|
||||
const expectedHsl = '0 100% 50% / 0.5';
|
||||
expect(convertToHslCssVar(color)).toEqual(expectedHsl);
|
||||
});
|
||||
|
||||
it('should correctly convert color to RGB CSS variable format', () => {
|
||||
const color = 'hsl(284, 100%, 50%)';
|
||||
const expectedRgb = 'rgb(187, 0, 255)';
|
||||
expect(convertToRgb(color)).toEqual(expectedRgb);
|
||||
});
|
||||
|
||||
it('should correctly convert color with alpha to RGBA CSS variable format', () => {
|
||||
const color = 'hsla(284, 100%, 50%, 0.92)';
|
||||
const expectedRgba = 'rgba(187, 0, 255, 0.92)';
|
||||
expect(convertToRgb(color)).toEqual(expectedRgba);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isValidColor', () => {
|
||||
it('isValidColor function', () => {
|
||||
// 测试有效颜色
|
||||
expect(isValidColor('blue')).toBe(true);
|
||||
expect(isValidColor('#000000')).toBe(true);
|
||||
|
||||
// 测试无效颜色
|
||||
expect(isValidColor('invalid color')).toBe(false);
|
||||
expect(isValidColor()).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,53 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { diff } from '../diff';
|
||||
|
||||
describe('diff function', () => {
|
||||
it('should return an empty object when comparing identical objects', () => {
|
||||
const obj1 = { a: 1, b: { c: 2 } };
|
||||
const obj2 = { a: 1, b: { c: 2 } };
|
||||
expect(diff(obj1, obj2)).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should detect simple changes in primitive values', () => {
|
||||
const obj1 = { a: 1, b: 2 };
|
||||
const obj2 = { a: 1, b: 3 };
|
||||
expect(diff(obj1, obj2)).toEqual({ b: 3 });
|
||||
});
|
||||
|
||||
it('should detect nested object changes', () => {
|
||||
const obj1 = { a: 1, b: { c: 2, d: 4 } };
|
||||
const obj2 = { a: 1, b: { c: 3, d: 4 } };
|
||||
expect(diff(obj1, obj2)).toEqual({ b: { c: 3 } });
|
||||
});
|
||||
|
||||
it('should handle array changes', () => {
|
||||
const obj1 = { a: [1, 2, 3], b: 2 };
|
||||
const obj2 = { a: [1, 2, 4], b: 2 };
|
||||
expect(diff(obj1, obj2)).toEqual({ a: [1, 2, 4] });
|
||||
});
|
||||
|
||||
it('should handle added keys', () => {
|
||||
const obj1 = { a: 1 };
|
||||
const obj2 = { a: 1, b: 2 };
|
||||
expect(diff(obj1, obj2)).toEqual({ b: 2 });
|
||||
});
|
||||
|
||||
it('should handle removed keys', () => {
|
||||
const obj1 = { a: 1, b: 2 };
|
||||
const obj2 = { a: 1 };
|
||||
expect(diff(obj1, obj2)).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should handle boolean value changes', () => {
|
||||
const obj1 = { a: true, b: false };
|
||||
const obj2 = { a: true, b: true };
|
||||
expect(diff(obj1, obj2)).toEqual({ b: true });
|
||||
});
|
||||
|
||||
it('should handle null and undefined values', () => {
|
||||
const obj1 = { a: null, b: undefined };
|
||||
const obj2: any = { a: 1, b: undefined };
|
||||
expect(diff(obj1, obj2)).toEqual({ a: 1 });
|
||||
});
|
||||
});
|
||||
@@ -1,127 +0,0 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { getElementVisibleRect } from '../dom';
|
||||
|
||||
describe('getElementVisibleRect', () => {
|
||||
// 设置浏览器视口尺寸的 mock
|
||||
beforeEach(() => {
|
||||
vi.spyOn(document.documentElement, 'clientHeight', 'get').mockReturnValue(
|
||||
800,
|
||||
);
|
||||
vi.spyOn(window, 'innerHeight', 'get').mockReturnValue(800);
|
||||
vi.spyOn(document.documentElement, 'clientWidth', 'get').mockReturnValue(
|
||||
1000,
|
||||
);
|
||||
vi.spyOn(window, 'innerWidth', 'get').mockReturnValue(1000);
|
||||
});
|
||||
|
||||
it('should return default rect if element is undefined', () => {
|
||||
expect(getElementVisibleRect()).toEqual({
|
||||
bottom: 0,
|
||||
height: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
width: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return default rect if element is null', () => {
|
||||
expect(getElementVisibleRect(null)).toEqual({
|
||||
bottom: 0,
|
||||
height: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
width: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return correct visible rect when element is fully visible', () => {
|
||||
const element = {
|
||||
getBoundingClientRect: () => ({
|
||||
bottom: 400,
|
||||
height: 300,
|
||||
left: 200,
|
||||
right: 600,
|
||||
top: 100,
|
||||
width: 400,
|
||||
}),
|
||||
} as HTMLElement;
|
||||
|
||||
expect(getElementVisibleRect(element)).toEqual({
|
||||
bottom: 400,
|
||||
height: 300,
|
||||
left: 200,
|
||||
right: 600,
|
||||
top: 100,
|
||||
width: 400,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return correct visible rect when element is partially off-screen at the top', () => {
|
||||
const element = {
|
||||
getBoundingClientRect: () => ({
|
||||
bottom: 200,
|
||||
height: 250,
|
||||
left: 100,
|
||||
right: 500,
|
||||
top: -50,
|
||||
width: 400,
|
||||
}),
|
||||
} as HTMLElement;
|
||||
|
||||
expect(getElementVisibleRect(element)).toEqual({
|
||||
bottom: 200,
|
||||
height: 200,
|
||||
left: 100,
|
||||
right: 500,
|
||||
top: 0,
|
||||
width: 400,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return correct visible rect when element is partially off-screen at the right', () => {
|
||||
const element = {
|
||||
getBoundingClientRect: () => ({
|
||||
bottom: 400,
|
||||
height: 300,
|
||||
left: 800,
|
||||
right: 1200,
|
||||
top: 100,
|
||||
width: 400,
|
||||
}),
|
||||
} as HTMLElement;
|
||||
|
||||
expect(getElementVisibleRect(element)).toEqual({
|
||||
bottom: 400,
|
||||
height: 300,
|
||||
left: 800,
|
||||
right: 1000,
|
||||
top: 100,
|
||||
width: 200,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return all zeros when element is completely off-screen', () => {
|
||||
const element = {
|
||||
getBoundingClientRect: () => ({
|
||||
bottom: 1200,
|
||||
height: 300,
|
||||
left: 1100,
|
||||
right: 1400,
|
||||
top: 900,
|
||||
width: 300,
|
||||
}),
|
||||
} as HTMLElement;
|
||||
|
||||
expect(getElementVisibleRect(element)).toEqual({
|
||||
bottom: 800,
|
||||
height: 0,
|
||||
left: 1100,
|
||||
right: 1000,
|
||||
top: 900,
|
||||
width: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,183 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
getFirstNonNullOrUndefined,
|
||||
isBoolean,
|
||||
isEmpty,
|
||||
isHttpUrl,
|
||||
isObject,
|
||||
isUndefined,
|
||||
isWindow,
|
||||
} from '../inference';
|
||||
|
||||
describe('isHttpUrl', () => {
|
||||
it("should return true when given 'http://example.com'", () => {
|
||||
expect(isHttpUrl('http://example.com')).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when given 'https://example.com'", () => {
|
||||
expect(isHttpUrl('https://example.com')).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false when given 'ftp://example.com'", () => {
|
||||
expect(isHttpUrl('ftp://example.com')).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false when given 'example.com'", () => {
|
||||
expect(isHttpUrl('example.com')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isUndefined', () => {
|
||||
it('isUndefined should return true for undefined values', () => {
|
||||
expect(isUndefined()).toBe(true);
|
||||
});
|
||||
|
||||
it('isUndefined should return false for null values', () => {
|
||||
expect(isUndefined(null)).toBe(false);
|
||||
});
|
||||
|
||||
it('isUndefined should return false for defined values', () => {
|
||||
expect(isUndefined(0)).toBe(false);
|
||||
expect(isUndefined('')).toBe(false);
|
||||
expect(isUndefined(false)).toBe(false);
|
||||
});
|
||||
|
||||
it('isUndefined should return false for objects and arrays', () => {
|
||||
expect(isUndefined({})).toBe(false);
|
||||
expect(isUndefined([])).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isEmpty', () => {
|
||||
it('should return true for empty string', () => {
|
||||
expect(isEmpty('')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for empty array', () => {
|
||||
expect(isEmpty([])).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for empty object', () => {
|
||||
expect(isEmpty({})).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for non-empty string', () => {
|
||||
expect(isEmpty('hello')).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for non-empty array', () => {
|
||||
expect(isEmpty([1, 2, 3])).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for non-empty object', () => {
|
||||
expect(isEmpty({ a: 1 })).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true for null or undefined', () => {
|
||||
expect(isEmpty(null)).toBe(true);
|
||||
expect(isEmpty()).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for number or boolean', () => {
|
||||
expect(isEmpty(0)).toBe(false);
|
||||
expect(isEmpty(true)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isWindow', () => {
|
||||
it('should return true for the window object', () => {
|
||||
expect(isWindow(window)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for other objects', () => {
|
||||
expect(isWindow({})).toBe(false);
|
||||
expect(isWindow([])).toBe(false);
|
||||
expect(isWindow(null)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isBoolean', () => {
|
||||
it('should return true for boolean values', () => {
|
||||
expect(isBoolean(true)).toBe(true);
|
||||
expect(isBoolean(false)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for non-boolean values', () => {
|
||||
expect(isBoolean(null)).toBe(false);
|
||||
expect(isBoolean(42)).toBe(false);
|
||||
expect(isBoolean('string')).toBe(false);
|
||||
expect(isBoolean({})).toBe(false);
|
||||
expect(isBoolean([])).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isObject', () => {
|
||||
it('should return true for objects', () => {
|
||||
expect(isObject({})).toBe(true);
|
||||
expect(isObject({ a: 1 })).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for non-objects', () => {
|
||||
expect(isObject(null)).toBe(false);
|
||||
expect(isObject(42)).toBe(false);
|
||||
expect(isObject('string')).toBe(false);
|
||||
expect(isObject(true)).toBe(false);
|
||||
expect(isObject([1, 2, 3])).toBe(true);
|
||||
expect(isObject(new Date())).toBe(true);
|
||||
expect(isObject(/regex/)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFirstNonNullOrUndefined', () => {
|
||||
describe('getFirstNonNullOrUndefined', () => {
|
||||
it('should return the first non-null and non-undefined value for a number array', () => {
|
||||
expect(getFirstNonNullOrUndefined<number>(undefined, null, 0, 42)).toBe(
|
||||
0,
|
||||
);
|
||||
expect(getFirstNonNullOrUndefined<number>(null, undefined, 42, 123)).toBe(
|
||||
42,
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the first non-null and non-undefined value for a string array', () => {
|
||||
expect(
|
||||
getFirstNonNullOrUndefined<string>(undefined, null, '', 'hello'),
|
||||
).toBe('');
|
||||
expect(
|
||||
getFirstNonNullOrUndefined<string>(null, undefined, 'test', 'world'),
|
||||
).toBe('test');
|
||||
});
|
||||
|
||||
it('should return undefined if all values are null or undefined', () => {
|
||||
expect(getFirstNonNullOrUndefined(undefined, null)).toBeUndefined();
|
||||
expect(getFirstNonNullOrUndefined(null)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should work with a single value', () => {
|
||||
expect(getFirstNonNullOrUndefined(42)).toBe(42);
|
||||
expect(getFirstNonNullOrUndefined()).toBeUndefined();
|
||||
expect(getFirstNonNullOrUndefined(null)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle mixed types correctly', () => {
|
||||
expect(
|
||||
getFirstNonNullOrUndefined<number | object | string>(
|
||||
undefined,
|
||||
null,
|
||||
'test',
|
||||
123,
|
||||
{ key: 'value' },
|
||||
),
|
||||
).toBe('test');
|
||||
expect(
|
||||
getFirstNonNullOrUndefined<number | object | string>(
|
||||
null,
|
||||
undefined,
|
||||
[1, 2, 3],
|
||||
'string',
|
||||
),
|
||||
).toEqual([1, 2, 3]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,116 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
capitalizeFirstLetter,
|
||||
kebabToCamelCase,
|
||||
toCamelCase,
|
||||
toLowerCaseFirstLetter,
|
||||
} from '../letter';
|
||||
|
||||
describe('capitalizeFirstLetter', () => {
|
||||
it('should capitalize the first letter of a string', () => {
|
||||
expect(capitalizeFirstLetter('hello')).toBe('Hello');
|
||||
expect(capitalizeFirstLetter('world')).toBe('World');
|
||||
});
|
||||
|
||||
it('should handle empty strings', () => {
|
||||
expect(capitalizeFirstLetter('')).toBe('');
|
||||
});
|
||||
|
||||
it('should handle single character strings', () => {
|
||||
expect(capitalizeFirstLetter('a')).toBe('A');
|
||||
expect(capitalizeFirstLetter('b')).toBe('B');
|
||||
});
|
||||
|
||||
it('should not change the case of other characters', () => {
|
||||
expect(capitalizeFirstLetter('hElLo')).toBe('HElLo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toLowerCaseFirstLetter', () => {
|
||||
it('should convert the first letter to lowercase', () => {
|
||||
expect(toLowerCaseFirstLetter('CommonAppName')).toBe('commonAppName');
|
||||
expect(toLowerCaseFirstLetter('AnotherKeyExample')).toBe(
|
||||
'anotherKeyExample',
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the same string if the first letter is already lowercase', () => {
|
||||
expect(toLowerCaseFirstLetter('alreadyLowerCase')).toBe('alreadyLowerCase');
|
||||
});
|
||||
|
||||
it('should handle empty strings', () => {
|
||||
expect(toLowerCaseFirstLetter('')).toBe('');
|
||||
});
|
||||
|
||||
it('should handle single character strings', () => {
|
||||
expect(toLowerCaseFirstLetter('A')).toBe('a');
|
||||
expect(toLowerCaseFirstLetter('a')).toBe('a');
|
||||
});
|
||||
|
||||
it('should handle strings with only one uppercase letter', () => {
|
||||
expect(toLowerCaseFirstLetter('A')).toBe('a');
|
||||
});
|
||||
|
||||
it('should handle strings with special characters', () => {
|
||||
expect(toLowerCaseFirstLetter('!Special')).toBe('!Special');
|
||||
expect(toLowerCaseFirstLetter('123Number')).toBe('123Number');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toCamelCase', () => {
|
||||
it('should return the key if parentKey is empty', () => {
|
||||
expect(toCamelCase('child', '')).toBe('child');
|
||||
});
|
||||
|
||||
it('should combine parentKey and key in camel case', () => {
|
||||
expect(toCamelCase('child', 'parent')).toBe('parentChild');
|
||||
});
|
||||
|
||||
it('should handle empty key and parentKey', () => {
|
||||
expect(toCamelCase('', '')).toBe('');
|
||||
});
|
||||
|
||||
it('should handle key with capital letters', () => {
|
||||
expect(toCamelCase('Child', 'parent')).toBe('parentChild');
|
||||
expect(toCamelCase('Child', 'Parent')).toBe('ParentChild');
|
||||
});
|
||||
});
|
||||
|
||||
describe('kebabToCamelCase', () => {
|
||||
it('should convert kebab-case to camelCase correctly', () => {
|
||||
expect(kebabToCamelCase('my-component-name')).toBe('myComponentName');
|
||||
});
|
||||
|
||||
it('should handle multiple consecutive hyphens', () => {
|
||||
expect(kebabToCamelCase('my--component--name')).toBe('myComponentName');
|
||||
});
|
||||
|
||||
it('should trim leading and trailing hyphens', () => {
|
||||
expect(kebabToCamelCase('-my-component-name-')).toBe('myComponentName');
|
||||
});
|
||||
|
||||
it('should preserve the case of the first word', () => {
|
||||
expect(kebabToCamelCase('My-component-name')).toBe('MyComponentName');
|
||||
});
|
||||
|
||||
it('should convert a single word correctly', () => {
|
||||
expect(kebabToCamelCase('component')).toBe('component');
|
||||
});
|
||||
|
||||
it('should return an empty string if input is empty', () => {
|
||||
expect(kebabToCamelCase('')).toBe('');
|
||||
});
|
||||
|
||||
it('should handle strings with no hyphens', () => {
|
||||
expect(kebabToCamelCase('mycomponentname')).toBe('mycomponentname');
|
||||
});
|
||||
|
||||
it('should handle strings with only hyphens', () => {
|
||||
expect(kebabToCamelCase('---')).toBe('');
|
||||
});
|
||||
|
||||
it('should handle mixed case inputs', () => {
|
||||
expect(kebabToCamelCase('my-Component-Name')).toBe('myComponentName');
|
||||
});
|
||||
});
|
||||
@@ -1,60 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { StateHandler } from '../state-handler';
|
||||
|
||||
describe('stateHandler', () => {
|
||||
it('should resolve when condition is set to true', async () => {
|
||||
const handler = new StateHandler();
|
||||
|
||||
// 模拟异步设置 condition 为 true
|
||||
setTimeout(() => {
|
||||
handler.setConditionTrue(); // 明确触发 condition 为 true
|
||||
}, 10);
|
||||
|
||||
// 等待条件被设置为 true
|
||||
await handler.waitForCondition();
|
||||
expect(handler.isConditionTrue()).toBe(true);
|
||||
});
|
||||
|
||||
it('should resolve immediately if condition is already true', async () => {
|
||||
const handler = new StateHandler();
|
||||
handler.setConditionTrue(); // 提前设置为 true
|
||||
|
||||
// 立即 resolve,因为 condition 已经是 true
|
||||
await handler.waitForCondition();
|
||||
expect(handler.isConditionTrue()).toBe(true);
|
||||
});
|
||||
|
||||
it('should reject when condition is set to false after waiting', async () => {
|
||||
const handler = new StateHandler();
|
||||
|
||||
// 模拟异步设置 condition 为 false
|
||||
setTimeout(() => {
|
||||
handler.setConditionFalse(); // 明确触发 condition 为 false
|
||||
}, 10);
|
||||
|
||||
// 等待过程中,期望 Promise 被 reject
|
||||
await expect(handler.waitForCondition()).rejects.toThrow();
|
||||
expect(handler.isConditionTrue()).toBe(false);
|
||||
});
|
||||
|
||||
it('should reset condition to false', () => {
|
||||
const handler = new StateHandler();
|
||||
handler.setConditionTrue(); // 设置为 true
|
||||
handler.reset(); // 重置为 false
|
||||
|
||||
expect(handler.isConditionTrue()).toBe(false);
|
||||
});
|
||||
|
||||
it('should resolve when condition is set to true after reset', async () => {
|
||||
const handler = new StateHandler();
|
||||
handler.reset(); // 确保初始为 false
|
||||
|
||||
setTimeout(() => {
|
||||
handler.setConditionTrue(); // 重置后设置为 true
|
||||
}, 10);
|
||||
|
||||
await handler.waitForCondition();
|
||||
expect(handler.isConditionTrue()).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -1,196 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { filterTree, mapTree, traverseTreeValues } from '../tree';
|
||||
|
||||
describe('traverseTreeValues', () => {
|
||||
interface Node {
|
||||
children?: Node[];
|
||||
name: string;
|
||||
}
|
||||
|
||||
type NodeValue = string;
|
||||
|
||||
const sampleTree: Node[] = [
|
||||
{
|
||||
name: 'A',
|
||||
children: [
|
||||
{ name: 'B' },
|
||||
{
|
||||
name: 'C',
|
||||
children: [{ name: 'D' }, { name: 'E' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'F',
|
||||
children: [
|
||||
{ name: 'G' },
|
||||
{
|
||||
name: 'H',
|
||||
children: [{ name: 'I' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
it('traverses tree and returns all node values', () => {
|
||||
const values = traverseTreeValues<Node, NodeValue>(
|
||||
sampleTree,
|
||||
(node) => node.name,
|
||||
{
|
||||
childProps: 'children',
|
||||
},
|
||||
);
|
||||
expect(values).toEqual(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']);
|
||||
});
|
||||
|
||||
it('handles empty tree', () => {
|
||||
const values = traverseTreeValues<Node, NodeValue>([], (node) => node.name);
|
||||
expect(values).toEqual([]);
|
||||
});
|
||||
|
||||
it('handles tree with only root node', () => {
|
||||
const rootNode = { name: 'A' };
|
||||
const values = traverseTreeValues<Node, NodeValue>(
|
||||
[rootNode],
|
||||
(node) => node.name,
|
||||
);
|
||||
expect(values).toEqual(['A']);
|
||||
});
|
||||
|
||||
it('handles tree with only leaf nodes', () => {
|
||||
const leafNodes = [{ name: 'A' }, { name: 'B' }, { name: 'C' }];
|
||||
const values = traverseTreeValues<Node, NodeValue>(
|
||||
leafNodes,
|
||||
(node) => node.name,
|
||||
);
|
||||
expect(values).toEqual(['A', 'B', 'C']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterTree', () => {
|
||||
const tree = [
|
||||
{
|
||||
id: 1,
|
||||
children: [
|
||||
{ id: 2 },
|
||||
{ id: 3, children: [{ id: 4 }, { id: 5 }, { id: 6 }] },
|
||||
{ id: 7 },
|
||||
],
|
||||
},
|
||||
{ id: 8, children: [{ id: 9 }, { id: 10 }] },
|
||||
{ id: 11 },
|
||||
];
|
||||
|
||||
it('should return all nodes when condition is always true', () => {
|
||||
const result = filterTree(tree, () => true, { childProps: 'children' });
|
||||
expect(result).toEqual(tree);
|
||||
});
|
||||
|
||||
it('should return only root nodes when condition is always false', () => {
|
||||
const result = filterTree(tree, () => false);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return nodes with even id values', () => {
|
||||
const result = filterTree(tree, (node) => node.id % 2 === 0);
|
||||
expect(result).toEqual([{ id: 8, children: [{ id: 10 }] }]);
|
||||
});
|
||||
|
||||
it('should return nodes with odd id values and their ancestors', () => {
|
||||
const result = filterTree(tree, (node) => node.id % 2 === 1);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
id: 1,
|
||||
children: [{ id: 3, children: [{ id: 5 }] }, { id: 7 }],
|
||||
},
|
||||
{ id: 11 },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return nodes with "leaf" in their name', () => {
|
||||
const tree = [
|
||||
{
|
||||
name: 'root',
|
||||
children: [
|
||||
{ name: 'leaf 1' },
|
||||
{
|
||||
name: 'branch',
|
||||
children: [{ name: 'leaf 2' }, { name: 'leaf 3' }],
|
||||
},
|
||||
{ name: 'leaf 4' },
|
||||
],
|
||||
},
|
||||
];
|
||||
const result = filterTree(
|
||||
tree,
|
||||
(node) => node.name.includes('leaf') || node.name === 'root',
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
name: 'root',
|
||||
children: [{ name: 'leaf 1' }, { name: 'leaf 4' }],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mapTree', () => {
|
||||
it('map infinite depth tree using mapTree', () => {
|
||||
const tree = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'node1',
|
||||
children: [
|
||||
{ id: 2, name: 'node2' },
|
||||
{ id: 3, name: 'node3' },
|
||||
{
|
||||
id: 4,
|
||||
name: 'node4',
|
||||
children: [
|
||||
{
|
||||
id: 5,
|
||||
name: 'node5',
|
||||
children: [
|
||||
{ id: 6, name: 'node6' },
|
||||
{ id: 7, name: 'node7' },
|
||||
],
|
||||
},
|
||||
{ id: 8, name: 'node8' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const newTree = mapTree(tree, (node) => ({
|
||||
...node,
|
||||
name: `${node.name}-new`,
|
||||
}));
|
||||
|
||||
expect(newTree).toEqual([
|
||||
{
|
||||
id: 1,
|
||||
name: 'node1-new',
|
||||
children: [
|
||||
{ id: 2, name: 'node2-new' },
|
||||
{ id: 3, name: 'node3-new' },
|
||||
{
|
||||
id: 4,
|
||||
name: 'node4-new',
|
||||
children: [
|
||||
{
|
||||
id: 5,
|
||||
name: 'node5-new',
|
||||
children: [
|
||||
{ id: 6, name: 'node6-new' },
|
||||
{ id: 7, name: 'node7-new' },
|
||||
],
|
||||
},
|
||||
{ id: 8, name: 'node8-new' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,60 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { uniqueByField } from '../unique';
|
||||
|
||||
describe('uniqueByField', () => {
|
||||
it('should return an array with unique items based on id field', () => {
|
||||
const items = [
|
||||
{ id: 1, name: 'Item 1' },
|
||||
{ id: 2, name: 'Item 2' },
|
||||
{ id: 3, name: 'Item 3' },
|
||||
{ id: 1, name: 'Duplicate Item' },
|
||||
];
|
||||
|
||||
const uniqueItems = uniqueByField(items, 'id');
|
||||
|
||||
expect(uniqueItems).toHaveLength(3);
|
||||
expect(uniqueItems).toEqual([
|
||||
{ id: 1, name: 'Item 1' },
|
||||
{ id: 2, name: 'Item 2' },
|
||||
{ id: 3, name: 'Item 3' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an empty array when input array is empty', () => {
|
||||
const items: any[] = []; // Empty array
|
||||
|
||||
const uniqueItems = uniqueByField(items, 'id');
|
||||
|
||||
// Assert expected results
|
||||
expect(uniqueItems).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle arrays with only one item correctly', () => {
|
||||
const items = [{ id: 1, name: 'Item 1' }];
|
||||
|
||||
const uniqueItems = uniqueByField(items, 'id');
|
||||
|
||||
// Assert expected results
|
||||
expect(uniqueItems).toHaveLength(1);
|
||||
expect(uniqueItems).toEqual([{ id: 1, name: 'Item 1' }]);
|
||||
});
|
||||
|
||||
it('should preserve the order of the first occurrence of each item', () => {
|
||||
const items = [
|
||||
{ id: 2, name: 'Item 2' },
|
||||
{ id: 1, name: 'Item 1' },
|
||||
{ id: 3, name: 'Item 3' },
|
||||
{ id: 1, name: 'Duplicate Item' },
|
||||
];
|
||||
|
||||
const uniqueItems = uniqueByField(items, 'id');
|
||||
|
||||
// Assert expected results (order of first occurrences preserved)
|
||||
expect(uniqueItems).toEqual([
|
||||
{ id: 2, name: 'Item 2' },
|
||||
{ id: 1, name: 'Item 1' },
|
||||
{ id: 3, name: 'Item 3' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,30 +0,0 @@
|
||||
import { expect, it } from 'vitest';
|
||||
|
||||
import { updateCSSVariables } from '../update-css-variables';
|
||||
|
||||
it('updateCSSVariables should update CSS variables in :root selector', () => {
|
||||
// 模拟初始的内联样式表内容
|
||||
const initialStyleContent = ':root { --primaryColor: red; }';
|
||||
document.head.innerHTML = `<style id="custom-styles">${initialStyleContent}</style>`;
|
||||
|
||||
// 要更新的CSS变量和它们的新值
|
||||
const updatedVariables = {
|
||||
fontSize: '16px',
|
||||
primaryColor: 'blue',
|
||||
secondaryColor: 'green',
|
||||
};
|
||||
|
||||
// 调用函数来更新CSS变量
|
||||
updateCSSVariables(updatedVariables, 'custom-styles');
|
||||
|
||||
// 获取更新后的样式内容
|
||||
const styleElement = document.querySelector('#custom-styles');
|
||||
const updatedStyleContent = styleElement ? styleElement.textContent : '';
|
||||
|
||||
// 检查更新后的样式内容是否包含正确的更新值
|
||||
expect(
|
||||
updatedStyleContent?.includes('primaryColor: blue;') &&
|
||||
updatedStyleContent?.includes('secondaryColor: green;') &&
|
||||
updatedStyleContent?.includes('fontSize: 16px;'),
|
||||
).toBe(true);
|
||||
});
|
||||
@@ -1,156 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { bindMethods, getNestedValue } from '../util';
|
||||
|
||||
class TestClass {
|
||||
public value: string;
|
||||
|
||||
constructor(value: string) {
|
||||
this.value = value;
|
||||
bindMethods(this); // 调用通用方法
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
setValue(newValue: string) {
|
||||
this.value = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
describe('bindMethods', () => {
|
||||
it('should bind methods to the instance correctly', () => {
|
||||
const instance = new TestClass('initial');
|
||||
|
||||
// 解构方法
|
||||
const { getValue } = instance;
|
||||
|
||||
// 检查 getValue 是否能正确调用,并且 this 绑定了 instance
|
||||
expect(getValue()).toBe('initial');
|
||||
});
|
||||
|
||||
it('should bind multiple methods', () => {
|
||||
const instance = new TestClass('initial');
|
||||
|
||||
const { getValue, setValue } = instance;
|
||||
|
||||
// 检查 getValue 和 setValue 方法是否正确绑定了 this
|
||||
setValue('newValue');
|
||||
expect(getValue()).toBe('newValue');
|
||||
});
|
||||
|
||||
it('should not bind non-function properties', () => {
|
||||
const instance = new TestClass('initial');
|
||||
|
||||
// 检查普通属性是否保持原样
|
||||
expect(instance.value).toBe('initial');
|
||||
});
|
||||
|
||||
it('should not bind constructor method', () => {
|
||||
const instance = new TestClass('test');
|
||||
|
||||
// 检查 constructor 是否没有被绑定
|
||||
expect(instance.constructor.name).toBe('TestClass');
|
||||
});
|
||||
|
||||
it('should not bind getter/setter properties', () => {
|
||||
class TestWithGetterSetter {
|
||||
get value() {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
set value(newValue: string) {
|
||||
this._value = newValue;
|
||||
}
|
||||
|
||||
private _value: string = 'test';
|
||||
|
||||
constructor() {
|
||||
bindMethods(this);
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new TestWithGetterSetter();
|
||||
const { value } = instance;
|
||||
|
||||
// Getter 和 setter 不应被绑定
|
||||
expect(value).toBe('test');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNestedValue', () => {
|
||||
interface UserProfile {
|
||||
age: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface UserSettings {
|
||||
theme: string;
|
||||
}
|
||||
|
||||
interface Data {
|
||||
user: {
|
||||
profile: UserProfile;
|
||||
settings: UserSettings;
|
||||
};
|
||||
}
|
||||
|
||||
const data: Data = {
|
||||
user: {
|
||||
profile: {
|
||||
age: 25,
|
||||
name: 'Alice',
|
||||
},
|
||||
settings: {
|
||||
theme: 'dark',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it('should get a nested value when the path is valid', () => {
|
||||
const result = getNestedValue(data, 'user.profile.name');
|
||||
expect(result).toBe('Alice');
|
||||
});
|
||||
|
||||
it('should return undefined for non-existent property', () => {
|
||||
const result = getNestedValue(data, 'user.profile.gender');
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return undefined when accessing a non-existent deep path', () => {
|
||||
const result = getNestedValue(data, 'user.nonexistent.field');
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return undefined if a middle level is undefined', () => {
|
||||
const result = getNestedValue({ user: undefined }, 'user.profile.name');
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return the correct value for a nested setting', () => {
|
||||
const result = getNestedValue(data, 'user.settings.theme');
|
||||
expect(result).toBe('dark');
|
||||
});
|
||||
|
||||
it('should work for a single-level path', () => {
|
||||
const result = getNestedValue({ a: 1, b: 2 }, 'b');
|
||||
expect(result).toBe(2);
|
||||
});
|
||||
|
||||
it('should return the entire object if path is empty', () => {
|
||||
expect(() => getNestedValue(data, '')()).toThrow();
|
||||
});
|
||||
|
||||
it('should handle paths with array indexes', () => {
|
||||
const complexData = { list: [{ name: 'Item1' }, { name: 'Item2' }] };
|
||||
const result = getNestedValue(complexData, 'list.1.name');
|
||||
expect(result).toBe('Item2');
|
||||
});
|
||||
|
||||
it('should return undefined when accessing an out-of-bounds array index', () => {
|
||||
const complexData = { list: [{ name: 'Item1' }] };
|
||||
const result = getNestedValue(complexData, 'list.2.name');
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -1,33 +0,0 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { openWindow } from '../window';
|
||||
|
||||
describe('openWindow', () => {
|
||||
// 保存原始的 window.open 函数
|
||||
let originalOpen: typeof window.open;
|
||||
|
||||
beforeEach(() => {
|
||||
originalOpen = window.open;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.open = originalOpen;
|
||||
});
|
||||
|
||||
it('should call window.open with correct arguments', () => {
|
||||
const url = 'https://example.com';
|
||||
const options = { noopener: true, noreferrer: true, target: '_blank' };
|
||||
|
||||
window.open = vi.fn();
|
||||
|
||||
// 调用函数
|
||||
openWindow(url, options);
|
||||
|
||||
// 验证 window.open 是否被正确地调用
|
||||
expect(window.open).toHaveBeenCalledWith(
|
||||
url,
|
||||
options.target,
|
||||
'noopener=yes,noreferrer=yes',
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user