Protractor: Protractor не работает при загрузке приложения с помощью ngUpgrade

Созданный на 16 мая 2017  ·  51Комментарии  ·  Источник: angular/protractor

Я работаю над переносом приложения AngularJS (1.6) на Angular (4), и теперь у меня есть гибридное приложение, загружаемое с помощью NgUpgrade. Кажется, это полностью нарушило мои тесты транспортира.

Простите меня, если я просто делаю что-то не так, но я не могу найти ответа, почему это не сработает.

Я получаю следующую ошибку:

Ошибка: время ожидания завершения асинхронных задач Angular истекло через 11 секунд. Это может быть связано с тем, что текущая страница не является приложением Angular. Дополнительные сведения см. В FAQ: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting -for-angular

В ожидании элемента с локатором - Локатор: По (селектор css, .toggle-summary-button)

Изменения в гибридном приложении

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

1 Из HTML-тега удалено приложение ng-app:

<html lang="en" *ng-app="myapp">

2 Добавлены модули приложений (@NgModule) и т. Д.

3 Использовал NgUpgrade для начальной загрузки приложения:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { UpgradeModule } from '@angular/upgrade/static';
import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
  const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
  upgrade.bootstrap(document.body, ['myapp'], {strictDi: true});
});

Транспортир тесты

Исходя из указанной выше ошибки, проблема, похоже, связана с тем, что делает Protractor, когда ожидает Angular. У меня есть блок beforeEach, который загружает страницу входа, заполняет данные и входит в систему. Как ни странно, он все еще открывает страницу и вводит текст в поле имени пользователя, но затем не может продолжить.

Я безуспешно пытался:

  • добавление rootElement: 'body' в конфигурационный файл транспортира
  • добавление «ng12Hybrid: true» в мой конфигурационный файл транспортира - я получаю сообщение о том, что он больше не нужен, поскольку он автоматически определяет.
  • увеличив параметр allScriptsTimeout с 11000 до 60000, время ожидания по-прежнему истекает.
  • отключение параметра waitForAngularEnabled. Это решает проблему с полями входа в систему, но тогда ни один из моих http-mocks не работает, и тесты терпят неудачу.

Самый полезный комментарий

Привет! Мы работаем над изменением, которое позволит вам настроить, какие задачи ожидает Protractor, которые могут помочь вам решить указанную выше проблему. Это широкое изменение, касающееся Zone.js и Angular, и не имеет конкретного ETA (будет в порядке недель). Здесь будет информация о прогрессе.

Все 51 Комментарий

У меня была аналогичная проблема, которую решили обернуть этот код обновления следующим образом:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { UpgradeModule } from '@angular/upgrade/static';
import { AppModule } from './app.module';

angular.element(document).ready(() => {
  platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
    const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
    upgrade.bootstrap(document.body, ['myapp'], {strictDi: true});
  });
});

И в конфигурации Я НЕ ИСПОЛЬЗУЮ ни одно из них:

//ng12Hybrid: true,
//useAllAngular2AppRoots: true,
//rootElement: 'body',

Не могли бы вы взглянуть на https://github.com/angular/protractor/issues/4234 и посмотреть, есть ли у вас такая же проблема? Кроме того, в ngUpgrade $interval теперь будет блокировать транспортир там, где этого не было раньше (https://github.com/angular/angular/issues/16349), но я работаю над этим.

@Evilweed - спасибо за предложение, к сожалению, оно не решает проблему для меня.

@heathkit - Я нашел один длинный интервал $, выполнив поиск по всему проекту, но даже после его удаления проблема не исчезла. Я также закомментировал все случаи использования $ interval или $ timeout во всем приложении, и он по-прежнему не работает с той же ошибкой. Знаете ли вы, есть ли какие-либо способы отладки и выяснить, что может блокировать, если это проблема с чем-то асинхронным?

Вы можете опубликовать свою полную ошибку? В сообщении об ошибке должно отображаться, какие задачи ожидают выполнения, но возможно, что некоторые из них отсутствуют. Например, ошибка еще не отображает ожидающие задачи со стороны Angular. Если у вас давно выполняются асинхронные задачи в части Angular вашего приложения, вам нужно запускать их за пределами зоны Angular, чтобы не блокировать Protractor. (См. Https://github.com/angular/protractor/blob/master/docs/timeouts.md)

Еще раз спасибо за ответ. Подробная информация об ошибке приведена ниже. Несколько замечаний:

  • В моем тестовом файле есть блок beforeAll который должен заполнить поля имени пользователя и пароля для входа в систему. Он успешно заполняет поле имени пользователя, но затем истекает время ожидания в waitForAngular и не заполняет поле пароля. Кажется, именно в этом и заключается первая половина ошибки. Вторая половина - это фактический провальный тест (что имеет смысл, потому что не удалось войти в систему, поэтому элемент отсутствует).

  • Ошибка предполагает, что проблема может быть связана с HTTP-вызовом, поэтому, возможно, не $ interval или $ timeout.

  • На данный момент у меня немного Angular, почти все это AngularJS. Он не доходит до показа нового компонента Angular, потому что ему не удается войти в систему (поэтому я сомневаюсь, что HTTP-вызов связан с компонентом Angular 4)

  Message:
    Failed: Timed out waiting for asynchronous Angular tasks to finish after 11 seconds. This may be because the current page is not an Angular application. Please see the FAQ for more details: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular
    While waiting for element with locator - Locator: By(css selector, *[name="password"])
  Stack:
    ScriptTimeoutError: asynchronous script timeout: result was not received in 11 seconds
      (Session info: chrome=58.0.3029.110)
      (Driver info: chromedriver=2.29.461585,platform=Mac OS X 10.12.4 x86_64) (WARNING: The server did not provide any stacktrace information)
    Command duration or timeout: 11.07 seconds
    Build info: version: '3.4.0', revision: 'unknown', time: 'unknown'
    System info: host: 'Matt.lan', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.12.4', java.version: '1.8.0_111'
    Driver info: org.openqa.selenium.chrome.ChromeDriver
    Capabilities [{applicationCacheEnabled=false, rotatable=false, mobileEmulationEnabled=false, networkConnectionEnabled=false, chrome={chromedriverVersion=2.29.461585, takesHeapSnapshot=true, pageLoadStrategy=normal, databaseEnabled=false, handlesAlerts=true, hasTouchScreen=false, version=58.0.3029.110, platform=MAC, browserConnectionEnabled=false, nativeEvents=true, acceptSslCerts=true, locationContextEnabled=true, webStorageEnabled=true, browserName=chrome, takesScreenshot=true, javascriptEnabled=true, cssSelectorsEnabled=true, unexpectedAlertBehaviour=}]
    Session ID: 40deafceb49b72b1c0188ad0895c1179
        at Object.checkLegacyResponse (/Users/matt/web-app/node_modules/selenium-webdriver/lib/error.js:505:15)
        at parseHttpResponse (/Users/matt/web-app/node_modules/selenium-webdriver/lib/http.js:509:13)
        at doSend.then.response (/Users/matt/web-app/node_modules/selenium-webdriver/lib/http.js:440:13)
        at process._tickCallback (internal/process/next_tick.js:109:7)
    From: Task: Protractor.waitForAngular() - Locator: By(css selector, *[name="password"])
        at thenableWebDriverProxy.schedule (/Users/matt/web-app/node_modules/selenium-webdriver/lib/webdriver.js:816:17)
        at ProtractorBrowser.executeAsyncScript_ (/Users/matt/web-app/node_modules/protractor/lib/browser.ts:608:24)
        at angularAppRoot.then (/Users/matt/web-app/node_modules/protractor/lib/browser.ts:642:23)
        at ManagedPromise.invokeCallback_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:1366:14)
        at TaskQueue.execute_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2970:14)
        at TaskQueue.executeNext_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2953:27)
        at asyncRun (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2813:27)
        at /Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:676:7
        at process._tickCallback (internal/process/next_tick.js:109:7)Error
        at ElementArrayFinder.applyAction_ (/Users/matt/web-app/node_modules/protractor/lib/element.ts:482:23)
        at ElementArrayFinder.(anonymous function).args [as sendKeys] (/Users/matt/web-app/node_modules/protractor/lib/element.ts:96:21)
        at ElementFinder.(anonymous function).args [as sendKeys] (/Users/matt/web-app/node_modules/protractor/lib/element.ts:873:14)
        at /Users/matt/web-app/e2e-tests/modules/authentication.js:83:38
        at ManagedPromise.invokeCallback_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:1366:14)
        at TaskQueue.execute_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2970:14)
        at TaskQueue.executeNext_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2953:27)
        at asyncRun (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2813:27)
        at /Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:676:7
        at process._tickCallback (internal/process/next_tick.js:109:7)
    From: Task: Run beforeAll in control flow
        at Object.<anonymous> (/Users/matt/web-app/node_modules/jasminewd2/index.js:94:19)
    From asynchronous test: 
    Error
        at Suite.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:28:3)
        at Object.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:7:1)
        at Module._compile (module.js:571:32)
        at Object.Module._extensions..js (module.js:580:10)
        at Module.load (module.js:488:32)
        at tryModuleLoad (module.js:447:12)
  Message:
    Failed: No element found using locator: By(css selector, .toggle-asset-summary-button)
  Stack:
    NoSuchElementError: No element found using locator: By(css selector, .toggle-asset-summary-button)
        at elementArrayFinder.getWebElements.then (/Users/matt/web-app/node_modules/protractor/lib/element.ts:851:17)
        at ManagedPromise.invokeCallback_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:1366:14)
        at TaskQueue.execute_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2970:14)
        at TaskQueue.executeNext_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2953:27)
        at asyncRun (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2813:27)
        at /Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:676:7
        at process._tickCallback (internal/process/next_tick.js:109:7)Error
        at ElementArrayFinder.applyAction_ (/Users/matt/web-app/node_modules/protractor/lib/element.ts:482:23)
        at ElementArrayFinder.(anonymous function).args [as click] (/Users/matt/web-app/node_modules/protractor/lib/element.ts:96:21)
        at ElementFinder.(anonymous function).args [as click] (/Users/matt/web-app/node_modules/protractor/lib/element.ts:873:14)
        at Page.showAssetSummaryComponent (/Users/matt/web-app/e2e-tests/specs/home/page-object.js:289:31)
        at Object.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:63:12)
        at /Users/matt/web-app/node_modules/jasminewd2/index.js:112:25
        at new ManagedPromise (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:1067:7)
        at ControlFlow.promise (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2396:12)
        at schedulerExecute (/Users/matt/web-app/node_modules/jasminewd2/index.js:95:18)
        at TaskQueue.execute_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2970:14)
    From: Task: Run fit("should open asset summary component") in control flow
        at Object.<anonymous> (/Users/matt/web-app/node_modules/jasminewd2/index.js:94:19)
        at /Users/matt/web-app/node_modules/jasminewd2/index.js:64:48
        at ControlFlow.emit (/Users/matt/web-app/node_modules/selenium-webdriver/lib/events.js:62:21)
        at ControlFlow.shutdown_ (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2565:10)
        at shutdownTask_.MicroTask (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2490:53)
        at MicroTask.asyncRun (/Users/matt/web-app/node_modules/selenium-webdriver/lib/promise.js:2619:9)
    From asynchronous test: 
    Error
        at Suite.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:62:5)
        at Suite.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:60:3)
        at Object.<anonymous> (/Users/matt/web-app/e2e-tests/specs/home/asset.spec.js:7:1)

Точно так же - со свежей новой конфигурацией angular4 'quickstart' и очень простым тестом транспортира. В негибридном режиме все работает нормально.

Failed: Timed out waiting for asynchronous Angular tasks to finish after 50 seconds. This may be because the current page is not an Angular application. Please see the FAQ for more details: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular
    While waiting for element with locator - Locator: By(css selector, #loginModal h4)

Я все еще вижу эту проблему.
Использование: @ angular / upgrade 4.2.2
который содержит исправление от @heathkit (https://github.com/angular/angular/pull/16836), чтобы получить стабильный API

Если кто-то может поделиться репо, в котором показана проблема, я займусь этим дальше.

Что я обнаружил до сих пор

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

Это работало нормально, когда мое приложение работало только на AngularJS, но при загрузке в качестве гибридного приложения возникала проблема с тайм-аутом (предположительно, из-за того, как оно запускается в зонах).

Пример репо

Я создал пример репо на https://github.com/mattwilson1024/angular-hybrid . Это гибридное (NgUpgrade) приложение со следующими частями:

  • Самая внешняя страница (CharactersPage) предоставляется AngularJS (1.6).
  • Страница содержит компонент CharacterListComponent, который представляет собой компонент Angular (4) более ранней версии.
  • CharacterListComponent использует другой компонент Angular 4, компонент CharacterDetail.

Основная ветвь содержит работающее приложение и один тест транспортира, который проходит без проблем.

Ветка углового момента добавляет зависимость от библиотеки angular-moment и использует свою директиву am-time-ago для добавления метки к странице, показывающей, сколько лет назад прошло с момента выхода первого эпизода. Само приложение работает нормально, но тест транспортира теперь не синхронизируется.

Таймауты

Чтобы убедиться, что это происходит, я попытался добавить различные типы тайм-аутов к контроллеру AngularJS со следующими результатами. Это подтвердило, что $ interval работает, а $ timeout, setInterval или setTimeout - нет.

function sayHello() { console.log('Hello, world'); }

$interval(sayHello, 30000); // Succeeds (with Angular 4.2.0 and later - see https://github.com/angular/angular/pull/16836)
$timeout(sayHello, 30000); // Fails - Timed out waiting for asynchronous Angular tasks to finish after 11 seconds
setInterval(sayHello, 30000); // Fails - Timed out waiting for asynchronous Angular tasks to finish after 11 seconds
setTimeout(sayHello, 300000); // Fails - Timed out waiting for asynchronous Angular tasks to finish after 11 seconds

Следующие шаги

Удалось отследить, что происходит, я до сих пор не знаю, какое решение. Я понимаю, что может потребоваться запустить определенные фрагменты кода «вне зоны Angular», хотя я не уверен, как это сделать из сегментов AngularJS, где у меня нет класса TypeScript, в который можно внедрить зону и т. Д.

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

Зоны устанавливаются в окне, поэтому вам не нужно вводить NgZone. Вы можете работать за пределами зоны Angular в AngularJS следующим образом

if (Zone && Zone.current.name === 'angular') {
  let angularZone = Zone.current;
  Zone.parent.run(() => {
    $timeout(() => {
       // Stuff here is run outside the Angular zone and will not be change detected
       angularZone.run(() => {
         // Inside the zone and will be change detected.
       });
    }, 500);
  });
}

Вы правы в том, что любое использование setTimeout или setInterval, даже в стороннем коде, может блокировать стабильность Protractor и вызывать тайм-ауты. Мы добавляем отслеживание задач в Angular и AngularJS, что должно упростить отладку этих проблем, показывая вам, откуда взялась задача async, для которой истекло время ожидания Protractor. На самом деле, пройдет пара месяцев, прежде чем мы сможем внедрить это в Protractor.

Что касается углового момента, лучшим вариантом может быть отправка им PR с учетом зоны, как показано выше. В противном случае вы можете выборочно отключить waitForAngular и вместо этого использовать browser.wait и ExpectedConditions. Мы работаем над более гибким API waitForAngular, который позволит вам контролировать, какие задачи вы ждете, но до этого еще далеко.

В нашей ситуации тесты могут быть написаны обычным способом (без синхронизации Angular) ( browser.ignoreSynchronization = true с использованием EC и т. Д.), Но терпят неудачу при установке browser.ignoreSynchronization = false; с "Error while waiting for Protractor to sync with the page: "Cannot read property '$$testability' of undefined"

По сути, это сводит на нет весь наш набор автоматизированных тестов E2E.

Используя Protractor 5.1.2, если я установил ng12Hybrid: true я получаю сообщение:

Вы установили ng12Hybrid. Начиная с Protractor 4.1.0, Protractor может автоматически определять, используете ли вы приложение ngUpgrade (если ng1 загружен до того, как вы вызываете platformBrowserDynamic ()), и этот флаг больше не нужен для большинства пользователей.

Очевидно, что документация angular.io здесь устарела: https://angular.io/guide/upgrade#e2e -tests

Знаменитые последние слова из того же самого документа:

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

В свете этого заявления на angular.io:

@heathkit Спасибо за совет о работе внутри и вне зон, но, честно говоря, мы ищем решение, которое работает с нашим существующим кодом AngularJS, как есть. У нас есть обширный набор Protractor, который полагается на неявную синхронизацию AngularJS. Наша цель - перенести наш AngularJS на Angular с помощью ngUpgrade, но эта проблема, связанная с невозможностью запустить наш существующий пакет Protractor в режиме гибридного обновления, помешала нашей инициативе. Разумное исправление было бы очень желательно прямо сейчас.

Если мы делаем что-то в корне неправильно, мы обязательно хотели бы знать, что.

Столкнувшись с той же проблемой! : 0

Здесь то же самое: AngularJS 1.6 -> Angular 4.2.4

Кто-нибудь знает какое-либо обходное решение, каким бы большим оно ни было? Без возврата на 1.6 то есть.

@ quinw68 - @heathkit предлагает метод запуска кода за пределами Angular «Зоны» в своем предыдущем ответе в этом потоке. Я думаю, что это означает изменения в исходном коде вашего приложения, чтобы оно работало, и может даже зависеть от сторонних библиотек, добавляющих поддержку. Пожалуйста, поправьте меня, если я ошибаюсь.

Еще один «обходной путь», о котором я знаю, - это относиться к вашему существующему приложению так, как будто оно больше не является приложением AngularJS. Это означает, что вам придется изменить тесты Protractor, чтобы они больше не ожидали Angular, а явно проверяли наличие элементов на странице, прежде чем запрашивать их состояние. В зависимости от размера вашего приложения это может быть непростой задачей.

Надеюсь, это поможет.

Насколько я понимаю, проблема связана не с ngUpgrade, а с Angular. Я имею в виду, что даже в чистом приложении Angular (2+) наличие длинного setTimeout (напрямую или через стороннюю библиотеку) вызовет тайм-аут транспортира.

@heathkit , ты можешь это подтвердить или я что-то пропустил? NgUpgrade сбрасывает транспортир или ухудшает ситуацию в некотором роде?

@gkalpak Вы правы, что это скорее проблема, связанная с Angular, но люди сталкиваются с ней, когда используют ngUpgrade с большим приложением AngularJS.

В AngularJS Protractor будет ждать только асинхронных задач, запущенных с $browser.defer() - в основном это просто вызовы $timeout и $http в коде приложения. Как только кто-то начнет использовать ngUpgrade, у его существующих тестов Protractor начнется тайм-аут из-за длинных вызовов setTimeout (возможно, из сторонних библиотек), чего они не делали раньше. В основном проблема в том, что ngUpgrade работает, как задумано, и теперь тесты приложения AngularJS и Protractor внезапно становятся предметом семантики Angular (2+).

Итак, как бы это решить для чистого приложения Angular? Что делать, если сторонняя библиотека Angular устанавливает длинный setTimeout , что приводит к тайм-ауту Protractor?

@vikerman Я слышал, что вы скоро поможете новому члену команды приступить к работе над этим, так что поручаю вам, пока они не появятся на борту.

На самом деле у нас нет хорошего ответа на случай, когда стороннее приложение устанавливает длительный тайм-аут. На данный момент все, что вы можете сделать, это отключить waitForAngular и полагаться на ExpectedConditions, но это не идеально. Я работаю над добавлением функции в Protractor, которая даст людям больше контроля над тем, какие макрозадачи ждут их тесты, но до этого еще далеко.

@heathkit @vikerman Какие-нибудь обновления по этому

В проекте, над которым я работаю, раньше была эта проблема, но они обернули функцию непрерывного опроса для работы за пределами угловой зоны, чтобы она не влияла на browser.waitForAngular ();

Прежде всего, спасибо команде / участникам Protractor . Protractor был краеугольным камнем моего приложения, так как у него было очень мало производственных проблем после десятков выпусков за 3 года, более 200 сервисов, директив, компонентов, контроллеров с около 400 тестами e2e Protractor.

Это хорошее напоминание о том, насколько я полагаюсь на Protractor, но эта проблема остановила мое обновление Angular после того, как я посвятил 2 недели простой начальной загрузке ngUpgrade и дополнительным потребностям. Это сделано, но теперь тесты e2e не могут быть запущены, и развертывание остановлено без видимого пути вперед (пока).

Благодаря отличной информации от @ mattwilson1024 я убедился, что $timeout , setTimeout , setInterval не используются в нашем приложении.

Но я обнаружил, что angular-ui-bootstrap (v.2.5.6) и ui-select (v0.19.8) действительно используют $timeout . Я широко использую обе библиотеки. Кажется, многие другие тоже полагаются на них - angular-ui-bootstrap - 400 тыс. Загрузок в минуту / мес, ui-select 100 тыс. + / Мес.

angular-ui-bootstrap создал проблему в 2015 году для замены $ timeout на $ interval специально из-за этой проблемы, но, к сожалению, решил, что «эту ошибку следует исправить в транспортире, а не здесь. Закрытие этого». Кроме того, angular-ui-bootstrap больше не предоставляет обновления liu своей Angular версии @ ng-bootstrap / ng-bootstrap, которая находится в стадии бета-тестирования.

Я удивляюсь, когда вижу широко используемые библиотеки angular, которые не гарантируют валидность Protractor. В этом я согласен с @heathkit, что эти библиотеки должны исправиться сами. Но я надеюсь, что Protractor сможет вмешаться и смягчить все это одним махом. Даже хороший обходной путь был бы золотым. Я разочарован тем, что мне придется отказаться от моего ngUpgrade, пока я не раскопаю все эти $timeout зависимости и, возможно, вместо того, чтобы просто включить ngGrade и начать отбирать элементы для обновления, когда это возможно, я вероятно, будет зависеть от обновления или перехода с этих сторонних элементов вместе с добавлением ngUpgrade. Фу.

Я продолжаю исследовать свое приложение (конкретные виновники, вызывающие тайм-аут (ы), как двигаться вперед и т. Д.) И сообщу обо всем, что может быть полезно другим.

Где я нашел $ timeout:
угловой-пользовательский-bootstrap v2.5.6

  • UibAlertController
  • UibCarouselController
  • UibDatepickerPopupController
  • UibTypeaheadController
  • UibTooltip

ui-select

  • uiSelect
  • uiSelectMultiple
  • uiSelectSingle
  • uiSelectSort
  • uisOpenClose

angular-resource $resource вводит и использует $timeout
угловой фильтр ( $window.setTimeout )

Привет! Мы работаем над изменением, которое позволит вам настроить, какие задачи ожидает Protractor, которые могут помочь вам решить указанную выше проблему. Это широкое изменение, касающееся Zone.js и Angular, и не имеет конкретного ETA (будет в порядке недель). Здесь будет информация о прогрессе.

У меня такая же проблема, когда я загружаю свое приложение [email protected] с NgUpgrade на [email protected].
Спасибо за комментарии выше, особенно @ mattwilson1024 и @tonybranfort , которые помогают мне лучше понять проблему.

В моем случае я нашел временное решение, добавив browser.ignoreSynchronization = true/false; вокруг кода, время ожидания которого истекло.

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

it('should be able to pick option', function () {
        expect(myPage.optionSelected()).toBe(false);
        myPage.pickOption('Option 1');
        expect(myPage.anyOptionSelected()).toBe(true);
        expect(myPage.getSelectedOption()).toBe('Option 1');
});

Когда я пытаюсь отладить этот код, я обнаружил, что тайм-аут произошел на последнем шаге. Я полагаю, что основная проблема - это инструмент из angular-ui-bootstrap с использованием $ timeout: https://github.com/angular-ui/bootstrap/blob/master/src/tooltip/tooltip.js

Однако в этом тесте нет асинхронных вызовов. Я просто добавляю ignoreSynchronization в начале и сбрасываю его в конце:

it('should be able to pick a font', function () {
        browser.ignoreSynchronization = true;  // tell protractor do not waitForAngular
        expect(myPage.optionSelected()).toBe(false);
        myPage.PickOption('Option 1');
        expect(myPage.anyOptionSelected()).toBe(true);
        expect(myPage.getSelectedOption()).toBe('Option 1');
        browser.ignoreSynchronization = false;  // reset the flag
});

Я не знаю, рекомендуется ли этот способ или он сделает тесты более нестабильными. Но для тестов, которые не требуют никаких асинхронных вызовов, но по-прежнему страдают проблемой тайм-аута, он должен их исправить.

Привет, @vikerman. Есть поводу ? https://github.com/angular/protractor/issues/4290#issuecomment -337094393

Мое решение:

В этом репозитории:

https://github.com/Innovic-io/protractor-angular-hybrid-app

Подождите, пока приложение здесь:
https://github.com/Innovic-io/protractor-angular-hybrid-app/blob/master/src/models/angular.ts#L9

@vikerman Есть ли обновления по этому

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

От: Castaway (ы?) На острове AngularJS скрученного по транспортиру Issue # 4290
Кому : @vikerman @juliemr

Эй! Можем ли мы получить последнюю информацию по этому вопросу? Последнее обновление 16 октября было расчетным временем прибытия в «порядке недель». Или закройте этот вопрос, чтобы мы могли двигаться дальше.

Вот плоты, которые я сейчас рассматриваю:

  • Замените все файлы angular-ui-bootstrap / ui-select . Для меня это важно. Это все равно нужно сделать, но меня беспокоит, что появится что-то еще, и я все равно застряну на AngularJS после этой работы. Извлеченный урок: оберните сторонние компоненты моими собственными.
  • waitForAngularEnabled(false) для всех тестов, предложенных @vladotesanovic. Я скептически отношусь к этому для всего приложения, но я не тестировал его. Сейчас я использую waitForAngularEnabled только в одной ситуации и только в очень конкретном случае.
  • Обновите все приложение до Angular, а не до AngularJS / Angular, параллельную инкрементную миграцию: Нет, просто не вариант с моими ресурсами.
  • Переходите на Vue или React вместо Angular. Одно из первых мест в списке, особенно учитывая, что оба они могут сосуществовать с AngularJs.
  • Перейти к кукловоду и отказаться от всех тестов транспортира - это, вероятно, стратегическое направление.
  • ИЛИ исправление от # 4290, и я могу раскомментировать те несколько строк, которые необходимы для начала использования ngUpgrade :)

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

Где мы с этим вопросом? Я боюсь, что мне придется перейти на другую среду тестирования, чтобы мы могли сохранить наши планы по обновлению Angular.

@tonybranfort : В той же лодке, что и вы ... Мы в значительной степени полагаемся на ui-select и angular-ui-bootstrap - это главный препятствие для нашего обновления. Похоже, что вы взяли на себя инициативу в этом вопросе, пожалуйста, держите нас в курсе! 😁

У нас та же проблема. Кто-нибудь из транспортира может сообщить нам об этом, пожалуйста?

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

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

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

Долгое редактирование: по-видимому, это зависит от того, когда вызывается интервал (в зоне или нет)

Привет, @vikerman! Есть новости об изменениях, которые позволят вам настроить, какие задачи ожидает Protractor?

Здесь та же проблема.

@vikerman есть ли какие-нибудь обновления или обходные пути?

Мы сталкиваемся с теми же проблемами. Было бы здорово, если бы в этом вопросе был какой-то прогресс.

К сожалению, у меня такая же проблема. Есть ли прогресс в этом отношении?

Мы добавляем отслеживание задач в Angular и AngularJS, что должно упростить отладку этих проблем, показывая вам, откуда взялась задача async, для которой истекло время ожидания Protractor.

@heathkit Это где-то уже сделано? нам трудно определить эти места в нашей кодовой базе.

Также было бы неплохо иметь некоторую документацию, которую так ждет транспортир. Этот кажется устаревшим: https://www.protractortest.org/#/system -setup. здесь упоминается только замена $timeout на $interval . Ничего о setTimeout или setInterval.
некоторые документы находятся здесь: http://www.protractortest.org/#/timeouts. но никаких подсказок, как их обнаружить ...

Думаю, это поможет вам запустить ваши тесты «угловой гибрид + транспортир». Мне это помогло. Проверьте ссылку: https://github.com/ui-router/angular-hybrid/issues/39#issuecomment -309057814

Мы немного продвинулись в понимании того, что происходит с этой проблемой. Кажется, у нас один и тот же. Похоже, что обновленный цикл наблюдателя / дайджеста angularjs работает немного по-другому и может включать некоторые бесконечные циклы цикла дайджеста. Мы нашли 1 у третьей стороны, которую использовали, и исследуем следующую. Но в основном наблюдатель вызывал изменение наблюдаемого объекта, вызывая срабатывание наблюдателя и т. Д. Чтобы проверить, есть ли у вас такая же проблема, в Chrome просто откройте инструменты разработчика, перейдите к производительности, запишите несколько секунд и посмотрите, много ли у вас вызовов NgZone. Если вы это сделаете, то где-то в вашем коде у вас будет бесконечный цикл дайджеста. Детализация звонков может помочь указать вам, что их вызывает.

Вы также можете немного изменить локальный файл zone.js и добавить журналы консоли, в которых исправляются задачи тайм-аута / интервала, чтобы увидеть, что их вызывает. Простой способ сделать это:

try {
  throw new Error("track");
} catch(err) {
  console.debug(err);
}

Изменить: так весело форматировать код на мобильном телефоне!

@vikerman У вас есть поводу ? Было бы здорово, если бы вы могли высказать свое мнение о том, как скоро это будет исправлено? Спасибо.

Вы также можете немного изменить локальный файл zone.js и добавить журналы консоли, в которых исправляются задачи тайм-аута / интервала, чтобы увидеть, что их вызывает. Простой способ сделать это:

try {
  throw new Error("track");
} catch(err) {
  console.debug(err);
}

Изменить: так весело форматировать код на мобильном телефоне!

Где бы вы поместили этот код для отслеживания таймаутов / интервалов?

@maurycyg patchTimer -> функция

Обидно видеть эту ветку. Есть ли какие-нибудь обновления по этому поводу?

Потенциальный обходной путь, чтобы не использовать $timeout - переопределить его с помощью $interval (для сторонних библиотек)
Код ниже решил для меня проблему с тайм-аутом (на всякий случай добавлена ​​проверка браузера). В моем случае проблема была вызвана компонентами angular-bootstrap, которые бесконечно вызывали $ timeout с нулевой задержкой.

app.config(function($provide) {
    function isUnderProtractor() {
        var nav = window.navigator;
        var lowercasedUserAgent = nav.userAgent;
        return !lowercasedUserAgent || lowercasedUserAgent.contains('protractor');
    }

    if (isUnderE2ETest()) {
        $provide.decorator('$timeout', ['$interval', '$delegate', function($interval, $delegate) {
            var newTimeout = function(fn, delay, invokeApply, pass) {
                if (!delay)
                    return $interval(fn, delay, 1, invokeApply, pass);
                else
                    return $delegate(fn, delay, invokeApply, pass);
            };
            newTimeout.cancel = $delegate.cancel;
            return newTimeout;
        }]);
    }
}

Начиная с zone.js v0.8.9, вы можете выбрать, какие модули веб-API вы хотите исправить, чтобы уменьшить накладные расходы, связанные с исправлением этих модулей.
https://github.com/angular/zone.js/blob/master/MODULE.md

Добавьте файл zone-flags.ts с содержимым ниже
(window as any).__Zone_disable_timers = true;

Импортируйте его в polyfills.ts перед zone.js

import './zone-flags';
import 'zone.js/dist/zone';  // Included with Angular CLI.

Оказалось, что вышеупомянутое решение с отключением таймеров зоны портит Obserables (например, asyncValidators). Решение, которое, наконец, сработало для меня, - это setTimeout с исправлением обезьяны.

platformBrowserDynamic().bootstrapModule(AppModule)
.then((moduleRef: NgModuleRef<AppModule>) => {
  const ngZone: NgZone = moduleRef.injector.get(NgZone);
  const originalSetTimeout = window.setTimeout;
  (window as any).setTimeout = function setTimeout(fn, ms) {
    return ngZone.runOutsideAngular(() => {
      return originalSetTimeout(() => {
        ngZone.run(fn);
      }, ms);
    });
  };
})

Возможно, проблема не в самом транспортире, а в коде приложения.

С переходом от Angular JS, который использует цикл дайджеста для отслеживания изменений, к гибридному приложению с Angular2 +, которое использует zone.js для обнаружения изменений, Protractor начал падать с таймаутами.

Транспортир запускает следующее тестовое действие только тогда, когда приложение работает стабильно. Стабильность в случае гибридного приложения означает стабильность части приложения Angular JS и части приложения Angular2 +. Обе части должны быть устойчивыми.

Стабильность Angular JS определяется через $$testability.whenStable . Стабильность Angular 2+ определяется через window.getAngularTestability(...).whenStable . Вы можете отладить код транспортира и убедиться в этом сами. Для этого добавьте оператор debugger; в файл waitForAngular _ \ node_modules \ protractor \ built \ clientsidescripts.js_;
Вы заметите, что стабильность зоны Angular2 + никогда не возникает. Для стабильности все микрозадачи и макрозадания должны быть выполнены.

Теперь мы разбираемся в проблеме. Часть приложения Angular 2+ всегда нестабильна, поэтому Protractor бесконечно ждет, пока она не станет стабильной, и падает с таймаутами. Стабильность Angular JS в случае гибридного приложения не должна измениться и не влияет на таймауты Protractor. Из этого можно сделать вывод, что необходимо исправить нестабильность зоны Angular2 + и тогда транспортир начнет работать как положено.

Первая попытка исправить:
Поскольку времени было мало и я хотел исправить все сразу, я написал код, который выводил планирование задач из зоны Angular2 +.

    $provide.decorator("$animateCss", ["$delegate", createDecorator]);
    $provide.decorator("$http", ["$delegate", createDecorator]);
    $provide.decorator("$timeout", ["$delegate", createDecorator]);
    $provide.decorator("$interval", ["$delegate", createDecorator]);
    $provide.decorator("$q", ["$delegate", createDecorator]);
    $provide.decorator("$xhrFactory", ["$delegate", createDecorator]);
    $provide.decorator("$templateRequest", ["$delegate", createDecorator]);
    $provide.decorator("$httpBackend", ["$delegate", createDecorator]);

    $provide.decorator("$animate", ["$delegate", createDecorator]);
    $provide.decorator("$rootScope", ["$delegate", createDecorator]);
    $provide.decorator("$templateCache", ["$delegate", createDecorator]);
    $provide.decorator("$browser", ["$delegate", createDecorator]);

    function runInRootZone(fn, ...args) {
        if (window.Zone.current.parent) {
            return window.Zone.current.parent.run(() => runInRootZone(fn, ...args));
        }

        return fn(...args);
    }

    function decorate(decorator, $delegate) {

        const keys = Object.keys($delegate);
        for (let i = 0; i < keys.length; i++) {

            const key = keys[i];
            if (angular.isFunction($delegate[key])) {
                decorator[key] = createDecoratorForFunctionLikeService($delegate[key]);
            } else {
                if (decorator[key] !== $delegate[key]) {
                    decorator[key] = $delegate[key];
                }
            }
        }
    }

    function createDecoratorForFunctionLikeService($delegate) {

        function decorator(...args) {
            return runInRootZone($delegate, ...args);
        }

        decorate(decorator, $delegate);

        return decorator;
    }

    function createDecoratorForService($delegate) {

        decorate($delegate, $delegate);

        return $delegate;
    }

    function createDecorator($delegate) {
        if (angular.isFunction($delegate)) {
            return createDecoratorForFunctionLikeService($delegate);
        }
        return createDecoratorForService($delegate);
    }

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

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

Теперь нам нужно понять, какой код делает зону нестабильной. Для этого в zone.js есть специальные утилиты, такие как _TaskTrackingZoneSpec_ (_ \ node_modules \ zone.js \ dist \ task-tracking.js_), позволяющие отслеживать, какие задачи находятся в зоне и где они были созданы ( creationLocation ). Кроме того, в начале части приложения Angular2 + сохраните зону Angular (window as any).a9Zone = (upgradeModule as any).ngZone._inner; и добавьте где-нибудь этот код.

$rootScope.$watch(function () {
    if (window.a9Zone) {
        console.log("Angular2+ task count:", window.a9Zone._zoneDelegate._taskCounts);

        // Optional.
        window.getAllAngularTestabilities().forEach(_ => {
            _.taskTrackingZone.macroTasks.forEach(t => {
                console.log(t, t.creationLocation);
            });
        });
    }
});

Таким образом, найдите в приложении места, где микрозадачи и макрозадания находятся в зоне долгое время (_microTasks.lenght! == 0 && macroTasks.lenght! == 0_).

Затем вам нужно вывести этот код из зоны Angular. Для этого оберните код в ngZone.runOutsideAngular . Однако делайте это очень осторожно и убедитесь, что корневая зона не появилась там, где она не нужна.

После того, как угловая зона станет стабильной, транспортир больше не будет падать с таймаутом .

Надеюсь это поможет.

Я придумал временный прием, чтобы решить эту проблему, переопределив функцию waitForAngular с помощью приведенной ниже логики.

onPrepare: function() {

            var script = "var callback = arguments[arguments.length - 1];" +
                "angular.element(document.querySelector(\"body\")).injector()"+
                ".get(\"$browser\").notifyWhenNoOutstandingRequests(callback)");

            browser.waitForAngular = function() {
                return browser.executeAsyncScript(script)
            };
}
Была ли эта страница полезной?
0 / 5 - 0 рейтинги