Commit 1d35b0dd authored by yancend's avatar yancend

[test]添加测试框架

parent 9a63150e
//beforeEach( function () {
// // customMatcher 例子
// this.addMatchers( {
// //用的时候这么用:
// //expect('***').customMatchers(true);
// //expect('***').not.customMatchers(true);
//// customMatchers: function ( expected ) {
//// return expected === true;
//// },
//
// } );
//} );
/**
* Created with JetBrains PhpStorm.
* User: dongyancen
* Date: 13-10-12
* Time: 下午1:18
* To change this template use File | Settings | File Templates.
*/
/*
* matchFunctions
* */
describe("matchFunctions examples", function () {
//toBe 相当于===,处理简单字面值和变量
it("toBe相当于===", function () {
var a = 12;
var b = a;
expect(a).toBe(b);
expect(a).not.toBe(null);
expect(false == 0).toBe(true);
});
it("toBe不能当==用", function () {
expect(false).toBe(0);
});
//toEqual 处理简单字面值和变量,而且可以处理对象,数组
it("toEqual可以处理字面值,变量和对象", function () {
var a = 12;
expect(a).toEqual(12);
var foo = {key: "key"};
var bar = {key: "key"};
expect(foo).toEqual(bar);
var arr1 = [];
arr1["p1"] = "string1";
var arr2 = [];
arr2["p1"] = "string1";
var obj = {};
obj["p1"] = "string1";
expect(arr1).toEqual(arr2);
expect(arr1).toEqual(obj);
});
//toMatch 按正则式检索。
it("toMatch匹配正则式", function () {
var message = "foo bar baz";
expect(message).toMatch(/bar/);
expect(message).toMatch("bar");
expect(message).not.toMatch(/quux/);
expect(message).toMatch(/^f/);
expect(message).not.toMatch(/f$/);
});
//toBeDefined 是否已声明且赋值
it("toBeDefined检测变量非undefined", function () {
var a = { key: "key"};
expect(a.key).toBeDefined();
expect(a.foo).not.toBeDefined();
//expect(c).not.toBeDefined(); //未声明出错
var b;
expect(b).not.toBeDefined();
//对象.未声明属性.not.toBeDefined(); 通过
//未声明变量.not.toBeDefined(); 报错
});
//toBeLessThan 数值比较,小于
//
//toBeGreaterThan 数值比较,大于
//
//toBeCloseTo 数值比较时定义精度,先四舍五入后再比较
it("toBeCloseTo数值比较,指定精度,先四舍五入再比较", function () {
var pi = 3.1415926, e = 2.78;
expect(pi).toBeCloseTo(e, 0);
expect(pi).not.toBeCloseTo(e, 0.1);
});
//toThrow 检验一个函数是否会抛出一个错误
it("toThrow检验一个函数是否会抛出一个错误", function () {
var foo = function () {
return 1 + 2;
};
var bar = function () {
return a + 1;
};
expect(foo).not.toThrow();
expect(bar).toThrow();
});
});
/*
* Spy
* */
describe("Spy examples", function () {
//Spy 存储函数的被调用情况和参数(函数监视器,记录被调用情况,但函数并不真执行)
describe("对spy函数的测试", function () {
var foo, bar = null;
beforeEach(function () {
foo = {
setBar: function (value) {
bar = value;
}
};
spyOn(foo, 'setBar'); //foo为spy函数
foo.setBar(123);
foo.setBar(456, 'another param');
});
it("测试foo函数是否被调用过", function () {
expect(foo.setBar).toHaveBeenCalled();
});
it("测试foo函数被调用的次数", function () {
expect(foo.setBar.calls.length).toEqual(2);
});
it("测试foo函数被调用时传入的参数", function () {
expect(foo.setBar).toHaveBeenCalledWith(123);
expect(foo.setBar).toHaveBeenCalledWith(456, 'another param');
});
it("上一次被调用的参数", function () {
expect(foo.setBar.mostRecentCall.args[0]).toEqual(456);
});
it("所有被调用的情况存在一个数组里", function () {
expect(foo.setBar.calls[0].args[0]).toEqual(123);
});
it("函数并未真的执行", function () {
expect(bar).toBeNull();
});
});
//Spy addCallThrough 函数监视器,但函数真的执行
describe("对spy函数的测试,函数真的执行", function () {
var foo, bar, fetchedBar;
beforeEach(function () {
foo = {
setBar: function (value) {
bar = value;
},
getBar: function () {
return bar;
}
};
//spyOn(foo, "setBar"); //如果加上这句,setBar不真的执行,后两个spec不通过
spyOn(foo, 'getBar').andCallThrough();
foo.setBar(123);
fetchedBar = foo.getBar();
});
it("测试foo中getBar函数是否被调用过", function () {
expect(foo.getBar).toHaveBeenCalled();
});
it("foo中setBar函数真的执行了", function () {
expect(bar).toEqual(123);
});
it("foo中getBar函数真的执行了", function () {
expect(fetchedBar).toEqual(123);
});
});
//Spy andReturn 函数监视器,函数不真的执行。指定监视的函数的返回值
describe("A spy, when faking a return value", function () {
var foo, bar, fetchedBar;
beforeEach(function () {
foo = {
setBar: function (value) {
bar = value;
},
getBar: function () {
return bar;
}
};
spyOn(foo, 'getBar').andReturn(745); //指定getBar函数返回745
foo.setBar(123);
fetchedBar = foo.getBar();
});
it("测试foo中getBar函数是否被调用过", function () {
expect(foo.getBar).toHaveBeenCalled();
});
it("不影响未被监视的其它函数", function () {
expect(bar).toEqual(123);
});
it("指定的返回值745", function () {
expect(fetchedBar).toEqual(745);
});
});
//Spy addCallFake 替代被监视的函数,原函数不执行
describe("替代被监视的函数,原函数不执行", function () {
var foo, bar, fetchedBar;
beforeEach(function () {
foo = {
setBar: function (value) {
bar = value;
},
getBar: function () {
alert("frostbelt");
return bar;
}
};
spyOn(foo, 'getBar').andCallFake(function () {
return 1001;
});
foo.setBar(123);
fetchedBar = foo.getBar();
});
it("测试foo中getBar函数是否被调用过", function () {
expect(foo.getBar).toHaveBeenCalled();
});
it("不影响未被监视的其它函数", function () {
expect(bar).toEqual(123);
});
it("getBar被addCallFake指定的匿名函数代替,getBar不执行", function () {
expect(fetchedBar).toEqual(1001);
});
});
//自己create一个被监视函数
//
//jasmine.createSpy(functionId)
describe("自己造一个被监视函数", function () {
var whatAmI;
beforeEach(function () {
whatAmI = jasmine.createSpy('whatAmI');
whatAmI("I", "am", "a", "spy");
});
it("有个id,是createSpy的传入函数,用于报错", function () {
expect(whatAmI.identity).toEqual('whatAmI')
});
it("是否被调用", function () {
expect(whatAmI).toHaveBeenCalled();
});
it("被调用的次数", function () {
expect(whatAmI.calls.length).toEqual(1);
});
it("被调用的参数", function () {
expect(whatAmI).toHaveBeenCalledWith("I", "am", "a", "spy");
});
it("最近一次被调用", function () {
expect(whatAmI.mostRecentCall.args[0]).toEqual("I");
});
});
//有时需要监视一个对象的很多方法,用createSpyObj添加方法数组
//jasmine.createSpyObj(obj, methodArray)
describe("有时需要监视一个对象的很多个方法,用createSpyObj添加数组", function () {
var tape;
beforeEach(function () {
tape = jasmine.createSpyObj('tape', ['play', 'pause', 'stop', 'rewind']);
tape.play();
tape.pause();
tape.rewind(0);
});
it("tape对象的这四个方法已被定义", function () {
expect(tape.play).toBeDefined();
expect(tape.pause).toBeDefined();
expect(tape.stop).toBeDefined();
expect(tape.rewind).toBeDefined();
});
it("四个方法是否被调用", function () {
expect(tape.play).toHaveBeenCalled();
expect(tape.pause).toHaveBeenCalled();
expect(tape.rewind).toHaveBeenCalled();
expect(tape.stop).not.toHaveBeenCalled();
});
it("被调用时传入的参数", function () {
expect(tape.rewind).toHaveBeenCalledWith(0);
});
});
});
/*
* instanceof
* */
describe("instanceof examples", function() {
//jasmine.any 类型判断。instanceof
it("相当于instanceof", function() {
expect({}).toEqual(jasmine.any(Object));
expect(12).toEqual(jasmine.any(Number));
});
it("也可以用于spy", function() {
var foo = jasmine.createSpy('foo');
foo(12, function() {
return true
});
expect(foo).toHaveBeenCalledWith(jasmine.any(Number), jasmine.any(Function));
//foo被调用时的参数 类型判断
});
});
/*
* jasmine.Clock.useMock() jasmine自己控制时间,实现异步调试,减少等待
* jasmine.Clock.tick(n:uint) 向前n毫秒
* */
describe("useMock + tick examples", function() {
var timerCallback;
beforeEach(function() {
timerCallback = jasmine.createSpy('timerCallback');
jasmine.Clock.useMock();
});
it("setTimeout", function() {
setTimeout(function() {
timerCallback();
}, 100);
expect(timerCallback).not.toHaveBeenCalled();
jasmine.Clock.tick(101);
expect(timerCallback).toHaveBeenCalled();
});
it("setInterval", function() {
setInterval(function() {
timerCallback();
}, 100);
expect(timerCallback).not.toHaveBeenCalled();
jasmine.Clock.tick(101);
expect(timerCallback.callCount).toEqual(1);
jasmine.Clock.tick(50);
expect(timerCallback.callCount).toEqual(1);
jasmine.Clock.tick(50);
expect(timerCallback.callCount).toEqual(2);
});
//注:在这种环境下setTimeout和setInterval的callback为同步的,系统时间不再影响执行
});
/*
*
* runs(function) waitsFor(function, message, millisec) Jasmine异步调试
* */
describe("runs+waitsFor examples", function() {
var value, flag;
it("这里写你测试需要异步的代码", function() {
runs(function() {
flag = false;
value = 0;
setTimeout(function() {
flag = true;
}, 500);
});
waitsFor(function() { //如果750 毫秒之内返回的flag为true,则执行runs方法,否则报错,返回并且显示第二个参数的内容
value++;
return flag;
}, "说明", 750);
runs(function() {
expect(value).toBeGreaterThan(0);
});
});
});
describe(" example2", function() {
//这里使用的是SpecHelper里面自定义的断言
it('helper',function(){
expect(true).toBe(true);
});
});
\ No newline at end of file
describe("command", function () {
it('',function(){
})
});
\ No newline at end of file
/**
* Created by dongyancen on 13-12-27.
*/
///import core/kityminder;
///import core/utils;
///import core/command;
///import core/node;
///import core/module;
///import core/event;
///import core/minder;
///import core/minder.data;
///import core/minder.event;
///import core/minder.module;
///import core/minder.command;
///import core/minder.node;
///import core/minder.select;
\ No newline at end of file
/**
* Created with JetBrains PhpStorm.
* User: dongyancen
* Date: 13-12-10
* Time: 下午7:24
* To change this template use File | Settings | File Templates.
/**
* Created by dongyancen on 13-12-30.
*/
describe("connect", function () {
it("KityMinder 已定义", function () {
expect(KityMinder).toBeDefined();
});
});
<?php
/**
* Created by JetBrains PhpStorm.
* User: dongyancen
* Date: 13-10-10
* Time: 上午1:20
* To change this template use File | Settings | File Templates.
*/
require_once 'config.php';
class Kiss
{
public $projroot ;
public $name;
public $srcPath;
public $testPath;
public $empty = false;
public $case_id;
function __construct( $name = '' )
{
$this->projroot = Config::$projroot;
$this->srcPath = Config::$projroot.Config::$src_PATH;
$this->testPath = Config::$projroot.Config::$test_PATH;
$this->name = $name;
$this->case_id = 'id_case_' . join( '_' , explode( '.' , $name ) );
}
public function print_js( $cov)
{
if($cov){
$basePath = '../../spec/coverage';
}
else{
$basePath = '../../src';
}
// print "<script src='../../dist/dev.php'></script>";
print "<script type='text/javascript' src='./js/UserAction.js' ></script>\n";
/* load case and case dependents*/
$ps = explode( '/' , $this->name );
/*load helper*/
foreach(Config::$helperFiles as $f){
if ( file_exists( $this->testPath . $f ) ) {
print '<script type="text/javascript" src="' . $this->testPath . $f . '"></script>' . "\n";
}
}
//先引用kity
print "<script src='../../kity/dist/kitygraph.all.js'></script>\n";
//分成两部分:一部分默认加载(可配置),一部分针对文件加载(例如在文件头部有注释标注依赖文件,根据依赖文件动态加载)
// core里面的文件全部加载,module加载用例对应的src文件,最后加载与用例同名的原文件
$importurl = "{$this->testPath}tools/import.php?f=$this->name";
if ( $cov ) $importurl .= '^&cov=true';
print "<script type='text/javascript' src='".$importurl."' ></script>\n";
//引用测试文件
print '<script type="text/javascript" src="' .$this->testPath.$this->name. '.js"></script>' . "\n";
}
public function match( $matcher )
{
if ( $matcher == '*' )
return true;
$len = strlen( $matcher );
/**
* 处理多选分支,有一个成功则成功,filter后面参数使用|切割
* @var unknown_type
*/
$ms = explode( ',' , $matcher );
if ( sizeof( $ms ) > 1 ) {
foreach ( $ms as $matcher1 ) {
if ( $this->match( $matcher1 ) )
return true;
}
return false;
}
/**
* 处理反向选择分支
*/
if ( substr( $matcher , 0 , 1 ) == '!' ) {
$m = substr( $matcher , 1 );
if ( substr( $this->name , 0 , strlen( $m ) ) == $m )
return false;
return true;
}
if ( $len > strlen( $this->name ) ) {
return false;
}
return substr( $this->name , 0 , $len ) == $matcher;
}
public static function listcase( $matcher = "*" ,$cov)
{
require_once 'fileHelper.php';
/*get files both in src path and test path*/
$caselist = getSameFile(Config::$projroot.Config::$src_PATH , Config::$projroot.Config::$test_PATH , '' );
sort($caselist,SORT_STRING);
foreach ( $caselist as $caseitem ) {
/*remove '.js' */
$name = substr( $caseitem , 0 , -3 );
$c = new Kiss( $name );
if ( $c->empty )
continue;
if ( $c->match( $matcher ) ) {
$newName = explode( '\\.' , $name );
$newName = $newName[ count( $newName ) - 1 ];
if($cov){
$covMsg = "&cov=true";
}else{
$covMsg="";
}
print( "<a href=\"run.php?case=$name".$covMsg."\" id=\"$c->case_id\" target=\"_blank\" title=\"$name\" onclick=\"run('$name');return false;\">". $newName . "</a>" );
}
}
}
public static function listSrcOnly( $print = true )
{
$srcpath = Config::$projroot.Config::$src_PATH;
$testpath = Config::$projroot.Config::$test_PATH;
require_once 'fileHelper.php';
$caselist = getSameFile( $srcpath , $testpath , '' );
$srclist = getSrcOnlyFile( $srcpath , $testpath , '' );
$srcList = array();
foreach ( $srclist as $case ) {
if ( in_array( $case , $caselist ) )
continue;
$name = str_replace( '/' , '.' , substr( $case , 0 , -3 ) );
$tag = "<a title=\"$name\">" . ( strlen( $name ) > 20 ? substr( $name , 6 )
: $name ) . "</a>";
array_push( $srcList , $tag );
if ( $print )
echo $tag;
}
return $srcList;
}
}
\ No newline at end of file
<?php
/**
* Created by JetBrains PhpStorm.
* User: dongyancen
* Date: 13-10-10
* Time: 上午1:50
* To change this template use File | Settings | File Templates.
*/
class Config
{
public static $projroot = "../../";
//相对于projroot的路径
public static $src_PATH = "src/" ;
public static $test_PATH = "spec/";
public static $helperFiles = array( "SpecHelper.js" );
}
\ No newline at end of file
* {
margin: 0;
padding: 0;
}
html {
border: 0;
height: 100%;
}
body {
font: 12px/1.5 Lucida Grande, Helvetica, Arial, sans-serif;
background: #F3F1F1;
color: #41464D;
}
body,#container {
width: 100%;
height: 100%;
}
.clear { /* generic container (i.e. div) for floating buttons */
overflow: hidden;
width: 100%;
}
a {
text-decoration: none;
overflow: hidden;
}
#title {
top: 0;
left: 0;
width: 100%;
padding: 5px 0;
background: #aaa;
background: #41464D;
color: #F3F1F1;
height: 30px;
}
a:link,a:visited {
color: #528CE0;
}
a:hover,a:active {
color: #41464D !important;
cursor: pointer !important;
}
#title h1 {
height: 30px;
font: 25px/1.1 Arial, sans-serif;
font-weight: bolder;
float: left;
margin: 1px 0 2px 20px;
text-shadow: 0 2px 2px rgba(0, 0, 0, 0.4);
}
h3 {
font-size: 14px;
padding: 3px 5px 1px;
}
.control {
background: #d5ded7;
width: 99%;
}
.testlist {
max-height: 200px;
overflow-y: scroll;
border-style: double;
}
.testlist a {
display: block;
width: 150px;
color: #657528;
background: #d5dea7;
border: 1px solid #c8dc7b;
margin: 5px 0 0 5px;
text-indent: 5px;
line-height: 24px;
font-size: 14px;
float: left;
}
a.button {
background: transparent url('bg_button_a.gif') no-repeat scroll top
right;
color: #444;
display: block;
float: left;
font: normal 12px arial, sans-serif;
height: 24px;
margin-right: 6px;
padding-right: 18px; /* sliding doors padding */
text-decoration: none;
}
a.button span {
background: transparent url('bg_button_span.gif') no-repeat;
display: block;
line-height: 14px;
padding: 5px 0 5px 18px;
}
a.button:active {
background-position: bottom right;
color: #000;
outline: none; /* hide dotted outline in Firefox */
}
a.button:active span {
background-position: bottom left;
padding: 6px 0 4px 18px; /* push text down 1px */
}
.testlist a:link {
}
.testlist a:visited {
}
.testlist a:hover {
background: #c8dc7b;
}
.testlist a.jsframe_jsspec {
background: #DDDDDD
}
.testlist a.running_case {
color: yellow;
}
.testlist a.fail_case {
color: red;
}
.testlist a.pass_case {
color: green;
}
.runningarea {
height: 60%;
}
.runningmaindiv {
height: 99%;
}
.runningframe {
height: 99.99%;
width: 99.99%;
}
.runningstatus {
clear: both;
height: 10%;
border: solid
}
.reportarea {
padding: 10px;
border: 10px blue;
max-height: 200px;
overflow-y: scroll;
}
\ No newline at end of file
<?php
/**
* Created by JetBrains PhpStorm.
* User: dongyancen
* Date: 13-10-11
* Time: 下午5:31
* To change this template use File | Settings | File Templates.
*///获取两个目录下的相同路径的文件数
function getSameFile($src, $test, $path=''){
$result = array();
$as = listFile($src.$path);
$ts = listFile($test.$path);
$ds = array_intersect($as, $ts);
foreach($ds as $item){
$si = $src.$path.$item;
$ti = $test.$path.$item;
if(is_dir($si) && is_dir($ti)){
$result = array_merge($result, getSameFile($src, $test, $path.$item.'/'));
}else if(is_file($si) && is_file($ti)){
if(substr($si, -3) == '.js')
array_push($result, $path.$item);
}
}
return $result;
}
//获取只在src中存在的文件,防止遗漏用例
function getSrcOnlyFile($src, $test, $path=''){
$result = array();
$as = listFile($src.$path);
$ts = listFile($test.$path);
foreach($as as $item){
$si = $src.$path.$item;
$ti = $test.$path.$item;
if(is_dir($si) && is_dir($ti)){
$result = array_merge($result, getSrcOnlyFile($src, $test, $path.$item.'/'));
}else if(is_file($si) && !is_file($ti)){
if(substr($si, -3) == '.js')
array_push($result,$path.$item);
}
else{
// print("error : $si");
}
}
return $result;
}
function listFile($dir){
$as = array();
if($dh = opendir($dir)){
while(($file = readdir($dh))!==false){
if(substr(basename($file), 0, 1) == '.')
continue;
array_push($as, basename($file));
}
closedir($dh);
}
return $as;
}
<?php
/**
* Created by JetBrains PhpStorm.
* User: dongyancen
* Date: 13-12-10
* Time: 上午1:48
* To change this template use File | Settings | File Templates.
*/
header( "Content-type: text/html; charset=utf-8" );
$import = 'import.js';
function custom_strEmpty($s){
$str = strlen(trim($s));
if(empty($str)){
return true;
}
return false;
}
function importSrc(){
require_once 'config.php';
global $import;
require_once 'config.php';
if(file_exists(Config::$projroot.Config::$test_PATH.$import)){
$cnt = file_get_contents(Config::$projroot.Config::$test_PATH.$import);
}
if($cnt == ''){
if(Config::$DEBUG)
print "fail read file : ".Config::$test_PATH.$import;
return '';
}
$is = array();
$flag_ownSrc = 1;
$caseName = $_GET[ 'f' ];
$this_src = Config::$projroot.Config::$src_PATH.$caseName.".js";
//正则匹配,提取所有(///import xxx;)中的xxx
preg_match_all('/\/\/\/import\s+([^;]+);?/ies', $cnt, $is, PREG_PATTERN_ORDER);
foreach($is[1] as $i) {
if(strcasecmp($i,$caseName)==0){
$flag_ownSrc = 0;
}
$path = $i . '.js';
$srcFile = Config::$projroot . Config::$src_PATH . $path;
if (file_exists($srcFile)) {
echo "document.write('<script charset=utf-8 src=\"$srcFile\"></script>');";
}
}
if(file_exists($this_src)){
$file=fopen($this_src,"r");
while(!feof($file))
{
$statment = fgets($file);
if(custom_strEmpty($statment)){
continue ;
}else if(preg_match('/\/\/\/import\s+([^;]+);?/ies', $statment,$r)){
echo "document.write('<script charset=utf-8 src=\"".Config::$projroot . Config::$src_PATH.$r[1].".js\"></script>');";
}else{
break;
}
}
fclose($file);
}
//加载与用例同名的原文件,并避免重复加载
if($flag_ownSrc){
echo "document.write('<script charset=utf-8 src=\"".$this_src."\"></script>');";
}
}
importSrc();
\ No newline at end of file
/**
* 测试用例库文件,提供如event mock、iframe封装等各种常用功能 部分方法来源于YUI测试框架
*/
UserAction = {
beforedispatch:null,
// flag : true,
isf /* is function ? */:function (value) {
return value && (typeof value == 'function');
},
isb /* is boolean? */:function (value) {
return value && (typeof value == 'boolean');
},
iso /* is object? */:function (value) {
return value && (typeof value == 'object');
},
iss /* is string? */:function (value) {
return value && (typeof value == 'string');
},
isn /* is number? */:function (value) {
return value && (typeof value == 'number');
},
// --------------------------------------------------------------------------
// Generic event methods
// --------------------------------------------------------------------------
/**
* Simulates a key event using the given event information to populate the
* generated event object. This method does browser-equalizing calculations
* to account for differences in the DOM and IE event models as well as
* different browser quirks. Note: keydown causes Safari 2.x to crash.
*
* @method simulateKeyEvent
* @private
* @static
* @param {HTMLElement}
* target The target of the given event.
* @param {String}
* type The type of event to fire. This can be any one of the
* following: keyup, keydown, and keypress.
* @param {Boolean}
* bubbles (Optional) Indicates if the event can be bubbled up.
* DOM Level 3 specifies that all key events bubble by default.
* The default is true.
* @param {Boolean}
* cancelable (Optional) Indicates if the event can be canceled
* using preventDefault(). DOM Level 3 specifies that all key
* events can be cancelled. The default is true.
* @param {Window}
* view (Optional) The view containing the target. This is
* typically the window object. The default is window.
* @param {Boolean}
* ctrlKey (Optional) Indicates if one of the CTRL keys is
* pressed while the event is firing. The default is false.
* @param {Boolean}
* altKey (Optional) Indicates if one of the ALT keys is pressed
* while the event is firing. The default is false.
* @param {Boolean}
* shiftKey (Optional) Indicates if one of the SHIFT keys is
* pressed while the event is firing. The default is false.
* @param {Boolean}
* metaKey (Optional) Indicates if one of the META keys is
* pressed while the event is firing. The default is false.
* @param {int}
* keyCode (Optional) The code for the key that is in use. The
* default is 0.
* @param {int}
* charCode (Optional) The Unicode code for the character
* associated with the key being used. The default is 0.
*/
simulateKeyEvent:function (target /* :HTMLElement */, type /* :String */, bubbles /* :Boolean */, cancelable /* :Boolean */, view /* :Window */, ctrlKey /* :Boolean */, altKey /* :Boolean */, shiftKey /* :Boolean */, metaKey /* :Boolean */, keyCode /* :int */, charCode /* :int */) /* :Void */ {
// check target
target = typeof target == 'string' ? document.getElementById(target)
: target;
if (!target) {
throw new Error("simulateKeyEvent(): Invalid target.");
}
// check event type
if (typeof type == 'string') {
type = type.toLowerCase();
switch (type) {
case "compositionend":
case "compositionstart":
case "paste":
case "cut":
case "keyup":
case "keydown":
case "keypress":
break;
case "textevent": // DOM Level 3
type = "keypress";
break;
// @TODO was the fallthrough intentional, if so throw error
default:
throw new Error("simulateKeyEvent(): Event type '" + type
+ "' not supported.");
}
} else {
throw new Error("simulateKeyEvent(): Event type must be a string.");
}
// setup default values
if (!this.isb(bubbles)) {
bubbles = true; // all key events bubble
}
if (!this.isb(cancelable)) {
cancelable = true; // all key events can be cancelled
}
if (!this.iso(view)) {
view = window; // view is typically window
}
if (!this.isb(ctrlKey)) {
ctrlKey = false;
}
if (!this.isb(typeof altKey == 'boolean')) {
altKey = false;
}
if (!this.isb(shiftKey)) {
shiftKey = false;
}
if (!this.isb(metaKey)) {
metaKey = false;
}
if (!(typeof keyCode == 'number')) {
keyCode = 0;
}
if (!(typeof charCode == 'number')) {
charCode = 0;
}
// try to create a mouse event
var customEvent /* :MouseEvent */ = null;
// check for DOM-compliant browsers first
if (this.isf(document.createEvent)) {
try {
// try to create key event
customEvent = document.createEvent("KeyEvents");
/*
* Interesting problem: Firefox implemented a non-standard
* version of initKeyEvent() based on DOM Level 2 specs. Key
* event was removed from DOM Level 2 and re-introduced in DOM
* Level 3 with a different interface. Firefox is the only
* browser with any implementation of Key Events, so for now,
* assume it's Firefox if the above line doesn't error.
*/
// TODO: Decipher between Firefox's implementation and a correct
// one.
customEvent.initKeyEvent(type, bubbles, cancelable, view,
ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode);
} catch (ex /* :Error */) {
/*
* If it got here, that means key events aren't officially
* supported. Safari/WebKit is a real problem now. WebKit 522
* won't let you set keyCode, charCode, or other properties if
* you use a UIEvent, so we first must try to create a generic
* event. The fun part is that this will throw an error on
* Safari 2.x. The end result is that we need another
* try...catch statement just to deal with this mess.
*/
try {
// try to create generic event - will fail in Safari 2.x
customEvent = document.createEvent("Events");
} catch (uierror /* :Error */) {
// the above failed, so create a UIEvent for Safari 2.x
customEvent = document.createEvent("UIEvents");
} finally {
customEvent.initEvent(type, bubbles, cancelable);
// initialize
customEvent.view = view;
customEvent.altKey = altKey;
customEvent.ctrlKey = ctrlKey;
customEvent.shiftKey = shiftKey;
customEvent.metaKey = metaKey;
customEvent.keyCode = keyCode;
customEvent.charCode = charCode;
}
}
// before dispatch
if (this.beforedispatch && typeof this.beforedispatch == 'function')
this.beforedispatch(customEvent);
this.beforedispatch = null;
// fire the event
target.dispatchEvent(customEvent);
} else if (this.iso(document.createEventObject)) { // IE
// create an IE event object
customEvent = document.createEventObject();
// assign available properties
customEvent.bubbles = bubbles;
customEvent.cancelable = cancelable;
customEvent.view = view;
customEvent.ctrlKey = ctrlKey;
customEvent.altKey = altKey;
customEvent.shiftKey = shiftKey;
customEvent.metaKey = metaKey;
/*
* IE doesn't support charCode explicitly. CharCode should take
* precedence over any keyCode value for accurate representation.
*/
customEvent.keyCode = (charCode > 0) ? charCode : keyCode;
// before dispatch
if (this.beforedispatch && typeof this.beforedispatch == 'function')
this.beforedispatch(customEvent);
this.beforedispatch = null;
// fire the event
target.fireEvent("on" + type, customEvent);
} else {
throw new Error(
"simulateKeyEvent(): No event simulation framework present.");
}
this.beforedispatch = null;
},
/**
* Simulates a mouse event using the given event information to populate the
* generated event object. This method does browser-equalizing calculations
* to account for differences in the DOM and IE event models as well as
* different browser quirks.
*
* @method simulateMouseEvent
* @private
* @static
* @param {HTMLElement}
* target The target of the given event.
* @param {String}
* type The type of event to fire. This can be any one of the
* following: click, dblclick, mousedown, mouseup, mouseout,
* mouseover, and mousemove.
* @param {Boolean}
* bubbles (Optional) Indicates if the event can be bubbled up.
* DOM Level 2 specifies that all mouse events bubble by default.
* The default is true.
* @param {Boolean}
* cancelable (Optional) Indicates if the event can be canceled
* using preventDefault(). DOM Level 2 specifies that all mouse
* events except mousemove can be cancelled. The default is true
* for all events except mousemove, for which the default is
* false.
* @param {Window}
* view (Optional) The view containing the target. This is
* typically the window object. The default is window.
* @param {int}
* detail (Optional) The number of times the mouse button has
* been used. The default value is 1.
* @param {int}
* screenX (Optional) The x-coordinate on the screen at which
* point the event occured. The default is 0.
* @param {int}
* screenY (Optional) The y-coordinate on the screen at which
* point the event occured. The default is 0.
* @param {int}
* clientX (Optional) The x-coordinate on the client at which
* point the event occured. The default is 0.
* @param {int}
* clientY (Optional) The y-coordinate on the client at which
* point the event occured. The default is 0.
* @param {Boolean}
* ctrlKey (Optional) Indicates if one of the CTRL keys is
* pressed while the event is firing. The default is false.
* @param {Boolean}
* altKey (Optional) Indicates if one of the ALT keys is pressed
* while the event is firing. The default is false.
* @param {Boolean}
* shiftKey (Optional) Indicates if one of the SHIFT keys is
* pressed while the event is firing. The default is false.
* @param {Boolean}
* metaKey (Optional) Indicates if one of the META keys is
* pressed while the event is firing. The default is false.
* @param {int}
* button (Optional) The button being pressed while the event is
* executing. The value should be 0 for the primary mouse button
* (typically the left button), 1 for the terciary mouse button
* (typically the middle button), and 2 for the secondary mouse
* button (typically the right button). The default is 0.
* @param {HTMLElement}
* relatedTarget (Optional) For mouseout events, this is the
* element that the mouse has moved to. For mouseover events,
* this is the element that the mouse has moved from. This
* argument is ignored for all other events. The default is null.
*/
simulateMouseEvent:function (target /* :HTMLElement */, type /* :String */, bubbles /* :Boolean */, cancelable /* :Boolean */, view /* :Window */, detail /* :int */, screenX /* :int */, screenY /* :int */, clientX /* :int */, clientY /* :int */, ctrlKey /* :Boolean */, altKey /* :Boolean */, shiftKey /* :Boolean */, metaKey /* :Boolean */, button /* :int */, relatedTarget /* :HTMLElement */,button) /* :Void */ {
// check target
target = typeof target == 'string' ? document.getElementById(target)
: target;
if (!target) {
throw new Error("simulateMouseEvent(): Invalid target.");
}
// check event type
if (this.iss(type)) {
type = type.toLowerCase();
switch (type) {
case "mouseover":
case "mouseout":
case "mousedown":
case "mouseup":
case "click":
case "dblclick":
case "mousemove":
case "mouseenter":// 非标准支持,仅为测试提供,该项仅IE下work
case "mouseleave":
case "contextmenu":
case "dragend":
case "blur":
break;
default:
throw new Error("simulateMouseEvent(): Event type '" + type
+ "' not supported.");
}
} else {
throw new Error(
"simulateMouseEvent(): Event type must be a string.");
}
// setup default values
if (!this.isb(bubbles)) {
bubbles = true; // all mouse events bubble
}
if (!this.isb(cancelable)) {
cancelable = (type != "mousemove"); // mousemove is the only one
// that can't be cancelled
}
if (!this.iso(view)) {
view = window; // view is typically window
}
if (!this.isn(detail)) {
detail = 1; // number of mouse clicks must be at least one
}
if (!this.isn(screenX)) {
screenX = 0;
}
if (!this.isn(screenY)) {
screenY = 0;
}
if (!this.isn(clientX)) {
clientX = 0;
}
if (!this.isn(clientY)) {
clientY = 0;
}
if (!this.isb(ctrlKey)) {
ctrlKey = false;
}
if (!this.isb(altKey)) {
altKey = false;
}
if (!this.isb(shiftKey)) {
shiftKey = false;
}
if (!this.isb(metaKey)) {
metaKey = false;
}
if (!this.isn(button)) {
button = 0;
}
// try to create a mouse event
var customEvent /* :MouseEvent */ = null;
// check for DOM-compliant browsers first
if (this.isf(document.createEvent)) {
customEvent = document.createEvent("MouseEvents");
// Safari 2.x (WebKit 418) still doesn't implement initMouseEvent()
if (this.browser.ie !== 9 && customEvent.initMouseEvent) {
customEvent.initMouseEvent(type, bubbles, cancelable, view,
detail, screenX, screenY, clientX, clientY, ctrlKey,
altKey, shiftKey, metaKey, button, relatedTarget);
} else { // Safari
// the closest thing available in Safari 2.x is UIEvents
customEvent = document.createEvent("UIEvents");
customEvent.initEvent(type, bubbles, cancelable);
customEvent.view = view;
customEvent.detail = detail;
customEvent.screenX = screenX;
customEvent.screenY = screenY;
customEvent.clientX = clientX;
customEvent.clientY = clientY;
customEvent.ctrlKey = ctrlKey;
customEvent.altKey = altKey;
customEvent.metaKey = metaKey;
customEvent.shiftKey = shiftKey;
customEvent.button = button;
customEvent.relatedTarget = relatedTarget;
}
/*
* Check to see if relatedTarget has been assigned. Firefox versions
* less than 2.0 don't allow it to be assigned via initMouseEvent()
* and the property is readonly after event creation, so in order to
* keep YAHOO.util.getRelatedTarget() working, assign to the IE
* proprietary toElement property for mouseout event and fromElement
* property for mouseover event.
*/
if (relatedTarget && !customEvent.relatedTarget) {
if (type == "mouseout") {
customEvent.toElement = relatedTarget;
} else if (type == "mouseover") {
customEvent.fromElement = relatedTarget;
}
}
// before dispatch
if (this.beforedispatch && typeof this.beforedispatch == 'function')
this.beforedispatch(customEvent);
this.beforedispatch = null;
// fire the event
target.dispatchEvent(customEvent);
} else if (this.iso(document.createEventObject)) { // IE
// create an IE event object
customEvent = document.createEventObject();
// assign available properties
customEvent.bubbles = bubbles;
customEvent.cancelable = cancelable;
customEvent.view = view;
customEvent.detail = detail;
customEvent.screenX = screenX;
customEvent.screenY = screenY;
customEvent.clientX = clientX;
customEvent.clientY = clientY;
customEvent.ctrlKey = ctrlKey;
customEvent.altKey = altKey;
customEvent.metaKey = metaKey;
customEvent.shiftKey = shiftKey;
// fix button property for IE's wacky implementation
switch (button) {
case 0:
customEvent.button = 1;
break;
case 1:
customEvent.button = 4;
break;
case 2:
customEvent.button = 2;
// leave as is
break;
default:
customEvent.button = 0;
}
/*
* Have to use relatedTarget because IE won't allow assignment to
* toElement or fromElement on generic events. This keeps
* YAHOO.util.customEvent.getRelatedTarget() functional.
*/
customEvent.relatedTarget = relatedTarget;
// before dispatch
if (this.beforedispatch && typeof this.beforedispatch == 'function')
this.beforedispatch(customEvent);
this.beforedispatch = null;
// fire the event
target.fireEvent("on" + type, customEvent);
} else {
throw new Error(
"simulateMouseEvent(): No event simulation framework present.");
}
},
// --------------------------------------------------------------------------
// Mouse events
// --------------------------------------------------------------------------
/**
* Simulates a mouse event on a particular element.
*
* @param {HTMLElement}
* target The element to click on.
* @param {String}
* type The type of event to fire. This can be any one of the
* following: click, dblclick, mousedown, mouseup, mouseout,
* mouseover, and mousemove.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method mouseEvent
* @static
*/
fireMouseEvent:function (target /* :HTMLElement */, type /* :String */, options /* :Object */) /* :Void */ {
options = options || {};
this.simulateMouseEvent(target, type, options.bubbles,
options.cancelable, options.view, options.detail,
options.screenX, options.screenY, options.clientX,
options.clientY, options.ctrlKey, options.altKey,
options.shiftKey, options.metaKey, options.button,
options.relatedTarget,options.button);
},
/**
* Simulates a click on a particular element.
*
* @param {HTMLElement}
* target The element to click on.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method click
* @static
*/
click:function (target /* :HTMLElement */, options /* :Object */) /* :Void */ {
this.fireMouseEvent(target, "click", options);
},
/**
* Simulates a double click on a particular element.
*
* @param {HTMLElement}
* target The element to double click on.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method dblclick
* @static
*/
dblclick:function (target /* :HTMLElement */, options /* :Object */) /* :Void */ {
this.fireMouseEvent(target, "dblclick", options);
},
/**
* Simulates a mousedown on a particular element.
*
* @param {HTMLElement}
* target The element to act on.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method mousedown
* @static
*/
mousedown:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
this.fireMouseEvent(target, "mousedown", options);
},
/**
* Simulates a mousemove on a particular element.
*
* @param {HTMLElement}
* target The element to act on.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method mousemove
* @static
*/
mousemove:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
this.fireMouseEvent(target, "mousemove", options);
},
/**
* Simulates a mouseout event on a particular element. Use "relatedTarget"
* on the options object to specify where the mouse moved to. Quirks:
* Firefox less than 2.0 doesn't set relatedTarget properly, so toElement is
* assigned in its place. IE doesn't allow toElement to be be assigned, so
* relatedTarget is assigned in its place. Both of these concessions allow
* YAHOO.util.Event.getRelatedTarget() to work correctly in both browsers.
*
* @param {HTMLElement}
* target The element to act on.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method mouseout
* @static
*/
mouseout:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
this.fireMouseEvent(target, "mouseout", options);
},
/**
* Simulates a mouseover event on a particular element. Use "relatedTarget"
* on the options object to specify where the mouse moved from. Quirks:
* Firefox less than 2.0 doesn't set relatedTarget properly, so fromElement
* is assigned in its place. IE doesn't allow fromElement to be be assigned,
* so relatedTarget is assigned in its place. Both of these concessions
* allow YAHOO.util.Event.getRelatedTarget() to work correctly in both
* browsers.
*
* @param {HTMLElement}
* target The element to act on.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method mouseover
* @static
*/
mouseover:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
this.fireMouseEvent(target, "mouseover", options);
},
/**
* Simulates a mouseup on a particular element.
*
* @param {HTMLElement}
* target The element to act on.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method mouseup
* @static
*/
mouseup:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
this.fireMouseEvent(target, "mouseup", options);
},
mouseenter:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
this.fireMouseEvent(target, "mouseenter", options);
},
mouseleave:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
this.fireMouseEvent(target, "mouseleave", options);
},
/**
* Simulates a contextmenu on a particular element.
*
* @param {HTMLElement}
* target The element to show contextmenu.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method contextmenu
* @static
*/
contextmenu:function (target /* :HTMLElement */, options /* :Object */) /* :Void */ {
this.fireMouseEvent(target, "contextmenu", options);
},
/**
* Simulates a dragend on a particular element.
*
* @param {HTMLElement}
* target The element to show dragend.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method dragend
* @static
*/
dragend:function (target /* :HTMLElement */, options /* :Object */) /* :Void */ {
this.fireMouseEvent(target, "dragend", options);
},
/**
* Simulates a blur on a particular element.
*
* @param {HTMLElement}
* target The element to show blur.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method blur
* @static
*/
blur:function (target /* :HTMLElement */, options /* :Object */) /* :Void */ {
this.fireMouseEvent(target, "blur", options);
},
dragto:function (target, options) {
var me = this;
me.mousemove(target, {
clientX:options.startX,
clientY:options.startY
});
setTimeout(function () {
me.mousedown(target, {
clientX:options.startX,
clientY:options.startY
});
setTimeout(function () {
me.mousemove(target, {
clientX:options.endX,
clientY:options.endY
});
setTimeout(function () {
me.mouseup(target, {
clientX:options.endX,
clientY:options.endY
});
if (options.callback)
options.callback();
}, options.aftermove || 20);
}, options.beforemove || 20);
}, options.beforestart || 50);
},
// --------------------------------------------------------------------------
// Key events
// --------------------------------------------------------------------------
/**
* Fires an event that normally would be fired by the keyboard (keyup,
* keydown, keypress). Make sure to specify either keyCode or charCode as an
* option.
*
* @private
* @param {String}
* type The type of event ("keyup", "keydown" or "keypress").
* @param {HTMLElement}
* target The target of the event.
* @param {Object}
* options Options for the event. Either keyCode or charCode are
* required.
* @method fireKeyEvent
* @static
*/
fireKeyEvent:function (type /* :String */, target /* :HTMLElement */, options /* :Object */) /* :Void */ {
options = options || {};
this.simulateKeyEvent(target, type, options.bubbles,
options.cancelable, options.view, options.ctrlKey,
options.altKey, options.shiftKey, options.metaKey,
options.keyCode, options.charCode);
},
/**
* Simulates a cut event on a particular element.
*
* @param {HTMLElement}
* target The element to act on.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method cut
* @static
*/
cut:function (target /* :HTMLElement */, options /* :Object */) /* :Void */ {
this.fireKeyEvent("cut", target, options);
},
/**
* Simulates a paste event on a particular element.
*
* @param {HTMLElement}
* target The element to act on.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method paste
* @static
*/
paste:function ( target /* :HTMLElement */, options /* :Object */ ) /* :Void */ {
this.fireKeyEvent( "paste", target, options );
},
/**
* Simulates a keydown event on a particular element.
*
* @param {HTMLElement}
* target The element to act on.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method keydown
* @static
*/
keydown:function (target /* :HTMLElement */, options /* :Object */) /* :Void */ {
this.fireKeyEvent("keydown", target, options);
},
/**
* Simulates a keypress on a particular element.
*
* @param {HTMLElement}
* target The element to act on.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method keypress
* @static
*/
keypress:function (target /* :HTMLElement */, options /* :Object */) /* :Void */ {
this.fireKeyEvent("keypress", target, options);
},
/**
* Simulates a keyup event on a particular element.
*
* @param {HTMLElement}
* target The element to act on.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method keyup
* @static
*/
keyup:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
this.fireKeyEvent("keyup", target, options);
},
/**
* Simulates a compositionstart event on a particular element.
*
* @param {HTMLElement}
* target The element to act on.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method compositionstart
* @static
*/
compositionstart:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
this.fireKeyEvent("compositionstart", target, options);
},
/**
* Simulates a compositionstart event on a particular element.
*
* @param {HTMLElement}
* target The element to act on.
* @param {Object}
* options Additional event options (use DOM standard names).
* @method compositionstart
* @static
*/
compositionend:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
this.fireKeyEvent("compositionend", target, options);
},
/**
* 提供iframe扩展支持,用例测试需要独立场景的用例,由于异步支持,通过finish方法触发start
* <li>事件绑定在frame上,包括afterfinish和jsloaded
*
* @param op.win
* @param op.nojs
* 不加载额外js
* @param op.ontest
* 测试步骤
* @param op.onbeforestart
* 测试启动前处理步骤,默认为QUnit.stop();
* @param op.onafterfinish
* 测试完毕执行步骤,默认为QUnit.start()
*
*/
frameExt:function (op) {
stop();
op = typeof op == 'function' ? {
ontest:op
} : op;
var pw = op.win || window, w, f, url = '', id = typeof op.id == 'undefined' ? 'f'
: op.id, fid = 'iframe#' + id;
op.finish = function () {
pw.$(fid).unbind();
setTimeout(function () {
pw.$('div#div' + id).remove();
start();
}, 20);
};
if (pw.$(fid).length == 0) {
/* 添加frame,部分情况下,iframe没有边框,为了可以看到效果,添加一个带边框的div */
pw.$(pw.document.body).append('<div id="div' + id + '"></div>');
pw.$('div#div' + id).append('<iframe id="' + id + '"></iframe>');
}
op.onafterstart && op.onafterstart($('iframe#f')[0]);
pw.$('script').each(function () {
if (this.src && this.src.indexOf('import.php') >= 0) {
url = this.src.split('import.php')[1];
}
});
pw.$(fid).one('load',
function (e) {
var w = e.target.contentWindow;
var h = setInterval(function () {
if (w.baidu) {// 等待加载完成,IE6下这地方总出问题
clearInterval(h);
op.ontest(w, w.frameElement);
}
}, 20);
// 找到当前操作的iframe,然后call ontest
}).attr('src', cpath + 'frame.php' + url);
},
/**
*
* 判断2个数组是否相等
*
* @static
*/
isEqualArray:function (array1, array2) {
if ('[object Array]' != Object.prototype.toString.call(array1)
|| '[object Array]' != Object.prototype.toString.call(array2))
return (array1 === array2);
else if (array1.length != array2.length)
return false;
else {
for (var i in array1) {
if (array1[i] != array2[i])
return false;
}
return true;
}
},
/***************************************************************************
*
* 通用数据模块
*
* @static
*
**************************************************************************/
commonData:{// 针对测试文件的路径而不是UserAction的路径
"testdir":'../../',
datadir:(function () {
return location.href.split("/_test/")[0] + "/_test/tools/data/";
})(),
currentPath:function () {
var params = location.search.substring(1).split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i];
if (p.split('=')[0] == 'case') {
var casepath = p.split('=')[1].split('.').join('/');
return location.href.split('/_test/')[0] + '/_test/'
+ casepath.substring(0, casepath.lastIndexOf('/'))
+ '/';
}
}
return "";
}
},
importsrc:function (src, callback, matcher, exclude, win) {
win = win || window;
var doc = win.document;
var srcpath = location.href.split("/_test/")[0]
+ "/_test/tools/br/import.php";
var param0 = src;
var ps = {
f:src
};
if (exclude)
ps.e = exclude;
var param1 = exclude || "";
/**
* IE下重复载入会出现无法执行情况
*/
if (win.execScript) {
$.get(srcpath, ps, function (data) {
win.execScript(data);
});
} else {
var head = doc.getElementsByTagName('head')[0];
var sc = doc.createElement('script');
sc.type = 'text/javascript';
sc.src = srcpath + "?f=" + param0 + "&e=" + param1;
head.appendChild(sc);
}
matcher = matcher || src;
var mm = matcher.split(",")[0].split(".");
var h = setInterval(function () {
var p = win;
for (var i = 0; i < mm.length; i++) {
if (typeof (p[mm[i]]) == 'undefined') {
// console.log(mm[i]);
return;
}
p = p[mm[i]];
}
clearInterval(h);
if (callback && 'function' == typeof callback)
callback();
}, 20);
},
/* 用于加载css文件,如果没有加载完毕则不执行回调函数 */
loadcss:function (url, callback, classname, style, value) {
var links = document.getElementsByTagName('link');
for (var link in links) {
if (link.href == url) {
callback();
return;
}
}
var head = document.getElementsByTagName('head')[0];
var link = head.appendChild(document.createElement('link'));
link.setAttribute("rel", "stylesheet");
link.setAttribute("type", "text/css");
link.setAttribute("href", url);
var div = document.body.appendChild(document.createElement("div"));
$(document).ready(
function () {
div.className = classname || 'cssloaded';
var h = setInterval(function () {
if ($(div).css(style || 'width') == value
|| $(div).css(style || 'width') == '20px') {
clearInterval(h);
document.body.removeChild(div);
setTimeout(callback, 20);
}
}, 20);
});
},
/**
* options supported
*/
delayhelper:function (oncheck, onsuccess, onfail, timeout) {
onsuccess = onsuccess || oncheck.onsuccess;
onfail = onfail || oncheck.onfail || function () {
window.QUnit.fail('timeout wait for timeout : ' + timeout + 'ms');
start();
};
timeout = timeout || oncheck.timeout || 10000;
oncheck = (typeof oncheck == 'function') ? oncheck : oncheck.oncheck;
var h1 = setInterval(function () {
if (!oncheck())
return;
else {
clearInterval(h1);
clearTimeout(h2);
typeof onsuccess == "function" && onsuccess();
}
}, 20);
var h2 = setTimeout(function () {
clearInterval(h1);
clearTimeout(h2);
onfail();
}, timeout);
},
browser:(function () {
var win = window;
var numberify = function (s) {
var c = 0;
return parseFloat(s.replace(/\./g, function () {
return (c++ == 1) ? '' : '.';
}));
},
nav = win && win.navigator,
o = {
/**
* Internet Explorer version number or 0. Example: 6
*
* @property ie
* @type float
* @static
*/
ie:0,
/**
* Opera version number or 0. Example: 9.2
*
* @property opera
* @type float
* @static
*/
opera:0,
/**
* Gecko engine revision number. Will evaluate to 1 if Gecko is
* detected but the revision could not be found. Other browsers will
* be 0. Example: 1.8
*
* <pre>
* Firefox 1.0.0.4: 1.7.8 &lt;-- Reports 1.7
* Firefox 1.5.0.9: 1.8.0.9 &lt;-- 1.8
* Firefox 2.0.0.3: 1.8.1.3 &lt;-- 1.81
* Firefox 3.0 &lt;-- 1.9
* Firefox 3.5 &lt;-- 1.91
* </pre>
*
* @property gecko
* @type float
* @static
*/
gecko:0,
/**
* AppleWebKit version. KHTML browsers that are not WebKit browsers
* will evaluate to 1, other browsers 0. Example: 418.9
*
* <pre>
* Safari 1.3.2 (312.6): 312.8.1 &lt;-- Reports 312.8 -- currently the
* latest available for Mac OSX 10.3.
* Safari 2.0.2: 416 &lt;-- hasOwnProperty introduced
* Safari 2.0.4: 418 &lt;-- preventDefault fixed
* Safari 2.0.4 (419.3): 418.9.1 &lt;-- One version of Safari may run
* different versions of webkit
* Safari 2.0.4 (419.3): 419 &lt;-- Tiger installations that have been
* updated, but not updated
* to the latest patch.
* Webkit 212 nightly: 522+ &lt;-- Safari 3.0 precursor (with native SVG
* and many major issues fixed).
* Safari 3.0.4 (523.12) 523.12 &lt;-- First Tiger release - automatic update
* from 2.x via the 10.4.11 OS patch
* Webkit nightly 1/2008:525+ &lt;-- Supports DOMContentLoaded event.
* yahoo.com user agent hack removed.
* </pre>
*
* http://en.wikipedia.org/wiki/Safari_version_history
*
* @property webkit
* @type float
* @static
*/
webkit:0,
/**
* Chrome will be detected as webkit, but this property will also be
* populated with the Chrome version number
*
* @property chrome
* @type float
* @static
*/
chrome:0,
safari:0,
firefox:0,
maxthon:0,
maxthonIE:0,
/**
* The mobile property will be set to a string containing any
* relevant user agent information when a modern mobile browser is
* detected. Currently limited to Safari on the iPhone/iPod Touch,
* Nokia N-series devices with the WebKit-based browser, and Opera
* Mini.
*
* @property mobile
* @type string
* @static
*/
mobile:null,
/**
* Adobe AIR version number or 0. Only populated if webkit is
* detected. Example: 1.0
*
* @property air
* @type float
*/
air:0,
/**
* Google Caja version number or 0.
*
* @property caja
* @type float
*/
caja:nav && nav.cajaVersion,
/**
* Set to true if the pagebreak appears to be in SSL
*
* @property secure
* @type boolean
* @static
*/
secure:false,
/**
* The operating system. Currently only detecting windows or
* macintosh
*
* @property os
* @type string
* @static
*/
os:null
},
ua = nav && nav.userAgent,
loc = win && win.location,
href = loc && loc.href,
m;
o.secure = href && (href.toLowerCase().indexOf("https") === 0);
if (ua) {
if ((/windows|win32/i).test(ua)) {
o.os = 'windows';
} else if ((/macintosh/i).test(ua)) {
o.os = 'macintosh';
} else if ((/rhino/i).test(ua)) {
o.os = 'rhino';
}
// Modern KHTML browsers should qualify as Safari X-Grade
if ((/KHTML/).test(ua)) {
o.webkit = 1;
}
if (window.external && /(\d+\.\d)/.test(external.max_version)) {
o.maxthon = parseFloat(RegExp['\x241']);
if (/MSIE/.test(ua)) {
o.maxthonIE = 1;
o.maxthon = 0;
}
}
// Modern WebKit browsers are at least X-Grade
m = ua.match(/AppleWebKit\/([^\s]*)/);
if (m && m[1]) {
o.webkit = numberify(m[1]);
// Mobile browser check
if (/ Mobile\//.test(ua)) {
o.mobile = "Apple"; // iPhone or iPod Touch
} else {
m = ua.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/);
if (m) {
o.mobile = m[0]; // Nokia N-series, Android, webOS,
// ex:
// NokiaN95
}
}
var m1 = ua.match(/Safari\/([^\s]*)/);
if (m1 && m1[1]) // Safari
o.safari = numberify(m1[1]);
m = ua.match(/Chrome\/([^\s]*)/);
if (o.safari && m && m[1]) {
o.chrome = numberify(m[1]); // Chrome
} else {
m = ua.match(/AdobeAIR\/([^\s]*)/);
if (m) {
o.air = m[0]; // Adobe AIR 1.0 or better
}
}
}
if (!o.webkit) { // not webkit
// @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316;
// fi; U;
// try get firefox and it's ver
// ssr)
m = ua.match(/Opera[\s\/]([^\s]*)/);
if (m && m[1]) {
m = ua.match(/Version[\s\/]([^\s]*)/);
o.opera = numberify(m[1]);
m = ua.match(/Opera Mini[^;]*/);
if (m) {
o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316
}
} else { // not opera or webkit
m = ua.match(/MSIE\s([^;]*)/);
if (m && m[1]) {
o.ie = numberify(m[1]);
} else { // not opera, webkit, or ie
m = ua.match(/Gecko\/([^\s]*)/);
if (m) {
o.gecko = 1; // Gecko detected, look for revision
m = ua.match(/rv:([^\s\)]*)/);
if (m && m[1]) {
o.gecko = numberify(m[1]);
}
}
}
}
}
}
return o;
})
(),
/**
* 提供队列方式执行用例的方案,接口包括start、add、next,方法全部执行完毕时会启动用例继续执行
*/
functionListHelper:function () {
var check = {
list:[],
start:function () {
var self = this;
$(this).bind('next', function () {
setTimeout(function () {// 避免太深的堆栈
if (self.list.length == 0)
start();
else
self.list.shift()();
}, 0);
});
self.next();
},
add:function (func) {
this.list.push(func);
},
next:function (delay) {
var self = this;
if (delay) {
setTimeout(function () {
$(self).trigger('next');
}, delay);
} else
$(this).trigger('next');
}
};
return check;
},
getHTML:function (co) {
var div = document.createElement('div'), h;
if (!co)
return 'null';
div.appendChild(co.cloneNode(true));
h = div.innerHTML.toLowerCase();
h = h.replace(/[\r\n\t\u200b\ufeff]/g, ''); // Remove line feeds and tabs
h = h.replace(/ (\w+)=([^\"][^\s>]*)/gi, ' $1="$2"'); // Restore
// attribs on IE
return h;
},
getChildHTML:function (co) {
var h = co.innerHTML.toLowerCase();
h = h.replace(/[\r\n\t\u200b\ufeff]/g, ''); // Remove line feeds and tabs
h = h.replace(/ (\w+)=([^\"][^\s>]*)/gi, ' $1="$2"'); // Restore attribs on IE
return h.replace(/\u200B/g, '');
},
getIndex:function (node) {
var childNodes = node.parentNode.childNodes, i = 0;
while (childNodes[i] !== node)
i++;
return i;
},
cssStyleToDomStyle:function (cssName) {
var test = document.createElement('div').style,
cssFloat = test.cssFloat != undefined ? 'cssFloat'
: test.styleFloat != undefined ? 'styleFloat'
: 'float',
cache = { 'float':cssFloat };
function replacer(match) {
return match.charAt(1).toUpperCase();
}
// return function( cssName ) {
return cache[cssName] || (cache[cssName] = cssName.replace(/-./g, replacer) );
// };
},
isSameStyle:function (elementA, elementB) {
// var styleA = elementA.style.cssText,
// styleB = elementB.style.cssText;
// if ( this.browser.ie && this.browser.version == 6 ) {
// styleA = styleA.toLowerCase();
// styleB = styleB.toLowerCase();
// }
// if ( !styleA && !styleB ) {
// return true;
// } else if ( !styleA || !styleB ) {
// return false;
// }
// var styleNameMap = {},
// record = [],
// exit = {};
// styleA.replace( /[\w-]+\s*(?=:)/g, function ( name ) {
// styleNameMap[name] = record.push( name );
// } );
// try {
// styleB.replace( /[\w-]+\s*(?=:)/g, function ( name ) {
// var index = styleNameMap[name];
// if ( index ) {
//// name = this.cssStyleToDomStyle( name );
// if ( elementA.style[name] !== elementB.style[name] ) {
// throw exit;
// }
// record[index - 1] = '';
// } else {
// throw exit;
// }
// } );
// } catch ( ex ) {
// if ( ex === exit ) {
// return false;
// }
// }
// return !record.join( '' );
function indexOf(array, item, at) {
for (var i = at || 0, l = array.length; i < l; i++) {
if (array[i] === item) {
return i;
}
}
return -1;
}
var styleA = elementA.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':'),
styleB = elementB.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':');
if (browser.opera) {
styleA = elementA.style;
styleB = elementB.style;
if (styleA.length != styleB.length)
return 0;
for (var p in styleA) {
if (/^(\d+|csstext)$/i.test(p))
continue;
if (styleA[p] != styleB[p])
return 0;
}
return 1;
}
if (!styleA || !styleB) {
return styleA == styleB ? 1 : 0;
}
styleA = styleA.split(';');
styleB = styleB.split(';');
if (styleA.length != styleB.length)
return 0;
for (var j = 0; j < styleB.length; j++) {
if (styleB[j].toLowerCase().indexOf("color") > -1 && styleB[j].toLowerCase().indexOf("rgb") > -1) {
var color = this.formatColor(styleB[j].substr(styleB[j].indexOf("rgb"), styleB[j].length));
styleB[j] = styleB[j].replace(styleB[j].substr(styleB[j].indexOf("rgb"), styleB[j].length), color);
}
}
for (var i = 0, ci; ci = styleA[i++];) {
if (ci.toLowerCase().indexOf("color") > -1 && ci.toLowerCase().indexOf("rgb") > -1) {
var color = this.formatColor(ci.substr(ci.indexOf("rgb"), ci.length));
ci = ci.replace(ci.substr(ci.indexOf("rgb"), ci.length), color);
}
if (indexOf(styleB, ci) == -1) {
return 0;
}//styleA[0].substr(styleA[0].indexOf("rga"),styleA[0].length)
}
return 1;
},
formatColor:function (color) {
var reg1 = /^\#[\da-f]{6}$/i,
reg2 = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/,
keyword = {
black:'#000000',
silver:'#c0c0c0',
gray:'#808080',
white:'#ffffff',
maroon:'#800000',
red:'#ff0000',
purple:'#800080',
fuchsia:'#ff00ff',
green:'#008000',
lime:'#00ff00',
olive:'#808000',
yellow:'#ffff0',
navy:'#000080',
blue:'#0000ff',
teal:'#008080',
aqua:'#00ffff'
};
if (reg1.test(color)) {
// #RRGGBB 直接返回
return color;
} else if (reg2.test(color)) {
// 非IE中的 rgb(0, 0, 0)
for (var s, i = 1, color = "#"; i < 4; i++) {
s = parseInt(RegExp["\x24" + i]).toString(16);
color += ("00" + s).substr(s.length);
}
return color;
} else if (/^\#[\da-f]{3}$/.test(color)) {
// 简写的颜色值: #F00
var s1 = color.charAt(1),
s2 = color.charAt(2),
s3 = color.charAt(3);
return "#" + s1 + s1 + s2 + s2 + s3 + s3;
} else if (keyword[color])
return keyword[color];
return "";
},
hasSameAttrs:function (nodeA, nodeB) {
if (nodeA.tagName != nodeB.tagName)
return 0;
var thisAttribs = nodeA.attributes,
otherAttribs = nodeB.attributes;
if (thisAttribs.length != otherAttribs.length)
return 0;
if (thisAttribs.length == 0)
return 1;
var attrA, attrB;
for (var i = 0; attrA = thisAttribs[i++];) {
if (attrA.nodeName == 'style') {
if (this.isSameStyle(nodeA, nodeB)) {
continue
} else {
return 0;
}
}
if (!ua.browser.ie || attrA.specified) {
attrB = nodeB.attributes[attrA.nodeName];
if (!attrB) {
return 0;
}
}
return 1;
}
return 1;
},
/**
*清除空Text节点
*/
clearWhiteNode:function (node) {
for (var i = 0; i < node.childNodes.length; i++) {
var tmpNode = node.childNodes[i];
if (tmpNode.nodeType == 3 && !tmpNode.length) {
tmpNode.parentNode.removeChild(tmpNode);
i--;
}
}
},
/**
*检查两个节点(包含所有子节点)是否具有相同的属性
*/
flag:true,
checkAllChildAttribs:function (nodeA, nodeB) {
var k = nodeA.childNodes.length;
if (k != nodeB.childNodes.length) {
if (ua.browser.opera) {
this.clearWhiteNode(nodeA);
k = nodeA.childNodes.length;
if (k != nodeB.childNodes.length)
this.flag = false;
}
else
this.flag = false;
}
if (!this.flag)
return this.flag;
while (k) {
var tmpNodeA = nodeA.childNodes[k - 1];
var tmpNodeB = nodeB.childNodes[k - 1];
k--;
if (tmpNodeA.nodeType == 3 || tmpNodeB.nodeType == 3 || tmpNodeA.nodeType == 8 || tmpNodeB.nodeType == 8)
continue;
if (!this.hasSameAttrs(tmpNodeA, tmpNodeB)) {
this.flag = false;
break;
}
this.checkAllChildAttribs(tmpNodeA, tmpNodeB);
}
return this.flag;
},
haveSameAllChildAttribs:function (nodeA, nodeB) {
this.flag = true;
return this.checkAllChildAttribs(nodeA, nodeB);
},
/*查看传入的html是否与传入的元素ele具有相同的style*/
checkHTMLSameStyle:function (html, doc, ele, descript) {
var tagEle = doc.createElement(ele.tagName);
tagEle.innerHTML = html;
/*会有一些不可见字符,在比较前提前删掉*/
this.manualDeleteFillData(ele);
ok(this.haveSameAllChildAttribs(ele, tagEle), descript);
// ok(this.equalsNode(ele.innerHMTL,html),descript);
},
equalsNode:function (na, nb) {
function compare(nodeA, nodeB) {
if (nodeA.nodeType != nodeB.nodeType) {
return 0;
}
if (nodeA.nodeType == 3) {
return nodeA.nodeValue == nodeB.nodeValue
}
if (domUtils.isSameElement(nodeA, nodeB)) {
if (!nodeA.firstChild && !nodeB.firstChild) {
return 1;
}
if (nodeA.firstChild && !nodeB.firstChild || !nodeA.firstChild && nodeB.firstChild) {
return 0
}
for (var i = 0, ai, bi; ai = nodeA.childNodes[i], bi = nodeB.childNodes[i++];) {
if (!compare(ai, bi)) {
return 0
}
}
return 1;
} else {
return 0;
}
}
return compare(domUtils.createElement(document, 'div', {
'innerHTML':na
}), domUtils.createElement(document, 'div', {
'innerHTML':nb
}));
},
getSelectedText:function () {
if (window.getSelection) {
// This technique is the most likely to be standardized.
// getSelection() returns a Selection object, which we do not document.
return window.getSelection().toString();
}
else if (document.getSelection) {
// This is an older, simpler technique that returns a string
return document.getSelection();
}
else if (document.selection) {
// This is the IE-specific technique.
// We do not document the IE selection property or TextRange objects.
return document.selection.createRange().text;
}
},
findPosition:function (oElement) {
var x2 = 0;
var y2 = 0;
var width = oElement.offsetWidth;
var height = oElement.offsetHeight;
if (typeof( oElement.offsetParent ) != 'undefined') {
for (var posX = 0, posY = 0; oElement; oElement = oElement.offsetParent) {
posX += oElement.offsetLeft;
posY += oElement.offsetTop;
}
x2 = posX + width;
y2 = posY + height;
return [ posX, posY , x2, y2];
} else {
x2 = oElement.x + width;
y2 = oElement.y + height;
return [ oElement.x, oElement.y, x2, y2];
}
},
checkElementPath:function (arr1, arr2, descript) {
if (!descript)
descript = '';
var index = arr1.length;
if (index != arr2.length)
ok(false, '路径深度不相同');
else {
while (index > 0)
equal(arr1[--index ], arr2[index ], descript + '---第' + index + '个元素' + arr1[index]);
}
},
getBrowser:function () {
var browser = "";
if (this.browser.ie == 6)
browser = 'ie6';
if (this.browser.ie == 7)
browser = 'ie7';
if (this.browser.ie == 8)
browser = 'ie8';
if (this.browser.ie == 9)
browser = 'ie9';
if (this.browser.safari)
browser = 'safari';
if (this.browser.firefox)
browser = 'firefox';
if (this.browser.chrome)
browser = 'chrome';
if (this.browser.maxthon) {
browser = 'maxthon';
}
if (this.browser.maxthonIE)
browser = 'maxIE';
if (this.browser.opera)
browser = 'opera';
return browser;
},
getFloatStyle:function (ele) {
if (this.browser.ie)
return ele.style['styleFloat'];
else
return ele.style['cssFloat'];
},
getComputedStyle:function(ele ){
if(this.browser.ie&&ua.browser.ie<9){
return ele.currentStyle;
}else{
return window.getComputedStyle(ele);
}
},
readTxt:function (name, f) {
var args = {};
args['name'] = './txt/' + name;
$.ajax({
url:'read.php',
type:'post',
data:args,
success:function (msg) {
f(msg);
},
error:function (xhr, msg) {
f(null);
}
});
}, checkLowerCase:function (stringA, stringB) {
if (!(stringA || stringB))
return true;
else if (!stringA || !stringB)
return false;
else {
return stringA.toLowerCase() == stringB.toLowerCase();
}
}, removeEndSemicolon:function (styleValue) {
if (styleValue.length - 1 == styleValue.lastIndexOf(';'))
styleValue = styleValue.substring(0, styleValue.length - 1);
return styleValue;
},
// checkNodeStyle:function (nodeA, nodeB) {
// var nodeAStyle = this.removeEndSemicolon(nodeA.getAttr("style").replace(/\s+/g, "")).replace(/&quot;/g,'').split(";");
// var nodeBStyle = this.removeEndSemicolon(nodeB.getAttr("style").replace(/\s+/g, "")).replace(/&quot;/g,'').split(";");
// var lengthA = nodeAStyle.length;
// var lengthB = nodeBStyle.length;
// if (!(lengthA && lengthB))
// return true;
// else if (lengthA != lengthB)
// return false;
// else {
// for (var i = 0; i < lengthA; i++) {
// if (nodeAStyle[i].match(/[-\w]+\s*:/) ) {
// var styleName = nodeAStyle[i].match(/[-\w]+\s*:/)[0].replace(/\s*:/, "");
// nodeA.attrs.style = nodeA.attrs.style.replace(/&quot;/g,'');
// nodeB.attrs.style = nodeB.attrs.style.replace(/&quot;/g,'');
// var styleValueA = nodeA.getStyle(styleName).toLowerCase().replace(/\s+/g, "");
// var styleValueB = nodeB.getStyle(styleName).toLowerCase().replace(/\s+/g, "");
// if(/color/.test(styleName)){
// styleValueA = this.formatColor(styleValueA);
// styleValueB = this.formatColor(styleValueB);
// }
// else;
// if (styleValueA != styleValueB)
// return false;
// }
// }
// }
// return true;
// },
getPropertyCount:function (o) {
var n, count = 0;
for (n in o) {
if (o.hasOwnProperty(n)) {
count++;
}
}
return count;
},formHref:function(str){
if(str.lastIndexOf('/')== str.length-1){
str = str.substring(0,str.length-1);
}
return str;
}
// ,
// checkSameNodeAttrs:function (nodeA, nodeB) {
// var lengthA = this.getPropertyCount(nodeA.attrs);
// var lengthB = this.getPropertyCount(nodeB.attrs);
// if (!(lengthA && lengthB))
// return true;
// else if (lengthA != lengthB)
// return false;
// else {
// for (var p in nodeA.attrs) {
// if(!nodeB.getAttr(p)&&!nodeA.getAttr(p))
// return true;
// else if (!nodeB.getAttr(p)||!nodeA.getAttr(p))
// return false;
// else if (p.toLowerCase() == "style") {
// if (!this.checkNodeStyle(nodeA, nodeB))
// return false;
// }
// else if(p.toLowerCase() == "href"){
// if (this.formHref(nodeA.getAttr(p).toLowerCase()) != this.formHref(nodeB.getAttr(p).toLowerCase()))
// return false;
// }
// else {
// if (nodeA.getAttr(p).toLowerCase() != nodeB.getAttr(p).toLowerCase())
// return false;
// }
// }
// }
// return true;
//
// },
// checkChildren:function (nodeA, nodeB) {
// if (!(nodeA.children || nodeB.children))
// return true;
// else if (!(nodeA.children && nodeB.children))
// return false;
// else if (nodeA.children.length != nodeB.children.length)
// return false;
// else {
// var lengthA = nodeA.children.length;
// for (var i = 0; i < lengthA; i++) {
// if (!this.checkSameNode(nodeA.children[i], nodeB.children[i]))
// return false;
// }
// }
// return true;
// }, checkSameNode:function (nodeA, nodeB) {
// if (!this.checkSameNodeAttrs(nodeA, nodeB))
// return false;
// else if (!this.checkChildren(nodeA, nodeB))
// return false;
// else if (nodeA.data != nodeB.data)
// return false;
// else if (!this.checkLowerCase(nodeA.tagName, nodeB.tagName))
// return false;
// else if (!this.checkLowerCase(nodeA.type, nodeB.type))
// return false;
// else
// return true;
// }, checkSameHtml:function (stringA, stringB, scholium) {
// ok(this.checkSameNode(UE.htmlparser(stringA), UE.htmlparser(stringB)), scholium);
// }
};
var ua = UserAction;
var upath = ua.commonData.currentPath();
var cpath = ua.commonData.datadir;
\ No newline at end of file
/**
* Created with JetBrains PhpStorm.
* User: dongyancen
* Date: 13-10-17
* Time: 上午2:24
* To change this template use File | Settings | File Templates.
*/
/**
* rewrite jasmine function to support batchrun
*/
(function() {
if (!jasmine)
return;
var s = jasmine.Queue.prototype.start
// ,n = jasmine.Queue.prototype.next_
,f = jasmine.Runner.prototype.finishCallback;
function _f(self,args /* failures, total */) {
//todo 写到调用HtmlReporter后
/*another way to apply:
*
* var reporterView = self.env.reporter.subReporters_[0].getReporterView();
*totalSpecCount,completeSpecCount,failedCount,passedCount
*reporterView.views.specs[0].detail
* */
var totalSpecCount = 0,failedCount = 0,passedCount = 0;
var tmpSpec = null;
for(var i=0;i<self.suites_.length;i++){
for(var j=0;j<self.suites_[i].specs_.length;j++){
totalSpecCount++;
tmpSpec = self.suites_[i].specs_[j].results_.items_;
for(var k=0;k<tmpSpec.length;k++){
if(tmpSpec[k].passed_){
passedCount++;
}else{
failedCount++;
}
}
}
}
//display failed Suite
// $('li.fail ol').toggle();
if (parent && parent.brtest) {
parent.brtest.customTrigger('done', [ new Date().getTime(), {
failed : failedCount,
passed : passedCount,
detail : self.suites_,
total : totalSpecCount
//todo jscoverage
}, window._$jscoverage || null ]);
}
}
jasmine.Runner.prototype.finishCallback = function(){
f.apply(this, arguments);
_f(this,arguments);
};
jasmine.Queue.prototype.start =function(){
//todo
/* wait for import.php return */
// var h = setInterval(function() {
// if (window && window['baidu']) {
// clearInterval(h);
s.apply(this, arguments);
// }
// }, 20);
};
// function _n(self,args /* failures, total */) {
/* // do not delete the following lines:
// the last Queue in the file has done ,there is no more Suite or Queue
if(self.index == self.blocks.length || (this.abort && !this.ensured[self.index])){
}
*/
// }
// jasmine.Queue.prototype.next_ = function() {
// _n(this,arguments);
// n.apply(this, arguments);
// };
})();
\ No newline at end of file
/**
* Created with JetBrains PhpStorm.
* User: dongyancen
* Date: 13-10-11
* Time: 下午6:52
* To change this template use File | Settings | File Templates.
*/
/**
* 为批量运行提供入口,参数携带batchrun=true
*/
function run(kiss,runnext) {
window.document.title = kiss;
var wb = window.brtest = window.brtest || {};
triggerEvent.call(wb);
wb.timeout = wb.timeout || 60000;
wb.breakOnError = /breakonerror=true/gi.test(location.search)
|| document.getElementById('id_control_breakonerror').checked;
wb.runnext = /batchrun=true/gi.test(location.search) || runnext
|| document.getElementById('id_control_runnext').checked;
wb.kiss = kiss;
var cid = 'id_case_' + kiss.split('.').join('_');
/* 只有参数有showsrconly的时候才显示div */
if (/showsrconly=true/gi.test(location.search)) {
var div = document.getElementById('id_showSrcOnly');
div.style.display = 'block';
}
/* id中由于嵌入用例名称,可能存在导致通过id直接$无法正确获取元素的情况 */
wb.kissnode = document.getElementById(cid);
wb.kisses = wb.kisses || {};
// 把没有用例的情况加入到报告中
if (!wb.kisslost) {
var as = document.getElementById('id_showSrcOnly').getElementsByTagName('a');
for (var i = 0; i < as.length; i++) {
wb.kisses[this.title] = '0;0;_;0;0';
}
wb.kisslost = true;
}
wb.kisscov = wb.kisscov || {};
var wbkiss = wb.kisses[wb.kiss] = wb.kisses[wb.kiss] || '';
/**
* 超时处理
*/
var toh = setTimeout(function () {
if (!window.brtest.breakOnError)
wb.customTrigger('done', [ new Date().getTime(), {
failed: 1,
passed: 1,
detail: null,
total: 0
}, frames[0].$_jscoverage, 'timeout' ]);
}, wb.timeout);
/**
* 为当前用例绑定一个一次性事件
*/
wb.customOne('done', function (time, result, covinfo) {
clearTimeout(toh);
var wb = window.brtest, errornum = result.failed, allnum = result.total;
wb.kissend = new Date().getTime();
//todo jscoverage
// if ( covinfo !== null )// 如果支持覆盖率
// {
// wb.kisscov[wb.kiss] = covinfo;
// }
removeClass(wb.kissnode, 'running_case');
wb.kisses[wb.kiss] = errornum + ';' + allnum + ';_;' + wb.kissstart + ';' + wb.kissend;
//todo log
// var args = kiss + ': 失败/所有:' + errornum + '/' + allnum + ',耗时:' + (wb.kissend - wb.kissstart);
// var html = upath + '../br/log.php?loginfo=' + args;
// html += '&detail='+result.detail;
// if ( errornum > 0 )
// html += '&fail=true';
if (errornum > 0) {
addClass(wb.kissnode, 'fail_case');
} else
addClass(wb.kissnode, 'pass_case');
if (wb.runnext && (!wb.breakOnError || parseInt(wb.kisses[wb.kiss].split(',')[0]) == 0)) {
var nextA = wb.kissnode.nextSibling;
if (nextA.tagName.toLowerCase() == 'a') {
if (wb.kisses[nextA.title] === undefined) {
run(nextA.title, wb.runnext);
}
// html += "&next=" + nextA.title;
} else {
//
// /* ending 提交数据到后台 */
// html += '&next=@_@end';
wb.kisses['config'] = location.search.substring(1);
var url = 'report.php';
//todo jscoverage
// covcalc();
// /**
// * 启动时间,结束时间,校验点失败数,校验点总数
// */
$.ajax({
url: url,
type: 'post',
data: wb.kisses,
success: function (msg) {
/* 展示报告区 */
// $('#id_reportarea').show().html(msg);
},
error: function (xhr, msg) {
alert('fail' + msg);
}
});
}
}
//todo log
// te.log( html );
});
/**
* 初始化执行区并通过嵌入iframe启动用例执行
*/
var url = 'run.php?case=' + kiss + '&time=' + new Date().getTime() + "&"
+ location.search.substring(1);
var fdiv = 'id_div_frame_' + kiss.split('.').join('_');
var fid = 'id_frame_' + kiss.split('.').join('_');
addClass(wb.kissnode, 'running_case');
/* 隐藏报告区 */
// $( 'div#id_reportarea' ).empty().hide();
/* 展示执行区 */
var runningarea = document.getElementById('id_runningarea');
empty(runningarea).style.display = 'block';
var iframe = document.createElement('iframe');
iframe.id = fid;
iframe.src = url;
addClass(iframe, "runningframe");
runningarea.appendChild(iframe);
wb.kissstart = new Date().getTime();
};
// 需要根据一次批量执行整合所有文件的覆盖率情况
//function covcalc() {
// function covmerge(cc, covinfo) {
// for (var key in covinfo) {//key :每个文件
// for (var idx in covinfo[key]) {
// if (idx != 'source') {
//
// cc[key] = cc[key] || [];
// cc[key][idx] = (cc[key][idx] || 0) + covinfo[key][idx];
// }
// }
// }
// return cc;
// }
//
// var cc = {};
// var brkisses = window.brtest.kisses;
// for (var key in window.brtest.kisscov) {
// covmerge(cc, window.brtest.kisscov[key]);//key:每个用例
//// brkisses[kiss]= brkisses[kiss] + ',' + key;
// }
// var file;
// var files = [];
// var filter = '';
// var ls = location.search.split('&');
// for (var i = 0; i < ls.length; i++) {
// if (ls[i].indexOf('filter') != -1) {
// filter = ls[i].split('=')[1];
// }
//
// }
// for (file in cc) {
// if (!cc.hasOwnProperty(file)) {
// continue;
// }
// if (file.indexOf(filter) != -1)
// files.push(file);
// }
// files.sort();
// for (var f = 0; f < files.length; f++) {
// file = files[f];
// var lineNumber;
// var num_statements = 0;
// var num_executed = 0;
// var missing = [];
// var fileCC = cc[file];
// var length = fileCC.length;
// var currentConditionalEnd = 0;
// var conditionals = null;
//
// if (fileCC.conditionals) {
// conditionals = fileCC.conditionals;
// }
// var recordCovForBrowser = null;//
// for (lineNumber = 0; lineNumber < length; lineNumber++) {
// var n = fileCC[lineNumber];
//
// if (lineNumber === currentConditionalEnd) {
// currentConditionalEnd = 0;
// } else if (currentConditionalEnd === 0 && conditionals
// && conditionals[lineNumber]) {
// currentConditionalEnd = conditionals[lineNumber];
// }
//
// if (currentConditionalEnd !== 0) {
// (recordCovForBrowser == null) ? (recordCovForBrowser = '2') : (recordCovForBrowser += ',2');
//
// continue;
// }
//
// if (n === undefined || n === null) {
// (recordCovForBrowser == null) ? (recordCovForBrowser = '2') : (recordCovForBrowser += ',2');
// continue;
// }
//
// if (n === 0) {
// (recordCovForBrowser == null) ? (recordCovForBrowser = '0') : (recordCovForBrowser += ',0');
// missing.push(lineNumber);
// } else {
// (recordCovForBrowser == null) ? (recordCovForBrowser = '1') : (recordCovForBrowser += ',1');
// num_executed++;
// }
// num_statements++;
// }
//
// var percentage = (num_statements === 0 ? 0 : ( 100 * num_executed / num_statements ).toFixed(1));
// var kiss = file.replace('.js', '');
// // 统计所有用例的覆盖率信息和测试结果
//
// if (brkisses[kiss] == undefined)
// brkisses[kiss] = '0;0;_;0;0';
// var info = brkisses[kiss].split(';_;');// 覆盖率的处理在最后环节加入到用例的测试结果中
// brkisses[kiss] = info[0] + ';' + percentage + ';' + info[1] + ';' + recordCovForBrowser;
// }
//}
window.onload =
function () {
if (location.href.search("[?&,]batchrun=true") > 0
|| document.getElementById('id_control_runnext').checked) {
run(document.getElementById('id_testlist').getElementsByTagName('a')[0].getAttribute('title'), true);
}
};
\ No newline at end of file
/**
* Created with JetBrains PhpStorm.
* User: dongyancen
* Date: 13-10-11
* Time: 下午5:34
* To change this template use File | Settings | File Templates.
*/
function hasClass(obj, cls) {
return obj.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
}
function addClass(ele,cls) {
if (!this.hasClass(ele,cls)) ele.className += " "+cls;
}
function removeClass(obj, cls) {
var clsArray= cls.split( " ");
for(var i=0;i<clsArray.length;i++){
if (hasClass(obj, clsArray[i])) {
var reg = new RegExp('(\\s|^)' + clsArray[i] + '(\\s|$)');
obj.className = obj.className.replace(reg, ' ');
}
}
}
function empty(obj){
var childs = obj.childNodes;
if(childs.length==0)return obj;
for(var i=0;i<childs.length;i++){
obj.removeChild(childs[i]);
}
return obj
}
function slideToggle(obj){
//todo 先写个大意,待完成
if(obj.style.display=='none'){
}else{
obj.style.display='block';
}
}
var triggerEvent = function () {
this.listeners = [];
this.customOne = function (type, listener) {
this.listeners[type] = listener;
for (var i = 0, l = this.listeners.length; i < l; i++) {
if (this.listeners[i] === this.listeners[type]) {
this.listeners.splice(i, 1);
i--;
}
}
},
this.customTrigger = function (type,arguments) {
if(this.listeners[type]){
return this.listeners[type].apply(this, arguments);
}
return false;
}
};
@echo off
jscoverage.exe --encoding=UTF-8 ../../../src ../../coverage
\ No newline at end of file
Copyright (c) 2008-2011 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
jasmine.HtmlReporterHelpers = {};
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
var el = document.createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
if (child) {
el.appendChild(child);
}
}
}
for (var attr in attrs) {
if (attr == "className") {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
};
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
var results = child.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.skipped) {
status = 'skipped';
}
return status;
};
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
var parentDiv = this.dom.summary;
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
var parent = child[parentSuite];
if (parent) {
if (typeof this.views.suites[parent.id] == 'undefined') {
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
}
parentDiv = this.views.suites[parent.id].element;
}
parentDiv.appendChild(childElement);
};
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
for(var fn in jasmine.HtmlReporterHelpers) {
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
}
};
jasmine.HtmlReporter = function(_doc) {
var self = this;
var doc = _doc || window.document;
var reporterView;
var dom = {};
// Jasmine Reporter Public Interface
self.logRunningSpecs = false;
self.reportRunnerStarting = function(runner) {
var specs = runner.specs() || [];
if (specs.length == 0) {
return;
}
createReporterDom(runner.env.versionString());
doc.body.appendChild(dom.reporter);
setExceptionHandling();
reporterView = new jasmine.HtmlReporter.ReporterView(dom);
reporterView.addSpecs(specs, self.specFilter);
};
self.reportRunnerResults = function(runner) {
reporterView && reporterView.complete();
};
self.reportSuiteResults = function(suite) {
reporterView.suiteComplete(suite);
};
self.reportSpecStarting = function(spec) {
if (self.logRunningSpecs) {
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
}
};
self.reportSpecResults = function(spec) {
reporterView.specComplete(spec);
};
self.log = function() {
var console = jasmine.getGlobal().console;
if (console && console.log) {
if (console.log.apply) {
console.log.apply(console, arguments);
} else {
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
}
}
};
self.specFilter = function(spec) {
if (!focusedSpecName()) {
return true;
}
return spec.getFullName().indexOf(focusedSpecName()) === 0;
};
/*
* add by dongyancen for rewrite finishCallback in ext_jasmine to record the runner result
* 10/17/2013
* */
// self.getReporterView = function(){
// return reporterView ;
// };
return self;
function focusedSpecName() {
var specName;
(function memoizeFocusedSpec() {
if (specName) {
return;
}
var paramMap = [];
var params = jasmine.HtmlReporter.parameters(doc);
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
}
specName = paramMap.spec;
})();
return specName;
}
function createReporterDom(version) {
dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
dom.banner = self.createDom('div', { className: 'banner' },
self.createDom('span', { className: 'title' }, "Jasmine "),
self.createDom('span', { className: 'version' }, version)),
dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
dom.alert = self.createDom('div', {className: 'alert'},
self.createDom('span', { className: 'exceptions' },
self.createDom('label', { className: 'label', for: 'no_try_catch' }, 'No try/catch'),
self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))),
dom.results = self.createDom('div', {className: 'results'},
dom.summary = self.createDom('div', { className: 'summary' }),
dom.details = self.createDom('div', { id: 'details' }))
);
}
function noTryCatch() {
return window.location.search.match(/catch=false/);
}
function searchWithCatch() {
var params = jasmine.HtmlReporter.parameters(window.document);
var removed = false;
var i = 0;
while (!removed && i < params.length) {
if (params[i].match(/catch=/)) {
params.splice(i, 1);
removed = true;
}
i++;
}
if (jasmine.CATCH_EXCEPTIONS) {
params.push("catch=false");
}
return params.join("&");
}
function setExceptionHandling() {
var chxCatch = document.getElementById('no_try_catch');
if (noTryCatch()) {
chxCatch.setAttribute('checked', true);
jasmine.CATCH_EXCEPTIONS = false;
}
chxCatch.onclick = function() {
window.location.search = searchWithCatch();
};
}
};
jasmine.HtmlReporter.parameters = function(doc) {
var paramStr = doc.location.search.substring(1);
var params = [];
if (paramStr.length > 0) {
params = paramStr.split('&');
}
return params;
}
jasmine.HtmlReporter.sectionLink = function(sectionName) {
var link = '?';
var params = [];
if (sectionName) {
params.push('spec=' + encodeURIComponent(sectionName));
}
if (!jasmine.CATCH_EXCEPTIONS) {
params.push("catch=false");
}
if (params.length > 0) {
link += params.join("&");
}
return link;
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);
jasmine.HtmlReporter.ReporterView = function(dom) {
this.startedAt = new Date();
this.runningSpecCount = 0;
this.completeSpecCount = 0;
this.passedCount = 0;
this.failedCount = 0;
this.skippedCount = 0;
this.createResultsMenu = function() {
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
' | ',
this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
this.summaryMenuItem.onclick = function() {
dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
};
this.detailsMenuItem.onclick = function() {
showDetails();
};
};
this.addSpecs = function(specs, specFilter) {
this.totalSpecCount = specs.length;
this.views = {
specs: {},
suites: {}
};
for (var i = 0; i < specs.length; i++) {
var spec = specs[i];
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
if (specFilter(spec)) {
this.runningSpecCount++;
}
}
};
this.specComplete = function(spec) {
this.completeSpecCount++;
if (isUndefined(this.views.specs[spec.id])) {
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
}
var specView = this.views.specs[spec.id];
switch (specView.status()) {
case 'passed':
this.passedCount++;
break;
case 'failed':
this.failedCount++;
break;
case 'skipped':
this.skippedCount++;
break;
}
specView.refresh();
this.refresh();
};
this.suiteComplete = function(suite) {
var suiteView = this.views.suites[suite.id];
if (isUndefined(suiteView)) {
return;
}
suiteView.refresh();
};
this.refresh = function() {
if (isUndefined(this.resultsMenu)) {
this.createResultsMenu();
}
// currently running UI
if (isUndefined(this.runningAlert)) {
this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" });
dom.alert.appendChild(this.runningAlert);
}
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
// skipped specs UI
if (isUndefined(this.skippedAlert)) {
this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" });
}
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
if (this.skippedCount === 1 && isDefined(dom.alert)) {
dom.alert.appendChild(this.skippedAlert);
}
// passing specs UI
if (isUndefined(this.passedAlert)) {
this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" });
}
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
// failing specs UI
if (isUndefined(this.failedAlert)) {
this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
}
this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
if (this.failedCount === 1 && isDefined(dom.alert)) {
dom.alert.appendChild(this.failedAlert);
dom.alert.appendChild(this.resultsMenu);
}
// summary info
this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
};
this.complete = function() {
dom.alert.removeChild(this.runningAlert);
this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
if (this.failedCount === 0) {
dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
} else {
showDetails();
}
dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
};
return this;
function showDetails() {
if (dom.reporter.className.search(/showDetails/) === -1) {
dom.reporter.className += " showDetails";
}
}
function isUndefined(obj) {
return typeof obj === 'undefined';
}
function isDefined(obj) {
return !isUndefined(obj);
}
function specPluralizedFor(count) {
var str = count + " spec";
if (count > 1) {
str += "s"
}
return str;
}
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
this.spec = spec;
this.dom = dom;
this.views = views;
this.symbol = this.createDom('li', { className: 'pending' });
this.dom.symbolSummary.appendChild(this.symbol);
this.summary = this.createDom('div', { className: 'specSummary' },
this.createDom('a', {
className: 'description',
href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()),
title: this.spec.getFullName()
}, this.spec.description)
);
this.detail = this.createDom('div', { className: 'specDetail' },
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
title: this.spec.getFullName()
}, this.spec.getFullName())
);
};
jasmine.HtmlReporter.SpecView.prototype.status = function() {
return this.getSpecStatus(this.spec);
};
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
this.symbol.className = this.status();
switch (this.status()) {
case 'skipped':
break;
case 'passed':
this.appendSummaryToSuiteDiv();
break;
case 'failed':
this.appendSummaryToSuiteDiv();
this.appendFailureDetail();
break;
}
};
jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
this.summary.className += ' ' + this.status();
this.appendToSummary(this.spec, this.summary);
};
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
this.detail.className += ' ' + this.status();
var resultItems = this.spec.results().getItems();
var messagesDiv = this.createDom('div', { className: 'messages' });
for (var i = 0; i < resultItems.length; i++) {
var result = resultItems[i];
if (result.type == 'log') {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
} else if (result.type == 'expect' && result.passed && !result.passed()) {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
if (result.trace.stack) {
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
}
}
}
if (messagesDiv.childNodes.length > 0) {
this.detail.appendChild(messagesDiv);
this.dom.details.appendChild(this.detail);
}
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
this.suite = suite;
this.dom = dom;
this.views = views;
this.element = this.createDom('div', { className: 'suite' },
this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description)
);
this.appendToSummary(this.suite, this.element);
};
jasmine.HtmlReporter.SuiteView.prototype.status = function() {
return this.getSpecStatus(this.suite);
};
jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
this.element.className += " " + this.status();
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
/* @deprecated Use jasmine.HtmlReporter instead
*/
jasmine.TrivialReporter = function(doc) {
this.document = doc || document;
this.suiteDivs = {};
this.logRunningSpecs = false;
};
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
var el = document.createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
if (child) { el.appendChild(child); }
}
}
for (var attr in attrs) {
if (attr == "className") {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
};
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
var showPassed, showSkipped;
this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
this.createDom('div', { className: 'banner' },
this.createDom('div', { className: 'logo' },
this.createDom('span', { className: 'title' }, "Jasmine"),
this.createDom('span', { className: 'version' }, runner.env.versionString())),
this.createDom('div', { className: 'options' },
"Show ",
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
)
),
this.runnerDiv = this.createDom('div', { className: 'runner running' },
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
);
this.document.body.appendChild(this.outerDiv);
var suites = runner.suites();
for (var i = 0; i < suites.length; i++) {
var suite = suites[i];
var suiteDiv = this.createDom('div', { className: 'suite' },
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
this.suiteDivs[suite.id] = suiteDiv;
var parentDiv = this.outerDiv;
if (suite.parentSuite) {
parentDiv = this.suiteDivs[suite.parentSuite.id];
}
parentDiv.appendChild(suiteDiv);
}
this.startedAt = new Date();
var self = this;
showPassed.onclick = function(evt) {
if (showPassed.checked) {
self.outerDiv.className += ' show-passed';
} else {
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
}
};
showSkipped.onclick = function(evt) {
if (showSkipped.checked) {
self.outerDiv.className += ' show-skipped';
} else {
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
}
};
};
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
var results = runner.results();
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
this.runnerDiv.setAttribute("class", className);
//do it twice for IE
this.runnerDiv.setAttribute("className", className);
var specs = runner.specs();
var specCount = 0;
for (var i = 0; i < specs.length; i++) {
if (this.specFilter(specs[i])) {
specCount++;
}
}
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
};
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
var results = suite.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.totalCount === 0) { // todo: change this to check results.skipped
status = 'skipped';
}
this.suiteDivs[suite.id].className += " " + status;
};
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
if (this.logRunningSpecs) {
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
}
};
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
var results = spec.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.skipped) {
status = 'skipped';
}
var specDiv = this.createDom('div', { className: 'spec ' + status },
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(spec.getFullName()),
title: spec.getFullName()
}, spec.description));
var resultItems = results.getItems();
var messagesDiv = this.createDom('div', { className: 'messages' });
for (var i = 0; i < resultItems.length; i++) {
var result = resultItems[i];
if (result.type == 'log') {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
} else if (result.type == 'expect' && result.passed && !result.passed()) {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
if (result.trace.stack) {
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
}
}
}
if (messagesDiv.childNodes.length > 0) {
specDiv.appendChild(messagesDiv);
}
this.suiteDivs[spec.suite.id].appendChild(specDiv);
};
jasmine.TrivialReporter.prototype.log = function() {
var console = jasmine.getGlobal().console;
if (console && console.log) {
if (console.log.apply) {
console.log.apply(console, arguments);
} else {
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
}
}
};
jasmine.TrivialReporter.prototype.getLocation = function() {
return this.document.location;
};
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
var paramMap = {};
var params = this.getLocation().search.substring(1).split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
}
if (!paramMap.spec) {
return true;
}
return spec.getFullName().indexOf(paramMap.spec) === 0;
};
body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
#HTMLReporter a { text-decoration: none; }
#HTMLReporter a:hover { text-decoration: underline; }
#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
#HTMLReporter .version { color: #aaaaaa; }
#HTMLReporter .banner { margin-top: 14px; }
#HTMLReporter .duration { color: #aaaaaa; float: right; }
#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
#HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
#HTMLReporter .runningAlert { background-color: #666666; }
#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
#HTMLReporter .passingAlert { background-color: #a6b779; }
#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
#HTMLReporter .failingAlert { background-color: #cf867e; }
#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
#HTMLReporter .results { margin-top: 14px; }
#HTMLReporter #details { display: none; }
#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
#HTMLReporter.showDetails .summary { display: none; }
#HTMLReporter.showDetails #details { display: block; }
#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
#HTMLReporter .summary { margin-top: 14px; }
#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
#HTMLReporter .description + .suite { margin-top: 0; }
#HTMLReporter .suite { margin-top: 14px; }
#HTMLReporter .suite a { color: #333333; }
#HTMLReporter #details .specDetail { margin-bottom: 28px; }
#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
#HTMLReporter .resultMessage span.result { display: block; }
#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
#TrivialReporter .runner.running { background-color: yellow; }
#TrivialReporter .options { text-align: right; font-size: .8em; }
#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
#TrivialReporter .suite .suite { margin: 5px; }
#TrivialReporter .suite.passed { background-color: #dfd; }
#TrivialReporter .suite.failed { background-color: #fdd; }
#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
#TrivialReporter .spec.skipped { background-color: #bbb; }
#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
#TrivialReporter .passed { background-color: #cfc; display: none; }
#TrivialReporter .failed { background-color: #fbb; }
#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
#TrivialReporter .resultMessage .mismatch { color: black; }
#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }
var isCommonJS = typeof window == "undefined" && typeof exports == "object";
/**
* Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
*
* @namespace
*/
var jasmine = {};
if (isCommonJS) exports.jasmine = jasmine;
/**
* @private
*/
jasmine.unimplementedMethod_ = function() {
throw new Error("unimplemented method");
};
/**
* Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just
* a plain old variable and may be redefined by somebody else.
*
* @private
*/
jasmine.undefined = jasmine.___undefined___;
/**
* Show diagnostic messages in the console if set to true
*
*/
jasmine.VERBOSE = false;
/**
* Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
*
*/
jasmine.DEFAULT_UPDATE_INTERVAL = 250;
/**
* Maximum levels of nesting that will be included when an object is pretty-printed
*/
jasmine.MAX_PRETTY_PRINT_DEPTH = 40;
/**
* Default timeout interval in milliseconds for waitsFor() blocks.
*/
jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
/**
* By default exceptions thrown in the context of a test are caught by jasmine so that it can run the remaining tests in the suite.
* Set to false to let the exception bubble up in the browser.
*
*/
jasmine.CATCH_EXCEPTIONS = true;
jasmine.getGlobal = function() {
function getGlobal() {
return this;
}
return getGlobal();
};
/**
* Allows for bound functions to be compared. Internal use only.
*
* @ignore
* @private
* @param base {Object} bound 'this' for the function
* @param name {Function} function to find
*/
jasmine.bindOriginal_ = function(base, name) {
var original = base[name];
if (original.apply) {
return function() {
return original.apply(base, arguments);
};
} else {
// IE support
return jasmine.getGlobal()[name];
}
};
jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
jasmine.MessageResult = function(values) {
this.type = 'log';
this.values = values;
this.trace = new Error(); // todo: test better
};
jasmine.MessageResult.prototype.toString = function() {
var text = "";
for (var i = 0; i < this.values.length; i++) {
if (i > 0) text += " ";
if (jasmine.isString_(this.values[i])) {
text += this.values[i];
} else {
text += jasmine.pp(this.values[i]);
}
}
return text;
};
jasmine.ExpectationResult = function(params) {
this.type = 'expect';
this.matcherName = params.matcherName;
this.passed_ = params.passed;
this.expected = params.expected;
this.actual = params.actual;
this.message = this.passed_ ? 'Passed.' : params.message;
var trace = (params.trace || new Error(this.message));
this.trace = this.passed_ ? '' : trace;
};
jasmine.ExpectationResult.prototype.toString = function () {
return this.message;
};
jasmine.ExpectationResult.prototype.passed = function () {
return this.passed_;
};
/**
* Getter for the Jasmine environment. Ensures one gets created
*/
jasmine.getEnv = function() {
var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
return env;
};
/**
* @ignore
* @private
* @param value
* @returns {Boolean}
*/
jasmine.isArray_ = function(value) {
return jasmine.isA_("Array", value);
};
/**
* @ignore
* @private
* @param value
* @returns {Boolean}
*/
jasmine.isString_ = function(value) {
return jasmine.isA_("String", value);
};
/**
* @ignore
* @private
* @param value
* @returns {Boolean}
*/
jasmine.isNumber_ = function(value) {
return jasmine.isA_("Number", value);
};
/**
* @ignore
* @private
* @param {String} typeName
* @param value
* @returns {Boolean}
*/
jasmine.isA_ = function(typeName, value) {
return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
};
/**
* Pretty printer for expecations. Takes any object and turns it into a human-readable string.
*
* @param value {Object} an object to be outputted
* @returns {String}
*/
jasmine.pp = function(value) {
var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
stringPrettyPrinter.format(value);
return stringPrettyPrinter.string;
};
/**
* Returns true if the object is a DOM Node.
*
* @param {Object} obj object to check
* @returns {Boolean}
*/
jasmine.isDomNode = function(obj) {
return obj.nodeType > 0;
};
/**
* Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter.
*
* @example
* // don't care about which function is passed in, as long as it's a function
* expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
*
* @param {Class} clazz
* @returns matchable object of the type clazz
*/
jasmine.any = function(clazz) {
return new jasmine.Matchers.Any(clazz);
};
/**
* Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the
* attributes on the object.
*
* @example
* // don't care about any other attributes than foo.
* expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"});
*
* @param sample {Object} sample
* @returns matchable object for the sample
*/
jasmine.objectContaining = function (sample) {
return new jasmine.Matchers.ObjectContaining(sample);
};
/**
* Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
*
* Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine
* expectation syntax. Spies can be checked if they were called or not and what the calling params were.
*
* A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
*
* Spies are torn down at the end of every spec.
*
* Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
*
* @example
* // a stub
* var myStub = jasmine.createSpy('myStub'); // can be used anywhere
*
* // spy example
* var foo = {
* not: function(bool) { return !bool; }
* }
*
* // actual foo.not will not be called, execution stops
* spyOn(foo, 'not');
// foo.not spied upon, execution will continue to implementation
* spyOn(foo, 'not').andCallThrough();
*
* // fake example
* var foo = {
* not: function(bool) { return !bool; }
* }
*
* // foo.not(val) will return val
* spyOn(foo, 'not').andCallFake(function(value) {return value;});
*
* // mock example
* foo.not(7 == 7);
* expect(foo.not).toHaveBeenCalled();
* expect(foo.not).toHaveBeenCalledWith(true);
*
* @constructor
* @see spyOn, jasmine.createSpy, jasmine.createSpyObj
* @param {String} name
*/
jasmine.Spy = function(name) {
/**
* The name of the spy, if provided.
*/
this.identity = name || 'unknown';
/**
* Is this Object a spy?
*/
this.isSpy = true;
/**
* The actual function this spy stubs.
*/
this.plan = function() {
};
/**
* Tracking of the most recent call to the spy.
* @example
* var mySpy = jasmine.createSpy('foo');
* mySpy(1, 2);
* mySpy.mostRecentCall.args = [1, 2];
*/
this.mostRecentCall = {};
/**
* Holds arguments for each call to the spy, indexed by call count
* @example
* var mySpy = jasmine.createSpy('foo');
* mySpy(1, 2);
* mySpy(7, 8);
* mySpy.mostRecentCall.args = [7, 8];
* mySpy.argsForCall[0] = [1, 2];
* mySpy.argsForCall[1] = [7, 8];
*/
this.argsForCall = [];
this.calls = [];
};
/**
* Tells a spy to call through to the actual implemenatation.
*
* @example
* var foo = {
* bar: function() { // do some stuff }
* }
*
* // defining a spy on an existing property: foo.bar
* spyOn(foo, 'bar').andCallThrough();
*/
jasmine.Spy.prototype.andCallThrough = function() {
this.plan = this.originalValue;
return this;
};
/**
* For setting the return value of a spy.
*
* @example
* // defining a spy from scratch: foo() returns 'baz'
* var foo = jasmine.createSpy('spy on foo').andReturn('baz');
*
* // defining a spy on an existing property: foo.bar() returns 'baz'
* spyOn(foo, 'bar').andReturn('baz');
*
* @param {Object} value
*/
jasmine.Spy.prototype.andReturn = function(value) {
this.plan = function() {
return value;
};
return this;
};
/**
* For throwing an exception when a spy is called.
*
* @example
* // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
* var foo = jasmine.createSpy('spy on foo').andThrow('baz');
*
* // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
* spyOn(foo, 'bar').andThrow('baz');
*
* @param {String} exceptionMsg
*/
jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
this.plan = function() {
throw exceptionMsg;
};
return this;
};
/**
* Calls an alternate implementation when a spy is called.
*
* @example
* var baz = function() {
* // do some stuff, return something
* }
* // defining a spy from scratch: foo() calls the function baz
* var foo = jasmine.createSpy('spy on foo').andCall(baz);
*
* // defining a spy on an existing property: foo.bar() calls an anonymnous function
* spyOn(foo, 'bar').andCall(function() { return 'baz';} );
*
* @param {Function} fakeFunc
*/
jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
this.plan = fakeFunc;
return this;
};
/**
* Resets all of a spy's the tracking variables so that it can be used again.
*
* @example
* spyOn(foo, 'bar');
*
* foo.bar();
*
* expect(foo.bar.callCount).toEqual(1);
*
* foo.bar.reset();
*
* expect(foo.bar.callCount).toEqual(0);
*/
jasmine.Spy.prototype.reset = function() {
this.wasCalled = false;
this.callCount = 0;
this.argsForCall = [];
this.calls = [];
this.mostRecentCall = {};
};
jasmine.createSpy = function(name) {
var spyObj = function() {
spyObj.wasCalled = true;
spyObj.callCount++;
var args = jasmine.util.argsToArray(arguments);
spyObj.mostRecentCall.object = this;
spyObj.mostRecentCall.args = args;
spyObj.argsForCall.push(args);
spyObj.calls.push({object: this, args: args});
return spyObj.plan.apply(this, arguments);
};
var spy = new jasmine.Spy(name);
for (var prop in spy) {
spyObj[prop] = spy[prop];
}
spyObj.reset();
return spyObj;
};
/**
* Determines whether an object is a spy.
*
* @param {jasmine.Spy|Object} putativeSpy
* @returns {Boolean}
*/
jasmine.isSpy = function(putativeSpy) {
return putativeSpy && putativeSpy.isSpy;
};
/**
* Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something
* large in one call.
*
* @param {String} baseName name of spy class
* @param {Array} methodNames array of names of methods to make spies
*/
jasmine.createSpyObj = function(baseName, methodNames) {
if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {
throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
}
var obj = {};
for (var i = 0; i < methodNames.length; i++) {
obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
}
return obj;
};
/**
* All parameters are pretty-printed and concatenated together, then written to the current spec's output.
*
* Be careful not to leave calls to <code>jasmine.log</code> in production code.
*/
jasmine.log = function() {
var spec = jasmine.getEnv().currentSpec;
spec.log.apply(spec, arguments);
};
/**
* Function that installs a spy on an existing object's method name. Used within a Spec to create a spy.
*
* @example
* // spy example
* var foo = {
* not: function(bool) { return !bool; }
* }
* spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
*
* @see jasmine.createSpy
* @param obj
* @param methodName
* @return {jasmine.Spy} a Jasmine spy that can be chained with all spy methods
*/
var spyOn = function(obj, methodName) {
return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
};
if (isCommonJS) exports.spyOn = spyOn;
/**
* Creates a Jasmine spec that will be added to the current suite.
*
* // TODO: pending tests
*
* @example
* it('should be true', function() {
* expect(true).toEqual(true);
* });
*
* @param {String} desc description of this specification
* @param {Function} func defines the preconditions and expectations of the spec
*/
var it = function(desc, func) {
return jasmine.getEnv().it(desc, func);
};
if (isCommonJS) exports.it = it;
/**
* Creates a <em>disabled</em> Jasmine spec.
*
* A convenience method that allows existing specs to be disabled temporarily during development.
*
* @param {String} desc description of this specification
* @param {Function} func defines the preconditions and expectations of the spec
*/
var xit = function(desc, func) {
return jasmine.getEnv().xit(desc, func);
};
if (isCommonJS) exports.xit = xit;
/**
* Starts a chain for a Jasmine expectation.
*
* It is passed an Object that is the actual value and should chain to one of the many
* jasmine.Matchers functions.
*
* @param {Object} actual Actual value to test against and expected value
* @return {jasmine.Matchers}
*/
var expect = function(actual) {
return jasmine.getEnv().currentSpec.expect(actual);
};
if (isCommonJS) exports.expect = expect;
/**
* Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.
*
* @param {Function} func Function that defines part of a jasmine spec.
*/
var runs = function(func) {
jasmine.getEnv().currentSpec.runs(func);
};
if (isCommonJS) exports.runs = runs;
/**
* Waits a fixed time period before moving to the next block.
*
* @deprecated Use waitsFor() instead
* @param {Number} timeout milliseconds to wait
*/
var waits = function(timeout) {
jasmine.getEnv().currentSpec.waits(timeout);
};
if (isCommonJS) exports.waits = waits;
/**
* Waits for the latchFunction to return true before proceeding to the next block.
*
* @param {Function} latchFunction
* @param {String} optional_timeoutMessage
* @param {Number} optional_timeout
*/
var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
};
if (isCommonJS) exports.waitsFor = waitsFor;
/**
* A function that is called before each spec in a suite.
*
* Used for spec setup, including validating assumptions.
*
* @param {Function} beforeEachFunction
*/
var beforeEach = function(beforeEachFunction) {
jasmine.getEnv().beforeEach(beforeEachFunction);
};
if (isCommonJS) exports.beforeEach = beforeEach;
/**
* A function that is called after each spec in a suite.
*
* Used for restoring any state that is hijacked during spec execution.
*
* @param {Function} afterEachFunction
*/
var afterEach = function(afterEachFunction) {
jasmine.getEnv().afterEach(afterEachFunction);
};
if (isCommonJS) exports.afterEach = afterEach;
/**
* Defines a suite of specifications.
*
* Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
* are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
* of setup in some tests.
*
* @example
* // TODO: a simple suite
*
* // TODO: a simple suite with a nested describe block
*
* @param {String} description A string, usually the class under test.
* @param {Function} specDefinitions function that defines several specs.
*/
var describe = function(description, specDefinitions) {
return jasmine.getEnv().describe(description, specDefinitions);
};
if (isCommonJS) exports.describe = describe;
/**
* Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.
*
* @param {String} description A string, usually the class under test.
* @param {Function} specDefinitions function that defines several specs.
*/
var xdescribe = function(description, specDefinitions) {
return jasmine.getEnv().xdescribe(description, specDefinitions);
};
if (isCommonJS) exports.xdescribe = xdescribe;
// Provide the XMLHttpRequest class for IE 5.x-6.x:
jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
function tryIt(f) {
try {
return f();
} catch(e) {
}
return null;
}
var xhr = tryIt(function() {
return new ActiveXObject("Msxml2.XMLHTTP.6.0");
}) ||
tryIt(function() {
return new ActiveXObject("Msxml2.XMLHTTP.3.0");
}) ||
tryIt(function() {
return new ActiveXObject("Msxml2.XMLHTTP");
}) ||
tryIt(function() {
return new ActiveXObject("Microsoft.XMLHTTP");
});
if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");
return xhr;
} : XMLHttpRequest;
/**
* @namespace
*/
jasmine.util = {};
/**
* Declare that a child class inherit it's prototype from the parent class.
*
* @private
* @param {Function} childClass
* @param {Function} parentClass
*/
jasmine.util.inherit = function(childClass, parentClass) {
/**
* @private
*/
var subclass = function() {
};
subclass.prototype = parentClass.prototype;
childClass.prototype = new subclass();
};
jasmine.util.formatException = function(e) {
var lineNumber;
if (e.line) {
lineNumber = e.line;
}
else if (e.lineNumber) {
lineNumber = e.lineNumber;
}
var file;
if (e.sourceURL) {
file = e.sourceURL;
}
else if (e.fileName) {
file = e.fileName;
}
var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
if (file && lineNumber) {
message += ' in ' + file + ' (line ' + lineNumber + ')';
}
return message;
};
jasmine.util.htmlEscape = function(str) {
if (!str) return str;
return str.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
};
jasmine.util.argsToArray = function(args) {
var arrayOfArgs = [];
for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
return arrayOfArgs;
};
jasmine.util.extend = function(destination, source) {
for (var property in source) destination[property] = source[property];
return destination;
};
/**
* Environment for Jasmine
*
* @constructor
*/
jasmine.Env = function() {
this.currentSpec = null;
this.currentSuite = null;
this.currentRunner_ = new jasmine.Runner(this);
this.reporter = new jasmine.MultiReporter();
this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
this.lastUpdate = 0;
this.specFilter = function() {
return true;
};
this.nextSpecId_ = 0;
this.nextSuiteId_ = 0;
this.equalityTesters_ = [];
// wrap matchers
this.matchersClass = function() {
jasmine.Matchers.apply(this, arguments);
};
jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
};
jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
jasmine.Env.prototype.setInterval = jasmine.setInterval;
jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
/**
* @returns an object containing jasmine version build info, if set.
*/
jasmine.Env.prototype.version = function () {
if (jasmine.version_) {
return jasmine.version_;
} else {
throw new Error('Version not set');
}
};
/**
* @returns string containing jasmine version build info, if set.
*/
jasmine.Env.prototype.versionString = function() {
if (!jasmine.version_) {
return "version unknown";
}
var version = this.version();
var versionString = version.major + "." + version.minor + "." + version.build;
if (version.release_candidate) {
versionString += ".rc" + version.release_candidate;
}
versionString += " revision " + version.revision;
return versionString;
};
/**
* @returns a sequential integer starting at 0
*/
jasmine.Env.prototype.nextSpecId = function () {
return this.nextSpecId_++;
};
/**
* @returns a sequential integer starting at 0
*/
jasmine.Env.prototype.nextSuiteId = function () {
return this.nextSuiteId_++;
};
/**
* Register a reporter to receive status updates from Jasmine.
* @param {jasmine.Reporter} reporter An object which will receive status updates.
*/
jasmine.Env.prototype.addReporter = function(reporter) {
this.reporter.addReporter(reporter);
};
jasmine.Env.prototype.execute = function() {
this.currentRunner_.execute();
};
jasmine.Env.prototype.describe = function(description, specDefinitions) {
var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
var parentSuite = this.currentSuite;
if (parentSuite) {
parentSuite.add(suite);
} else {
this.currentRunner_.add(suite);
}
this.currentSuite = suite;
var declarationError = null;
try {
specDefinitions.call(suite);
} catch(e) {
declarationError = e;
}
if (declarationError) {
this.it("encountered a declaration exception", function() {
throw declarationError;
});
}
this.currentSuite = parentSuite;
return suite;
};
jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
if (this.currentSuite) {
this.currentSuite.beforeEach(beforeEachFunction);
} else {
this.currentRunner_.beforeEach(beforeEachFunction);
}
};
jasmine.Env.prototype.currentRunner = function () {
return this.currentRunner_;
};
jasmine.Env.prototype.afterEach = function(afterEachFunction) {
if (this.currentSuite) {
this.currentSuite.afterEach(afterEachFunction);
} else {
this.currentRunner_.afterEach(afterEachFunction);
}
};
jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
return {
execute: function() {
}
};
};
jasmine.Env.prototype.it = function(description, func) {
var spec = new jasmine.Spec(this, this.currentSuite, description);
this.currentSuite.add(spec);
this.currentSpec = spec;
if (func) {
spec.runs(func);
}
return spec;
};
jasmine.Env.prototype.xit = function(desc, func) {
return {
id: this.nextSpecId(),
runs: function() {
}
};
};
jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) {
if (a.source != b.source)
mismatchValues.push("expected pattern /" + b.source + "/ is not equal to the pattern /" + a.source + "/");
if (a.ignoreCase != b.ignoreCase)
mismatchValues.push("expected modifier i was" + (b.ignoreCase ? " " : " not ") + "set and does not equal the origin modifier");
if (a.global != b.global)
mismatchValues.push("expected modifier g was" + (b.global ? " " : " not ") + "set and does not equal the origin modifier");
if (a.multiline != b.multiline)
mismatchValues.push("expected modifier m was" + (b.multiline ? " " : " not ") + "set and does not equal the origin modifier");
if (a.sticky != b.sticky)
mismatchValues.push("expected modifier y was" + (b.sticky ? " " : " not ") + "set and does not equal the origin modifier");
return (mismatchValues.length === 0);
};
jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
return true;
}
a.__Jasmine_been_here_before__ = b;
b.__Jasmine_been_here_before__ = a;
var hasKey = function(obj, keyName) {
return obj !== null && obj[keyName] !== jasmine.undefined;
};
for (var property in b) {
if (!hasKey(a, property) && hasKey(b, property)) {
mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
}
}
for (property in a) {
if (!hasKey(b, property) && hasKey(a, property)) {
mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
}
}
for (property in b) {
if (property == '__Jasmine_been_here_before__') continue;
if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
}
}
if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
mismatchValues.push("arrays were not the same length");
}
delete a.__Jasmine_been_here_before__;
delete b.__Jasmine_been_here_before__;
return (mismatchKeys.length === 0 && mismatchValues.length === 0);
};
jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
mismatchKeys = mismatchKeys || [];
mismatchValues = mismatchValues || [];
for (var i = 0; i < this.equalityTesters_.length; i++) {
var equalityTester = this.equalityTesters_[i];
var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
if (result !== jasmine.undefined) return result;
}
if (a === b) return true;
if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
return (a == jasmine.undefined && b == jasmine.undefined);
}
if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
return a === b;
}
if (a instanceof Date && b instanceof Date) {
return a.getTime() == b.getTime();
}
if (a.jasmineMatches) {
return a.jasmineMatches(b);
}
if (b.jasmineMatches) {
return b.jasmineMatches(a);
}
if (a instanceof jasmine.Matchers.ObjectContaining) {
return a.matches(b);
}
if (b instanceof jasmine.Matchers.ObjectContaining) {
return b.matches(a);
}
if (jasmine.isString_(a) && jasmine.isString_(b)) {
return (a == b);
}
if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
return (a == b);
}
if (a instanceof RegExp && b instanceof RegExp) {
return this.compareRegExps_(a, b, mismatchKeys, mismatchValues);
}
if (typeof a === "object" && typeof b === "object") {
return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
}
//Straight check
return (a === b);
};
jasmine.Env.prototype.contains_ = function(haystack, needle) {
if (jasmine.isArray_(haystack)) {
for (var i = 0; i < haystack.length; i++) {
if (this.equals_(haystack[i], needle)) return true;
}
return false;
}
return haystack.indexOf(needle) >= 0;
};
jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
this.equalityTesters_.push(equalityTester);
};
/** No-op base class for Jasmine reporters.
*
* @constructor
*/
jasmine.Reporter = function() {
};
//noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
};
//noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
};
//noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
};
//noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
};
//noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.reportSpecResults = function(spec) {
};
//noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.log = function(str) {
};
/**
* Blocks are functions with executable code that make up a spec.
*
* @constructor
* @param {jasmine.Env} env
* @param {Function} func
* @param {jasmine.Spec} spec
*/
jasmine.Block = function(env, func, spec) {
this.env = env;
this.func = func;
this.spec = spec;
};
jasmine.Block.prototype.execute = function(onComplete) {
if (!jasmine.CATCH_EXCEPTIONS) {
this.func.apply(this.spec);
}
else {
try {
this.func.apply(this.spec);
} catch (e) {
this.spec.fail(e);
}
}
onComplete();
};
/** JavaScript API reporter.
*
* @constructor
*/
jasmine.JsApiReporter = function() {
this.started = false;
this.finished = false;
this.suites_ = [];
this.results_ = {};
};
jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
this.started = true;
var suites = runner.topLevelSuites();
for (var i = 0; i < suites.length; i++) {
var suite = suites[i];
this.suites_.push(this.summarize_(suite));
}
};
jasmine.JsApiReporter.prototype.suites = function() {
return this.suites_;
};
jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
var isSuite = suiteOrSpec instanceof jasmine.Suite;
var summary = {
id: suiteOrSpec.id,
name: suiteOrSpec.description,
type: isSuite ? 'suite' : 'spec',
children: []
};
if (isSuite) {
var children = suiteOrSpec.children();
for (var i = 0; i < children.length; i++) {
summary.children.push(this.summarize_(children[i]));
}
}
return summary;
};
jasmine.JsApiReporter.prototype.results = function() {
return this.results_;
};
jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
return this.results_[specId];
};
//noinspection JSUnusedLocalSymbols
jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
this.finished = true;
};
//noinspection JSUnusedLocalSymbols
jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
};
//noinspection JSUnusedLocalSymbols
jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
this.results_[spec.id] = {
messages: spec.results().getItems(),
result: spec.results().failedCount > 0 ? "failed" : "passed"
};
};
//noinspection JSUnusedLocalSymbols
jasmine.JsApiReporter.prototype.log = function(str) {
};
jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
var results = {};
for (var i = 0; i < specIds.length; i++) {
var specId = specIds[i];
results[specId] = this.summarizeResult_(this.results_[specId]);
}
return results;
};
jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
var summaryMessages = [];
var messagesLength = result.messages.length;
for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
var resultMessage = result.messages[messageIndex];
summaryMessages.push({
text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
passed: resultMessage.passed ? resultMessage.passed() : true,
type: resultMessage.type,
message: resultMessage.message,
trace: {
stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
}
});
}
return {
result : result.result,
messages : summaryMessages
};
};
/**
* @constructor
* @param {jasmine.Env} env
* @param actual
* @param {jasmine.Spec} spec
*/
jasmine.Matchers = function(env, actual, spec, opt_isNot) {
this.env = env;
this.actual = actual;
this.spec = spec;
this.isNot = opt_isNot || false;
this.reportWasCalled_ = false;
};
// todo: @deprecated as of Jasmine 0.11, remove soon [xw]
jasmine.Matchers.pp = function(str) {
throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
};
// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
jasmine.Matchers.prototype.report = function(result, failing_message, details) {
throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
};
jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
for (var methodName in prototype) {
if (methodName == 'report') continue;
var orig = prototype[methodName];
matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
}
};
jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
return function() {
var matcherArgs = jasmine.util.argsToArray(arguments);
var result = matcherFunction.apply(this, arguments);
if (this.isNot) {
result = !result;
}
if (this.reportWasCalled_) return result;
var message;
if (!result) {
if (this.message) {
message = this.message.apply(this, arguments);
if (jasmine.isArray_(message)) {
message = message[this.isNot ? 1 : 0];
}
} else {
var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
if (matcherArgs.length > 0) {
for (var i = 0; i < matcherArgs.length; i++) {
if (i > 0) message += ",";
message += " " + jasmine.pp(matcherArgs[i]);
}
}
message += ".";
}
}
var expectationResult = new jasmine.ExpectationResult({
matcherName: matcherName,
passed: result,
expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
actual: this.actual,
message: message
});
this.spec.addMatcherResult(expectationResult);
return jasmine.undefined;
};
};
/**
* toBe: compares the actual to the expected using ===
* @param expected
*/
jasmine.Matchers.prototype.toBe = function(expected) {
return this.actual === expected;
};
/**
* toNotBe: compares the actual to the expected using !==
* @param expected
* @deprecated as of 1.0. Use not.toBe() instead.
*/
jasmine.Matchers.prototype.toNotBe = function(expected) {
return this.actual !== expected;
};
/**
* toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
*
* @param expected
*/
jasmine.Matchers.prototype.toEqual = function(expected) {
return this.env.equals_(this.actual, expected);
};
/**
* toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
* @param expected
* @deprecated as of 1.0. Use not.toEqual() instead.
*/
jasmine.Matchers.prototype.toNotEqual = function(expected) {
return !this.env.equals_(this.actual, expected);
};
/**
* Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes
* a pattern or a String.
*
* @param expected
*/
jasmine.Matchers.prototype.toMatch = function(expected) {
return new RegExp(expected).test(this.actual);
};
/**
* Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
* @param expected
* @deprecated as of 1.0. Use not.toMatch() instead.
*/
jasmine.Matchers.prototype.toNotMatch = function(expected) {
return !(new RegExp(expected).test(this.actual));
};
/**
* Matcher that compares the actual to jasmine.undefined.
*/
jasmine.Matchers.prototype.toBeDefined = function() {
return (this.actual !== jasmine.undefined);
};
/**
* Matcher that compares the actual to jasmine.undefined.
*/
jasmine.Matchers.prototype.toBeUndefined = function() {
return (this.actual === jasmine.undefined);
};
/**
* Matcher that compares the actual to null.
*/
jasmine.Matchers.prototype.toBeNull = function() {
return (this.actual === null);
};
/**
* Matcher that compares the actual to NaN.
*/
jasmine.Matchers.prototype.toBeNaN = function() {
this.message = function() {
return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ];
};
return (this.actual !== this.actual);
};
/**
* Matcher that boolean not-nots the actual.
*/
jasmine.Matchers.prototype.toBeTruthy = function() {
return !!this.actual;
};
/**
* Matcher that boolean nots the actual.
*/
jasmine.Matchers.prototype.toBeFalsy = function() {
return !this.actual;
};
/**
* Matcher that checks to see if the actual, a Jasmine spy, was called.
*/
jasmine.Matchers.prototype.toHaveBeenCalled = function() {
if (arguments.length > 0) {
throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
}
if (!jasmine.isSpy(this.actual)) {
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
}
this.message = function() {
return [
"Expected spy " + this.actual.identity + " to have been called.",
"Expected spy " + this.actual.identity + " not to have been called."
];
};
return this.actual.wasCalled;
};
/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
/**
* Matcher that checks to see if the actual, a Jasmine spy, was not called.
*
* @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
*/
jasmine.Matchers.prototype.wasNotCalled = function() {
if (arguments.length > 0) {
throw new Error('wasNotCalled does not take arguments');
}
if (!jasmine.isSpy(this.actual)) {
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
}
this.message = function() {
return [
"Expected spy " + this.actual.identity + " to not have been called.",
"Expected spy " + this.actual.identity + " to have been called."
];
};
return !this.actual.wasCalled;
};
/**
* Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
*
* @example
*
*/
jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {
var expectedArgs = jasmine.util.argsToArray(arguments);
if (!jasmine.isSpy(this.actual)) {
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
}
this.message = function() {
var invertedMessage = "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was.";
var positiveMessage = "";
if (this.actual.callCount === 0) {
positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.";
} else {
positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but actual calls were " + jasmine.pp(this.actual.argsForCall).replace(/^\[ | \]$/g, '')
}
return [positiveMessage, invertedMessage];
};
return this.env.contains_(this.actual.argsForCall, expectedArgs);
};
/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
jasmine.Matchers.prototype.wasNotCalledWith = function() {
var expectedArgs = jasmine.util.argsToArray(arguments);
if (!jasmine.isSpy(this.actual)) {
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
}
this.message = function() {
return [
"Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
"Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
];
};
return !this.env.contains_(this.actual.argsForCall, expectedArgs);
};
/**
* Matcher that checks that the expected item is an element in the actual Array.
*
* @param {Object} expected
*/
jasmine.Matchers.prototype.toContain = function(expected) {
return this.env.contains_(this.actual, expected);
};
/**
* Matcher that checks that the expected item is NOT an element in the actual Array.
*
* @param {Object} expected
* @deprecated as of 1.0. Use not.toContain() instead.
*/
jasmine.Matchers.prototype.toNotContain = function(expected) {
return !this.env.contains_(this.actual, expected);
};
jasmine.Matchers.prototype.toBeLessThan = function(expected) {
return this.actual < expected;
};
jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
return this.actual > expected;
};
/**
* Matcher that checks that the expected item is equal to the actual item
* up to a given level of decimal precision (default 2).
*
* @param {Number} expected
* @param {Number} precision, as number of decimal places
*/
jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {
if (!(precision === 0)) {
precision = precision || 2;
}
return Math.abs(expected - this.actual) < (Math.pow(10, -precision) / 2);
};
/**
* Matcher that checks that the expected exception was thrown by the actual.
*
* @param {String} [expected]
*/
jasmine.Matchers.prototype.toThrow = function(expected) {
var result = false;
var exception;
if (typeof this.actual != 'function') {
throw new Error('Actual is not a function');
}
try {
this.actual();
} catch (e) {
exception = e;
}
if (exception) {
result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
}
var not = this.isNot ? "not " : "";
this.message = function() {
if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');
} else {
return "Expected function to throw an exception.";
}
};
return result;
};
jasmine.Matchers.Any = function(expectedClass) {
this.expectedClass = expectedClass;
};
jasmine.Matchers.Any.prototype.jasmineMatches = function(other) {
if (this.expectedClass == String) {
return typeof other == 'string' || other instanceof String;
}
if (this.expectedClass == Number) {
return typeof other == 'number' || other instanceof Number;
}
if (this.expectedClass == Function) {
return typeof other == 'function' || other instanceof Function;
}
if (this.expectedClass == Object) {
return typeof other == 'object';
}
return other instanceof this.expectedClass;
};
jasmine.Matchers.Any.prototype.jasmineToString = function() {
return '<jasmine.any(' + this.expectedClass + ')>';
};
jasmine.Matchers.ObjectContaining = function (sample) {
this.sample = sample;
};
jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {
mismatchKeys = mismatchKeys || [];
mismatchValues = mismatchValues || [];
var env = jasmine.getEnv();
var hasKey = function(obj, keyName) {
return obj != null && obj[keyName] !== jasmine.undefined;
};
for (var property in this.sample) {
if (!hasKey(other, property) && hasKey(this.sample, property)) {
mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
}
else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {
mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual.");
}
}
return (mismatchKeys.length === 0 && mismatchValues.length === 0);
};
jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () {
return "<jasmine.objectContaining(" + jasmine.pp(this.sample) + ")>";
};
// Mock setTimeout, clearTimeout
// Contributed by Pivotal Computer Systems, www.pivotalsf.com
jasmine.FakeTimer = function() {
this.reset();
var self = this;
self.setTimeout = function(funcToCall, millis) {
self.timeoutsMade++;
self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
return self.timeoutsMade;
};
self.setInterval = function(funcToCall, millis) {
self.timeoutsMade++;
self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
return self.timeoutsMade;
};
self.clearTimeout = function(timeoutKey) {
self.scheduledFunctions[timeoutKey] = jasmine.undefined;
};
self.clearInterval = function(timeoutKey) {
self.scheduledFunctions[timeoutKey] = jasmine.undefined;
};
};
jasmine.FakeTimer.prototype.reset = function() {
this.timeoutsMade = 0;
this.scheduledFunctions = {};
this.nowMillis = 0;
};
jasmine.FakeTimer.prototype.tick = function(millis) {
var oldMillis = this.nowMillis;
var newMillis = oldMillis + millis;
this.runFunctionsWithinRange(oldMillis, newMillis);
this.nowMillis = newMillis;
};
jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
var scheduledFunc;
var funcsToRun = [];
for (var timeoutKey in this.scheduledFunctions) {
scheduledFunc = this.scheduledFunctions[timeoutKey];
if (scheduledFunc != jasmine.undefined &&
scheduledFunc.runAtMillis >= oldMillis &&
scheduledFunc.runAtMillis <= nowMillis) {
funcsToRun.push(scheduledFunc);
this.scheduledFunctions[timeoutKey] = jasmine.undefined;
}
}
if (funcsToRun.length > 0) {
funcsToRun.sort(function(a, b) {
return a.runAtMillis - b.runAtMillis;
});
for (var i = 0; i < funcsToRun.length; ++i) {
try {
var funcToRun = funcsToRun[i];
this.nowMillis = funcToRun.runAtMillis;
funcToRun.funcToCall();
if (funcToRun.recurring) {
this.scheduleFunction(funcToRun.timeoutKey,
funcToRun.funcToCall,
funcToRun.millis,
true);
}
} catch(e) {
}
}
this.runFunctionsWithinRange(oldMillis, nowMillis);
}
};
jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
this.scheduledFunctions[timeoutKey] = {
runAtMillis: this.nowMillis + millis,
funcToCall: funcToCall,
recurring: recurring,
timeoutKey: timeoutKey,
millis: millis
};
};
/**
* @namespace
*/
jasmine.Clock = {
defaultFakeTimer: new jasmine.FakeTimer(),
reset: function() {
jasmine.Clock.assertInstalled();
jasmine.Clock.defaultFakeTimer.reset();
},
tick: function(millis) {
jasmine.Clock.assertInstalled();
jasmine.Clock.defaultFakeTimer.tick(millis);
},
runFunctionsWithinRange: function(oldMillis, nowMillis) {
jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
},
scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
},
useMock: function() {
if (!jasmine.Clock.isInstalled()) {
var spec = jasmine.getEnv().currentSpec;
spec.after(jasmine.Clock.uninstallMock);
jasmine.Clock.installMock();
}
},
installMock: function() {
jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
},
uninstallMock: function() {
jasmine.Clock.assertInstalled();
jasmine.Clock.installed = jasmine.Clock.real;
},
real: {
setTimeout: jasmine.getGlobal().setTimeout,
clearTimeout: jasmine.getGlobal().clearTimeout,
setInterval: jasmine.getGlobal().setInterval,
clearInterval: jasmine.getGlobal().clearInterval
},
assertInstalled: function() {
if (!jasmine.Clock.isInstalled()) {
throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
}
},
isInstalled: function() {
return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
},
installed: null
};
jasmine.Clock.installed = jasmine.Clock.real;
//else for IE support
jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
if (jasmine.Clock.installed.setTimeout.apply) {
return jasmine.Clock.installed.setTimeout.apply(this, arguments);
} else {
return jasmine.Clock.installed.setTimeout(funcToCall, millis);
}
};
jasmine.getGlobal().setInterval = function(funcToCall, millis) {
if (jasmine.Clock.installed.setInterval.apply) {
return jasmine.Clock.installed.setInterval.apply(this, arguments);
} else {
return jasmine.Clock.installed.setInterval(funcToCall, millis);
}
};
jasmine.getGlobal().clearTimeout = function(timeoutKey) {
if (jasmine.Clock.installed.clearTimeout.apply) {
return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
} else {
return jasmine.Clock.installed.clearTimeout(timeoutKey);
}
};
jasmine.getGlobal().clearInterval = function(timeoutKey) {
if (jasmine.Clock.installed.clearTimeout.apply) {
return jasmine.Clock.installed.clearInterval.apply(this, arguments);
} else {
return jasmine.Clock.installed.clearInterval(timeoutKey);
}
};
/**
* @constructor
*/
jasmine.MultiReporter = function() {
this.subReporters_ = [];
};
jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
jasmine.MultiReporter.prototype.addReporter = function(reporter) {
this.subReporters_.push(reporter);
};
(function() {
var functionNames = [
"reportRunnerStarting",
"reportRunnerResults",
"reportSuiteResults",
"reportSpecStarting",
"reportSpecResults",
"log"
];
for (var i = 0; i < functionNames.length; i++) {
var functionName = functionNames[i];
jasmine.MultiReporter.prototype[functionName] = (function(functionName) {
return function() {
for (var j = 0; j < this.subReporters_.length; j++) {
var subReporter = this.subReporters_[j];
if (subReporter[functionName]) {
subReporter[functionName].apply(subReporter, arguments);
}
}
};
})(functionName);
}
})();
/**
* Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
*
* @constructor
*/
jasmine.NestedResults = function() {
/**
* The total count of results
*/
this.totalCount = 0;
/**
* Number of passed results
*/
this.passedCount = 0;
/**
* Number of failed results
*/
this.failedCount = 0;
/**
* Was this suite/spec skipped?
*/
this.skipped = false;
/**
* @ignore
*/
this.items_ = [];
};
/**
* Roll up the result counts.
*
* @param result
*/
jasmine.NestedResults.prototype.rollupCounts = function(result) {
this.totalCount += result.totalCount;
this.passedCount += result.passedCount;
this.failedCount += result.failedCount;
};
/**
* Adds a log message.
* @param values Array of message parts which will be concatenated later.
*/
jasmine.NestedResults.prototype.log = function(values) {
this.items_.push(new jasmine.MessageResult(values));
};
/**
* Getter for the results: message & results.
*/
jasmine.NestedResults.prototype.getItems = function() {
return this.items_;
};
/**
* Adds a result, tracking counts (total, passed, & failed)
* @param {jasmine.ExpectationResult|jasmine.NestedResults} result
*/
jasmine.NestedResults.prototype.addResult = function(result) {
if (result.type != 'log') {
if (result.items_) {
this.rollupCounts(result);
} else {
this.totalCount++;
if (result.passed()) {
this.passedCount++;
} else {
this.failedCount++;
}
}
}
this.items_.push(result);
};
/**
* @returns {Boolean} True if <b>everything</b> below passed
*/
jasmine.NestedResults.prototype.passed = function() {
return this.passedCount === this.totalCount;
};
/**
* Base class for pretty printing for expectation results.
*/
jasmine.PrettyPrinter = function() {
this.ppNestLevel_ = 0;
};
/**
* Formats a value in a nice, human-readable string.
*
* @param value
*/
jasmine.PrettyPrinter.prototype.format = function(value) {
this.ppNestLevel_++;
try {
if (value === jasmine.undefined) {
this.emitScalar('undefined');
} else if (value === null) {
this.emitScalar('null');
} else if (value === jasmine.getGlobal()) {
this.emitScalar('<global>');
} else if (value.jasmineToString) {
this.emitScalar(value.jasmineToString());
} else if (typeof value === 'string') {
this.emitString(value);
} else if (jasmine.isSpy(value)) {
this.emitScalar("spy on " + value.identity);
} else if (value instanceof RegExp) {
this.emitScalar(value.toString());
} else if (typeof value === 'function') {
this.emitScalar('Function');
} else if (typeof value.nodeType === 'number') {
this.emitScalar('HTMLNode');
} else if (value instanceof Date) {
this.emitScalar('Date(' + value + ')');
} else if (value.__Jasmine_been_here_before__) {
this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');
} else if (jasmine.isArray_(value) || typeof value == 'object') {
value.__Jasmine_been_here_before__ = true;
if (jasmine.isArray_(value)) {
this.emitArray(value);
} else {
this.emitObject(value);
}
delete value.__Jasmine_been_here_before__;
} else {
this.emitScalar(value.toString());
}
} finally {
this.ppNestLevel_--;
}
};
jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {
for (var property in obj) {
if (!obj.hasOwnProperty(property)) continue;
if (property == '__Jasmine_been_here_before__') continue;
fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined &&
obj.__lookupGetter__(property) !== null) : false);
}
};
jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
jasmine.StringPrettyPrinter = function() {
jasmine.PrettyPrinter.call(this);
this.string = '';
};
jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {
this.append(value);
};
jasmine.StringPrettyPrinter.prototype.emitString = function(value) {
this.append("'" + value + "'");
};
jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {
if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) {
this.append("Array");
return;
}
this.append('[ ');
for (var i = 0; i < array.length; i++) {
if (i > 0) {
this.append(', ');
}
this.format(array[i]);
}
this.append(' ]');
};
jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {
if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) {
this.append("Object");
return;
}
var self = this;
this.append('{ ');
var first = true;
this.iterateObject(obj, function(property, isGetter) {
if (first) {
first = false;
} else {
self.append(', ');
}
self.append(property);
self.append(' : ');
if (isGetter) {
self.append('<getter>');
} else {
self.format(obj[property]);
}
});
this.append(' }');
};
jasmine.StringPrettyPrinter.prototype.append = function(value) {
this.string += value;
};
jasmine.Queue = function(env) {
this.env = env;
// parallel to blocks. each true value in this array means the block will
// get executed even if we abort
this.ensured = [];
this.blocks = [];
this.running = false;
this.index = 0;
this.offset = 0;
this.abort = false;
};
jasmine.Queue.prototype.addBefore = function(block, ensure) {
if (ensure === jasmine.undefined) {
ensure = false;
}
this.blocks.unshift(block);
this.ensured.unshift(ensure);
};
jasmine.Queue.prototype.add = function(block, ensure) {
if (ensure === jasmine.undefined) {
ensure = false;
}
this.blocks.push(block);
this.ensured.push(ensure);
};
jasmine.Queue.prototype.insertNext = function(block, ensure) {
if (ensure === jasmine.undefined) {
ensure = false;
}
this.ensured.splice((this.index + this.offset + 1), 0, ensure);
this.blocks.splice((this.index + this.offset + 1), 0, block);
this.offset++;
};
jasmine.Queue.prototype.start = function(onComplete) {
this.running = true;
this.onComplete = onComplete;
this.next_();
};
jasmine.Queue.prototype.isRunning = function() {
return this.running;
};
jasmine.Queue.LOOP_DONT_RECURSE = true;
jasmine.Queue.prototype.next_ = function() {
var self = this;
var goAgain = true;
while (goAgain) {
goAgain = false;
if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) {
var calledSynchronously = true;
var completedSynchronously = false;
var onComplete = function () {
if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
completedSynchronously = true;
return;
}
if (self.blocks[self.index].abort) {
self.abort = true;
}
self.offset = 0;
self.index++;
var now = new Date().getTime();
if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
self.env.lastUpdate = now;
self.env.setTimeout(function() {
self.next_();
}, 0);
} else {
if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
goAgain = true;
} else {
self.next_();
}
}
};
self.blocks[self.index].execute(onComplete);
calledSynchronously = false;
if (completedSynchronously) {
onComplete();
}
} else {
self.running = false;
if (self.onComplete) {
self.onComplete();
}
}
}
};
jasmine.Queue.prototype.results = function() {
var results = new jasmine.NestedResults();
for (var i = 0; i < this.blocks.length; i++) {
if (this.blocks[i].results) {
results.addResult(this.blocks[i].results());
}
}
return results;
};
/**
* Runner
*
* @constructor
* @param {jasmine.Env} env
*/
jasmine.Runner = function(env) {
var self = this;
self.env = env;
self.queue = new jasmine.Queue(env);
self.before_ = [];
self.after_ = [];
self.suites_ = [];
};
jasmine.Runner.prototype.execute = function() {
var self = this;
if (self.env.reporter.reportRunnerStarting) {
self.env.reporter.reportRunnerStarting(this);
}
self.queue.start(function () {
self.finishCallback();
});
};
jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {
beforeEachFunction.typeName = 'beforeEach';
this.before_.splice(0,0,beforeEachFunction);
};
jasmine.Runner.prototype.afterEach = function(afterEachFunction) {
afterEachFunction.typeName = 'afterEach';
this.after_.splice(0,0,afterEachFunction);
};
jasmine.Runner.prototype.finishCallback = function() {
this.env.reporter.reportRunnerResults(this);
};
jasmine.Runner.prototype.addSuite = function(suite) {
this.suites_.push(suite);
};
jasmine.Runner.prototype.add = function(block) {
if (block instanceof jasmine.Suite) {
this.addSuite(block);
}
this.queue.add(block);
};
jasmine.Runner.prototype.specs = function () {
var suites = this.suites();
var specs = [];
for (var i = 0; i < suites.length; i++) {
specs = specs.concat(suites[i].specs());
}
return specs;
};
jasmine.Runner.prototype.suites = function() {
return this.suites_;
};
jasmine.Runner.prototype.topLevelSuites = function() {
var topLevelSuites = [];
for (var i = 0; i < this.suites_.length; i++) {
if (!this.suites_[i].parentSuite) {
topLevelSuites.push(this.suites_[i]);
}
}
return topLevelSuites;
};
jasmine.Runner.prototype.results = function() {
return this.queue.results();
};
/**
* Internal representation of a Jasmine specification, or test.
*
* @constructor
* @param {jasmine.Env} env
* @param {jasmine.Suite} suite
* @param {String} description
*/
jasmine.Spec = function(env, suite, description) {
if (!env) {
throw new Error('jasmine.Env() required');
}
if (!suite) {
throw new Error('jasmine.Suite() required');
}
var spec = this;
spec.id = env.nextSpecId ? env.nextSpecId() : null;
spec.env = env;
spec.suite = suite;
spec.description = description;
spec.queue = new jasmine.Queue(env);
spec.afterCallbacks = [];
spec.spies_ = [];
spec.results_ = new jasmine.NestedResults();
spec.results_.description = description;
spec.matchersClass = null;
};
jasmine.Spec.prototype.getFullName = function() {
return this.suite.getFullName() + ' ' + this.description + '.';
};
jasmine.Spec.prototype.results = function() {
return this.results_;
};
/**
* All parameters are pretty-printed and concatenated together, then written to the spec's output.
*
* Be careful not to leave calls to <code>jasmine.log</code> in production code.
*/
jasmine.Spec.prototype.log = function() {
return this.results_.log(arguments);
};
jasmine.Spec.prototype.runs = function (func) {
var block = new jasmine.Block(this.env, func, this);
this.addToQueue(block);
return this;
};
jasmine.Spec.prototype.addToQueue = function (block) {
if (this.queue.isRunning()) {
this.queue.insertNext(block);
} else {
this.queue.add(block);
}
};
/**
* @param {jasmine.ExpectationResult} result
*/
jasmine.Spec.prototype.addMatcherResult = function(result) {
this.results_.addResult(result);
};
jasmine.Spec.prototype.expect = function(actual) {
var positive = new (this.getMatchersClass_())(this.env, actual, this);
positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
return positive;
};
/**
* Waits a fixed time period before moving to the next block.
*
* @deprecated Use waitsFor() instead
* @param {Number} timeout milliseconds to wait
*/
jasmine.Spec.prototype.waits = function(timeout) {
var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
this.addToQueue(waitsFunc);
return this;
};
/**
* Waits for the latchFunction to return true before proceeding to the next block.
*
* @param {Function} latchFunction
* @param {String} optional_timeoutMessage
* @param {Number} optional_timeout
*/
jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
var latchFunction_ = null;
var optional_timeoutMessage_ = null;
var optional_timeout_ = null;
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
switch (typeof arg) {
case 'function':
latchFunction_ = arg;
break;
case 'string':
optional_timeoutMessage_ = arg;
break;
case 'number':
optional_timeout_ = arg;
break;
}
}
var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
this.addToQueue(waitsForFunc);
return this;
};
jasmine.Spec.prototype.fail = function (e) {
var expectationResult = new jasmine.ExpectationResult({
passed: false,
message: e ? jasmine.util.formatException(e) : 'Exception',
trace: { stack: e.stack }
});
this.results_.addResult(expectationResult);
};
jasmine.Spec.prototype.getMatchersClass_ = function() {
return this.matchersClass || this.env.matchersClass;
};
jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
var parent = this.getMatchersClass_();
var newMatchersClass = function() {
parent.apply(this, arguments);
};
jasmine.util.inherit(newMatchersClass, parent);
jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
this.matchersClass = newMatchersClass;
};
jasmine.Spec.prototype.finishCallback = function() {
this.env.reporter.reportSpecResults(this);
};
jasmine.Spec.prototype.finish = function(onComplete) {
this.removeAllSpies();
this.finishCallback();
if (onComplete) {
onComplete();
}
};
jasmine.Spec.prototype.after = function(doAfter) {
if (this.queue.isRunning()) {
this.queue.add(new jasmine.Block(this.env, doAfter, this), true);
} else {
this.afterCallbacks.unshift(doAfter);
}
};
jasmine.Spec.prototype.execute = function(onComplete) {
var spec = this;
if (!spec.env.specFilter(spec)) {
spec.results_.skipped = true;
spec.finish(onComplete);
return;
}
this.env.reporter.reportSpecStarting(this);
spec.env.currentSpec = spec;
spec.addBeforesAndAftersToQueue();
spec.queue.start(function () {
spec.finish(onComplete);
});
};
jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
var runner = this.env.currentRunner();
var i;
for (var suite = this.suite; suite; suite = suite.parentSuite) {
for (i = 0; i < suite.before_.length; i++) {
this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
}
}
for (i = 0; i < runner.before_.length; i++) {
this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
}
for (i = 0; i < this.afterCallbacks.length; i++) {
this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this), true);
}
for (suite = this.suite; suite; suite = suite.parentSuite) {
for (i = 0; i < suite.after_.length; i++) {
this.queue.add(new jasmine.Block(this.env, suite.after_[i], this), true);
}
}
for (i = 0; i < runner.after_.length; i++) {
this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true);
}
};
jasmine.Spec.prototype.explodes = function() {
throw 'explodes function should not have been called';
};
jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
if (obj == jasmine.undefined) {
throw "spyOn could not find an object to spy upon for " + methodName + "()";
}
if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
throw methodName + '() method does not exist';
}
if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
throw new Error(methodName + ' has already been spied upon');
}
var spyObj = jasmine.createSpy(methodName);
this.spies_.push(spyObj);
spyObj.baseObj = obj;
spyObj.methodName = methodName;
spyObj.originalValue = obj[methodName];
obj[methodName] = spyObj;
return spyObj;
};
jasmine.Spec.prototype.removeAllSpies = function() {
for (var i = 0; i < this.spies_.length; i++) {
var spy = this.spies_[i];
spy.baseObj[spy.methodName] = spy.originalValue;
}
this.spies_ = [];
};
/**
* Internal representation of a Jasmine suite.
*
* @constructor
* @param {jasmine.Env} env
* @param {String} description
* @param {Function} specDefinitions
* @param {jasmine.Suite} parentSuite
*/
jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
var self = this;
self.id = env.nextSuiteId ? env.nextSuiteId() : null;
self.description = description;
self.queue = new jasmine.Queue(env);
self.parentSuite = parentSuite;
self.env = env;
self.before_ = [];
self.after_ = [];
self.children_ = [];
self.suites_ = [];
self.specs_ = [];
};
jasmine.Suite.prototype.getFullName = function() {
var fullName = this.description;
for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
fullName = parentSuite.description + ' ' + fullName;
}
return fullName;
};
jasmine.Suite.prototype.finish = function(onComplete) {
this.env.reporter.reportSuiteResults(this);
this.finished = true;
if (typeof(onComplete) == 'function') {
onComplete();
}
};
jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
beforeEachFunction.typeName = 'beforeEach';
this.before_.unshift(beforeEachFunction);
};
jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
afterEachFunction.typeName = 'afterEach';
this.after_.unshift(afterEachFunction);
};
jasmine.Suite.prototype.results = function() {
return this.queue.results();
};
jasmine.Suite.prototype.add = function(suiteOrSpec) {
this.children_.push(suiteOrSpec);
if (suiteOrSpec instanceof jasmine.Suite) {
this.suites_.push(suiteOrSpec);
this.env.currentRunner().addSuite(suiteOrSpec);
} else {
this.specs_.push(suiteOrSpec);
}
this.queue.add(suiteOrSpec);
};
jasmine.Suite.prototype.specs = function() {
return this.specs_;
};
jasmine.Suite.prototype.suites = function() {
return this.suites_;
};
jasmine.Suite.prototype.children = function() {
return this.children_;
};
jasmine.Suite.prototype.execute = function(onComplete) {
var self = this;
this.queue.start(function () {
self.finish(onComplete);
});
};
jasmine.WaitsBlock = function(env, timeout, spec) {
this.timeout = timeout;
jasmine.Block.call(this, env, null, spec);
};
jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
jasmine.WaitsBlock.prototype.execute = function (onComplete) {
if (jasmine.VERBOSE) {
this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
}
this.env.setTimeout(function () {
onComplete();
}, this.timeout);
};
/**
* A block which waits for some condition to become true, with timeout.
*
* @constructor
* @extends jasmine.Block
* @param {jasmine.Env} env The Jasmine environment.
* @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
* @param {Function} latchFunction A function which returns true when the desired condition has been met.
* @param {String} message The message to display if the desired condition hasn't been met within the given time period.
* @param {jasmine.Spec} spec The Jasmine spec.
*/
jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
this.timeout = timeout || env.defaultTimeoutInterval;
this.latchFunction = latchFunction;
this.message = message;
this.totalTimeSpentWaitingForLatch = 0;
jasmine.Block.call(this, env, null, spec);
};
jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
if (jasmine.VERBOSE) {
this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
}
var latchFunctionResult;
try {
latchFunctionResult = this.latchFunction.apply(this.spec);
} catch (e) {
this.spec.fail(e);
onComplete();
return;
}
if (latchFunctionResult) {
onComplete();
} else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
this.spec.fail({
name: 'timeout',
message: message
});
this.abort = true;
onComplete();
} else {
this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
var self = this;
this.env.setTimeout(function() {
self.execute(onComplete);
}, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
}
};
jasmine.version_= {
"major": 1,
"minor": 3,
"build": 0,
"revision": 1354052693
};
<?php
/**
* Created by JetBrains PhpStorm.
* User: dongyancen
* Date: 13-10-11
* Time: 下午5:17
* To change this template use File | Settings | File Templates.
*/
header( "Content-type: text/html; charset=utf-8" );
$filter = array_key_exists( 'filter' , $_GET ) ? $_GET[ 'filter' ] : '*';
$quirk = array_key_exists( 'quirk' , $_GET );
$cov = array_key_exists( 'cov' , $_GET );
if ( !$quirk ) {
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<?php } ?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Kity Test Index Page</title>
<script type="text/javascript" src="js/tools.js"></script>
<!-- <script type="text/javascript" src="js/UserAction.js"></script>-->
<script type="text/javascript" src="js/run.js"></script>
<link media="screen" href="css/test.css" type="text/css"
rel="stylesheet"/>
</head>
<body>
<div id="title">
<h1>Kity Test Index Page</h1>
</div>
<!--浏览器插件,可调用windows api-->
<div>
<object id="plugin" type="application/x-plugintest" width="1" height="1">
<param name="onload" value="pluginLoaded"/>
</object>
</div>
<div id="id_control" class="control">
<input id="id_control_runnext" type="checkbox"/>自动下一个
<input id="id_control_breakonerror" type="checkbox"/>出错时终止
<input id="id_control_clearstatus" type="button" value="清除用例状态"
onclick="removeClass(document.getElementById('id_testlist'),'running_case pass_case fail_case');"/>
</div>
<div>
<a id="id_testlist_status" class="button"> <span
onclick="slideToggle(document.getElementById('id_testlist');"> 折叠用例 </span> </a>
<a id="id_srconly" class="button"><span
onclick="slideToggle(document.getElementById('id_runningarea');">折叠执行</span> </a>
</div>
<div style="clear: both"></div>
<div id="id_testlist" class="testlist">
<?php
/*分析所有源码与测试代码js文件一一对应的文件并追加到当前列表中*/
require_once "caseSource.php";
Kiss::listcase( $filter ,$cov);
?>
<div style="clear: both; overflow: hidden"></div>
</div>
<div id="id_runningarea" class="runningarea"
style="border: solid; display: none"></div>
<div id="id_reportarea" class="reportarea" style="display: none;"></div>
<div class='clear'></div>
<div id="id_showSrcOnly" class="testlist" style="display: none;">
<?php
require_once "caseSource.php";
Kiss::listSrcOnly( true );
?>
<div class="clear"></div>
</div>
</body>
</html>
\ No newline at end of file
<?php
/**
* Created by JetBrains PhpStorm.
* User: dongyancen
* Date: 13-10-17
* Time: 下午6:28
* To change this template use File | Settings | File Templates.
*/
error_reporting(E_ERROR|E_WARNING);
function report()
{
return;
}
report();
\ No newline at end of file
<?php
/**
* Created by JetBrains PhpStorm.
* User: dongyancen
* Date: 13-10-10
* Time: 上午1:17
* To change this template use File | Settings | File Templates.
*/
header( "Content-type: text/html; charset=utf-8" );
header( "Cache-Control: no-cache, max-age=10, must-revalidate" );
if ( !array_key_exists( 'quirk' , $_GET ) ) {
print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
}
;
require_once "caseSource.php";
$c = new Kiss( $_GET[ 'case' ] );
$title = $c->name;
$cov = array_key_exists( 'cov' , $_GET );
?>
<html>
<head>
<title>Jasmine Spec Runner</title>
<link rel="shortcut icon" type="image/png" href="lib/jasmine-1.3.0/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="lib/jasmine-1.3.0/jasmine.css">
<script type="text/javascript" src="lib/jasmine-1.3.0/jasmine.js"></script>
<script type="text/javascript" src="lib/jasmine-1.3.0/jasmine-html.js"></script>
<script type="text/javascript" src="js/ext_jasmine.js"></script>
<?php $c->print_js($cov); ?>
</head>
<script type="text/javascript">
//todo
// if (parent && parent.brtest) {
// parent.$(parent.brtest).trigger('done', [ new Date().getTime(), {
// failed : args[0],
// passed : args[1],
// detail:args[2]
// }, window._$jscoverage || null ]);
// }
(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
execJasmine();
};
function execJasmine() {
jasmineEnv.execute();
}
})();
</script>
<body>
</body>
</html>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment