Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions src/04-stack/__test__/stack-js.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { describe, expect, test, beforeEach } from '@jest/globals';

// eslint-disable-next-line @typescript-eslint/no-require-imports
const Stack = require('../stack.js') as new () => {
push(item: any): void;
pop(): any;
peek(): any;
isEmpty(): boolean;
size: number;
clear(): void;
toString(): string;
};

describe('Stack (stack.js)', () => {
let stack: InstanceType<typeof Stack>;

beforeEach(() => {
stack = new Stack();
});

test('should create an empty stack', () => {
expect(stack.isEmpty()).toBe(true);
expect(stack.size).toBe(0);
expect(stack.peek()).toBeUndefined();
expect(stack.pop()).toBeUndefined();
expect(stack.toString()).toBe('Empty Stack');
});

test('should push items onto the top of the stack', () => {
stack.push(1);
stack.push(2);
stack.push(3);
expect(stack.size).toBe(3);
expect(stack.isEmpty()).toBe(false);
expect(stack.toString()).toBe('1, 2, 3');
});

test('should peek the top item without removing it', () => {
stack.push('a');
stack.push('b');
expect(stack.peek()).toBe('b');
expect(stack.size).toBe(2);
});

test('should pop items in LIFO order', () => {
stack.push(1);
stack.push(2);
stack.push(3);
expect(stack.pop()).toBe(3);
expect(stack.pop()).toBe(2);
expect(stack.size).toBe(1);
expect(stack.peek()).toBe(1);
expect(stack.pop()).toBe(1);
expect(stack.isEmpty()).toBe(true);
expect(stack.pop()).toBeUndefined();
});

test('should clear all items from the stack', () => {
stack.push(1);
stack.push(2);
stack.clear();
expect(stack.isEmpty()).toBe(true);
expect(stack.size).toBe(0);
expect(stack.toString()).toBe('Empty Stack');
});

test('should stringify object items using JSON.stringify', () => {
stack.push({ a: 1 });
stack.push([1, 2]);
expect(stack.toString()).toBe('{"a":1}, [1,2]');
});

test('should stringify primitive items using String()', () => {
stack.push(1);
stack.push('two');
stack.push(true);
stack.push(null);
stack.push(undefined);
expect(stack.toString()).toBe('1, two, true, null, undefined');
});

test('should handle a single item', () => {
stack.push(42);
expect(stack.size).toBe(1);
expect(stack.peek()).toBe(42);
expect(stack.toString()).toBe('42');
expect(stack.pop()).toBe(42);
expect(stack.isEmpty()).toBe(true);
});
});
2 changes: 1 addition & 1 deletion src/04-stack/stack.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class Stack {
if (typeof item === 'object' && item !== null) { // {2}
return JSON.stringify(item); // Handle objects
} else {
return item.toString(); // Handle other types {3}
return String(item); // Handle other types {3}
}
}).join(', '); // {4}
}
Expand Down
90 changes: 90 additions & 0 deletions src/05-queue-deque/__test__/queue-js.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { describe, expect, test, beforeEach } from '@jest/globals';

// eslint-disable-next-line @typescript-eslint/no-require-imports
const Queue = require('../queue.js') as new () => {
enqueue(item: any): void;
dequeue(): any;
front(): any;
isEmpty(): boolean;
size: number;
clear(): void;
toString(): string;
};

describe('Queue (queue.js)', () => {
let queue: InstanceType<typeof Queue>;

beforeEach(() => {
queue = new Queue();
});

test('should create an empty queue', () => {
expect(queue.isEmpty()).toBe(true);
expect(queue.size).toBe(0);
expect(queue.front()).toBeUndefined();
expect(queue.dequeue()).toBeUndefined();
expect(queue.toString()).toBe('Empty Queue');
});

test('should enqueue items to the rear of the queue', () => {
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
expect(queue.size).toBe(3);
expect(queue.isEmpty()).toBe(false);
expect(queue.toString()).toBe('1, 2, 3');
});

test('should return the front item without removing it', () => {
queue.enqueue('a');
queue.enqueue('b');
expect(queue.front()).toBe('a');
expect(queue.size).toBe(2);
});

test('should dequeue items in FIFO order', () => {
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
expect(queue.dequeue()).toBe(1);
expect(queue.dequeue()).toBe(2);
expect(queue.size).toBe(1);
expect(queue.front()).toBe(3);
expect(queue.dequeue()).toBe(3);
expect(queue.isEmpty()).toBe(true);
expect(queue.dequeue()).toBeUndefined();
});

test('should clear all items from the queue', () => {
queue.enqueue(1);
queue.enqueue(2);
queue.clear();
expect(queue.isEmpty()).toBe(true);
expect(queue.size).toBe(0);
expect(queue.toString()).toBe('Empty Queue');
});

test('should stringify object items using JSON.stringify', () => {
queue.enqueue({ a: 1 });
queue.enqueue([1, 2]);
expect(queue.toString()).toBe('{"a":1}, [1,2]');
});

test('should stringify primitive items using String()', () => {
queue.enqueue(1);
queue.enqueue('two');
queue.enqueue(true);
queue.enqueue(null);
queue.enqueue(undefined);
expect(queue.toString()).toBe('1, two, true, null, undefined');
});

test('should handle a single item', () => {
queue.enqueue(42);
expect(queue.size).toBe(1);
expect(queue.front()).toBe(42);
expect(queue.toString()).toBe('42');
expect(queue.dequeue()).toBe(42);
expect(queue.isEmpty()).toBe(true);
});
});
2 changes: 1 addition & 1 deletion src/05-queue-deque/queue.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Queue {
if (typeof item === 'object' && item !== null) { // {2}
return JSON.stringify(item); // Handle objects
} else {
return item.toString(); // Handle other types {3}
return String(item); // Handle other types {3}
}
}).join(', '); // {4}
}
Expand Down
195 changes: 195 additions & 0 deletions src/06-linked-list/__test__/circular-linked-list_.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import { describe, expect, test, beforeEach } from '@jest/globals';

// eslint-disable-next-line @typescript-eslint/no-require-imports
const CircularLinkedList = require('../circular-linked-list_.js') as new () => {
append(data: any): void;
prepend(data: any): void;
insert(data: any, position: number): boolean;
removeAt(position: number): any;
size: number;
isEmpty(): boolean;
clear(): void;
remove(data: any): any;
indexOf(data: any): number;
toString(): string;
reverse(): void;
};

describe('CircularLinkedList (circular-linked-list_.js)', () => {
let list: InstanceType<typeof CircularLinkedList>;

beforeEach(() => {
list = new CircularLinkedList();
});

test('should create an empty list', () => {
expect(list.isEmpty()).toBe(true);
expect(list.size).toBe(0);
expect(list.toString()).toBe('');
});

test('should append elements and keep the list circular', () => {
list.append(1);
list.append(2);
list.append(3);
expect(list.toString()).toBe('1 -> 2 -> 3');
expect(list.size).toBe(3);
expect(list.isEmpty()).toBe(false);
});

test('should append a single element pointing to itself', () => {
list.append(1);
expect(list.toString()).toBe('1');
expect(list.size).toBe(1);
});

test('should prepend elements to the beginning of the list', () => {
list.append(2);
list.append(3);
list.prepend(1);
expect(list.toString()).toBe('1 -> 2 -> 3');
expect(list.size).toBe(3);
});

test('should prepend to an empty list', () => {
list.prepend(1);
expect(list.toString()).toBe('1');
expect(list.size).toBe(1);
});

test('should keep the list circular after multiple prepends', () => {
list.prepend(3);
list.prepend(2);
list.prepend(1);
expect(list.toString()).toBe('1 -> 2 -> 3');
expect(list.indexOf(3)).toBe(2);
expect(list.size).toBe(3);
});

test('should insert an element at position 0 (prepend behavior)', () => {
list.append(2);
expect(list.insert(1, 0)).toBe(true);
expect(list.toString()).toBe('1 -> 2');
});

test('should insert an element in the middle', () => {
list.append(1);
list.append(3);
expect(list.insert(2, 1)).toBe(true);
expect(list.toString()).toBe('1 -> 2 -> 3');
expect(list.size).toBe(3);
});

test('should return false when inserting at an invalid position', () => {
list.append(1);
expect(list.insert(5, -1)).toBe(false);
expect(list.insert(5, 10)).toBe(false);
expect(list.size).toBe(1);
});

test('should remove from the head', () => {
list.append(1);
list.append(2);
list.append(3);
expect(list.removeAt(0)).toBe(1);
expect(list.toString()).toBe('2 -> 3');
expect(list.size).toBe(2);
});

test('should remove from the tail', () => {
list.append(1);
list.append(2);
list.append(3);
expect(list.removeAt(2)).toBe(3);
expect(list.toString()).toBe('1 -> 2');
expect(list.size).toBe(2);
});

test('should remove from the middle', () => {
list.append(1);
list.append(2);
list.append(3);
list.append(4);
expect(list.removeAt(1)).toBe(2);
expect(list.toString()).toBe('1 -> 3 -> 4');
expect(list.size).toBe(3);
});

test('should remove the only element and become empty', () => {
list.append(1);
expect(list.removeAt(0)).toBe(1);
expect(list.isEmpty()).toBe(true);
expect(list.size).toBe(0);
expect(list.toString()).toBe('');
});

test('should remove the tail of a single-element list', () => {
list.append(1);
expect(list.removeAt(0)).toBe(1);
expect(list.isEmpty()).toBe(true);
});

test('should throw when removing from an empty list', () => {
expect(() => list.removeAt(0)).toThrow(RangeError);
});

test('should throw when removing at an invalid position', () => {
list.append(1);
expect(() => list.removeAt(-1)).toThrow(RangeError);
expect(() => list.removeAt(5)).toThrow(RangeError);
});

test('should keep the list circular after removing from the middle', () => {
list.append(1);
list.append(2);
list.append(3);
list.removeAt(1);
// append again to make sure the internal circular link was preserved
list.append(4);
expect(list.toString()).toBe('1 -> 3 -> 4');
});

test('should find the index of an element', () => {
list.append('a');
list.append('b');
list.append('c');
expect(list.indexOf('b')).toBe(1);
expect(list.indexOf('z')).toBe(-1);
});

test('should return -1 when calling indexOf on an empty list', () => {
expect(list.indexOf('anything')).toBe(-1);
});

test('should remove an element by value', () => {
list.append(1);
list.append(2);
list.append(3);
expect(list.remove(2)).toBe(2);
expect(list.toString()).toBe('1 -> 3');
expect(list.size).toBe(2);
});

test('should return null when removing a value not present', () => {
list.append(1);
expect(list.remove(99)).toBeNull();
expect(list.size).toBe(1);
});

test('should clear all elements', () => {
list.append(1);
list.append(2);
list.clear();
expect(list.isEmpty()).toBe(true);
expect(list.size).toBe(0);
expect(list.toString()).toBe('');
});

test('should reverse the list', () => {
list.append(1);
list.append(2);
list.append(3);
list.reverse();
expect(list.toString().startsWith('3 -> 2 -> 1')).toBe(true);
});
});
Loading
Loading