hunk.code_window:  * Methods for traversing the DOM structure.
    *
    * @module cheerio/traversing
    */
   var select = require('cheerio-select');
   var utils = require('../utils');
   var domEach = utils.domEach;
   var uniqueSort = require('htmlparser2').DomUtils.uniqueSort;
   var isTag = utils.isTag;
  
hunk.labels: keep keep keep keep keep replace keep keep keep keep keep
hunk.after_edit: var select = require('cheerio-select-tmp');
  
hunk.file_path: lib/api/traversing.js
hunk.type: replace
file: var expect = require('expect.js');
  var htmlparser2 = require('htmlparser2');
  var cheerio = require('../');
  var fixtures = require('./fixtures');
  var fruits = fixtures.fruits;
  var food = fixtures.food;
  
  // HTML
  var script = '<script src="script.js" type="text/javascript"></script>';
  var multiclass = '<p><a class="btn primary" href="#">Save</a></p>';
  
  describe('cheerio', function () {
    it('should get the version', function () {
      expect(/\d+\.\d+\.\d+/.test(cheerio.version)).to.be.ok();
    });
  
    it('cheerio(null) should return be empty', function () {
      expect(cheerio(null)).to.be.empty();
    });
  
    it('cheerio(undefined) should be empty', function () {
      expect(cheerio(undefined)).to.be.empty();
    });
  
    it('cheerio(null) should be empty', function () {
      expect(cheerio('')).to.be.empty();
    });
  
    it('cheerio(selector) with no context or root should be empty', function () {
      expect(cheerio('.h2')).to.be.empty();
      expect(cheerio('#fruits')).to.be.empty();
    });
  
    it('cheerio(node) : should override previously-loaded nodes', function () {
      var $ = cheerio.load('<div><span></span></div>');
      var spanNode = $('span')[0];
      var $span = $(spanNode);
      expect($span[0]).to.equal(spanNode);
    });
  
    it('should be able to create html without a root or context', function () {
      var $h2 = cheerio('<h2>');
      expect($h2).to.not.be.empty();
      expect($h2).to.have.length(1);
      expect($h2[0].tagName).to.equal('h2');
    });
  
    it('should be able to create complicated html', function () {
      var $script = cheerio(script);
      expect($script).to.not.be.empty();
      expect($script).to.have.length(1);
      expect($script[0].attribs.src).to.equal('script.js');
      expect($script[0].attribs.type).to.equal('text/javascript');
      expect($script[0].childNodes).to.be.empty();
    });
  
    var testAppleSelect = function ($apple) {
      expect($apple).to.have.length(1);
      $apple = $apple[0];
      expect($apple.parentNode.tagName).to.equal('ul');
      expect($apple.prev).to.be(null);
      expect($apple.next.attribs['class']).to.equal('orange');
      expect($apple.childNodes).to.have.length(1);
      expect($apple.childNodes[0].data).to.equal('Apple');
    };
  
    it('should be able to select .apple with only a context', function () {
      var $apple = cheerio('.apple', fruits);
      testAppleSelect($apple);
    });
  
    it('should be able to select .apple with a node as context', function () {
      var $apple = cheerio('.apple', cheerio(fruits)[0]);
      testAppleSelect($apple);
    });
  
    it('should be able to select .apple with only a root', function () {
      var $apple = cheerio('.apple', null, fruits);
      testAppleSelect($apple);
    });
  
    it('should be able to select an id', function () {
      var $fruits = cheerio('#fruits', null, fruits);
      expect($fruits).to.have.length(1);
      expect($fruits[0].attribs.id).to.equal('fruits');
    });
  
    it('should be able to select a tag', function () {
      var $ul = cheerio('ul', fruits);
      expect($ul).to.have.length(1);
      expect($ul[0].tagName).to.equal('ul');
    });
  
    it('should accept a node reference as a context', function () {
      var $elems = cheerio('<div><span></span></div>');
      expect(cheerio('span', $elems[0])).to.have.length(1);
    });
  
    it('should accept an array of node references as a context', function () {
      var $elems = cheerio('<div><span></span></div>');
      expect(cheerio('span', $elems.toArray())).to.have.length(1);
    });
  
    it('should select only elements inside given context (Issue #193)', function () {
      var $ = cheerio.load(food);
      var $fruits = $('#fruits');
      var fruitElements = $('li', $fruits);
  
      expect(fruitElements).to.have.length(3);
    });
  
    it('should be able to select multiple tags', function () {
      var $fruits = cheerio('li', null, fruits);
      expect($fruits).to.have.length(3);
      var classes = ['apple', 'orange', 'pear'];
      $fruits.each(function (idx, $fruit) {
        expect($fruit.attribs['class']).to.equal(classes[idx]);
      });
    });
  
    it('should be able to do: cheerio("#fruits .apple")', function () {
      var $apple = cheerio('#fruits .apple', fruits);
      testAppleSelect($apple);
    });
  
    it('should be able to do: cheerio("li.apple")', function () {
      var $apple = cheerio('li.apple', fruits);
      testAppleSelect($apple);
    });
  
    it('should be able to select by attributes', function () {
      var $apple = cheerio('li[class=apple]', fruits);
      testAppleSelect($apple);
    });
  
    it('should be able to select multiple classes: cheerio(".btn.primary")', function () {
      var $a = cheerio('.btn.primary', multiclass);
      expect($a).to.have.length(1);
      expect($a[0].childNodes[0].data).to.equal('Save');
    });
  
    it('should not create a top-level node', function () {
      var $elem = cheerio('* div', '<div>');
      expect($elem).to.have.length(0);
    });
  
    it('should be able to select multiple elements: cheerio(".apple, #fruits")', function () {
      var $elems = cheerio('.apple, #fruits', fruits);
      expect($elems).to.have.length(2);
  
      var $apple = $elems.toArray().filter(function (elem) {
        return elem.attribs['class'] === 'apple';
      });
      var $fruits = $elems.toArray().filter(function (elem) {
        return elem.attribs.id === 'fruits';
      });
      testAppleSelect($apple);
      expect($fruits[0].attribs.id).to.equal('fruits');
    });
  
    it('should select first element cheerio(:first)', function () {
      var $elem = cheerio('li:first', fruits);
      expect($elem.attr('class')).to.equal('apple');
  
      var $filtered = cheerio('li', fruits).filter(':even');
      expect($filtered).to.have.length(2);
    });
  
    it('should be able to select immediate children: cheerio("#fruits > .pear")', function () {
      var $food = cheerio(food);
      cheerio('.pear', $food).append('<li class="pear">Another Pear!</li>');
      expect(cheerio('#fruits .pear', $food)).to.have.length(2);
      var $elem = cheerio('#fruits > .pear', $food);
      expect($elem).to.have.length(1);
      expect($elem.attr('class')).to.equal('pear');
    });
  
    it('should be able to select immediate children: cheerio(".apple + .pear")', function () {
      var $elem = cheerio('.apple + li', fruits);
      expect($elem).to.have.length(1);
      $elem = cheerio('.apple + .pear', fruits);
      expect($elem).to.have.length(0);
      $elem = cheerio('.apple + .orange', fruits);
      expect($elem).to.have.length(1);
      expect($elem.attr('class')).to.equal('orange');
    });
  
    it('should be able to select immediate children: cheerio(".apple ~ .pear")', function () {
      var $elem = cheerio('.apple ~ li', fruits);
      expect($elem).to.have.length(2);
      $elem = cheerio('.apple ~ .pear', fruits);
      expect($elem.attr('class')).to.equal('pear');
    });
  
    it('should handle wildcards on attributes: cheerio("li[class*=r]")', function () {
      var $elem = cheerio('li[class*=r]', fruits);
      expect($elem).to.have.length(2);
      expect($elem.eq(0).attr('class')).to.equal('orange');
      expect($elem.eq(1).attr('class')).to.equal('pear');
    });
  
    it('should handle beginning of attr selectors: cheerio("li[class^=o]")', function () {
      var $elem = cheerio('li[class^=o]', fruits);
      expect($elem).to.have.length(1);
      expect($elem.eq(0).attr('class')).to.equal('orange');
    });
  
    it('should handle beginning of attr selectors: cheerio("li[class$=e]")', function () {
      var $elem = cheerio('li[class$=e]', fruits);
      expect($elem).to.have.length(2);
      expect($elem.eq(0).attr('class')).to.equal('apple');
      expect($elem.eq(1).attr('class')).to.equal('orange');
    });
  
    it('should gracefully degrade on complex, unmatched queries', function () {
      var $elem = cheerio('Eastern States Cup #8-fin&nbsp;<br>Downhill&nbsp;');
      expect($elem).to.have.length(0); // []
    });
  
    it('(extended Array) should not interfere with prototype methods (issue #119)', function () {
      var extended = [];
      extended.find = extended.children = extended.each = function () {};
      var $empty = cheerio(extended);
  
      expect($empty.find).to.be(cheerio.prototype.find);
      expect($empty.children).to.be(cheerio.prototype.children);
      expect($empty.each).to.be(cheerio.prototype.each);
    });
  
    it('should set html(number) as a string', function () {
      var $elem = cheerio('<div>');
      $elem.html(123);
      expect(typeof $elem.text()).to.equal('string');
    });
  
    it('should set text(number) as a string', function () {
      var $elem = cheerio('<div>');
      $elem.text(123);
      expect(typeof $elem.text()).to.equal('string');
    });
  
    describe('.load', function () {
      it('should generate selections as proper instances', function () {
        var $ = cheerio.load(fruits);
  
        expect($('.apple')).to.be.a($);
      });
  
      it('should be able to filter down using the context', function () {
        var $ = cheerio.load(fruits);
        var apple = $('.apple', 'ul');
        var lis = $('li', 'ul');
  
        expect(apple).to.have.length(1);
        expect(lis).to.have.length(3);
      });
  
      it('should allow loading a pre-parsed DOM', function () {
        var dom = htmlparser2.parseDOM(food);
        var $ = cheerio.load(dom);
  
        expect($('ul')).to.have.length(3);
      });
  
      it('should render xml in html() when options.xml = true', function () {
        var str = '<MixedCaseTag UPPERCASEATTRIBUTE=""></MixedCaseTag>';
        var expected = '<MixedCaseTag UPPERCASEATTRIBUTE=""/>';
        var $ = cheerio.load(str, { xml: true });
  
        expect($('MixedCaseTag').get(0).tagName).to.equal('MixedCaseTag');
        expect($.html()).to.be(expected);
      });
  
      it('should render xml in html() when options.xml = true passed to html()', function () {
        var str = '<MixedCaseTag UPPERCASEATTRIBUTE=""></MixedCaseTag>';
        // since parsing done without xml flag, all tags converted to lowercase
        var expectedXml =
          '<html><head/><body><mixedcasetag uppercaseattribute=""/></body></html>';
        var expectedNoXml =
          '<html><head></head><body><mixedcasetag uppercaseattribute=""></mixedcasetag></body></html>';
        var $ = cheerio.load(str);
  
        expect($('MixedCaseTag').get(0).tagName).to.equal('mixedcasetag');
        expect($.html()).to.be(expectedNoXml);
        expect($.html({ xml: true })).to.be(expectedXml);
      });
  
      it('should respect options on the element level', function () {
        var str =
          '<!doctype html><html><head><title>Some test</title></head><body><footer><p>Copyright &copy; 2003-2014</p></footer></body></html>';
        var expectedHtml = '<p>Copyright &copy; 2003-2014</p>';
        var expectedXml = '<p>Copyright © 2003-2014</p>';
        var domNotEncoded = cheerio.load(str, {
          xml: { decodeEntities: false },
        });
        var domEncoded = cheerio.load(str);
  
        expect(domNotEncoded('footer').html()).to.be(expectedHtml);
        // TODO: Make it more html friendly, maybe with custom encode tables
        expect(domEncoded('footer').html()).to.be(expectedXml);
      });
  
      it('should use htmlparser2 if xml option is used', function () {
        var str = '<div></div>';
        var dom = cheerio.load(str, null, false);
        // Should use htmlparser2 and not add <html>, <body> etc. tags
        expect(dom.html()).to.be(str);
      });
  
      it('should return a fully-qualified Function', function () {
        var $ = cheerio.load('<div>');
  
        expect($).to.be.a(Function);
      });
  
      describe('prototype extensions', function () {
        it('should honor extensions defined on `prototype` property', function () {
          var $ = cheerio.load('<div>');
          var $div;
          $.prototype.myPlugin = function () {
            return {
              context: this,
              args: arguments,
            };
          };
  
          $div = $('div');
  
          expect($div.myPlugin).to.be.a('function');
          expect($div.myPlugin().context).to.be($div);
          expect(Array.prototype.slice.call($div.myPlugin(1, 2, 3).args)).to.eql([
            1,
            2,
            3,
          ]);
        });
  
        it('should honor extensions defined on `fn` property', function () {
          var $ = cheerio.load('<div>');
          var $div;
          $.fn.myPlugin = function () {
            return {
              context: this,
              args: arguments,
            };
          };
  
          $div = $('div');
  
          expect($div.myPlugin).to.be.a('function');
          expect($div.myPlugin().context).to.be($div);
          expect(Array.prototype.slice.call($div.myPlugin(1, 2, 3).args)).to.eql([
            1,
            2,
            3,
          ]);
        });
  
        it('should isolate extensions between loaded functions', function () {
          var $a = cheerio.load('<div>');
          var $b = cheerio.load('<div>');
  
          $a.prototype.foo = function () {};
  
          expect($b('div').foo).to.be(undefined);
        });
      });
    });
  });
  