mirror of https://github.com/zulip/zulip.git
Import CasperJS 1.0.0-RC4-0-g8c798c7
(imported from commit b24184d9de034ecf4054dbc72cd6c28b49309182)
This commit is contained in:
parent
488370b681
commit
921f7832c5
|
@ -0,0 +1,4 @@
|
||||||
|
.DS_Store
|
||||||
|
*.xml
|
||||||
|
*.png
|
||||||
|
/tmp
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "docs"]
|
||||||
|
path = docs
|
||||||
|
url = git://github.com/n1k0/casperjs.git
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"asi": true,
|
||||||
|
"browser": true,
|
||||||
|
"debug": true,
|
||||||
|
"devel": true,
|
||||||
|
"eqeqeq": true,
|
||||||
|
"evil": true,
|
||||||
|
"maxparams": 5,
|
||||||
|
"maxdepth": 3,
|
||||||
|
"maxstatements": 15,
|
||||||
|
"maxcomplexity": 7,
|
||||||
|
"regexdash": true,
|
||||||
|
"strict": true,
|
||||||
|
"sub": true,
|
||||||
|
"trailing": true,
|
||||||
|
"undef": true,
|
||||||
|
|
||||||
|
"predef" : [
|
||||||
|
"exports",
|
||||||
|
"phantom",
|
||||||
|
"require",
|
||||||
|
"window"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
docs
|
||||||
|
modules/vendors
|
||||||
|
modules/events.js
|
||||||
|
modules/querystring.js
|
||||||
|
samples
|
||||||
|
tests/site
|
||||||
|
tests/testdir
|
|
@ -0,0 +1,14 @@
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
before_script:
|
||||||
|
- "phantomjs --version"
|
||||||
|
- "export PHANTOMJS_EXECUTABLE='phantomjs --local-to-remote-url-access=yes --ignore-ssl-errors=yes'"
|
||||||
|
- "export DISPLAY=:99.0"
|
||||||
|
- "sh -e /etc/init.d/xvfb start"
|
||||||
|
script:
|
||||||
|
- "DISPLAY=:99.0 ./bin/casperjs selftest"
|
||||||
|
notifications:
|
||||||
|
irc:
|
||||||
|
channels:
|
||||||
|
- "irc.freenode.org#casperjs"
|
|
@ -0,0 +1,298 @@
|
||||||
|
CasperJS Changelog
|
||||||
|
==================
|
||||||
|
|
||||||
|
2012-10-31, v1.0.0-RC4
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Next version should be 1.0.0 stable.
|
||||||
|
|
||||||
|
- fixed [#261](https://github.com/n1k0/casperjs/issues/261) - Impossible to require CoffeeScript modules
|
||||||
|
- fixed [#262](https://github.com/n1k0/casperjs/issues/262) - Injecting clientScripts is not working
|
||||||
|
- fixed [#259](https://github.com/n1k0/casperjs/issues/259) - enhanced `Tester.assertField()` method, which can now tests for other field types than `input`s.
|
||||||
|
- fixed `Casper.getCurrentUrl()` could misbehave with encoded urls
|
||||||
|
- added [`Casper.echo()`](http://casperjs.org/api.html#clientutils.echo) to print a message to the casper console from the remote DOM environment
|
||||||
|
- added [`Casper.waitForText()`](http://casperjs.org/api.html#casper.waitForText) to wait for a given text to be present in page HTML contents
|
||||||
|
- added [`ClientUtils.getFieldValue()`](http://casperjs.org/api.html#clientutils.getFieldValue)
|
||||||
|
- Local CoffeeScript version has been upgraded to 1.4.0
|
||||||
|
|
||||||
|
2012-10-23, v1.0.0-RC3
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
### Important Changes & Caveats
|
||||||
|
|
||||||
|
- the `injector` module is now deprecated, but kept for backward compatibility purpose.
|
||||||
|
- **BC BREAK**: fixes [#220](https://github.com/n1k0/casperjs/issues/220), [#237](https://github.com/n1k0/casperjs/issues/237) - added a `waitTimeout` options, removed `defaultWaitTimeout` option.
|
||||||
|
- **BC BREAK** (for the better): fixes [#249](https://github.com/n1k0/casperjs/issues/249) - default timeout functions don't `die()` anymore in tests
|
||||||
|
- **BC BREAK** (for the better): merged [#188](https://github.com/n1k0/casperjs/issues/188) - Easy access to current response object;
|
||||||
|
You can now access the current response object as the first parameter of step callbacks:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
require('casper').create().start('http://www.google.fr/', function(response) {
|
||||||
|
require('utils').dump(response);
|
||||||
|
}).run();
|
||||||
|
```
|
||||||
|
|
||||||
|
That gives:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ casperjs dump-headers.js
|
||||||
|
{
|
||||||
|
"contentType": "text/html; charset=UTF-8",
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"name": "Date",
|
||||||
|
"value": "Thu, 18 Oct 2012 08:17:29 GMT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Expires",
|
||||||
|
"value": "-1"
|
||||||
|
},
|
||||||
|
// ... lots of other headers
|
||||||
|
],
|
||||||
|
"id": 1,
|
||||||
|
"redirectURL": null,
|
||||||
|
"stage": "end",
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"time": "2012-10-18T08:17:37.068Z",
|
||||||
|
"url": "http://www.google.fr/"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To fetch a particular header by its name:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
require('casper').create().start('http://www.google.fr/', function(response) {
|
||||||
|
this.echo(response.headers.get('Date'));
|
||||||
|
}).run();
|
||||||
|
```
|
||||||
|
|
||||||
|
That gives:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$ casperjs dump-single-header.js
|
||||||
|
Thu, 18 Oct 2012 08:26:34 GMT
|
||||||
|
```
|
||||||
|
|
||||||
|
The documentation has been [updated accordingly](http://casperjs.org/api.html#casper.then.callbacks).
|
||||||
|
|
||||||
|
### Bugfixes & enhancements
|
||||||
|
|
||||||
|
- merged [#234](https://github.com/n1k0/casperjs/issues/234) - New Windows Loader written in Batch. Python is no more a requirement for using CasperJS on Windows. New installation instructions are [available](http://casperjs.org/installation.html#windows).
|
||||||
|
- a new `onWaitTimeout` option has been added, to allow defining a default behavior when a `waitFor*` function times out.
|
||||||
|
- [Casper.resourceExists()](http://casperjs.org/api.html#casper.resourceExists) and related functions now checks for non HTTP-404 received responses.
|
||||||
|
- fixed [#167](https://github.com/n1k0/casperjs/issues/167) - fixed opening truncated/uncomplete root urls may give erroneous HTTP statuses
|
||||||
|
- closes [#205](https://github.com/n1k0/casperjs/issues/205) - [`debugHTML()`](http://casperjs.org/api.html#casper.debugHTML) can have a selector passed; added [`getHTML()`](http://casperjs.org/api.html#casper.getHTML)
|
||||||
|
- closes [#230](https://github.com/n1k0/casperjs/issues/230) - added [`ClientUtils.getElementsBound()`](http://casperjs.org/api.html#clientutils.getElementsBounds) and [`Casper.getElementsBound()`](http://casperjs.org/api.html#casper.getElementsBounds)
|
||||||
|
- fixed [#235](https://github.com/n1k0/casperjs/issues/235) - updated `Casper.evaluate()` to use phantomjs >= 1.6 native one. As a consequence, **the `injector` module is marked as deprecated**.
|
||||||
|
- fixed [#250](https://github.com/n1k0/casperjs/issues/250) - prevent self tests to be run using the standard `casper test` command
|
||||||
|
- fixed [#254](https://github.com/n1k0/casperjs/issues/254) - fix up one use of qsa, hit when filling forms with missing elements
|
||||||
|
- [fixed](https://github.com/n1k0/casperjs/commit/ef6c1828c7b64e1cf99b98e27600d0b63308cad3) edge case when current document url couldn't be properly decoded
|
||||||
|
|
||||||
|
2012-10-01, v1.0.0-RC2
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
### Important Changes & Caveats
|
||||||
|
|
||||||
|
- **PhantomJS 1.6 is now the minimal requirement**, PhantomJS 1.7 is supported.
|
||||||
|
- CasperJS continues to ship with its own implementation of CommonJS' module pattern, due to the way it has to work to offer its own executable. While the implementations are nearly the same, **100% compatibility is not guaranteed**.
|
||||||
|
|
||||||
|
### Bugfixes & enhancements
|
||||||
|
|
||||||
|
- fixed [#119](https://github.com/n1k0/casperjs/issues/119) - `Casper.currentHTTPStatus` now defaults to `null` when resource are loaded using the `file://` protocol
|
||||||
|
- fixed [#130](https://github.com/n1k0/casperjs/issues/130) - added a `--no-colors` option to the `casper test` command to skip output coloration
|
||||||
|
- fixed [#153](https://github.com/n1k0/casperjs/issues/153) - erroneous mouse event results when `event.preventDefault()` was used.
|
||||||
|
- fixed [#164](https://github.com/n1k0/casperjs/issues/164) - ability to force CLI parameters as strings (see [related documentation](http://casperjs.org/cli.html#raw)).
|
||||||
|
- fixed [#178](https://github.com/n1k0/casperjs/issues/178) - added `Casper.getPageContent()` to access raw page body contents on non-html received content-types.
|
||||||
|
- fixed [#180](https://github.com/n1k0/casperjs/issues/180) - CasperJS tests are now run against a local HTTP test server. A new `casper selftest` command has been added as well.
|
||||||
|
- fixed [#189](https://github.com/n1k0/casperjs/issue/189) - fixed invalid XML due to message colorization
|
||||||
|
- fixed [#197](https://github.com/n1k0/casperjs/pull/197) & [#240](https://github.com/n1k0/casperjs/pull/240/) - Added new tester methods:
|
||||||
|
* [`assertField`](http://casperjs.org/api.html#tester.assertField)
|
||||||
|
* [`assertSelectorHasText`](http://casperjs.org/api.html#tester.assertSelectorHasText)
|
||||||
|
* [`assertSelectorDoesntHaveText`](http://casperjs.org/api.html#tester.assertSelectorDoesntHaveText)
|
||||||
|
* [`assertVisible`](http://casperjs.org/api.html#tester.assertVisible)
|
||||||
|
* [`assertNotVisible`](http://casperjs.org/api.html#tester.assertNotVisible)
|
||||||
|
- fixed [#202](https://github.com/n1k0/casperjs/pull/202) - Fix test status timeouts when running multiple suites
|
||||||
|
- fixed [#204](https://github.com/n1k0/casperjs/pull/204) - Fix for when the url is changed via javascript
|
||||||
|
- fixed [#210](https://github.com/n1k0/casperjs/pull/210) - Changed `escape` to `encodeURIComponent` for downloading binaries via POST
|
||||||
|
- fixed [#216](https://github.com/n1k0/casperjs/pull/216) - Change clientutils to be able to set a global scope
|
||||||
|
- fixed [#219](https://github.com/n1k0/casperjs/issues/219) - ease chaining of `run()` calls ([more explanations](https://groups.google.com/forum/#!topic/casperjs/jdQ-CrgnUd8))
|
||||||
|
- fixed [#222](https://github.com/n1k0/casperjs/pull/222) & [#211](https://github.com/n1k0/casperjs/issues/211) - Change mouse event to include an X + Y value for click position
|
||||||
|
- fixed [#231](https://github.com/n1k0/casperjs/pull/231) - added `--pre` and `--post` options to the `casperjs test` command to load test files before and after the execution of testsuite
|
||||||
|
- fixed [#232](https://github.com/n1k0/casperjs/issues/232) - symlink resolution in the ruby version of the `casperjs` executable
|
||||||
|
- fixed [#236](https://github.com/n1k0/casperjs/issues/236) - fixed `Casper.exit` returned `this` after calling `phantom.exit()` which may caused PhantomJS to hang
|
||||||
|
- fixed [#252](https://github.com/n1k0/casperjs/issues/252) - better form.fill() error handling
|
||||||
|
- added [`ClientUtils.getDocumentHeight()`](http://casperjs.org/api.html#clientutils.getDocumentHeight)
|
||||||
|
- added [`toString()`](http://casperjs.org/api.html#casper.toString) and [`status()`](http://casperjs.org/api.html#casper.status) methods to `Casper` prototype.
|
||||||
|
|
||||||
|
2012-06-26, v1.0.0-RC1
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
### PhantomJS 1.5 & 1.6
|
||||||
|
|
||||||
|
- fixed [#119](https://github.com/n1k0/casperjs/issues/119) - HTTP status wasn't properly caught
|
||||||
|
- fixed [#132](https://github.com/n1k0/casperjs/issues/132) - added ability to include js/coffee files using a dedicated option when using the [`casper test` command](http://casperjs.org/testing.html)
|
||||||
|
- fixed [#140](https://github.com/n1k0/casperjs/issues/140) - `casper test` now resolves local paths urls
|
||||||
|
- fixed [#148](https://github.com/n1k0/casperjs/issues/148) - [`utils.isWebPage()`](http://casperjs.org/api.html#utils.isWebPage) was broken
|
||||||
|
- fixed [#149](https://github.com/n1k0/casperjs/issues/149) - [`ClientUtils.fill()`](http://casperjs.org/api.html#casper.fill) was searching elements globally
|
||||||
|
- fixed [#154](https://github.com/n1k0/casperjs/issues/154) - firing the `change` event after a field value has been set
|
||||||
|
- fixed [#144](https://github.com/n1k0/casperjs/issues/144) - added a [`safeLogs` option](http://casperjs.org/api.html#casper.options) to blur password values in debug logs. **This option is set to `true` by default.**
|
||||||
|
- added [`Casper.userAgent()`](http://casperjs.org/api.html#casper.userAgent) to ease a more dynamic setting of user-agent string
|
||||||
|
- added [`Tester.assertTitleMatch()`](http://casperjs.org/api.html#tester.assertTitleMatch) method
|
||||||
|
- added [`utils.getPropertyPath()`](http://casperjs.org/api.html#utils.getPropertyPath)
|
||||||
|
- added [`Casper.captureBase64()`](http://casperjs.org/api.html#casper.captureBase64) for rendering screen captures as base64 strings - closes [#150](https://github.com/n1k0/casperjs/issues/150)
|
||||||
|
- added [`Casper.reload()`](http://casperjs.org/api.html#casper.reload)
|
||||||
|
- fixed failed test messages didn't expose the subject correctly
|
||||||
|
- switched to more standard `.textContent` property to get a node text; this allows a better compatibility of the clientutils bookmarklet with non-webkit browsers
|
||||||
|
- casper modules now all use [javascript strict mode](http://www.nczonline.net/blog/2012/03/13/its-time-to-start-using-javascript-strict-mode/)
|
||||||
|
|
||||||
|
### PhantomJS >= 1.6 supported features
|
||||||
|
|
||||||
|
- added support of custom headers sending in outgoing request - refs [#137](https://github.com/n1k0/casperjs/issues/137))
|
||||||
|
- added support for `prompt()` and `confirm()` - closes [#125](https://github.com/n1k0/casperjs/issues/125)
|
||||||
|
- fixed [#157](https://github.com/n1k0/casperjs/issues/157) - added support for PhantomJS 1.6 `WebPage#zoomFactor`
|
||||||
|
- added `url.changed` & `navigation.requested` events - refs [#151](https://github.com/n1k0/casperjs/issues/151)
|
||||||
|
|
||||||
|
2012-06-04, v0.6.10
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
- fixed [#73](https://github.com/n1k0/casperjs/issues/73) - `Casper.download()` not working correctly with binaries
|
||||||
|
- fixed [#129](https://github.com/n1k0/casperjs/issues/129) - Can't put `//` comments in evaluate() function
|
||||||
|
- closed [#130](https://github.com/n1k0/casperjs/issues/130) - Added a `Dummy` [colorizer](http://casperjs.org/api.html#colorizer) class, in order to disable colors in console output
|
||||||
|
- fixed [#133](https://github.com/n1k0/casperjs/issues/133) - updated and fixed documentation about [extensibility](http://casperjs.org/extending.html)
|
||||||
|
- added `Casper.clickLabel()` for clicking on an element found by its `innerText` content
|
||||||
|
|
||||||
|
As a side note, the official website monolithic page has been split across several ones: http://casperjs.org/
|
||||||
|
|
||||||
|
2012-05-29, v0.6.9
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- **BC BREAK:** PhantomJS 1.5 is now the minimal PhantomJS version supported.
|
||||||
|
- fixed [#114](https://github.com/n1k0/casperjs/issues/114) - ensured client-side utils are injected before any `evaluate()` call
|
||||||
|
- merged [#89](https://github.com/n1k0/casperjs/pull/89) - Support for more mouse events (@nrabinowitz)
|
||||||
|
- [added a new `error` event, better error reporting](https://github.com/n1k0/casperjs/commit/2e6988ae821b3251e063d11ba28af59b0683852a)
|
||||||
|
- fixed [#117](https://github.com/n1k0/casperjs/issues/117) - `fill()` coulnd't `submit()` a form with a submit input named *submit*
|
||||||
|
- merged [#122](https://github.com/n1k0/casperjs/pull/122) - allow downloads to be triggered by more than just `GET` requests
|
||||||
|
- closed [#57](https://github.com/n1k0/casperjs/issues/57) - added context to emitted test events + complete assertion framework refactor
|
||||||
|
- fixed loaded resources array is now reset adequately [reference discussion](https://groups.google.com/forum/?hl=fr?fromgroups#!topic/casperjs/TCkNzrj1IoA)
|
||||||
|
- fixed incomplete error message logged when passed an erroneous selector (xpath and css)
|
||||||
|
|
||||||
|
2012-05-20, v0.6.8
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- added support for [XPath selectors](http://casperjs.org/#selectors)
|
||||||
|
- added `Tester.assertNotEquals()` ([@juliangruber](https://github.com/juliangruber))
|
||||||
|
- fixed [#109](https://github.com/n1k0/casperjs/issues/109) - CLI args containing `=` (equals sign) were not being parsed properly
|
||||||
|
|
||||||
|
2012-05-12, v0.6.7
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- fixes [#107](https://github.com/n1k0/casperjs/issues/107): client utils were possibly not yet being injected and available when calling `Capser.base64encode()` from some events
|
||||||
|
- merged [PR #96](https://github.com/n1k0/casperjs/pull/96): make python launcher use `os.execvp()` instead of `subprocess.Popen()` ([@jart](https://github.com/jart)):
|
||||||
|
> This patch fixes a bug where casperjs' python launcher process won't pass along kill
|
||||||
|
> signals to the phantomjs subprocess. This patch works by using an exec system call
|
||||||
|
> which causes the phantomjs subprocess to completely replace the casperjs parent
|
||||||
|
> process (while maintaining the same pid). This patch also has the added benefit of
|
||||||
|
> saving 10 megs or so of memory because the python process is discarded.
|
||||||
|
- fixes [#109](https://github.com/n1k0/casperjs/issues/109) - CLI args containing `=` (equals sign) were not parsed properly
|
||||||
|
- fixes [#100](https://github.com/n1k0/casperjs/issues/100) & [#110](https://github.com/n1k0/casperjs/issues/110) - *googlepagination* sample was broken
|
||||||
|
- merged #103 - added `Tester.assertNotEquals` method (@juliangruber)
|
||||||
|
|
||||||
|
2012-04-27, v0.6.6
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- **BC BREAK:**: moved the `page.initialized` event to where it should have always been, and is now using native phantomjs `onInitialized` event
|
||||||
|
- fixed [#95](https://github.com/n1k0/casperjs/issues/95) - `Tester.assertSelectorExists` was broken
|
||||||
|
|
||||||
|
2012-03-28, v0.6.5
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- **BC BREAK:** reverted 8347278 (refs [#34](https://github.com/n1k0/casperjs/issues/34) and added a new `clear()` method to *close* a page
|
||||||
|
You now have to call `casper.clear()` if you want to stop javascript execution within the remote DOM environment.
|
||||||
|
- **BC BREAK:** removed `fallbackToHref` option handling in `ClientUtils.click()` (refs [#63](https://github.com/n1k0/casperjs/issues/63))
|
||||||
|
- `tester.findTestFiles()` now returns results in predictable order
|
||||||
|
- added `--log-level` and `--direct` options to `casper test` command
|
||||||
|
- fixed 0.6.4 version number in `bootstrap.js`
|
||||||
|
- centralized version number to package.json
|
||||||
|
- ensured compatibility with PhantomJS 1.5
|
||||||
|
|
||||||
|
2012-02-09, v0.6.4
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- fixed `casperjs` command wasn't passing phantomjs native option in the correct order, resulting them not being taken into account by phantomjs engine:
|
||||||
|
- fixed [#49](https://github.com/n1k0/casperjs/issues/49) - `casperjs` is not sending `--ssl-ignore-errors`
|
||||||
|
- fixed [#50](https://github.com/n1k0/casperjs/issues/50) - Cookies not being set when passing `--cookies-file` option
|
||||||
|
- fixed Python3 compatibility of the `casperjs` executable
|
||||||
|
|
||||||
|
2012-02-05, v0.6.3
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- fixed [#48](https://github.com/n1k0/casperjs/issues/48) - XML Output file doesn't have classpath populated with file name
|
||||||
|
- refs [#46](https://github.com/n1k0/casperjs/issues/46) - added value details to Tester `fail` event
|
||||||
|
- new site design, new [domain](http://casperjs.org/), enhanced & updated docs
|
||||||
|
|
||||||
|
2012-01-19, v0.6.2
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- fixed [#41](https://github.com/n1k0/casperjs/issues/41) - injecting casperjs lib crashes `cmd.exe` on Windows 7
|
||||||
|
- fixed [#42](https://github.com/n1k0/casperjs/issues/42) - Use file name of test script as 'classname' in JUnit XML report (@mpeltonen)
|
||||||
|
- fixed [#43](https://github.com/n1k0/casperjs/issues/43) - Exit status not reported back to caller
|
||||||
|
- suppressed colorized output syntax for windows; was making output hard to read
|
||||||
|
- added patchy `fs.isWindows()` method
|
||||||
|
- added `--xunit=<filename>` cli option to `$ casperjs test` command for saving xunit results, eg.:
|
||||||
|
|
||||||
|
$ casperjs test tests/suites --xunit=build-result.xml
|
||||||
|
|
||||||
|
|
||||||
|
2012-01-16, v0.6.1
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- restablished js-emulated click simulation first, then native QtWebKit
|
||||||
|
events as a fallback; some real world testing have surprinsingly proven the former being often
|
||||||
|
more efficient than the latter
|
||||||
|
- fixed casperjs executable could not handle a `PHANTOMJS_EXECUTABLE` containing spaces
|
||||||
|
- fixed casper could not be used without the executable [as documented](http://casperjs.org/#faq-executable)
|
||||||
|
- fixed wrong `debug` log level on `ClientUtils.click()` error; set to `error`
|
||||||
|
|
||||||
|
Please check the [updated documentation](http://casperjs.org).
|
||||||
|
|
||||||
|
2012-01-12, v0.6.0
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- **BC BREAK:** `Casper.click()` now uses native Webkit mouse events instead of previous crazy utopic javascript emulation
|
||||||
|
- **BC BREAK:** All errors thrown by CasperJS core are of the new `CasperError` type
|
||||||
|
- **BC BREAK:** removed obsolete `replaceFunctionPlaceholders()`
|
||||||
|
- *Deprecated*: `Casper.extend()` method has been deprecated; use natural javascript extension mechanisms instead (see samples)
|
||||||
|
- added `$ casperjs test` command for running split test suites
|
||||||
|
- `Casper.open()` can now perform HTTP `GET`, `POST`, `PUT`, `DELETE` and `HEAD` operations
|
||||||
|
- commonjs/nodejs-like module exports implementation
|
||||||
|
- ported nodejs' `events` module to casperjs; lots of events added, plus some value filtering capabilities
|
||||||
|
- introduced the `mouse` module to handle native Webkit mouse events
|
||||||
|
- added support for `RegExp` input in `Casper.resourceExists()`
|
||||||
|
- added printing of source file path for any uncaught exception printed onto the console
|
||||||
|
- added an emulation of stack trace printing (but PhantomJS will have to upgrade its javascript engine for it to be fully working though)
|
||||||
|
|
||||||
|
Please check the [updated documentation](http://casperjs.org).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
2011-12-25, v0.4.2
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- merged PR #30 - Add request method and request data to the `base64encode()` method (@jasonlfunk)
|
||||||
|
- `casperjs` executable now gracefully exists on KeyboardInterrupt
|
||||||
|
- added `Casper.download()` method, for downloading any resource and save it onto the filesystem
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
2011-12-21, v0.4.1
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- fixed #31 - replaced bash executable script by a Python one
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
2011-12-20, v0.4.0
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- first numbered version
|
|
@ -0,0 +1,93 @@
|
||||||
|
# Contribution Guide
|
||||||
|
|
||||||
|
*Note: this guide has been heavily inspired by [PhantomJS' one](https://github.com/ariya/phantomjs/blob/master/CONTRIBUTING.md).*
|
||||||
|
|
||||||
|
**This page describes how to contribute changes to the [CasperJS](http://casperjs.org/) project.**
|
||||||
|
|
||||||
|
Please do **not** create a pull request without reading this guide first. Failure to do so may result in the **rejection** of the pull request.
|
||||||
|
|
||||||
|
## For The Impatients
|
||||||
|
|
||||||
|
**Work on a feature branch**.
|
||||||
|
If your changes need to be modified due to some reviews, it is less clutter to tweak an isolated feature branch and push it again.
|
||||||
|
|
||||||
|
**Create a ticket in the [issue tracker](https://github.com/n1k0/casperjs/issues/)**.
|
||||||
|
This serves as a placeholder for important feedback, review, or any future updates. **Please ensure searching the bugtracker for an already opened issue matching your case before filing a new issue.**
|
||||||
|
|
||||||
|
In the commit message(s):
|
||||||
|
|
||||||
|
* **Keep the first line short**. Write additional paragraphs if necessary.
|
||||||
|
* **Reference an opened issue**, by referencing the issue ID prefixed by a `#` and the keyword `refs`, eg. `refs #123`
|
||||||
|
|
||||||
|
Sample commit message:
|
||||||
|
|
||||||
|
> refs #123 - fixed error message formatting
|
||||||
|
>
|
||||||
|
> (optional: a short explanation of what the patch actually does)
|
||||||
|
|
||||||
|
**Run tests**
|
||||||
|
|
||||||
|
Run CasperJS' test suite to see you didn't break something:
|
||||||
|
|
||||||
|
$ casperjs selftest
|
||||||
|
|
||||||
|
The result status bar **must be green** before sending your PR.
|
||||||
|
|
||||||
|
## Communicate
|
||||||
|
|
||||||
|
**Improvement and feature request**. If you have an improvement idea, please send an email to the [mailing list](http://groups.google.com/group/casperjs) (preferable than contacting the developers directly) so that other people can give their insights and opinions. This is also important to avoid duplicate work.
|
||||||
|
|
||||||
|
**Help request**. If you're stuck using CasperJS and don't understand how to achieve something, please [ask on the mailing-list](https://groups.google.com/forum/#!forum/casperjs) first. Please don't ask for all the kind people to write your scripts for you.
|
||||||
|
|
||||||
|
**Ensure the issue is related to CasperJS**. Please try to reproduce the issue using plain PhantomJS. If it works with the native PhantomJS API but doesn't with CasperJS, then the issue is probably valid. In the opposite case, please file an issue on [PhantomJS issue tracker](http://code.google.com/p/phantomjs/issues/list).
|
||||||
|
|
||||||
|
**Extending with new API**. Whenever you want to introduce a new API, please send an email to the mailing list along with the link to the issue if any. It may require few iterations to agree on the final API and hence it is important to engage all interested parties as early as possible.
|
||||||
|
|
||||||
|
## Get Ready
|
||||||
|
|
||||||
|
### Use Feature Branch
|
||||||
|
|
||||||
|
To isolate your change, please avoid working on the master branch. Instead, work on a *feature branch* (often also known as *topic branch*). You can create a new branch (example here crash-fix) off the master branch by using:
|
||||||
|
|
||||||
|
git checkout -b crash-fix master
|
||||||
|
|
||||||
|
Refer to your favorite Git tutorial/book for further detailed help.
|
||||||
|
|
||||||
|
Some good practices for the feature branch:
|
||||||
|
|
||||||
|
* Give it a meaningful name instead of, e.g. `prevent-zero-divide` instead of just `fix`
|
||||||
|
* Make *granular* and *atomic* commits, e.g. do not mix a typo fix with some major refactoring
|
||||||
|
* Keep one branch for one specific issue. If you need to work on other unrelated issues, create another branch.
|
||||||
|
|
||||||
|
### Write tests
|
||||||
|
|
||||||
|
CasperJS being partly a testing framework, how irrelevant would be to send a pull request with no test? So, please take the time to write and attach tests to your PR. Furthermore, testing with CasperJS is quite [exhaustively documented](http://casperjs.org/testing.html).
|
||||||
|
|
||||||
|
### Run tests!
|
||||||
|
|
||||||
|
This may sound obvious but **don't send pull requests which break the casperjs test suite**.
|
||||||
|
|
||||||
|
To see if your modifications broke the suite, just run:
|
||||||
|
|
||||||
|
$ casperjs selftest
|
||||||
|
|
||||||
|
### Write documentation
|
||||||
|
|
||||||
|
Do you appreciate the [CasperJS documentation](http://casperjs.org/)? I do too. As the documentation contents are managed and generated using Github, Markdown and CasperJS itself, take the time to read the [Documentation Contribution Guide](https://github.com/n1k0/casperjs/blob/gh-pages/README.md#casperjs-documentation) and write the documentation related to your PR whenever applicable.
|
||||||
|
|
||||||
|
**Note:** As the documentation is handled in a [dedicated separated `gh-pages` branch](https://github.com/n1k0/casperjs/tree/gh-pages), you'll have to send a dedicated PR for doc patches. I'm working on a more comfortable solution, but it's no easy task though.
|
||||||
|
|
||||||
|
## Review and Merge
|
||||||
|
|
||||||
|
When your branch is ready, send the pull request.
|
||||||
|
|
||||||
|
While it is not always the case, often it is necessary to improve parts of your code in the branch. This is the actual review process.
|
||||||
|
|
||||||
|
Here is a check list for the review:
|
||||||
|
|
||||||
|
* It does not break the test suite
|
||||||
|
* There is no typo
|
||||||
|
* The coding style follows the existing one
|
||||||
|
* There is a reasonable amount of comment
|
||||||
|
* The license header is intact
|
||||||
|
* All examples are still working
|
|
@ -0,0 +1,46 @@
|
||||||
|
# CasperJS contributors
|
||||||
|
|
||||||
|
You can check out the [contribution graphs on github](https://github.com/n1k0/casperjs/graphs/contributors).
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git shortlog -s -n
|
||||||
|
689 Nicolas Perriault
|
||||||
|
14 oncletom
|
||||||
|
14 Brikou CARRE
|
||||||
|
8 hannyu
|
||||||
|
6 Chris Lorenzo
|
||||||
|
4 pborreli
|
||||||
|
4 nrabinowitz
|
||||||
|
3 Andrew Childs
|
||||||
|
3 Solomon White
|
||||||
|
3 reina.sweet
|
||||||
|
2 Reina Sweet
|
||||||
|
2 Jason Funk
|
||||||
|
2 Michael Geers
|
||||||
|
2 Julien Moulin
|
||||||
|
2 Donovan Hutchinson
|
||||||
|
2 Clochix
|
||||||
|
1 Marcel Duran
|
||||||
|
1 Mathieu Agopian
|
||||||
|
1 Mehdi Kabab
|
||||||
|
1 Mikko Peltonen
|
||||||
|
1 Pascal Borreli
|
||||||
|
1 Rafael
|
||||||
|
1 Rafael Garcia
|
||||||
|
1 Raphaël Benitte
|
||||||
|
1 Andrew de Andrade
|
||||||
|
1 Tim Bunce
|
||||||
|
1 Victor Yap
|
||||||
|
1 alfetopito
|
||||||
|
1 Christophe Benz
|
||||||
|
1 jean-philippe serafin
|
||||||
|
1 Chris Winters
|
||||||
|
1 Ben Lowery
|
||||||
|
1 Jan Pochyla
|
||||||
|
1 Harrison Reiser
|
||||||
|
1 Julian Gruber
|
||||||
|
1 Justine Tunney
|
||||||
|
1 KaroDidi
|
||||||
|
1 Leandro Boscariol
|
||||||
|
1 Maisons du monde
|
||||||
|
```
|
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (c) 2011-2012 Nicolas Perriault
|
||||||
|
|
||||||
|
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.
|
|
@ -0,0 +1,58 @@
|
||||||
|
# CasperJS [![Build Status](https://secure.travis-ci.org/n1k0/casperjs.png)](http://travis-ci.org/n1k0/casperjs)
|
||||||
|
|
||||||
|
CasperJS is a navigation scripting & testing utility for [PhantomJS](http://www.phantomjs.org/).
|
||||||
|
It eases the process of defining a full navigation scenario and provides useful
|
||||||
|
high-level functions, methods & syntaxic sugar for doing common tasks such as:
|
||||||
|
|
||||||
|
- defining & ordering [navigation steps](http://casperjs.org/quickstart.html)
|
||||||
|
- [filling forms](http://casperjs.org/api.html#casper.fill)
|
||||||
|
- [clicking links](http://casperjs.org/api.html#casper.click)
|
||||||
|
- [capturing screenshots](http://casperjs.org/api.html#casper.captureSelector) of a page (or an area)
|
||||||
|
- [making assertions on remote DOM](http://casperjs.org/api.html#tester)
|
||||||
|
- [logging](http://casperjs.org/logging.html) & [events](http://casperjs.org/events-filters.html)
|
||||||
|
- [downloading base64](http://casperjs.org/api.html#casper.download) encoded resources, even binary ones
|
||||||
|
- catching errors and react accordingly
|
||||||
|
- writing [functional test suites](http://casperjs.org/testing.html), exporting results as JUnit XML (xUnit)
|
||||||
|
|
||||||
|
Browse the [sample examples repository](https://github.com/n1k0/casperjs/tree/master/samples).
|
||||||
|
Don't hesitate to pull request for any cool example of yours as well!
|
||||||
|
|
||||||
|
**Read the [full documentation](http://casperjs.org/) on casperjs dedicated website.**
|
||||||
|
|
||||||
|
Subscribe to the [project mailing-list](https://groups.google.com/forum/#!forum/casperjs)
|
||||||
|
|
||||||
|
Follow the CasperJS project [on twitter](https://twitter.com/casperjs_org) and [Google+](https://plus.google.com/b/106641872690063476159/).
|
||||||
|
|
||||||
|
## Show me some code!
|
||||||
|
|
||||||
|
Sample test to see if some dropdown can be opened:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
casper.start('http://twitter.github.com/bootstrap/javascript.html#dropdowns', function() {
|
||||||
|
this.test.assertExists('#navbar-example');
|
||||||
|
this.click('#dropdowns .nav-pills .dropdown:last-of-type a.dropdown-toggle');
|
||||||
|
this.waitUntilVisible('#dropdowns .nav-pills .open', function() {
|
||||||
|
this.test.pass('Dropdown is open');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.run(function() {
|
||||||
|
this.test.done();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the script:
|
||||||
|
|
||||||
|
![](http://cl.ly/image/112m0F2n162i/Capture%20d%E2%80%99%C3%A9cran%202012-10-19%20%C3%A0%2016.37.15.png)
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
### Contributing code
|
||||||
|
|
||||||
|
Please read the [CONTRIBUTING.md](https://github.com/n1k0/casperjs/blob/master/CONTRIBUTING.md) file contents.
|
||||||
|
|
||||||
|
### Contributing documentation
|
||||||
|
|
||||||
|
CasperJS's documentation is written using the [Markdown format](http://daringfireball.net/projects/markdown/), and hosted on Github thanks to the [Github Pages Feature](http://pages.github.com/).
|
||||||
|
|
||||||
|
To view the source files on github, head to [the gh-pages branch](https://github.com/n1k0/casperjs/tree/gh-pages), and check the [documentation's README](https://github.com/n1k0/casperjs/tree/gh-pages#readme) for further instructions.
|
|
@ -0,0 +1,22 @@
|
||||||
|
@ECHO OFF
|
||||||
|
set CASPER_PATH=%~dp0..\
|
||||||
|
set CASPER_BIN=%CASPER_PATH%bin\
|
||||||
|
|
||||||
|
set PHANTOMJS_NATIVE_ARGS=(--cookies-file --config --debug --disk-cache --ignore-ssl-errors --load-images --load-plugins --local-storage-path --local-storage-quota --local-to-remote-url-access --max-disk-cache-size --output-encoding --proxy --proxy-auth --proxy-type --remote-debugger-port --remote-debugger-autorun --script-encoding --web-security)
|
||||||
|
|
||||||
|
set PHANTOM_ARGS=
|
||||||
|
set CASPER_ARGS=
|
||||||
|
|
||||||
|
:Loop
|
||||||
|
if "%1"=="" goto Continue
|
||||||
|
set IS_PHANTOM_ARG=0
|
||||||
|
for %%i in %PHANTOMJS_NATIVE_ARGS% do (
|
||||||
|
if "%%i"=="%1" set IS_PHANTOM_ARG=1
|
||||||
|
)
|
||||||
|
if %IS_PHANTOM_ARG%==0 set CASPER_ARGS=%CASPER_ARGS% %1
|
||||||
|
if %IS_PHANTOM_ARG%==1 set PHANTOM_ARGS=%PHANTOM_ARGS% %1
|
||||||
|
shift
|
||||||
|
goto Loop
|
||||||
|
:Continue
|
||||||
|
|
||||||
|
call phantomjs%PHANTOM_ARGS% %CASPER_BIN%bootstrap.js --casper-path=%CASPER_PATH% --cli%CASPER_ARGS%
|
|
@ -0,0 +1,319 @@
|
||||||
|
/*!
|
||||||
|
* Casper is a navigation utility for PhantomJS.
|
||||||
|
*
|
||||||
|
* Documentation: http://casperjs.org/
|
||||||
|
* Repository: http://github.com/n1k0/casperjs
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011-2012 Nicolas Perriault
|
||||||
|
*
|
||||||
|
* Part of source code is Copyright Joyent, Inc. and other Node contributors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*global console phantom require*/
|
||||||
|
/*jshint maxstatements:30 maxcomplexity:10*/
|
||||||
|
|
||||||
|
if (!phantom) {
|
||||||
|
console.error('CasperJS needs to be executed in a PhantomJS environment http://phantomjs.org/');
|
||||||
|
phantom.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phantom.version.major === 1 && phantom.version.minor < 6) {
|
||||||
|
console.error('CasperJS needs at least PhantomJS v1.6.0 or later.');
|
||||||
|
phantom.exit(1);
|
||||||
|
} else {
|
||||||
|
bootstrap(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Polyfills
|
||||||
|
if (typeof Function.prototype.bind !== "function") {
|
||||||
|
Function.prototype.bind = function(scope) {
|
||||||
|
"use strict";
|
||||||
|
var _function = this;
|
||||||
|
return function() {
|
||||||
|
return _function.apply(scope, arguments);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CasperJS ships with its own implementation of CommonJS' require() because
|
||||||
|
* PhantomJS' native one doesn't allow to specify supplementary, alternative
|
||||||
|
* lookup directories to fetch modules from.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function patchRequire(require, requireDirs) {
|
||||||
|
"use strict";
|
||||||
|
require('webserver'); // force generation of phantomjs' require.cache for the webserver module
|
||||||
|
var fs = require('fs');
|
||||||
|
var phantomBuiltins = ['fs', 'webpage', 'system', 'webserver'];
|
||||||
|
var phantomRequire = phantom.__orig__require = require;
|
||||||
|
var requireCache = {};
|
||||||
|
function possiblePaths(path, requireDir) {
|
||||||
|
var dir, paths = [];
|
||||||
|
if (path[0] === '.') {
|
||||||
|
paths.push.apply(paths, [
|
||||||
|
fs.absolute(path),
|
||||||
|
fs.absolute(fs.pathJoin(requireDir, path))
|
||||||
|
]);
|
||||||
|
} else if (path[0] === '/') {
|
||||||
|
paths.push(path);
|
||||||
|
} else {
|
||||||
|
dir = fs.absolute(requireDir);
|
||||||
|
while (dir !== '' && dir.lastIndexOf(':') !== dir.length - 1) {
|
||||||
|
paths.push(fs.pathJoin(dir, 'modules', path));
|
||||||
|
// nodejs compatibility
|
||||||
|
paths.push(fs.pathJoin(dir, 'node_modules', path));
|
||||||
|
dir = fs.dirname(dir);
|
||||||
|
}
|
||||||
|
paths.push(fs.pathJoin(requireDir, 'lib', path));
|
||||||
|
paths.push(fs.pathJoin(requireDir, 'modules', path));
|
||||||
|
}
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
var patchedRequire = function _require(path) {
|
||||||
|
var i, paths = [],
|
||||||
|
fileGuesses = [],
|
||||||
|
file,
|
||||||
|
module = {
|
||||||
|
exports: {}
|
||||||
|
};
|
||||||
|
if (phantomBuiltins.indexOf(path) !== -1) {
|
||||||
|
return phantomRequire(path);
|
||||||
|
}
|
||||||
|
requireDirs.forEach(function(requireDir) {
|
||||||
|
paths = paths.concat(possiblePaths(path, requireDir));
|
||||||
|
});
|
||||||
|
paths.forEach(function _forEach(testPath) {
|
||||||
|
fileGuesses.push.apply(fileGuesses, [
|
||||||
|
testPath,
|
||||||
|
testPath + '.js',
|
||||||
|
testPath + '.coffee',
|
||||||
|
fs.pathJoin(testPath, 'index.js'),
|
||||||
|
fs.pathJoin(testPath, 'index.coffee'),
|
||||||
|
fs.pathJoin(testPath, 'lib', fs.basename(testPath) + '.js'),
|
||||||
|
fs.pathJoin(testPath, 'lib', fs.basename(testPath) + '.coffee')
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
file = null;
|
||||||
|
for (i = 0; i < fileGuesses.length && !file; ++i) {
|
||||||
|
if (fs.isFile(fileGuesses[i])) {
|
||||||
|
file = fileGuesses[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!file) {
|
||||||
|
throw new Error("CasperJS couldn't find module " + path);
|
||||||
|
}
|
||||||
|
if (file in requireCache) {
|
||||||
|
return requireCache[file].exports;
|
||||||
|
}
|
||||||
|
var scriptCode = (function getScriptCode(file) {
|
||||||
|
var scriptCode = fs.read(file);
|
||||||
|
if (/\.coffee$/i.test(file)) {
|
||||||
|
/*global CoffeeScript*/
|
||||||
|
scriptCode = CoffeeScript.compile(scriptCode);
|
||||||
|
}
|
||||||
|
return scriptCode;
|
||||||
|
})(file);
|
||||||
|
var fn = new Function('__file__', 'require', 'module', 'exports', scriptCode);
|
||||||
|
try {
|
||||||
|
fn(file, _require, module, module.exports);
|
||||||
|
} catch (e) {
|
||||||
|
var error = new window.CasperError('__mod_error(' + path + '):: ' + e);
|
||||||
|
error.file = file;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
requireCache[file] = module;
|
||||||
|
return module.exports;
|
||||||
|
};
|
||||||
|
patchedRequire.patched = true;
|
||||||
|
return patchedRequire;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bootstrap(global) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var phantomArgs = require('system').args;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads and initialize the CasperJS environment.
|
||||||
|
*/
|
||||||
|
phantom.loadCasper = function loadCasper() {
|
||||||
|
// Patching fs
|
||||||
|
// TODO: watch for these methods being implemented in official fs module
|
||||||
|
var fs = (function _fs(fs) {
|
||||||
|
if (!fs.hasOwnProperty('basename')) {
|
||||||
|
fs.basename = function basename(path) {
|
||||||
|
return path.replace(/.*\//, '');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!fs.hasOwnProperty('dirname')) {
|
||||||
|
fs.dirname = function dirname(path) {
|
||||||
|
return path.replace(/\\/g, '/').replace(/\/[^\/]*$/, '');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!fs.hasOwnProperty('isWindows')) {
|
||||||
|
fs.isWindows = function isWindows() {
|
||||||
|
var testPath = arguments[0] || this.workingDirectory;
|
||||||
|
return (/^[a-z]{1,2}:/i).test(testPath) || testPath.indexOf("\\\\") === 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!fs.hasOwnProperty('pathJoin')) {
|
||||||
|
fs.pathJoin = function pathJoin() {
|
||||||
|
return Array.prototype.join.call(arguments, this.separator);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return fs;
|
||||||
|
})(require('fs'));
|
||||||
|
|
||||||
|
// casper root path
|
||||||
|
if (!phantom.casperPath) {
|
||||||
|
try {
|
||||||
|
phantom.casperPath = phantom.args.map(function _map(i) {
|
||||||
|
var match = i.match(/^--casper-path=(.*)/);
|
||||||
|
if (match) {
|
||||||
|
return fs.absolute(match[1]);
|
||||||
|
}
|
||||||
|
}).filter(function _filter(path) {
|
||||||
|
return fs.isDirectory(path);
|
||||||
|
}).pop();
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!phantom.casperPath) {
|
||||||
|
console.error("Couldn't find nor compute phantom.casperPath, exiting.");
|
||||||
|
phantom.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Embedded, up-to-date, validatable & controlable CoffeeScript
|
||||||
|
phantom.injectJs(fs.pathJoin(phantom.casperPath, 'modules', 'vendors', 'coffee-script.js'));
|
||||||
|
|
||||||
|
// custom global CasperError
|
||||||
|
global.CasperError = function CasperError(msg) {
|
||||||
|
Error.call(this);
|
||||||
|
this.message = msg;
|
||||||
|
this.name = 'CasperError';
|
||||||
|
};
|
||||||
|
|
||||||
|
// standard Error prototype inheritance
|
||||||
|
global.CasperError.prototype = Object.getPrototypeOf(new Error());
|
||||||
|
|
||||||
|
// CasperJS version, extracted from package.json - see http://semver.org/
|
||||||
|
phantom.casperVersion = (function getVersion(path) {
|
||||||
|
var parts, patchPart, pkg, pkgFile;
|
||||||
|
var fs = require('fs');
|
||||||
|
pkgFile = fs.absolute(fs.pathJoin(path, 'package.json'));
|
||||||
|
if (!fs.exists(pkgFile)) {
|
||||||
|
throw new global.CasperError('Cannot find package.json at ' + pkgFile);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
pkg = JSON.parse(require('fs').read(pkgFile));
|
||||||
|
} catch (e) {
|
||||||
|
throw new global.CasperError('Cannot read package file contents: ' + e);
|
||||||
|
}
|
||||||
|
parts = pkg.version.trim().split(".");
|
||||||
|
if (parts < 3) {
|
||||||
|
throw new global.CasperError("Invalid version number");
|
||||||
|
}
|
||||||
|
patchPart = parts[2].split('-');
|
||||||
|
return {
|
||||||
|
major: ~~parts[0] || 0,
|
||||||
|
minor: ~~parts[1] || 0,
|
||||||
|
patch: ~~patchPart[0] || 0,
|
||||||
|
ident: patchPart[1] || "",
|
||||||
|
toString: function toString() {
|
||||||
|
var version = [this.major, this.minor, this.patch].join('.');
|
||||||
|
if (this.ident) {
|
||||||
|
version = [version, this.ident].join('-');
|
||||||
|
}
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})(phantom.casperPath);
|
||||||
|
|
||||||
|
// patch require
|
||||||
|
global.require = patchRequire(global.require, [phantom.casperPath, fs.workingDirectory]);
|
||||||
|
|
||||||
|
// casper cli args
|
||||||
|
phantom.casperArgs = global.require('cli').parse(phantom.args);
|
||||||
|
|
||||||
|
// loaded status
|
||||||
|
phantom.casperLoaded = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the CasperJS Command Line Interface.
|
||||||
|
*/
|
||||||
|
phantom.initCasperCli = function initCasperCli() {
|
||||||
|
var fs = require("fs");
|
||||||
|
|
||||||
|
if (!!phantom.casperArgs.options.version) {
|
||||||
|
console.log(phantom.casperVersion.toString());
|
||||||
|
phantom.exit(0);
|
||||||
|
} else if (phantom.casperArgs.get(0) === "test") {
|
||||||
|
phantom.casperScript = fs.absolute(fs.pathJoin(phantom.casperPath, 'tests', 'run.js'));
|
||||||
|
phantom.casperTest = true;
|
||||||
|
phantom.casperArgs.drop("test");
|
||||||
|
} else if (phantom.casperArgs.get(0) === "selftest") {
|
||||||
|
phantom.casperScript = fs.absolute(fs.pathJoin(phantom.casperPath, 'tests', 'run.js'));
|
||||||
|
phantom.casperSelfTest = true;
|
||||||
|
phantom.casperArgs.options.includes = fs.pathJoin(phantom.casperPath, 'tests', 'selftest.js');
|
||||||
|
if (phantom.casperArgs.args.length <= 1) {
|
||||||
|
phantom.casperArgs.args.push(fs.pathJoin(phantom.casperPath, 'tests', 'suites'));
|
||||||
|
}
|
||||||
|
phantom.casperArgs.drop("selftest");
|
||||||
|
} else if (phantom.casperArgs.args.length === 0 || !!phantom.casperArgs.options.help) {
|
||||||
|
var phantomVersion = [phantom.version.major, phantom.version.minor, phantom.version.patch].join('.');
|
||||||
|
var f = require("utils").format;
|
||||||
|
console.log(f('CasperJS version %s at %s, using PhantomJS version %s',
|
||||||
|
phantom.casperVersion.toString(),
|
||||||
|
phantom.casperPath, phantomVersion));
|
||||||
|
console.log(fs.read(fs.pathJoin(phantom.casperPath, 'bin', 'usage.txt')));
|
||||||
|
phantom.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!phantom.casperScript) {
|
||||||
|
phantom.casperScript = phantom.casperArgs.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phantom.casperScript) {
|
||||||
|
if (!fs.isFile(phantom.casperScript)) {
|
||||||
|
console.error('Unable to open file: ' + phantom.casperScript);
|
||||||
|
phantom.exit(1);
|
||||||
|
} else {
|
||||||
|
// filter out the called script name from casper args
|
||||||
|
phantom.casperArgs.drop(phantom.casperScript);
|
||||||
|
|
||||||
|
// passed casperjs script execution
|
||||||
|
phantom.injectJs(phantom.casperScript);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!phantom.casperLoaded) {
|
||||||
|
phantom.loadCasper();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true === phantom.casperArgs.get('cli')) {
|
||||||
|
phantom.initCasperCli();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def resolve(path):
|
||||||
|
if os.path.islink(path):
|
||||||
|
path = os.path.join(os.path.dirname(path), os.readlink(path))
|
||||||
|
return resolve(path)
|
||||||
|
return path
|
||||||
|
|
||||||
|
PHANTOMJS_NATIVE_ARGS = [
|
||||||
|
'cookies-file',
|
||||||
|
'config',
|
||||||
|
'debug',
|
||||||
|
'disk-cache',
|
||||||
|
'ignore-ssl-errors',
|
||||||
|
'load-images',
|
||||||
|
'load-plugins',
|
||||||
|
'local-storage-path',
|
||||||
|
'local-storage-quota',
|
||||||
|
'local-to-remote-url-access',
|
||||||
|
'max-disk-cache-size',
|
||||||
|
'output-encoding',
|
||||||
|
'proxy',
|
||||||
|
'proxy-auth',
|
||||||
|
'proxy-type',
|
||||||
|
'remote-debugger-port',
|
||||||
|
'remote-debugger-autorun',
|
||||||
|
'script-encoding',
|
||||||
|
'web-security',
|
||||||
|
]
|
||||||
|
CASPER_ARGS = []
|
||||||
|
PHANTOMJS_ARGS = []
|
||||||
|
|
||||||
|
for arg in sys.argv[1:]:
|
||||||
|
found = False
|
||||||
|
for native in PHANTOMJS_NATIVE_ARGS:
|
||||||
|
if arg.startswith('--%s' % native):
|
||||||
|
PHANTOMJS_ARGS.append(arg)
|
||||||
|
found = True
|
||||||
|
if not found:
|
||||||
|
CASPER_ARGS.append(arg)
|
||||||
|
|
||||||
|
CASPER_PATH = os.path.abspath(os.path.join(os.path.dirname(resolve(__file__)), '..'))
|
||||||
|
CASPER_COMMAND = os.environ.get('PHANTOMJS_EXECUTABLE', 'phantomjs').split(' ')
|
||||||
|
CASPER_COMMAND.extend(PHANTOMJS_ARGS)
|
||||||
|
CASPER_COMMAND.extend([
|
||||||
|
os.path.join(CASPER_PATH, 'bin', 'bootstrap.js'),
|
||||||
|
'--casper-path=%s' % CASPER_PATH,
|
||||||
|
'--cli'
|
||||||
|
])
|
||||||
|
CASPER_COMMAND.extend(CASPER_ARGS)
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.execvp(CASPER_COMMAND[0], CASPER_COMMAND)
|
||||||
|
except OSError as err:
|
||||||
|
print(('Fatal: %s; did you install phantomjs?' % err))
|
||||||
|
sys.exit(1)
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
Usage: casperjs [options] script.[js|coffee] [script argument [script argument ...]]
|
||||||
|
casperjs [options] test [test path [test path ...]]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
--help Prints this help
|
||||||
|
--version Prints out CasperJS version
|
||||||
|
|
||||||
|
Read the docs http://casperjs.org/
|
|
@ -0,0 +1,23 @@
|
||||||
|
# by hannyu
|
||||||
|
|
||||||
|
CASPERJS_VERSION = File.read("package.json")[/version.*:.*"(.*?)"/,1].gsub(/[\-_\+]/,".")
|
||||||
|
|
||||||
|
Gem::Specification.new do |s|
|
||||||
|
s.name = "casperjs"
|
||||||
|
s.version = CASPERJS_VERSION
|
||||||
|
s.homepage = "http://casperjs.org/"
|
||||||
|
s.authors = ["Nicolas Perriault", ]
|
||||||
|
s.email = ["nperriault@gmail.com",]
|
||||||
|
s.description = "CasperJS is a navigation scripting & testing utility for [PhantomJS](http://www.phantomjs.org/).
|
||||||
|
It eases the process of defining a full navigation scenario and provides useful
|
||||||
|
high-level functions, methods & syntaxic sugar for doing common tasks."
|
||||||
|
s.summary = "Navigation scripting & testing utility for PhantomJS"
|
||||||
|
s.extra_rdoc_files = ["LICENSE.md", "README.md"]
|
||||||
|
s.files = Dir["LICENSE.md", "README.md", "CHANGELOG.md", "package.json", "casperjs.gemspec",
|
||||||
|
"bin/bootstrap.js", "bin/usage.txt", "bin/casperjs_python",
|
||||||
|
"docs/**/*", "modules/**/*", "samples/**/*", "tests/**/*"]
|
||||||
|
s.bindir = "rubybin"
|
||||||
|
s.executables = [ "casperjs" ]
|
||||||
|
s.license = "MIT"
|
||||||
|
s.requirements = [ "PhantomJS v1.6" ]
|
||||||
|
end
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,138 @@
|
||||||
|
/*!
|
||||||
|
* Casper is a navigation utility for PhantomJS.
|
||||||
|
*
|
||||||
|
* Documentation: http://casperjs.org/
|
||||||
|
* Repository: http://github.com/n1k0/casperjs
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011-2012 Nicolas Perriault
|
||||||
|
*
|
||||||
|
* Part of source code is Copyright Joyent, Inc. and other Node contributors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*global CasperError console exports phantom require*/
|
||||||
|
|
||||||
|
var system = require('system');
|
||||||
|
var utils = require('utils');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts, normalize ad organize PhantomJS CLI arguments in a dedicated
|
||||||
|
* Object.
|
||||||
|
*
|
||||||
|
* @param array phantomArgs system.args value
|
||||||
|
* @return Object
|
||||||
|
*/
|
||||||
|
exports.parse = function parse(phantomArgs) {
|
||||||
|
"use strict";
|
||||||
|
var extract = {
|
||||||
|
args: [],
|
||||||
|
options: {},
|
||||||
|
raw: {
|
||||||
|
args: [],
|
||||||
|
options: {}
|
||||||
|
},
|
||||||
|
drop: function drop(what) {
|
||||||
|
if (utils.isNumber(what)) {
|
||||||
|
// deleting an arg by its position
|
||||||
|
this.args = this.args.filter(function _filter(arg, index) {
|
||||||
|
return index !== what;
|
||||||
|
});
|
||||||
|
} else if (utils.isString(what)) {
|
||||||
|
// deleting an arg by its value
|
||||||
|
this.args = this.args.filter(function _filter(arg) {
|
||||||
|
return arg !== what;
|
||||||
|
});
|
||||||
|
// deleting an option by its name (key)
|
||||||
|
var self = this;
|
||||||
|
Object.keys(this.options).forEach(function _forEach(option) {
|
||||||
|
if (option === what) {
|
||||||
|
delete self.options[what];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new CasperError("cannot drop argument of type " + typeof what);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
has: function has(what) {
|
||||||
|
if (utils.isNumber(what)) {
|
||||||
|
return what in this.args;
|
||||||
|
} else if (utils.isString(what)) {
|
||||||
|
return what in this.options;
|
||||||
|
} else {
|
||||||
|
throw new CasperError("Unsupported cli arg tester " + typeof what);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
get: function get(what) {
|
||||||
|
if (utils.isNumber(what)) {
|
||||||
|
return this.args[what];
|
||||||
|
} else if (utils.isString(what)) {
|
||||||
|
return this.options[what];
|
||||||
|
} else {
|
||||||
|
throw new CasperError("Unsupported cli arg getter " + typeof what);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
phantomArgs.forEach(function _forEach(arg) {
|
||||||
|
if (arg.indexOf('--') === 0) {
|
||||||
|
// named option
|
||||||
|
var optionMatch = arg.match(/^--(.*?)=(.*)/i);
|
||||||
|
if (optionMatch) {
|
||||||
|
extract.options[optionMatch[1]] = castArgument(optionMatch[2]);
|
||||||
|
extract.raw.options[optionMatch[1]] = optionMatch[2];
|
||||||
|
} else {
|
||||||
|
// flag
|
||||||
|
var flagMatch = arg.match(/^--(.*)/);
|
||||||
|
if (flagMatch) {
|
||||||
|
extract.options[flagMatch[1]] = extract.raw.options[flagMatch[1]] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// positional arg
|
||||||
|
extract.args.push(castArgument(arg));
|
||||||
|
extract.raw.args.push(castArgument(arg));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
extract.raw = utils.mergeObjects(extract.raw, {
|
||||||
|
drop: extract.drop,
|
||||||
|
has: extract.has,
|
||||||
|
get: extract.get
|
||||||
|
});
|
||||||
|
return extract;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cast a string argument to its typed equivalent.
|
||||||
|
*
|
||||||
|
* @param String arg
|
||||||
|
* @return Mixed
|
||||||
|
*/
|
||||||
|
function castArgument(arg) {
|
||||||
|
"use strict";
|
||||||
|
if (arg.match(/^-?\d+$/)) {
|
||||||
|
return parseInt(arg, 10);
|
||||||
|
} else if (arg.match(/^-?\d+\.\d+$/)) {
|
||||||
|
return parseFloat(arg);
|
||||||
|
} else if (arg.match(/^(true|false)$/i)) {
|
||||||
|
return arg.trim().toLowerCase() === "true" ? true : false;
|
||||||
|
} else {
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,731 @@
|
||||||
|
/*!
|
||||||
|
* Casper is a navigation utility for PhantomJS.
|
||||||
|
*
|
||||||
|
* Documentation: http://casperjs.org/
|
||||||
|
* Repository: http://github.com/n1k0/casperjs
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011-2012 Nicolas Perriault
|
||||||
|
*
|
||||||
|
* Part of source code is Copyright Joyent, Inc. and other Node contributors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*global console escape exports NodeList window*/
|
||||||
|
|
||||||
|
(function(exports) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
exports.create = function create(options) {
|
||||||
|
return new this.ClientUtils(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Casper client-side helpers.
|
||||||
|
*/
|
||||||
|
exports.ClientUtils = function ClientUtils(options) {
|
||||||
|
// private members
|
||||||
|
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
|
||||||
|
);
|
||||||
|
var SUPPORTED_SELECTOR_TYPES = ['css', 'xpath'];
|
||||||
|
|
||||||
|
// public members
|
||||||
|
this.options = options || {};
|
||||||
|
this.options.scope = this.options.scope || document;
|
||||||
|
/**
|
||||||
|
* Clicks on the DOM element behind the provided selector.
|
||||||
|
*
|
||||||
|
* @param String selector A CSS3 selector to the element to click
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
this.click = function click(selector) {
|
||||||
|
return this.mouseEvent('click', selector);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a base64 encoded string. Succeeds where window.atob() fails.
|
||||||
|
*
|
||||||
|
* @param String str The base64 encoded contents
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
this.decode = function decode(str) {
|
||||||
|
/*jshint maxstatements:30 maxcomplexity:30 */
|
||||||
|
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 & 0XF) << 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;
|
||||||
|
}
|
||||||
|
out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Echoes something to casper console.
|
||||||
|
*
|
||||||
|
* @param String message
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
this.echo = function echo(message) {
|
||||||
|
console.log("[casper.echo] " + message);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 encode(str) {
|
||||||
|
/*jshint maxstatements:30 */
|
||||||
|
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) | ((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 exists(selector) {
|
||||||
|
try {
|
||||||
|
return this.findAll(selector).length > 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 fetchText(selector) {
|
||||||
|
var text = '', elements = this.findAll(selector);
|
||||||
|
if (elements && elements.length) {
|
||||||
|
Array.prototype.forEach.call(elements, function _forEach(element) {
|
||||||
|
text += element.textContent || element.innerText;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
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 fill(form, vals) {
|
||||||
|
var out = {
|
||||||
|
errors: [],
|
||||||
|
fields: [],
|
||||||
|
files: []
|
||||||
|
};
|
||||||
|
if (!(form instanceof HTMLElement) || typeof form === "string") {
|
||||||
|
this.log("attempting to fetch form element from selector: '" + form + "'", "info");
|
||||||
|
try {
|
||||||
|
form = this.findOne(form);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.name === "SYNTAX_ERR") {
|
||||||
|
out.errors.push("invalid form selector provided: '" + form + "'");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!form) {
|
||||||
|
out.errors.push("form not found");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
for (var name in vals) {
|
||||||
|
if (!vals.hasOwnProperty(name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var field = this.findAll('[name="' + name + '"]', form);
|
||||||
|
var value = vals[name];
|
||||||
|
if (!field || field.length === 0) {
|
||||||
|
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 if(err.name === "FieldNotFound") {
|
||||||
|
out.errors.push('Form field named "' + name + '" was not found.');
|
||||||
|
} else {
|
||||||
|
out.errors.push(err.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds all DOM elements matching by the provided selector.
|
||||||
|
*
|
||||||
|
* @param String selector CSS3 selector
|
||||||
|
* @param HTMLElement|null scope Element to search child elements within
|
||||||
|
* @return NodeList|undefined
|
||||||
|
*/
|
||||||
|
this.findAll = function findAll(selector, scope) {
|
||||||
|
scope = scope || this.options.scope;
|
||||||
|
try {
|
||||||
|
var pSelector = this.processSelector(selector);
|
||||||
|
if (pSelector.type === 'xpath') {
|
||||||
|
return this.getElementsByXPath(pSelector.path);
|
||||||
|
} else {
|
||||||
|
return scope.querySelectorAll(pSelector.path);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.log('findAll(): invalid selector provided "' + selector + '":' + e, "error");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a DOM element by the provided selector.
|
||||||
|
*
|
||||||
|
* @param String selector CSS3 selector
|
||||||
|
* @param HTMLElement|null scope Element to search child elements within
|
||||||
|
* @return HTMLElement|undefined
|
||||||
|
*/
|
||||||
|
this.findOne = function findOne(selector, scope) {
|
||||||
|
scope = scope || this.options.scope;
|
||||||
|
try {
|
||||||
|
var pSelector = this.processSelector(selector);
|
||||||
|
if (pSelector.type === 'xpath') {
|
||||||
|
return this.getElementByXPath(pSelector.path);
|
||||||
|
} else {
|
||||||
|
return scope.querySelector(pSelector.path);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.log('findOne(): invalid selector provided "' + selector + '":' + e, "error");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 getBase64(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 Url.
|
||||||
|
* @param String method HTTP method.
|
||||||
|
* @param Object data Request parameters.
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
this.getBinary = function getBinary(url, method, data) {
|
||||||
|
try {
|
||||||
|
return this.sendAJAX(url, method, data, false);
|
||||||
|
} 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 "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves total document height.
|
||||||
|
* http://james.padolsey.com/javascript/get-document-height-cross-browser/
|
||||||
|
*
|
||||||
|
* @return {Number}
|
||||||
|
*/
|
||||||
|
this.getDocumentHeight = function getDocumentHeight() {
|
||||||
|
return Math.max(
|
||||||
|
Math.max(document.body.scrollHeight, document.documentElement.scrollHeight),
|
||||||
|
Math.max(document.body.offsetHeight, document.documentElement.offsetHeight),
|
||||||
|
Math.max(document.body.clientHeight, document.documentElement.clientHeight)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves bounding rect coordinates of the HTML element matching the
|
||||||
|
* provided CSS3 selector in the following form:
|
||||||
|
*
|
||||||
|
* {top: y, left: x, width: w, height:, h}
|
||||||
|
*
|
||||||
|
* @param String selector
|
||||||
|
* @return Object or null
|
||||||
|
*/
|
||||||
|
this.getElementBounds = function getElementBounds(selector) {
|
||||||
|
try {
|
||||||
|
var clipRect = this.findOne(selector).getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
top: clipRect.top,
|
||||||
|
left: clipRect.left,
|
||||||
|
width: clipRect.width,
|
||||||
|
height: clipRect.height
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
this.log("Unable to fetch bounds for element " + selector, "warning");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the list of bounding rect coordinates for all the HTML elements matching the
|
||||||
|
* provided CSS3 selector, in the following form:
|
||||||
|
*
|
||||||
|
* [{top: y, left: x, width: w, height:, h},
|
||||||
|
* {top: y, left: x, width: w, height:, h},
|
||||||
|
* ...]
|
||||||
|
*
|
||||||
|
* @param String selector
|
||||||
|
* @return Array
|
||||||
|
*/
|
||||||
|
this.getElementsBounds = function getElementsBounds(selector) {
|
||||||
|
var elements = this.findAll(selector);
|
||||||
|
var self = this;
|
||||||
|
try {
|
||||||
|
return Array.prototype.map.call(elements, function(element) {
|
||||||
|
var clipRect = element.getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
top: clipRect.top,
|
||||||
|
left: clipRect.left,
|
||||||
|
width: clipRect.width,
|
||||||
|
height: clipRect.height
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
this.log("Unable to fetch bounds for elements matching " + selector, "warning");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a single DOM element matching a given XPath expression.
|
||||||
|
*
|
||||||
|
* @param String expression The XPath expression
|
||||||
|
* @return HTMLElement or null
|
||||||
|
*/
|
||||||
|
this.getElementByXPath = function getElementByXPath(expression) {
|
||||||
|
var a = document.evaluate(expression, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
||||||
|
if (a.snapshotLength > 0) {
|
||||||
|
return a.snapshotItem(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves all DOM elements matching a given XPath expression.
|
||||||
|
*
|
||||||
|
* @param String expression The XPath expression
|
||||||
|
* @return Array
|
||||||
|
*/
|
||||||
|
this.getElementsByXPath = function getElementsByXPath(expression) {
|
||||||
|
var nodes = [];
|
||||||
|
var a = document.evaluate(expression, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
||||||
|
for (var i = 0; i < a.snapshotLength; i++) {
|
||||||
|
nodes.push(a.snapshotItem(i));
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the value of a form field.
|
||||||
|
*
|
||||||
|
* @param String inputName The for input name attr value
|
||||||
|
* @return Mixed
|
||||||
|
*/
|
||||||
|
this.getFieldValue = function getFieldValue(inputName) {
|
||||||
|
function getSingleValue(input) {
|
||||||
|
try {
|
||||||
|
type = input.getAttribute('type').toLowerCase();
|
||||||
|
} catch (e) {
|
||||||
|
type = 'other';
|
||||||
|
}
|
||||||
|
if (['checkbox', 'radio'].indexOf(type) === -1) {
|
||||||
|
return input.value;
|
||||||
|
}
|
||||||
|
// single checkbox or… radio button (weird, I know)
|
||||||
|
if (input.hasAttribute('value')) {
|
||||||
|
return input.checked ? input.getAttribute('value') : undefined;
|
||||||
|
}
|
||||||
|
return input.checked;
|
||||||
|
}
|
||||||
|
function getMultipleValues(inputs) {
|
||||||
|
type = inputs[0].getAttribute('type').toLowerCase();
|
||||||
|
if (type === 'radio') {
|
||||||
|
var value;
|
||||||
|
[].forEach.call(inputs, function(radio) {
|
||||||
|
value = radio.checked ? radio.value : undefined;
|
||||||
|
});
|
||||||
|
return value;
|
||||||
|
} else if (type === 'checkbox') {
|
||||||
|
var values = [];
|
||||||
|
[].forEach.call(inputs, function(checkbox) {
|
||||||
|
if (checkbox.checked) {
|
||||||
|
values.push(checkbox.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var inputs = this.findAll('[name="' + inputName + '"]'), type;
|
||||||
|
switch (inputs.length) {
|
||||||
|
case 0: return null;
|
||||||
|
case 1: return getSingleValue(inputs[0]);
|
||||||
|
default: return getMultipleValues(inputs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a message. Will format the message a way CasperJS will be able
|
||||||
|
* to log phantomjs side.
|
||||||
|
*
|
||||||
|
* @param String message The message to log
|
||||||
|
* @param String level The log level
|
||||||
|
*/
|
||||||
|
this.log = function log(message, level) {
|
||||||
|
console.log("[casper:" + (level || "debug") + "] " + message);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches a mouse event to the DOM element behind the provided selector.
|
||||||
|
*
|
||||||
|
* @param String type Type of event to dispatch
|
||||||
|
* @param String selector A CSS3 selector to the element to click
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
this.mouseEvent = function mouseEvent(type, selector) {
|
||||||
|
var elem = this.findOne(selector);
|
||||||
|
if (!elem) {
|
||||||
|
this.log("mouseEvent(): Couldn't find any element matching '" + selector + "' selector", "error");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
var evt = document.createEvent("MouseEvents");
|
||||||
|
var center_x = 1, center_y = 1;
|
||||||
|
try {
|
||||||
|
var pos = elem.getBoundingClientRect();
|
||||||
|
center_x = Math.floor((pos.left + pos.right) / 2),
|
||||||
|
center_y = Math.floor((pos.top + pos.bottom) / 2);
|
||||||
|
} catch(e) {}
|
||||||
|
evt.initMouseEvent(type, true, true, window, 1, 1, 1, center_x, center_y, false, false, false, false, 0, elem);
|
||||||
|
// dispatchEvent return value is false if at least one of the event
|
||||||
|
// handlers which handled this event called preventDefault;
|
||||||
|
// so we cannot returns this results as it cannot accurately informs on the status
|
||||||
|
// of the operation
|
||||||
|
// let's assume the event has been sent ok it didn't raise any error
|
||||||
|
elem.dispatchEvent(evt);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
this.log("Failed dispatching " + type + "mouse event on " + selector + ": " + e, "error");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a selector input, either as a string or an object.
|
||||||
|
*
|
||||||
|
* If passed an object, if must be of the form:
|
||||||
|
*
|
||||||
|
* selectorObject = {
|
||||||
|
* type: <'css' or 'xpath'>,
|
||||||
|
* path: <a string>
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @param String|Object selector The selector string or object
|
||||||
|
*
|
||||||
|
* @return an object containing 'type' and 'path' keys
|
||||||
|
*/
|
||||||
|
this.processSelector = function processSelector(selector) {
|
||||||
|
var selectorObject = {
|
||||||
|
toString: function toString() {
|
||||||
|
return this.type + ' selector: ' + this.path;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (typeof selector === "string") {
|
||||||
|
// defaults to CSS selector
|
||||||
|
selectorObject.type = "css";
|
||||||
|
selectorObject.path = selector;
|
||||||
|
return selectorObject;
|
||||||
|
} else if (typeof selector === "object") {
|
||||||
|
// validation
|
||||||
|
if (!selector.hasOwnProperty('type') || !selector.hasOwnProperty('path')) {
|
||||||
|
throw new Error("Incomplete selector object");
|
||||||
|
} else if (SUPPORTED_SELECTOR_TYPES.indexOf(selector.type) === -1) {
|
||||||
|
throw new Error("Unsupported selector type: " + selector.type);
|
||||||
|
}
|
||||||
|
if (!selector.hasOwnProperty('toString')) {
|
||||||
|
selector.toString = selectorObject.toString;
|
||||||
|
}
|
||||||
|
return selector;
|
||||||
|
}
|
||||||
|
throw new Error("Unsupported selector type: " + typeof selector);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all DOM elements matching a given XPath expression.
|
||||||
|
*
|
||||||
|
* @param String expression The XPath expression
|
||||||
|
* @return Array
|
||||||
|
*/
|
||||||
|
this.removeElementsByXPath = function removeElementsByXPath(expression) {
|
||||||
|
var a = document.evaluate(expression, document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
|
||||||
|
for (var i = 0; i < a.snapshotLength; i++) {
|
||||||
|
a.snapshotItem(i).parentNode.removeChild(a.snapshotItem(i));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs an AJAX request.
|
||||||
|
*
|
||||||
|
* @param String url Url.
|
||||||
|
* @param String method HTTP method.
|
||||||
|
* @param Object data Request parameters.
|
||||||
|
* @param Boolean async Asynchroneous request? (default: false)
|
||||||
|
* @return String Response text.
|
||||||
|
*/
|
||||||
|
this.sendAJAX = function sendAJAX(url, method, data, async) {
|
||||||
|
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, !!async);
|
||||||
|
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(encodeURIComponent(k) + "=" + encodeURIComponent(data[k].toString()));
|
||||||
|
}
|
||||||
|
dataString = dataList.join('&');
|
||||||
|
this.log("sendAJAX(): Using request data: '" + dataString + "'", "debug");
|
||||||
|
} else if (typeof data === "string") {
|
||||||
|
dataString = data;
|
||||||
|
}
|
||||||
|
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||||
|
}
|
||||||
|
xhr.send(method === "POST" ? dataString : null);
|
||||||
|
return xhr.responseText;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 setField(field, value) {
|
||||||
|
var logValue, fields, out;
|
||||||
|
value = logValue = (value || "");
|
||||||
|
if (field instanceof NodeList) {
|
||||||
|
fields = field;
|
||||||
|
field = fields[0];
|
||||||
|
}
|
||||||
|
if (!(field instanceof HTMLElement)) {
|
||||||
|
var error = new Error('Invalid field type; only HTMLElement and NodeList are supported');
|
||||||
|
error.name = 'FieldNotFound';
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
if (this.options && this.options.safeLogs && field.getAttribute('type') === "password") {
|
||||||
|
// obfuscate password value
|
||||||
|
logValue = new Array(value.length + 1).join("*");
|
||||||
|
}
|
||||||
|
this.log('Set "' + field.getAttribute('name') + '" field value to ' + logValue, "debug");
|
||||||
|
try {
|
||||||
|
field.focus();
|
||||||
|
} catch (e) {
|
||||||
|
this.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 _forEach(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 _forEach(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;
|
||||||
|
}
|
||||||
|
// firing the `change` event
|
||||||
|
var changeEvent = document.createEvent("HTMLEvents");
|
||||||
|
changeEvent.initEvent('change', true, true);
|
||||||
|
field.dispatchEvent(changeEvent);
|
||||||
|
// blur the field
|
||||||
|
try {
|
||||||
|
field.blur();
|
||||||
|
} catch (err) {
|
||||||
|
this.log("Unable to blur() input field " + field.getAttribute('name') + ": " + err, "warning");
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a given DOM element is visible in remote page.
|
||||||
|
*
|
||||||
|
* @param String selector CSS3 selector
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
this.visible = function visible(selector) {
|
||||||
|
try {
|
||||||
|
var comp,
|
||||||
|
el = this.findOne(selector);
|
||||||
|
|
||||||
|
if (el) {
|
||||||
|
comp = window.getComputedStyle(el, null);
|
||||||
|
return comp.visibility !== 'hidden' && comp.display !== 'none' && el.offsetHeight > 0 && el.offsetWidth > 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})(typeof exports === "object" ? exports : window);
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*!
|
||||||
|
* Casper is a navigation utility for PhantomJS.
|
||||||
|
*
|
||||||
|
* Documentation: http://casperjs.org/
|
||||||
|
* Repository: http://github.com/n1k0/casperjs
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011-2012 Nicolas Perriault
|
||||||
|
*
|
||||||
|
* Part of source code is Copyright Joyent, Inc. and other Node contributors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*global exports console require*/
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var utils = require('utils');
|
||||||
|
|
||||||
|
exports.create = function create(type) {
|
||||||
|
"use strict";
|
||||||
|
if (!type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(type in exports)) {
|
||||||
|
throw new Error(utils.format('Unsupported colorizer type "%s"', type));
|
||||||
|
}
|
||||||
|
return new exports[type]();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a port of lime colorizer.
|
||||||
|
* http://trac.symfony-project.org/browser/tools/lime/trunk/lib/lime.php
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier, Symfony project, MIT license
|
||||||
|
*/
|
||||||
|
var Colorizer = function Colorizer() {
|
||||||
|
"use strict";
|
||||||
|
var options = { bold: 1, underscore: 4, blink: 5, reverse: 7, conceal: 8 };
|
||||||
|
var foreground = { black: 30, red: 31, green: 32, yellow: 33, blue: 34, magenta: 35, cyan: 36, white: 37 };
|
||||||
|
var background = { black: 40, red: 41, green: 42, yellow: 43, blue: 44, magenta: 45, cyan: 46, white: 47 };
|
||||||
|
var styles = {
|
||||||
|
'ERROR': { bg: 'red', fg: 'white', bold: true },
|
||||||
|
'INFO': { fg: 'green', bold: true },
|
||||||
|
'TRACE': { fg: 'green', bold: true },
|
||||||
|
'PARAMETER': { fg: 'cyan' },
|
||||||
|
'COMMENT': { fg: 'yellow' },
|
||||||
|
'WARNING': { fg: 'red', bold: true },
|
||||||
|
'GREEN_BAR': { fg: 'white', bg: 'green', bold: true },
|
||||||
|
'RED_BAR': { fg: 'white', bg: 'red', bold: true },
|
||||||
|
'INFO_BAR': { bg: 'cyan', fg: 'white', bold: true }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a style to provided text.
|
||||||
|
*
|
||||||
|
* @param String text
|
||||||
|
* @param String styleName
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
this.colorize = function colorize(text, styleName, pad) {
|
||||||
|
if (fs.isWindows() || !(styleName in styles)) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
return this.format(text, styles[styleName], pad);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a text using a style declaration object.
|
||||||
|
*
|
||||||
|
* @param String text
|
||||||
|
* @param Object style
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
this.format = function format(text, style, pad) {
|
||||||
|
if (fs.isWindows() || !utils.isObject(style)) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
var codes = [];
|
||||||
|
if (style.fg && foreground[style.fg]) {
|
||||||
|
codes.push(foreground[style.fg]);
|
||||||
|
}
|
||||||
|
if (style.bg && background[style.bg]) {
|
||||||
|
codes.push(background[style.bg]);
|
||||||
|
}
|
||||||
|
for (var option in options) {
|
||||||
|
if (style[option] === true) {
|
||||||
|
codes.push(options[option]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// pad
|
||||||
|
if (typeof pad === "number" && text.length < pad) {
|
||||||
|
text += new Array(pad - text.length + 1).join(' ');
|
||||||
|
}
|
||||||
|
return "\u001b[" + codes.join(';') + 'm' + text + "\u001b[0m";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
exports.Colorizer = Colorizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy colorizer. Does basically nothing.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
var Dummy = function Dummy() {
|
||||||
|
"use strict";
|
||||||
|
this.colorize = function colorize(text, styleName, pad) {
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
this.format = function format(text, style, pad){
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
exports.Dummy = Dummy;
|
|
@ -0,0 +1,246 @@
|
||||||
|
// Copyright Joyent, Inc. and other Node contributors.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
var isArray = Array.isArray;
|
||||||
|
|
||||||
|
function EventEmitter() {
|
||||||
|
this._filters = {};
|
||||||
|
}
|
||||||
|
exports.EventEmitter = EventEmitter;
|
||||||
|
|
||||||
|
// By default EventEmitters will print a warning if more than
|
||||||
|
// 10 listeners are added to it. This is a useful default which
|
||||||
|
// helps finding memory leaks.
|
||||||
|
//
|
||||||
|
// Obviously not all Emitters should be limited to 10. This function allows
|
||||||
|
// that to be increased. Set to zero for unlimited.
|
||||||
|
var defaultMaxListeners = 10;
|
||||||
|
EventEmitter.prototype.setMaxListeners = function(n) {
|
||||||
|
if (!this._events) this._events = {};
|
||||||
|
this._maxListeners = n;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
EventEmitter.prototype.emit = function() {
|
||||||
|
var type = arguments[0];
|
||||||
|
// If there is no 'error' event listener then throw.
|
||||||
|
if (type === 'error') {
|
||||||
|
if (!this._events || !this._events.error ||
|
||||||
|
(isArray(this._events.error) && !this._events.error.length))
|
||||||
|
{
|
||||||
|
if (arguments[1] instanceof Error) {
|
||||||
|
throw arguments[1]; // Unhandled 'error' event
|
||||||
|
} else {
|
||||||
|
throw new CasperError("Uncaught, unspecified 'error' event.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._events) return false;
|
||||||
|
var handler = this._events[type];
|
||||||
|
if (!handler) return false;
|
||||||
|
|
||||||
|
if (typeof handler == 'function') {
|
||||||
|
switch (arguments.length) {
|
||||||
|
// fast cases
|
||||||
|
case 1:
|
||||||
|
handler.call(this);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
handler.call(this, arguments[1]);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
handler.call(this, arguments[1], arguments[2]);
|
||||||
|
break;
|
||||||
|
// slower
|
||||||
|
default:
|
||||||
|
var l = arguments.length;
|
||||||
|
var args = new Array(l - 1);
|
||||||
|
for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
|
||||||
|
handler.apply(this, args);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else if (isArray(handler)) {
|
||||||
|
var l = arguments.length;
|
||||||
|
var args = new Array(l - 1);
|
||||||
|
for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
|
||||||
|
|
||||||
|
var listeners = handler.slice();
|
||||||
|
for (var i = 0, l = listeners.length; i < l; i++) {
|
||||||
|
listeners[i].apply(this, args);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// EventEmitter is defined in src/node_events.cc
|
||||||
|
// EventEmitter.prototype.emit() is also defined there.
|
||||||
|
EventEmitter.prototype.addListener = function(type, listener) {
|
||||||
|
if ('function' !== typeof listener) {
|
||||||
|
throw new CasperError('addListener only takes instances of Function');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._events) this._events = {};
|
||||||
|
|
||||||
|
// To avoid recursion in the case that type == "newListeners"! Before
|
||||||
|
// adding it to the listeners, first emit "newListeners".
|
||||||
|
this.emit('newListener', type, listener);
|
||||||
|
|
||||||
|
if (!this._events[type]) {
|
||||||
|
// Optimize the case of one listener. Don't need the extra array object.
|
||||||
|
this._events[type] = listener;
|
||||||
|
} else if (isArray(this._events[type])) {
|
||||||
|
|
||||||
|
// If we've already got an array, just append.
|
||||||
|
this._events[type].push(listener);
|
||||||
|
|
||||||
|
// Check for listener leak
|
||||||
|
if (!this._events[type].warned) {
|
||||||
|
var m;
|
||||||
|
if (this._maxListeners !== undefined) {
|
||||||
|
m = this._maxListeners;
|
||||||
|
} else {
|
||||||
|
m = defaultMaxListeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m && m > 0 && this._events[type].length > m) {
|
||||||
|
this._events[type].warned = true;
|
||||||
|
console.error('(node) warning: possible EventEmitter memory ' +
|
||||||
|
'leak detected. %d listeners added. ' +
|
||||||
|
'Use emitter.setMaxListeners() to increase limit.',
|
||||||
|
this._events[type].length);
|
||||||
|
console.trace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Adding the second element, need to change to array.
|
||||||
|
this._events[type] = [this._events[type], listener];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
|
||||||
|
|
||||||
|
EventEmitter.prototype.once = function(type, listener) {
|
||||||
|
if ('function' !== typeof listener) {
|
||||||
|
throw new CasperError('.once only takes instances of Function');
|
||||||
|
}
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
function g() {
|
||||||
|
self.removeListener(type, g);
|
||||||
|
listener.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
g.listener = listener;
|
||||||
|
self.on(type, g);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.removeListener = function(type, listener) {
|
||||||
|
if ('function' !== typeof listener) {
|
||||||
|
throw new CasperError('removeListener only takes instances of Function');
|
||||||
|
}
|
||||||
|
|
||||||
|
// does not use listeners(), so no side effect of creating _events[type]
|
||||||
|
if (!this._events || !this._events[type]) return this;
|
||||||
|
|
||||||
|
var list = this._events[type];
|
||||||
|
|
||||||
|
if (isArray(list)) {
|
||||||
|
var position = -1;
|
||||||
|
for (var i = 0, length = list.length; i < length; i++) {
|
||||||
|
if (list[i] === listener ||
|
||||||
|
(list[i].listener && list[i].listener === listener))
|
||||||
|
{
|
||||||
|
position = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position < 0) return this;
|
||||||
|
list.splice(position, 1);
|
||||||
|
if (list.length == 0)
|
||||||
|
delete this._events[type];
|
||||||
|
} else if (list === listener ||
|
||||||
|
(list.listener && list.listener === listener))
|
||||||
|
{
|
||||||
|
delete this._events[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.removeAllListeners = function(type) {
|
||||||
|
if (arguments.length === 0) {
|
||||||
|
this._events = {};
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// does not use listeners(), so no side effect of creating _events[type]
|
||||||
|
if (type && this._events && this._events[type]) this._events[type] = null;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.listeners = function(type) {
|
||||||
|
if (!this._events) this._events = {};
|
||||||
|
if (!this._events[type]) this._events[type] = [];
|
||||||
|
if (!isArray(this._events[type])) {
|
||||||
|
this._events[type] = [this._events[type]];
|
||||||
|
}
|
||||||
|
return this._events[type];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Added for CasperJS: filters a value attached to an event
|
||||||
|
EventEmitter.prototype.filter = function() {
|
||||||
|
var type = arguments[0];
|
||||||
|
if (!this._filters) {
|
||||||
|
this._filters = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var filter = this._filters[type];
|
||||||
|
if (typeof filter !== 'function') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return filter.apply(this, Array.prototype.splice.call(arguments, 1));
|
||||||
|
};
|
||||||
|
|
||||||
|
EventEmitter.prototype.setFilter = function(type, filterFn) {
|
||||||
|
if (!this._filters) {
|
||||||
|
this._filters = {};
|
||||||
|
}
|
||||||
|
if ('function' !== typeof filterFn) {
|
||||||
|
throw new CasperError('setFilter only takes instances of Function');
|
||||||
|
}
|
||||||
|
if (!this._filters[type]) {
|
||||||
|
this._filters[type] = filterFn;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// TODO: process multiple filters? in which order? disallow?
|
||||||
|
return false;
|
||||||
|
};
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*!
|
||||||
|
* Casper is a navigation utility for PhantomJS.
|
||||||
|
*
|
||||||
|
* Documentation: http://casperjs.org/
|
||||||
|
* Repository: http://github.com/n1k0/casperjs
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011-2012 Nicolas Perriault
|
||||||
|
*
|
||||||
|
* Part of source code is Copyright Joyent, Inc. and other Node contributors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
var utils = require('utils');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Building an Array subclass
|
||||||
|
*/
|
||||||
|
function responseHeaders(){}
|
||||||
|
responseHeaders.prototype = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a given header based on its name
|
||||||
|
*
|
||||||
|
* @param String name A case-insensitive response header name
|
||||||
|
* @return mixed A header string or `null` if not found
|
||||||
|
*/
|
||||||
|
responseHeaders.prototype.get = function get(name){
|
||||||
|
"use strict";
|
||||||
|
var headerValue = null;
|
||||||
|
name = name.toLowerCase();
|
||||||
|
this.some(function(header){
|
||||||
|
if (header.name.toLowerCase() === name){
|
||||||
|
headerValue = header.value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return headerValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Augment the response with proper prototypes
|
||||||
|
*
|
||||||
|
* @param mixed response Phantom response or undefined (generally with local files)
|
||||||
|
*/
|
||||||
|
exports.augmentResponse = function(response) {
|
||||||
|
"use strict";
|
||||||
|
if (!utils.isHTTPResource(response)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
response.headers.__proto__ = responseHeaders.prototype;
|
||||||
|
};
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*!
|
||||||
|
* Casper is a navigation utility for PhantomJS.
|
||||||
|
*
|
||||||
|
* Documentation: http://casperjs.org/
|
||||||
|
* Repository: http://github.com/n1k0/casperjs
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011-2012 Nicolas Perriault
|
||||||
|
*
|
||||||
|
* Part of source code is Copyright Joyent, Inc. and other Node contributors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*global CasperError console encodeURIComponent escape exports require*/
|
||||||
|
|
||||||
|
// WARNING: this module is deprecated since CasperJS 1.0.0-RC3
|
||||||
|
|
||||||
|
var utils = require('utils');
|
||||||
|
|
||||||
|
exports.create = function create(fn) {
|
||||||
|
"use strict";
|
||||||
|
return new FunctionArgsInjector(fn);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function argument injector.
|
||||||
|
*
|
||||||
|
* FIXME: use new Function() instead of eval()
|
||||||
|
*/
|
||||||
|
var FunctionArgsInjector = function FunctionArgsInjector(fn) {
|
||||||
|
"use strict";
|
||||||
|
console.error('Warning: the injector module has been deprecated.');
|
||||||
|
|
||||||
|
if (!utils.isFunction(fn)) {
|
||||||
|
throw new CasperError("FunctionArgsInjector() can only process functions");
|
||||||
|
}
|
||||||
|
this.fn = fn;
|
||||||
|
|
||||||
|
this.extract = function extract(fn) {
|
||||||
|
var match = /^function\s?(\w+)?\s?\((.*)\)\s?\{([\s\S]*)\}/i.exec(fn.toString().trim());
|
||||||
|
if (match && match.length > 1) {
|
||||||
|
var args = match[2].split(',').map(function _map(arg) {
|
||||||
|
return arg.replace(new RegExp(/\/\*+.*\*\//ig), "").trim();
|
||||||
|
}).filter(function _filter(arg) {
|
||||||
|
return arg;
|
||||||
|
}) || [];
|
||||||
|
return {
|
||||||
|
name: match[1] ? match[1].trim() : null,
|
||||||
|
args: args,
|
||||||
|
body: match[3] ? match[3].trim() : ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.process = function process(values) {
|
||||||
|
var fnObj = this.extract(this.fn);
|
||||||
|
if (!utils.isObject(fnObj)) {
|
||||||
|
throw new CasperError("Unable to process function " + this.fn.toString());
|
||||||
|
}
|
||||||
|
var inject = this.getArgsInjectionString(fnObj.args, values);
|
||||||
|
var newFn = new Function([inject, fnObj.body].join('\n'));
|
||||||
|
newFn.name = fnObj.name || '';
|
||||||
|
return newFn;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getArgsInjectionString = function getArgsInjectionString(args, values) {
|
||||||
|
values = typeof values === "object" ? values : {};
|
||||||
|
var jsonValues = escape(encodeURIComponent(JSON.stringify(values)));
|
||||||
|
var inject = [
|
||||||
|
'var __casper_params__ = JSON.parse(decodeURIComponent(unescape(\'' + jsonValues + '\')));'
|
||||||
|
];
|
||||||
|
args.forEach(function _forEach(arg) {
|
||||||
|
if (arg in values) {
|
||||||
|
inject.push('var ' + arg + '=__casper_params__["' + arg + '"];');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return inject.join('\n') + '\n';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
exports.FunctionArgsInjector = FunctionArgsInjector;
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*!
|
||||||
|
* Casper is a navigation utility for PhantomJS.
|
||||||
|
*
|
||||||
|
* Documentation: http://casperjs.org/
|
||||||
|
* Repository: http://github.com/n1k0/casperjs
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011-2012 Nicolas Perriault
|
||||||
|
*
|
||||||
|
* Part of source code is Copyright Joyent, Inc. and other Node contributors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*global CasperError exports require*/
|
||||||
|
|
||||||
|
var utils = require('utils');
|
||||||
|
|
||||||
|
exports.create = function create(casper) {
|
||||||
|
"use strict";
|
||||||
|
return new Mouse(casper);
|
||||||
|
};
|
||||||
|
|
||||||
|
var Mouse = function Mouse(casper) {
|
||||||
|
"use strict";
|
||||||
|
if (!utils.isCasperObject(casper)) {
|
||||||
|
throw new CasperError('Mouse() needs a Casper instance');
|
||||||
|
}
|
||||||
|
|
||||||
|
var slice = Array.prototype.slice;
|
||||||
|
|
||||||
|
var nativeEvents = ['mouseup', 'mousedown', 'click', 'mousemove'];
|
||||||
|
var emulatedEvents = ['mouseover', 'mouseout'];
|
||||||
|
var supportedEvents = nativeEvents.concat(emulatedEvents);
|
||||||
|
|
||||||
|
function computeCenter(selector) {
|
||||||
|
var bounds = casper.getElementBounds(selector);
|
||||||
|
if (utils.isClipRect(bounds)) {
|
||||||
|
var x = Math.round(bounds.left + bounds.width / 2);
|
||||||
|
var y = Math.round(bounds.top + bounds.height / 2);
|
||||||
|
return [x, y];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function processEvent(type, args) {
|
||||||
|
if (!utils.isString(type) || supportedEvents.indexOf(type) === -1) {
|
||||||
|
throw new CasperError('Mouse.processEvent(): Unsupported mouse event type: ' + type);
|
||||||
|
}
|
||||||
|
if (emulatedEvents.indexOf(type) > -1) {
|
||||||
|
casper.log("Mouse.processEvent(): no native fallback for type " + type, "warning");
|
||||||
|
}
|
||||||
|
args = slice.call(args); // cast Arguments -> Array
|
||||||
|
casper.emit('mouse.' + type.replace('mouse', ''), args);
|
||||||
|
switch (args.length) {
|
||||||
|
case 0:
|
||||||
|
throw new CasperError('Mouse.processEvent(): Too few arguments');
|
||||||
|
case 1:
|
||||||
|
// selector
|
||||||
|
var selector = args[0];
|
||||||
|
casper.page.sendEvent.apply(casper.page, [type].concat(computeCenter(selector)));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
// coordinates
|
||||||
|
if (!utils.isNumber(args[0]) || !utils.isNumber(args[1])) {
|
||||||
|
throw new CasperError('Mouse.processEvent(): No valid coordinates passed: ' + args);
|
||||||
|
}
|
||||||
|
casper.page.sendEvent(type, args[0], args[1]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new CasperError('Mouse.processEvent(): Too many arguments');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.processEvent = function() {
|
||||||
|
processEvent(arguments[0], slice.call(arguments, 1));
|
||||||
|
};
|
||||||
|
|
||||||
|
this.click = function click() {
|
||||||
|
processEvent('click', arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.down = function down() {
|
||||||
|
processEvent('mousedown', arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.move = function move() {
|
||||||
|
processEvent('mousemove', arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.up = function up() {
|
||||||
|
processEvent('mouseup', arguments);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
exports.Mouse = Mouse;
|
|
@ -0,0 +1,187 @@
|
||||||
|
// Copyright Joyent, Inc. and other Node contributors.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Query String Utilities
|
||||||
|
|
||||||
|
var QueryString = exports;
|
||||||
|
//var urlDecode = process.binding('http_parser').urlDecode; // phantomjs incompatible
|
||||||
|
|
||||||
|
|
||||||
|
// If obj.hasOwnProperty has been overridden, then calling
|
||||||
|
// obj.hasOwnProperty(prop) will break.
|
||||||
|
// See: https://github.com/joyent/node/issues/1707
|
||||||
|
function hasOwnProperty(obj, prop) {
|
||||||
|
return Object.prototype.hasOwnProperty.call(obj, prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function charCode(c) {
|
||||||
|
return c.charCodeAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// a safe fast alternative to decodeURIComponent
|
||||||
|
QueryString.unescapeBuffer = function(s, decodeSpaces) {
|
||||||
|
var out = new Buffer(s.length);
|
||||||
|
var state = 'CHAR'; // states: CHAR, HEX0, HEX1
|
||||||
|
var n, m, hexchar;
|
||||||
|
|
||||||
|
for (var inIndex = 0, outIndex = 0; inIndex <= s.length; inIndex++) {
|
||||||
|
var c = s.charCodeAt(inIndex);
|
||||||
|
switch (state) {
|
||||||
|
case 'CHAR':
|
||||||
|
switch (c) {
|
||||||
|
case charCode('%'):
|
||||||
|
n = 0;
|
||||||
|
m = 0;
|
||||||
|
state = 'HEX0';
|
||||||
|
break;
|
||||||
|
case charCode('+'):
|
||||||
|
if (decodeSpaces) c = charCode(' ');
|
||||||
|
// pass thru
|
||||||
|
default:
|
||||||
|
out[outIndex++] = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'HEX0':
|
||||||
|
state = 'HEX1';
|
||||||
|
hexchar = c;
|
||||||
|
if (charCode('0') <= c && c <= charCode('9')) {
|
||||||
|
n = c - charCode('0');
|
||||||
|
} else if (charCode('a') <= c && c <= charCode('f')) {
|
||||||
|
n = c - charCode('a') + 10;
|
||||||
|
} else if (charCode('A') <= c && c <= charCode('F')) {
|
||||||
|
n = c - charCode('A') + 10;
|
||||||
|
} else {
|
||||||
|
out[outIndex++] = charCode('%');
|
||||||
|
out[outIndex++] = c;
|
||||||
|
state = 'CHAR';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'HEX1':
|
||||||
|
state = 'CHAR';
|
||||||
|
if (charCode('0') <= c && c <= charCode('9')) {
|
||||||
|
m = c - charCode('0');
|
||||||
|
} else if (charCode('a') <= c && c <= charCode('f')) {
|
||||||
|
m = c - charCode('a') + 10;
|
||||||
|
} else if (charCode('A') <= c && c <= charCode('F')) {
|
||||||
|
m = c - charCode('A') + 10;
|
||||||
|
} else {
|
||||||
|
out[outIndex++] = charCode('%');
|
||||||
|
out[outIndex++] = hexchar;
|
||||||
|
out[outIndex++] = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out[outIndex++] = 16 * n + m;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO support returning arbitrary buffers.
|
||||||
|
|
||||||
|
return out.slice(0, outIndex - 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
QueryString.unescape = function(s, decodeSpaces) {
|
||||||
|
return QueryString.unescapeBuffer(s, decodeSpaces).toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
QueryString.escape = function(str) {
|
||||||
|
return encodeURIComponent(str);
|
||||||
|
};
|
||||||
|
|
||||||
|
var stringifyPrimitive = function(v) {
|
||||||
|
switch (typeof v) {
|
||||||
|
case 'string':
|
||||||
|
return v;
|
||||||
|
|
||||||
|
case 'boolean':
|
||||||
|
return v ? 'true' : 'false';
|
||||||
|
|
||||||
|
case 'number':
|
||||||
|
return isFinite(v) ? v : '';
|
||||||
|
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
QueryString.stringify = QueryString.encode = function(obj, sep, eq, name) {
|
||||||
|
sep = sep || '&';
|
||||||
|
eq = eq || '=';
|
||||||
|
obj = (obj === null) ? undefined : obj;
|
||||||
|
|
||||||
|
switch (typeof obj) {
|
||||||
|
case 'object':
|
||||||
|
return Object.keys(obj).map(function(k) {
|
||||||
|
if (Array.isArray(obj[k])) {
|
||||||
|
return obj[k].map(function(v) {
|
||||||
|
return QueryString.escape(stringifyPrimitive(k)) +
|
||||||
|
eq +
|
||||||
|
QueryString.escape(stringifyPrimitive(v));
|
||||||
|
}).join(sep);
|
||||||
|
} else {
|
||||||
|
return QueryString.escape(stringifyPrimitive(k)) +
|
||||||
|
eq +
|
||||||
|
QueryString.escape(stringifyPrimitive(obj[k]));
|
||||||
|
}
|
||||||
|
}).join(sep);
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (!name) return '';
|
||||||
|
return QueryString.escape(stringifyPrimitive(name)) + eq +
|
||||||
|
QueryString.escape(stringifyPrimitive(obj));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse a key=val string.
|
||||||
|
QueryString.parse = QueryString.decode = function(qs, sep, eq) {
|
||||||
|
sep = sep || '&';
|
||||||
|
eq = eq || '=';
|
||||||
|
var obj = {};
|
||||||
|
|
||||||
|
if (typeof qs !== 'string' || qs.length === 0) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
qs.split(sep).forEach(function(kvp) {
|
||||||
|
var x = kvp.split(eq);
|
||||||
|
var k = QueryString.unescape(x[0], true);
|
||||||
|
var v = QueryString.unescape(x.slice(1).join(eq), true);
|
||||||
|
|
||||||
|
if (!hasOwnProperty(obj, k)) {
|
||||||
|
obj[k] = v;
|
||||||
|
} else if (!Array.isArray(obj[k])) {
|
||||||
|
obj[k] = [obj[k], v];
|
||||||
|
} else {
|
||||||
|
obj[k].push(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,508 @@
|
||||||
|
/*!
|
||||||
|
* Casper is a navigation utility for PhantomJS.
|
||||||
|
*
|
||||||
|
* Documentation: http://casperjs.org/
|
||||||
|
* Repository: http://github.com/n1k0/casperjs
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011-2012 Nicolas Perriault
|
||||||
|
*
|
||||||
|
* Part of source code is Copyright Joyent, Inc. and other Node contributors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*global CasperError console exports phantom require*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a better typeof operator equivalent, able to retrieve the array
|
||||||
|
* type.
|
||||||
|
*
|
||||||
|
* @param mixed input
|
||||||
|
* @return String
|
||||||
|
* @see http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
|
||||||
|
*/
|
||||||
|
function betterTypeOf(input) {
|
||||||
|
"use strict";
|
||||||
|
try {
|
||||||
|
return Object.prototype.toString.call(input).match(/^\[object\s(.*)\]$/)[1].toLowerCase();
|
||||||
|
} catch (e) {
|
||||||
|
return typeof input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.betterTypeOf = betterTypeOf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans a passed URL if it lacks a slash at the end when a sole domain is used.
|
||||||
|
*
|
||||||
|
* @param String url An HTTP URL
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
function cleanUrl(url) {
|
||||||
|
"use strict";
|
||||||
|
var parts = /(https?):\/\/(.*)/i.exec(url);
|
||||||
|
if (!parts) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
var protocol = parts[1];
|
||||||
|
var subparts = parts[2].split('/');
|
||||||
|
if (subparts.length === 1) {
|
||||||
|
return format("%s://%s/", protocol, subparts[0]);
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
exports.cleanUrl = cleanUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumps a JSON representation of passed value to the console. Used for
|
||||||
|
* debugging purpose only.
|
||||||
|
*
|
||||||
|
* @param Mixed value
|
||||||
|
*/
|
||||||
|
function dump(value) {
|
||||||
|
"use strict";
|
||||||
|
console.log(serialize(value, 4));
|
||||||
|
}
|
||||||
|
exports.dump = dump;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests equality between the two passed arguments.
|
||||||
|
*
|
||||||
|
* @param Mixed v1
|
||||||
|
* @param Mixed v2
|
||||||
|
* @param Boolean
|
||||||
|
*/
|
||||||
|
function equals(v1, v2) {
|
||||||
|
"use strict";
|
||||||
|
if (isFunction(v1)) {
|
||||||
|
return v1.toString() === v2.toString();
|
||||||
|
}
|
||||||
|
if (v1 instanceof Object) {
|
||||||
|
if (Object.keys(v1).length !== Object.keys(v2).length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (var k in v1) {
|
||||||
|
if (!equals(v1[k], v2[k])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return v1 === v2;
|
||||||
|
}
|
||||||
|
exports.equals = equals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the file extension in lower case.
|
||||||
|
*
|
||||||
|
* @param String file File path
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function fileExt(file) {
|
||||||
|
"use strict";
|
||||||
|
try {
|
||||||
|
return file.split('.').pop().toLowerCase().trim();
|
||||||
|
} catch(e) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.fileExt = fileExt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a string and append blanks until the pad value is reached.
|
||||||
|
*
|
||||||
|
* @param String text
|
||||||
|
* @param Number pad Pad value (optional; default: 80)
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
function fillBlanks(text, pad) {
|
||||||
|
"use strict";
|
||||||
|
pad = pad || 80;
|
||||||
|
if (text.length < pad) {
|
||||||
|
text += new Array(pad - text.length + 1).join(' ');
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
exports.fillBlanks = fillBlanks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a string with passed parameters. Ported from nodejs `util.format()`.
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
function format(f) {
|
||||||
|
"use strict";
|
||||||
|
var i = 1;
|
||||||
|
var args = arguments;
|
||||||
|
var len = args.length;
|
||||||
|
var str = String(f).replace(/%[sdj%]/g, function _replace(x) {
|
||||||
|
if (i >= len) return x;
|
||||||
|
switch (x) {
|
||||||
|
case '%s':
|
||||||
|
return String(args[i++]);
|
||||||
|
case '%d':
|
||||||
|
return Number(args[i++]);
|
||||||
|
case '%j':
|
||||||
|
return JSON.stringify(args[i++]);
|
||||||
|
case '%%':
|
||||||
|
return '%';
|
||||||
|
default:
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (var x = args[i]; i < len; x = args[++i]) {
|
||||||
|
if (x === null || typeof x !== 'object') {
|
||||||
|
str += ' ' + x;
|
||||||
|
} else {
|
||||||
|
str += '[obj]';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
exports.format = format;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the value of an Object foreign property using a dot-separated
|
||||||
|
* path string.
|
||||||
|
*
|
||||||
|
* Beware, this function doesn't handle object key names containing a dot.
|
||||||
|
*
|
||||||
|
* @param Object obj The source object
|
||||||
|
* @param String path Dot separated path, eg. "x.y.z"
|
||||||
|
*/
|
||||||
|
function getPropertyPath(obj, path) {
|
||||||
|
"use strict";
|
||||||
|
if (!isObject(obj) || !isString(path)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
var value = obj;
|
||||||
|
path.split('.').forEach(function(property) {
|
||||||
|
if (typeof value === "object" && property in value) {
|
||||||
|
value = value[property];
|
||||||
|
} else {
|
||||||
|
value = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
exports.getPropertyPath = getPropertyPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inherit the prototype methods from one constructor into another.
|
||||||
|
*
|
||||||
|
* @param {function} ctor Constructor function which needs to inherit the
|
||||||
|
* prototype.
|
||||||
|
* @param {function} superCtor Constructor function to inherit prototype from.
|
||||||
|
*/
|
||||||
|
function inherits(ctor, superCtor) {
|
||||||
|
"use strict";
|
||||||
|
ctor.super_ = ctor.__super__ = superCtor;
|
||||||
|
ctor.prototype = Object.create(superCtor.prototype, {
|
||||||
|
constructor: {
|
||||||
|
value: ctor,
|
||||||
|
enumerable: false,
|
||||||
|
writable: true,
|
||||||
|
configurable: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.inherits = inherits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if value is a javascript Array
|
||||||
|
*
|
||||||
|
* @param mixed value
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
function isArray(value) {
|
||||||
|
"use strict";
|
||||||
|
return Array.isArray(value) || isType(value, "array");
|
||||||
|
}
|
||||||
|
exports.isArray = isArray;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if passed argument is an instance of Capser object.
|
||||||
|
*
|
||||||
|
* @param mixed value
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
function isCasperObject(value) {
|
||||||
|
"use strict";
|
||||||
|
return value instanceof require('casper').Casper;
|
||||||
|
}
|
||||||
|
exports.isCasperObject = isCasperObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if value is a phantomjs clipRect-compatible object
|
||||||
|
*
|
||||||
|
* @param mixed value
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
function isClipRect(value) {
|
||||||
|
"use strict";
|
||||||
|
return isType(value, "cliprect") || (
|
||||||
|
isObject(value) &&
|
||||||
|
isNumber(value.top) && isNumber(value.left) &&
|
||||||
|
isNumber(value.width) && isNumber(value.height)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
exports.isClipRect = isClipRect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if value is a javascript Function
|
||||||
|
*
|
||||||
|
* @param mixed value
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
function isFunction(value) {
|
||||||
|
"use strict";
|
||||||
|
return isType(value, "function");
|
||||||
|
}
|
||||||
|
exports.isFunction = isFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if passed resource involves an HTTP url.
|
||||||
|
*
|
||||||
|
* @param Object resource The PhantomJS HTTP resource object
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
function isHTTPResource(resource) {
|
||||||
|
"use strict";
|
||||||
|
return isObject(resource) && /^http/i.test(resource.url);
|
||||||
|
}
|
||||||
|
exports.isHTTPResource = isHTTPResource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a file is apparently javascript compatible (.js or .coffee).
|
||||||
|
*
|
||||||
|
* @param String file Path to the file to test
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
function isJsFile(file) {
|
||||||
|
"use strict";
|
||||||
|
var ext = fileExt(file);
|
||||||
|
return isString(ext, "string") && ['js', 'coffee'].indexOf(ext) !== -1;
|
||||||
|
}
|
||||||
|
exports.isJsFile = isJsFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the provided value is null
|
||||||
|
*
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
function isNull(value) {
|
||||||
|
"use strict";
|
||||||
|
return isType(value, "null");
|
||||||
|
}
|
||||||
|
exports.isNull = isNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if value is a javascript Number
|
||||||
|
*
|
||||||
|
* @param mixed value
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
function isNumber(value) {
|
||||||
|
"use strict";
|
||||||
|
return isType(value, "number");
|
||||||
|
}
|
||||||
|
exports.isNumber = isNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if value is a javascript Object
|
||||||
|
*
|
||||||
|
* @param mixed value
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
function isObject(value) {
|
||||||
|
"use strict";
|
||||||
|
var objectTypes = ["array", "object", "qtruntimeobject"];
|
||||||
|
return objectTypes.indexOf(betterTypeOf(value)) >= 0;
|
||||||
|
}
|
||||||
|
exports.isObject = isObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if value is a javascript String
|
||||||
|
*
|
||||||
|
* @param mixed value
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
function isString(value) {
|
||||||
|
"use strict";
|
||||||
|
return isType(value, "string");
|
||||||
|
}
|
||||||
|
exports.isString = isString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shorthands for checking if a value is of the given type. Can check for
|
||||||
|
* arrays.
|
||||||
|
*
|
||||||
|
* @param mixed what The value to check
|
||||||
|
* @param String typeName The type name ("string", "number", "function", etc.)
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
function isType(what, typeName) {
|
||||||
|
"use strict";
|
||||||
|
if (typeof typeName !== "string" || !typeName) {
|
||||||
|
throw new CasperError("You must pass isType() a typeName string");
|
||||||
|
}
|
||||||
|
return betterTypeOf(what).toLowerCase() === typeName.toLowerCase();
|
||||||
|
}
|
||||||
|
exports.isType = isType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the provided value is undefined
|
||||||
|
*
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
function isUndefined(value) {
|
||||||
|
"use strict";
|
||||||
|
return isType(value, "undefined");
|
||||||
|
}
|
||||||
|
exports.isUndefined = isUndefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if value is a valid selector Object.
|
||||||
|
*
|
||||||
|
* @param mixed value
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
function isValidSelector(value) {
|
||||||
|
"use strict";
|
||||||
|
if (isString(value)) {
|
||||||
|
try {
|
||||||
|
// phantomjs env has a working document object, let's use it
|
||||||
|
document.querySelector(value);
|
||||||
|
} catch(e) {
|
||||||
|
if ('name' in e && e.name === 'SYNTAX_ERR') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (isObject(value)) {
|
||||||
|
if (!value.hasOwnProperty('type')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!value.hasOwnProperty('path')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (['css', 'xpath'].indexOf(value.type) === -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
exports.isValidSelector = isValidSelector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the provided var is a WebPage instance
|
||||||
|
*
|
||||||
|
* @param mixed what
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
function isWebPage(what) {
|
||||||
|
"use strict";
|
||||||
|
return betterTypeOf(what) === "qtruntimeobject" && what.objectName === 'WebPage';
|
||||||
|
}
|
||||||
|
exports.isWebPage = isWebPage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object recursive merging utility.
|
||||||
|
*
|
||||||
|
* @param Object origin the origin object
|
||||||
|
* @param Object add the object to merge data into origin
|
||||||
|
* @return Object
|
||||||
|
*/
|
||||||
|
function mergeObjects(origin, add) {
|
||||||
|
"use strict";
|
||||||
|
for (var p in add) {
|
||||||
|
try {
|
||||||
|
if (add[p].constructor === Object) {
|
||||||
|
origin[p] = mergeObjects(origin[p], add[p]);
|
||||||
|
} else {
|
||||||
|
origin[p] = add[p];
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
origin[p] = add[p];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
exports.mergeObjects = mergeObjects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an (SG|X)ML node element.
|
||||||
|
*
|
||||||
|
* @param String name The node name
|
||||||
|
* @param Object attributes Optional attributes
|
||||||
|
* @return HTMLElement
|
||||||
|
*/
|
||||||
|
function node(name, attributes) {
|
||||||
|
"use strict";
|
||||||
|
var _node = document.createElement(name);
|
||||||
|
for (var attrName in attributes) {
|
||||||
|
var value = attributes[attrName];
|
||||||
|
if (attributes.hasOwnProperty(attrName) && isString(attrName)) {
|
||||||
|
_node.setAttribute(attrName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _node;
|
||||||
|
}
|
||||||
|
exports.node = node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes a value using JSON.
|
||||||
|
*
|
||||||
|
* @param Mixed value
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
function serialize(value, indent) {
|
||||||
|
"use strict";
|
||||||
|
if (isArray(value)) {
|
||||||
|
value = value.map(function _map(prop) {
|
||||||
|
return isFunction(prop) ? prop.toString().replace(/\s{2,}/, '') : prop;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return JSON.stringify(value, null, indent);
|
||||||
|
}
|
||||||
|
exports.serialize = serialize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns unique values from an array.
|
||||||
|
*
|
||||||
|
* Note: ugly code is ugly, but efficient: http://jsperf.com/array-unique2/8
|
||||||
|
*
|
||||||
|
* @param Array array
|
||||||
|
* @return Array
|
||||||
|
*/
|
||||||
|
function unique(array) {
|
||||||
|
"use strict";
|
||||||
|
var o = {},
|
||||||
|
r = [];
|
||||||
|
for (var i = 0, len = array.length; i !== len; i++) {
|
||||||
|
var d = array[i];
|
||||||
|
if (o[d] !== 1) {
|
||||||
|
o[d] = 1;
|
||||||
|
r[r.length] = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
exports.unique = unique;
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,128 @@
|
||||||
|
/*!
|
||||||
|
* Casper is a navigation utility for PhantomJS.
|
||||||
|
*
|
||||||
|
* Documentation: http://casperjs.org/
|
||||||
|
* Repository: http://github.com/n1k0/casperjs
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011-2012 Nicolas Perriault
|
||||||
|
*
|
||||||
|
* Part of source code is Copyright Joyent, Inc. and other Node contributors.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*global CasperError console exports phantom require*/
|
||||||
|
|
||||||
|
var utils = require('utils');
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a value for 'classname' attribute of the JUnit XML report.
|
||||||
|
*
|
||||||
|
* Uses the (relative) file name of the current casper script without file
|
||||||
|
* extension as classname.
|
||||||
|
*
|
||||||
|
* @param String classname
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
function generateClassName(classname) {
|
||||||
|
"use strict";
|
||||||
|
classname = classname.replace(phantom.casperPath, "").trim();
|
||||||
|
var script = classname || phantom.casperScript;
|
||||||
|
if (script.indexOf(fs.workingDirectory) === 0) {
|
||||||
|
script = script.substring(fs.workingDirectory.length + 1);
|
||||||
|
}
|
||||||
|
if (script.indexOf('/') === 0) {
|
||||||
|
script = script.substring(1, script.length);
|
||||||
|
}
|
||||||
|
if (~script.indexOf('.')) {
|
||||||
|
script = script.substring(0, script.lastIndexOf('.'));
|
||||||
|
}
|
||||||
|
return script || "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a XUnit instance
|
||||||
|
*
|
||||||
|
* @return XUnit
|
||||||
|
*/
|
||||||
|
exports.create = function create() {
|
||||||
|
"use strict";
|
||||||
|
return new XUnitExporter();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JUnit XML (xUnit) exporter for test results.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function XUnitExporter() {
|
||||||
|
"use strict";
|
||||||
|
this._xml = utils.node('testsuite');
|
||||||
|
this._xml.toString = function toString() {
|
||||||
|
return this.outerHTML; // ouch
|
||||||
|
};
|
||||||
|
}
|
||||||
|
exports.XUnitExporter = XUnitExporter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a successful test result.
|
||||||
|
*
|
||||||
|
* @param String classname
|
||||||
|
* @param String name
|
||||||
|
*/
|
||||||
|
XUnitExporter.prototype.addSuccess = function addSuccess(classname, name) {
|
||||||
|
"use strict";
|
||||||
|
this._xml.appendChild(utils.node('testcase', {
|
||||||
|
classname: generateClassName(classname),
|
||||||
|
name: name
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a failed test result.
|
||||||
|
*
|
||||||
|
* @param String classname
|
||||||
|
* @param String name
|
||||||
|
* @param String message
|
||||||
|
* @param String type
|
||||||
|
*/
|
||||||
|
XUnitExporter.prototype.addFailure = function addFailure(classname, name, message, type) {
|
||||||
|
"use strict";
|
||||||
|
var fnode = utils.node('testcase', {
|
||||||
|
classname: generateClassName(classname),
|
||||||
|
name: name
|
||||||
|
});
|
||||||
|
var failure = utils.node('failure', {
|
||||||
|
type: type || "unknown"
|
||||||
|
});
|
||||||
|
failure.appendChild(document.createTextNode(message || "no message left"));
|
||||||
|
fnode.appendChild(failure);
|
||||||
|
this._xml.appendChild(fnode);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves generated XML object - actually an HTMLElement.
|
||||||
|
*
|
||||||
|
* @return HTMLElement
|
||||||
|
*/
|
||||||
|
XUnitExporter.prototype.getXML = function getXML() {
|
||||||
|
"use strict";
|
||||||
|
return this._xml;
|
||||||
|
};
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"name": "casperjs",
|
||||||
|
"description": "Navigation scripting & testing utility for PhantomJS",
|
||||||
|
"version": "1.0.0-RC4",
|
||||||
|
"keywords": [
|
||||||
|
"phantomjs",
|
||||||
|
"javascript"
|
||||||
|
],
|
||||||
|
"maintainers": [
|
||||||
|
{
|
||||||
|
"name": "Nicolas Perriault",
|
||||||
|
"email": "nperriault@gmail.com",
|
||||||
|
"web": "http://www.akei.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"http://www.phantomjs.org/": "1.6"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/n1k0/casperjs/issues"
|
||||||
|
},
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/n1k0/casperjs.git"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"licenses": [
|
||||||
|
{
|
||||||
|
"name": "MIT",
|
||||||
|
"url": "http://www.opensource.org/licenses/mit-license.php"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"homepage": "http://casperjs.org"
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
# Ruby Wrapper for CasperJs
|
||||||
|
# by hannyu
|
||||||
|
|
||||||
|
def resolve(file_path)
|
||||||
|
while File.symlink?(file_path) do
|
||||||
|
file_path = File.readlink(file_path)
|
||||||
|
end
|
||||||
|
file_path
|
||||||
|
end
|
||||||
|
|
||||||
|
CASPER_PATH = File.dirname(File.dirname(File.expand_path(resolve(__FILE__))))
|
||||||
|
|
||||||
|
PHANTOMJS_NATIVE_ARGS = [
|
||||||
|
'--cookies-file',
|
||||||
|
'--config',
|
||||||
|
'--debug',
|
||||||
|
'--disk-cache',
|
||||||
|
'--ignore-ssl-errors',
|
||||||
|
'--load-images',
|
||||||
|
'--load-plugins',
|
||||||
|
'--local-storage-path',
|
||||||
|
'--local-storage-quota',
|
||||||
|
'--local-to-remote-url-access',
|
||||||
|
'--max-disk-cache-size',
|
||||||
|
'--output-encoding',
|
||||||
|
'--proxy',
|
||||||
|
'--proxy-auth',
|
||||||
|
'--proxy-type',
|
||||||
|
'--remote-debugger-port',
|
||||||
|
'--remote-debugger-autorun',
|
||||||
|
'--script-encoding',
|
||||||
|
'--web-security',
|
||||||
|
]
|
||||||
|
|
||||||
|
CASPER_ARGS = []
|
||||||
|
PHANTOMJS_ARGS = []
|
||||||
|
ARGV.each do |arg|
|
||||||
|
is_found = false
|
||||||
|
PHANTOMJS_NATIVE_ARGS.each do |pna|
|
||||||
|
if arg.start_with? pna
|
||||||
|
PHANTOMJS_ARGS << arg
|
||||||
|
is_found = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
CASPER_ARGS << arg if not is_found
|
||||||
|
end
|
||||||
|
|
||||||
|
CASPER_COMMAND = []
|
||||||
|
CASPER_COMMAND << (ENV["PHANTOMJS_EXECUTABLE"] || "phantomjs")
|
||||||
|
CASPER_COMMAND.concat PHANTOMJS_ARGS
|
||||||
|
CASPER_COMMAND << File.join(CASPER_PATH, "bin", "bootstrap.js")
|
||||||
|
CASPER_COMMAND << "--casper-path=#{CASPER_PATH}"
|
||||||
|
CASPER_COMMAND << "--cli"
|
||||||
|
CASPER_COMMAND.concat CASPER_ARGS
|
||||||
|
|
||||||
|
if system(CASPER_COMMAND.join(" ")).nil?
|
||||||
|
puts "Fatal: Did you install phantomjs?"
|
||||||
|
end
|
||||||
|
|
||||||
|
exit $?.exitstatus
|
|
@ -0,0 +1,64 @@
|
||||||
|
###
|
||||||
|
Create a mosaic image from all headline photos on BBC homepage
|
||||||
|
###
|
||||||
|
|
||||||
|
casper = require("casper").create()
|
||||||
|
nbLinks = 0
|
||||||
|
currentLink = 1
|
||||||
|
images = []
|
||||||
|
|
||||||
|
# helper to hide some element from remote DOM
|
||||||
|
casper.hide = (selector) ->
|
||||||
|
@evaluate (selector) ->
|
||||||
|
document.querySelector(selector).style.display = "none"
|
||||||
|
, selector: selector
|
||||||
|
|
||||||
|
casper.start "http://www.bbc.co.uk/", ->
|
||||||
|
nbLinks = @evaluate ->
|
||||||
|
return __utils__.findAll('#promo2_carousel_items_items li').length
|
||||||
|
@echo "#{nbLinks} items founds"
|
||||||
|
# hide navigation arrows
|
||||||
|
@hide ".nav_left"
|
||||||
|
@hide ".nav_right"
|
||||||
|
@mouse.move "#promo2_carousel"
|
||||||
|
@waitUntilVisible ".autoplay.nav_pause", ->
|
||||||
|
@echo "Moving over pause button"
|
||||||
|
@mouse.move ".autoplay.nav_pause"
|
||||||
|
@click ".autoplay.nav_pause"
|
||||||
|
@echo "Clicked on pause button"
|
||||||
|
@waitUntilVisible ".autoplay.nav_play", ->
|
||||||
|
@echo "Carousel has been paused"
|
||||||
|
# hide play button
|
||||||
|
@hide ".autoplay"
|
||||||
|
|
||||||
|
# Capture carrousel area
|
||||||
|
next = ->
|
||||||
|
image = "bbcshot#{currentLink}.png"
|
||||||
|
images.push image
|
||||||
|
@echo "Processing image #{currentLink}"
|
||||||
|
@captureSelector image, '.carousel_viewport'
|
||||||
|
if currentLink < nbLinks
|
||||||
|
@click ".carousel_itemList_li[rel='#{currentLink}']"
|
||||||
|
@wait 1000, ->
|
||||||
|
@then next
|
||||||
|
currentLink++
|
||||||
|
else
|
||||||
|
@then buildPage
|
||||||
|
|
||||||
|
# Building resulting page and image
|
||||||
|
buildPage = ->
|
||||||
|
@echo "Build result page"
|
||||||
|
fs = require "fs"
|
||||||
|
@viewport 624, 400
|
||||||
|
pageHtml = "<html><body style='background:black;margin:0;padding:0'>"
|
||||||
|
images.forEach (image) ->
|
||||||
|
pageHtml += "<img src='file://#{fs.workingDirectory}/#{image}'><br>"
|
||||||
|
pageHtml += "</body></html>"
|
||||||
|
fs.write "result.html", pageHtml, 'w'
|
||||||
|
@thenOpen "file://#{fs.workingDirectory}/result.html", ->
|
||||||
|
@echo "Resulting image saved to result.png"
|
||||||
|
@capture "result.png"
|
||||||
|
|
||||||
|
casper.then next
|
||||||
|
|
||||||
|
casper.run()
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Create a mosaic image from all headline photos on BBC homepage
|
||||||
|
*/
|
||||||
|
|
||||||
|
var casper = require("casper").create();
|
||||||
|
var nbLinks = 0;
|
||||||
|
var currentLink = 1;
|
||||||
|
var images = [];
|
||||||
|
var buildPage, next;
|
||||||
|
|
||||||
|
// helper to hide some element from remote DOM
|
||||||
|
casper.hide = function(selector) {
|
||||||
|
this.evaluate(function(selector) {
|
||||||
|
document.querySelector(selector).style.display = "none";
|
||||||
|
}, {
|
||||||
|
selector: selector
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
casper.start("http://www.bbc.co.uk/", function() {
|
||||||
|
nbLinks = this.evaluate(function() {
|
||||||
|
return __utils__.findAll('#promo2_carousel_items_items li').length;
|
||||||
|
});
|
||||||
|
this.echo(nbLinks + " items founds");
|
||||||
|
// hide navigation arrows
|
||||||
|
this.hide(".nav_left");
|
||||||
|
this.hide(".nav_right");
|
||||||
|
this.mouse.move("#promo2_carousel");
|
||||||
|
this.waitUntilVisible(".autoplay.nav_pause", function() {
|
||||||
|
this.echo("Moving over pause button");
|
||||||
|
this.mouse.move(".autoplay.nav_pause");
|
||||||
|
this.click(".autoplay.nav_pause");
|
||||||
|
this.echo("Clicked on pause button");
|
||||||
|
this.waitUntilVisible(".autoplay.nav_play", function() {
|
||||||
|
this.echo("Carousel has been paused");
|
||||||
|
// hide play button
|
||||||
|
this.hide(".autoplay");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Capture carrousel area
|
||||||
|
next = function() {
|
||||||
|
var image;
|
||||||
|
image = "bbcshot" + currentLink + ".png";
|
||||||
|
images.push(image);
|
||||||
|
this.echo("Processing image " + currentLink);
|
||||||
|
this.captureSelector(image, '.carousel_viewport');
|
||||||
|
if (currentLink < nbLinks) {
|
||||||
|
this.click(".carousel_itemList_li[rel='" + currentLink + "']");
|
||||||
|
this.wait(1000, function() {
|
||||||
|
this.then(next);
|
||||||
|
currentLink++;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.then(buildPage);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Building resulting page and image
|
||||||
|
buildPage = function() {
|
||||||
|
var fs, pageHtml;
|
||||||
|
this.echo("Build result page");
|
||||||
|
fs = require("fs");
|
||||||
|
this.viewport(624, 400);
|
||||||
|
pageHtml = "<html><body style='background:black;margin:0;padding:0'>";
|
||||||
|
images.forEach(function(image) {
|
||||||
|
pageHtml += "<img src='file://" + fs.workingDirectory + "/" + image + "'><br>";
|
||||||
|
});
|
||||||
|
pageHtml += "</body></html>";
|
||||||
|
fs.write("result.html", pageHtml, 'w');
|
||||||
|
this.thenOpen("file://" + fs.workingDirectory + "/result.html", function() {
|
||||||
|
this.echo("Resulting image saved to result.png");
|
||||||
|
this.capture("result.png");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
casper.then(next);
|
||||||
|
|
||||||
|
casper.run();
|
|
@ -0,0 +1,19 @@
|
||||||
|
casper = require("casper").create()
|
||||||
|
dump = require("utils").dump
|
||||||
|
|
||||||
|
# removing default options passed by the Python executable
|
||||||
|
casper.cli.drop "cli"
|
||||||
|
casper.cli.drop "casper-path"
|
||||||
|
|
||||||
|
if casper.cli.args.length is 0 and Object.keys(casper.cli.options).length is 0
|
||||||
|
casper
|
||||||
|
.echo("Pass some args and options to see how they are handled by CasperJS")
|
||||||
|
.exit(1)
|
||||||
|
|
||||||
|
casper.echo "Casper CLI passed args:"
|
||||||
|
dump casper.cli.args
|
||||||
|
|
||||||
|
casper.echo "Casper CLI passed options:"
|
||||||
|
dump casper.cli.options
|
||||||
|
|
||||||
|
casper.exit()
|
|
@ -0,0 +1,21 @@
|
||||||
|
var casper = require("casper").create();
|
||||||
|
var dump = require("utils").dump;
|
||||||
|
|
||||||
|
// removing default options passed by the Python executable
|
||||||
|
casper.cli.drop("cli");
|
||||||
|
casper.cli.drop("casper-path");
|
||||||
|
|
||||||
|
if (casper.cli.args.length === 0 && Object.keys(casper.cli.options).length === 0) {
|
||||||
|
casper
|
||||||
|
.echo("Pass some args and options to see how they are handled by CasperJS")
|
||||||
|
.exit(1)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
casper.echo("Casper CLI passed args:");
|
||||||
|
dump(casper.cli.args);
|
||||||
|
|
||||||
|
casper.echo("Casper CLI passed options:");
|
||||||
|
dump(casper.cli.options);
|
||||||
|
|
||||||
|
casper.exit();
|
|
@ -0,0 +1,11 @@
|
||||||
|
casper = require("casper").create()
|
||||||
|
|
||||||
|
# listening to a custom event
|
||||||
|
casper.on "google.loaded", (title) ->
|
||||||
|
@echo "Google page title is #{title}"
|
||||||
|
|
||||||
|
casper.start "http://google.com/", ->
|
||||||
|
# emitting a custom event
|
||||||
|
@emit "google.loaded", @getTitle()
|
||||||
|
|
||||||
|
casper.run()
|
|
@ -0,0 +1,13 @@
|
||||||
|
var casper = require("casper").create();
|
||||||
|
|
||||||
|
// listening to a custom event
|
||||||
|
casper.on("google.loaded", function(title) {
|
||||||
|
this.echo("Google page title is " + title);
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.start("http://google.com/", function() {
|
||||||
|
// emitting a custom event
|
||||||
|
this.emit("google.loaded", this.getTitle());
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.run();
|
|
@ -0,0 +1,33 @@
|
||||||
|
###
|
||||||
|
A basic custom logging implementation. The idea is to (extremely) verbosely
|
||||||
|
log every received resource.
|
||||||
|
###
|
||||||
|
|
||||||
|
casper = require("casper").create
|
||||||
|
###
|
||||||
|
Every time a resource is received, a new log entry is added to the stack
|
||||||
|
at the 'verbose' level.
|
||||||
|
|
||||||
|
@param Object resource A phantomjs resource object
|
||||||
|
###
|
||||||
|
onResourceReceived: (self, resource) ->
|
||||||
|
infos = []
|
||||||
|
props = [
|
||||||
|
"url"
|
||||||
|
"status"
|
||||||
|
"statusText"
|
||||||
|
"redirectURL"
|
||||||
|
"bodySize"
|
||||||
|
]
|
||||||
|
infos.push resource[prop] for prop in props
|
||||||
|
infos.push "[#{header.name}: #{header.value}]" for header in resource.headers
|
||||||
|
@log infos.join(", "), "verbose"
|
||||||
|
verbose: true # we want to see the log printed out to the console
|
||||||
|
logLevel: "verbose" # of course we want to see logs to our new level :)
|
||||||
|
|
||||||
|
# add a new 'verbose' logging level at the lowest priority
|
||||||
|
casper.logLevels = ["verbose"].concat casper.logLevels
|
||||||
|
|
||||||
|
# test our new logger with google
|
||||||
|
casper.start("http://www.google.com/").run ->
|
||||||
|
@exit()
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* A basic custom logging implementation. The idea is to (extremely) verbosely
|
||||||
|
* log every received resource.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var casper = require("casper").create({
|
||||||
|
/*
|
||||||
|
Every time a resource is received, a new log entry is added to the stack at
|
||||||
|
the 'verbose' level.
|
||||||
|
*/
|
||||||
|
onResourceReceived: function(self, resource) {
|
||||||
|
var header, infos, prop, props, _i, _j, _len, _len1, _ref;
|
||||||
|
infos = [];
|
||||||
|
props = [
|
||||||
|
"url",
|
||||||
|
"status",
|
||||||
|
"statusText",
|
||||||
|
"redirectURL",
|
||||||
|
"bodySize"
|
||||||
|
];
|
||||||
|
for (_i = 0, _len = props.length; _i < _len; _i++) {
|
||||||
|
prop = props[_i];
|
||||||
|
infos.push(resource[prop]);
|
||||||
|
}
|
||||||
|
_ref = resource.headers;
|
||||||
|
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
|
||||||
|
header = _ref[_j];
|
||||||
|
infos.push("[" + header.name + ": " + header.value + "]");
|
||||||
|
}
|
||||||
|
this.log(infos.join(", "), "verbose");
|
||||||
|
},
|
||||||
|
verbose: true,
|
||||||
|
logLevel: "verbose"
|
||||||
|
});
|
||||||
|
|
||||||
|
// add a new 'verbose' logging level at the lowest priority
|
||||||
|
casper.logLevels = ["verbose"].concat(casper.logLevels);
|
||||||
|
|
||||||
|
// test our new logger with google
|
||||||
|
casper.start("http://www.google.com/").run(function() {
|
||||||
|
this.exit();
|
||||||
|
});
|
|
@ -0,0 +1,10 @@
|
||||||
|
###
|
||||||
|
Download the google logo image onto the local filesystem
|
||||||
|
###
|
||||||
|
|
||||||
|
casper = require("casper").create()
|
||||||
|
|
||||||
|
casper.start "http://www.google.fr/", ->
|
||||||
|
@echo @download "http://www.google.fr/images/srpr/logo3w.png", "logo.png"
|
||||||
|
|
||||||
|
casper.run()
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* download the google logo image onto the local filesystem
|
||||||
|
*/
|
||||||
|
|
||||||
|
var casper = require("casper").create();
|
||||||
|
|
||||||
|
casper.start("http://www.google.fr/", function() {
|
||||||
|
this.download("http://www.google.fr/images/srpr/logo3w.png", "logo.png");
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.run();
|
|
@ -0,0 +1,60 @@
|
||||||
|
casper = require("casper").create
|
||||||
|
verbose: true
|
||||||
|
|
||||||
|
# The base links array
|
||||||
|
links = [
|
||||||
|
"http://google.com/"
|
||||||
|
"http://yahoo.com/"
|
||||||
|
"http://bing.com/"
|
||||||
|
]
|
||||||
|
|
||||||
|
currentLink = 0;
|
||||||
|
|
||||||
|
# If we don't set a limit, it could go on forever
|
||||||
|
upTo = ~~casper.cli.get(0) || 10
|
||||||
|
|
||||||
|
###
|
||||||
|
Get the links, and add them to the links array
|
||||||
|
(It could be done all in one step, but it is intentionally splitted)
|
||||||
|
###
|
||||||
|
addLinks = (link) ->
|
||||||
|
@then ->
|
||||||
|
found = @evaluate searchLinks
|
||||||
|
@echo "#{found.length} links found on #{link}"
|
||||||
|
links = links.concat found
|
||||||
|
|
||||||
|
###
|
||||||
|
Fetch all <a> elements from the page and return
|
||||||
|
the ones which contains a href starting with 'http://'
|
||||||
|
###
|
||||||
|
searchLinks = ->
|
||||||
|
filter = Array::filter
|
||||||
|
map = Array::map
|
||||||
|
map.call filter.call(document.querySelectorAll("a"), (a) ->
|
||||||
|
(/^http:\/\/.*/i).test a.getAttribute("href")
|
||||||
|
), (a) ->
|
||||||
|
a.getAttribute "href"
|
||||||
|
|
||||||
|
# Just opens the page and prints the title
|
||||||
|
start = (link) ->
|
||||||
|
@start link, ->
|
||||||
|
@echo "Page title: #{ @getTitle() }"
|
||||||
|
|
||||||
|
# As long as it has a next link, and is under the maximum limit, will keep running
|
||||||
|
check = ->
|
||||||
|
if links[currentLink] && currentLink < upTo
|
||||||
|
@echo "--- Link #{currentLink} ---"
|
||||||
|
start.call @, links[currentLink]
|
||||||
|
addLinks.call @, links[currentLink]
|
||||||
|
currentLink++
|
||||||
|
@run check
|
||||||
|
else
|
||||||
|
@echo "All done."
|
||||||
|
@exit()
|
||||||
|
|
||||||
|
casper.start()
|
||||||
|
|
||||||
|
casper.then ->
|
||||||
|
@echo "Starting"
|
||||||
|
|
||||||
|
casper.run check
|
|
@ -0,0 +1,65 @@
|
||||||
|
var casper = require("casper").create({
|
||||||
|
verbose: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// The base links array
|
||||||
|
var links = [
|
||||||
|
"http://google.com/",
|
||||||
|
"http://yahoo.com/",
|
||||||
|
"http://bing.com/"
|
||||||
|
];
|
||||||
|
|
||||||
|
// If we don't set a limit, it could go on forever
|
||||||
|
var upTo = ~~casper.cli.get(0) || 10;
|
||||||
|
|
||||||
|
var currentLink = 0;
|
||||||
|
|
||||||
|
// Get the links, and add them to the links array
|
||||||
|
// (It could be done all in one step, but it is intentionally splitted)
|
||||||
|
function addLinks(link) {
|
||||||
|
this.then(function() {
|
||||||
|
var found = this.evaluate(searchLinks);
|
||||||
|
this.echo(found.length + " links found on " + link);
|
||||||
|
links = links.concat(found);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch all <a> elements from the page and return
|
||||||
|
// the ones which contains a href starting with 'http://'
|
||||||
|
function searchLinks() {
|
||||||
|
var filter, map;
|
||||||
|
filter = Array.prototype.filter;
|
||||||
|
map = Array.prototype.map;
|
||||||
|
return map.call(filter.call(document.querySelectorAll("a"), function(a) {
|
||||||
|
return (/^http:\/\/.*/i).test(a.getAttribute("href"));
|
||||||
|
}), function(a) {
|
||||||
|
return a.getAttribute("href");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just opens the page and prints the title
|
||||||
|
function start(link) {
|
||||||
|
this.start(link, function() {
|
||||||
|
this.echo('Page title: ' + this.getTitle());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// As long as it has a next link, and is under the maximum limit, will keep running
|
||||||
|
function check() {
|
||||||
|
if (links[currentLink] && currentLink < upTo) {
|
||||||
|
this.echo('--- Link ' + currentLink + ' ---');
|
||||||
|
start.call(this, links[currentLink]);
|
||||||
|
addLinks.call(this, links[currentLink]);
|
||||||
|
currentLink++;
|
||||||
|
this.run(check);
|
||||||
|
} else {
|
||||||
|
this.echo("All done.");
|
||||||
|
this.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
casper.start().then(function() {
|
||||||
|
this.echo("Starting");
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.run(check);
|
|
@ -0,0 +1,14 @@
|
||||||
|
casper = require("casper").create()
|
||||||
|
|
||||||
|
links = [
|
||||||
|
"http://google.com/"
|
||||||
|
"http://yahoo.com/"
|
||||||
|
"http://bing.com/"
|
||||||
|
]
|
||||||
|
|
||||||
|
casper.start()
|
||||||
|
|
||||||
|
casper.each links, (self, link) ->
|
||||||
|
@thenOpen link, -> @echo "#{@getTitle()} - #{link}"
|
||||||
|
|
||||||
|
casper.run()
|
|
@ -0,0 +1,17 @@
|
||||||
|
var casper = require("casper").create();
|
||||||
|
|
||||||
|
var links = [
|
||||||
|
"http://google.com/",
|
||||||
|
"http://yahoo.com/",
|
||||||
|
"http://bing.com/"
|
||||||
|
];
|
||||||
|
|
||||||
|
casper.start();
|
||||||
|
|
||||||
|
casper.each(links, function(self, link) {
|
||||||
|
this.thenOpen(link, function() {
|
||||||
|
this.echo(this.getTitle() + " - " + link);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.run();
|
|
@ -0,0 +1,34 @@
|
||||||
|
###
|
||||||
|
This script will add a custom HTTP status code handler, here for 404 pages.
|
||||||
|
###
|
||||||
|
|
||||||
|
casper = require("casper").create()
|
||||||
|
|
||||||
|
casper.on "http.status.200", (resource) ->
|
||||||
|
@echo "#{resource.url} is OK", "INFO"
|
||||||
|
|
||||||
|
casper.on "http.status.301", (resource) ->
|
||||||
|
@echo "#{resource.url} is permanently redirected", "PARAMETER"
|
||||||
|
|
||||||
|
casper.on "http.status.302", (resource) ->
|
||||||
|
@echo "#{resource.url} is temporarily redirected", "PARAMETER"
|
||||||
|
|
||||||
|
casper.on "http.status.404", (resource) ->
|
||||||
|
@echo "#{resource.url} is not found", "COMMENT"
|
||||||
|
|
||||||
|
casper.on "http.status.500", (resource) ->
|
||||||
|
@echo "#{resource.url} is in error", "ERROR"
|
||||||
|
|
||||||
|
links = [
|
||||||
|
"http://google.com/"
|
||||||
|
"http://www.google.com/"
|
||||||
|
"http://www.google.com/plop"
|
||||||
|
]
|
||||||
|
|
||||||
|
casper.start()
|
||||||
|
|
||||||
|
casper.each links, (self, link) ->
|
||||||
|
self.thenOpen link, ->
|
||||||
|
@echo "#{link} loaded"
|
||||||
|
|
||||||
|
casper.run()
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* This script will add a custom HTTP status code handler, here for 404 pages.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var casper = require("casper").create();
|
||||||
|
|
||||||
|
casper.on("http.status.200", function(resource) {
|
||||||
|
this.echo(resource.url + " is OK", "INFO");
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.on("http.status.301", function(resource) {
|
||||||
|
this.echo(resource.url + " is permanently redirected", "PARAMETER");
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.on("http.status.302", function(resource) {
|
||||||
|
this.echo(resource.url + " is temporarily redirected", "PARAMETER");
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.on("http.status.404", function(resource) {
|
||||||
|
this.echo(resource.url + " is not found", "COMMENT");
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.on("http.status.500", function(resource) {
|
||||||
|
this.echo(resource.url + " is in error", "ERROR");
|
||||||
|
});
|
||||||
|
|
||||||
|
var links = [
|
||||||
|
"http://google.com/",
|
||||||
|
"http://www.google.com/",
|
||||||
|
"http://www.google.com/plop"
|
||||||
|
];
|
||||||
|
|
||||||
|
casper.start();
|
||||||
|
|
||||||
|
casper.each(links, function(self, link) {
|
||||||
|
self.thenOpen(link, function() {
|
||||||
|
this.echo(link + " loaded");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.run();
|
|
@ -0,0 +1,29 @@
|
||||||
|
casper = require("casper").create
|
||||||
|
loadImages: false
|
||||||
|
logLevel: "debug"
|
||||||
|
verbose: true
|
||||||
|
|
||||||
|
links =
|
||||||
|
"http://edition.cnn.com/": 0
|
||||||
|
"http://www.nytimes.com/": 0
|
||||||
|
"http://www.bbc.co.uk/": 0
|
||||||
|
"http://www.guardian.co.uk/": 0
|
||||||
|
|
||||||
|
fantomas = Object.create(casper)
|
||||||
|
|
||||||
|
fantomas.countLinks = ->
|
||||||
|
@evaluate ->
|
||||||
|
__utils__.findAll("a[href]").length
|
||||||
|
|
||||||
|
fantomas.renderJSON = (what) ->
|
||||||
|
@echo JSON.stringify(what, null, " ")
|
||||||
|
|
||||||
|
fantomas.start()
|
||||||
|
|
||||||
|
Object.keys(links).forEach (url) ->
|
||||||
|
fantomas.thenOpen url, ->
|
||||||
|
links[url] = @countLinks()
|
||||||
|
|
||||||
|
fantomas.run ->
|
||||||
|
@renderJSON(links)
|
||||||
|
@exit()
|
|
@ -0,0 +1,37 @@
|
||||||
|
var casper = require("casper").create({
|
||||||
|
loadImages: false,
|
||||||
|
logLevel: "debug",
|
||||||
|
verbose: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var links = {
|
||||||
|
"http://edition.cnn.com/": 0,
|
||||||
|
"http://www.nytimes.com/": 0,
|
||||||
|
"http://www.bbc.co.uk/": 0,
|
||||||
|
"http://www.guardian.co.uk/": 0
|
||||||
|
};
|
||||||
|
|
||||||
|
var fantomas = Object.create(casper);
|
||||||
|
|
||||||
|
fantomas.countLinks = function() {
|
||||||
|
return this.evaluate(function() {
|
||||||
|
return __utils__.findAll("a[href]").length;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
fantomas.renderJSON = function(what) {
|
||||||
|
this.echo(JSON.stringify(what, null, " "));
|
||||||
|
};
|
||||||
|
|
||||||
|
fantomas.start();
|
||||||
|
|
||||||
|
Object.keys(links).forEach(function(url) {
|
||||||
|
fantomas.thenOpen(url, function() {
|
||||||
|
links[url] = this.countLinks();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
fantomas.run(function() {
|
||||||
|
this.renderJSON(links);
|
||||||
|
this.exit();
|
||||||
|
});
|
|
@ -0,0 +1,30 @@
|
||||||
|
links = []
|
||||||
|
casper = require("casper").create()
|
||||||
|
|
||||||
|
getLinks = ->
|
||||||
|
links = document.querySelectorAll("h3.r a")
|
||||||
|
Array::map.call links, (e) ->
|
||||||
|
try
|
||||||
|
(/url\?q=(.*)&sa=U/).exec(e.getAttribute("href"))[1]
|
||||||
|
catch e
|
||||||
|
e.getAttribute "href"
|
||||||
|
|
||||||
|
casper.start "http://google.fr/", ->
|
||||||
|
# search for 'casperjs' from google form
|
||||||
|
@fill "form[action=\"/search\"]", q: "casperjs", true
|
||||||
|
|
||||||
|
casper.then ->
|
||||||
|
# aggregate results for the 'casperjs' search
|
||||||
|
links = @evaluate(getLinks)
|
||||||
|
# now search for 'phantomjs' by fillin the form again
|
||||||
|
@fill "form[action=\"/search\"]", q: "phantomjs", true
|
||||||
|
|
||||||
|
casper.then ->
|
||||||
|
# aggregate results for the 'phantomjs' search
|
||||||
|
links = links.concat(@evaluate(getLinks))
|
||||||
|
|
||||||
|
casper.run ->
|
||||||
|
# echo results in some pretty fashion
|
||||||
|
@echo links.length + " links found:"
|
||||||
|
@echo " - " + links.join("\n - ")
|
||||||
|
@exit()
|
|
@ -0,0 +1,38 @@
|
||||||
|
var links = [];
|
||||||
|
var casper = require("casper").create();
|
||||||
|
|
||||||
|
function getLinks() {
|
||||||
|
var links = document.querySelectorAll("h3.r a");
|
||||||
|
return Array.prototype.map.call(links, function(e) {
|
||||||
|
try {
|
||||||
|
// google handles redirects hrefs to some script of theirs
|
||||||
|
return (/url\?q=(.*)&sa=U/).exec(e.getAttribute("href"))[1];
|
||||||
|
} catch (err) {
|
||||||
|
return e.getAttribute("href");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
casper.start("http://google.fr/", function() {
|
||||||
|
// search for 'casperjs' from google form
|
||||||
|
this.fill('form[action="/search"]', { q: "casperjs" }, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.then(function() {
|
||||||
|
// aggregate results for the 'casperjs' search
|
||||||
|
links = this.evaluate(getLinks);
|
||||||
|
// now search for 'phantomjs' by fillin the form again
|
||||||
|
this.fill('form[action="/search"]', { q: "phantomjs" }, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.then(function() {
|
||||||
|
// aggregate results for the 'phantomjs' search
|
||||||
|
links = links.concat(this.evaluate(getLinks));
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.run(function() {
|
||||||
|
// echo results in some pretty fashion
|
||||||
|
this.echo(links.length + " links found:");
|
||||||
|
this.echo(" - " + links.join("\n - "));
|
||||||
|
this.exit();
|
||||||
|
});
|
|
@ -0,0 +1,47 @@
|
||||||
|
###
|
||||||
|
Takes provided terms passed as arguments and query google for the number of
|
||||||
|
estimated results each have.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
$ casperjs googlematch.coffee nicolas chuck borris
|
||||||
|
nicolas: 69600000
|
||||||
|
chuck: 49500000
|
||||||
|
borris: 2370000
|
||||||
|
winner is "nicolas" with 69600000 results
|
||||||
|
###
|
||||||
|
|
||||||
|
casper = require("casper").create verbose: true
|
||||||
|
|
||||||
|
casper.fetchScore = ->
|
||||||
|
@evaluate ->
|
||||||
|
result = document.querySelector('#resultStats').innerText
|
||||||
|
parseInt /Environ ([0-9\s]{1,}).*/.exec(result)[1].replace(/\s/g, '')
|
||||||
|
|
||||||
|
terms = casper.cli.args # terms are passed through command-line arguments
|
||||||
|
|
||||||
|
if terms.length < 2
|
||||||
|
casper
|
||||||
|
.echo("Usage: $ casperjs googlematch.js term1 term2 [term3]...")
|
||||||
|
.exit(1)
|
||||||
|
|
||||||
|
scores = []
|
||||||
|
|
||||||
|
casper.echo "Let the match begin between \"#{terms.join '", "'}\"!"
|
||||||
|
|
||||||
|
casper.start "http://google.fr/"
|
||||||
|
|
||||||
|
casper.each terms, (self, term) ->
|
||||||
|
@then -> @fill 'form[action="/search"]', { q: term }, true
|
||||||
|
@then ->
|
||||||
|
score = @fetchScore()
|
||||||
|
scores.push term: term, score: score
|
||||||
|
@echo "#{term}: #{score}"
|
||||||
|
|
||||||
|
casper.run ->
|
||||||
|
if scores.length is 0
|
||||||
|
@echo "No result found"
|
||||||
|
else
|
||||||
|
scores.sort (a, b) -> b.score - a.score
|
||||||
|
winner = scores[0]
|
||||||
|
@echo "Winner is \"" + winner.term + "\" with " + winner.score + " results"
|
||||||
|
@exit()
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Takes provided terms passed as arguments and query google for the number of
|
||||||
|
* estimated results each have.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* $ casperjs googlematch.js nicolas chuck borris
|
||||||
|
* nicolas: 69600000
|
||||||
|
* chuck: 49500000
|
||||||
|
* borris: 2370000
|
||||||
|
* winner is "nicolas" with 69600000 results
|
||||||
|
*/
|
||||||
|
|
||||||
|
var casper = require("casper").create({
|
||||||
|
verbose: true
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.fetchScore = function() {
|
||||||
|
return this.evaluate(function() {
|
||||||
|
var result = document.querySelector('#resultStats').innerText;
|
||||||
|
return parseInt(/Environ ([0-9\s]{1,}).*/.exec(result)[1].replace(/\s/g, ''), 10);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var terms = casper.cli.args;
|
||||||
|
|
||||||
|
if (terms.length < 2) {
|
||||||
|
casper
|
||||||
|
.echo("Usage: $ casperjs googlematch.js term1 term2 [term3]...")
|
||||||
|
.exit(1)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
var scores = [];
|
||||||
|
|
||||||
|
casper.echo("Let the match begin between \"" + (terms.join('", "')) + "\"!");
|
||||||
|
|
||||||
|
casper.start("http://google.fr/");
|
||||||
|
|
||||||
|
casper.each(terms, function(casper, term, i) {
|
||||||
|
this.echo('Fecthing score for ' + term);
|
||||||
|
this.then(function() {
|
||||||
|
this.fill('form[action="/search"]', {q: term}, true);
|
||||||
|
});
|
||||||
|
this.then(function() {
|
||||||
|
var score = this.fetchScore();
|
||||||
|
scores.push({
|
||||||
|
term: term,
|
||||||
|
score: score
|
||||||
|
});
|
||||||
|
this.echo(term + ': ' + score);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.run(function() {
|
||||||
|
if (scores.length === 0) {
|
||||||
|
this.echo("No result found");
|
||||||
|
} else {
|
||||||
|
scores.sort(function(a, b) {
|
||||||
|
return b.score - a.score;
|
||||||
|
});
|
||||||
|
var winner = scores[0];
|
||||||
|
this.echo("Winner is \"" + winner.term + "\" with " + winner.score + " results");
|
||||||
|
}
|
||||||
|
this.exit();
|
||||||
|
});
|
|
@ -0,0 +1,40 @@
|
||||||
|
###
|
||||||
|
Capture multiple pages of google search results
|
||||||
|
|
||||||
|
Usage: $ casperjs googlepagination.coffee my search terms
|
||||||
|
|
||||||
|
(all arguments will be used as the query)
|
||||||
|
###
|
||||||
|
|
||||||
|
casper = require("casper").create()
|
||||||
|
currentPage = 1
|
||||||
|
|
||||||
|
if casper.cli.args.length is 0
|
||||||
|
casper
|
||||||
|
.echo("Usage: $ casperjs googlepagination.coffee my search terms")
|
||||||
|
.exit(1)
|
||||||
|
|
||||||
|
processPage = ->
|
||||||
|
@echo "capturing page #{currentPage}"
|
||||||
|
@capture "google-results-p#{currentPage}.png"
|
||||||
|
|
||||||
|
# don't go too far down the rabbit hole
|
||||||
|
return if currentPage >= 5
|
||||||
|
|
||||||
|
if @exists "#pnnext"
|
||||||
|
currentPage++
|
||||||
|
@echo "requesting next page: #{currentPage}"
|
||||||
|
url = @getCurrentUrl()
|
||||||
|
@thenClick("#pnnext").then ->
|
||||||
|
@waitFor (->
|
||||||
|
url isnt @getCurrentUrl()
|
||||||
|
), processPage
|
||||||
|
else
|
||||||
|
@echo "that's all, folks."
|
||||||
|
|
||||||
|
casper.start "http://google.fr/", ->
|
||||||
|
@fill 'form[action="/search"]', q: casper.cli.args.join(" "), true
|
||||||
|
|
||||||
|
casper.then processPage
|
||||||
|
|
||||||
|
casper.run()
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Capture multiple pages of google search results
|
||||||
|
*
|
||||||
|
* Usage: $ casperjs googlepagination.coffee my search terms
|
||||||
|
*
|
||||||
|
* (all arguments will be used as the query)
|
||||||
|
*/
|
||||||
|
|
||||||
|
var casper = require("casper").create();
|
||||||
|
var currentPage = 1;
|
||||||
|
|
||||||
|
if (casper.cli.args.length === 0) {
|
||||||
|
casper
|
||||||
|
.echo("Usage: $ casperjs googlepagination.js my search terms")
|
||||||
|
.exit(1)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
var processPage = function() {
|
||||||
|
var url;
|
||||||
|
this.echo("capturing page " + currentPage);
|
||||||
|
this.capture("google-results-p" + currentPage + ".png");
|
||||||
|
|
||||||
|
// don't go too far down the rabbit hole
|
||||||
|
if (currentPage >= 5) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.exists("#pnnext")) {
|
||||||
|
currentPage++;
|
||||||
|
this.echo("requesting next page: " + currentPage);
|
||||||
|
url = this.getCurrentUrl();
|
||||||
|
this.thenClick("#pnnext").then(function() {
|
||||||
|
this.waitFor(function() {
|
||||||
|
return url !== this.getCurrentUrl();
|
||||||
|
}, processPage);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.echo("that's all, folks.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
casper.start("http://google.fr/", function() {
|
||||||
|
this.fill('form[action="/search"]', {
|
||||||
|
q: casper.cli.args.join(" ")
|
||||||
|
}, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.then(processPage);
|
||||||
|
|
||||||
|
casper.run();
|
|
@ -0,0 +1,17 @@
|
||||||
|
casper = require("casper").create
|
||||||
|
logLevel: "debug"
|
||||||
|
|
||||||
|
casper.start "http://www.google.fr/", ->
|
||||||
|
@test.assertTitle "Google", "google homepage title is the one expected"
|
||||||
|
@test.assertExists 'form[action="/search"]', "main form is found"
|
||||||
|
@fill 'form[action="/search"]', q: "foo", true
|
||||||
|
|
||||||
|
casper.then ->
|
||||||
|
@test.assertTitle "foo - Recherche Google", "google title is ok"
|
||||||
|
@test.assertUrlMatch /q=foo/, "search term has been submitted"
|
||||||
|
@test.assertEval (->
|
||||||
|
__utils__.findAll("h3.r").length >= 10
|
||||||
|
), "google search for \"foo\" retrieves 10 or more results"
|
||||||
|
|
||||||
|
casper.run ->
|
||||||
|
@test.renderResults true
|
|
@ -0,0 +1,23 @@
|
||||||
|
var casper = require("casper").create({
|
||||||
|
logLevel: "debug"
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.start("http://www.google.fr/", function() {
|
||||||
|
this.test.assertTitle("Google", "google homepage title is the one expected");
|
||||||
|
this.test.assertExists('form[action="/search"]', "main form is found");
|
||||||
|
this.fill('form[action="/search"]', {
|
||||||
|
q: "foo"
|
||||||
|
}, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.then(function() {
|
||||||
|
this.test.assertTitle("foo - Recherche Google", "google title is ok");
|
||||||
|
this.test.assertUrlMatch(/q=foo/, "search term has been submitted");
|
||||||
|
this.test.assertEval((function() {
|
||||||
|
return __utils__.findAll("h3.r").length >= 10;
|
||||||
|
}), "google search for \"foo\" retrieves 10 or more results");
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.run(function() {
|
||||||
|
this.test.renderResults(true);
|
||||||
|
});
|
|
@ -0,0 +1,10 @@
|
||||||
|
casper = require("casper").create
|
||||||
|
verbose: true
|
||||||
|
logLevel: "debug"
|
||||||
|
|
||||||
|
casper.log "this is a debug message", "debug"
|
||||||
|
casper.log "and an informative one", "info"
|
||||||
|
casper.log "and a warning", "warning"
|
||||||
|
casper.log "and an error", "error"
|
||||||
|
|
||||||
|
casper.exit()
|
|
@ -0,0 +1,11 @@
|
||||||
|
var casper = require("casper").create({
|
||||||
|
verbose: true,
|
||||||
|
logLevel: "debug"
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.log("this is a debug message", "debug");
|
||||||
|
casper.log("and an informative one", "info");
|
||||||
|
casper.log("and a warning", "warning");
|
||||||
|
casper.log("and an error", "error");
|
||||||
|
|
||||||
|
casper.exit();
|
|
@ -0,0 +1,23 @@
|
||||||
|
casper = require("casper").create()
|
||||||
|
url = casper.cli.get 0
|
||||||
|
metas = []
|
||||||
|
|
||||||
|
if not url
|
||||||
|
casper
|
||||||
|
.echo("Usage: $ casperjs metaextract.coffee <url>")
|
||||||
|
.exit 1
|
||||||
|
|
||||||
|
casper.start url, ->
|
||||||
|
metas = @evaluate ->
|
||||||
|
metas = []
|
||||||
|
castarray = (arr) -> [].slice.call(arr)
|
||||||
|
for elem in castarray document.querySelectorAll "meta"
|
||||||
|
meta = {}
|
||||||
|
for attr in castarray elem.attributes
|
||||||
|
meta[attr.name] = attr.value
|
||||||
|
metas.push meta
|
||||||
|
metas
|
||||||
|
|
||||||
|
casper.run ->
|
||||||
|
require("utils").dump metas
|
||||||
|
this.exit()
|
|
@ -0,0 +1,29 @@
|
||||||
|
var casper = require("casper").create();
|
||||||
|
var url = casper.cli.get(0);
|
||||||
|
var metas = [];
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
casper
|
||||||
|
.echo("Usage: $ casperjs metaextract.js <url>")
|
||||||
|
.exit(1)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
casper.start(url, function() {
|
||||||
|
metas = this.evaluate(function() {
|
||||||
|
var metas = [];
|
||||||
|
[].forEach.call(document.querySelectorAll("meta"), function(elem) {
|
||||||
|
var meta = {};
|
||||||
|
[].slice.call(elem.attributes).forEach(function(attr) {
|
||||||
|
meta[attr.name] = attr.value;
|
||||||
|
});
|
||||||
|
metas.push(meta);
|
||||||
|
});
|
||||||
|
return metas;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.run(function() {
|
||||||
|
require("utils").dump(metas);
|
||||||
|
this.exit();
|
||||||
|
});
|
|
@ -0,0 +1,37 @@
|
||||||
|
casper = require("casper").create verbose: true
|
||||||
|
|
||||||
|
countLinks = ->
|
||||||
|
document.querySelectorAll('a').length
|
||||||
|
|
||||||
|
suites = [
|
||||||
|
->
|
||||||
|
@echo "Suite 1"
|
||||||
|
@start "http://google.com/", -> @echo "Page title: #{@getTitle()}"
|
||||||
|
@then -> @echo "#{@evaluate(countLinks)} links"
|
||||||
|
->
|
||||||
|
@echo "Suite 2"
|
||||||
|
@start "http://yahoo.com/", -> @echo "Page title: #{@getTitle()}"
|
||||||
|
@then -> @echo "#{@evaluate(countLinks)} links"
|
||||||
|
->
|
||||||
|
@echo "Suite 3"
|
||||||
|
@start "http://bing.com/", -> @echo "Page title: #{@getTitle()}"
|
||||||
|
@then -> @echo "#{@evaluate(countLinks)} links"
|
||||||
|
]
|
||||||
|
|
||||||
|
casper.start()
|
||||||
|
|
||||||
|
casper.then ->
|
||||||
|
@echo("Starting")
|
||||||
|
|
||||||
|
currentSuite = 0;
|
||||||
|
|
||||||
|
check = ->
|
||||||
|
if suites[currentSuite]
|
||||||
|
suites[currentSuite].call @
|
||||||
|
currentSuite++;
|
||||||
|
casper.run check
|
||||||
|
else
|
||||||
|
@echo "All done."
|
||||||
|
@exit()
|
||||||
|
|
||||||
|
casper.run check
|
|
@ -0,0 +1,56 @@
|
||||||
|
var casper = require("casper").create({
|
||||||
|
verbose: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var countLinks = function() {
|
||||||
|
return document.querySelectorAll('a').length;
|
||||||
|
};
|
||||||
|
|
||||||
|
var suites = [
|
||||||
|
function() {
|
||||||
|
this.echo("Suite 1");
|
||||||
|
this.start("http://google.com/", function() {
|
||||||
|
this.echo("Page title: " + (this.getTitle()));
|
||||||
|
});
|
||||||
|
this.then(function() {
|
||||||
|
this.echo((this.evaluate(countLinks)) + " links");
|
||||||
|
});
|
||||||
|
}, function() {
|
||||||
|
this.echo("Suite 2");
|
||||||
|
this.start("http://yahoo.com/", function() {
|
||||||
|
this.echo("Page title: " + (this.getTitle()));
|
||||||
|
});
|
||||||
|
this.then(function() {
|
||||||
|
this.echo((this.evaluate(countLinks)) + " links");
|
||||||
|
});
|
||||||
|
}, function() {
|
||||||
|
this.echo("Suite 3");
|
||||||
|
this.start("http://bing.com/", function() {
|
||||||
|
this.echo("Page title: " + (this.getTitle()));
|
||||||
|
});
|
||||||
|
this.then(function() {
|
||||||
|
this.echo((this.evaluate(countLinks)) + " links");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
casper.start();
|
||||||
|
|
||||||
|
casper.then(function() {
|
||||||
|
this.echo("Starting");
|
||||||
|
});
|
||||||
|
|
||||||
|
var currentSuite = 0;
|
||||||
|
|
||||||
|
var check = function() {
|
||||||
|
if (suites[currentSuite]) {
|
||||||
|
suites[currentSuite].call(this);
|
||||||
|
currentSuite++;
|
||||||
|
casper.run(check);
|
||||||
|
} else {
|
||||||
|
this.echo("All done.");
|
||||||
|
this.exit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
casper.run(check);
|
|
@ -0,0 +1,28 @@
|
||||||
|
###
|
||||||
|
This script will capture a screenshot of a twitter account page
|
||||||
|
Usage: $ casperjs screenshot.coffee <twitter-account> <filename.[jpg|png|pdf]>
|
||||||
|
###
|
||||||
|
|
||||||
|
casper = require("casper").create
|
||||||
|
viewportSize:
|
||||||
|
width: 1024
|
||||||
|
height: 768
|
||||||
|
|
||||||
|
twitterAccount = casper.cli.get 0
|
||||||
|
filename = casper.cli.get 1
|
||||||
|
|
||||||
|
if not twitterAccount or not filename or not /\.(png|jpg|pdf)$/i.test filename
|
||||||
|
casper
|
||||||
|
.echo("Usage: $ casperjs screenshot.coffee <twitter-account> <filename.[jpg|png|pdf]>")
|
||||||
|
.exit(1)
|
||||||
|
|
||||||
|
casper.start "https://twitter.com/#!/#{twitterAccount}", ->
|
||||||
|
@waitForSelector ".tweet-row", (->
|
||||||
|
@captureSelector filename, "html"
|
||||||
|
@echo "Saved screenshot of #{@getCurrentUrl()} to #{filename}"
|
||||||
|
), (->
|
||||||
|
@die("Timeout reached. Fail whale?")
|
||||||
|
@exit()
|
||||||
|
), 12000
|
||||||
|
|
||||||
|
casper.run()
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* This script will capture a screenshot of a twitter account page
|
||||||
|
* Usage: $ casperjs screenshot.coffee <twitter-account> <filename.[jpg|png|pdf]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
var casper = require("casper").create({
|
||||||
|
viewportSize: {
|
||||||
|
width: 1024,
|
||||||
|
height: 768
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var twitterAccount = casper.cli.get(0);
|
||||||
|
var filename = casper.cli.get(1);
|
||||||
|
|
||||||
|
if (!twitterAccount || !filename || !/\.(png|jpg|pdf)$/i.test(filename)) {
|
||||||
|
casper
|
||||||
|
.echo("Usage: $ casperjs screenshot.coffee <twitter-account> <filename.[jpg|png|pdf]>")
|
||||||
|
.exit(1)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
casper.start("https://twitter.com/#!/" + twitterAccount, function() {
|
||||||
|
this.waitForSelector(".tweet-row", (function() {
|
||||||
|
this.captureSelector(filename, "html");
|
||||||
|
this.echo("Saved screenshot of " + (this.getCurrentUrl()) + " to " + filename);
|
||||||
|
}), (function() {
|
||||||
|
this.die("Timeout reached. Fail whale?");
|
||||||
|
this.exit();
|
||||||
|
}), 12000);
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.run();
|
|
@ -0,0 +1,15 @@
|
||||||
|
###
|
||||||
|
This script will add a custom HTTP status code handler, here for 404 pages.
|
||||||
|
###
|
||||||
|
|
||||||
|
casper = require("casper").create
|
||||||
|
httpStatusHandlers:
|
||||||
|
404: (self, resource) ->
|
||||||
|
@echo "Resource at #{resource.url} not found (404)", "COMMENT"
|
||||||
|
verbose: true
|
||||||
|
|
||||||
|
casper.start "http://www.google.com/plop", ->
|
||||||
|
@echo "Done."
|
||||||
|
@exit()
|
||||||
|
|
||||||
|
casper.run()
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* This script will add a custom HTTP status code handler, here for 404 pages.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var casper = require("casper").create({
|
||||||
|
httpStatusHandlers: {
|
||||||
|
404: function(self, resource) {
|
||||||
|
this.echo("Resource at " + resource.url + " not found (404)", "COMMENT");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
verbose: true
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.start("http://www.google.com/plop", function() {
|
||||||
|
this.echo("Done.");
|
||||||
|
this.exit();
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.run();
|
|
@ -0,0 +1,37 @@
|
||||||
|
failed = []
|
||||||
|
start = null
|
||||||
|
links = [
|
||||||
|
"http://google.com/"
|
||||||
|
"http://akei.com/"
|
||||||
|
"http://lemonde.fr/"
|
||||||
|
"http://liberation.fr/"
|
||||||
|
"http://cdiscount.fr/"
|
||||||
|
]
|
||||||
|
|
||||||
|
casper = require("casper").create
|
||||||
|
onStepTimeout: ->
|
||||||
|
failed.push @requestUrl
|
||||||
|
@test.fail "#{@requestUrl} loads in less than #{timeout}ms."
|
||||||
|
|
||||||
|
casper.on "load.finished", ->
|
||||||
|
@echo "#{@requestUrl} loaded in #{new Date() - start}ms", "PARAMETER"
|
||||||
|
|
||||||
|
timeout = ~~casper.cli.get(0)
|
||||||
|
timeout = 1000 if timeout < 1
|
||||||
|
casper.options.stepTimeout = timeout
|
||||||
|
|
||||||
|
casper.echo "Testing with timeout=#{timeout}ms, please be patient."
|
||||||
|
|
||||||
|
casper.start()
|
||||||
|
|
||||||
|
casper.each links, (self, link) ->
|
||||||
|
@then ->
|
||||||
|
@test.comment "Loading #{link}"
|
||||||
|
start = new Date()
|
||||||
|
@open link
|
||||||
|
@then ->
|
||||||
|
if @requestUrl not in failed
|
||||||
|
@test.pass "#{@requestUrl} loaded in less than #{timeout}ms."
|
||||||
|
|
||||||
|
casper.run ->
|
||||||
|
@test.renderResults true
|
|
@ -0,0 +1,45 @@
|
||||||
|
var failed = [];
|
||||||
|
var start = null;
|
||||||
|
var links = [
|
||||||
|
"http://google.com/'",
|
||||||
|
"http://akei.com/'",
|
||||||
|
"http://lemonde.fr/'",
|
||||||
|
"http://liberation.fr/'",
|
||||||
|
"http://cdiscount.fr/"
|
||||||
|
];
|
||||||
|
|
||||||
|
var casper = require("casper").create({
|
||||||
|
onStepTimeout: function() {
|
||||||
|
failed.push(this.requestUrl);
|
||||||
|
this.test.fail(this.requestUrl + " loads in less than " + timeout + "ms.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.on("load.finished", function() {
|
||||||
|
this.echo(this.requestUrl + " loaded in " + (new Date() - start) + "ms", "PARAMETER");
|
||||||
|
});
|
||||||
|
|
||||||
|
var timeout = ~~casper.cli.get(0);
|
||||||
|
casper.options.stepTimeout = timeout > 0 ? timeout : 1000;
|
||||||
|
|
||||||
|
casper.echo("Testing with timeout=" + casper.options.stepTimeout + "ms, please be patient.");
|
||||||
|
|
||||||
|
casper.start();
|
||||||
|
|
||||||
|
casper.each(links, function(casper, link) {
|
||||||
|
this.then(function() {
|
||||||
|
this.test.comment("Loading " + link);
|
||||||
|
start = new Date();
|
||||||
|
this.open(link);
|
||||||
|
});
|
||||||
|
this.then(function() {
|
||||||
|
var message = this.requestUrl + " loads in less than " + timeout + "ms.";
|
||||||
|
if (failed.indexOf(this.requestUrl) === -1) {
|
||||||
|
this.test.pass(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.run(function() {
|
||||||
|
this.test.renderResults(true);
|
||||||
|
});
|
|
@ -0,0 +1,39 @@
|
||||||
|
###
|
||||||
|
Just a silly game.
|
||||||
|
|
||||||
|
$ casperjs samples/timeout.js 500
|
||||||
|
Will google.com load in less than 500ms?
|
||||||
|
NOPE.
|
||||||
|
|
||||||
|
$ casperjs samples/timeout.js 1000
|
||||||
|
Will google.com load in less than 1000ms?
|
||||||
|
NOPE.
|
||||||
|
|
||||||
|
$ casperjs samples/timeout.js 1500
|
||||||
|
Will google.com load in less than 1500ms?
|
||||||
|
NOPE.
|
||||||
|
|
||||||
|
$ casperjs samples/timeout.js 2000
|
||||||
|
Will google.com load in less than 2000ms?
|
||||||
|
YES!
|
||||||
|
###
|
||||||
|
|
||||||
|
casper = require("casper").create
|
||||||
|
onTimeout: ->
|
||||||
|
@echo "NOPE.", "RED_BAR"
|
||||||
|
@exit()
|
||||||
|
|
||||||
|
timeout = ~~casper.cli.get 0
|
||||||
|
if timeout < 1
|
||||||
|
casper
|
||||||
|
.echo("You must pass a valid timeout value")
|
||||||
|
.exit(1)
|
||||||
|
|
||||||
|
casper.echo "Will google.com load in less than #{timeout}ms?"
|
||||||
|
casper.options.timeout = timeout
|
||||||
|
|
||||||
|
casper.start "http://www.google.com/", ->
|
||||||
|
@echo "YES!", "GREEN_BAR"
|
||||||
|
@exit()
|
||||||
|
|
||||||
|
casper.run()
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Just a silly game.
|
||||||
|
*
|
||||||
|
* $ casperjs samples/timeout.js 500
|
||||||
|
* Will google.com load in less than 500ms?
|
||||||
|
* NOPE.
|
||||||
|
*
|
||||||
|
* $ casperjs samples/timeout.js 1000
|
||||||
|
* Will google.com load in less than 1000ms?
|
||||||
|
* NOPE.
|
||||||
|
*
|
||||||
|
* $ casperjs samples/timeout.js 1500
|
||||||
|
* Will google.com load in less than 1500ms?
|
||||||
|
* NOPE.
|
||||||
|
*
|
||||||
|
* $ casperjs samples/timeout.js 2000
|
||||||
|
* Will google.com load in less than 2000ms?
|
||||||
|
* YES!
|
||||||
|
*/
|
||||||
|
|
||||||
|
var casper = require("casper").create({
|
||||||
|
onTimeout: function() {
|
||||||
|
this
|
||||||
|
.echo("NOPE.", "RED_BAR")
|
||||||
|
.exit()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var timeout = ~~casper.cli.get(0);
|
||||||
|
|
||||||
|
if (timeout < 1) {
|
||||||
|
casper
|
||||||
|
.echo("You must pass a valid timeout value")
|
||||||
|
.exit(1)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
casper.echo("Will google.com load in less than " + timeout + "ms?");
|
||||||
|
casper.options.timeout = timeout;
|
||||||
|
|
||||||
|
casper.start("http://www.google.com/", function() {
|
||||||
|
this.echo("YES!", "GREEN_BAR");
|
||||||
|
this.exit();
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.run();
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*global phantom*/
|
||||||
|
|
||||||
|
if (!phantom.casperLoaded) {
|
||||||
|
console.log('This script must be invoked using the casperjs executable');
|
||||||
|
phantom.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var colorizer = require('colorizer');
|
||||||
|
var utils = require('utils');
|
||||||
|
var f = utils.format;
|
||||||
|
var loadIncludes = ['includes', 'pre', 'post'];
|
||||||
|
var tests = [];
|
||||||
|
var casper = require('casper').create({
|
||||||
|
exitOnError: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// local utils
|
||||||
|
function checkSelfTest(tests) {
|
||||||
|
"use strict";
|
||||||
|
var isCasperTest = false;
|
||||||
|
tests.forEach(function(test) {
|
||||||
|
var testDir = fs.absolute(fs.dirname(test));
|
||||||
|
if (fs.isDirectory(testDir)) {
|
||||||
|
if (fs.exists(fs.pathJoin(testDir, '.casper'))) {
|
||||||
|
isCasperTest = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return isCasperTest;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkIncludeFile(include) {
|
||||||
|
"use strict";
|
||||||
|
var absInclude = fs.absolute(include.trim());
|
||||||
|
if (!fs.exists(absInclude)) {
|
||||||
|
casper.warn("%s file not found, can't be included", absInclude);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!utils.isJsFile(absInclude)) {
|
||||||
|
casper.warn("%s is not a supported file type, can't be included", absInclude);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fs.isDirectory(absInclude)) {
|
||||||
|
casper.warn("%s is a directory, can't be included", absInclude);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tests.indexOf(include) > -1 || tests.indexOf(absInclude) > -1) {
|
||||||
|
casper.warn("%s is a test file, can't be included", absInclude);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return absInclude;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse some options from cli
|
||||||
|
casper.options.verbose = casper.cli.get('direct') || false;
|
||||||
|
casper.options.logLevel = casper.cli.get('log-level') || "error";
|
||||||
|
if (casper.cli.get('no-colors') === true) {
|
||||||
|
var cls = 'Dummy';
|
||||||
|
casper.options.colorizerType = cls;
|
||||||
|
casper.colorizer = colorizer.create(cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
// test paths are passed as args
|
||||||
|
if (casper.cli.args.length) {
|
||||||
|
tests = casper.cli.args.filter(function(path) {
|
||||||
|
"use strict";
|
||||||
|
return fs.isFile(path) || fs.isDirectory(path);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
casper.echo('No test path passed, exiting.', 'RED_BAR', 80);
|
||||||
|
casper.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for casper selftests
|
||||||
|
if (!phantom.casperSelfTest && checkSelfTest(tests)) {
|
||||||
|
casper.warn('To run casper self tests, use the `selftest` command.');
|
||||||
|
casper.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// includes handling
|
||||||
|
this.loadIncludes.forEach(function(include){
|
||||||
|
"use strict";
|
||||||
|
var container;
|
||||||
|
if (casper.cli.has(include)) {
|
||||||
|
container = casper.cli.get(include).split(',').map(function(file) {
|
||||||
|
return checkIncludeFile(file);
|
||||||
|
}).filter(function(file) {
|
||||||
|
return utils.isString(file);
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.test.loadIncludes[include] = utils.unique(container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// test suites completion listener
|
||||||
|
casper.test.on('tests.complete', function() {
|
||||||
|
"use strict";
|
||||||
|
this.renderResults(true, undefined, casper.cli.get('xunit') || undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
// run all the suites
|
||||||
|
casper.test.runSuites.apply(casper.test, tests);
|
|
@ -0,0 +1,5 @@
|
||||||
|
try
|
||||||
|
exports.ok = true
|
||||||
|
catch e
|
||||||
|
casper.test.fail('error in coffeescript module code: ' + e)
|
||||||
|
casper.test.done()
|
|
@ -0,0 +1,7 @@
|
||||||
|
/*global casper*/
|
||||||
|
try {
|
||||||
|
exports.ok = true;
|
||||||
|
} catch (e) {
|
||||||
|
casper.test.fail('error in js module code' + e);
|
||||||
|
casper.test.done()
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/**
|
||||||
|
* CasperJS local HTTP test server
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*global phantom casper require*/
|
||||||
|
|
||||||
|
var colorizer = require('colorizer').create('Colorizer');
|
||||||
|
var fs = require('fs');
|
||||||
|
var utils = require('utils');
|
||||||
|
var server = require('webserver').create();
|
||||||
|
var service;
|
||||||
|
var testServerPort = 54321;
|
||||||
|
|
||||||
|
function info(message) {
|
||||||
|
"use strict";
|
||||||
|
console.log(colorizer.colorize('INFO', 'INFO_BAR') + ' ' + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
service = server.listen(testServerPort, function(request, response) {
|
||||||
|
"use strict";
|
||||||
|
var requestPath = request.url;
|
||||||
|
if (requestPath.indexOf('?') !== -1) {
|
||||||
|
requestPath = request.url.split('?')[0];
|
||||||
|
}
|
||||||
|
var pageFile = fs.pathJoin(phantom.casperPath, requestPath);
|
||||||
|
if (!fs.exists(pageFile) || !fs.isFile(pageFile)) {
|
||||||
|
response.statusCode = 404;
|
||||||
|
console.log(utils.format('Test server url not found: %s (file: %s)', request.url, pageFile), "warning");
|
||||||
|
response.write("404 - NOT FOUND");
|
||||||
|
} else {
|
||||||
|
response.statusCode = 200;
|
||||||
|
response.write(fs.read(pageFile));
|
||||||
|
}
|
||||||
|
response.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
// overriding Casper.open to prefix all test urls
|
||||||
|
casper.setFilter('open.location', function(location) {
|
||||||
|
"use strict";
|
||||||
|
if (/^file/.test(location)) {
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
if (!/^http/.test(location)) {
|
||||||
|
return utils.format('http://localhost:%d/%s', testServerPort, location);
|
||||||
|
}
|
||||||
|
return location;
|
||||||
|
});
|
||||||
|
|
||||||
|
// test suites completion listener
|
||||||
|
casper.test.on('tests.complete', function() {
|
||||||
|
"use strict";
|
||||||
|
server.close();
|
||||||
|
});
|
|
@ -0,0 +1,10 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>CasperJS test alert</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>alert('plop')</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,40 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>CasperJS test click</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a id="test1" href="javascript:results.test1 = true;">test1</a>
|
||||||
|
<a id="test2" href="#" onclick="results.test2 = true;">test2</a>
|
||||||
|
<a id="test3" href="page1.html" onclick="results.test3 = true; return false">test3</a>
|
||||||
|
<a id="test4" href="http://www.google.com/">test4</a>
|
||||||
|
<script>
|
||||||
|
(function(window) {
|
||||||
|
window.results = {
|
||||||
|
test1: false,
|
||||||
|
test2: false,
|
||||||
|
test3: false,
|
||||||
|
test4: false,
|
||||||
|
testdown: [],
|
||||||
|
testup: [],
|
||||||
|
testmove: [],
|
||||||
|
testclick: []
|
||||||
|
};
|
||||||
|
document.querySelector('#test4').onclick = function(event) {
|
||||||
|
results.test4 = true;
|
||||||
|
event.preventDefault();
|
||||||
|
};
|
||||||
|
window.onmousedown = function(event) {
|
||||||
|
results.testdown = [event.x, event.y];
|
||||||
|
};
|
||||||
|
window.onmouseup = function(event) {
|
||||||
|
results.testup = [event.x, event.y];
|
||||||
|
};
|
||||||
|
window.onmousemove = function(event) {
|
||||||
|
results.testmove = [event.x, event.y];
|
||||||
|
};
|
||||||
|
})(window);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>CasperJS test confirm</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
var confirmed = confirm('are you sure?');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1 @@
|
||||||
|
document.write('foo');
|
|
@ -0,0 +1,6 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<div class="tester testo testme" data-stuff="beautiful string"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,10 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>CasperJS error test</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>plop(); // intentional error here</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<title>CasperJS test form</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form action="result.html" enctype="multipart/form-data">
|
||||||
|
<input type="text" name="email" placeholder="email">
|
||||||
|
<input type="password" name="password" placeholder="password">
|
||||||
|
<textarea name="content"></textarea>
|
||||||
|
<select name="topic">
|
||||||
|
<option>foo</option>
|
||||||
|
<option value="bar">baz</option>
|
||||||
|
</select>
|
||||||
|
<input type="checkbox" name="check">
|
||||||
|
<input type="radio" name="choice" value="yes">
|
||||||
|
<input type="radio" name="choice" value="no">
|
||||||
|
<input type="file" name="file">
|
||||||
|
<input type="checkbox" name="checklist[]" value="1">
|
||||||
|
<input type="checkbox" name="checklist[]" value="2">
|
||||||
|
<input type="checkbox" name="checklist[]" value="3">
|
||||||
|
<input type="submit" name="submit" value="submit">
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var myGlobal = 'awesome string';
|
||||||
|
var myUnencodableGlobal = document;
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>CasperJS test index</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<img src="images/phantom.png"/>
|
||||||
|
<a href="test.html">test</a>
|
||||||
|
<a href="form.html">form</a>
|
||||||
|
<ul>
|
||||||
|
<li>one</li>
|
||||||
|
<li>two</li>
|
||||||
|
<li>three</li>
|
||||||
|
</ul>
|
||||||
|
<input type="text" name="dummy_name" value="dummy_value" />
|
||||||
|
<h1>Title</h1>
|
||||||
|
<p id="hidden" style="display:none">I'm hidden.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,47 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>CasperJS test mouse events</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a id="test1" href="#" onmousedown="results.test1 = true;">test</a>
|
||||||
|
<a id="test2" href="#">test</a>
|
||||||
|
<a id="test3" href="#" onmouseup="results.test3 = true;">test</a>
|
||||||
|
<a id="test4" href="#">test</a>
|
||||||
|
<a id="test5" href="#" onmouseover="results.test5 = true;">test</a>
|
||||||
|
<a id="test6" href="#">test</a>
|
||||||
|
<a id="test7" href="#" onmouseout="results.test7 = true;">test</a>
|
||||||
|
<a id="test8" href="#">test</a>
|
||||||
|
<script>
|
||||||
|
(function(window) {
|
||||||
|
window.results = {
|
||||||
|
test1: false,
|
||||||
|
test2: false,
|
||||||
|
test3: false,
|
||||||
|
test4: false,
|
||||||
|
test5: false,
|
||||||
|
test6: false,
|
||||||
|
test7: false,
|
||||||
|
test8: false
|
||||||
|
};
|
||||||
|
document.querySelector('#test2').onmousedown = function(event) {
|
||||||
|
results.test2 = true;
|
||||||
|
event.preventDefault();
|
||||||
|
};
|
||||||
|
document.querySelector('#test4').onmouseup = function(event) {
|
||||||
|
results.test4 = true;
|
||||||
|
event.preventDefault();
|
||||||
|
};
|
||||||
|
document.querySelector('#test6').onmouseover = function(event) {
|
||||||
|
results.test6 = true;
|
||||||
|
event.preventDefault();
|
||||||
|
};
|
||||||
|
document.querySelector('#test8').onmouseout = function(event) {
|
||||||
|
results.test8 = true;
|
||||||
|
event.preventDefault();
|
||||||
|
};
|
||||||
|
})(window);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Multiple forms test</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form name="f1">
|
||||||
|
<input type="hidden" name="f" value="f1">
|
||||||
|
<input type="text" name="yo">
|
||||||
|
</form>
|
||||||
|
<form name="f2">
|
||||||
|
<input type="hidden" name="f" value="f2">
|
||||||
|
<input type="text" name="yo">
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>CasperJS test page 1</title>
|
||||||
|
</head>
|
||||||
|
<body>Booh.</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>CasperJS test page 2</title>
|
||||||
|
</head>
|
||||||
|
<body>Booh.</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>CasperJS test page 1</title>
|
||||||
|
</head>
|
||||||
|
<body>Booh.</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>CasperJS test prompt</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
var name = prompt('what is your name?', 'Norris');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>CasperJS test resource</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script id="loader"></script>
|
||||||
|
<script>
|
||||||
|
setTimeout(function () {
|
||||||
|
document.querySelector("#loader").setAttribute("src", "dummy.js");
|
||||||
|
}, 1000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>CasperJS test form result</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>this is the result page</p>
|
||||||
|
<p><a href="index.html">Return back home</a></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,10 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>CasperJS test target</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a href="form.html">test form</a>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>CasperJS url tests</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<ul>
|
||||||
|
<li><a href="?test=Forlì">raw unicode</a></li>
|
||||||
|
<li><a href="?test=Forl%EC">escaped</a></li>
|
||||||
|
<li><a href="?test=Forl%C3%AC">uri encoded</a></li>
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>CasperJS test index</title>
|
||||||
|
<script>
|
||||||
|
setTimeout(function() {
|
||||||
|
document.querySelector('#img1').style.display = 'none';
|
||||||
|
}, 1000);
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<img src="images/phantom.png" id="img1">
|
||||||
|
<img src="images/phantom.png" id="img2" style="display:none">
|
||||||
|
<img src="images/phantom.png" id="img3" style="visibility:hidden">
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>waitFor test</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<img src="images/phantom.png"/>
|
||||||
|
<ul>
|
||||||
|
<li>one</li>
|
||||||
|
<li>two</li>
|
||||||
|
<li>three</li>
|
||||||
|
</ul>
|
||||||
|
<script>
|
||||||
|
setTimeout(function() {
|
||||||
|
var li = document.createElement('li')
|
||||||
|
li.appendChild(document.createTextNode('four'));
|
||||||
|
document.querySelector('ul').appendChild(li);
|
||||||
|
}, 2000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*global casper*/
|
||||||
|
/*jshint strict:false*/
|
||||||
|
function testUA(ua, match) {
|
||||||
|
casper.test.assertMatch(
|
||||||
|
ua, match, 'Default user agent matches ' + match
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchUA(request) {
|
||||||
|
testUA(request.headers.filter(function(header) {
|
||||||
|
return header.name === "User-Agent";
|
||||||
|
}).pop().value, /plop/);
|
||||||
|
}
|
||||||
|
|
||||||
|
testUA(casper.options.pageSettings.userAgent, /CasperJS/);
|
||||||
|
|
||||||
|
casper.start();
|
||||||
|
|
||||||
|
casper.userAgent('plop').on('resource.requested', fetchUA);
|
||||||
|
|
||||||
|
casper.thenOpen('tests/site/index.html');
|
||||||
|
|
||||||
|
casper.run(function() {
|
||||||
|
this.removeListener('resource.requested', fetchUA);
|
||||||
|
this.test.done();
|
||||||
|
});
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*global casper*/
|
||||||
|
/*jshint strict:false*/
|
||||||
|
var fs = require('fs'), testFile = '/tmp/__casper_test_capture.png';
|
||||||
|
|
||||||
|
if (fs.exists(testFile) && fs.isFile(testFile)) {
|
||||||
|
fs.remove(testFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
casper.start('tests/site/index.html', function() {
|
||||||
|
this.viewport(300, 200);
|
||||||
|
this.test.comment('Casper.capture()');
|
||||||
|
this.capture(testFile);
|
||||||
|
this.test.assert(fs.isFile(testFile), 'Casper.capture() captured a screenshot');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (phantom.version.major === 1 && phantom.version.minor >= 6) {
|
||||||
|
casper.thenOpen('tests/site/index.html', function() {
|
||||||
|
this.test.comment('Casper.captureBase64()');
|
||||||
|
this.test.assert(this.captureBase64('png').length > 0,
|
||||||
|
'Casper.captureBase64() rendered a page capture as base64');
|
||||||
|
this.test.assert(this.captureBase64('png', 'ul').length > 0,
|
||||||
|
'Casper.captureBase64() rendered a capture from a selector as base64');
|
||||||
|
this.test.assert(this.captureBase64('png', {top: 0, left: 0, width: 30, height: 30}).length > 0,
|
||||||
|
'Casper.captureBase64() rendered a capture from a clipRect as base64');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
casper.run(function() {
|
||||||
|
try {
|
||||||
|
fs.remove(testFile);
|
||||||
|
} catch(e) {}
|
||||||
|
this.test.done();
|
||||||
|
});
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*global casper*/
|
||||||
|
/*jshint strict:false*/
|
||||||
|
casper.start('tests/site/index.html', function() {
|
||||||
|
this.click('a[href="test.html"]');
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.then(function() {
|
||||||
|
this.test.comment('Casper.click()');
|
||||||
|
this.test.assertTitle('CasperJS test target', 'Casper.click() can click on a link');
|
||||||
|
}).thenClick('a', function() {
|
||||||
|
this.test.comment('Casper.thenClick()');
|
||||||
|
this.test.assertTitle('CasperJS test form', 'Casper.thenClick() can click on a link');
|
||||||
|
});
|
||||||
|
|
||||||
|
// onclick variants tests
|
||||||
|
casper.thenOpen('tests/site/click.html', function() {
|
||||||
|
this.test.comment('Casper.click()');
|
||||||
|
this.test.assert(this.click('#test1'), 'Casper.click() can click an `href="javascript:` link');
|
||||||
|
this.test.assert(this.click('#test2'), 'Casper.click() can click an `href="#"` link');
|
||||||
|
this.test.assert(this.click('#test3'), 'Casper.click() can click an `onclick=".*; return false"` link');
|
||||||
|
this.test.assert(this.click('#test4'), 'Casper.click() can click an unobstrusive js handled link');
|
||||||
|
var results = this.getGlobal('results');
|
||||||
|
this.test.assert(results.test1, 'Casper.click() has clicked an `href="javascript:` link');
|
||||||
|
this.test.assert(results.test2, 'Casper.click() has clicked an `href="#"` link');
|
||||||
|
this.test.assert(results.test3, 'Casper.click() has clicked an `onclick=".*; return false"` link');
|
||||||
|
this.test.assert(results.test4, 'Casper.click() has clicked an unobstrusive js handled link');
|
||||||
|
});
|
||||||
|
|
||||||
|
// clickLabel tests
|
||||||
|
casper.thenOpen('tests/site/click.html', function() {
|
||||||
|
this.test.comment('Casper.clickLabel()');
|
||||||
|
this.test.assert(this.clickLabel('test1'), 'Casper.clickLabel() can click an `href="javascript:` link');
|
||||||
|
this.test.assert(this.clickLabel('test2'), 'Casper.clickLabel() can click an `href="#"` link');
|
||||||
|
this.test.assert(this.clickLabel('test3'), 'Casper.clickLabel() can click an `onclick=".*; return false"` link');
|
||||||
|
this.test.assert(this.clickLabel('test4'), 'Casper.clickLabel() can click an unobstrusive js handled link');
|
||||||
|
var results = this.getGlobal('results');
|
||||||
|
this.test.assert(results.test1, 'Casper.clickLabel() has clicked an `href="javascript:` link');
|
||||||
|
this.test.assert(results.test2, 'Casper.clickLabel() has clicked an `href="#"` link');
|
||||||
|
this.test.assert(results.test3, 'Casper.clickLabel() has clicked an `onclick=".*; return false"` link');
|
||||||
|
this.test.assert(results.test4, 'Casper.clickLabel() has clicked an unobstrusive js handled link');
|
||||||
|
});
|
||||||
|
|
||||||
|
// casper.mouse
|
||||||
|
casper.then(function() {
|
||||||
|
this.test.comment('Mouse.down()');
|
||||||
|
this.mouse.down(200, 100);
|
||||||
|
var results = this.getGlobal('results');
|
||||||
|
this.test.assertEquals(results.testdown, [200, 100], 'Mouse.down() has pressed button to the specified position');
|
||||||
|
|
||||||
|
this.test.comment('Mouse.up()');
|
||||||
|
this.mouse.up(200, 100);
|
||||||
|
results = this.getGlobal('results');
|
||||||
|
this.test.assertEquals(results.testup, [200, 100], 'Mouse.up() has released button to the specified position');
|
||||||
|
|
||||||
|
this.test.comment('Mouse.move()');
|
||||||
|
this.mouse.move(200, 100);
|
||||||
|
results = this.getGlobal('results');
|
||||||
|
this.test.assertEquals(results.testmove, [200, 100], 'Mouse.move() has moved to the specified position');
|
||||||
|
});
|
||||||
|
|
||||||
|
casper.run(function() {
|
||||||
|
this.test.done();
|
||||||
|
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue