diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index d17d209e86..20b7c6211a 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -152,16 +152,16 @@ jobs: if: "always() && steps.pkg_web_css_pub_get.conclusion == 'success'" working-directory: pkg/web_css job_003: - name: "smoke_test; PKG: pkg/indexed_blob; `dart format --output=none --set-exit-if-changed .`, `dart analyze --fatal-infos .`" + name: "smoke_test; PKGS: pkg/indexed_blob, pkg/puppeteer_screenshots; `dart format --output=none --set-exit-if-changed .`, `dart analyze --fatal-infos .`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.9.0;packages:pkg/indexed_blob;commands:format-analyze_1" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.9.0;packages:pkg/indexed_blob-pkg/puppeteer_screenshots;commands:format-analyze_1" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:3.9.0;packages:pkg/indexed_blob + os:ubuntu-latest;pub-cache-hosted;sdk:3.9.0;packages:pkg/indexed_blob-pkg/puppeteer_screenshots os:ubuntu-latest;pub-cache-hosted;sdk:3.9.0 os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest @@ -185,6 +185,19 @@ jobs: run: dart analyze --fatal-infos . if: "always() && steps.pkg_indexed_blob_pub_get.conclusion == 'success'" working-directory: pkg/indexed_blob + - id: pkg_puppeteer_screenshots_pub_get + name: pkg/puppeteer_screenshots; dart pub get + run: dart pub get + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkg/puppeteer_screenshots + - name: "pkg/puppeteer_screenshots; dart format --output=none --set-exit-if-changed ." + run: "dart format --output=none --set-exit-if-changed ." + if: "always() && steps.pkg_puppeteer_screenshots_pub_get.conclusion == 'success'" + working-directory: pkg/puppeteer_screenshots + - name: "pkg/puppeteer_screenshots; dart analyze --fatal-infos ." + run: dart analyze --fatal-infos . + if: "always() && steps.pkg_puppeteer_screenshots_pub_get.conclusion == 'success'" + working-directory: pkg/puppeteer_screenshots job_004: name: "smoke_test; PKG: pkg/pub_integration; `dart format --output=none --set-exit-if-changed .`, `dart analyze --fatal-infos lib/`, `dart analyze --fatal-infos test/`" runs-on: ubuntu-latest @@ -872,6 +885,45 @@ jobs: - job_007 - job_008 job_021: + name: "unit_test; PKG: pkg/puppeteer_screenshots; `dart test --run-skipped`" + runs-on: ubuntu-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf + with: + path: "~/.pub-cache/hosted" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.9.0;packages:pkg/puppeteer_screenshots;commands:test_09" + restore-keys: | + os:ubuntu-latest;pub-cache-hosted;sdk:3.9.0;packages:pkg/puppeteer_screenshots + os:ubuntu-latest;pub-cache-hosted;sdk:3.9.0 + os:ubuntu-latest;pub-cache-hosted + os:ubuntu-latest + - name: Setup Dart SDK + uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c + with: + sdk: "3.9.0" + - id: checkout + name: Checkout repository + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + - id: pkg_puppeteer_screenshots_pub_get + name: pkg/puppeteer_screenshots; dart pub get + run: dart pub get + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkg/puppeteer_screenshots + - name: "pkg/puppeteer_screenshots; dart test --run-skipped" + run: dart test --run-skipped + if: "always() && steps.pkg_puppeteer_screenshots_pub_get.conclusion == 'success'" + working-directory: pkg/puppeteer_screenshots + needs: + - job_001 + - job_002 + - job_003 + - job_004 + - job_005 + - job_006 + - job_007 + - job_008 + job_022: name: "unit_test; PKG: pkg/web_app; `dart test --run-skipped`" runs-on: ubuntu-latest steps: @@ -910,7 +962,7 @@ jobs: - job_006 - job_007 - job_008 - job_022: + job_023: name: "unit_test; PKG: pkg/web_css; `dart test --run-skipped`" runs-on: ubuntu-latest steps: @@ -949,7 +1001,7 @@ jobs: - job_006 - job_007 - job_008 - job_023: + job_024: name: "unit_test; PKG: pkg/pub_integration; `dart test -j1 --run-skipped `find test -name \"*_test\\\\.dart\" | sort | sed -n '0~4p'``" runs-on: ubuntu-latest steps: @@ -988,7 +1040,7 @@ jobs: - job_006 - job_007 - job_008 - job_024: + job_025: name: "unit_test; PKG: pkg/pub_integration; `dart test -j1 --run-skipped `find test -name \"*_test\\\\.dart\" | sort | sed -n '1~4p'``" runs-on: ubuntu-latest steps: @@ -1027,7 +1079,7 @@ jobs: - job_006 - job_007 - job_008 - job_025: + job_026: name: "unit_test; PKG: pkg/pub_integration; `dart test -j1 --run-skipped `find test -name \"*_test\\\\.dart\" | sort | sed -n '2~4p'``" runs-on: ubuntu-latest steps: @@ -1066,7 +1118,7 @@ jobs: - job_006 - job_007 - job_008 - job_026: + job_027: name: "unit_test; PKG: pkg/pub_integration; `dart test -j1 --run-skipped `find test -name \"*_test\\\\.dart\" | sort | sed -n '3~4p'``" runs-on: ubuntu-latest steps: @@ -1105,7 +1157,7 @@ jobs: - job_006 - job_007 - job_008 - job_027: + job_028: name: "unit_test; PKG: pkg/pub_worker; `dart test --run-skipped --concurrency=1 `find test -name \"*_test\\\\.dart\" | sort | sed -n '0~3p'``" runs-on: ubuntu-latest steps: @@ -1144,7 +1196,7 @@ jobs: - job_006 - job_007 - job_008 - job_028: + job_029: name: "unit_test; PKG: pkg/pub_worker; `dart test --run-skipped --concurrency=1 `find test -name \"*_test\\\\.dart\" | sort | sed -n '1~3p'``" runs-on: ubuntu-latest steps: @@ -1183,7 +1235,7 @@ jobs: - job_006 - job_007 - job_008 - job_029: + job_030: name: "unit_test; PKG: pkg/pub_worker; `dart test --run-skipped --concurrency=1 `find test -name \"*_test\\\\.dart\" | sort | sed -n '2~3p'``" runs-on: ubuntu-latest steps: diff --git a/pkg/pub_integration/README.md b/pkg/pub_integration/README.md deleted file mode 100644 index be6608391a..0000000000 --- a/pkg/pub_integration/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Screenshots - -## Tools - -The screenshots are created during the `puppeteer` tests, if the `PUB_SCREENSHOT_DIR` -environment variable is set. - -The comparison is created by the `odiff` tool using `docker`. To build the `odiff` -container, user the following command: - -``` -cd tool -docker build . -f Dockerfile.odiff -t odiff -``` - -## Before/after screenshots - -To create before/after screenshot comparisons, use the following workflow: - -1. Run `pkg/pub_integration` tests with `PUB_SCREENSHOT_DIR=/path/to/before/dir`. -1. Make the changes. -1. Run `pkg/pub_integration` tests with `PUB_SCREENSHOT_DIR=/path/to/after/dir`. -1. Run `dart tool/compare_screenshots.dart ` - -The comparison will put the files into the `` with the before/after/diff -versions, alongside a summary `index.html` file to have a quick overview of them. - -## Missing features - -- Create a better side-by-side comparison for images, as right now everything is just a list of images. diff --git a/pkg/pub_integration/lib/src/pub_puppeteer_helpers.dart b/pkg/pub_integration/lib/src/pub_puppeteer_helpers.dart index 6bda35c46a..3548666aa1 100644 --- a/pkg/pub_integration/lib/src/pub_puppeteer_helpers.dart +++ b/pkg/pub_integration/lib/src/pub_puppeteer_helpers.dart @@ -4,8 +4,8 @@ import 'dart:async'; import 'package:puppeteer/puppeteer.dart'; +import 'package:puppeteer_screenshots/puppeteer_utils.dart'; -import 'screenshot_utils.dart'; import 'test_browser.dart'; const webmastersReadonlyScope = diff --git a/pkg/pub_integration/lib/src/test_browser.dart b/pkg/pub_integration/lib/src/test_browser.dart index 81cf6c2684..643689a828 100644 --- a/pkg/pub_integration/lib/src/test_browser.dart +++ b/pkg/pub_integration/lib/src/test_browser.dart @@ -9,10 +9,9 @@ import 'dart:io'; import 'package:_pub_shared/validation/html/html_validation.dart'; import 'package:path/path.dart' as p; import 'package:puppeteer/puppeteer.dart'; +import 'package:puppeteer_screenshots/puppeteer_utils.dart'; import 'package:retry/retry.dart'; -import 'screenshot_utils.dart'; - /// Creates and tracks the headless Chrome environment, its temp directories and /// and uncaught exceptions. class TestBrowser { diff --git a/pkg/pub_integration/pubspec.yaml b/pkg/pub_integration/pubspec.yaml index 7a0db82e34..97882a12ae 100644 --- a/pkg/pub_integration/pubspec.yaml +++ b/pkg/pub_integration/pubspec.yaml @@ -13,12 +13,12 @@ dependencies: path: ^1.8.0 pub_semver: ^2.0.0 puppeteer: 3.16.0 + puppeteer_screenshots: retry: ^3.1.2 oauth2: ^2.0.0 dev_dependencies: coverage: any # test already depends on it - markdown: ^7.3.0 pubspec_parse: ^1.5.0 shelf: ^1.4.0 test: ^1.16.5 diff --git a/pkg/pub_integration/test/browser_test.dart b/pkg/pub_integration/test/browser_test.dart index cd2898bba1..a4176d75f3 100644 --- a/pkg/pub_integration/test/browser_test.dart +++ b/pkg/pub_integration/test/browser_test.dart @@ -7,9 +7,9 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:pub_integration/src/fake_test_context_provider.dart'; import 'package:pub_integration/src/pub_puppeteer_helpers.dart'; -import 'package:pub_integration/src/screenshot_utils.dart'; import 'package:pub_integration/src/test_browser.dart'; import 'package:puppeteer/puppeteer.dart'; +import 'package:puppeteer_screenshots/puppeteer_utils.dart'; import 'package:test/test.dart'; void main() { diff --git a/pkg/pub_integration/test/dartdoc_search_test.dart b/pkg/pub_integration/test/dartdoc_search_test.dart index c17216b832..bf452e01fa 100644 --- a/pkg/pub_integration/test/dartdoc_search_test.dart +++ b/pkg/pub_integration/test/dartdoc_search_test.dart @@ -6,9 +6,9 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:pub_integration/src/fake_test_context_provider.dart'; -import 'package:pub_integration/src/screenshot_utils.dart'; import 'package:pub_integration/src/test_browser.dart'; import 'package:puppeteer/puppeteer.dart'; +import 'package:puppeteer_screenshots/puppeteer_utils.dart'; import 'package:test/test.dart'; void main() { diff --git a/pkg/pub_integration/test/fake_sign_in_test.dart b/pkg/pub_integration/test/fake_sign_in_test.dart index 23413c6402..a994705bae 100644 --- a/pkg/pub_integration/test/fake_sign_in_test.dart +++ b/pkg/pub_integration/test/fake_sign_in_test.dart @@ -6,8 +6,8 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:pub_integration/src/fake_test_context_provider.dart'; -import 'package:pub_integration/src/screenshot_utils.dart'; import 'package:pub_integration/src/test_browser.dart'; +import 'package:puppeteer_screenshots/puppeteer_utils.dart'; import 'package:test/test.dart'; void main() { diff --git a/pkg/pub_integration/test/pkg_admin_page_test.dart b/pkg/pub_integration/test/pkg_admin_page_test.dart index 044181e3f0..0ed68ac015 100644 --- a/pkg/pub_integration/test/pkg_admin_page_test.dart +++ b/pkg/pub_integration/test/pkg_admin_page_test.dart @@ -9,8 +9,8 @@ import 'dart:io' show Platform; import 'package:http/http.dart' as http; import 'package:pub_integration/src/fake_test_context_provider.dart'; import 'package:pub_integration/src/pub_puppeteer_helpers.dart'; -import 'package:pub_integration/src/screenshot_utils.dart'; import 'package:pub_integration/src/test_browser.dart'; +import 'package:puppeteer_screenshots/puppeteer_utils.dart'; import 'package:test/test.dart'; void main() { diff --git a/pkg/pub_integration/test/report_test.dart b/pkg/pub_integration/test/report_test.dart index 240fdefeb1..055e6d941d 100644 --- a/pkg/pub_integration/test/report_test.dart +++ b/pkg/pub_integration/test/report_test.dart @@ -8,8 +8,8 @@ import 'package:_pub_shared/data/admin_api.dart'; import 'package:http/http.dart' as http; import 'package:pub_integration/src/fake_test_context_provider.dart'; import 'package:pub_integration/src/pub_puppeteer_helpers.dart'; -import 'package:pub_integration/src/screenshot_utils.dart'; import 'package:pub_integration/src/test_browser.dart'; +import 'package:puppeteer_screenshots/puppeteer_utils.dart'; import 'package:test/test.dart'; final _caseIdExpr = RegExp(r'[0-9]{8}I[0-9a-f]{10}'); diff --git a/pkg/pub_integration/test/search_completition_test.dart b/pkg/pub_integration/test/search_completition_test.dart index f311fbddaf..34ff597cf6 100644 --- a/pkg/pub_integration/test/search_completition_test.dart +++ b/pkg/pub_integration/test/search_completition_test.dart @@ -6,9 +6,9 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:pub_integration/src/fake_test_context_provider.dart'; -import 'package:pub_integration/src/screenshot_utils.dart'; import 'package:pub_integration/src/test_browser.dart'; import 'package:puppeteer/puppeteer.dart'; +import 'package:puppeteer_screenshots/puppeteer_utils.dart'; import 'package:test/test.dart'; void main() { diff --git a/pkg/pub_integration/test/search_update_test.dart b/pkg/pub_integration/test/search_update_test.dart index cb8136af81..45bf87e210 100644 --- a/pkg/pub_integration/test/search_update_test.dart +++ b/pkg/pub_integration/test/search_update_test.dart @@ -7,9 +7,9 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:pub_integration/src/fake_test_context_provider.dart'; import 'package:pub_integration/src/pub_puppeteer_helpers.dart'; -import 'package:pub_integration/src/screenshot_utils.dart'; import 'package:pub_integration/src/test_browser.dart'; import 'package:puppeteer/puppeteer.dart'; +import 'package:puppeteer_screenshots/puppeteer_utils.dart'; import 'package:test/test.dart'; void main() { diff --git a/pkg/puppeteer_screenshots/README.md b/pkg/puppeteer_screenshots/README.md new file mode 100644 index 0000000000..6327f34374 --- /dev/null +++ b/pkg/puppeteer_screenshots/README.md @@ -0,0 +1,38 @@ +# Screenshots + +This tool helps create and compare screenshots before/after changes that affect visual styles. + +## Creating screenshots + +The screenshots are created during the `puppeteer` tests, if the `SCREENSHOT_DIR` +environment variable is set. + +The tests are instrumented with calls to take screenshots on selected parts of +the screens. During the method call, it will change the viewport and the main +theme of the page, taking 6 screenshots for each call. + +## Before/after screenshots + +To create before/after screenshot comparisons, use the following workflow: + +1. Generate screenshots by running tests with `SCREENSHOT_DIR=/path/to/before/dir`. +1. Make the changes. +1. Generate screenshots by running tests with `SCREENSHOT_DIR=/path/to/after/dir`. +1. Run `dart bin/compare_screenshots.dart ` + +The comparison will put the files into the `` with the before/after/diff +versions, alongside a summary `index.html` file to have a quick overview of them. + +### Required tool for comparisons + +The comparison is created by the `odiff` tool running in a container using `docker`. +To build the `odiff` container, user the following command: + +``` +cd tool +docker build . -f Dockerfile.odiff -t odiff +``` + +## Missing features + +- Create a better side-by-side comparison for images, as right now everything is only a list of images. diff --git a/pkg/pub_integration/tool/compare_screenshots.dart b/pkg/puppeteer_screenshots/bin/compare_screenshots.dart similarity index 100% rename from pkg/pub_integration/tool/compare_screenshots.dart rename to pkg/puppeteer_screenshots/bin/compare_screenshots.dart diff --git a/pkg/pub_integration/lib/src/screenshot_utils.dart b/pkg/puppeteer_screenshots/lib/puppeteer_utils.dart similarity index 77% rename from pkg/pub_integration/lib/src/screenshot_utils.dart rename to pkg/puppeteer_screenshots/lib/puppeteer_utils.dart index 59db463ae6..d1f19d34d4 100644 --- a/pkg/pub_integration/lib/src/screenshot_utils.dart +++ b/pkg/puppeteer_screenshots/lib/puppeteer_utils.dart @@ -10,15 +10,14 @@ import 'package:puppeteer/puppeteer.dart'; // Default screen with 16:10 ratio. final desktopDeviceViewport = DeviceViewport(width: 1280, height: 800); -final _screenshotDir = Platform.environment['PUB_SCREENSHOT_DIR']; +final _screenshotDir = Platform.environment['SCREENSHOT_DIR']; final _isScreenshotDirSet = _screenshotDir != null && _screenshotDir!.isNotEmpty; // Set this variable to enable screenshot files to be updated with new takes. // The default is to throw an exception to prevent accidental overrides from // separate tests. -final _allowScreeshotUpdates = - Platform.environment['PUB_SCREENSHOT_UPDATE'] == '1'; +final _allowScreeshotUpdates = Platform.environment['SCREENSHOT_UPDATE'] == '1'; // Note: The default values are the last, so we don't need reset // the original values after taking the screenshots. @@ -34,16 +33,16 @@ extension ScreenshotPageExt on Page { await File(path).writeAsBytes(await screenshot()); } - /// Takes screenshots **if** `PUB_SCREENSHOT_DIR` environment variable is set. + /// Takes screenshots **if** `SCREENSHOT_DIR` environment variable is set. /// /// Iterates over viewports and themes, and generates screenshot files with the /// following pattern: - /// - `PUB_SCREENSHOT_DIR/$prefix-desktop-dark.png` - /// - `PUB_SCREENSHOT_DIR/$prefix-desktop-light.png` - /// - `PUB_SCREENSHOT_DIR/$prefix-mobile-dark.png` - /// - `PUB_SCREENSHOT_DIR/$prefix-mobile-light.png` - /// - `PUB_SCREENSHOT_DIR/$prefix-tablet-dark.png` - /// - `PUB_SCREENSHOT_DIR/$prefix-tablet-light.png` + /// - `SCREENSHOT_DIR/$prefix-desktop-dark.png` + /// - `SCREENSHOT_DIR/$prefix-desktop-light.png` + /// - `SCREENSHOT_DIR/$prefix-mobile-dark.png` + /// - `SCREENSHOT_DIR/$prefix-mobile-light.png` + /// - `SCREENSHOT_DIR/$prefix-tablet-dark.png` + /// - `SCREENSHOT_DIR/$prefix-tablet-light.png` Future takeScreenshots({ required String selector, required String prefix, @@ -54,16 +53,16 @@ extension ScreenshotPageExt on Page { } extension ScreenshotElementHandleExt on ElementHandle { - /// Takes screenshots **if** `PUB_SCREENSHOT_DIR` environment variable is set. + /// Takes screenshots **if** `SCREENSHOT_DIR` environment variable is set. /// /// Iterates over viewports and themes, and generates screenshot files with the /// following pattern: - /// - `PUB_SCREENSHOT_DIR/$prefix-desktop-dark.png` - /// - `PUB_SCREENSHOT_DIR/$prefix-desktop-light.png` - /// - `PUB_SCREENSHOT_DIR/$prefix-mobile-dark.png` - /// - `PUB_SCREENSHOT_DIR/$prefix-mobile-light.png` - /// - `PUB_SCREENSHOT_DIR/$prefix-tablet-dark.png` - /// - `PUB_SCREENSHOT_DIR/$prefix-tablet-light.png` + /// - `SCREENSHOT_DIR/$prefix-desktop-dark.png` + /// - `SCREENSHOT_DIR/$prefix-desktop-light.png` + /// - `SCREENSHOT_DIR/$prefix-mobile-dark.png` + /// - `SCREENSHOT_DIR/$prefix-mobile-light.png` + /// - `SCREENSHOT_DIR/$prefix-tablet-dark.png` + /// - `SCREENSHOT_DIR/$prefix-tablet-light.png` Future takeScreenshots(String prefix) async { final body = await page.$('body'); final bodyClassAttr = diff --git a/pkg/puppeteer_screenshots/mono_pkg.yaml b/pkg/puppeteer_screenshots/mono_pkg.yaml new file mode 100644 index 0000000000..4c966478d4 --- /dev/null +++ b/pkg/puppeteer_screenshots/mono_pkg.yaml @@ -0,0 +1,11 @@ +# See https://pub.dev/packages/mono_repo for details on this file +sdk: + - 3.9.0 + +stages: + - smoke_test: + - group: + - format + - analyze: --fatal-infos . + - unit_test: + - test: --run-skipped diff --git a/pkg/puppeteer_screenshots/pubspec.yaml b/pkg/puppeteer_screenshots/pubspec.yaml new file mode 100644 index 0000000000..d885d44179 --- /dev/null +++ b/pkg/puppeteer_screenshots/pubspec.yaml @@ -0,0 +1,13 @@ +name: puppeteer_screenshots +publish_to: none +environment: + sdk: ^3.9.0 +resolution: workspace + +dependencies: + markdown: ^7.3.0 + path: ^1.8.0 + puppeteer: ^3.16.0 + +dev_dependencies: + test: ^1.0.0 diff --git a/pkg/puppeteer_screenshots/test/no_op_test.dart b/pkg/puppeteer_screenshots/test/no_op_test.dart new file mode 100644 index 0000000000..8ba33b8ea6 --- /dev/null +++ b/pkg/puppeteer_screenshots/test/no_op_test.dart @@ -0,0 +1,11 @@ +// Copyright (c) 2025, 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. + +import 'package:test/test.dart'; + +void main() { + test('no-op', () { + // TODO: create end-to-end test for screenshot creation and comparison + }); +} diff --git a/pkg/pub_integration/tool/Dockerfile.odiff b/pkg/puppeteer_screenshots/tool/Dockerfile.odiff similarity index 100% rename from pkg/pub_integration/tool/Dockerfile.odiff rename to pkg/puppeteer_screenshots/tool/Dockerfile.odiff diff --git a/pkg/pub_integration/tool/comparison_web.css b/pkg/puppeteer_screenshots/tool/comparison_web.css similarity index 100% rename from pkg/pub_integration/tool/comparison_web.css rename to pkg/puppeteer_screenshots/tool/comparison_web.css diff --git a/pkg/pub_integration/tool/comparison_web.js b/pkg/puppeteer_screenshots/tool/comparison_web.js similarity index 100% rename from pkg/pub_integration/tool/comparison_web.js rename to pkg/puppeteer_screenshots/tool/comparison_web.js diff --git a/pubspec.yaml b/pubspec.yaml index bec665f480..78f1b83c5e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,6 +14,7 @@ workspace: - pkg/fake_gcloud - pkg/indexed_blob - pkg/pub_package_reader + - pkg/puppeteer_screenshots dev_dependencies: lints: ^6.0.0