From ff71d1c8c9108e1282afe2ec8b12281e25249c31 Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Wed, 18 Feb 2026 20:18:18 +0300 Subject: [PATCH] test(splitArray): add unit tests for splitArray util --- .../lib/utils/splitArray/splitArray.test.ts | 405 ++++++++++++++++++ 1 file changed, 405 insertions(+) create mode 100644 src/shared/lib/utils/splitArray/splitArray.test.ts diff --git a/src/shared/lib/utils/splitArray/splitArray.test.ts b/src/shared/lib/utils/splitArray/splitArray.test.ts new file mode 100644 index 0000000..f03bbf9 --- /dev/null +++ b/src/shared/lib/utils/splitArray/splitArray.test.ts @@ -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]); + }); + }); +});