Files
tasknote/client/cypress/e2e/notes.cy.ts
T
rmcamposandGitHub 5fec30fea0 feat: add SDD and DDD filer for AI based development and Spec 001 implementation (#60)
* feat: add initial files and spec 1

* feat: add spec for rich text feature

* chore: restore file from broken AI session

* feat: add project constraints to ai

* chore: update spec feature prompt

* feat: add multiple tags support - wip

* test: fix test cases

* chore: move tag list to below the inputs

* chore: add ngrok files and updates

* fix: session issues and query using HQL

* test: fix test cases
2026-05-26 11:58:04 -03:00

274 lines
7.7 KiB
TypeScript

/**
* E2E tests for Notes Management: Create, Update, Delete, and Share notes.
*/
const mockUser = {
userId: 1,
name: 'Test User',
email: 'test@example.com',
admin: false,
createdAt: '2026-01-01T00:00:00.000Z',
gravatarImageUrl: '',
lang: 'en'
};
const mockNote = {
id: 1,
title: 'Meeting notes',
description: 'Discuss quarterly goals',
url: null,
tags: ['work'],
lastUpdate: '2026-05-01',
shared: false,
shareToken: null
};
describe('Notes Management', () => {
/**
* Sets up authentication intercepts and visits the given path.
*
* @param {string} path - The path to visit.
*/
const visitWithAuth = (path: string): void => {
cy.intercept('GET', /\/rest\/user-sessions\/refresh/, {
statusCode: 200,
body: { token: 'fake-jwt-token', ...mockUser }
}).as('refreshToken');
cy.intercept('GET', /\/rest\/users\/me/, {
statusCode: 200,
body: mockUser
}).as('getCurrentUser');
cy.intercept('GET', /\/rest\/home\/tasks\/tags/, {
statusCode: 200,
body: ['work', 'personal']
}).as('getTags');
cy.visit(path, {
onBeforeLoad(win) {
win.localStorage.setItem('TASKNOTE-TOKEN', 'fake-jwt-token');
win.localStorage.setItem('TASKNOTE-USER', JSON.stringify(mockUser));
}
});
};
/**
* Create note
*/
describe('Create', () => {
beforeEach(() => {
visitWithAuth('/notes/new');
});
it('displays the add note form', () => {
cy.contains('Add Note').should('be.visible');
cy.get('input[name="note_title"]').should('be.visible');
cy.get('textarea[name="note_description"]').should('be.visible');
cy.contains('button', 'Save note').should('be.visible');
});
it('shows a validation error when submitted without a title', () => {
cy.contains('button', 'Save note').click();
cy.get('.alert-danger').should('be.visible');
});
it('shows a validation error when submitted without content', () => {
cy.get('input[name="note_title"]').type('A note title');
cy.contains('button', 'Save note').click();
cy.get('.alert-danger').should('be.visible');
});
it('creates a note and navigates to home on success', () => {
cy.intercept('POST', /\/rest\/notes/, {
statusCode: 201,
body: {
id: 10,
title: 'New note',
description: 'Some content',
url: null,
tags: [],
lastUpdate: '',
shared: false,
shareToken: null
}
}).as('createNote');
cy.intercept('GET', /\/rest\/tasks$/, { statusCode: 200, body: [] }).as('getTasks');
cy.intercept('GET', /\/rest\/notes$/, { statusCode: 200, body: [] }).as('getNotes');
cy.get('input[name="note_title"]').type('New note');
cy.get('textarea[name="note_description"]').type('Some content');
cy.contains('button', 'Save note').click();
cy.wait('@createNote');
cy.url().should('include', '/home');
});
it('shows an error alert when the API returns an error on create', () => {
cy.intercept('POST', /\/rest\/notes/, {
statusCode: 500,
body: { message: 'Internal Server Error' }
}).as('createNoteFail');
cy.get('input[name="note_title"]').type('Failing note');
cy.get('textarea[name="note_description"]').type('Some content');
cy.contains('button', 'Save note').click();
cy.wait('@createNoteFail');
cy.get('.alert-danger').should('be.visible');
});
it('navigates back to home when Cancel is clicked', () => {
cy.intercept('GET', /\/rest\/tasks$/, { statusCode: 200, body: [] }).as('getTasks');
cy.intercept('GET', /\/rest\/notes$/, { statusCode: 200, body: [] }).as('getNotes');
cy.contains('button', 'Cancel').click();
cy.url().should('include', '/home');
});
});
/**
* Update note
*/
describe('Update', () => {
beforeEach(() => {
cy.intercept('GET', /\/rest\/notes\/\d+/, {
statusCode: 200,
body: mockNote
}).as('getNote');
visitWithAuth('/notes/edit/1');
cy.wait('@getNote');
});
it('pre-fills the form with the existing note data', () => {
cy.get('input[name="note_title"]').should('have.value', 'Meeting notes');
cy.get('textarea[name="note_description"]').should('have.value', 'Discuss quarterly goals');
});
it('updates the note and navigates to home on success', () => {
cy.intercept('PATCH', /\/rest\/notes\/\d+/, {
statusCode: 200,
body: { ...mockNote, title: 'Updated meeting notes' }
}).as('updateNote');
cy.intercept('GET', /\/rest\/tasks$/, { statusCode: 200, body: [] }).as('getTasks');
cy.intercept('GET', /\/rest\/notes$/, { statusCode: 200, body: [] }).as('getNotes');
cy.get('input[name="note_title"]').clear().type('Updated meeting notes');
cy.contains('button', 'Save note').click();
cy.wait('@updateNote');
cy.url().should('include', '/home');
});
it('shows an error alert when the update API call fails', () => {
cy.intercept('PATCH', /\/rest\/notes\/\d+/, {
statusCode: 500,
body: { message: 'Internal Server Error' }
}).as('updateNoteFail');
cy.get('input[name="note_title"]').clear().type('Updated note');
cy.contains('button', 'Save note').click();
cy.wait('@updateNoteFail');
cy.get('.alert-danger').should('be.visible');
});
});
/**
* Delete note
*/
describe('Delete', () => {
beforeEach(() => {
cy.intercept('GET', /\/rest\/tasks$/, {
statusCode: 200,
body: []
}).as('getTasks');
cy.intercept('GET', /\/rest\/notes$/, {
statusCode: 200,
body: [mockNote]
}).as('getNotes');
visitWithAuth('/home');
cy.wait('@getNotes');
});
it('deletes a note via the note dropdown menu', () => {
cy.intercept('DELETE', /\/rest\/notes\/\d+/, {
statusCode: 204
}).as('deleteNote');
cy.get('[data-testid="note-dropdown-menu-1"]').click();
cy.get('[data-testid="note-dropdown-delete-item-1"]').click();
cy.wait('@deleteNote');
});
});
/**
* Share note
*/
describe('Share', () => {
beforeEach(() => {
cy.intercept('GET', /\/rest\/tasks$/, {
statusCode: 200,
body: []
}).as('getTasks');
cy.intercept('GET', /\/rest\/notes$/, {
statusCode: 200,
body: [mockNote]
}).as('getNotes');
visitWithAuth('/home');
cy.wait('@getNotes');
});
it('shares a note via the note dropdown menu', () => {
cy.intercept('PUT', /\/rest\/notes\/\d+\/share/, {
statusCode: 200,
body: { ...mockNote, shared: true, shareToken: 'abc123' }
}).as('shareNote');
cy.get('[data-testid="note-dropdown-menu-1"]').click();
cy.get('[data-testid="note-dropdown-share-item-1"]').click();
cy.wait('@shareNote');
});
it('unshares a shared note via the note dropdown menu', () => {
const sharedNote = { ...mockNote, shared: true, shareToken: 'abc123' };
cy.intercept('GET', /\/rest\/notes$/, {
statusCode: 200,
body: [sharedNote]
}).as('getSharedNotes');
cy.intercept('PUT', /\/rest\/notes\/\d+\/unshare/, {
statusCode: 200,
body: { ...sharedNote, shared: false, shareToken: null }
}).as('unshareNote');
cy.visit('/home', {
onBeforeLoad(win) {
win.localStorage.setItem('TASKNOTE-TOKEN', 'fake-jwt-token');
win.localStorage.setItem('TASKNOTE-USER', JSON.stringify(mockUser));
}
});
cy.wait('@getSharedNotes');
cy.get('[data-testid="note-dropdown-menu-1"]').click();
cy.get('[data-testid="note-dropdown-share-item-1"]').click();
cy.wait('@unshareNote');
});
});
});