AutoPagerize SITEINFO Test

By youpy Last update Dec 12, 2007 — Installed 334 times. Daily Installs: 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0
// ==UserScript==
// @name           AutoPagerize SITEINFO Test
// @namespace      http://d.hatena.ne.jp/youpy/
// @description    unit test for AutoPagerize SITEINFO
// @include        *
// ==/UserScript==

var SiteInfo = function() { this.init.apply(this, arguments); };
SiteInfo.prototype = {
  init: function(str) {
    this.str = str;
    this.info = parseInfo(str);
  },
};

var AutoPagerizeTest = function() { this.init.apply(this, arguments); };
AutoPagerizeTest.prototype = {
  tests: [],
  finished: 0,
  failed: 0,
  skipped: 0,
  init: function(ui) {
    this.tests = [] // xxx
    var self = this;
    var tests = $x('//*[@class="autopagerize_data"]')
      .map(function(i) {
	return new TestCase(new SiteInfo(i.value));
      });

    tests.forEach(function(test) {
      self.add(test);
    });
      
    ui.test = this;
    this.ui = ui;
  },
  add: function(test) {
    this.tests.push(test);
  },
  start: function() {
    this.ui.start();
  },
  finish: function(result) {
    this.finished ++;
    if(result.isFailed()) {
      this.failed ++;
    } else if(result.isSkipped()) {
      this.skipped ++;
    }

    this.ui.out(result);
  },
  run: function() {
    var self = this;
    this.start();
    
    var i = 0;
    setTimeout(function forEach() {
      if(!(i < self.tests.length))
	return;
      self.tests[i++].run(function(result) { self.finish(result); });
      setTimeout(forEach);
    });
  },
  status: function() {
    return 'Test Results ('
      + this.finished + '/' + this.tests.length + ' tests, '
      + this.failed + ' failures, '
      + this.skipped + ' skips)';
  },
};

var TestCase = function() { this.init.apply(this, arguments); };
TestCase.prototype = {
  init: function(siteInfo) {
    this.siteInfo = siteInfo;
  },
  run: function(callback) {
    var info = this.siteInfo.info;
    if(!info.exampleUrl) {
      callback(new TestResult(info.url, 'skip', 'no example url'));
    } else {
      var self = this;
      getCharSet(info.exampleUrl, function(url, charSet) {
	//console.log(charSet);
	GM_xmlhttpRequest({
	  method: 'get',
          url: url,
	  overrideMimeType: "text/html; charset=" + charSet,
          onload: function(res){
	    var doc = createHTMLDocumentByString(res.responseText);
	    if($x(info.pageElement, doc).length == 0) {
	      callback(new TestResult(info.url,
				      'failure',
				      '<a href="'
				      + info.exampleUrl
				      + '">no page element found</a>: '
				      + info.pageElement));
	    } else if(info.insertBefore && $x(info.insertBefore, doc).length == 0) {
	      callback(new TestResult(info.url,
				      'failure',
				      '<a href="'
				      + info.exampleUrl
				      + '">no insertBefore element found</a>: '
				      + info.insertBefore));
	    } else if($x(info.nextLink, doc).length == 0) {
	      callback(new TestResult(info.url,
				      'failure',
				      '<a href="'
				      + info.exampleUrl
				      + '">no nextLink element found</a>: '
				      + info.nextLink));
	    } else {
	      callback(new TestResult(info.url,
				      'pass',
				      'ok'));
	    }
	  },
	});
      });
    }
  },
};

var TestResult = function() { this.init.apply(this, arguments); };
TestResult.prototype = {
  init: function(name, type, message) {
    this.name = name;
    this.type = type;
    this.message = message;
  },
  isFailed: function() {
    return this.type == 'failure';
  },
  isSkipped: function() {
    return this.type == 'skip';
  },
  toString: function() {
    return [this.name, this.type, this.message].join(': ');
  },
};

var TestUI = function() { this.init.apply(this, arguments); };
TestUI.prototype = {
  test: null,
  init: function() {
    var div;
    if(div = $x('//div[@class="auto_pagerize_test_result"]')[0]) {
      div.innerHTML = '';
    } else {
      div = document.createElement('div');
      div.className = 'auto_pagerize_test_result';
    }
    var ul = document.createElement('ul');
    div.innerHTML = '<p></p>';
    div.appendChild(ul);
    document.body.insertBefore(div, document.body.childNodes[0]);
    this.div = div;
    this.ul = ul;
  },
  start: function() {
    this.refreshProgress();
  },
  out: function(result) {
    this.refreshProgress();
    var li = document.createElement('li');
    li.innerHTML = result;
    li.className = result.type;
    this.ul.appendChild(li);
  },
  refreshProgress: function() {
    this.div.childNodes[0].innerHTML = this.test.status();
  },
}

GM_registerMenuCommand('AutoPagerize SITEINFO Test - run', function() {
  new AutoPagerizeTest(new TestUI()).run();
});

// from AutoPagerize
var parseInfo = function(str) {
  var lines = str.split(/\r\n|\r|\n/)
  var re = /(^[^:]*?):(.*)$/
  var strip = function(str) {
            return str.replace(/^\s*/, '').replace(/\s*$/, '')
  }
  var info = {}
  for (var i = 0; i < lines.length; i++) {
    if (lines[i].match(re)) {
      info[RegExp.$1] = strip(RegExp.$2)
    }
  }
  info.remainHeight = parseInt(info.remainHeight)
  var isValid = function(info) {
    var infoProp = ['nextLink', 'insertBefore', 'pageElement', 'exampleUrl']
    for (var i = 0; i < infoProp.length; i++) {
      if (!infoProp[i]) {
        return false
      }
    }
    return true
  }

  return isValid(info) ? info : null
}

// utility functions
function createHTMLDocumentByString(str) {
  var html = str.replace(/<!DOCTYPE.*?>/, '').replace(/<html.*?>/, '').replace(/<\/html>.*/, '');
  var htmlDoc  = document.implementation.createDocument(null, 'html', null);
  var fragment = createDocumentFragmentByString(html);
  htmlDoc.documentElement.appendChild(fragment);
  return htmlDoc;
}

function createDocumentFragmentByString(str) {
  var range = document.createRange()
  range.setStartAfter(document.body)
  return range.createContextualFragment(str)
}

// cho45 - http://lowreal.net/
function $x(exp, context) {
  if (!context) context = document;
    var resolver = function (prefix) {
      var o = document.createNSResolver(context)(prefix);
      return o ? o : (document.contentType == "text/html") ? "" : "http://www.w3.org/1999/xhtml";
    }
  var exp = document.createExpression(exp, resolver);
		
  var result = exp.evaluate(context, XPathResult.ANY_TYPE, null);
  switch (result.resultType) {
    case XPathResult.STRING_TYPE : return result.stringValue;
    case XPathResult.NUMBER_TYPE : return result.numberValue;
    case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
    case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: {
      result = exp.evaluate(context, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
      var ret = [];
      for (var i = 0, len = result.snapshotLength; i < len ; i++) {
	ret.push(result.snapshotItem(i));
      }
      return ret;
    }
  }
  return null;
}

function getCharSet(url, callback){
  GM_xmlhttpRequest({
    method: 'get',
    url: url,
    onload: function(res) {
      var doc = createHTMLDocumentByString(res.responseText);
      callback(url, getContentType(doc).match(/charset=(.*)/i) ? RegExp.$1 : 'iso-8859-1');
    }});
}

function getContentType(doc){
  for(var i=0, metas=doc.getElementsByTagName("meta") ; i<metas.length ; i++)
    if((metas[i].httpEquiv).match(/content\-type/i))
      return metas[i].content;
  
  return '';
}

GM_addStyle(<><![CDATA[
  .auto_pagerize_test_result {
    padding: 1em;
    background: #fff;
    border-bottom: 1px solid #000;
    font-family: monospace;
  }

  .auto_pagerize_test_result p {
    font-weight: bold;
  }

  .auto_pagerize_test_result .pass {
    color: green;
  }

  .auto_pagerize_test_result .failure {
    color: red;
  }

  .auto_pagerize_test_result .failure a {
    color: red;
    text-decoration: underline;
  }

  .auto_pagerize_test_result .skip {
    color: #ddd;
  }
]]></>);