diff --git a/projects/cookie/cookie.html b/projects/cookie/cookie.html
new file mode 100644
index 0000000..766c545
--- /dev/null
+++ b/projects/cookie/cookie.html
@@ -0,0 +1,55 @@
+
+
+
+
+ Поиск cookie:
+
+
+
+ Добавить cookie:
+
+
+
+
+
+
+ Доступные cookie:
+
+
+
+ | имя |
+ значение |
+ удалить |
+
+
+
+
+
+
+
+
diff --git a/projects/cookie/cookie.spec.js b/projects/cookie/cookie.spec.js
new file mode 100644
index 0000000..d09d615
--- /dev/null
+++ b/projects/cookie/cookie.spec.js
@@ -0,0 +1,250 @@
+const hasOwnProperty = Object.prototype.hasOwnProperty;
+
+function getCookies() {
+ return document.cookie
+ .split('; ')
+ .filter(Boolean)
+ .map((cookie) => cookie.match(/^([^=]+)=(.+)/))
+ .reduce((obj, [, name, value]) => {
+ obj[name] = value;
+
+ return obj;
+ }, {});
+}
+
+describe('ДЗ 7.2 - Cookie editor', () => {
+ require('./index');
+
+ const app = document.querySelector('#app');
+ let filterNameInput;
+ let addNameInput;
+ let addValueInput;
+ let addButton;
+ let listTable;
+
+ describe('Интеграционное тестирование', () => {
+ beforeEach(() => {
+ const oldCookies = getCookies();
+
+ Object.keys(oldCookies).forEach(
+ (cookie) => (document.cookie = `${cookie}=;expires=${new Date(0)}`)
+ );
+
+ if (listTable) {
+ listTable.innerHTML = '';
+ }
+ });
+
+ it('на старнице должны быть элементы с нужными id', () => {
+ filterNameInput = app.querySelector('#filter-name-input');
+ addNameInput = app.querySelector('#add-name-input');
+ addValueInput = app.querySelector('#add-value-input');
+ addButton = app.querySelector('#add-button');
+ listTable = app.querySelector('#list-table tbody');
+
+ expect(filterNameInput).toBeInstanceOf(Element);
+ expect(addNameInput).toBeInstanceOf(Element);
+ expect(addValueInput).toBeInstanceOf(Element);
+ expect(addButton).toBeInstanceOf(Element);
+ expect(listTable).toBeInstanceOf(Element);
+ });
+
+ it('cookie должны добавляться при нажатии на "добавить"', () => {
+ let cookies;
+
+ addNameInput.value = 'test-cookie-name-1';
+ addValueInput.value = 'test-cookie-value-1';
+ addButton.click();
+
+ cookies = getCookies();
+ expect(hasOwnProperty.call(cookies, addNameInput.value));
+ expect(cookies[addNameInput.value]).toBe(addValueInput.value);
+ expect(listTable.children.length).toBe(1);
+
+ addNameInput.value = 'test-cookie-name-2';
+ addValueInput.value = 'test-cookie-value-2';
+ addButton.click();
+
+ cookies = getCookies();
+ expect(hasOwnProperty.call(cookies, addNameInput.value));
+ expect(cookies[addNameInput.value]).toBe(addValueInput.value);
+ expect(listTable.children.length).toBe(2);
+ });
+
+ it('если при добавлении указано имя существующей cookie, то в таблице не должно быть дублей', () => {
+ addNameInput.value = 'test-cookie-name-1';
+ addValueInput.value = 'test-cookie-value-1';
+ addButton.click();
+
+ addNameInput.value = 'test-cookie-name-2';
+ addValueInput.value = 'test-cookie-value-2';
+ addButton.click();
+
+ addNameInput.value = 'test-cookie-name-2';
+ addValueInput.value = 'test-cookie-value-2';
+ addButton.click();
+
+ const cookies = getCookies();
+ expect(hasOwnProperty.call(cookies, addNameInput.value));
+ expect(cookies[addNameInput.value]).toBe(addValueInput.value);
+ expect(listTable.children.length).toBe(2);
+ });
+
+ it('если при добавлении указано имя существующей cookie, то в таблице должно быть изменено ее значение', () => {
+ addNameInput.value = 'test-cookie-name-1';
+ addValueInput.value = 'test-cookie-value-1';
+ addButton.click();
+
+ addNameInput.value = 'test-cookie-name-2';
+ addValueInput.value = 'test-cookie-value-2';
+ addButton.click();
+
+ addNameInput.value = 'test-cookie-name-2';
+ addValueInput.value = 'other-test-cookie-value-2';
+ addButton.click();
+
+ const rows = [...listTable.children];
+ const changedRow = rows.filter(
+ (row) => row.children[1].textContent.trim() === 'other-test-cookie-value-2'
+ );
+ expect(changedRow.length).toBe(1);
+ });
+
+ it('cookie должны удаляться при нажатии на "удалить"', () => {
+ let cookies;
+ let deleteButton;
+
+ addNameInput.value = 'test-cookie-name-1';
+ addValueInput.value = 'test-cookie-value-1';
+ addButton.click();
+
+ addNameInput.value = 'test-cookie-name-2';
+ addValueInput.value = 'test-cookie-value-2';
+ addButton.click();
+
+ deleteButton = listTable.querySelector('button');
+
+ deleteButton.click();
+ cookies = getCookies();
+ expect(Object.keys(cookies).length).toBe(1);
+ expect(listTable.children.length).toBe(1);
+
+ deleteButton = listTable.querySelector('button');
+ deleteButton.click();
+ cookies = getCookies();
+ expect(Object.keys(cookies).length).toBe(0);
+ expect(listTable.children.length).toBe(0);
+ });
+
+ describe('Фильтрация', () => {
+ it('выводить список cookie, имя или значение которых соответствует фильтру', () => {
+ addNameInput.value = 'test-cookie-name-1';
+ addValueInput.value = 'test-cookie-value-1';
+ addButton.click();
+
+ addNameInput.value = 'test-cookie-name-2';
+ addValueInput.value = 'test-cookie-value-2';
+ addButton.click();
+
+ filterNameInput.value = 'test-cookie';
+ filterNameInput.dispatchEvent(new KeyboardEvent('input'));
+ expect(listTable.children.length).toBe(2);
+
+ filterNameInput.value = 'name-1';
+ filterNameInput.dispatchEvent(new KeyboardEvent('input'));
+ expect(listTable.children.length).toBe(1);
+
+ filterNameInput.value = 'name-2';
+ filterNameInput.dispatchEvent(new KeyboardEvent('input'));
+ expect(listTable.children.length).toBe(1);
+ });
+
+ it('добавлять cookie в таблицу, только если значение cookie соответствует фильтру', () => {
+ addNameInput.value = 'test-cookie-name-1';
+ addValueInput.value = 'test-cookie-value-1';
+ addButton.click();
+
+ addNameInput.value = 'test-cookie-name-2';
+ addValueInput.value = 'test-cookie-value-2';
+ addButton.click();
+
+ filterNameInput.value = 'value-2';
+ filterNameInput.dispatchEvent(new KeyboardEvent('input'));
+ expect(listTable.children.length).toBe(1);
+
+ addNameInput.value = 'test-cookie-name-3';
+ addValueInput.value = 'test-cookie-more-value-2';
+ addButton.click();
+
+ const cookies = getCookies();
+ expect(hasOwnProperty.call(cookies, addNameInput.value));
+ expect(cookies[addNameInput.value]).toBe(addValueInput.value);
+ expect(listTable.children.length).toBe(2);
+ });
+
+ it('не добавлять cookie в таблицу, если значение cookie не соответствует фильтру', () => {
+ addNameInput.value = 'test-cookie-name-1';
+ addValueInput.value = 'test-cookie-value-1';
+ addButton.click();
+
+ addNameInput.value = 'test-cookie-name-2';
+ addValueInput.value = 'test-cookie-value-2';
+ addButton.click();
+
+ filterNameInput.value = 'value-2';
+ filterNameInput.dispatchEvent(new KeyboardEvent('input'));
+ expect(listTable.children.length).toBe(2);
+
+ addNameInput.value = 'test-cookie-name-3';
+ addValueInput.value = 'test-cookie-value-3';
+ addButton.click();
+
+ const cookies = getCookies();
+ expect(hasOwnProperty.call(cookies, addNameInput.value));
+ expect(cookies[addNameInput.value]).toBe(addValueInput.value);
+ expect(listTable.children.length).toBe(1);
+ });
+
+ it('удалить cookie из табилицы, если ее значение перестало соответствовать фильтр', () => {
+ addNameInput.value = 'test-cookie-name-1';
+ addValueInput.value = 'test-cookie-value-1';
+ addButton.click();
+
+ addNameInput.value = 'test-cookie-name-2';
+ addValueInput.value = 'test-cookie-value-2';
+ addButton.click();
+
+ addNameInput.value = 'test-cookie-name-3';
+ addValueInput.value = 'test-cookie-value-2';
+ addButton.click();
+
+ filterNameInput.value = 'value-2';
+ filterNameInput.dispatchEvent(new KeyboardEvent('input'));
+ expect(listTable.children.length).toBe(2);
+
+ addNameInput.value = 'test-cookie-name-3';
+ addValueInput.value = 'test-cookie-value-3';
+ addButton.click();
+
+ const cookies = getCookies();
+ expect(hasOwnProperty.call(cookies, addNameInput.value));
+ expect(cookies[addNameInput.value]).toBe(addValueInput.value);
+ expect(listTable.children.length).toBe(1);
+ });
+
+ it('выводить все cookie, если фильтр не задан', () => {
+ addNameInput.value = 'test-cookie-name-1';
+ addValueInput.value = 'test-cookie-value-1';
+ addButton.click();
+
+ addNameInput.value = 'test-cookie-name-2';
+ addValueInput.value = 'test-cookie-value-2';
+ addButton.click();
+
+ filterNameInput.value = '';
+ filterNameInput.dispatchEvent(new KeyboardEvent('input'));
+ expect(listTable.children.length).toBe(3);
+ });
+ });
+ });
+});
diff --git a/projects/cookie/index.js b/projects/cookie/index.js
new file mode 100644
index 0000000..05c1ecb
--- /dev/null
+++ b/projects/cookie/index.js
@@ -0,0 +1,135 @@
+/*
+ ДЗ 7 - Создать редактор cookie с возможностью фильтрации
+
+ 7.1: На странице должна быть таблица со списком имеющихся cookie. Таблица должна иметь следующие столбцы:
+ - имя
+ - значение
+ - удалить (при нажатии на кнопку, выбранная cookie удаляется из браузера и таблицы)
+
+ 7.2: На странице должна быть форма для добавления новой cookie. Форма должна содержать следующие поля:
+ - имя
+ - значение
+ - добавить (при нажатии на кнопку, в браузер и таблицу добавляется новая cookie с указанным именем и значением)
+
+ Если добавляется cookie с именем уже существующей cookie, то ее значение в браузере и таблице должно быть обновлено
+
+ 7.3: На странице должно быть текстовое поле для фильтрации cookie
+ В таблице должны быть только те cookie, в имени или значении которых, хотя бы частично, есть введенное значение
+ Если в поле фильтра пусто, то должны выводиться все доступные cookie
+ Если добавляемая cookie не соответствует фильтру, то она должна быть добавлена только в браузер, но не в таблицу
+ Если добавляется cookie, с именем уже существующей cookie и ее новое значение не соответствует фильтру,
+ то ее значение должно быть обновлено в браузере, а из таблицы cookie должна быть удалена
+
+ Запрещено использовать сторонние библиотеки. Разрешено пользоваться только тем, что встроено в браузер
+ */
+
+import './cookie.html';
+
+/*
+ app - это контейнер для всех ваших домашних заданий
+ Если вы создаете новые html-элементы и добавляете их на страницу, то добавляйте их только в этот контейнер
+
+ Пример:
+ const newDiv = document.createElement('div');
+ homeworkContainer.appendChild(newDiv);
+ */
+const homeworkContainer = document.querySelector('#homework-container');
+// текстовое поле для фильтрации cookie
+const filterNameInput = homeworkContainer.querySelector('#filter-name-input');
+// текстовое поле с именем cookie
+const addNameInput = homeworkContainer.querySelector('#add-name-input');
+// текстовое поле со значением cookie
+const addValueInput = homeworkContainer.querySelector('#add-value-input');
+// кнопка "добавить cookie"
+const addButton = homeworkContainer.querySelector('#add-button');
+// таблица со списком cookie
+const listTable = homeworkContainer.querySelector('#list-table tbody');
+
+const cookiesMap = getCookies();
+let filterValue = '';
+
+updateTable();
+
+function getCookies() {
+ return document.cookie
+ .split('; ')
+ .filter(Boolean)
+ .map((cookie) => cookie.match(/^([^=]+)=(.+)/))
+ .reduce((obj, [, name, value]) => {
+ obj.set(name, value);
+
+ return obj;
+ }, new Map());
+}
+
+filterNameInput.addEventListener('input', function () {
+ filterValue = this.value;
+ updateTable();
+});
+
+addButton.addEventListener('click', () => {
+ const name = encodeURIComponent(addNameInput.value.trim());
+ const value = encodeURIComponent(addValueInput.value.trim());
+
+ if (!name) {
+ return;
+ }
+
+ document.cookie = `${name}=${value}`;
+ cookiesMap.set(name, value);
+
+ updateTable();
+});
+
+listTable.addEventListener('click', (e) => {
+ const { role, cookieName } = e.target.dataset;
+
+ if (role === 'remove-cookie') {
+ cookiesMap.delete(cookieName);
+ document.cookie = `${cookieName}=deleted; max-age=0`;
+ updateTable();
+ }
+});
+
+function updateTable() {
+ const fragment = document.createDocumentFragment();
+ let total = 0;
+
+ listTable.innerHTML = '';
+
+ for (const [name, value] of cookiesMap) {
+ if (
+ filterValue &&
+ !name.toLowerCase().includes(filterValue.toLowerCase()) &&
+ !value.toLowerCase().includes(filterValue.toLowerCase())
+ ) {
+ continue;
+ }
+
+ total++;
+
+ const tr = document.createElement('tr');
+ const nameTD = document.createElement('td');
+ const valueTD = document.createElement('td');
+ const removeTD = document.createElement('td');
+ const removeButton = document.createElement('button');
+
+ removeButton.dataset.role = 'remove-cookie';
+ removeButton.dataset.cookieName = name;
+ removeButton.textContent = 'Удалить';
+ nameTD.textContent = name;
+ valueTD.textContent = value;
+ valueTD.classList.add('value');
+ tr.append(nameTD, valueTD, removeTD);
+ removeTD.append(removeButton);
+
+ fragment.append(tr);
+ }
+
+ if (total) {
+ listTable.parentNode.classList.remove('hidden');
+ listTable.append(fragment);
+ } else {
+ listTable.parentNode.classList.add('hidden');
+ }
+}