Хобрук: Ваш путь к мастерству в программировании

Selenium — определить, завершена ли загрузка веб-страницы в Angular 2+

У меня есть набор тестов Selenium, в котором выполняются интеграционные тесты Selenium для ряда веб-приложений, некоторые из которых написаны на Angular 2+, а некоторые — на AngularJS.

Мы используем пользовательский ExpectedCondition с WebDriverWait, который мы используем, чтобы тестовые примеры ждали, пока приложения AngularJS не закончат загрузку, чтобы избежать ожидания произвольного периода времени:

private static ExpectedCondition<Boolean> angularIsFinished() {
    return new ExpectedCondition<Boolean>() {
        public Boolean apply(final WebDriver driver) {
            Object result = null;

            while(result == null || result.toString().equals("undefined")) {
                result = ((JavascriptExecutor)driver).executeScript("return typeof angular;");

                try {
                    Thread.sleep(200L);
                } catch (final InterruptedException ex) {
                    logger.error("Error while trying to sleep", ex);
                }
            }

            final String script = "  var el = document.querySelector(\"body\");\n" +
                    "  var callback = arguments[arguments.length - 1];\n" +
                    "  angular.element(el).injector().get('$browser').notifyWhenNoOutstandingRequests(callback);";
            ((JavascriptExecutor)driver).executeAsyncScript(script);
            return true;
        }

        public String toString() {
            return "Wait for AngularJS";
        }
    };
}

Однако return typeof angular; всегда будет возвращать undefined для приложения Angular 2+. Есть ли способ, аналогичный notifyWhenNoOutstandingRequests в AngularJS, который вы можете использовать, чтобы определить, когда приложение Angular 2+ закончило загрузку?

В этом вопросе упоминается использование NgZone как возможного решение, но как бы вы справились с этим с помощью скрипта, выполняемого через JavascriptExecutor?


Ответы:


1

Вы можете проверить это, позвонив, например. document.querySelector('app-root')? или произвольный селектор компонентов...

Или как насчет звонка document.readyState? Он должен иметь результат 'complete' после полностью загруженной веб-страницы, и не имеет значения, основана ли веб-страница на angular.

21.03.2019
  • Angular многое делает в фоновом режиме после вызова document.readyState. Вы не можете полагаться на него для автоматизации Angular. 22.03.2019

  • 2

    Благодаря @Ardesco answer я смог сделать что-то похожее на то, что делает Protractor, используя функцию window.getAllAngularTestabilities. Вот скрипт, который я запускаю, чтобы определить, загружается ли страница Angular 2+:

    var testability = window.getAllAngularTestabilities()[0];
    var callback = arguments[arguments.length - 1];
    testability.whenStable(callback);
    

    А вот как выглядит полный ExpectedCondition, который работает как для AngularJS, так и для Angular 2+:

    private static ExpectedCondition<Boolean> angularIsFinished() {
        return new ExpectedCondition<Boolean>() {
            public Boolean apply(final WebDriver driver) {
                Object result = null;
    
                boolean isAngular2Plus = false;
    
                while(result == null || result.toString().equals("undefined")) {
                    result = ((JavascriptExecutor)driver).executeScript("return typeof angular;");
                    if (result == null || result.toString().equals("undefined")) {
                        result = ((JavascriptExecutor)driver).executeScript("return typeof window.getAngularTestability;");
                        if (result != null && !result.toString().equals("undefined")) {
                            isAngular2Plus = true;
                        }
                    }
    
                    try {
                        Thread.sleep(200L);
                    } catch (final InterruptedException ex) {
                        logger.error("Error while trying to sleep", ex);
                    }
                }
    
                final String script;
                if (isAngular2Plus) {
                    script ="  var testability = window.getAllAngularTestabilities()[0];\n" +
                            "  var callback = arguments[arguments.length - 1];\n" +
                            "  testability.whenStable(callback);";
                } else {
                    script ="  var el = document.querySelector(\"body\");\n" +
                            "  var callback = arguments[arguments.length - 1];\n" +
                            "  angular.element(el).injector().get('$browser').notifyWhenNoOutstandingRequests(callback);";
                }
                ((JavascriptExecutor) driver).executeAsyncScript(script);
                return true;
            }
    
            public String toString() {
                return "Wait for AngularJS";
            }
        };
    }
    
    21.03.2019
  • Привет, Эндрю, я получаю тайм-аут для вышеуказанного кода скрипта. Не могли бы вы подтвердить, работает ли он для вас 16.03.2020

  • 3

    Глядя на код транспортира, я придумал два возможных решения:

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

    private static ExpectedCondition angular2IsTestable() {
        return (ExpectedCondition<Boolean>) driver -> {
            JavascriptExecutor jsexec = ((JavascriptExecutor) driver);
            Object result = jsexec.executeAsyncScript("window.seleniumCallback = arguments[arguments.length -1];\n" +
                            "if (window.getAllAngularTestabilities()) {\n" +
                            "    window.getAllAngularTestabilities().forEach(function (testability) {\n" +
                            "            testability.whenStable(window.seleniumCallback(true))\n" +
                            "        }\n" +
                            "    );\n" +
                            "} else {\n" +
                            "    window.seleniumCallback(false)\n" +
                            "}"
            );
    
            return Boolean.parseBoolean(result.toString());
        };
    }
    

    Второй вариант — специально проверить состояние тестируемости угловых корневых элементов:

    private static ExpectedCondition angular2ElementIsTestable(final WebElement element) {
        return (ExpectedCondition<Boolean>) driver -> {
            JavascriptExecutor jsexec = ((JavascriptExecutor) driver);
            Object result = jsexec.executeAsyncScript(
                    "window.seleniumCallback = arguments[arguments.length -1];\n" +
                            "var element = arguments[0];\n" +
                            "if (window.getAngularTestability && window.getAngularTestability(element)) {\n" +
                            "    window.getAngularTestability(element).whenStable(window.seleniumCallback(true));\n" +
                            "} else {\n" +
                            "    window.seleniumCallback(false)\n" +
                            "}"
            , element);
    
            return Boolean.parseBoolean(result.toString());
        };
    }
    

    Второй вариант более целенаправленный и, следовательно, более надежный, если вы хотите протестировать определенную область сайта.

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

    21.03.2019
  • Это меня приблизило - это просто проверяет, существует ли функция getAngularTestability для объекта окна. Это подтверждает, что приложение является приложением Angular 2+. Необходимо фактически вызвать функцию и добавить обратный вызов в метод whenStable, чтобы определить, когда страница завершила загрузку. 21.03.2019
  • Исправленный ответ использует обратные вызовы для возврата состояния. Однако это все еще можно улучшить. В настоящее время он вернет true, как только какая-либо тестируемость станет стабильной, может быть лучше нацелиться на конкретную тестируемость (путем передачи rootElement) или собрать все тестируемости и дождаться, пока они все станут стабильными. 22.03.2019
  • Было бы неплохо узнать, почему за это проголосовали, я что-то пропустил? 29.03.2019
  • Новые материалы

    Основы принципов S.O.L.I.D, Javascript, Git и NoSQL
    каковы принципы S.O.L.I.D? Принципы SOLID призваны помочь разработчикам создавать надежные, удобные в сопровождении приложения. мы видим пять ключевых принципов. Принципы SOLID были разработаны..

    Как настроить Selenium в проекте Angular
    Угловой | Селен Как настроить Selenium в проекте Angular Держите свое приложение Angular и тесты Selenium в одной рабочей области и запускайте их с помощью Mocha. В этой статье мы..

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..

    Настольный ПК как «одно кольцо, чтобы править всеми» домашних компьютеров
    Вид после 9 месяцев использования С настольных компьютеров все началось, но в какой-то момент они стали «серверами», и мы все перешли на ноутбуки. В прошлом году я столкнулся с идеей настольных..

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

    стройный-i18следующий
    Представляем стройную оболочку для i18next. Эта библиотека, основанная на i18next, заключает экземпляр i18next в хранилище svelte и отслеживает события i18next, такие как languageChanged,..