Buscar..


Observaciones

Nightwatch ha estado proporcionando pruebas de aceptación y de extremo a extremo para las aplicaciones Meteor desde v0.5 días, y ha gestionado migraciones de PHP a Spark to Blaze y a React; y todas las principales plataformas de integración continua. Para ayuda adicional, por favor vea:

Documentación de la API de Nightwatch
Nightwatch.js Grupo de Google

Área de superficie de la aplicación

En su nivel más básico, la prueba de aceptación es esencialmente una prueba de caja negra, que se ocupa fundamentalmente de probar las entradas y salidas de un sistema cerrado. Como tal, hay tres características esenciales para las pruebas de aceptación: ubicar un recurso, leer datos y escribir datos. Cuando se trata de navegadores y aplicaciones web, estas tres características básicamente se reducen a lo siguiente:

  1. Cargar una página web o una vista de aplicación.
  2. Inspeccionar elementos de la interfaz de usuario (es decir, DOM)
  3. Activar un evento / simular una interacción de usuario

A esto le llamamos el área de superficie de la aplicación. El área de superficie es cualquier cosa que un usuario ve o experimenta. Es el exterior de un sistema de caja negra. Y dado que los usuarios interactúan con las aplicaciones web modernas en pantallas de video utilizando navegadores web, nuestra cobertura de superficie se define mediante localizadores de recursos universales (URL) y ventanas gráficas. Y así, nuestro primer recorrido comienza con algo como lo siguiente:

module.exports = {
  "Hello World" : function (client) {
    client
      // the location of our Meteor app
      .url("http://localhost:3000")

      // the size of the viewport 
      .resizeWindow(1024, 768)

      // test app output
      .verify.elementPresent('h1')
      .verify.containsText('h1', "Welcome to Meteor!")
      .verify.containsText('p', "You've pressed the button 0 times")
      .verify.elementPresent('button')

      // simulate user input
      .click('button').pause(500)

      // test app output again, to make sure input worked
      .verify.containsText('p', "button 1 times")

      // saving a copy of our viewport pixel grid
      .saveScreenshot('tests/nightwatch/screenshots/homepage.png')
      .end();
  }
};

Comandos personalizados

Nightwatch admite la creación de comandos personalizados que pueden simular pulsaciones de teclas, clics del mouse y otras entradas. Un comando personalizado se puede encadenar con otros comandos de Nightwatch, así:

module.exports = {
  "Login App" : function (client) {
    client
      .url("http://localhost:3000")
      .login("[email protected]", "janedoe123")
      .end();
  }
};

Para habilitar esto, defina un comando en ./tests/nightwatch/commands/login así:

exports.command = function(username, password) {

  this
    .verify.elementPresent('#login')

      // we clear the input in case there's any data remaining from previous visits
      .clearValue("#emailInput")
      .clearValue("#passwordInput")

      // we simulate key presses
      .setValue("#emailInput", username)
      .setValue("#passwordInput", password)

    // and we simulate a mouse click
    .click("#signInToAppButton").pause(1000)

  return this; // allows the command to be chained.
};

Para que todo esto funcione, deberá agregar atributos de id a su página de inicio de sesión. En algún nivel, tendrá que tener un aspecto similar al siguiente:

<template name="login">
  <div id="login">
    <input id="emailInput" name="email" type="email" />
    <input id="passwordInput" name="password" type="password" />
    <button id="#signInToAppButton">Sign In</button>
  </div>
</template>

Inspeccionar objetos de meteoros en el cliente

Dado que Nightwatch tiene acceso a la consola del navegador, es posible inspeccionar los objetos del lado del cliente utilizando la API .execute() . En el siguiente ejemplo, estamos revisando el objeto Session para una variable de sesión particular. Primero, comenzamos creando el archivo ./tests/nightwatch/api/meteor/checkSession , donde mantendremos el siguiente comando:

// syncrhonous version; only works for checking javascript objects on client
exports.command = function(sessionVarName, expectedValue) {
  var client = this;
  this
    .execute(function(data){
      return Session.get(data);
    }, [sessionVarName], function(result){
      client.assert.ok(result.value);
      if(expectedValue){
        client.assert.equal(result.value, expectedValue);
      }
    })
    return this;
};

Entonces podemos encadenarlo así:

module.exports = {
  "Check Client Session" : function (client) {
    client
      .url("http://localhost:3000")
      .checkSession("currentUser", "Jane Doe")
      .end();
  }
};

Formularios y tipos de entrada

Para cargar un archivo, primero deberá crear un directorio / data y agregar el archivo que desea cargar.

tests/nightwatch/data/IM-0001-1001.dcm

Su formulario necesitará una entrada con el tipo de archivo. (A algunas personas no les gustan las opciones de estilo que proporciona esta entrada; un patrón común es ocultar esta entrada y hacer que otro botón de la página haga clic en nombre del usuario).

<form id="myform">
    <input type="file" id="fileUpload">
    <input type="text" name="first_name">
    <input type="text" name="last_name">

    <input type="date" name="dob_month">
    <input type="date" name="dob_day">
    <input type="date" name="dob_year">

    <input type="radio" name="gender" value="M">
    <input type="radio" name="gender" value="F">
    <input type="radio" name="gender" value="O">

    <input type="select" name="hs_graduation_year">
    <input type="text" name="city">
    <input type="select" name="state">

    <input type="submit" name="submit" value="Submit">
</form>

Luego, sus pruebas deberán usar setValue () y resolver la ruta al archivo local activo.

module.exports = {
  "Upload Study" : function (client) {
    console.log(require('path').resolve(__dirname +  '/../data' ));

    var stringArray = "Chicago";

    client
      .url(client.globals.url)
      .verify.elementPresent("form#myform")

      // input[type="file"]
      .verify.elementPresent("input#fileUpload")
      .setValue('input#fileUpload', require('path').resolve(__dirname + '/../data/IM-0001-1001.dcm'))

      // input[type="text"]
      .setValue('input[name="first_name"]', 'First')
      .setValue('input[name="last_name"]', 'Last')

      // input[type="date"]
      .click('select[name="dob_month"] option[value="3"]')
      .click('select[name="dob_day"] option[value="18"]')
      .click('select[name="dob_year"] option[value="1987"]')

      // input[type="radio"]
      .click('input[name="gender"][value="M"]')

      // input[type="number"]
      .click('select[name="hs_graduation_year"] option[value="2002"]')

      // input[type="text"]
      // sometimes Nightwatch will send text faster than the browser can handle
      // which will cause skipping of letters.  In such cases, we need to slow
      // Nightwatch down; which we do by splitting our input into an array
      // and adding short 50ms pauses between each letter
      for(var i=0; i < userIdArray.length; i++) {
        client.setValue('input[name="city"]', stringArray[i]).pause(50)
      }

      // input[type="select"]
      // after an array input above, we need to resume our method chain...
      client.click('select[name="state"] option[value="CA"]')

      // input[type="number"]
      .setValue('input[name="zip"]', '01234')

      //input [ type="submit" ]
      .click('button[type="submit"]')
      .end();
  }
};

Gracias a Daniel Rinehart por invertir este ejemplo.

Componentes y objetos de página

Los objetos de página son similares a los comandos personalizados; excepto que son colecciones de comandos personalizados que están asociados con un componente de IU específico. Esto funciona extremadamente bien con el diseño moderno basado en componentes, como en React.

module.exports = {
  url: 'http://localhost:3000/login',
  commands: [{
  login: function(email, password) {
    return this
      .clearValue('input[name="emailAddress"]')
      .clearValue('input[name="password"]')

      .setValue('input[name="emailAddress"]', email)
      .setValue('input[name="password"]', password)

      .verify.elementPresent('#loginButton')
      .click("#loginButton");
  },
  clear: function() {
    return this
      .waitForElementVisible('@emailInput')
      .clearValue('@emailInput')
      .clearValue('@passInput')
      .waitForElementVisible('@loginButton')
      .click('@loginButton')
  },
  checkElementsRendered: function(){
    return this
      .verify.elementPresent("#loginPage")
      .verify.elementPresent('input[name="emailAddress"]')
      .verify.elementPresent('input[name="password"]')
  },
  pause: function(time, client) {
    client.pause(time);
    return this;
  },
  saveScreenshot: function(path, client){
    client.saveScreenshot(path);
    return this;
  }
}],
  elements: {
    emailInput: {
      selector: 'input[name=email]'
    },
    passInput: {
      selector: 'input[name=password]'
    },
    loginButton: {
      selector: 'button[type=submit]'
    }
  }
};

La única advertencia con el uso del patrón PageObject en los componentes de prueba, es que la implementación interrumpe el flujo de encadenamiento del método que proporciona el Nightwatch verify.elementPresent nativo. En su lugar, deberá asignar el objeto de la página a una variable y crear una instancia de una nueva cadena de métodos para cada página. Un precio razonable a pagar por un patrón consistente y confiable para probar la reutilización del código.

module.exports = {
  tags: ['accounts', 'passwords', 'users', 'entry'],
  'User can sign up.': function (client) {

    const signupPage = client.page.signupPage();
    const indexPage = client.page.indexPage();

    client.page.signupPage()
      .navigate()
      .checkElementsRendered()
      .signup('Alice', 'Doe', '[email protected]', 'alicedoe')
      .pause(1500, client);

    indexPage.expect.element('#indexPage').to.be.present;
    indexPage.expect.element('#authenticatedUsername').text.to.contain('Alice Doe');
  },
}


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow