0
votes

Rapporteur angulaire - Vérification de la redirection de la page

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

  • Attendez Angular

browser.waitForAngular();

Référence a>

  • Vérifiez l'URL actuelle du navigateur et comparez-la avec l'URL attendue:
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));
    });
});

Référence


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:

  • Angulaire : 8.2.14
  • CLI angulaire : 8.2.2
  • Rapporteur : 5.4.2
  • Jasmin : 2.8.0


0 commentaires

3 Réponses :


0
votes

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 >


3 commentaires

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 :)



0
votes

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.


0 commentaires

0
votes

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


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


0 commentaires