diff --git a/README.md b/README.md index 903c876f9..36d7ac162 100644 --- a/README.md +++ b/README.md @@ -33,4 +33,4 @@ Implement a simple [TODO app](https://mate-academy.github.io/react_todo-app/) th - Implement a solution following the [React task guidelines](https://github.com/mate-academy/react_task-guideline#react-tasks-guideline). - Use the [React TypeScript cheat sheet](https://mate-academy.github.io/fe-program/js/extra/react-typescript). - Open another terminal and run tests with `npm test` to ensure your solution is correct. -- Replace `` with your GitHub username in the [DEMO LINK](https://.github.io/react_todo-app/) and add it to the PR description. +- Replace `` with your GitHub username in the [DEMO LINK](https://przwojwwp.github.io/react_todo-app/) and add it to the PR description. diff --git a/cypress/integration/page.spec.js b/cypress/integration/page.spec.js index 0875764e1..4b744d08a 100644 --- a/cypress/integration/page.spec.js +++ b/cypress/integration/page.spec.js @@ -10,10 +10,13 @@ const page = { localStorage: () => cy.getAllLocalStorage().its('http://localhost:3001'), data: () => page.localStorage().then(({ todos = '[]' }) => JSON.parse(todos)), - visit: (initialTodos) => { + visit: (initialTodos) => + { cy.visit('/', { - onBeforeLoad: win => { - if (initialTodos) { + onBeforeLoad: win => + { + if (initialTodos) + { win.localStorage.setItem('todos', JSON.stringify(initialTodos)); } }, @@ -52,47 +55,58 @@ const filter = { let failed = false; -Cypress.on('fail', e => { +Cypress.on('fail', e => +{ failed = true; throw e; }); -describe('', () => { - beforeEach(() => { +describe('', () => +{ + beforeEach(() => + { if (failed) Cypress.runner.stop(); }); - describe('Page with no todos', () => { - beforeEach(() => { + describe('Page with no todos', () => + { + beforeEach(() => + { page.visit(); }); - it('should have focused NewTodoField', () => { + it('should have focused NewTodoField', () => + { page.newTodoField().should('be.focused'); }); - it('should not have Todos', () => { + it('should not have Todos', () => + { page.todoList().should('not.exist'); todos.assertCount(0); }); - it('should not have Footer', () => { + it('should not have Footer', () => + { page.footer().should('not.exist'); filter.el().should('not.exist'); page.clearCompletedButton().should('not.exist'); page.todosCounter().should('not.exist'); }); - it('should not have ToggleAllButton', () => { + it('should not have ToggleAllButton', () => + { page.toggleAllButton().should('not.exist'); }); - it('should allow to type a new title', () => { + it('should allow to type a new title', () => + { page.newTodoField().type('First todo'); page.newTodoField().should('have.value', 'First todo'); }); - it('should add a not completed todo on Enter', () => { + it('should add a not completed todo on Enter', () => + { page.newTodoField().type('First todo{enter}'); page.todoList().should('exist'); @@ -101,44 +115,54 @@ describe('', () => { todos.assertNotCompleted(0); }); - it('should not have todos in localStorage', () => { + it('should not have todos in localStorage', () => + { page.data().should('deep.equal', []); }); }); - describe('Page after adding a first todo', () => { - beforeEach(() => { + describe('Page after adding a first todo', () => + { + beforeEach(() => + { page.visit(); page.newTodoField().type('First todo{enter}'); }); - it('should show TodoList with one active todo', () => { + it('should show TodoList with one active todo', () => + { page.todoList().should('exist'); todos.assertCount(1); todos.assertTitle(0, 'First todo'); todos.assertNotCompleted(0); }); - it('should show Filter', () => { + it('should show Filter', () => + { filter.assertVisible(); }); - it('should show todosCounter', () => { + it('should show todosCounter', () => + { page.todosCounter().should('contain.text', '1 item'); }); - it('should show ToggleAllButton', () => { + it('should show ToggleAllButton', () => + { page.toggleAllButton().should('exist'); }); - it('should not show edit field', () => { + it('should not show edit field', () => + { todos.titleField(0).should('not.exist'); }); - it('should save todos to localStorage in JSON', () => { + it('should save todos to localStorage in JSON', () => + { page.localStorage().should('have.keys', 'todos'); - page.data().then((todos) => { + page.data().then((todos) => + { expect(todos).to.be.instanceOf(Array); expect(todos).to.have.length(1); expect(todos[0].title).to.equal('First todo'); @@ -147,21 +171,26 @@ describe('', () => { }); }); - describe('Page with mixed todos', () => { - beforeEach(() => { + describe('Page with mixed todos', () => + { + beforeEach(() => + { cy.fixture('todos').then(page.visit); }); - it('should have todos loaded from localStorage', () => { + it('should have todos loaded from localStorage', () => + { page.todoList().should('exist'); todos.assertCount(5); }); - it('should have NewTodoField', () => { + it('should have NewTodoField', () => + { page.newTodoField().should('exist'); }); - it('should have delete buttons for every todo', () => { + it('should have delete buttons for every todo', () => + { todos.deleteButton(0).should('exist'); todos.deleteButton(1).should('exist'); todos.deleteButton(2).should('exist'); @@ -169,7 +198,8 @@ describe('', () => { todos.deleteButton(4).should('exist'); }); - it('should not show edit fields', () => { + it('should not show edit fields', () => + { todos.titleField(0).should('not.exist'); todos.titleField(1).should('not.exist'); todos.titleField(2).should('not.exist'); @@ -177,7 +207,8 @@ describe('', () => { todos.titleField(4).should('not.exist'); }); - it('should have correct todo titles', () => { + it('should have correct todo titles', () => + { todos.assertTitle(0, 'HTML'); todos.assertTitle(1, 'CSS'); todos.assertTitle(2, 'JS'); @@ -185,18 +216,21 @@ describe('', () => { todos.assertTitle(4, 'React'); }); - it('should higlight all completed todos', () => { + it('should higlight all completed todos', () => + { todos.assertCompleted(0); todos.assertCompleted(1); todos.assertCompleted(2); }); - it('should not higlight not completed todos', () => { + it('should not higlight not completed todos', () => + { todos.assertNotCompleted(3); todos.assertNotCompleted(4); }); - it('should have correct completed statuses', () => { + it('should have correct completed statuses', () => + { todos.statusToggler(0).should('be.checked'); todos.statusToggler(1).should('be.checked'); todos.statusToggler(2).should('be.checked'); @@ -204,32 +238,40 @@ describe('', () => { todos.statusToggler(4).should('not.be.checked'); }); - it('should have Filter', () => { + it('should have Filter', () => + { filter.assertVisible(); }); - it('should have todosCounter with a number of not completed todos', () => { + it('should have todosCounter with a number of not completed todos', () => + { page.todosCounter().should('have.text', '2 items left'); }); - it('should have clearCompletedButton', () => { + it('should have clearCompletedButton', () => + { page.clearCompletedButton().should('exist'); }); }); - describe('Filtering', () => { - describe('with mixed todos', () => { - beforeEach(() => { + describe('Filtering', () => + { + describe('with mixed todos', () => + { + beforeEach(() => + { cy.fixture('todos').then(page.visit); }); - it('should have only filterLinkAll active', () => { + it('should have only filterLinkAll active', () => + { filter.assertSelected('all'); filter.assertNotSelected('active'); filter.assertNotSelected('completed'); }); - it('should allow to select the active filter', () => { + it('should allow to select the active filter', () => + { filter.link('active').click(); filter.assertNotSelected('all'); @@ -237,7 +279,8 @@ describe('', () => { filter.assertNotSelected('completed'); }); - it('should show only active todos when active filter is selected', () => { + it('should show only active todos when active filter is selected', () => + { filter.link('active').click(); todos.assertCount(2); @@ -245,7 +288,8 @@ describe('', () => { todos.assertTitle(1, 'React'); }); - it('should keep footer when active todos are shown', () => { + it('should keep footer when active todos are shown', () => + { filter.link('active').click(); page.todosCounter().should('have.text', '2 items left'); @@ -253,7 +297,8 @@ describe('', () => { page.clearCompletedButton().should('exist'); }); - it('should allow to select the completed filter', () => { + it('should allow to select the completed filter', () => + { filter.link('completed').click(); filter.assertNotSelected('all'); @@ -261,7 +306,8 @@ describe('', () => { filter.assertSelected('completed'); }); - it('should show only completed todos when completed filter is selected', () => { + it('should show only completed todos when completed filter is selected', () => + { filter.link('completed').click(); todos.assertCount(3); @@ -270,7 +316,8 @@ describe('', () => { todos.assertTitle(2, 'JS'); }); - it('should keep footer when completed todos are shown', () => { + it('should keep footer when completed todos are shown', () => + { filter.link('completed').click(); page.todosCounter().should('have.text', '2 items left'); @@ -278,7 +325,8 @@ describe('', () => { page.clearCompletedButton().should('exist'); }); - it('should allow to reset filter', () => { + it('should allow to reset filter', () => + { filter.link('completed').click(); filter.link('all').click(); @@ -289,72 +337,88 @@ describe('', () => { }); }); - describe('with active todos only', () => { - beforeEach(() => { + describe('with active todos only', () => + { + beforeEach(() => + { cy.fixture('active-todos').then(page.visit); }); - it('should hide todos on completed selection', () => { + it('should hide todos on completed selection', () => + { filter.link('completed').click(); todos.assertCount(0); }); - it('should keep footer on completed selection', () => { + it('should keep footer on completed selection', () => + { filter.link('completed').click(); filter.assertVisible(); }); - it('should keep todos counter on completed selection', () => { + it('should keep todos counter on completed selection', () => + { filter.link('completed').click(); page.todosCounter().should('have.text', '5 items left'); }); }); - describe('with completed todos only', () => { - beforeEach(() => { + describe('with completed todos only', () => + { + beforeEach(() => + { cy.fixture('completed-todos').then(page.visit); }); - it('should hide todos on active selection', () => { + it('should hide todos on active selection', () => + { filter.link('active').click(); todos.assertCount(0); }); - it('should keep footer on active selection', () => { + it('should keep footer on active selection', () => + { filter.link('active').click(); filter.assertVisible(); }); - it('should keep todos counter on active selection', () => { + it('should keep todos counter on active selection', () => + { filter.link('active').click(); page.todosCounter().should('have.text', '0 items left'); }); }); }); - describe('Adding a todo', () => { - beforeEach(() => { + describe('Adding a todo', () => + { + beforeEach(() => + { cy.fixture('todos').then(page.visit); }); - it('should focus text field by default', () => { + it('should focus text field by default', () => + { page.newTodoField().should('be.focused'); }); - it('should keep text field focused if title is empty', () => { + it('should keep text field focused if title is empty', () => + { page.newTodoField().type('{enter}'); page.newTodoField().should('be.focused'); }); - it('should keep text field focused if title title has only whitespaces', () => { + it('should keep text field focused if title title has only whitespaces', () => + { page.newTodoField().type(' {enter}'); page.newTodoField().should('be.focused'); }); - it('should add an active todo', () => { + it('should add an active todo', () => + { page.newTodoField().type('Test Todo{enter}'); todos.assertCount(6); @@ -362,35 +426,41 @@ describe('', () => { todos.assertNotCompleted(5); }); - it('should save updated todos to localStorage', () => { + it('should save updated todos to localStorage', () => + { page.newTodoField().type('Test Todo{enter}'); - page.data().then((todos) => { + page.data().then((todos) => + { expect(todos).to.have.length(6); expect(todos[5].title).to.equal('Test Todo'); expect(todos[5].completed).to.be.false; }); }); - it('should update active counter', () => { + it('should update active counter', () => + { page.newTodoField().type('Test Todo{enter}'); page.todosCounter().should('have.text', '3 items left'); }); - it('should clear text field', () => { + it('should clear text field', () => + { page.newTodoField().type('Test Todo{enter}'); page.newTodoField().should('have.value', ''); }); - it('should focus text field', () => { + it('should focus text field', () => + { page.newTodoField().type('Test Todo{enter}'); page.newTodoField().should('be.focused'); }); - it('should allow to add one more todo', () => { + it('should allow to add one more todo', () => + { page.newTodoField().type('Test Todo{enter}'); page.newTodoField().type('Hello world{enter}'); @@ -400,24 +470,28 @@ describe('', () => { page.todosCounter().should('have.text', '4 items left'); }); - it('should save all added todos to localStorage', () => { + it('should save all added todos to localStorage', () => + { page.newTodoField().type('Test Todo{enter}'); page.newTodoField().type('Hello world{enter}'); - page.data().then((todos) => { + page.data().then((todos) => + { expect(todos).to.have.length(7); expect(todos[6].title).to.equal('Hello world'); expect(todos[6].completed).to.be.false; }); }); - it('should add trimmed title', () => { + it('should add trimmed title', () => + { page.newTodoField().type(' Other Title {enter}'); todos.assertTitle(5, 'Other Title'); }); - it('should keep current filter', () => { + it('should keep current filter', () => + { filter.link('active').click(); page.newTodoField().type('Test Todo{enter}'); @@ -425,99 +499,123 @@ describe('', () => { }); }); - describe('Individual Todo Deletion', () => { - describe('in the list with many todos', () => { - beforeEach(() => { + describe('Individual Todo Deletion', () => + { + describe('in the list with many todos', () => + { + beforeEach(() => + { cy.fixture('todos').then(page.visit); }); - it('should remove the todo from the list', () => { + it('should remove the todo from the list', () => + { todos.deleteButton(0).click(); todos.assertCount(4); todos.assertTitle(0, 'CSS'); }); - it('should save all changes to localStorage', () => { + it('should save all changes to localStorage', () => + { todos.deleteButton(0).click(); - page.data().then((todos) => { + page.data().then((todos) => + { expect(todos).to.have.length(4); expect(todos[0].title).to.equal('CSS'); }); }); - it('should focus text field after todo deletion', () => { + it('should focus text field after todo deletion', () => + { todos.deleteButton(0).click(); page.newTodoField().should('be.focused'); }); - it('should keep active todos count if completed todo is deleted', () => { + it('should keep active todos count if completed todo is deleted', () => + { todos.deleteButton(0).click(); page.todosCounter().should('have.text', '2 items left'); }); - it('should adjust active todos count if active todo is deleted', () => { + it('should adjust active todos count if active todo is deleted', () => + { todos.deleteButton(4).click(); page.todosCounter().should('contain.text', '1 item'); }); }); - describe('for the last todo in the list', () => { - beforeEach(() => { + describe('for the last todo in the list', () => + { + beforeEach(() => + { page.visit([{ id: 42, title: 'HTML', completed: false }]); todos.deleteButton(0).click(); }); - it('should hide todos', () => { + it('should hide todos', () => + { todos.assertCount(0); }); - it('should hide footer', () => { + it('should hide footer', () => + { filter.assertHidden(); page.clearCompletedButton().should('not.exist'); page.todosCounter().should('not.exist'); }); - it('should focus text field after todo deletion', () => { + it('should focus text field after todo deletion', () => + { page.newTodoField().should('be.focused'); }); - it('should hide ToggleAllButton', () => { + it('should hide ToggleAllButton', () => + { page.toggleAllButton().should('not.exist'); }); - it('should save an empty array to localStorage', () => { + it('should save an empty array to localStorage', () => + { page.data().should('deep.equal', []); }); }); }); - describe('ClearCompleted button', () => { - describe('with no completed todos', () => { - beforeEach(() => { + describe('ClearCompleted button', () => + { + describe('with no completed todos', () => + { + beforeEach(() => + { cy.fixture('active-todos').then(page.visit); }); - it('should be disabled', () => { + it('should be disabled', () => + { page.clearCompletedButton().should('be.disabled'); }); }); - describe('with some completed todos', () => { - beforeEach(() => { + describe('with some completed todos', () => + { + beforeEach(() => + { cy.fixture('todos').then(page.visit); }); - it('should be enabled', () => { + it('should be enabled', () => + { page.clearCompletedButton().should('not.be.disabled'); }); - it('should remove all completed todos from the list', () => { + it('should remove all completed todos from the list', () => + { page.clearCompletedButton().click(); todos.assertCount(2); @@ -525,10 +623,12 @@ describe('', () => { todos.assertTitle(1, 'React'); }); - it('should save all changes to localStorage', () => { + it('should save all changes to localStorage', () => + { page.clearCompletedButton().click(); - page.data().then((todos) => { + page.data().then((todos) => + { expect(todos).to.have.length(2); expect(todos[0].title).to.equal('TypeScript'); expect(todos[0].completed).to.be.false; @@ -537,34 +637,41 @@ describe('', () => { }); }); - it('should become disabled after deletion', () => { + it('should become disabled after deletion', () => + { page.clearCompletedButton().click(); page.clearCompletedButton().should('be.disabled'); }); - it('should focus new todo field after deletion', () => { + it('should focus new todo field after deletion', () => + { page.clearCompletedButton().click(); page.newTodoField().should('be.focused'); }); }); - describe('if all todos are completed', () => { - beforeEach(() => { + describe('if all todos are completed', () => + { + beforeEach(() => + { cy.fixture('completed-todos').then(page.visit); page.clearCompletedButton().click(); }); - it('should hide todos after clearing all completed todos', () => { + it('should hide todos after clearing all completed todos', () => + { todos.assertCount(0); }); - it('should save an empty array to localStorage', () => { + it('should save an empty array to localStorage', () => + { page.data().should('deep.equal', []); }); - it('should hide footer after clearing all completed todos', () => { + it('should hide footer after clearing all completed todos', () => + { filter.assertHidden(); page.clearCompletedButton().should('not.exist'); page.todosCounter().should('not.exist'); @@ -572,46 +679,58 @@ describe('', () => { }); }); - describe('Todo Toggler', () => { - beforeEach(() => { + describe('Todo Toggler', () => + { + beforeEach(() => + { cy.fixture('todos').then(page.visit); }); - describe('', () => { - beforeEach(() => { + describe('', () => + { + beforeEach(() => + { todos.statusToggler(0).click(); }); - it('should toggle a todo', () => { + it('should toggle a todo', () => + { todos.assertNotCompleted(0); todos.statusToggler(0).should('not.be.checked'); }); - it('should save changes to localStorage', () => { - page.data().then((todos) => { + it('should save changes to localStorage', () => + { + page.data().then((todos) => + { expect(todos).to.have.length(5); expect(todos[0].title).to.equal('HTML'); expect(todos[0].completed).to.be.false; }); }); - it('should update the counter', () => { + it('should update the counter', () => + { page.todosCounter().should('have.text', '3 items left'); }); }); - describe('if filtered', () => { - beforeEach(() => { + describe('if filtered', () => + { + beforeEach(() => + { filter.link('completed').click(); todos.statusToggler(0).click(); }); - it('should hide a todo', () => { + it('should hide a todo', () => + { todos.assertCount(2); todos.assertTitle(0, 'CSS'); }); - it('should show a todo after selecting an oposite filter', () => { + it('should show a todo after selecting an oposite filter', () => + { filter.link('active').click(); todos.assertCount(3); @@ -620,29 +739,36 @@ describe('', () => { }); }); - describe('Toggle All Button', () => { - describe('if all todos are completed', () => { - beforeEach(() => { + describe('Toggle All Button', () => + { + describe('if all todos are completed', () => + { + beforeEach(() => + { cy.fixture('completed-todos').then(page.visit); }); - it('should be active', () => { + it('should be active', () => + { page.toggleAllButton().should('have.class', 'active'); }); - it('should stay active after filtering out all the todos', () => { + it('should stay active after filtering out all the todos', () => + { filter.link('active').click(); page.toggleAllButton().should('have.class', 'active'); }); - it('should become not active after toggling a todo', () => { + it('should become not active after toggling a todo', () => + { todos.statusToggler(1).click(); page.toggleAllButton().should('not.have.class', 'active'); }); - it('should make all todos active on click', () => { + it('should make all todos active on click', () => + { page.toggleAllButton().click(); todos.assertNotCompleted(0); @@ -652,10 +778,12 @@ describe('', () => { todos.assertNotCompleted(4); }); - it('should save changes to localStorage', () => { + it('should save changes to localStorage', () => + { page.toggleAllButton().click(); - page.data().then((todos) => { + page.data().then((todos) => + { expect(todos).to.have.length(5); expect(todos[0].completed).to.be.false; expect(todos[1].completed).to.be.false; @@ -665,29 +793,35 @@ describe('', () => { }); }); - it('should become not active on click', () => { + it('should become not active on click', () => + { page.toggleAllButton().click(); page.toggleAllButton().should('not.have.class', 'active'); }); }); - describe('if all todos are active', () => { - beforeEach(() => { + describe('if all todos are active', () => + { + beforeEach(() => + { cy.fixture('active-todos').then(page.visit); }); - it('should not be active', () => { + it('should not be active', () => + { page.toggleAllButton().should('not.have.class', 'active'); }); - it('should not become active after toggling a todo', () => { + it('should not become active after toggling a todo', () => + { todos.statusToggler(1).click(); page.toggleAllButton().should('not.have.class', 'active'); }); - it('should make all todos completed on click', () => { + it('should make all todos completed on click', () => + { page.toggleAllButton().click(); todos.assertCompleted(0); @@ -697,10 +831,12 @@ describe('', () => { todos.assertCompleted(4); }); - it('should save changes to localStorage', () => { + it('should save changes to localStorage', () => + { page.toggleAllButton().click(); - page.data().then((todos) => { + page.data().then((todos) => + { expect(todos).to.have.length(5); expect(todos[0].completed).to.be.true; expect(todos[1].completed).to.be.true; @@ -710,30 +846,36 @@ describe('', () => { }); }); - it('should become active on click', () => { + it('should become active on click', () => + { page.toggleAllButton().click(); page.toggleAllButton().should('have.class', 'active'); }); }); - describe('if there are some mixed todos', () => { - beforeEach(() => { + describe('if there are some mixed todos', () => + { + beforeEach(() => + { cy.fixture('todos').then(page.visit); }); - it('should not be active', () => { + it('should not be active', () => + { page.toggleAllButton().should('not.have.class', 'active'); }); - it('should become active after completing all todos', () => { + it('should become active after completing all todos', () => + { todos.statusToggler(3).click(); todos.statusToggler(4).click(); page.toggleAllButton().should('have.class', 'active'); }); - it('should make all todos completed on click', () => { + it('should make all todos completed on click', () => + { page.toggleAllButton().click(); todos.assertCompleted(0); @@ -743,10 +885,12 @@ describe('', () => { todos.assertCompleted(4); }); - it('should save changes to localStorage', () => { + it('should save changes to localStorage', () => + { page.toggleAllButton().click(); - page.data().then((todos) => { + page.data().then((todos) => + { expect(todos).to.have.length(5); expect(todos[0].completed).to.be.true; expect(todos[1].completed).to.be.true; @@ -756,7 +900,8 @@ describe('', () => { }); }); - it('should become active on click', () => { + it('should become active on click', () => + { page.toggleAllButton().click(); page.toggleAllButton().should('have.class', 'active'); @@ -764,38 +909,48 @@ describe('', () => { }); }); - describe('Renaming Form', () => { - beforeEach(() => { + describe('Renaming Form', () => + { + beforeEach(() => + { cy.fixture('todos').then(page.visit); todos.title(0).trigger('dblclick'); }); - describe('', () => { - it('should be opened on dblclick', () => { + describe('', () => + { + it('should be opened on dblclick', () => + { todos.titleField(0).should('exist'); }); - it('should have current value', () => { + it('should have current value', () => + { todos.titleField(0).should('have.value', 'HTML'); }); - it('should be focused', () => { + it('should be focused', () => + { todos.titleField(0).should('be.focused'); }); - it('should hide a title', () => { + it('should hide a title', () => + { todos.title(0).should('not.exist'); }); - it('should hide DeleteButton', () => { + it('should hide DeleteButton', () => + { todos.deleteButton(0).should('not.exist'); }); - it('should keep StatusToggler', () => { + it('should keep StatusToggler', () => + { todos.statusToggler(0).should('exist'); }); - it('should not open forms for other todos', () => { + it('should not open forms for other todos', () => + { todos.titleField(1).should('not.exist'); todos.titleField(2).should('not.exist'); todos.titleField(3).should('not.exist'); @@ -803,56 +958,69 @@ describe('', () => { }); }); - describe('if title was changed', () => { - beforeEach(() => { + describe('if title was changed', () => + { + beforeEach(() => + { todos.titleField(0).clear(); }); - describe('on Enter', () => { - it('should be closed', () => { + describe('on Enter', () => + { + it('should be closed', () => + { todos.titleField(0).type('123{enter}'); todos.titleField(0).should('not.exist'); }); - it('should show the updated title on Enter', () => { + it('should show the updated title on Enter', () => + { todos.titleField(0).type('Something{enter}'); todos.assertTitle(0, 'Something'); }); - it('should trim the new title on Enter', () => { + it('should trim the new title on Enter', () => + { todos.titleField(0).type(' Some new title {enter}'); todos.assertTitle(0, 'Some new title'); }); - it('should save changes to localStorage', () => { + it('should save changes to localStorage', () => + { todos.titleField(0).type(' Some new title {enter}'); - page.data().then((todos) => { + page.data().then((todos) => + { expect(todos).to.have.length(5); expect(todos[0].title).to.equal('Some new title'); }); }); }); - describe('on Escape', () => { - it('should be closed', () => { + describe('on Escape', () => + { + it('should be closed', () => + { todos.titleField(0).type('123{esc}'); todos.titleField(0).should('not.exist'); }); - it('should keep old title', () => { + it('should keep old title', () => + { todos.titleField(0).type('Something{esc}'); todos.assertTitle(0, 'HTML'); }); }); - describe('on blur', () => { - it('should save', () => { + describe('on blur', () => + { + it('should save', () => + { todos.titleField(0).type('Something'); todos.titleField(0).blur(); @@ -860,18 +1028,21 @@ describe('', () => { todos.assertTitle(0, 'Something'); }); - it('should save trimmed title', () => { + it('should save trimmed title', () => + { todos.titleField(0).type(' Some new title '); todos.titleField(0).blur(); todos.assertTitle(0, 'Some new title'); }); - it('should save changes to localStorage', () => { + it('should save changes to localStorage', () => + { todos.titleField(0).type(' Some new title '); todos.titleField(0).blur(); - page.data().then((todos) => { + page.data().then((todos) => + { expect(todos).to.have.length(5); expect(todos[0].title).to.equal('Some new title'); }); @@ -879,32 +1050,38 @@ describe('', () => { }); }); - describe('if title was not changed', () => { - it('should be close on Enter', () => { + describe('if title was not changed', () => + { + it('should be close on Enter', () => + { todos.titleField(0).type('{enter}'); todos.titleField(0).should('not.exist'); }); - it('should preserve current title on Enter', () => { + it('should preserve current title on Enter', () => + { todos.titleField(0).type('{enter}'); todos.assertTitle(0, 'HTML'); }); - it('should be closed on Escape', () => { + it('should be closed on Escape', () => + { todos.titleField(0).type('{esc}'); todos.titleField(0).should('not.exist'); }); - it('should preserve current title on Escape', () => { + it('should preserve current title on Escape', () => + { todos.titleField(0).type('{esc}'); todos.assertTitle(0, 'HTML'); }); - it('should save on blur', () => { + it('should save on blur', () => + { todos.titleField(0).clear(); todos.titleField(0).type('Something'); todos.titleField(0).blur(); @@ -914,63 +1091,77 @@ describe('', () => { }); }); - describe('if title became empty', () => { - beforeEach(() => { + describe('if title became empty', () => + { + beforeEach(() => + { todos.titleField(0).clear(); }); - describe('on Enter', () => { - it('should delete a todo', () => { + describe('on Enter', () => + { + it('should delete a todo', () => + { todos.titleField(0).type('{enter}'); todos.assertCount(4); todos.assertTitle(0, 'CSS'); }); - it('should save changes to localStorage', () => { + it('should save changes to localStorage', () => + { todos.titleField(0).type('{enter}'); - page.data().then((todos) => { + page.data().then((todos) => + { expect(todos).to.have.length(4); expect(todos[0].title).to.equal('CSS'); }); }); }); - describe('on Escape', () => { + describe('on Escape', () => + { - it('should be closed', () => { + it('should be closed', () => + { todos.titleField(0).type('{esc}'); todos.titleField(0).should('not.exist'); }); - it('should preserve todos', () => { + it('should preserve todos', () => + { todos.titleField(0).type('{esc}'); todos.assertCount(5); }); - it('should preserve current title', () => { + it('should preserve current title', () => + { todos.titleField(0).type('{esc}'); todos.title(0).should('have.text', 'HTML'); }); }); - describe('on blur', () => { - it('should delete a todo', () => { + describe('on blur', () => + { + it('should delete a todo', () => + { todos.titleField(0).blur(); todos.assertCount(4); todos.assertTitle(0, 'CSS'); }); - it('should save changes to localStorage', () => { + it('should save changes to localStorage', () => + { todos.titleField(0).blur(); - page.data().then((todos) => { + page.data().then((todos) => + { expect(todos).to.have.length(4); expect(todos[0].title).to.equal('CSS'); }); diff --git a/package-lock.json b/package-lock.json index 0adcc869f..b2887aa61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ }, "devDependencies": { "@cypress/react18": "^2.0.1", - "@mate-academy/scripts": "^1.8.5", + "@mate-academy/scripts": "^1.9.12", "@mate-academy/students-ts-config": "*", "@mate-academy/stylelint-config": "*", "@types/node": "^20.14.10", @@ -1170,10 +1170,11 @@ } }, "node_modules/@mate-academy/scripts": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@mate-academy/scripts/-/scripts-1.8.5.tgz", - "integrity": "sha512-mHRY2FkuoYCf5U0ahIukkaRo5LSZsxrTSgMJheFoyf3VXsTvfM9OfWcZIDIDB521kdPrScHHnRp+JRNjCfUO5A==", + "version": "1.9.12", + "resolved": "https://registry.npmjs.org/@mate-academy/scripts/-/scripts-1.9.12.tgz", + "integrity": "sha512-/OcmxMa34lYLFlGx7Ig926W1U1qjrnXbjFJ2TzUcDaLmED+A5se652NcWwGOidXRuMAOYLPU2jNYBEkKyXrFJA==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/rest": "^17.11.2", "@types/get-port": "^4.2.0", diff --git a/package.json b/package.json index e6134ce84..91d7489b9 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@cypress/react18": "^2.0.1", - "@mate-academy/scripts": "^1.8.5", + "@mate-academy/scripts": "^1.9.12", "@mate-academy/students-ts-config": "*", "@mate-academy/stylelint-config": "*", "@types/node": "^20.14.10", diff --git a/src/App.tsx b/src/App.tsx index a399287bd..da45588fa 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,8 @@ -/* eslint-disable jsx-a11y/control-has-associated-label */ import React from 'react'; +import { Header } from './components/Header/Header'; +import { Todos } from './components/Todos/Todos'; +import { Footer } from './components/Footer/Footer'; +import { TodoProvider } from './components/context/TodoContext'; export const App: React.FC = () => { return ( @@ -7,150 +10,13 @@ export const App: React.FC = () => {

todos

-
- {/* this button should have `active` class only if all todos are completed */} - -
- - {/* This todo is an active todo */} -
- - - - Not Completed Todo - - - -
- - {/* This todo is being edited */} -
- - - {/* This form is shown instead of the title and remove button */} -
- -
-
- - {/* This todo is in loadind state */} -
- - - - Todo is being saved now - - - -
- - - {/* Hide the footer if there are no todos */} -
- - 3 items left - - - {/* Active link should have the 'selected' class */} - - - {/* this button should be disabled if there are no completed todos */} - -
+