Wait on two conditions until one succeed

Viewed 281

I'm using TestCafe and i can't implement this behavior:

  1. Log-in to my website using Role mechanism
  2. Check for conditions:
    • Url change OR
    • An error message appears

I know how to get current url using ClientFunction and check for element visibility with Selector but i can't figure out how to mix the two.

Is it possible ?


Base on NoriSte's answer (thanks!), a working sample adapted from my use case. I did not find it neither elegant nor extendable (what to do if you want ORing more conditions).

AuthPage.js

import { Selector, t, Role, ClientFunction } from "testcafe"

const url = 'http://localhost:8081'
const loginUrl = `${url}/login`;
const logoutUrl = `${url}/logout`;

const loginInput = Selector('[data-qa="login input"]');
const passwordInput = Selector('[data-qa="password input"]');
const submitButton = Selector('[data-qa="submit button"]')
const loginError = Selector('[data-qa="login error"]');

const getLocation = ClientFunction(() => document.location.href.toString());

const login = (username, password) => async tt =>
  await tt
    .typeText(loginInput, username)
    .pressKey('tab')
    .typeText(passwordInput, password)
    .click(submitButton);

export default class AuthPage {
  constructor () {
    this.roles = {
      // 'almighty god': Role(loginUrl, login('filou', 'filoucfou')),
      // 'bad mutafuka': Role(loginUrl, login('badbadbad', 'password'))
      // Can't use Role, don't know why ?!
      'almighty god': login('gooood', 'password'),
      'bad mutafuka': login('badbadbad', 'password')
    }
  }

  async loginWith (roleName) {
    const oldUrl = await getLocation();

    // await t
    //   .useRole(this.roles[roleName]);
    // Does weird thing, not what i would expect
    await this.roles[roleName](t)
    if (await loginError.with({ timeout: 5000, visibilityCheck: true }).exists) {
      console.log('login error:', oldUrl)
      return {url: oldUrl, login: false};
    }

    await t
      .wait(10000);

    const newUrl = await getLocation()
    return {url: newUrl, login: oldUrl !== newUrl};
  }
}

test.js

import LoginPage from 'AuthPage'

fixture `sinoc-fixture`
  .page `http://localhost:8081`;

const loginPage = new LoginPage()

test('Login with wrong credentials should fail', async (t) => {
  const result = await loginPage.loginWith('bad mutafuka')
  await t
    .expect(result.login === false).ok()
});

test('Login with good credentials should succeed', async (t) => {
  const result = await loginPage.loginWith('almighty god')
  await t
    .expect(result.login === true)
    .ok()
    .expect(result.url)
    .contains('/dashboard');
});
2 Answers

Do you have a public page where I can test it? Anyway I drafted this code, I'm not sure that it suits your needs neither it works...

import { ClientFunction } from 'testcafe';
const getLocation = ClientFunction(() => document.location.href);

test('redirect OR error', async t => {
    let errorMesageDidAppear = false;
    let didRedirect = false;

    const oldUrl = getLocation();

    await t.useRole(yourUser)

    // If a redirect happens TestCafe automatically waits for it
    // @see http://devexpress.github.io/testcafe/documentation/test-api/built-in-waiting-mechanisms.html#waiting-for-redirects
    // if it doesn't happen TestCafe goes ahead with the test

    const newUrl = getLocation();

    didRedirect = newUrl !== oldUrl;
    if(!didRedirect) {
        await Selector('#ERROR_BOX_ID').with({ visibilityCheck: true })();
        errorMesageDidAppear = true;
        // if it doesn't appear the test will fail due to the timeout limit
    }

    // now you have two flags: didRedirect and errorMesageDidAppear
});

I resembled the code seen here Waiting for Elements When Evaluating Selectors and these instructions

I ended up with a ClientFunction, polling the 2 conditions:

const getLocationAndError = ClientFunction(({url}) => {
  return new Promise(resolve => {
    (function polling() {
      const errorModal = document.querySelector('[data-qa="login error"]');
      const newUrl = document.location.href.toString();
      const navigated = newUrl !== url;
      if (errorModal || navigated) {
        resolve({navigated, loginError: !!errorModal, url: newUrl});
      } else {
        setTimeout(polling, 200);
      }
    })();
  });
});
Related