Перейти к содержанию

Тестовое задание frontend-разработчика: полный разбор

Тестовое задание frontend-разработчика начинается с уточнения поведения. Даже небольшой каталог содержит решения, которых нет в словах «добавьте поиск». Нужно определить, какие поля искать, как сочетать условия и что показывать при отсутствии результата. Пройдём учебное задание от требований до передачи работающего примера.

Задание создано для этого урока и не выдано реальной компанией. У нас четыре материала «Учебной библиотеки», текстовый поиск и выбор темы. Решение использует HTML, CSS и JavaScript без дополнительных библиотек. Если в настоящем задании обязательны React, определённая структура или другой способ загрузки данных, эти требования нужно учитывать отдельно.

Фиксируем условие до реализации

Каталог должен показывать все материалы при открытии. Поиск работает только по названию, игнорирует регистр и пробелы по краям. Выбор темы применяется одновременно с текстовым условием. При отсутствии совпадений появляется сообщение; кнопка очистки возвращает все четыре материала и исходную тему.

Ситуация Ожидаемое поведение
Пустой запрос, все темы Четыре карточки
Запрос JAVASCRIPT Одна карточка JavaScript
Тема Frontend, пустой запрос Две карточки
Запрос javascript, тема Backend Ни одной карточки, сообщение
Очистка после фильтра Четыре карточки, тема «Все»

Эта таблица становится основой проверки. В ней нет сортировки, запросов к серверу и сохранения фильтра в URL: они не входят в выбранное условие. Если хотите добавить их как улучшения, сначала доведите согласованный результат и обозначьте дополнительный объём отдельно.

В реальном процессе также уточните срок, формат передачи и допустимые зависимости. Если задача предполагает существенную работу, заранее согласуйте ожидаемый объём. Не следует молча строить целый сервис по короткому заданию на один экран.

Отделяем выбор данных от отображения

Для начала напишем чистую функцию: она принимает массив и фильтры, возвращает подходящие элементы. Ей не нужен браузер, поэтому поведение удобно проверять отдельно. Это содержимое файла catalog.mjs:

export const articles = [
  { id: 1, title: "JavaScript: массивы", topic: "frontend" },
  { id: 2, title: "CSS Grid", topic: "frontend" },
  { id: 3, title: "HTTP: основы", topic: "backend" },
  { id: 4, title: "QA: поиск", topic: "quality" }
];

export function selectArticles(items, { query, topic }) {
  const normalized = query.trim().toLowerCase();
  return items.filter(item =>
    item.title.toLowerCase().includes(normalized) &&
    (topic === "all" || item.topic === topic)
  );
}

Пустая строка содержится в любом названии, поэтому при пустом запросе выбор ограничивает только тема. Два условия связаны через AND. Если использовать OR, выбор Backend вместе с запросом JavaScript вернёт неподходящие карточки: они будут проходить по одному из условий.

Функция не меняет входной массив и не знает, как выглядят карточки. Это позволяет обсуждать ошибку поиска без оформления страницы. Но она не является универсальным поисковым движком: морфология, исправление опечаток и поиск по содержимому здесь отсутствуют.

Готовим понятные элементы управления

Поле должно иметь видимую подпись, а не только исчезающую подсказку. Выбор темы и очистка также получают определённые названия:

<label for="query">Название материала</label>
<input id="query" type="search" autocomplete="off">

<label for="topic">Тема</label>
<select id="topic">
  <option value="all">Все темы</option>
  <option value="frontend">Frontend</option>
  <option value="backend">Backend</option>
  <option value="quality">QA</option>
</select>
<button id="clear" type="button">Очистить фильтры</button>

<p id="summary" aria-live="polite"></p>
<ul id="results"></ul>
<p id="empty" hidden>Материалы не найдены. Измените запрос или тему.</p>
<script type="module" src="./app.js"></script>

Счётчик расположен отдельно от списка. Изменение его текста не требует перемещения фокуса пользователя к карточкам. aria-live помогает сообщать об обновлении, однако одна эта атрибутика не подтверждает доступность всего приложения. Маршрут с клавиатуры и ясность подписей всё равно нужно проверить.

В комплекте есть полная HTML-страница с оформлением и этим поведением. Ссылки на настоящие статьи в карточках намеренно не добавлены: задача проверяет выбор материалов, а не навигацию по действующему сайту.

Связываем данные и интерфейс

Файл app.js импортирует данные и функцию. Карточки создаются через DOM, а название записывается как текст:

import { articles, selectArticles } from "./catalog.mjs";

const query = document.querySelector("#query");
const topic = document.querySelector("#topic");
const clear = document.querySelector("#clear");
const results = document.querySelector("#results");
const summary = document.querySelector("#summary");
const empty = document.querySelector("#empty");
const topicNames = {
  frontend: "Frontend", backend: "Backend", quality: "QA"
};

function render() {
  const selected = selectArticles(articles, {
    query: query.value, topic: topic.value
  });
  const cards = selected.map(article => {
    const card = document.createElement("li");
    const title = document.createElement("h2");
    const label = document.createElement("p");
    title.textContent = article.title;
    label.textContent = topicNames[article.topic];
    card.append(title, label);
    return card;
  });
  results.replaceChildren(...cards);
  summary.textContent = `Найдено материалов: ${selected.length}`;
  empty.hidden = selected.length > 0;
}

query.addEventListener("input", render);
topic.addEventListener("change", render);
clear.addEventListener("click", () => {
  query.value = "";
  topic.value = "all";
  render();
  query.focus();
});
render();

Здесь функция отображения каждый раз читает оба текущих значения. Поэтому результат не зависит от порядка действий: сначала выбрать тему или сначала ввести запрос. Очистка меняет оба поля, обновляет список и возвращает фокус к поиску.

textContent показывает название как текст. В отличие от сборки HTML-строки, это не интерпретирует символы названия как разметку. Для четырёх карточек полная замена списка проста и достаточна. Для большого интерфейса с интерактивными карточками потребовалось бы отдельно учитывать сохранение их состояния и фокуса.

Запускаем и проверяем основные маршруты

Откройте рабочую демонстрацию. Для запуска скачанного комплекта используйте локальный HTTP-сервер из корня папки frontend, например python3 -m http.server 8000, и перейдите на локальный адрес. Открытие файла напрямую не является выбранным способом проверки ES-модулей.

Сначала убедитесь, что видны четыре карточки. Введите JAVASCRIPT, добавьте пробелы по краям и получите одну карточку. Затем выберите Backend: результат станет пустым. Нажмите очистку — должны вернуться четыре карточки, а фокус окажется в поиске. Именно такой маршрут проверен в локальном браузере при подготовке материала.

Отдельно пройдите элементы с клавиатуры и посмотрите узкое окно. Видимый фокус, доступная кнопка и карточки без горизонтального выпадения важны для передачи результата. При этом один браузер и один размер окна не подтверждают совместимость со всеми устройствами; запишите фактически проверенную среду.

Что покажет просмотр кода

Выберите одно изменение и проследите его целиком. Пользователь меняет тему, обработчик вызывает render, функция выбирает данные, список заменяется, счётчик получает новое число. В этом маршруте нет скрытого второго источника состояния. Для небольшого задания такая прозрачность помогает найти ошибку и объяснить решение.

Если карточек станет несколько тысяч, этот способ полной перерисовки потребуется оценить по наблюдаемой скорости и поведению. Не добавляйте оптимизацию только из-за самого числа: сначала определите узкое место и необходимый результат. В учебной версии четыре элемента, поэтому мы проверяем корректность основного маршрута, а не обещаем масштабирование на произвольный объём.

Передаём решение вместе с объяснением

README должен содержать условие, способ запуска и выполненные проверки. Назовите ограничения: данные в памяти, поиск только по названию, отсутствие серверной загрузки и реальных ссылок на материалы. Это помогает получателю отличить принятые границы от забытых задач.

В сопроводительном сообщении достаточно указать, где находится решение, как его запустить и какое поведение реализовано. Не нужно повторять весь код. Будьте готовы объяснить нормализацию запроса, сочетание фильтров и пустую выдачу — именно эти решения связывают условие с интерфейсом.

Для самостоятельного продолжения добавьте фильтр в URL или загрузку данных и расширьте таблицу случаев. Сначала определите новое поведение при ошибке и повторной загрузке. Так учебное тестовое задание превращается в развивающийся проект с понятными изменениями.

Скачать полное решение и README · Практикум по поиску работы