From 281e7742eb0e882d7aef787c57edddef9401d4a3 Mon Sep 17 00:00:00 2001 From: Vlad Possin Date: Wed, 23 Dec 2020 08:38:24 +0600 Subject: [PATCH] created homework_5 --- projects/async/functions.js | 34 +++++++++ projects/async/functions.spec.js | 52 ++++++++++++++ projects/async/index.js | 115 +++++++++++++++++++++++++++++++ projects/async/index.spec.js | 82 ++++++++++++++++++++++ projects/async/towns.html | 12 ++++ 5 files changed, 295 insertions(+) create mode 100644 projects/async/functions.js create mode 100644 projects/async/functions.spec.js create mode 100644 projects/async/index.js create mode 100644 projects/async/index.spec.js create mode 100644 projects/async/towns.html diff --git a/projects/async/functions.js b/projects/async/functions.js new file mode 100644 index 0000000..4309257 --- /dev/null +++ b/projects/async/functions.js @@ -0,0 +1,34 @@ +/* ДЗ 5 - Асинхронность и работа с сетью */ + +/* + Задание 1: + + Функция должна возвращать Promise, который должен быть разрешен через указанное количество секунд + + Пример: + delayPromise(3) // вернет promise, который будет разрешен через 3 секунды + */ +function delayPromise(seconds) { + return new Promise((resolve) => setTimeout(resolve, seconds * 1000)); +} + +/* + Задание 2: + + 2.1: Функция должна вернуть Promise, который должен быть разрешен с массивом городов в качестве значения + + Массив городов можно получить отправив асинхронный запрос по адресу + https://raw.githubusercontent.com/smelukov/citiesTest/master/cities.json + + 2.2: Элементы полученного массива должны быть отсортированы по имени города + + Пример: + loadAndSortTowns().then(towns => console.log(towns)) // должна вывести в консоль отсортированный массив городов + */ +function loadAndSortTowns() { + return fetch('https://raw.githubusercontent.com/smelukov/citiesTest/master/cities.json') + .then((res) => res.json()) + .then((towns) => towns.sort((a, b) => a.name.localeCompare(b.name))); +} + +export { delayPromise, loadAndSortTowns }; diff --git a/projects/async/functions.spec.js b/projects/async/functions.spec.js new file mode 100644 index 0000000..f95df26 --- /dev/null +++ b/projects/async/functions.spec.js @@ -0,0 +1,52 @@ +import { delayPromise, loadAndSortTowns } from './functions'; + +const hasOwnProperty = Object.prototype.hasOwnProperty; + +describe('ДЗ 6.1 - Асинхронность и работа с сетью', () => { + describe('delayPromise', () => { + it('должна возвращать Promise', () => { + const result = delayPromise(1); + + expect(result).toBeInstanceOf(Promise); + }); + + it('Promise должен быть resolved через указанное количество секунд', (done) => { + const result = delayPromise(1); + const startTime = new Date(); + + result + .then(() => { + expect(new Date() - startTime).toBeGreaterThanOrEqual(1000); + done(); + }) + .catch(done); + }); + }); + + describe('loadAndSortTowns', () => { + it('должна возвращать Promise', () => { + const result = loadAndSortTowns(); + + expect(result).toBeInstanceOf(Promise); + }); + + it('Promise должен разрешаться массивом из городов', (done) => { + const result = loadAndSortTowns(); + + result + .then((towns) => { + expect(Array.isArray(towns)); + expect(towns.length).toBe(50); + towns.forEach((town, i, towns) => { + expect(hasOwnProperty.call(towns, 'name')); + + if (i) { + expect(towns[i - 1].name.localeCompare(town.name)).toBeLessThanOrEqual(0); + } + }); + done(); + }) + .catch(done); + }); + }); +}); diff --git a/projects/async/index.js b/projects/async/index.js new file mode 100644 index 0000000..e0f8c57 --- /dev/null +++ b/projects/async/index.js @@ -0,0 +1,115 @@ +/* + Страница должна предварительно загрузить список городов из + https://raw.githubusercontent.com/smelukov/citiesTest/master/cities.json + и отсортировать в алфавитном порядке. + + При вводе в текстовое поле, под ним должен появляться список тех городов, + в названии которых, хотя бы частично, есть введенное значение. + Регистр символов учитываться не должен, то есть "Moscow" и "moscow" - одинаковые названия. + + Во время загрузки городов, на странице должна быть надпись "Загрузка..." + После окончания загрузки городов, надпись исчезает и появляется текстовое поле. + + Разметку смотрите в файле towns.html + + Запрещено использовать сторонние библиотеки. Разрешено пользоваться только тем, что встроено в браузер + + *** Часть со звездочкой *** + Если загрузка городов не удалась (например, отключился интернет или сервер вернул ошибку), + то необходимо показать надпись "Не удалось загрузить города" и кнопку "Повторить". + При клике на кнопку, процесс загрузки повторяется заново + */ + +/* + homeworkContainer - это контейнер для всех ваших домашних заданий + Если вы создаете новые html-элементы и добавляете их на страницу, то добавляйте их только в этот контейнер + + Пример: + const newDiv = document.createElement('div'); + homeworkContainer.appendChild(newDiv); + */ + +import { loadAndSortTowns } from './functions'; +import './towns.html'; + +const homeworkContainer = document.querySelector('#app'); + +/* + Функция должна вернуть Promise, который должен быть разрешен с массивом городов в качестве значения + + Массив городов пожно получить отправив асинхронный запрос по адресу + https://raw.githubusercontent.com/smelukov/citiesTest/master/cities.json + */ +function loadTowns() { + return loadAndSortTowns(); +} + +/* + Функция должна проверять встречается ли подстрока chunk в строке full + Проверка должна происходить без учета регистра символов + + Пример: + isMatching('Moscow', 'moscow') // true + isMatching('Moscow', 'mosc') // true + isMatching('Moscow', 'cow') // true + isMatching('Moscow', 'SCO') // true + isMatching('Moscow', 'Moscov') // false + */ +function isMatching(full, chunk) { + return full.toLowerCase().includes(chunk.toLowerCase()); +} + +/* Блок с надписью "Загрузка" */ +const loadingBlock = homeworkContainer.querySelector('#loading-block'); +/* Блок с надписью "Не удалось загрузить города" и кнопкой "Повторить" */ +const loadingFailedBlock = homeworkContainer.querySelector('#loading-failed'); +/* Кнопка "Повторить" */ +const retryButton = homeworkContainer.querySelector('#retry-button'); +/* Блок с текстовым полем и результатом поиска */ +const filterBlock = homeworkContainer.querySelector('#filter-block'); +/* Текстовое поле для поиска по городам */ +const filterInput = homeworkContainer.querySelector('#filter-input'); +/* Блок с результатами поиска */ +const filterResult = homeworkContainer.querySelector('#filter-result'); + +let towns = []; + +retryButton.addEventListener('click', () => { + tryToLoad(); +}); + +filterInput.addEventListener('input', function () { + updateFilter(this.value); +}); + +loadingFailedBlock.classList.add('hidden'); +filterBlock.classList.add('hidden'); + +async function tryToLoad() { + try { + towns = await loadTowns(); + loadingBlock.classList.add('hidden'); + loadingFailedBlock.classList.add('hidden'); + filterBlock.classList.remove('hidden'); + } catch (e) { + loadingBlock.classList.add('hidden'); + loadingFailedBlock.classList.remove('hidden'); + } +} + +function updateFilter(filterValue) { + filterResult.innerHTML = ''; + const fragment = document.createDocumentFragment(); + for (const town of towns) { + if (filterValue && isMatching(town.name, filterValue)) { + const townDiv = document.createElement('div'); + townDiv.textContent = town.name; + fragment.append(townDiv); + } + } + filterResult.append(fragment); +} + +tryToLoad(); + +export { loadTowns, isMatching }; diff --git a/projects/async/index.spec.js b/projects/async/index.spec.js new file mode 100644 index 0000000..8ea7e06 --- /dev/null +++ b/projects/async/index.spec.js @@ -0,0 +1,82 @@ +const hasOwnProperty = Object.prototype.hasOwnProperty; + +describe('ДЗ 6.2 - Фильтр городов', () => { + const filterPage = require('./index'); + const homeworkContainer = document.querySelector('#app'); + let loadingBlock; + let filterBlock; + let filterInput; + let filterResult; + + beforeAll(() => filterPage.loadTowns()); + + describe('Функциональное тестирование', () => { + describe('isMatching', () => { + it('должна определять присутствие подстроки в строке', () => { + expect(filterPage.isMatching('Moscow', 'moscow')); + expect(filterPage.isMatching('Moscow', 'mos')); + expect(filterPage.isMatching('Moscow', 'MoS')); + expect(filterPage.isMatching('Moscow', 'cow')); + expect(!filterPage.isMatching('Moscow', 'Berlin')); + }); + }); + describe('loadTowns', () => { + it('должна возвращать Promise', () => { + const result = filterPage.loadTowns(); + expect(result).toBeInstanceOf(Promise); + }); + + it('Promise должен разрешаться массивом из городов', (done) => { + /* eslint-disable max-nested-callbacks */ + const result = filterPage.loadTowns(); + + result + .then((towns) => { + expect(Array.isArray(towns)); + expect(towns.length).toBe(50); + towns.forEach((town, i, towns) => { + expect(hasOwnProperty.call(town, 'name')); + + if (i) { + expect(towns[i - 1].name.localeCompare(town.name)).toBeLessThanOrEqual(0); + } + }); + done(); + }) + .catch(done); + }); + }); + }); + + describe('Интеграционное тестирование', () => { + it('на старнице должны быть элементы с нужными id', () => { + loadingBlock = homeworkContainer.querySelector('#loading-block'); + filterBlock = homeworkContainer.querySelector('#filter-block'); + filterInput = homeworkContainer.querySelector('#filter-input'); + filterResult = homeworkContainer.querySelector('#filter-result'); + + expect(loadingBlock).toBeInstanceOf(Element); + expect(filterBlock).toBeInstanceOf(Element); + expect(filterInput).toBeInstanceOf(Element); + expect(filterResult).toBeInstanceOf(Element); + }); + + it('должен показываться список городов, соответствующих фильтру', (done) => { + filterInput.value = 'fr'; + filterInput.dispatchEvent(new KeyboardEvent('input')); + setTimeout(() => { + expect(filterResult.children.length).toBe(3); + done(); + }, 1000); + }); + + it('результат должен быть пуст, если в поле пусто', (done) => { + filterInput.value = ''; + filterInput.dispatchEvent(new KeyboardEvent('input')); + setTimeout(() => { + expect(filterResult.children.length).toBe(0); + done(); + }, 1000); + }); + }); +}); diff --git a/projects/async/towns.html b/projects/async/towns.html new file mode 100644 index 0000000..ae6639c --- /dev/null +++ b/projects/async/towns.html @@ -0,0 +1,12 @@ +
+
Загрузка...
+
+
Не удалось загрузить города
+ +
+
+ + +
+
+