Skip to content

Commit 8637a09

Browse files
authored
Move Webdev injected client tests over to DWDS (#495)
* cleanup tests
1 parent 500416f commit 8637a09

File tree

12 files changed

+315
-492
lines changed

12 files changed

+315
-492
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<html>
2+
3+
<head>
4+
<script defer src="main.dart.js"></script>
5+
</head>
6+
7+
</html>

webdev/test/serve/injected/fixtures/web/main.dart renamed to _test/example/append_body/main.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ void main() {
1313
print('Count is: ${++count}');
1414
});
1515

16-
document.body.appendText('Hello World!!');
16+
document.body.appendText('Hello World!');
1717

1818
registerExtension('ext.flutter.disassemble', (_, __) async {
1919
document.body.appendText('start disassemble ');

dwds/test/devtools_test.dart

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
@Timeout(Duration(minutes: 5))
6+
import 'package:test/test.dart';
7+
import 'package:webdriver/io.dart';
8+
9+
import 'fixtures/context.dart';
10+
11+
final context = TestContext(
12+
path: 'append_body/index.html',
13+
);
14+
15+
void main() {
16+
group('Injected client', () {
17+
setUp(() async {
18+
await context.setUp(serveDevTools: true);
19+
await context.webDriver.driver.keyboard.sendChord([Keyboard.alt, 'd']);
20+
// Wait for DevTools to actually open.
21+
await Future.delayed(const Duration(seconds: 1));
22+
});
23+
24+
tearDown(() async {
25+
await context.tearDown();
26+
});
27+
28+
test('can launch devtools', () async {
29+
var windows = await context.webDriver.windows.toList();
30+
await context.webDriver.driver.switchTo.window(windows.last);
31+
expect(await context.webDriver.title, 'Dart DevTools');
32+
});
33+
34+
test('can not launch devtools for the same app in multiple tabs', () async {
35+
var appUrl = await context.webDriver.currentUrl;
36+
// Open a new tab, select it, and navigate to the app
37+
await context.webDriver.driver
38+
.execute("window.open('$appUrl', '_blank');", []);
39+
await Future.delayed(const Duration(seconds: 1));
40+
var windows = await context.webDriver.windows.toList();
41+
var oldAppWindow = windows[0];
42+
var newAppWindow = windows[1];
43+
var devToolsWindow = windows[2];
44+
await newAppWindow.setAsActive();
45+
46+
// Try to open devtools and check for the alert.
47+
await context.webDriver.driver.keyboard.sendChord([Keyboard.alt, 'd']);
48+
await Future.delayed(const Duration(seconds: 1));
49+
var alert = context.webDriver.driver.switchTo.alert;
50+
expect(alert, isNotNull);
51+
expect(await alert.text,
52+
contains('This app is already being debugged in a different tab'));
53+
await alert.accept();
54+
55+
// Now close the old app and try to re-open devtools.
56+
await oldAppWindow.setAsActive();
57+
await oldAppWindow.close();
58+
await devToolsWindow.setAsActive();
59+
await devToolsWindow.close();
60+
await newAppWindow.setAsActive();
61+
await context.webDriver.driver.keyboard.sendChord([Keyboard.alt, 'd']);
62+
await Future.delayed(const Duration(seconds: 1));
63+
windows = await context.webDriver.windows.toList();
64+
devToolsWindow = windows.firstWhere((window) => window != newAppWindow);
65+
await devToolsWindow.setAsActive();
66+
expect(await context.webDriver.title, 'Dart DevTools');
67+
});
68+
});
69+
70+
group('Injected client without DevTools', () {
71+
setUp(() async {
72+
await context.setUp(serveDevTools: false);
73+
});
74+
75+
tearDown(() async {
76+
await context.tearDown();
77+
});
78+
79+
test('gives a good error if devtools is not served', () async {
80+
// Try to open devtools and check for the alert.
81+
await context.webDriver.driver.keyboard.sendChord([Keyboard.alt, 'd']);
82+
await Future.delayed(const Duration(seconds: 1));
83+
var alert = context.webDriver.driver.switchTo.alert;
84+
expect(alert, isNotNull);
85+
expect(await alert.text, contains('--debug'));
86+
await alert.accept();
87+
});
88+
});
89+
}

dwds/test/fixtures/context.dart

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ class TestContext {
3131
DebugConnection debugConnection;
3232
ChromeProxyService chromeProxyService;
3333
int port;
34+
File _entryFile;
35+
String _entryContents;
3436

3537
/// Top level directory in which we run the test server..
3638
String workingDirectory;
@@ -47,9 +49,15 @@ class TestContext {
4749
this.pathToServe = 'example'}) {
4850
workingDirectory = p.normalize(
4951
p.absolute(directory ?? p.relative('../_test', from: p.current)));
52+
_entryFile = File(p.absolute(p.join(p.relative('../_test', from: p.current),
53+
'example', 'append_body', 'main.dart')));
54+
_entryContents = _entryFile.readAsStringSync();
5055
}
5156

52-
Future<void> setUp() async {
57+
Future<void> setUp(
58+
{ReloadConfiguration reloadConfiguration, bool serveDevTools}) async {
59+
reloadConfiguration ??= ReloadConfiguration.none;
60+
serveDevTools ??= false;
5361
port = await findUnusedPort();
5462
try {
5563
chromeDriver = await Process.start(
@@ -70,7 +78,7 @@ class TestContext {
7078
await daemonClient.buildResults
7179
.firstWhere((results) => results.results
7280
.any((result) => result.status == BuildStatus.succeeded))
73-
.timeout(Duration(seconds: 60));
81+
.timeout(const Duration(seconds: 60));
7482

7583
var debugPort = await findUnusedPort();
7684
var capabilities = Capabilities.chrome
@@ -89,6 +97,8 @@ class TestContext {
8997
pathToServe,
9098
daemonClient.buildResults,
9199
() async => connection,
100+
reloadConfiguration,
101+
serveDevTools,
92102
);
93103

94104
appUrl = 'http://localhost:$port/$path';
@@ -110,9 +120,22 @@ class TestContext {
110120
}
111121

112122
Future<Null> tearDown() async {
123+
_entryFile.writeAsStringSync(_entryContents);
113124
await daemonClient.close();
114125
await testServer.stop();
115126
await webDriver?.quit();
116127
chromeDriver.kill();
117128
}
129+
130+
Future<void> changeInput() async {
131+
_entryFile.writeAsStringSync(
132+
_entryContents.replaceAll('Hello World!', 'Gary is awesome!'));
133+
134+
// Wait for the build.
135+
await daemonClient.buildResults.firstWhere((results) => results.results
136+
.any((result) => result.status == BuildStatus.succeeded));
137+
138+
// Allow change to propagate to the browser.
139+
await Future.delayed(const Duration(seconds: 2));
140+
}
118141
}

dwds/test/fixtures/server.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ class TestServer {
4646
String target,
4747
Stream<BuildResults> buildResults,
4848
Future<ChromeConnection> Function() chromeConnection,
49+
ReloadConfiguration reloadConfiguration,
50+
bool serveDevTools,
4951
) async {
5052
var pipeline = const Pipeline();
5153

@@ -59,6 +61,8 @@ class TestServer {
5961
buildResults: filteredBuildResults,
6062
chromeConnection: chromeConnection,
6163
logWriter: (level, message) => printOnFailure(message),
64+
reloadConfiguration: reloadConfiguration,
65+
serveDevTools: serveDevTools,
6266
verbose: true,
6367
);
6468

dwds/test/reload_test.dart

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
@Timeout(Duration(minutes: 5))
6+
import 'dart:io';
7+
8+
import 'package:dwds/dwds.dart';
9+
import 'package:test/test.dart';
10+
import 'package:vm_service_lib/vm_service_lib.dart';
11+
12+
import 'fixtures/context.dart';
13+
14+
final context = TestContext(
15+
path: 'append_body/index.html',
16+
);
17+
18+
void main() {
19+
group('Injected client with live reload', () {
20+
setUp(() async {
21+
await context.setUp(reloadConfiguration: ReloadConfiguration.liveReload);
22+
});
23+
24+
tearDown(() async {
25+
await context.tearDown();
26+
});
27+
28+
test('can live reload changes ', () async {
29+
await context.changeInput();
30+
31+
var source = await context.webDriver.pageSource;
32+
33+
// A full reload should clear the state.
34+
expect(source.contains('Hello World!'), isFalse);
35+
expect(source.contains('Gary is awesome!'), isTrue);
36+
});
37+
});
38+
39+
group('Injected client', () {
40+
setUp(() async {
41+
await context.setUp();
42+
});
43+
44+
tearDown(() async {
45+
await context.tearDown();
46+
});
47+
48+
test('destroys and recreates the isolate during a hot restart', () async {
49+
var client = context.debugConnection.vmService;
50+
await client.streamListen('Isolate');
51+
await context.changeInput();
52+
53+
var eventsDone = expectLater(
54+
client.onIsolateEvent,
55+
emitsThrough(emitsInOrder([
56+
_hasKind(EventKind.kIsolateExit),
57+
_hasKind(EventKind.kIsolateStart),
58+
_hasKind(EventKind.kIsolateRunnable),
59+
])));
60+
61+
expect(await client.callServiceExtension('hotRestart'),
62+
const TypeMatcher<Success>());
63+
64+
await eventsDone;
65+
});
66+
67+
test('destroys and recreates the isolate during a page refresh', () async {
68+
var client = context.debugConnection.vmService;
69+
await client.streamListen('Isolate');
70+
await context.changeInput();
71+
72+
var eventsDone = expectLater(
73+
client.onIsolateEvent,
74+
emitsThrough(emitsInOrder([
75+
_hasKind(EventKind.kIsolateExit),
76+
_hasKind(EventKind.kIsolateStart),
77+
_hasKind(EventKind.kIsolateRunnable),
78+
])));
79+
80+
await context.webDriver.driver.refresh();
81+
82+
await eventsDone;
83+
});
84+
85+
test('can hot restart via the service extension', () async {
86+
var client = context.debugConnection.vmService;
87+
await context.changeInput();
88+
89+
expect(await client.callServiceExtension('hotRestart'),
90+
const TypeMatcher<Success>());
91+
await Future.delayed(const Duration(seconds: 2));
92+
93+
var source = await context.webDriver.pageSource;
94+
// Main is re-invoked which shouldn't clear the state.
95+
expect(source, contains('Hello World!'));
96+
expect(source, contains('Gary is awesome!'));
97+
});
98+
99+
test('can refresh the page via the fullReload service extension', () async {
100+
var client = context.debugConnection.vmService;
101+
await context.changeInput();
102+
103+
expect(await client.callServiceExtension('fullReload'), isA<Success>());
104+
await Future.delayed(const Duration(seconds: 2));
105+
106+
var source = await context.webDriver.pageSource;
107+
// Should see only the new text
108+
expect(source, isNot(contains('Hello World!')));
109+
expect(source, contains('Gary is awesome!'));
110+
});
111+
112+
test('can hot restart while paused', () async {
113+
var client = context.debugConnection.vmService;
114+
var vm = await client.getVM();
115+
var isolateId = vm.isolates.first.id;
116+
await client.streamListen('Debug');
117+
var stream = client.onEvent('Debug');
118+
var scriptList = await client.getScripts(isolateId);
119+
var main = scriptList.scripts
120+
.firstWhere((script) => script.uri.contains('main.dart'));
121+
await client.addBreakpoint(isolateId, main.id, 13);
122+
await stream
123+
.firstWhere((event) => event.kind == EventKind.kPauseBreakpoint);
124+
125+
await context.changeInput();
126+
await client.streamListen('Isolate');
127+
stream = client.onEvent('Isolate');
128+
await client.callServiceExtension('hotRestart');
129+
await stream.firstWhere((event) => event.kind == EventKind.kIsolateStart);
130+
var source = await context.webDriver.pageSource;
131+
132+
// Main is re-invoked which shouldn't clear the state.
133+
expect(source.contains('Hello World!'), isTrue);
134+
expect(source.contains('Gary is awesome!'), isTrue);
135+
136+
// Should not be paused.
137+
vm = await client.getVM();
138+
isolateId = vm.isolates.first.id;
139+
var isolate = await client.getIsolate(isolateId) as Isolate;
140+
expect(isolate.pauseEvent.kind, EventKind.kResume);
141+
expect(isolate.breakpoints.isEmpty, isTrue);
142+
143+
// TODO(sdk/issues/37364) - Remove once corresponding SDK issue is fixed.
144+
}, skip: Platform.isWindows);
145+
});
146+
147+
group('Injected client with hot restart', () {
148+
setUp(() async {
149+
await context.setUp(reloadConfiguration: ReloadConfiguration.hotRestart);
150+
});
151+
152+
tearDown(() async {
153+
await context.tearDown();
154+
});
155+
156+
test('can hot restart changes ', () async {
157+
await context.changeInput();
158+
159+
var source = await context.webDriver.pageSource;
160+
161+
// Main is re-invoked which shouldn't clear the state.
162+
expect(source.contains('Hello World!'), isTrue);
163+
expect(source.contains('Gary is awesome!'), isTrue);
164+
// The ext.flutter.disassemble callback is invoked and waited for.
165+
expect(source,
166+
contains('start disassemble end disassemble Gary is awesome'));
167+
});
168+
169+
test('fires isolate create/destroy events during hot restart', () async {
170+
var client = context.debugConnection.vmService;
171+
await client.streamListen('Isolate');
172+
173+
var eventsDone = expectLater(
174+
client.onIsolateEvent,
175+
emitsThrough(emitsInOrder([
176+
_hasKind(EventKind.kIsolateExit),
177+
_hasKind(EventKind.kIsolateStart),
178+
_hasKind(EventKind.kIsolateRunnable),
179+
])));
180+
181+
await context.changeInput();
182+
183+
await eventsDone;
184+
});
185+
});
186+
}
187+
188+
TypeMatcher<Event> _hasKind(String kind) =>
189+
isA<Event>().having((e) => e.kind, 'kind', kind);
File renamed without changes.

0 commit comments

Comments
 (0)