microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
e45838cbf8bb84beab7d36042bcdbc57fe0319c8

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/test/debugger/appWorker.test.ts

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