undead 0.1.1 → 0.2.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.
- checksums.yaml +4 -4
- data/lib/undead.rb +1 -2
- data/lib/undead/agent.rb +6 -11
- data/lib/undead/browser.rb +68 -0
- data/lib/undead/client.rb +151 -0
- data/lib/undead/client/compiled/agent.js +594 -0
- data/lib/undead/client/compiled/browser.js +692 -0
- data/lib/undead/client/compiled/cmd.js +31 -0
- data/lib/undead/client/compiled/connection.js +25 -0
- data/lib/undead/client/compiled/main.js +229 -0
- data/lib/undead/client/compiled/node.js +187 -0
- data/lib/undead/client/compiled/web_page.js +589 -0
- data/lib/undead/command.rb +20 -0
- data/lib/undead/driver.rb +73 -0
- data/lib/undead/errors.rb +187 -0
- data/lib/undead/server.rb +44 -0
- data/lib/undead/utility.rb +7 -0
- data/lib/undead/version.rb +1 -1
- data/lib/undead/web_socket_server.rb +109 -0
- data/undead.gemspec +4 -4
- metadata +27 -11
@@ -0,0 +1,31 @@
|
|
1
|
+
Poltergeist.Cmd = (function() {
|
2
|
+
function Cmd(owner, id, name, args) {
|
3
|
+
this.owner = owner;
|
4
|
+
this.id = id;
|
5
|
+
this.name = name;
|
6
|
+
this.args = args;
|
7
|
+
}
|
8
|
+
|
9
|
+
Cmd.prototype.sendResponse = function(response) {
|
10
|
+
var errors;
|
11
|
+
errors = this.browser.currentPage.errors;
|
12
|
+
this.browser.currentPage.clearErrors();
|
13
|
+
if (errors.length > 0 && this.browser.js_errors) {
|
14
|
+
return this.sendError(new Poltergeist.JavascriptError(errors));
|
15
|
+
} else {
|
16
|
+
return this.owner.sendResponse(this.id, response);
|
17
|
+
}
|
18
|
+
};
|
19
|
+
|
20
|
+
Cmd.prototype.sendError = function(errors) {
|
21
|
+
return this.owner.sendError(this.id, errors);
|
22
|
+
};
|
23
|
+
|
24
|
+
Cmd.prototype.run = function(browser) {
|
25
|
+
this.browser = browser;
|
26
|
+
return this.browser.runCommand(this);
|
27
|
+
};
|
28
|
+
|
29
|
+
return Cmd;
|
30
|
+
|
31
|
+
})();
|
@@ -0,0 +1,25 @@
|
|
1
|
+
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
2
|
+
|
3
|
+
Poltergeist.Connection = (function() {
|
4
|
+
function Connection(owner, port) {
|
5
|
+
this.owner = owner;
|
6
|
+
this.port = port;
|
7
|
+
this.commandReceived = bind(this.commandReceived, this);
|
8
|
+
this.socket = new WebSocket("ws://127.0.0.1:" + this.port + "/");
|
9
|
+
this.socket.onmessage = this.commandReceived;
|
10
|
+
this.socket.onclose = function() {
|
11
|
+
return phantom.exit();
|
12
|
+
};
|
13
|
+
}
|
14
|
+
|
15
|
+
Connection.prototype.commandReceived = function(message) {
|
16
|
+
return this.owner.runCommand(JSON.parse(message.data));
|
17
|
+
};
|
18
|
+
|
19
|
+
Connection.prototype.send = function(message) {
|
20
|
+
return this.socket.send(JSON.stringify(message));
|
21
|
+
};
|
22
|
+
|
23
|
+
return Connection;
|
24
|
+
|
25
|
+
})();
|
@@ -0,0 +1,229 @@
|
|
1
|
+
var Poltergeist, system,
|
2
|
+
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
3
|
+
hasProp = {}.hasOwnProperty;
|
4
|
+
|
5
|
+
Poltergeist = (function() {
|
6
|
+
function Poltergeist(port, width, height) {
|
7
|
+
var that;
|
8
|
+
this.browser = new Poltergeist.Browser(width, height);
|
9
|
+
this.connection = new Poltergeist.Connection(this, port);
|
10
|
+
that = this;
|
11
|
+
phantom.onError = function(message, stack) {
|
12
|
+
return that.onError(message, stack);
|
13
|
+
};
|
14
|
+
this.running = false;
|
15
|
+
}
|
16
|
+
|
17
|
+
Poltergeist.prototype.runCommand = function(command) {
|
18
|
+
var error, error1;
|
19
|
+
this.running = true;
|
20
|
+
command = new Poltergeist.Cmd(this, command.id, command.name, command.args);
|
21
|
+
try {
|
22
|
+
return command.run(this.browser);
|
23
|
+
} catch (error1) {
|
24
|
+
error = error1;
|
25
|
+
if (error instanceof Poltergeist.Error) {
|
26
|
+
return this.sendError(command.id, error);
|
27
|
+
} else {
|
28
|
+
return this.sendError(command.id, new Poltergeist.BrowserError(error.toString(), error.stack));
|
29
|
+
}
|
30
|
+
}
|
31
|
+
};
|
32
|
+
|
33
|
+
Poltergeist.prototype.sendResponse = function(command_id, response) {
|
34
|
+
return this.send({
|
35
|
+
command_id: command_id,
|
36
|
+
response: response
|
37
|
+
});
|
38
|
+
};
|
39
|
+
|
40
|
+
Poltergeist.prototype.sendError = function(command_id, error) {
|
41
|
+
return this.send({
|
42
|
+
command_id: command_id,
|
43
|
+
error: {
|
44
|
+
name: error.name || 'Generic',
|
45
|
+
args: error.args && error.args() || [error.toString()]
|
46
|
+
}
|
47
|
+
});
|
48
|
+
};
|
49
|
+
|
50
|
+
Poltergeist.prototype.send = function(data) {
|
51
|
+
if (this.running) {
|
52
|
+
this.connection.send(data);
|
53
|
+
this.running = false;
|
54
|
+
return true;
|
55
|
+
}
|
56
|
+
return false;
|
57
|
+
};
|
58
|
+
|
59
|
+
return Poltergeist;
|
60
|
+
|
61
|
+
})();
|
62
|
+
|
63
|
+
window.Poltergeist = Poltergeist;
|
64
|
+
|
65
|
+
Poltergeist.Error = (function() {
|
66
|
+
function Error() {}
|
67
|
+
|
68
|
+
return Error;
|
69
|
+
|
70
|
+
})();
|
71
|
+
|
72
|
+
Poltergeist.ObsoleteNode = (function(superClass) {
|
73
|
+
extend(ObsoleteNode, superClass);
|
74
|
+
|
75
|
+
function ObsoleteNode() {
|
76
|
+
return ObsoleteNode.__super__.constructor.apply(this, arguments);
|
77
|
+
}
|
78
|
+
|
79
|
+
ObsoleteNode.prototype.name = "Poltergeist.ObsoleteNode";
|
80
|
+
|
81
|
+
ObsoleteNode.prototype.args = function() {
|
82
|
+
return [];
|
83
|
+
};
|
84
|
+
|
85
|
+
ObsoleteNode.prototype.toString = function() {
|
86
|
+
return this.name;
|
87
|
+
};
|
88
|
+
|
89
|
+
return ObsoleteNode;
|
90
|
+
|
91
|
+
})(Poltergeist.Error);
|
92
|
+
|
93
|
+
Poltergeist.InvalidSelector = (function(superClass) {
|
94
|
+
extend(InvalidSelector, superClass);
|
95
|
+
|
96
|
+
function InvalidSelector(method, selector) {
|
97
|
+
this.method = method;
|
98
|
+
this.selector = selector;
|
99
|
+
}
|
100
|
+
|
101
|
+
InvalidSelector.prototype.name = "Poltergeist.InvalidSelector";
|
102
|
+
|
103
|
+
InvalidSelector.prototype.args = function() {
|
104
|
+
return [this.method, this.selector];
|
105
|
+
};
|
106
|
+
|
107
|
+
return InvalidSelector;
|
108
|
+
|
109
|
+
})(Poltergeist.Error);
|
110
|
+
|
111
|
+
Poltergeist.FrameNotFound = (function(superClass) {
|
112
|
+
extend(FrameNotFound, superClass);
|
113
|
+
|
114
|
+
function FrameNotFound(frameName) {
|
115
|
+
this.frameName = frameName;
|
116
|
+
}
|
117
|
+
|
118
|
+
FrameNotFound.prototype.name = "Poltergeist.FrameNotFound";
|
119
|
+
|
120
|
+
FrameNotFound.prototype.args = function() {
|
121
|
+
return [this.frameName];
|
122
|
+
};
|
123
|
+
|
124
|
+
return FrameNotFound;
|
125
|
+
|
126
|
+
})(Poltergeist.Error);
|
127
|
+
|
128
|
+
Poltergeist.MouseEventFailed = (function(superClass) {
|
129
|
+
extend(MouseEventFailed, superClass);
|
130
|
+
|
131
|
+
function MouseEventFailed(eventName, selector, position) {
|
132
|
+
this.eventName = eventName;
|
133
|
+
this.selector = selector;
|
134
|
+
this.position = position;
|
135
|
+
}
|
136
|
+
|
137
|
+
MouseEventFailed.prototype.name = "Poltergeist.MouseEventFailed";
|
138
|
+
|
139
|
+
MouseEventFailed.prototype.args = function() {
|
140
|
+
return [this.eventName, this.selector, this.position];
|
141
|
+
};
|
142
|
+
|
143
|
+
return MouseEventFailed;
|
144
|
+
|
145
|
+
})(Poltergeist.Error);
|
146
|
+
|
147
|
+
Poltergeist.JavascriptError = (function(superClass) {
|
148
|
+
extend(JavascriptError, superClass);
|
149
|
+
|
150
|
+
function JavascriptError(errors) {
|
151
|
+
this.errors = errors;
|
152
|
+
}
|
153
|
+
|
154
|
+
JavascriptError.prototype.name = "Poltergeist.JavascriptError";
|
155
|
+
|
156
|
+
JavascriptError.prototype.args = function() {
|
157
|
+
return [this.errors];
|
158
|
+
};
|
159
|
+
|
160
|
+
return JavascriptError;
|
161
|
+
|
162
|
+
})(Poltergeist.Error);
|
163
|
+
|
164
|
+
Poltergeist.BrowserError = (function(superClass) {
|
165
|
+
extend(BrowserError, superClass);
|
166
|
+
|
167
|
+
function BrowserError(message1, stack1) {
|
168
|
+
this.message = message1;
|
169
|
+
this.stack = stack1;
|
170
|
+
}
|
171
|
+
|
172
|
+
BrowserError.prototype.name = "Poltergeist.BrowserError";
|
173
|
+
|
174
|
+
BrowserError.prototype.args = function() {
|
175
|
+
return [this.message, this.stack];
|
176
|
+
};
|
177
|
+
|
178
|
+
return BrowserError;
|
179
|
+
|
180
|
+
})(Poltergeist.Error);
|
181
|
+
|
182
|
+
Poltergeist.StatusFailError = (function(superClass) {
|
183
|
+
extend(StatusFailError, superClass);
|
184
|
+
|
185
|
+
function StatusFailError(url, details) {
|
186
|
+
this.url = url;
|
187
|
+
this.details = details;
|
188
|
+
}
|
189
|
+
|
190
|
+
StatusFailError.prototype.name = "Poltergeist.StatusFailError";
|
191
|
+
|
192
|
+
StatusFailError.prototype.args = function() {
|
193
|
+
return [this.url, this.details];
|
194
|
+
};
|
195
|
+
|
196
|
+
return StatusFailError;
|
197
|
+
|
198
|
+
})(Poltergeist.Error);
|
199
|
+
|
200
|
+
Poltergeist.NoSuchWindowError = (function(superClass) {
|
201
|
+
extend(NoSuchWindowError, superClass);
|
202
|
+
|
203
|
+
function NoSuchWindowError() {
|
204
|
+
return NoSuchWindowError.__super__.constructor.apply(this, arguments);
|
205
|
+
}
|
206
|
+
|
207
|
+
NoSuchWindowError.prototype.name = "Poltergeist.NoSuchWindowError";
|
208
|
+
|
209
|
+
NoSuchWindowError.prototype.args = function() {
|
210
|
+
return [];
|
211
|
+
};
|
212
|
+
|
213
|
+
return NoSuchWindowError;
|
214
|
+
|
215
|
+
})(Poltergeist.Error);
|
216
|
+
|
217
|
+
phantom.injectJs(phantom.libraryPath + "/web_page.js");
|
218
|
+
|
219
|
+
phantom.injectJs(phantom.libraryPath + "/node.js");
|
220
|
+
|
221
|
+
phantom.injectJs(phantom.libraryPath + "/connection.js");
|
222
|
+
|
223
|
+
phantom.injectJs(phantom.libraryPath + "/cmd.js");
|
224
|
+
|
225
|
+
phantom.injectJs(phantom.libraryPath + "/browser.js");
|
226
|
+
|
227
|
+
system = require('system');
|
228
|
+
|
229
|
+
new Poltergeist(system.args[1], system.args[2], system.args[3]);
|
@@ -0,0 +1,187 @@
|
|
1
|
+
var slice = [].slice;
|
2
|
+
|
3
|
+
Poltergeist.Node = (function() {
|
4
|
+
var fn, j, len, name, ref;
|
5
|
+
|
6
|
+
Node.DELEGATES = ['allText', 'visibleText', 'getAttribute', 'value', 'set', 'setAttribute', 'isObsolete', 'removeAttribute', 'isMultiple', 'select', 'tagName', 'find', 'getAttributes', 'isVisible', 'isInViewport', 'position', 'trigger', 'parentId', 'parentIds', 'mouseEventTest', 'scrollIntoView', 'isDOMEqual', 'isDisabled', 'deleteText', 'containsSelection', 'path', 'getProperty'];
|
7
|
+
|
8
|
+
function Node(page, id) {
|
9
|
+
this.page = page;
|
10
|
+
this.id = id;
|
11
|
+
}
|
12
|
+
|
13
|
+
Node.prototype.parent = function() {
|
14
|
+
return new Poltergeist.Node(this.page, this.parentId());
|
15
|
+
};
|
16
|
+
|
17
|
+
ref = Node.DELEGATES;
|
18
|
+
fn = function(name) {
|
19
|
+
return Node.prototype[name] = function() {
|
20
|
+
var args;
|
21
|
+
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
|
22
|
+
return this.page.nodeCall(this.id, name, args);
|
23
|
+
};
|
24
|
+
};
|
25
|
+
for (j = 0, len = ref.length; j < len; j++) {
|
26
|
+
name = ref[j];
|
27
|
+
fn(name);
|
28
|
+
}
|
29
|
+
|
30
|
+
Node.prototype.mouseEventPosition = function() {
|
31
|
+
var area_offset, image, middle, pos, res, viewport;
|
32
|
+
viewport = this.page.viewportSize();
|
33
|
+
if (image = this._getAreaImage()) {
|
34
|
+
pos = image.position();
|
35
|
+
if (area_offset = this._getAreaOffsetRect()) {
|
36
|
+
pos.left = pos.left + area_offset.x;
|
37
|
+
pos.right = pos.left + area_offset.width;
|
38
|
+
pos.top = pos.top + area_offset.y;
|
39
|
+
pos.bottom = pos.top + area_offset.height;
|
40
|
+
}
|
41
|
+
} else {
|
42
|
+
pos = this.position();
|
43
|
+
}
|
44
|
+
middle = function(start, end, size) {
|
45
|
+
return start + ((Math.min(end, size) - start) / 2);
|
46
|
+
};
|
47
|
+
return res = {
|
48
|
+
x: middle(pos.left, pos.right, viewport.width),
|
49
|
+
y: middle(pos.top, pos.bottom, viewport.height)
|
50
|
+
};
|
51
|
+
};
|
52
|
+
|
53
|
+
Node.prototype.mouseEvent = function(name) {
|
54
|
+
var area_image, pos, test;
|
55
|
+
if (area_image = this._getAreaImage()) {
|
56
|
+
area_image.scrollIntoView();
|
57
|
+
} else {
|
58
|
+
this.scrollIntoView();
|
59
|
+
}
|
60
|
+
pos = this.mouseEventPosition();
|
61
|
+
test = this.mouseEventTest(pos.x, pos.y);
|
62
|
+
if (test.status === 'success') {
|
63
|
+
if (name === 'rightclick') {
|
64
|
+
this.page.mouseEvent('click', pos.x, pos.y, 'right');
|
65
|
+
this.trigger('contextmenu');
|
66
|
+
} else {
|
67
|
+
this.page.mouseEvent(name, pos.x, pos.y);
|
68
|
+
}
|
69
|
+
return pos;
|
70
|
+
} else {
|
71
|
+
throw new Poltergeist.MouseEventFailed(name, test.selector, pos);
|
72
|
+
}
|
73
|
+
};
|
74
|
+
|
75
|
+
Node.prototype.dragTo = function(other) {
|
76
|
+
var otherPosition, position;
|
77
|
+
this.scrollIntoView();
|
78
|
+
position = this.mouseEventPosition();
|
79
|
+
otherPosition = other.mouseEventPosition();
|
80
|
+
this.page.mouseEvent('mousedown', position.x, position.y);
|
81
|
+
return this.page.mouseEvent('mouseup', otherPosition.x, otherPosition.y);
|
82
|
+
};
|
83
|
+
|
84
|
+
Node.prototype.dragBy = function(x, y) {
|
85
|
+
var final_pos, position;
|
86
|
+
this.scrollIntoView();
|
87
|
+
position = this.mouseEventPosition();
|
88
|
+
final_pos = {
|
89
|
+
x: position.x + x,
|
90
|
+
y: position.y + y
|
91
|
+
};
|
92
|
+
this.page.mouseEvent('mousedown', position.x, position.y);
|
93
|
+
return this.page.mouseEvent('mouseup', final_pos.x, final_pos.y);
|
94
|
+
};
|
95
|
+
|
96
|
+
Node.prototype.isEqual = function(other) {
|
97
|
+
return this.page === other.page && this.isDOMEqual(other.id);
|
98
|
+
};
|
99
|
+
|
100
|
+
Node.prototype._getAreaOffsetRect = function() {
|
101
|
+
var centerX, centerY, coord, coords, i, maxX, maxY, minX, minY, radius, rect, shape, x, xs, y, ys;
|
102
|
+
shape = this.getAttribute('shape').toLowerCase();
|
103
|
+
coords = (function() {
|
104
|
+
var k, len1, ref1, results;
|
105
|
+
ref1 = this.getAttribute('coords').split(',');
|
106
|
+
results = [];
|
107
|
+
for (k = 0, len1 = ref1.length; k < len1; k++) {
|
108
|
+
coord = ref1[k];
|
109
|
+
results.push(parseInt(coord, 10));
|
110
|
+
}
|
111
|
+
return results;
|
112
|
+
}).call(this);
|
113
|
+
return rect = (function() {
|
114
|
+
switch (shape) {
|
115
|
+
case 'rect':
|
116
|
+
case 'rectangle':
|
117
|
+
x = coords[0], y = coords[1];
|
118
|
+
return {
|
119
|
+
x: x,
|
120
|
+
y: y,
|
121
|
+
width: coords[2] - x,
|
122
|
+
height: coords[3] - y
|
123
|
+
};
|
124
|
+
case 'circ':
|
125
|
+
case 'circle':
|
126
|
+
centerX = coords[0], centerY = coords[1], radius = coords[2];
|
127
|
+
return {
|
128
|
+
x: centerX - radius,
|
129
|
+
y: centerY - radius,
|
130
|
+
width: 2 * radius,
|
131
|
+
height: 2 * radius
|
132
|
+
};
|
133
|
+
case 'poly':
|
134
|
+
case 'polygon':
|
135
|
+
xs = (function() {
|
136
|
+
var k, ref1, results;
|
137
|
+
results = [];
|
138
|
+
for (i = k = 0, ref1 = coords.length; k < ref1; i = k += 2) {
|
139
|
+
results.push(coords[i]);
|
140
|
+
}
|
141
|
+
return results;
|
142
|
+
})();
|
143
|
+
ys = (function() {
|
144
|
+
var k, ref1, results;
|
145
|
+
results = [];
|
146
|
+
for (i = k = 1, ref1 = coords.length; k < ref1; i = k += 2) {
|
147
|
+
results.push(coords[i]);
|
148
|
+
}
|
149
|
+
return results;
|
150
|
+
})();
|
151
|
+
minX = Math.min.apply(Math, xs);
|
152
|
+
maxX = Math.max.apply(Math, xs);
|
153
|
+
minY = Math.min.apply(Math, ys);
|
154
|
+
maxY = Math.max.apply(Math, ys);
|
155
|
+
return {
|
156
|
+
x: minX,
|
157
|
+
y: minY,
|
158
|
+
width: maxX - minX,
|
159
|
+
height: maxY - minY
|
160
|
+
};
|
161
|
+
}
|
162
|
+
})();
|
163
|
+
};
|
164
|
+
|
165
|
+
Node.prototype._getAreaImage = function() {
|
166
|
+
var image_node_id, map, mapName;
|
167
|
+
if ('area' === this.tagName().toLowerCase()) {
|
168
|
+
map = this.parent();
|
169
|
+
if (map.tagName().toLowerCase() !== 'map') {
|
170
|
+
throw new Error('the area is not within a map');
|
171
|
+
}
|
172
|
+
mapName = map.getAttribute('name');
|
173
|
+
if (mapName == null) {
|
174
|
+
throw new Error("area's parent map must have a name");
|
175
|
+
}
|
176
|
+
mapName = '#' + mapName.toLowerCase();
|
177
|
+
image_node_id = this.page.find('css', "img[usemap='" + mapName + "']")[0];
|
178
|
+
if (image_node_id == null) {
|
179
|
+
throw new Error("no image matches the map");
|
180
|
+
}
|
181
|
+
return this.page.get(image_node_id);
|
182
|
+
}
|
183
|
+
};
|
184
|
+
|
185
|
+
return Node;
|
186
|
+
|
187
|
+
})();
|