Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
John McEleney
/
casperjs
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
Commit
301d6345
...
301d63454d0f1bd9a337ce02f585d2a7d23828dd
authored
2011-12-24 16:54:22 +0100
by
Nicolas Perriault
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
continued migration to a full require() based layout
1 parent
d5ff7c42
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
604 additions
and
553 deletions
casperjs
lib/casper.js
lib/clientutils.js
lib/colorizer.js
lib/injector.js
lib/tester.js
lib/utils.js
lib/xunit.js
casperjs
View file @
301d634
...
...
@@ -106,6 +106,28 @@ phantom.casperArgs = (function(cliArgs) {
return extract;
})(phantom.args);
var sourceIds = {};
// Inspired by phantomjs-nodify: https://github.com/jgonera/phantomjs-nodify/
// TODO: remoive when phantomjs has js engine upgrade
if (!new Error().hasOwnProperty('stack')) {
Object.defineProperty(Error.prototype, 'stack', {
set: function(string) {
this._stack = string;
},
get: function() {
if (this._stack) {
return this._stack;
} else if (this.fileName || this.sourceId) {
return this.toString() + '\nat ' + getErrorMessage(this);
}
return this.toString() + '\nat unknown';
},
configurable: true,
enumerable: true
});
}
// Inspired by phantomjs-nodify: https://github.com/jgonera/phantomjs-nodify/
// TODO: remove when PhantomJS has full module support
require = (function(require, requireDir) {
...
...
@@ -159,7 +181,14 @@ require = (function(require, requireDir) {
return requireCache[file].exports;
}
code = fs.read(file);
if (file.match(/\.coffee$/)) {
if (file.match(/\.js$/i)) {
try {
// TODO: esprima syntax check
} catch (e) {
e.fileName = file;
throw e;
}
} else if (file.match(/\.coffee$/i)) {
try {
code = CoffeeScript.compile(code);
} catch (e) {
...
...
@@ -168,12 +197,12 @@ require = (function(require, requireDir) {
}
}
// a trick to associate Error's sourceId with file
//
code += ";throw new Error('__sourceId__');";
code += ";throw new Error('__sourceId__');";
try {
fn = new Function('module', 'exports', code);
fn(module, module.exports);
} catch (e) {
if (
typeof sourceIds === "object" &&
!sourceIds.hasOwnProperty(e.sourceId)) {
if (!sourceIds.hasOwnProperty(e.sourceId)) {
sourceIds[e.sourceId] = file;
}
if (e.message !== '__sourceId__') {
...
...
lib/casper.js
View file @
301d634
...
...
@@ -28,12 +28,9 @@
var
utils
=
require
(
'./lib/utils'
);
exports
.
create
=
create
;
exports
.
Casper
=
Casper
;
function
create
(
options
)
{
exports
.
create
=
function
(
options
)
{
return
new
Casper
(
options
);
}
}
;
/**
* Main Casper object.
...
...
@@ -89,7 +86,7 @@ var Casper = function(options) {
warning
:
'COMMENT'
,
error
:
'ERROR'
};
this
.
options
=
mergeObjects
(
this
.
defaults
,
options
);
this
.
options
=
utils
.
mergeObjects
(
this
.
defaults
,
options
);
this
.
page
=
null
;
this
.
pendingWait
=
false
;
this
.
requestUrl
=
'about:blank'
;
...
...
@@ -152,7 +149,7 @@ Casper.prototype = {
capture
:
function
(
targetFile
,
clipRect
)
{
var
previousClipRect
;
if
(
clipRect
)
{
if
(
!
isType
(
clipRect
,
"object"
))
{
if
(
!
utils
.
isType
(
clipRect
,
"object"
))
{
throw
new
Error
(
"clipRect must be an Object instance."
);
}
previousClipRect
=
this
.
page
.
clipRect
;
...
...
@@ -206,13 +203,13 @@ Casper.prototype = {
return
;
}
var
step
=
self
.
steps
[
self
.
step
++
];
if
(
isType
(
step
,
"function"
))
{
if
(
utils
.
isType
(
step
,
"function"
))
{
self
.
runStep
(
step
);
}
else
{
self
.
result
.
time
=
new
Date
().
getTime
()
-
self
.
startTime
;
self
.
log
(
"Done "
+
self
.
steps
.
length
+
" steps in "
+
self
.
result
.
time
+
'ms.'
,
"info"
);
clearInterval
(
self
.
checker
);
if
(
isType
(
onComplete
,
"function"
))
{
if
(
utils
.
isType
(
onComplete
,
"function"
))
{
try
{
onComplete
.
call
(
self
,
self
);
}
catch
(
err
)
{
...
...
@@ -234,7 +231,7 @@ Casper.prototype = {
* @return Boolean
*/
click
:
function
(
selector
,
fallbackToHref
)
{
fallbackToHref
=
isType
(
fallbackToHref
,
"undefined"
)
?
true
:
!!
fallbackToHref
;
fallbackToHref
=
utils
.
isType
(
fallbackToHref
,
"undefined"
)
?
true
:
!!
fallbackToHref
;
this
.
log
(
"click on selector: "
+
selector
,
"debug"
);
return
this
.
evaluate
(
function
(
selector
,
fallbackToHref
)
{
return
__utils__
.
click
(
selector
,
fallbackToHref
);
...
...
@@ -252,10 +249,10 @@ Casper.prototype = {
* @return Function The final step function
*/
createStep
:
function
(
fn
,
options
)
{
if
(
!
isType
(
fn
,
"function"
))
{
if
(
!
utils
.
isType
(
fn
,
"function"
))
{
throw
new
Error
(
"createStep(): a step definition must be a function"
);
}
fn
.
options
=
isType
(
options
,
"object"
)
?
options
:
{};
fn
.
options
=
utils
.
isType
(
options
,
"object"
)
?
options
:
{};
return
fn
;
},
...
...
@@ -293,9 +290,9 @@ Casper.prototype = {
die
:
function
(
message
,
status
)
{
this
.
result
.
status
=
'error'
;
this
.
result
.
time
=
new
Date
().
getTime
()
-
this
.
startTime
;
message
=
isType
(
message
,
"string"
)
&&
message
.
length
>
0
?
message
:
DEFAULT_DIE_MESSAGE
;
message
=
utils
.
isType
(
message
,
"string"
)
&&
message
.
length
>
0
?
message
:
DEFAULT_DIE_MESSAGE
;
this
.
log
(
message
,
"error"
);
if
(
isType
(
this
.
options
.
onDie
,
"function"
))
{
if
(
utils
.
isType
(
this
.
options
.
onDie
,
"function"
))
{
this
.
options
.
onDie
.
call
(
this
,
this
,
message
,
status
);
}
return
this
.
exit
(
Number
(
status
)
>
0
?
Number
(
status
)
:
1
);
...
...
@@ -327,7 +324,7 @@ Casper.prototype = {
* @return Casper
*/
each
:
function
(
array
,
fn
)
{
if
(
!
isType
(
array
,
"array"
))
{
if
(
!
utils
.
isType
(
array
,
"array"
))
{
this
.
log
(
"each() only works with arrays"
,
"error"
);
return
this
;
}
...
...
@@ -374,7 +371,7 @@ Casper.prototype = {
* @see WebPage#evaluate
*/
evaluate
:
function
(
fn
,
context
)
{
context
=
isType
(
context
,
"object"
)
?
context
:
{};
context
=
utils
.
isType
(
context
,
"object"
)
?
context
:
{};
var
newFn
=
require
(
'./lib/injector'
).
create
(
fn
).
process
(
context
);
return
this
.
page
.
evaluate
(
newFn
);
},
...
...
@@ -454,10 +451,10 @@ Casper.prototype = {
*/
fill
:
function
(
selector
,
vals
,
submit
)
{
submit
=
submit
===
true
?
submit
:
false
;
if
(
!
isType
(
selector
,
"string"
)
||
!
selector
.
length
)
{
if
(
!
utils
.
isType
(
selector
,
"string"
)
||
!
selector
.
length
)
{
throw
new
Error
(
"Form selector must be a non-empty string"
);
}
if
(
!
isType
(
vals
,
"object"
))
{
if
(
!
utils
.
isType
(
vals
,
"object"
))
{
throw
new
Error
(
"Form values must be provided as an object"
);
}
var
fillResults
=
this
.
evaluate
(
function
(
selector
,
values
)
{
...
...
@@ -569,7 +566,7 @@ Casper.prototype = {
log
:
function
(
message
,
level
,
space
)
{
level
=
level
&&
this
.
logLevels
.
indexOf
(
level
)
>
-
1
?
level
:
"debug"
;
space
=
space
?
space
:
"phantom"
;
if
(
level
===
"error"
&&
isType
(
this
.
options
.
onError
,
"function"
))
{
if
(
level
===
"error"
&&
utils
.
isType
(
this
.
options
.
onError
,
"function"
))
{
this
.
options
.
onError
.
call
(
this
,
this
,
message
,
space
);
}
if
(
this
.
logLevels
.
indexOf
(
level
)
<
this
.
logLevels
.
indexOf
(
this
.
options
.
logLevel
))
{
...
...
@@ -581,7 +578,7 @@ Casper.prototype = {
message
:
message
,
date
:
new
Date
().
toString
()
};
if
(
level
in
this
.
logFormats
&&
isType
(
this
.
logFormats
[
level
],
"function"
))
{
if
(
level
in
this
.
logFormats
&&
utils
.
isType
(
this
.
logFormats
[
level
],
"function"
))
{
message
=
this
.
logFormats
[
level
](
message
,
level
,
space
);
}
else
{
var
levelStr
=
this
.
colorizer
.
colorize
(
'['
+
level
+
']'
,
this
.
logStyles
[
level
]);
...
...
@@ -603,7 +600,7 @@ Casper.prototype = {
* @return Casper
*/
open
:
function
(
location
,
options
)
{
options
=
isType
(
options
,
"object"
)
?
options
:
{};
options
=
utils
.
isType
(
options
,
"object"
)
?
options
:
{};
this
.
requestUrl
=
location
;
// http auth
var
httpAuthMatch
=
location
.
match
(
/^https
?
:
\/\/(
.+
)
:
(
.+
)
@/i
);
...
...
@@ -637,7 +634,7 @@ Casper.prototype = {
*/
resourceExists
:
function
(
test
)
{
var
testFn
;
if
(
isType
(
test
,
"string"
))
{
if
(
utils
.
isType
(
test
,
"string"
))
{
testFn
=
function
(
res
)
{
return
res
.
url
.
search
(
test
)
!==
-
1
;
};
...
...
@@ -670,17 +667,17 @@ Casper.prototype = {
* @param Function step
*/
runStep
:
function
(
step
)
{
var
skipLog
=
isType
(
step
.
options
,
"object"
)
&&
step
.
options
.
skipLog
===
true
;
var
skipLog
=
utils
.
isType
(
step
.
options
,
"object"
)
&&
step
.
options
.
skipLog
===
true
;
var
stepInfo
=
"Step "
+
(
this
.
step
)
+
"/"
+
this
.
steps
.
length
;
var
stepResult
;
if
(
!
skipLog
)
{
this
.
log
(
stepInfo
+
' '
+
this
.
getCurrentUrl
()
+
' (HTTP '
+
this
.
currentHTTPStatus
+
')'
,
"info"
);
}
if
(
isType
(
this
.
options
.
stepTimeout
,
"number"
)
&&
this
.
options
.
stepTimeout
>
0
)
{
if
(
utils
.
isType
(
this
.
options
.
stepTimeout
,
"number"
)
&&
this
.
options
.
stepTimeout
>
0
)
{
var
stepTimeoutCheckInterval
=
setInterval
(
function
(
self
,
start
,
stepNum
)
{
if
(
new
Date
().
getTime
()
-
start
>
self
.
options
.
stepTimeout
)
{
if
(
self
.
step
==
stepNum
)
{
if
(
isType
(
self
.
options
.
onStepTimeout
,
"function"
))
{
if
(
utils
.
isType
(
self
.
options
.
onStepTimeout
,
"function"
))
{
self
.
options
.
onStepTimeout
.
call
(
self
,
self
);
}
else
{
self
.
die
(
"Maximum step execution timeout exceeded for step "
+
stepNum
,
"error"
);
...
...
@@ -699,7 +696,7 @@ Casper.prototype = {
throw
e
;
}
}
if
(
isType
(
this
.
options
.
onStepComplete
,
"function"
))
{
if
(
utils
.
isType
(
this
.
options
.
onStepComplete
,
"function"
))
{
this
.
options
.
onStepComplete
.
call
(
this
,
this
,
stepResult
);
}
if
(
!
skipLog
)
{
...
...
@@ -718,7 +715,7 @@ Casper.prototype = {
if
(
!
this
.
started
)
{
throw
new
Error
(
"Casper must be started in order to use the setHttpAuth() method"
);
}
if
(
!
isType
(
username
,
"string"
)
||
!
isType
(
password
,
"string"
))
{
if
(
!
utils
.
isType
(
username
,
"string"
)
||
!
utils
.
isType
(
password
,
"string"
))
{
throw
new
Error
(
"Both username and password must be strings"
);
}
this
.
page
.
settings
.
userName
=
username
;
...
...
@@ -746,37 +743,37 @@ Casper.prototype = {
this
.
options
.
logLevel
=
"warning"
;
}
// WebPage
if
(
!
isWebPage
(
this
.
page
))
{
if
(
isWebPage
(
this
.
options
.
page
))
{
if
(
!
utils
.
isWebPage
(
this
.
page
))
{
if
(
utils
.
isWebPage
(
this
.
options
.
page
))
{
this
.
page
=
this
.
options
.
page
;
}
else
{
this
.
page
=
createPage
(
this
);
}
}
this
.
page
.
settings
=
mergeObjects
(
this
.
page
.
settings
,
this
.
options
.
pageSettings
);
if
(
isType
(
this
.
options
.
clipRect
,
"object"
))
{
this
.
page
.
settings
=
utils
.
mergeObjects
(
this
.
page
.
settings
,
this
.
options
.
pageSettings
);
if
(
utils
.
isType
(
this
.
options
.
clipRect
,
"object"
))
{
this
.
page
.
clipRect
=
this
.
options
.
clipRect
;
}
if
(
isType
(
this
.
options
.
viewportSize
,
"object"
))
{
if
(
utils
.
isType
(
this
.
options
.
viewportSize
,
"object"
))
{
this
.
page
.
viewportSize
=
this
.
options
.
viewportSize
;
}
this
.
started
=
true
;
if
(
isType
(
this
.
options
.
timeout
,
"number"
)
&&
this
.
options
.
timeout
>
0
)
{
if
(
utils
.
isType
(
this
.
options
.
timeout
,
"number"
)
&&
this
.
options
.
timeout
>
0
)
{
this
.
log
(
"Execution timeout set to "
+
this
.
options
.
timeout
+
'ms'
,
"info"
);
setTimeout
(
function
(
self
)
{
if
(
isType
(
self
.
options
.
onTimeout
,
"function"
))
{
if
(
utils
.
isType
(
self
.
options
.
onTimeout
,
"function"
))
{
self
.
options
.
onTimeout
.
call
(
self
,
self
);
}
else
{
self
.
die
(
"Timeout of "
+
self
.
options
.
timeout
+
"ms exceeded, exiting."
);
}
},
this
.
options
.
timeout
,
this
);
}
if
(
isType
(
this
.
options
.
onPageInitialized
,
"function"
))
{
if
(
utils
.
isType
(
this
.
options
.
onPageInitialized
,
"function"
))
{
this
.
log
(
"Post-configuring WebPage instance"
,
"debug"
);
this
.
options
.
onPageInitialized
.
call
(
this
,
this
.
page
);
}
if
(
isType
(
location
,
"string"
)
&&
location
.
length
>
0
)
{
return
this
.
thenOpen
(
location
,
isType
(
then
,
"function"
)
?
then
:
this
.
createStep
(
function
(
self
)
{
if
(
utils
.
isType
(
location
,
"string"
)
&&
location
.
length
>
0
)
{
return
this
.
thenOpen
(
location
,
utils
.
isType
(
then
,
"function"
)
?
then
:
this
.
createStep
(
function
(
self
)
{
self
.
log
(
"start page is loaded"
,
"debug"
);
}));
}
...
...
@@ -793,7 +790,7 @@ Casper.prototype = {
if
(
!
this
.
started
)
{
throw
new
Error
(
"Casper not started; please use Casper#start"
);
}
if
(
!
isType
(
step
,
"function"
))
{
if
(
!
utils
.
isType
(
step
,
"function"
))
{
throw
new
Error
(
"You can only define a step as a function"
);
}
// check if casper is running
...
...
@@ -832,7 +829,7 @@ Casper.prototype = {
this
.
then
(
function
(
self
)
{
self
.
click
(
selector
,
fallbackToHref
);
});
return
isType
(
then
,
"function"
)
?
this
.
then
(
then
)
:
this
;
return
utils
.
isType
(
then
,
"function"
)
?
this
.
then
(
then
)
:
this
;
},
/**
...
...
@@ -864,7 +861,7 @@ Casper.prototype = {
},
{
skipLog
:
true
}));
return
isType
(
then
,
"function"
)
?
this
.
then
(
then
)
:
this
;
return
utils
.
isType
(
then
,
"function"
)
?
this
.
then
(
then
)
:
this
;
},
/**
...
...
@@ -890,7 +887,7 @@ Casper.prototype = {
* @return Casper
*/
viewport
:
function
(
width
,
height
)
{
if
(
!
isType
(
width
,
"number"
)
||
!
isType
(
height
,
"number"
)
||
width
<=
0
||
height
<=
0
)
{
if
(
!
utils
.
isType
(
width
,
"number"
)
||
!
utils
.
isType
(
height
,
"number"
)
||
width
<=
0
||
height
<=
0
)
{
throw
new
Error
(
"Invalid viewport width/height set: "
+
width
+
'x'
+
height
);
}
this
.
page
.
viewportSize
=
{
...
...
@@ -910,10 +907,10 @@ Casper.prototype = {
*/
wait
:
function
(
timeout
,
then
)
{
timeout
=
Number
(
timeout
,
10
);
if
(
!
isType
(
timeout
,
"number"
)
||
timeout
<
1
)
{
if
(
!
utils
.
isType
(
timeout
,
"number"
)
||
timeout
<
1
)
{
this
.
die
(
"wait() only accepts a positive integer > 0 as a timeout value"
);
}
if
(
then
&&
!
isType
(
then
,
"function"
))
{
if
(
then
&&
!
utils
.
isType
(
then
,
"function"
))
{
this
.
die
(
"wait() a step definition must be a function"
);
}
return
this
.
then
(
function
(
self
)
{
...
...
@@ -947,10 +944,10 @@ Casper.prototype = {
*/
waitFor
:
function
(
testFx
,
then
,
onTimeout
,
timeout
)
{
timeout
=
timeout
?
timeout
:
this
.
defaultWaitTimeout
;
if
(
!
isType
(
testFx
,
"function"
))
{
if
(
!
utils
.
isType
(
testFx
,
"function"
))
{
this
.
die
(
"waitFor() needs a test function"
);
}
if
(
then
&&
!
isType
(
then
,
"function"
))
{
if
(
then
&&
!
utils
.
isType
(
then
,
"function"
))
{
this
.
die
(
"waitFor() next step definition must be a function"
);
}
return
this
.
then
(
function
(
self
)
{
...
...
@@ -964,7 +961,7 @@ Casper.prototype = {
self
.
waitDone
();
if
(
!
condition
)
{
self
.
log
(
"Casper.waitFor() timeout"
,
"warning"
);
if
(
isType
(
onTimeout
,
"function"
))
{
if
(
utils
.
isType
(
onTimeout
,
"function"
))
{
onTimeout
.
call
(
self
,
self
);
}
else
{
self
.
die
(
"Timeout of "
+
timeout
+
"ms expired, exiting."
,
"error"
);
...
...
@@ -1073,8 +1070,109 @@ Casper.prototype = {
* @param Object proto Prototype methods to add to Casper
*/
Casper
.
extend
=
function
(
proto
)
{
if
(
!
isType
(
proto
,
"object"
))
{
if
(
!
utils
.
isType
(
proto
,
"object"
))
{
throw
new
Error
(
"extends() only accept objects as prototypes"
);
}
mergeObjects
(
Casper
.
prototype
,
proto
);
};
exports
.
Casper
=
Casper
;
/**
* Creates a new WebPage instance for Casper use.
*
* @param Casper casper A Casper instance
* @return WebPage
*/
function
createPage
(
casper
)
{
var
page
;
if
(
phantom
.
version
.
major
<=
1
&&
phantom
.
version
.
minor
<
3
&&
utils
.
isType
(
require
,
"function"
))
{
page
=
new
WebPage
();
}
else
{
page
=
require
(
'webpage'
).
create
();
}
page
.
onAlert
=
function
(
message
)
{
casper
.
log
(
'[alert] '
+
message
,
"info"
,
"remote"
);
if
(
utils
.
isType
(
casper
.
options
.
onAlert
,
"function"
))
{
casper
.
options
.
onAlert
.
call
(
casper
,
casper
,
message
);
}
};
page
.
onConsoleMessage
=
function
(
msg
)
{
var
level
=
"info"
,
test
=
/^
\[
casper:
(\w
+
)\]\s?(
.*
)
/
.
exec
(
msg
);
if
(
test
&&
test
.
length
===
3
)
{
level
=
test
[
1
];
msg
=
test
[
2
];
}
casper
.
log
(
msg
,
level
,
"remote"
);
};
page
.
onLoadStarted
=
function
()
{
casper
.
resources
=
[];
casper
.
loadInProgress
=
true
;
};
page
.
onLoadFinished
=
function
(
status
)
{
if
(
status
!==
"success"
)
{
var
message
=
'Loading resource failed with status='
+
status
;
if
(
casper
.
currentHTTPStatus
)
{
message
+=
' (HTTP '
+
casper
.
currentHTTPStatus
+
')'
;
}
message
+=
': '
+
casper
.
requestUrl
;
casper
.
log
(
message
,
"warning"
);
if
(
utils
.
isType
(
casper
.
options
.
onLoadError
,
"function"
))
{
casper
.
options
.
onLoadError
.
call
(
casper
,
casper
,
casper
.
requestUrl
,
status
);
}
}
if
(
casper
.
options
.
clientScripts
)
{
if
(
betterTypeOf
(
casper
.
options
.
clientScripts
)
!==
"array"
)
{
casper
.
log
(
"The clientScripts option must be an array"
,
"error"
);
}
else
{
for
(
var
i
=
0
;
i
<
casper
.
options
.
clientScripts
.
length
;
i
++
)
{
var
script
=
casper
.
options
.
clientScripts
[
i
];
if
(
casper
.
page
.
injectJs
(
script
))
{
casper
.
log
(
'Automatically injected '
+
script
+
' client side'
,
"debug"
);
}
else
{
casper
.
log
(
'Failed injecting '
+
script
+
' client side'
,
"warning"
);
}
}
}
}
// Client-side utils injection
var
injected
=
page
.
evaluate
(
replaceFunctionPlaceholders
(
function
()
{
eval
(
"var ClientUtils = "
+
decodeURIComponent
(
"%utils%"
));
__utils__
=
new
ClientUtils
();
return
__utils__
instanceof
ClientUtils
;
},
{
utils
:
encodeURIComponent
(
require
(
'./lib/clientutils'
).
ClientUtils
.
toString
())
}));
if
(
!
injected
)
{
casper
.
log
(
"Failed to inject Casper client-side utilities!"
,
"warning"
);
}
else
{
casper
.
log
(
"Successfully injected Casper client-side utilities"
,
"debug"
);
}
// history
casper
.
history
.
push
(
casper
.
getCurrentUrl
());
casper
.
loadInProgress
=
false
;
};
page
.
onResourceReceived
=
function
(
resource
)
{
if
(
utils
.
isType
(
casper
.
options
.
onResourceReceived
,
"function"
))
{
casper
.
options
.
onResourceReceived
.
call
(
casper
,
casper
,
resource
);
}
if
(
resource
.
stage
===
"end"
)
{
casper
.
resources
.
push
(
resource
);
}
if
(
resource
.
url
===
casper
.
requestUrl
&&
resource
.
stage
===
"start"
)
{
casper
.
currentHTTPStatus
=
resource
.
status
;
if
(
utils
.
isType
(
casper
.
options
.
httpStatusHandlers
,
"object"
)
&&
resource
.
status
in
casper
.
options
.
httpStatusHandlers
&&
utils
.
isType
(
casper
.
options
.
httpStatusHandlers
[
resource
.
status
],
"function"
))
{
casper
.
options
.
httpStatusHandlers
[
resource
.
status
].
call
(
casper
,
casper
,
resource
);
}
casper
.
currentUrl
=
resource
.
url
;
}
};
page
.
onResourceRequested
=
function
(
request
)
{
if
(
utils
.
isType
(
casper
.
options
.
onResourceRequested
,
"function"
))
{
casper
.
options
.
onResourceRequested
.
call
(
casper
,
casper
,
request
);
}
};
return
page
;
}
...
...
lib/clientutils.js
View file @
301d634
...
...
@@ -26,418 +26,420 @@
*
*/
function
create
()
{
return
new
ClientUtils
();
}
/**
* Casper client-side helpers.
*/
var
ClientUtils
=
function
()
{
var
BASE64_ENCODE_CHARS
=
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
;
var
BASE64_DECODE_CHARS
=
new
Array
(
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
62
,
-
1
,
-
1
,
-
1
,
63
,
52
,
53
,
54
,
55
,
56
,
57
,
58
,
59
,
60
,
61
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
,
11
,
12
,
13
,
14
,
15
,
16
,
17
,
18
,
19
,
20
,
21
,
22
,
23
,
24
,
25
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
26
,
27
,
28
,
29
,
30
,
31
,
32
,
33
,
34
,
35
,
36
,
37
,
38
,
39
,
40
,
41
,
42
,
43
,
44
,
45
,
46
,
47
,
48
,
49
,
50
,
51
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
);
/**
* Clicks on the DOM element behind the provided selector.
*
* @param String selector A CSS3 selector to the element to click
* @param Boolean fallbackToHref Whether to try to relocate to the value of any href attribute (default: true)
* @return Boolean
*/
this
.
click
=
function
(
selector
,
fallbackToHref
)
{
fallbackToHref
=
typeof
fallbackToHref
===
"undefined"
?
true
:
!!
fallbackToHref
;
var
elem
=
this
.
findOne
(
selector
);
if
(
!
elem
)
{
return
false
;
}
var
evt
=
document
.
createEvent
(
"MouseEvents"
);
evt
.
initMouseEvent
(
"click"
,
true
,
true
,
window
,
1
,
1
,
1
,
1
,
1
,
false
,
false
,
false
,
false
,
0
,
elem
);
if
(
elem
.
dispatchEvent
(
evt
))
{
return
true
;
}
if
(
fallbackToHref
&&
elem
.
hasAttribute
(
'href'
))
{
document
.
location
=
elem
.
getAttribute
(
'href'
);
return
true
;
}
return
false
;
(
function
(
exports
)
{
exports
.
create
=
function
()
{
return
new
ClientUtils
();
};
/**
* Decodes a base64 encoded string. Succeeds where window.atob() fails.
*
* @param String str The base64 encoded contents
* @return string
* Casper client-side helpers.
*/
this
.
decode
=
function
(
str
)
{
var
c1
,
c2
,
c3
,
c4
,
i
=
0
,
len
=
str
.
length
,
out
=
""
;
while
(
i
<
len
)
{
do
{
c1
=
BASE64_DECODE_CHARS
[
str
.
charCodeAt
(
i
++
)
&
0xff
];
}
while
(
i
<
len
&&
c1
==
-
1
);
if
(
c1
==
-
1
)
{
break
;
var
ClientUtils
=
function
()
{
var
BASE64_ENCODE_CHARS
=
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
;
var
BASE64_DECODE_CHARS
=
new
Array
(
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
62
,
-
1
,
-
1
,
-
1
,
63
,
52
,
53
,
54
,
55
,
56
,
57
,
58
,
59
,
60
,
61
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
,
11
,
12
,
13
,
14
,
15
,
16
,
17
,
18
,
19
,
20
,
21
,
22
,
23
,
24
,
25
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
26
,
27
,
28
,
29
,
30
,
31
,
32
,
33
,
34
,
35
,
36
,
37
,
38
,
39
,
40
,
41
,
42
,
43
,
44
,
45
,
46
,
47
,
48
,
49
,
50
,
51
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
);
/**
* Clicks on the DOM element behind the provided selector.
*
* @param String selector A CSS3 selector to the element to click
* @param Boolean fallbackToHref Whether to try to relocate to the value of any href attribute (default: true)
* @return Boolean
*/
this
.
click
=
function
(
selector
,
fallbackToHref
)
{
fallbackToHref
=
typeof
fallbackToHref
===
"undefined"
?
true
:
!!
fallbackToHref
;
var
elem
=
this
.
findOne
(
selector
);
if
(
!
elem
)
{
return
false
;
}
do
{
c2
=
BASE64_DECODE_CHARS
[
str
.
charCodeAt
(
i
++
)
&
0xff
];
}
while
(
i
<
len
&&
c2
==
-
1
);
if
(
c2
==
-
1
)
{
break
;
var
evt
=
document
.
createEvent
(
"MouseEvents"
);
evt
.
initMouseEvent
(
"click"
,
true
,
true
,
window
,
1
,
1
,
1
,
1
,
1
,
false
,
false
,
false
,
false
,
0
,
elem
);
if
(
elem
.
dispatchEvent
(
evt
))
{
return
true
;
}
out
+=
String
.
fromCharCode
((
c1
<<
2
)
|
((
c2
&
0x30
)
>>
4
));
do
{
c3
=
str
.
charCodeAt
(
i
++
)
&
0xff
;
if
(
c3
==
61
)
return
out
;
c3
=
BASE64_DECODE_CHARS
[
c3
];
}
while
(
i
<
len
&&
c3
==
-
1
);
if
(
c3
==
-
1
)
{
break
;
if
(
fallbackToHref
&&
elem
.
hasAttribute
(
'href'
))
{
document
.
location
=
elem
.
getAttribute
(
'href'
);
return
true
;
}
out
+=
String
.
fromCharCode
(((
c2
&
0
XF
)
<<
4
)
|
((
c3
&
0x3C
)
>>
2
));
do
{
c4
=
str
.
charCodeAt
(
i
++
)
&
0xff
;
if
(
c4
==
61
)
{
return
false
;
};
/**
* Decodes a base64 encoded string. Succeeds where window.atob() fails.
*
* @param String str The base64 encoded contents
* @return string
*/
this
.
decode
=
function
(
str
)
{
var
c1
,
c2
,
c3
,
c4
,
i
=
0
,
len
=
str
.
length
,
out
=
""
;
while
(
i
<
len
)
{
do
{
c1
=
BASE64_DECODE_CHARS
[
str
.
charCodeAt
(
i
++
)
&
0xff
];
}
while
(
i
<
len
&&
c1
==
-
1
);
if
(
c1
==
-
1
)
{
break
;
}
do
{
c2
=
BASE64_DECODE_CHARS
[
str
.
charCodeAt
(
i
++
)
&
0xff
];
}
while
(
i
<
len
&&
c2
==
-
1
);
if
(
c2
==
-
1
)
{
break
;
}
out
+=
String
.
fromCharCode
((
c1
<<
2
)
|
((
c2
&
0x30
)
>>
4
));
do
{
c3
=
str
.
charCodeAt
(
i
++
)
&
0xff
;
if
(
c3
==
61
)
return
out
;
c3
=
BASE64_DECODE_CHARS
[
c3
];
}
while
(
i
<
len
&&
c3
==
-
1
);
if
(
c3
==
-
1
)
{
break
;
}
out
+=
String
.
fromCharCode
(((
c2
&
0
XF
)
<<
4
)
|
((
c3
&
0x3C
)
>>
2
));
do
{
c4
=
str
.
charCodeAt
(
i
++
)
&
0xff
;
if
(
c4
==
61
)
{
return
out
;
}
c4
=
BASE64_DECODE_CHARS
[
c4
];
}
while
(
i
<
len
&&
c4
==
-
1
);
if
(
c4
==
-
1
)
{
break
;
}
c4
=
BASE64_DECODE_CHARS
[
c4
];
}
while
(
i
<
len
&&
c4
==
-
1
);
if
(
c4
==
-
1
)
{
break
;
out
+=
String
.
fromCharCode
(((
c3
&
0x03
)
<<
6
)
|
c4
);
}
out
+=
String
.
fromCharCode
(((
c3
&
0x03
)
<<
6
)
|
c4
);
}
return
out
;
};
return
out
;
};
/**
* Base64 encodes a string, even binary ones. Succeeds where
* window.btoa() fails.
*
* @param String str The string content to encode
* @return string
*/
this
.
encode
=
function
(
str
)
{
var
out
=
""
,
i
=
0
,
len
=
str
.
length
,
c1
,
c2
,
c3
;
while
(
i
<
len
)
{
c1
=
str
.
charCodeAt
(
i
++
)
&
0xff
;
if
(
i
==
len
)
{
out
+=
BASE64_ENCODE_CHARS
.
charAt
(
c1
>>
2
);
out
+=
BASE64_ENCODE_CHARS
.
charAt
((
c1
&
0x3
)
<<
4
);
out
+=
"=="
;
break
;
}
c2
=
str
.
charCodeAt
(
i
++
);
if
(
i
==
len
)
{
/**
* Base64 encodes a string, even binary ones. Succeeds where
* window.btoa() fails.
*
* @param String str The string content to encode
* @return string
*/
this
.
encode
=
function
(
str
)
{
var
out
=
""
,
i
=
0
,
len
=
str
.
length
,
c1
,
c2
,
c3
;
while
(
i
<
len
)
{
c1
=
str
.
charCodeAt
(
i
++
)
&
0xff
;
if
(
i
==
len
)
{
out
+=
BASE64_ENCODE_CHARS
.
charAt
(
c1
>>
2
);
out
+=
BASE64_ENCODE_CHARS
.
charAt
((
c1
&
0x3
)
<<
4
);
out
+=
"=="
;
break
;
}
c2
=
str
.
charCodeAt
(
i
++
);
if
(
i
==
len
)
{
out
+=
BASE64_ENCODE_CHARS
.
charAt
(
c1
>>
2
);
out
+=
BASE64_ENCODE_CHARS
.
charAt
(((
c1
&
0x3
)
<<
4
)
|
((
c2
&
0xF0
)
>>
4
));
out
+=
BASE64_ENCODE_CHARS
.
charAt
((
c2
&
0xF
)
<<
2
);
out
+=
"="
;
break
;
}
c3
=
str
.
charCodeAt
(
i
++
);
out
+=
BASE64_ENCODE_CHARS
.
charAt
(
c1
>>
2
);
out
+=
BASE64_ENCODE_CHARS
.
charAt
(((
c1
&
0x3
)
<<
4
)
|
((
c2
&
0xF0
)
>>
4
));
out
+=
BASE64_ENCODE_CHARS
.
charAt
((
c2
&
0xF
)
<<
2
);
out
+=
"="
;
break
;
out
+=
BASE64_ENCODE_CHARS
.
charAt
(((
c1
&
0x3
)
<<
4
)
|
((
c2
&
0xF0
)
>>
4
));
out
+=
BASE64_ENCODE_CHARS
.
charAt
(((
c2
&
0xF
)
<<
2
)
|
((
c3
&
0xC0
)
>>
6
));
out
+=
BASE64_ENCODE_CHARS
.
charAt
(
c3
&
0x3F
);
}
c3
=
str
.
charCodeAt
(
i
++
);
out
+=
BASE64_ENCODE_CHARS
.
charAt
(
c1
>>
2
);
out
+=
BASE64_ENCODE_CHARS
.
charAt
(((
c1
&
0x3
)
<<
4
)
|
((
c2
&
0xF0
)
>>
4
));
out
+=
BASE64_ENCODE_CHARS
.
charAt
(((
c2
&
0xF
)
<<
2
)
|
((
c3
&
0xC0
)
>>
6
));
out
+=
BASE64_ENCODE_CHARS
.
charAt
(
c3
&
0x3F
);
}
return
out
;
};
/**
* Checks if a given DOM element exists in remote page.
*
* @param String selector CSS3 selector
* @return Boolean
*/
this
.
exists
=
function
(
selector
)
{
try
{
return
document
.
querySelectorAll
(
selector
).
length
>
0
;
}
catch
(
e
)
{
return
false
;
}
};
/**
* Checks if a given DOM element is visible in remote page.
*
* @param String selector CSS3 selector
* @return Boolean
*/
this
.
visible
=
function
(
selector
)
{
try
{
var
el
=
document
.
querySelector
(
selector
);
return
el
&&
el
.
style
.
visibility
!==
'hidden'
&&
el
.
offsetHeight
>
0
&&
el
.
offsetWidth
>
0
;
}
catch
(
e
)
{
return
false
;
}
};
/**
* Fetches innerText within the element(s) matching a given CSS3
* selector.
*
* @param String selector A CSS3 selector
* @return String
*/
this
.
fetchText
=
function
(
selector
)
{
var
text
=
''
,
elements
=
this
.
findAll
(
selector
);
if
(
elements
&&
elements
.
length
)
{
Array
.
prototype
.
forEach
.
call
(
elements
,
function
(
element
)
{
text
+=
element
.
innerText
;
});
}
return
text
;
};
return
out
;
};
/**
* Fills a form with provided field values, and optionnaly submits it
.
*
* @param HTMLElement|String form A form element, or a CSS3 selector to a form element
* @param Object vals Field values
* @return Object An object containing setting result for each field, including file uploads
*/
this
.
fill
=
function
(
form
,
vals
)
{
var
out
=
{
errors
:
[],
fields
:
[],
files
:
[]
/**
* Checks if a given DOM element exists in remote page
.
*
* @param String selector CSS3 selector
* @return Boolean
*/
this
.
exists
=
function
(
selector
)
{
try
{
return
document
.
querySelectorAll
(
selector
).
length
>
0
;
}
catch
(
e
)
{
return
false
;
}
};
if
(
!
(
form
instanceof
HTMLElement
)
||
typeof
form
===
"string"
)
{
__utils__
.
log
(
"attempting to fetch form element from selector: '"
+
form
+
"'"
,
"info"
);
/**
* Checks if a given DOM element is visible in remote page.
*
* @param String selector CSS3 selector
* @return Boolean
*/
this
.
visible
=
function
(
selector
)
{
try
{
form
=
document
.
querySelector
(
form
);
var
el
=
document
.
querySelector
(
selector
);
return
el
&&
el
.
style
.
visibility
!==
'hidden'
&&
el
.
offsetHeight
>
0
&&
el
.
offsetWidth
>
0
;
}
catch
(
e
)
{
if
(
e
.
name
===
"SYNTAX_ERR"
)
{
out
.
errors
.
push
(
"invalid form selector provided: '"
+
form
+
"'"
);
return
out
;
}
return
false
;
}
}
if
(
!
form
)
{
out
.
errors
.
push
(
"form not found"
);
return
out
;
}
for
(
var
name
in
vals
)
{
if
(
!
vals
.
hasOwnProperty
(
name
))
{
continue
;
};
/**
* Fetches innerText within the element(s) matching a given CSS3
* selector.
*
* @param String selector A CSS3 selector
* @return String
*/
this
.
fetchText
=
function
(
selector
)
{
var
text
=
''
,
elements
=
this
.
findAll
(
selector
);
if
(
elements
&&
elements
.
length
)
{
Array
.
prototype
.
forEach
.
call
(
elements
,
function
(
element
)
{
text
+=
element
.
innerText
;
});
}
var
field
=
form
.
querySelectorAll
(
'[name="'
+
name
+
'"]'
);
var
value
=
vals
[
name
];
if
(
!
field
)
{
out
.
errors
.
push
(
'no field named "'
+
name
+
'" in form'
);
continue
;
return
text
;
};
/**
* Fills a form with provided field values, and optionnaly submits it.
*
* @param HTMLElement|String form A form element, or a CSS3 selector to a form element
* @param Object vals Field values
* @return Object An object containing setting result for each field, including file uploads
*/
this
.
fill
=
function
(
form
,
vals
)
{
var
out
=
{
errors
:
[],
fields
:
[],
files
:
[]
};
if
(
!
(
form
instanceof
HTMLElement
)
||
typeof
form
===
"string"
)
{
__utils__
.
log
(
"attempting to fetch form element from selector: '"
+
form
+
"'"
,
"info"
);
try
{
form
=
document
.
querySelector
(
form
);
}
catch
(
e
)
{
if
(
e
.
name
===
"SYNTAX_ERR"
)
{
out
.
errors
.
push
(
"invalid form selector provided: '"
+
form
+
"'"
);
return
out
;
}
}
}
try
{
out
.
fields
[
name
]
=
this
.
setField
(
field
,
value
);
}
catch
(
err
)
{
if
(
err
.
name
===
"FileUploadError"
)
{
out
.
files
.
push
({
name
:
name
,
path
:
err
.
path
});
}
else
{
this
.
log
(
err
,
"error"
);
throw
err
;
if
(
!
form
)
{
out
.
errors
.
push
(
"form not found"
);
return
out
;
}
for
(
var
name
in
vals
)
{
if
(
!
vals
.
hasOwnProperty
(
name
))
{
continue
;
}
var
field
=
form
.
querySelectorAll
(
'[name="'
+
name
+
'"]'
);
var
value
=
vals
[
name
];
if
(
!
field
)
{
out
.
errors
.
push
(
'no field named "'
+
name
+
'" in form'
);
continue
;
}
try
{
out
.
fields
[
name
]
=
this
.
setField
(
field
,
value
);
}
catch
(
err
)
{
if
(
err
.
name
===
"FileUploadError"
)
{
out
.
files
.
push
({
name
:
name
,
path
:
err
.
path
});
}
else
{
this
.
log
(
err
,
"error"
);
throw
err
;
}
}
}
}
return
out
;
};
return
out
;
};
/**
* Finds all DOM elements matching by the provided selector.
*
* @param String selector CSS3 selector
* @return NodeList|undefined
*/
this
.
findAll
=
function
(
selector
)
{
try
{
return
document
.
querySelectorAll
(
selector
);
}
catch
(
e
)
{
this
.
log
(
'findAll(): invalid selector provided "'
+
selector
+
'":'
+
e
,
"error"
);
}
};
/**
* Finds all DOM elements matching by the provided selector.
*
* @param String selector CSS3 selector
* @return NodeList|undefined
*/
this
.
findAll
=
function
(
selector
)
{
try
{
return
document
.
querySelectorAll
(
selector
);
}
catch
(
e
)
{
this
.
log
(
'findAll(): invalid selector provided "'
+
selector
+
'":'
+
e
,
"error"
);
}
};
/**
* Finds a DOM element by the provided selector.
*
* @param String selector CSS3 selector
* @return HTMLElement|undefined
*/
this
.
findOne
=
function
(
selector
)
{
try
{
return
document
.
querySelector
(
selector
);
}
catch
(
e
)
{
this
.
log
(
'findOne(): invalid selector provided "'
+
selector
+
'":'
+
e
,
"errors"
);
}
};
/**
* Finds a DOM element by the provided selector.
*
* @param String selector CSS3 selector
* @return HTMLElement|undefined
*/
this
.
findOne
=
function
(
selector
)
{
try
{
return
document
.
querySelector
(
selector
);
}
catch
(
e
)
{
this
.
log
(
'findOne(): invalid selector provided "'
+
selector
+
'":'
+
e
,
"errors"
);
}
};
/**
* Downloads a resource behind an url and returns its base64-encoded
* contents.
*
* @param String url The resource url
* @param String method The request method, optional (default: GET)
* @param Object data The request data, optional
* @return String Base64 contents string
*/
this
.
getBase64
=
function
(
url
,
method
,
data
)
{
return
this
.
encode
(
this
.
getBinary
(
url
,
method
,
data
));
};
/**
* Downloads a resource behind an url and returns its base64-encoded
* contents.
*
* @param String url The resource url
* @param String method The request method, optional (default: GET)
* @param Object data The request data, optional
* @return String Base64 contents string
*/
this
.
getBase64
=
function
(
url
,
method
,
data
)
{
return
this
.
encode
(
this
.
getBinary
(
url
,
method
,
data
));
};
/**
* Retrieves string contents from a binary file behind an url. Silently
* fails but log errors.
*
* @param String url
* @param String method
* @param Object data
* @return string
*/
this
.
getBinary
=
function
(
url
,
method
,
data
)
{
try
{
var
xhr
=
new
XMLHttpRequest
(),
dataString
=
""
;
if
(
typeof
method
!==
"string"
||
[
"GET"
,
"POST"
].
indexOf
(
method
.
toUpperCase
())
===
-
1
)
{
method
=
"GET"
;
}
else
{
method
=
method
.
toUpperCase
();
}
xhr
.
open
(
method
,
url
,
false
);
this
.
log
(
"getBinary(): Using HTTP method: '"
+
method
+
"'"
,
"debug"
);
xhr
.
overrideMimeType
(
"text/plain; charset=x-user-defined"
);
if
(
method
===
"POST"
)
{
if
(
typeof
data
===
"object"
)
{
var
dataList
=
[];
for
(
var
k
in
data
)
{
dataList
.
push
(
escape
(
k
)
+
"="
+
escape
(
data
[
k
].
toString
()));
/**
* Retrieves string contents from a binary file behind an url. Silently
* fails but log errors.
*
* @param String url
* @param String method
* @param Object data
* @return string
*/
this
.
getBinary
=
function
(
url
,
method
,
data
)
{
try
{
var
xhr
=
new
XMLHttpRequest
(),
dataString
=
""
;
if
(
typeof
method
!==
"string"
||
[
"GET"
,
"POST"
].
indexOf
(
method
.
toUpperCase
())
===
-
1
)
{
method
=
"GET"
;
}
else
{
method
=
method
.
toUpperCase
();
}
xhr
.
open
(
method
,
url
,
false
);
this
.
log
(
"getBinary(): Using HTTP method: '"
+
method
+
"'"
,
"debug"
);
xhr
.
overrideMimeType
(
"text/plain; charset=x-user-defined"
);
if
(
method
===
"POST"
)
{
if
(
typeof
data
===
"object"
)
{
var
dataList
=
[];
for
(
var
k
in
data
)
{
dataList
.
push
(
escape
(
k
)
+
"="
+
escape
(
data
[
k
].
toString
()));
}
dataString
=
dataList
.
join
(
'&'
);
this
.
log
(
"getBinary(): Using request data: '"
+
dataString
+
"'"
,
"debug"
);
}
dataString
=
dataList
.
join
(
'&'
);
this
.
log
(
"getBinary(): Using request data: '"
+
dataString
+
"'"
,
"debug"
);
xhr
.
setRequestHeader
(
"Content-type"
,
"application/x-www-form-urlencoded"
);
}
xhr
.
setRequestHeader
(
"Content-type"
,
"application/x-www-form-urlencoded"
);
}
xhr
.
send
(
method
===
"POST"
?
dataString
:
null
);
return
xhr
.
responseText
;
}
catch
(
e
)
{
if
(
e
.
name
===
"NETWORK_ERR"
&&
e
.
code
===
101
)
{
this
.
log
(
"getBinary(): Unfortunately, casperjs cannot make cross domain ajax requests"
,
"warning"
);
xhr
.
send
(
method
===
"POST"
?
dataString
:
null
);
return
xhr
.
responseText
;
}
catch
(
e
)
{
if
(
e
.
name
===
"NETWORK_ERR"
&&
e
.
code
===
101
)
{
this
.
log
(
"getBinary(): Unfortunately, casperjs cannot make cross domain ajax requests"
,
"warning"
);
}
this
.
log
(
"getBinary(): Error while fetching "
+
url
+
": "
+
e
,
"error"
);
return
""
;
}
this
.
log
(
"getBinary(): Error while fetching "
+
url
+
": "
+
e
,
"error"
);
return
""
;
}
};
};
/**
* Logs a message.
*
* @param String message
* @param String level
*/
this
.
log
=
function
(
message
,
level
)
{
console
.
log
(
"[casper:"
+
(
level
||
"debug"
)
+
"] "
+
message
);
};
/**
* Logs a message.
*
* @param String message
* @param String level
*/
this
.
log
=
function
(
message
,
level
)
{
console
.
log
(
"[casper:"
+
(
level
||
"debug"
)
+
"] "
+
message
);
};
/**
* Sets a field (or a set of fields) value. Fails silently, but log
* error messages.
*
* @param HTMLElement|NodeList field One or more element defining a field
* @param mixed value The field value to set
*/
this
.
setField
=
function
(
field
,
value
)
{
var
fields
,
out
;
value
=
value
||
""
;
if
(
field
instanceof
NodeList
)
{
fields
=
field
;
field
=
fields
[
0
];
}
if
(
!
field
instanceof
HTMLElement
)
{
this
.
log
(
"invalid field type; only HTMLElement and NodeList are supported"
,
"error"
);
}
this
.
log
(
'set "'
+
field
.
getAttribute
(
'name'
)
+
'" field value to '
+
value
,
"debug"
);
try
{
field
.
focus
();
}
catch
(
e
)
{
__utils__
.
log
(
"Unable to focus() input field "
+
field
.
getAttribute
(
'name'
)
+
": "
+
e
,
"warning"
);
}
var
nodeName
=
field
.
nodeName
.
toLowerCase
();
switch
(
nodeName
)
{
case
"input"
:
var
type
=
field
.
getAttribute
(
'type'
)
||
"text"
;
switch
(
type
.
toLowerCase
())
{
case
"color"
:
case
"date"
:
case
"datetime"
:
case
"datetime-local"
:
case
"email"
:
case
"hidden"
:
case
"month"
:
case
"number"
:
case
"password"
:
case
"range"
:
case
"search"
:
case
"tel"
:
case
"text"
:
case
"time"
:
case
"url"
:
case
"week"
:
field
.
value
=
value
;
break
;
case
"checkbox"
:
if
(
fields
.
length
>
1
)
{
var
values
=
value
;
if
(
!
Array
.
isArray
(
values
))
{
values
=
[
values
];
/**
* Sets a field (or a set of fields) value. Fails silently, but log
* error messages.
*
* @param HTMLElement|NodeList field One or more element defining a field
* @param mixed value The field value to set
*/
this
.
setField
=
function
(
field
,
value
)
{
var
fields
,
out
;
value
=
value
||
""
;
if
(
field
instanceof
NodeList
)
{
fields
=
field
;
field
=
fields
[
0
];
}
if
(
!
field
instanceof
HTMLElement
)
{
this
.
log
(
"invalid field type; only HTMLElement and NodeList are supported"
,
"error"
);
}
this
.
log
(
'set "'
+
field
.
getAttribute
(
'name'
)
+
'" field value to '
+
value
,
"debug"
);
try
{
field
.
focus
();
}
catch
(
e
)
{
__utils__
.
log
(
"Unable to focus() input field "
+
field
.
getAttribute
(
'name'
)
+
": "
+
e
,
"warning"
);
}
var
nodeName
=
field
.
nodeName
.
toLowerCase
();
switch
(
nodeName
)
{
case
"input"
:
var
type
=
field
.
getAttribute
(
'type'
)
||
"text"
;
switch
(
type
.
toLowerCase
())
{
case
"color"
:
case
"date"
:
case
"datetime"
:
case
"datetime-local"
:
case
"email"
:
case
"hidden"
:
case
"month"
:
case
"number"
:
case
"password"
:
case
"range"
:
case
"search"
:
case
"tel"
:
case
"text"
:
case
"time"
:
case
"url"
:
case
"week"
:
field
.
value
=
value
;
break
;
case
"checkbox"
:
if
(
fields
.
length
>
1
)
{
var
values
=
value
;
if
(
!
Array
.
isArray
(
values
))
{
values
=
[
values
];
}
Array
.
prototype
.
forEach
.
call
(
fields
,
function
(
f
)
{
f
.
checked
=
values
.
indexOf
(
f
.
value
)
!==
-
1
?
true
:
false
;
});
}
else
{
field
.
checked
=
value
?
true
:
false
;
}
Array
.
prototype
.
forEach
.
call
(
fields
,
function
(
f
)
{
f
.
checked
=
values
.
indexOf
(
f
.
value
)
!==
-
1
?
true
:
false
;
});
}
else
{
field
.
checked
=
value
?
true
:
false
;
}
break
;
case
"file"
:
throw
{
name
:
"FileUploadError"
,
message
:
"file field must be filled using page.uploadFile"
,
path
:
value
};
case
"radio"
:
if
(
fields
)
{
Array
.
prototype
.
forEach
.
call
(
fields
,
function
(
e
)
{
e
.
checked
=
(
e
.
value
===
value
);
});
}
else
{
out
=
'provided radio elements are empty'
;
}
break
;
default
:
out
=
"unsupported input field type: "
+
type
;
break
;
}
break
;
case
"select"
:
case
"textarea"
:
field
.
value
=
value
;
break
;
default
:
out
=
'unsupported field type: '
+
nodeName
;
break
;
}
try
{
field
.
blur
();
}
catch
(
err
)
{
__utils__
.
log
(
"Unable to blur() input field "
+
field
.
getAttribute
(
'name'
)
+
": "
+
err
,
"warning"
);
}
return
out
;
break
;
case
"file"
:
throw
{
name
:
"FileUploadError"
,
message
:
"file field must be filled using page.uploadFile"
,
path
:
value
};
case
"radio"
:
if
(
fields
)
{
Array
.
prototype
.
forEach
.
call
(
fields
,
function
(
e
)
{
e
.
checked
=
(
e
.
value
===
value
);
});
}
else
{
out
=
'provided radio elements are empty'
;
}
break
;
default
:
out
=
"unsupported input field type: "
+
type
;
break
;
}
break
;
case
"select"
:
case
"textarea"
:
field
.
value
=
value
;
break
;
default
:
out
=
'unsupported field type: '
+
nodeName
;
break
;
}
try
{
field
.
blur
();
}
catch
(
err
)
{
__utils__
.
log
(
"Unable to blur() input field "
+
field
.
getAttribute
(
'name'
)
+
": "
+
err
,
"warning"
);
}
return
out
;
};
};
};
}
)(
exports
||
{})
;
...
...
lib/colorizer.js
View file @
301d634
...
...
@@ -26,12 +26,9 @@
*
*/
exports
.
create
=
create
;
exports
.
Colorizer
=
Colorizer
;
function
create
()
{
exports
.
create
=
function
()
{
return
new
Colorizer
();
}
}
;
/**
* This is a port of lime colorizer.
...
...
@@ -95,3 +92,4 @@ var Colorizer = function() {
return
"\033["
+
codes
.
join
(
';'
)
+
'm'
+
text
+
"\033[0m"
;
};
};
exports
.
Colorizer
=
Colorizer
;
...
...
lib/injector.js
View file @
301d634
...
...
@@ -27,15 +27,15 @@
*/
exports
.
create
=
create
;
exports
.
FunctionArgsInjector
=
FunctionArgsInjector
;
function
create
(
fn
)
{
exports
.
create
=
function
(
fn
)
{
return
new
FunctionArgsInjector
(
fn
);
}
}
;
/**
* Function argument injector.
*
* FIXME: use new Function() instead of eval()
*/
var
FunctionArgsInjector
=
function
(
fn
)
{
if
(
!
isType
(
fn
,
"function"
))
{
...
...
@@ -82,3 +82,4 @@ var FunctionArgsInjector = function(fn) {
return
inject
.
join
(
'\n'
)
+
'\n'
;
};
};
exports
.
FunctionArgsInjector
=
FunctionArgsInjector
;
...
...
lib/tester.js
View file @
301d634
...
...
@@ -27,10 +27,11 @@
*/
var
fs
=
require
(
'fs'
);
var
utils
=
require
(
'./lib/utils'
);
function
create
(
casper
,
options
)
{
exports
.
create
=
function
(
casper
,
options
)
{
return
new
Tester
(
casper
,
options
);
}
}
;
/**
* Casper tester: makes assertions, stores test results and display then.
...
...
@@ -39,9 +40,9 @@ function create(casper, options) {
var
Tester
=
function
(
casper
,
options
)
{
this
.
running
=
false
;
this
.
suites
=
[];
this
.
options
=
isType
(
options
,
"object"
)
?
options
:
{};
this
.
options
=
utils
.
isType
(
options
,
"object"
)
?
options
:
{};
if
(
!
casper
instanceof
require
(
'./lib/casper'
).
Casper
)
{
if
(
!
utils
.
isCasperObject
(
casper
)
)
{
throw
new
Error
(
"Tester needs a Casper instance"
);
}
...
...
@@ -219,7 +220,7 @@ var Tester = function(casper, options) {
* @param String message Test description
*/
this
.
assertType
=
function
(
input
,
type
,
message
)
{
return
this
.
assertEquals
(
betterTypeOf
(
input
),
type
,
message
);
return
this
.
assertEquals
(
utils
.
betterTypeOf
(
input
),
type
,
message
);
};
/**
...
...
@@ -368,7 +369,7 @@ var Tester = function(casper, options) {
* @param Boolean exit
*/
this
.
renderResults
=
function
(
exit
,
status
,
save
)
{
save
=
isType
(
save
,
"string"
)
?
save
:
this
.
options
.
save
;
save
=
utils
.
isType
(
save
,
"string"
)
?
save
:
this
.
options
.
save
;
var
total
=
this
.
testResults
.
passed
+
this
.
testResults
.
failed
,
statusText
,
style
,
result
;
if
(
this
.
testResults
.
failed
>
0
)
{
statusText
=
FAIL
;
...
...
@@ -379,7 +380,7 @@ var Tester = function(casper, options) {
}
result
=
statusText
+
' '
+
total
+
' tests executed, '
+
this
.
testResults
.
passed
+
' passed, '
+
this
.
testResults
.
failed
+
' failed.'
;
casper
.
echo
(
this
.
colorize
(
fillBlanks
(
result
),
style
));
if
(
save
&&
isType
(
require
,
"function"
))
{
if
(
save
&&
utils
.
isType
(
require
,
"function"
))
{
try
{
fs
.
write
(
save
,
exporter
.
getXML
(),
'w'
);
casper
.
echo
(
'result log stored in '
+
save
,
'INFO'
);
...
...
@@ -455,10 +456,10 @@ var Tester = function(casper, options) {
* @param Boolean
*/
this
.
testEquals
=
function
(
v1
,
v2
)
{
if
(
betterTypeOf
(
v1
)
!==
betterTypeOf
(
v2
))
{
if
(
utils
.
betterTypeOf
(
v1
)
!==
utils
.
betterTypeOf
(
v2
))
{
return
false
;
}
if
(
isType
(
v1
,
"function"
))
{
if
(
utils
.
isType
(
v1
,
"function"
))
{
return
v1
.
toString
()
===
v2
.
toString
();
}
if
(
v1
instanceof
Object
&&
v2
instanceof
Object
)
{
...
...
@@ -475,3 +476,4 @@ var Tester = function(casper, options) {
return
v1
===
v2
;
};
};
exports
.
Tester
=
Tester
;
...
...
lib/utils.js
View file @
301d634
...
...
@@ -41,105 +41,7 @@ function betterTypeOf(input) {
return
typeof
input
;
}
}
/**
* Creates a new WebPage instance for Casper use.
*
* @param Casper casper A Casper instance
* @return WebPage
*/
function
createPage
(
casper
)
{
var
page
;
if
(
phantom
.
version
.
major
<=
1
&&
phantom
.
version
.
minor
<
3
&&
isType
(
require
,
"function"
))
{
page
=
new
WebPage
();
}
else
{
page
=
require
(
'webpage'
).
create
();
}
page
.
onAlert
=
function
(
message
)
{
casper
.
log
(
'[alert] '
+
message
,
"info"
,
"remote"
);
if
(
isType
(
casper
.
options
.
onAlert
,
"function"
))
{
casper
.
options
.
onAlert
.
call
(
casper
,
casper
,
message
);
}
};
page
.
onConsoleMessage
=
function
(
msg
)
{
var
level
=
"info"
,
test
=
/^
\[
casper:
(\w
+
)\]\s?(
.*
)
/
.
exec
(
msg
);
if
(
test
&&
test
.
length
===
3
)
{
level
=
test
[
1
];
msg
=
test
[
2
];
}
casper
.
log
(
msg
,
level
,
"remote"
);
};
page
.
onLoadStarted
=
function
()
{
casper
.
resources
=
[];
casper
.
loadInProgress
=
true
;
};
page
.
onLoadFinished
=
function
(
status
)
{
if
(
status
!==
"success"
)
{
var
message
=
'Loading resource failed with status='
+
status
;
if
(
casper
.
currentHTTPStatus
)
{
message
+=
' (HTTP '
+
casper
.
currentHTTPStatus
+
')'
;
}
message
+=
': '
+
casper
.
requestUrl
;
casper
.
log
(
message
,
"warning"
);
if
(
isType
(
casper
.
options
.
onLoadError
,
"function"
))
{
casper
.
options
.
onLoadError
.
call
(
casper
,
casper
,
casper
.
requestUrl
,
status
);
}
}
if
(
casper
.
options
.
clientScripts
)
{
if
(
betterTypeOf
(
casper
.
options
.
clientScripts
)
!==
"array"
)
{
casper
.
log
(
"The clientScripts option must be an array"
,
"error"
);
}
else
{
for
(
var
i
=
0
;
i
<
casper
.
options
.
clientScripts
.
length
;
i
++
)
{
var
script
=
casper
.
options
.
clientScripts
[
i
];
if
(
casper
.
page
.
injectJs
(
script
))
{
casper
.
log
(
'Automatically injected '
+
script
+
' client side'
,
"debug"
);
}
else
{
casper
.
log
(
'Failed injecting '
+
script
+
' client side'
,
"warning"
);
}
}
}
}
// Client-side utils injection
var
injected
=
page
.
evaluate
(
replaceFunctionPlaceholders
(
function
()
{
eval
(
"var ClientUtils = "
+
decodeURIComponent
(
"%utils%"
));
__utils__
=
new
ClientUtils
();
return
__utils__
instanceof
ClientUtils
;
},
{
utils
:
encodeURIComponent
(
require
(
'./lib/clientutils'
).
ClientUtils
.
toString
())
}));
if
(
!
injected
)
{
casper
.
log
(
"Failed to inject Casper client-side utilities!"
,
"warning"
);
}
else
{
casper
.
log
(
"Successfully injected Casper client-side utilities"
,
"debug"
);
}
// history
casper
.
history
.
push
(
casper
.
getCurrentUrl
());
casper
.
loadInProgress
=
false
;
};
page
.
onResourceReceived
=
function
(
resource
)
{
if
(
isType
(
casper
.
options
.
onResourceReceived
,
"function"
))
{
casper
.
options
.
onResourceReceived
.
call
(
casper
,
casper
,
resource
);
}
if
(
resource
.
stage
===
"end"
)
{
casper
.
resources
.
push
(
resource
);
}
if
(
resource
.
url
===
casper
.
requestUrl
&&
resource
.
stage
===
"start"
)
{
casper
.
currentHTTPStatus
=
resource
.
status
;
if
(
isType
(
casper
.
options
.
httpStatusHandlers
,
"object"
)
&&
resource
.
status
in
casper
.
options
.
httpStatusHandlers
&&
isType
(
casper
.
options
.
httpStatusHandlers
[
resource
.
status
],
"function"
))
{
casper
.
options
.
httpStatusHandlers
[
resource
.
status
].
call
(
casper
,
casper
,
resource
);
}
casper
.
currentUrl
=
resource
.
url
;
}
};
page
.
onResourceRequested
=
function
(
request
)
{
if
(
isType
(
casper
.
options
.
onResourceRequested
,
"function"
))
{
casper
.
options
.
onResourceRequested
.
call
(
casper
,
casper
,
request
);
}
};
return
page
;
}
exports
.
betterTypeOf
=
betterTypeOf
;
/**
* Dumps a JSON representation of passed value to the console. Used for
...
...
@@ -150,6 +52,7 @@ function createPage(casper) {
function
dump
(
value
)
{
console
.
log
(
serialize
(
value
));
}
exports
.
dump
=
dump
;
/**
* Returns the file extension in lower case.
...
...
@@ -164,6 +67,7 @@ function fileExt(file) {
return
''
;
}
}
exports
.
fileExt
=
fileExt
;
/**
* Takes a string and append blank until the pad value is reached.
...
...
@@ -179,6 +83,18 @@ function fillBlanks(text, pad) {
}
return
text
;
}
exports
.
fillBlanks
=
fillBlanks
;
/**
* Checks if passed argument is an instance of Capser object.
*
* @param mixed value
* @return Boolean
*/
function
isCasperObject
(
value
)
{
return
value
instanceof
require
(
'./lib/casper'
).
Casper
;
}
exports
.
isCasperObject
=
isCasperObject
;
/**
* Checks if a file is apparently javascript compatible (.js or .coffee).
...
...
@@ -190,6 +106,7 @@ function isJsFile(file) {
var
ext
=
fileExt
(
file
);
return
isType
(
ext
,
"string"
)
&&
[
'js'
,
'coffee'
].
indexOf
(
ext
)
!==
-
1
;
}
exports
.
isJsFile
=
isJsFile
;
/**
* Shorthands for checking if a value is of the given type. Can check for
...
...
@@ -202,6 +119,7 @@ function isJsFile(file) {
function
isType
(
what
,
typeName
)
{
return
betterTypeOf
(
what
)
===
typeName
;
}
exports
.
isType
=
isType
;
/**
* Checks if the provided var is a WebPage instance
...
...
@@ -219,6 +137,7 @@ function isWebPage(what) {
return
what
.
toString
().
indexOf
(
'WebPage('
)
===
0
;
}
}
exports
.
isWebPage
=
isWebPage
;
/**
* Object recursive merging utility.
...
...
@@ -241,6 +160,7 @@ function mergeObjects(obj1, obj2) {
}
return
obj1
;
}
exports
.
mergeObjects
=
mergeObjects
;
/**
* Replaces a function string contents with placeholders provided by an
...
...
@@ -262,6 +182,7 @@ function replaceFunctionPlaceholders(fn, replacements) {
}
return
fn
;
}
exports
.
replaceFunctionPlaceholders
=
replaceFunctionPlaceholders
;
/**
* Serializes a value using JSON.
...
...
@@ -277,3 +198,4 @@ function serialize(value) {
}
return
JSON
.
stringify
(
value
,
null
,
4
);
}
exports
.
serialize
=
serialize
;
...
...
lib/xunit.js
View file @
301d634
...
...
@@ -26,12 +26,9 @@
*
*/
exports
.
create
=
create
;
exports
.
XUnitExporter
=
XUnitExporter
;
function
create
()
{
exports
.
create
=
function
()
{
return
new
XUnitExporter
();
}
}
;
/**
* JUnit XML (xUnit) exporter for test results.
...
...
@@ -97,3 +94,5 @@ XUnitExporter = function() {
return
xml
;
};
};
exports
.
XUnitExporter
=
XUnitExporter
;
...
...
Please
register
or
sign in
to post a comment