microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0766856f41443745c16987486e91d4444e290f07

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/test/debugger/appWorker.test.ts

232lines · 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";
12
13suite("appWorker", function() {
14 suite("debuggerContext", function() {
15 const packagerPort = 8081;
16
17 suite("SandboxedAppWorker", function() {
18 const sourcesStoragePath = path.resolve(__dirname, "assets");
19 const startScriptFileName = require.resolve(path.join(sourcesStoragePath, ScriptImporter.DEBUGGER_WORKER_FILE_BASENAME));
20 const debugAdapterPort = 9090;
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, debugAdapterPort, 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, debugAdapterPort).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
104 suite("MultipleLifetimesAppWorker", function() {
105 const sourcesStoragePath = path.resolve(__dirname, "assets");
106 const debugAdapterPort = 9090;
107
108 let multipleLifetimesWorker: AppWorker.MultipleLifetimesAppWorker;
109 let sandboxedAppWorkerStub: Sinon.SinonStub;
110 let webSocket: Sinon.SinonStub;
111 let sandboxedAppConstructor: Sinon.SinonStub;
112 let webSocketConstructor: Sinon.SinonStub;
113
114 let sendMessage: (message: string) => void;
115
116 let clock: Sinon.SinonFakeTimers;
117
118 setup(function() {
119 webSocket = sinon.createStubInstance(WebSocket);
120 sandboxedAppWorkerStub = sinon.createStubInstance(AppWorker.SandboxedAppWorker);
121
122 const messageInvocation: Sinon.SinonStub = (<any>webSocket).on.withArgs("message");
123 sendMessage = (message: string) => messageInvocation.callArgWith(1, message);
124
125 sandboxedAppConstructor = sinon.stub();
126 sandboxedAppConstructor.returns(sandboxedAppWorkerStub);
127 webSocketConstructor = sinon.stub();
128 webSocketConstructor.returns(webSocket);
129
130 multipleLifetimesWorker = new AppWorker.MultipleLifetimesAppWorker(packagerPort, sourcesStoragePath, debugAdapterPort, {
131 sandboxedAppConstructor: sandboxedAppConstructor,
132 webSocketConstructor: webSocketConstructor,
133 });
134 });
135
136 teardown(function() {
137 // Reset everything
138 multipleLifetimesWorker = null;
139 webSocket = null;
140 sandboxedAppWorkerStub = null;
141 sandboxedAppConstructor = null;
142 webSocketConstructor = null;
143 sendMessage = null;
144
145 if (clock) {
146 clock.restore();
147 clock = null;
148 }
149 });
150
151 test("should construct a websocket connection to the correct endpoint and listen for events", function() {
152 return multipleLifetimesWorker.start().then(() => {
153 const websocketRegex = new RegExp("ws://[^:]*:[0-9]*/debugger-proxy\\?role=debugger");
154 assert(webSocketConstructor.calledWithMatch(websocketRegex), "The web socket was not constructed to the correct url: " + webSocketConstructor.args[0][0]);
155
156 const expectedListeners = ["open", "close", "message", "error"];
157 expectedListeners.forEach((event) => {
158 assert((<any>webSocket).on.calledWithMatch(event), `Missing listener for ${event}`);
159 });
160 });
161 });
162
163 test("should attempt to reconnect after disconnecting", function() {
164 return multipleLifetimesWorker.start().then(() => {
165 // Forget previous invocations
166 webSocketConstructor.reset();
167
168 clock = sinon.useFakeTimers();
169
170 const closeInvocation: Sinon.SinonStub = (<any>webSocket).on.withArgs("close");
171 closeInvocation.callArg(1);
172
173 // Ensure that the retry is 100ms after the disconnection
174 clock.tick(99);
175 assert(webSocketConstructor.notCalled, "Attempted to reconnect too quickly");
176
177 clock.tick(1);
178 assert(webSocketConstructor.called);
179 });
180 });
181
182 test("should respond correctly to prepareJSRuntime messages", function() {
183 return multipleLifetimesWorker.start().then(() => {
184 const messageId = 1;
185 const testMessage = JSON.stringify({ method: "prepareJSRuntime", id: messageId });
186 const expectedReply = JSON.stringify({ replyID: messageId });
187
188 const appWorkerDeferred = Q.defer<void>();
189
190 const appWorkerStart: Sinon.SinonStub = (<any>sandboxedAppWorkerStub).start;
191 const websocketSend: Sinon.SinonStub = (<any>webSocket).send;
192
193 appWorkerStart.returns(appWorkerDeferred.promise);
194
195 sendMessage(testMessage);
196
197 assert(appWorkerStart.called, "SandboxedAppWorker not started in respones to prepareJSRuntime");
198 assert(websocketSend.notCalled, "Response sent prior to configuring sandbox worker");
199
200 appWorkerDeferred.resolve(void 0);
201
202 return Q.delay(1).then(() => {
203 assert(websocketSend.calledWith(expectedReply), "Did not receive the expected response to prepareJSRuntime");
204 });
205 });
206 });
207
208 test("should pass unknown messages to the sandboxedAppWorker", function() {
209 return multipleLifetimesWorker.start().then(() => {
210 // Start up an app worker
211 const prepareJSMessage = JSON.stringify({ method: "prepareJSRuntime", id: 1 });
212 const appWorkerStart: Sinon.SinonStub = (<any>sandboxedAppWorkerStub).start;
213 appWorkerStart.returns(Q.resolve(void 0));
214
215 sendMessage(prepareJSMessage);
216
217 // Then attempt to message it
218
219 const testMessage = { method: "unknownMethod" };
220 const testMessageString = JSON.stringify(testMessage);
221
222 const postMessageStub: Sinon.SinonStub = (<any>sandboxedAppWorkerStub).postMessage;
223
224 assert(postMessageStub.notCalled, "sandboxedAppWorker.postMessage called prior to any message");
225 sendMessage(testMessageString);
226
227 assert(postMessageStub.calledWith(testMessage), "message was not passed to sandboxedAppWorker");
228 });
229 });
230 });
231 });
232});