-
Notifications
You must be signed in to change notification settings - Fork 64
/
web_driver.dart
252 lines (209 loc) · 6.72 KB
/
web_driver.dart
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
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'dart:convert' show base64;
import 'package:stack_trace/stack_trace.dart' show Chain;
import 'element_finder.dart';
import 'keyboard.dart';
import 'mouse.dart';
import 'navigation.dart';
import 'target_locator.dart';
import 'timeouts.dart';
import 'web_element.dart';
import 'window.dart';
import '../../../async_core.dart' as async_core;
import '../common_spec/cookies.dart';
import '../keyboard.dart';
import '../mouse.dart';
// We don't implement this, but we need the types to define the API.
import '../json_wire_spec/logs.dart';
import '../command_event.dart';
import '../command_processor.dart';
import '../common.dart';
import '../navigation.dart';
import '../target_locator.dart';
import '../timeouts.dart';
import '../web_driver.dart';
import '../web_element.dart';
import '../window.dart';
class W3cWebDriver implements WebDriver, SearchContext {
final CommandProcessor _commandProcessor;
final Uri _prefix;
@override
final Map<String, dynamic> capabilities;
@override
final String id;
@override
final Uri uri;
@override
final bool filterStackTraces;
ElementFinder _finder;
@override
bool notifyListeners = true;
final _commandListeners = <WebDriverListener>[];
W3cWebDriver(this._commandProcessor, this.uri, this.id, this.capabilities,
{this.filterStackTraces: true})
: this._prefix = uri.resolve('session/$id/') {
_finder = new ElementFinder(this, new Resolver(driver, ''), this);
}
@override
async_core.WebDriver get asyncDriver => createAsyncWebDriver(this);
@override
async_core.SearchContext get asyncContext => asyncDriver;
@override
void addEventListener(WebDriverListener listener) =>
_commandListeners.add(listener);
@override
String get currentUrl => getRequest('url') as String;
@override
void get(/* Uri | String */ url) {
final urlStr = (url is Uri) ? url.toString() : url;
postRequest('url', {'url': urlStr as String});
}
@override
String get title => getRequest('title') as String;
@override
List<WebElement> findElements(By by) => _finder.findElements(by);
@override
WebElement findElement(By by) => _finder.findElement(by);
@override
String get pageSource => getRequest('source') as String;
@override
void close() {
deleteRequest('window');
}
@override
void quit({bool closeSession: true}) {
try {
if (closeSession) {
_commandProcessor.delete(uri.resolve('session/$id'));
}
} finally {
_commandProcessor.close();
}
}
@override
List<Window> get windows => new W3cWindows(this).allWindows;
@override
Window get window => new W3cWindows(this).activeWindow;
@override
WebElement get activeElement => _finder.findActiveElement();
@override
Windows get windowsManager => new W3cWindows(this);
@override
TargetLocator get switchTo => new W3cTargetLocator(this);
@override
Navigation get navigate => new W3cNavigation(this);
@override
Cookies get cookies => new Cookies(this);
@override
Timeouts get timeouts => new W3cTimeouts(this);
//TODO(staats): better exceptions. Also I should probably write an Action API.
@override
Logs get logs => throw 'Unsupported in W3C spec.';
@override
Keyboard get keyboard => new W3cKeyboard(this);
@override
Mouse get mouse => new W3cMouse(this);
@override
String captureScreenshotAsBase64() => getRequest('screenshot');
@override
List<int> captureScreenshotAsList() {
final base64Encoded = captureScreenshotAsBase64();
return base64.decode(base64Encoded);
}
@override
dynamic executeAsync(String script, List args) => _recursiveElementify(
postRequest('execute/async', {'script': script, 'args': args}));
@override
dynamic execute(String script, List args) => _recursiveElementify(
postRequest('execute/sync', {'script': script, 'args': args}));
dynamic _recursiveElementify(result) {
if (result is Map) {
if (result.containsKey(jsonWireElementStr)) {
return new W3cWebElement(
this, result[jsonWireElementStr], this, 'javascript');
} else {
final newResult = {};
result.forEach((key, value) {
newResult[key] = _recursiveElementify(value);
});
return newResult;
}
} else if (result is List) {
return result.map(_recursiveElementify).toList();
} else {
return result;
}
}
@override
dynamic postRequest(String command, [params]) => _performRequestWithLog(
() => _commandProcessor.post(_resolve(command), params),
'POST',
command,
params);
@override
dynamic getRequest(String command) => _performRequestWithLog(
() => _commandProcessor.get(_resolve(command)), 'GET', command, null);
@override
dynamic deleteRequest(String command) => _performRequestWithLog(
() => _commandProcessor.delete(_resolve(command)),
'DELETE',
command,
null);
// Performs request and sends the result to listeners/onCommandController.
dynamic _performRequestWithLog(
Function fn, String method, String command, params) {
final startTime = new DateTime.now();
var trace = new Chain.current();
if (filterStackTraces) {
trace = trace.foldFrames(
(f) => f.library.startsWith('package:webdriver/'),
terse: true);
}
Object result;
Object exception;
try {
result = fn();
return result;
} catch (e) {
exception = e;
rethrow;
} finally {
if (notifyListeners) {
for (WebDriverListener listener in _commandListeners) {
listener(new WebDriverCommandEvent(
method: method,
endPoint: command,
params: params,
startTime: startTime,
endTime: new DateTime.now(),
exception: exception,
result: result,
stackTrace: trace));
}
}
}
}
Uri _resolve(String command) {
var uri = _prefix.resolve(command);
if (uri.path.endsWith('/')) {
uri = uri.replace(path: uri.path.substring(0, uri.path.length - 1));
}
return uri;
}
@override
WebDriver get driver => this;
@override
String toString() => 'W3cWebDriver($_prefix)';
}