poltergeist 0.7.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,7 @@ Poltergeist.Browser = (function() {
6
6
  this.width = width || 1024;
7
7
  this.height = height || 768;
8
8
  this.state = 'default';
9
+ this.page_stack = [];
9
10
  this.page_id = 0;
10
11
  this.resetPage();
11
12
  }
@@ -14,8 +15,13 @@ Poltergeist.Browser = (function() {
14
15
  var _this = this;
15
16
  if (this.page != null) {
16
17
  this.page.release();
18
+ phantom.clearCookies();
17
19
  }
18
- this.page = new Poltergeist.WebPage(this.width, this.height);
20
+ this.page = new Poltergeist.WebPage;
21
+ this.page.setViewportSize({
22
+ width: this.width,
23
+ height: this.height
24
+ });
19
25
  this.page.onLoadStarted = function() {
20
26
  if (_this.state === 'clicked') {
21
27
  return _this.state = 'loading';
@@ -28,13 +34,30 @@ Poltergeist.Browser = (function() {
28
34
  };
29
35
  this.page.onLoadFinished = function(status) {
30
36
  if (_this.state === 'loading') {
31
- _this.sendResponse(status);
37
+ _this.sendResponse({
38
+ status: status,
39
+ click: _this.last_click
40
+ });
41
+ return _this.state = 'default';
42
+ } else if (_this.state === 'awaiting_frame_load') {
43
+ _this.sendResponse(true);
32
44
  return _this.state = 'default';
33
45
  }
34
46
  };
35
- return this.page.onInitialized = function() {
47
+ this.page.onInitialized = function() {
36
48
  return _this.page_id += 1;
37
49
  };
50
+ return this.page.onPageCreated = function(sub_page) {
51
+ var name;
52
+ if (_this.state === 'awaiting_sub_page') {
53
+ name = _this.page_name;
54
+ _this.state = 'default';
55
+ _this.page_name = null;
56
+ return setTimeout((function() {
57
+ return _this.push_window(name);
58
+ }), 0);
59
+ }
60
+ };
38
61
  };
39
62
 
40
63
  Browser.prototype.sendResponse = function(response) {
@@ -42,7 +65,7 @@ Poltergeist.Browser = (function() {
42
65
  errors = this.page.errors();
43
66
  if (errors.length > 0) {
44
67
  this.page.clearErrors();
45
- throw new Poltergeist.JavascriptError(errors);
68
+ return this.owner.sendError(new Poltergeist.JavascriptError(errors));
46
69
  } else {
47
70
  return this.owner.sendResponse(response);
48
71
  }
@@ -56,12 +79,15 @@ Poltergeist.Browser = (function() {
56
79
  }
57
80
  };
58
81
 
59
- Browser.prototype.visit = function(url, headers) {
82
+ Browser.prototype.visit = function(url) {
83
+ var prev_url;
60
84
  this.state = 'loading';
61
- return this.page.open(url, {
62
- operation: "get",
63
- headers: headers
64
- });
85
+ prev_url = this.page.currentUrl();
86
+ this.page.open(url);
87
+ if (/#/.test(url) && prev_url.split('#')[0] === url.split('#')[0]) {
88
+ this.state = 'default';
89
+ return this.sendResponse('success');
90
+ }
65
91
  };
66
92
 
67
93
  Browser.prototype.current_url = function() {
@@ -138,25 +164,68 @@ Poltergeist.Browser = (function() {
138
164
  return this.sendResponse(true);
139
165
  };
140
166
 
141
- Browser.prototype.push_frame = function(id) {
142
- this.page.pushFrame(id);
143
- return this.sendResponse(true);
167
+ Browser.prototype.push_frame = function(name) {
168
+ var _this = this;
169
+ if (this.page.pushFrame(name)) {
170
+ if (this.page.currentUrl() === 'about:blank') {
171
+ return this.state = 'awaiting_frame_load';
172
+ } else {
173
+ return this.sendResponse(true);
174
+ }
175
+ } else {
176
+ return setTimeout((function() {
177
+ return _this.push_frame(name);
178
+ }), 50);
179
+ }
144
180
  };
145
181
 
146
182
  Browser.prototype.pop_frame = function() {
147
- this.page.popFrame();
183
+ return this.sendResponse(this.page.popFrame());
184
+ };
185
+
186
+ Browser.prototype.push_window = function(name) {
187
+ var sub_page,
188
+ _this = this;
189
+ sub_page = this.page.getPage(name);
190
+ if (sub_page) {
191
+ if (sub_page.currentUrl() === 'about:blank') {
192
+ return sub_page.onLoadFinished = function() {
193
+ sub_page.onLoadFinished = null;
194
+ return _this.push_window(name);
195
+ };
196
+ } else {
197
+ this.page_stack.push(this.page);
198
+ this.page = sub_page;
199
+ this.page_id += 1;
200
+ return this.sendResponse(true);
201
+ }
202
+ } else {
203
+ this.page_name = name;
204
+ return this.state = 'awaiting_sub_page';
205
+ }
206
+ };
207
+
208
+ Browser.prototype.pop_window = function() {
209
+ var prev_page;
210
+ prev_page = this.page_stack.pop();
211
+ if (prev_page) {
212
+ this.page = prev_page;
213
+ }
148
214
  return this.sendResponse(true);
149
215
  };
150
216
 
151
217
  Browser.prototype.click = function(page_id, id) {
152
- var node;
218
+ var node,
219
+ _this = this;
153
220
  node = this.node(page_id, id);
154
221
  this.state = 'clicked';
155
- node.click();
156
- if (this.state !== 'loading') {
157
- this.state = 'default';
158
- return this.sendResponse(true);
159
- }
222
+ this.last_click = node.click();
223
+ return setTimeout(function() {
224
+ if (_this.state !== 'loading') {
225
+ _this.state = 'default';
226
+ return _this.sendResponse(_this.last_click);
227
+ }
228
+ }, 5);
160
229
  };
161
230
 
162
231
  Browser.prototype.drag = function(page_id, id, other_id) {
@@ -223,6 +292,32 @@ Poltergeist.Browser = (function() {
223
292
  return this.sendResponse(this.page.networkTraffic());
224
293
  };
225
294
 
295
+ Browser.prototype.set_headers = function(headers) {
296
+ if (headers['User-Agent']) {
297
+ this.page.setUserAgent(headers['User-Agent']);
298
+ }
299
+ this.page.setCustomHeaders(headers);
300
+ return this.sendResponse(true);
301
+ };
302
+
303
+ Browser.prototype.response_headers = function() {
304
+ return this.sendResponse(this.page.responseHeaders());
305
+ };
306
+
307
+ Browser.prototype.cookies = function() {
308
+ return this.sendResponse(this.page.cookies());
309
+ };
310
+
311
+ Browser.prototype.set_cookie = function(cookie) {
312
+ phantom.addCookie(cookie);
313
+ return this.sendResponse(true);
314
+ };
315
+
316
+ Browser.prototype.remove_cookie = function(name) {
317
+ this.page.deleteCookie(name);
318
+ return this.sendResponse(true);
319
+ };
320
+
226
321
  Browser.prototype.exit = function() {
227
322
  return phantom.exit();
228
323
  };
@@ -4,7 +4,7 @@ Poltergeist.Node = (function() {
4
4
  var name, _fn, _i, _len, _ref,
5
5
  _this = this;
6
6
 
7
- Node.DELEGATES = ['text', 'getAttribute', 'value', 'set', 'setAttribute', 'isObsolete', 'removeAttribute', 'isMultiple', 'select', 'tagName', 'find', 'isVisible', 'position', 'trigger', 'parentId', 'clickTest', 'scrollIntoView', 'isDOMEqual'];
7
+ Node.DELEGATES = ['text', 'getAttribute', 'value', 'setAttribute', 'isObsolete', 'removeAttribute', 'isMultiple', 'select', 'tagName', 'find', 'isVisible', 'position', 'trigger', 'parentId', 'clickTest', 'scrollIntoView', 'isDOMEqual', 'focusAndHighlight', 'blur'];
8
8
 
9
9
  function Node(page, id) {
10
10
  this.page = page;
@@ -47,7 +47,8 @@ Poltergeist.Node = (function() {
47
47
  pos = this.clickPosition();
48
48
  test = this.clickTest(pos.x, pos.y);
49
49
  if (test.status === 'success') {
50
- return this.page.sendEvent('click', pos.x, pos.y);
50
+ this.page.mouseEvent('click', pos.x, pos.y);
51
+ return pos;
51
52
  } else {
52
53
  throw new Poltergeist.ClickFailed(test.selector, pos);
53
54
  }
@@ -58,15 +59,20 @@ Poltergeist.Node = (function() {
58
59
  this.scrollIntoView();
59
60
  position = this.clickPosition();
60
61
  otherPosition = other.clickPosition();
61
- this.page.sendEvent('mousedown', position.x, position.y);
62
- this.page.sendEvent('mousemove', otherPosition.x, otherPosition.y);
63
- return this.page.sendEvent('mouseup', otherPosition.x, otherPosition.y);
62
+ this.page.mouseEvent('mousedown', position.x, position.y);
63
+ return this.page.mouseEvent('mouseup', otherPosition.x, otherPosition.y);
64
64
  };
65
65
 
66
66
  Node.prototype.isEqual = function(other) {
67
67
  return this.page === other.page && this.isDOMEqual(other.id);
68
68
  };
69
69
 
70
+ Node.prototype.set = function(value) {
71
+ this.focusAndHighlight();
72
+ this.page.sendEvent('keypress', value.toString());
73
+ return this.blur();
74
+ };
75
+
70
76
  return Node;
71
77
 
72
78
  }).call(this);
@@ -4,22 +4,21 @@ Poltergeist.WebPage = (function() {
4
4
  var command, delegate, _fn, _fn1, _i, _j, _len, _len1, _ref, _ref1,
5
5
  _this = this;
6
6
 
7
- WebPage.CALLBACKS = ['onAlert', 'onConsoleMessage', 'onLoadFinished', 'onInitialized', 'onLoadStarted', 'onResourceRequested', 'onResourceReceived', 'onError', 'onNavigationRequested', 'onUrlChanged'];
7
+ WebPage.CALLBACKS = ['onAlert', 'onConsoleMessage', 'onLoadFinished', 'onInitialized', 'onLoadStarted', 'onResourceRequested', 'onResourceReceived', 'onError', 'onNavigationRequested', 'onUrlChanged', 'onPageCreated'];
8
8
 
9
9
  WebPage.DELEGATES = ['open', 'sendEvent', 'uploadFile', 'release', 'render'];
10
10
 
11
- WebPage.COMMANDS = ['currentUrl', 'find', 'nodeCall', 'pushFrame', 'popFrame', 'documentSize'];
11
+ WebPage.COMMANDS = ['currentUrl', 'find', 'nodeCall', 'documentSize'];
12
12
 
13
- function WebPage(width, height) {
13
+ function WebPage(_native) {
14
14
  var callback, _i, _len, _ref;
15
- this["native"] = require('webpage').create();
15
+ this["native"] = _native;
16
+ this["native"] || (this["native"] = require('webpage').create());
16
17
  this._source = "";
17
18
  this._errors = [];
18
19
  this._networkTraffic = {};
19
- this.setViewportSize({
20
- width: width,
21
- height: height
22
- });
20
+ this.frames = [];
21
+ this.sub_pages = {};
23
22
  _ref = WebPage.CALLBACKS;
24
23
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
25
24
  callback = _ref[_i];
@@ -65,8 +64,7 @@ Poltergeist.WebPage = (function() {
65
64
  if (this["native"].evaluate(function() {
66
65
  return typeof __poltergeist;
67
66
  }) === "undefined") {
68
- this["native"].injectJs("" + phantom.libraryPath + "/agent.js");
69
- return this.nodes = {};
67
+ return this["native"].injectJs("" + phantom.libraryPath + "/agent.js");
70
68
  }
71
69
  };
72
70
 
@@ -107,6 +105,10 @@ Poltergeist.WebPage = (function() {
107
105
 
108
106
  WebPage.prototype.onResourceRequestedNative = function(request) {
109
107
  this.lastRequestId = request.id;
108
+ if (request.url === this.redirectURL) {
109
+ this.redirectURL = null;
110
+ this.requestId = request.id;
111
+ }
110
112
  return this._networkTraffic[request.id] = {
111
113
  request: request,
112
114
  responseParts: []
@@ -114,12 +116,16 @@ Poltergeist.WebPage = (function() {
114
116
  };
115
117
 
116
118
  WebPage.prototype.onResourceReceivedNative = function(response) {
117
- this._networkTraffic[response.id].responseParts.push(response);
119
+ var _ref2;
120
+ if ((_ref2 = this._networkTraffic[response.id]) != null) {
121
+ _ref2.responseParts.push(response);
122
+ }
118
123
  if (this.requestId === response.id) {
119
124
  if (response.redirectURL) {
120
- return this.requestId = response.id;
125
+ return this.redirectURL = response.redirectURL;
121
126
  } else {
122
- return this._statusCode = response.status;
127
+ this._statusCode = response.status;
128
+ return this._responseHeaders = response.headers;
123
129
  }
124
130
  }
125
131
  };
@@ -129,7 +135,7 @@ Poltergeist.WebPage = (function() {
129
135
  };
130
136
 
131
137
  WebPage.prototype.content = function() {
132
- return this["native"].content;
138
+ return this["native"].frameContent;
133
139
  };
134
140
 
135
141
  WebPage.prototype.source = function() {
@@ -148,6 +154,23 @@ Poltergeist.WebPage = (function() {
148
154
  return this._statusCode;
149
155
  };
150
156
 
157
+ WebPage.prototype.responseHeaders = function() {
158
+ var headers;
159
+ headers = {};
160
+ this._responseHeaders.forEach(function(item) {
161
+ return headers[item.name] = item.value;
162
+ });
163
+ return headers;
164
+ };
165
+
166
+ WebPage.prototype.cookies = function() {
167
+ return this["native"].cookies;
168
+ };
169
+
170
+ WebPage.prototype.deleteCookie = function(name) {
171
+ return this["native"].deleteCookie(name);
172
+ };
173
+
151
174
  WebPage.prototype.viewportSize = function() {
152
175
  return this["native"].viewportSize;
153
176
  };
@@ -172,6 +195,43 @@ Poltergeist.WebPage = (function() {
172
195
  return this["native"].clipRect = rect;
173
196
  };
174
197
 
198
+ WebPage.prototype.setUserAgent = function(userAgent) {
199
+ return this["native"].settings.userAgent = userAgent;
200
+ };
201
+
202
+ WebPage.prototype.setCustomHeaders = function(headers) {
203
+ return this["native"].customHeaders = headers;
204
+ };
205
+
206
+ WebPage.prototype.pushFrame = function(name) {
207
+ var res;
208
+ this.frames.push(name);
209
+ res = this["native"].switchToFrame(name);
210
+ this.injectAgent();
211
+ return res;
212
+ };
213
+
214
+ WebPage.prototype.popFrame = function() {
215
+ this.frames.pop();
216
+ if (this.frames.length === 0) {
217
+ return this["native"].switchToMainFrame();
218
+ } else {
219
+ return this["native"].switchToFrame(this.frames[this.frames.length - 1]);
220
+ }
221
+ };
222
+
223
+ WebPage.prototype.getPage = function(name) {
224
+ var page;
225
+ if (this.sub_pages[name]) {
226
+ return this.sub_pages[name];
227
+ } else {
228
+ page = this["native"].getPage(name);
229
+ if (page) {
230
+ return this.sub_pages[name] = new Poltergeist.WebPage(page);
231
+ }
232
+ }
233
+ };
234
+
175
235
  WebPage.prototype.dimensions = function() {
176
236
  var scroll, viewport;
177
237
  scroll = this.scrollPosition();
@@ -206,8 +266,12 @@ Poltergeist.WebPage = (function() {
206
266
  };
207
267
 
208
268
  WebPage.prototype.get = function(id) {
209
- var _base;
210
- return (_base = this.nodes)[id] || (_base[id] = new Poltergeist.Node(this, id));
269
+ return new Poltergeist.Node(this, id);
270
+ };
271
+
272
+ WebPage.prototype.mouseEvent = function(name, x, y) {
273
+ this.sendEvent('mousemove', x, y);
274
+ return this.sendEvent(name, x, y);
211
275
  };
212
276
 
213
277
  WebPage.prototype.evaluate = function() {
@@ -1,10 +1,10 @@
1
1
  # Proxy object for forwarding method calls to the node object inside the page.
2
2
 
3
3
  class Poltergeist.Node
4
- @DELEGATES = ['text', 'getAttribute', 'value', 'set', 'setAttribute', 'isObsolete',
4
+ @DELEGATES = ['text', 'getAttribute', 'value', 'setAttribute', 'isObsolete',
5
5
  'removeAttribute', 'isMultiple', 'select', 'tagName', 'find',
6
6
  'isVisible', 'position', 'trigger', 'parentId', 'clickTest',
7
- 'scrollIntoView', 'isDOMEqual']
7
+ 'scrollIntoView', 'isDOMEqual', 'focusAndHighlight', 'blur']
8
8
 
9
9
  constructor: (@page, @id) ->
10
10
 
@@ -35,7 +35,8 @@ class Poltergeist.Node
35
35
  test = this.clickTest(pos.x, pos.y)
36
36
 
37
37
  if test.status == 'success'
38
- @page.sendEvent('click', pos.x, pos.y)
38
+ @page.mouseEvent('click', pos.x, pos.y)
39
+ pos
39
40
  else
40
41
  throw new Poltergeist.ClickFailed(test.selector, pos)
41
42
 
@@ -45,9 +46,13 @@ class Poltergeist.Node
45
46
  position = this.clickPosition()
46
47
  otherPosition = other.clickPosition()
47
48
 
48
- @page.sendEvent('mousedown', position.x, position.y)
49
- @page.sendEvent('mousemove', otherPosition.x, otherPosition.y)
50
- @page.sendEvent('mouseup', otherPosition.x, otherPosition.y)
49
+ @page.mouseEvent('mousedown', position.x, position.y)
50
+ @page.mouseEvent('mouseup', otherPosition.x, otherPosition.y)
51
51
 
52
52
  isEqual: (other) ->
53
53
  @page == other.page && this.isDOMEqual(other.id)
54
+
55
+ set: (value) ->
56
+ this.focusAndHighlight()
57
+ @page.sendEvent('keypress', value.toString())
58
+ this.blur()
@@ -1,19 +1,20 @@
1
1
  class Poltergeist.WebPage
2
2
  @CALLBACKS = ['onAlert', 'onConsoleMessage', 'onLoadFinished', 'onInitialized',
3
3
  'onLoadStarted', 'onResourceRequested', 'onResourceReceived',
4
- 'onError', 'onNavigationRequested', 'onUrlChanged']
4
+ 'onError', 'onNavigationRequested', 'onUrlChanged', 'onPageCreated']
5
5
 
6
6
  @DELEGATES = ['open', 'sendEvent', 'uploadFile', 'release', 'render']
7
7
 
8
- @COMMANDS = ['currentUrl', 'find', 'nodeCall', 'pushFrame', 'popFrame', 'documentSize']
8
+ @COMMANDS = ['currentUrl', 'find', 'nodeCall', 'documentSize']
9
+
10
+ constructor: (@native) ->
11
+ @native or= require('webpage').create()
9
12
 
10
- constructor: (width, height) ->
11
- @native = require('webpage').create()
12
13
  @_source = ""
13
14
  @_errors = []
14
15
  @_networkTraffic = {}
15
-
16
- this.setViewportSize(width: width, height: height)
16
+ @frames = []
17
+ @sub_pages = {}
17
18
 
18
19
  for callback in WebPage.CALLBACKS
19
20
  this.bindCallback(callback)
@@ -38,7 +39,6 @@ class Poltergeist.WebPage
38
39
  injectAgent: ->
39
40
  if @native.evaluate(-> typeof __poltergeist) == "undefined"
40
41
  @native.injectJs("#{phantom.libraryPath}/agent.js")
41
- @nodes = {}
42
42
 
43
43
  onConsoleMessageNative: (message) ->
44
44
  if message == '__DOMContentLoaded'
@@ -67,25 +67,30 @@ class Poltergeist.WebPage
67
67
  onResourceRequestedNative: (request) ->
68
68
  @lastRequestId = request.id
69
69
 
70
+ if request.url == @redirectURL
71
+ @redirectURL = null
72
+ @requestId = request.id
73
+
70
74
  @_networkTraffic[request.id] = {
71
75
  request: request,
72
76
  responseParts: []
73
77
  }
74
78
 
75
79
  onResourceReceivedNative: (response) ->
76
- @_networkTraffic[response.id].responseParts.push(response)
80
+ @_networkTraffic[response.id]?.responseParts.push(response)
77
81
 
78
82
  if @requestId == response.id
79
83
  if response.redirectURL
80
- @requestId = response.id
84
+ @redirectURL = response.redirectURL
81
85
  else
82
- @_statusCode = response.status
86
+ @_statusCode = response.status
87
+ @_responseHeaders = response.headers
83
88
 
84
89
  networkTraffic: ->
85
90
  @_networkTraffic
86
91
 
87
92
  content: ->
88
- @native.content
93
+ @native.frameContent
89
94
 
90
95
  source: ->
91
96
  @_source
@@ -99,6 +104,18 @@ class Poltergeist.WebPage
99
104
  statusCode: ->
100
105
  @_statusCode
101
106
 
107
+ responseHeaders: ->
108
+ headers = {}
109
+ @_responseHeaders.forEach (item) ->
110
+ headers[item.name] = item.value
111
+ headers
112
+
113
+ cookies: ->
114
+ @native.cookies
115
+
116
+ deleteCookie: (name) ->
117
+ @native.deleteCookie(name)
118
+
102
119
  viewportSize: ->
103
120
  @native.viewportSize
104
121
 
@@ -117,6 +134,33 @@ class Poltergeist.WebPage
117
134
  setClipRect: (rect) ->
118
135
  @native.clipRect = rect
119
136
 
137
+ setUserAgent: (userAgent) ->
138
+ @native.settings.userAgent = userAgent
139
+
140
+ setCustomHeaders: (headers) ->
141
+ @native.customHeaders = headers
142
+
143
+ pushFrame: (name) ->
144
+ @frames.push(name)
145
+ res = @native.switchToFrame(name)
146
+ this.injectAgent()
147
+ res
148
+
149
+ popFrame: ->
150
+ @frames.pop()
151
+
152
+ if @frames.length == 0
153
+ @native.switchToMainFrame()
154
+ else
155
+ @native.switchToFrame(@frames[@frames.length - 1])
156
+
157
+ getPage: (name) ->
158
+ if @sub_pages[name]
159
+ @sub_pages[name]
160
+ else
161
+ page = @native.getPage(name)
162
+ @sub_pages[name] = new Poltergeist.WebPage(page) if page
163
+
120
164
  dimensions: ->
121
165
  scroll = this.scrollPosition()
122
166
  viewport = this.viewportSize()
@@ -144,7 +188,13 @@ class Poltergeist.WebPage
144
188
  dimensions
145
189
 
146
190
  get: (id) ->
147
- @nodes[id] or= new Poltergeist.Node(this, id)
191
+ new Poltergeist.Node(this, id)
192
+
193
+ # Before each mouse event we make sure that the mouse is moved to where the
194
+ # event will take place. This deals with e.g. :hover changes.
195
+ mouseEvent: (name, x, y) ->
196
+ this.sendEvent('mousemove', x, y)
197
+ this.sendEvent(name, x, y)
148
198
 
149
199
  evaluate: (fn, args...) ->
150
200
  JSON.parse @native.evaluate("function() { return PoltergeistAgent.stringify(#{this.stringifyCall(fn, args)}) }")