microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
75c7d921c8412f73630c6d1964c571077e286014

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/test/debugger/appWorker.test.ts

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