-
Notifications
You must be signed in to change notification settings - Fork 210
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
See #31 R=kevmoo@google.com Review URL: https://codereview.chromium.org//1062683004
- Loading branch information
Showing
9 changed files
with
243 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
library test.runner.browser.safari; | ||
|
||
import 'dart:async'; | ||
import 'dart:convert'; | ||
import 'dart:io'; | ||
|
||
import 'package:path/path.dart' as p; | ||
|
||
import '../../util/io.dart'; | ||
import 'browser.dart'; | ||
|
||
/// A class for running an instance of Safari. | ||
/// | ||
/// Any errors starting or running the process are reported through [onExit]. | ||
class Safari implements Browser { | ||
/// The underlying process. | ||
Process _process; | ||
|
||
Future get onExit => _onExitCompleter.future; | ||
final _onExitCompleter = new Completer(); | ||
|
||
/// A future that completes when the browser process has started. | ||
/// | ||
/// This is used to ensure that [close] works regardless of when it's called. | ||
Future get _onProcessStarted => _onProcessStartedCompleter.future; | ||
final _onProcessStartedCompleter = new Completer(); | ||
|
||
/// Starts a new instance of Safari open to the given [url], which may be a | ||
/// [Uri] or a [String]. | ||
/// | ||
/// If [executable] is passed, it's used as the Safari executable. Otherwise | ||
/// the default executable name for the current OS will be used. | ||
Safari(url, {String executable}) { | ||
if (executable == null) { | ||
executable = '/Applications/Safari.app/Contents/MacOS/Safari'; | ||
} | ||
|
||
// Don't return a Future here because there's no need for the caller to wait | ||
// for the process to actually start. They should just wait for the HTTP | ||
// request instead. | ||
withTempDir((dir) { | ||
// Safari will only open files (not general URLs) via the command-line | ||
// API, so we create a dummy file to redirect it to the page we actually | ||
// want it to load. | ||
var redirect = p.join(dir, 'redirect.html'); | ||
new File(redirect).writeAsStringSync( | ||
"<script>location = " + JSON.encode(url.toString()) + "</script>"); | ||
|
||
return Process.start(executable, [redirect]).then((process) { | ||
_process = process; | ||
_onProcessStartedCompleter.complete(); | ||
|
||
// TODO(nweiz): the browser's standard output is almost always useless | ||
// noise, but we should allow the user to opt in to seeing it. | ||
return _process.exitCode; | ||
}); | ||
}).then((exitCode) { | ||
if (exitCode != 0) throw "Safari failed with exit code $exitCode."; | ||
}).then(_onExitCompleter.complete) | ||
.catchError(_onExitCompleter.completeError); | ||
} | ||
|
||
Future close() { | ||
_onProcessStarted.then((_) => _process.kill()); | ||
|
||
// Swallow exceptions. The user should explicitly use [onExit] for these. | ||
return onExit.catchError((_) {}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
@TestOn("vm && mac-os") | ||
|
||
import 'dart:async'; | ||
import 'dart:io'; | ||
|
||
import 'package:test/test.dart'; | ||
import 'package:test/src/runner/browser/safari.dart'; | ||
import 'package:test/src/util/io.dart'; | ||
import 'package:shelf/shelf.dart' as shelf; | ||
import 'package:shelf/shelf_io.dart' as shelf_io; | ||
import 'package:shelf_web_socket/shelf_web_socket.dart'; | ||
|
||
void main() { | ||
group("running JavaScript", () { | ||
// The JavaScript to serve in the server. We use actual JavaScript here to | ||
// avoid the pain of compiling to JS in a test | ||
var javaScript; | ||
|
||
var servePage = (request) { | ||
var path = request.url.path; | ||
|
||
// We support both shelf 0.5.x and 0.6.x. The former has a leading "/" | ||
// here, the latter does not. | ||
if (path.startsWith("/")) path = path.substring(1); | ||
|
||
if (path.isEmpty) { | ||
return new shelf.Response.ok(""" | ||
<!doctype html> | ||
<html> | ||
<head> | ||
<script src="index.js"></script> | ||
</head> | ||
</html> | ||
""", headers: {'content-type': 'text/html'}); | ||
} else if (path == "index.js") { | ||
return new shelf.Response.ok(javaScript, | ||
headers: {'content-type': 'application/javascript'}); | ||
} else { | ||
return new shelf.Response.notFound(null); | ||
} | ||
}; | ||
|
||
var server; | ||
var webSockets; | ||
setUp(() { | ||
var webSocketsController = new StreamController(); | ||
webSockets = webSocketsController.stream; | ||
|
||
return shelf_io.serve( | ||
new shelf.Cascade() | ||
.add(webSocketHandler(webSocketsController.add)) | ||
.add(servePage).handler, | ||
'localhost', 0).then((server_) { | ||
server = server_; | ||
}); | ||
}); | ||
|
||
tearDown(() { | ||
if (server != null) server.close(); | ||
|
||
javaScript = null; | ||
server = null; | ||
webSockets = null; | ||
}); | ||
|
||
test("starts Safari with the given URL", () { | ||
javaScript = ''' | ||
var webSocket = new WebSocket(window.location.href.replace("http://", "ws://")); | ||
webSocket.addEventListener("open", function() { | ||
webSocket.send("loaded!"); | ||
}); | ||
'''; | ||
var safari = new Safari(baseUrlForAddress(server.address, server.port)); | ||
|
||
return webSockets.first.then((webSocket) { | ||
return webSocket.first.then( | ||
(message) => expect(message, equals("loaded!"))); | ||
}).whenComplete(safari.close); | ||
}); | ||
|
||
test("doesn't preserve state across runs", () { | ||
javaScript = ''' | ||
localStorage.setItem("data", "value"); | ||
var webSocket = new WebSocket(window.location.href.replace("http://", "ws://")); | ||
webSocket.addEventListener("open", function() { | ||
webSocket.send("done"); | ||
}); | ||
'''; | ||
var safari = new Safari(baseUrlForAddress(server.address, server.port)); | ||
|
||
var first = true; | ||
webSockets.listen(expectAsync((webSocket) { | ||
if (first) { | ||
// The first request will set local storage data. We can't kill the | ||
// old safari and start a new one until we're sure that that has | ||
// finished. | ||
webSocket.first.then((_) { | ||
safari.close(); | ||
|
||
javaScript = ''' | ||
var webSocket = new WebSocket(window.location.href.replace("http://", "ws://")); | ||
webSocket.addEventListener("open", function() { | ||
webSocket.send(localStorage.getItem("data")); | ||
}); | ||
'''; | ||
safari = new Safari(baseUrlForAddress(server.address, server.port)); | ||
first = false; | ||
}); | ||
} else { | ||
// The second request will return the local storage data. This should | ||
// be null, indicating that no data was saved between runs. | ||
expect( | ||
webSocket.first | ||
.then((message) => expect(message, equals('null'))) | ||
.whenComplete(safari.close), | ||
completes); | ||
} | ||
}, count: 2)); | ||
}); | ||
}); | ||
|
||
test("a process can be killed synchronously after it's started", () { | ||
return shelf_io.serve(expectAsync((_) {}, count: 0), 'localhost', 0) | ||
.then((server) { | ||
var safari = new Safari(baseUrlForAddress(server.address, server.port)); | ||
return safari.close().whenComplete(server.close); | ||
}); | ||
}); | ||
|
||
test("reports an error in onExit", () { | ||
var safari = new Safari("http://dart-lang.org", | ||
executable: "_does_not_exist"); | ||
expect(safari.onExit, throwsA(new isInstanceOf<ProcessException>())); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters