Selenium - определяет, закончилась ли загрузка веб-страницы в Angular 2+ - PullRequest
1 голос
/ 21 марта 2019

У меня есть набор тестов 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+. Есть ли способ, аналогичный AngularJS notifyWhenNoOutstandingRequests , который можно использовать, чтобы определить, когда приложение Angular 2+ завершило загрузку?

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

Ответы [ 3 ]

1 голос
/ 21 марта 2019

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

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

0 голосов
/ 21 марта 2019

Благодаря ответу @ Ardesco я смог сделать что-то похожее на то, что делает 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";
        }
    };
}
0 голосов
/ 21 марта 2019

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

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

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());
    };
}

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

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

...