test(splitArray): add unit tests for splitArray util
This commit is contained in:
405
src/shared/lib/utils/splitArray/splitArray.test.ts
Normal file
405
src/shared/lib/utils/splitArray/splitArray.test.ts
Normal file
@@ -0,0 +1,405 @@
|
||||
import {
|
||||
describe,
|
||||
expect,
|
||||
it,
|
||||
} from 'vitest';
|
||||
import { splitArray } from './splitArray';
|
||||
|
||||
describe('splitArray', () => {
|
||||
describe('Basic Functionality', () => {
|
||||
it('should split an array into two arrays based on callback', () => {
|
||||
const input = [1, 2, 3, 4, 5];
|
||||
const [pass, fail] = splitArray(input, n => n > 2);
|
||||
|
||||
expect(pass).toEqual([3, 4, 5]);
|
||||
expect(fail).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
it('should return two arrays', () => {
|
||||
const result = splitArray([1, 2, 3], () => true);
|
||||
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
expect(result).toHaveLength(2);
|
||||
expect(Array.isArray(result[0])).toBe(true);
|
||||
expect(Array.isArray(result[1])).toBe(true);
|
||||
});
|
||||
|
||||
it('should preserve original array', () => {
|
||||
const input = [1, 2, 3, 4, 5];
|
||||
const original = [...input];
|
||||
|
||||
splitArray(input, n => n % 2 === 0);
|
||||
|
||||
expect(input).toEqual(original);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Empty Array', () => {
|
||||
it('should return two empty arrays for empty input', () => {
|
||||
const [pass, fail] = splitArray([], () => true);
|
||||
|
||||
expect(pass).toEqual([]);
|
||||
expect(fail).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle empty array with falsy callback', () => {
|
||||
const [pass, fail] = splitArray([], () => false);
|
||||
|
||||
expect(pass).toEqual([]);
|
||||
expect(fail).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('All Pass', () => {
|
||||
it('should put all elements in pass array when callback returns true for all', () => {
|
||||
const input = [1, 2, 3, 4, 5];
|
||||
const [pass, fail] = splitArray(input, () => true);
|
||||
|
||||
expect(pass).toEqual([1, 2, 3, 4, 5]);
|
||||
expect(fail).toEqual([]);
|
||||
});
|
||||
|
||||
it('should put all elements in pass array using always-true condition', () => {
|
||||
const input = ['a', 'b', 'c'];
|
||||
const [pass, fail] = splitArray(input, s => s.length > 0);
|
||||
|
||||
expect(pass).toEqual(['a', 'b', 'c']);
|
||||
expect(fail).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('All Fail', () => {
|
||||
it('should put all elements in fail array when callback returns false for all', () => {
|
||||
const input = [1, 2, 3, 4, 5];
|
||||
const [pass, fail] = splitArray(input, () => false);
|
||||
|
||||
expect(pass).toEqual([]);
|
||||
expect(fail).toEqual([1, 2, 3, 4, 5]);
|
||||
});
|
||||
|
||||
it('should put all elements in fail array using always-false condition', () => {
|
||||
const input = ['a', 'b', 'c'];
|
||||
const [pass, fail] = splitArray(input, s => s.length > 10);
|
||||
|
||||
expect(pass).toEqual([]);
|
||||
expect(fail).toEqual(['a', 'b', 'c']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Mixed Results', () => {
|
||||
it('should split even and odd numbers', () => {
|
||||
const input = [1, 2, 3, 4, 5, 6];
|
||||
const [even, odd] = splitArray(input, n => n % 2 === 0);
|
||||
|
||||
expect(even).toEqual([2, 4, 6]);
|
||||
expect(odd).toEqual([1, 3, 5]);
|
||||
});
|
||||
|
||||
it('should split positive and negative numbers', () => {
|
||||
const input = [-3, -2, -1, 0, 1, 2, 3];
|
||||
const [positive, negative] = splitArray(input, n => n >= 0);
|
||||
|
||||
expect(positive).toEqual([0, 1, 2, 3]);
|
||||
expect(negative).toEqual([-3, -2, -1]);
|
||||
});
|
||||
|
||||
it('should split strings by length', () => {
|
||||
const input = ['a', 'ab', 'abc', 'abcd'];
|
||||
const [long, short] = splitArray(input, s => s.length >= 3);
|
||||
|
||||
expect(long).toEqual(['abc', 'abcd']);
|
||||
expect(short).toEqual(['a', 'ab']);
|
||||
});
|
||||
|
||||
it('should split objects by property', () => {
|
||||
interface Item {
|
||||
id: number;
|
||||
active: boolean;
|
||||
}
|
||||
const input: Item[] = [
|
||||
{ id: 1, active: true },
|
||||
{ id: 2, active: false },
|
||||
{ id: 3, active: true },
|
||||
{ id: 4, active: false },
|
||||
];
|
||||
const [active, inactive] = splitArray(input, item => item.active);
|
||||
|
||||
expect(active).toEqual([
|
||||
{ id: 1, active: true },
|
||||
{ id: 3, active: true },
|
||||
]);
|
||||
expect(inactive).toEqual([
|
||||
{ id: 2, active: false },
|
||||
{ id: 4, active: false },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Type Safety', () => {
|
||||
it('should work with number arrays', () => {
|
||||
const [pass, fail] = splitArray([1, 2, 3], n => n > 1);
|
||||
|
||||
expect(pass).toEqual([2, 3]);
|
||||
expect(fail).toEqual([1]);
|
||||
|
||||
// Type check - should be numbers
|
||||
const sum = pass[0] + pass[1];
|
||||
expect(sum).toBe(5);
|
||||
});
|
||||
|
||||
it('should work with string arrays', () => {
|
||||
const [pass, fail] = splitArray(['a', 'bb', 'ccc'], s => s.length > 1);
|
||||
|
||||
expect(pass).toEqual(['bb', 'ccc']);
|
||||
expect(fail).toEqual(['a']);
|
||||
|
||||
// Type check - should be strings
|
||||
const concatenated = pass.join('');
|
||||
expect(concatenated).toBe('bbccc');
|
||||
});
|
||||
|
||||
it('should work with boolean arrays', () => {
|
||||
const [pass, fail] = splitArray([true, false, true], b => b);
|
||||
|
||||
expect(pass).toEqual([true, true]);
|
||||
expect(fail).toEqual([false]);
|
||||
});
|
||||
|
||||
it('should work with generic objects', () => {
|
||||
interface Person {
|
||||
name: string;
|
||||
age: number;
|
||||
}
|
||||
const people: Person[] = [
|
||||
{ name: 'Alice', age: 25 },
|
||||
{ name: 'Bob', age: 30 },
|
||||
{ name: 'Charlie', age: 20 },
|
||||
];
|
||||
const [adults, minors] = splitArray(people, p => p.age >= 21);
|
||||
|
||||
expect(adults).toEqual([
|
||||
{ name: 'Alice', age: 25 },
|
||||
{ name: 'Bob', age: 30 },
|
||||
]);
|
||||
expect(minors).toEqual([{ name: 'Charlie', age: 20 }]);
|
||||
});
|
||||
|
||||
it('should work with null and undefined', () => {
|
||||
const input = [null, undefined, 1, 0, ''];
|
||||
const [truthy, falsy] = splitArray(input, item => !!item);
|
||||
|
||||
expect(truthy).toEqual([1]);
|
||||
expect(falsy).toEqual([null, undefined, 0, '']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Callback Functions', () => {
|
||||
it('should support arrow function syntax', () => {
|
||||
const [pass, fail] = splitArray([1, 2, 3, 4], x => x % 2 === 0);
|
||||
|
||||
expect(pass).toEqual([2, 4]);
|
||||
expect(fail).toEqual([1, 3]);
|
||||
});
|
||||
|
||||
it('should support regular function syntax', () => {
|
||||
const [pass, fail] = splitArray([1, 2, 3, 4], function(x) {
|
||||
return x % 2 === 0;
|
||||
});
|
||||
|
||||
expect(pass).toEqual([2, 4]);
|
||||
expect(fail).toEqual([1, 3]);
|
||||
});
|
||||
|
||||
it('should support inline conditions', () => {
|
||||
const input = [1, 2, 3, 4, 5];
|
||||
const [greaterThan3, others] = splitArray(input, x => x > 3);
|
||||
|
||||
expect(greaterThan3).toEqual([4, 5]);
|
||||
expect(others).toEqual([1, 2, 3]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Order Preservation', () => {
|
||||
it('should maintain order within each resulting array', () => {
|
||||
const input = [5, 1, 4, 2, 3];
|
||||
const [greaterThan2, lessOrEqual] = splitArray(input, n => n > 2);
|
||||
|
||||
expect(greaterThan2).toEqual([5, 4, 3]);
|
||||
expect(lessOrEqual).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
it('should preserve relative order for complex objects', () => {
|
||||
interface Item {
|
||||
id: number;
|
||||
value: string;
|
||||
}
|
||||
const input: Item[] = [
|
||||
{ id: 1, value: 'a' },
|
||||
{ id: 2, value: 'b' },
|
||||
{ id: 3, value: 'c' },
|
||||
{ id: 4, value: 'd' },
|
||||
];
|
||||
const [evenIds, oddIds] = splitArray(input, item => item.id % 2 === 0);
|
||||
|
||||
expect(evenIds).toEqual([
|
||||
{ id: 2, value: 'b' },
|
||||
{ id: 4, value: 'd' },
|
||||
]);
|
||||
expect(oddIds).toEqual([
|
||||
{ id: 1, value: 'a' },
|
||||
{ id: 3, value: 'c' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
it('should handle single element array (truthy)', () => {
|
||||
const [pass, fail] = splitArray([1], () => true);
|
||||
|
||||
expect(pass).toEqual([1]);
|
||||
expect(fail).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle single element array (falsy)', () => {
|
||||
const [pass, fail] = splitArray([1], () => false);
|
||||
|
||||
expect(pass).toEqual([]);
|
||||
expect(fail).toEqual([1]);
|
||||
});
|
||||
|
||||
it('should handle two element array', () => {
|
||||
const [pass, fail] = splitArray([1, 2], n => n === 1);
|
||||
|
||||
expect(pass).toEqual([1]);
|
||||
expect(fail).toEqual([2]);
|
||||
});
|
||||
|
||||
it('should handle array with duplicate values', () => {
|
||||
const [pass, fail] = splitArray([1, 1, 2, 2, 1, 1], n => n === 1);
|
||||
|
||||
expect(pass).toEqual([1, 1, 1, 1]);
|
||||
expect(fail).toEqual([2, 2]);
|
||||
});
|
||||
|
||||
it('should handle zero values', () => {
|
||||
const [truthy, falsy] = splitArray([0, 1, 0, 2], Boolean);
|
||||
|
||||
expect(truthy).toEqual([1, 2]);
|
||||
expect(falsy).toEqual([0, 0]);
|
||||
});
|
||||
|
||||
it('should handle NaN values', () => {
|
||||
const input = [1, NaN, 2, NaN, 3];
|
||||
const [numbers, nans] = splitArray(input, n => !Number.isNaN(n));
|
||||
|
||||
expect(numbers).toEqual([1, 2, 3]);
|
||||
expect(nans).toEqual([NaN, NaN]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Large Arrays', () => {
|
||||
it('should handle large arrays efficiently', () => {
|
||||
const largeArray = Array.from({ length: 10000 }, (_, i) => i);
|
||||
const [even, odd] = splitArray(largeArray, n => n % 2 === 0);
|
||||
|
||||
expect(even).toHaveLength(5000);
|
||||
expect(odd).toHaveLength(5000);
|
||||
expect(even[0]).toBe(0);
|
||||
expect(even[9999]).toBeUndefined();
|
||||
expect(even[4999]).toBe(9998);
|
||||
});
|
||||
|
||||
it('should maintain correct results for all elements in large array', () => {
|
||||
const input = Array.from({ length: 1000 }, (_, i) => i);
|
||||
const [multiplesOf3, others] = splitArray(input, n => n % 3 === 0);
|
||||
|
||||
// Verify counts
|
||||
expect(multiplesOf3).toHaveLength(334); // 0, 3, 6, ..., 999
|
||||
expect(others).toHaveLength(666);
|
||||
|
||||
// Verify all multiples of 3 are in correct array
|
||||
multiplesOf3.forEach(n => {
|
||||
expect(n % 3).toBe(0);
|
||||
});
|
||||
|
||||
// Verify no multiples of 3 are in others
|
||||
others.forEach(n => {
|
||||
expect(n % 3).not.toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Real-World Use Cases', () => {
|
||||
it('should separate valid from invalid emails', () => {
|
||||
const emails = [
|
||||
'valid@example.com',
|
||||
'invalid',
|
||||
'another@test.org',
|
||||
'not-an-email',
|
||||
'user@domain.co.uk',
|
||||
];
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
const [valid, invalid] = splitArray(emails, email => emailRegex.test(email));
|
||||
|
||||
expect(valid).toEqual([
|
||||
'valid@example.com',
|
||||
'another@test.org',
|
||||
'user@domain.co.uk',
|
||||
]);
|
||||
expect(invalid).toEqual(['invalid', 'not-an-email']);
|
||||
});
|
||||
|
||||
it('should separate completed from pending tasks', () => {
|
||||
interface Task {
|
||||
id: number;
|
||||
title: string;
|
||||
completed: boolean;
|
||||
}
|
||||
const tasks: Task[] = [
|
||||
{ id: 1, title: 'Task 1', completed: true },
|
||||
{ id: 2, title: 'Task 2', completed: false },
|
||||
{ id: 3, title: 'Task 3', completed: true },
|
||||
{ id: 4, title: 'Task 4', completed: false },
|
||||
];
|
||||
const [completed, pending] = splitArray(tasks, task => task.completed);
|
||||
|
||||
expect(completed).toHaveLength(2);
|
||||
expect(pending).toHaveLength(2);
|
||||
expect(completed.every(t => t.completed)).toBe(true);
|
||||
expect(pending.every(t => !t.completed)).toBe(true);
|
||||
});
|
||||
|
||||
it('should separate adults from minors by age', () => {
|
||||
interface Person {
|
||||
name: string;
|
||||
age: number;
|
||||
}
|
||||
const people: Person[] = [
|
||||
{ name: 'Alice', age: 17 },
|
||||
{ name: 'Bob', age: 25 },
|
||||
{ name: 'Charlie', age: 16 },
|
||||
{ name: 'Diana', age: 30 },
|
||||
{ name: 'Eve', age: 18 },
|
||||
];
|
||||
const [adults, minors] = splitArray(people, person => person.age >= 18);
|
||||
|
||||
expect(adults).toEqual([
|
||||
{ name: 'Bob', age: 25 },
|
||||
{ name: 'Diana', age: 30 },
|
||||
{ name: 'Eve', age: 18 },
|
||||
]);
|
||||
expect(minors).toEqual([
|
||||
{ name: 'Alice', age: 17 },
|
||||
{ name: 'Charlie', age: 16 },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should separate truthy from falsy values', () => {
|
||||
const mixed = [0, 1, false, true, '', 'hello', null, undefined, [], [0]];
|
||||
const [truthy, falsy] = splitArray(mixed, Boolean);
|
||||
|
||||
expect(truthy).toEqual([1, true, 'hello', [], [0]]);
|
||||
expect(falsy).toEqual([0, false, '', null, undefined]);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user