Реализация процесса выгрузки файла из контейнера с браузером в тестовый фреймворк

    Автоматизация End-2-End тестирования комплексной информационной системы
    Часть 2-2. Реализация процесса выгрузки файла из контейнера с браузером в тестовый фреймворк. Поиск имени загруженного браузером файла


    Автор: habr.com/ru/users/anrad
    Хабы:
    Теги: #autotest, #selenium, #selenoid, #headlessbrowser, #download


    Когда мы в разработке End-2-End автотестов для UI столкнулись с вопросом “Как получить имя последнего загруженного браузером файла из WebDriver?”, нагуглить ничего по-быстрому не получилось. Поэтому я и написал эту статью, в которой заодно рассказал, в чем именно у нас была проблема и как мы ее решили.

    Этой статьей мы продолжаем серию публикаций о том, как мы автоматизировали в одном из крупных проектов ЛАНИТ процесс ручного тестирования (далее – автотесты) большой информационной системы (далее – Системы) и что у нас из этого вышло.

    image
    source


    Сама статья завершает цикл, ниже ссылки на все предыдущие части:
    Часть 1. Организационно-управленческая. Зачем нам была нужна автоматизация. Организация процесса разработки и управления. Организация использования
    Часть 2. Техническая. Архитектура и технический стек. Детали реализации и технические сюрпризы
    Часть 2-1. Реализация базового класса для всех тестов и JUnit RuleChain

    Это было год назад. Итак, сегодня это может быть неактуально. Напишите в комментарии, если вы знаете, какой эффективный способ обработки файлов в случае нескольких серверов selenoid grid.

    При разработке “автотестов” у нас возникла задача по выгрузке (download) файлов из системы с последующим анализом их содержимого. Для «выгрузки» файлов из тестируемой системы мы используем следующие решения:
    • для «локального» режима запуска (запуск непосредственно на рабочем ПК) – при инициализации «локального» браузера ему в качестве параметра передается имя временной локальной папки. Далее файл читается напрямую из локальной папки для последующего анализа;
    • для «удаленного» режима (через Bamboo) – файл «забирался» из контейнера с браузером через фичу Solenoid сервера: selenoid-host.example.com:4444/download/{SESSION_ID}/{FILE_NAME}


    Детали описаны в документации.

    Для обоих режимов для доступа к файлу необходимо было знать его имя. В этом и был сюрприз. Проблема отсутствия данных об имени «download» файла заключалась в следующем:
    • после «клика» на кнопочку «загрузить файл», браузер загружал файл в соответствующую локальную папку в контейнере;
    • при этом имя файла формировалось динамически и, как его определить заранее, мы не смогли найти. Это была особенность нашей тестируемой системы;
    • далее этот файл следовало «вытащить» из контейнера и провести бизнес-проверки его содержимого;
    • чтобы «вытащить» файл, следовало знать его имя, а имени мы и не знали, так как имя генерировалось динамически и в ссылке его значения не было.


    Дополнительно ситуация усугублялась тем, что из-за большой сложности некоторых тестов в ходе теста могли выгружаться несколько файлов в рамках разных независимых тест-сценариев из состава самого теста.
    Наилучшим для нас решением оказался метод, при котором мы определяли последний загруженный файл. Сделать это можно было несколькими способами:
    • анализировать состав файлов непосредственно перед операцией и сразу после. Разница как раз и будет наш искомый файл. Или не будет, если файл не загрузился;
    • использовать возможность для браузера Google Chrome открыть через javascript страницу chrome://downloads/, которую можно обработать как обычную html-страницу. Требуемый нам файл будет первым в списке.


    Получение имени файла через анализ состава файлов


    Анализ состава загруженных браузером файлов для локального режима является тривиальной задачей.
    Для удаленного режима необходимо использовать «недокументированную» фичу селеноид сервера: selenoid-host.example.com:4444/download/{SESSION_ID} выводит список всех успешно загруженных файлов, где SESSION_ID – selenoid session ID
    В целом схема отлично работает за одним исключением: нам надо ждать некоторое время, пока файл загрузится и появится в списке. Ожидание можно установить через цикл таймаутов, однако так мы не можем получить информацию о возможной ошибке загрузки файла. Мы можем только определить, что файл не загрузился в этой схеме. Поэтому мы в конце концов остановились на методе определения имени файлов через страницу chrome://downloads/.

    Получение имени файла через страницу chrome://downloads/


    Этот метод одинаково хорошо работает как в локальном, так и в удаленном режиме. Схема работы достаточно простая:
    • запустить java script для открытия окна chrome://downloads/;
    • обработать данные в окне обычными способами. Дождаться, когда первый в списке файл загрузится и определить его имя;
    • закрыть окно chrome://downloads/ и вернуть имя искомого файла.


    Идею использования chrome://downloads/ мы нагуглили и, к моему сожалению, я не могу привести ссылку на источник, так как она банально не сохранилась. Сама реализация класса получения имени файла приведена далее.

    Класс получения имени загруженного файла через chrome://downloads/
    	private static final long DOWNLOADS_PAGE_LOAD_TIMEOUT = 5_000;
    	private static final long MAX_GET_FILE_NAME_ATTEMPT = 5;
     
    	public static String getLastDownloadedFilename() throws DownloaderException {
            String[] filename = new String[1];
            Exception[] ex = new Exception[1];
            WebDriver driver = WebDriverRunner.getWebDriver();
            JavascriptExecutor executor = (JavascriptExecutor) driver;
            Utils.driver.openNewTabCheckAndClose(
                    () -> {
                        executor.executeScript("window.open('','_blank');");
                    },
                    () -> {
                        driver.get("chrome://downloads/");
     
                        for (int i = 0; i < MAX_GET_FILE_NAME_ATTEMPT; i++) {
                        	ex[0] = null;
                            sleep();
                        	try {
                            	WebElement element = (WebElement) executor.executeScript(
                                        "return document.querySelector('downloads-manager').shadowRoot.querySelector('downloads-item').shadowRoot.getElementById('file-link')");
                            	filename[0] = element.getText();
                                LOGGER.info("Attempt get file name " + i + ". Name = '" + filename[0] + "'");
                        	} catch (WebDriverException e) {
                            	ex[0] = e;
                                LOGGER.info("Failed attempt "+ i + " to get filename text: " + e.getMessage());
                            	continue;
                        	}
                        	if (filename[0] != null && !filename[0].isEmpty()) {
                            	 // здесь мы удаляем ссылку на файл со страницы чтобы она не мешала потом
                             	executor.executeScript(
                                        "document.querySelector('downloads-manager').shadowRoot.querySelector('downloads-item').shadowRoot.getElementById('remove').click()");
                            	break;
                        	}
                        }
                    }
        	);
     
        	if (filename[0] != null && !filename[0].isEmpty()) {
                return filename[0];
        	}
     
        	String message = "Timeout. Can not get last downloaded file name from chrome://downloads/. File name is '" + filename[0] + "'. Exception: " + ex[0].getMessage();
            LOGGER.warning(message);
        	throw new DownloaderException(message, ex[0]);
    	}
    
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 0

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое