E2E тестирование

E2E (End-to-End) тестирование представляет собой проверку полного пользовательского сценария работы приложения от начала до конца. В отличие от модульных тестов, которые изолированно проверяют отдельные функции, и интеграционных тестов, ориентированных на взаимодействие компонентов, E2E-тесты имитируют реальные действия пользователя в браузере.

Для библиотеки Pikaday такой подход особенно важен, поскольку календарь тесно взаимодействует с DOM, пользовательским вводом, событиями браузера и внешними компонентами интерфейса.

Типичные сценарии E2E-тестирования включают:

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

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


Почему Pikaday требует E2E-тестирования

Многие ошибки невозможно обнаружить при помощи unit-тестов.

Например:

const picker = new Pikaday({
    field: document.getElementById('date')
});

Модульный тест может подтвердить успешное создание экземпляра:

expect(picker).toBeDefined();

Однако он не покажет:

  • отображается ли календарь после клика;
  • корректно ли позиционируется всплывающее окно;
  • выбирается ли дата мышью;
  • обновляется ли значение поля;
  • возникают ли проблемы при прокрутке страницы.

Именно подобные проблемы выявляются посредством E2E-проверок.


Инструменты для E2E-тестирования

Наиболее популярными решениями являются:

Cypress

Особенности:

  • быстрый запуск;
  • удобный интерфейс отладки;
  • автоматическое ожидание элементов;
  • удобная работа с DOM.

Пример:

cy.get('#date').click();
cy.get('.pika-day').contains('15').click();
cy.get('#date').should('have.value', '2025-05-15');

Playwright

Современный инструмент от Microsoft.

Преимущества:

  • поддержка нескольких браузеров;
  • параллельное выполнение тестов;
  • автоматические ожидания;
  • поддержка мобильных устройств.

Пример:

await page.click('#date');
await page.click('text=15');

await expect(
    page.locator('#date')
).toHaveValue('2025-05-15');

Selenium

Классическое решение для автоматизации браузеров.

Подходит для:

  • крупных корпоративных проектов;
  • тестирования старых браузеров;
  • сложных инфраструктур.

Пример:

driver.findElement(
    By.id('date')
).click();

Подготовка тестового приложения

Типичная HTML-разметка:

<input id="date">

<script>
new Pikaday({
    field: document.getElementById('date'),
    format: 'YYYY-MM-DD'
});
</script>

Такой пример служит основой для большинства сценариев автоматизированной проверки.


Проверка открытия календаря

Первый пользовательский сценарий заключается в отображении календаря после взаимодействия с полем.

Пример Cypress:

describe('Pikaday', () => {

    it('opens calendar', () => {

        cy.visit('/');

        cy.get('#date').click();

        cy.get('.pika-single')
            .should('be.visible');

    });

});

Проверяются:

  • обработчики событий;
  • создание DOM-элементов;
  • корректность отображения.

Проверка выбора даты

Основная функция Pikaday — выбор даты.

Пример:

cy.get('#date').click();

cy.contains('.pika-button', '15')
    .click();

cy.get('#date')
    .should('have.value', '2025-05-15');

После выполнения теста подтверждается:

  • успешный выбор даты;
  • обновление поля;
  • закрытие календаря при необходимости.

Проверка автоматического закрытия

Во многих интерфейсах календарь скрывается после выбора даты.

Проверка:

cy.get('#date').click();

cy.contains('.pika-button', '15')
    .click();

cy.get('.pika-single')
    .should('not.be.visible');

Подобный тест защищает от регрессий после обновления версии библиотеки.


Тестирование переключения месяцев

Навигация между месяцами является одним из ключевых элементов интерфейса.

Пример:

cy.get('#date').click();

cy.get('.pika-next')
    .click();

cy.get('.pika-label')
    .should('contain', 'June');

Проверяется корректность:

  • навигационных кнопок;
  • смены месяца;
  • перерисовки календаря.

Проверка перехода на предыдущий месяц

cy.get('.pika-prev')
    .click();

cy.get('.pika-label')
    .should('contain', 'April');

Такие тесты особенно полезны при кастомизации интерфейса.


Тестирование ограничения минимальной даты

Настройка:

new Pikaday({
    field: dateField,
    minDate: new Date(2025, 0, 10)
});

Проверка:

cy.get('#date').click();

cy.get('.is-disabled')
    .should('exist');

Дополнительно:

cy.get('.is-disabled')
    .first()
    .click({ force: true });

cy.get('#date')
    .should('have.value', '');

Подтверждается невозможность выбора запрещённых дат.


Тестирование максимальной даты

Конфигурация:

new Pikaday({
    field: dateField,
    maxDate: new Date(2025, 11, 31)
});

Проверка:

cy.get('.is-disabled')
    .should('exist');

Подобные сценарии критически важны для систем бронирования и оформления заказов.


Проверка ручного ввода

Многие приложения позволяют вводить дату напрямую.

Тест:

cy.get('#date')
    .type('2025-05-15');

cy.get('#date')
    .should('have.value', '2025-05-15');

Дополнительно проверяется синхронизация состояния календаря и поля ввода.


Проверка обработки ошибочного ввода

Некорректные данные должны обрабатываться безопасно.

Пример:

cy.get('#date')
    .type('invalid date');

cy.get('#date')
    .blur();

Далее проверяется логика приложения:

cy.get('.error')
    .should('be.visible');

Либо:

cy.get('#date')
    .should('have.value', '');

Проверка локализации

Конфигурация:

new Pikaday({
    field: dateField,
    i18n: {
        previousMonth: 'Назад',
        nextMonth: 'Вперёд',
        months: [
            'Январь',
            'Февраль',
            'Март',
            'Апрель',
            'Май',
            'Июнь',
            'Июль',
            'Август',
            'Сентябрь',
            'Октябрь',
            'Ноябрь',
            'Декабрь'
        ]
    }
});

Проверка:

cy.get('#date').click();

cy.get('.pika-label')
    .should('contain', 'Май');

Также проверяются:

  • дни недели;
  • подписи кнопок;
  • формат даты.

Тестирование нескольких календарей

Частый сценарий — выбор диапазона дат.

Пример:

<input id="start">
<input id="end">

Тест:

cy.get('#start').click();

cy.contains('.pika-button', '10')
    .click();

cy.get('#end').click();

cy.contains('.pika-button', '20')
    .click();

cy.get('#start')
    .should('have.value', '2025-05-10');

cy.get('#end')
    .should('have.value', '2025-05-20');

Проверяется независимость экземпляров Pikaday.


Проверка отправки формы

Пользовательский сценарий обычно завершается отправкой формы.

Пример:

cy.get('#date').click();

cy.contains('.pika-button', '15')
    .click();

cy.get('form')
    .submit();

Проверка:

cy.url()
    .should('include', '/success');

Либо:

cy.intercept('POST', '/booking')
    .as('booking');

cy.get('form')
    .submit();

cy.wait('@booking');

Проверка адаптивности

На мобильных устройствах календарь может вести себя иначе.

Playwright:

test.use({
    viewport: {
        width: 390,
        height: 844
    }
});

Тест:

await page.click('#date');

await expect(
    page.locator('.pika-single')
).toBeVisible();

Проверяется:

  • позиционирование;
  • размеры;
  • отсутствие выхода за границы экрана.

Проверка работы клавиатуры

Некоторые проекты требуют полной поддержки клавиатурной навигации.

Пример:

await page.focus('#date');

await page.keyboard.press('Enter');

Проверка:

await expect(
    page.locator('.pika-single')
).toBeVisible();

Навигация:

await page.keyboard.press('ArrowRight');
await page.keyboard.press('Enter');

Подтверждается доступность интерфейса для пользователей клавиатуры.


Проверка повторного открытия календаря

После выбора даты календарь должен сохранять корректное состояние.

Пример:

cy.get('#date').click();

cy.contains('.pika-button', '15')
    .click();

cy.get('#date').click();

cy.get('.is-selected')
    .should('exist');

Проверяется сохранение выбранного значения.


Тестирование асинхронной инициализации

Во многих SPA-приложениях календарь создаётся после загрузки данных.

Пример:

cy.visit('/');

cy.wait('@settings');

cy.get('#date')
    .click();

Проверка:

cy.get('.pika-single')
    .should('be.visible');

Тест подтверждает корректность работы после динамического создания компонента.


Проверка отсутствия JavaScript-ошибок

Во время выполнения сценариев полезно отслеживать исключения браузера.

Пример Cypress:

Cypress.on(
    'uncaught:exception',
    (err) => {

        expect(err).to.be.null;

        return false;
    }
);

Либо:

cy.window().then((win) => {

    cy.spy(win.console, 'error');

});

Подобные проверки позволяют обнаруживать скрытые проблемы даже при внешне корректной работе интерфейса.


Организация E2E-тестов

Распространённая структура проекта:

cypress/
├── e2e/
│   ├── date-selection.cy.js
│   ├── localization.cy.js
│   ├── navigation.cy.js
│   ├── forms.cy.js
│   └── accessibility.cy.js

Разделение по функциональным областям облегчает сопровождение тестового набора.


Практики повышения стабильности тестов

Избегать жёстких задержек

Плохо:

cy.wait(3000);

Хорошо:

cy.get('.pika-single')
    .should('be.visible');

Использовать устойчивые селекторы

Плохо:

cy.get('div:nth-child(5)');

Хорошо:

cy.get('[data-testid="date-input"]');

Изолировать сценарии

Каждый тест должен выполняться независимо:

beforeEach(() => {
    cy.visit('/');
});

Проверять бизнес-результат

Вместо проверки только DOM:

cy.get('.pika-button')
    .click();

целесообразно проверять итоговое состояние:

cy.get('#date')
    .should('have.value', '2025-05-15');

Такой подход обеспечивает надёжную проверку полного пользовательского взаимодействия с Pikaday, включая интерфейс календаря, обработку событий, интеграцию с формами, локализацию, ограничения дат, адаптивность и реальные сценарии эксплуатации приложения в браузере.