microsoft/vscode-react-native

Public

mirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
8f87e13531e1fc4465733c748fd76d4ae5710eaa

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/test/debugger/appWorker.test.ts

281lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import * as assert from "assert";
5import * as WebSocket from "ws";
6import * as path from "path";
7import * as Q from "q";
8import * as sinon from "sinon";
9
10import * as AppWorker from "../../debugger/appWorker";
11import {ScriptImporter} from "../../debugger/scriptImporter";
12import {Packager} from "../../common/packager";
13
14suite("appWorker", function() {
15 suite("debuggerContext", function() {
16 const packagerPort = 8081;
17
18 suite("SandboxedAppWorker", function() {
19 const sourcesStoragePath = path.resolve(__dirname, "assets");
20 const startScriptFileName = require.resolve(path.join(sourcesStoragePath, ScriptImporter.DEBUGGER_WORKER_FILE_BASENAME));
21 const debugAdapterPort = 9090;
22
23 let sandboxedWorker: AppWorker.SandboxedAppWorker;
24 let downloadAppScriptStub = sinon.stub();
25 let postReplyFunction = sinon.stub();
26 let readFileStub = sinon.stub();
27
28 setup(function() {
29 const nodeFileSystemMock: any = {
30 readFile: readFileStub,
31 };
32
33 const scriptImporterMock: any = {
34 downloadAppScript: downloadAppScriptStub,
35 };
36
37 sandboxedWorker = new AppWorker.SandboxedAppWorker(packagerPort, sourcesStoragePath, debugAdapterPort, postReplyFunction, {
38 nodeFileSystem: nodeFileSystemMock,
39 scriptImporter: scriptImporterMock,
40 });
41 });
42
43 teardown(function() {
44 // Reset everything
45 sandboxedWorker = null;
46
47 readFileStub = sinon.stub();
48 downloadAppScriptStub = sinon.stub();
49 postReplyFunction = sinon.stub();
50 });
51
52
53 test("should execute scripts correctly and be able to invoke the callback", function() {
54 const expectedMessageResult = { success: true };
55 const startScriptContents = `var testResponse = ${JSON.stringify(expectedMessageResult)}; postMessage(testResponse);`;
56
57 readFileStub.withArgs(startScriptFileName).returns(Q.resolve(startScriptContents));
58
59 return sandboxedWorker.start().then(() => {
60 assert(postReplyFunction.calledWithExactly(expectedMessageResult));
61 });
62 });
63
64 test("should be able to import scripts", function() {
65 const scriptImportPath = "testScript.js";
66 const startScriptContents = `importScripts("${scriptImportPath}"); postMessage("postImport");`;
67 readFileStub.withArgs(startScriptFileName).returns(Q.resolve(startScriptContents));
68
69 const testScriptContents = "postMessage('inImport')";
70 const scriptImportDeferred = Q.defer<void>();
71 downloadAppScriptStub.withArgs(scriptImportPath, debugAdapterPort).returns(scriptImportDeferred.promise.then(() => {
72 return {
73 contents: testScriptContents,
74 filepath: scriptImportPath,
75 };
76 }));
77
78 return sandboxedWorker.start().then(() => {
79 // We have not yet finished importing the script, we should not have posted a response yet
80 assert(postReplyFunction.notCalled, "postReplyFuncton called before scripts imported");
81 scriptImportDeferred.resolve(void 0);
82 return Q.delay(1);
83 }).then(() => {
84 assert(postReplyFunction.calledWith("postImport"), "postMessage after import not handled");
85 assert(postReplyFunction.calledWith("inImport"), "postMessage not registered from within import");
86 });
87 });
88
89 test("should correctly pass postMessage to the loaded script", function() {
90 const startScriptContents = `onmessage = postMessage;`;
91 readFileStub.withArgs(startScriptFileName).returns(Q.resolve(startScriptContents));
92
93 const testMessage = { method: "test", success: true };
94
95 return sandboxedWorker.start().then(() => {
96 assert(postReplyFunction.notCalled, "postRepyFunction called before message sent");
97 sandboxedWorker.postMessage(testMessage);
98 return Q.delay(1);
99 }).then(() => {
100 assert(postReplyFunction.calledWith({ data: testMessage }), "No echo back from app");
101 });
102 });
103
104 test("should be able to require an installed node module via __debug__.require", function () {
105 const expectedMessageResult = { qString: Q.toString() };
106 const startScriptContents = `var Q = __debug__.require('q');
107 var testResponse = { qString: Q.toString() };
108 postMessage(testResponse);`;
109
110 readFileStub.withArgs(startScriptFileName).returns(Q.resolve(startScriptContents));
111
112 return sandboxedWorker.start().then(() => {
113 assert(postReplyFunction.calledWithExactly(expectedMessageResult));
114 });
115 });
116 });
117
118 suite("MultipleLifetimesAppWorker", function() {
119 const sourcesStoragePath = path.resolve(__dirname, "assets");
120 const debugAdapterPort = 9090;
121
122 let multipleLifetimesWorker: AppWorker.MultipleLifetimesAppWorker;
123 let sandboxedAppWorkerStub: Sinon.SinonStub;
124 let webSocket: Sinon.SinonStub;
125 let sandboxedAppConstructor: Sinon.SinonStub;
126 let webSocketConstructor: Sinon.SinonStub;
127 let packagerIsRunning: Sinon.SinonStub;
128
129 let sendMessage: (message: string) => void;
130
131 let clock: Sinon.SinonFakeTimers;
132
133 setup(function() {
134 webSocket = sinon.createStubInstance(WebSocket);
135 sandboxedAppWorkerStub = sinon.createStubInstance(AppWorker.SandboxedAppWorker);
136
137 const messageInvocation: Sinon.SinonStub = (<any>webSocket).on.withArgs("message");
138 sendMessage = (message: string) => messageInvocation.callArgWith(1, message);
139
140 sandboxedAppConstructor = sinon.stub();
141 sandboxedAppConstructor.returns(sandboxedAppWorkerStub);
142 webSocketConstructor = sinon.stub();
143 webSocketConstructor.returns(webSocket);
144 packagerIsRunning = sinon.stub(Packager, "isPackagerRunning");
145 packagerIsRunning.returns(Q.resolve(true));
146
147 multipleLifetimesWorker = new AppWorker.MultipleLifetimesAppWorker(packagerPort, sourcesStoragePath, debugAdapterPort, {
148 sandboxedAppConstructor: sandboxedAppConstructor,
149 webSocketConstructor: webSocketConstructor,
150 });
151 });
152
153 teardown(function() {
154 // Reset everything
155 multipleLifetimesWorker = null;
156 webSocket = null;
157 sandboxedAppWorkerStub = null;
158 sandboxedAppConstructor = null;
159 webSocketConstructor = null;
160 sendMessage = null;
161 packagerIsRunning.restore();
162 packagerIsRunning = null;
163
164 if (clock) {
165 clock.restore();
166 clock = null;
167 }
168 });
169
170 test("with packager running should construct a websocket connection to the correct endpoint and listen for events", function() {
171 return multipleLifetimesWorker.start().then(() => {
172 const websocketRegex = new RegExp("ws://[^:]*:[0-9]*/debugger-proxy\\?role=debugger");
173 assert(webSocketConstructor.calledWithMatch(websocketRegex), "The web socket was not constructed to the correct url: " + webSocketConstructor.args[0][0]);
174
175 const expectedListeners = ["open", "close", "message", "error"];
176 expectedListeners.forEach((event) => {
177 assert((<any>webSocket).on.calledWithMatch(event), `Missing listener for ${event}`);
178 });
179 });
180 });
181
182 test("with packager running should attempt to reconnect after disconnecting", function() {
183 return multipleLifetimesWorker.start().then(() => {
184 // Forget previous invocations
185 webSocketConstructor.reset();
186 packagerIsRunning.returns(Q.resolve(true));
187
188 clock = sinon.useFakeTimers();
189
190 const closeInvocation: Sinon.SinonStub = (<any>webSocket).on.withArgs("close");
191 closeInvocation.callArg(1);
192
193 // Ensure that the retry is 100ms after the disconnection
194 clock.tick(99);
195 assert(webSocketConstructor.notCalled, "Attempted to reconnect too quickly");
196
197 clock.tick(1);
198 }).then(() => {
199 assert(webSocketConstructor.called);
200 });
201 });
202
203 test("with packager running should respond correctly to prepareJSRuntime messages", function() {
204 return multipleLifetimesWorker.start().then(() => {
205 const messageId = 1;
206 const testMessage = JSON.stringify({ method: "prepareJSRuntime", id: messageId });
207 const expectedReply = JSON.stringify({ replyID: messageId });
208
209 const appWorkerDeferred = Q.defer<void>();
210
211 const appWorkerStart: Sinon.SinonStub = (<any>sandboxedAppWorkerStub).start;
212 const websocketSend: Sinon.SinonStub = (<any>webSocket).send;
213
214 appWorkerStart.returns(appWorkerDeferred.promise);
215
216 sendMessage(testMessage);
217
218 assert(appWorkerStart.called, "SandboxedAppWorker not started in respones to prepareJSRuntime");
219 assert(websocketSend.notCalled, "Response sent prior to configuring sandbox worker");
220
221 appWorkerDeferred.resolve(void 0);
222
223 return Q.delay(1).then(() => {
224 assert(websocketSend.calledWith(expectedReply), "Did not receive the expected response to prepareJSRuntime");
225 });
226 });
227 });
228
229 test("with packager running should pass unknown messages to the sandboxedAppWorker", function() {
230 return multipleLifetimesWorker.start().then(() => {
231 // Start up an app worker
232 const prepareJSMessage = JSON.stringify({ method: "prepareJSRuntime", id: 1 });
233 const appWorkerStart: Sinon.SinonStub = (<any>sandboxedAppWorkerStub).start;
234 appWorkerStart.returns(Q.resolve(void 0));
235
236 sendMessage(prepareJSMessage);
237
238 // Then attempt to message it
239
240 const testMessage = { method: "unknownMethod" };
241 const testMessageString = JSON.stringify(testMessage);
242
243 const postMessageStub: Sinon.SinonStub = (<any>sandboxedAppWorkerStub).postMessage;
244
245 assert(postMessageStub.notCalled, "sandboxedAppWorker.postMessage called prior to any message");
246 sendMessage(testMessageString);
247
248 assert(postMessageStub.calledWith(testMessage), "message was not passed to sandboxedAppWorker");
249 });
250 });
251
252 test("with packager running should close connection if there is another debugger connected to packager", () => {
253 return multipleLifetimesWorker.start().then(() => {
254 // Forget previous invocations
255 webSocketConstructor.reset();
256 clock = sinon.useFakeTimers(new Date().getTime());
257
258 const closeInvocation: Sinon.SinonStub = (<any>webSocket).on.withArgs("close");
259 (<any>webSocket)._closeMessage = "Another debugger is already connected";
260 closeInvocation.callArg(1);
261
262 // Ensure it doesn't try to reconnect
263 clock.tick(100);
264 assert(webSocketConstructor.notCalled, "socket attempted to reconnect");
265 });
266 });
267
268 test("without packager running should not start if there is no packager running", () => {
269 packagerIsRunning.returns(Q.resolve(false));
270
271 return multipleLifetimesWorker.start()
272 .done(() => {
273 assert(webSocketConstructor.notCalled, "socket should not be created");
274 }, reason => {
275 assert(reason.message === `Cannot attach to packager. Are you sure there is a packager and it is running in the port ${packagerPort}? If your packager is configured to run in another port make sure to add that to the setting.json.`);
276 });
277 });
278 });
279
280 });
281});