| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library test.runner.browser.host; | 5 library test.runner.browser.host; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:convert'; | 8 import 'dart:convert'; |
| 9 import 'dart:html'; | 9 import 'dart:html'; |
| 10 import 'dart:js' as js; | 10 import 'dart:js' as js; |
| 11 | 11 |
| 12 import 'package:stack_trace/stack_trace.dart'; | 12 import 'package:stack_trace/stack_trace.dart'; |
| 13 import 'package:test/src/util/multi_channel.dart'; | 13 import 'package:test/src/util/multi_channel.dart'; |
| 14 import 'package:test/src/util/stream_channel.dart'; | 14 import 'package:test/src/util/stream_channel.dart'; |
| 15 | 15 |
| 16 /// The iframes created for each loaded test suite, indexed by the suite id. |
| 17 final _iframes = new Map<int, IFrameElement>(); |
| 18 |
| 16 // TODO(nweiz): test this once we can run browser tests. | 19 // TODO(nweiz): test this once we can run browser tests. |
| 17 /// Code that runs in the browser and loads test suites at the server's behest. | 20 /// Code that runs in the browser and loads test suites at the server's behest. |
| 18 /// | 21 /// |
| 19 /// One instance of this runs for each browser. When the server tells it to load | 22 /// One instance of this runs for each browser. When the server tells it to load |
| 20 /// a test, it starts an iframe pointing at that test's code; from then on, it | 23 /// a test, it starts an iframe pointing at that test's code; from then on, it |
| 21 /// just relays messages between the two. | 24 /// just relays messages between the two. |
| 22 /// | 25 /// |
| 23 /// The browser uses two layers of [MultiChannel]s when communicating with the | 26 /// The browser uses two layers of [MultiChannel]s when communicating with the |
| 24 /// server: | 27 /// server: |
| 25 /// | 28 /// |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 67 /// the same place the client does. | 70 /// the same place the client does. |
| 68 void main() { | 71 void main() { |
| 69 // This tells content_shell not to close immediately after the page has | 72 // This tells content_shell not to close immediately after the page has |
| 70 // rendered. | 73 // rendered. |
| 71 var testRunner = js.context['testRunner']; | 74 var testRunner = js.context['testRunner']; |
| 72 if (testRunner != null) testRunner.callMethod('waitUntilDone', []); | 75 if (testRunner != null) testRunner.callMethod('waitUntilDone', []); |
| 73 | 76 |
| 74 runZoned(() { | 77 runZoned(() { |
| 75 var serverChannel = _connectToServer(); | 78 var serverChannel = _connectToServer(); |
| 76 serverChannel.stream.listen((message) { | 79 serverChannel.stream.listen((message) { |
| 77 assert(message['command'] == 'loadSuite'); | 80 if (message['command'] == 'loadSuite') { |
| 78 var suiteChannel = serverChannel.virtualChannel(message['channel']); | 81 var suiteChannel = serverChannel.virtualChannel(message['channel']); |
| 79 var iframeChannel = _connectToIframe(message['url']); | 82 var iframeChannel = _connectToIframe(message['url'], message['id']); |
| 80 suiteChannel.pipe(iframeChannel); | 83 suiteChannel.pipe(iframeChannel); |
| 84 } else { |
| 85 assert(message['command'] == 'closeSuite'); |
| 86 _iframes[message['id']].remove(); |
| 87 } |
| 81 }); | 88 }); |
| 82 }, onError: (error, stackTrace) { | 89 }, onError: (error, stackTrace) { |
| 83 print("$error\n${new Trace.from(stackTrace).terse}"); | 90 print("$error\n${new Trace.from(stackTrace).terse}"); |
| 84 }); | 91 }); |
| 85 } | 92 } |
| 86 | 93 |
| 87 /// Creates a [MultiChannel] connection to the server, using a [WebSocket] as | 94 /// Creates a [MultiChannel] connection to the server, using a [WebSocket] as |
| 88 /// the underlying protocol. | 95 /// the underlying protocol. |
| 89 MultiChannel _connectToServer() { | 96 MultiChannel _connectToServer() { |
| 90 // The `managerUrl` query parameter contains the WebSocket URL of the remote | 97 // The `managerUrl` query parameter contains the WebSocket URL of the remote |
| 91 // [BrowserManager] with which this communicates. | 98 // [BrowserManager] with which this communicates. |
| 92 var currentUrl = Uri.parse(window.location.href); | 99 var currentUrl = Uri.parse(window.location.href); |
| 93 var webSocket = new WebSocket(currentUrl.queryParameters['managerUrl']); | 100 var webSocket = new WebSocket(currentUrl.queryParameters['managerUrl']); |
| 94 | 101 |
| 95 var inputController = new StreamController(sync: true); | 102 var inputController = new StreamController(sync: true); |
| 96 webSocket.onMessage.listen( | 103 webSocket.onMessage.listen( |
| 97 (message) => inputController.add(JSON.decode(message.data))); | 104 (message) => inputController.add(JSON.decode(message.data))); |
| 98 | 105 |
| 99 var outputController = new StreamController(sync: true); | 106 var outputController = new StreamController(sync: true); |
| 100 outputController.stream.listen( | 107 outputController.stream.listen( |
| 101 (message) => webSocket.send(JSON.encode(message))); | 108 (message) => webSocket.send(JSON.encode(message))); |
| 102 | 109 |
| 103 return new MultiChannel(inputController.stream, outputController.sink); | 110 return new MultiChannel(inputController.stream, outputController.sink); |
| 104 } | 111 } |
| 105 | 112 |
| 106 /// Creates an iframe with `src` [url] and establishes a connection to it using | 113 /// Creates an iframe with `src` [url] and establishes a connection to it using |
| 107 /// `postMessage`. | 114 /// `postMessage`. |
| 108 StreamChannel _connectToIframe(String url) { | 115 /// |
| 116 /// [id] identifies the suite loaded in this iframe. |
| 117 StreamChannel _connectToIframe(String url, int id) { |
| 109 var iframe = new IFrameElement(); | 118 var iframe = new IFrameElement(); |
| 119 _iframes[id] = iframe; |
| 110 iframe.src = url; | 120 iframe.src = url; |
| 111 document.body.children.add(iframe); | 121 document.body.children.add(iframe); |
| 112 | 122 |
| 113 var inputController = new StreamController(sync: true); | 123 var inputController = new StreamController(sync: true); |
| 114 var outputController = new StreamController(sync: true); | 124 var outputController = new StreamController(sync: true); |
| 115 | 125 |
| 116 // Use this to avoid sending a message to the iframe before it's sent a | 126 // Use this to avoid sending a message to the iframe before it's sent a |
| 117 // message to us. This ensures that no messages get dropped on the floor. | 127 // message to us. This ensures that no messages get dropped on the floor. |
| 118 var readyCompleter = new Completer(); | 128 var readyCompleter = new Completer(); |
| 119 | 129 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 135 if (!readyCompleter.isCompleted) readyCompleter.complete(); | 145 if (!readyCompleter.isCompleted) readyCompleter.complete(); |
| 136 }); | 146 }); |
| 137 | 147 |
| 138 outputController.stream.listen((message) async { | 148 outputController.stream.listen((message) async { |
| 139 await readyCompleter.future; | 149 await readyCompleter.future; |
| 140 iframe.contentWindow.postMessage(message, window.location.origin); | 150 iframe.contentWindow.postMessage(message, window.location.origin); |
| 141 }); | 151 }); |
| 142 | 152 |
| 143 return new StreamChannel(inputController.stream, outputController.sink); | 153 return new StreamChannel(inputController.stream, outputController.sink); |
| 144 } | 154 } |
| OLD | NEW |