@vscode/test-web 0.0.14 → 0.0.18

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.0.18
4
+ * new option `--browserType none` to start the server without opening a browser.
5
+
6
+ ## 0.0.17
7
+ * new options `--host` and `--port`: If provided runs the server from the given host and port.
8
+ * new option `--verbose` to print out the browser console log.
9
+
10
+ ## 0.0.16
11
+ * new option `--sourcesPath`: If provided, runs the server from VS Code sources at the given location.
12
+ * option `--version` is deprecated and replaced with `quality`. Supported values: `stable`, `insiders`. Instead of `sources` use `--insiders`
13
+
3
14
  ## 0.0.14
4
15
  * new option `--extensionPath` : A path pointing to a folder containing additional extensions to include. Argument can be provided multiple times.
5
16
  * new option `--permission`: Permission granted to the opened browser: e.g. clipboard-read, clipboard-write. See full list of options [here](https://playwright.dev/docs/1.14/emulation#permissions). Argument can be provided multiple times.
package/README.md CHANGED
@@ -1,15 +1,17 @@
1
1
  # @vscode/test-web
2
2
 
3
- This module helps testing [VS Code web extensions](https://code.visualstudio.com/api/extension-guides/web-extensions) locally.
3
+ This module helps testing VS Code web extensions locally.
4
4
 
5
5
  [![Test Status Badge](https://github.com/microsoft/vscode-test-web/workflows/Tests/badge.svg)](https://github.com/microsoft/vscode-test-web/actions/workflows/tests.yml)
6
6
  [![npm Package](https://img.shields.io/npm/v/@vscode/test-web.svg?style=flat-square)](https://www.npmjs.org/package/@vscode/test-web)
7
7
  [![NPM Downloads](https://img.shields.io/npm/dm/@vscode/test-web.svg)](https://npmjs.org/package/@vscode/test-web)
8
8
 
9
9
 
10
- The node module runs a local web server that serves VS Code for the browser including the extensions located at the given local path. Additionally the extension tests are automatically run.
10
+ See the [web extensions guide](https://code.visualstudio.com/api/extension-guides/web-extensions) to learn about web extensions.
11
11
 
12
- The node module providers a command line as well as an API.
12
+ The node module runs a local web server that serves VS Code in the browser including the extension under development. Additionally the extension tests are automatically run.
13
+
14
+ The node module provides a command line as well as an API.
13
15
 
14
16
  ## Usage
15
17
 
@@ -39,19 +41,23 @@ Via API:
39
41
 
40
42
  ```ts
41
43
  async function go() {
42
- try {
43
- // The folder containing the Extension Manifest package.json
44
- const extensionDevelopmentPath = path.resolve(__dirname, '../../../');
45
-
46
- // The path to module with the test runner and tests
47
- const extensionTestsPath = path.resolve(__dirname, './suite/index');
48
-
49
- // Start a web server that serves VSCode in a browser, run the tests
50
- await runTests({ browserType: 'chromium', extensionDevelopmentPath, extensionTestsPath });
51
- } catch (err) {
52
- console.error('Failed to run tests');
53
- process.exit(1);
54
- }
44
+ try {
45
+ // The folder containing the Extension Manifest package.json
46
+ const extensionDevelopmentPath = path.resolve(__dirname, '../../../');
47
+
48
+ // The path to module with the test runner and tests
49
+ const extensionTestsPath = path.resolve(__dirname, './suite/index');
50
+
51
+ // Start a web server that serves VSCode in a browser, run the tests
52
+ await runTests({
53
+ browserType: 'chromium',
54
+ extensionDevelopmentPath
55
+ extensionTestsPath
56
+ });
57
+ } catch (err) {
58
+ console.error('Failed to run tests');
59
+ process.exit(1);
60
+ }
55
61
  }
56
62
 
57
63
  go()
@@ -59,18 +65,22 @@ go()
59
65
 
60
66
  CLI options:
61
67
 
62
- |Option|Argument Description||
63
- |-----|-----|----|
64
- | --browserType | The browser to launch: `chromium` (default), `firefox` or `webkit` |
68
+ |Option|Argument Description|
69
+ |-----|-----|
70
+ | --browserType | The browser to launch: `chromium` (default), `firefox`, `webkit` or `none` |
65
71
  | --extensionDevelopmentPath | A path pointing to an extension under development to include. |
66
- | --extensionTestsPath | A path to a test module to run. |
67
- | --version | `insiders` (default), `stable` or `sources`.<br>For sources, also run `yarn web` in a vscode repo |
68
- | --open-devtools| If set, opens the dev tools |
69
- | --headless| If set, hides the browser. Defaults to true when an extensionTestsPath is provided, otherwise false. |
70
- | --hideServerLog| If set, hides the server log. Defaults to true when an extensionTestsPath is provided, otherwise false. |
71
- | --permission| Permission granted to the opened browser: e.g. `clipboard-read`, `clipboard-write`. See [full list of options](https://playwright.dev/docs/api/class-browsercontext#browser-context-grant-permissions). Argument can be provided multiple times. |
72
+ | --extensionTestsPath | A path to a test module to run. |
73
+ | --quality | `insiders` (default), or `stable`. Ignored when sourcesPath is provided. |
74
+ | --sourcesPath | If set, runs the server from VS Code sources located at the given path. Make sure the sources and extensions are compiled (`yarn compile` and `yarn compile-web`) |
75
+ | --headless | If set, hides the browser. Defaults to true when an extensionTestsPath is provided, otherwise false. |
76
+ | --permission | Permission granted to the opened browser: e.g. `clipboard-read`, `clipboard-write`. See [full list of options](https://playwright.dev/docs/api/class-browsercontext#browser-context-grant-permissions). Argument can be provided multiple times. |
72
77
  | --folder-uri | URI of the workspace to open VS Code on. Ignored when `folderPath` is provided |
73
78
  | --extensionPath | A path pointing to a folder containing additional extensions to include. Argument can be provided multiple times. |
79
+ | --host | The host name the server is opened on. Defaults to `localhost` |
80
+ | --port | The port the server is opened on. Defaults to `3000` |
81
+ | --open-devtools | If set, opens the dev tools in the browser |
82
+ | --verbose | If set, prints out more information when running the server |
83
+ | --hideServerLog | If set, hides the server log. Defaults to true when an extensionTestsPath is provided, otherwise false. |
74
84
  | folderPath | A local folder to open VS Code on. The folder content will be available as a virtual file system and opened as workspace. |
75
85
 
76
86
  Corresponding options are available in the API.
@@ -2,13 +2,19 @@
2
2
  /******/ var __webpack_modules__ = ([
3
3
  /* 0 */,
4
4
  /* 1 */
5
+ /***/ ((__unused_webpack_module, exports) => {
6
+
7
+ (()=>{"use strict";var e={};(()=>{var r=e;Object.defineProperty(r,"__esModule",{value:!0}),r.getErrorStatusDescription=r.xhr=r.configure=void 0,r.configure=(e,r)=>{},r.xhr=async e=>{const r=new Headers;if(e.headers)for(const t in e.headers){const s=e.headers[t];Array.isArray(s)?s.forEach((e=>r.set(t,e))):r.set(t,s)}e.user&&e.password&&r.set("Authorization","Basic "+btoa(e.user+":"+e.password));const t={method:e.type,redirect:e.followRedirects>0?"follow":"manual",mode:"cors",headers:r};e.data&&(t.body=e.data);const s=new Request(e.url,t),o=await fetch(s),a={};o.headers.forEach(((e,r)=>{a[r]=e}));const n=await o.arrayBuffer();return new class{constructor(){this.status=o.status,this.headers=a}get responseText(){return(new TextDecoder).decode(n)}get body(){return new Uint8Array(n)}}},r.getErrorStatusDescription=function(e){return String(e)}})();var r=exports;for(var t in e)r[t]=e[t];e.__esModule&&Object.defineProperty(r,"__esModule",{value:!0})})();
8
+
9
+ /***/ }),
10
+ /* 2 */
5
11
  /***/ ((module) => {
6
12
 
7
13
  "use strict";
8
14
  module.exports = require("vscode");
9
15
 
10
16
  /***/ }),
11
- /* 2 */
17
+ /* 3 */
12
18
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
13
19
 
14
20
  "use strict";
@@ -18,92 +24,32 @@ module.exports = require("vscode");
18
24
  * Licensed under the MIT License. See License.txt in the project root for license information.
19
25
  *--------------------------------------------------------------------------------------------*/
20
26
  Object.defineProperty(exports, "__esModule", ({ value: true }));
21
- exports.MountsFileSystemProvider = exports.SCHEME = void 0;
22
- const vscode_1 = __webpack_require__(1);
23
- const vscode_uri_1 = __webpack_require__(3);
24
- const request_light_1 = __webpack_require__(5);
25
- exports.SCHEME = 'vscode-test-web';
26
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
- function isEntry(e) {
28
- return e && (e.type === vscode_1.FileType.Directory || e.type === vscode_1.FileType.File) && typeof e.name === 'string' && e.name.length > 0;
29
- }
30
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
- function isStat(e) {
32
- return e && (e.type === vscode_1.FileType.Directory || e.type === vscode_1.FileType.File) && typeof e.ctime === 'number' && typeof e.mtime === 'number' && typeof e.size === 'number';
33
- }
27
+ exports.MemFileSystemProvider = void 0;
28
+ const vscode_1 = __webpack_require__(2);
29
+ const vscode_uri_1 = __webpack_require__(4);
34
30
  function newFileStat(type, size) {
35
- return { type, ctime: Date.now(), mtime: Date.now(), size };
31
+ return Promise.resolve({ type, ctime: Date.now(), mtime: Date.now(), size });
36
32
  }
37
33
  function modifiedFileStat(stats, size) {
38
- return { type: stats.type, ctime: stats.ctime, mtime: Date.now(), size: size !== null && size !== void 0 ? size : stats.size };
34
+ return Promise.resolve({ type: stats.type, ctime: stats.ctime, mtime: Date.now(), size: size !== null && size !== void 0 ? size : stats.size });
39
35
  }
40
- async function getStats(entry) {
41
- let stats = entry.stats;
42
- if (stats === undefined) {
43
- if (entry.serverUri) {
44
- const url = entry.serverUri.with({ query: 'stat' }).toString();
45
- const response = await (0, request_light_1.xhr)({ url });
46
- if (response.status === 200) {
47
- try {
48
- const res = JSON.parse(response.responseText);
49
- if (isStat(res)) {
50
- stats = res;
51
- }
52
- }
53
- catch {
54
- // ignore
55
- }
56
- }
57
- }
58
- if (!stats) {
59
- stats = newFileStat(entry.type, 0);
60
- }
61
- entry.stats = stats;
62
- }
63
- return stats;
64
- }
65
- async function getEntries(entry) {
66
- if (entry.entries === undefined) {
67
- entry.entries = new Map();
68
- if (entry.serverUri) {
69
- const url = entry.serverUri.with({ query: 'readdir' }).toString();
70
- const response = await (0, request_light_1.xhr)({ url });
71
- if (response.status === 200) {
72
- try {
73
- const res = JSON.parse(response.responseText);
74
- if (Array.isArray(res)) {
75
- for (const r of res) {
76
- if (isEntry(r)) {
77
- const newEntry = { type: r.type, name: r.name, serverUri: vscode_uri_1.Utils.joinPath(entry.serverUri, r.name) };
78
- entry.entries.set(newEntry.name, newEntry);
79
- }
80
- }
81
- }
82
- }
83
- catch {
84
- // ignore
85
- }
86
- }
87
- }
88
- }
89
- return entry.entries;
90
- }
91
- class MountsFileSystemProvider {
92
- constructor(serverUri) {
36
+ class MemFileSystemProvider {
37
+ constructor(scheme, root) {
38
+ this.scheme = scheme;
39
+ this.root = root;
93
40
  // --- manage file events
94
41
  this._onDidChangeFile = new vscode_1.EventEmitter();
95
42
  this.onDidChangeFile = this._onDidChangeFile.event;
96
43
  this._bufferedChanges = [];
97
- this.root = { type: vscode_1.FileType.Directory, name: '', serverUri };
98
44
  }
99
45
  // --- manage file metadata
100
46
  async stat(resource) {
101
47
  const entry = await this._lookup(resource, false);
102
- return getStats(entry);
48
+ return entry.stats;
103
49
  }
104
50
  async readDirectory(resource) {
105
51
  const entry = await this._lookupAsDirectory(resource, false);
106
- const entries = await getEntries(entry);
52
+ const entries = await entry.entries;
107
53
  const result = [];
108
54
  entries.forEach((child, name) => result.push([name, child.type]));
109
55
  return result;
@@ -111,26 +57,12 @@ class MountsFileSystemProvider {
111
57
  // --- manage file contents
112
58
  async readFile(resource) {
113
59
  const entry = await this._lookupAsFile(resource, false);
114
- let content = entry.content;
115
- if (content) {
116
- return content;
117
- }
118
- const serverUri = entry.serverUri;
119
- if (serverUri) {
120
- const response = await (0, request_light_1.xhr)({ url: serverUri.toString() });
121
- if (response.status >= 200 && response.status <= 204) {
122
- content = entry.content = response.body;
123
- }
124
- }
125
- if (!content) {
126
- throw vscode_1.FileSystemError.FileNotFound(resource);
127
- }
128
- return content;
60
+ return entry.content;
129
61
  }
130
62
  async writeFile(uri, content, opts) {
131
63
  const basename = vscode_uri_1.Utils.basename(uri);
132
64
  const parent = await this._lookupParentDirectory(uri);
133
- const entries = await getEntries(parent);
65
+ const entries = await parent.entries;
134
66
  let entry = entries.get(basename);
135
67
  if (entry && entry.type === vscode_1.FileType.Directory) {
136
68
  throw vscode_1.FileSystemError.FileIsADirectory(uri);
@@ -143,13 +75,13 @@ class MountsFileSystemProvider {
143
75
  }
144
76
  const stats = newFileStat(vscode_1.FileType.File, content.byteLength);
145
77
  if (!entry) {
146
- entry = { type: vscode_1.FileType.File, name: basename, stats, content };
78
+ entry = { type: vscode_1.FileType.File, name: basename, stats, content: Promise.resolve(content) };
147
79
  entries.set(basename, entry);
148
80
  this._fireSoon({ type: vscode_1.FileChangeType.Created, uri });
149
81
  }
150
82
  else {
151
83
  entry.stats = stats;
152
- entry.content = content;
84
+ entry.content = Promise.resolve(content);
153
85
  }
154
86
  this._fireSoon({ type: vscode_1.FileChangeType.Changed, uri });
155
87
  }
@@ -162,24 +94,18 @@ class MountsFileSystemProvider {
162
94
  const oldParent = await this._lookupParentDirectory(from);
163
95
  const newParent = await this._lookupParentDirectory(to);
164
96
  const newName = vscode_uri_1.Utils.basename(to);
165
- const oldParentEntries = await getEntries(oldParent);
97
+ const oldParentEntries = await oldParent.entries;
166
98
  oldParentEntries.delete(entry.name);
167
- let newEntry;
168
- if (entry.type === vscode_1.FileType.File) {
169
- newEntry = { type: vscode_1.FileType.File, name: newName, stats: entry.stats, serverUri: entry.serverUri, content: entry.content };
170
- }
171
- else {
172
- newEntry = { type: vscode_1.FileType.Directory, name: newName, stats: entry.stats, serverUri: entry.serverUri, entries: entry.entries };
173
- }
174
- const newParentEntries = await getEntries(newParent);
175
- newParentEntries.set(newName, newEntry);
99
+ entry.name = newName;
100
+ const newParentEntries = await newParent.entries;
101
+ newParentEntries.set(newName, entry);
176
102
  this._fireSoon({ type: vscode_1.FileChangeType.Deleted, uri: from }, { type: vscode_1.FileChangeType.Created, uri: to });
177
103
  }
178
104
  async delete(uri, opts) {
179
105
  const dirname = vscode_uri_1.Utils.dirname(uri);
180
106
  const basename = vscode_uri_1.Utils.basename(uri);
181
107
  const parent = await this._lookupAsDirectory(dirname, false);
182
- const parentEntries = await getEntries(parent);
108
+ const parentEntries = await parent.entries;
183
109
  if (parentEntries.has(basename)) {
184
110
  parentEntries.delete(basename);
185
111
  parent.stats = newFileStat(parent.type, -1);
@@ -190,15 +116,15 @@ class MountsFileSystemProvider {
190
116
  const basename = vscode_uri_1.Utils.basename(uri);
191
117
  const dirname = vscode_uri_1.Utils.dirname(uri);
192
118
  const parent = await this._lookupAsDirectory(dirname, false);
193
- const parentEntries = await getEntries(parent);
194
- const entry = { type: vscode_1.FileType.Directory, name: basename, stats: newFileStat(vscode_1.FileType.Directory, 0) };
119
+ const parentEntries = await parent.entries;
120
+ const entry = { type: vscode_1.FileType.Directory, name: basename, stats: newFileStat(vscode_1.FileType.Directory, 0), entries: Promise.resolve(new Map()) };
195
121
  parentEntries.set(entry.name, entry);
196
- const stats = await getStats(parent);
122
+ const stats = await parent.stats;
197
123
  parent.stats = modifiedFileStat(stats, stats.size + 1);
198
124
  this._fireSoon({ type: vscode_1.FileChangeType.Changed, uri: dirname }, { type: vscode_1.FileChangeType.Created, uri });
199
125
  }
200
126
  async _lookup(uri, silent) {
201
- if (uri.scheme !== exports.SCHEME) {
127
+ if (uri.scheme !== this.scheme) {
202
128
  if (!silent) {
203
129
  throw vscode_1.FileSystemError.FileNotFound(uri);
204
130
  }
@@ -214,7 +140,7 @@ class MountsFileSystemProvider {
214
140
  }
215
141
  let child;
216
142
  if (entry.type === vscode_1.FileType.Directory) {
217
- child = (await getEntries(entry)).get(part);
143
+ child = (await entry.entries).get(part);
218
144
  }
219
145
  if (!child) {
220
146
  if (!silent) {
@@ -267,11 +193,11 @@ class MountsFileSystemProvider {
267
193
  this._onDidChangeFile.dispose();
268
194
  }
269
195
  }
270
- exports.MountsFileSystemProvider = MountsFileSystemProvider;
196
+ exports.MemFileSystemProvider = MemFileSystemProvider;
271
197
 
272
198
 
273
199
  /***/ }),
274
- /* 3 */
200
+ /* 4 */
275
201
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
276
202
 
277
203
  "use strict";
@@ -280,12 +206,12 @@ __webpack_require__.r(__webpack_exports__);
280
206
  /* harmony export */ "URI": () => (/* binding */ URI),
281
207
  /* harmony export */ "Utils": () => (/* binding */ Utils)
282
208
  /* harmony export */ });
283
- /* provided dependency */ var process = __webpack_require__(4);
209
+ /* provided dependency */ var process = __webpack_require__(5);
284
210
  var LIB;LIB=(()=>{"use strict";var t={470:t=>{function e(t){if("string"!=typeof t)throw new TypeError("Path must be a string. Received "+JSON.stringify(t))}function r(t,e){for(var r,n="",o=0,i=-1,a=0,h=0;h<=t.length;++h){if(h<t.length)r=t.charCodeAt(h);else{if(47===r)break;r=47}if(47===r){if(i===h-1||1===a);else if(i!==h-1&&2===a){if(n.length<2||2!==o||46!==n.charCodeAt(n.length-1)||46!==n.charCodeAt(n.length-2))if(n.length>2){var s=n.lastIndexOf("/");if(s!==n.length-1){-1===s?(n="",o=0):o=(n=n.slice(0,s)).length-1-n.lastIndexOf("/"),i=h,a=0;continue}}else if(2===n.length||1===n.length){n="",o=0,i=h,a=0;continue}e&&(n.length>0?n+="/..":n="..",o=2)}else n.length>0?n+="/"+t.slice(i+1,h):n=t.slice(i+1,h),o=h-i-1;i=h,a=0}else 46===r&&-1!==a?++a:a=-1}return n}var n={resolve:function(){for(var t,n="",o=!1,i=arguments.length-1;i>=-1&&!o;i--){var a;i>=0?a=arguments[i]:(void 0===t&&(t=process.cwd()),a=t),e(a),0!==a.length&&(n=a+"/"+n,o=47===a.charCodeAt(0))}return n=r(n,!o),o?n.length>0?"/"+n:"/":n.length>0?n:"."},normalize:function(t){if(e(t),0===t.length)return".";var n=47===t.charCodeAt(0),o=47===t.charCodeAt(t.length-1);return 0!==(t=r(t,!n)).length||n||(t="."),t.length>0&&o&&(t+="/"),n?"/"+t:t},isAbsolute:function(t){return e(t),t.length>0&&47===t.charCodeAt(0)},join:function(){if(0===arguments.length)return".";for(var t,r=0;r<arguments.length;++r){var o=arguments[r];e(o),o.length>0&&(void 0===t?t=o:t+="/"+o)}return void 0===t?".":n.normalize(t)},relative:function(t,r){if(e(t),e(r),t===r)return"";if((t=n.resolve(t))===(r=n.resolve(r)))return"";for(var o=1;o<t.length&&47===t.charCodeAt(o);++o);for(var i=t.length,a=i-o,h=1;h<r.length&&47===r.charCodeAt(h);++h);for(var s=r.length-h,f=a<s?a:s,u=-1,c=0;c<=f;++c){if(c===f){if(s>f){if(47===r.charCodeAt(h+c))return r.slice(h+c+1);if(0===c)return r.slice(h+c)}else a>f&&(47===t.charCodeAt(o+c)?u=c:0===c&&(u=0));break}var l=t.charCodeAt(o+c);if(l!==r.charCodeAt(h+c))break;47===l&&(u=c)}var p="";for(c=o+u+1;c<=i;++c)c!==i&&47!==t.charCodeAt(c)||(0===p.length?p+="..":p+="/..");return p.length>0?p+r.slice(h+u):(h+=u,47===r.charCodeAt(h)&&++h,r.slice(h))},_makeLong:function(t){return t},dirname:function(t){if(e(t),0===t.length)return".";for(var r=t.charCodeAt(0),n=47===r,o=-1,i=!0,a=t.length-1;a>=1;--a)if(47===(r=t.charCodeAt(a))){if(!i){o=a;break}}else i=!1;return-1===o?n?"/":".":n&&1===o?"//":t.slice(0,o)},basename:function(t,r){if(void 0!==r&&"string"!=typeof r)throw new TypeError('"ext" argument must be a string');e(t);var n,o=0,i=-1,a=!0;if(void 0!==r&&r.length>0&&r.length<=t.length){if(r.length===t.length&&r===t)return"";var h=r.length-1,s=-1;for(n=t.length-1;n>=0;--n){var f=t.charCodeAt(n);if(47===f){if(!a){o=n+1;break}}else-1===s&&(a=!1,s=n+1),h>=0&&(f===r.charCodeAt(h)?-1==--h&&(i=n):(h=-1,i=s))}return o===i?i=s:-1===i&&(i=t.length),t.slice(o,i)}for(n=t.length-1;n>=0;--n)if(47===t.charCodeAt(n)){if(!a){o=n+1;break}}else-1===i&&(a=!1,i=n+1);return-1===i?"":t.slice(o,i)},extname:function(t){e(t);for(var r=-1,n=0,o=-1,i=!0,a=0,h=t.length-1;h>=0;--h){var s=t.charCodeAt(h);if(47!==s)-1===o&&(i=!1,o=h+1),46===s?-1===r?r=h:1!==a&&(a=1):-1!==r&&(a=-1);else if(!i){n=h+1;break}}return-1===r||-1===o||0===a||1===a&&r===o-1&&r===n+1?"":t.slice(r,o)},format:function(t){if(null===t||"object"!=typeof t)throw new TypeError('The "pathObject" argument must be of type Object. Received type '+typeof t);return function(t,e){var r=e.dir||e.root,n=e.base||(e.name||"")+(e.ext||"");return r?r===e.root?r+n:r+"/"+n:n}(0,t)},parse:function(t){e(t);var r={root:"",dir:"",base:"",ext:"",name:""};if(0===t.length)return r;var n,o=t.charCodeAt(0),i=47===o;i?(r.root="/",n=1):n=0;for(var a=-1,h=0,s=-1,f=!0,u=t.length-1,c=0;u>=n;--u)if(47!==(o=t.charCodeAt(u)))-1===s&&(f=!1,s=u+1),46===o?-1===a?a=u:1!==c&&(c=1):-1!==a&&(c=-1);else if(!f){h=u+1;break}return-1===a||-1===s||0===c||1===c&&a===s-1&&a===h+1?-1!==s&&(r.base=r.name=0===h&&i?t.slice(1,s):t.slice(h,s)):(0===h&&i?(r.name=t.slice(1,a),r.base=t.slice(1,s)):(r.name=t.slice(h,a),r.base=t.slice(h,s)),r.ext=t.slice(a,s)),h>0?r.dir=t.slice(0,h-1):i&&(r.dir="/"),r},sep:"/",delimiter:":",win32:null,posix:null};n.posix=n,t.exports=n},447:(t,e,r)=>{var n;if(r.r(e),r.d(e,{URI:()=>g,Utils:()=>O}),"object"==typeof process)n="win32"===process.platform;else if("object"==typeof navigator){var o=navigator.userAgent;n=o.indexOf("Windows")>=0}var i,a,h=(i=function(t,e){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}i(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),s=/^\w[\w\d+.-]*$/,f=/^\//,u=/^\/\//,c="",l="/",p=/^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/,g=function(){function t(t,e,r,n,o,i){void 0===i&&(i=!1),"object"==typeof t?(this.scheme=t.scheme||c,this.authority=t.authority||c,this.path=t.path||c,this.query=t.query||c,this.fragment=t.fragment||c):(this.scheme=function(t,e){return t||e?t:"file"}(t,i),this.authority=e||c,this.path=function(t,e){switch(t){case"https":case"http":case"file":e?e[0]!==l&&(e=l+e):e=l}return e}(this.scheme,r||c),this.query=n||c,this.fragment=o||c,function(t,e){if(!t.scheme&&e)throw new Error('[UriError]: Scheme is missing: {scheme: "", authority: "'+t.authority+'", path: "'+t.path+'", query: "'+t.query+'", fragment: "'+t.fragment+'"}');if(t.scheme&&!s.test(t.scheme))throw new Error("[UriError]: Scheme contains illegal characters.");if(t.path)if(t.authority){if(!f.test(t.path))throw new Error('[UriError]: If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character')}else if(u.test(t.path))throw new Error('[UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")')}(this,i))}return t.isUri=function(e){return e instanceof t||!!e&&"string"==typeof e.authority&&"string"==typeof e.fragment&&"string"==typeof e.path&&"string"==typeof e.query&&"string"==typeof e.scheme&&"function"==typeof e.fsPath&&"function"==typeof e.with&&"function"==typeof e.toString},Object.defineProperty(t.prototype,"fsPath",{get:function(){return C(this,!1)},enumerable:!1,configurable:!0}),t.prototype.with=function(t){if(!t)return this;var e=t.scheme,r=t.authority,n=t.path,o=t.query,i=t.fragment;return void 0===e?e=this.scheme:null===e&&(e=c),void 0===r?r=this.authority:null===r&&(r=c),void 0===n?n=this.path:null===n&&(n=c),void 0===o?o=this.query:null===o&&(o=c),void 0===i?i=this.fragment:null===i&&(i=c),e===this.scheme&&r===this.authority&&n===this.path&&o===this.query&&i===this.fragment?this:new v(e,r,n,o,i)},t.parse=function(t,e){void 0===e&&(e=!1);var r=p.exec(t);return r?new v(r[2]||c,x(r[4]||c),x(r[5]||c),x(r[7]||c),x(r[9]||c),e):new v(c,c,c,c,c)},t.file=function(t){var e=c;if(n&&(t=t.replace(/\\/g,l)),t[0]===l&&t[1]===l){var r=t.indexOf(l,2);-1===r?(e=t.substring(2),t=l):(e=t.substring(2,r),t=t.substring(r)||l)}return new v("file",e,t,c,c)},t.from=function(t){return new v(t.scheme,t.authority,t.path,t.query,t.fragment)},t.prototype.toString=function(t){return void 0===t&&(t=!1),A(this,t)},t.prototype.toJSON=function(){return this},t.revive=function(e){if(e){if(e instanceof t)return e;var r=new v(e);return r._formatted=e.external,r._fsPath=e._sep===d?e.fsPath:null,r}return e},t}(),d=n?1:void 0,v=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e._formatted=null,e._fsPath=null,e}return h(e,t),Object.defineProperty(e.prototype,"fsPath",{get:function(){return this._fsPath||(this._fsPath=C(this,!1)),this._fsPath},enumerable:!1,configurable:!0}),e.prototype.toString=function(t){return void 0===t&&(t=!1),t?A(this,!0):(this._formatted||(this._formatted=A(this,!1)),this._formatted)},e.prototype.toJSON=function(){var t={$mid:1};return this._fsPath&&(t.fsPath=this._fsPath,t._sep=d),this._formatted&&(t.external=this._formatted),this.path&&(t.path=this.path),this.scheme&&(t.scheme=this.scheme),this.authority&&(t.authority=this.authority),this.query&&(t.query=this.query),this.fragment&&(t.fragment=this.fragment),t},e}(g),m=((a={})[58]="%3A",a[47]="%2F",a[63]="%3F",a[35]="%23",a[91]="%5B",a[93]="%5D",a[64]="%40",a[33]="%21",a[36]="%24",a[38]="%26",a[39]="%27",a[40]="%28",a[41]="%29",a[42]="%2A",a[43]="%2B",a[44]="%2C",a[59]="%3B",a[61]="%3D",a[32]="%20",a);function y(t,e){for(var r=void 0,n=-1,o=0;o<t.length;o++){var i=t.charCodeAt(o);if(i>=97&&i<=122||i>=65&&i<=90||i>=48&&i<=57||45===i||46===i||95===i||126===i||e&&47===i)-1!==n&&(r+=encodeURIComponent(t.substring(n,o)),n=-1),void 0!==r&&(r+=t.charAt(o));else{void 0===r&&(r=t.substr(0,o));var a=m[i];void 0!==a?(-1!==n&&(r+=encodeURIComponent(t.substring(n,o)),n=-1),r+=a):-1===n&&(n=o)}}return-1!==n&&(r+=encodeURIComponent(t.substring(n))),void 0!==r?r:t}function b(t){for(var e=void 0,r=0;r<t.length;r++){var n=t.charCodeAt(r);35===n||63===n?(void 0===e&&(e=t.substr(0,r)),e+=m[n]):void 0!==e&&(e+=t[r])}return void 0!==e?e:t}function C(t,e){var r;return r=t.authority&&t.path.length>1&&"file"===t.scheme?"//"+t.authority+t.path:47===t.path.charCodeAt(0)&&(t.path.charCodeAt(1)>=65&&t.path.charCodeAt(1)<=90||t.path.charCodeAt(1)>=97&&t.path.charCodeAt(1)<=122)&&58===t.path.charCodeAt(2)?e?t.path.substr(1):t.path[1].toLowerCase()+t.path.substr(2):t.path,n&&(r=r.replace(/\//g,"\\")),r}function A(t,e){var r=e?b:y,n="",o=t.scheme,i=t.authority,a=t.path,h=t.query,s=t.fragment;if(o&&(n+=o,n+=":"),(i||"file"===o)&&(n+=l,n+=l),i){var f=i.indexOf("@");if(-1!==f){var u=i.substr(0,f);i=i.substr(f+1),-1===(f=u.indexOf(":"))?n+=r(u,!1):(n+=r(u.substr(0,f),!1),n+=":",n+=r(u.substr(f+1),!1)),n+="@"}-1===(f=(i=i.toLowerCase()).indexOf(":"))?n+=r(i,!1):(n+=r(i.substr(0,f),!1),n+=i.substr(f))}if(a){if(a.length>=3&&47===a.charCodeAt(0)&&58===a.charCodeAt(2))(c=a.charCodeAt(1))>=65&&c<=90&&(a="/"+String.fromCharCode(c+32)+":"+a.substr(3));else if(a.length>=2&&58===a.charCodeAt(1)){var c;(c=a.charCodeAt(0))>=65&&c<=90&&(a=String.fromCharCode(c+32)+":"+a.substr(2))}n+=r(a,!0)}return h&&(n+="?",n+=r(h,!1)),s&&(n+="#",n+=e?s:y(s,!1)),n}function w(t){try{return decodeURIComponent(t)}catch(e){return t.length>3?t.substr(0,3)+w(t.substr(3)):t}}var _=/(%[0-9A-Za-z][0-9A-Za-z])+/g;function x(t){return t.match(_)?t.replace(_,(function(t){return w(t)})):t}var O,P=r(470),j=function(){for(var t=0,e=0,r=arguments.length;e<r;e++)t+=arguments[e].length;var n=Array(t),o=0;for(e=0;e<r;e++)for(var i=arguments[e],a=0,h=i.length;a<h;a++,o++)n[o]=i[a];return n},U=P.posix||P;!function(t){t.joinPath=function(t){for(var e=[],r=1;r<arguments.length;r++)e[r-1]=arguments[r];return t.with({path:U.join.apply(U,j([t.path],e))})},t.resolvePath=function(t){for(var e=[],r=1;r<arguments.length;r++)e[r-1]=arguments[r];var n=t.path||"/";return t.with({path:U.resolve.apply(U,j([n],e))})},t.dirname=function(t){var e=U.dirname(t.path);return 1===e.length&&46===e.charCodeAt(0)?t:t.with({path:e})},t.basename=function(t){return U.basename(t.path)},t.extname=function(t){return U.extname(t.path)}}(O||(O={}))}},e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={exports:{}};return t[n](o,o.exports,r),o.exports}return r.d=(t,e)=>{for(var n in e)r.o(e,n)&&!r.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},r.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r(447)})();const{URI,Utils}=LIB;
285
211
  //# sourceMappingURL=index.js.map
286
212
 
287
213
  /***/ }),
288
- /* 4 */
214
+ /* 5 */
289
215
  /***/ ((module) => {
290
216
 
291
217
  // shim for using process in browser
@@ -474,12 +400,6 @@ process.chdir = function (dir) {
474
400
  process.umask = function() { return 0; };
475
401
 
476
402
 
477
- /***/ }),
478
- /* 5 */
479
- /***/ ((__unused_webpack_module, exports) => {
480
-
481
- (()=>{"use strict";var e={};(()=>{var r=e;Object.defineProperty(r,"__esModule",{value:!0}),r.getErrorStatusDescription=r.xhr=r.configure=void 0,r.configure=(e,r)=>{},r.xhr=async e=>{const r=new Headers;if(e.headers)for(const t in e.headers){const s=e.headers[t];Array.isArray(s)?s.forEach((e=>r.set(t,e))):r.set(t,s)}e.user&&e.password&&r.set("Authorization","Basic "+btoa(e.user+":"+e.password));const t={method:e.type,redirect:e.followRedirects>0?"follow":"manual",mode:"cors",headers:r};e.data&&(t.body=e.data);const s=new Request(e.url,t),o=await fetch(s),a={};o.headers.forEach(((e,r)=>{a[r]=e}));const n=await o.arrayBuffer();return new class{constructor(){this.status=o.status,this.headers=a}get responseText(){return(new TextDecoder).decode(n)}get body(){return new Uint8Array(n)}}},r.getErrorStatusDescription=function(e){return String(e)}})();var r=exports;for(var t in e)r[t]=e[t];e.__esModule&&Object.defineProperty(r,"__esModule",{value:!0})})();
482
-
483
403
  /***/ })
484
404
  /******/ ]);
485
405
  /************************************************************************/
@@ -549,15 +469,123 @@ var exports = __webpack_exports__;
549
469
  *--------------------------------------------------------------------------------------------*/
550
470
  Object.defineProperty(exports, "__esModule", ({ value: true }));
551
471
  exports.activate = void 0;
552
- const vscode = __webpack_require__(1);
553
- const fsProvider_1 = __webpack_require__(2);
472
+ const request_light_1 = __webpack_require__(1);
473
+ const vscode_1 = __webpack_require__(2);
474
+ const fsProvider_1 = __webpack_require__(3);
475
+ const SCHEME = 'vscode-test-web';
554
476
  function activate(context) {
555
477
  const serverUri = context.extensionUri.with({ path: '/static/mount', query: undefined });
556
- const disposable = vscode.workspace.registerFileSystemProvider(fsProvider_1.SCHEME, new fsProvider_1.MountsFileSystemProvider(serverUri));
478
+ const serverBackedRootDirectory = new ServerBackedDirectory(serverUri, '');
479
+ const disposable = vscode_1.workspace.registerFileSystemProvider(SCHEME, new fsProvider_1.MemFileSystemProvider(SCHEME, serverBackedRootDirectory));
557
480
  context.subscriptions.push(disposable);
558
- console.log(`vscode-test-web-support fs provider registers for ${fsProvider_1.SCHEME}, initial content from ${serverUri.toString()}`);
481
+ console.log(`vscode-test-web-support fs provider registers for ${SCHEME}, initial content from ${serverUri.toString()}`);
559
482
  }
560
483
  exports.activate = activate;
484
+ class ServerBackedFile {
485
+ constructor(_serverUri, name) {
486
+ this._serverUri = _serverUri;
487
+ this.name = name;
488
+ this.type = vscode_1.FileType.File;
489
+ }
490
+ get stats() {
491
+ if (this._stats === undefined) {
492
+ this._stats = getStats(this._serverUri);
493
+ }
494
+ return this._stats;
495
+ }
496
+ set stats(stats) {
497
+ this._stats = stats;
498
+ }
499
+ get content() {
500
+ if (this._content === undefined) {
501
+ this._content = getContent(this._serverUri);
502
+ }
503
+ return this._content;
504
+ }
505
+ set content(content) {
506
+ this._content = content;
507
+ }
508
+ }
509
+ class ServerBackedDirectory {
510
+ constructor(_serverUri, name) {
511
+ this._serverUri = _serverUri;
512
+ this.name = name;
513
+ this.type = vscode_1.FileType.Directory;
514
+ }
515
+ get stats() {
516
+ if (this._stats === undefined) {
517
+ this._stats = getStats(this._serverUri);
518
+ }
519
+ return this._stats;
520
+ }
521
+ set stats(stats) {
522
+ this._stats = stats;
523
+ }
524
+ get entries() {
525
+ if (this._entries === undefined) {
526
+ this._entries = getEntries(this._serverUri);
527
+ }
528
+ return this._entries;
529
+ }
530
+ set entries(entries) {
531
+ this._entries = entries;
532
+ }
533
+ }
534
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
535
+ function isEntry(e) {
536
+ return e && (e.type === vscode_1.FileType.Directory || e.type === vscode_1.FileType.File) && typeof e.name === 'string' && e.name.length > 0;
537
+ }
538
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
539
+ function isStat(e) {
540
+ return e && (e.type === vscode_1.FileType.Directory || e.type === vscode_1.FileType.File) && typeof e.ctime === 'number' && typeof e.mtime === 'number' && typeof e.size === 'number';
541
+ }
542
+ async function getEntries(serverUri) {
543
+ const url = serverUri.with({ query: 'readdir' }).toString();
544
+ const response = await (0, request_light_1.xhr)({ url });
545
+ if (response.status === 200 && response.status <= 204) {
546
+ try {
547
+ const res = JSON.parse(response.responseText);
548
+ if (Array.isArray(res)) {
549
+ const entries = new Map();
550
+ for (const r of res) {
551
+ if (isEntry(r)) {
552
+ const childPath = vscode_1.Uri.joinPath(serverUri, r.name);
553
+ const newEntry = r.type === vscode_1.FileType.Directory ? new ServerBackedDirectory(childPath, r.name) : new ServerBackedFile(childPath, r.name);
554
+ entries.set(newEntry.name, newEntry);
555
+ }
556
+ }
557
+ return entries;
558
+ }
559
+ }
560
+ catch {
561
+ // ignore
562
+ }
563
+ console.log(`Invalid server response format for ${url.toString()}.`);
564
+ }
565
+ else {
566
+ console.log(`Invalid server response for ${url.toString()}. Status ${response.status}`);
567
+ }
568
+ return new Map();
569
+ }
570
+ async function getStats(serverUri) {
571
+ const url = serverUri.with({ query: 'stat' }).toString();
572
+ const response = await (0, request_light_1.xhr)({ url });
573
+ if (response.status === 200 && response.status <= 204) {
574
+ const res = JSON.parse(response.responseText);
575
+ if (isStat(res)) {
576
+ return res;
577
+ }
578
+ throw vscode_1.FileSystemError.FileNotFound(`Invalid server response for ${serverUri.toString()}.`);
579
+ }
580
+ throw vscode_1.FileSystemError.FileNotFound(`Invalid server response for ${serverUri.toString()}. Status ${response.status}.`);
581
+ }
582
+ async function getContent(serverUri) {
583
+ const response = await (0, request_light_1.xhr)({ url: serverUri.toString() });
584
+ if (response.status >= 200 && response.status <= 204) {
585
+ return response.body;
586
+ }
587
+ throw vscode_1.FileSystemError.FileNotFound(`Invalid server response for ${serverUri.toString()}. Status ${response.status}.`);
588
+ }
561
589
 
562
590
  })();
563
591
 
@@ -24,10 +24,10 @@
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/vscode": "^1.55.0",
27
- "@types/webpack-env": "^1.16.0",
28
- "ts-loader": "^9.2.3",
29
- "webpack": "^5.40.0",
30
- "webpack-cli": "^4.7.2",
27
+ "@types/webpack-env": "^1.16.2",
28
+ "ts-loader": "^9.2.6",
29
+ "webpack": "^5.55.0",
30
+ "webpack-cli": "^4.8.0",
31
31
  "process": "^0.11.10",
32
32
  "path-browserify": "^1.0.1",
33
33
  "request-light": "^0.5.3",
package/out/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- export declare type BrowserType = 'chromium' | 'firefox' | 'webkit';
3
- export declare type VSCodeVersion = 'insiders' | 'stable' | 'sources';
2
+ export declare type BrowserType = 'chromium' | 'firefox' | 'webkit' | 'none';
3
+ export declare type VSCodeQuality = 'insiders' | 'stable';
4
4
  export interface Options {
5
5
  /**
6
- * Browser to run the test against: 'chromium' | 'firefox' | 'webkit'
6
+ * Browser to open: 'chromium' | 'firefox' | 'webkit' | 'none'.
7
7
  */
8
8
  browserType: BrowserType;
9
9
  /**
@@ -25,20 +25,25 @@ export interface Options {
25
25
  */
26
26
  extensionTestsPath?: string;
27
27
  /**
28
- * The VS Code version to use. Valid versions are:
29
- * - `'stable'` : The latest stable build
30
- * - `'insiders'` : The latest insiders build
31
- * - `'sources'`: From sources, served at localhost:8080 by running `yarn web` in the vscode repo
28
+ * The quality of the VS Code to use. Supported qualities are:
29
+ * - `'stable'` : The latest stable build will be used
30
+ * - `'insiders'` : The latest insiders build will be used
32
31
  *
33
32
  * Currently defaults to `insiders`, which is latest stable insiders.
33
+ *
34
+ * The setting is ignored when a vsCodeDevPath is provided.
35
+ */
36
+ quality?: VSCodeQuality;
37
+ /**
38
+ * @deprecated. Use `quality` or `vsCodeDevPath` instead.
34
39
  */
35
- version?: VSCodeVersion;
40
+ version?: string;
36
41
  /**
37
42
  * Open the dev tools.
38
43
  */
39
44
  devTools?: boolean;
40
45
  /**
41
- * Do not show the browser. Defaults to `true` if a extensionTestsPath is provided, `false` otherwise.
46
+ * Do not show the browser. Defaults to `true` if a `extensionTestsPath` is provided, `false` otherwise.
42
47
  */
43
48
  headless?: boolean;
44
49
  /**
@@ -70,6 +75,22 @@ export interface Options {
70
75
  * Absolute paths pointing to built-in extensions to include.
71
76
  */
72
77
  extensionPaths?: string[];
78
+ /**
79
+ * Absolute path pointing to VS Code sources to use.
80
+ */
81
+ vsCodeDevPath?: string;
82
+ /**
83
+ * Print out more information while the server is running, e.g. the console output in the browser
84
+ */
85
+ verbose?: boolean;
86
+ /**
87
+ * The port to start the server on. Defaults to `3000`.
88
+ */
89
+ port?: number;
90
+ /**
91
+ * The host name to start the server on. Defaults to `localhost`
92
+ */
93
+ host?: string;
73
94
  }
74
95
  export interface Disposable {
75
96
  dispose(): void;
package/out/index.js CHANGED
@@ -18,68 +18,83 @@ const path = require("path");
18
18
  * @param options The options defining browser type, extension and test location.
19
19
  */
20
20
  async function runTests(options) {
21
+ var _a, _b, _c;
21
22
  const config = {
22
23
  extensionDevelopmentPath: options.extensionDevelopmentPath,
23
24
  extensionTestsPath: options.extensionTestsPath,
24
- build: await getBuild(options.version),
25
+ build: await getBuild(options),
25
26
  folderUri: options.folderUri,
26
27
  folderMountPath: options.folderPath,
27
- hideServerLog: true,
28
+ hideServerLog: (_a = options.hideServerLog) !== null && _a !== void 0 ? _a : true,
28
29
  extensionPaths: options.extensionPaths
29
30
  };
30
- const port = 3000;
31
- const server = await (0, main_1.runServer)(port, config);
31
+ const host = (_b = options.host) !== null && _b !== void 0 ? _b : 'localhost';
32
+ const port = (_c = options.port) !== null && _c !== void 0 ? _c : 3000;
33
+ const server = await (0, main_1.runServer)(host, port, config);
32
34
  return new Promise(async (s, e) => {
33
- const endpoint = `http://localhost:${port}`;
35
+ const endpoint = `http://${host}:${port}`;
34
36
  const context = await openBrowser(endpoint, options);
35
- context.once('close', () => server.close());
36
- await context.exposeFunction('codeAutomationLog', (type, args) => {
37
- console[type](...args);
38
- });
39
- await context.exposeFunction('codeAutomationExit', async (code) => {
40
- var _a;
41
- try {
42
- await ((_a = context.browser()) === null || _a === void 0 ? void 0 : _a.close());
43
- }
44
- catch (error) {
45
- console.error(`Error when closing browser: ${error}`);
46
- }
37
+ if (context) {
38
+ context.once('close', () => server.close());
39
+ await context.exposeFunction('codeAutomationLog', (type, args) => {
40
+ console[type](...args);
41
+ });
42
+ await context.exposeFunction('codeAutomationExit', async (code) => {
43
+ var _a;
44
+ try {
45
+ await ((_a = context.browser()) === null || _a === void 0 ? void 0 : _a.close());
46
+ }
47
+ catch (error) {
48
+ console.error(`Error when closing browser: ${error}`);
49
+ }
50
+ server.close();
51
+ if (code === 0) {
52
+ s();
53
+ }
54
+ else {
55
+ e(new Error('Test failed'));
56
+ }
57
+ });
58
+ }
59
+ else {
47
60
  server.close();
48
- if (code === 0) {
49
- s();
50
- }
51
- else {
52
- e(new Error('Test failed'));
53
- }
54
- });
61
+ e(new Error('Can not run test as opening of browser failed.'));
62
+ }
55
63
  });
56
64
  }
57
65
  exports.runTests = runTests;
58
- async function getBuild(version) {
59
- if (version === 'sources') {
60
- return { type: 'sources' };
66
+ async function getBuild(options) {
67
+ if (options.vsCodeDevPath) {
68
+ return {
69
+ type: 'sources',
70
+ location: options.vsCodeDevPath
71
+ };
61
72
  }
62
- return await (0, download_1.downloadAndUnzipVSCode)(version === 'stable' ? 'stable' : 'insider');
73
+ const quality = options.quality || options.version;
74
+ return await (0, download_1.downloadAndUnzipVSCode)(quality === 'stable' ? 'stable' : 'insider');
63
75
  }
64
76
  async function open(options) {
77
+ var _a, _b, _c;
65
78
  const config = {
66
79
  extensionDevelopmentPath: options.extensionDevelopmentPath,
67
80
  extensionTestsPath: options.extensionTestsPath,
68
- build: await getBuild(options.version),
81
+ build: await getBuild(options),
69
82
  folderUri: options.folderUri,
70
83
  folderMountPath: options.folderPath,
84
+ hideServerLog: (_a = options.hideServerLog) !== null && _a !== void 0 ? _a : true,
71
85
  extensionPaths: options.extensionPaths
72
86
  };
73
- const port = 3000;
74
- const server = await (0, main_1.runServer)(port, config);
75
- const endpoint = `http://localhost:${port}`;
87
+ const host = (_b = options.host) !== null && _b !== void 0 ? _b : 'localhost';
88
+ const port = (_c = options.port) !== null && _c !== void 0 ? _c : 3000;
89
+ const server = await (0, main_1.runServer)(host, port, config);
90
+ const endpoint = `http://${host}:${port}`;
76
91
  const context = await openBrowser(endpoint, options);
77
- context.once('close', () => server.close());
92
+ context === null || context === void 0 ? void 0 : context.once('close', () => server.close());
78
93
  return {
79
94
  dispose: () => {
80
95
  var _a;
81
96
  server.close();
82
- (_a = context.browser()) === null || _a === void 0 ? void 0 : _a.close();
97
+ (_a = context === null || context === void 0 ? void 0 : context.browser()) === null || _a === void 0 ? void 0 : _a.close();
83
98
  }
84
99
  };
85
100
  }
@@ -88,6 +103,14 @@ const width = 1200;
88
103
  const height = 800;
89
104
  async function openBrowser(endpoint, options) {
90
105
  var _a, _b;
106
+ if (options.browserType === 'none') {
107
+ return undefined;
108
+ }
109
+ const browserType = await playwright[options.browserType];
110
+ if (!browserType) {
111
+ console.error(`Can not open browser type: ${options.browserType}`);
112
+ return undefined;
113
+ }
91
114
  const args = [];
92
115
  if (process.platform === 'linux' && options.browserType === 'chromium') {
93
116
  args.push('--no-sandbox');
@@ -95,8 +118,8 @@ async function openBrowser(endpoint, options) {
95
118
  if (options.waitForDebugger) {
96
119
  args.push(`--remote-debugging-port=${options.waitForDebugger}`);
97
120
  }
98
- const headless = (_a = options.headless) !== null && _a !== void 0 ? _a : options.extensionDevelopmentPath !== undefined;
99
- const browser = await playwright[options.browserType].launch({ headless, args, devtools: options.devTools });
121
+ const headless = (_a = options.headless) !== null && _a !== void 0 ? _a : options.extensionTestsPath !== undefined;
122
+ const browser = await browserType.launch({ headless, args, devtools: options.devTools });
100
123
  const context = await browser.newContext();
101
124
  if (options.permissions) {
102
125
  context.grantPermissions(options.permissions);
@@ -116,6 +139,11 @@ async function openBrowser(endpoint, options) {
116
139
  if (options.waitForDebugger) {
117
140
  await page.waitForFunction(() => '__jsDebugIsReady' in globalThis);
118
141
  }
142
+ if (options.verbose) {
143
+ page.on('console', (message) => {
144
+ console.log(message.text());
145
+ });
146
+ }
119
147
  await page.setViewportSize({ width, height });
120
148
  await page.goto(endpoint);
121
149
  return context;
@@ -146,7 +174,7 @@ function valdiateBrowserType(browserType) {
146
174
  if (browserType === undefined) {
147
175
  return 'chromium';
148
176
  }
149
- if ((typeof browserType === 'string') && ['chromium', 'firefox', 'webkit'].includes(browserType)) {
177
+ if ((typeof browserType === 'string') && ['chromium', 'firefox', 'webkit', 'none'].includes(browserType)) {
150
178
  return browserType;
151
179
  }
152
180
  console.log(`Invalid browser type.`);
@@ -209,11 +237,24 @@ async function validatePath(loc, isFile) {
209
237
  }
210
238
  return loc;
211
239
  }
212
- function validateVersion(version) {
213
- if (version === undefined || ((typeof version === 'string') && ['insiders', 'stable', 'sources'].includes(version))) {
214
- return version;
240
+ function validateQuality(quality, version, vsCodeDevPath) {
241
+ if (version) {
242
+ console.log(`--version has been replaced by --quality`);
243
+ quality = quality || version;
244
+ }
245
+ if (vsCodeDevPath && quality) {
246
+ console.log(`Sources folder is provided as input, quality is ignored.`);
247
+ return undefined;
248
+ }
249
+ if (quality === undefined || ((typeof quality === 'string') && ['insiders', 'stable'].includes(quality))) {
250
+ return quality;
251
+ }
252
+ if (version === 'sources') {
253
+ console.log(`Instead of version=sources use 'sourcesPath' with the location of the VS Code repository.`);
254
+ }
255
+ else {
256
+ console.log(`Invalid quality.`);
215
257
  }
216
- console.log(`Invalid version.`);
217
258
  showHelp();
218
259
  process.exit(-1);
219
260
  }
@@ -228,40 +269,57 @@ function validatePortNumber(port) {
228
269
  }
229
270
  function showHelp() {
230
271
  console.log('Usage:');
231
- console.log(` --browserType 'chromium' | 'firefox' | 'webkit': The browser to launch. [Optional, default 'chromium']`);
272
+ console.log(` --browserType 'chromium' | 'firefox' | 'webkit' | 'none': The browser to launch. [Optional, defaults to 'chromium']`);
232
273
  console.log(` --extensionDevelopmentPath path: A path pointing to an extension under development to include. [Optional]`);
233
274
  console.log(` --extensionTestsPath path: A path to a test module to run. [Optional]`);
234
- console.log(` --version 'insiders' | 'stable' | 'sources' [Optional, default 'insiders']`);
275
+ console.log(` --quality 'insiders' | 'stable' [Optional, default 'insiders', ignored when running from sources]`);
276
+ console.log(` --sourcesPath path: If provided, running from VS Code sources at the given location [Optional]`);
235
277
  console.log(` --open-devtools: If set, opens the dev tools [Optional]`);
236
278
  console.log(` --headless: Whether to hide the browser. Defaults to true when an extensionTestsPath is provided, otherwise false. [Optional]`);
237
- console.log(` --hideServerLog: Whether to hide the server log. Defaults to true when an extensionTestsPath is provided, otherwise false. [Optional]`);
238
279
  console.log(` --permission: Permission granted in the opened browser: e.g. 'clipboard-read', 'clipboard-write': [Optional, Multiple]`);
239
280
  console.log(` --folder-uri: workspace to open VS Code on. Ignored when folderPath is provided [Optional]`);
240
281
  console.log(` --extensionPath: A path pointing to a folder containing additional extensions to include [Optional, Multiple]`);
282
+ console.log(` --host: The host name the server is opened on [Optional, defaults to localhost]`);
283
+ console.log(` --port: The port the server is opened on [Optional, defaults to 3000]`);
284
+ console.log(` --open-devtools: If set, opens the dev tools [Optional]`);
285
+ console.log(` --verbose: If set, prints out more information when running the server [Optional]`);
286
+ console.log(` --hideServerLog: Whether to hide the server log. Defaults to true when an extensionTestsPath is provided, otherwise false. [Optional]`);
241
287
  console.log(` folderPath. A local folder to open VS Code on. The folder content will be available as a virtual file system. [Optional]`);
242
288
  }
243
289
  async function cliMain() {
290
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
291
+ const manifest = require('../package.json');
292
+ console.log(`${manifest.name}: ${manifest.version}`);
244
293
  const options = {
245
- string: ['extensionDevelopmentPath', 'extensionTestsPath', 'browserType', 'version', 'waitForDebugger', 'folder-uri', 'permission', 'extensionPath'],
246
- boolean: ['open-devtools', 'headless', 'hideServerLog'],
294
+ string: ['extensionDevelopmentPath', 'extensionTestsPath', 'browserType', 'quality', 'version', 'waitForDebugger', 'folder-uri', 'permission', 'extensionPath', 'sourcesPath', 'host', 'port'],
295
+ boolean: ['open-devtools', 'headless', 'hideServerLog', 'help', 'verbose'],
247
296
  unknown: arg => {
248
297
  if (arg.startsWith('-')) {
249
298
  console.log(`Unknown argument ${arg}`);
250
- return false;
299
+ showHelp();
300
+ process.exit();
251
301
  }
252
302
  return true;
253
303
  }
254
304
  };
255
305
  const args = minimist(process.argv.slice(2), options);
306
+ if (args.help) {
307
+ showHelp();
308
+ process.exit();
309
+ }
256
310
  const browserType = valdiateBrowserType(args.browserType);
257
311
  const extensionTestsPath = await validatePathOrUndefined(args, 'extensionTestsPath', true);
258
312
  const extensionDevelopmentPath = await validatePathOrUndefined(args, 'extensionDevelopmentPath');
259
313
  const extensionPaths = await valdiateExtensionPaths(args.extensionPath);
260
- const version = validateVersion(args.version);
314
+ const vsCodeDevPath = await validatePathOrUndefined(args, 'sourcesPath');
315
+ const quality = validateQuality(args.quality, args.version, vsCodeDevPath);
261
316
  const devTools = validateBooleanOrUndefined(args, 'open-devtools');
262
317
  const headless = validateBooleanOrUndefined(args, 'headless');
263
318
  const permissions = valdiatePermissions(args.permission);
264
319
  const hideServerLog = validateBooleanOrUndefined(args, 'hideServerLog');
320
+ const verbose = validateBooleanOrUndefined(args, 'verbose');
321
+ const port = validatePortNumber(args.port);
322
+ const host = validateStringOrUndefined(args, 'host');
265
323
  const waitForDebugger = validatePortNumber(args.waitForDebugger);
266
324
  let folderUri = validateStringOrUndefined(args, 'folder-uri');
267
325
  let folderPath;
@@ -281,7 +339,7 @@ async function cliMain() {
281
339
  extensionTestsPath,
282
340
  extensionDevelopmentPath,
283
341
  browserType,
284
- version,
342
+ quality,
285
343
  devTools,
286
344
  waitForDebugger,
287
345
  folderUri,
@@ -289,14 +347,21 @@ async function cliMain() {
289
347
  headless,
290
348
  hideServerLog,
291
349
  permissions,
292
- extensionPaths
350
+ extensionPaths,
351
+ vsCodeDevPath,
352
+ verbose,
353
+ host,
354
+ port
355
+ }).catch(e => {
356
+ console.log(e.message);
357
+ process.exit(1);
293
358
  });
294
359
  }
295
360
  else {
296
361
  open({
297
362
  extensionDevelopmentPath,
298
363
  browserType,
299
- version,
364
+ quality,
300
365
  devTools,
301
366
  waitForDebugger,
302
367
  folderUri,
@@ -304,7 +369,11 @@ async function cliMain() {
304
369
  headless,
305
370
  hideServerLog,
306
371
  permissions,
307
- extensionPaths
372
+ extensionPaths,
373
+ vsCodeDevPath,
374
+ verbose,
375
+ host,
376
+ port
308
377
  });
309
378
  }
310
379
  }
package/out/server/app.js CHANGED
@@ -8,9 +8,10 @@ const Koa = require("koa");
8
8
  const morgan = require("koa-morgan");
9
9
  const kstatic = require("koa-static");
10
10
  const kmount = require("koa-mount");
11
+ const path_1 = require("path");
11
12
  const workbench_1 = require("./workbench");
12
- const path = require("path");
13
13
  const mounts_1 = require("./mounts");
14
+ const extensions_1 = require("./extensions");
14
15
  async function createApp(config) {
15
16
  const app = new Koa();
16
17
  if (!config.hideServerLog) {
@@ -21,19 +22,25 @@ async function createApp(config) {
21
22
  ctx.set('Access-Control-Allow-Origin', '*');
22
23
  return next();
23
24
  });
24
- app.use(kmount('/static', kstatic(path.join(__dirname, '../static'))));
25
+ const serveOptions = { hidden: true };
25
26
  if (config.extensionDevelopmentPath) {
26
27
  console.log('Serving dev extensions from ' + config.extensionDevelopmentPath);
27
- app.use(kmount('/static/devextensions', kstatic(config.extensionDevelopmentPath, { hidden: true })));
28
+ app.use(kmount('/static/devextensions', kstatic(config.extensionDevelopmentPath, serveOptions)));
28
29
  }
29
30
  if (config.build.type === 'static') {
30
- app.use(kmount('/static/build', kstatic(config.build.location, { hidden: true })));
31
+ app.use(kmount('/static/build', kstatic(config.build.location, serveOptions)));
32
+ }
33
+ else if (config.build.type === 'sources') {
34
+ app.use(kmount('/static/sources', kstatic(config.build.location, serveOptions)));
35
+ app.use(kmount('/static/sources', kstatic((0, path_1.join)(config.build.location, 'resources', 'server'), serveOptions))); // for manifest.json, favicon and code icons.
36
+ // built-in extension are at 'extensions` as well as prebuilt extensions dowloaded from the marketplace
37
+ app.use(kmount(`/static/sources/extensions`, kstatic((0, path_1.join)(config.build.location, extensions_1.prebuiltExtensionsLocation), serveOptions)));
31
38
  }
32
39
  (0, mounts_1.configureMounts)(config, app);
33
40
  if (config.extensionPaths) {
34
41
  config.extensionPaths.forEach((extensionPath, index) => {
35
42
  console.log('Serving additional built-in extensions from ' + extensionPath);
36
- app.use(kmount(`/static/extensions/${index}`, kstatic(extensionPath, { hidden: true })));
43
+ app.use(kmount(`/static/extensions/${index}`, kstatic(extensionPath, serveOptions)));
37
44
  });
38
45
  }
39
46
  app.use((0, workbench_1.default)(config));
@@ -70,7 +70,7 @@ async function downloadAndUnzipVSCode(quality) {
70
70
  const folderName = `vscode-web-${quality}-${info.version}`;
71
71
  const downloadedPath = path.resolve(vscodeTestDir, folderName);
72
72
  if ((0, fs_1.existsSync)(downloadedPath) && (0, fs_1.existsSync)(path.join(downloadedPath, 'version'))) {
73
- return { type: 'static', location: downloadedPath };
73
+ return { type: 'static', location: downloadedPath, quality, version: info.version };
74
74
  }
75
75
  if ((0, fs_1.existsSync)(vscodeTestDir)) {
76
76
  await fs_1.promises.rmdir(vscodeTestDir, { recursive: true, maxRetries: 5 });
@@ -95,7 +95,7 @@ async function downloadAndUnzipVSCode(quality) {
95
95
  // ignore
96
96
  }
97
97
  }
98
- return { type: 'static', location: downloadedPath };
98
+ return { type: 'static', location: downloadedPath, quality, version: info.version };
99
99
  }
100
100
  exports.downloadAndUnzipVSCode = downloadAndUnzipVSCode;
101
101
  async function fetch(api) {
@@ -4,7 +4,7 @@
4
4
  * Licensed under the MIT License. See License.txt in the project root for license information.
5
5
  *--------------------------------------------------------------------------------------------*/
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.scanForExtensions = void 0;
7
+ exports.getScannedBuiltinExtensions = exports.prebuiltExtensionsLocation = exports.scanForExtensions = void 0;
8
8
  const fs_1 = require("fs");
9
9
  const path = require("path");
10
10
  async function scanForExtensions(rootPath, serverURI) {
@@ -43,3 +43,11 @@ async function scanForExtensions(rootPath, serverURI) {
43
43
  return result;
44
44
  }
45
45
  exports.scanForExtensions = scanForExtensions;
46
+ exports.prebuiltExtensionsLocation = '.build/builtInExtensions';
47
+ async function getScannedBuiltinExtensions(vsCodeDevLocation) {
48
+ // use the build utility as to not duplicate the code
49
+ const extensionsUtil = await Promise.resolve().then(() => require(path.join(vsCodeDevLocation, 'build', 'lib', 'extensions.js')));
50
+ return extensionsUtil.scanBuiltinExtensions(path.join(vsCodeDevLocation, 'extensions'))
51
+ .concat(extensionsUtil.scanBuiltinExtensions(path.join(vsCodeDevLocation, exports.prebuiltExtensionsLocation)));
52
+ }
53
+ exports.getScannedBuiltinExtensions = getScannedBuiltinExtensions;
@@ -6,10 +6,10 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.runServer = void 0;
8
8
  const app_1 = require("./app");
9
- async function runServer(port, config) {
9
+ async function runServer(host, port, config) {
10
10
  const app = await (0, app_1.default)(config);
11
- const server = app.listen(port);
12
- console.log(`Listening on http://localhost:${port}`);
11
+ const server = app.listen(port, host);
12
+ console.log(`Listening on http://${host}:${port}`);
13
13
  return server;
14
14
  }
15
15
  exports.runServer = runServer;
@@ -47,9 +47,6 @@ class Workbench {
47
47
  return await (0, download_1.fetch)(`${this.baseUrl}/out/vs/code/browser/workbench/callback.html`);
48
48
  }
49
49
  }
50
- function valueOrFirst(value) {
51
- return Array.isArray(value) ? value[0] : value;
52
- }
53
50
  async function getWorkbenchOptions(ctx, config) {
54
51
  const options = {};
55
52
  if (config.extensionPaths) {
@@ -91,15 +88,9 @@ async function getWorkbenchOptions(ctx, config) {
91
88
  function default_1(config) {
92
89
  const router = new Router();
93
90
  router.use(async (ctx, next) => {
94
- if (ctx.query['dev'] || config.build.type === 'sources') {
95
- try {
96
- const builtInExtensions = await (0, download_1.fetchJSON)('http://localhost:8080/builtin');
97
- ctx.state.workbench = new Workbench('http://localhost:8080/static', true, builtInExtensions);
98
- }
99
- catch (err) {
100
- console.log(err);
101
- ctx.throw('Could not connect to localhost:8080, make sure you start `yarn web`', 400);
102
- }
91
+ if (config.build.type === 'sources') {
92
+ const builtInExtensions = await (0, extensions_1.getScannedBuiltinExtensions)(config.build.location);
93
+ ctx.state.workbench = new Workbench(`${ctx.protocol}://${ctx.host}/static/sources`, true, builtInExtensions);
103
94
  }
104
95
  else if (config.build.type === 'static') {
105
96
  ctx.state.workbench = new Workbench(`${ctx.protocol}://${ctx.host}/static/build`, false);
@@ -109,41 +100,13 @@ function default_1(config) {
109
100
  }
110
101
  await next();
111
102
  });
112
- const callbacks = new Map();
113
103
  router.get('/callback', async (ctx) => {
114
- const { 'vscode-requestId': vscodeRequestId, 'vscode-scheme': vscodeScheme, 'vscode-authority': vscodeAuthority, 'vscode-path': vscodePath, 'vscode-query': vscodeQuery, 'vscode-fragment': vscodeFragment, } = ctx.query;
115
- if (!vscodeRequestId || !vscodeScheme || !vscodeAuthority) {
116
- return ctx.throw(400);
117
- }
118
- const requestId = valueOrFirst(vscodeRequestId);
119
- const uri = vscode_uri_1.URI.from({
120
- scheme: valueOrFirst(vscodeScheme),
121
- authority: valueOrFirst(vscodeAuthority),
122
- path: valueOrFirst(vscodePath),
123
- query: valueOrFirst(vscodeQuery),
124
- fragment: valueOrFirst(vscodeFragment),
125
- });
126
- callbacks.set(requestId, uri);
127
104
  ctx.body = await ctx.state.workbench.renderCallback();
128
105
  });
129
- router.get('/fetch-callback', async (ctx) => {
130
- const { 'vscode-requestId': vscodeRequestId } = ctx.query;
131
- if (!vscodeRequestId) {
132
- return ctx.throw(400);
133
- }
134
- const requestId = valueOrFirst(vscodeRequestId);
135
- const uri = callbacks.get(requestId);
136
- if (!uri) {
137
- return ctx.throw(400);
138
- }
139
- callbacks.delete(requestId);
140
- ctx.body = uri.toJSON();
141
- });
142
106
  router.get('/', async (ctx) => {
143
107
  const options = await getWorkbenchOptions(ctx, config);
144
108
  ctx.body = await ctx.state.workbench.render(options);
145
109
  });
146
- //mountAPI(config, router);
147
110
  return router.routes();
148
111
  }
149
112
  exports.default = default_1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vscode/test-web",
3
- "version": "0.0.14",
3
+ "version": "0.0.18",
4
4
  "scripts": {
5
5
  "install-extensions": "yarn --cwd=fs-provider && yarn --cwd=sample",
6
6
  "compile": "tsc -p ./ && yarn compile-fs-provider",
@@ -11,8 +11,8 @@
11
11
  "postversion": "git push && git push --tags",
12
12
  "compile-fs-provider": "yarn --cwd=fs-provider compile-web",
13
13
  "compile-sample": "yarn --cwd=sample compile-web",
14
- "sample": "npm run compile && npm run compile-sample && node . --extensionDevelopmentPath=sample --browserType=chromium sample/test-workspace",
15
- "sample-tests": "npm run compile && npm run compile-sample && node . --extensionDevelopmentPath=sample --extensionTestsPath=sample/dist/web/test/suite/index.js --browserType=chromium --headless=false sample/test-workspace"
14
+ "sample": "npm run compile && npm run compile-sample && node . --extensionDevelopmentPath=sample --verbose sample/test-workspace",
15
+ "sample-tests": "npm run compile && npm run compile-sample && node . --extensionDevelopmentPath=sample --extensionTestsPath=sample/dist/web/test/suite/index.js --headless=false sample/test-workspace"
16
16
  },
17
17
  "main": "./out/index.js",
18
18
  "bin": {
@@ -7,6 +7,12 @@
7
7
  </script>
8
8
  <meta charset="utf-8" />
9
9
 
10
+ <!-- Mobile tweaks -->
11
+ <meta name="mobile-web-app-capable" content="yes" />
12
+ <meta name="apple-mobile-web-app-capable" content="yes" />
13
+ <meta name="apple-mobile-web-app-title" content="Code">
14
+ <link rel="apple-touch-icon" href="{{WORKBENCH_WEB_BASE_URL}}/code-192.png" />
15
+
10
16
  <!-- Disable pinch zooming -->
11
17
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
12
18