selenium-webdriver 3.6.0 → 4.0.0-alpha.1

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.
Files changed (97) hide show
  1. package/CHANGES.md +181 -0
  2. package/README.md +25 -27
  3. package/chrome.js +113 -127
  4. package/edge.js +42 -117
  5. package/example/chrome_android.js +8 -8
  6. package/example/chrome_mobile_emulation.js +8 -8
  7. package/example/firefox_channels.js +22 -18
  8. package/example/google_search_test.js +39 -26
  9. package/example/logging.js +39 -6
  10. package/firefox.js +769 -0
  11. package/http/index.js +41 -4
  12. package/http/util.js +11 -6
  13. package/ie.js +56 -97
  14. package/index.js +178 -114
  15. package/jasmine.json +11 -0
  16. package/lib/actions.js +68 -63
  17. package/lib/by.js +1 -1
  18. package/lib/capabilities.js +255 -192
  19. package/lib/command.js +26 -50
  20. package/lib/error.js +85 -44
  21. package/lib/http.js +133 -110
  22. package/lib/input.js +1044 -8
  23. package/lib/promise.js +222 -3323
  24. package/lib/proxy.js +134 -39
  25. package/lib/test/bootstrap_jasmine.js +28 -0
  26. package/lib/test/data/actions/click.html +24 -0
  27. package/lib/test/data/actions/drag.html +77 -0
  28. package/lib/test/data/actions/record_click.html +21 -0
  29. package/lib/test/data/chrome/download.html +2 -0
  30. package/lib/test/data/inputs.html +42 -0
  31. package/lib/test/data/selectPage.html +12 -0
  32. package/lib/test/data/simpleTest.html +5 -0
  33. package/lib/test/data/upload_invisible.html +45 -0
  34. package/lib/test/fileserver.js +23 -19
  35. package/lib/test/index.js +30 -212
  36. package/lib/test/resources.js +0 -1
  37. package/lib/webdriver.js +675 -936
  38. package/net/index.js +21 -25
  39. package/net/portprober.js +51 -68
  40. package/package.json +5 -3
  41. package/remote/index.js +5 -19
  42. package/safari.js +41 -128
  43. package/test/actions_test.js +173 -18
  44. package/test/{session_test.js → builder_test.js} +44 -37
  45. package/test/chrome/devtools_test.js +93 -0
  46. package/test/chrome/options_test.js +48 -154
  47. package/test/chrome/service_test.js +7 -7
  48. package/test/cookie_test.js +77 -63
  49. package/test/element_finding_test.js +206 -201
  50. package/test/execute_script_test.js +115 -114
  51. package/test/fingerprint_test.js +16 -15
  52. package/test/firefox_test.js +251 -0
  53. package/test/http/http_test.js +0 -1
  54. package/test/http/util_test.js +2 -2
  55. package/test/io/zip_test.js +6 -7
  56. package/test/lib/error_test.js +34 -7
  57. package/test/lib/http_test.js +8 -128
  58. package/test/lib/input_test.js +1379 -0
  59. package/test/lib/promise_test.js +473 -908
  60. package/test/lib/webdriver_test.js +296 -868
  61. package/test/logging_test.js +37 -43
  62. package/test/page_loading_test.js +71 -81
  63. package/test/proxy_test.js +64 -72
  64. package/test/rect_test.js +14 -25
  65. package/test/remote_test.js +10 -29
  66. package/test/safari_test.js +7 -71
  67. package/test/stale_element_test.js +20 -23
  68. package/test/tag_name_test.js +10 -9
  69. package/test/upload_test.js +27 -35
  70. package/test/window_test.js +72 -85
  71. package/testing/index.js +386 -314
  72. package/.npmignore +0 -2
  73. package/example/async_await_test.js +0 -69
  74. package/example/google_search_generator.js +0 -47
  75. package/example/parallel_flows.js +0 -51
  76. package/firefox/binary.js +0 -347
  77. package/firefox/extension.js +0 -224
  78. package/firefox/index.js +0 -576
  79. package/firefox/profile.js +0 -311
  80. package/lib/events.js +0 -210
  81. package/lib/test/data/firefox/jetpack-sample.xpi +0 -0
  82. package/lib/test/data/firefox/sample.xpi +0 -0
  83. package/lib/test/promise.js +0 -79
  84. package/opera.js +0 -405
  85. package/phantomjs.js +0 -282
  86. package/test/firefox/extension_test.js +0 -120
  87. package/test/firefox/firefox_test.js +0 -244
  88. package/test/firefox/profile_test.js +0 -140
  89. package/test/lib/events_test.js +0 -177
  90. package/test/lib/promise_aplus_test.js +0 -78
  91. package/test/lib/promise_error_test.js +0 -884
  92. package/test/lib/promise_flow_test.js +0 -2288
  93. package/test/lib/promise_generator_test.js +0 -310
  94. package/test/phantomjs/execute_phantomjs_test.js +0 -59
  95. package/test/testing/assert_test.js +0 -373
  96. package/test/testing/index_test.js +0 -224
  97. package/testing/assert.js +0 -378
package/lib/proxy.js CHANGED
@@ -18,26 +18,126 @@
18
18
  /**
19
19
  * @fileoverview Defines functions for configuring a webdriver proxy:
20
20
  *
21
- * const Capabilities = require('./capabilities').Capabilities;
21
+ * const proxy = require('selenium-webdriver/proxy');
22
+ * const {Capabilities} = require('selenium-webdriver');
22
23
  *
23
- * var capabilities = new Capabilities();
24
+ * let capabilities = new Capabilities();
24
25
  * capabilities.setProxy(proxy.manual({http: 'host:1234'});
25
26
  */
26
27
 
27
28
  'use strict';
28
29
 
29
- var ProxyConfig = require('./capabilities').ProxyConfig;
30
+ /**
31
+ * Supported {@linkplain Config proxy configuration} types.
32
+ *
33
+ * @enum {string}
34
+ */
35
+ const Type = {
36
+ AUTODETECT: 'autodetect',
37
+ DIRECT: 'direct',
38
+ MANUAL: 'manual',
39
+ PAC: 'pac',
40
+ SYSTEM: 'system',
41
+ };
42
+
43
+
44
+ /**
45
+ * Describes how a proxy should be configured for a WebDriver session.
46
+ * @record
47
+ */
48
+ function Config() {}
49
+
50
+ /**
51
+ * The proxy type.
52
+ * @type {Type}
53
+ */
54
+ Config.prototype.proxyType;
55
+
56
+
57
+ /**
58
+ * Describes how to configure a PAC proxy.
59
+ * @record
60
+ * @extends {Config}
61
+ */
62
+ function PacConfig() {}
63
+
64
+ /**
65
+ * URL for the PAC file to use.
66
+ *
67
+ * @type {string}
68
+ */
69
+ PacConfig.prototype.proxyAutoconfigUrl;
70
+
71
+
72
+ /**
73
+ * Record object that defines a manual proxy configuration. Manual
74
+ * configurations can be easily created using either the
75
+ * {@link ./proxy.manual proxy.manual()} or {@link ./proxy.socks proxy.socks()}
76
+ * factory method.
77
+ *
78
+ * @record
79
+ * @extends {Config}
80
+ */
81
+ function ManualConfig() {}
82
+
83
+ /**
84
+ * The proxy host for FTP requests.
85
+ *
86
+ * @type {(string|undefined)}
87
+ */
88
+ ManualConfig.prototype.ftpProxy;
89
+
90
+ /**
91
+ * The proxy host for HTTP requests.
92
+ *
93
+ * @type {(string|undefined)}
94
+ */
95
+ ManualConfig.prototype.httpProxy;
96
+
97
+ /**
98
+ * An array of hosts which should bypass all proxies.
99
+ *
100
+ * @type {(Array<string>|undefined)}
101
+ */
102
+ ManualConfig.prototype.noProxy;
103
+
104
+ /**
105
+ * The proxy host for HTTPS requests.
106
+ *
107
+ * @type {(string|undefined)}
108
+ */
109
+ ManualConfig.prototype.sslProxy;
110
+
111
+ /**
112
+ * Defines the host and port for the SOCKS proxy to use.
113
+ *
114
+ * @type {(number|undefined)}
115
+ */
116
+ ManualConfig.prototype.socksProxy;
117
+
118
+ /**
119
+ * Defines the SOCKS proxy version. Must be a number in the range [0, 255].
120
+ *
121
+ * @type {(number|undefined)}
122
+ */
123
+ ManualConfig.prototype.socksVersion;
30
124
 
31
125
 
32
126
  // PUBLIC API
33
127
 
34
128
 
129
+ /** @const */ exports.Config = Config;
130
+ /** @const */ exports.ManualConfig = ManualConfig;
131
+ /** @const */ exports.PacConfig = PacConfig;
132
+ /** @const */ exports.Type = Type;
133
+
134
+
35
135
  /**
36
136
  * Configures WebDriver to bypass all browser proxies.
37
- * @return {!ProxyConfig} A new proxy configuration object.
137
+ * @return {!Config} A new proxy configuration object.
38
138
  */
39
139
  exports.direct = function() {
40
- return {proxyType: 'direct'};
140
+ return {proxyType: Type.DIRECT};
41
141
  };
42
142
 
43
143
 
@@ -58,21 +158,18 @@ exports.direct = function() {
58
158
  * @param {{ftp: (string|undefined),
59
159
  * http: (string|undefined),
60
160
  * https: (string|undefined),
61
- * bypass: (string|!Array.<string>|undefined)}} options Proxy
161
+ * bypass: (Array<string>|undefined)}} options Proxy
62
162
  * configuration options.
63
- * @return {!ProxyConfig} A new proxy configuration object.
64
- */
65
- exports.manual = function(options) {
66
- // TODO(jleyba): Figure out why the Closure compiler does not think this is
67
- // a ProxyConfig record without the cast.
68
- return /** @type {!ProxyConfig} */({
69
- proxyType: 'manual',
70
- ftpProxy: options.ftp,
71
- httpProxy: options.http,
72
- sslProxy: options.https,
73
- noProxy: Array.isArray(options.bypass) ?
74
- options.bypass.join(',') : options.bypass
75
- });
163
+ * @return {!ManualConfig} A new proxy configuration object.
164
+ */
165
+ exports.manual = function({ftp, http, https, bypass}) {
166
+ return {
167
+ proxyType: Type.MANUAL,
168
+ ftpProxy: ftp,
169
+ httpProxy: http,
170
+ sslProxy: https,
171
+ noProxy: bypass,
172
+ };
76
173
  };
77
174
 
78
175
 
@@ -85,21 +182,22 @@ exports.manual = function(options) {
85
182
  * const proxy = require('selenium-webdriver/lib/proxy');
86
183
  *
87
184
  * let capabilities = new Capabilities();
88
- * capabilities.setProxy(proxy.socks('localhost:1234', 'bob', 'password'));
185
+ * capabilities.setProxy(proxy.socks('localhost:1234'));
89
186
  *
187
+ * // Or, to include authentication.
188
+ * capabilities.setProxy(proxy.socks('bob:password@localhost:1234'));
90
189
  *
91
- * @param {string} host The proxy host, in the form `hostname:port`.
92
- * @param {string} username The user name to authenticate as.
93
- * @param {string} password The password to authenticate with.
94
- * @return {!ProxyConfig} A new proxy configuration object.
190
+ *
191
+ * @param {string} socksProxy The proxy host, in the form `hostname:port`.
192
+ * @param {number=} socksVersion The SOCKS proxy version.
193
+ * @return {!ManualConfig} A new proxy configuration object.
95
194
  * @see https://en.wikipedia.org/wiki/SOCKS
96
195
  */
97
- exports.socks = function(host, username, password) {
98
- return /** @type {!ProxyConfig} */({
99
- proxyType: 'manual',
100
- socksProxy: host,
101
- socksUsername: username,
102
- socksPassword: password
196
+ exports.socks = function(socksProxy, socksVersion = undefined) {
197
+ return /** @type {!Config} */({
198
+ proxyType: Type.MANUAL,
199
+ socksProxy,
200
+ socksVersion
103
201
  });
104
202
  };
105
203
 
@@ -107,21 +205,18 @@ exports.socks = function(host, username, password) {
107
205
  /**
108
206
  * Configures WebDriver to configure the browser proxy using the PAC file at
109
207
  * the given URL.
110
- * @param {string} url URL for the PAC proxy to use.
111
- * @return {!ProxyConfig} A new proxy configuration object.
208
+ * @param {string} proxyAutoconfigUrl URL for the PAC proxy to use.
209
+ * @return {!PacConfig} A new proxy configuration object.
112
210
  */
113
- exports.pac = function(url) {
114
- return {
115
- proxyType: 'pac',
116
- proxyAutoconfigUrl: url
117
- };
211
+ exports.pac = function(proxyAutoconfigUrl) {
212
+ return {proxyType: Type.PAC, proxyAutoconfigUrl};
118
213
  };
119
214
 
120
215
 
121
216
  /**
122
217
  * Configures WebDriver to use the current system's proxy.
123
- * @return {!ProxyConfig} A new proxy configuration object.
218
+ * @return {!Config} A new proxy configuration object.
124
219
  */
125
220
  exports.system = function() {
126
- return {proxyType: 'system'};
221
+ return {proxyType: Type.SYSTEM};
127
222
  };
@@ -0,0 +1,28 @@
1
+ // Licensed to the Software Freedom Conservancy (SFC) under one
2
+ // or more contributor license agreements. See the NOTICE file
3
+ // distributed with this work for additional information
4
+ // regarding copyright ownership. The SFC licenses this file
5
+ // to you under the Apache License, Version 2.0 (the
6
+ // "License"); you may not use this file except in compliance
7
+ // with the License. You may obtain a copy of the License at
8
+ //
9
+ // http://www.apache.org/licenses/LICENSE-2.0
10
+ //
11
+ // Unless required by applicable law or agreed to in writing,
12
+ // software distributed under the License is distributed on an
13
+ // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ // KIND, either express or implied. See the License for the
15
+ // specific language governing permissions and limitations
16
+ // under the License.
17
+
18
+ 'use strict';
19
+
20
+ /**
21
+ * @fileoverview Defines necessary adapaters for jasmine to have an equivalent
22
+ * API to mocha.
23
+ */
24
+
25
+ global.after = global.afterAll;
26
+ global.before = global.beforeAll;
27
+
28
+ global.jasmine.DEFAULT_TIMEOUT_INTERVAL = 120 * 1000;
@@ -0,0 +1,24 @@
1
+ <!DOCTYPE html>
2
+ <style>
3
+ body {
4
+ margin: 0;
5
+ padding: 0;
6
+ }
7
+ #box {
8
+ background: red;
9
+ position: absolute;
10
+ top: 0;
11
+ left: 0;
12
+ width: 100px;
13
+ height: 100px;
14
+ }
15
+ #box.green { background: green; }
16
+ #box.blue { background: blue; }
17
+ </style>
18
+ <body>
19
+ <div id="box" onclick="setColor('green');" ondblclick="setColor('blue');"></div>
20
+ <script>
21
+ function setColor(c) {
22
+ document.getElementById('box').className = c;
23
+ }
24
+ </script>
@@ -0,0 +1,77 @@
1
+ <!DOCTYPE html>
2
+ <style>
3
+ body {
4
+ margin: 0;
5
+ padding: 0;
6
+ }
7
+ div {
8
+ position: absolute;
9
+ width: 50px;
10
+ height: 50px;
11
+ left: 0;
12
+ top: 0;
13
+ }
14
+ #TR { left: 206px; }
15
+ #BL { top: 206px; }
16
+ #BR { left: 206px; top: 206px; }
17
+ #container {
18
+ width: 256px;
19
+ height: 256px;
20
+ left: 0;
21
+ top: 0;
22
+ border: 1px solid black;
23
+ }
24
+ #slide {
25
+ background: silver;
26
+ z-index: 1;
27
+ }
28
+ </style>
29
+ <body>
30
+ <div id="container">
31
+ <div id="slide"></div>
32
+ <div id="TR"></div>
33
+ <div id="BR"></div>
34
+ <div id="BL"></div>
35
+ </div>
36
+ <script>
37
+ let offset;
38
+ let mouseDown = false;
39
+ let slide = document.getElementById('slide');
40
+ slide.addEventListener('mousedown', function(e) {
41
+ mouseDown = true;
42
+
43
+ let bbox = slide.getBoundingClientRect();
44
+ offset = {
45
+ x: e.clientX - bbox.left,
46
+ y: e.clientY - bbox.top
47
+ };
48
+ });
49
+
50
+ let container = document.getElementById('container');
51
+
52
+ let slideBox = slide.getBoundingClientRect();
53
+ let containerBox = container.getBoundingClientRect();
54
+ let maxLeft = containerBox.width - slideBox.width - 2;
55
+ let maxTop = containerBox.height - slideBox.height - 2;
56
+
57
+ container.addEventListener('mousemove', function(e) {
58
+ if (!mouseDown) return;
59
+
60
+ let bbox = slide.getBoundingClientRect();
61
+ let newLeft = e.clientX - offset.x;
62
+ newLeft = Math.max(newLeft, 0);
63
+ newLeft = Math.min(newLeft, maxLeft);
64
+
65
+ let newTop = e.clientY - offset.y;
66
+ newTop = Math.max(newTop, 0);
67
+ newTop = Math.min(newTop, maxTop);
68
+
69
+ slide.style.top = `${newTop}px`;
70
+ slide.style.left = `${newLeft}px`;
71
+ });
72
+
73
+ document.body.addEventListener('mouseup', function() {
74
+ mouseDown = false;
75
+ });
76
+ </script>
77
+ </body>
@@ -0,0 +1,21 @@
1
+ <!DOCTYPE html>
2
+ <style>
3
+ body {
4
+ margin: 0;
5
+ padding: 0;
6
+ }
7
+ div {
8
+ position: absolute;
9
+ top: 0;
10
+ left: 0;
11
+ width: 500px;
12
+ height: 500px;
13
+ background: green;
14
+ }
15
+ </style>
16
+ <div></div>
17
+ <script>
18
+ var clicks = [];
19
+ document.querySelector('div')
20
+ .addEventListener('click', e => clicks.push([e.clientX, e.clientY]));
21
+ </script>
@@ -0,0 +1,2 @@
1
+ <!DOCTYPE html>
2
+ <h1>Hello, world!</h1>
@@ -0,0 +1,42 @@
1
+ <html>
2
+ <head>
3
+ <title>inputs</title>
4
+ </head>
5
+ <body>
6
+ <h1>Testing Inputs</h1>
7
+
8
+ <form action="#">
9
+ <input name="no_type" value="input with no type" /><br/>
10
+ <br/>
11
+ <input name="number_input" type="number" value="42" /><br/>
12
+ <input name="range_input" type="range" value="42" /><br/>
13
+ <br/>
14
+ <input name="email_input" type="email" value="admin@localhost" /><br/>
15
+ <input name="password_input" type="password" value="qwerty" /><br/>
16
+ <input name="search_input" type="search" value="search" /><br/>
17
+ <input name="tel_input" type="tel" value="911" /><br/>
18
+ <input name="text_input" type="text" value="text input" /><br/>
19
+ <input name="url_input" type="url" value="http://seleniumhq.org/" /><br/>
20
+ <br/>
21
+ <input name="checkbox_input" type="checkbox" value="Checkbox" checked /><br/>
22
+ <input name="radio_input" type="radio" value="radio1" checked /><br/>
23
+ <input name="radio_input" type="radio" value="radio2" /><br/>
24
+ <br/>
25
+ <input name="color_input" type="color" value="#00ffff" /><br/>
26
+ <br/>
27
+ <input name="date_input" type="date" value="2017-11-22" /><br/>
28
+ <input name="datetime_input" type="datetime" value="2017-11-22T11:22" /><br/>
29
+ <input name="datetime_local_input" type="datetime-local" value="2017-11-22T11:22" /><br/>
30
+ <input name="time_input" type="time" value="11:22" /><br/>
31
+ <input name="month_input" type="month" value="2017-11" /><br/>
32
+ <input name="week_input" type="week" value="2017-W47" /><br/>
33
+ <br/>
34
+ <input name="button_input" type="button" value="button" />&nbsp;
35
+ <input name="image_input" type="image" value="image" src="icon.gif" />&nbsp;
36
+ <input name="reset_input" type="reset" value="reset" />&nbsp;
37
+ <input name="submit_input" type="submit" value="submit" />&nbsp;
38
+
39
+ <input name="hidden_input" type="hidden" value="hidden" />
40
+ </form>
41
+ </body>
42
+ </html>
@@ -54,5 +54,17 @@
54
54
  <option value="cheddar">Avonlea Clothbound Cheddar</option>
55
55
  </select>
56
56
 
57
+ <select id="visibility">
58
+ <option value="regular" class="regular">Regular</option>
59
+ <option value="disabled" disabled="disabled" class="disabled">Disabled</option>
60
+ <option value="hidden" style="visibility: hidden;" class="hidden">Hidden</option>
61
+ <option value="invisible" style="display: none;" class="invisible">Invisible</option>
62
+ </select>
63
+
64
+ <select id="transparent" style="opacity: 0;">
65
+ <option value="one">one</option>
66
+ <option value="two">two</option>
67
+ </select>
68
+
57
69
  </body>
58
70
  </html>
@@ -94,5 +94,10 @@ document.write(" and with document.write again");
94
94
 
95
95
  <span id="trimmedSpace">            ​‌‍  ⁠ test            ​‌‍  ⁠ </span>
96
96
 
97
+ <br/>
98
+ <a id="capitalized" style="text-transform: capitalize">Hello, world! bla-bla-BLA</a><br/>
99
+ <a id="lowercased" style="text-transform: lowercase">Hello, world! bla-bla-BLA</a><br/>
100
+ <a id="uppercased" style="text-transform: uppercase">Hello, world! bla-bla-BLA</a><br/>
101
+
97
102
  </body>
98
103
  </html>
@@ -0,0 +1,45 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Upload Form</title>
5
+ <script>
6
+ var intervalId;
7
+ function onTick() {
8
+ var label = document.getElementById('upload_label');
9
+ label.innerHTML += '.';
10
+ }
11
+
12
+ function onUploadSubmit() {
13
+ document.getElementById('upload_target').contentWindow.document.body.
14
+ innerHTML = '';
15
+ var label = document.getElementById('upload_label');
16
+ label.innerHTML = 'Uploading "' + document.forms[0].upload.value + '"';
17
+ label.style.display = '';
18
+ intervalId = window.setInterval(onTick, 500);
19
+ return true;
20
+ }
21
+
22
+ function onUploadDone() {
23
+ var label = document.getElementById('upload_label');
24
+ label.style.display = 'none';
25
+ window.clearInterval(intervalId);
26
+ return true;
27
+ }
28
+ </script>
29
+ </head>
30
+ <body>
31
+ <form action="/common/upload" method="post" name="upload_form"
32
+ target="upload_target" enctype="multipart/form-data"
33
+ onsubmit="onUploadSubmit();">
34
+ <div style='height: 0; width: 0; overflow: hidden;'>
35
+ <div><input id="upload" name="upload" type="file"/></div>
36
+ </div>
37
+ <label id="visible_label" for="upload">Upload</label>
38
+ <div><input id="go" type="submit" value="Go!"/></div>
39
+ <div id="upload_label" style="display:none"></div>
40
+ <iframe src="" id="upload_target" name="upload_target"
41
+ style="width:300px;height:200px">
42
+ </iframe>
43
+ </form>
44
+ </body>
45
+ </html>
@@ -17,26 +17,28 @@
17
17
 
18
18
  'use strict';
19
19
 
20
- var fs = require('fs'),
21
- http = require('http'),
22
- path = require('path'),
23
- url = require('url');
20
+ const fs = require('fs');
21
+ const http = require('http');
22
+ const path = require('path');
23
+ const url = require('url');
24
24
 
25
- var express = require('express');
26
- var multer = require('multer');
27
- var serveIndex = require('serve-index');
25
+ const express = require('express');
26
+ const multer = require('multer');
27
+ const serveIndex = require('serve-index');
28
28
 
29
- var Server = require('./httpserver').Server,
30
- resources = require('./resources'),
31
- isDevMode = require('../devmode');
29
+ const isDevMode = require('../devmode');
30
+ const resources = require('./resources');
31
+ const {Server} = require('./httpserver');
32
32
 
33
- var WEB_ROOT = '/common';
34
- var JS_ROOT = '/javascript';
33
+ const WEB_ROOT = '/common';
34
+ const DATA_ROOT = '/data';
35
+ const JS_ROOT = '/javascript';
35
36
 
36
- var baseDirectory = resources.locate(isDevMode ? 'common/src/web' : '.');
37
- var jsDirectory = resources.locate(isDevMode ? 'javascript' : '..');
37
+ const baseDirectory = resources.locate(isDevMode ? 'common/src/web' : '.');
38
+ const dataDirectory = path.join(__dirname, 'data');
39
+ const jsDirectory = resources.locate(isDevMode ? 'javascript' : '..');
38
40
 
39
- var Pages = (function() {
41
+ const Pages = (function() {
40
42
  var pages = {};
41
43
  function addPage(page, path) {
42
44
  pages.__defineGetter__(page, function() {
@@ -105,7 +107,7 @@ var Pages = (function() {
105
107
  })();
106
108
 
107
109
 
108
- var Path = {
110
+ const Path = {
109
111
  BASIC_AUTH: WEB_ROOT + '/basicAuth',
110
112
  ECHO: WEB_ROOT + '/echo',
111
113
  GENERATED: WEB_ROOT + '/generated',
@@ -126,6 +128,7 @@ app.get('/', sendIndex)
126
128
  .use(JS_ROOT, serveIndex(jsDirectory), express.static(jsDirectory))
127
129
  .post(Path.UPLOAD, handleUpload)
128
130
  .use(WEB_ROOT, serveIndex(baseDirectory), express.static(baseDirectory))
131
+ .use(DATA_ROOT, serveIndex(dataDirectory), express.static(dataDirectory))
129
132
  .get(Path.ECHO, sendEcho)
130
133
  .get(Path.PAGE, sendInifinitePage)
131
134
  .get(Path.PAGE + '/*', sendInifinitePage)
@@ -250,7 +253,8 @@ function sendIndex(request, response) {
250
253
  }
251
254
 
252
255
  var data = ['<!DOCTYPE html><h1>/</h1><hr/><ul>',
253
- createListEntry('common')];
256
+ createListEntry('common'),
257
+ createListEntry('data')];
254
258
  if (isDevMode) {
255
259
  data.push(createListEntry('javascript'));
256
260
  }
@@ -305,9 +309,9 @@ exports.url = server.url.bind(server);
305
309
  exports.whereIs = function(filePath) {
306
310
  filePath = filePath.replace(/\\/g, '/');
307
311
  if (!filePath.startsWith('/')) {
308
- filePath = '/' + filePath;
312
+ filePath = `${WEB_ROOT}/${filePath}`;
309
313
  }
310
- return server.url(WEB_ROOT + filePath);
314
+ return server.url(filePath);
311
315
  };
312
316
 
313
317