poltergeist 0.4.0 → 0.5.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.
@@ -7,15 +7,28 @@ PoltergeistAgent = (function() {
7
7
  this.windows = [];
8
8
  this.pushWindow(window);
9
9
  }
10
+ PoltergeistAgent.prototype.externalCall = function(name, arguments) {
11
+ try {
12
+ return {
13
+ value: this[name].apply(this, arguments)
14
+ };
15
+ } catch (error) {
16
+ return {
17
+ error: error.toString()
18
+ };
19
+ }
20
+ };
10
21
  PoltergeistAgent.prototype.pushWindow = function(new_window) {
11
22
  this.windows.push(new_window);
12
23
  this.window = new_window;
13
- return this.document = this.window.document;
24
+ this.document = this.window.document;
25
+ return null;
14
26
  };
15
27
  PoltergeistAgent.prototype.popWindow = function() {
16
28
  this.windows.pop();
17
29
  this.window = this.windows[this.windows.length - 1];
18
- return this.document = this.window.document;
30
+ this.document = this.window.document;
31
+ return null;
19
32
  };
20
33
  PoltergeistAgent.prototype.pushFrame = function(id) {
21
34
  return this.pushWindow(this.document.getElementById(id).contentWindow);
@@ -26,10 +39,12 @@ PoltergeistAgent = (function() {
26
39
  PoltergeistAgent.prototype.currentUrl = function() {
27
40
  return window.location.toString();
28
41
  };
29
- PoltergeistAgent.prototype.find = function(selector, id) {
30
- var context, i, ids, results, _ref;
31
- context = id != null ? this.elements[id] : this.document;
32
- results = this.document.evaluate(selector, context, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
42
+ PoltergeistAgent.prototype.find = function(selector, within) {
43
+ var i, ids, results, _ref;
44
+ if (within == null) {
45
+ within = this.document;
46
+ }
47
+ results = this.document.evaluate(selector, within, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
33
48
  ids = [];
34
49
  for (i = 0, _ref = results.snapshotLength; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) {
35
50
  ids.push(this.register(results.snapshotItem(i)));
@@ -53,10 +68,20 @@ PoltergeistAgent = (function() {
53
68
  PoltergeistAgent.prototype.nodeCall = function(id, name, arguments) {
54
69
  var node;
55
70
  node = this.get(id);
71
+ if (node.isObsolete()) {
72
+ throw new PoltergeistAgent.ObsoleteNode;
73
+ }
56
74
  return node[name].apply(node, arguments);
57
75
  };
58
76
  return PoltergeistAgent;
59
77
  })();
78
+ PoltergeistAgent.ObsoleteNode = (function() {
79
+ function ObsoleteNode() {}
80
+ ObsoleteNode.prototype.toString = function() {
81
+ return "PoltergeistAgent.ObsoleteNode";
82
+ };
83
+ return ObsoleteNode;
84
+ })();
60
85
  PoltergeistAgent.Node = (function() {
61
86
  Node.EVENTS = {
62
87
  FOCUS: ['blur', 'focus', 'focusin', 'focusout'],
@@ -69,6 +94,9 @@ PoltergeistAgent.Node = (function() {
69
94
  Node.prototype.parentId = function() {
70
95
  return this.agent.register(this.element.parentNode);
71
96
  };
97
+ Node.prototype.find = function(selector) {
98
+ return this.agent.find(selector, this.element);
99
+ };
72
100
  Node.prototype.isObsolete = function() {
73
101
  var obsolete;
74
102
  obsolete = __bind(function(element) {
@@ -178,10 +206,14 @@ PoltergeistAgent.Node = (function() {
178
206
  };
179
207
  Node.prototype.position = function() {
180
208
  var rect;
181
- rect = this.element.getBoundingClientRect();
209
+ rect = this.element.getClientRects()[0];
182
210
  return {
183
211
  top: rect.top,
184
- left: rect.left
212
+ right: rect.right,
213
+ left: rect.left,
214
+ bottom: rect.bottom,
215
+ width: rect.width,
216
+ height: rect.height
185
217
  };
186
218
  };
187
219
  Node.prototype.trigger = function(name) {
@@ -197,6 +229,37 @@ PoltergeistAgent.Node = (function() {
197
229
  }
198
230
  return this.element.dispatchEvent(event);
199
231
  };
232
+ Node.prototype.clickTest = function(x, y) {
233
+ var el, origEl;
234
+ el = origEl = document.elementFromPoint(x, y);
235
+ while (el) {
236
+ if (el === this.element) {
237
+ return {
238
+ status: 'success'
239
+ };
240
+ } else {
241
+ el = el.parentNode;
242
+ }
243
+ }
244
+ return {
245
+ status: 'failure',
246
+ selector: origEl && this.getSelector(origEl)
247
+ };
248
+ };
249
+ Node.prototype.getSelector = function(el) {
250
+ var className, selector, _i, _len, _ref;
251
+ selector = el.tagName !== 'HTML' ? this.getSelector(el.parentNode) + ' ' : '';
252
+ selector += el.tagName.toLowerCase();
253
+ if (el.id) {
254
+ selector += "#" + el.id;
255
+ }
256
+ _ref = el.classList;
257
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
258
+ className = _ref[_i];
259
+ selector += "." + className;
260
+ }
261
+ return selector;
262
+ };
200
263
  return Node;
201
264
  })();
202
265
  window.__poltergeist = new PoltergeistAgent;
@@ -3,6 +3,7 @@ Poltergeist.Browser = (function() {
3
3
  function Browser(owner) {
4
4
  this.owner = owner;
5
5
  this.state = 'default';
6
+ this.page_id = 0;
6
7
  this.resetPage();
7
8
  }
8
9
  Browser.prototype.resetPage = function() {
@@ -15,45 +16,71 @@ Poltergeist.Browser = (function() {
15
16
  return this.state = 'loading';
16
17
  }
17
18
  }, this);
18
- return this.page.onLoadFinished = __bind(function(status) {
19
+ this.page.onLoadFinished = __bind(function(status) {
19
20
  if (this.state === 'loading') {
20
- this.owner.sendResponse(status);
21
+ this.sendResponse(status);
21
22
  return this.state = 'default';
22
23
  }
23
24
  }, this);
25
+ return this.page.onInitialized = __bind(function() {
26
+ return this.page_id += 1;
27
+ }, this);
28
+ };
29
+ Browser.prototype.sendResponse = function(response) {
30
+ var errors;
31
+ errors = this.page.errors();
32
+ if (errors.length > 0) {
33
+ this.page.clearErrors();
34
+ return this.owner.sendError(new Poltergeist.JavascriptError(errors));
35
+ } else {
36
+ return this.owner.sendResponse(response);
37
+ }
38
+ };
39
+ Browser.prototype.node = function(page_id, id) {
40
+ if (page_id === this.page_id) {
41
+ return this.page.get(id);
42
+ } else {
43
+ throw new Poltergeist.ObsoleteNode;
44
+ }
24
45
  };
25
46
  Browser.prototype.visit = function(url) {
26
47
  this.state = 'loading';
27
48
  return this.page.open(url);
28
49
  };
29
50
  Browser.prototype.current_url = function() {
30
- return this.owner.sendResponse(this.page.currentUrl());
51
+ return this.sendResponse(this.page.currentUrl());
31
52
  };
32
53
  Browser.prototype.body = function() {
33
- return this.owner.sendResponse(this.page.content());
54
+ return this.sendResponse(this.page.content());
34
55
  };
35
56
  Browser.prototype.source = function() {
36
- return this.owner.sendResponse(this.page.source());
57
+ return this.sendResponse(this.page.source());
58
+ };
59
+ Browser.prototype.find = function(selector) {
60
+ return this.sendResponse({
61
+ page_id: this.page_id,
62
+ ids: this.page.find(selector)
63
+ });
37
64
  };
38
- Browser.prototype.find = function(selector, id) {
39
- return this.owner.sendResponse(this.page.find(selector, id));
65
+ Browser.prototype.find_within = function(page_id, id, selector) {
66
+ return this.sendResponse(this.node(page_id, id).find(selector));
40
67
  };
41
- Browser.prototype.text = function(id) {
42
- return this.owner.sendResponse(this.page.get(id).text());
68
+ Browser.prototype.text = function(page_id, id) {
69
+ return this.sendResponse(this.node(page_id, id).text());
43
70
  };
44
- Browser.prototype.attribute = function(id, name) {
45
- return this.owner.sendResponse(this.page.get(id).getAttribute(name));
71
+ Browser.prototype.attribute = function(page_id, id, name) {
72
+ return this.sendResponse(this.node(page_id, id).getAttribute(name));
46
73
  };
47
- Browser.prototype.value = function(id) {
48
- return this.owner.sendResponse(this.page.get(id).value());
74
+ Browser.prototype.value = function(page_id, id) {
75
+ return this.sendResponse(this.node(page_id, id).value());
49
76
  };
50
- Browser.prototype.set = function(id, value) {
51
- this.page.get(id).set(value);
52
- return this.owner.sendResponse(true);
77
+ Browser.prototype.set = function(page_id, id, value) {
78
+ this.node(page_id, id).set(value);
79
+ return this.sendResponse(true);
53
80
  };
54
- Browser.prototype.select_file = function(id, value) {
81
+ Browser.prototype.select_file = function(page_id, id, value) {
55
82
  var element, multiple;
56
- element = this.page.get(id);
83
+ element = this.node(page_id, id);
57
84
  multiple = element.isMultiple();
58
85
  if (multiple) {
59
86
  element.removeAttribute('multiple');
@@ -64,53 +91,53 @@ Poltergeist.Browser = (function() {
64
91
  if (multiple) {
65
92
  element.setAttribute('multiple', 'multiple');
66
93
  }
67
- return this.owner.sendResponse(true);
94
+ return this.sendResponse(true);
68
95
  };
69
- Browser.prototype.select = function(id, value) {
70
- return this.owner.sendResponse(this.page.get(id).select(value));
96
+ Browser.prototype.select = function(page_id, id, value) {
97
+ return this.sendResponse(this.node(page_id, id).select(value));
71
98
  };
72
- Browser.prototype.tag_name = function(id) {
73
- return this.owner.sendResponse(this.page.get(id).tagName());
99
+ Browser.prototype.tag_name = function(page_id, id) {
100
+ return this.sendResponse(this.node(page_id, id).tagName());
74
101
  };
75
- Browser.prototype.visible = function(id) {
76
- return this.owner.sendResponse(this.page.get(id).isVisible());
102
+ Browser.prototype.visible = function(page_id, id) {
103
+ return this.sendResponse(this.node(page_id, id).isVisible());
77
104
  };
78
105
  Browser.prototype.evaluate = function(script) {
79
- return this.owner.sendResponse(JSON.parse(this.page.evaluate("function() { return JSON.stringify(" + script + ") }")));
106
+ return this.sendResponse(JSON.parse(this.page.evaluate("function() { return JSON.stringify(" + script + ") }")));
80
107
  };
81
108
  Browser.prototype.execute = function(script) {
82
109
  this.page.execute("function() { " + script + " }");
83
- return this.owner.sendResponse(true);
110
+ return this.sendResponse(true);
84
111
  };
85
112
  Browser.prototype.push_frame = function(id) {
86
113
  this.page.pushFrame(id);
87
- return this.owner.sendResponse(true);
114
+ return this.sendResponse(true);
88
115
  };
89
116
  Browser.prototype.pop_frame = function() {
90
117
  this.page.popFrame();
91
- return this.owner.sendResponse(true);
118
+ return this.sendResponse(true);
92
119
  };
93
- Browser.prototype.click = function(id) {
120
+ Browser.prototype.click = function(page_id, id) {
94
121
  this.state = 'clicked';
95
- this.page.get(id).click();
122
+ this.node(page_id, id).click();
96
123
  return setTimeout(__bind(function() {
97
124
  if (this.state === 'clicked') {
98
125
  this.state = 'default';
99
- return this.owner.sendResponse(true);
126
+ return this.sendResponse(true);
100
127
  }
101
128
  }, this), 10);
102
129
  };
103
- Browser.prototype.drag = function(id, other_id) {
104
- this.page.get(id).dragTo(this.page.get(other_id));
105
- return this.owner.sendResponse(true);
130
+ Browser.prototype.drag = function(page_id, id, other_id) {
131
+ this.node(page_id, id).dragTo(this.page.get(other_id));
132
+ return this.sendResponse(true);
106
133
  };
107
- Browser.prototype.trigger = function(id, event) {
108
- this.page.get(id).trigger(event);
109
- return this.owner.sendResponse(event);
134
+ Browser.prototype.trigger = function(page_id, id, event) {
135
+ this.node(page_id, id).trigger(event);
136
+ return this.sendResponse(event);
110
137
  };
111
138
  Browser.prototype.reset = function() {
112
139
  this.resetPage();
113
- return this.owner.sendResponse(true);
140
+ return this.sendResponse(true);
114
141
  };
115
142
  Browser.prototype.render = function(path, full) {
116
143
  var dimensions, document, viewport;
@@ -142,14 +169,14 @@ Poltergeist.Browser = (function() {
142
169
  });
143
170
  this.page.render(path);
144
171
  }
145
- return this.owner.sendResponse(true);
172
+ return this.sendResponse(true);
146
173
  };
147
174
  Browser.prototype.resize = function(width, height) {
148
175
  this.page.setViewportSize({
149
176
  width: width,
150
177
  height: height
151
178
  });
152
- return this.owner.sendResponse(true);
179
+ return this.sendResponse(true);
153
180
  };
154
181
  Browser.prototype.exit = function() {
155
182
  return phantom.exit();
@@ -8,9 +8,7 @@ Poltergeist = (function() {
8
8
  try {
9
9
  return this.browser[command.name].apply(this.browser, command.args);
10
10
  } catch (error) {
11
- return this.connection.send({
12
- error: error.toString()
13
- });
11
+ return this.sendError(error);
14
12
  }
15
13
  };
16
14
  Poltergeist.prototype.sendResponse = function(response) {
@@ -18,15 +16,46 @@ Poltergeist = (function() {
18
16
  response: response
19
17
  });
20
18
  };
19
+ Poltergeist.prototype.sendError = function(error) {
20
+ return this.connection.send({
21
+ error: {
22
+ name: error.name || 'Generic',
23
+ args: error.args && error.args() || [error.toString()]
24
+ }
25
+ });
26
+ };
21
27
  return Poltergeist;
22
28
  })();
29
+ window.Poltergeist = Poltergeist;
23
30
  Poltergeist.ObsoleteNode = (function() {
24
31
  function ObsoleteNode() {}
25
- ObsoleteNode.prototype.toString = function() {
26
- return "Poltergeist.ObsoleteNode";
32
+ ObsoleteNode.prototype.name = "Poltergeist.ObsoleteNode";
33
+ ObsoleteNode.prototype.args = function() {
34
+ return [];
27
35
  };
28
36
  return ObsoleteNode;
29
37
  })();
38
+ Poltergeist.ClickFailed = (function() {
39
+ function ClickFailed(selector, position) {
40
+ this.selector = selector;
41
+ this.position = position;
42
+ }
43
+ ClickFailed.prototype.name = "Poltergeist.ClickFailed";
44
+ ClickFailed.prototype.args = function() {
45
+ return [this.selector, this.position];
46
+ };
47
+ return ClickFailed;
48
+ })();
49
+ Poltergeist.JavascriptError = (function() {
50
+ function JavascriptError(errors) {
51
+ this.errors = errors;
52
+ }
53
+ JavascriptError.prototype.name = "Poltergeist.JavascriptError";
54
+ JavascriptError.prototype.args = function() {
55
+ return [this.errors];
56
+ };
57
+ return JavascriptError;
58
+ })();
30
59
  phantom.injectJs('web_page.js');
31
60
  phantom.injectJs('node.js');
32
61
  phantom.injectJs('connection.js');
@@ -1,7 +1,7 @@
1
1
  var __slice = Array.prototype.slice, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
2
2
  Poltergeist.Node = (function() {
3
3
  var name, _fn, _i, _len, _ref;
4
- Node.DELEGATES = ['text', 'getAttribute', 'value', 'set', 'setAttribute', 'removeAttribute', 'isMultiple', 'select', 'tagName', 'isVisible', 'position', 'trigger', 'parentId'];
4
+ Node.DELEGATES = ['text', 'getAttribute', 'value', 'set', 'setAttribute', 'removeAttribute', 'isMultiple', 'select', 'tagName', 'find', 'isVisible', 'position', 'trigger', 'parentId', 'clickTest'];
5
5
  function Node(page, id) {
6
6
  this.page = page;
7
7
  this.id = id;
@@ -9,27 +9,23 @@ Poltergeist.Node = (function() {
9
9
  Node.prototype.parent = function() {
10
10
  return new Poltergeist.Node(this.page, this.parentId());
11
11
  };
12
- Node.prototype.isObsolete = function() {
13
- return this.page.nodeCall(this.id, 'isObsolete');
14
- };
15
12
  _ref = Node.DELEGATES;
16
13
  _fn = __bind(function(name) {
17
14
  return this.prototype[name] = function() {
18
15
  var arguments, _ref2;
19
16
  _ref2 = arguments, arguments = 1 <= _ref2.length ? __slice.call(_ref2, 0) : [];
20
- if (this.isObsolete()) {
21
- throw new Poltergeist.ObsoleteNode;
22
- } else {
23
- return this.page.nodeCall(this.id, name, arguments);
24
- }
17
+ return this.page.nodeCall(this.id, name, arguments);
25
18
  };
26
19
  }, Node);
27
20
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
28
21
  name = _ref[_i];
29
22
  _fn(name);
30
23
  }
31
- Node.prototype.scrollIntoView = function() {
32
- var adjust, dimensions, document, pos, scroll, viewport;
24
+ Node.prototype.clickPosition = function(scrollIntoView) {
25
+ var adjust, dimensions, document, middle, pos, scroll, viewport;
26
+ if (scrollIntoView == null) {
27
+ scrollIntoView = true;
28
+ }
33
29
  dimensions = this.page.validatedDimensions();
34
30
  document = dimensions.document;
35
31
  viewport = dimensions.viewport;
@@ -45,26 +41,39 @@ Poltergeist.Node = (function() {
45
41
  return scroll[coord] = Math.min(document[measurement] - viewport[measurement], scroll[coord] + pos[coord] - viewport[measurement] + (viewport[measurement] / 2));
46
42
  }
47
43
  };
48
- adjust('left', 'width');
49
- adjust('top', 'height');
50
- if (scroll.left !== dimensions.left || scroll.top !== dimensions.top) {
51
- this.page.setScrollPosition(scroll);
52
- pos = this.position();
44
+ if (scrollIntoView) {
45
+ adjust('left', 'width');
46
+ adjust('top', 'height');
47
+ if (scroll.left !== dimensions.left || scroll.top !== dimensions.top) {
48
+ this.page.setScrollPosition(scroll);
49
+ pos = this.position();
50
+ }
53
51
  }
54
- return pos;
52
+ middle = function(start, end, size) {
53
+ return start + ((Math.min(end, size) - start) / 2);
54
+ };
55
+ return {
56
+ x: middle(pos.left, pos.right, viewport.width),
57
+ y: middle(pos.top, pos.bottom, viewport.height)
58
+ };
55
59
  };
56
60
  Node.prototype.click = function() {
57
- var position;
58
- position = this.scrollIntoView();
59
- return this.page.sendEvent('click', position.left, position.top);
61
+ var pos, test;
62
+ pos = this.clickPosition();
63
+ test = this.clickTest(pos.x, pos.y);
64
+ if (test.status === 'success') {
65
+ return this.page.sendEvent('click', pos.x, pos.y);
66
+ } else {
67
+ throw new Poltergeist.ClickFailed(test.selector, pos);
68
+ }
60
69
  };
61
70
  Node.prototype.dragTo = function(other) {
62
71
  var otherPosition, position;
63
- position = this.scrollIntoView();
64
- otherPosition = other.position();
65
- this.page.sendEvent('mousedown', position.left, position.top);
66
- this.page.sendEvent('mousemove', otherPosition.left, otherPosition.top);
67
- return this.page.sendEvent('mouseup', otherPosition.left, otherPosition.top);
72
+ position = this.clickPosition();
73
+ otherPosition = other.clickPosition(false);
74
+ this.page.sendEvent('mousedown', position.x, position.y);
75
+ this.page.sendEvent('mousemove', otherPosition.x, otherPosition.y);
76
+ return this.page.sendEvent('mouseup', otherPosition.x, otherPosition.y);
68
77
  };
69
78
  return Node;
70
79
  }).call(this);