J'écris quelques tests E2E dans une application Angular. Un problème que j'ai est que chaque fois que je redirige vers une nouvelle route, j'aimerais vérifier que le bon composant est affiché. Pour une raison quelconque, cela entraîne toujours un échec avec l'erreur suivante:
- Échec: délai d'expiration du script
J'ai cherché partout sur le Web pour essayer de trouver un solution, mais toutes les solutions que j'ai trouvées n'ont pas fonctionné pour moi.
Exemple
browser.waitForAngular();
describe('Login Functionality', () => { let loginPage: LoginPage; beforeEach(() => { loginPage = new LoginPage(); }); // Test runs successfully it('should display login', () => { loginPage.navigateTo(); expect(loginPage.getLoginComponent().isPresent()).toBeTruthy(); }); // Test runs successfully it('should have the login button disabled', () => { loginPage.navigateTo(); expect(loginPage.getLoginButton().isEnabled()).toBe(false); }); // Test runs fails it('should redirect to a secure page', () => { loginPage.navigateTo(); loginPage.login(); expect(loginPage.getSecurePage().isPresent()).toBeTruthy(); }); afterEach(async () => { // Assert that there are no errors emitted from the browser const logs = await browser.manage().logs().get(logging.Type.BROWSER); expect(logs).not.toContain(jasmine.objectContaining({ level: logging.Level.SEVERE, } as logging.Entry)); }); });
Exemple de code
Voici un exemple de code qui teste la fonctionnalité de connexion.
Classe d'objet de la page de connexion
import { browser, element, by } from 'protractor'; export class LoginPage { private credentials: { username: string, password: string } = { username: 'admin', password: 'admin' }; navigateTo() { return browser.get(browser.baseUrl) as Promise<any>; } getLoginComponent() { return element(by.css('ra-login')); } getUsernameTextbox() { return element(by.css('ra-login [data-test-id="username"]')); } getPasswordTextbox() { return element(by.css('ra-login [data-test-id="password"]')); } getLoginButton() { return element(by.css('ra-login [data-test-id="login-btn"]')); } getSecurePage() { return element(by.css('ra-secure-content')); } login(credentials: { username: string, password: string } = this.credentials) { this.getUsernameTextbox().sendKeys(credentials.username); this.getPasswordTextbox().sendKeys(credentials.password); this.getLoginButton().click(); } getErrorMessage() { return element(by.css('ra-login [data-test-id="login-error"]')); } }
Fichier de spécification E2E de connexion
browser.driver.wait(() => { browser.driver.getCurrentUrl().then((url) => { return /expected-url/.test(url); }); }, 10000);
En utilisant le code ci-dessus, le test ' devrait rediriger vers une page sécurisée '
échoue. Ce test tente de vérifier qu'après une connexion réussie, le composant «contenu sécurisé» est affiché. Ce composant est un composant wrapper pour héberger les pages qui ne peuvent être affichées que si un utilisateur est connecté.
La redirection de page réussit toujours et la bonne page est affichée. Je soupçonne que le test essaie d'une manière ou d'une autre de récupérer l'élément avant de le rediriger?
Je suis nouveau sur E2E, donc je ne suis pas tout à fait sûr que ce soit le problème.
Juste au cas où voici le numéro de version des paquets importants:
3 Réponses :
Je vous suggère d'utiliser la structure d'instruction async / await. Vos soupçons semblent se corriger et le problème auquel vous êtes confronté est dû à une mauvaise gestion des promesses.
Vous pouvez essayer d'écrire les spécifications comme suit:
async navigateTo() { return await browser.get(browser.baseUrl); }
Et aussi changer les fonctions de navigation et de connexion comme:
it('should redirect to a secure page', async() => { await loginPage.navigateTo(); await loginPage.login(); expect(await(await loginPage.getSecurePage()).isPresent()).toBeTruthy(); });
Vous peut ajouter await chaque fois qu'il y a une interaction avec le navigateur et rendre la fonction asynchrone s'il y a une instruction await.
refer: [1]: https://hackernoon.com/should-i-use-promises-or-async-await-126ab5c98789 p >
Merci pour votre réponse, malheureusement cela n'a toujours pas fonctionné. Je vais continuer à enquêter ... pour être honnête, je ne sais toujours pas si async / await est nécessaire, car dans certains articles que j'ai lus, il est mentionné que Protractor doit automatiquement attendre chaque promesse entre les étapes. Je me trompe peut-être bien sûr.
Quand j'ai commencé à travailler avec rapporteur, je résolvais des promesses en utilisant then (). Lisez quelques articles par la suite et comprenez comment fonctionne await / async. Cela a rendu l'exécution un peu facile et fluide, supprimant la dépendance à la résolution manuelle des promesses. C'est un choix cependant, de l'utiliser ou non. Merci.
Oui d'accord, je préfère le modèle async / await car il rend le code beaucoup plus lisible. Je trouve que l'utilisation du then ()
avec plusieurs rappels imbriqués est parfois difficile à lire. Bien sûr, c'est une préférence personnelle, les deux approches aboutissent au même résultat :)
vous devez utiliser la promesse avec l'opérateur alors
cette solution a fonctionné pour moi
si vous connaissez le temps de réponse, vous pouvez utiliser
browser.driver.sleep(1000)
mais c'est une mauvaise pratique
NB: si l'application est très lente, il est temps de signaler un problème de performance.
Dans mon cas, mis à part l'attente de certains objets Promise, j'avais aussi quelque chose d'autre à l'origine de l'erreur. Pour faire court, j'ai un intervalle qui est chargé d'afficher l'heure actuelle à l'utilisateur. Puisqu'un intervalle est une fonction asynchrone, Protractor supposera qu'Angular exécute toujours une fonction, et continue donc d'attendre qu'Angular soit "réactif".
Dans Angular 2+, pour répondre à un tel scénario, le setInterval ()
doit être exécutée en dehors de la zone angulaire.
Exemple
it('should redirect to a secure page', async () => { loginPage.navigateTo(); loginPage.login(); const secureContent = loginPage.getSecureContentComponent(); waitForElement(secureContent); expect(secureContent.isPresent()).toBe(true); });
Ce qui précède code entraînera le délai d'expiration de Protractor dès que le composant avec l'intervalle existe sur la page.
Pour résoudre ce problème, nous pouvons utiliser NgZone
.
Exemple
import { ElementFinder, ExpectedConditions, browser } from 'protractor'; const until = ExpectedConditions; export const waitForElement = (elementToFind: ElementFinder, timeout = 10000) => { return browser.wait(until.presenceOf(elementToFind), timeout, 'Element not found'); };
Références
zone.js
et son rôle dans Angular) Dans mes tests, j'ai également ajouté une fonction utilitaire qui aide à attendre qu'un élément soit affiché à l'écran.
import { NgZone } from '@angular/core'; constructor(private ngZone: NgZone) { } ngOnInit() { this.now = new Date(); // running setInterval() function outside angular // this sets the asynchronous call to not run within the angular zone this.ngZone.runOutsideAngular(() => { setInterval(() => { // update the date within angular, so that change detection is triggered this.ngZone.runTask(() => { this.now = new Date(); }); }, 1000); }); }
Lequel est ensuite utilisé dans les tests comme ceci:
ngOnInit() { this.now = new Date(); setInterval(() => { this.now = new Date(); }, 1000); }
Références