2026-05-06 09:14:51 -03:00
|
|
|
import React, { act } from 'react';
|
2025-03-13 09:56:57 -07:00
|
|
|
import { render, fireEvent, waitFor } from '@testing-library/react';
|
2026-05-26 11:58:04 -03:00
|
|
|
import userEvent from '@testing-library/user-event';
|
2025-10-31 21:51:32 +00:00
|
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
2025-03-09 14:39:31 -07:00
|
|
|
import { MemoryRouter } from 'react-router';
|
|
|
|
|
import { I18nextProvider } from 'react-i18next';
|
|
|
|
|
import NoteAdd from '../../views/NoteAdd';
|
|
|
|
|
import AuthContext from '../../context/AuthContext';
|
|
|
|
|
import i18n from '../../i18n';
|
|
|
|
|
import api from '../../api-service/api';
|
|
|
|
|
import ApiConfig from '../../api-service/apiConfig';
|
|
|
|
|
import { NoteResponse } from '../../types/NoteResponse';
|
2025-04-21 18:59:55 -03:00
|
|
|
import SidebarContext from '../../context/SidebarContext';
|
2025-03-09 14:39:31 -07:00
|
|
|
|
2025-06-10 16:04:26 -03:00
|
|
|
// Mock the entire api module
|
|
|
|
|
vi.mock('../../api-service/api', () => ({
|
|
|
|
|
default: {
|
|
|
|
|
postJSON: vi.fn(),
|
|
|
|
|
getJSON: vi.fn(),
|
2026-05-26 11:58:04 -03:00
|
|
|
patchJSON: vi.fn(),
|
2025-06-10 16:04:26 -03:00
|
|
|
}
|
|
|
|
|
}));
|
2025-03-09 14:39:31 -07:00
|
|
|
|
|
|
|
|
vi.mock('react-i18next', () => ({
|
|
|
|
|
useTranslation: () => ({
|
|
|
|
|
i18n: {
|
|
|
|
|
changeLanguage: vi.fn(),
|
|
|
|
|
language: 'en',
|
|
|
|
|
},
|
|
|
|
|
t: (key: string) => key,
|
|
|
|
|
}),
|
|
|
|
|
initReactI18next: {
|
|
|
|
|
type: '3rdParty',
|
|
|
|
|
init: vi.fn(),
|
|
|
|
|
},
|
|
|
|
|
I18nextProvider: ({ children }: any) => children,
|
|
|
|
|
}));
|
|
|
|
|
|
2025-06-10 16:04:26 -03:00
|
|
|
vi.mock('react-router', async () => {
|
|
|
|
|
const actual = await vi.importActual<any>('react-router');
|
2025-04-21 18:59:55 -03:00
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...actual,
|
|
|
|
|
useSearchParams: vi.fn(),
|
2025-06-10 16:04:26 -03:00
|
|
|
useParams: vi.fn()
|
2025-04-21 18:59:55 -03:00
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
2025-06-10 16:04:26 -03:00
|
|
|
import { useSearchParams, useParams } from 'react-router';
|
2025-04-21 18:59:55 -03:00
|
|
|
|
2025-03-09 14:39:31 -07:00
|
|
|
const authContextMock = {
|
|
|
|
|
signed: true,
|
|
|
|
|
user: {
|
|
|
|
|
userId: 1,
|
|
|
|
|
name: 'Ricardo',
|
|
|
|
|
email: 'test@example.com',
|
|
|
|
|
admin: false,
|
2025-03-11 17:01:00 -07:00
|
|
|
createdAt: new Date(),
|
2025-06-10 16:04:26 -03:00
|
|
|
gravatarImageUrl: 'http://url.com',
|
|
|
|
|
lang: 'en'
|
2025-03-09 14:39:31 -07:00
|
|
|
},
|
|
|
|
|
checkCurrentAuthUser: vi.fn(),
|
|
|
|
|
signIn: vi.fn(),
|
|
|
|
|
signOut: vi.fn(),
|
|
|
|
|
register: vi.fn(),
|
|
|
|
|
isAdmin: false,
|
|
|
|
|
updateUser: vi.fn(),
|
|
|
|
|
};
|
|
|
|
|
|
2025-04-21 18:59:55 -03:00
|
|
|
const sidebarContextMock = {
|
|
|
|
|
currentPage: '/home',
|
|
|
|
|
setNewPage: vi.fn()
|
|
|
|
|
};
|
|
|
|
|
|
2025-06-10 16:04:26 -03:00
|
|
|
// Mock the lang handler
|
|
|
|
|
vi.mock('../../lang-service/LangHandler', () => ({
|
|
|
|
|
handleDefaultLang: vi.fn()
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// Mock the translator utils
|
|
|
|
|
vi.mock('../../utils/TranslatorUtils', () => ({
|
|
|
|
|
translateServerResponse: (message: string) => message
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
const mockedApi = vi.mocked(api);
|
|
|
|
|
const mockedUseParams = vi.mocked(useParams);
|
2026-05-26 11:58:04 -03:00
|
|
|
const mockedUseSearchParams = vi.mocked(useSearchParams);
|
2025-06-10 16:04:26 -03:00
|
|
|
|
2025-03-09 14:39:31 -07:00
|
|
|
describe('NoteAdd Component', () => {
|
|
|
|
|
const renderNoteAdd = () => {
|
|
|
|
|
return render(
|
|
|
|
|
<MemoryRouter>
|
|
|
|
|
<AuthContext.Provider value={authContextMock}>
|
|
|
|
|
<I18nextProvider i18n={i18n}>
|
2025-04-21 18:59:55 -03:00
|
|
|
<SidebarContext.Provider value={sidebarContextMock}>
|
|
|
|
|
<NoteAdd />
|
|
|
|
|
</SidebarContext.Provider>
|
2025-03-09 14:39:31 -07:00
|
|
|
</I18nextProvider>
|
|
|
|
|
</AuthContext.Provider>
|
|
|
|
|
</MemoryRouter>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2025-04-21 18:59:55 -03:00
|
|
|
beforeEach(() => {
|
|
|
|
|
// Reset mock between tests
|
2026-05-26 11:58:04 -03:00
|
|
|
mockedUseSearchParams.mockReturnValue([new URLSearchParams(), vi.fn()]);
|
|
|
|
|
mockedUseParams.mockReturnValue({});
|
2025-06-10 16:04:26 -03:00
|
|
|
vi.clearAllMocks();
|
2025-04-21 18:59:55 -03:00
|
|
|
});
|
|
|
|
|
|
2026-05-06 09:14:51 -03:00
|
|
|
it('should render the NoteAdd component', async () => {
|
|
|
|
|
let result: any;
|
|
|
|
|
await act(async () => {
|
|
|
|
|
result = renderNoteAdd();
|
|
|
|
|
});
|
|
|
|
|
const { getByText } = result;
|
2025-03-09 14:39:31 -07:00
|
|
|
expect(getByText('note_form_title_label')).toBeDefined();
|
|
|
|
|
expect(getByText('task_form_url_label')).toBeDefined();
|
|
|
|
|
expect(getByText('note_form_content_label')).toBeDefined();
|
|
|
|
|
expect(getByText('note_form_submit')).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should show error message when form is invalid', async () => {
|
2026-05-26 11:58:04 -03:00
|
|
|
const { getByText, getByRole } = renderNoteAdd();
|
2025-03-09 14:39:31 -07:00
|
|
|
const submitButton = getByRole('button', { name: 'note_form_submit' });
|
2026-05-26 11:58:04 -03:00
|
|
|
|
2025-03-09 14:39:31 -07:00
|
|
|
fireEvent.click(submitButton);
|
2026-05-26 11:58:04 -03:00
|
|
|
|
2025-03-09 14:39:31 -07:00
|
|
|
await waitFor(() => {
|
|
|
|
|
expect(getByText('Please fill in all the fields')).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should add a new note when form is valid', async () => {
|
2026-05-26 11:58:04 -03:00
|
|
|
mockedUseSearchParams.mockReturnValue([
|
2025-04-21 18:59:55 -03:00
|
|
|
new URLSearchParams("backTo=home"),
|
2026-05-26 11:58:04 -03:00
|
|
|
vi.fn(),
|
2025-04-21 18:59:55 -03:00
|
|
|
]);
|
|
|
|
|
|
2026-05-26 11:58:04 -03:00
|
|
|
const { getByLabelText, getByTestId, getByRole } = renderNoteAdd();
|
2025-03-09 14:39:31 -07:00
|
|
|
const descriptionInput = getByLabelText('note_form_title_label') as HTMLInputElement;
|
|
|
|
|
const noteContentInput = getByTestId('note-content-input-area') as HTMLAreaElement;
|
|
|
|
|
const submitButton = getByRole('button', { name: 'note_form_submit' });
|
|
|
|
|
|
|
|
|
|
fireEvent.change(descriptionInput, { target: { value: 'New Note' } });
|
|
|
|
|
fireEvent.change(noteContentInput, { target: { value: 'Note content' } });
|
|
|
|
|
fireEvent.click(submitButton);
|
|
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
const newNote: NoteResponse = {
|
|
|
|
|
id: 0,
|
|
|
|
|
title: 'New Note',
|
|
|
|
|
description: 'Note content',
|
2025-05-24 15:07:26 -03:00
|
|
|
url: '',
|
2026-05-26 11:58:04 -03:00
|
|
|
tags: [],
|
2026-02-28 13:08:23 -03:00
|
|
|
lastUpdate: '',
|
|
|
|
|
shared: false,
|
|
|
|
|
shareToken: null
|
2025-03-09 14:39:31 -07:00
|
|
|
}
|
|
|
|
|
expect(api.postJSON).toHaveBeenCalledWith(ApiConfig.notesUrl, newNote);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2026-05-06 09:14:51 -03:00
|
|
|
it('should render text based on new contentHeader component', async () => {
|
|
|
|
|
let result: any;
|
|
|
|
|
await act(async () => {
|
|
|
|
|
result = renderNoteAdd();
|
|
|
|
|
});
|
|
|
|
|
const { getByText } = result;
|
2025-03-09 14:39:31 -07:00
|
|
|
|
2025-03-13 09:56:57 -07:00
|
|
|
expect(getByText('Add')).toBeDefined();
|
|
|
|
|
expect(getByText('Note')).toBeDefined();
|
2025-03-09 14:39:31 -07:00
|
|
|
expect(getByText('Save your notes in plain text or Markdown format')).toBeDefined();
|
|
|
|
|
expect(getByText('Create, Filter, and Easily Find')).toBeDefined();
|
|
|
|
|
expect(getByText('Them')).toBeDefined();
|
|
|
|
|
});
|
2025-06-10 16:04:26 -03:00
|
|
|
|
|
|
|
|
it('should render a note to edit', async () => {
|
|
|
|
|
mockedUseParams.mockReturnValue({ id: '1' });
|
|
|
|
|
|
|
|
|
|
const toEdit: NoteResponse = {
|
|
|
|
|
id: 1,
|
|
|
|
|
title: 'Note one',
|
|
|
|
|
description: 'Description of note one',
|
|
|
|
|
url: 'http://notes.domain.com',
|
2026-05-26 11:58:04 -03:00
|
|
|
tags: ['dev'],
|
2025-06-10 16:04:26 -03:00
|
|
|
lastUpdate: '3 minutes ago',
|
2026-05-26 11:58:04 -03:00
|
|
|
shared: false,
|
|
|
|
|
shareToken: null
|
2025-06-10 16:04:26 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
vi.spyOn(api, 'getJSON').mockResolvedValue(toEdit);
|
|
|
|
|
|
|
|
|
|
const { getByLabelText, getByTestId } = renderNoteAdd();
|
|
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
const noteTitle = getByLabelText('note_form_title_label') as HTMLInputElement;
|
|
|
|
|
const noteUrl = getByLabelText('task_form_url_label') as HTMLInputElement;
|
|
|
|
|
const noteContentInput = getByTestId('note-content-input-area') as HTMLAreaElement;
|
|
|
|
|
expect(noteTitle.value).toBe(toEdit.title);
|
|
|
|
|
expect(noteUrl.value).toBe(toEdit.url);
|
|
|
|
|
expect(noteContentInput.innerHTML).toBe(toEdit.description);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should render a cloned note', async () => {
|
|
|
|
|
window.history.pushState({}, '', '?cloneFrom=123');
|
|
|
|
|
|
|
|
|
|
const toClone: NoteResponse = {
|
|
|
|
|
id: 11,
|
|
|
|
|
title: 'Old title',
|
|
|
|
|
description: 'Old description',
|
|
|
|
|
url: 'http://notes.domain.com',
|
2026-05-26 11:58:04 -03:00
|
|
|
tags: ['dev'],
|
2025-06-10 16:04:26 -03:00
|
|
|
lastUpdate: '1 minute ago',
|
2026-05-26 11:58:04 -03:00
|
|
|
shared: false,
|
|
|
|
|
shareToken: null
|
2025-06-10 16:04:26 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
vi.spyOn(api, 'getJSON').mockResolvedValue(toClone);
|
|
|
|
|
|
|
|
|
|
const { getByLabelText, getByTestId } = renderNoteAdd();
|
|
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
const noteTitle = getByLabelText('note_form_title_label') as HTMLInputElement;
|
|
|
|
|
const noteUrl = getByLabelText('task_form_url_label') as HTMLInputElement;
|
|
|
|
|
const noteContentInput = getByTestId('note-content-input-area') as HTMLAreaElement;
|
|
|
|
|
expect(noteTitle.value).toBe(toClone.title);
|
|
|
|
|
expect(noteUrl.value).toBe(toClone.url);
|
|
|
|
|
expect(noteContentInput.innerHTML).toBe(toClone.description);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should not render a cloned note when user is trying to mess up', async () => {
|
|
|
|
|
window.history.pushState({}, '', '?cloneFrom=12345');
|
|
|
|
|
|
|
|
|
|
const { getByLabelText, getByTestId } = renderNoteAdd();
|
|
|
|
|
|
|
|
|
|
await waitFor(() => {
|
|
|
|
|
const noteTitle = getByLabelText('note_form_title_label') as HTMLInputElement;
|
|
|
|
|
const noteUrl = getByLabelText('task_form_url_label') as HTMLInputElement;
|
|
|
|
|
const noteContentInput = getByTestId('note-content-input-area') as HTMLAreaElement;
|
|
|
|
|
expect(noteTitle.value).toBe('');
|
|
|
|
|
expect(noteUrl.value).toBe('');
|
|
|
|
|
expect(noteContentInput.innerHTML).toBe('');
|
|
|
|
|
});
|
|
|
|
|
});
|
2025-03-09 14:39:31 -07:00
|
|
|
});
|