Skip to content
This repository has been archived by the owner on Sep 24, 2019. It is now read-only.

Return code snippet #125

Closed
hannolans opened this issue Jan 20, 2014 · 3 comments
Closed

Return code snippet #125

hannolans opened this issue Jan 20, 2014 · 3 comments
Assignees

Comments

@hannolans
Copy link
Contributor

When a test failed, we would like to have the code snippet of the place that failed. At the moment we do the following to get the first 255 characters of the code:

testFailed : function(event) {
                              outerHTML=jQuery('<textarea />').append(event.element);
                              outerHTML.val(outerHTML.html());
                              var elem=JSON.stringify(outerHTML.html().substr(0,255));

Would be a nice addition if QUAIL can return the first snippet of code.

@jessebeach
Copy link
Member

This is a method that attempts to create a unique selector for an HTML element. It's part of the Visitor Actions Drupal module (still to be posted as a project).

/**
   * Creates a page-unique selector for the selected DOM element.
   *
   * @param String id
   *   A selector string e.g. '#some-element'.
   * @param String type
   *   The type of actionable element e.g. 'link' or 'form'.
   *
   * @return String
   *   A unique selector for this element.
   */
var defineUniqueSelector = function (id, type) {
    var selector = '';

    /**
     * Indicates whether the selector string represents a unique DOM element.
     *
     * @param String selector
     *   A string selector that can be used to query a DOM element.
     *
     * @return Boolean
     *   Whether or not the selector string represents a unique DOM element.
     */
    function isUniquePath (selector) {
      return $(selector).length === 1;
    }

    /**
     * Creates a selector from the element's id attribute.
     *
     * Temporary IDs created by the module that contain "visitorActions" are excluded.
     *
     * @param DOM element
     *
     * @return String
     *   An id selector or an empty string.
     */
    function applyID (element) {
      var selector = '';
      var id = element.id;
      if (id.length > 0 && !/visitorActions/.test(id)) {
        selector = '#' + id;
      }
      return selector;
    }

    /**
     * Creates a selector from classes on the element.
     *
     * Classes with known functional components like the word 'active' are
     * excluded because these often denote state, not identity.
     *
     * @param DOM element
     *
     * @return String
     *   A selector of classes or an empty string.
     */
    function applyClasses (element) {
      var selector = '';
      // Try to make a selector from the element's classes.
      var classes = element.className || '';
      if (classes.length > 0) {
        classes = classes.split(/\s+/);
        // Filter out classes that might represent state.
        classes = _.reject(classes, function (cl) {
          return /active|enabled|disabled|first|last|only|collapsed|open|clearfix|processed/.test(cl);
        });
        if (classes.length > 0) {
          return '.' + classes.join('.');
        }
      }
      return selector;
    }

    /**
     * Finds attributes on the element and creates a selector from them.
     *
     * @param DOM element
     *
     * @return String
     *   A selector of attributes or an empty string.
     */
    function applyAttributes (element) {
      var selector = '';
      var attributes = ['href', 'type'];
      var value;
      // Try to make a selector from the element's classes.
      for (var i = 0, len = attributes.length; i < len; i++) {
        value = element.attributes[attributes[i]] && element.attributes[attributes[i]].value;
        if (value) {
          // If the attr is href and it points to a specific user account,
          // just tack on the attr name and not the value.
          if (attributes[i] === 'href' && /user\/\d+/.test(value)) {
            selector += '[' + attributes[i] + ']'
          }
          else {
            selector += '[' + attributes[i] + '="' + value + '"]';
          }
        }
      }
      return selector;
    }

    /**
     * Creates a unique selector using id, classes and attributes.
     *
     * It is possible that the selector will not be unique if there is no
     * unique description using only ids, classes and attributes of an
     * element that exist on the page already. If uniqueness cannot be
     * determined and is required, you will need to add a unique identifier
     * to the element through theming development.
     *
     * @param DOM element
     *
     * @return String
     *   A unique selector for the element.
     */
    function generateSelector (element) {
      var selector = '';
      var scopeSelector = '';
      var pseudoUnique = false;
      var firstPass = true;

      do {
        scopeSelector = '';
        // Try to apply an ID.
        if ((scopeSelector = applyID(element)).length > 0) {
          selector = scopeSelector + ' ' + selector;
          // Assume that a selector with an ID in the string is unique.
          break;
        }

        // Try to apply classes.
        if (!pseudoUnique && (scopeSelector = applyClasses(element)).length > 0) {
          // If the classes don't create a unique path, tack them on and
          // continue.
          selector = scopeSelector + ' ' + selector;
          // If the classes do create a unique path, mark this selector as
          // pseudo unique. We will keep attempting to find an ID to really
          // guarantee uniqueness.
          if (isUniquePath(selector)) {
            pseudoUnique = true;
          }
        }

        // Process the original element.
        if (firstPass) {
          // Try to add attributes.
          if ((scopeSelector = applyAttributes(element)).length > 0) {
            // Do not include a space because the attributes qualify the
            // element. Append classes if they exist.
            selector = scopeSelector + selector;
          }

          // Add the element nodeName.
          selector = element.nodeName.toLowerCase() + selector;

          // The original element has been processed.
          firstPass = false;
        }

        // Try the parent element to apply some scope.
        element = element.parentNode;
      } while (element && element.nodeType === 1 && element.nodeName !== 'BODY' && element.nodeName !== 'HTML');

      return selector.trim();
    }

    // The method for determining a unique selector depends on the type of
    // element.
    switch (type) {
      case 'form':
        selector = id;
        break;
      case 'page':
        selector = id;
        break;
      case 'link':
      default:
        selector = generateSelector($(id)[0]);
        break;
    }
    return selector;
  }
});

@hannolans
Copy link
Contributor Author

Sounds perfect! We had a meeting today and would love this functionality as this can aggregate reporting with a collection of pages and can therefore provide better statistics. Is it difficult to integrate this?

@jessebeach
Copy link
Member

I'll have a look this weekend at integrating it.

@ghost ghost assigned jessebeach Jan 23, 2014
jessebeach added a commit to jessebeach/quail that referenced this issue Jan 27, 2014
Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>
kevee pushed a commit that referenced this issue Jan 28, 2014
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants