k8s.io/test-infra@v0.0.0-20240520184403-27c6b4c223d8/gopherage/cmd/html/static/utils_test.ts (about) 1 import "jasmine"; 2 import {enumerate, filter, map, reduce} from './utils'; 3 4 describe('map', () => { 5 it('should map over an array', () => { 6 expect(Array.from(map([1, 3, 5], (x) => 2 * x))).toEqual([2, 6, 10]); 7 }); 8 9 it('should call the function lazily', () => { 10 const spy = jasmine.createSpy('mapper').and.callFake((x: number) => 2 * x); 11 12 const [generatorSpy, input] = iterableSpy([1, 3]); 13 const iterable = map(input, spy); 14 const iterator = iterable[Symbol.iterator](); 15 expect(generatorSpy).not.toHaveBeenCalled(); 16 expect(spy).not.toHaveBeenCalled(); 17 18 let next = iterator.next(); 19 expect(next.value).toBe(2); 20 expect(spy).toHaveBeenCalledTimes(1); 21 expect(spy).toHaveBeenCalledWith(1); 22 expect(generatorSpy).toHaveBeenCalledTimes(1); 23 24 next = iterator.next(); 25 expect(next.value).toBe(6); 26 expect(spy).toHaveBeenCalledTimes(2); 27 expect(spy).toHaveBeenCalledWith(3); 28 expect(generatorSpy).toHaveBeenCalledTimes(2); 29 30 next = iterator.next(); 31 expect(next.done).toBe(true); 32 expect(spy).toHaveBeenCalledTimes(2); 33 expect(generatorSpy).toHaveBeenCalledTimes(3); 34 }); 35 36 it('should accept non-array iterables', () => { 37 function* generator(): Iterable<number> { 38 yield 1; 39 yield 3; 40 } 41 expect(Array.from(map(generator(), (x) => x * 2))).toEqual([2, 6]); 42 }); 43 44 it('should do nothing with empty input', () => { 45 expect(Array.from(map([], (x) => x))).toEqual([]); 46 }); 47 }); 48 49 describe('reduce', () => { 50 it('should reduce non-array iterators', () => { 51 function* generator(): Iterable<number> { 52 yield 1; 53 yield 2; 54 } 55 expect(reduce(generator(), (acc, x) => acc + x, 0)).toBe(3); 56 }); 57 }); 58 59 describe('filter', () => { 60 it('should accept and produce iterables', () => { 61 const [inputSpy, input] = iterableSpy([1, 2, 3, 4]); 62 63 const f = jasmine.createSpy('f').and.callFake((x: number) => x % 2 === 0); 64 const iterable = filter(input, f); 65 const iterator = iterable[Symbol.iterator](); 66 67 expect(f).not.toHaveBeenCalled(); 68 let value = iterator.next(); 69 expect(value.value).toBe(2); 70 expect(f).toHaveBeenCalledTimes(2); 71 expect(inputSpy).toHaveBeenCalledTimes(2); 72 73 value = iterator.next(); 74 expect(value.value).toBe(4); 75 expect(f).toHaveBeenCalledTimes(4); 76 expect(inputSpy).toHaveBeenCalledTimes(4); 77 78 value = iterator.next(); 79 expect(value.done).toBe(true); 80 expect(f).toHaveBeenCalledTimes(4); 81 }); 82 }); 83 84 describe('enumerate', () => { 85 it('should count up', () => { 86 expect(Array.from(enumerate(['hello', 'world']))).toEqual([ 87 [0, 'hello'], [1, 'world'], 88 ]); 89 }); 90 91 it('should accept and produce iterables', () => { 92 const [inputSpy, input] = iterableSpy(['hello', 'world']); 93 94 const iterable = enumerate(input); 95 const iterator = iterable[Symbol.iterator](); 96 97 expect(inputSpy).not.toHaveBeenCalled(); 98 99 let value = iterator.next(); 100 expect(value.value).toEqual([0, 'hello']); 101 expect(inputSpy).toHaveBeenCalledTimes(1); 102 103 value = iterator.next(); 104 expect(value.value).toEqual([1, 'world']); 105 expect(inputSpy).toHaveBeenCalledTimes(2); 106 107 value = iterator.next(); 108 expect(value.done).toBe(true); 109 expect(inputSpy).toHaveBeenCalledTimes(3); 110 }); 111 }); 112 113 // Given an array, returns an iterable that yields the values one at a time, 114 // and also a Spy that lets you observe the usage of that iterable. 115 function iterableSpy<T>(output: T[]): [jasmine.Spy, Iterable<T>] { 116 const iterator = { 117 next(): IteratorResult<T> { 118 if (output.length > 0) { 119 return {value: output.shift()!, done: false}; 120 } else { 121 // IteratorResult<T> is incorrect for finished iterators, apparently. 122 return {done: true} as IteratorResult<T>; 123 } 124 }, 125 }; 126 127 const iterable = { 128 [Symbol.iterator]() { 129 return iterator; 130 }, 131 }; 132 133 const spy = spyOn(iterator, 'next').and.callThrough(); 134 return [spy, iterable]; 135 }