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
303 changes: 278 additions & 25 deletions src/app/stores/task-store/task.effects.spec.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,306 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { TestBed } from '@angular/core/testing';
import { of, throwError } from 'rxjs';
import { TaskService } from '../../services/task.service';
import { signalStore, withState, type } from '@ngrx/signals';
import { withEntities } from '@ngrx/signals/entities';
import { withTaskEffects } from './task.effects';
import { TaskStatus, Task } from '../../interfaces/task';
import { TaskService } from '../../services/task.service';
import { taskPageEvents } from './task.events';
import { Task, TaskStatus } from '../../interfaces/task';
import { of, throwError } from 'rxjs';
import { injectDispatch } from '@ngrx/signals/events';

describe('Task Effects', () => {
let taskService: TaskService;
let mockTaskService: {
getTasks: ReturnType<typeof vi.fn>;
createTask: ReturnType<typeof vi.fn>;
deleteTask: ReturnType<typeof vi.fn>;
updateTaskStatus: ReturnType<typeof vi.fn>;
};

beforeEach(() => {
const taskServiceMock = {
mockTaskService = {
getTasks: vi.fn(),
createTask: vi.fn(),
deleteTask: vi.fn(),
updateTaskStatus: vi.fn(),
};

TestBed.configureTestingModule({
providers: [{ provide: TaskService, useValue: taskServiceMock }],
providers: [{ provide: TaskService, useValue: mockTaskService }],
});

taskService = TestBed.inject(TaskService);
});

it('should dispatch tasksLoadedSuccess when tasks load successfully', () => {
const mockResponse = {
tasks: [
describe('loadTasks$ effect', () => {
it('should call getTasks service method when opened event is dispatched', async () => {
const mockTasks: Task[] = [
{
id: '1',
title: 'Test',
title: 'Test Task',
status: TaskStatus.TODO,
createdAt: new Date().toISOString(),
},
] as Task[],
totalPages: 1,
};
vi.mocked(taskService.getTasks).mockReturnValue(of(mockResponse));
];
mockTaskService.getTasks.mockReturnValue(
of({ tasks: mockTasks, totalPages: 1 })
);

const TestStore = signalStore(
withState({ isLoading: false }),
withEntities({ entity: type<Task>(), collection: 'task' }),
withTaskEffects()
);

TestBed.configureTestingModule({
providers: [
TestStore,
{ provide: TaskService, useValue: mockTaskService },
],
});

TestBed.inject(TestStore);
const dispatch = TestBed.runInInjectionContext(() =>
injectDispatch(taskPageEvents)
);

dispatch.opened();

await new Promise(resolve => setTimeout(resolve, 100));

expect(mockTaskService.getTasks).toHaveBeenCalledWith(1, 10);
});

it('should dispatch tasksLoadedFailure on service error', async () => {
mockTaskService.getTasks.mockReturnValue(
throwError(() => ({ message: 'Network error' }))
);

const TestStore = signalStore(
withState({ isLoading: false }),
withEntities({ entity: type<Task>(), collection: 'task' }),
withTaskEffects()
);

TestBed.configureTestingModule({
providers: [
TestStore,
{ provide: TaskService, useValue: mockTaskService },
],
});

TestBed.inject(TestStore);
const dispatch = TestBed.runInInjectionContext(() =>
injectDispatch(taskPageEvents)
);

dispatch.opened();

await new Promise(resolve => setTimeout(resolve, 100));

expect(mockTaskService.getTasks).toHaveBeenCalled();
});
});

describe('createTask$ effect', () => {
it('should call createTask service method when taskCreated event is dispatched', async () => {
const newTask: Task = {
id: '2',
title: 'New Task',
description: 'Description',
status: TaskStatus.TODO,
createdAt: new Date().toISOString(),
};
mockTaskService.createTask.mockReturnValue(of(newTask));

const TestStore = signalStore(
withState({ isLoading: false }),
withEntities({ entity: type<Task>(), collection: 'task' }),
withTaskEffects()
);

TestBed.configureTestingModule({
providers: [
TestStore,
{ provide: TaskService, useValue: mockTaskService },
],
});

TestBed.inject(TestStore);
const dispatch = TestBed.runInInjectionContext(() =>
injectDispatch(taskPageEvents)
);

dispatch.taskCreated({
title: newTask.title,
description: newTask.description,
status: newTask.status,
});

await new Promise(resolve => setTimeout(resolve, 100));

expect(mockTaskService.createTask).toHaveBeenCalledWith({
title: newTask.title,
description: newTask.description,
status: newTask.status,
});
});

it('should dispatch taskCreatedFailure on service error', async () => {
mockTaskService.createTask.mockReturnValue(
throwError(() => ({ message: 'Creation failed' }))
);

const TestStore = signalStore(
withState({ isLoading: false }),
withEntities({ entity: type<Task>(), collection: 'task' }),
withTaskEffects()
);

TestBed.configureTestingModule({
providers: [
TestStore,
{ provide: TaskService, useValue: mockTaskService },
],
});

TestBed.inject(TestStore);
const dispatch = TestBed.runInInjectionContext(() =>
injectDispatch(taskPageEvents)
);

// Test the effects by triggering events
const effects = withTaskEffects();
expect(effects).toBeDefined();
dispatch.taskCreated({
title: 'Test',
status: TaskStatus.TODO,
});

await new Promise(resolve => setTimeout(resolve, 100));

expect(mockTaskService.createTask).toHaveBeenCalled();
});
});

describe('deleteTask$ effect', () => {
it('should call deleteTask service method when taskDeleted event is dispatched', async () => {
const taskId = '1';
mockTaskService.deleteTask.mockReturnValue(of(void 0));

const TestStore = signalStore(
withState({ isLoading: false }),
withEntities({ entity: type<Task>(), collection: 'task' }),
withTaskEffects()
);

TestBed.configureTestingModule({
providers: [
TestStore,
{ provide: TaskService, useValue: mockTaskService },
],
});

TestBed.inject(TestStore);
const dispatch = TestBed.runInInjectionContext(() =>
injectDispatch(taskPageEvents)
);

dispatch.taskDeleted(taskId);

await new Promise(resolve => setTimeout(resolve, 100));

expect(mockTaskService.deleteTask).toHaveBeenCalledWith(taskId);
});

it('should dispatch taskDeletedFailure on service error', async () => {
mockTaskService.deleteTask.mockReturnValue(
throwError(() => ({ message: 'Deletion failed' }))
);

const TestStore = signalStore(
withState({ isLoading: false }),
withEntities({ entity: type<Task>(), collection: 'task' }),
withTaskEffects()
);

TestBed.configureTestingModule({
providers: [
TestStore,
{ provide: TaskService, useValue: mockTaskService },
],
});

TestBed.inject(TestStore);
const dispatch = TestBed.runInInjectionContext(() =>
injectDispatch(taskPageEvents)
);

dispatch.taskDeleted('1');

await new Promise(resolve => setTimeout(resolve, 100));

expect(mockTaskService.deleteTask).toHaveBeenCalled();
});
});

it('should dispatch tasksLoadedFailure on error', () => {
vi.mocked(taskService.getTasks).mockReturnValue(
throwError(() => new Error('API Error'))
);
describe('changeTaskStatus$ effect', () => {
it('should call updateTaskStatus service method when taskStatusChanged event is dispatched', async () => {
const taskId = '1';
const newStatus = TaskStatus.IN_PROGRESS;
mockTaskService.updateTaskStatus.mockReturnValue(of(void 0));

const TestStore = signalStore(
withState({ isLoading: false }),
withEntities({ entity: type<Task>(), collection: 'task' }),
withTaskEffects()
);

TestBed.configureTestingModule({
providers: [
TestStore,
{ provide: TaskService, useValue: mockTaskService },
],
});

const effects = withTaskEffects();
expect(effects).toBeDefined();
TestBed.inject(TestStore);
const dispatch = TestBed.runInInjectionContext(() =>
injectDispatch(taskPageEvents)
);

dispatch.taskStatusChanged({ id: taskId, status: newStatus });

await new Promise(resolve => setTimeout(resolve, 100));

expect(mockTaskService.updateTaskStatus).toHaveBeenCalledWith(
taskId,
newStatus
);
});

it('should dispatch taskStatusChangedFailure on service error', async () => {
mockTaskService.updateTaskStatus.mockReturnValue(
throwError(() => ({ message: 'Update failed' }))
);

const TestStore = signalStore(
withState({ isLoading: false }),
withEntities({ entity: type<Task>(), collection: 'task' }),
withTaskEffects()
);

TestBed.configureTestingModule({
providers: [
TestStore,
{ provide: TaskService, useValue: mockTaskService },
],
});

TestBed.inject(TestStore);
const dispatch = TestBed.runInInjectionContext(() =>
injectDispatch(taskPageEvents)
);

dispatch.taskStatusChanged({ id: '1', status: TaskStatus.DONE });

await new Promise(resolve => setTimeout(resolve, 100));

expect(mockTaskService.updateTaskStatus).toHaveBeenCalled();
});
});
});
Loading