web_test.dart 24.4 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
// @dart = 2.8

7
import 'package:file_testing/file_testing.dart';
8
import 'package:flutter_tools/src/artifacts.dart';
9
import 'package:flutter_tools/src/base/file_system.dart';
10
import 'package:flutter_tools/src/base/platform.dart';
11
import 'package:flutter_tools/src/build_info.dart';
12
import 'package:flutter_tools/src/build_system/build_system.dart';
13
import 'package:flutter_tools/src/build_system/depfile.dart';
14
import 'package:flutter_tools/src/build_system/targets/common.dart';
15
import 'package:flutter_tools/src/build_system/targets/web.dart';
16
import 'package:flutter_tools/src/globals.dart' as globals;
17
import 'package:process/process.dart';
18 19

import '../../../src/common.dart';
20
import '../../../src/context.dart';
21 22
import '../../../src/testbed.dart';

23 24
const List<String> kDart2jsLinuxArgs = <String>[
  'bin/cache/dart-sdk/bin/dart',
25
   '--disable-dart-dev',
26 27 28 29
  'bin/cache/dart-sdk/bin/snapshots/dart2js.dart.snapshot',
  '--libraries-spec=bin/cache/flutter_web_sdk/libraries.json',
];

30 31 32
void main() {
  Testbed testbed;
  Environment environment;
33 34 35 36 37 38 39 40 41
  FakeProcessManager processManager;
  final Platform linux = FakePlatform(
    operatingSystem: 'linux',
    environment: <String, String>{},
  );
  final Platform windows = FakePlatform(
    operatingSystem: 'windows',
    environment: <String, String>{},
  );
42
  DepfileService depfileService;
43 44 45

  setUp(() {
    testbed = Testbed(setup: () {
46
      globals.fs.file('.packages')
47
        ..createSync(recursive: true)
48
        ..writeAsStringSync('foo:foo/lib/\n');
49
      globals.fs.currentDirectory.childDirectory('bar').createSync();
50
      processManager = FakeProcessManager.list(<FakeCommand>[]);
51

52 53
      environment = Environment.test(
        globals.fs.currentDirectory,
54
        projectDir: globals.fs.currentDirectory.childDirectory('foo'),
55
        outputDir: globals.fs.currentDirectory.childDirectory('bar'),
56
        defines: <String, String>{
57
          kTargetFile: globals.fs.path.join('foo', 'lib', 'main.dart'),
58
        },
59
        artifacts: Artifacts.test(),
60 61 62
        processManager: FakeProcessManager.any(),
        logger: globals.logger,
        fileSystem: globals.fs,
63
      );
64 65 66 67
      depfileService = DepfileService(
      fileSystem: globals.fs,
      logger: globals.logger,
    );
68 69
      environment.buildDir.createSync(recursive: true);
    }, overrides: <Type, Generator>{
70
      Platform: () => linux,
71 72 73 74
    });
  });

  test('WebEntrypointTarget generates an entrypoint with plugins and init platform', () => testbed.run(() async {
75 76 77 78
    final File mainFile = globals.fs.file(globals.fs.path.join('foo', 'lib', 'main.dart'))
      ..createSync(recursive: true)
      ..writeAsStringSync('void main() {}');
    environment.defines[kTargetFile] = mainFile.path;
79 80 81 82 83 84
    environment.defines[kHasWebPlugins] = 'true';
    await const WebEntrypointTarget().build(environment);

    final String generated = environment.buildDir.childFile('main.dart').readAsStringSync();

    // Plugins
85
    expect(generated, contains("import 'package:foo/generated_plugin_registrant.dart';"));
86
    expect(generated, contains('registerPlugins(webPluginRegistrar);'));
87 88 89 90 91

    // Main
    expect(generated, contains('entrypoint.main();'));

    // Import.
92
    expect(generated, contains("import 'package:foo/main.dart' as entrypoint;"));
93 94
  }));

95 96 97 98 99 100 101 102 103 104 105
  test('version.json is created after release build', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'release';
    final Directory webResources = environment.projectDir.childDirectory('web');
    webResources.childFile('index.html')
        .createSync(recursive: true);
    environment.buildDir.childFile('main.dart.js').createSync();
    await const WebReleaseBundle().build(environment);

    expect(environment.outputDir.childFile('version.json'), exists);
  }));

106
  test('WebReleaseBundle copies dart2js output and resource files to output directory', () => testbed.run(() async {
107
    environment.defines[kBuildMode] = 'release';
108 109
    final Directory webResources = environment.projectDir.childDirectory('web');
    webResources.childFile('index.html')
110 111 112 113 114 115 116 117 118
      ..createSync(recursive: true)
      ..writeAsStringSync('''
<html>
  <script src="main.dart.js" type="application/javascript"></script>
  <script>
    navigator.serviceWorker.register('flutter_service_worker.js');
  </script>
</html>
''');
119
    webResources.childFile('foo.txt')
120
      .writeAsStringSync('A');
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
    environment.buildDir.childFile('main.dart.js').createSync();

    await const WebReleaseBundle().build(environment);

    expect(environment.outputDir.childFile('foo.txt')
      .readAsStringSync(), 'A');
    expect(environment.outputDir.childFile('main.dart.js')
      .existsSync(), true);
    expect(environment.outputDir.childDirectory('assets')
      .childFile('AssetManifest.json').existsSync(), true);

    // Update to arbitary resource file triggers rebuild.
    webResources.childFile('foo.txt').writeAsStringSync('B');

    await const WebReleaseBundle().build(environment);

    expect(environment.outputDir.childFile('foo.txt')
      .readAsStringSync(), 'B');
139
    // Appends number to requests for service worker only
140
    expect(environment.outputDir.childFile('index.html').readAsStringSync(), allOf(
141
      contains('<script src="main.dart.js" type="application/javascript">'),
142 143
      contains('flutter_service_worker.js?v='),
    ));
144 145
  }));

146
  test('WebEntrypointTarget generates an entrypoint for a file outside of main', () => testbed.run(() async {
147 148 149 150
    final File mainFile = globals.fs.file(globals.fs.path.join('other', 'lib', 'main.dart'))
      ..createSync(recursive: true)
      ..writeAsStringSync('void main() {}');
    environment.defines[kTargetFile] = mainFile.path;
151 152 153 154 155 156 157 158
    await const WebEntrypointTarget().build(environment);

    final String generated = environment.buildDir.childFile('main.dart').readAsStringSync();

    // Import.
    expect(generated, contains("import 'file:///other/lib/main.dart' as entrypoint;"));
  }));

159
  test('WebEntrypointTarget generates a plugin registrant for a file outside of main', () => testbed.run(() async {
160 161 162 163
    final File mainFile = globals.fs.file(globals.fs.path.join('other', 'lib', 'main.dart'))
      ..createSync(recursive: true)
      ..writeAsStringSync('void main() {}');
    environment.defines[kTargetFile] = mainFile.path;
164 165 166 167 168 169 170
    environment.defines[kHasWebPlugins] = 'true';
    await const WebEntrypointTarget().build(environment);

    final String generated = environment.buildDir.childFile('main.dart').readAsStringSync();

    // Import.
    expect(generated, contains("import 'file:///other/lib/main.dart' as entrypoint;"));
171
    expect(generated, contains("import 'package:foo/generated_plugin_registrant.dart';"));
172 173
  }));

174

175
  test('WebEntrypointTarget generates an entrypoint with plugins and init platform on windows', () => testbed.run(() async {
176 177 178 179 180
    final File mainFile = globals.fs.file(globals.fs.path.join('foo', 'lib', 'main.dart'))
      ..createSync(recursive: true)
      ..writeAsStringSync('void main() {}');
    environment.defines[kTargetFile] = mainFile.path;

181 182 183 184 185 186
    environment.defines[kHasWebPlugins] = 'true';
    await const WebEntrypointTarget().build(environment);

    final String generated = environment.buildDir.childFile('main.dart').readAsStringSync();

    // Plugins
187
    expect(generated, contains("import 'package:foo/generated_plugin_registrant.dart';"));
188
    expect(generated, contains('registerPlugins(webPluginRegistrar);'));
189 190 191 192 193

    // Main
    expect(generated, contains('entrypoint.main();'));

    // Import.
194
    expect(generated, contains("import 'package:foo/main.dart' as entrypoint;"));
195
  }, overrides: <Type, Generator>{
196
    Platform: () => windows,
197 198 199
  }));

  test('WebEntrypointTarget generates an entrypoint without plugins and init platform', () => testbed.run(() async {
200 201 202 203
    final File mainFile = globals.fs.file(globals.fs.path.join('foo', 'lib', 'main.dart'))
      ..createSync(recursive: true)
      ..writeAsStringSync('void main() {}');
    environment.defines[kTargetFile] = mainFile.path;
204 205 206 207 208 209
    environment.defines[kHasWebPlugins] = 'false';
    await const WebEntrypointTarget().build(environment);

    final String generated = environment.buildDir.childFile('main.dart').readAsStringSync();

    // Plugins
210
    expect(generated, isNot(contains("import 'package:foo/generated_plugin_registrant.dart';")));
211
    expect(generated, isNot(contains('registerPlugins(webPluginRegistrar);')));
212 213 214 215
    // Main
    expect(generated, contains('entrypoint.main();'));
  }));

216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
  test('WebEntrypointTarget generates an entrypoint with a language version', () => testbed.run(() async {
    final File mainFile = globals.fs.file(globals.fs.path.join('foo', 'lib', 'main.dart'))
      ..createSync(recursive: true)
      ..writeAsStringSync('// @dart=2.8\nvoid main() {}');
    environment.defines[kTargetFile] = mainFile.path;
    await const WebEntrypointTarget().build(environment);

    final String generated = environment.buildDir.childFile('main.dart').readAsStringSync();

    // Language version
    expect(generated, contains('// @dart=2.8'));
  }));

  test('WebEntrypointTarget generates an entrypoint with a language version from a package config', () => testbed.run(() async {
    final File mainFile = globals.fs.file(globals.fs.path.join('foo', 'lib', 'main.dart'))
      ..createSync(recursive: true)
      ..writeAsStringSync('void main() {}');
    globals.fs.file(globals.fs.path.join('pubspec.yaml'))
      .writeAsStringSync('name: foo\n');
    environment.defines[kTargetFile] = mainFile.path;
    await const WebEntrypointTarget().build(environment);

    final String generated = environment.buildDir.childFile('main.dart').readAsStringSync();

    // Language version
241
    expect(generated, contains('// @dart=2.7'));
242 243
  }));

244
  test('WebEntrypointTarget generates an entrypoint without plugins and without init platform', () => testbed.run(() async {
245 246 247 248
    final File mainFile = globals.fs.file(globals.fs.path.join('foo', 'lib', 'main.dart'))
      ..createSync(recursive: true)
      ..writeAsStringSync('void main() {}');
    environment.defines[kTargetFile] = mainFile.path;
249 250 251 252 253 254
    environment.defines[kHasWebPlugins] = 'false';
    await const WebEntrypointTarget().build(environment);

    final String generated = environment.buildDir.childFile('main.dart').readAsStringSync();

    // Plugins
255
    expect(generated, isNot(contains("import 'package:foo/generated_plugin_registrant.dart';")));
256
    expect(generated, isNot(contains('registerPlugins(webPluginRegistrar);')));
257 258 259 260 261

    // Main
    expect(generated, contains('entrypoint.main();'));
  }));

262 263 264
  test('Dart2JSTarget calls dart2js with expected args with csp', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'profile';
    environment.defines[kCspMode] = 'true';
265 266 267
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
268
        '-Ddart.vm.profile=true',
269
        '--no-source-maps',
270 271
        '-o',
        environment.buildDir.childFile('app.dill').absolute.path,
272
        '--packages=.packages',
273 274 275 276 277 278 279 280
        '--cfe-only',
        environment.buildDir.childFile('main.dart').absolute.path,
      ]
    ));
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
        '-Ddart.vm.profile=true',
281
        '--no-source-maps',
282
        '-O4',
283 284 285 286 287 288 289
        '--no-minify',
        '--csp',
        '-o',
        environment.buildDir.childFile('main.dart.js').absolute.path,
        environment.buildDir.childFile('app.dill').absolute.path,
      ]
    ));
290

291
    await const Dart2JSTarget().build(environment);
292
  }, overrides: <Type, Generator>{
293
    ProcessManager: () => processManager,
294 295 296
  }));


297 298
  test('Dart2JSTarget calls dart2js with expected args with enabled experiment', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'profile';
299
    environment.defines[kExtraFrontEndOptions] = '--enable-experiment=non-nullable';
300 301 302 303
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
        '--enable-experiment=non-nullable',
304
        '-Ddart.vm.profile=true',
305
        '--no-source-maps',
306 307
        '-o',
        environment.buildDir.childFile('app.dill').absolute.path,
308
        '--packages=.packages',
309 310 311 312 313 314 315 316 317
        '--cfe-only',
        environment.buildDir.childFile('main.dart').absolute.path,
      ]
    ));
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
        '--enable-experiment=non-nullable',
        '-Ddart.vm.profile=true',
318
        '--no-source-maps',
319
        '-O4',
320 321 322 323 324 325 326 327 328 329 330 331
        '--no-minify',
        '-o',
        environment.buildDir.childFile('main.dart.js').absolute.path,
        environment.buildDir.childFile('app.dill').absolute.path,
      ]
    ));

    await const Dart2JSTarget().build(environment);
  }, overrides: <Type, Generator>{
    ProcessManager: () => processManager,
  }));

332 333
  test('Dart2JSTarget calls dart2js with expected args in profile mode', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'profile';
334 335 336
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
337
        '-Ddart.vm.profile=true',
338
        '--no-source-maps',
339 340
        '-o',
        environment.buildDir.childFile('app.dill').absolute.path,
341
        '--packages=.packages',
342 343 344 345 346 347 348 349
        '--cfe-only',
        environment.buildDir.childFile('main.dart').absolute.path,
      ]
    ));
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
        '-Ddart.vm.profile=true',
350
        '--no-source-maps',
351
        '-O4',
352 353 354 355 356 357
        '--no-minify',
        '-o',
        environment.buildDir.childFile('main.dart.js').absolute.path,
        environment.buildDir.childFile('app.dill').absolute.path,
      ]
    ));
358

359
    await const Dart2JSTarget().build(environment);
360
  }, overrides: <Type, Generator>{
361
    ProcessManager: () => processManager,
362 363 364 365
  }));

  test('Dart2JSTarget calls dart2js with expected args in release mode', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'release';
366 367 368
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
369
        '-Ddart.vm.product=true',
370
        '--no-source-maps',
371 372
        '-o',
        environment.buildDir.childFile('app.dill').absolute.path,
373
        '--packages=.packages',
374 375 376 377 378 379 380 381
        '--cfe-only',
        environment.buildDir.childFile('main.dart').absolute.path,
      ]
    ));
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
        '-Ddart.vm.product=true',
382
        '--no-source-maps',
383
        '-O4',
384 385 386 387 388
        '-o',
        environment.buildDir.childFile('main.dart.js').absolute.path,
        environment.buildDir.childFile('app.dill').absolute.path,
      ]
    ));
389

390
    await const Dart2JSTarget().build(environment);
391
  }, overrides: <Type, Generator>{
392
    ProcessManager: () => processManager,
393 394
  }));

395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
  test('Dart2JSTarget calls dart2js with expected args in release mode with native null assertions', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'release';
    environment.defines[kNativeNullAssertions] = 'true';
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
        '--native-null-assertions',
        '-Ddart.vm.product=true',
        '--no-source-maps',
        '-o',
        environment.buildDir.childFile('app.dill').absolute.path,
        '--packages=.packages',
        '--cfe-only',
        environment.buildDir.childFile('main.dart').absolute.path,
      ]
    ));
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
        '--native-null-assertions',
        '-Ddart.vm.product=true',
        '--no-source-maps',
        '-O4',
        '-o',
        environment.buildDir.childFile('main.dart.js').absolute.path,
        environment.buildDir.childFile('app.dill').absolute.path,
      ]
    ));

    await const Dart2JSTarget().build(environment);
  }, overrides: <Type, Generator>{
    ProcessManager: () => processManager,
  }));

429 430 431
  test('Dart2JSTarget calls dart2js with expected args in release with dart2js optimization override', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'release';
    environment.defines[kDart2jsOptimization] = 'O3';
432 433 434
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
435
        '-Ddart.vm.product=true',
436
        '--no-source-maps',
437 438
        '-o',
        environment.buildDir.childFile('app.dill').absolute.path,
439
        '--packages=.packages',
440 441 442 443 444 445 446 447
        '--cfe-only',
        environment.buildDir.childFile('main.dart').absolute.path,
      ]
    ));
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
        '-Ddart.vm.product=true',
448
        '--no-source-maps',
449
        '-O3',
450 451 452 453 454
        '-o',
        environment.buildDir.childFile('main.dart.js').absolute.path,
        environment.buildDir.childFile('app.dill').absolute.path,
      ]
    ));
455

456
    await const Dart2JSTarget().build(environment);
457
  }, overrides: <Type, Generator>{
458
    ProcessManager: () => processManager,
459
  }));
460 461 462

  test('Dart2JSTarget produces expected depfile', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'release';
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
        '-Ddart.vm.product=true',
        '--no-source-maps',
        '-o',
        environment.buildDir.childFile('app.dill').absolute.path,
        '--packages=.packages',
        '--cfe-only',
        environment.buildDir.childFile('main.dart').absolute.path,
      ], onRun: () {
        environment.buildDir.childFile('app.dill.deps')
          .writeAsStringSync('file:///a.dart');
      },
    ));
478 479
    await const Dart2JSTarget().build(environment);

480
    expect(environment.buildDir.childFile('dart2js.d'), exists);
481
    final Depfile depfile = depfileService.parse(environment.buildDir.childFile('dart2js.d'));
482

483
    expect(depfile.inputs.single.path, globals.fs.path.absolute('a.dart'));
484 485 486
    expect(depfile.outputs.single.path,
      environment.buildDir.childFile('main.dart.js').absolute.path);
  }, overrides: <Type, Generator>{
487
    ProcessManager: () => processManager,
488
  }));
489 490 491

  test('Dart2JSTarget calls dart2js with Dart defines in release mode', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'release';
492
    environment.defines[kDartDefines] = encodeDartDefines(<String>['FOO=bar', 'BAZ=qux']);
493 494 495
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
496
        '-Ddart.vm.product=true',
497 498
        '-DFOO=bar',
        '-DBAZ=qux',
499
        '--no-source-maps',
500 501 502
        '-o',
        environment.buildDir.childFile('app.dill').absolute.path,
        '--packages=.packages',
503
       '--cfe-only',
504 505 506 507 508 509 510 511 512
        environment.buildDir.childFile('main.dart').absolute.path,
      ]
    ));
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
        '-Ddart.vm.product=true',
        '-DFOO=bar',
        '-DBAZ=qux',
513
        '--no-source-maps',
514
        '-O4',
515 516 517 518 519
        '-o',
        environment.buildDir.childFile('main.dart.js').absolute.path,
        environment.buildDir.childFile('app.dill').absolute.path,
      ]
    ));
520

521
    await const Dart2JSTarget().build(environment);
522
  }, overrides: <Type, Generator>{
523
    ProcessManager: () => processManager,
524 525
  }));

526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
  test('Dart2JSTarget can enable source maps', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'release';
    environment.defines[kSourceMapsEnabled] = 'true';
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
        '-Ddart.vm.product=true',
        '-o',
        environment.buildDir.childFile('app.dill').absolute.path,
        '--packages=.packages',
       '--cfe-only',
        environment.buildDir.childFile('main.dart').absolute.path,
      ]
    ));
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
        '-Ddart.vm.product=true',
        '-O4',
        '-o',
        environment.buildDir.childFile('main.dart.js').absolute.path,
        environment.buildDir.childFile('app.dill').absolute.path,
      ]
    ));

    await const Dart2JSTarget().build(environment);
  }, overrides: <Type, Generator>{
    ProcessManager: () => processManager,
  }));


557 558
  test('Dart2JSTarget calls dart2js with Dart defines in profile mode', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'profile';
559
    environment.defines[kDartDefines] = encodeDartDefines(<String>['FOO=bar', 'BAZ=qux']);
560 561 562
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
563
        '-Ddart.vm.profile=true',
564 565
        '-DFOO=bar',
        '-DBAZ=qux',
566
        '--no-source-maps',
567 568 569
        '-o',
        environment.buildDir.childFile('app.dill').absolute.path,
        '--packages=.packages',
570 571 572 573 574 575 576 577 578 579
        '--cfe-only',
        environment.buildDir.childFile('main.dart').absolute.path,
      ]
    ));
    processManager.addCommand(FakeCommand(
      command: <String>[
        ...kDart2jsLinuxArgs,
        '-Ddart.vm.profile=true',
        '-DFOO=bar',
        '-DBAZ=qux',
580
        '--no-source-maps',
581
        '-O4',
582 583 584 585 586 587
        '--no-minify',
        '-o',
        environment.buildDir.childFile('main.dart.js').absolute.path,
        environment.buildDir.childFile('app.dill').absolute.path,
      ]
    ));
588

589
    await const Dart2JSTarget().build(environment);
590
  }, overrides: <Type, Generator>{
591
    ProcessManager: () => processManager,
592 593
  }));

594 595 596 597 598 599
  test('Generated service worker is empty with none-strategy', () {
    final String result = generateServiceWorker(<String, String>{'/foo': 'abcd'}, <String>[], serviceWorkerStrategy: ServiceWorkerStrategy.none);

    expect(result, '');
  });

600
  test('Generated service worker correctly inlines file hashes', () {
601
    final String result = generateServiceWorker(<String, String>{'/foo': 'abcd'}, <String>[], serviceWorkerStrategy: ServiceWorkerStrategy.offlineFirst);
602 603 604 605

    expect(result, contains('{\n  "/foo": "abcd"\n};'));
  });

606
  test('Generated service worker includes core files', () {
607
    final String result = generateServiceWorker(<String, String>{'/foo': 'abcd'}, <String>['foo', 'bar'], serviceWorkerStrategy: ServiceWorkerStrategy.offlineFirst);
608 609 610 611

    expect(result, contains('"foo",\n"bar"'));
  });

612
  test('WebServiceWorker generates a service_worker for a web resource folder', () => testbed.run(() async {
613
    environment.outputDir.childDirectory('a').childFile('a.txt')
614 615 616 617 618 619 620
      ..createSync(recursive: true)
      ..writeAsStringSync('A');
    await const WebServiceWorker().build(environment);

    expect(environment.outputDir.childFile('flutter_service_worker.js'), exists);
    // Contains file hash.
    expect(environment.outputDir.childFile('flutter_service_worker.js').readAsStringSync(),
621
      contains('"a/a.txt": "7fc56270e7a70fa81a5935b72eacbe29"'));
622 623
    expect(environment.buildDir.childFile('service_worker.d'), exists);
    // Depends on resource file.
624 625 626 627 628
    expect(environment.buildDir.childFile('service_worker.d').readAsStringSync(),
      contains('a/a.txt'));
    // Contains NOTICES
    expect(environment.outputDir.childFile('flutter_service_worker.js').readAsStringSync(),
      contains('NOTICES'));
629
  }));
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644

  test('WebServiceWorker contains baseUrl cache', () => testbed.run(() async {
    environment.outputDir
      .childFile('index.html')
      .createSync(recursive: true);
    await const WebServiceWorker().build(environment);

    expect(environment.outputDir.childFile('flutter_service_worker.js'), exists);
    // Contains file hash for both `/` and index.html.
    expect(environment.outputDir.childFile('flutter_service_worker.js').readAsStringSync(),
      contains('"/": "d41d8cd98f00b204e9800998ecf8427e"'));
    expect(environment.outputDir.childFile('flutter_service_worker.js').readAsStringSync(),
      contains('"index.html": "d41d8cd98f00b204e9800998ecf8427e"'));
    expect(environment.buildDir.childFile('service_worker.d'), exists);
  }));
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661

  test('WebServiceWorker does not cache source maps', () => testbed.run(() async {
    environment.outputDir
      .childFile('main.dart.js')
      .createSync(recursive: true);
    environment.outputDir
      .childFile('main.dart.js.map')
      .createSync(recursive: true);
    await const WebServiceWorker().build(environment);

    // No caching of source maps.
    expect(environment.outputDir.childFile('flutter_service_worker.js').readAsStringSync(),
      isNot(contains('"main.dart.js.map"')));
    // Expected twice, once for RESOURCES and once for CORE.
    expect(environment.outputDir.childFile('flutter_service_worker.js').readAsStringSync(),
      contains('"main.dart.js"'));
  }));
662
}